-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Open
0 / 50 of 5 issues completedLabels
ci/cdPull requests that update GitHub Actions codePull requests that update GitHub Actions coderefactorRefactoring tasksRefactoring tasksui/uxissue related and being worked with the figma file of the Admin UIissue related and being worked with the figma file of the Admin UI
Description
Problem Statement
30+ modals across the codebase implement repetitive CRUD (Create, Read, Update, Delete) patterns with:
- Duplicate modal structure (header, body, footer, close button)
- Inconsistent form submission and error handling
- Repetitive loading and success states
- Varying modal sizes and styling approaches
- Examples: VolunteerGroupModal, CampaignModal, VenueModal, PledgeModal, ActionItemModal
Proposed Solution
-
Create flexible CRUD modal templates:
CRUDModalTemplate: Base template with common structureCreateModal: Template for create operationsEditModal: Template for edit operations with data loadingDeleteModal: Confirmation modal with warning UIViewModal: Read-only detail modal
-
Create a linter to prevent new code reverting to the unapproved way.
- This command was used to identify files:
rg -n --glob 'src/screens/**' -e '<Modal\b' -e "from\\s+'react-bootstrap'.*Modal" -e "from\\s+\"react-bootstrap\".*Modal" | cut -d: -f1 | sort -u - The linter must run as a lint staged check. Look at the
.huskydirectory for details. - It must also be part of our Pull Request GitHub action file
.github/workflows/pull-request.ymlas a job that is a prerequisite to thePre-Test-Checks-Passjob.
Implementation Plan
- This command was used to identify files:
Example Starter Code
src/types/CRUDModalTemplate/interface.ts
export interface CrudModalBaseProps {
open: boolean; title: string; onClose: ()=>void;
primaryText?: string; secondaryText?: string; loading?: boolean; error?: string;
}src/shared-components/CRUDModalTemplate/CRUDModalTemplate.tsx
import React from 'react';
import { Modal, Button, Spinner, Alert } from 'react-bootstrap';
import type { CrudModalBaseProps } from '../../types/CRUDModalTemplate/interface';
export const CRUDModalTemplate: React.FC<CrudModalBaseProps & { children?: React.ReactNode; onPrimary?: ()=>void; }> = ({ open, title, onClose, onPrimary, loading, error, children, primaryText='Save', secondaryText='Cancel' }) => (
<Modal show={open} onHide={onClose} aria-labelledby="crud-modal-title" centered>
<Modal.Header closeButton><Modal.Title id="crud-modal-title">{title}</Modal.Title></Modal.Header>
<Modal.Body>
{loading && <Spinner role="status" animation="border" className="me-2" />}
{error && <Alert variant="danger">{error}</Alert>}
{!loading && children}
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={onClose}>{secondaryText}</Button>
{onPrimary && <Button variant="primary" onClick={onPrimary} disabled={!!loading}>{primaryText}</Button>}
</Modal.Footer>
</Modal>
);src/shared-components/CRUDModalTemplate/CRUDModalTemplate.spec.tsx
import { render, screen } from '@testing-library/react';
import { CRUDModalTemplate } from './CRUDModalTemplate';
test('renders title', () => {
render(<CRUDModalTemplate open title="Edit Item" onClose={()=>{}} />);
expect(screen.getByText(/edit item/i)).toBeInTheDocument();
});Learning Resources
- Modal a11y (WAI-ARIA): https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/
- React Bootstrap Modal: https://react-bootstrap.github.io/components/modal/
Files to Modify
-
src/shared-components/CRUDModalTemplate/CRUDModalTemplate.tsx(create) -
src/shared-components/CRUDModalTemplate/CreateModal.tsx(create) -
src/shared-components/CRUDModalTemplate/EditModal.tsx(create) -
src/shared-components/CRUDModalTemplate/DeleteModal.tsx(create) -
src/shared-components/CRUDModalTemplate/ViewModal.tsx(create) -
src/shared-components/CRUDModalTemplate/CRUDModalTemplate.spec.tsx(create) -
src/shared-components/CRUDModalTemplate/types.ts(create) -
src/shared-components/CRUDModalTemplate/index.ts(create) -
src/shared-components/CRUDModalTemplate/CRUDModalTemplate.module.css(create)
Implementation Approach
Step 1: Design Modal Template Interface
Define props for:
- Modal state (isOpen, onClose, onSave/onDelete)
- Content (title, children/render prop for body)
- Loading and error states
- Action buttons (customize text, icons, colors)
- Size variants (sm, md, lg, xl, full)
Step 2: Implement Base CRUDModalTemplate
- Wrap react-bootstrap Modal with consistent structure
- Header with title and close button
- Body with loading/error state handling
- Footer with action buttons
- Keyboard shortcuts (Esc to close, Enter to submit)
Step 3: Implement Specialized Templates
- CreateModal: Form submission with validation feedback
- EditModal: Data fetching, pre-population, and update logic
- DeleteModal: Warning UI, confirmation input, destructive action styling
- ViewModal: Read-only display with tabbed sections support
Step 4: Integration Hooks
useModalState: Custom hook for modal open/close managementuseFormModal: Hook combining modal state with form handlinguseMutationModal: Hook for GraphQL mutation modals
Step 5: Testing & Documentation
- Unit tests for all modal variants
- Integration tests with form submission
- Accessibility testing (focus trapping, keyboard nav)
- Migration examples from existing modals
Dependencies
Blocked by: (none)
Blocks: (Modal rollout issues in later phases)
Blocking Issues
- None
Acceptance Criteria
- All 5 modal templates implemented with TypeScript
- Support for custom content via children or render props
- Loading, error, and success states handled consistently
- Focus trap and keyboard navigation working
- Consistent button placement and styling
- Test coverage > 90%
- Migration guide with before/after examples
- Storybook stories for all modal types
Effort Estimate
- Template architecture design: 3 hours
- Base CRUDModalTemplate implementation: 5 hours
- Specialized templates (4 types): 6 hours
- Integration hooks: 2 hours
- Testing & documentation: 2 hours
- Total: 18 hours
Testing Requirements
- Unit tests: Modal opens/closes correctly
- Unit tests: Action buttons trigger callbacks
- Unit tests: Loading states display correctly
- Integration tests: Form submission flow
- Integration tests: Error handling and display
- Accessibility tests: Focus trap on modal open
- Accessibility tests: Keyboard navigation (Esc, Tab, Enter)
- Visual regression tests: All modal sizes and states
Success Criteria
- Modal templates reduce modal code by 70%+
- All modals have consistent UX (loading, errors, actions)
- Migration from existing modals is straightforward
- Focus management and keyboard navigation work correctly in all modals
Other information
Related past PRs.
Sub-issues
Metadata
Metadata
Assignees
Labels
ci/cdPull requests that update GitHub Actions codePull requests that update GitHub Actions coderefactorRefactoring tasksRefactoring tasksui/uxissue related and being worked with the figma file of the Admin UIissue related and being worked with the figma file of the Admin UI
Type
Projects
Status
Backlog
Status
Backlog
Status
Backlog