docbrown/app/javascript/utilities/showModal.jsx
Arit Amana 750d379807
Allow ModRole & Admin to Suspend & Unsuspend User (#17946)
* gotta commit at some point

* out of gas

* IT'S WORKING NOW! 💥

* Cypress tests

* fix Travis failures

* incorporate PR review suggestions

* fix one failing Cypress test

* update API call

* add Unsuspend flow; fix Travis failures

* add tests for unsuspend flow

* remove unnecessary atrribute

* add article_policy spec

* fix failing Travis

* nudge Travis

* fix apparent typo  o o 🤷

* refactor a bunch of duplication

* nudge Travis

* fix Cypress failure related to authorizing moderator for user_status action

* rename modal partial

* remove unnecessary button-exists check

* address PR review suggestions

* hide suspend user btn after action

* toggle btn based on author suspension status

* add data-testids

* use data-testids

* controller refactor
2022-06-27 12:45:35 -04:00

89 lines
3.1 KiB
JavaScript

// Unique ID applied to modals created using the showWindowModal function
export const WINDOW_MODAL_ID = 'window-modal';
// We only import these modules if a user actually triggers a modal. Here we cache them so they are only imported once.
let preactImport;
let modalImports;
const getPreactImport = () => {
if (!preactImport) {
preactImport = import('preact');
}
return preactImport;
};
const getModalImports = () => {
if (!modalImports) {
modalImports = [import('@crayons/Modal'), getPreactImport()];
}
return Promise.all(modalImports);
};
/**
* This helper function presents content inside a Preact modal.
*
* The modal content may be passed either as:
* - the actual HTML (using modalContent prop), which will be dropped straight into the modal
* - a CSS selector (using contentSelector prop), which will be used to locate the HTML content on the current page before dropping it into the modal
*
* Only one modal will be presented at any given time. All additional props will be passed directly to the Modal component.
*
* @param {Object} args
* @param {HTMLElement} args.modalContent The HTML to display inside of the modal
* @param {string} args.contentSelector The CSS query to locate the HTML to be presented in the modal, as an alternative to passing the actual HTML (e.g. '#my-modal-content')
* @param {Function} args.onOpen A callback function to run when the modal opens. This can be useful, for example, to attach any event listeners to items inside the modal.
* @param {Object} args.document Allows us to specify which "document" is relevant; e.g. use within iframes.
*/
export const showWindowModal = async ({
modalContent,
contentSelector,
onOpen,
document = window.document,
...modalProps
}) => {
const [{ Modal }, { render, h }] = await getModalImports();
// Guard against two modals being opened at once
let currentModalContainer = document.getElementById(WINDOW_MODAL_ID);
if (currentModalContainer) {
render(null, currentModalContainer);
} else {
currentModalContainer = document.createElement('div');
currentModalContainer.setAttribute('id', WINDOW_MODAL_ID);
document.body.appendChild(currentModalContainer);
}
render(
<Modal
onClose={() => {
render(null, currentModalContainer);
}}
focusTrapSelector={`#${WINDOW_MODAL_ID}`}
document={document}
{...modalProps}
>
<div
className="h-100 w-100"
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{
__html:
modalContent ?? document.querySelector(contentSelector)?.innerHTML,
}}
/>
</Modal>,
currentModalContainer,
);
onOpen?.();
};
/**
* This helper function closes any currently open window modal. This can be useful, for example, if your modal contains a "cancel" button.
*/
export const closeWindowModal = async (document = window.document) => {
const currentModalContainer = document.getElementById(WINDOW_MODAL_ID);
if (currentModalContainer) {
const { render } = await getPreactImport();
render(null, currentModalContainer);
}
};