improved Select.test.jsx

This commit is contained in:
Bernt Christian Egeland 2021-12-11 12:15:24 +01:00
parent 4e5a368a97
commit 313deb986b
4 changed files with 81 additions and 34 deletions

View File

@ -53,7 +53,7 @@ export const DateFilterOptions = [
}, },
{ {
label: 'Custom Range', label: 'Custom Range',
value: null, value: 'custom_range',
}, },
]; ];
@ -134,6 +134,7 @@ export default function DatePicker({
tabIndex="0" tabIndex="0"
onClick={onClick} onClick={onClick}
value={propValue} value={propValue}
{...props}
/> />
<div <div
className={`absolute top-3 transition transform text-gray-600 dark:text-gray-400 ${ className={`absolute top-3 transition transform text-gray-600 dark:text-gray-400 ${

View File

@ -76,19 +76,20 @@ export default function Select({
const ref = useRef(null); const ref = useRef(null);
const handleSelect = useCallback( const handleSelect = useCallback(
(value, label) => { (value) => {
setSelected(options.findIndex(({ value }) => Object.values(propSelected).includes(value))); setSelected(options.findIndex(({ value }) => Object.values(propSelected).includes(value)));
setShowMenu(false); setShowMenu(false);
if (!value) return setShowDatePicker(true); //show calender date range picker
onChange && onChange(value, label); if (value === 'custom_range') return setShowDatePicker(true);
onChange && onChange(value);
}, },
[onChange, options, propSelected, setSelected] [onChange, options, propSelected, setSelected]
); );
const handleDateRange = useCallback( const handleDateRange = useCallback(
(range) => { (range) => {
onChange && onChange(range, 'range'); onChange && onChange(range);
setShowMenu(false); setShowMenu(false);
}, },
[onChange] [onChange]
@ -98,6 +99,44 @@ export default function Select({
setShowMenu(true); setShowMenu(true);
}, [setShowMenu]); }, [setShowMenu]);
const handleKeydownDatePicker = useCallback(
(event) => {
switch (event.key) {
case 'Enter': {
if (!showMenu) {
setShowMenu(true);
setFocused(selected);
} else {
setSelected(focused);
if (options[focused].value === 'custom_range') {
setShowMenu(false);
return setShowDatePicker(true);
}
onChange && onChange(options[focused].value);
setShowMenu(false);
}
break;
}
case 'ArrowDown': {
const newIndex = focused + 1;
newIndex < options.length && setFocused(newIndex);
break;
}
case 'ArrowUp': {
const newIndex = focused - 1;
newIndex > -1 && setFocused(newIndex);
break;
}
// no default
}
},
[onChange, options, showMenu, setShowMenu, setFocused, focused, selected]
);
const handleKeydown = useCallback( const handleKeydown = useCallback(
(event) => { (event) => {
switch (event.key) { switch (event.key) {
@ -107,7 +146,7 @@ export default function Select({
setFocused(selected); setFocused(selected);
} else { } else {
setSelected(focused); setSelected(focused);
onChange && onChange(options[focused].value, options[focused].label); onChange && onChange({ [paramName]: options[focused].value });
setShowMenu(false); setShowMenu(false);
} }
break; break;
@ -146,7 +185,7 @@ export default function Select({
} }
}; };
window.addEventListener('click', addBackDrop); window.addEventListener('click', addBackDrop);
// setDateToInput(state.selectedDay);
return function cleanup() { return function cleanup() {
window.removeEventListener('click', addBackDrop); window.removeEventListener('click', addBackDrop);
}; };
@ -161,7 +200,7 @@ export default function Select({
label={label} label={label}
onchange={onChange} onchange={onChange}
onclick={handleClick} onclick={handleClick}
onkeydown={handleKeydown} onkeydown={handleKeydownDatePicker}
trailingIcon={showMenu ? ArrowDropup : ArrowDropdown} trailingIcon={showMenu ? ArrowDropup : ArrowDropdown}
value={datePickerValue} value={datePickerValue}
/> />

View File

@ -4,6 +4,30 @@ import { fireEvent, render, screen } from '@testing-library/preact';
describe('Select', () => { describe('Select', () => {
test('on focus, shows a menu', async () => { test('on focus, shows a menu', async () => {
const handleChange = jest.fn();
render(
<Select
label="Tacos"
type="dropdown"
onChange={handleChange}
options={['all', 'tacos', 'burritos']}
paramName={['dinner']}
selected=""
/>
);
expect(screen.queryByRole('listbox')).not.toBeInTheDocument();
fireEvent.click(screen.getByRole('textbox'));
expect(screen.queryByRole('listbox')).toBeInTheDocument();
expect(screen.queryByRole('option', { name: 'all' })).toBeInTheDocument();
expect(screen.queryByRole('option', { name: 'tacos' })).toBeInTheDocument();
expect(screen.queryByRole('option', { name: 'burritos' })).toBeInTheDocument();
fireEvent.click(screen.queryByRole('option', { name: 'tacos' }));
expect(handleChange).toHaveBeenCalledWith({ dinner: 'tacos' });
});
test('allows keyboard navigation', async () => {
const handleChange = jest.fn(); const handleChange = jest.fn();
render( render(
<Select <Select
@ -17,27 +41,13 @@ describe('Select', () => {
); );
expect(screen.queryByRole('listbox')).not.toBeInTheDocument(); expect(screen.queryByRole('listbox')).not.toBeInTheDocument();
fireEvent.click(screen.getByRole('textbox')); const input = screen.getByRole('textbox');
fireEvent.focus(input);
fireEvent.keyDown(input, { key: 'Enter', code: 'Enter' });
expect(screen.queryByRole('listbox')).toBeInTheDocument(); expect(screen.queryByRole('listbox')).toBeInTheDocument();
expect(screen.queryByRole('option', { name: 'tacos' })).toBeInTheDocument();
expect(screen.queryByRole('option', { name: 'burritos' })).toBeInTheDocument();
fireEvent.click(screen.queryByRole('option', { name: 'tacos' })); fireEvent.keyDown(input, { key: 'ArrowDown', code: 'ArrowDown' });
expect(handleChange).toHaveBeenCalledWith({ dinner: 'tacos' }, 'tacos'); fireEvent.keyDown(input, { key: 'Enter', code: 'Enter' });
expect(handleChange).toHaveBeenCalledWith({ dinner: 'burritos' });
}); });
// test('allows keyboard navigation', async () => {
// const handleChange = jest.fn();
// render(<Select label="Tacos" onChange={handleChange} options={['tacos', 'burritos']} paramName={['burritos']} />);
// expect(screen.queryByRole('listbox')).not.toBeInTheDocument();
// const input = screen.getByRole('textbox');
// fireEvent.focus(input);
// fireEvent.keyDown(input, { key: 'Enter', code: 'Enter' });
// expect(screen.queryByRole('listbox')).toBeInTheDocument();
// fireEvent.keyDown(input, { key: 'ArrowDown', code: 'ArrowDown' });
// fireEvent.keyDown(input, { key: 'Enter', code: 'Enter' });
// expect(handleChange).toHaveBeenCalledWith('burritos', 'burritos');
// });
}); });

View File

@ -2,7 +2,7 @@ import { h } from 'preact';
import Select from '../../../components/Select'; import Select from '../../../components/Select';
import { useCallback } from 'preact/hooks'; import { useCallback } from 'preact/hooks';
function Filter({ onChange, searchParams, paramName, options, type, ...rest }) { function Filter({ onChange, searchParams, paramName, options, ...rest }) {
const handleSelect = useCallback( const handleSelect = useCallback(
(key) => { (key) => {
const newParams = new URLSearchParams(searchParams.toString()); const newParams = new URLSearchParams(searchParams.toString());
@ -20,10 +20,7 @@ function Filter({ onChange, searchParams, paramName, options, type, ...rest }) {
); );
const obj = {}; const obj = {};
paramName.map((p) => Object.assign(obj, { [p]: searchParams.get(p) }), [searchParams]); paramName.map((name) => Object.assign(obj, { [name]: searchParams.get(name) }), [searchParams]);
return <Select onChange={handleSelect} options={options} selected={obj} paramName={paramName} {...rest} />;
return (
<Select onChange={handleSelect} options={options} selected={obj} paramName={paramName} type={type} {...rest} />
);
} }
export default Filter; export default Filter;