-
Notifications
You must be signed in to change notification settings - Fork 4.5k
Refine dialog wrapper and use it for all existing dialogs #3407
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
240ad07
Refine dialog wrapper and use it for all existing dialogs
kravets-levko cbb0501
Merge branch 'master' into refine-dialogs
kravets-levko a54dd68
Merge branch 'master' into refine-dialogs
kravets-levko c23cccd
CR1
kravets-levko 5b0f845
Minor updates
kravets-levko 79c0f3b
Merge branch 'master' into refine-dialogs
kravets-levko 5d08f25
Update docs
kravets-levko 0a4111d
Merge branch 'master' into refine-dialogs
kravets-levko File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,209 @@ | ||
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; | ||
}), | ||
kravets-levko marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}; | ||
|
||
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; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.