Modal and Dialogs
Modal and Dialogs
Section titled “Modal and Dialogs”Purpose
Section titled “Purpose”Modal and dialog components provide overlays for focused user interactions like confirmations, forms, and bottom sheet selections.
Public API
Section titled “Public API”Generic modal wrapper with backdrop.
interface ModalProps { open: boolean; onClose: () => void; title?: string; children: ReactNode; maxWidth?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'; fullScreen?: boolean;}ConfirmationModal
Section titled “ConfirmationModal”Pre-built confirmation dialog.
interface ConfirmationModalProps { open: boolean; onClose: () => void; onConfirm: () => void; title: string; message: string; confirmText?: string; cancelText?: string; severity?: 'info' | 'warning' | 'error' | 'success';}CustomBottomSheet
Section titled “CustomBottomSheet”Mobile-friendly bottom sheet.
interface CustomBottomSheetProps { open: boolean; onClose: () => void; title?: string; children: ReactNode; snapPoints?: number[];}Confirmation Dialog
Section titled “Confirmation Dialog”import { ConfirmationModal } from 'src/components/confirmation-modal';
function DeleteGroupButton({ groupId }: Props) { const [open, setOpen] = useState(false);
const handleConfirm = async () => { await groupsHandlers.deleteGroup(groupId); toast.success('Group deleted'); setOpen(false); };
return ( <> <Button onClick={() => setOpen(true)} color="error"> Delete Group </Button>
<ConfirmationModal open={open} onClose={() => setOpen(false)} onConfirm={handleConfirm} title="Delete Group?" message="This action cannot be undone. All members will be removed." confirmText="Delete" severity="error" /> </> );}Bottom Sheet (Mobile)
Section titled “Bottom Sheet (Mobile)”import { CustomBottomSheet } from 'src/components/custom-bottom-sheet';
function SelectWishlistSheet({ open, onClose, onSelect }: Props) { const wishlists = useSelector((state) => state.wishlists.userWishlists);
return ( <CustomBottomSheet open={open} onClose={onClose} title="Select Wishlist" > <List> {wishlists.map((wishlist) => ( <ListItem key={wishlist.id} button onClick={() => { onSelect(wishlist); onClose(); }} > <ListItemText primary={wishlist.name} /> </ListItem> ))} </List> </CustomBottomSheet> );}Custom Modal
Section titled “Custom Modal”import { Modal } from 'src/components/modal';
function EditGroupModal({ open, onClose, group }: Props) { return ( <Modal open={open} onClose={onClose} title="Edit Group" maxWidth="md" > <EditGroupForm group={group} onSubmit={(data) => { // Handle save onClose(); }} /> </Modal> );}Behavior and Constraints
Section titled “Behavior and Constraints”- Backdrop click: Closes modal by default (configurable)
- Escape key: Closes modal
- Focus trap: Focus stays within modal when open
- Scroll lock: Body scroll disabled when modal open
- Responsive: Bottom sheets on mobile, modals on desktop
Testing
Section titled “Testing”it('should call onConfirm when confirmed', async () => { const onConfirm = jest.fn(); render( <ConfirmationModal open onClose={() => {}} onConfirm={onConfirm} title="Confirm" message="Are you sure?" /> );
await userEvent.click(screen.getByText('Confirm')); expect(onConfirm).toHaveBeenCalled();});Further Reading
Section titled “Further Reading”- Secret Santa Groups - Delete confirmations
- Item Management - Bottom sheet selectors