mirror of
https://github.com/getredash/redash.git
synced 2025-12-19 17:37:19 -05:00
* Prettier all the JS files * Add GitHub Action to autoformat code pushed to master * Fix eslint violation due to formatting. * Remove GitHub actions for styling * Add restyled.io config
210 lines
5.3 KiB
JavaScript
210 lines
5.3 KiB
JavaScript
import { isFunction } from "lodash";
|
|
import React from "react";
|
|
import PropTypes from "prop-types";
|
|
import ReactDOM from "react-dom";
|
|
|
|
/**
|
|
Wrapper for dialogs based on Ant's <Modal> component.
|
|
|
|
|
|
Using wrapped dialogs
|
|
=====================
|
|
|
|
Wrapped component is an object with two fields:
|
|
|
|
{
|
|
showModal: (dialogProps) => object({
|
|
result: Promise,
|
|
close: (result) => void,
|
|
dismiss: (reason) => void,
|
|
}),
|
|
Component: React.Component, // wrapped dialog component
|
|
}
|
|
|
|
To open dialog, use `showModal` method; optionally you can pass additional properties that
|
|
will be expanded on wrapped component:
|
|
|
|
const dialog = SomeWrappedDialog.showModal()
|
|
|
|
const dialog = SomeWrappedDialog.showModal({ greeting: 'Hello' })
|
|
|
|
To get result of modal, use `result` property:
|
|
|
|
dialog.result
|
|
.then(...) // pressed OK button or used `close` method; resolved value is a result of dialog
|
|
.catch(...) // pressed Cancel button or used `dismiss` method; optional argument is a rejection reason.
|
|
|
|
Also, dialog has `close` and `dismiss` methods that allows to close dialog by caller. Passed arguments
|
|
will be used to resolve/reject `dialog.result` promise. `update` methods allows to pass new properties
|
|
to dialog.
|
|
|
|
|
|
Creating a dialog
|
|
================
|
|
|
|
1. Add imports:
|
|
|
|
import { wrap as wrapDialog, DialogPropType } from 'path/to/DialogWrapper';
|
|
|
|
2. define a `dialog` property on your component:
|
|
|
|
propTypes = {
|
|
dialog: DialogPropType.isRequired,
|
|
};
|
|
|
|
`dialog` property is an object:
|
|
|
|
{
|
|
props: object, // properties for <Modal> component;
|
|
close: (result) => void, // method to confirm dialog; `result` will be returned to caller
|
|
dismiss: (reason) => void, // method to reject dialog; `reason` will be returned to caller
|
|
}
|
|
|
|
3. expand additional properties on <Modal> component:
|
|
|
|
render() {
|
|
const { dialog } = this.props;
|
|
return (
|
|
<Modal {...dialog.props}>
|
|
);
|
|
}
|
|
|
|
4. wrap your component and export it:
|
|
|
|
export default wrapDialog(YourComponent).
|
|
|
|
Your component is ready to use. Wrapper will manage <Modal>'s visibility and events.
|
|
If you want to override behavior of `onOk`/`onCancel` - don't forget to close dialog:
|
|
|
|
customOkHandler() {
|
|
this.saveData().then(() => {
|
|
this.props.dialog.close({ success: true }); // or dismiss();
|
|
});
|
|
}
|
|
|
|
render() {
|
|
const { dialog } = this.props;
|
|
return (
|
|
<Modal {...dialog.props} onOk={() => this.customOkHandler()}>
|
|
);
|
|
}
|
|
|
|
|
|
Settings
|
|
========
|
|
|
|
You can setup this wrapper to use custom `Promise` library (for example, Bluebird):
|
|
|
|
import DialogWrapper from 'path/to/DialogWrapper';
|
|
import Promise from 'bluebird';
|
|
|
|
DialogWrapper.Promise = Promise;
|
|
|
|
It could be useful to avoid `unhandledrejection` exception that would fire with native Promises,
|
|
or when some custom Promise library is used in application.
|
|
|
|
*/
|
|
|
|
export const DialogPropType = PropTypes.shape({
|
|
props: PropTypes.shape({
|
|
visible: PropTypes.bool,
|
|
onOk: PropTypes.func,
|
|
onCancel: PropTypes.func,
|
|
afterClose: PropTypes.func,
|
|
}).isRequired,
|
|
close: PropTypes.func.isRequired,
|
|
dismiss: PropTypes.func.isRequired,
|
|
});
|
|
|
|
// default export of module
|
|
const DialogWrapper = {
|
|
Promise,
|
|
DialogPropType,
|
|
wrap() {},
|
|
};
|
|
|
|
function openDialog(DialogComponent, props) {
|
|
const dialog = {
|
|
props: {
|
|
visible: true,
|
|
onOk: () => {},
|
|
onCancel: () => {},
|
|
afterClose: () => {},
|
|
},
|
|
close: () => {},
|
|
dismiss: () => {},
|
|
};
|
|
|
|
const dialogResult = {
|
|
resolve: () => {},
|
|
reject: () => {},
|
|
};
|
|
|
|
const container = document.createElement("div");
|
|
document.body.appendChild(container);
|
|
|
|
function render() {
|
|
ReactDOM.render(<DialogComponent {...props} dialog={dialog} />, container);
|
|
}
|
|
|
|
function destroyDialog() {
|
|
// Allow calling chain to roll up, and then destroy component
|
|
setTimeout(() => {
|
|
ReactDOM.unmountComponentAtNode(container);
|
|
document.body.removeChild(container);
|
|
}, 10);
|
|
}
|
|
|
|
function closeDialog(result) {
|
|
dialogResult.resolve(result);
|
|
dialog.props.visible = false;
|
|
render();
|
|
}
|
|
|
|
function dismissDialog(reason) {
|
|
dialogResult.reject(reason);
|
|
dialog.props.visible = false;
|
|
render();
|
|
}
|
|
|
|
dialog.props.onOk = closeDialog;
|
|
dialog.props.onCancel = dismissDialog;
|
|
dialog.props.afterClose = destroyDialog;
|
|
dialog.close = closeDialog;
|
|
dialog.dismiss = dismissDialog;
|
|
|
|
const result = {
|
|
close: closeDialog,
|
|
dismiss: dismissDialog,
|
|
update: newProps => {
|
|
props = { ...props, ...newProps };
|
|
render();
|
|
},
|
|
result: new DialogWrapper.Promise((resolve, reject) => {
|
|
dialogResult.resolve = resolve;
|
|
dialogResult.reject = reject;
|
|
}),
|
|
};
|
|
|
|
render(); // show it only when all structures initialized to avoid unnecessary re-rendering
|
|
|
|
// Some known libraries support
|
|
// Bluebird: http://bluebirdjs.com/docs/api/suppressunhandledrejections.html
|
|
if (isFunction(result.result.suppressUnhandledRejections)) {
|
|
result.result.suppressUnhandledRejections();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
export function wrap(DialogComponent) {
|
|
return {
|
|
Component: DialogComponent,
|
|
showModal: props => openDialog(DialogComponent, props),
|
|
};
|
|
}
|
|
|
|
DialogWrapper.wrap = wrap;
|
|
|
|
export default DialogWrapper;
|