Skip to content

Commit

Permalink
lib: allow awaiting Dialogs.show()
Browse files Browse the repository at this point in the history
This new feature can be useful when you create a dialog in an async
function and want to return the result of that dialog via async/await
features.

For example in the files plugin we want to query a user to replace a
file and await the result of that. In the Dialog component you return
the interesting dialog values via `Dialogs.close` or `Dialogs.reject` if
you want to throw an exception.
  • Loading branch information
jelly committed Apr 25, 2024
1 parent d39255f commit b726844
Showing 1 changed file with 62 additions and 5 deletions.
67 changes: 62 additions & 5 deletions pkg/lib/dialogs.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,19 +82,56 @@
* }
* }
*
* If there is a situation where you want to wait until a Dialog is closed
* after opening, you can "await Dialogs.show(<My Dialog />)"
*
* class Example extends React.Component {
* static contextType = DialogsContext;
*
* async function handleClick() {
* try {
* const result = await Dialogs.show(<MyDialog />);
* console.log(result);
* catch (err) {
* }
*
* }
*
* function render() {
* const Dialogs = this.context;
* return <Button onClick={() => this.handleClick}>Open dialog</Button>;
* }
* }
*
* class MyDialog extends React.Component {
* static contextType = DialogsContext;
*
* render() {
* <Button onClick={() => Dialogs.close("yes")}>Yes</Button>
* <Button onClick={() => Dialogs.close("no")}>No</Button>
* }
* }
*
*
* - Dialogs.show(component)
*
* Calling "Dialogs.show" will render the given component as a direct
* child of the inner-most enclosing "WithDialogs" component. The
* component is of course intended to be a dialog, such as
* Patternfly's "Modal". There is only ever one of these; a second
* call to "show" will remove the previously rendered component.
* Passing "null" will remove the currently rendered componenet, if any.
* call to "show" is considered a bug and "Dialogs.close" should be called first.
* "Dialogs.show" returns a promise that is settled by either "Dialogs.close" or
* "Dialogs.reject".
*
* - Dialogs.close([args])
*
* - Dialogs.close()
* Calling "Dialogs.close([args])" will close the currently open Dialog and
* optionally resolve the promise with the provided "args".
*
* Same as "Dialogs.show(null)".
* - Dialogs.reject(err)
*
* Calling "Dialogs.reject(err)" will close the currently open Dialog
* and reject the promise with the provided Error.
*/

import React, { useContext, useRef, useState } from "react";
Expand All @@ -104,6 +141,8 @@ export const useDialogs = () => useContext(DialogsContext);

export const WithDialogs = ({ children }) => {
const is_open = useRef(false); // synchronous
const resolveRef = useRef(null);
const rejectRef = useRef(null);
const [dialog, setDialog] = useState(null);

const Dialogs = {
Expand All @@ -115,10 +154,28 @@ export const WithDialogs = ({ children }) => {
JSON.stringify(dialog));
is_open.current = !!component;
setDialog(component);
return new Promise((resolve, reject) => {
resolveRef.current = resolve;
rejectRef.current = reject;
});
},
close: (args) => {
is_open.current = false;
setDialog(null);
if (resolveRef.current !== null) {
resolveRef.current(args);
resolveRef.current = null;
rejectRef.current = null;
}
},
close: () => {
reject: (err) => {
is_open.current = false;
setDialog(null);
if (rejectRef.current !== null) {
rejectRef.current(err);
resolveRef.current = null;
rejectRef.current = null;
}
},
isActive: () => dialog !== null
};
Expand Down

0 comments on commit b726844

Please sign in to comment.