Skip to content

fix(Modal, AddContactModal): Fix Storybook docs mode rendering issues#40

Merged
wreiske merged 2 commits into
mainfrom
feature/add-contact-modal-updates
Jan 30, 2026
Merged

fix(Modal, AddContactModal): Fix Storybook docs mode rendering issues#40
wreiske merged 2 commits into
mainfrom
feature/add-contact-modal-updates

Conversation

@garrity-miepub

Copy link
Copy Markdown
Collaborator

Summary: Fixes multiple issues with Modal components in Storybook docs mode where multiple stories are rendered inline on the same page.

Modal.tsx:

  • Add isStorybookDocsMode() helper to detect Storybook docs view
  • Skip scroll lock (body overflow: hidden) in docs mode to allow page scroll
  • Add multi-modal support with openModalCount reference counter
  • Preserve and restore original body overflow correctly

useFocusTrap.ts:

  • Add isStorybookDocsMode() helper to detect Storybook docs view
  • Skip focus trap in docs mode to prevent auto-scroll to middle of page
  • Focus trap still works normally in canvas mode and production

AddContactModal.stories.tsx:

  • Rewrite stories to use InteractiveWrapper that syncs with Storybook args
  • Add proper argTypes for all props including contact, onOpenChange, onSave
  • Add default args at meta level for consistent behavior
  • Change layout to fullscreen with iframeHeight: 700 for better visibility
  • All stories now render with working controls in docs page
  • Remove redundant wrapper components

Issues Fixed:

  1. No controls displayed on AddContactModal docs page
  2. Page scroll broken in docs mode (body overflow locked)
  3. Page auto-scrolled to middle on load (focus trap focusing elements)
  4. Modal examples cut off in docs page (insufficient container height)
add-contact-modal.mov

Summary: Fixes multiple issues with Modal components in Storybook docs mode where multiple stories are rendered inline on the same page.

Modal.tsx:
- Add isStorybookDocsMode() helper to detect Storybook docs view
- Skip scroll lock (body overflow: hidden) in docs mode to allow page scroll
- Add multi-modal support with openModalCount reference counter
- Preserve and restore original body overflow correctly

useFocusTrap.ts:
- Add isStorybookDocsMode() helper to detect Storybook docs view
- Skip focus trap in docs mode to prevent auto-scroll to middle of page
- Focus trap still works normally in canvas mode and production

AddContactModal.stories.tsx:
- Rewrite stories to use InteractiveWrapper that syncs with Storybook args
- Add proper argTypes for all props including contact, onOpenChange, onSave
- Add default args at meta level for consistent behavior
- Change layout to fullscreen with iframeHeight: 700 for better visibility
- All stories now render with working controls in docs page
- Remove redundant wrapper components

Issues Fixed:
1. No controls displayed on AddContactModal docs page
2. Page scroll broken in docs mode (body overflow locked)
3. Page auto-scrolled to middle on load (focus trap focusing elements)
4. Modal examples cut off in docs page (insufficient container height)
Copilot AI review requested due to automatic review settings January 30, 2026 21:59
@garrity-miepub garrity-miepub marked this pull request as ready for review January 30, 2026 21:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes Storybook docs mode rendering issues for Modal components where multiple stories are rendered inline on the same page. The changes prevent body scroll locking and focus trapping in docs mode, which were causing page scroll issues and auto-scrolling behavior.

Changes:

  • Added isStorybookDocsMode() detection to skip scroll lock and focus trap in Storybook docs view
  • Implemented multi-modal support with reference counter for proper body overflow management
  • Refactored AddContactModal stories to use InteractiveWrapper with proper args synchronization and improved layout

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
src/hooks/useFocusTrap.ts Added Storybook docs mode detection to skip focus trap and prevent auto-scroll in docs view
src/components/Modal/Modal.tsx Added docs mode detection, multi-modal counting, and body overflow management to prevent scroll issues
src/components/AddContactModal/AddContactModal.stories.tsx Refactored stories to use InteractiveWrapper with proper args, removed redundant wrappers, changed to fullscreen layout

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/hooks/useFocusTrap.ts Outdated
Comment on lines +4 to +7
function isStorybookDocsMode(): boolean {
if (typeof window === 'undefined') return false;
return window.location.search.includes('viewMode=docs');
}

Copilot AI Jan 30, 2026

Copy link

Choose a reason for hiding this comment

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

The isStorybookDocsMode() function is duplicated in both useFocusTrap.ts and Modal.tsx. This creates a maintainability issue - any changes to the detection logic need to be applied in multiple places. Consider extracting this function to a shared utility file (e.g., src/utils/storybook.ts or src/utils/environment.ts) and importing it where needed.

Copilot uses AI. Check for mistakes.
Comment thread src/components/Modal/Modal.tsx Outdated
Comment on lines +7 to +9
// Track number of open modals to manage body scroll lock
let openModalCount = 0;
let originalBodyOverflow: string | null = null;

Copilot AI Jan 30, 2026

Copy link

Choose a reason for hiding this comment

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

The module-level variables openModalCount and originalBodyOverflow can cause issues in testing environments, hot module replacement (HMR), and server-side rendering scenarios. These values persist across component unmounts and remounts, which could lead to incorrect state if components are rapidly mounted/unmounted or if HMR reloads occur. Consider using a ref-based approach or React context to manage this state more safely, or at minimum add a reset mechanism for testing environments.

Copilot uses AI. Check for mistakes.
Comment on lines 142 to 159
React.useEffect(() => {
if (open) {
const originalOverflow = document.body.style.overflow;
document.body.style.overflow = 'hidden';
if (open && !isStorybookDocsMode()) {
openModalCount++;
// Only capture and set overflow when first modal opens
if (openModalCount === 1) {
originalBodyOverflow = document.body.style.overflow;
document.body.style.overflow = 'hidden';
}
return () => {
document.body.style.overflow = originalOverflow;
openModalCount--;
// Only restore overflow when last modal closes
if (openModalCount === 0 && originalBodyOverflow !== null) {
document.body.style.overflow = originalBodyOverflow;
originalBodyOverflow = null;
}
};
}
}, [open]);

Copilot AI Jan 30, 2026

Copy link

Choose a reason for hiding this comment

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

When isStorybookDocsMode() returns true, the useEffect doesn't return a cleanup function. This means if the modal is open and then the open prop changes to false while still in Storybook docs mode, the effect cleanup won't decrement the openModalCount. This could lead to a memory leak or incorrect counter state. The cleanup function should always be returned, even when in Storybook docs mode, to ensure proper cleanup of the effect subscription.

Copilot uses AI. Check for mistakes.
1. Extract isStorybookDocsMode() to shared utility (src/utils/environment.ts)
   - Removes duplication between Modal.tsx and useFocusTrap.ts
   - Single source of truth for Storybook docs mode detection

2. Replace module-level variables with scrollLockState object
   - Safer for HMR, testing, and SSR scenarios
   - Includes reset() method for testing environments
   - Exports __resetScrollLockState for test cleanup

3. Fix useEffect cleanup function
   - Always return cleanup function (or undefined) explicitly
   - Prevents potential memory leaks when open prop changes in docs mode
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jan 30, 2026

Copy link
Copy Markdown

Deploying ui with  Cloudflare Pages  Cloudflare Pages

Latest commit: a480e38
Status: ✅  Deploy successful!
Preview URL: https://94fe2db5.ui-6d0.pages.dev
Branch Preview URL: https://feature-add-contact-modal-up.ui-6d0.pages.dev

View logs

@wreiske wreiske merged commit 43dda39 into main Jan 30, 2026
7 checks passed
@wreiske wreiske deleted the feature/add-contact-modal-updates branch January 30, 2026 22:12
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.

3 participants