diff --git a/packages/react-components/react-drawer/library/src/components/OverlayDrawer/OverlayDrawer.cy.tsx b/packages/react-components/react-drawer/library/src/components/OverlayDrawer/OverlayDrawer.cy.tsx index b9eab9343bce39..c1f22a63a601fb 100644 --- a/packages/react-components/react-drawer/library/src/components/OverlayDrawer/OverlayDrawer.cy.tsx +++ b/packages/react-components/react-drawer/library/src/components/OverlayDrawer/OverlayDrawer.cy.tsx @@ -4,6 +4,8 @@ import { FluentProvider } from '@fluentui/react-provider'; import { webLightTheme } from '@fluentui/react-theme'; import { testDrawerBaseScenarios } from '../../e2e/DrawerShared'; +import { MotionBehaviourProvider } from '@fluentui/react-motion'; +import { Dialog, DialogActions, DialogBody, DialogSurface, DialogTrigger } from '@fluentui/react-dialog'; import { OverlayDrawer } from './OverlayDrawer'; import type { OverlayDrawerProps } from './OverlayDrawer.types'; import { overlayDrawerClassNames } from './useOverlayDrawerStyles.styles'; @@ -83,6 +85,65 @@ describe('OverlayDrawer', () => { }); }); + // https://github.com/microsoft/fluentui/issues/35389 + describe('mountNode with modalType="non-modal"', () => { + const ExampleDrawerInContainer = () => { + const [container, setContainer] = React.useState(null); + + return ( + + + + + + + + + + + + + + + + +
+ {container && ( + + + + )} +
+
+ ); + }; + + it('should keep focus on elements outside the container', () => { + mountFluent(); + + cy.get('#drawer-input').realClick(); + cy.get('#drawer-input').should('be.focused'); + + cy.get('#outside-input').realClick(); + // Tabster pulls focus back into a trapped modalizer after a delay, so wait past it + cy.wait(300); + cy.get('#outside-input').should('be.focused'); + }); + + it('should let focus return to the Drawer after a modal Dialog outside the container closes', () => { + mountFluent(); + + cy.get('#open-dialog').realClick(); + cy.get('#close-dialog').realClick(); + cy.get('#open-dialog').should('be.focused'); + + cy.get('#drawer-input').realClick(); + cy.wait(300); + cy.get('#drawer-input').should('be.focused'); + cy.get('.fui-OverlayDrawer').should('not.have.attr', 'aria-hidden'); + }); + }); + describe('body scroll', () => { it('should hide scroll when opened', () => { const ExampleDrawer = (props: OverlayDrawerProps) => { diff --git a/packages/react-components/react-drawer/stories/src/Drawer/OverlayInsideContainer.stories.tsx b/packages/react-components/react-drawer/stories/src/Drawer/OverlayInsideContainer.stories.tsx index e9e6d0a8160671..76edcda98bdb4c 100644 --- a/packages/react-components/react-drawer/stories/src/Drawer/OverlayInsideContainer.stories.tsx +++ b/packages/react-components/react-drawer/stories/src/Drawer/OverlayInsideContainer.stories.tsx @@ -48,6 +48,8 @@ export const OverlayInsideContainer = (): JSXElement => { as="aside" {...restoreFocusSourceAttributes} mountNode={ref.current} + // The rest of the page stays visible and interactive, so the Drawer is not modal. + modalType="non-modal" open={isOpen} onOpenChange={(_, { open }) => setIsOpen(open)} > @@ -87,6 +89,10 @@ OverlayInsideContainer.parameters = { story: [ 'The overlay Drawer can be rendered inside a specific container by setting the `mountNode` prop to the desired container element.', 'This approach is useful when you need the Drawer to appear within a particular section of the DOM, rather than being attached to the root element.', + '', + 'A Drawer inside a container only covers that container, so the rest of the page stays visible and interactive.', + 'In that case, set `modalType="non-modal"`: a modal Drawer traps focus and hides the rest of the page from assistive technology,', + 'which conflicts with users clicking or tabbing to the controls outside the container (for example, a header that opens other dialogs).', ].join('\n'), }, },