Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@
"pem": "1.12.3",
"reflect-metadata": "0.1.12",
"rxjs": "6.2.2",
"rxjs-spy": "^7.5.1",
"sortablejs": "1.7.0",
"text-mask-core": "5.0.1",
"ts-loader": "^5.2.1",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,9 @@
<div>
<div class="filters py-2">
<a *ngFor="let value of (selectedValues | async)" class="d-flex flex-row"
[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>
<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">
<div [@facetLoad]="animationState">
<ng-container *ngFor="let value of page.page; let i=index">
<a *ngIf="!(selectedValues | async).includes(value.value)" class="d-flex flex-row"
[routerLink]="[getSearchLink()]"
[queryParams]="getAddParams(value.value) | async" queryParamsHandling="merge">
<input type="checkbox" [checked]="false" class="my-1 align-self-stretch"/>
<span class="filter-value px-1">{{value.value}}</span>
<span class="float-right filter-value-count ml-auto">
<span class="badge badge-secondary badge-pill">{{value.count}}</span>
</span>
</a>
</ng-container>
<ds-search-facet-option *ngFor="let value of page.page; trackBy: trackUpdate" [filterConfig]="filterConfig" [filterValue]="value" [selectedValues$]="selectedValues$"></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
@@ -0,0 +1,9 @@
<a *ngIf="isVisible | async" class="d-flex flex-row"
[routerLink]="[getSearchLink()]"
[queryParams]="addQueryParams" queryParamsHandling="merge">
<input type="checkbox" [checked]="false" class="my-1 align-self-stretch"/>
<span class="filter-value px-1">{{filterValue.value}}</span>
<span class="float-right filter-value-count ml-auto">
<span class="badge badge-secondary badge-pill">{{filterValue.count}}</span>
</span>
</a>
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { TranslateModule } from '@ngx-translate/core';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { SearchFacetOptionComponent } from './search-facet-option.component';
import { SearchFilterConfig } from '../../../../search-service/search-filter-config.model';
import { FilterType } from '../../../../search-service/filter-type.model';
import { FacetValue } from '../../../../search-service/facet-value.model';
import { FormsModule } from '@angular/forms';
import { of as observableOf } from 'rxjs';
import { SearchService } from '../../../../search-service/search.service';
import { SearchServiceStub } from '../../../../../shared/testing/search-service-stub';
import { Router } from '@angular/router';
import { RouterStub } from '../../../../../shared/testing/router-stub';
import { SearchConfigurationService } from '../../../../search-service/search-configuration.service';
import { SearchFilterService } from '../../search-filter.service';
import { By } from '@angular/platform-browser';

describe('SearchFacetOptionComponent', () => {
let comp: SearchFacetOptionComponent;
let fixture: ComponentFixture<SearchFacetOptionComponent>;
const filterName1 = 'test name';
const value1 = 'testvalue1';
const value2 = 'test2';
const value3 = 'another value3';
const mockFilterConfig = Object.assign(new SearchFilterConfig(), {
name: filterName1,
type: FilterType.range,
hasFacets: false,
isOpenByDefault: false,
pageSize: 2,
minValue: 200,
maxValue: 3000,
});
const value: FacetValue = {
value: value2,
count: 20,
search: ''
};

const searchLink = '/search';
const selectedValues = [value1];
const selectedValues$ = observableOf(selectedValues);
let filterService;
let searchService;
let router;
const page = observableOf(0);

beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [TranslateModule.forRoot(), NoopAnimationsModule, FormsModule],
declarations: [SearchFacetOptionComponent],
providers: [
{ provide: SearchService, useValue: new SearchServiceStub(searchLink) },
{ provide: Router, useValue: new RouterStub() },
{
provide: SearchConfigurationService, useValue: {
searchOptions: observableOf({})
}
},
{
provide: SearchFilterService, useValue: {
getSelectedValuesForFilter: () => selectedValues,
isFilterActiveWithValue: (paramName: string, filterValue: string) => observableOf(true),
getPage: (paramName: string) => page,
/* tslint:disable:no-empty */
incrementPage: (filterName: string) => {
},
resetPage: (filterName: string) => {
}
/* tslint:enable:no-empty */
}
}
],
schemas: [NO_ERRORS_SCHEMA]
}).overrideComponent(SearchFacetOptionComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
}).compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(SearchFacetOptionComponent);
comp = fixture.componentInstance; // SearchPageComponent test instance
filterService = (comp as any).filterService;
searchService = (comp as any).searchService;
router = (comp as any).router;
comp.filterValue = value;
comp.selectedValues$ = selectedValues$;
comp.filterConfig = mockFilterConfig;
fixture.detectChanges();
});

describe('when the updateAddParams method is called wih a value', () => {
it('should update the addQueryParams with the new parameter values', () => {
comp.addQueryParams = {};
(comp as any).updateAddParams(selectedValues);
expect(comp.addQueryParams).toEqual({
[mockFilterConfig.paramName]: [value1, value.value],
page: 1
});
});
});

describe('when isVisible emits true', () => {
it('the facet option should be visible', () => {
comp.isVisible = observableOf(true);
fixture.detectChanges();
const linkEl = fixture.debugElement.query(By.css('a'));
expect(linkEl).not.toBeNull();
});
});

describe('when isVisible emits false', () => {
it('the facet option should not be visible', () => {
comp.isVisible = observableOf(false);
fixture.detectChanges();
const linkEl = fixture.debugElement.query(By.css('a'));
expect(linkEl).toBeNull();
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { combineLatest as observableCombineLatest, Observable, Subscription } from 'rxjs';
import { map, take } from 'rxjs/operators';
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { FacetValue } from '../../../../search-service/facet-value.model';
import { SearchFilterConfig } from '../../../../search-service/search-filter-config.model';
import { SearchService } from '../../../../search-service/search.service';
import { SearchFilterService } from '../../search-filter.service';
import { SearchConfigurationService } from '../../../../search-service/search-configuration.service';
import { hasValue } from '../../../../../shared/empty.util';

@Component({
selector: 'ds-search-facet-option',
templateUrl: './search-facet-option.component.html',
})

/**
* Represents a single option in a filter facet
*/
export class SearchFacetOptionComponent implements OnInit, OnDestroy {
/**
* A single value for this component
*/
@Input() filterValue: FacetValue;

/**
* The filter configuration for this facet option
*/
@Input() filterConfig: SearchFilterConfig;

/**
* Emits the active values for this filter
*/
@Input() selectedValues$: Observable<string[]>;

/**
* Emits true when this option should be visible and false when it should be invisible
*/
isVisible: Observable<boolean>;

/**
* UI parameters when this filter is added
*/
addQueryParams;

/**
* Subscription to unsubscribe from on destroy
*/
sub: Subscription;

constructor(protected searchService: SearchService,
protected filterService: SearchFilterService,
protected searchConfigService: SearchConfigurationService,
protected router: Router
) {
}

/**
* Initializes all observable instance variables and starts listening to them
*/
ngOnInit(): void {
this.isVisible = this.isChecked().pipe(map((checked: boolean) => !checked));
this.sub = observableCombineLatest(this.selectedValues$, this.searchConfigService.searchOptions)
.subscribe(([selectedValues, searchOptions]) => {
this.updateAddParams(selectedValues)
});
}

/**
* Checks if a value for this filter is currently active
*/
private isChecked(): Observable<boolean> {
return this.filterService.isFilterActiveWithValue(this.filterConfig.paramName, this.filterValue.value);
}

/**
* @returns {string} The base path to the search page
*/
getSearchLink() {
return this.searchService.getSearchLink();
}

/**
* Calculates the parameters that should change if a given value for this filter would be added to the active filters
* @param {string[]} selectedValues The values that are currently selected for this filter
*/
private updateAddParams(selectedValues: string[]): void {
this.addQueryParams = {
[this.filterConfig.paramName]: [...selectedValues, this.filterValue.value],
page: 1
};
}

/**
* Make sure the subscription is unsubscribed from when this component is destroyed
*/
ngOnDestroy(): void {
if (hasValue(this.sub)) {
this.sub.unsubscribe();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<a *ngIf="isVisible | async" class="d-flex flex-row"
[routerLink]="[getSearchLink()]"
[queryParams]="changeQueryParams" queryParamsHandling="merge">
<span class="filter-value px-1">{{filterValue.value}}</span>
<span class="float-right filter-value-count ml-auto">
<span class="badge badge-secondary badge-pill">{{filterValue.count}}</span>
</span>
</a>
Loading