From 02c15c62155a271aca03a9b2f14b11b4eb1e2b9c Mon Sep 17 00:00:00 2001 From: mainframev Date: Fri, 23 Jan 2026 19:39:57 +0100 Subject: [PATCH 01/12] feat(react-dialog): extend backdrop slot to support appearance prop --- .../library/etc/react-dialog.api.md | 10 ++- .../react-dialog/library/src/DialogSurface.ts | 1 + .../src/components/Dialog/useDialog.ts | 1 + .../DialogSurface/DialogSurface.types.ts | 27 +++++- .../src/components/DialogSurface/index.ts | 1 + .../DialogSurface/useDialogSurface.ts | 7 +- .../useDialogSurfaceStyles.styles.ts | 26 +++++- .../react-dialog/library/src/index.ts | 1 + .../src/Dialog/DialogBackdropAppearance.md | 12 +++ .../DialogBackdropAppearance.stories.tsx | 85 +++++++++++++++++++ .../stories/src/Dialog/index.stories.tsx | 1 + 11 files changed, 163 insertions(+), 9 deletions(-) create mode 100644 packages/react-components/react-dialog/stories/src/Dialog/DialogBackdropAppearance.md create mode 100644 packages/react-components/react-dialog/stories/src/Dialog/DialogBackdropAppearance.stories.tsx 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..e0ff27fd49275 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,13 +9,14 @@ 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'; import type { PresenceMotionSlotProps } from '@fluentui/react-motion'; import * as React_2 from 'react'; import type { Slot } from '@fluentui/react-utilities'; -import type { SlotClassNames } from '@fluentui/react-utilities'; +import { SlotClassNames } from '@fluentui/react-utilities'; import type { TriggerProps } from '@fluentui/react-utilities'; import { useModalAttributes } from '@fluentui/react-tabster'; @@ -54,6 +55,11 @@ export type DialogActionsSlots = { // @public export type DialogActionsState = ComponentState & Pick, 'position' | 'fluid'>; +// @public +export type DialogBackdropSlotProps = ExtractSlotProps & { + appearance?: 'dimmed' | 'transparent'; +}>; + // @public export const DialogBody: ForwardRefComponent; @@ -180,7 +186,7 @@ export const DialogSurfaceProvider: React_2.Provider; // @public (undocumented) export type DialogSurfaceSlots = { - backdrop?: Slot<'div'>; + backdrop?: Slot; root: Slot<'div'>; backdropMotion: Slot; }; 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..22f15b030cbfa 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,6 @@ export type DialogSurfaceState = ComponentState & Pick & { open?: boolean; unmountOnClose?: boolean; - /** * Transition status for animation. * In test environment, this is always `undefined`. 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..de1d223f4cc23 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 @@ -16,7 +16,12 @@ import { useDialogContext_unstable } from '../../contexts'; import { useDisableBodyScroll } from '../../utils/useDisableBodyScroll'; import { DialogBackdropMotion } from '../DialogBackdropMotion'; import { useMotionForwardedRef } from '../MotionRefForwarder'; -import type { DialogSurfaceElement, DialogSurfaceProps, DialogSurfaceState } from './DialogSurface.types'; +import type { + DialogSurfaceElement, + DialogSurfaceProps, + DialogSurfaceState, + DialogBackdropSlotProps, +} from './DialogSurface.types'; /** * Create the state required to render DialogSurface. 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..d1fe9a2e397a8 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,7 @@ 'use client'; import { makeResetStyles, makeStyles, mergeClasses } from '@griffel/react'; -import type { SlotClassNames } from '@fluentui/react-utilities'; +import { isResolvedShorthand, type SlotClassNames } from '@fluentui/react-utilities'; import { tokens } from '@fluentui/react-theme'; import { createFocusOutlineStyle } from '@fluentui/react-tabster'; import { @@ -76,17 +76,33 @@ const useStyles = makeStyles({ }, }); +/** + * Resolves whether the backdrop should be transparent based on context and prop override. + */ +function resolveBackdropTransparent(isNestedDialog: boolean, backdropAppearance?: 'dimmed' | 'transparent'): boolean { + switch (backdropAppearance) { + case 'dimmed': + return false; + case 'transparent': + return true; + default: + return isNestedDialog; + } +} + /** * Apply styling to the DialogSurface slots based on the state */ export const useDialogSurfaceStyles_unstable = (state: DialogSurfaceState): DialogSurfaceState => { 'use no memo'; - const { isNestedDialog, root, backdrop, open, unmountOnClose } = state; + const { root, backdrop, open, unmountOnClose, isNestedDialog } = state; const rootBaseStyle = useRootBaseStyle(); const backdropBaseStyle = useBackdropBaseStyle(); const styles = useStyles(); + const backdropAppearance = isResolvedShorthand(backdrop) ? backdrop.appearance : undefined; + const isBackdropTransparent = resolveBackdropTransparent(isNestedDialog, backdropAppearance); const mountedAndClosed = !unmountOnClose && !open; @@ -101,10 +117,14 @@ export const useDialogSurfaceStyles_unstable = (state: DialogSurfaceState): Dial backdrop.className = mergeClasses( dialogSurfaceClassNames.backdrop, backdropBaseStyle, - isNestedDialog && styles.nestedDialogBackdrop, mountedAndClosed && styles.dialogHidden, + isBackdropTransparent && styles.nestedDialogBackdrop, backdrop.className, ); + + if (backdrop?.appearance) { + delete backdrop.appearance; + } } return state; diff --git a/packages/react-components/react-dialog/library/src/index.ts b/packages/react-components/react-dialog/library/src/index.ts index b3f35c11aea04..2803b3603d3e1 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, 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..1f98f5371f738 --- /dev/null +++ b/packages/react-components/react-dialog/stories/src/Dialog/DialogBackdropAppearance.stories.tsx @@ -0,0 +1,85 @@ +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, +} from '@fluentui/react-components'; +import { Dismiss24Regular } from '@fluentui/react-icons'; +import story from './DialogBackdropAppearance.md'; + +export const BackdropAppearance = (): JSXElement => { + const [drawerOpen, setDrawerOpen] = React.useState(false); + + return ( + <> + + + setDrawerOpen(open)}> + + } + onClick={() => setDrawerOpen(false)} + /> + } + > + Drawer + + + +

When you open a Dialog from here, it will show a dimmed backdrop.

+

+ Without{' '} + + backdrop={'{{'} appearance: "dimmed" {'}}'} + + , the backdrop would be transparent since this Dialog is nested inside the Drawer (which internally uses + Dialog). +

+ + + + + + + Dialog with dimmed backdrop + + This dialog uses backdrop appearance on DialogSurface to force a dimmed backdrop, even though it's + rendered inside the OverlayDrawer component. + + + + + + + + + +
+
+ + ); +}; + +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'; From 4f2ca66dcf7211ebea85766cc9079e39d3b05d58 Mon Sep 17 00:00:00 2001 From: mainframev Date: Fri, 23 Jan 2026 19:40:01 +0100 Subject: [PATCH 02/12] chore: change files --- ...-react-dialog-e96349b1-d4e8-4754-ac99-d97eb5a1e817.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/@fluentui-react-dialog-e96349b1-d4e8-4754-ac99-d97eb5a1e817.json 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" +} From 2eca07ee1ec84ca8f30b76612c91a6295e899f1b Mon Sep 17 00:00:00 2001 From: mainframev Date: Tue, 27 Jan 2026 14:13:01 +0100 Subject: [PATCH 03/12] fixup! feat(react-dialog): extend backdrop slot to support appearance prop --- .../react-dialog/library/etc/react-dialog.api.md | 2 +- .../src/components/DialogSurface/useDialogSurface.ts | 7 +------ .../DialogSurface/useDialogSurfaceStyles.styles.ts | 3 ++- 3 files changed, 4 insertions(+), 8 deletions(-) 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 e0ff27fd49275..61aba88e0130c 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 @@ -16,7 +16,7 @@ import type { PortalProps } from '@fluentui/react-portal'; import type { PresenceMotionSlotProps } from '@fluentui/react-motion'; import * as React_2 from 'react'; import type { Slot } from '@fluentui/react-utilities'; -import { SlotClassNames } from '@fluentui/react-utilities'; +import type { SlotClassNames } from '@fluentui/react-utilities'; import type { TriggerProps } from '@fluentui/react-utilities'; import { useModalAttributes } from '@fluentui/react-tabster'; 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 de1d223f4cc23..df05129c64297 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 @@ -16,12 +16,7 @@ import { useDialogContext_unstable } from '../../contexts'; import { useDisableBodyScroll } from '../../utils/useDisableBodyScroll'; import { DialogBackdropMotion } from '../DialogBackdropMotion'; import { useMotionForwardedRef } from '../MotionRefForwarder'; -import type { - DialogSurfaceElement, - DialogSurfaceProps, - DialogSurfaceState, - DialogBackdropSlotProps, -} from './DialogSurface.types'; +import type { DialogSurfaceElement, DialogSurfaceProps, DialogSurfaceState } from './DialogSurface.types'; /** * Create the state required to render DialogSurface. 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 d1fe9a2e397a8..6519fde43de98 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,7 @@ 'use client'; import { makeResetStyles, makeStyles, mergeClasses } from '@griffel/react'; -import { isResolvedShorthand, type SlotClassNames } from '@fluentui/react-utilities'; +import { isResolvedShorthand } from '@fluentui/react-utilities'; import { tokens } from '@fluentui/react-theme'; import { createFocusOutlineStyle } from '@fluentui/react-tabster'; import { @@ -12,6 +12,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', From 53b59cab8e537c24dbf7a75c5d17805a17b7b6c1 Mon Sep 17 00:00:00 2001 From: mainframev Date: Tue, 27 Jan 2026 14:24:19 +0100 Subject: [PATCH 04/12] fixup! feat(react-dialog): extend backdrop slot to support appearance prop --- .../DialogBackdropAppearance.stories.tsx | 53 ++++++++++++++----- 1 file changed, 40 insertions(+), 13 deletions(-) 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 index 1f98f5371f738..b313c1cbda768 100644 --- a/packages/react-components/react-dialog/stories/src/Dialog/DialogBackdropAppearance.stories.tsx +++ b/packages/react-components/react-dialog/stories/src/Dialog/DialogBackdropAppearance.stories.tsx @@ -13,12 +13,33 @@ import { 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 ( <> @@ -41,26 +62,32 @@ export const BackdropAppearance = (): JSXElement => { Drawer + -

When you open a Dialog from here, it will show a dimmed backdrop.

-

- Without{' '} - - backdrop={'{{'} appearance: "dimmed" {'}}'} - - , the backdrop would be transparent since this Dialog is nested inside the Drawer (which internally uses - Dialog). -

+
+ + setBackdropAppearance(data.value as BackdropAppearanceOption)} + aria-labelledby={labelId} + > + + + + +
+ - + - Dialog with dimmed backdrop + Dialog - This dialog uses backdrop appearance on DialogSurface to force a dimmed backdrop, even though it's - rendered inside the OverlayDrawer component. + This Dialog is rendered inside an OverlayDrawer, which internally uses Dialog. By default, nested + dialogs have a transparent backdrop to avoid stacking multiple dimmed layers. Use the{' '} + backdrop prop to override this behavior. From 6f4bdccce59a5c41600383d1978a3f291d7685c9 Mon Sep 17 00:00:00 2001 From: mainframev Date: Wed, 28 Jan 2026 17:29:16 +0100 Subject: [PATCH 05/12] test(vr-tests): add Dialog backdrop appearance tests --- .../src/stories/Dialog/Dialog.stories.tsx | 149 +++++++++++++++++- 1 file changed, 148 insertions(+), 1 deletion(-) 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); From c08be81d89bc10837ac90049b34c6ea166d8cc70 Mon Sep 17 00:00:00 2001 From: mainframev Date: Fri, 30 Jan 2026 12:33:52 +0100 Subject: [PATCH 06/12] fixup! feat(react-dialog): extend backdrop slot to support appearance prop --- .../library/etc/react-dialog.api.md | 1 + .../DialogSurface/DialogSurface.types.ts | 1 + .../DialogSurface/useDialogSurface.ts | 5 ++++ .../useDialogSurfaceStyles.styles.ts | 24 ++----------------- .../DialogBackdropAppearance.stories.tsx | 2 +- 5 files changed, 10 insertions(+), 23 deletions(-) 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 61aba88e0130c..3564a6129e20b 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 @@ -196,6 +196,7 @@ export type DialogSurfaceState = ComponentState & Pick & * @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/useDialogSurface.ts b/packages/react-components/react-dialog/library/src/components/DialogSurface/useDialogSurface.ts index df05129c64297..008a8f0210a01 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 @@ -79,8 +79,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 +113,7 @@ export const useDialogSurface_unstable = ( open, backdrop, isNestedDialog, + 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 6519fde43de98..4329c7dff5863 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 { isResolvedShorthand } from '@fluentui/react-utilities'; import { tokens } from '@fluentui/react-theme'; import { createFocusOutlineStyle } from '@fluentui/react-tabster'; import { @@ -77,33 +76,18 @@ const useStyles = makeStyles({ }, }); -/** - * Resolves whether the backdrop should be transparent based on context and prop override. - */ -function resolveBackdropTransparent(isNestedDialog: boolean, backdropAppearance?: 'dimmed' | 'transparent'): boolean { - switch (backdropAppearance) { - case 'dimmed': - return false; - case 'transparent': - return true; - default: - return isNestedDialog; - } -} - /** * Apply styling to the DialogSurface slots based on the state */ export const useDialogSurfaceStyles_unstable = (state: DialogSurfaceState): DialogSurfaceState => { 'use no memo'; - const { root, backdrop, open, unmountOnClose, isNestedDialog } = state; + const { root, backdrop, open, unmountOnClose, isNestedDialog, backdropAppearance } = state; const rootBaseStyle = useRootBaseStyle(); const backdropBaseStyle = useBackdropBaseStyle(); const styles = useStyles(); - const backdropAppearance = isResolvedShorthand(backdrop) ? backdrop.appearance : undefined; - const isBackdropTransparent = resolveBackdropTransparent(isNestedDialog, backdropAppearance); + const isBackdropTransparent = backdropAppearance ? backdropAppearance === 'transparent' : isNestedDialog; const mountedAndClosed = !unmountOnClose && !open; @@ -122,10 +106,6 @@ export const useDialogSurfaceStyles_unstable = (state: DialogSurfaceState): Dial isBackdropTransparent && styles.nestedDialogBackdrop, backdrop.className, ); - - if (backdrop?.appearance) { - delete backdrop.appearance; - } } return state; 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 index b313c1cbda768..12c531d7bfc3d 100644 --- a/packages/react-components/react-dialog/stories/src/Dialog/DialogBackdropAppearance.stories.tsx +++ b/packages/react-components/react-dialog/stories/src/Dialog/DialogBackdropAppearance.stories.tsx @@ -71,7 +71,7 @@ export const BackdropAppearance = (): JSXElement => { onChange={(_, data) => setBackdropAppearance(data.value as BackdropAppearanceOption)} aria-labelledby={labelId} > - + From 6377b983df4dbac1067daf41ac7976d9f4881a7e Mon Sep 17 00:00:00 2001 From: mainframev Date: Mon, 9 Feb 2026 14:07:41 +0100 Subject: [PATCH 07/12] fixup! fixup! feat(react-dialog): extend backdrop slot to support appearance prop --- .../react-dialog/library/etc/react-dialog.api.md | 10 ++++++++++ .../components/DialogSurface/DialogSurface.types.ts | 6 ++++++ .../src/components/DialogSurface/useDialogSurface.ts | 4 +++- .../DialogSurface/useDialogSurfaceStyles.styles.ts | 4 ++-- .../library/src/contexts/dialogBackdropContext.ts | 12 ++++++++++++ .../react-dialog/library/src/contexts/index.ts | 1 + .../react-dialog/library/src/index.ts | 3 +++ .../src/Dialog/DialogBackdropAppearance.stories.tsx | 5 ++--- 8 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 packages/react-components/react-dialog/library/src/contexts/dialogBackdropContext.ts 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 3564a6129e20b..470d331ba561e 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 @@ -60,6 +60,12 @@ export type DialogBackdropSlotProps = ExtractSlotProps & { appearance?: 'dimmed' | 'transparent'; }>; +// @public (undocumented) +export const DialogBackdropContext: React_2.Context; + +// @public (undocumented) +export const DialogBackdropProvider: React_2.Provider; + // @public export const DialogBody: ForwardRefComponent; @@ -195,6 +201,7 @@ 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']; }; @@ -269,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: (isNestedDialog: boolean) => boolean; + // @public export const useDialogBody_unstable: (props: DialogBodyProps, ref: React_2.Ref) => DialogBodyState; 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 66e18a2be3ec9..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 @@ -66,6 +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`. 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 008a8f0210a01..b83e05c5004dc 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 } from '../../contexts'; import { useDisableBodyScroll } from '../../utils/useDisableBodyScroll'; import { DialogBackdropMotion } from '../DialogBackdropMotion'; import { useMotionForwardedRef } from '../MotionRefForwarder'; @@ -35,6 +35,7 @@ export const useDialogSurface_unstable = ( const modalType = useDialogContext_unstable(ctx => ctx.modalType); const isNestedDialog = useDialogContext_unstable(ctx => ctx.isNestedDialog); + const treatBackdropAsNested = useDialogBackdropContext(isNestedDialog); const modalAttributes = useDialogContext_unstable(ctx => ctx.modalAttributes); const dialogRef = useDialogContext_unstable(ctx => ctx.dialogRef); @@ -113,6 +114,7 @@ export const useDialogSurface_unstable = ( open, backdrop, isNestedDialog, + treatBackdropAsNested, backdropAppearance, unmountOnClose, mountNode: props.mountNode, 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 4329c7dff5863..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 @@ -82,12 +82,12 @@ const useStyles = makeStyles({ export const useDialogSurfaceStyles_unstable = (state: DialogSurfaceState): DialogSurfaceState => { 'use no memo'; - const { root, backdrop, open, unmountOnClose, isNestedDialog, backdropAppearance } = state; + const { root, backdrop, open, unmountOnClose, treatBackdropAsNested, backdropAppearance } = state; const rootBaseStyle = useRootBaseStyle(); const backdropBaseStyle = useBackdropBaseStyle(); const styles = useStyles(); - const isBackdropTransparent = backdropAppearance ? backdropAppearance === 'transparent' : isNestedDialog; + const isBackdropTransparent = backdropAppearance ? backdropAppearance === 'transparent' : treatBackdropAsNested; const mountedAndClosed = !unmountOnClose && !open; 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..25cb28dc19e55 --- /dev/null +++ b/packages/react-components/react-dialog/library/src/contexts/dialogBackdropContext.ts @@ -0,0 +1,12 @@ +'use client'; + +import * as React from 'react'; + +export const DialogBackdropContext = React.createContext(undefined); + +export const DialogBackdropProvider = DialogBackdropContext.Provider; + +export const useDialogBackdropContext = (isNestedDialog: boolean): boolean => { + const backdropOverride = React.useContext(DialogBackdropContext); + return backdropOverride !== undefined ? backdropOverride : isNestedDialog; +}; 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..81635ee4d6c13 100644 --- a/packages/react-components/react-dialog/library/src/contexts/index.ts +++ b/packages/react-components/react-dialog/library/src/contexts/index.ts @@ -10,3 +10,4 @@ export type { DialogContextValue } from './dialogContext'; export { DialogContext, DialogProvider, useDialogContext_unstable } from './dialogContext'; export type { DialogSurfaceContextValue } from './dialogSurfaceContext'; export { DialogSurfaceContext, DialogSurfaceProvider, useDialogSurfaceContext_unstable } from './dialogSurfaceContext'; +export { DialogBackdropContext, DialogBackdropProvider, useDialogBackdropContext } 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 2803b3603d3e1..bd65503ba2f3c 100644 --- a/packages/react-components/react-dialog/library/src/index.ts +++ b/packages/react-components/react-dialog/library/src/index.ts @@ -81,6 +81,9 @@ export { useDialogSurfaceContext_unstable, DialogProvider, DialogSurfaceProvider, + DialogBackdropContext, + DialogBackdropProvider, + useDialogBackdropContext, } from './contexts/index'; export type { DialogContextValue, DialogSurfaceContextValue } from './contexts/index'; 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 index 12c531d7bfc3d..1cbe749a9367b 100644 --- a/packages/react-components/react-dialog/stories/src/Dialog/DialogBackdropAppearance.stories.tsx +++ b/packages/react-components/react-dialog/stories/src/Dialog/DialogBackdropAppearance.stories.tsx @@ -71,7 +71,6 @@ export const BackdropAppearance = (): JSXElement => { onChange={(_, data) => setBackdropAppearance(data.value as BackdropAppearanceOption)} aria-labelledby={labelId} > - @@ -86,8 +85,8 @@ export const BackdropAppearance = (): JSXElement => { Dialog This Dialog is rendered inside an OverlayDrawer, which internally uses Dialog. By default, nested - dialogs have a transparent backdrop to avoid stacking multiple dimmed layers. Use the{' '} - backdrop prop to override this behavior. + dialogs have a backdrop applied based on inner context. Use the backdrop prop to override + this behavior. From 8943ca601581e0c357565e8d7ca0f720ede124b3 Mon Sep 17 00:00:00 2001 From: mainframev Date: Mon, 9 Feb 2026 14:08:39 +0100 Subject: [PATCH 08/12] fix(react-drawer): use DialogBackdropProvider to fix issue with no dimmed nested dialogs --- .../useOverlayDrawerSurfaceStyles.styles.ts | 4 ++-- .../src/components/OverlayDrawer/renderOverlayDrawer.tsx | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) 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 ( - - - + + + + + ); }; From 5b199c60077cd0a21effc4c53a5f7996218ddf6e Mon Sep 17 00:00:00 2001 From: mainframev Date: Mon, 9 Feb 2026 14:09:29 +0100 Subject: [PATCH 09/12] chore: update change files --- ...-react-drawer-e0316a31-a891-42e7-8854-26521bfab9e1.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/@fluentui-react-drawer-e0316a31-a891-42e7-8854-26521bfab9e1.json 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" +} From dff217fe3f6bc91abd6982c566f999dc934879db Mon Sep 17 00:00:00 2001 From: mainframev Date: Mon, 9 Feb 2026 14:38:45 +0100 Subject: [PATCH 10/12] fixup! fixup! fixup! feat(react-dialog): extend backdrop slot to support appearance prop --- .../react-components/etc/react-components.api.md | 6 ++++++ .../react-components/react-components/src/index.ts | 2 ++ .../react-dialog/library/etc/react-dialog.api.md | 14 +++++++------- .../components/DialogSurface/useDialogSurface.ts | 4 ++-- .../library/src/contexts/dialogBackdropContext.ts | 6 ++++-- .../react-dialog/library/src/contexts/index.ts | 7 ++++++- .../react-dialog/library/src/index.ts | 5 ++--- 7 files changed, 29 insertions(+), 15 deletions(-) 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 470d331ba561e..07ab830d8fbd1 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 @@ -55,17 +55,17 @@ export type DialogActionsSlots = { // @public export type DialogActionsState = ComponentState & Pick, 'position' | 'fluid'>; -// @public -export type DialogBackdropSlotProps = ExtractSlotProps & { - appearance?: 'dimmed' | 'transparent'; -}>; - // @public (undocumented) -export const DialogBackdropContext: React_2.Context; +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; @@ -277,7 +277,7 @@ export const useDialogActions_unstable: (props: DialogActionsProps, ref: React_2 export const useDialogActionsStyles_unstable: (state: DialogActionsState) => DialogActionsState; // @public (undocumented) -export const useDialogBackdropContext: (isNestedDialog: boolean) => boolean; +export const useDialogBackdropContext_unstable: (isNestedDialog: boolean) => DialogBackdropContextValue; // @public export const useDialogBody_unstable: (props: DialogBodyProps, ref: React_2.Ref) => DialogBodyState; 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 b83e05c5004dc..2697178582321 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, useDialogBackdropContext } from '../../contexts'; +import { useDialogContext_unstable, useDialogBackdropContext_unstable } from '../../contexts'; import { useDisableBodyScroll } from '../../utils/useDisableBodyScroll'; import { DialogBackdropMotion } from '../DialogBackdropMotion'; import { useMotionForwardedRef } from '../MotionRefForwarder'; @@ -35,7 +35,7 @@ export const useDialogSurface_unstable = ( const modalType = useDialogContext_unstable(ctx => ctx.modalType); const isNestedDialog = useDialogContext_unstable(ctx => ctx.isNestedDialog); - const treatBackdropAsNested = useDialogBackdropContext(isNestedDialog); + const treatBackdropAsNested = useDialogBackdropContext_unstable(isNestedDialog); const modalAttributes = useDialogContext_unstable(ctx => ctx.modalAttributes); const dialogRef = useDialogContext_unstable(ctx => ctx.dialogRef); diff --git a/packages/react-components/react-dialog/library/src/contexts/dialogBackdropContext.ts b/packages/react-components/react-dialog/library/src/contexts/dialogBackdropContext.ts index 25cb28dc19e55..d4396ed4b288c 100644 --- a/packages/react-components/react-dialog/library/src/contexts/dialogBackdropContext.ts +++ b/packages/react-components/react-dialog/library/src/contexts/dialogBackdropContext.ts @@ -2,11 +2,13 @@ import * as React from 'react'; -export const DialogBackdropContext = React.createContext(undefined); +export type DialogBackdropContextValue = boolean; + +export const DialogBackdropContext = React.createContext(undefined); export const DialogBackdropProvider = DialogBackdropContext.Provider; -export const useDialogBackdropContext = (isNestedDialog: boolean): boolean => { +export const useDialogBackdropContext_unstable = (isNestedDialog: boolean): DialogBackdropContextValue => { const backdropOverride = React.useContext(DialogBackdropContext); return backdropOverride !== undefined ? backdropOverride : isNestedDialog; }; 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 81635ee4d6c13..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,5 +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 } from './dialogBackdropContext'; +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 bd65503ba2f3c..57466863cc3e5 100644 --- a/packages/react-components/react-dialog/library/src/index.ts +++ b/packages/react-components/react-dialog/library/src/index.ts @@ -81,12 +81,11 @@ export { useDialogSurfaceContext_unstable, DialogProvider, DialogSurfaceProvider, - DialogBackdropContext, DialogBackdropProvider, - useDialogBackdropContext, + 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, From 294312448a88a6c860443c53b0442429edf38491 Mon Sep 17 00:00:00 2001 From: mainframev Date: Mon, 9 Feb 2026 15:34:24 +0100 Subject: [PATCH 11/12] fixup! chore: update change files --- ...ct-components-f8ff49c3-56bc-4ab7-92bb-ca31d8f2b6a9.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/@fluentui-react-components-f8ff49c3-56bc-4ab7-92bb-ca31d8f2b6a9.json 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" +} From 3e33f15989b473f5db75248d606b2cc3eca1e5c0 Mon Sep 17 00:00:00 2001 From: mainframev Date: Tue, 10 Feb 2026 10:51:18 +0100 Subject: [PATCH 12/12] fixup! fixup! fixup! fixup! feat(react-dialog): extend backdrop slot to support appearance prop --- .../react-dialog/library/etc/react-dialog.api.md | 2 +- .../library/src/components/DialogSurface/useDialogSurface.ts | 3 ++- .../library/src/contexts/dialogBackdropContext.ts | 5 ++--- 3 files changed, 5 insertions(+), 5 deletions(-) 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 07ab830d8fbd1..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 @@ -277,7 +277,7 @@ export const useDialogActions_unstable: (props: DialogActionsProps, ref: React_2 export const useDialogActionsStyles_unstable: (state: DialogActionsState) => DialogActionsState; // @public (undocumented) -export const useDialogBackdropContext_unstable: (isNestedDialog: boolean) => DialogBackdropContextValue; +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/components/DialogSurface/useDialogSurface.ts b/packages/react-components/react-dialog/library/src/components/DialogSurface/useDialogSurface.ts index 2697178582321..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 @@ -35,7 +35,8 @@ export const useDialogSurface_unstable = ( const modalType = useDialogContext_unstable(ctx => ctx.modalType); const isNestedDialog = useDialogContext_unstable(ctx => ctx.isNestedDialog); - const treatBackdropAsNested = useDialogBackdropContext_unstable(isNestedDialog); + const backdropOverride = useDialogBackdropContext_unstable(); + const treatBackdropAsNested = backdropOverride ?? isNestedDialog; const modalAttributes = useDialogContext_unstable(ctx => ctx.modalAttributes); const dialogRef = useDialogContext_unstable(ctx => ctx.dialogRef); diff --git a/packages/react-components/react-dialog/library/src/contexts/dialogBackdropContext.ts b/packages/react-components/react-dialog/library/src/contexts/dialogBackdropContext.ts index d4396ed4b288c..f03656f8ab8a7 100644 --- a/packages/react-components/react-dialog/library/src/contexts/dialogBackdropContext.ts +++ b/packages/react-components/react-dialog/library/src/contexts/dialogBackdropContext.ts @@ -8,7 +8,6 @@ export const DialogBackdropContext = React.createContext { - const backdropOverride = React.useContext(DialogBackdropContext); - return backdropOverride !== undefined ? backdropOverride : isNestedDialog; +export const useDialogBackdropContext_unstable = (): DialogBackdropContextValue | undefined => { + return React.useContext(DialogBackdropContext); };