1
0
mirror of https://github.com/Unleash/unleash.git synced 2024-12-22 19:07:54 +01:00

feat: up and down arrow navigation for filter items (#5673)

This commit is contained in:
Mateusz Kwasniewski 2023-12-18 14:13:32 +01:00 committed by GitHub
parent 75bdd73c15
commit e380d28924
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 76 additions and 6 deletions

View File

@ -1,4 +1,4 @@
import { screen } from '@testing-library/react';
import { screen, fireEvent } from '@testing-library/react';
import { render } from 'utils/testRenderer';
import { FilterItem, FilterItemParams, IFilterItemProps } from './FilterItem';
@ -163,4 +163,32 @@ describe('FilterItem Component', () => {
},
]);
});
it('navigates between items with arrow keys', async () => {
setup(null);
const searchInput = await screen.findByPlaceholderText('Search');
fireEvent.keyDown(searchInput, { key: 'ArrowDown' });
const firstOption = screen.getByText('Option 1').closest('li')!;
expect(document.activeElement).toBe(firstOption);
fireEvent.keyDown(firstOption, { key: 'ArrowUp' });
expect(document.activeElement).toBe(searchInput);
});
it('selects an item with the Enter key', async () => {
const recordedChanges = setup(null);
const searchInput = await screen.findByPlaceholderText('Search');
fireEvent.keyDown(searchInput, { key: 'ArrowDown' });
const firstOption = screen.getByText('Option 1').closest('li')!;
fireEvent.keyDown(firstOption, { key: 'Enter' });
expect(recordedChanges).toContainEqual({
operator: 'IS',
values: ['1'],
});
});
});

View File

@ -9,7 +9,6 @@ import {
StyledTextField,
} from './FilterItem.styles';
import { FilterItemChip } from './FilterItemChip/FilterItemChip';
import { onEnter } from '../../common/Search/SearchSuggestions/onEnter';
export interface IFilterItemProps {
name: string;
@ -27,6 +26,37 @@ export type FilterItemParams = {
values: string[];
};
interface UseSelectionManagementProps {
options: Array<{ label: string; value: string }>;
handleToggle: (value: string) => () => void;
}
const useSelectionManagement = ({
options,
handleToggle,
}: UseSelectionManagementProps) => {
const listRefs = useRef<Array<HTMLInputElement | HTMLLIElement | null>>([]);
const handleSelection = (event: React.KeyboardEvent, index: number) => {
// we have to be careful not to prevent other keys e.g tab
if (event.key === 'ArrowDown' && index < listRefs.current.length - 1) {
event.preventDefault();
listRefs.current[index + 1]?.focus();
} else if (event.key === 'ArrowUp' && index > 0) {
event.preventDefault();
listRefs.current[index - 1]?.focus();
} else if (event.key === 'Enter') {
event.preventDefault();
if (index > 0) {
const listItemIndex = index - 1;
handleToggle(options[listItemIndex].value)();
}
}
};
return { listRefs, handleSelection };
};
export const FilterItem: FC<IFilterItemProps> = ({
name,
label,
@ -92,6 +122,11 @@ export const FilterItem: FC<IFilterItemProps> = ({
}
};
const { listRefs, handleSelection } = useSelectionManagement({
options,
handleToggle,
});
useEffect(() => {
if (state && !currentOperators.includes(state.operator)) {
onChange({
@ -144,6 +179,10 @@ export const FilterItem: FC<IFilterItemProps> = ({
</InputAdornment>
),
}}
inputRef={(el) => {
listRefs.current[0] = el;
}}
onKeyDown={(event) => handleSelection(event, 0)}
/>
<List sx={{ overflowY: 'auto' }} disablePadding>
{options
@ -152,7 +191,7 @@ export const FilterItem: FC<IFilterItemProps> = ({
.toLowerCase()
.includes(searchText.toLowerCase()),
)
.map((option) => {
.map((option, index) => {
const labelId = `checkbox-list-label-${option.value}`;
return (
@ -161,10 +200,13 @@ export const FilterItem: FC<IFilterItemProps> = ({
dense
disablePadding
tabIndex={0}
onKeyDown={onEnter(
handleToggle(option.value),
)}
onClick={handleToggle(option.value)}
ref={(el) => {
listRefs.current[index + 1] = el;
}}
onKeyDown={(event) =>
handleSelection(event, index + 1)
}
>
<StyledCheckbox
edge='start'