diff --git a/apps/vr-tests-react-components/src/stories/Dialog/Dialog.stories.tsx b/apps/vr-tests-react-components/src/stories/Dialog/Dialog.stories.tsx index 899db533bdcaf..bf269121e4c8d 100644 --- a/apps/vr-tests-react-components/src/stories/Dialog/Dialog.stories.tsx +++ b/apps/vr-tests-react-components/src/stories/Dialog/Dialog.stories.tsx @@ -8,9 +8,10 @@ import { DialogTitle, DialogTrigger, } from '@fluentui/react-dialog'; +import { OverlayDrawer, DrawerBody, DrawerHeader, DrawerHeaderTitle } from '@fluentui/react-drawer'; import { Button } from '@fluentui/react-button'; import { Combobox, Option } from '@fluentui/react-combobox'; -import { Rocket24Regular } from '@fluentui/react-icons'; +import { Rocket24Regular, Dismiss24Regular } from '@fluentui/react-icons'; import type { Meta } from '@storybook/react-webpack5'; import { getStoryVariant, DARK_MODE, HIGH_CONTRAST, RTL } from '../../utilities'; @@ -528,3 +529,149 @@ export const IntegrationComboboxInline = () => { ); }; + +export const DialogInsideDrawerDefault = () => ( + + + } />}> + Drawer with Dialog + + + + + + + + + + Dialog inside Drawer + This dialog has the default transparent backdrop when nested inside a Drawer. + + + + + + + + + + + +); + +DialogInsideDrawerDefault.storyName = 'dialog inside drawer default'; + +export const DialogInsideDrawerDefaultDarkMode = getStoryVariant(DialogInsideDrawerDefault, DARK_MODE); +export const DialogInsideDrawerDefaultHighContrast = getStoryVariant(DialogInsideDrawerDefault, HIGH_CONTRAST); +export const DialogInsideDrawerDefaultRTL = getStoryVariant(DialogInsideDrawerDefault, RTL); + +export const DialogInsideDrawerDimmed = () => ( + + + } />}> + Drawer with Dialog + + + + + + + + + + Dialog inside Drawer + This dialog has a dimmed backdrop even though it is nested inside a Drawer. + + + + + + + + + + + +); + +DialogInsideDrawerDimmed.storyName = 'dialog inside drawer dimmed'; + +export const DialogInsideDrawerDimmedDarkMode = getStoryVariant(DialogInsideDrawerDimmed, DARK_MODE); +export const DialogInsideDrawerDimmedHighContrast = getStoryVariant(DialogInsideDrawerDimmed, HIGH_CONTRAST); +export const DialogInsideDrawerDimmedRTL = getStoryVariant(DialogInsideDrawerDimmed, RTL); + +export const DialogInsideDrawerTransparent = () => ( + + + } />}> + Drawer with Dialog + + + + + + + + + + Dialog inside Drawer + This dialog explicitly sets transparent backdrop appearance. + + + + + + + + + + + +); + +DialogInsideDrawerTransparent.storyName = 'dialog inside drawer transparent'; + +export const DialogInsideDrawerTransparentDarkMode = getStoryVariant(DialogInsideDrawerTransparent, DARK_MODE); +export const DialogInsideDrawerTransparentHighContrast = getStoryVariant(DialogInsideDrawerTransparent, HIGH_CONTRAST); +export const DialogInsideDrawerTransparentRTL = getStoryVariant(DialogInsideDrawerTransparent, RTL); + +export const NestedDialogDimmed = () => ( + + + + + + + Outer Dialog + This is the outer dialog. + + + + + + + + Inner Dialog with dimmed backdrop + + This inner dialog explicitly sets dimmed backdrop to override the default transparent behavior for + nested dialogs. + + + + + + + + + + + + + +); + +NestedDialogDimmed.storyName = 'nested dialog dimmed'; + +export const NestedDialogDimmedDarkMode = getStoryVariant(NestedDialogDimmed, DARK_MODE); +export const NestedDialogDimmedHighContrast = getStoryVariant(NestedDialogDimmed, HIGH_CONTRAST); +export const NestedDialogDimmedRTL = getStoryVariant(NestedDialogDimmed, RTL); diff --git a/change/@fluentui-react-components-f8ff49c3-56bc-4ab7-92bb-ca31d8f2b6a9.json b/change/@fluentui-react-components-f8ff49c3-56bc-4ab7-92bb-ca31d8f2b6a9.json new file mode 100644 index 0000000000000..16f355c406392 --- /dev/null +++ b/change/@fluentui-react-components-f8ff49c3-56bc-4ab7-92bb-ca31d8f2b6a9.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "feat: add DialogBackdropProvider to react-dialog", + "packageName": "@fluentui/react-components", + "email": "vgenaev@gmail.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-dialog-e96349b1-d4e8-4754-ac99-d97eb5a1e817.json b/change/@fluentui-react-dialog-e96349b1-d4e8-4754-ac99-d97eb5a1e817.json new file mode 100644 index 0000000000000..2d12f4348a734 --- /dev/null +++ b/change/@fluentui-react-dialog-e96349b1-d4e8-4754-ac99-d97eb5a1e817.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "feat: add appearance to backdrop slot", + "packageName": "@fluentui/react-dialog", + "email": "vgenaev@gmail.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-drawer-e0316a31-a891-42e7-8854-26521bfab9e1.json b/change/@fluentui-react-drawer-e0316a31-a891-42e7-8854-26521bfab9e1.json new file mode 100644 index 0000000000000..4bfe0bc9f2a4b --- /dev/null +++ b/change/@fluentui-react-drawer-e0316a31-a891-42e7-8854-26521bfab9e1.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: use DialogBackdropProvider to fix no dimmed nested dialogs inside OverlayDrawer'", + "packageName": "@fluentui/react-drawer", + "email": "vgenaev@gmail.com", + "dependentChangeType": "patch" +} diff --git a/packages/react-components/react-components/etc/react-components.api.md b/packages/react-components/react-components/etc/react-components.api.md index 226222d38a22f..971f50ed4c6c8 100644 --- a/packages/react-components/react-components/etc/react-components.api.md +++ b/packages/react-components/react-components/etc/react-components.api.md @@ -342,6 +342,7 @@ import { DialogActionsPosition } from '@fluentui/react-dialog'; import { DialogActionsProps } from '@fluentui/react-dialog'; import { DialogActionsSlots } from '@fluentui/react-dialog'; import { DialogActionsState } from '@fluentui/react-dialog'; +import { DialogBackdropContextValue } from '@fluentui/react-dialog'; import { DialogBody } from '@fluentui/react-dialog'; import { dialogBodyClassNames } from '@fluentui/react-dialog'; import { DialogBodyProps } from '@fluentui/react-dialog'; @@ -1642,6 +1643,7 @@ import { useDataGridStyles_unstable } from '@fluentui/react-table'; import { useDialog_unstable } from '@fluentui/react-dialog'; import { useDialogActions_unstable } from '@fluentui/react-dialog'; import { useDialogActionsStyles_unstable } from '@fluentui/react-dialog'; +import { useDialogBackdropContext_unstable } from '@fluentui/react-dialog'; import { useDialogBody_unstable } from '@fluentui/react-dialog'; import { useDialogBodyStyles_unstable } from '@fluentui/react-dialog'; import { useDialogContent_unstable } from '@fluentui/react-dialog'; @@ -2701,6 +2703,8 @@ export { DialogActionsSlots } export { DialogActionsState } +export { DialogBackdropContextValue } + export { DialogBody } export { dialogBodyClassNames } @@ -5301,6 +5305,8 @@ export { useDialogActions_unstable } export { useDialogActionsStyles_unstable } +export { useDialogBackdropContext_unstable } + export { useDialogBody_unstable } export { useDialogBodyStyles_unstable } diff --git a/packages/react-components/react-components/src/index.ts b/packages/react-components/react-components/src/index.ts index 9b217c266ef34..f6a55180d9688 100644 --- a/packages/react-components/react-components/src/index.ts +++ b/packages/react-components/react-components/src/index.ts @@ -879,6 +879,7 @@ export { useDialogContent_unstable, renderDialogContent_unstable, useDialogContext_unstable, + useDialogBackdropContext_unstable, useDialogSurfaceContext_unstable, useDialogSurfaceContextValues_unstable, DialogProvider, @@ -914,6 +915,7 @@ export type { DialogContextValue, DialogSurfaceContextValue, DialogSurfaceContextValues, + DialogBackdropContextValue, } from '@fluentui/react-dialog'; export { diff --git a/packages/react-components/react-dialog/library/etc/react-dialog.api.md b/packages/react-components/react-dialog/library/etc/react-dialog.api.md index 529c5a1b98cc1..9f06b1188b3f1 100644 --- a/packages/react-components/react-dialog/library/etc/react-dialog.api.md +++ b/packages/react-components/react-dialog/library/etc/react-dialog.api.md @@ -9,6 +9,7 @@ import { ARIAButtonType } from '@fluentui/react-aria'; import type { ComponentProps } from '@fluentui/react-utilities'; import type { ComponentState } from '@fluentui/react-utilities'; import { ContextSelector } from '@fluentui/react-context-selector'; +import type { ExtractSlotProps } from '@fluentui/react-utilities'; import type { ForwardRefComponent } from '@fluentui/react-utilities'; import type { JSXElement } from '@fluentui/react-utilities'; import type { PortalProps } from '@fluentui/react-portal'; @@ -54,6 +55,17 @@ export type DialogActionsSlots = { // @public export type DialogActionsState = ComponentState & Pick, 'position' | 'fluid'>; +// @public (undocumented) +export type DialogBackdropContextValue = boolean; + +// @public (undocumented) +export const DialogBackdropProvider: React_2.Provider; + +// @public +export type DialogBackdropSlotProps = ExtractSlotProps & { + appearance?: 'dimmed' | 'transparent'; +}>; + // @public export const DialogBody: ForwardRefComponent; @@ -180,7 +192,7 @@ export const DialogSurfaceProvider: React_2.Provider; // @public (undocumented) export type DialogSurfaceSlots = { - backdrop?: Slot<'div'>; + backdrop?: Slot; root: Slot<'div'>; backdropMotion: Slot; }; @@ -189,7 +201,9 @@ export type DialogSurfaceSlots = { export type DialogSurfaceState = ComponentState & Pick & Pick & { open?: boolean; unmountOnClose?: boolean; + treatBackdropAsNested: boolean; transitionStatus?: 'entering' | 'entered' | 'idle' | 'exiting' | 'exited' | 'unmounted'; + backdropAppearance?: DialogBackdropSlotProps['appearance']; }; // @public @@ -262,6 +276,9 @@ export const useDialogActions_unstable: (props: DialogActionsProps, ref: React_2 // @public export const useDialogActionsStyles_unstable: (state: DialogActionsState) => DialogActionsState; +// @public (undocumented) +export const useDialogBackdropContext_unstable: () => DialogBackdropContextValue | undefined; + // @public export const useDialogBody_unstable: (props: DialogBodyProps, ref: React_2.Ref) => DialogBodyState; diff --git a/packages/react-components/react-dialog/library/src/DialogSurface.ts b/packages/react-components/react-dialog/library/src/DialogSurface.ts index d571e0ce21a18..f0cc976563a52 100644 --- a/packages/react-components/react-dialog/library/src/DialogSurface.ts +++ b/packages/react-components/react-dialog/library/src/DialogSurface.ts @@ -1,4 +1,5 @@ export type { + DialogBackdropSlotProps, DialogSurfaceContextValues, DialogSurfaceElement, DialogSurfaceProps, diff --git a/packages/react-components/react-dialog/library/src/components/Dialog/useDialog.ts b/packages/react-components/react-dialog/library/src/components/Dialog/useDialog.ts index 85923fb806347..1438f4080beb1 100644 --- a/packages/react-components/react-dialog/library/src/components/Dialog/useDialog.ts +++ b/packages/react-components/react-dialog/library/src/components/Dialog/useDialog.ts @@ -49,6 +49,7 @@ export const useDialog_unstable = (props: DialogProps): DialogState => { trapFocus: modalType !== 'non-modal', legacyTrapFocus: !inertTrapFocus, }); + const isNestedDialog = useHasParentContext(DialogContext); return { diff --git a/packages/react-components/react-dialog/library/src/components/DialogSurface/DialogSurface.types.ts b/packages/react-components/react-dialog/library/src/components/DialogSurface/DialogSurface.types.ts index 7667018f654a7..5dd62ad898d46 100644 --- a/packages/react-components/react-dialog/library/src/components/DialogSurface/DialogSurface.types.ts +++ b/packages/react-components/react-dialog/library/src/components/DialogSurface/DialogSurface.types.ts @@ -1,9 +1,23 @@ import type { PresenceMotionSlotProps } from '@fluentui/react-motion'; import type { PortalProps } from '@fluentui/react-portal'; -import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities'; +import type { ComponentProps, ComponentState, Slot, ExtractSlotProps } from '@fluentui/react-utilities'; import { DialogContextValue, DialogSurfaceContextValue } from '../../contexts'; +/** + * Custom slot props for the backdrop slot. + */ +export type DialogBackdropSlotProps = ExtractSlotProps< + Slot<'div'> & { + /** + * Controls the backdrop appearance. + * - 'dimmed': Shows a dimmed backdrop (default for standalone dialogs) + * - 'transparent': Shows a transparent backdrop (default for nested dialogs) + */ + appearance?: 'dimmed' | 'transparent'; + } +>; + export type DialogSurfaceSlots = { /** * Dimmed background of dialog. @@ -11,8 +25,16 @@ export type DialogSurfaceSlots = { * This slot expects a `
` element which will replace the default backdrop. * The backdrop should have `aria-hidden="true"`. * + * Accepts an `appearance` prop to control backdrop visibility: + * - `'dimmed'`: Always shows a dimmed backdrop, regardless of nesting. + * - `'transparent'`: Always shows a transparent backdrop. + * + * @example + * ```tsx + * + * ``` */ - backdrop?: Slot<'div'>; + backdrop?: Slot; root: Slot<'div'>; /** * For more information refer to the [Motion docs page](https://react.fluentui.dev/?path=/docs/motion-motion-slot--docs). @@ -44,7 +66,12 @@ export type DialogSurfaceState = ComponentState & Pick & { open?: boolean; unmountOnClose?: boolean; - + /** + * Whether the backdrop should be treated as nested (transparent). + * When inside an OverlayDrawer, this is `false` even though `isNestedDialog` may be `true`, + * preventing the false-positive transparent backdrop. + */ + treatBackdropAsNested: boolean; /** * Transition status for animation. * In test environment, this is always `undefined`. @@ -52,4 +79,5 @@ export type DialogSurfaceState = ComponentState & * @deprecated Will be always `undefined`. */ transitionStatus?: 'entering' | 'entered' | 'idle' | 'exiting' | 'exited' | 'unmounted'; + backdropAppearance?: DialogBackdropSlotProps['appearance']; }; diff --git a/packages/react-components/react-dialog/library/src/components/DialogSurface/index.ts b/packages/react-components/react-dialog/library/src/components/DialogSurface/index.ts index 6de98e07cf806..78aec16f608ca 100644 --- a/packages/react-components/react-dialog/library/src/components/DialogSurface/index.ts +++ b/packages/react-components/react-dialog/library/src/components/DialogSurface/index.ts @@ -1,5 +1,6 @@ export { DialogSurface } from './DialogSurface'; export type { + DialogBackdropSlotProps, DialogSurfaceContextValues, DialogSurfaceElement, DialogSurfaceProps, diff --git a/packages/react-components/react-dialog/library/src/components/DialogSurface/useDialogSurface.ts b/packages/react-components/react-dialog/library/src/components/DialogSurface/useDialogSurface.ts index df05129c64297..c8b6892de25ce 100644 --- a/packages/react-components/react-dialog/library/src/components/DialogSurface/useDialogSurface.ts +++ b/packages/react-components/react-dialog/library/src/components/DialogSurface/useDialogSurface.ts @@ -12,7 +12,7 @@ import { } from '@fluentui/react-utilities'; import * as React from 'react'; -import { useDialogContext_unstable } from '../../contexts'; +import { useDialogContext_unstable, useDialogBackdropContext_unstable } from '../../contexts'; import { useDisableBodyScroll } from '../../utils/useDisableBodyScroll'; import { DialogBackdropMotion } from '../DialogBackdropMotion'; import { useMotionForwardedRef } from '../MotionRefForwarder'; @@ -35,6 +35,8 @@ export const useDialogSurface_unstable = ( const modalType = useDialogContext_unstable(ctx => ctx.modalType); const isNestedDialog = useDialogContext_unstable(ctx => ctx.isNestedDialog); + const backdropOverride = useDialogBackdropContext_unstable(); + const treatBackdropAsNested = backdropOverride ?? isNestedDialog; const modalAttributes = useDialogContext_unstable(ctx => ctx.modalAttributes); const dialogRef = useDialogContext_unstable(ctx => ctx.dialogRef); @@ -79,8 +81,12 @@ export const useDialogSurface_unstable = ( elementType: 'div', }); + const backdropAppearance = backdrop?.appearance; + if (backdrop) { backdrop.onClick = handledBackdropClick; + // remove backdrop.appearance so it is not passed to the DOM + delete backdrop.appearance; } const { disableBodyScroll, enableBodyScroll } = useDisableBodyScroll(); @@ -109,6 +115,8 @@ export const useDialogSurface_unstable = ( open, backdrop, isNestedDialog, + treatBackdropAsNested, + backdropAppearance, unmountOnClose, mountNode: props.mountNode, root: slot.always( diff --git a/packages/react-components/react-dialog/library/src/components/DialogSurface/useDialogSurfaceStyles.styles.ts b/packages/react-components/react-dialog/library/src/components/DialogSurface/useDialogSurfaceStyles.styles.ts index d3fed44c49bd2..c6fac8f9d4eb2 100644 --- a/packages/react-components/react-dialog/library/src/components/DialogSurface/useDialogSurfaceStyles.styles.ts +++ b/packages/react-components/react-dialog/library/src/components/DialogSurface/useDialogSurfaceStyles.styles.ts @@ -1,7 +1,6 @@ 'use client'; import { makeResetStyles, makeStyles, mergeClasses } from '@griffel/react'; -import type { SlotClassNames } from '@fluentui/react-utilities'; import { tokens } from '@fluentui/react-theme'; import { createFocusOutlineStyle } from '@fluentui/react-tabster'; import { @@ -12,6 +11,7 @@ import { SURFACE_PADDING, } from '../../contexts'; import type { DialogSurfaceSlots, DialogSurfaceState } from './DialogSurface.types'; +import type { SlotClassNames } from '@fluentui/react-utilities'; export const dialogSurfaceClassNames: SlotClassNames> = { root: 'fui-DialogSurface', @@ -82,11 +82,12 @@ const useStyles = makeStyles({ export const useDialogSurfaceStyles_unstable = (state: DialogSurfaceState): DialogSurfaceState => { 'use no memo'; - const { isNestedDialog, root, backdrop, open, unmountOnClose } = state; + const { root, backdrop, open, unmountOnClose, treatBackdropAsNested, backdropAppearance } = state; const rootBaseStyle = useRootBaseStyle(); const backdropBaseStyle = useBackdropBaseStyle(); const styles = useStyles(); + const isBackdropTransparent = backdropAppearance ? backdropAppearance === 'transparent' : treatBackdropAsNested; const mountedAndClosed = !unmountOnClose && !open; @@ -101,8 +102,8 @@ export const useDialogSurfaceStyles_unstable = (state: DialogSurfaceState): Dial backdrop.className = mergeClasses( dialogSurfaceClassNames.backdrop, backdropBaseStyle, - isNestedDialog && styles.nestedDialogBackdrop, mountedAndClosed && styles.dialogHidden, + isBackdropTransparent && styles.nestedDialogBackdrop, backdrop.className, ); } diff --git a/packages/react-components/react-dialog/library/src/contexts/dialogBackdropContext.ts b/packages/react-components/react-dialog/library/src/contexts/dialogBackdropContext.ts new file mode 100644 index 0000000000000..f03656f8ab8a7 --- /dev/null +++ b/packages/react-components/react-dialog/library/src/contexts/dialogBackdropContext.ts @@ -0,0 +1,13 @@ +'use client'; + +import * as React from 'react'; + +export type DialogBackdropContextValue = boolean; + +export const DialogBackdropContext = React.createContext(undefined); + +export const DialogBackdropProvider = DialogBackdropContext.Provider; + +export const useDialogBackdropContext_unstable = (): DialogBackdropContextValue | undefined => { + return React.useContext(DialogBackdropContext); +}; diff --git a/packages/react-components/react-dialog/library/src/contexts/index.ts b/packages/react-components/react-dialog/library/src/contexts/index.ts index 4fe17b52c0c6d..5adcb18912f63 100644 --- a/packages/react-components/react-dialog/library/src/contexts/index.ts +++ b/packages/react-components/react-dialog/library/src/contexts/index.ts @@ -9,4 +9,10 @@ export { export type { DialogContextValue } from './dialogContext'; export { DialogContext, DialogProvider, useDialogContext_unstable } from './dialogContext'; export type { DialogSurfaceContextValue } from './dialogSurfaceContext'; +export type { DialogBackdropContextValue } from './dialogBackdropContext'; export { DialogSurfaceContext, DialogSurfaceProvider, useDialogSurfaceContext_unstable } from './dialogSurfaceContext'; +export { + DialogBackdropContext, + DialogBackdropProvider, + useDialogBackdropContext_unstable, +} from './dialogBackdropContext'; diff --git a/packages/react-components/react-dialog/library/src/index.ts b/packages/react-components/react-dialog/library/src/index.ts index b3f35c11aea04..57466863cc3e5 100644 --- a/packages/react-components/react-dialog/library/src/index.ts +++ b/packages/react-components/react-dialog/library/src/index.ts @@ -59,6 +59,7 @@ export { renderDialogSurface_unstable, } from './DialogSurface'; export type { + DialogBackdropSlotProps, DialogSurfaceProps, DialogSurfaceSlots, DialogSurfaceState, @@ -80,9 +81,11 @@ export { useDialogSurfaceContext_unstable, DialogProvider, DialogSurfaceProvider, + DialogBackdropProvider, + useDialogBackdropContext_unstable, } from './contexts/index'; -export type { DialogContextValue, DialogSurfaceContextValue } from './contexts/index'; +export type { DialogContextValue, DialogSurfaceContextValue, DialogBackdropContextValue } from './contexts/index'; export { DIALOG_MEDIA_QUERY_BREAKPOINT_SELECTOR, diff --git a/packages/react-components/react-dialog/stories/src/Dialog/DialogBackdropAppearance.md b/packages/react-components/react-dialog/stories/src/Dialog/DialogBackdropAppearance.md new file mode 100644 index 0000000000000..e163cfe3ff7c9 --- /dev/null +++ b/packages/react-components/react-dialog/stories/src/Dialog/DialogBackdropAppearance.md @@ -0,0 +1,12 @@ +The `backdrop` slot on `DialogSurface` accepts an `appearance` prop that allows you to explicitly control the backdrop appearance of the dialog. + +By default, DialogSurface automatically determines the backdrop appearance based on context: standalone dialogs show a dimmed backdrop, while nested dialogs (inside another Dialog) show a transparent backdrop to avoid stacking multiple dimmed layers. + +Use `backdrop={{ appearance: "dimmed" }}` when rendering a Dialog inside components that internally use Dialog (like `OverlayDrawer`) but the dialog should visually behave as standalone with a dimmed backdrop. + +- **`'dimmed'`**: Always shows a dimmed backdrop, regardless of nesting. +- **`'transparent'`**: Always shows a transparent backdrop. + +```tsx + +``` diff --git a/packages/react-components/react-dialog/stories/src/Dialog/DialogBackdropAppearance.stories.tsx b/packages/react-components/react-dialog/stories/src/Dialog/DialogBackdropAppearance.stories.tsx new file mode 100644 index 0000000000000..1cbe749a9367b --- /dev/null +++ b/packages/react-components/react-dialog/stories/src/Dialog/DialogBackdropAppearance.stories.tsx @@ -0,0 +1,111 @@ +import * as React from 'react'; +import type { JSXElement } from '@fluentui/react-components'; +import { + Dialog, + DialogTrigger, + DialogSurface, + DialogTitle, + DialogBody, + DialogActions, + DialogContent, + OverlayDrawer, + DrawerBody, + DrawerHeader, + DrawerHeaderTitle, + Button, + Label, + RadioGroup, + Radio, + useId, + tokens, + makeStyles, +} from '@fluentui/react-components'; +import { Dismiss24Regular } from '@fluentui/react-icons'; +import story from './DialogBackdropAppearance.md'; + +const useStyles = makeStyles({ + field: { + display: 'grid', + gridRowGap: tokens.spacingVerticalS, + marginBottom: tokens.spacingVerticalL, + }, +}); + +type BackdropAppearanceOption = 'dimmed' | 'transparent'; + +export const BackdropAppearance = (): JSXElement => { + const styles = useStyles(); + const labelId = useId('backdrop-appearance-label'); + + const [drawerOpen, setDrawerOpen] = React.useState(false); + const [backdropAppearance, setBackdropAppearance] = React.useState(); + const backdropProp = backdropAppearance ? { appearance: backdropAppearance } : undefined; + + return ( + <> + + + setDrawerOpen(open)}> + + } + onClick={() => setDrawerOpen(false)} + /> + } + > + Drawer + + + + +
+ + setBackdropAppearance(data.value as BackdropAppearanceOption)} + aria-labelledby={labelId} + > + + + +
+ + + + + + + + Dialog + + This Dialog is rendered inside an OverlayDrawer, which internally uses Dialog. By default, nested + dialogs have a backdrop applied based on inner context. Use the backdrop prop to override + this behavior. + + + + + + + + + +
+
+ + ); +}; + +BackdropAppearance.parameters = { + docs: { + description: { + story, + }, + }, +}; diff --git a/packages/react-components/react-dialog/stories/src/Dialog/index.stories.tsx b/packages/react-components/react-dialog/stories/src/Dialog/index.stories.tsx index defa29b465345..08b5b71bd7ac7 100644 --- a/packages/react-components/react-dialog/stories/src/Dialog/index.stories.tsx +++ b/packages/react-components/react-dialog/stories/src/Dialog/index.stories.tsx @@ -8,6 +8,7 @@ import ssrMd from './DialogSSR.md'; export { Default } from './DialogDefault.stories'; export { NonModal } from './DialogNonModal.stories'; export { Alert } from './DialogAlert.stories'; +export { BackdropAppearance } from './DialogBackdropAppearance.stories'; export { ScrollingLongContent } from './DialogScrollingLongContent.stories'; export { KeepRenderedInTheDOM } from './DialogKeepRenderedInTheDOM.stories'; export { Actions } from './DialogActions.stories'; diff --git a/packages/react-components/react-drawer/library/src/components/OverlayDrawer/OverlayDrawerSurface/useOverlayDrawerSurfaceStyles.styles.ts b/packages/react-components/react-drawer/library/src/components/OverlayDrawer/OverlayDrawerSurface/useOverlayDrawerSurfaceStyles.styles.ts index 0e8a4befb77d4..b2434d690ea5a 100644 --- a/packages/react-components/react-drawer/library/src/components/OverlayDrawer/OverlayDrawerSurface/useOverlayDrawerSurfaceStyles.styles.ts +++ b/packages/react-components/react-drawer/library/src/components/OverlayDrawer/OverlayDrawerSurface/useOverlayDrawerSurfaceStyles.styles.ts @@ -29,7 +29,7 @@ const useBackdropStyles = makeStyles({ export const useOverlayDrawerSurfaceStyles_unstable = (state: DialogSurfaceState): DialogSurfaceState => { 'use no memo'; - const { isNestedDialog, backdrop, open, unmountOnClose } = state; + const { treatBackdropAsNested, backdrop, open, unmountOnClose } = state; const backdropResetStyles = useBackdropResetStyles(); const backdropStyles = useBackdropStyles(); @@ -39,7 +39,7 @@ export const useOverlayDrawerSurfaceStyles_unstable = (state: DialogSurfaceState if (backdrop) { backdrop.className = mergeClasses( backdropResetStyles, - isNestedDialog && backdropStyles.nested, + treatBackdropAsNested && backdropStyles.nested, mountedAndClosed && backdropStyles.drawerHidden, backdrop.className, ); diff --git a/packages/react-components/react-drawer/library/src/components/OverlayDrawer/renderOverlayDrawer.tsx b/packages/react-components/react-drawer/library/src/components/OverlayDrawer/renderOverlayDrawer.tsx index 6f1530705c4b2..e7389bb00baa5 100644 --- a/packages/react-components/react-drawer/library/src/components/OverlayDrawer/renderOverlayDrawer.tsx +++ b/packages/react-components/react-drawer/library/src/components/OverlayDrawer/renderOverlayDrawer.tsx @@ -3,6 +3,7 @@ import { assertSlots } from '@fluentui/react-utilities'; import type { JSXElement } from '@fluentui/react-utilities'; import { DrawerContextValue, DrawerProvider } from '../../contexts/drawerContext'; +import { DialogBackdropProvider } from '@fluentui/react-dialog'; import type { OverlayDrawerState, OverlayDrawerInternalSlots } from './OverlayDrawer.types'; @@ -17,9 +18,11 @@ export const renderOverlayDrawer_unstable = ( return ( - - - + + + + + ); };