Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{

@github-actions github-actions Bot Jun 24, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🕵🏾‍♀️ visual changes to review in the Visual Change Report

vr-tests-react-components/Menu Converged - submenuIndicator slotted content 2 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/Menu Converged - submenuIndicator slotted content.default - RTL.submenus open.chromium.png 404 Changed
vr-tests-react-components/Menu Converged - submenuIndicator slotted content.default.submenus open.chromium.png 413 Changed
vr-tests-react-components/Positioning 2 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/Positioning.Positioning end.chromium.png 962 Changed
vr-tests-react-components/Positioning.Positioning end.updated 2 times.chromium.png 511 Changed
vr-tests-react-components/ProgressBar converged 1 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/ProgressBar converged.Indeterminate + thickness - High Contrast.default.chromium.png 188 Changed
vr-tests-react-components/TagPicker 3 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/TagPicker.disabled - Dark Mode.disabled input hover.chromium.png 658 Changed
vr-tests-react-components/TagPicker.disabled - RTL.disabled input hover.chromium.png 635 Changed
vr-tests-react-components/TagPicker.disabled.chromium.png 677 Changed

There were 3 duplicate changes discarded. Check the build logs for more information.

"type": "patch",
"comment": "fix: do not expose default icon in base hook",
"packageName": "@fluentui/react-button",
"email": "vgenaev@gmail.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import * as React from 'react';
import { render } from '@testing-library/react';
import { renderHook } from '@testing-library/react-hooks';
import '@testing-library/jest-dom';
import { ButtonContextProvider } from '../../contexts/ButtonContext';
import type { ButtonContextValue } from '../../contexts/ButtonContext';
import { useMenuButton_unstable } from './useMenuButton';
import { MenuButton } from './MenuButton';

const wrap = (contextValue: ButtonContextValue = {}): React.FC<{ children?: React.ReactNode }> => {
const Wrapper: React.FC<{ children?: React.ReactNode }> = ({ children }) => (
Expand All @@ -19,10 +21,9 @@ describe('useMenuButton_unstable', () => {
expect(result.current.components).toEqual({ root: 'button', icon: 'span', menuIcon: 'span' });
});

it('renders a menuIcon slot with a default chevron icon', () => {
const { result } = renderHook(() => useMenuButton_unstable({}, React.createRef()));
expect(result.current.menuIcon).toBeDefined();
expect(React.isValidElement(result.current.menuIcon?.children)).toBe(true);
it('renders a menuIcon slot with a default icon', () => {
const result = render(<MenuButton />);
expect(result.container.querySelector('svg')).toBeInTheDocument();
});

it('preserves a user-provided menuIcon over the default chevron', () => {
Expand All @@ -33,6 +34,16 @@ describe('useMenuButton_unstable', () => {
expect(result.current.menuIcon?.children).toBe(customIcon);
});

it('renders the default chevron when menuIcon is explicitly undefined', () => {
const result = render(<MenuButton menuIcon={undefined} />);
expect(result.container.querySelector('svg')).toBeInTheDocument();
});

it('hides the menuIcon slot when menuIcon is null', () => {
const { result } = renderHook(() => useMenuButton_unstable({ menuIcon: null }, React.createRef()));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we also check the edge-case if undefined overrides the default icon as well

@mainframev mainframev Jun 25, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added a test asserting it

expect(result.current.menuIcon).toBeUndefined();
});

it('defaults aria-expanded to false when not provided', () => {
const { result } = renderHook(() => useMenuButton_unstable({}, React.createRef()));
expect(result.current.root['aria-expanded']).toBe(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import type { MenuButtonBaseProps, MenuButtonBaseState, MenuButtonProps, MenuBut
/**
* Base hook for MenuButton.
*
* The `menuIcon` slot ships no icon of its own and only renders when a consumer
* provides one, so headless consumers can supply their own visuals. The styled
* `useMenuButton_unstable` adds the default chevron on top of this.
*
* @param props - User provided props to the MenuButton component.
* @param ref - User provided ref to be passed to the MenuButton component.
*/
Expand Down Expand Up @@ -40,10 +44,6 @@ export const useMenuButtonBase_unstable = (
},

menuIcon: slot.optional(menuIcon, {
defaultProps: {
children: <ChevronDownRegular />,
},
renderByDefault: true,
elementType: 'span',
}),
};
Expand All @@ -61,8 +61,14 @@ export const useMenuButton_unstable = (
ref: React.Ref<HTMLButtonElement | HTMLAnchorElement>,
): MenuButtonState => {
const { size: contextSize } = useButtonContext();
const { appearance = 'secondary', shape = 'rounded', size = contextSize ?? 'medium', ...baseProps } = props;
const baseState = useMenuButtonBase_unstable(baseProps, ref);
const { appearance = 'secondary', menuIcon, shape = 'rounded', size = contextSize ?? 'medium', ...baseProps } = props;
const baseState = useMenuButtonBase_unstable(
{
...baseProps,
menuIcon: menuIcon === null ? null : { children: <ChevronDownRegular />, ...slot.resolveShorthand(menuIcon) },
},
ref,
);

return {
...baseState,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,18 @@ import '@testing-library/jest-dom';
import { useMenuButtonBase_unstable } from './useMenuButton';

describe('useMenuButtonBase_unstable', () => {
it('returns a menuIcon slot by default', () => {
it('does not render the menuIcon slot when none is provided', () => {
const { result } = renderHook(() => useMenuButtonBase_unstable({}, React.createRef()));
expect(result.current.menuIcon).toBeUndefined();
});

it('renders the menuIcon slot only when one is provided, shipping no default icon', () => {
const customIcon = <span data-testid="custom-menu-icon" />;
const { result } = renderHook(() =>
useMenuButtonBase_unstable({ menuIcon: { children: customIcon } }, React.createRef()),
);
expect(result.current.menuIcon).toBeDefined();
expect(result.current.menuIcon?.children).toBe(customIcon);
});

it('forces aria-expanded to a boolean on root', () => {
Expand Down
Loading