feat(composedmodal): L3-10803 style updates to composed modal pin footer at bottom and go fullscreen on mobile#770
feat(composedmodal): L3-10803 style updates to composed modal pin footer at bottom and go fullscreen on mobile#770scottdickerson wants to merge 10 commits intomainfrom
Conversation
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…PhillipsAuctionHouse/seldon into L3-10803-composed-modal-styles
✅ Deploy Preview for phillips-seldon ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
There was a problem hiding this comment.
Pull Request Overview
This PR enhances the ComposedModal component to support a pinned footer at the bottom and full-screen behavior on mobile devices. The changes introduce dynamic footer height measurement using ResizeObserver and CSS custom properties to ensure proper layout across different viewports.
Key changes:
- Dynamic footer height tracking in
ComposedModalusing refs, state, andResizeObserverto calculate and apply footer positioning - Updated SCSS styles to position the footer absolutely at the bottom, adjust modal dimensions for mobile/desktop, and use CSS variables for responsive spacing
- Storybook configuration updates including custom viewport definitions, default viewport settings, and Chromatic visual regression viewports
Reviewed Changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
src/components/ComposedModal/ComposedModal.tsx |
Added footer ref, height state, and ResizeObserver logic to dynamically measure footer height; applied CSS variable to modal style |
src/components/ComposedModal/_composedModal.scss |
Refactored styles to use absolute positioning for footer, added mobile fullscreen layout, and applied CSS variables for dynamic height calculations |
src/components/ComposedModal/ComposedModal.stories.tsx |
Changed default isOpen state to true in stories and added empty tags array |
src/components/Modal/Modal.stories.tsx |
Changed default isOpen state to true in stories and added empty tags array |
.storybook/preview.tsx |
Added custom viewport configurations, set default viewport to desktop, configured Chromatic viewports, and removed unused React import |
src/patterns/ProgressWizard/ProgressWizard.stories.tsx |
Migrated viewport configuration from parameters to globals API |
src/patterns/ViewingDetails/_viewingDetails.scss |
Simplified margin styling by removing margin-top and updating margin-bottom |
| return () => { | ||
| resizeObserver.disconnect(); | ||
| }; | ||
| }, [secondaryButton, primaryButton, footerContent, footerHeight]); |
There was a problem hiding this comment.
The footerHeight state variable is included in the useEffect dependency array, but it's updated within the effect's callback. This creates an unnecessary re-run of the effect whenever footerHeight changes. Since the ResizeObserver already handles dynamic measurements, footerHeight should be removed from the dependencies to avoid potential infinite loops or redundant observer setup/teardown cycles.
| }, [secondaryButton, primaryButton, footerContent, footerHeight]); | |
| }, [secondaryButton, primaryButton, footerContent]); |
| }; | ||
|
|
||
| // Initial measurement | ||
| requestAnimationFrame(measureFooter); |
There was a problem hiding this comment.
The requestAnimationFrame call for initial measurement is not cancelled if the component unmounts before the frame executes. This could lead to a state update on an unmounted component. Consider storing the returned frame ID and cancelling it in the cleanup function using cancelAnimationFrame.
| min-width: 0; | ||
| display: flex; | ||
| flex-direction: column; | ||
| height: min(fit-content, calc(100svh - (2 * var(--modal-footer-height, 0px)))); |
There was a problem hiding this comment.
The calculation 100svh - (2 * var(--modal-footer-height, 0px)) subtracts twice the footer height, but the footer only appears once. This appears to be incorrect and may cause unexpected layout issues. The calculation should likely be 100svh - var(--modal-footer-height, 0px) unless there's a specific reason for doubling the footer height.
| height: min(fit-content, calc(100svh - (2 * var(--modal-footer-height, 0px)))); | |
| height: min(fit-content, calc(100svh - var(--modal-footer-height, 0px))); |
| height: min(fit-content, calc(100svh - (2 * var(--modal-footer-height, 0px)))); | ||
| max-height: 100svh; | ||
| min-width: 35rem; | ||
| overflow: auto; |
There was a problem hiding this comment.
Setting overflow: auto on the modal container combined with overflow-y: auto on the body (line 27) creates nested scrollable regions. This can lead to confusing scroll behavior for users. Consider managing overflow only at the body level to maintain a single, predictable scroll context.
| overflow: auto; | |
| /* Removed overflow: auto; to prevent nested scroll regions */ |
| &__footer { | ||
| background-color: $white-100; | ||
| bottom: 0; | ||
| padding-bottom: $spacing-md; | ||
| position: absolute; | ||
| width: 100%; | ||
| } |
There was a problem hiding this comment.
The absolutely positioned footer with width: 100% may overflow the modal container if the modal has padding or borders. Additionally, absolute positioning requires the parent to have position: relative set. The modal container (.#{$px}-composed-modal) should explicitly set position: relative to establish a positioning context for the footer.
| margin: 0 $spacing-lg $spacing-md; | ||
| max-height: var('--max-modal-body-height'); | ||
| max-height: min(fit-content, calc(100% - var(--modal-footer-height, 0px))); | ||
| overflow-y: auto; |
There was a problem hiding this comment.
[nitpick] Adding padding-bottom: var(--modal-footer-height) to create space for the absolutely positioned footer is correct, but this creates a dependency between the body padding and footer positioning. If the footer height changes before the CSS variable updates, content may be obscured. Consider documenting this relationship in a comment for future maintainers.
| overflow-y: auto; | |
| overflow-y: auto; | |
| // NOTE: This padding-bottom must match the height of the absolutely positioned footer. | |
| // If the footer height changes, ensure --modal-footer-height is updated to prevent content from being obscured. |
| style={ | ||
| { | ||
| '--modal-footer-height': footerHeight, | ||
| } as React.CSSProperties | ||
| } |
There was a problem hiding this comment.
The new dynamic footer height functionality using CSS custom properties lacks test coverage. The existing test file doesn't verify that the --modal-footer-height CSS variable is correctly set or that the ResizeObserver properly updates it. Add tests to ensure this critical responsive behavior works as expected.
|
🚀 Storybook preview is ready. • Preview: https://68b9f094608b90f3cfec5a06-tbeasoynte.chromatic.com/ |
✅ Deploy Preview for phillips-seldon ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
looks similar to another open pr: L3-10837-add-min-widths-to-modals https://phillipsauctions.atlassian.net/browse/L3-10837 The figma linked in that ticket (which is from design) doesn't have the modal going full screen on mobile . It also has defined widths at each breakpoint |
|
|
||
| export const Playground = (props: ComposedModalProps) => { | ||
| const [isOpen, setIsOpen] = useState(false); | ||
| const [isOpen, setIsOpen] = useState(true); |
There was a problem hiding this comment.
This makes it easier to see per story, but it opens them all at the same time in the overview, not sure if that would cause issues in chromatic
| border-radius: $radius-md; | ||
| max-width: 95vw; | ||
| min-width: 0; | ||
| display: flex; |
There was a problem hiding this comment.
I think we might want to use the changes in Aaron's pr for the modal
Jira ticket
L3-10803
Screenshots
On Chromatic you'll see that now we have both

desktopandmobilebreakpoints for all the stories:Figma link
Figma Link
Summary
This pull request introduces improvements to modal components and Storybook configuration, focusing on better responsive behavior and enhanced modal footer handling. The main changes include dynamic footer height measurement for
ComposedModal, updated Storybook viewports for more accurate previews on mobile components, and improved styling for modals to support mobile and desktop layouts.Modal Component Enhancements
ComposedModal, usinguseRef,useState, andResizeObserverto set a CSS variable for responsive layout adjustments. The footer is now rendered as a dedicated element with a reference. (src/components/ComposedModal/ComposedModal.tsx) [1] [2] [3] [4]src/components/ComposedModal/_composedModal.scss) [1] [2] [3] [4]Storybook Configuration and Stories
.storybook/preview.tsx)tagsarrays for consistency. (src/components/ComposedModal/ComposedModal.stories.tsx,src/components/Modal/Modal.stories.tsx) [1] [2] [3] [4] [5]src/patterns/ProgressWizard/ProgressWizard.stories.tsx)Minor Style Adjustments
src/patterns/ViewingDetails/_viewingDetails.scss)Acceptance Test (how to verify the PR)
Regression Test
Evidence of testing
Things to look for during review
feat(scope): ...if aminorrelease should be triggered.phillipsclass prefix are using the prefix variabledata-testidattribute.New Components
index.tsfilecomponentStyles.scssfile.