add sort-picker
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
import '@testing-library/jest-dom';
|
||||
import { fireEvent, waitFor } from '@testing-library/preact';
|
||||
import { h } from 'preact';
|
||||
|
||||
import { render } from 'tests/utils';
|
||||
import * as commentsActions from 'store/comments/actions';
|
||||
import type { StoreState } from 'store';
|
||||
|
||||
import { SortPicker } from './sort-picker';
|
||||
|
||||
const defaultState = { comments: {} as StoreState['comments'], hiddenUsers: {} };
|
||||
const stateWithSort = { comments: { sort: '-active' } as StoreState['comments'] };
|
||||
|
||||
describe('<SortPicker />', () => {
|
||||
it('should render sort picker with options', () => {
|
||||
const { container, queryAllByText, queryByText } = render(<SortPicker />, defaultState);
|
||||
|
||||
expect(container.querySelectorAll('option')).toHaveLength(8);
|
||||
expect(queryAllByText('Best')).toHaveLength(2);
|
||||
expect(queryByText('Sort by')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should has static class names', () => {
|
||||
const { container } = render(<SortPicker />, defaultState);
|
||||
|
||||
expect(container.querySelector('.sort-picker')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render selected element', () => {
|
||||
const { container, queryAllByText } = render(<SortPicker />, stateWithSort);
|
||||
|
||||
expect(queryAllByText('Recently updated')).toHaveLength(2);
|
||||
expect(container.querySelector<HTMLOptionElement>('[value="-active"]')?.selected).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should change selected store', async () => {
|
||||
const nextOption = '-controversy';
|
||||
const updateSorting = jest.spyOn(commentsActions, 'updateSorting');
|
||||
const { container } = render(<SortPicker />, defaultState);
|
||||
const select = container.querySelector('select') as HTMLSelectElement;
|
||||
|
||||
expect(select).toBeInTheDocument();
|
||||
|
||||
fireEvent.change(select, { target: { value: nextOption } });
|
||||
|
||||
await waitFor(() => expect(updateSorting).toHaveBeenCalledWith(nextOption));
|
||||
|
||||
expect(container.querySelector<HTMLOptionElement>(`[value="${nextOption}"]`)?.selected).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,88 @@
|
||||
import { h } from 'preact';
|
||||
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
|
||||
import { useMemo } from 'preact/hooks';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
|
||||
import { StoreState } from 'store';
|
||||
import { Select } from 'components/select';
|
||||
import { updateSorting } from 'store/comments/actions';
|
||||
import type { Sorting } from 'common/types';
|
||||
|
||||
export function SortPicker() {
|
||||
const dispatch = useDispatch();
|
||||
const intl = useIntl();
|
||||
const [items, itemsById] = useMemo(() => {
|
||||
const sortOptions = {
|
||||
'-score': intl.formatMessage(messages.best),
|
||||
'+score': intl.formatMessage(messages.worst),
|
||||
'-time': intl.formatMessage(messages.newest),
|
||||
'+time': intl.formatMessage(messages.oldest),
|
||||
'-active': intl.formatMessage(messages.recentlyUpdated),
|
||||
'+active': intl.formatMessage(messages.leastRecentlyUpdated),
|
||||
'-controversy': intl.formatMessage(messages.mostControversial),
|
||||
'+controversy': intl.formatMessage(messages.leastControversial),
|
||||
};
|
||||
type SortItem = { value: string; label: string };
|
||||
const sortItems: SortItem[] = Object.entries(sortOptions).map(([k, v]) => ({ value: k, label: v }));
|
||||
const sortById = sortItems.reduce(
|
||||
(accum, s) => ({ ...accum, [s.value]: s }),
|
||||
{} as Record<keyof typeof sortOptions, SortItem>
|
||||
);
|
||||
|
||||
return [sortItems, sortById];
|
||||
}, [intl]);
|
||||
const sort = useSelector((s: StoreState) => s.comments.sort) || items[0].value;
|
||||
const selected = itemsById[sort];
|
||||
|
||||
function handleSortChange(evt: Event) {
|
||||
const { value } = evt.target as HTMLOptionElement;
|
||||
|
||||
if (!(value in itemsById)) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(updateSorting(value as Sorting));
|
||||
}
|
||||
|
||||
return (
|
||||
<span className="sort-picker">
|
||||
<FormattedMessage id="sort-by" defaultMessage="Sort by" />{' '}
|
||||
<Select items={items} selected={selected} onChange={handleSortChange} />
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
const messages = defineMessages({
|
||||
best: {
|
||||
id: 'commentsSort.best',
|
||||
defaultMessage: 'Best',
|
||||
},
|
||||
worst: {
|
||||
id: 'commentsSort.worst',
|
||||
defaultMessage: 'Worst',
|
||||
},
|
||||
newest: {
|
||||
id: 'commentsSort.newest',
|
||||
defaultMessage: 'Newest',
|
||||
},
|
||||
oldest: {
|
||||
id: 'commentsSort.oldest',
|
||||
defaultMessage: 'Oldest',
|
||||
},
|
||||
recentlyUpdated: {
|
||||
id: 'commentsSort.recently-updated',
|
||||
defaultMessage: 'Recently updated',
|
||||
},
|
||||
leastRecentlyUpdated: {
|
||||
id: 'commentsSort.least-recently-updated',
|
||||
defaultMessage: 'Least recently updated',
|
||||
},
|
||||
mostControversial: {
|
||||
id: 'commentsSort.most-controversial',
|
||||
defaultMessage: 'Most controversial',
|
||||
},
|
||||
leastControversial: {
|
||||
id: 'commentsSort.least-controversial',
|
||||
defaultMessage: 'Least controversial',
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user