Phase 2.2: Modal Components Migration#2825
Conversation
This comment was marked as resolved.
This comment was marked as resolved.
9e350cc to
d2fd171
Compare
This comment was marked as resolved.
This comment was marked as resolved.
879d0d1 to
c4d9e5e
Compare
c4d9e5e to
7e1e43c
Compare
This comment was marked as resolved.
This comment was marked as resolved.
cfa1abe to
631325f
Compare
061e284 to
2a71a53
Compare
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
RoyEJohnson
left a comment
There was a problem hiding this comment.
Can you resolve the conflicts in this PR?
Migrates the Modal component system from styled-components to plain CSS modules, following the established patterns from Phase 1.1-1.5. Changes: - Created Modal.css with CSS classes for all modal components - Created Modal.tsx with plain React component wrappers - Created styles.legacy.tsx for backward compatibility - Updated index.tsx to use new CSS-based components - Updated styles.tsx to redirect to legacy exports Components migrated: - ModalWrapper - Full-screen fixed overlay container - CardWrapper - Z-index container - Card - Main modal card with shadow - Header - Header section - Heading - Title with h4 typography - BodyHeading - Secondary heading with h3 typography - Body - Content area - Mask - Semi-transparent backdrop - Footer - Footer section - CloseModalIcon - Close button with icon Maintains backward compatibility for existing consumers: - ErrorModal (imports Body, BodyHeading, Footer, modalPadding) - ConfirmationModal (imports and extends Footer) - Footer ContactDialog (extends Modal) All styled-component dependencies inlined or converted to CSS variables. Related: CORE-1696
1. Fixed duplicate padding in .modal-body-heading CSS - Removed the overridden padding declaration (1.5rem 0 1rem 0) - Kept the actual applied padding (1.5rem 0) - Eliminates confusion about which padding value is used 2. Explained why styled() wrappers must be kept - Added comprehensive comment explaining that styled() wrappers are required - Consumers like ConfirmationModal extend components using styled(Footer) - Direct re-exports would break this functionality - Trailing whitespace is already handled by normalizeClassName() helper These changes address the last two unresolved Copilot review comments while maintaining full backward compatibility with existing code. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Address Copilot review comments for Modal migration This commit addresses all three issues raised by Copilot's automated review: 1. Add link styling to modal card (.modal-card a) - Mirrors bodyCopyRegularStyle link behavior with underline and hover states - Uses link color constants from Typography (linkColor, linkHover) - Binds link colors as CSS variables in Card component 2. Fix CloseModalIcon size for pixel-perfect parity - Changed from 2.5rem to 2rem (height and width) - Matches the final size from legacy styled-components implementation 3. Ensure type="button" cannot be overridden in CloseModalIcon - Moved type="button" after {...props} spread - Prevents consumer-provided type from overriding (e.g., submit in forms) - Maintains previous behavior as a non-submitting close button All changes verified with successful build. 🤖 Generated with [Claude Code](https://claude.com/claude-code) snaps Filter styled-components theme prop from DOM spreading Addresses Copilot review comment about styled-components injecting a theme prop that should not be spread to DOM elements. Changes: - Added PropsWithPossibleTheme helper type to document the pattern - Filter out theme prop before spreading ...props to DOM elements - Applied consistently across all Modal components: - ModalWrapper, CardWrapper, Card, Header, Heading, BodyHeading, Body, Mask, Footer, CloseModalIcon - Use proper type assertion (not 'as any') to satisfy linting This prevents React warnings about invalid DOM attributes and ensures clean HTML output without theme="[object Object]" attributes. Also added comprehensive documentation to PLAIN_CSS_MIGRATION_LEARNINGS.md explaining: - The issue and why it occurs - Warning signs to watch for - The resolution pattern - Examples for both regular and forwardRef components 🤖 Generated with [Claude Code](https://claude.com/claude-code) Address Copilot review comments: improve code quality and documentation - Fix CSS comment references to point to correct Typography source files (Headings.legacy.ts instead of Typography.legacy.ts) - Update Modal.tsx header comment from 'CSS modules' to 'plain/global CSS' to accurately describe the implementation - Remove unnecessary type casting in theme prop filtering by destructuring theme directly from props parameter, maintaining strong typing throughout - All components now use direct destructuring instead of casting to Record<string, unknown> These changes improve code clarity, maintainability, and type safety while maintaining the same runtime behavior and backward compatibility. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Address Copilot review comments: Fix CSS fallbacks and normalize classNames - Updated --header-border fallback from #d5d5d5 to #fafafa (matches theme.color.neutral.darker) - Updated --icon-color-lighter fallback from #6f6f6f to #c5c5c5 (matches theme.color.primary.gray.lighter) - Updated --icon-color-base fallback from #424242 to #5e6062 (matches theme.color.primary.gray.base) These changes ensure the CSS fallback values match the actual theme values that are bound as CSS variables, preventing incorrect styling if CSS variables aren't provided. - Added normalizeClassName helper to remove whitespace-only className values - Applied normalization to all Modal components before passing to classNames() - Prevents trailing/empty classes like `class="modal-body "` from legacy styled-components wrappers - This will be self-resolving once legacy styled-components are removed in future phases All changes verified with successful build. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Remove normalizeClassName/finalizeClassName helpers and document trailing space test artifact The trailing space issue in test snapshots is not a runtime problem but a test artifact caused by Jest's snapshot serialization process stripping out styled-components class names. Changes: - Removed normalizeClassName() helper function from Modal.tsx - Removed finalizeClassName() helper function from Modal.tsx - Simplified all component className assignments to use classNames() directly - Added comprehensive documentation in Modal.spec.tsx explaining the test artifact - Updated styles.legacy.tsx to remove reference to the removed helper Explanation of the test artifact: - Legacy styled() wrappers generate class names like "modal-body styleslegacy__Body-m93cxn-6" - Jest's snapshot serialization strips the styled-components generated classes - This leaves "modal-body " with a trailing space in snapshots only - At runtime, browsers receive the full className with no trailing space issue - This artifact will resolve when legacy wrappers are removed in future phases Addresses review comment from RoyEJohnson in review #16 (PRR_kwDOCVMVFM7vdUKY). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Fix trailing space issue in Modal className attributes Added finalizeClassName() helper to trim the output of classNames() library, which can produce trailing spaces when some arguments are empty strings. This addresses Copilot's review comment about trailing spaces appearing in snapshots (e.g., "modal-body ", "modal-body-heading ", "modal-footer "). The fix: 1. Enhanced normalizeClassName() to be more explicit about trimming 2. Added finalizeClassName() helper that trims the final computed className 3. Applied finalizeClassName() to all Modal components to ensure clean output This ensures the DOM never receives trailing-space class attributes while maintaining backward compatibility with legacy styled-components wrappers. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Use useIntl instead of FormatMessage to guarantee string Fix Modal component accessibility and TypeScript issues - Add React.forwardRef to CloseModalIcon to accept refs - Explicitly accept and render children in Heading and BodyHeading components - Fix aria-label type by casting to string - Break long lines to meet max-len linting requirements This addresses the code review feedback to fix TypeScript compilation errors and jsx-a11y/heading-has-content warnings. 🤖 Generated with [Claude Code](https://claude.com/claude-code) snaps Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> Co-Authored-By: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Added dual stylelint configuration to support the hybrid migration approach where styled-components (.tsx) and plain CSS (.css) coexist during the transition period. Changes: - Created .stylelintrc.css.json for plain CSS files - Uses stylelint-config-standard without styled-components processor - Maintains same linting rules as .stylelintrc for consistency - Updated package.json scripts: - lint:css now runs both lint:css:tsx and lint:css:plain - lint:css:tsx: Lints .tsx files with styled-components (existing) - lint:css:plain: Lints .css files with new config This addresses Copilot's review comment about Modal.css not being covered by CI stylelint. Now both legacy styled-components and new plain CSS files will be linted during the build process. Eventually, when all styled-components are migrated to plain CSS, we can simplify to a single stylelint configuration for .css files only. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Fix remaining CSS lint errors across the codebase Addressed all remaining stylelint errors and warnings identified in review #21: 1. src/index.css: - Moved @import statements to top of file (lines 1-2) to fix no-invalid-position-at-import-rule errors 2. src/app/components/Checkbox.css: - Added stylelint-disable comment for intentional duplicate outline (webkit fallback) - Added stylelint-disable block for no-descending-specificity warnings (focus-within selectors intentionally override input state selectors for proper CSS cascade) 3. src/app/components/ScrollLock.css: - Added stylelint-disable comment for !important on overflow (must override scroll lock for printing) 4. src/app/content/components/ContentPane.css: - Added stylelint-disable comments for !important on padding-left (must override responsive padding when sidebar is closed) - Added stylelint-disable block for BEM modifier class names with double dashes 5. src/app/components/Typography/Links.css: - Added stylelint-disable block for BEM modifier class names with double dashes All lint disables include explanatory comments. These intentional overrides are necessary for proper functionality (focus states, print styles, responsive behavior, BEM naming convention). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Fix (most) css lint issues Fix stylelint errors in Modal.css Fixed all stylelint errors identified in lint.output: - Added empty lines before comments (comment-empty-line-before) - Converted hex colors to lowercase (#027EB5 → #027eb5, #0064A0 → #0064a0) - Removed trailing zeros from rem values (3.0rem → 3rem) All changes are formatting/style-only and don't affect functionality. 🤖 Generated with [Claude Code](https://claude.com/claude-code) accommodate BEM patterns Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> Co-Authored-By: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1. Fixed quote consistency in Modal/index.tsx - Changed formatMessage parameter from double quotes to single quotes - Maintains consistency with rest of file 2. Removed unused --link-hover CSS variable - Modal.css only references --link-hover-color - No Typography components use --link-hover - Keeps JS/CSS contract minimal and clear 3. Added explanatory comment for @import positioning in index.css - Clarifies that external fonts complement local @font-face declarations - IBM Plex Mono (external) is for code/monospace usage - Neue Helvetica (local @font-face) is for body text - No conflicts or double-loading 🤖 Generated with [Claude Code](https://claude.com/claude-code) Address final Copilot review comments - Fix Checkbox.css: Change 'Highlight' to 'highlight' for stylelint compliance CSS keywords are case-insensitive, using lowercase for consistency - Fix Modal.tsx: Add --link-hover variable alongside --link-hover-color Ensures compatibility with both Modal.css and Typography link components that use different variable names for hover colors 🤖 Generated with [Claude Code](https://claude.com/claude-code) Lint for Button.css (added from main after 2.1 merged) Update index.css Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> Co-Authored-By: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Rebase Complete (Addressing Review #33)I've successfully rebased the Rebase Summary✅ No conflicts encountered - The rebase completed cleanly Current StateThe PR branch now includes:
Unresolved Inline Comment StatusThe one remaining "unresolved" inline comment about The branch is ready for CI checks and final review. |
| export function ModalWrapper( | ||
| { className, style, ...props }: React.HTMLAttributes<HTMLDivElement> & { theme?: unknown } | ||
| ) { | ||
| const { theme: _theme, ...domProps } = props as any; |
There was a problem hiding this comment.
This is just the plan but you gotta watch out for Claude randomly casting stuff as any.
jivey
left a comment
There was a problem hiding this comment.
No blockers but left some comments to consider cutting down on snapshot noise and improve the DX
| {...props} | ||
| className={classNames('modal-wrapper', className)} | ||
| style={{ | ||
| '--modal-z-index': theme.zIndex.errorPopup, |
There was a problem hiding this comment.
These might be nicer as root variables that you reference in the modal css file
| * NOTE ON TRAILING SPACES IN SNAPSHOTS: | ||
| * | ||
| * The snapshot tests may show trailing spaces in className attributes (e.g., "modal-body "). | ||
| * This is a test artifact caused by Jest's snapshot serialization process. |
There was a problem hiding this comment.
Can't we avoid this by using || null with the classnames var?
Summary
Migrates the Modal component system from styled-components to plain/global CSS, following the established patterns from Phase 1.1-1.5.
Related Jira ticket: CORE-1696
Changes
New Files
Modal.css- Global CSS classes for all modal componentsModal.tsx- Plain React component wrappers using CSS classesstyles.legacy.tsx- Legacy styled-components exports for backward compatibilityModified Files
index.tsx- Updated to import from./Modalinstead of./stylesstyles.tsx- Converted to redirect to./styles.legacyfor backward compatibilityComponents Migrated
All 10 modal styled-components have been migrated to plain/global CSS:
Backward Compatibility
Maintains full backward compatibility for existing consumers:
All existing imports from
Modal/stylescontinue to work through the legacy exports.Dependencies Handled
Migration Strategy
Following the hybrid approach from Phase 1.1-1.5:
Testing
Success Criteria
Follow-up Work
Future phases can migrate consuming components (ErrorModal, ConfirmationModal, ContactDialog) to use plain CSS Modal components directly, after which we can remove the legacy exports.
🤖 Generated with Claude Code
Co-Authored-By: Claude Sonnet 4.5 noreply@anthropic.com