Skip to content

Commit

Permalink
Merge branch 'use-applied-filter-to-display-label-on-search_contribut…
Browse files Browse the repository at this point in the history
…e-7.4' into use-applied-filter-to-display-label-on-search_contribute-7.6

# Conflicts:
#	src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.html
#	src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.spec.ts
#	src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.html
#	src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.spec.ts
#	src/app/shared/search/search-filters/search-filter/search-range-filter/search-range-filter.component.ts
#	src/app/shared/search/search-labels/search-label/search-label.component.spec.ts
#	src/app/shared/search/search-labels/search-label/search-label.component.ts
#	src/app/shared/search/search-labels/search-labels.component.ts
#	src/app/shared/search/search-sidebar/search-sidebar.component.html
#	src/app/shared/search/search.component.html
#	src/app/shared/search/search.component.ts
#	src/app/shared/search/search.module.ts
  • Loading branch information
alexandrevryghem committed Feb 15, 2024
2 parents 404ccd9 + a6675b8 commit f655777
Show file tree
Hide file tree
Showing 53 changed files with 1,097 additions and 733 deletions.
61 changes: 53 additions & 8 deletions src/app/core/services/route.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { RouteService } from './route.service';
import { RouterMock } from '../../shared/mocks/router.mock';
import { TestScheduler } from 'rxjs/testing';
import { AddUrlToHistoryAction } from '../history/history.actions';
import { ActivatedRouteStub } from 'src/app/shared/testing/active-router.stub';
import { take } from 'rxjs/operators';

describe('RouteService', () => {
let scheduler: TestScheduler;
Expand All @@ -29,23 +31,19 @@ describe('RouteService', () => {
select: jasmine.createSpy('select')
});

let route: ActivatedRouteStub;
const router = new RouterMock();
router.setParams(convertToParamMap(paramObject));

paramObject[paramName1] = paramValue1;
paramObject[paramName2] = [paramValue2a, paramValue2b];

beforeEach(waitForAsync(() => {
route = new ActivatedRouteStub(paramObject);

return TestBed.configureTestingModule({
providers: [
{
provide: ActivatedRoute,
useValue: {
queryParams: observableOf(paramObject),
params: observableOf(paramObject),
queryParamMap: observableOf(convertToParamMap(paramObject))
},
},
{ provide: ActivatedRoute, useValue: route },
{ provide: Router, useValue: router },
{ provide: Store, useValue: store },
]
Expand Down Expand Up @@ -181,4 +179,51 @@ describe('RouteService', () => {
});
});
});

describe('getParamsWithoutAppliedFilter', () => {
beforeEach(() => {
route.testParams = {
'query': '',
'spc.page': '1',
'f.author': '1282121b-5394-4689-ab93-78d537764052,authority',
'f.has_content_in_original_bundle': 'true,equals',
};
});

it('should remove the parameter completely if only one value is defined', (done: DoneFn) => {
service.getParamsExceptValue('f.author', '1282121b-5394-4689-ab93-78d537764052,authority').pipe(take(1)).subscribe((params: Params) => {
expect(params).toEqual({
'query': '',
'spc.page': '1',
'f.has_content_in_original_bundle': 'true,equals',
});
done();
});
});

it('should remove the parameter completely if only one value is defined in an array', (done: DoneFn) => {
route.testParams['f.author'] = ['1282121b-5394-4689-ab93-78d537764052,authority'];
service.getParamsExceptValue('f.author', '1282121b-5394-4689-ab93-78d537764052,authority').pipe(take(1)).subscribe((params: Params) => {
expect(params).toEqual({
'query': '',
'spc.page': '1',
'f.has_content_in_original_bundle': 'true,equals',
});
done();
});
});

it('should return all params except the applied filter even when multiple filters of the same type are selected', (done: DoneFn) => {
route.testParams['f.author'] = ['1282121b-5394-4689-ab93-78d537764052,authority', '71b91a28-c280-4352-a199-bd7fc3312501,authority'];
service.getParamsExceptValue('f.author', '1282121b-5394-4689-ab93-78d537764052,authority').pipe(take(1)).subscribe((params: Params) => {
expect(params).toEqual({
'query': '',
'spc.page': '1',
'f.author': ['71b91a28-c280-4352-a199-bd7fc3312501,authority'],
'f.has_content_in_original_bundle': 'true,equals',
});
done();
});
});
});
});
52 changes: 52 additions & 0 deletions src/app/core/services/route.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,4 +225,56 @@ export class RouteService {
}
);
}

/**
* Returns all the query parameters except for the one with the given name & value.
*
* @param name The name of the query param to exclude
* @param value The optional value that the query param needs to have to be excluded
*/
getParamsExceptValue(name: string, value?: string): Observable<Params> {
return this.route.queryParams.pipe(
map((params: Params) => {
const newParams: Params = Object.assign({}, params);
const queryParamValues: string | string[] = newParams[name];

if (queryParamValues === value || value === undefined) {
delete newParams[name];
} else if (Array.isArray(queryParamValues) && queryParamValues.includes(value)) {
newParams[name] = (queryParamValues as string[]).filter((paramValue: string) => paramValue !== value);
if (newParams[name].length === 0) {
delete newParams[name];
}
}
return newParams;
}),
);
}

/**
* Returns all the existing query parameters and the new value pair with the given name & value.
*
* @param name The name of the query param for which you need to add the value
* @param value The optional value that the query param needs to have in addition to the current ones
*/
getParamsWithAdditionalValue(name: string, value: string): Observable<Params> {
return this.route.queryParams.pipe(
map((params: Params) => {
const newParams: Params = Object.assign({}, params);
const queryParamValues: string | string[] = newParams[name];

if (queryParamValues === undefined) {
newParams[name] = value;
} else {
if (Array.isArray(queryParamValues)) {
newParams[name] = [...queryParamValues, value];
} else {
newParams[name] = [queryParamValues, value];
}
}
return newParams;
}),
);
}

}
33 changes: 30 additions & 3 deletions src/app/core/shared/search/search-configuration.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.u
import { getMockRequestService } from '../../../shared/mocks/request.service.mock';
import { RequestEntry } from '../../data/request-entry.model';
import { SearchObjects } from '../../../shared/search/models/search-objects.model';
import { AppliedFilter } from '../../../shared/search/models/applied-filter.model';

describe('SearchConfigurationService', () => {
let service: SearchConfigurationService;
Expand All @@ -38,13 +39,14 @@ describe('SearchConfigurationService', () => {
const routeService = jasmine.createSpyObj('RouteService', {
getQueryParameterValue: observableOf(value1),
getQueryParamsWithPrefix: observableOf(prefixFilter),
getRouteParameterValue: observableOf('')
getRouteParameterValue: observableOf(''),
getParamsExceptValue: observableOf({}),
});

const paginationService = new PaginationServiceStub();


const activatedRoute: any = new ActivatedRouteStub();
const activatedRoute: ActivatedRouteStub = new ActivatedRouteStub();
const linkService: any = {};
const requestService: any = getMockRequestService();
const halService: any = {
Expand All @@ -70,7 +72,7 @@ describe('SearchConfigurationService', () => {
}
};
beforeEach(() => {
service = new SearchConfigurationService(routeService, paginationService as any, activatedRoute, linkService, halService, requestService, rdb);
service = new SearchConfigurationService(routeService, paginationService as any, activatedRoute as any, linkService, halService, requestService, rdb);
});

describe('when the scope is called', () => {
Expand Down Expand Up @@ -279,4 +281,29 @@ describe('SearchConfigurationService', () => {
expect((service as any).requestService.send).toHaveBeenCalledWith(jasmine.objectContaining({ href: requestUrl }), true);
});
});

describe('unselectAppliedFilterParams', () => {
let appliedFilter: AppliedFilter;

beforeEach(() => {
appliedFilter = Object.assign(new AppliedFilter(), {
filter: 'author',
operator: 'authority',
value: '1282121b-5394-4689-ab93-78d537764052',
label: 'Odinson, Thor',
});
});

it('should return all params except the applied filter', () => {
service.unselectAppliedFilterParams(appliedFilter.filter, appliedFilter.value, appliedFilter.operator);

expect(routeService.getParamsExceptValue).toHaveBeenCalledWith('f.author', '1282121b-5394-4689-ab93-78d537764052,authority');
});

it('should be able to remove AppliedFilter without operator', () => {
service.unselectAppliedFilterParams('dateIssued.max', '2000');

expect(routeService.getParamsExceptValue).toHaveBeenCalledWith('f.dateIssued.max', '2000');
});
});
});
22 changes: 22 additions & 0 deletions src/app/core/shared/search/search-configuration.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { FacetConfigResponseParsingService } from '../../data/facet-config-respo
import { ViewMode } from '../view-mode.model';
import { SearchFilterConfig } from '../../../shared/search/models/search-filter-config.model';
import { FacetConfigResponse } from '../../../shared/search/models/facet-config-response.model';
import { addOperatorToFilterValue } from '../../../shared/search/search.utils';

/**
* Service that performs all actions that have to do with the current search configuration
Expand Down Expand Up @@ -525,6 +526,27 @@ export class SearchConfigurationService implements OnDestroy {
);
}

/**
* Calculates the {@link Params} of the search after removing a filter with a certain value
*
* @param filterName The {@link AppliedFilter}'s name
* @param value The {@link AppliedFilter}'s value
* @param operator The {@link AppliedFilter}'s optional operator
*/
unselectAppliedFilterParams(filterName: string, value: string, operator?: string): Observable<Params> {
return this.routeService.getParamsExceptValue(`f.${filterName}`, hasValue(operator) ? addOperatorToFilterValue(value, operator) : value);
}

/**
* Calculates the {@link Params} of the search after removing a filter with a certain value
*
* @param filterName The {@link AppliedFilter}'s name
* @param value The {@link AppliedFilter}'s value
* @param operator The {@link AppliedFilter}'s optional operator
*/
selectNewAppliedFilterParams(filterName: string, value: string, operator?: string): Observable<Params> {
return this.routeService.getParamsWithAdditionalValue(`f.${filterName}`, hasValue(operator) ? addOperatorToFilterValue(value, operator) : value);
}

/**
* @returns {Observable<Params>} Emits the current view mode as a partial SearchOptions object
Expand Down
12 changes: 7 additions & 5 deletions src/app/core/shared/search/search-filter.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { BehaviorSubject, combineLatest as observableCombineLatest, Observable } from 'rxjs';
import { distinctUntilChanged, map } from 'rxjs/operators';
import { Injectable, InjectionToken } from '@angular/core';
import { Injectable, InjectionToken, EventEmitter } from '@angular/core';
import {
SearchFiltersState,
SearchFilterState
Expand All @@ -21,12 +21,14 @@ import { SortDirection, SortOptions } from '../../cache/models/sort-options.mode
import { RouteService } from '../../services/route.service';
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
import { Params } from '@angular/router';
import { AppliedFilter } from '../../../shared/search/models/applied-filter.model';

const filterStateSelector = (state: SearchFiltersState) => state.searchFilter;

export const FILTER_CONFIG: InjectionToken<SearchFilterConfig> = new InjectionToken<SearchFilterConfig>('filterConfig');
export const IN_PLACE_SEARCH: InjectionToken<boolean> = new InjectionToken<boolean>('inPlaceSearch');
export const REFRESH_FILTER: InjectionToken<BehaviorSubject<any>> = new InjectionToken<boolean>('refreshFilters');
export const CHANGE_APPLIED_FILTERS: InjectionToken<EventEmitter<AppliedFilter[]>> = new InjectionToken('changeAppliedFilters');

/**
* Service that performs all actions that have to do with search filters and facets
Expand Down Expand Up @@ -61,15 +63,15 @@ export class SearchFilterService {
* Fetch the current active scope from the query parameters
* @returns {Observable<string>}
*/
getCurrentScope() {
getCurrentScope(): Observable<string> {
return this.routeService.getQueryParameterValue('scope');
}

/**
* Fetch the current query from the query parameters
* @returns {Observable<string>}
*/
getCurrentQuery() {
getCurrentQuery(): Observable<string> {
return this.routeService.getQueryParameterValue('query');
}

Expand Down Expand Up @@ -111,15 +113,15 @@ export class SearchFilterService {
* Fetch the current active filters from the query parameters
* @returns {Observable<Params>}
*/
getCurrentFilters() {
getCurrentFilters(): Observable<Params> {
return this.routeService.getQueryParamsWithPrefix('f.');
}

/**
* Fetch the current view from the query parameters
* @returns {Observable<string>}
*/
getCurrentView() {
getCurrentView(): Observable<string> {
return this.routeService.getQueryParameterValue('view');
}

Expand Down
17 changes: 17 additions & 0 deletions src/app/shared/search/models/applied-filter.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { autoserialize } from 'cerialize';

export class AppliedFilter {

@autoserialize
filter: string;

@autoserialize
operator: string;

@autoserialize
value: string;

@autoserialize
label: string;

}
12 changes: 7 additions & 5 deletions src/app/shared/search/models/search-query-response.model.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { autoserialize } from 'cerialize';
import { autoserialize, autoserializeAs } from 'cerialize';
import { PageInfo } from '../../../core/shared/page-info.model';
import { PaginatedList } from '../../../core/data/paginated-list.model';
import { AppliedFilter } from './applied-filter.model';
import { SearchResultSorting } from './search-result-sorting.model';

/**
* Class representing the response returned by the server when performing a search request
Expand All @@ -21,14 +23,14 @@ export abstract class SearchQueryResponse<T> extends PaginatedList<T> {
/**
* The currently active filters used in the search request
*/
@autoserialize
appliedFilters: any[]; // TODO
@autoserializeAs(AppliedFilter)
appliedFilters: AppliedFilter[];

/**
* The sort parameters used in the search request
*/
@autoserialize
sort: any; // TODO
@autoserializeAs(SearchResultSorting)
sort: SearchResultSorting;

/**
* The sort parameters used in the search request
Expand Down
11 changes: 11 additions & 0 deletions src/app/shared/search/models/search-result-sorting.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { autoserialize } from 'cerialize';

export class SearchResultSorting {

@autoserialize
by: string;

@autoserialize
order: string;

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<div>
<div class="filters py-2">
<ds-search-facet-selected-option *ngFor="let value of (selectedValues$ | async)" [selectedValue]="value" [filterConfig]="filterConfig" [selectedValues$]="selectedValues$" [inPlaceSearch]="inPlaceSearch"></ds-search-facet-selected-option>
<ng-container *ngFor="let page of (filterValues$ | async)?.payload">
<ds-search-facet-selected-option *ngFor="let value of (selectedAppliedFilters$ | async)" [selectedValue]="value" [filterConfig]="filterConfig" [inPlaceSearch]="inPlaceSearch"></ds-search-facet-selected-option>
<ng-container *ngFor="let page of (facetValues$ | async)">
<div [@facetLoad]="animationState">
<ds-search-facet-option *ngFor="let value of page.page; trackBy: trackUpdate" [filterConfig]="filterConfig" [filterValue]="value" [selectedValues$]="selectedValues$" [inPlaceSearch]="inPlaceSearch"></ds-search-facet-option>
<ds-search-facet-option *ngFor="let value of page.page; trackBy: trackUpdate" [filterConfig]="filterConfig" [filterValue]="value" [inPlaceSearch]="inPlaceSearch"></ds-search-facet-option>
</div>
</ng-container>
<div class="clearfix toggle-more-filters">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<div>
<div class="filters py-2">
<ds-search-facet-selected-option *ngFor="let value of (selectedValues$ | async)" [selectedValue]="value" [filterConfig]="filterConfig" [selectedValues$]="selectedValues$" [inPlaceSearch]="inPlaceSearch"></ds-search-facet-selected-option>
<ng-container *ngFor="let page of (filterValues$ | async)?.payload">
<ds-search-facet-selected-option *ngFor="let value of (selectedAppliedFilters$ | async)" [selectedValue]="value" [filterConfig]="filterConfig" [inPlaceSearch]="inPlaceSearch"></ds-search-facet-selected-option>
<ng-container *ngFor="let page of (facetValues$ | async)">
<div [@facetLoad]="animationState">
<ds-search-facet-option *ngFor="let value of page.page; trackBy: trackUpdate" [filterConfig]="filterConfig" [filterValue]="value" [selectedValues$]="selectedValues$" [inPlaceSearch]="inPlaceSearch"></ds-search-facet-option>
<ds-search-facet-option *ngFor="let value of page.page; trackBy: trackUpdate" [filterConfig]="filterConfig" [filterValue]="value" [inPlaceSearch]="inPlaceSearch"></ds-search-facet-option>
</div>
</ng-container>
<div class="clearfix toggle-more-filters">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<a *ngIf="isVisible | async" class="d-flex flex-row"
[tabIndex]="-1"
[routerLink]="[searchLink]"
[queryParams]="addQueryParams" queryParamsHandling="merge">
[queryParams]="addQueryParams$ | async">
<label class="mb-0 d-flex w-100">
<input type="checkbox" [checked]="false" class="my-1 align-self-stretch filter-checkbox"/>
<span class="w-100 pl-1 break-facet">
Expand Down
Loading

0 comments on commit f655777

Please sign in to comment.