Skip to content

Commit 47f89a6

Browse files
111731: Moved common remove/add queryParam logic for facet option/label to routeService
1 parent dca62d6 commit 47f89a6

24 files changed

Lines changed: 438 additions & 418 deletions

src/app/core/services/route.service.spec.ts

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import { RouteService } from './route.service';
99
import { RouterMock } from '../../shared/mocks/router.mock';
1010
import { TestScheduler } from 'rxjs/testing';
1111
import { AddUrlToHistoryAction } from '../history/history.actions';
12+
import { ActivatedRouteStub } from 'src/app/shared/testing/active-router.stub';
13+
import { take } from 'rxjs/operators';
1214

1315
describe('RouteService', () => {
1416
let scheduler: TestScheduler;
@@ -29,23 +31,19 @@ describe('RouteService', () => {
2931
select: jasmine.createSpy('select')
3032
});
3133

34+
let route: ActivatedRouteStub;
3235
const router = new RouterMock();
3336
router.setParams(convertToParamMap(paramObject));
3437

3538
paramObject[paramName1] = paramValue1;
3639
paramObject[paramName2] = [paramValue2a, paramValue2b];
3740

3841
beforeEach(waitForAsync(() => {
42+
route = new ActivatedRouteStub(paramObject);
43+
3944
return TestBed.configureTestingModule({
4045
providers: [
41-
{
42-
provide: ActivatedRoute,
43-
useValue: {
44-
queryParams: observableOf(paramObject),
45-
params: observableOf(paramObject),
46-
queryParamMap: observableOf(convertToParamMap(paramObject))
47-
},
48-
},
46+
{ provide: ActivatedRoute, useValue: route },
4947
{ provide: Router, useValue: router },
5048
{ provide: Store, useValue: store },
5149
]
@@ -181,4 +179,39 @@ describe('RouteService', () => {
181179
});
182180
});
183181
});
182+
183+
describe('getParamsWithoutAppliedFilter', () => {
184+
beforeEach(() => {
185+
route.testParams = {
186+
'query': '',
187+
'spc.page': '1',
188+
'f.author': '1282121b-5394-4689-ab93-78d537764052,authority',
189+
'f.has_content_in_original_bundle': 'true,equals',
190+
};
191+
});
192+
193+
it('should remove the parameter completely if only one value is defined', (done: DoneFn) => {
194+
service.getParamsExceptValue('f.author', '1282121b-5394-4689-ab93-78d537764052,authority').pipe(take(1)).subscribe((params: Params) => {
195+
expect(params).toEqual({
196+
'query': '',
197+
'spc.page': '1',
198+
'f.has_content_in_original_bundle': 'true,equals',
199+
});
200+
done();
201+
});
202+
});
203+
204+
it('should return all params except the applied filter even when multiple filters of the same type are selected', (done: DoneFn) => {
205+
route.testParams['f.author'] = ['1282121b-5394-4689-ab93-78d537764052,authority', '71b91a28-c280-4352-a199-bd7fc3312501,authority'];
206+
service.getParamsExceptValue('f.author', '1282121b-5394-4689-ab93-78d537764052,authority').pipe(take(1)).subscribe((params: Params) => {
207+
expect(params).toEqual({
208+
'query': '',
209+
'spc.page': '1',
210+
'f.author': ['71b91a28-c280-4352-a199-bd7fc3312501,authority'],
211+
'f.has_content_in_original_bundle': 'true,equals',
212+
});
213+
done();
214+
});
215+
});
216+
});
184217
});

src/app/core/services/route.service.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,4 +225,53 @@ export class RouteService {
225225
}
226226
);
227227
}
228+
229+
/**
230+
* Returns all the query parameters except for the one with the given name & value.
231+
*
232+
* @param name The name of the query param to exclude
233+
* @param value The optional value that the query param needs to have to be excluded
234+
*/
235+
getParamsExceptValue(name: string, value?: string): Observable<Params> {
236+
return this.route.queryParams.pipe(
237+
map((params: Params) => {
238+
const newParams: Params = Object.assign({}, params);
239+
const queryParamValues: string | string[] = newParams[name];
240+
241+
if (queryParamValues === value || value === undefined) {
242+
delete newParams[name];
243+
} else if (Array.isArray(queryParamValues) && queryParamValues.includes(value)) {
244+
newParams[name] = (queryParamValues as string[]).filter((paramValue: string) => paramValue !== value);
245+
}
246+
return newParams;
247+
}),
248+
);
249+
}
250+
251+
/**
252+
* Returns all the existing query parameters and the new value pair with the given name & value.
253+
*
254+
* @param name The name of the query param for which you need to add the value
255+
* @param value The optional value that the query param needs to have in addition to the current ones
256+
*/
257+
getParamsWithAdditionalValue(name: string, value: string): Observable<Params> {
258+
return this.route.queryParams.pipe(
259+
map((params: Params) => {
260+
const newParams: Params = Object.assign({}, params);
261+
const queryParamValues: string | string[] = newParams[name];
262+
263+
if (queryParamValues === undefined) {
264+
newParams[name] = value;
265+
} else {
266+
if (Array.isArray(queryParamValues)) {
267+
newParams[name] = [...queryParamValues, value];
268+
} else {
269+
newParams[name] = [queryParamValues, value];
270+
}
271+
}
272+
return newParams;
273+
}),
274+
);
275+
}
276+
228277
}

src/app/core/shared/search/search-configuration.service.spec.ts

Lines changed: 11 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.u
1313
import { getMockRequestService } from '../../../shared/mocks/request.service.mock';
1414
import { RequestEntry } from '../../data/request-entry.model';
1515
import { SearchObjects } from '../../../shared/search/models/search-objects.model';
16-
import { Params } from '@angular/router';
17-
import { addOperatorToFilterValue } from '../../../shared/search/search.utils';
1816
import { AppliedFilter } from '../../../shared/search/models/applied-filter.model';
1917

2018
describe('SearchConfigurationService', () => {
@@ -41,7 +39,8 @@ describe('SearchConfigurationService', () => {
4139
const routeService = jasmine.createSpyObj('RouteService', {
4240
getQueryParameterValue: observableOf(value1),
4341
getQueryParamsWithPrefix: observableOf(prefixFilter),
44-
getRouteParameterValue: observableOf('')
42+
getRouteParameterValue: observableOf(''),
43+
getParamsExceptValue: observableOf({}),
4544
});
4645

4746
const paginationService = new PaginationServiceStub();
@@ -283,7 +282,7 @@ describe('SearchConfigurationService', () => {
283282
});
284283
});
285284

286-
describe('getParamsWithoutAppliedFilter', () => {
285+
describe('unselectAppliedFilterParams', () => {
287286
let appliedFilter: AppliedFilter;
288287

289288
beforeEach(() => {
@@ -293,51 +292,18 @@ describe('SearchConfigurationService', () => {
293292
value: '1282121b-5394-4689-ab93-78d537764052',
294293
label: 'Odinson, Thor',
295294
});
296-
activatedRoute.testParams = {
297-
'query': '',
298-
'spc.page': '1',
299-
'f.author': addOperatorToFilterValue(appliedFilter.value, appliedFilter.operator),
300-
'f.has_content_in_original_bundle': addOperatorToFilterValue('true', 'equals'),
301-
'f.dateIssued.max': '2000',
302-
};
303-
});
304-
305-
it('should return all params except the applied filter', (done: DoneFn) => {
306-
service.getParamsWithoutAppliedFilter(appliedFilter.filter, appliedFilter.value, appliedFilter.operator).pipe(take(1)).subscribe((params: Params) => {
307-
expect(params).toEqual({
308-
'query': '',
309-
'spc.page': '1',
310-
'f.has_content_in_original_bundle': addOperatorToFilterValue('true', 'equals'),
311-
'f.dateIssued.max': '2000',
312-
});
313-
done();
314-
});
315295
});
316296

317-
it('should return all params except the applied filter even when multiple filters of the same type are selected', (done: DoneFn) => {
318-
activatedRoute.testParams['f.author'] = [addOperatorToFilterValue(appliedFilter.value, appliedFilter.operator), addOperatorToFilterValue('71b91a28-c280-4352-a199-bd7fc3312501', 'authority')];
319-
service.getParamsWithoutAppliedFilter(appliedFilter.filter, appliedFilter.value, appliedFilter.operator).pipe(take(1)).subscribe((params: Params) => {
320-
expect(params).toEqual({
321-
'query': '',
322-
'spc.page': '1',
323-
'f.author': [addOperatorToFilterValue('71b91a28-c280-4352-a199-bd7fc3312501', 'authority')],
324-
'f.has_content_in_original_bundle': addOperatorToFilterValue('true', 'equals'),
325-
'f.dateIssued.max': '2000',
326-
});
327-
done();
328-
});
297+
it('should return all params except the applied filter', () => {
298+
service.unselectAppliedFilterParams(appliedFilter.filter, appliedFilter.value, appliedFilter.operator);
299+
300+
expect(routeService.getParamsExceptValue).toHaveBeenCalledWith('f.author', '1282121b-5394-4689-ab93-78d537764052,authority');
329301
});
330302

331-
it('should be able to remove AppliedFilter without operator', (done: DoneFn) => {
332-
service.getParamsWithoutAppliedFilter('dateIssued.max', '2000').pipe(take(1)).subscribe((params: Params) => {
333-
expect(params).toEqual({
334-
'query': '',
335-
'spc.page': '1',
336-
'f.author': addOperatorToFilterValue(appliedFilter.value, appliedFilter.operator),
337-
'f.has_content_in_original_bundle': addOperatorToFilterValue('true', 'equals'),
338-
});
339-
done();
340-
});
303+
it('should be able to remove AppliedFilter without operator', () => {
304+
service.unselectAppliedFilterParams('dateIssued.max', '2000');
305+
306+
expect(routeService.getParamsExceptValue).toHaveBeenCalledWith('f.dateIssued.max', '2000');
341307
});
342308
});
343309
});

src/app/core/shared/search/search-configuration.service.ts

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -526,22 +526,26 @@ export class SearchConfigurationService implements OnDestroy {
526526
);
527527
}
528528

529-
getParamsWithoutAppliedFilter(filterName: string, value: string, operator?: string): Observable<Params> {
530-
return this.route.queryParams.pipe(
531-
map((params: Params) => {
532-
const newParams: Params = Object.assign({}, params);
533-
const queryParamValues: string | string[] = newParams[`f.${filterName}`];
534-
const excludeValue = hasValue(operator) ? addOperatorToFilterValue(value, operator) : value;
535-
536-
if (queryParamValues === excludeValue) {
537-
delete newParams[`f.${filterName}`];
538-
} else if (queryParamValues?.includes(excludeValue)) {
539-
newParams[`f.${filterName}`] = (queryParamValues as string[])
540-
.filter((paramValue: string) => paramValue !== excludeValue);
541-
}
542-
return newParams;
543-
}),
544-
);
529+
/**
530+
* Calculates the {@link Params} of the search after removing a filter with a certain value
531+
*
532+
* @param filterName The {@link AppliedFilter}'s name
533+
* @param value The {@link AppliedFilter}'s value
534+
* @param operator The {@link AppliedFilter}'s optional operator
535+
*/
536+
unselectAppliedFilterParams(filterName: string, value: string, operator?: string): Observable<Params> {
537+
return this.routeService.getParamsExceptValue(`f.${filterName}`, hasValue(operator) ? addOperatorToFilterValue(value, operator) : value);
538+
}
539+
540+
/**
541+
* Calculates the {@link Params} of the search after removing a filter with a certain value
542+
*
543+
* @param filterName The {@link AppliedFilter}'s name
544+
* @param value The {@link AppliedFilter}'s value
545+
* @param operator The {@link AppliedFilter}'s optional operator
546+
*/
547+
selectNewAppliedFilterParams(filterName: string, value: string, operator?: string): Observable<Params> {
548+
return this.routeService.getParamsWithAdditionalValue(`f.${filterName}`, hasValue(operator) ? addOperatorToFilterValue(value, operator) : value);
545549
}
546550

547551
/**

src/app/core/shared/search/search-filter.service.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,15 @@ export class SearchFilterService {
6363
* Fetch the current active scope from the query parameters
6464
* @returns {Observable<string>}
6565
*/
66-
getCurrentScope() {
66+
getCurrentScope(): Observable<string> {
6767
return this.routeService.getQueryParameterValue('scope');
6868
}
6969

7070
/**
7171
* Fetch the current query from the query parameters
7272
* @returns {Observable<string>}
7373
*/
74-
getCurrentQuery() {
74+
getCurrentQuery(): Observable<string> {
7575
return this.routeService.getQueryParameterValue('query');
7676
}
7777

@@ -113,15 +113,15 @@ export class SearchFilterService {
113113
* Fetch the current active filters from the query parameters
114114
* @returns {Observable<Params>}
115115
*/
116-
getCurrentFilters() {
116+
getCurrentFilters(): Observable<Params> {
117117
return this.routeService.getQueryParamsWithPrefix('f.');
118118
}
119119

120120
/**
121121
* Fetch the current view from the query parameters
122122
* @returns {Observable<string>}
123123
*/
124-
getCurrentView() {
124+
getCurrentView(): Observable<string> {
125125
return this.routeService.getQueryParameterValue('view');
126126
}
127127

src/app/shared/search/search-filters/search-filter/search-authority-filter/search-authority-filter.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<div>
22
<div class="filters py-2">
3-
<ds-search-facet-selected-option *ngFor="let value of (selectedAppliedFilters$ | async)" [selectedValue]="value" [filterConfig]="filterConfig" [selectedValues$]="selectedAppliedFilters$" [inPlaceSearch]="inPlaceSearch"></ds-search-facet-selected-option>
3+
<ds-search-facet-selected-option *ngFor="let value of (selectedAppliedFilters$ | async)" [selectedValue]="value" [filterConfig]="filterConfig" [inPlaceSearch]="inPlaceSearch"></ds-search-facet-selected-option>
44
<ng-container *ngFor="let page of (facetValues$ | async)">
55
<div [@facetLoad]="animationState">
6-
<ds-search-facet-option *ngFor="let value of page.page; trackBy: trackUpdate" [filterConfig]="filterConfig" [filterValue]="value" [selectedValues$]="selectedAppliedFilters$" [inPlaceSearch]="inPlaceSearch"></ds-search-facet-option>
6+
<ds-search-facet-option *ngFor="let value of page.page; trackBy: trackUpdate" [filterConfig]="filterConfig" [filterValue]="value" [inPlaceSearch]="inPlaceSearch"></ds-search-facet-option>
77
</div>
88
</ng-container>
99
<div class="clearfix toggle-more-filters">

src/app/shared/search/search-filters/search-filter/search-boolean-filter/search-boolean-filter.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<div>
22
<div class="filters py-2">
3-
<ds-search-facet-selected-option *ngFor="let value of (selectedAppliedFilters$ | async)" [selectedValue]="value" [filterConfig]="filterConfig" [selectedValues$]="selectedAppliedFilters$" [inPlaceSearch]="inPlaceSearch"></ds-search-facet-selected-option>
3+
<ds-search-facet-selected-option *ngFor="let value of (selectedAppliedFilters$ | async)" [selectedValue]="value" [filterConfig]="filterConfig" [inPlaceSearch]="inPlaceSearch"></ds-search-facet-selected-option>
44
<ng-container *ngFor="let page of (facetValues$ | async)">
55
<div [@facetLoad]="animationState">
6-
<ds-search-facet-option *ngFor="let value of page.page; trackBy: trackUpdate" [filterConfig]="filterConfig" [filterValue]="value" [selectedValues$]="selectedAppliedFilters$" [inPlaceSearch]="inPlaceSearch"></ds-search-facet-option>
6+
<ds-search-facet-option *ngFor="let value of page.page; trackBy: trackUpdate" [filterConfig]="filterConfig" [filterValue]="value" [inPlaceSearch]="inPlaceSearch"></ds-search-facet-option>
77
</div>
88
</ng-container>
99
<div class="clearfix toggle-more-filters">

src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<a *ngIf="isVisible | async" class="d-flex flex-row"
22
[tabIndex]="-1"
33
[routerLink]="[searchLink]"
4-
[queryParams]="addQueryParams" queryParamsHandling="merge">
4+
[queryParams]="addQueryParams$ | async">
55
<label class="mb-0">
66
<input type="checkbox" [checked]="false" class="my-1 align-self-stretch"/>
77
<span class="filter-value px-1">

0 commit comments

Comments
 (0)