diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 40bb6caf..f41b19eb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: run: yarn run test - name: Update Coveralls - uses: coverallsapp/github-action@main + uses: coverallsapp/github-action@v2 with: github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/package.json b/package.json index 3739f4b9..619704b1 100644 --- a/package.json +++ b/package.json @@ -135,8 +135,8 @@ ], "coverageThreshold": { "global": { - "branches": 80, - "functions": 88, + "branches": 81, + "functions": 89, "lines": 89, "statements": 89 } diff --git a/src/components/Filters/Aggregation.less b/src/components/Filters/Aggregation/Aggregation.less similarity index 100% rename from src/components/Filters/Aggregation.less rename to src/components/Filters/Aggregation/Aggregation.less diff --git a/src/components/Filters/Aggregation/AggregationBranch/AggregationBranch.js b/src/components/Filters/Aggregation/AggregationBranch/AggregationBranch.js new file mode 100644 index 00000000..5930e6a0 --- /dev/null +++ b/src/components/Filters/Aggregation/AggregationBranch/AggregationBranch.js @@ -0,0 +1,143 @@ +import './AggregationBranch.less'; +import { useState } from 'react'; +import { useSelector, useDispatch } from 'react-redux'; +import PropTypes from 'prop-types'; +import { FormattedNumber } from 'react-intl'; +import { + coalesce, + getAllFilters, + sanitizeHtmlId, + slugify, +} from '../../../../utils'; +import { + removeMultipleFilters, + replaceFilters, +} from '../../../../actions/filter'; +import { selectQueryState } from '../../../../reducers/query/selectors'; +import AggregationItem from '../AggregationItem/AggregationItem'; +import getIcon from '../../../iconMap'; +import { SLUG_SEPARATOR } from '../../../../constants'; + +export const UNCHECKED = 'UNCHECKED'; +export const INDETERMINATE = 'INDETERMINATE'; +export const CHECKED = 'CHECKED'; + +export const AggregationBranch = ({ fieldName, item, subitems }) => { + const query = useSelector(selectQueryState); + const dispatch = useDispatch(); + const [isOpen, setOpen] = useState(false); + + // Find all query filters that refer to the field name + const allFilters = coalesce(query, fieldName, []); + + // Do any of these values start with the key? + const keyFilters = allFilters.filter( + (aFilter) => aFilter.indexOf(item.key) === 0, + ); + + // Does the key contain the separator? + const activeChildren = keyFilters.filter( + (key) => key.indexOf(SLUG_SEPARATOR) !== -1, + ); + + const activeParent = keyFilters.filter((key) => key === item.key); + + let checkedState = UNCHECKED; + if (activeParent.length === 0 && activeChildren.length > 0) { + checkedState = INDETERMINATE; + } else if (activeParent.length > 0) { + checkedState = CHECKED; + } + + // Fix up the subitems to prepend the current item key + const buckets = subitems.map((sub) => ({ + disabled: item.isDisabled, + key: slugify(item.key, sub.key), + value: sub.key, + // eslint-disable-next-line camelcase + doc_count: sub.doc_count, + })); + + const liStyle = 'parent m-form-field m-form-field--checkbox body-copy'; + const id = sanitizeHtmlId(`${fieldName} ${item.key}`); + + const toggleParent = () => { + const subItemFilters = getAllFilters(item.key, subitems); + + // Add the active filters (that might be hidden) + activeChildren.forEach((child) => subItemFilters.add(child)); + + if (checkedState === CHECKED) { + dispatch(removeMultipleFilters(fieldName, [...subItemFilters])); + } else { + // remove all of the child filters + const replacementFilters = allFilters.filter( + (filter) => filter.indexOf(item.key + SLUG_SEPARATOR) === -1, + ); + // add self/ parent filter + replacementFilters.push(item.key); + dispatch(replaceFilters(fieldName, [...replacementFilters])); + } + }; + + if (buckets.length === 0) { + return ; + } + + return ( + <> +
  • + + + + + + +
  • + {isOpen ? ( + + ) : null} + + ); +}; + +AggregationBranch.propTypes = { + fieldName: PropTypes.string.isRequired, + item: PropTypes.shape({ + // eslint-disable-next-line camelcase + doc_count: PropTypes.number.isRequired, + key: PropTypes.string.isRequired, + value: PropTypes.string, + isDisabled: PropTypes.bool, + }).isRequired, + subitems: PropTypes.array.isRequired, +}; diff --git a/src/components/Filters/AggregationBranch.less b/src/components/Filters/Aggregation/AggregationBranch/AggregationBranch.less similarity index 100% rename from src/components/Filters/AggregationBranch.less rename to src/components/Filters/Aggregation/AggregationBranch/AggregationBranch.less diff --git a/src/components/Filters/Aggregation/AggregationBranch/AggregationBranch.spec.js b/src/components/Filters/Aggregation/AggregationBranch/AggregationBranch.spec.js new file mode 100644 index 00000000..568993f1 --- /dev/null +++ b/src/components/Filters/Aggregation/AggregationBranch/AggregationBranch.spec.js @@ -0,0 +1,144 @@ +import { testRender as render, screen } from '../../../../testUtils/test-utils'; +import userEvent from '@testing-library/user-event'; +import { merge } from '../../../../testUtils/functionHelpers'; +import { defaultQuery } from '../../../../reducers/query/query'; +import * as filter from '../../../../actions/filter'; +import { AggregationBranch } from './AggregationBranch'; + +const fieldName = 'abc'; + +const item = { + key: 'foo', + doc_count: 99, +}; + +const subitems = [ + { key: 'bar', doc_count: 90 }, + { key: 'baz', doc_count: 5 }, + { key: 'qaz', doc_count: 4 }, +]; + +const renderComponent = (props, newQueryState) => { + merge(newQueryState, defaultQuery); + + const data = { + query: newQueryState, + }; + + return render(, { + preloadedState: data, + }); +}; + +let props; + +describe('component::AggregationBranch', () => { + beforeEach(() => { + props = { + fieldName, + item, + subitems, + }; + }); + + describe('initial state', () => { + test('renders as list item button with unchecked state when one or more subitems are present', () => { + renderComponent(props); + + expect(screen.getByRole('checkbox')).not.toBeChecked(); + expect(screen.getByLabelText(props.item.key)).toBeInTheDocument(); + expect( + screen.getByText(props.item.key, { selector: 'button' }), + ).toBeInTheDocument(); + expect(screen.getByText(props.item.doc_count)).toBeInTheDocument(); + expect(screen.queryByRole('list')).not.toBeInTheDocument(); + }); + + test('renders list item button with disabled checkbox when item property is disabled', () => { + const aitem = { ...item, isDisabled: true }; + props.item = aitem; + + renderComponent(props); + + expect(screen.getByRole('checkbox')).toBeDisabled(); + }); + + test('renders AggregationItem when no subitems are present', () => { + props.subitems = []; + + renderComponent(props); + + //list item doesn't render with toggle button; + //no need to test rendering of values, since it's covered by AggregationItem tests + expect(screen.queryByRole('button')).not.toBeInTheDocument(); + }); + + test('renders with checkbox in checked state', () => { + const query = { + abc: [props.item.key], + }; + + renderComponent(props, query); + expect(screen.getByRole('checkbox')).toBeChecked(); + }); + + test('renders with checkbox in indeterminate state', () => { + const query = { + abc: [`${props.item.key}•${props.subitems[0].key}`], + }; + + renderComponent(props, query); + expect( + screen.getByRole('checkbox', { indeterminate: true }), + ).toBeInTheDocument(); + }); + }); + + describe('toggle states', () => { + const user = userEvent.setup({ delay: null }); + + let replaceFiltersFn, removeMultipleFiltersFn; + + beforeEach(() => { + replaceFiltersFn = jest.spyOn(filter, 'replaceFilters'); + removeMultipleFiltersFn = jest.spyOn(filter, 'removeMultipleFilters'); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + test('should properly check the component', async () => { + renderComponent(props); + + await user.click(screen.getByRole('checkbox')); + + expect(replaceFiltersFn).toHaveBeenCalledWith(props.fieldName, ['foo']); + }); + + test('should properly uncheck the component', async () => { + const query = { + abc: [props.item.key], + }; + + renderComponent(props, query); + + await user.click(screen.getByRole('checkbox')); + + expect(removeMultipleFiltersFn).toHaveBeenCalledWith(props.fieldName, [ + 'foo', + 'foo•bar', + 'foo•baz', + 'foo•qaz', + ]); + }); + + test('should show children list items on button click', async () => { + renderComponent(props); + + await user.click(screen.getByRole('button')); + + expect(screen.getByRole('list')).toBeInTheDocument(); + }); + }); +}); diff --git a/src/components/Filters/indeterminate.png b/src/components/Filters/Aggregation/AggregationBranch/indeterminate.png similarity index 100% rename from src/components/Filters/indeterminate.png rename to src/components/Filters/Aggregation/AggregationBranch/indeterminate.png diff --git a/src/components/Filters/AggregationItem/AggregationItem.js b/src/components/Filters/Aggregation/AggregationItem/AggregationItem.js similarity index 88% rename from src/components/Filters/AggregationItem/AggregationItem.js rename to src/components/Filters/Aggregation/AggregationItem/AggregationItem.js index d7382b9e..b15430a4 100644 --- a/src/components/Filters/AggregationItem/AggregationItem.js +++ b/src/components/Filters/Aggregation/AggregationItem/AggregationItem.js @@ -1,13 +1,13 @@ import PropTypes from 'prop-types'; import { useSelector, useDispatch } from 'react-redux'; import { FormattedNumber } from 'react-intl'; -import { filterPatch, SLUG_SEPARATOR } from '../../../constants'; -import { coalesce, sanitizeHtmlId } from '../../../utils'; -import { arrayEquals } from '../../../utils/compare'; -import { replaceFilters, toggleFilter } from '../../../actions/filter'; -import { getUpdatedFilters } from '../../../utils/filters'; -import { selectAggsState } from '../../../reducers/aggs/selectors'; -import { selectQueryState } from '../../../reducers/query/selectors'; +import { filterPatch, SLUG_SEPARATOR } from '../../../../constants'; +import { coalesce, sanitizeHtmlId } from '../../../../utils'; +import { arrayEquals } from '../../../../utils/compare'; +import { replaceFilters, toggleFilter } from '../../../../actions/filter'; +import { getUpdatedFilters } from '../../../../utils/filters'; +import { selectAggsState } from '../../../../reducers/aggs/selectors'; +import { selectQueryState } from '../../../../reducers/query/selectors'; const appliedFilters = ({ fieldName, item, aggs, filters }) => { // We should find the parent diff --git a/src/components/Filters/AggregationItem/AggregationItem.spec.js b/src/components/Filters/Aggregation/AggregationItem/AggregationItem.spec.js similarity index 95% rename from src/components/Filters/AggregationItem/AggregationItem.spec.js rename to src/components/Filters/Aggregation/AggregationItem/AggregationItem.spec.js index 9afc2e0f..e0137256 100644 --- a/src/components/Filters/AggregationItem/AggregationItem.spec.js +++ b/src/components/Filters/Aggregation/AggregationItem/AggregationItem.spec.js @@ -1,11 +1,11 @@ -import { testRender as render, screen } from '../../../testUtils/test-utils'; -import { merge } from '../../../testUtils/functionHelpers'; +import { testRender as render, screen } from '../../../../testUtils/test-utils'; +import { merge } from '../../../../testUtils/functionHelpers'; import userEvent from '@testing-library/user-event'; -import * as filter from '../../../actions/filter'; -import * as utils from '../../../utils'; -import { slugify } from '../../../utils'; -import { defaultAggs } from '../../../reducers/aggs/aggs'; -import { defaultQuery } from '../../../reducers/query/query'; +import * as filter from '../../../../actions/filter'; +import * as utils from '../../../../utils'; +import { slugify } from '../../../../utils'; +import { defaultAggs } from '../../../../reducers/aggs/aggs'; +import { defaultQuery } from '../../../../reducers/query/query'; import AggregationItem from './AggregationItem'; const defaultTestProps = { diff --git a/src/components/Filters/AggregationBranch.js b/src/components/Filters/AggregationBranch.js deleted file mode 100644 index 49048684..00000000 --- a/src/components/Filters/AggregationBranch.js +++ /dev/null @@ -1,213 +0,0 @@ -import './AggregationBranch.less'; -import { - bindAll, - coalesce, - getAllFilters, - sanitizeHtmlId, - slugify, -} from '../../utils'; -import { removeMultipleFilters, replaceFilters } from '../../actions/filter'; -import AggregationItem from './AggregationItem/AggregationItem'; -import { connect } from 'react-redux'; -import { FormattedNumber } from 'react-intl'; -import getIcon from '../iconMap'; -import PropTypes from 'prop-types'; -import React from 'react'; -import { SLUG_SEPARATOR } from '../../constants'; - -export const UNCHECKED = 'UNCHECKED'; -export const INDETERMINATE = 'INDETERMINATE'; -export const CHECKED = 'CHECKED'; - -// ---------------------------------------------------------------------------- -// Class - -export class AggregationBranch extends React.Component { - constructor(props) { - super(props); - - this.state = { hasChildren: props.hasChildren }; - - bindAll(this, ['_decideClickAction', '_toggleChildDisplay']); - } - - _decideClickAction() { - const { activeChildren, item, subitems, filters, fieldName, checkedState } = - this.props; - - const values = getAllFilters(item.key, subitems); - // Add the active filters (that might be hidden) - activeChildren.forEach((child) => values.add(child)); - - if (checkedState === CHECKED) { - this.props.uncheckParent(fieldName, [...values]); - } else { - this.props.checkParent({ fieldName, filters, item }); - } - } - - _toggleChildDisplay() { - this.setState({ - hasChildren: !this.state.hasChildren, - }); - } - - render() { - const { item, subitems, fieldName, checkedState } = this.props; - - // Fix up the subitems to prepend the current item key - const buckets = subitems.map((sub) => ({ - disabled: item.isDisabled, - key: slugify(item.key, sub.key), - value: sub.key, - // eslint-disable-next-line camelcase - doc_count: sub.doc_count, - })); - - // Special returns - if (buckets.length === 0) { - return ( - - ); - } - - const liStyle = 'parent m-form-field m-form-field--checkbox body-copy'; - const id = sanitizeHtmlId(fieldName + '-' + item.key); - - let chevronIcon; - if (this.state.hasChildren) { - chevronIcon = getIcon('up'); - } else { - chevronIcon = getIcon('down'); - } - - return ( - <> -
  • - - - - - - -
  • - {this.state.hasChildren === false ? null : ( -
      - {buckets.map((bucket) => ( - - ))} -
    - )} - - ); - } - - // -------------------------------------------------------------------------- - // Properties - - get _labelStyle() { - let str = 'toggle a-label'; - if (this.props.checkedState === INDETERMINATE) { - str += ' indeterminate'; - } - - return str; - } -} - -export const mapStateToProps = (state, ownProps) => { - // Find all query filters that refer to the field name - const candidates = coalesce(state.query, ownProps.fieldName, []); - - // Do any of these values start with the key? - const hasKey = candidates.filter( - (candidate) => candidate.indexOf(ownProps.item.key) === 0, - ); - - // Does the key contain the separator? - const activeChildren = hasKey.filter( - (key) => key.indexOf(SLUG_SEPARATOR) !== -1, - ); - const activeParent = hasKey.filter((key) => key === ownProps.item.key); - - let checkedState = UNCHECKED; - if (activeParent.length === 0 && activeChildren.length > 0) { - checkedState = INDETERMINATE; - } else if (activeParent.length > 0) { - checkedState = CHECKED; - } - - return { - activeChildren, - checkedState, - filters: candidates, - focus: state.query.focus, - hasChildren: activeChildren.length > 0, - }; -}; - -export const mapDispatchToProps = (dispatch) => ({ - uncheckParent: (fieldName, values) => { - dispatch(removeMultipleFilters(fieldName, values)); - }, - checkParent: (props) => { - const { fieldName, filters, item } = props; - // remove all of the child filters - const replacementFilters = filters.filter( - (filter) => filter.indexOf(item.key + SLUG_SEPARATOR) === -1, - ); - // add self/ parent filter - replacementFilters.push(item.key); - dispatch(replaceFilters(fieldName, replacementFilters)); - }, -}); - -// eslint-disable-next-line react-redux/prefer-separate-component-file -export default connect(mapStateToProps, mapDispatchToProps)(AggregationBranch); - -AggregationBranch.propTypes = { - activeChildren: PropTypes.array, - checkParent: PropTypes.func.isRequired, - checkedState: PropTypes.string, - fieldName: PropTypes.string.isRequired, - item: PropTypes.shape({ - // eslint-disable-next-line camelcase - doc_count: PropTypes.number.isRequired, - key: PropTypes.string.isRequired, - value: PropTypes.string, - isDisabled: PropTypes.bool, - }).isRequired, - hasChildren: PropTypes.bool, - subitems: PropTypes.array.isRequired, - uncheckParent: PropTypes.func.isRequired, - filters: PropTypes.array.isRequired, -}; - -AggregationBranch.defaultProps = { - checkedState: UNCHECKED, - hasChildren: false, -}; diff --git a/src/components/Filters/CollapsibleFilter/CollapsibleFilter.spec.js b/src/components/Filters/CollapsibleFilter/CollapsibleFilter.spec.js index 589f85fc..17c5bf71 100644 --- a/src/components/Filters/CollapsibleFilter/CollapsibleFilter.spec.js +++ b/src/components/Filters/CollapsibleFilter/CollapsibleFilter.spec.js @@ -45,13 +45,13 @@ describe('component::CollapsibleFilter', () => { test('shows children when Show button is clicked', async () => { renderComponent(props); - await user.click(screen.getByRole('button')); - await user.click(screen.getByRole('button')); + const buttonBefore = screen.getByRole('button'); + await user.click(buttonBefore); + await user.click(buttonBefore); const buttonAfter = screen.getByRole('button', { expanded: true, }); - expect(buttonAfter).toBeInTheDocument(); expect(screen.getByText(props.desc)).toBeInTheDocument(); expect(screen.getByText('test child element')).toBeInTheDocument(); diff --git a/src/components/Filters/Issue.js b/src/components/Filters/Issue.js index 2837f0d9..03fffcfb 100644 --- a/src/components/Filters/Issue.js +++ b/src/components/Filters/Issue.js @@ -8,7 +8,7 @@ import { selectQueryState } from '../../reducers/query/selectors'; import { Typeahead } from '../Typeahead/Typeahead/Typeahead'; import { selectAggsState } from '../../reducers/aggs/selectors'; import MoreOrLess from './MoreOrLess/MoreOrLess'; -import AggregationBranch from './AggregationBranch'; +import { AggregationBranch } from './Aggregation/AggregationBranch/AggregationBranch'; export const Issue = () => { const dispatch = useDispatch(); diff --git a/src/components/Filters/MoreOrLess/MoreOrLess.spec.js b/src/components/Filters/MoreOrLess/MoreOrLess.spec.js index e94c8901..145ec9d3 100644 --- a/src/components/Filters/MoreOrLess/MoreOrLess.spec.js +++ b/src/components/Filters/MoreOrLess/MoreOrLess.spec.js @@ -1,7 +1,7 @@ import { testRender as render, screen } from '../../../testUtils/test-utils'; import userEvent from '@testing-library/user-event'; import MoreOrLess from './MoreOrLess'; -import AggregationItem from '../AggregationItem/AggregationItem'; +import AggregationItem from '../Aggregation/AggregationItem/AggregationItem'; const fixture = [ { key: 'alpha', doc_count: 99 }, diff --git a/src/components/Filters/Product.js b/src/components/Filters/Product.js index 347be322..ff78f2f8 100644 --- a/src/components/Filters/Product.js +++ b/src/components/Filters/Product.js @@ -1,5 +1,6 @@ import { MODE_TRENDS, SLUG_SEPARATOR } from '../../constants'; -import AggregationBranch from './AggregationBranch'; +import { AggregationBranch } from './Aggregation/AggregationBranch/AggregationBranch'; + import CollapsibleFilter from './CollapsibleFilter/CollapsibleFilter'; import { useSelector } from 'react-redux'; import { sortSelThenCount } from '../../utils'; diff --git a/src/components/Filters/SimpleFilter/SimpleFilter.js b/src/components/Filters/SimpleFilter/SimpleFilter.js index e83f6555..254ff88e 100644 --- a/src/components/Filters/SimpleFilter/SimpleFilter.js +++ b/src/components/Filters/SimpleFilter/SimpleFilter.js @@ -4,8 +4,9 @@ import { selectAggsState } from '../../../reducers/aggs/selectors'; import { coalesce } from '../../../utils'; import CollapsibleFilter from '../CollapsibleFilter/CollapsibleFilter'; import MoreOrLess from '../MoreOrLess/MoreOrLess'; -import AggregationItem from '../AggregationItem/AggregationItem'; -import '../Aggregation.less'; +import AggregationItem from '../Aggregation/AggregationItem/AggregationItem'; + +import '../Aggregation/Aggregation.less'; const SimpleFilter = ({ fieldName, title, desc }) => { const aggs = useSelector(selectAggsState); diff --git a/src/components/Filters/StickyOptions/StickyOptions.js b/src/components/Filters/StickyOptions/StickyOptions.js index f7fa9d14..aeba5a68 100644 --- a/src/components/Filters/StickyOptions/StickyOptions.js +++ b/src/components/Filters/StickyOptions/StickyOptions.js @@ -1,6 +1,6 @@ import { useEffect, useState } from 'react'; import PropTypes from 'prop-types'; -import AggregationItem from '../AggregationItem/AggregationItem'; +import AggregationItem from '../Aggregation/AggregationItem/AggregationItem'; import { isEqual } from '../../../utils/compare'; const mapOfOptions = (options) => { return options.reduce((map, opt) => { diff --git a/src/components/Filters/__tests__/AggregationBranch.spec.js b/src/components/Filters/__tests__/AggregationBranch.spec.js deleted file mode 100644 index 9f0ecf5a..00000000 --- a/src/components/Filters/__tests__/AggregationBranch.spec.js +++ /dev/null @@ -1,191 +0,0 @@ -import React from 'react'; -import { IntlProvider } from 'react-intl'; -import { Provider } from 'react-redux'; -//import { shallow } from 'enzyme'; -import configureMockStore from 'redux-mock-store'; -import thunk from 'redux-thunk'; -import renderer from 'react-test-renderer'; -import ReduxAggregationBranch /*, { - AggregationBranch, - CHECKED, - mapDispatchToProps, - UNCHECKED, -} */ from '../AggregationBranch'; -import { slugify } from '../../../utils'; - -// ---------------------------------------------------------------------------- -// Setup - -const item = { - key: 'foo', - doc_count: 99, -}; - -const subitems = [ - { key: 'bar', doc_count: 90 }, - { key: 'baz', doc_count: 5 }, - { key: 'qaz', doc_count: 4 }, -]; - -// /** -// * -// * @param {string} checkedState - The checked state -// * @returns {object} - Props and the component -// */ -// function setupEnzyme(checkedState = UNCHECKED) { -// const props = { -// activeChildren: [ -// // not hidden -// slugify('foo', 'bar'), -// // hidden -// slugify('foo', 'quux'), -// ], -// checkedState, -// checkParent: jest.fn(), -// fieldName: 'issue', -// filters: [], -// item: item, -// subitems: subitems, -// uncheckParent: jest.fn(), -// }; - -// const target = shallow(); - -// return { -// props, -// target, -// }; -// } - -/** - * - * @param {Array} selections - Array of selections - * @returns {Function} - Rendering function - */ -function setupSnapshot(selections) { - const middlewares = [thunk]; - const mockStore = configureMockStore(middlewares); - const store = mockStore({ - query: { - issue: selections, - }, - }); - - return renderer.create( - - - - - , - ); -} - -// ---------------------------------------------------------------------------- -// Test - -describe('component::AggregationBranch', () => { - describe('snapshots', () => { - it('renders with all checked', () => { - const selections = [ - 'foo', - slugify('foo', 'bar'), - slugify('foo', 'baz'), - slugify('foo', 'qaz'), - ]; - const target = setupSnapshot(selections); - const tree = target.toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('renders with indeterminate', () => { - const selections = [slugify('foo', 'bar')]; - const target = setupSnapshot(selections); - const tree = target.toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it('renders with none checked', () => { - const target = setupSnapshot([]); - const tree = target.toJSON(); - expect(tree).toMatchSnapshot(); - }); - }); - // TODO: rewrite these tests with testing library - /*describe('toggle behavior', () => { - it('shows the children when the label is clicked', () => { - const { target } = setupEnzyme(); - const theButton = target.find('button'); - - expect(target.state('hasChildren')).toEqual(false); - theButton.simulate('click'); - expect(target.state('hasChildren')).toEqual(true); - }); - }); - - describe('parent checkbox logic', () => { - it('calls one action when the checkbox is already selected', () => { - const { target, props } = setupEnzyme(CHECKED); - const checkbox = target.find('li.parent input[type="checkbox"]'); - checkbox.simulate('change'); - expect(props.uncheckParent).toHaveBeenCalledWith('issue', [ - 'foo', - 'foo•bar', - 'foo•baz', - 'foo•qaz', - 'foo•quux', - ]); - expect(props.checkParent).not.toHaveBeenCalled(); - }); - - it('calls another action when the checkbox is not selected', () => { - const { target, props } = setupEnzyme(); - const checkbox = target.find('li.parent input[type="checkbox"]'); - checkbox.simulate('change'); - expect(props.uncheckParent).not.toHaveBeenCalled(); - expect(props.checkParent).toHaveBeenCalledWith({ - fieldName: 'issue', - filters: [], - item: { doc_count: 99, key: 'foo' }, - }); - }); - }); - - describe('mapDispatchToProps', () => { - it('hooks into replaceFilters', () => { - const dispatch = jest.fn(); - mapDispatchToProps(dispatch).checkParent({ - fieldName: 'foo', - filters: [ - slugify('bay', 'bee'), - slugify('bay', 'ah'), - 'another filter', - ], - item: { key: 'bay' }, - values: ['bar', 'baz'], - }); - expect(dispatch.mock.calls).toEqual([ - [ - { - filterName: 'foo', - requery: 'REQUERY_ALWAYS', - type: 'FILTER_REPLACED', - values: ['another filter', 'bay'], - }, - ], - ]); - }); - - it('hooks into removeMultipleFilters', () => { - const dispatch = jest.fn(); - mapDispatchToProps(dispatch).uncheckParent({ - fieldName: 'foo', - values: ['bar', 'baz'], - }); - expect(dispatch.mock.calls.length).toEqual(1); - }); - });*/ -}); diff --git a/src/components/Filters/__tests__/__snapshots__/AggregationBranch.spec.js.snap b/src/components/Filters/__tests__/__snapshots__/AggregationBranch.spec.js.snap deleted file mode 100644 index 98317002..00000000 --- a/src/components/Filters/__tests__/__snapshots__/AggregationBranch.spec.js.snap +++ /dev/null @@ -1,274 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`component::AggregationBranch snapshots renders with all checked 1`] = ` -Array [ -
  • - - - - - 99 - -
  • , -
      -
    • - - - - 90 - -
    • -
    • - - - - 5 - -
    • -
    • - - - - 4 - -
    • -
    , -] -`; - -exports[`component::AggregationBranch snapshots renders with indeterminate 1`] = ` -Array [ -
  • - - - - - 99 - -
  • , -
      -
    • - - - - 90 - -
    • -
    • - - - - 5 - -
    • -
    • - - - - 4 - -
    • -
    , -] -`; - -exports[`component::AggregationBranch snapshots renders with none checked 1`] = ` -
  • - - - - - 99 - -
  • -`; diff --git a/src/reducers/query/query.js b/src/reducers/query/query.js index 05207843..8286f503 100644 --- a/src/reducers/query/query.js +++ b/src/reducers/query/query.js @@ -640,17 +640,19 @@ function replaceFilters(state, action) { */ function removeMultipleFilters(state, action) { const newState = { ...state }; - const obj = newState[action.filterName]; + // remove the focus if it exists in one of the filter values we are removing newState.focus = action.values.includes(state.focus) ? '' : state.focus || ''; + let obj = newState[action.filterName]; if (obj) { action.values.forEach((val) => { const idx = obj.indexOf(val); if (idx !== -1) { - obj.splice(idx, 1); + obj = [...obj.slice(0, idx), ...obj.slice(idx + 1)]; } }); + newState[action.filterName] = obj; } return newState;