refactor(Menu): Upgrade Menu Component to Ant Design 5 (#31699)

Co-authored-by: Beto Dealmeida <roberto@dealmeida.net>
This commit is contained in:
Geido
2025-01-21 19:06:53 +01:00
committed by GitHub
parent 550123882c
commit eec374426f
54 changed files with 984 additions and 1379 deletions

View File

@@ -17,28 +17,13 @@
* under the License.
*/
import { KeyboardEvent, ReactElement } from 'react';
import userEvent from '@testing-library/user-event';
import { render, screen } from 'spec/helpers/testing-library';
import { FeatureFlag, VizType } from '@superset-ui/core';
import mockState from 'spec/fixtures/mockState';
import { Menu } from 'src/components/Menu';
import SliceHeaderControls from '.';
import { SliceHeaderControlsProps } from './types';
import { handleDropdownNavigation } from './utils';
import SliceHeaderControls, { SliceHeaderControlsProps } from '.';
jest.mock('src/components/Dropdown', () => {
const original = jest.requireActual('src/components/Dropdown');
return {
...original,
NoAnimationDropdown: (props: any) => (
<div data-test="NoAnimationDropdown" className="ant-dropdown">
{props.overlay}
{props.children}
</div>
),
};
});
const SLICE_ID = 371;
const createProps = (viz_type = VizType.Sunburst) =>
({
@@ -54,7 +39,7 @@ const createProps = (viz_type = VizType.Sunburst) =>
toggleExpandSlice: jest.fn(),
logEvent: jest.fn(),
slice: {
slice_id: 371,
slice_id: SLICE_ID,
slice_url: '/explore/?form_data=%7B%22slice_id%22%3A%20371%7D',
slice_name: 'Vaccine Candidates per Country & Stage',
slice_description: 'Table of vaccine candidates for 100 countries',
@@ -73,7 +58,7 @@ const createProps = (viz_type = VizType.Sunburst) =>
secondary_metric: 'metrics',
},
row_limit: 10000,
slice_id: 371,
slice_id: SLICE_ID,
time_range: 'No filter',
url_params: {},
viz_type,
@@ -104,6 +89,7 @@ const createProps = (viz_type = VizType.Sunburst) =>
viz_type: VizType.Sunburst,
},
exploreUrl: '/explore',
defaultOpen: true,
}) as SliceHeaderControlsProps;
const renderWrapper = (
@@ -131,7 +117,7 @@ test('Should render', () => {
expect(
screen.getByRole('button', { name: 'More Options' }),
).toBeInTheDocument();
expect(screen.getByTestId('NoAnimationDropdown')).toBeInTheDocument();
expect(screen.getByTestId(`slice_${SLICE_ID}-menu`)).toBeInTheDocument();
});
test('Should render default props', () => {
@@ -157,27 +143,17 @@ test('Should render default props', () => {
delete props.isExpanded;
renderWrapper(props);
expect(
screen.getByRole('menuitem', { name: 'Enter fullscreen' }),
).toBeInTheDocument();
expect(
screen.getByRole('menuitem', { name: /Force refresh/ }),
).toBeInTheDocument();
expect(
screen.getByRole('menuitem', { name: 'Show chart description' }),
).toBeInTheDocument();
expect(
screen.getByRole('menuitem', { name: 'Edit chart' }),
).toBeInTheDocument();
expect(
screen.getByRole('menuitem', { name: 'Download' }),
).toBeInTheDocument();
expect(screen.getByRole('menuitem', { name: 'Share' })).toBeInTheDocument();
expect(screen.getByText('Enter fullscreen')).toBeInTheDocument();
expect(screen.getByText('Force refresh')).toBeInTheDocument();
expect(screen.getByText('Show chart description')).toBeInTheDocument();
expect(screen.getByText('Edit chart')).toBeInTheDocument();
expect(screen.getByText('Download')).toBeInTheDocument();
expect(screen.getByText('Share')).toBeInTheDocument();
expect(
screen.getByRole('button', { name: 'More Options' }),
).toBeInTheDocument();
expect(screen.getByTestId('NoAnimationDropdown')).toBeInTheDocument();
expect(screen.getByTestId(`slice_${SLICE_ID}-menu`)).toBeInTheDocument();
});
test('Should "export to CSV"', async () => {
@@ -449,168 +425,3 @@ test('Should not show the "Edit chart" button', () => {
});
expect(screen.queryByText('Edit chart')).not.toBeInTheDocument();
});
describe('handleDropdownNavigation', () => {
const mockToggleDropdown = jest.fn();
const mockSetSelectedKeys = jest.fn();
const mockSetOpenKeys = jest.fn();
const menu = (
<Menu selectedKeys={['item1']}>
<Menu.Item key="item1">Item 1</Menu.Item>
<Menu.Item key="item2">Item 2</Menu.Item>
<Menu.Item key="item3">Item 3</Menu.Item>
</Menu>
);
beforeEach(() => {
jest.clearAllMocks();
});
test('should continue with system tab navigation if dropdown is closed and tab key is pressed', () => {
const event = {
key: 'Tab',
preventDefault: jest.fn(),
} as unknown as KeyboardEvent<HTMLDivElement>;
handleDropdownNavigation(
event,
false,
<div />,
mockToggleDropdown,
mockSetSelectedKeys,
mockSetOpenKeys,
);
expect(mockToggleDropdown).not.toHaveBeenCalled();
expect(mockSetSelectedKeys).not.toHaveBeenCalled();
});
test(`should prevent default behavior and toggle dropdown if dropdown
is closed and action key is pressed`, () => {
const event = {
key: 'Enter',
preventDefault: jest.fn(),
} as unknown as KeyboardEvent<HTMLDivElement>;
handleDropdownNavigation(
event,
false,
<div />,
mockToggleDropdown,
mockSetSelectedKeys,
mockSetOpenKeys,
);
expect(mockToggleDropdown).toHaveBeenCalled();
expect(mockSetSelectedKeys).not.toHaveBeenCalled();
});
test(`should trigger menu item click,
clear selected keys, close dropdown, and focus on menu trigger
if action key is pressed and menu item is selected`, () => {
const event = {
key: 'Enter',
preventDefault: jest.fn(),
currentTarget: { focus: jest.fn() },
} as unknown as KeyboardEvent<HTMLDivElement>;
handleDropdownNavigation(
event,
true,
menu,
mockToggleDropdown,
mockSetSelectedKeys,
mockSetOpenKeys,
);
expect(mockToggleDropdown).toHaveBeenCalled();
expect(mockSetSelectedKeys).toHaveBeenCalledWith([]);
expect(event.currentTarget.focus).toHaveBeenCalled();
});
test('should select the next menu item if down arrow key is pressed', () => {
const event = {
key: 'ArrowDown',
preventDefault: jest.fn(),
} as unknown as KeyboardEvent<HTMLDivElement>;
handleDropdownNavigation(
event,
true,
menu,
mockToggleDropdown,
mockSetSelectedKeys,
mockSetOpenKeys,
);
expect(mockSetSelectedKeys).toHaveBeenCalledWith(['item2']);
});
test('should select the previous menu item if up arrow key is pressed', () => {
const event = {
key: 'ArrowUp',
preventDefault: jest.fn(),
} as unknown as KeyboardEvent<HTMLDivElement>;
handleDropdownNavigation(
event,
true,
menu,
mockToggleDropdown,
mockSetSelectedKeys,
mockSetOpenKeys,
);
expect(mockSetSelectedKeys).toHaveBeenCalledWith(['item1']);
});
test('should close dropdown menu if escape key is pressed', () => {
const event = {
key: 'Escape',
preventDefault: jest.fn(),
} as unknown as KeyboardEvent<HTMLDivElement>;
handleDropdownNavigation(
event,
true,
<div />,
mockToggleDropdown,
mockSetSelectedKeys,
mockSetOpenKeys,
);
expect(mockToggleDropdown).toHaveBeenCalled();
expect(mockSetSelectedKeys).not.toHaveBeenCalled();
});
test('should do nothing if an unsupported key is pressed', () => {
const event = {
key: 'Shift',
preventDefault: jest.fn(),
} as unknown as KeyboardEvent<HTMLDivElement>;
handleDropdownNavigation(
event,
true,
<div />,
mockToggleDropdown,
mockSetSelectedKeys,
mockSetOpenKeys,
);
expect(mockToggleDropdown).not.toHaveBeenCalled();
expect(mockSetSelectedKeys).not.toHaveBeenCalled();
});
test('should find a child element with a key', () => {
const item = {
props: {
children: [
<div key="1">Child 1</div>,
<div key="2">Child 2</div>,
<div key="3">Child 3</div>,
],
},
};
const childWithKey = item?.props?.children?.find(
(child: ReactElement) => child?.key,
);
expect(childWithKey).toBeDefined();
});
});