From cd21e2b0d71c847959b8f9b996ed8c564cd339d6 Mon Sep 17 00:00:00 2001 From: Dmytro Kirpa Date: Thu, 21 May 2026 15:44:57 +0200 Subject: [PATCH 1/2] fix(react-menu): change the signature for the useMenuTriggerBase hook --- ...-8bb06d39-1438-4d76-9874-026b92c92ee7.json | 7 ++++++ ...-5dc54882-30b5-4f86-affc-ec45b2a32981.json | 7 ++++++ .../Menu/MenuTrigger/useMenuTrigger.ts | 2 +- .../react-menu/library/etc/react-menu.api.md | 12 ++++----- .../react-menu/library/src/MenuTrigger.ts | 2 +- .../MenuTrigger/MenuTrigger.types.ts | 13 ++++++++++ .../src/components/MenuTrigger/index.ts | 8 ++++-- .../components/MenuTrigger/useMenuTrigger.ts | 25 +++---------------- .../react-menu/library/src/index.ts | 7 +----- 9 files changed, 46 insertions(+), 37 deletions(-) create mode 100644 change/@fluentui-react-headless-components-preview-8bb06d39-1438-4d76-9874-026b92c92ee7.json create mode 100644 change/@fluentui-react-menu-5dc54882-30b5-4f86-affc-ec45b2a32981.json diff --git a/change/@fluentui-react-headless-components-preview-8bb06d39-1438-4d76-9874-026b92c92ee7.json b/change/@fluentui-react-headless-components-preview-8bb06d39-1438-4d76-9874-026b92c92ee7.json new file mode 100644 index 00000000000000..1eda764bdb44e9 --- /dev/null +++ b/change/@fluentui-react-headless-components-preview-8bb06d39-1438-4d76-9874-026b92c92ee7.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: update useMenuTriggerBase hook signature", + "packageName": "@fluentui/react-headless-components-preview", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/@fluentui-react-menu-5dc54882-30b5-4f86-affc-ec45b2a32981.json b/change/@fluentui-react-menu-5dc54882-30b5-4f86-affc-ec45b2a32981.json new file mode 100644 index 00000000000000..4d85dce5802e38 --- /dev/null +++ b/change/@fluentui-react-menu-5dc54882-30b5-4f86-affc-ec45b2a32981.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix: update useMenuTriggerBase hook signature", + "packageName": "@fluentui/react-menu", + "email": "dmytrokirpa@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/packages/react-components/react-headless-components-preview/library/src/components/Menu/MenuTrigger/useMenuTrigger.ts b/packages/react-components/react-headless-components-preview/library/src/components/Menu/MenuTrigger/useMenuTrigger.ts index 1631fa8914a80f..ff568785d8574a 100644 --- a/packages/react-components/react-headless-components-preview/library/src/components/Menu/MenuTrigger/useMenuTrigger.ts +++ b/packages/react-components/react-headless-components-preview/library/src/components/Menu/MenuTrigger/useMenuTrigger.ts @@ -38,7 +38,7 @@ export const useMenuTrigger = (props: MenuTriggerProps): MenuTriggerState => { firstFocusable?.focus(); }, [menuPopoverRef]); - const baseState = useMenuTriggerBase_unstable(props, { focusFirst }); + const baseState = useMenuTriggerBase_unstable({ ...props, focusFirst }); const open = useMenuContext(ctx => ctx.open); const openOnContext = useMenuContext(ctx => ctx.openOnContext); const setOpen = useMenuContext(ctx => ctx.setOpen); diff --git a/packages/react-components/react-menu/library/etc/react-menu.api.md b/packages/react-components/react-menu/library/etc/react-menu.api.md index 368d88b246a5bc..0188381c8766cc 100644 --- a/packages/react-components/react-menu/library/etc/react-menu.api.md +++ b/packages/react-components/react-menu/library/etc/react-menu.api.md @@ -414,6 +414,11 @@ export type MenuState = ComponentState & Required; +// @public (undocumented) +export type MenuTriggerBaseProps = MenuTriggerProps & { + focusFirst?: () => void; +}; + // @public export type MenuTriggerChildProps = ARIAButtonResultProps M export const useMenuTrigger_unstable: (props: MenuTriggerProps) => MenuTriggerState; // @public -export const useMenuTriggerBase_unstable: (props: MenuTriggerProps, options?: UseMenuTriggerBaseOptions) => MenuTriggerState; - -// @public -export type UseMenuTriggerBaseOptions = { - focusFirst?: () => void; -}; +export const useMenuTriggerBase_unstable: (props: MenuTriggerBaseProps) => MenuTriggerState; // @public (undocumented) export const useMenuTriggerContext_unstable: () => boolean; diff --git a/packages/react-components/react-menu/library/src/MenuTrigger.ts b/packages/react-components/react-menu/library/src/MenuTrigger.ts index aabd7fa221051e..8511c5385873db 100644 --- a/packages/react-components/react-menu/library/src/MenuTrigger.ts +++ b/packages/react-components/react-menu/library/src/MenuTrigger.ts @@ -1,8 +1,8 @@ export type { MenuTriggerChildProps, + MenuTriggerBaseProps, MenuTriggerProps, MenuTriggerState, - UseMenuTriggerBaseOptions, } from './components/MenuTrigger/index'; export { MenuTrigger, diff --git a/packages/react-components/react-menu/library/src/components/MenuTrigger/MenuTrigger.types.ts b/packages/react-components/react-menu/library/src/components/MenuTrigger/MenuTrigger.types.ts index 8f2d8a02e8f425..ead918a83afa38 100644 --- a/packages/react-components/react-menu/library/src/components/MenuTrigger/MenuTrigger.types.ts +++ b/packages/react-components/react-menu/library/src/components/MenuTrigger/MenuTrigger.types.ts @@ -10,6 +10,19 @@ export type MenuTriggerProps = TriggerProps & { disableButtonEnhancement?: boolean; }; +export type MenuTriggerBaseProps = MenuTriggerProps & { + /** + * Pluggable "focus the first focusable element in the menu popover" callback, + * invoked when an already-open submenu trigger receives the open arrow key. + * + * Not provided by the base hook itself - the base hook is intentionally headless + * and leaves focus discovery to the caller. `useMenuTrigger_unstable` plugs in a + * Tabster-aware implementation; a headless consumer is expected to supply its own. + * If omitted, the keyboard handler is a no-op for that case. + */ + focusFirst?: () => void; +}; + /** * Props that are passed to the child of the MenuTrigger when cloned to ensure correct behaviour for the Menu */ diff --git a/packages/react-components/react-menu/library/src/components/MenuTrigger/index.ts b/packages/react-components/react-menu/library/src/components/MenuTrigger/index.ts index 40b0220ee18f85..b584b8ea426c42 100644 --- a/packages/react-components/react-menu/library/src/components/MenuTrigger/index.ts +++ b/packages/react-components/react-menu/library/src/components/MenuTrigger/index.ts @@ -1,5 +1,9 @@ export { MenuTrigger } from './MenuTrigger'; -export type { MenuTriggerChildProps, MenuTriggerProps, MenuTriggerState } from './MenuTrigger.types'; +export type { + MenuTriggerChildProps, + MenuTriggerBaseProps, + MenuTriggerProps, + MenuTriggerState, +} from './MenuTrigger.types'; export { renderMenuTrigger_unstable } from './renderMenuTrigger'; export { useMenuTrigger_unstable, useMenuTriggerBase_unstable } from './useMenuTrigger'; -export type { UseMenuTriggerBaseOptions } from './useMenuTrigger'; diff --git a/packages/react-components/react-menu/library/src/components/MenuTrigger/useMenuTrigger.ts b/packages/react-components/react-menu/library/src/components/MenuTrigger/useMenuTrigger.ts index 9ee9a91e217b73..f612bc9ec2968d 100644 --- a/packages/react-components/react-menu/library/src/components/MenuTrigger/useMenuTrigger.ts +++ b/packages/react-components/react-menu/library/src/components/MenuTrigger/useMenuTrigger.ts @@ -15,7 +15,7 @@ import { } from '@fluentui/react-utilities'; import * as React from 'react'; -import type { MenuTriggerProps, MenuTriggerState } from './MenuTrigger.types'; +import type { MenuTriggerBaseProps, MenuTriggerProps, MenuTriggerState } from './MenuTrigger.types'; import { useMenuContext_unstable } from '../../contexts/menuContext'; import { useMenuListContext_unstable } from '../../contexts/menuListContext'; import { useIsSubmenu, useOnMenuSafeZoneTimeout } from '../../utils'; @@ -44,24 +44,12 @@ export const useMenuTrigger_unstable = (props: MenuTriggerProps): MenuTriggerSta firstFocusable?.focus(); }, [findFirstFocusable, menuPopoverRef]); - return useMenuTriggerBase_unstable(props, { focusFirst }); + return useMenuTriggerBase_unstable({ ...props, focusFirst }); }; /** * Options accepted by `useMenuTriggerBase_unstable`. */ -export type UseMenuTriggerBaseOptions = { - /** - * Pluggable "focus the first focusable element in the menu popover" callback, - * invoked when an already-open submenu trigger receives the open arrow key. - * - * Not provided by the base hook itself - the base hook is intentionally headless - * and leaves focus discovery to the caller. `useMenuTrigger_unstable` plugs in a - * Tabster-aware implementation; a headless consumer is expected to supply its own. - * If omitted, the keyboard handler is a no-op for that case. - */ - focusFirst?: () => void; -}; /** * Base hook for MenuTrigger component, produces state required to render the component. @@ -75,11 +63,8 @@ export type UseMenuTriggerBaseOptions = { * * @public */ -export const useMenuTriggerBase_unstable = ( - props: MenuTriggerProps, - options?: UseMenuTriggerBaseOptions, -): MenuTriggerState => { - const { children, disableButtonEnhancement = false } = props; +export const useMenuTriggerBase_unstable = (props: MenuTriggerBaseProps): MenuTriggerState => { + const { children, disableButtonEnhancement = false, focusFirst } = props; const triggerRef = useMenuContext_unstable(context => context.triggerRef); const setOpen = useMenuContext_unstable(context => context.setOpen); @@ -91,8 +76,6 @@ export const useMenuTriggerBase_unstable = ( const isSubmenu = useIsSubmenu(); const shouldOpenOnArrowRight = useMenuListContext_unstable(ctx => ctx.shouldOpenOnArrowRight ?? true); - const focusFirst = options?.focusFirst; - const openedWithKeyboardRef = React.useRef(false); const openedViaSafeZoneRef = React.useRef(false); const hasMouseMovedRef = React.useRef(false); diff --git a/packages/react-components/react-menu/library/src/index.ts b/packages/react-components/react-menu/library/src/index.ts index ff5dd5dab0a4fb..2c1e8f99bd4641 100644 --- a/packages/react-components/react-menu/library/src/index.ts +++ b/packages/react-components/react-menu/library/src/index.ts @@ -126,12 +126,7 @@ export { useMenuTrigger_unstable, useMenuTriggerBase_unstable, } from './MenuTrigger'; -export type { - MenuTriggerChildProps, - MenuTriggerProps, - MenuTriggerState, - UseMenuTriggerBaseOptions, -} from './MenuTrigger'; +export type { MenuTriggerChildProps, MenuTriggerProps, MenuTriggerState, MenuTriggerBaseProps } from './MenuTrigger'; export { useCheckmarkStyles_unstable } from './selectable/index'; export type { MenuItemSelectableProps, MenuItemSelectableState, SelectableHandler } from './selectable/index'; From 8da24819d1e3a6cc87c6b08a5b515068a9db99bb Mon Sep 17 00:00:00 2001 From: Dmytro Kirpa Date: Thu, 21 May 2026 16:39:34 +0200 Subject: [PATCH 2/2] improve comment --- .../src/components/MenuTrigger/MenuTrigger.types.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/packages/react-components/react-menu/library/src/components/MenuTrigger/MenuTrigger.types.ts b/packages/react-components/react-menu/library/src/components/MenuTrigger/MenuTrigger.types.ts index ead918a83afa38..617a8b199e2a2e 100644 --- a/packages/react-components/react-menu/library/src/components/MenuTrigger/MenuTrigger.types.ts +++ b/packages/react-components/react-menu/library/src/components/MenuTrigger/MenuTrigger.types.ts @@ -12,13 +12,9 @@ export type MenuTriggerProps = TriggerProps & { export type MenuTriggerBaseProps = MenuTriggerProps & { /** - * Pluggable "focus the first focusable element in the menu popover" callback, - * invoked when an already-open submenu trigger receives the open arrow key. - * - * Not provided by the base hook itself - the base hook is intentionally headless - * and leaves focus discovery to the caller. `useMenuTrigger_unstable` plugs in a - * Tabster-aware implementation; a headless consumer is expected to supply its own. - * If omitted, the keyboard handler is a no-op for that case. + * Optional callback to focus the first focusable element in the menu popover + * when an arrow key is pressed on an open submenu trigger. + * If omitted, the keyboard handler is a no-op. */ focusFirst?: () => void; };