feat: Search and filters added

This commit is contained in:
XnpioChV
2023-03-17 14:45:08 -05:00
parent b78e58cd2a
commit 3b69958427
3 changed files with 195 additions and 3 deletions

View File

@@ -6,6 +6,7 @@ import {
filterMessages,
sortKeys,
sortMessages,
sortFunctions,
} from './utils';
export const state = {
@@ -43,7 +44,43 @@ export const searchAndSortHooks = () => {
};
};
export const videoListHooks = ({ videos }) => {
export const filterListBySearch = ({ searchString, videoList }) => (
videoList.filter(({ displayName }) => displayName.toLowerCase().includes(searchString.toLowerCase()))
);
export const filterListByStatus = ({ statusFilter, videoList }) => {
if (statusFilter === filterKeys.videoStatus) {
return videoList;
}
return videoList.filter(({ status }) => status === statusFilter);
};
export const filterListByHideSelectedCourse = ({ videoList }) => (
// TODO Missing to implement this
videoList
);
export const filterList = ({
sortBy,
filterBy,
searchString,
videos,
}) => {
let filteredList = module.filterListBySearch({
searchString,
videoList: videos,
});
filteredList = module.filterListByStatus({
statusFilter: filterBy,
videoList: filteredList,
});
filteredList = module.filterListByHideSelectedCourse({
videoList: filteredList,
});
return filteredList.sort(sortFunctions[sortBy in sortKeys ? sortKeys[sortBy] : sortKeys.dateNewest]);
};
export const videoListHooks = ({ searchSortProps, videos }) => {
const [highlighted, setHighlighted] = module.state.highlighted(null);
const [
showSelectVideoError,
@@ -53,7 +90,7 @@ export const videoListHooks = ({ videos }) => {
showSizeError,
setShowSizeError,
] = module.state.showSizeError(false);
const filteredList = videos; // TODO missing filters and sort
const filteredList = module.filterList({ ...searchSortProps, videos });
return {
galleryError: {
show: showSelectVideoError,
@@ -118,7 +155,7 @@ export const buildVideos = ({ rawVideos }) => {
export const videoHooks = ({ videos }) => {
const searchSortProps = module.searchAndSortHooks();
const videoList = module.videoListHooks({ videos });
const videoList = module.videoListHooks({ searchSortProps, videos });
const {
galleryError,
galleryProps,

View File

@@ -0,0 +1,134 @@
import * as hooks from './hooks';
import { filterKeys, sortKeys } from './utils';
import { MockUseState } from '../../../testUtils';
import { keyStore } from '../../utils';
jest.mock('react', () => ({
...jest.requireActual('react'),
useRef: jest.fn(val => ({ current: val })),
useEffect: jest.fn(),
useCallback: (cb, prereqs) => ({ cb, prereqs }),
}));
jest.mock('react-redux', () => {
const dispatchFn = jest.fn();
return {
...jest.requireActual('react-redux'),
dispatch: dispatchFn,
useDispatch: jest.fn(() => dispatchFn),
};
});
const state = new MockUseState(hooks);
const hookKeys = keyStore(hooks);
let hook;
const testValue = 'testVALUEVALID';
describe('VideoGallery hooks', () => {
beforeEach(() => {
jest.clearAllMocks();
});
describe('using state', () => {
beforeEach(() => { state.mock(); });
afterEach(() => { state.restore(); });
describe('searchAndSortHooks', () => {
beforeEach(() => {
hook = hooks.searchAndSortHooks();
});
it('returns searchString value, initialized to an empty string', () => {
expect(state.stateVals.searchString).toEqual(hook.searchString);
expect(state.stateVals.searchString).toEqual('');
});
it('returns highlighted value, initialized to dateNewest', () => {
expect(state.stateVals.sortBy).toEqual(hook.sortBy);
expect(state.stateVals.sortBy).toEqual(sortKeys.dateNewest);
});
test('onSearchChange sets searchString with event target value', () => {
hook.onSearchChange({ target: { value: testValue } });
expect(state.setState.searchString).toHaveBeenCalledWith(testValue);
});
test('clearSearchString sets search string to empty string', () => {
hook.clearSearchString();
expect(state.setState.searchString).toHaveBeenCalledWith('');
});
test('onSortClick takes a key and returns callback to set sortBY to that key', () => {
hook.onSortClick(testValue);
expect(state.setState.sortBy).not.toHaveBeenCalled();
hook.onSortClick(testValue)();
expect(state.setState.sortBy).toHaveBeenCalledWith(testValue);
});
});
describe('filterListBySearch', () => {
const matching = [
'test',
'TEst',
'eeees',
'essSSSS',
];
const notMatching = ['bad', 'other', 'bad stuff'];
const searchString = 'eS';
test('returns list filtered lowercase by displayName', () => {
const filter = jest.fn(cb => ({ filter: cb }));
hook = hooks.filterListBySearch({ searchString, videoList: { filter } });
expect(filter).toHaveBeenCalled();
const [[filterCb]] = filter.mock.calls;
matching.forEach(val => expect(filterCb({ displayName: val })).toEqual(true));
notMatching.forEach(val => expect(filterCb({ displayName: val })).toEqual(false));
});
});
describe('imgListHooks outputs', () => {
const props = {
searchSortProps: { searchString: 'Es', sortBy: sortKeys.dateNewest, filterBy: filterKeys.videoStatus },
videos: [
{
displayName: 'sOmEuiMAge',
staTICUrl: '/assets/sOmEuiMAge',
id: 'sOmEuiMAgeURl',
},
],
};
const filterList = (args) => ({ filterList: args });
const load = () => {
jest.spyOn(hooks, hookKeys.filterList).mockImplementationOnce(filterList);
hook = hooks.videoListHooks(props);
};
beforeEach(() => {
load();
});
describe('galleryProps', () => {
it('returns highlighted value, initialized to null', () => {
expect(hook.galleryProps.highlighted).toEqual(state.stateVals.highlighted);
expect(state.stateVals.highlighted).toEqual(null);
});
test('onHighlightChange sets highlighted with event target value', () => {
hook.galleryProps.onHighlightChange({ target: { value: testValue } });
expect(state.setState.highlighted).toHaveBeenCalledWith(testValue);
});
test('displayList returns filterListhook called with searchSortProps and videos', () => {
expect(hook.galleryProps.displayList).toEqual(filterList({
...props.searchSortProps,
videos: props.videos,
}));
});
});
describe('galleryError', () => {
test('show is initialized to false and returns properly', () => {
const show = 'sHOWSelectiRROr';
expect(hook.galleryError.show).toEqual(false);
state.mockVal(state.keys.showSelectVideoError, show);
hook = hooks.videoListHooks(props);
expect(hook.galleryError.show).toEqual(show);
});
test('set sets showSelectVideoError to true', () => {
hook.galleryError.set();
expect(state.setState.showSelectVideoError).toHaveBeenCalledWith(true);
});
test('dismiss sets showSelectVideoError to false', () => {
hook.galleryError.dismiss();
expect(state.setState.showSelectVideoError).toHaveBeenCalledWith(false);
});
});
});
});
});

View File

@@ -37,6 +37,27 @@ export const filterMessages = StrictDict({
failed: messages[messageKeys.filterByVideoStatusFailed],
});
export const sortFunctions = StrictDict({
dateNewest: (a, b) => b.dateAdded - a.dateAdded,
dateOldest: (a, b) => a.dateAdded - b.dateAdded,
nameAscending: (a, b) => {
const nameA = a.displayName.toLowerCase();
const nameB = b.displayName.toLowerCase();
if (nameA < nameB) { return -1; }
if (nameB < nameA) { return 1; }
return b.dateAdded - a.dateAdded;
},
nameDescending: (a, b) => {
const nameA = a.displayName.toLowerCase();
const nameB = b.displayName.toLowerCase();
if (nameA < nameB) { return 1; }
if (nameB < nameA) { return -1; }
return b.dateAdded - a.dateAdded;
},
durationShortest: (a, b) => a.duration - b.duration,
durationLongest: (a, b) => b.duration - a.duration,
});
export const acceptedImgKeys = StrictDict({
mp4: '.mp4',
});