Skip to content

Conversation

@Esheshwari
Copy link

@Esheshwari Esheshwari commented Dec 25, 2025

What kind of change does this PR introduce?

Feature / Foundation Component

Issue Number

Fixes #5292

Snapshots / Videos

N/A (foundational shared component, no UI integration yet).
A short video showing the added/changed files is attached.

If relevant, did you update the documentation?

Not applicable for this foundational component.

Summary

This PR introduces the foundational CRUDModalTemplate shared component.

  • Provides a reusable modal template for CRUD operations
  • Strongly typed with TypeScript
  • Includes unit tests for rendering and interactions
  • Non-breaking, additive change

This PR only covers Phase 1 (foundation). Phase 2 will be submitted separately.

Does this PR introduce a breaking change?

No. This is an additive, non-breaking change.

Checklist

CodeRabbit AI Review

  • I have reviewed and addressed all critical issues flagged by CodeRabbit AI
  • I have implemented or provided justification for each non-critical suggestion
  • I have documented my reasoning in the PR comments where CodeRabbit AI suggestions were not implemented

Test Coverage

  • I have written tests for all new changes/features
  • I have verified that test coverage meets or exceeds 95%
  • I have run the test suite locally and all tests pass

Other information

N/A

Have you read the contributing guide?

Yes
https://github.com/user-attachments/assets/0c1e4db2-94f2-4ab4-88b4-091baf2497af

Summary by CodeRabbit

  • New Features

    • Introduced a reusable modal component for CRUD operations with built-in support for loading states, error messages, and keyboard shortcuts (Enter key to submit).
  • Tests

    • Added comprehensive test coverage for modal rendering, user interactions, and keyboard event handling.

✏️ Tip: You can customize this high-level summary in your review settings.

@keploy
Copy link

keploy bot commented Dec 25, 2025

No significant changes currently retry

@github-actions
Copy link

Our Pull Request Approval Process

This PR will be reviewed according to our:

  1. Palisadoes Contributing Guidelines

  2. AI Usage Policy

Your PR may be automatically closed if:

  1. Our PR template isn't filled in correctly

  2. You haven't correctly linked your PR to an issue

Thanks for contributing!

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 25, 2025

Walkthrough

Introduces a new CRUD modal template component with supporting types, styles, and test suite. Includes a React functional component rendering a modal with header, body, and footer sections; handling loading and error states; and supporting keyboard shortcuts. Defines base props interface and exports public APIs.

Changes

Cohort / File(s) Summary
Type Definitions
src/shared-components/CRUDModalTemplate/types.ts
New CrudModalBaseProps interface defining modal props: open, title, onClose, optional primaryText, secondaryText, loading, and error.
Component Implementation
src/shared-components/CRUDModalTemplate/CRUDModalTemplate.tsx
New React functional component with Enter key handler effect; renders Bootstrap modal with title header, body (spinner/error/children based on state), and footer with secondary and conditional primary buttons. Primary button disabled during loading.
Styling
src/shared-components/CRUDModalTemplate/CRUDModalTemplate.module.css
New CSS module for modal overlay positioning, centering, and styling for modal container, header, body, footer, and button components.
Testing
src/shared-components/CRUDModalTemplate/CRUDModalTemplate.spec.tsx
New test suite covering modal rendering with title and content, onClose callback on cancel button click, and onPrimary callback on Enter key press.
Public Exports
src/shared-components/CRUDModalTemplate/index.ts
Re-exports default component from ./CRUDModalTemplate and all exports from ./types.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: add CRUDModalTemplate foundation with tests' accurately describes the main change—introducing a foundational CRUD modal component with accompanying test coverage.
Description check ✅ Passed The description follows the template structure, includes issue reference (#5292), provides clear summary of the feature, and addresses all checklist items including CodeRabbit review and test coverage verification.
Linked Issues check ✅ Passed The PR implements Phase 1 of issue #5292 by creating the base CRUDModalTemplate component with TypeScript typing, test coverage, and supporting files as specified in the linked issue requirements.
Out of Scope Changes check ✅ Passed All changes are scoped to the CRUDModalTemplate foundation phase (types, component, tests, CSS, and exports) with no unrelated modifications or out-of-scope additions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3c5dd15 and 7c442eb.

📒 Files selected for processing (5)
  • src/shared-components/CRUDModalTemplate/CRUDModalTemplate.module.css
  • src/shared-components/CRUDModalTemplate/CRUDModalTemplate.spec.tsx
  • src/shared-components/CRUDModalTemplate/CRUDModalTemplate.tsx
  • src/shared-components/CRUDModalTemplate/index.ts
  • src/shared-components/CRUDModalTemplate/types.ts
🧰 Additional context used
🧠 Learnings (22)
📓 Common learnings
Learnt from: palisadoes
Repo: PalisadoesFoundation/talawa-admin PR: 0
File: :0-0
Timestamp: 2025-12-18T02:57:44.185Z
Learning: Phase 1 issues created for foundations: #5083 (ProfileAvatarDisplay), #5084 (AdminPortal structure), #5085 (UserTableRow), #5086 (BaseModal), #5087 (EmptyState), #5088 (LoadingState).
Learnt from: Ansingh0305
Repo: PalisadoesFoundation/talawa-admin PR: 0
File: :0-0
Timestamp: 2025-11-01T16:34:10.347Z
Learning: In talawa-admin parallel test sharding (PR #4565), Bootstrap modals render via portals to document.body, causing screen.getByRole('dialog') to find dialogs from ALL concurrent test shards. Solution: use unique data-testid per modal instance and query within that scope using within() helper, or use findAllByTestId and take last element (less robust, timing-dependent).
Learnt from: palisadoes
Repo: PalisadoesFoundation/talawa-admin PR: 0
File: :0-0
Timestamp: 2025-12-20T23:32:16.510Z
Learning: In PalisadoesFoundation/talawa-admin bulk issue creation prompts, each issue must include two developer-enablement sections: (1) Example Starter Code (types.ts, component scaffold, spec scaffold, and before→after for rollout), and (2) Learning Resources with issue-specific tutorials (framework/library/a11y/testing). Titles must include the Phase, all issues assigned to palisadoes-bot, only the refactor label, and both upstream (Blocked by) and downstream (Blocks) links using actual GitHub issue numbers. Also apply a special duplicate check for LoadingStateWrapper vs #5088.
Learnt from: palisadoes
Repo: PalisadoesFoundation/talawa-admin PR: 0
File: :0-0
Timestamp: 2025-12-19T05:13:00.954Z
Learning: When creating ErrorBoundaryWrapper implementation plans for PalisadoesFoundation/talawa-admin, include: (1) Complete sample implementation with interface.ts, component, and tests, (2) Top 15-20 candidate files table with complexity, LOC impact, error patterns, priority, and feature area, (3) Two detailed before/after examples (simple and complex cases) showing code diffs and benefits, (4) Phased implementation plan with summary table showing files, PRs, hours, dependencies, and priority, (5) GitHub-ready issue templates with markdown format including: Issue Title, Problem Statement, Proposed Solution, Files to Modify (checkbox format), Implementation Approach (step-by-step), Dependencies (Blocked by/Blocks/Related with #numbers), Acceptance Criteria (checkbox format), Effort Estimate (hours breakdown), Testing Requirements (specific test cases), and Success Criteria, (6) Timeline with Gantt chart options (conservative vs aggressive), (7) Success metrics (before/after comparison). This ...
Learnt from: Chaitanya1672
Repo: PalisadoesFoundation/talawa-admin PR: 2049
File: src/screens/OrganizationActionItems/ActionItemUpdateModal.tsx:112-138
Timestamp: 2024-10-08T16:13:41.996Z
Learning: The `istanbul ignore next` comments in the `ActionItemUpdateModal.tsx` file were added as part of a commit that introduced tests for the `ActionItemUpdateModal` component. Removing these comments and writing tests to cover the previously ignored lines is recommended to ensure code integrity and improve test coverage.
Learnt from: rawadhossain
Repo: PalisadoesFoundation/talawa-admin PR: 4865
File: src/components/EventRegistrantsModal/Modal/EventRegistrantsModal.spec.tsx:261-280
Timestamp: 2025-11-27T11:40:30.747Z
Learning: In Talawa Admin tests (e.g., src/components/EventRegistrantsModal/Modal/EventRegistrantsModal.spec.tsx), react-bootstrap Modal header close buttons don't support custom test IDs, so using getAllByRole('button', { name: /close/i })[0] to select the modal's primary close action is acceptable and stable, as the Modal component consistently exposes "Close" as the accessible label.
Learnt from: Ansingh0305
Repo: PalisadoesFoundation/talawa-admin PR: 0
File: :0-0
Timestamp: 2025-11-12T00:28:53.713Z
Learning: In talawa-admin PR #4565 continuation (PR #4664 planned), Phase 2A mock isolation strategy uses global vi.clearAllMocks() in afterEach (vitest.setup.ts) plus refactoring module-level vi.fn() into per-test beforeEach blocks across 6 parallelizable issues (2A.0 foundation, 2A.1-2A.4 directory-based fixes, 2A.5 validation). This approach addresses mock call history accumulation enabling maxConcurrency: 2 and 2.3-2.5x speedup. vi.restoreAllMocks() alternative considered for spy restoration during validation phase. Phase 2A (mocks) correctly separated from Phase 2B (DOM/Apollo).
Learnt from: palisadoes
Repo: PalisadoesFoundation/talawa-admin PR: 0
File: :0-0
Timestamp: 2025-12-21T01:21:06.670Z
Learning: In PalisadoesFoundation/talawa-admin, when creating bulk reusable-component rollout work, the maintainer expects not only the foundation component issues but also one adoption issue per affected screen/file (auto-discovered), each assigned to palisadoes-bot, labeled only 'refactor', titled with the Phase, linked to actual upstream blockers (e.g., #5248, #5088, and the foundation issue), and including an "Implementation Plan" link to the initiating comment plus "Example Starter Code" and "Learning Resources".
Learnt from: palisadoes
Repo: PalisadoesFoundation/talawa-admin PR: 0
File: :0-0
Timestamp: 2025-12-24T17:07:11.187Z
Learning: PalisadoesFoundation/talawa-admin (#5426): For the shared-components migration plan and its sub-issues, do not include sample code in the plan or issue bodies; keep concise checklists that state “move files to target directory,” “update importing files,” and “update tests.” Phase 3 adoption must be batched in groups of ≤10 files (by portal), and all issue titles must use the “SharedComp - ” prefix.
Learnt from: palisadoes
Repo: PalisadoesFoundation/talawa-admin PR: 0
File: :0-0
Timestamp: 2025-12-19T00:27:59.172Z
Learning: In the PalisadoesFoundation/talawa-admin repo, when rolling out multi-PR component work (e.g., ProfileAvatarDisplay), the maintainer expects: (1) comprehensive plan to be reposted on the foundation issue (#5083), (2) all phase issues cross-linked to both #5083 and the coordinating epic (#5130), and (3) a tracking checklist table added to #5130. Use idempotent markers "AUTO-PLAN", "AUTO-LINK", and "AUTO-TRACKING" to avoid duplicate comments.
Learnt from: meetulr
Repo: PalisadoesFoundation/talawa-admin PR: 2387
File: src/screens/ManageTag/ManageTag.tsx:58-97
Timestamp: 2024-10-30T13:38:29.300Z
Learning: In the `ManageTag` component (`src/screens/ManageTag/ManageTag.tsx`), extracting modal state management to a custom hook may require additional state modifications for the `toggle` and `show` functions.
Learnt from: palisadoes
Repo: PalisadoesFoundation/talawa-admin PR: 0
File: :0-0
Timestamp: 2025-12-06T11:50:22.732Z
Learning: Talawa Admin reusable components: Each shared component must expose its props via src/types/<Component>/interface.ts (no inline interfaces) and include a short TSDoc header describing purpose and props.
Learnt from: palisadoes
Repo: PalisadoesFoundation/talawa-admin PR: 0
File: :0-0
Timestamp: 2025-12-06T11:50:22.732Z
Learning: Talawa Admin reusable components: Define each shared component’s props in src/types/<Component>/interface.ts (no inline interfaces) and add a short TSDoc header describing purpose and props.
Learnt from: palisadoes
Repo: PalisadoesFoundation/talawa-admin PR: 0
File: :0-0
Timestamp: 2025-12-06T11:50:22.732Z
Learning: Talawa Admin reusable components: Each shared component’s props must be defined in src/types/<Component>/interface.ts (strict typing; avoid inline prop types or any).
Learnt from: palisadoes
Repo: PalisadoesFoundation/talawa-admin PR: 0
File: :0-0
Timestamp: 2025-12-06T11:50:22.732Z
Learning: Talawa Admin reusable components: Define each shared component’s props in src/types/<Component>/interface.ts (avoid inline interfaces) and add a brief TSDoc header describing purpose and props.
📚 Learning: 2025-11-27T11:40:30.747Z
Learnt from: rawadhossain
Repo: PalisadoesFoundation/talawa-admin PR: 4865
File: src/components/EventRegistrantsModal/Modal/EventRegistrantsModal.spec.tsx:261-280
Timestamp: 2025-11-27T11:40:30.747Z
Learning: In Talawa Admin tests (e.g., src/components/EventRegistrantsModal/Modal/EventRegistrantsModal.spec.tsx), react-bootstrap Modal header close buttons don't support custom test IDs, so using getAllByRole('button', { name: /close/i })[0] to select the modal's primary close action is acceptable and stable, as the Modal component consistently exposes "Close" as the accessible label.

Applied to files:

  • src/shared-components/CRUDModalTemplate/CRUDModalTemplate.spec.tsx
  • src/shared-components/CRUDModalTemplate/CRUDModalTemplate.tsx
  • src/shared-components/CRUDModalTemplate/types.ts
  • src/shared-components/CRUDModalTemplate/CRUDModalTemplate.module.css
📚 Learning: 2024-10-08T16:13:41.996Z
Learnt from: Chaitanya1672
Repo: PalisadoesFoundation/talawa-admin PR: 2049
File: src/screens/OrganizationActionItems/ActionItemUpdateModal.tsx:112-138
Timestamp: 2024-10-08T16:13:41.996Z
Learning: The `istanbul ignore next` comments in the `ActionItemUpdateModal.tsx` file were added as part of a commit that introduced tests for the `ActionItemUpdateModal` component. Removing these comments and writing tests to cover the previously ignored lines is recommended to ensure code integrity and improve test coverage.

Applied to files:

  • src/shared-components/CRUDModalTemplate/CRUDModalTemplate.spec.tsx
📚 Learning: 2025-12-06T11:50:22.732Z
Learnt from: palisadoes
Repo: PalisadoesFoundation/talawa-admin PR: 0
File: :0-0
Timestamp: 2025-12-06T11:50:22.732Z
Learning: Talawa Admin reusable components: Each shared component must have a corresponding Component.spec.tsx test file.

Applied to files:

  • src/shared-components/CRUDModalTemplate/CRUDModalTemplate.spec.tsx
📚 Learning: 2025-12-18T09:49:48.620Z
Learnt from: AdityaRathi-10
Repo: PalisadoesFoundation/talawa-admin PR: 0
File: :0-0
Timestamp: 2025-12-18T09:49:48.620Z
Learning: In PalisadoesFoundation/talawa-admin PostCard.spec.tsx, custom vi.mock for useLocalstorage with shared module-level store object violated test isolation in Shard 3. The correct approach: remove custom mock, use real useLocalStorage() hook in beforeEach, change afterEach from vi.restoreAllMocks() to vi.clearAllMocks() + localStorage.clear(). The real hook uses window.localStorage which is mocked by Vitest test environment globally.

Applied to files:

  • src/shared-components/CRUDModalTemplate/CRUDModalTemplate.spec.tsx
📚 Learning: 2025-11-28T16:02:31.814Z
Learnt from: rawadhossain
Repo: PalisadoesFoundation/talawa-admin PR: 4882
File: src/screens/UserPortal/Volunteer/UpcomingEvents/RecurringEventVolunteerModal.spec.tsx:69-109
Timestamp: 2025-11-28T16:02:31.814Z
Learning: In Talawa Admin test files (e.g., src/screens/UserPortal/Volunteer/UpcomingEvents/RecurringEventVolunteerModal.spec.tsx), developers prefer keeping date/time mocks (like vi.spyOn(Date.prototype, 'toLocaleDateString')) inline within individual test cases rather than consolidating them in beforeEach blocks, to maintain clarity and keep mocks close to the tests that use them, even if it results in some duplication.

Applied to files:

  • src/shared-components/CRUDModalTemplate/CRUDModalTemplate.spec.tsx
📚 Learning: 2025-11-01T16:34:10.347Z
Learnt from: Ansingh0305
Repo: PalisadoesFoundation/talawa-admin PR: 0
File: :0-0
Timestamp: 2025-11-01T16:34:10.347Z
Learning: In talawa-admin parallel test sharding (PR #4565), Bootstrap modals render via portals to document.body, causing screen.getByRole('dialog') to find dialogs from ALL concurrent test shards. Solution: use unique data-testid per modal instance and query within that scope using within() helper, or use findAllByTestId and take last element (less robust, timing-dependent).

Applied to files:

  • src/shared-components/CRUDModalTemplate/CRUDModalTemplate.spec.tsx
📚 Learning: 2025-11-30T23:13:22.697Z
Learnt from: Ansingh0305
Repo: PalisadoesFoundation/talawa-admin PR: 0
File: :0-0
Timestamp: 2025-11-30T23:13:22.697Z
Learning: In talawa-admin PR #4908, increasing test concurrency from maxConcurrency: 1 to 6-12 with TOTAL_SHARDS: 12 exposed three critical latent bugs: (1) EventsAttendedByUser.spec.tsx used wrong GraphQL query mock (EVENT_DETAILS vs EVENT_DETAILS_BASIC with incorrect variable names), (2) User.mocks.ts missing search/reset refetch scenarios causing "No more mocked responses" errors, (3) EventCalendar.spec.tsx UTC timezone bug where new Date().toISOString() caused date calculation mismatches in non-UTC timezones. These bugs were masked at lower concurrency but surfaced consistently under parallel execution stress-testing. Fix involved aligning mocks with actual component queries and explicit timezone-aware date construction.

Applied to files:

  • src/shared-components/CRUDModalTemplate/CRUDModalTemplate.spec.tsx
📚 Learning: 2025-08-17T07:39:34.255Z
Learnt from: syedali237
Repo: PalisadoesFoundation/talawa-admin PR: 4077
File: package.json:189-213
Timestamp: 2025-08-17T07:39:34.255Z
Learning: The Talawa Admin codebase primarily uses .spec.tsx/.spec.ts naming convention for unit tests, with Cypress tests using .cy.ts pattern. However, there is at least one .test.tsx file in the codebase, so NYC exclude patterns should include both .spec and .test patterns.

Applied to files:

  • src/shared-components/CRUDModalTemplate/CRUDModalTemplate.spec.tsx
📚 Learning: 2025-11-11T11:51:09.236Z
Learnt from: Ansingh0305
Repo: PalisadoesFoundation/talawa-admin PR: 0
File: :0-0
Timestamp: 2025-11-11T11:51:09.236Z
Learning: In talawa-admin PR #4664 (continuation of PR #4565 sharding work), global mock cleanup was implemented via vi.clearAllMocks() in vitest.setup.ts afterEach, along with Apollo Client warning suppression (temporary, pending follow-up PR) and test suite refactoring to per-test StaticMockLink instances. This test isolation strategy addresses mock state leakage that forced maxConcurrency: 1 in PR #4565, enabling future parallel test execution improvements and 3-4x speedup potential.

Applied to files:

  • src/shared-components/CRUDModalTemplate/CRUDModalTemplate.spec.tsx
📚 Learning: 2025-10-22T22:22:27.696Z
Learnt from: Jashan32
Repo: PalisadoesFoundation/talawa-admin PR: 4524
File: src/screens/Requests/Requests.spec.tsx:0-0
Timestamp: 2025-10-22T22:22:27.696Z
Learning: In talawa-admin tests using react-infinite-scroll-component (e.g., src/screens/Requests/Requests.spec.tsx), jsdom scroll is flaky. Prefer a resilient approach: try querying the container via document.querySelector('[data-testid="infinite-scroll-component"]') and fall back to window scroll if missing. Strengthen assertions by checking for page-2 items (e.g., "User9 Test") to ensure fetchMore actually ran instead of relying only on row counts.

Applied to files:

  • src/shared-components/CRUDModalTemplate/CRUDModalTemplate.spec.tsx
📚 Learning: 2025-11-27T11:41:54.843Z
Learnt from: rawadhossain
Repo: PalisadoesFoundation/talawa-admin PR: 4865
File: src/components/EventRegistrantsModal/Modal/EventRegistrantsModal.spec.tsx:302-328
Timestamp: 2025-11-27T11:41:54.843Z
Learning: In Talawa Admin tests (e.g., src/components/EventRegistrantsModal/Modal/EventRegistrantsModal.spec.tsx), using queryByLabelText with an HTMLElement cast for MUI Autocomplete inputs is acceptable because the Autocomplete input can mount asynchronously and isn't always available for strict getBy* queries at initial render. This pattern is stable for MUI Autocomplete components.

Applied to files:

  • src/shared-components/CRUDModalTemplate/CRUDModalTemplate.spec.tsx
📚 Learning: 2025-01-26T12:32:45.867Z
Learnt from: IITI-tushar
Repo: PalisadoesFoundation/talawa-admin PR: 3400
File: src/screens/UserPortal/Organizations/Organizations.spec.tsx:19-19
Timestamp: 2025-01-26T12:32:45.867Z
Learning: In React test files, avoid using React hooks outside component scope (including in static objects like mock data). Instead, initialize hooks inside describe blocks or extract the needed functionality without using hooks.

Applied to files:

  • src/shared-components/CRUDModalTemplate/CRUDModalTemplate.spec.tsx
📚 Learning: 2025-12-21T08:59:37.942Z
Learnt from: rawadhossain
Repo: PalisadoesFoundation/talawa-admin PR: 5287
File: src/shared-components/Recurrence/CustomRecurrenceModal.spec.tsx:78-84
Timestamp: 2025-12-21T08:59:37.942Z
Learning: In talawa-admin test files, ensure each spec file uses an explicit vi.clearAllMocks() in an afterEach block to guarantee test isolation. This should be present even if a global cleanup exists in vitest.setup.ts, as the linter enforces per-file hygiene. Apply this guideline to all spec files (e.g., src/**/*.spec.tsx) to satisfy ESLint/Vitest requirements and prevent bleed-over between tests.

Applied to files:

  • src/shared-components/CRUDModalTemplate/CRUDModalTemplate.spec.tsx
📚 Learning: 2024-10-30T13:38:29.300Z
Learnt from: meetulr
Repo: PalisadoesFoundation/talawa-admin PR: 2387
File: src/screens/ManageTag/ManageTag.tsx:58-97
Timestamp: 2024-10-30T13:38:29.300Z
Learning: In the `ManageTag` component (`src/screens/ManageTag/ManageTag.tsx`), extracting modal state management to a custom hook may require additional state modifications for the `toggle` and `show` functions.

Applied to files:

  • src/shared-components/CRUDModalTemplate/CRUDModalTemplate.tsx
  • src/shared-components/CRUDModalTemplate/types.ts
📚 Learning: 2025-12-18T02:57:44.185Z
Learnt from: palisadoes
Repo: PalisadoesFoundation/talawa-admin PR: 0
File: :0-0
Timestamp: 2025-12-18T02:57:44.185Z
Learning: Refactoring standard: Use components/AdminPortal/** for admin-only UI (with mirrored src/types/AdminPortal/**), components/UserPortal/** for user-only UI, and components/<Name>/ at root for shared UI; all props in src/types/<Component>/interface.ts with brief TSDoc.

Applied to files:

  • src/shared-components/CRUDModalTemplate/CRUDModalTemplate.tsx
  • src/shared-components/CRUDModalTemplate/types.ts
  • src/shared-components/CRUDModalTemplate/index.ts
📚 Learning: 2025-12-06T11:50:22.732Z
Learnt from: palisadoes
Repo: PalisadoesFoundation/talawa-admin PR: 0
File: :0-0
Timestamp: 2025-12-06T11:50:22.732Z
Learning: Talawa Admin reusable components: Define each shared component’s props in src/types/<Component>/interface.ts (avoid inline interfaces) and add a brief TSDoc header describing purpose and props.

Applied to files:

  • src/shared-components/CRUDModalTemplate/types.ts
📚 Learning: 2025-12-06T11:50:22.732Z
Learnt from: palisadoes
Repo: PalisadoesFoundation/talawa-admin PR: 0
File: :0-0
Timestamp: 2025-12-06T11:50:22.732Z
Learning: Talawa Admin reusable components: Define each shared component’s props in src/types/<Component>/interface.ts (no inline interfaces) and add a short TSDoc header describing purpose and props.

Applied to files:

  • src/shared-components/CRUDModalTemplate/types.ts
📚 Learning: 2025-12-06T11:50:22.732Z
Learnt from: palisadoes
Repo: PalisadoesFoundation/talawa-admin PR: 0
File: :0-0
Timestamp: 2025-12-06T11:50:22.732Z
Learning: Talawa Admin reusable components: Each shared component must expose its props via src/types/<Component>/interface.ts (no inline interfaces) and include a short TSDoc header describing purpose and props.

Applied to files:

  • src/shared-components/CRUDModalTemplate/types.ts
📚 Learning: 2025-12-14T21:12:48.274Z
Learnt from: Jashan32
Repo: PalisadoesFoundation/talawa-admin PR: 5009
File: src/types/Post/interface.ts:106-123
Timestamp: 2025-12-14T21:12:48.274Z
Learning: In talawa-admin, post-related interfaces (including component props like ICreatePostModalProps, InterfacePinnedPostsLayoutProps, InterfacePinnedPostCardProps, and data interfaces like InterfacePost, InterfacePostEdge) are centralized in src/types/Post/interface.ts to facilitate reuse across multiple post directories and components, following a functionality-based organization pattern similar to Event and User types.

Applied to files:

  • src/shared-components/CRUDModalTemplate/types.ts
📚 Learning: 2025-12-06T11:50:22.732Z
Learnt from: palisadoes
Repo: PalisadoesFoundation/talawa-admin PR: 0
File: :0-0
Timestamp: 2025-12-06T11:50:22.732Z
Learning: Talawa Admin reusable components: Each shared component’s props must be defined in src/types/<Component>/interface.ts (avoid inline prop interfaces).

Applied to files:

  • src/shared-components/CRUDModalTemplate/types.ts
📚 Learning: 2025-12-06T11:50:22.732Z
Learnt from: palisadoes
Repo: PalisadoesFoundation/talawa-admin PR: 0
File: :0-0
Timestamp: 2025-12-06T11:50:22.732Z
Learning: Talawa Admin reusable components: Each shared component’s props must be defined in src/types/<Component>/interface.ts (strict typing; avoid inline prop types or any).

Applied to files:

  • src/shared-components/CRUDModalTemplate/types.ts
🧬 Code graph analysis (1)
src/shared-components/CRUDModalTemplate/CRUDModalTemplate.tsx (1)
src/shared-components/CRUDModalTemplate/types.ts (1)
  • CrudModalBaseProps (1-22)
🔇 Additional comments (4)
src/shared-components/CRUDModalTemplate/index.ts (1)

1-2: LGTM with note: update after types relocation.

The barrel export pattern is correct. Note that line 2 will need updating once the types are moved to src/types/CRUDModalTemplate/interface.ts as flagged in the types.ts review.

src/shared-components/CRUDModalTemplate/CRUDModalTemplate.tsx (2)

36-43: Verify keyboard interaction compatibility.

The Modal has keyboard prop (line 42) which enables react-bootstrap's built-in Escape key handling for onHide. The custom Enter handler (lines 22-33) adds to this. Ensure these don't conflict and that the behavior matches PR objectives for "Esc to close, Enter to submit."

Test both keyboard shortcuts together to ensure:

  1. Escape triggers onClose (via react-bootstrap's keyboard prop)
  2. Enter triggers onPrimary (via custom handler) only when appropriate
  3. No double-firing or event bubbling issues

48-52: Consider showing children alongside error messages.

Currently, when an error is present, the children content is still displayed (line 51 checks !loading, not !loading && !error). This may be intentional for showing validation errors alongside form fields, but verify this matches the intended UX. If errors should hide the form content, add && !error to the condition.

src/shared-components/CRUDModalTemplate/CRUDModalTemplate.module.css (1)

1-57: CSS module is unused; remove it.

The component uses only react-bootstrap components (Modal, Button, Spinner, Alert) with their built-in styling and never imports CRUDModalTemplate.module.css. Delete this file or refactor to use custom elements with these styles instead.

⛔ Skipped due to learnings
Learnt from: rawadhossain
Repo: PalisadoesFoundation/talawa-admin PR: 4865
File: src/components/EventRegistrantsModal/Modal/EventRegistrantsModal.spec.tsx:261-280
Timestamp: 2025-11-27T11:40:30.747Z
Learning: In Talawa Admin tests (e.g., src/components/EventRegistrantsModal/Modal/EventRegistrantsModal.spec.tsx), react-bootstrap Modal header close buttons don't support custom test IDs, so using getAllByRole('button', { name: /close/i })[0] to select the modal's primary close action is acceptable and stable, as the Modal component consistently exposes "Close" as the accessible label.
Learnt from: meetulr
Repo: PalisadoesFoundation/talawa-admin PR: 2387
File: src/screens/ManageTag/ManageTag.tsx:58-97
Timestamp: 2024-10-30T13:38:29.300Z
Learning: In the `ManageTag` component (`src/screens/ManageTag/ManageTag.tsx`), extracting modal state management to a custom hook may require additional state modifications for the `toggle` and `show` functions.
Learnt from: IITI-tushar
Repo: PalisadoesFoundation/talawa-admin PR: 3400
File: src/screens/UserPortal/Settings/Settings.tsx:10-10
Timestamp: 2025-01-26T12:28:42.904Z
Learning: Global CSS files like `app.module.css` can be imported directly without using the `styles` import pattern, as they are meant to apply styles globally rather than as CSS modules.

Comment on lines +7 to +53
describe('CRUDModalTemplate', () => {
it('renders title and content when open', () => {
render(
<CRUDModalTemplate
open
title="Create Item"
onClose={() => {}}
>
<div>Modal Content</div>
</CRUDModalTemplate>
);

expect(screen.getByText(/create item/i)).toBeInTheDocument();
expect(screen.getByText(/modal content/i)).toBeInTheDocument();
});

it('calls onClose when cancel button is clicked', async () => {
const onClose = vi.fn();

render(
<CRUDModalTemplate
open
title="Create Item"
onClose={onClose}
/>
);

await userEvent.click(screen.getByText(/cancel/i));
expect(onClose).toHaveBeenCalled();
});

it('calls onPrimary when Enter key is pressed', async () => {
const onPrimary = vi.fn();

render(
<CRUDModalTemplate
open
title="Create Item"
onClose={() => {}}
onPrimary={onPrimary}
/>
);

await userEvent.keyboard('{Enter}');
expect(onPrimary).toHaveBeenCalled();
});
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add afterEach cleanup and expand test coverage.

Two issues:

  1. Missing required cleanup: Per project standards, each spec file must include afterEach(() => { vi.clearAllMocks(); }); to ensure test isolation.

  2. Insufficient test coverage: The PR objectives specify >90% coverage and accessibility requirements. Current tests are missing:

    • Loading state rendering (spinner display)
    • Error state rendering (alert display)
    • Primary button click behavior
    • Close button (X) click behavior
    • Primary button disabled state during loading
    • Custom primaryText and secondaryText rendering
    • Behavior when onPrimary is undefined (primary button should not render)

Based on learnings, test isolation requires explicit per-file cleanup.

🔎 Proposed test additions
+import { afterEach } from 'vitest';
+
 describe('CRUDModalTemplate', () => {
+  afterEach(() => {
+    vi.clearAllMocks();
+  });
+
   it('renders title and content when open', () => {
     // ... existing test
   });

+  it('does not render when closed', () => {
+    render(
+      <CRUDModalTemplate
+        open={false}
+        title="Create Item"
+        onClose={() => {}}
+      >
+        <div>Modal Content</div>
+      </CRUDModalTemplate>
+    );
+
+    expect(screen.queryByText(/create item/i)).not.toBeInTheDocument();
+  });
+
+  it('displays loading spinner when loading', () => {
+    render(
+      <CRUDModalTemplate
+        open
+        title="Create Item"
+        onClose={() => {}}
+        loading
+      >
+        <div>Modal Content</div>
+      </CRUDModalTemplate>
+    );
+
+    expect(screen.getByRole('status')).toBeInTheDocument();
+    expect(screen.queryByText(/modal content/i)).not.toBeInTheDocument();
+  });
+
+  it('displays error message when error prop is provided', () => {
+    render(
+      <CRUDModalTemplate
+        open
+        title="Create Item"
+        onClose={() => {}}
+        error="Something went wrong"
+      />
+    );
+
+    expect(screen.getByText(/something went wrong/i)).toBeInTheDocument();
+  });
+
+  it('calls onPrimary when primary button is clicked', async () => {
+    const onPrimary = vi.fn();
+
+    render(
+      <CRUDModalTemplate
+        open
+        title="Create Item"
+        onClose={() => {}}
+        onPrimary={onPrimary}
+      />
+    );
+
+    await userEvent.click(screen.getByRole('button', { name: /save/i }));
+    expect(onPrimary).toHaveBeenCalledTimes(1);
+  });
+
+  it('disables primary button during loading', () => {
+    render(
+      <CRUDModalTemplate
+        open
+        title="Create Item"
+        onClose={() => {}}
+        onPrimary={() => {}}
+        loading
+      />
+    );
+
+    expect(screen.getByRole('button', { name: /save/i })).toBeDisabled();
+  });
+
+  it('does not render primary button when onPrimary is undefined', () => {
+    render(
+      <CRUDModalTemplate
+        open
+        title="Create Item"
+        onClose={() => {}}
+      />
+    );
+
+    expect(screen.queryByRole('button', { name: /save/i })).not.toBeInTheDocument();
+    expect(screen.getByRole('button', { name: /cancel/i })).toBeInTheDocument();
+  });
+
+  it('renders custom button text', () => {
+    render(
+      <CRUDModalTemplate
+        open
+        title="Create Item"
+        onClose={() => {}}
+        onPrimary={() => {}}
+        primaryText="Create"
+        secondaryText="Close"
+      />
+    );
+
+    expect(screen.getByRole('button', { name: /create/i })).toBeInTheDocument();
+    expect(screen.getByRole('button', { name: /close/i })).toBeInTheDocument();
+  });
 });
🤖 Prompt for AI Agents
In src/shared-components/CRUDModalTemplate/CRUDModalTemplate.spec.tsx around
lines 7 to 53, add per-file cleanup and expand tests: include afterEach(() => {
vi.clearAllMocks(); }) to reset mocks between tests; add tests to assert loading
state shows a spinner and disables/hides primary button, error state shows an
alert, clicking primary button invokes onPrimary, clicking close (X) invokes
onClose, custom primaryText/secondaryText are rendered, and verify primary
button is not rendered when onPrimary is undefined; ensure each new test renders
the component with the appropriate props and asserts DOM and callback behavior
to meet >90% coverage and accessibility checks.

Comment on lines +1 to +8
import React, { useEffect } from 'react';
import { Modal, Button, Spinner, Alert } from 'react-bootstrap';
import type { CrudModalBaseProps } from './types';

export interface CRUDModalTemplateProps extends CrudModalBaseProps {
children?: React.ReactNode;
onPrimary?: () => void;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Add component-level TSDoc.

Per project standards, shared components must include a brief TSDoc header describing the component's purpose and props. Add a JSDoc comment above the interface definition.

🔎 Proposed addition
+/**
+ * Base props interface for CRUD modal templates.
+ * Provides foundational properties for modal visibility, content, actions, and state.
+ */
 export interface CRUDModalTemplateProps extends CrudModalBaseProps {
   children?: React.ReactNode;
   onPrimary?: () => void;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import React, { useEffect } from 'react';
import { Modal, Button, Spinner, Alert } from 'react-bootstrap';
import type { CrudModalBaseProps } from './types';
export interface CRUDModalTemplateProps extends CrudModalBaseProps {
children?: React.ReactNode;
onPrimary?: () => void;
}
import React, { useEffect } from 'react';
import { Modal, Button, Spinner, Alert } from 'react-bootstrap';
import type { CrudModalBaseProps } from './types';
/**
* Base props interface for CRUD modal templates.
* Provides foundational properties for modal visibility, content, actions, and state.
*/
export interface CRUDModalTemplateProps extends CrudModalBaseProps {
children?: React.ReactNode;
onPrimary?: () => void;
}
🤖 Prompt for AI Agents
In src/shared-components/CRUDModalTemplate/CRUDModalTemplate.tsx around lines 1
to 8, there is no component-level TSDoc for the CRUDModalTemplateProps
interface; add a JSDoc/TSDoc comment immediately above the interface that
briefly describes the component's purpose, summarizes the props (children and
onPrimary plus inherited CrudModalBaseProps), and any important behavior or
usage notes to comply with project standards.

Comment on lines +22 to +33
useEffect(() => {
if (!open || !onPrimary) return;

const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Enter') {
onPrimary();
}
};

document.addEventListener('keydown', handleKeyDown);
return () => document.removeEventListener('keydown', handleKeyDown);
}, [open, onPrimary]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Enter key handler triggers on ALL keydown events.

The global keydown listener will fire onPrimary even when users are typing in form inputs, textareas, or contenteditable elements within the modal. This breaks standard form UX where Enter should add a newline in textareas or submit only when focused on non-text inputs.

🔎 Proposed fix to check event target
   useEffect(() => {
     if (!open || !onPrimary) return;

     const handleKeyDown = (e: KeyboardEvent) => {
-      if (e.key === 'Enter') {
+      const target = e.target as HTMLElement;
+      const isFormElement = target.matches('input, textarea, [contenteditable="true"]');
+      
+      // Allow Enter in textareas and contenteditable; prevent in single-line inputs unless they're buttons/submits
+      if (e.key === 'Enter' && !isFormElement) {
         onPrimary();
       }
     };

     document.addEventListener('keydown', handleKeyDown);
     return () => document.removeEventListener('keydown', handleKeyDown);
   }, [open, onPrimary]);

Alternatively, consider using a more refined approach that allows Enter on single-line inputs but not textareas:

const handleKeyDown = (e: KeyboardEvent) => {
  const target = e.target as HTMLElement;
  const tagName = target.tagName.toLowerCase();
  
  // Block Enter in textareas and contenteditable
  if (tagName === 'textarea' || target.isContentEditable) {
    return;
  }
  
  // Allow Enter to trigger primary action
  if (e.key === 'Enter') {
    e.preventDefault();
    onPrimary();
  }
};
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useEffect(() => {
if (!open || !onPrimary) return;
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Enter') {
onPrimary();
}
};
document.addEventListener('keydown', handleKeyDown);
return () => document.removeEventListener('keydown', handleKeyDown);
}, [open, onPrimary]);
useEffect(() => {
if (!open || !onPrimary) return;
const handleKeyDown = (e: KeyboardEvent) => {
const target = e.target as HTMLElement;
const isFormElement = target.matches('input, textarea, [contenteditable="true"]');
// Allow Enter in textareas and contenteditable; prevent in single-line inputs unless they're buttons/submits
if (e.key === 'Enter' && !isFormElement) {
onPrimary();
}
};
document.addEventListener('keydown', handleKeyDown);
return () => document.removeEventListener('keydown', handleKeyDown);
}, [open, onPrimary]);
🤖 Prompt for AI Agents
In src/shared-components/CRUDModalTemplate/CRUDModalTemplate.tsx around lines 22
to 33, the global keydown listener currently triggers onPrimary for every Enter
press (including when typing in inputs, textareas, or contenteditable elements);
update the handler to inspect e.target and only call onPrimary for
non-text-entry elements: cast e.target to HTMLElement, check tagName (block if
'textarea') and check target.isContentEditable, and optionally block when the
target is an input of types that accept Enter (e.g., avoid triggering for inputs
where Enter should insert/submit inline); when triggering onPrimary call
e.preventDefault(); keep adding/removing the listener as-is.

Comment on lines +1 to +22
export interface CrudModalBaseProps {
/** Controls modal visibility */
open: boolean;

/** Modal title */
title: string;

/** Close handler (Cancel / X / Esc) */
onClose: () => void;

/** Primary button text */
primaryText?: string;

/** Secondary button text */
secondaryText?: string;

/** Loading state */
loading?: boolean;

/** Error message */
error?: string;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Move type definitions to standard location.

Per project conventions, shared component prop interfaces must be defined in src/types/CRUDModalTemplate/interface.ts rather than co-located with the component. This maintains consistency with other shared components in the codebase.

Based on learnings, the refactoring standard requires: "all props in src/types//interface.ts with brief TSDoc."

🔎 Proposed file relocation
  1. Create src/types/CRUDModalTemplate/interface.ts with this content:
export interface CrudModalBaseProps {
  /** Controls modal visibility */
  open: boolean;

  /** Modal title */
  title: string;

  /** Close handler (Cancel / X / Esc) */
  onClose: () => void;

  /** Primary button text */
  primaryText?: string;

  /** Secondary button text */
  secondaryText?: string;

  /** Loading state */
  loading?: boolean;

  /** Error message */
  error?: string;
}
  1. Update imports in CRUDModalTemplate.tsx:
-import type { CrudModalBaseProps } from './types';
+import type { CrudModalBaseProps } from '../../types/CRUDModalTemplate/interface';
  1. Update re-export in index.ts:
-export * from './types';
+export * from '../../types/CRUDModalTemplate/interface';
  1. Delete src/shared-components/CRUDModalTemplate/types.ts
🤖 Prompt for AI Agents
In src/shared-components/CRUDModalTemplate/types.ts around lines 1 to 22, move
the CrudModalBaseProps interface to the canonical location
src/types/CRUDModalTemplate/interface.ts by creating that file with the same
interface and brief TSDoc comments for each prop; update any imports in the
CRUDModalTemplate component file(s) to import CrudModalBaseProps from
"src/types/CRUDModalTemplate/interface" and update the package's index
re-exports to point to the new file if applicable; then remove the original
src/shared-components/CRUDModalTemplate/types.ts file to avoid duplication and
ensure the codebase follows the project convention.

@palisadoes
Copy link
Contributor

Are you still working on this?

@Esheshwari
Copy link
Author

Are you still working on this?

Yes @palisadoes I'm still working on this, my health isn't good so need some time but yeah I will finish it asap

@palisadoes
Copy link
Contributor

@Esheshwari

  1. I wish you all the best with your health. Take some rest and return when you feel better.
  2. I'm going to close the issue and unassign it so that others can give it a try in the meantime.
  3. Please return to the repo when you are recovered
  4. All the best

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants