mirror of
https://github.com/apache/superset.git
synced 2026-04-20 16:44:46 +00:00
feat(accessibility): add tabbing to chart menu in dashboard (#26138)
Co-authored-by: geido <diegopucci.me@gmail.com> Co-authored-by: Diego Pucci <geido@Diegos-MBP.wind3.hub>
This commit is contained in:
committed by
GitHub
parent
662c1ed618
commit
34b1db219c
@@ -22,7 +22,11 @@ import React from 'react';
|
||||
import { render, screen } from 'spec/helpers/testing-library';
|
||||
import { FeatureFlag } from '@superset-ui/core';
|
||||
import mockState from 'spec/fixtures/mockState';
|
||||
import SliceHeaderControls, { SliceHeaderControlsProps } from '.';
|
||||
import { Menu } from 'src/components/Menu';
|
||||
import SliceHeaderControls, {
|
||||
SliceHeaderControlsProps,
|
||||
handleDropdownNavigation,
|
||||
} from '.';
|
||||
|
||||
jest.mock('src/components/Dropdown', () => {
|
||||
const original = jest.requireActual('src/components/Dropdown');
|
||||
@@ -194,8 +198,7 @@ test('Should "export to Excel"', async () => {
|
||||
});
|
||||
|
||||
test('Export full CSV is under featureflag', async () => {
|
||||
// @ts-ignore
|
||||
global.featureFlags = {
|
||||
(global as any).featureFlags = {
|
||||
[FeatureFlag.AllowFullCsvExport]: false,
|
||||
};
|
||||
const props = createProps('table');
|
||||
@@ -206,8 +209,7 @@ test('Export full CSV is under featureflag', async () => {
|
||||
});
|
||||
|
||||
test('Should "export full CSV"', async () => {
|
||||
// @ts-ignore
|
||||
global.featureFlags = {
|
||||
(global as any).featureFlags = {
|
||||
[FeatureFlag.AllowFullCsvExport]: true,
|
||||
};
|
||||
const props = createProps('table');
|
||||
@@ -220,8 +222,7 @@ test('Should "export full CSV"', async () => {
|
||||
});
|
||||
|
||||
test('Should not show export full CSV if report is not table', async () => {
|
||||
// @ts-ignore
|
||||
global.featureFlags = {
|
||||
(global as any).featureFlags = {
|
||||
[FeatureFlag.AllowFullCsvExport]: true,
|
||||
};
|
||||
renderWrapper();
|
||||
@@ -231,8 +232,7 @@ test('Should not show export full CSV if report is not table', async () => {
|
||||
});
|
||||
|
||||
test('Export full Excel is under featureflag', async () => {
|
||||
// @ts-ignore
|
||||
global.featureFlags = {
|
||||
(global as any).featureFlags = {
|
||||
[FeatureFlag.AllowFullCsvExport]: false,
|
||||
};
|
||||
const props = createProps('table');
|
||||
@@ -243,8 +243,7 @@ test('Export full Excel is under featureflag', async () => {
|
||||
});
|
||||
|
||||
test('Should "export full Excel"', async () => {
|
||||
// @ts-ignore
|
||||
global.featureFlags = {
|
||||
(global as any).featureFlags = {
|
||||
[FeatureFlag.AllowFullCsvExport]: true,
|
||||
};
|
||||
const props = createProps('table');
|
||||
@@ -257,8 +256,7 @@ test('Should "export full Excel"', async () => {
|
||||
});
|
||||
|
||||
test('Should not show export full Excel if report is not table', async () => {
|
||||
// @ts-ignore
|
||||
global.featureFlags = {
|
||||
(global as any).featureFlags = {
|
||||
[FeatureFlag.AllowFullCsvExport]: true,
|
||||
};
|
||||
renderWrapper();
|
||||
@@ -296,8 +294,7 @@ test('Should "Enter fullscreen"', () => {
|
||||
});
|
||||
|
||||
test('Drill to detail modal is under featureflag', () => {
|
||||
// @ts-ignore
|
||||
global.featureFlags = {
|
||||
(global as any).featureFlags = {
|
||||
[FeatureFlag.DrillToDetail]: false,
|
||||
};
|
||||
const props = createProps();
|
||||
@@ -306,8 +303,7 @@ test('Drill to detail modal is under featureflag', () => {
|
||||
});
|
||||
|
||||
test('Should show "Drill to detail"', () => {
|
||||
// @ts-ignore
|
||||
global.featureFlags = {
|
||||
(global as any).featureFlags = {
|
||||
[FeatureFlag.DrillToDetail]: true,
|
||||
};
|
||||
const props = {
|
||||
@@ -322,8 +318,7 @@ test('Should show "Drill to detail"', () => {
|
||||
});
|
||||
|
||||
test('Should not show "Drill to detail"', () => {
|
||||
// @ts-ignore
|
||||
global.featureFlags = {
|
||||
(global as any).featureFlags = {
|
||||
[FeatureFlag.DrillToDetail]: true,
|
||||
};
|
||||
const props = {
|
||||
@@ -400,3 +395,168 @@ 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 React.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 React.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 React.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 React.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 React.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 React.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 React.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: React.ReactElement) => child?.key,
|
||||
);
|
||||
|
||||
expect(childWithKey).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user