Added more optimalisations and fixed tests

This commit is contained in:
lotte
2019-03-21 15:52:34 +01:00
parent fc73fc1d3e
commit 037063ea63
23 changed files with 132 additions and 208 deletions

View File

@@ -1,10 +1,9 @@
<div> <div>
<div class="filters py-2"> <div class="filters py-2">
<ds-search-facet-selected-option *ngFor="let value of (selectedValues | async)" [selectedValue]="value" [filterConfig]="filterConfig"></ds-search-facet-selected-option> <ds-search-facet-selected-option *ngFor="let value of (selectedValues$ | async)" [selectedValue]="value" [filterConfig]="filterConfig" [selectedValues$]="selectedValues$"></ds-search-facet-selected-option>
<ng-container *ngFor="let page of (filterValues$ | async)?.payload"> <ng-container *ngFor="let page of (filterValues$ | async)?.payload">
<div [@facetLoad]="animationState"> <div [@facetLoad]="animationState">
<ds-search-facet-option *ngFor="let value of page.page; trackBy: trackUpdate" [filterConfig]="filterConfig" [filterValue]="value"> <ds-search-facet-option *ngFor="let value of page.page; trackBy: trackUpdate" [filterConfig]="filterConfig" [filterValue]="value" [selectedValues$]="selectedValues$"></ds-search-facet-option>
</ds-search-facet-option>
</div> </div>
</ng-container> </ng-container>
<div class="clearfix toggle-more-filters"> <div class="clearfix toggle-more-filters">

View File

@@ -1,6 +1,6 @@
<a *ngIf="isVisible" class="d-flex flex-row" <script src="search-facet-option.component.ts"></script><a *ngIf="isVisible | async" class="d-flex flex-row"
[routerLink]="[getSearchLink()]" [routerLink]="[getSearchLink()]"
[queryParams]="addQueryParams | async" queryParamsHandling="merge"> [queryParams]="addQueryParams" queryParamsHandling="merge">
<input type="checkbox" [checked]="false" class="my-1 align-self-stretch"/> <input type="checkbox" [checked]="false" class="my-1 align-self-stretch"/>
<span class="filter-value px-1">{{filterValue.value}}</span> <span class="filter-value px-1">{{filterValue.value}}</span>
<span class="float-right filter-value-count ml-auto"> <span class="float-right filter-value-count ml-auto">

View File

@@ -1,11 +1,13 @@
import { Observable, of as observableOf } from 'rxjs'; import { combineLatest as observableCombineLatest, Observable, Subscription } from 'rxjs';
import { map } from 'rxjs/operators'; import { map, take } from 'rxjs/operators';
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { FacetValue } from '../../../../search-service/facet-value.model'; import { FacetValue } from '../../../../search-service/facet-value.model';
import { SearchFilterConfig } from '../../../../search-service/search-filter-config.model'; import { SearchFilterConfig } from '../../../../search-service/search-filter-config.model';
import { SearchService } from '../../../../search-service/search.service'; import { SearchService } from '../../../../search-service/search.service';
import { SearchFilterService } from '../../search-filter.service'; import { SearchFilterService } from '../../search-filter.service';
import { SearchConfigurationService } from '../../../../search-service/search-configuration.service';
import { hasValue } from '../../../../../shared/empty.util';
@Component({ @Component({
selector: 'ds-search-facet-option', selector: 'ds-search-facet-option',
@@ -15,24 +17,22 @@ import { SearchFilterService } from '../../search-filter.service';
/** /**
* Represents a single option in a filter facet * Represents a single option in a filter facet
*/ */
export class SearchFacetOptionComponent implements OnInit { export class SearchFacetOptionComponent implements OnInit, OnDestroy {
/** /**
* A single value for this component * A single value for this component
*/ */
@Input() filterValue: FacetValue; @Input() filterValue: FacetValue;
@Input() filterConfig: SearchFilterConfig; @Input() filterConfig: SearchFilterConfig;
@Input() selectedValues$: Observable<string[]>;
/**
* Emits the active values for this filter
*/
selectedValues$: Observable<string[]>;
isVisible: Observable<boolean>; isVisible: Observable<boolean>;
addQueryParams; addQueryParams;
sub: Subscription;
constructor(protected searchService: SearchService, constructor(protected searchService: SearchService,
protected filterService: SearchFilterService, protected filterService: SearchFilterService,
protected searchConfigService: SearchConfigurationService,
protected router: Router protected router: Router
) { ) {
} }
@@ -41,9 +41,11 @@ export class SearchFacetOptionComponent implements OnInit {
* Initializes all observable instance variables and starts listening to them * Initializes all observable instance variables and starts listening to them
*/ */
ngOnInit(): void { ngOnInit(): void {
this.selectedValues$ = this.filterService.getSelectedValuesForFilter(this.filterConfig);
this.isVisible = this.isChecked().pipe(map((checked: boolean) => !checked)); this.isVisible = this.isChecked().pipe(map((checked: boolean) => !checked));
this.addQueryParams = this.getAddParams(); this.sub = observableCombineLatest(this.selectedValues$, this.searchConfigService.searchOptions)
.subscribe(([selectedValues, searchOptions]) => {
this.updateAddParams(selectedValues)
});
} }
/** /**
@@ -63,16 +65,17 @@ export class SearchFacetOptionComponent implements OnInit {
/** /**
* Calculates the parameters that should change if a given value for this filter would be added to the active filters * Calculates the parameters that should change if a given value for this filter would be added to the active filters
* @param {string} value The value that is added for this filter * @param {string} value The value that is added for this filter
* @returns {Observable<any>} The changed filter parameters
*/ */
private getAddParams(): Observable<any> { private updateAddParams(selectedValues: string[]): void {
return this.selectedValues$.pipe(map((selectedValues) => { this.addQueryParams = {
return { [this.filterConfig.paramName]: [...selectedValues, this.filterValue.value],
[this.filterConfig.paramName]: [...selectedValues, this.filterValue.value], page: 1
page: 1 };
};
}));
} }
ngOnDestroy(): void {
if (hasValue(this.sub)) {
this.sub.unsubscribe();
}
}
} }

View File

@@ -1,6 +1,6 @@
<a *ngIf="isVisible" class="d-flex flex-row" <a *ngIf="isVisible | async" class="d-flex flex-row"
[routerLink]="[getSearchLink()]" [routerLink]="[getSearchLink()]"
[queryParams]="changeQueryParams | async" queryParamsHandling="merge"> [queryParams]="changeQueryParams" queryParamsHandling="merge">
<span class="filter-value px-1">{{filterValue.value}}</span> <span class="filter-value px-1">{{filterValue.value}}</span>
<span class="float-right filter-value-count ml-auto"> <span class="float-right filter-value-count ml-auto">
<span class="badge badge-secondary badge-pill">{{filterValue.count}}</span> <span class="badge badge-secondary badge-pill">{{filterValue.count}}</span>

View File

@@ -1,6 +1,6 @@
import { Observable, of as observableOf } from 'rxjs'; import { Observable, Subscription } from 'rxjs';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { FacetValue } from '../../../../search-service/facet-value.model'; import { FacetValue } from '../../../../search-service/facet-value.model';
import { SearchFilterConfig } from '../../../../search-service/search-filter-config.model'; import { SearchFilterConfig } from '../../../../search-service/search-filter-config.model';
@@ -10,6 +10,8 @@ import {
RANGE_FILTER_MAX_SUFFIX, RANGE_FILTER_MAX_SUFFIX,
RANGE_FILTER_MIN_SUFFIX RANGE_FILTER_MIN_SUFFIX
} from '../../search-range-filter/search-range-filter.component'; } from '../../search-range-filter/search-range-filter.component';
import { SearchConfigurationService } from '../../../../search-service/search-configuration.service';
import { hasValue } from '../../../../../shared/empty.util';
const rangeDelimiter = '-'; const rangeDelimiter = '-';
@@ -21,24 +23,21 @@ const rangeDelimiter = '-';
/** /**
* Represents a single option in a filter facet * Represents a single option in a filter facet
*/ */
export class SearchFacetRangeOptionComponent implements OnInit { export class SearchFacetRangeOptionComponent implements OnInit, OnDestroy {
/** /**
* A single value for this component * A single value for this component
*/ */
@Input() filterValue: FacetValue; @Input() filterValue: FacetValue;
@Input() filterConfig: SearchFilterConfig; @Input() filterConfig: SearchFilterConfig;
/**
* Emits the active values for this filter
*/
selectedValues$: Observable<string[]>;
isVisible: Observable<boolean>; isVisible: Observable<boolean>;
changeQueryParams; changeQueryParams;
sub: Subscription;
constructor(protected searchService: SearchService, constructor(protected searchService: SearchService,
protected filterService: SearchFilterService, protected filterService: SearchFilterService,
protected searchConfigService: SearchConfigurationService,
protected router: Router protected router: Router
) { ) {
} }
@@ -47,9 +46,10 @@ export class SearchFacetRangeOptionComponent implements OnInit {
* Initializes all observable instance variables and starts listening to them * Initializes all observable instance variables and starts listening to them
*/ */
ngOnInit(): void { ngOnInit(): void {
this.selectedValues$ = this.filterService.getSelectedValuesForFilter(this.filterConfig);
this.isVisible = this.isChecked().pipe(map((checked: boolean) => !checked)); this.isVisible = this.isChecked().pipe(map((checked: boolean) => !checked));
this.changeQueryParams = this.getChangeParams(); this.sub = this.searchConfigService.searchOptions.subscribe(() => {
this.updateChangeParams()
});
} }
/** /**
@@ -66,23 +66,24 @@ export class SearchFacetRangeOptionComponent implements OnInit {
return this.searchService.getSearchLink(); return this.searchService.getSearchLink();
} }
/** /**
* Calculates the parameters that should change if a given values for this range filter would be changed * Calculates the parameters that should change if a given values for this range filter would be changed
* @param {string} value The values that are changed for this filter * @param {string} value The values that are changed for this filter
* @returns {Observable<any>} The changed filter parameters
*/ */
getChangeParams() { updateChangeParams(): void {
const parts = this.filterValue.value.split(rangeDelimiter); const parts = this.filterValue.value.split(rangeDelimiter);
const min = parts.length > 1 ? parts[0].trim() : this.filterValue.value; const min = parts.length > 1 ? parts[0].trim() : this.filterValue.value;
const max = parts.length > 1 ? parts[1].trim() : this.filterValue.value; const max = parts.length > 1 ? parts[1].trim() : this.filterValue.value;
return observableOf( this.changeQueryParams = {
{ [this.filterConfig.paramName + RANGE_FILTER_MIN_SUFFIX]: [min],
[this.filterConfig.paramName + RANGE_FILTER_MIN_SUFFIX]: [min], [this.filterConfig.paramName + RANGE_FILTER_MAX_SUFFIX]: [max],
[this.filterConfig.paramName + RANGE_FILTER_MAX_SUFFIX]: [max], page: 1
page: 1 };
});
} }
ngOnDestroy(): void {
if (hasValue(this.sub)) {
this.sub.unsubscribe();
}
}
} }

View File

@@ -1,6 +1,6 @@
<a class="d-flex flex-row" <a class="d-flex flex-row"
[routerLink]="[getSearchLink()]" [routerLink]="[getSearchLink()]"
[queryParams]="removeQueryParams | async" queryParamsHandling="merge"> [queryParams]="removeQueryParams" queryParamsHandling="merge">
<input type="checkbox" [checked]="true" class="my-1 align-self-stretch"/> <input type="checkbox" [checked]="true" class="my-1 align-self-stretch"/>
<span class="filter-value pl-1">{{selectedValue}}</span> <span class="filter-value pl-1">{{selectedValue}}</span>
</a> </a>

View File

@@ -1,11 +1,19 @@
import { Observable, of as observableOf } from 'rxjs'; import {
import { map } from 'rxjs/operators'; combineLatest as observableCombineLatest,
import { Component, Input, OnInit } from '@angular/core'; Observable,
of as observableOf,
Subscription
} from 'rxjs';
import { delay, map } from 'rxjs/operators';
import { Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { FacetValue } from '../../../../search-service/facet-value.model'; import { FacetValue } from '../../../../search-service/facet-value.model';
import { SearchFilterConfig } from '../../../../search-service/search-filter-config.model'; import { SearchFilterConfig } from '../../../../search-service/search-filter-config.model';
import { SearchService } from '../../../../search-service/search.service'; import { SearchService } from '../../../../search-service/search.service';
import { SearchFilterService } from '../../search-filter.service'; import { SearchFilterService } from '../../search-filter.service';
import { hasValue } from '../../../../../shared/empty.util';
import { SearchOptions } from '../../../../search-options.model';
import { SearchConfigurationService } from '../../../../search-service/search-configuration.service';
@Component({ @Component({
selector: 'ds-search-facet-selected-option', selector: 'ds-search-facet-selected-option',
@@ -15,7 +23,7 @@ import { SearchFilterService } from '../../search-filter.service';
/** /**
* Represents a single option in a filter facet * Represents a single option in a filter facet
*/ */
export class SearchFacetSelectedOptionComponent implements OnInit { export class SearchFacetSelectedOptionComponent implements OnInit, OnDestroy {
/** /**
* A single value for this component * A single value for this component
*/ */
@@ -25,12 +33,14 @@ export class SearchFacetSelectedOptionComponent implements OnInit {
/** /**
* Emits the active values for this filter * Emits the active values for this filter
*/ */
selectedValues$: Observable<string[]>; @Input() selectedValues$: Observable<string[]>;
removeQueryParams; removeQueryParams;
sub: Subscription;
constructor(protected searchService: SearchService, constructor(protected searchService: SearchService,
protected filterService: SearchFilterService, protected filterService: SearchFilterService,
protected searchConfigService: SearchConfigurationService,
protected router: Router protected router: Router
) { ) {
} }
@@ -39,8 +49,10 @@ export class SearchFacetSelectedOptionComponent implements OnInit {
* Initializes all observable instance variables and starts listening to them * Initializes all observable instance variables and starts listening to them
*/ */
ngOnInit(): void { ngOnInit(): void {
this.selectedValues$ = this.filterService.getSelectedValuesForFilter(this.filterConfig); this.sub = observableCombineLatest(this.selectedValues$, this.searchConfigService.searchOptions)
this.removeQueryParams = this.getRemoveParams(); .subscribe(([selectedValues, searchOptions]) => {
this.updateRemoveParams(selectedValues)
});
} }
/** /**
@@ -55,13 +67,16 @@ export class SearchFacetSelectedOptionComponent implements OnInit {
* @param {string} value The value that is removed for this filter * @param {string} value The value that is removed for this filter
* @returns {Observable<any>} The changed filter parameters * @returns {Observable<any>} The changed filter parameters
*/ */
private getRemoveParams(): Observable<any> { private updateRemoveParams(selectedValues: string[]): void {
return this.selectedValues$.pipe(map((selectedValues) => { this.removeQueryParams = {
return { [this.filterConfig.paramName]: selectedValues.filter((v) => v !== this.selectedValue),
[this.filterConfig.paramName]: selectedValues.filter((v) => v !== this.selectedValue), page: 1
page: 1 };
}; }
}));
ngOnDestroy(): void {
if (hasValue(this.sub)) {
this.sub.unsubscribe();
}
} }
} }

View File

@@ -120,20 +120,6 @@ describe('SearchFacetFilterComponent', () => {
}); });
}); });
describe('when the getAddParams method is called wih a value', () => {
it('should return the selectedValue list with the new parameter value', () => {
const result = comp.getAddParams(value3);
result.subscribe((r) => expect(r[mockFilterConfig.paramName]).toEqual([value1, value2, value3]));
});
});
describe('when the getRemoveParams method is called wih a value', () => {
it('should return the selectedValue list with the parameter value left out', () => {
const result = comp.getRemoveParams(value1);
result.subscribe((r) => expect(r[mockFilterConfig.paramName]).toEqual([value2]));
});
});
describe('when the showMore method is called', () => { describe('when the showMore method is called', () => {
beforeEach(() => { beforeEach(() => {
spyOn(filterService, 'incrementPage'); spyOn(filterService, 'incrementPage');

View File

@@ -22,6 +22,7 @@ import { FILTER_CONFIG, SearchFilterService } from '../search-filter.service';
import { SearchConfigurationService } from '../../../search-service/search-configuration.service'; import { SearchConfigurationService } from '../../../search-service/search-configuration.service';
import { getSucceededRemoteData } from '../../../../core/shared/operators'; import { getSucceededRemoteData } from '../../../../core/shared/operators';
import { InputSuggestion } from '../../../../shared/input-suggestions/input-suggestions.model'; import { InputSuggestion } from '../../../../shared/input-suggestions/input-suggestions.model';
import { SearchOptions } from '../../../search-options.model';
@Component({ @Component({
selector: 'ds-search-facet-filter', selector: 'ds-search-facet-filter',
@@ -65,13 +66,14 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy {
/** /**
* Emits the active values for this filter * Emits the active values for this filter
*/ */
selectedValues: Observable<string[]>; selectedValues$: Observable<string[]>;
private collapseNextUpdate = true; private collapseNextUpdate = true;
/** /**
* State of the requested facets used to time the animation * State of the requested facets used to time the animation
*/ */
animationState = 'loading'; animationState = 'loading';
searchOptions$: Observable<SearchOptions>;
constructor(protected searchService: SearchService, constructor(protected searchService: SearchService,
protected filterService: SearchFilterService, protected filterService: SearchFilterService,
@@ -85,15 +87,13 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy {
* Initializes all observable instance variables and starts listening to them * Initializes all observable instance variables and starts listening to them
*/ */
ngOnInit(): void { ngOnInit(): void {
console.log('renderSearchFacetFilterComponent')
this.filterValues$ = new BehaviorSubject(new RemoteData(true, false, undefined, undefined, undefined)); this.filterValues$ = new BehaviorSubject(new RemoteData(true, false, undefined, undefined, undefined));
this.currentPage = this.getCurrentPage().pipe(distinctUntilChanged()); this.currentPage = this.getCurrentPage().pipe(distinctUntilChanged());
this.selectedValues = this.filterService.getSelectedValuesForFilter(this.filterConfig); this.selectedValues$ = this.filterService.getSelectedValuesForFilter(this.filterConfig);
const searchOptions = this.searchConfigService.searchOptions; this.searchOptions$ = this.searchConfigService.searchOptions;
this.subs.push(this.searchConfigService.searchOptions.subscribe(() => this.updateFilterValueList())); this.subs.push(this.searchOptions$.subscribe(() => this.updateFilterValueList()));
const facetValues = observableCombineLatest(searchOptions, this.currentPage).pipe( const facetValues = observableCombineLatest(this.searchOptions$, this.currentPage).pipe(
map(([options, page]) => { map(([options, page]) => {
return { options, page } return { options, page }
}), }),
@@ -193,8 +193,7 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy {
* @param data The string from the input field * @param data The string from the input field
*/ */
onSubmit(data: any) { onSubmit(data: any) {
console.log('onsubmit'); this.selectedValues$.pipe(take(1)).subscribe((selectedValues) => {
this.selectedValues.pipe(take(1)).subscribe((selectedValues) => {
if (isNotEmpty(data)) { if (isNotEmpty(data)) {
this.router.navigate([this.getSearchLink()], { this.router.navigate([this.getSearchLink()], {
queryParams: queryParams:
@@ -219,34 +218,6 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy {
return hasValue(o); return hasValue(o);
} }
/**
* Calculates the parameters that should change if a given value for this filter would be removed from the active filters
* @param {string} value The value that is removed for this filter
* @returns {Observable<any>} The changed filter parameters
*/
getRemoveParams(value: string): Observable<any> {
return this.selectedValues.pipe(map((selectedValues) => {
return {
[this.filterConfig.paramName]: selectedValues.filter((v) => v !== value),
page: 1
};
}));
}
/**
* Calculates the parameters that should change if a given value for this filter would be added to the active filters
* @param {string} value The value that is added for this filter
* @returns {Observable<any>} The changed filter parameters
*/
getAddParams(value: string): Observable<any> {
return this.selectedValues.pipe(map((selectedValues) => {
return {
[this.filterConfig.paramName]: [...selectedValues, value],
page: 1
};
}));
}
/** /**
* Unsubscribe from all subscriptions * Unsubscribe from all subscriptions
*/ */
@@ -263,7 +234,7 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy {
*/ */
findSuggestions(data): void { findSuggestions(data): void {
if (isNotEmpty(data)) { if (isNotEmpty(data)) {
this.searchConfigService.searchOptions.pipe(take(1)).subscribe( this.searchOptions$.pipe(take(1)).subscribe(
(options) => { (options) => {
this.filterSearchResults = this.searchService.getFacetValuesFor(this.filterConfig, 1, options, data.toLowerCase()) this.filterSearchResults = this.searchService.getFacetValuesFor(this.filterConfig, 1, options, data.toLowerCase())
.pipe( .pipe(
@@ -295,7 +266,6 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy {
return new EmphasizePipe().transform(facet.value, query) + ' (' + facet.count + ')'; return new EmphasizePipe().transform(facet.value, query) + ' (' + facet.count + ')';
} }
/** /**
* Prevent unnecessary rerendering * Prevent unnecessary rerendering
*/ */

View File

@@ -10,6 +10,7 @@ import { SearchService } from '../../search-service/search.service';
import { SearchFilterComponent } from './search-filter.component'; import { SearchFilterComponent } from './search-filter.component';
import { SearchFilterConfig } from '../../search-service/search-filter-config.model'; import { SearchFilterConfig } from '../../search-service/search-filter-config.model';
import { FilterType } from '../../search-service/filter-type.model'; import { FilterType } from '../../search-service/filter-type.model';
import { SearchConfigurationService } from '../../search-service/search-configuration.service';
describe('SearchFilterComponent', () => { describe('SearchFilterComponent', () => {
let comp: SearchFilterComponent; let comp: SearchFilterComponent;
@@ -33,9 +34,7 @@ describe('SearchFilterComponent', () => {
}, },
expand: (filter) => { expand: (filter) => {
}, },
initialCollapse: (filter) => { initializeFilter: (filter) => {
},
initialExpand: (filter) => {
}, },
getSelectedValuesForFilter: (filter) => { getSelectedValuesForFilter: (filter) => {
return observableOf([filterName1, filterName2, filterName3]) return observableOf([filterName1, filterName2, filterName3])
@@ -55,6 +54,8 @@ describe('SearchFilterComponent', () => {
getFacetValuesFor: (filter) => mockResults getFacetValuesFor: (filter) => mockResults
}; };
const searchConfigServiceStub = {};
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NoopAnimationsModule], imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NoopAnimationsModule],
@@ -65,6 +66,7 @@ describe('SearchFilterComponent', () => {
provide: SearchFilterService, provide: SearchFilterService,
useValue: mockFilterService useValue: mockFilterService
}, },
{ provide: SearchConfigurationService, useValue: searchConfigServiceStub },
], ],
schemas: [NO_ERRORS_SCHEMA] schemas: [NO_ERRORS_SCHEMA]
}).overrideComponent(SearchFilterComponent, { }).overrideComponent(SearchFilterComponent, {
@@ -91,32 +93,21 @@ describe('SearchFilterComponent', () => {
}); });
}); });
describe('when the initialCollapse method is triggered', () => { describe('when the initializeFilter method is triggered', () => {
beforeEach(() => { beforeEach(() => {
spyOn(filterService, 'initialCollapse'); spyOn(filterService, 'initializeFilter');
comp.initialCollapse(); comp.initializeFilter();
}); });
it('should call initialCollapse with the correct filter configuration name', () => { it('should call initialCollapse with the correct filter configuration name', () => {
expect(filterService.initialCollapse).toHaveBeenCalledWith(mockFilterConfig.name) expect(filterService.initializeFilter).toHaveBeenCalledWith(mockFilterConfig)
});
});
describe('when the initialExpand method is triggered', () => {
beforeEach(() => {
spyOn(filterService, 'initialExpand');
comp.initialExpand();
});
it('should call initialCollapse with the correct filter configuration name', () => {
expect(filterService.initialExpand).toHaveBeenCalledWith(mockFilterConfig.name)
}); });
}); });
describe('when getSelectedValues is called', () => { describe('when getSelectedValues is called', () => {
let valuesObservable: Observable<string[]>; let valuesObservable: Observable<string[]>;
beforeEach(() => { beforeEach(() => {
valuesObservable = comp.getSelectedValues(); valuesObservable = (comp as any).getSelectedValues();
}); });
it('should return an observable containing the existing filters', () => { it('should return an observable containing the existing filters', () => {
@@ -141,7 +132,7 @@ describe('SearchFilterComponent', () => {
let isActive: Observable<boolean>; let isActive: Observable<boolean>;
beforeEach(() => { beforeEach(() => {
filterService.isCollapsed = () => observableOf(true); filterService.isCollapsed = () => observableOf(true);
isActive = comp.isCollapsed(); isActive = (comp as any).isCollapsed();
}); });
it('should return an observable containing true', () => { it('should return an observable containing true', () => {
@@ -156,7 +147,7 @@ describe('SearchFilterComponent', () => {
let isActive: Observable<boolean>; let isActive: Observable<boolean>;
beforeEach(() => { beforeEach(() => {
filterService.isCollapsed = () => observableOf(false); filterService.isCollapsed = () => observableOf(false);
isActive = comp.isCollapsed(); isActive = (comp as any).isCollapsed();
}); });
it('should return an observable containing false', () => { it('should return an observable containing false', () => {

View File

@@ -1,10 +1,8 @@
import * as deepFreeze from 'deep-freeze'; import * as deepFreeze from 'deep-freeze';
import { import {
SearchFilterCollapseAction, SearchFilterExpandAction, SearchFilterIncrementPageAction, SearchFilterCollapseAction, SearchFilterExpandAction, SearchFilterIncrementPageAction,
SearchFilterInitialCollapseAction,
SearchFilterInitialExpandAction,
SearchFilterToggleAction, SearchFilterToggleAction,
SearchFilterDecrementPageAction, SearchFilterResetPageAction SearchFilterDecrementPageAction, SearchFilterResetPageAction, SearchFilterInitializeAction
} from './search-filter.actions'; } from './search-filter.actions';
import { filterReducer } from './search-filter.reducer'; import { filterReducer } from './search-filter.reducer';
@@ -98,35 +96,39 @@ describe('filterReducer', () => {
filterReducer(state, action); filterReducer(state, action);
}); });
it('should set filterCollapsed to true in response to the INITIAL_COLLAPSE action when no state has been set for this filter', () => { it('should set filterCollapsed to true in response to the INITIALIZE action with isOpenByDefault to false when no state has been set for this filter', () => {
const state = {}; const state = {};
state[filterName2] = { filterCollapsed: false, page: 1 }; state[filterName2] = { filterCollapsed: false, page: 1 };
const action = new SearchFilterInitialCollapseAction(filterName1); const filterConfig = {isOpenByDefault: false, name: filterName1} as any;
const action = new SearchFilterInitializeAction(filterConfig);
const newState = filterReducer(state, action); const newState = filterReducer(state, action);
expect(newState[filterName1].filterCollapsed).toEqual(true); expect(newState[filterName1].filterCollapsed).toEqual(true);
}); });
it('should set filterCollapsed to true in response to the INITIAL_EXPAND action when no state has been set for this filter', () => { it('should set filterCollapsed to false in response to the INITIALIZE action with isOpenByDefault to true when no state has been set for this filter', () => {
const state = {}; const state = {};
state[filterName2] = { filterCollapsed: true, page: 1 }; state[filterName2] = { filterCollapsed: true, page: 1 };
const action = new SearchFilterInitialExpandAction(filterName1); const filterConfig = {isOpenByDefault: true, name: filterName1} as any;
const action = new SearchFilterInitializeAction(filterConfig);
const newState = filterReducer(state, action); const newState = filterReducer(state, action);
expect(newState[filterName1].filterCollapsed).toEqual(false); expect(newState[filterName1].filterCollapsed).toEqual(false);
}); });
it('should not change the state in response to the INITIAL_COLLAPSE action when the state has already been set for this filter', () => { it('should not change the state in response to the INITIALIZE action with isOpenByDefault to false when the state has already been set for this filter', () => {
const state = {}; const state = {};
state[filterName1] = { filterCollapsed: false, page: 1 }; state[filterName1] = { filterCollapsed: false, page: 1 };
const action = new SearchFilterInitialCollapseAction(filterName1); const filterConfig = { isOpenByDefault: true, name: filterName1 } as any;
const action = new SearchFilterInitializeAction(filterConfig);
const newState = filterReducer(state, action); const newState = filterReducer(state, action);
expect(newState).toEqual(state); expect(newState).toEqual(state);
}); });
it('should not change the state in response to the INITIAL_EXPAND action when the state has already been set for this filter', () => { it('should not change the state in response to the INITIALIZE action with isOpenByDefault to true when the state has already been set for this filter', () => {
const state = {}; const state = {};
state[filterName1] = { filterCollapsed: true, page: 1 }; state[filterName1] = { filterCollapsed: true, page: 1 };
const action = new SearchFilterInitialExpandAction(filterName1); const filterConfig = { isOpenByDefault: false, name: filterName1 } as any;
const action = new SearchFilterInitializeAction(filterConfig);
const newState = filterReducer(state, action); const newState = filterReducer(state, action);
expect(newState).toEqual(state); expect(newState).toEqual(state);
}); });

View File

@@ -71,7 +71,6 @@ describe('SearchFilterService', () => {
}); });
}); });
describe('when the collapse method is triggered', () => { describe('when the collapse method is triggered', () => {
beforeEach(() => { beforeEach(() => {
service.collapse(mockFilterConfig.name); service.collapse(mockFilterConfig.name);

View File

@@ -1,6 +1,6 @@
import { combineLatest as observableCombineLatest, Observable } from 'rxjs'; import { combineLatest as observableCombineLatest, Observable } from 'rxjs';
import { Injectable, InjectionToken } from '@angular/core'; import { Injectable, InjectionToken } from '@angular/core';
import { distinctUntilChanged, map, tap } from 'rxjs/operators'; import { distinctUntilChanged, map } from 'rxjs/operators';
import { SearchFiltersState, SearchFilterState } from './search-filter.reducer'; import { SearchFiltersState, SearchFilterState } from './search-filter.reducer';
import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store'; import { createSelector, MemoizedSelector, select, Store } from '@ngrx/store';
import { import {
@@ -16,9 +16,8 @@ import { hasValue, isNotEmpty, } from '../../../shared/empty.util';
import { SearchFilterConfig } from '../../search-service/search-filter-config.model'; import { SearchFilterConfig } from '../../search-service/search-filter-config.model';
import { RouteService } from '../../../shared/services/route.service'; import { RouteService } from '../../../shared/services/route.service';
import { Params } from '@angular/router'; import { Params } from '@angular/router';
import { tag } from 'rxjs-spy/operators'; import { SearchOptions } from '../../search-options.model';
import { create, detect } from "rxjs-spy"; // const spy = create();
const spy = create();
const filterStateSelector = (state: SearchFiltersState) => state.searchFilter; const filterStateSelector = (state: SearchFiltersState) => state.searchFilter;
export const FILTER_CONFIG: InjectionToken<SearchFilterConfig> = new InjectionToken<SearchFilterConfig>('filterConfig'); export const FILTER_CONFIG: InjectionToken<SearchFilterConfig> = new InjectionToken<SearchFilterConfig>('filterConfig');
@@ -59,17 +58,10 @@ export class SearchFilterService {
* @returns {Observable<string[]>} Emits the active filters for the given filter configuration * @returns {Observable<string[]>} Emits the active filters for the given filter configuration
*/ */
getSelectedValuesForFilter(filterConfig: SearchFilterConfig): Observable<string[]> { getSelectedValuesForFilter(filterConfig: SearchFilterConfig): Observable<string[]> {
const values$ = this.routeService.getQueryParameterValues(filterConfig.paramName).pipe( const values$ = this.routeService.getQueryParameterValues(filterConfig.paramName);
tag("parameter")
);
const prefixValues$ = this.routeService.getQueryParamsWithPrefix(filterConfig.paramName + '.').pipe( const prefixValues$ = this.routeService.getQueryParamsWithPrefix(filterConfig.paramName + '.').pipe(
map((params: Params) => [].concat(...Object.values(params))), map((params: Params) => [].concat(...Object.values(params))),
tag("prefix-tag")
); );
spy.log();
detect('prefix-tag');
return observableCombineLatest(values$, prefixValues$).pipe( return observableCombineLatest(values$, prefixValues$).pipe(
map(([values, prefixValues]) => { map(([values, prefixValues]) => {

View File

@@ -1,15 +1,9 @@
<div> <div>
<div class="filters py-2"> <div class="filters py-2">
<a *ngFor="let value of (selectedValues | async)" class="d-flex flex-row" <ds-search-facet-selected-option *ngFor="let value of (selectedValues$ | async)" [selectedValue]="value" [filterConfig]="filterConfig" [selectedValues$]="selectedValues$"></ds-search-facet-selected-option>
[routerLink]="[getSearchLink()]"
[queryParams]="getRemoveParams(value) | async" queryParamsHandling="merge">
<input type="checkbox" [checked]="true" class="my-1 align-self-stretch"/>
<span class="filter-value pl-1">{{value}}</span>
</a>
<ng-container *ngFor="let page of (filterValues$ | async)?.payload"> <ng-container *ngFor="let page of (filterValues$ | async)?.payload">
<div [@facetLoad]="animationState"> <div [@facetLoad]="animationState">
<ds-search-facet-option *ngFor="let value of page.page; trackBy: trackUpdate" [filterConfig]="filterConfig" [filterValue]="value"> <ds-search-facet-option *ngFor="let value of page.page; trackBy: trackUpdate" [filterConfig]="filterConfig" [filterValue]="value" [selectedValues$]="selectedValues$"></ds-search-facet-option>
</ds-search-facet-option>
</div> </div>
</ng-container> </ng-container>
<div class="clearfix toggle-more-filters"> <div class="clearfix toggle-more-filters">

View File

@@ -24,9 +24,7 @@
</ng-container> </ng-container>
<ng-container *ngFor="let page of (filterValues$ | async)?.payload"> <ng-container *ngFor="let page of (filterValues$ | async)?.payload">
<div [@facetLoad]="animationState"> <div [@facetLoad]="animationState">
<ng-container *ngFor="let value of page.page; trackBy: trackUpdate"> <ds-search-facet-range-option *ngFor="let value of page.page; trackBy: trackUpdate" [filterConfig]="filterConfig" [filterValue]="value"></ds-search-facet-range-option>
</ng-container>
</div> </div>
</ng-container> </ng-container>
</div> </div>

View File

@@ -106,16 +106,6 @@ describe('SearchRangeFilterComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
}); });
describe('when the getChangeParams method is called wih a value', () => {
it('should return the selectedValue list with the new parameter value', () => {
const result$ = comp.getChangeParams(value3);
result$.subscribe((result) => {
expect(result[mockFilterConfig.paramName + minSuffix]).toEqual(['1990']);
expect(result[mockFilterConfig.paramName + maxSuffix]).toEqual(['1992']);
});
});
});
describe('when the onSubmit method is called with data', () => { describe('when the onSubmit method is called with data', () => {
const searchUrl = '/search/path'; const searchUrl = '/search/path';
// const data = { [mockFilterConfig.paramName + minSuffix]: '1900', [mockFilterConfig.paramName + maxSuffix]: '1950' }; // const data = { [mockFilterConfig.paramName + minSuffix]: '1900', [mockFilterConfig.paramName + maxSuffix]: '1950' };

View File

@@ -95,8 +95,6 @@ export class SearchRangeFilterComponent extends SearchFacetFilterComponent imple
).subscribe((minmax) => this.range = minmax); ).subscribe((minmax) => this.range = minmax);
} }
/** /**
* Submits new custom range values to the range filter from the widget * Submits new custom range values to the range filter from the widget
*/ */

View File

@@ -1,19 +1,9 @@
<div> <div>
<div class="filters py-2"> <div class="filters py-2">
<a *ngFor="let value of (selectedValues | async)" class="d-flex flex-row" <ds-search-facet-selected-option *ngFor="let value of (selectedValues$ | async)" [selectedValue]="value" [filterConfig]="filterConfig" [selectedValues$]="selectedValues$"></ds-search-facet-selected-option>
[routerLink]="[getSearchLink()]" <ng-container *ngFor="let page of (filterValues$ | async)?.payload">
[queryParams]="getRemoveParams(value) | async" queryParamsHandling="merge">
<input type="checkbox" [checked]="true" class="my-1 align-self-stretch"/>
<span class="filter-value pl-1">{{value}}</span>
</a>
<ng-container *ngVar="(filterValues$ | async) as filterValuesRD">
<div [@facetLoad]="animationState"> <div [@facetLoad]="animationState">
<ng-container *ngFor="let page of filterValuesRD?.payload"> <ds-search-facet-option *ngFor="let value of page.page; trackBy: trackUpdate" [filterConfig]="filterConfig" [filterValue]="value" [selectedValues$]="selectedValues$"></ds-search-facet-option>
<ng-container *ngFor="let value of page.page; trackBy: trackUpdate">
<ds-search-facet-option *ngFor="let value of page.page; trackBy: trackUpdate" [filterConfig]="filterConfig" [filterValue]="value">
</ds-search-facet-option>
</ng-container>
</ng-container>
</div> </div>
</ng-container> </ng-container>
<div class="clearfix toggle-more-filters"> <div class="clearfix toggle-more-filters">

View File

@@ -117,7 +117,7 @@ describe('SearchConfigurationService', () => {
describe('when subscribeToSearchOptions is called', () => { describe('when subscribeToSearchOptions is called', () => {
beforeEach(() => { beforeEach(() => {
service.subscribeToSearchOptions(defaults) (service as any).subscribeToSearchOptions(defaults)
}); });
it('should call all getters it needs, but not call any others', () => { it('should call all getters it needs, but not call any others', () => {
expect(service.getCurrentPagination).not.toHaveBeenCalled(); expect(service.getCurrentPagination).not.toHaveBeenCalled();
@@ -131,7 +131,7 @@ describe('SearchConfigurationService', () => {
describe('when subscribeToPaginatedSearchOptions is called', () => { describe('when subscribeToPaginatedSearchOptions is called', () => {
beforeEach(() => { beforeEach(() => {
service.subscribeToPaginatedSearchOptions(defaults); (service as any).subscribeToPaginatedSearchOptions(defaults);
}); });
it('should call all getters it needs', () => { it('should call all getters it needs', () => {
expect(service.getCurrentPagination).toHaveBeenCalled(); expect(service.getCurrentPagination).toHaveBeenCalled();

View File

@@ -186,7 +186,7 @@ export class SearchConfigurationService implements OnDestroy {
* @param {SearchOptions} defaults Default values for when no parameters are available * @param {SearchOptions} defaults Default values for when no parameters are available
* @returns {Subscription} The subscription to unsubscribe from * @returns {Subscription} The subscription to unsubscribe from
*/ */
subscribeToSearchOptions(defaults: SearchOptions): Subscription { private subscribeToSearchOptions(defaults: SearchOptions): Subscription {
return observableMerge( return observableMerge(
this.getScopePart(defaults.scope), this.getScopePart(defaults.scope),
this.getQueryPart(defaults.query), this.getQueryPart(defaults.query),
@@ -204,7 +204,7 @@ export class SearchConfigurationService implements OnDestroy {
* @param {PaginatedSearchOptions} defaults Default values for when no parameters are available * @param {PaginatedSearchOptions} defaults Default values for when no parameters are available
* @returns {Subscription} The subscription to unsubscribe from * @returns {Subscription} The subscription to unsubscribe from
*/ */
subscribeToPaginatedSearchOptions(defaults: PaginatedSearchOptions): Subscription { private subscribeToPaginatedSearchOptions(defaults: PaginatedSearchOptions): Subscription {
return observableMerge( return observableMerge(
this.getPaginationPart(defaults.pagination), this.getPaginationPart(defaults.pagination),
this.getSortPart(defaults.sort), this.getSortPart(defaults.sort),

View File

@@ -33,7 +33,7 @@ const entryFromSelfLinkSelector =
(state: ObjectCacheState) => state[selfLink], (state: ObjectCacheState) => state[selfLink],
); );
/** /**
* A service to interact with the object cache * A service to interact with the object cache
*/ */
@Injectable() @Injectable()

View File

@@ -15,7 +15,6 @@ export class RouteService {
} }
getQueryParameterValues(paramName: string): Observable<string[]> { getQueryParameterValues(paramName: string): Observable<string[]> {
console.log('called');
return this.route.queryParamMap.pipe( return this.route.queryParamMap.pipe(
map((params) => [...params.getAll(paramName)]), map((params) => [...params.getAll(paramName)]),
distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b)) distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b))
@@ -46,7 +45,6 @@ export class RouteService {
getQueryParamsWithPrefix(prefix: string): Observable<Params> { getQueryParamsWithPrefix(prefix: string): Observable<Params> {
return this.route.queryParamMap.pipe( return this.route.queryParamMap.pipe(
map((qparams) => { map((qparams) => {
console.log('map');
const params = {}; const params = {};
qparams.keys qparams.keys
.filter((key) => key.startsWith(prefix)) .filter((key) => key.startsWith(prefix))
@@ -55,8 +53,7 @@ export class RouteService {
}); });
return params; return params;
}), }),
distinctUntilChanged((a, b) => { console.log('changed?', a, b, JSON.stringify(a) === JSON.stringify(b)); return JSON.stringify(a) === JSON.stringify(b)}), distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b)),
tap((t) => console.log('changed'))
); );
} }
} }

View File

@@ -12,7 +12,6 @@ import { BrowserAppModule } from './modules/app/browser-app.module';
import { ENV_CONFIG } from './config'; import { ENV_CONFIG } from './config';
if (ENV_CONFIG.production) { if (ENV_CONFIG.production) {
enableProdMode(); enableProdMode();
} }