Skip to content

Commit 9b20c5f

Browse files
authored
Merge pull request #5360 from fedspendingtransparency/fix/dev-14944
Fix/dev 14944: Advanced Search - Active filter is not removed for Custom dates
2 parents 68405f2 + b15088d commit 9b20c5f

10 files changed

Lines changed: 64 additions & 95 deletions

File tree

src/js/components/search/SearchSidebarSubmit.jsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const propTypes = {
2121
setShowMobileFilters: PropTypes.func
2222
};
2323

24+
2425
const SearchSidebarSubmit = ({
2526
stagedFiltersAreEmpty,
2627
requestsComplete,
@@ -33,11 +34,11 @@ const SearchSidebarSubmit = ({
3334
let title = 'Click to submit your search.';
3435
const { hash: urlHash } = getObjFromQueryParams(useLocation().search);
3536

36-
if (stagedFiltersAreEmpty) {
37+
if (stagedFiltersAreEmpty || !filtersChanged) {
3738
title = 'Add or update a filter to submit.';
3839
disabled = true;
3940
}
40-
else if (!requestsComplete || !filtersChanged) {
41+
else if (!requestsComplete ) {
4142
title = 'Add or update a filter to submit.';
4243
disabled = true;
4344
}

src/js/components/search/filters/timePeriod/DateRange.jsx

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -225,14 +225,7 @@ const DateRange = (props) => {
225225
}
226226
});
227227

228-
if (timePeriodApplied.size > 0 && !matchFound) {
229-
props.updateFilter({
230-
dateType: 'dr',
231-
startDate: startValue,
232-
endDate: endValue
233-
});
234-
}
235-
else if (!matchFound && !matchFoundApplied) {
228+
if (!matchFound && (timePeriodApplied?.size > 0 || !matchFoundApplied)) {
236229
props.updateFilter({
237230
dateType: 'dr',
238231
startDate: startValue,

src/js/components/search/filters/timePeriod/TimePeriod.jsx

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Created by Emily Gullo 11/03/2016
44
**/
55

6-
import React, { useEffect, useState } from 'react';
6+
import React, { useCallback, useEffect, useState } from 'react';
77
import PropTypes from 'prop-types';
88
import { Set } from 'immutable';
99

@@ -129,7 +129,7 @@ const TimePeriod = ({
129129
}
130130
};
131131

132-
const handleDateChange = (date, dateType) => {
132+
const handleDateChange = useCallback((date, dateType) => {
133133
// the component will hold values of the start/end dates for use by the UI only
134134
// this is because the start/end range will be incomplete during the time the user has only
135135
// picked one date, or if they have picked an invalid range
@@ -153,42 +153,37 @@ const TimePeriod = ({
153153
break;
154154
// no default
155155
}
156-
};
157-
158-
const removeDateRange = (newValue) => {
159-
updateGenericFilter({
160-
type: 'timePeriodType',
161-
value: 'dr'
162-
});
156+
}, []);
163157

158+
const removeDateRange = useCallback((newValue) => {
164159
setDateRangeChipRemoved(true);
165160
setStartDateUI(null);
166161
setEndDateUI(null);
167162
setStartDateDropdown(null);
168163
setEndDateDropdown(null);
169164

170-
if (activeTab === 'dr') {
171-
updateGenericFilter({
172-
type: 'timePeriodType',
173-
value: 'dr'
174-
});
175-
updateGenericFilter({
176-
type: 'time_period',
177-
value: newValue
178-
});
179-
}
180-
};
165+
const newDRValue = newValue?.size ? newValue : new Set();
166+
167+
updateGenericFilter({
168+
type: 'time_period',
169+
value: newDRValue
170+
});
171+
updateGenericFilter({
172+
type: 'timePeriodType',
173+
value: activeTab
174+
});
175+
}, [activeTab, updateGenericFilter]);
181176

182-
const showErrorFunc = (error, message) => {
177+
const showErrorFunc = useCallback((error, message) => {
183178
setShowError(true);
184179
setHeader(error);
185180
setErrorMessage(message);
186-
};
181+
}, []);
187182

188-
const hideError = () => {
183+
const hideError = useCallback(() => {
189184
setShowError(false);
190185
setHeader('');
191-
};
186+
}, []);
192187

193188
let errorDetails;
194189
let showFilter;

src/js/components/search/topFilterBar/filterGroups/TimePeriodDRFilterGroup.jsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import React, { useEffect } from 'react';
77
import { useDispatch, useSelector } from "react-redux";
88
import PropTypes from "prop-types";
9+
import { Set } from 'immutable';
910

1011
import { updateGenericFilter } from "redux/actions/search/searchFilterActions";
1112
import { dateRangeChipLabel } from "helpers/searchHelper";
@@ -33,14 +34,15 @@ const TimePeriodDRFilterGroup = ({ name, resultsView }) => {
3334

3435
if (!staged) newValue = newValue.add({ end_date: endDate, start_date: startDate });
3536

36-
dispatch(updateGenericFilter({
37-
type: 'timePeriodType',
38-
value: 'dr'
39-
}));
37+
const newDRValue = newValue?.size >= 1 ? newValue : new Set();
4038
dispatch(updateGenericFilter({
4139
type: 'time_period',
42-
value: newValue
40+
value: newDRValue
4341
}));
42+
dispatch(updateGenericFilter({
43+
type: 'timePeriodType',
44+
value: 'dr'
45+
}));
4446
};
4547

4648
const filters = {

src/js/components/search/topFilterBar/filterGroups/TimePeriodFYFilterGroup.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import React, { useEffect } from 'react';
77
import PropTypes from 'prop-types';
88
import { useDispatch, useSelector } from "react-redux";
9+
import { Set } from 'immutable';
910

1011
import { updateTimePeriod } from "redux/actions/search/searchFilterActions";
1112
import BaseTopFilterGroup from '../BaseTopFilterGroup';
@@ -23,8 +24,9 @@ const TimePeriodFYFilterGroup = ({ name, resultsView }) => {
2324
timePeriodFY.delete(value) :
2425
timePeriodFY.add(value);
2526

27+
const newStateValue = newValue?.size ? newValue : new Set();
2628
dispatch(updateTimePeriod({
27-
fy: newValue,
29+
fy: newStateValue,
2830
dateType: 'fy'
2931
}));
3032
};

src/js/containers/search/SearchSidebarSubmitContainer.jsx

Lines changed: 4 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,19 @@
33
* Created by Kevin Li 12/21/17
44
*/
55

6-
import React, { useCallback, useEffect, useState } from 'react';
6+
import React, { useCallback } from 'react';
77
import PropTypes from 'prop-types';
88
import { useDispatch, useSelector } from 'react-redux';
99

1010
import { areFiltersEqual } from 'helpers/searchHelper';
1111
import SearchSidebarSubmit from 'components/search/SearchSidebarSubmit';
12-
import { initialState, initialStateFY } from 'redux/reducers/search/searchFiltersReducer';
12+
import { initialState, initialStateDR } from 'redux/reducers/search/searchFiltersReducer';
1313
import { clearAllFilters as clearStagedFilters } from 'redux/actions/search/searchFilterActions';
1414
import { resetMapLegendToggle } from 'redux/actions/search/mapLegendToggleActions';
1515
import {
1616
applyStagedFilters,
1717
resetAppliedFilters, setAppliedFilterCompletion
1818
} from "redux/actions/search/appliedFilterActions";
19-
import usePrevious from "hooks/usePrevious";
2019
import {
2120
convertFiltersToAnalyticEvents,
2221
sendAnalyticEvents,
@@ -32,38 +31,17 @@ const SearchSidebarSubmitContainer = ({ setShowMobileFilters }) => {
3231
} = useSelector((state) => state.appliedFilters);
3332
const stagedFilters = useSelector((state) => state.filters);
3433
const dispatch = useDispatch();
35-
const [filtersChanged, setFiltersChanged] = useState(false);
36-
const prevStagedFilters = usePrevious(stagedFilters);
37-
const prevAppliedFilters = usePrevious(appliedFilters);
3834

35+
const filtersChanged = !areFiltersEqual(stagedFilters, appliedFilters);
3936
const areStagedFiltersEmpty = areFiltersEqual(stagedFilters, initialState) ||
40-
areFiltersEqual(stagedFilters, initialStateFY);
41-
42-
const compareStores = () => {
43-
// we need to do a deep equality check by comparing every store key
44-
const storeKeys = Object.keys(stagedFilters);
45-
if (storeKeys.length !== Object.keys(appliedFilters).length) {
46-
// key lengths do not match, there's a difference so fail immediately
47-
return false;
48-
}
49-
return areFiltersEqual(stagedFilters, appliedFilters);
50-
};
37+
areFiltersEqual(stagedFilters, initialStateDR);
5138

5239
const resetFilters = useCallback(() => {
5340
dispatch(clearStagedFilters());
5441
dispatch(resetAppliedFilters());
5542
dispatch(resetMapLegendToggle());
5643
}, [dispatch]);
5744

58-
const stagingChanged = () => {
59-
// do a deep equality check between the staged filters and applied filters
60-
if (!compareStores()) {
61-
setFiltersChanged(true);
62-
}
63-
else if (filtersChanged) {
64-
setFiltersChanged(false);
65-
}
66-
};
6745

6846
const applyFilters = useCallback(() => {
6947
dispatch(setAppliedFilterCompletion(false));
@@ -75,24 +53,12 @@ const SearchSidebarSubmitContainer = ({ setShowMobileFilters }) => {
7553
dispatch(applyStagedFilters(stagedFilters));
7654
dispatch(setAppliedFilterCompletion(true));
7755
}
78-
setFiltersChanged(false);
7956

8057
const events = convertFiltersToAnalyticEvents(stagedFilters);
8158
sendAnalyticEvents(events);
8259
sendFieldCombinations(events);
8360
}, [dispatch, resetFilters, stagedFilters]);
8461

85-
useEffect(() => {
86-
const areStagedAndAppliedFiltersEquivalent = (
87-
areFiltersEqual(stagedFilters, prevStagedFilters) &&
88-
areFiltersEqual(appliedFilters, prevAppliedFilters)
89-
);
90-
if (!areStagedAndAppliedFiltersEquivalent) {
91-
stagingChanged();
92-
}
93-
// eslint-disable-next-line react-hooks/exhaustive-deps
94-
}, [stagedFilters, appliedFilters]);
95-
9662
return (
9763
<SearchSidebarSubmit
9864
stagedFiltersAreEmpty={areStagedFiltersEmpty}

src/js/containers/search/filters/TimePeriodContainer.jsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Created by Kevin Li 11/21/16
44
**/
55

6-
import React, { useState, useEffect } from 'react';
6+
import React, { useState, useEffect, useCallback } from 'react';
77
import PropTypes from 'prop-types';
88
import { bindActionCreators } from 'redux';
99
import { connect } from 'react-redux';
@@ -50,11 +50,11 @@ const TimePeriodContainer = (props) => {
5050
setTimePeriods(timePeriodArr);
5151
};
5252

53-
const changeTab = (tab) => {
53+
const changeTab = useCallback((tab) => {
5454
setActiveTab(tab);
55-
};
55+
}, []);
5656

57-
const updateFilter = (params) => {
57+
const updateFilter = useCallback((params) => {
5858
const newFilters = Object.assign({}, params);
5959

6060
if (activeTab === 'fy') {
@@ -67,7 +67,7 @@ const TimePeriodContainer = (props) => {
6767
newFilters.fy = [];
6868
props.updateTimePeriodArray(newFilters);
6969
}
70-
};
70+
}, [activeTab]);
7171

7272
const dirtyFilters = () => {
7373
const appliedFields = [

src/js/helpers/searchHelper.js

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Created by Kevin Li 11/2/16
44
**/
55

6-
import { is } from 'immutable';
6+
import { is as immutableIs, Iterable } from 'immutable';
77
import { isEqual, sortBy } from 'lodash-es';
88
import dayjs from "dayjs";
99
import { initialState } from 'redux/reducers/search/searchFiltersReducer';
@@ -182,12 +182,20 @@ export const fetchLastUpdate = () => apiRequest({
182182
url: 'v2/awards/last_updated/'
183183
});
184184

185+
// eslint-disable-next-line max-len
185186
const areCheckboxSelectionsEqual = ({ exclude: exclude1, require: require1 }, { exclude: exclude2, require: require2 }) => {
186187
if (!isEqual(sortBy(require1), sortBy(require2))) return false;
187188
if (!isEqual(sortBy(exclude1), sortBy(exclude2))) return false;
188189
return true;
189190
};
190191

192+
const valuesAreEqual = (a, b) => {
193+
if(Iterable.isIterable(a) || Iterable.isIterable(b)) {
194+
return immutableIs(a, b);
195+
}
196+
197+
return isEqual(a, b);
198+
}
191199
/**
192200
* Equality Comparison of two objects:
193201
* @param {Object} filters object to be measured for equality
@@ -196,8 +204,10 @@ const areCheckboxSelectionsEqual = ({ exclude: exclude1, require: require1 }, {
196204
*/
197205
export const areFiltersEqual = (filters = initialState, filterReference = initialState) => {
198206
if (!filterReference && filters) return false;
199-
const referenceObject = Object.assign({}, filterReference);
200-
const comparisonObject = Object.assign({}, filters);
207+
208+
const referenceObject = {...filterReference};
209+
const comparisonObject = {...filters};
210+
201211
if (referenceObject.timePeriodType === 'fy') {
202212
// if the time period is fiscal year, we don't care about the date range values, even
203213
// if they're provided because the date range tab isn't selected
@@ -209,26 +219,26 @@ export const areFiltersEqual = (filters = initialState, filterReference = initia
209219
delete referenceObject.filterNaoActiveFromFyOrDateRange;
210220
}
211221
else if (referenceObject.timePeriodType === 'dr') {
212-
// if the time period is date range, we don't care about the fiscal year values, even
213-
// if they're provided because the fiscal year tab isn't selected
222+
// if the time period is date range, we don't care about the fiscal year values, even
223+
// if they're provided because the fiscal year tab isn't selected
214224
delete comparisonObject.timePeriodFY;
215225
delete referenceObject.timePeriodFY;
216226
delete comparisonObject.filterNewAwardsOnlyActive;
217227
delete referenceObject.filterNewAwardsOnlyActive;
218228
delete comparisonObject.filterNaoActiveFromFyOrDateRange;
219229
delete referenceObject.filterNaoActiveFromFyOrDateRange;
220230
}
231+
221232
// we need to iterate through each of the filter Redux keys in order to perform equality
222233
// comparisons on Immutable children (via the Immutable is() function)
223234
const immutableFilterKeys = Object
224235
.keys(comparisonObject)
225236
.filter((k) => !checkboxTreeFilters.includes(k));
226237

227-
for (const value of immutableFilterKeys) {
228-
const key = value;
238+
for (const key of immutableFilterKeys) {
229239
const unfilteredValue = comparisonObject[key];
230240
const currentValue = referenceObject[key];
231-
if (!is(unfilteredValue, currentValue)) {
241+
if (!valuesAreEqual(unfilteredValue, currentValue)) {
232242
return false;
233243
}
234244
}

src/js/redux/reducers/search/searchFiltersReducer.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,9 @@ export const initialState = {
8282
awardDescription: ''
8383
};
8484

85-
export const initialStateFY = {
85+
export const initialStateDR = {
8686
keyword: OrderedMap(),
87-
timePeriodType: 'fy',
87+
timePeriodType: 'dr',
8888
timePeriodFY: Set(),
8989
time_period: Set(),
9090
filterNewAwardsOnlySelected: false,

tests/helpers/searchHelper-test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
/**
22
* @jest-environment jsdom
33
*/
4-
import { initialState, initialStateFY, CheckboxTreeSelections } from 'redux/reducers/search/searchFiltersReducer';
4+
import { initialState, CheckboxTreeSelections } from 'redux/reducers/search/searchFiltersReducer';
55
import { areFiltersEqual, isSearchHashReady } from 'helpers/searchHelper';
66

77
test.each([
88
['obj1 & obj2 are both initial state', true, initialState, initialState],
9-
['obj1 is initial state and obj2 has timePeriodFY changed', false, initialStateFY, { ...initialState, timePeriodFY: new Set(['2020']) }],
9+
['obj1 is initial state and obj2 has timePeriodFY changed', false, initialState, { ...initialState, timePeriodFY: new Set(['2020']) }],
1010
['empty vs defined def codes', false, initialState, {
1111
...initialState,
1212
defCodes: CheckboxTreeSelections({ require: ['12'], counts: [], exclude: [] })

0 commit comments

Comments
 (0)