refactor: SearchControls component modernization
This commit is contained in:
@@ -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>
|
||||
`;
|
||||
41
src/components/GradesView/SearchControls/hooks.js
Normal file
41
src/components/GradesView/SearchControls/hooks.js
Normal 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;
|
||||
71
src/components/GradesView/SearchControls/hooks.test.js
Normal file
71
src/components/GradesView/SearchControls/hooks.test.js
Normal 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));
|
||||
});
|
||||
});
|
||||
});
|
||||
38
src/components/GradesView/SearchControls/index.jsx
Normal file
38
src/components/GradesView/SearchControls/index.jsx
Normal 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;
|
||||
48
src/components/GradesView/SearchControls/index.test.jsx
Normal file
48
src/components/GradesView/SearchControls/index.test.jsx
Normal 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);
|
||||
});
|
||||
});
|
||||
});
|
||||
16
src/components/GradesView/SearchControls/messages.js
Normal file
16
src/components/GradesView/SearchControls/messages.js
Normal 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;
|
||||
Reference in New Issue
Block a user