From 278ac101a7ed1581e1ff3b9ecbbaf908187fc435 Mon Sep 17 00:00:00 2001 From: Leangseu Kim Date: Wed, 16 Feb 2022 14:43:08 -0500 Subject: [PATCH] feat: override filter status component fix: review modal test failure chore: update unit test chore: update hook and test chore: add unit test for set all filter chore: update use callback mock --- .../ListView/FilterStatusComponent.jsx | 65 +++++++++++++++ .../ListView/FilterStatusComponent.test.jsx | 80 +++++++++++++++++++ src/containers/ListView/SubmissionsTable.jsx | 2 + .../ListView/SubmissionsTable.test.jsx | 2 + .../FilterStatusComponent.test.jsx.snap | 37 +++++++++ .../SubmissionsTable.test.jsx.snap | 2 + src/containers/ReviewModal/index.jsx | 2 +- 7 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 src/containers/ListView/FilterStatusComponent.jsx create mode 100644 src/containers/ListView/FilterStatusComponent.test.jsx create mode 100644 src/containers/ListView/__snapshots__/FilterStatusComponent.test.jsx.snap diff --git a/src/containers/ListView/FilterStatusComponent.jsx b/src/containers/ListView/FilterStatusComponent.jsx new file mode 100644 index 0000000..e6a2a69 --- /dev/null +++ b/src/containers/ListView/FilterStatusComponent.jsx @@ -0,0 +1,65 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Button, DataTableContext } from '@edx/paragon'; + +import * as module from './FilterStatusComponent'; + +export const filterHooks = () => { + const { state, setAllFilters, headers } = React.useContext(DataTableContext); + if (!setAllFilters || !state.filters) { + return {}; + } + const clearFilters = React.useCallback(() => setAllFilters([]), []); + const headerMap = headers.reduce( + (obj, cur) => ({ ...obj, [cur.id]: cur.Header }), + {}, + ); + const filterNames = state.filters.map((filter) => headerMap[filter.id]); + return { clearFilters, filterNames }; +}; + +export const FilterStatusComponent = ({ + className, + variant, + size, + clearFiltersText, + buttonClassName, + showFilteredFields, +}) => { + const { clearFilters, filterNames } = module.filterHooks(); + if (!clearFilters) { + return null; + } + const filterTexts =

Filtered by {filterNames.join(', ')}

; + return ( +
+ {showFilteredFields && filterTexts} + +
+ ); +}; + +FilterStatusComponent.defaultProps = { + className: '', + buttonClassName: 'pgn__smart-status-button', + variant: 'link', + size: 'inline', + clearFiltersText: 'Clear Filters', + showFilteredFields: true, +}; + +FilterStatusComponent.propTypes = { + className: PropTypes.string, + buttonClassName: PropTypes.string, + variant: PropTypes.string, + size: PropTypes.string, + clearFiltersText: PropTypes.string, + showFilteredFields: PropTypes.bool, +}; diff --git a/src/containers/ListView/FilterStatusComponent.test.jsx b/src/containers/ListView/FilterStatusComponent.test.jsx new file mode 100644 index 0000000..7157ff2 --- /dev/null +++ b/src/containers/ListView/FilterStatusComponent.test.jsx @@ -0,0 +1,80 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import * as module from './FilterStatusComponent'; + +const mockFilterHooks = { + clearFilters: jest.fn().mockName('clearFilters'), + filterNames: ['some abitary', 'column'], +}; + +const mockSetAllFilters = jest.fn().mockName('setAllFilters'); + +describe('FilterStatusComponent component', () => { + const props = { + className: 'css-class-name', + variant: 'button-variant', + size: 'button-size', + clearFiltersText: 'clear-filter-text', + buttonClassName: 'css-class-name-for-button', + showFilteredFields: true, + }; + + const contextProps = { + state: { + filters: [ + { + id: 'filter-column', + value: 'abitary', + }, + ], + }, + setAllFilters: () => mockSetAllFilters, + headers: [ + { + id: 'filter-column', + Header: 'Formatted Filter Column', + }, + { + id: 'dummy-column', + Header: 'Not showing column', + }, + ], + }; + const { FilterStatusComponent } = module; + describe('snapshot', () => { + test('with filter', () => { + jest.spyOn(module, 'filterHooks').mockImplementationOnce(() => mockFilterHooks); + const el = shallow(); + expect(el).toMatchSnapshot(); + }); + + test('with filter but do not show filterd field', () => { + jest.spyOn(module, 'filterHooks').mockImplementationOnce(() => mockFilterHooks); + const el = shallow(); + expect(el).toMatchSnapshot(); + }); + + test('without filter', () => { + jest.spyOn(module, 'filterHooks').mockImplementationOnce(() => ({})); + const el = shallow(); + expect(el).toMatchSnapshot(); + }); + }); + + describe('component', () => { + test('clear filter click', () => { + jest.spyOn(module, 'filterHooks').mockImplementationOnce(() => mockFilterHooks); + const el = shallow(); + el.find('Button').simulate('click'); + expect(mockFilterHooks.clearFilters).toHaveBeenCalledTimes(1); + }); + + test('on clear filter, set all filter get called with empty array', () => { + jest.spyOn(React, 'useContext').mockImplementationOnce(() => ({ ...contextProps })); + jest.spyOn(React, 'useCallback').mockImplementationOnce(cb => cb()); + const el = shallow(); + el.find('Button').simulate('click'); + expect(mockSetAllFilters).toHaveBeenCalledTimes(1); + }); + }); +}); diff --git a/src/containers/ListView/SubmissionsTable.jsx b/src/containers/ListView/SubmissionsTable.jsx index fe4f2a6..e970402 100644 --- a/src/containers/ListView/SubmissionsTable.jsx +++ b/src/containers/ListView/SubmissionsTable.jsx @@ -15,6 +15,7 @@ import lmsMessages from 'data/services/lms/messages'; import { selectors, thunkActions } from 'data/redux'; import StatusBadge from 'components/StatusBadge'; +import FilterStatusComponent from './FilterStatusComponent'; import messages from './messages'; @@ -89,6 +90,7 @@ export class SubmissionsTable extends React.Component { return ( jest.fn().mockName('FilterStatusComponent')); + jest.mock('data/redux', () => ({ selectors: { app: { diff --git a/src/containers/ListView/__snapshots__/FilterStatusComponent.test.jsx.snap b/src/containers/ListView/__snapshots__/FilterStatusComponent.test.jsx.snap new file mode 100644 index 0000000..b2fc489 --- /dev/null +++ b/src/containers/ListView/__snapshots__/FilterStatusComponent.test.jsx.snap @@ -0,0 +1,37 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`FilterStatusComponent component snapshot with filter 1`] = ` +
+

+ Filtered by + some abitary, column +

+ +
+`; + +exports[`FilterStatusComponent component snapshot with filter but do not show filterd field 1`] = ` +
+ +
+`; + +exports[`FilterStatusComponent component snapshot without filter 1`] = `""`; diff --git a/src/containers/ListView/__snapshots__/SubmissionsTable.test.jsx.snap b/src/containers/ListView/__snapshots__/SubmissionsTable.test.jsx.snap index 5bd942a..c6818f6 100644 --- a/src/containers/ListView/__snapshots__/SubmissionsTable.test.jsx.snap +++ b/src/containers/ListView/__snapshots__/SubmissionsTable.test.jsx.snap @@ -4,6 +4,7 @@ exports[`SubmissionsTable component component render tests snapshots snapshot: e exports[`SubmissionsTable component component render tests snapshots snapshot: happy path 1`] = ` ({ export const mapDispatchToProps = { setShowReview: actions.app.setShowReview, stopGrading: thunkActions.grading.cancelGrading, - reloadSubmissions: thunkActions.app.reloadSubmissions, + reloadSubmissions: thunkActions.app.initialize, }; export default connect(mapStateToProps, mapDispatchToProps)(ReviewModal);