Skip to content

[Bug]: headless MenuSplitGroup renders without a contexts argument, so useIsInMenuSplitGroup can never return true #36651

Description

Component

Menu

Package version

@fluentui/react-headless-components-preview 0.2.5

React version

18.3.1

Environment

node v22.12.0
@fluentui/react-headless-components-preview 0.2.5

Current Behavior

components/Menu/MenuSplitGroup/MenuSplitGroup.tsx calls

renderMenuSplitGroup(state);

with no contexts argument. useIsInMenuSplitGroup compares the value it receives against the
module-level default by identity. With nothing provided, the default is what every descendant
reads, the identity comparison is always false, and no descendant half can tell that it is inside
a split group.

The user-visible consequence is that the gutter suppression a split group is supposed to apply —
the icon and checkmark columns that the trigger half drops — never fires, because that suppression
is gated on exactly this hook.

Expected Behavior

useIsInMenuSplitGroup returns true for descendants of a MenuSplitGroup, matching the
behaviour of the Griffel-styled MenuSplitGroup the headless one mirrors.

Reproduction

Render a headless MenuSplitGroup containing a trigger half and call useIsInMenuSplitGroup from
any descendant: it returns false. Visually, in a styled layer over the headless package, the
trigger half keeps the icon and checkmark gutters that the Griffel-styled equivalent drops.

Steps to reproduce

  1. Render a MenuSplitGroup with a menu item as its trigger half.
  2. From a descendant, read useIsInMenuSplitGroup().
  3. Observe false.

Discovery context

Found during pixel-level verification of a styling layer built over
@fluentui/react-headless-components-preview: the styled trigger half kept the icon and checkmark
gutters that the @fluentui/react-components twin drops, and tracing the gutter rule led back to a
hook that could never return true.

Proposed fix

Provide the contexts argument, with a useMemo keyed on state.setMultiline so the identity is
stable across renders (identity stability is the whole point — an inline object literal would defeat
the comparison in the other direction, re-firing on every render):

const contexts = React.useMemo(
  () => ({ menuSplitGroup: { setMultiline: state.setMultiline } }),
  [state.setMultiline],
);
return renderMenuSplitGroup(state, contexts);

setMultiline itself stays the documented no-op — this changes what descendants can observe, not
what the group does.

A working implementation is included in PR #[WINDMOD-PR-NUMBER] (commit 568297d44a); happy to
split it into a standalone PR against this issue.

Suggested severity

Medium - Has workaround (a styling layer can re-declare its own split-group context, at the cost of
duplicating a contract the package already owns).

Are you willing to submit a PR to fix?

yes

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions