Skip to content

Commit

Permalink
adding suggest typeahead and sub-agg search for product and issue
Browse files Browse the repository at this point in the history
update test coverage
  • Loading branch information
flacoman91 committed Dec 20, 2024
1 parent f68201f commit 8172430
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 54 deletions.
87 changes: 87 additions & 0 deletions src/components/Filters/FilterSearch/FilterSearch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import '../../Typeahead/Typeahead.scss';
import { useEffect, useRef, useState } from 'react';
import { useDispatch } from 'react-redux';
import { Typeahead } from 'react-bootstrap-typeahead';
import { filterAdded } from '../../../actions';
import PropTypes from 'prop-types';
import { useGetAggregations } from '../../../api/hooks/useGetAggregations';
import { SLUG_SEPARATOR } from '../../../constants';
import { ClearButton } from '../../Typeahead/ClearButton/ClearButton';

export const FilterSearch = ({ fieldName }) => {
const ref = useRef();
const dispatch = useDispatch();
const [inputText, setInputText] = useState('');
const fieldNameNew = fieldName.replace(/_/g, ' ');
const { data } = useGetAggregations();

const aggResults = data[fieldName] || [];
const subaggName = `sub_${fieldName}.raw`.toLowerCase();
const buckets = [];
aggResults.forEach((option) => {
buckets.push(option);
if (option[subaggName] && option[subaggName].buckets) {
option[subaggName].buckets.forEach((bucket) => {
const item = {
key: option.key + SLUG_SEPARATOR + bucket.key,
value: bucket.key,
};
buckets.push(item);
});
}
});

const handleClear = () => {
ref.current.clear();
setInputText('');
};

const handleSelections = (selected) => {
dispatch(filterAdded(fieldName, selected[0].key));
handleClear();
};

// give the input focus when the component renders the first time
useEffect(() => {
ref.current.focus();
}, [ref]);

return (
<section className="typeahead">
<div className="o-search-input">
<div className="o-search-input__input">
<Typeahead
id={'filter-search' + fieldName}
minLength={2}
className="typeahead-selector"
filterBy={['key']}
onChange={(selected) => handleSelections(selected)}
onInputChange={(text) => setInputText(text)}
placeholder={'Enter name of ' + fieldNameNew}
labelKey="key"
options={buckets}
ref={ref}
inputProps={{
'aria-label': `${fieldNameNew} Filter Menu Input`,
}}
renderMenuItemChildren={(option) => (
<div>
{option.key.split(SLUG_SEPARATOR)[0]}
{option.value ? (
<div>
<small>{option.value}</small>
</div>
) : null}
</div>
)}
/>
{!!inputText && <ClearButton onClear={handleClear} />}
</div>
</div>
</section>
);
};

FilterSearch.propTypes = {
fieldName: PropTypes.string.isRequired,
};
47 changes: 4 additions & 43 deletions src/components/Filters/Issue/Issue.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
import { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useSelector } from 'react-redux';
import { sortSelThenCount } from '../../../utils';
import { CollapsibleFilter } from '../CollapsibleFilter/CollapsibleFilter';
import { filtersReplaced } from '../../../reducers/filters/filtersSlice';
import { SLUG_SEPARATOR } from '../../../constants';
import { Typeahead } from '../../Typeahead/Typeahead/Typeahead';
import { selectFiltersIssue } from '../../../reducers/filters/selectors';
import { MoreOrLess } from '../MoreOrLess/MoreOrLess';
import { AggregationBranch } from '../Aggregation/AggregationBranch/AggregationBranch';
import { useGetAggregations } from '../../../api/hooks/useGetAggregations';
import { FilterSearch } from '../FilterSearch/FilterSearch';
import { SLUG_SEPARATOR } from '../../../constants';

// eslint-disable-next-line react/prop-types
export const Issue = () => {
const dispatch = useDispatch();
const [dropdownOptions, setDropdownOptions] = useState([]);
const { data } = useGetAggregations();
const filters = useSelector(selectFiltersIssue);

Expand All @@ -40,33 +36,6 @@ export const Issue = () => {
});
// Make a cloned, sorted version of the aggs
const options = sortSelThenCount(aggsFilters, selections);
// create an array optimized for typeahead
const optionKeys = options.map((opt) => opt.key);

const onInputChange = (value) => {
const num = value.toLowerCase();
if (num === '') {
setDropdownOptions([]);
return;
}
const options = optionKeys.map((opt) => ({
key: opt,
label: opt,
position: opt.toLowerCase().indexOf(num),
value,
}));
setDropdownOptions(options);
};

const onSelection = (items) => {
const replacementFilters = filters
// remove child items
.filter((filter) => filter.indexOf(items[0].key + SLUG_SEPARATOR) === -1)
// add parent item
.concat(items[0].key);
dispatch(filtersReplaced('issue', replacementFilters));
};

const onBucket = (bucket, props) => {
props.subitems = bucket['sub_issue.raw'].buckets;
return props;
Expand All @@ -78,15 +47,7 @@ export const Issue = () => {
desc={desc}
className="aggregation issue"
>
<Typeahead
ariaLabel="Start typing to begin listing issues"
htmlId="issue-typeahead"
placeholder="Enter name of issue"
handleChange={onSelection}
handleInputChange={onInputChange}
hasClearButton={true}
options={dropdownOptions}
/>
<FilterSearch fieldName="issue" />
<MoreOrLess
listComponent={AggregationBranch}
listComponentProps={listComponentProps}
Expand Down
21 changes: 10 additions & 11 deletions src/components/Filters/Issue/Issue.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import fetchMock from 'jest-fetch-mock';
import userEvent from '@testing-library/user-event';
import { merge } from '../../../testUtils/functionHelpers';
import { Issue } from './Issue';
import { listOfIssues } from '../../../testUtils/aggsConstants';
import * as filterActions from '../../../reducers/filters/filtersSlice';
import { filtersState } from '../../../reducers/filters/filtersSlice';
import { aggResponse } from './fixture';
Expand All @@ -32,8 +31,8 @@ describe('Issue', () => {
});

test('Options appear when user types and dispatches filtersReplaced on selection', async () => {
const filtersReplacedSpy = jest
.spyOn(filterActions, 'filtersReplaced')
const filterAddedSpy = jest
.spyOn(filterActions, 'filterAdded')
.mockImplementation(() => jest.fn());
fetchMock.mockResponseOnce(JSON.stringify(aggResponse));
renderComponent();
Expand All @@ -43,15 +42,15 @@ describe('Issue', () => {
const input = screen.getByPlaceholderText('Enter name of issue');
await user.type(input, 'Improper');
const option = await screen.findByRole('option', {
name: /Improper use of your report/,
name: 'Improper use of your report•Reporting company used your report improperly',
});
await user.click(option);

await waitFor(() =>
expect(filtersReplacedSpy).toBeCalledWith('issue', [
'Incorrect information on your report',
listOfIssues[0].key,
]),
expect(filterAddedSpy).toBeCalledWith(
'issue',
'Improper use of your report•Reporting company used your report improperly',
),
);
});

Expand All @@ -78,10 +77,10 @@ describe('Issue', () => {
const input = screen.getByPlaceholderText('Enter name of issue');
await user.type(input, 'Improper');
const option = await screen.findByRole('option', {
name: /Improper use of your report/,
name: 'Improper use of your report•Reporting company used your report improperly',
});
await user.clear(input);

await user.clear(screen.getByPlaceholderText('Enter name of issue'));
expect(input).toHaveValue('');
expect(option).not.toBeInTheDocument();
});
});
2 changes: 2 additions & 0 deletions src/components/Filters/Product/Product.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
import { selectFiltersProduct } from '../../../reducers/filters/selectors';
import { selectViewTab } from '../../../reducers/view/selectors';
import { useGetAggregations } from '../../../api/hooks/useGetAggregations';
import { FilterSearch } from '../FilterSearch/FilterSearch';

/**
* Helper function generate and sort options
Expand Down Expand Up @@ -98,6 +99,7 @@ export const Product = () => {
desc={desc}
className="aggregation product"
>
<FilterSearch fieldName="product" />
<MoreOrLess
listComponent={AggregationBranch}
listComponentProps={listComponentProps}
Expand Down

0 comments on commit 8172430

Please sign in to comment.