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:
parent
75bdd73c15
commit
e380d28924
@ -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'],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -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'
|
||||
|
Loading…
Reference in New Issue
Block a user