diff --git a/src/components/Filters/FilterSearch/FilterSearch.js b/src/components/Filters/FilterSearch/FilterSearch.js
new file mode 100644
index 00000000..6cf04c02
--- /dev/null
+++ b/src/components/Filters/FilterSearch/FilterSearch.js
@@ -0,0 +1,96 @@
+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';
+import getIcon from '../../Common/Icon/iconMap';
+
+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 (
+
+
+
+
+
handleSelections(selected)}
+ onInputChange={(text) => setInputText(text)}
+ placeholder={'Enter name of ' + fieldNameNew}
+ labelKey="key"
+ options={buckets}
+ ref={ref}
+ inputProps={{
+ 'aria-label': `${fieldNameNew} Filter Menu Input`,
+ className: 'a-text-input a-text-input--full',
+ }}
+ renderMenuItemChildren={(option) => (
+
+ {option.key.split(SLUG_SEPARATOR)[0]}
+ {option.value ? (
+
+ {option.value}
+
+ ) : null}
+
+ )}
+ />
+ {!!inputText && }
+
+
+
+ );
+};
+
+FilterSearch.propTypes = {
+ fieldName: PropTypes.string.isRequired,
+};
diff --git a/src/components/Filters/Issue/Issue.js b/src/components/Filters/Issue/Issue.js
index 092f1746..c8a62681 100644
--- a/src/components/Filters/Issue/Issue.js
+++ b/src/components/Filters/Issue/Issue.js
@@ -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);
@@ -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;
@@ -78,15 +47,7 @@ export const Issue = () => {
desc={desc}
className="aggregation 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();
@@ -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',
+ ),
);
});
@@ -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();
});
});
diff --git a/src/components/Filters/Product/Product.js b/src/components/Filters/Product/Product.js
index 6ddac2da..ccef2216 100644
--- a/src/components/Filters/Product/Product.js
+++ b/src/components/Filters/Product/Product.js
@@ -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
@@ -98,6 +99,7 @@ export const Product = () => {
desc={desc}
className="aggregation product"
>
+