111731: Made the code completely dependent on the SearchService#appliedFilters$ instead of relying on the route query parameters

This commit is contained in:
Alexandre Vryghem
2024-05-07 18:20:00 +01:00
parent 58d73875b8
commit 28088bc8c8
10 changed files with 94 additions and 227 deletions

View File

@@ -153,17 +153,6 @@ describe('SearchFilterService', () => {
});
});
describe('when the getSelectedValuesForFilter method is called', () => {
beforeEach(() => {
spyOn(routeServiceStub, 'getQueryParameterValues');
service.getSelectedValuesForFilter(mockFilterConfig);
});
it('should call getQueryParameterValues on the route service with the same parameters', () => {
expect(routeServiceStub.getQueryParameterValues).toHaveBeenCalledWith(mockFilterConfig.paramName);
});
});
describe('when the getCurrentScope method is called', () => {
beforeEach(() => {
spyOn(routeServiceStub, 'getQueryParameterValue');

View File

@@ -136,27 +136,6 @@ export class SearchFilterService {
return this.routeService.getQueryParameterValue('view');
}
/**
* Requests the active filter values set for a given filter
* @param {SearchFilterConfig} filterConfig The configuration for which the filters are active
* @returns {Observable<string[]>} Emits the active filters for the given filter configuration
*/
getSelectedValuesForFilter(filterConfig: SearchFilterConfig): Observable<string[]> {
const values$ = this.routeService.getQueryParameterValues(filterConfig.paramName);
const prefixValues$ = this.routeService.getQueryParamsWithPrefix(filterConfig.paramName + '.').pipe(
map((params: Params) => [].concat(...Object.values(params))),
);
return observableCombineLatest(values$, prefixValues$).pipe(
map(([values, prefixValues]) => {
if (isNotEmpty(values)) {
return values;
}
return prefixValues;
}
)
);
}
/**
* Updates the found facet value suggestions for a given query
* Transforms the found values into display values

View File

@@ -1,7 +1,7 @@
/* eslint-disable max-classes-per-file */
import { combineLatest as observableCombineLatest, Observable, BehaviorSubject } from 'rxjs';
import { Injectable, OnDestroy } from '@angular/core';
import { map, switchMap, take, tap } from 'rxjs/operators';
import { Injectable } from '@angular/core';
import { map, switchMap, take, tap, distinctUntilChanged } from 'rxjs/operators';
import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model';
import { ResponseParsingService } from '../../data/parsing.service';
import { RemoteData } from '../../data/remote-data';
@@ -61,7 +61,7 @@ class SearchDataService extends BaseDataService<any> {
* Service that performs all general actions that have to do with the search page
*/
@Injectable()
export class SearchService implements OnDestroy {
export class SearchService {
/**
* Endpoint link path for retrieving general search results
@@ -78,11 +78,6 @@ export class SearchService implements OnDestroy {
*/
private request: GenericConstructor<RestRequest> = GetRequest;
/**
* Subscription to unsubscribe from
*/
private sub;
/**
* Instance of SearchDataService to forward data service methods to
*/
@@ -103,6 +98,18 @@ export class SearchService implements OnDestroy {
this.searchDataService = new SearchDataService();
}
/**
* Get the currently {@link AppliedFilter}s for the given filter.
*
* @param filterName The name of the filter
*/
getSelectedValuesForFilter(filterName: string): Observable<AppliedFilter[]> {
return this.appliedFilters$.pipe(
map((appliedFilters: AppliedFilter[]) => appliedFilters.filter((appliedFilter: AppliedFilter) => appliedFilter.filter === filterName)),
distinctUntilChanged((previous: AppliedFilter[], next: AppliedFilter[]) => JSON.stringify(previous) === JSON.stringify(next)),
);
}
/**
* Method to set service options
* @param {GenericConstructor<ResponseParsingService>} parser The ResponseParsingService constructor name
@@ -176,26 +183,11 @@ export class SearchService implements OnDestroy {
return this.directlyAttachIndexableObjects(sqr$, useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}
/**
* Method to retrieve request entries for search results from the server
* @param {PaginatedSearchOptions} searchOptions The configuration necessary to perform this search
* @returns {Observable<RemoteData<SearchObjects<T>>>} Emits a paginated list with all search results found
*/
searchEntries<T extends DSpaceObject>(searchOptions?: PaginatedSearchOptions): Observable<RemoteData<SearchObjects<T>>> {
const href$ = this.getEndpoint(searchOptions);
const sqr$ = href$.pipe(
switchMap((href: string) => this.rdb.buildFromHref<SearchObjects<T>>(href))
);
return this.directlyAttachIndexableObjects(sqr$);
}
/**
* Method to directly attach the indexableObjects to search results, instead of using RemoteData.
* For compatibility with the way the search was written originally
*
* @param sqr$: a SearchObjects RemotaData Observable without its
* @param sqr$ A {@link SearchObjects} {@link RemoteData} Observable without its
* indexableObjects attached
* @param useCachedVersionIfAvailable If this is true, the request will only be sent if there's
* no valid cached version. Defaults to true
@@ -384,12 +376,4 @@ export class SearchService implements OnDestroy {
return '/search';
}
/**
* Unsubscribe from the subscription
*/
ngOnDestroy(): void {
if (this.sub !== undefined) {
this.sub.unsubscribe();
}
}
}

View File

@@ -3,7 +3,7 @@ import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
import { Router, Params } from '@angular/router';
import { BehaviorSubject, combineLatest as observableCombineLatest, Observable, of as observableOf, Subscription } from 'rxjs';
import { debounceTime, distinctUntilChanged, filter, map, mergeMap, switchMap, take, tap } from 'rxjs/operators';
import { distinctUntilChanged, map, switchMap, take, tap } from 'rxjs/operators';
import { RemoteDataBuildService } from '../../../../../core/cache/builders/remote-data-build.service';
import { hasNoValue, hasValue } from '../../../../empty.util';
@@ -17,7 +17,6 @@ import { InputSuggestion } from '../../../../input-suggestions/input-suggestions
import { SearchOptions } from '../../../models/search-options.model';
import { SEARCH_CONFIG_SERVICE } from '../../../../../my-dspace-page/my-dspace-page.component';
import { currentPath } from '../../../../utils/route.utils';
import { stripOperatorFromFilterValue } from '../../../search.utils';
import { FacetValues } from '../../../models/facet-values.model';
import { AppliedFilter } from '../../../models/applied-filter.model';
@@ -103,14 +102,9 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy {
this.searchOptions$ = this.searchConfigService.searchOptions;
this.subs.push(
this.searchOptions$.subscribe(() => this.updateFilterValueList()),
this.refreshFilters.asObservable().pipe(
filter((toRefresh: boolean) => toRefresh),
// NOTE This is a workaround, otherwise retrieving filter values returns tha old cached response
debounceTime((100)),
mergeMap(() => this.retrieveFilterValues(false))
).subscribe()
this.retrieveFilterValues().subscribe(),
);
this.retrieveFilterValues().subscribe();
this.selectedAppliedFilters$ = this.searchService.getSelectedValuesForFilter(this.filterConfig.name);
}
/**
@@ -217,9 +211,13 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy {
}
}
protected retrieveFilterValues(useCachedVersionIfAvailable = true): Observable<FacetValues[]> {
/**
* Retrieves all the filter value suggestion pages that need to be displayed in the facet and combines it into one
* list.
*/
protected retrieveFilterValues(): Observable<FacetValues[]> {
return observableCombineLatest([this.searchOptions$, this.currentPage]).pipe(
switchMap(([options, page]: [SearchOptions, number]) => this.searchService.getFacetValuesFor(this.filterConfig, page, options, null, useCachedVersionIfAvailable).pipe(
switchMap(([options, page]: [SearchOptions, number]) => this.searchService.getFacetValuesFor(this.filterConfig, page, options).pipe(
getFirstSucceededRemoteDataPayload(),
tap((facetValues: FacetValues) => {
this.isLastPage$.next(hasNoValue(facetValues?.next));
@@ -242,27 +240,12 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy {
return filterValues;
}),
tap((allFacetValues: FacetValues[]) => {
this.setAppliedFilter(allFacetValues);
this.animationState = 'ready';
this.facetValues$.next(allFacetValues);
})
);
}
setAppliedFilter(allFacetValues: FacetValues[]): void {
const allAppliedFilters: AppliedFilter[] = [].concat(...allFacetValues.map((facetValues: FacetValues) => facetValues.appliedFilters))
.filter((appliedFilter: AppliedFilter) => hasValue(appliedFilter));
this.selectedAppliedFilters$ = this.filterService.getSelectedValuesForFilter(this.filterConfig).pipe(
map((selectedValues: string[]) => {
const appliedFilters: AppliedFilter[] = selectedValues.map((value: string) => {
return allAppliedFilters.find((appliedFilter: AppliedFilter) => appliedFilter.value === stripOperatorFromFilterValue(value));
}).filter((appliedFilter: AppliedFilter) => hasValue(appliedFilter));
return appliedFilters;
}),
);
}
/**
* Prevent unnecessary rerendering
*/

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
import { ChangeDetectionStrategy, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
@@ -14,51 +14,28 @@ import { SearchConfigurationServiceStub } from '../../../testing/search-configur
import { SEARCH_CONFIG_SERVICE } from '../../../../my-dspace-page/my-dspace-page.component';
import { SequenceService } from '../../../../core/shared/sequence.service';
import { BrowserOnlyMockPipe } from '../../../testing/browser-only-mock.pipe';
import { SearchServiceStub } from '../../../testing/search-service.stub';
import { SearchFilterServiceStub } from '../../../testing/search-filter-service.stub';
describe('SearchFilterComponent', () => {
let comp: SearchFilterComponent;
let fixture: ComponentFixture<SearchFilterComponent>;
const filterName1 = 'test name';
const filterName2 = 'test2';
const filterName3 = 'another name3';
const nonExistingFilter1 = 'non existing 1';
const nonExistingFilter2 = 'non existing 2';
const mockFilterConfig: SearchFilterConfig = Object.assign(new SearchFilterConfig(), {
name: filterName1,
filterType: FilterType.text,
hasFacets: false,
isOpenByDefault: false
});
const mockFilterService = {
/* eslint-disable no-empty,@typescript-eslint/no-empty-function */
toggle: (filter) => {
},
collapse: (filter) => {
},
expand: (filter) => {
},
initializeFilter: (filter) => {
},
getSelectedValuesForFilter: (filter) => {
return observableOf([filterName1, filterName2, filterName3]);
},
isFilterActive: (filter) => {
return observableOf([filterName1, filterName2, filterName3].indexOf(filter) >= 0);
},
isCollapsed: (filter) => {
return observableOf(true);
}
/* eslint-enable no-empty, @typescript-eslint/no-empty-function */
};
let filterService;
let searchFilterService: SearchFilterServiceStub;
let sequenceService;
const mockResults = observableOf(['test', 'data']);
const searchServiceStub = {
getFacetValuesFor: (filter) => mockResults
};
let searchService: SearchServiceStub;
beforeEach(waitForAsync(() => {
searchFilterService = new SearchFilterServiceStub();
searchService = new SearchServiceStub();
sequenceService = jasmine.createSpyObj('sequenceService', { next: 17 });
TestBed.configureTestingModule({
@@ -68,26 +45,23 @@ describe('SearchFilterComponent', () => {
BrowserOnlyMockPipe,
],
providers: [
{ provide: SearchService, useValue: searchServiceStub },
{
provide: SearchFilterService,
useValue: mockFilterService
},
{ provide: SearchService, useValue: searchService },
{ provide: SearchFilterService, useValue: searchFilterService },
{ provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() },
{ provide: SequenceService, useValue: sequenceService },
],
schemas: [NO_ERRORS_SCHEMA]
schemas: [CUSTOM_ELEMENTS_SCHEMA],
}).overrideComponent(SearchFilterComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
}).compileComponents();
}));
beforeEach(() => {
spyOn(searchService, 'getFacetValuesFor').and.returnValue(mockResults);
fixture = TestBed.createComponent(SearchFilterComponent);
comp = fixture.componentInstance; // SearchPageComponent test instance
comp.filter = mockFilterConfig;
fixture.detectChanges();
filterService = (comp as any).filterService;
});
it('should generate unique IDs', () => {
@@ -98,54 +72,30 @@ describe('SearchFilterComponent', () => {
describe('when the toggle method is triggered', () => {
beforeEach(() => {
spyOn(filterService, 'toggle');
spyOn(searchFilterService, 'toggle');
comp.toggle();
});
it('should call toggle with the correct filter configuration name', () => {
expect(filterService.toggle).toHaveBeenCalledWith(mockFilterConfig.name);
expect(searchFilterService.toggle).toHaveBeenCalledWith(mockFilterConfig.name);
});
});
describe('when the initializeFilter method is triggered', () => {
beforeEach(() => {
spyOn(filterService, 'initializeFilter');
spyOn(searchFilterService, 'initializeFilter');
comp.initializeFilter();
});
it('should call initialCollapse with the correct filter configuration name', () => {
expect(filterService.initializeFilter).toHaveBeenCalledWith(mockFilterConfig);
});
});
describe('when getSelectedValues is called', () => {
let valuesObservable: Observable<string[]>;
beforeEach(() => {
valuesObservable = (comp as any).getSelectedValues();
});
it('should return an observable containing the existing filters', () => {
const sub = valuesObservable.subscribe((values) => {
expect(values).toContain(filterName1);
expect(values).toContain(filterName2);
expect(values).toContain(filterName3);
});
sub.unsubscribe();
});
it('should return an observable that does not contain the non-existing filters', () => {
const sub = valuesObservable.subscribe((values) => {
expect(values).not.toContain(nonExistingFilter1);
expect(values).not.toContain(nonExistingFilter2);
});
sub.unsubscribe();
expect(searchFilterService.initializeFilter).toHaveBeenCalledWith(mockFilterConfig);
});
});
describe('when isCollapsed is called and the filter is collapsed', () => {
let isActive: Observable<boolean>;
beforeEach(() => {
filterService.isCollapsed = () => observableOf(true);
searchFilterService.isCollapsed = () => observableOf(true);
isActive = (comp as any).isCollapsed();
});
@@ -160,7 +110,7 @@ describe('SearchFilterComponent', () => {
describe('when isCollapsed is called and the filter is not collapsed', () => {
let isActive: Observable<boolean>;
beforeEach(() => {
filterService.isCollapsed = () => observableOf(false);
searchFilterService.isCollapsed = () => observableOf(false);
isActive = (comp as any).isCollapsed();
});

View File

@@ -1,6 +1,6 @@
import { Component, Inject, Input, OnInit } from '@angular/core';
import { Component, Inject, Input, OnDestroy, OnInit } from '@angular/core';
import { BehaviorSubject, Observable, of as observableOf } from 'rxjs';
import { BehaviorSubject, Observable, of as observableOf, combineLatest, Subscription } from 'rxjs';
import { filter, map, startWith, switchMap, take } from 'rxjs/operators';
import { SearchFilterConfig } from '../../models/search-filter-config.model';
@@ -11,6 +11,10 @@ import { SearchService } from '../../../../core/shared/search/search.service';
import { SearchConfigurationService } from '../../../../core/shared/search/search-configuration.service';
import { SEARCH_CONFIG_SERVICE } from '../../../../my-dspace-page/my-dspace-page.component';
import { SequenceService } from '../../../../core/shared/sequence.service';
import { FacetValues } from '../../models/facet-values.model';
import { RemoteData } from '../../../../core/data/remote-data';
import { AppliedFilter } from '../../models/applied-filter.model';
import { SearchOptions } from '../../models/search-options.model';
@Component({
selector: 'ds-search-filter',
@@ -22,7 +26,7 @@ import { SequenceService } from '../../../../core/shared/sequence.service';
/**
* Represents a part of the filter section for a single type of filter
*/
export class SearchFilterComponent implements OnInit {
export class SearchFilterComponent implements OnInit, OnDestroy {
/**
* The filter config for this component
*/
@@ -61,13 +65,15 @@ export class SearchFilterComponent implements OnInit {
/**
* Emits all currently selected values for this filter
*/
selectedValues$: Observable<string[]>;
appliedFilters$: Observable<AppliedFilter[]>;
/**
* Emits true when the current filter is supposed to be shown
*/
active$: Observable<boolean>;
subs: Subscription[] = [];
private readonly sequenceId: number;
constructor(
@@ -85,15 +91,19 @@ export class SearchFilterComponent implements OnInit {
* Else, the filter should initially be collapsed
*/
ngOnInit() {
this.selectedValues$ = this.getSelectedValues();
this.appliedFilters$ = this.searchService.getSelectedValuesForFilter(this.filter.name);
this.active$ = this.isActive();
this.collapsed$ = this.isCollapsed();
this.initializeFilter();
this.selectedValues$.pipe(take(1)).subscribe((selectedValues) => {
this.subs.push(this.appliedFilters$.pipe(take(1)).subscribe((selectedValues: AppliedFilter[]) => {
if (isNotEmpty(selectedValues)) {
this.filterService.expand(this.filter.name);
}
});
}));
}
ngOnDestroy(): void {
this.subs.forEach((sub: Subscription) => sub.unsubscribe());
}
/**
@@ -118,13 +128,6 @@ export class SearchFilterComponent implements OnInit {
this.filterService.initializeFilter(this.filter);
}
/**
* @returns {Observable<string[]>} Emits a list of all values that are currently active for this filter
*/
private getSelectedValues(): Observable<string[]> {
return this.filterService.getSelectedValuesForFilter(this.filter);
}
/**
* Method to change this.collapsed to false when the slide animation ends and is sliding open
* @param event The animation event
@@ -164,20 +167,20 @@ export class SearchFilterComponent implements OnInit {
* @returns {Observable<boolean>} Emits true whenever a given filter config should be shown
*/
private isActive(): Observable<boolean> {
return this.selectedValues$.pipe(
switchMap((isActive) => {
if (isNotEmpty(isActive)) {
return combineLatest([
this.appliedFilters$,
this.searchConfigService.searchOptions,
]).pipe(
switchMap(([selectedValues, options]: [AppliedFilter[], SearchOptions]) => {
if (isNotEmpty(selectedValues)) {
return observableOf(true);
} else {
return this.searchConfigService.searchOptions.pipe(
switchMap((options) => {
return this.searchService.getFacetValuesFor(this.filter, 1, options).pipe(
filter((RD) => !RD.isLoading),
map((valuesRD) => {
return valuesRD.payload?.totalElements > 0;
}),);
}
));
return this.searchService.getFacetValuesFor(this.filter, 1, options).pipe(
filter((RD: RemoteData<FacetValues>) => !RD.isLoading),
map((valuesRD: RemoteData<FacetValues>) => {
return valuesRD.payload?.totalElements > 0;
}),
);
}
}),
startWith(true));

View File

@@ -1,6 +1,6 @@
import { SearchHierarchyFilterComponent } from './search-hierarchy-filter.component';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { DebugElement, EventEmitter, NO_ERRORS_SCHEMA } from '@angular/core';
import { DebugElement, EventEmitter, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { By } from '@angular/platform-browser';
import { VocabularyService } from '../../../../../core/submission/vocabularies/vocabulary.service';
import { of as observableOf, BehaviorSubject } from 'rxjs';
@@ -26,6 +26,7 @@ import { SearchConfigurationServiceStub } from '../../../../testing/search-confi
import { VocabularyEntryDetail } from '../../../../../core/submission/vocabularies/models/vocabulary-entry-detail.model';
import { AppliedFilter } from '../../../models/applied-filter.model';
import { SearchFilterConfig } from '../../../models/search-filter-config.model';
import { SearchServiceStub } from '../../../../testing/search-service.stub';
describe('SearchHierarchyFilterComponent', () => {
@@ -38,10 +39,7 @@ describe('SearchHierarchyFilterComponent', () => {
select: new EventEmitter<VocabularyEntryDetail>(),
};
const searchService = {
getSearchLink: () => testSearchLink,
getFacetValuesFor: () => observableOf([]),
};
let searchService: SearchServiceStub;
const searchFilterService = {
getPage: () => observableOf(0),
};
@@ -56,6 +54,8 @@ describe('SearchHierarchyFilterComponent', () => {
};
beforeEach(() => {
searchService = new SearchServiceStub();
TestBed.configureTestingModule({
imports: [
CommonModule,
@@ -77,7 +77,7 @@ describe('SearchHierarchyFilterComponent', () => {
{ provide: FILTER_CONFIG, useValue: Object.assign(new SearchFilterConfig(), { name: testSearchFilter }) },
{ provide: REFRESH_FILTER, useValue: new BehaviorSubject<boolean>(false) },
],
schemas: [NO_ERRORS_SCHEMA],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
}).compileComponents();
});

View File

@@ -1,7 +1,7 @@
import { BehaviorSubject, combineLatest as observableCombineLatest, of as observableOf , Subscription } from 'rxjs';
import { BehaviorSubject, combineLatest as observableCombineLatest } from 'rxjs';
import { map, startWith } from 'rxjs/operators';
import { isPlatformBrowser } from '@angular/common';
import { Component, Inject, OnDestroy, OnInit, PLATFORM_ID } from '@angular/core';
import { Component, Inject, OnInit, PLATFORM_ID } from '@angular/core';
import { RemoteDataBuildService } from '../../../../../core/cache/builders/remote-data-build.service';
import { FilterType } from '../../../models/filter-type.model';
import { renderFacetFor } from '../search-filter-type-decorator';
@@ -19,8 +19,6 @@ import { SEARCH_CONFIG_SERVICE } from '../../../../../my-dspace-page/my-dspace-p
import { SearchConfigurationService } from '../../../../../core/shared/search/search-configuration.service';
import { RouteService } from '../../../../../core/services/route.service';
import { hasValue } from '../../../../empty.util';
import { AppliedFilter } from '../../../models/applied-filter.model';
import { FacetValues } from '../../../models/facet-values.model';
import { yearFromString } from 'src/app/shared/date.util';
/**
@@ -49,7 +47,7 @@ export const RANGE_FILTER_MAX_SUFFIX = '.max';
* Component that represents a range facet for a specific filter configuration
*/
@renderFacetFor(FilterType.range)
export class SearchRangeFilterComponent extends SearchFacetFilterComponent implements OnInit, OnDestroy {
export class SearchRangeFilterComponent extends SearchFacetFilterComponent implements OnInit {
/**
* Fallback minimum for the range
*/
@@ -65,11 +63,6 @@ export class SearchRangeFilterComponent extends SearchFacetFilterComponent imple
*/
range: [number | undefined, number | undefined];
/**
* Subscription to unsubscribe from
*/
sub: Subscription;
/**
* Whether the sider is being controlled by the keyboard.
* Supresses any changes until the key is released.
@@ -100,25 +93,13 @@ export class SearchRangeFilterComponent extends SearchFacetFilterComponent imple
this.max = yearFromString(this.filterConfig.maxValue) || this.max;
const iniMin = this.route.getQueryParameterValue(this.filterConfig.paramName + RANGE_FILTER_MIN_SUFFIX).pipe(startWith(undefined));
const iniMax = this.route.getQueryParameterValue(this.filterConfig.paramName + RANGE_FILTER_MAX_SUFFIX).pipe(startWith(undefined));
this.sub = observableCombineLatest([iniMin, iniMax]).pipe(
this.subs.push(observableCombineLatest([iniMin, iniMax]).pipe(
map(([min, max]: [string, string]) => {
const minimum = hasValue(min) ? Number(min) : this.min;
const maximum = hasValue(max) ? Number(max) : this.max;
return [minimum, maximum];
})
).subscribe((minmax: [number, number]) => this.range = minmax);
}
setAppliedFilter(allFacetValues: FacetValues[]): void {
const appliedFilters: AppliedFilter[] = [].concat(...allFacetValues.map((facetValues: FacetValues) => facetValues.appliedFilters))
.filter((appliedFilter: AppliedFilter) => hasValue(appliedFilter))
.filter((appliedFilter: AppliedFilter) => appliedFilter.filter === this.filterConfig.name)
// TODO this should ideally be fixed in the backend
.map((appliedFilter: AppliedFilter) => Object.assign({}, appliedFilter, {
operator: 'range',
}));
this.selectedAppliedFilters$ = observableOf(appliedFilters);
).subscribe((minmax: [number, number]) => this.range = minmax));
}
/**
@@ -159,13 +140,4 @@ export class SearchRangeFilterComponent extends SearchFacetFilterComponent imple
return isPlatformBrowser(this.platformId);
}
/**
* Unsubscribe from all subscriptions
*/
ngOnDestroy() {
super.ngOnDestroy();
if (hasValue(this.sub)) {
this.sub.unsubscribe();
}
}
}

View File

@@ -5,6 +5,9 @@ import { SearchFilterConfig } from '../search/models/search-filter-config.model'
import { Params } from '@angular/router';
/* eslint-disable no-empty,@typescript-eslint/no-empty-function */
/**
* Stub class of {@link SearchFilterService}
*/
export class SearchFilterServiceStub {
isFilterActiveWithValue(_paramName: string, _filterValue: string): Observable<boolean> {
@@ -39,10 +42,6 @@ export class SearchFilterServiceStub {
return observableOf(undefined);
}
getSelectedValuesForFilter(_filterConfig: SearchFilterConfig): Observable<string[]> {
return observableOf([]);
}
isCollapsed(_filterName: string): Observable<boolean> {
return observableOf(true);
}

View File

@@ -2,7 +2,11 @@ import {of as observableOf, Observable , BehaviorSubject } from 'rxjs';
import { ViewMode } from '../../core/shared/view-mode.model';
import { SearchFilterConfig } from '../search/models/search-filter-config.model';
import { PaginatedSearchOptions } from '../search/models/paginated-search-options.model';
import { AppliedFilter } from '../search/models/applied-filter.model';
/**
* Stub class of {@link SearchService}
*/
export class SearchServiceStub {
private _viewMode: ViewMode;
@@ -14,6 +18,10 @@ export class SearchServiceStub {
this.setViewMode(ViewMode.ListElement);
}
getSelectedValuesForFilter(_filterName: string): Observable<AppliedFilter[]> {
return observableOf([]);
}
getViewMode(): Observable<ViewMode> {
return this.viewMode;
}