Modals
Modals are dialog boxes that appear on top of the current page, typically used for confirmations, quick actions, or focused interactions that require user attention.
-
Modal components should use the
.modal.tsxsuffix to clearly indicate their purpose and enable proper translation key extraction. -
Use Carbon's
Modalcomponents (ModalHeader,ModalBody,ModalFooter) for consistent modal structure:import { ModalHeader, ModalBody, ModalFooter } from '@carbon/react'; const DeleteModal: React.FC<DeleteModalProps> = ({ closeModal, itemId }) => { return ( <> <ModalHeader closeModal={closeModal} title={t('deleteItem', 'Delete item')} /> <ModalBody> <p>{t('deleteConfirmation', 'Are you sure you want to delete this item?')}</p> </ModalBody> <ModalFooter> <Button kind="secondary" onClick={closeModal}> {t('cancel', 'Cancel')} </Button> <Button kind="danger" onClick={handleDelete}> {t('delete', 'Delete')} </Button> </ModalFooter> </> ); }; -
Use loading states in modal action buttons to provide feedback during async operations:
const [isDeleting, setIsDeleting] = useState(false); <Button kind="danger" onClick={handleDelete} disabled={isDeleting} > {isDeleting ? ( <InlineLoading description={t('deleting', 'Deleting') + '...'} /> ) : ( <span>{t('delete', 'Delete')}</span> )} </Button> -
Modals should handle errors gracefully and display them using snackbars. Prefer
async/awaitover promise chains for better readability:const handleDelete = useCallback(async () => { setIsDeleting(true); try { await deleteItem(itemId); mutate(); // Update SWR cache closeModal(); showSnackbar({ isLowContrast: true, kind: 'success', title: t('itemDeleted', 'Item deleted'), }); } catch (error) { showSnackbar({ kind: 'error', title: t('errorDeletingItem', 'Error deleting item'), subtitle: error?.message, }); } finally { setIsDeleting(false); } }, [itemId, closeModal, mutate, t]); -
Use
showModalfrom@openmrs/esm-frameworkto launch modals programmatically:import { showModal } from '@openmrs/esm-framework'; const handleOpenModal = () => { const dispose = showModal('my-modal-name', { itemId: '123', closeModal: () => dispose(), }); }; -
Modal components should be registered using
getAsyncLifecyclefor code splitting:export const deleteItemModal = getAsyncLifecycle( () => import('./delete-item.modal'), options ); -
Use appropriate button kinds for modal actions:
dangerfor destructive actions (delete, remove)primaryfor primary confirmationssecondaryfor cancel actions
<ModalFooter> <Button kind="secondary" onClick={closeModal}> {t('cancel', 'Cancel')} </Button> <Button kind="danger" onClick={handleDelete}> {t('delete', 'Delete')} </Button> </ModalFooter>