refactor: SearchControls component modernization

This commit is contained in:
Ben Warzeski
2023-05-11 13:29:47 -04:00
parent dde8e759b6
commit 35ee68ea9d
6 changed files with 232 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`SearchControls component render snapshot 1`] = `
<div>
<SearchField
inputLabel="test-input-label"
onBlur={[MockFunction hooks.onBlur]}
onClear={[MockFunction hooks.onClear]}
onSubmit={[MockFunction hooks.onSubmit]}
value="test-search-value"
/>
<small
className="form-text text-muted search-help-text"
>
test-hint-text
</small>
</div>
`;

View File

@@ -0,0 +1,41 @@
import { useIntl } from '@edx/frontend-platform/i18n';
import { actions, selectors, thunkActions } from 'data/redux/hooks';
import messages from './messages';
/**
* Controls for filtering the GradebookTable. Contains the "Edit Filters" button for opening the filter drawer
* as well as the search box for searching by username/email.
*/
export const useSearchControlsData = () => {
const { formatMessage } = useIntl();
const searchValue = selectors.app.useSearchValue();
const fetchGrades = thunkActions.grades.useFetchGrades();
const setSearchValue = actions.app.useSetSearchValue();
const onBlur = (e) => {
setSearchValue(e.target.value);
};
const onClear = () => {
setSearchValue('');
fetchGrades();
};
const onSubmit = (newValue) => {
setSearchValue(newValue);
fetchGrades();
};
return {
onSubmit,
onBlur,
onClear,
searchValue,
inputLabel: formatMessage(messages.label),
hintText: formatMessage(messages.hint),
};
};
export default useSearchControlsData;

View File

@@ -0,0 +1,71 @@
import { useIntl } from '@edx/frontend-platform/i18n';
import { formatMessage } from 'testUtils';
import { actions, selectors, thunkActions } from 'data/redux/hooks';
import useSearchControlsData from './hooks';
import messages from './messages';
jest.mock('data/redux/hooks', () => ({
actions: {
app: { useSetSearchValue: jest.fn() },
},
selectors: {
app: { useSearchValue: jest.fn() },
},
thunkActions: {
grades: { useFetchGrades: jest.fn() },
},
}));
const searchValue = 'test-search-value';
selectors.app.useSearchValue.mockReturnValue(searchValue);
const setSearchValue = jest.fn();
actions.app.useSetSearchValue.mockReturnValue(setSearchValue);
const fetchGrades = jest.fn();
thunkActions.grades.useFetchGrades.mockReturnValue(fetchGrades);
const testValue = 'test-value';
let out;
describe('useSearchControlsData', () => {
beforeEach(() => {
jest.clearAllMocks();
out = useSearchControlsData();
});
describe('behavior', () => {
it('initializes intl hook', () => {
expect(useIntl).toHaveBeenCalled();
});
it('initializes redux hooks', () => {
expect(actions.app.useSetSearchValue).toHaveBeenCalled();
expect(selectors.app.useSearchValue).toHaveBeenCalled();
expect(thunkActions.grades.useFetchGrades).toHaveBeenCalled();
});
});
describe('output', () => {
test('onSubmit sets search value and fetches grades', () => {
out.onSubmit(testValue);
expect(setSearchValue).toHaveBeenCalledWith(testValue);
expect(fetchGrades).toHaveBeenCalled();
});
test('onBlur sets search value to event target', () => {
out.onBlur({ target: { value: testValue } });
expect(setSearchValue).toHaveBeenCalledWith(testValue);
expect(fetchGrades).not.toHaveBeenCalled();
});
test('onClear clears search value and fetches grades', () => {
out.onClear();
expect(setSearchValue).toHaveBeenCalledWith('');
expect(fetchGrades).toHaveBeenCalled();
});
it('forwards searchValue from redux', () => {
expect(out.searchValue).toEqual(searchValue);
});
test('input label message', () => {
expect(out.inputLabel).toEqual(formatMessage(messages.label));
});
test('hint text message', () => {
expect(out.hintText).toEqual(formatMessage(messages.hint));
});
});
});

View File

@@ -0,0 +1,38 @@
import React from 'react';
import { SearchField } from '@edx/paragon';
import useSearchControlsData from './hooks';
/**
* Controls for filtering the GradebookTable. Contains the "Edit Filters" button for opening the filter drawer
* as well as the search box for searching by username/email.
*/
export const SearchControls = () => {
const {
onSubmit,
onBlur,
onClear,
searchValue,
inputLabel,
hintText,
} = useSearchControlsData();
return (
<div>
<SearchField
onSubmit={onSubmit}
inputLabel={inputLabel}
onBlur={onBlur}
onClear={onClear}
value={searchValue}
/>
<small className="form-text text-muted search-help-text">
{hintText}
</small>
</div>
);
};
SearchControls.propTypes = {};
export default SearchControls;

View File

@@ -0,0 +1,48 @@
import React from 'react';
import { shallow } from 'enzyme';
import { SearchField } from '@edx/paragon';
import useSearchControlsData from './hooks';
import SearchControls from '.';
jest.mock('./hooks', () => jest.fn());
const hookProps = {
onSubmit: jest.fn().mockName('hooks.onSubmit'),
onBlur: jest.fn().mockName('hooks.onBlur'),
onClear: jest.fn().mockName('hooks.onClear'),
searchValue: 'test-search-value',
inputLabel: 'test-input-label',
hintText: 'test-hint-text',
};
useSearchControlsData.mockReturnValue(hookProps);
let el;
describe('SearchControls component', () => {
beforeEach(() => {
jest.clearAllMocks();
el = shallow(<SearchControls />);
});
describe('behavior', () => {
it('initializes component hooks', () => {
expect(useSearchControlsData).toHaveBeenCalled();
});
});
describe('render', () => {
test('snapshot', () => {
expect(el).toMatchSnapshot();
});
test('search field', () => {
const props = el.find(SearchField).props();
expect(props.onSubmit).toEqual(hookProps.onSubmit);
expect(props.onBlur).toEqual(hookProps.onBlur);
expect(props.onClear).toEqual(hookProps.onClear);
expect(props.inputLabel).toEqual(hookProps.inputLabel);
expect(props.value).toEqual(hookProps.searchValue);
});
test('hint text', () => {
expect(el.find('small').text()).toEqual(hookProps.hintText);
});
});
});

View File

@@ -0,0 +1,16 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
label: {
id: 'gradebook.GradesView.search.label',
defaultMessage: 'Search for a learner',
description: 'Text prompting a user to use this functionality to search for a learner',
},
hint: {
id: 'gradebook.GradesView.search.hint',
defaultMessage: 'Search by username, email, or student key',
description: 'A hint explaining the ways a user can search',
},
});
export default messages;