mirror of
https://github.com/zoriya/v10.git
synced 2026-08-05 05:37:21 +00:00
fix(packages): handle menu child mutations (#1739)
This commit is contained in:
@@ -12,6 +12,7 @@ import {
|
||||
getRootPositionOptions,
|
||||
isEventWithinElement,
|
||||
isMenuNavigationKey,
|
||||
observeMenuViewContent,
|
||||
resolveOffsets,
|
||||
resolvePositioningBoundary,
|
||||
syncMenuViewRoot,
|
||||
@@ -242,6 +243,18 @@ export const MenuContent = forwardRef<HTMLDivElement, MenuContentProps>(function
|
||||
syncMenuViewRoot(internalRef.current, activeSubMenuId !== null);
|
||||
}, [isSubmenu, state.open, activeSubMenuId]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (isSubmenu) return;
|
||||
if (!state.open) return;
|
||||
|
||||
const contentElement = internalRef.current;
|
||||
if (!contentElement) return;
|
||||
|
||||
return observeMenuViewContent(contentElement, () => {
|
||||
syncMenuViewRoot(contentElement, activeSubMenuId !== null);
|
||||
});
|
||||
}, [isSubmenu, state.open, activeSubMenuId]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!isSubmenu) return;
|
||||
|
||||
|
||||
@@ -331,6 +331,48 @@ function expectNoMenuStateAttrs(element: HTMLElement): void {
|
||||
}
|
||||
}
|
||||
|
||||
function createRect(width: number, height: number): DOMRect {
|
||||
return {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width,
|
||||
height,
|
||||
top: 0,
|
||||
right: width,
|
||||
bottom: height,
|
||||
left: 0,
|
||||
toJSON: () => ({}),
|
||||
} as DOMRect;
|
||||
}
|
||||
|
||||
function mockMenuViewSize(element: HTMLElement, getHeight: () => number): void {
|
||||
element.getBoundingClientRect = vi.fn(() => createRect(160, getHeight()));
|
||||
|
||||
Object.defineProperty(element, 'scrollWidth', {
|
||||
configurable: true,
|
||||
get: () => 160,
|
||||
});
|
||||
|
||||
Object.defineProperty(element, 'scrollHeight', {
|
||||
configurable: true,
|
||||
get: getHeight,
|
||||
});
|
||||
}
|
||||
|
||||
function DynamicMenuFixture({ showCaptions }: { showCaptions: boolean }) {
|
||||
return (
|
||||
<MenuRoot defaultOpen>
|
||||
<MenuTrigger>Settings</MenuTrigger>
|
||||
<MenuContent data-testid="content">
|
||||
<MenuView data-testid="root-view">
|
||||
<MenuItem>Speed</MenuItem>
|
||||
{showCaptions ? <MenuItem>Captions</MenuItem> : null}
|
||||
</MenuView>
|
||||
</MenuContent>
|
||||
</MenuRoot>
|
||||
);
|
||||
}
|
||||
|
||||
describe('MenuContent', () => {
|
||||
it('scopes menu state data attributes to content elements', async () => {
|
||||
render(
|
||||
@@ -457,6 +499,20 @@ describe('MenuContent', () => {
|
||||
expect(screen.getByTestId('root-view').hasAttribute('data-menu-view')).toBe(true);
|
||||
});
|
||||
|
||||
it('remeasures an open root view when menu items are added', async () => {
|
||||
const { rerender } = render(<DynamicMenuFixture showCaptions={false} />);
|
||||
const content = screen.getByTestId('content');
|
||||
const rootView = screen.getByTestId('root-view');
|
||||
|
||||
mockMenuViewSize(rootView, () => rootView.children.length * 20);
|
||||
|
||||
rerender(<DynamicMenuFixture showCaptions />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(content.style.getPropertyValue('--media-menu-height')).toBe('40px');
|
||||
});
|
||||
});
|
||||
|
||||
it('forces layout while the submenu starting style is applied', async () => {
|
||||
const startingStyleMeasurements: boolean[] = [];
|
||||
const getBoundingClientRect = HTMLElement.prototype.getBoundingClientRect;
|
||||
|
||||
Reference in New Issue
Block a user