almost finished date widget

This commit is contained in:
Lotte Hofstede
2018-05-15 11:03:18 +02:00
parent ce08f52b94
commit 95060ee573
18 changed files with 205 additions and 524 deletions

View File

@@ -104,9 +104,9 @@
"methods": "1.1.2", "methods": "1.1.2",
"moment": "^2.22.1", "moment": "^2.22.1",
"morgan": "1.9.0", "morgan": "1.9.0",
"ng2-nouislider": "^1.7.7", "ng2-nouislider": "1.7.8",
"ngx-pagination": "3.0.3", "ngx-pagination": "3.0.3",
"nouislider": "^10.0.0", "nouislider": "^11.0.0",
"pem": "1.12.3", "pem": "1.12.3",
"reflect-metadata": "0.1.12", "reflect-metadata": "0.1.12",
"rxjs": "5.5.6", "rxjs": "5.5.6",

View File

@@ -1,8 +1,7 @@
import { Component, InjectionToken, Injector, Input, OnInit } from '@angular/core'; import { Component, Injector, Input, OnInit } from '@angular/core';
import { renderFilterType } from '../search-filter-type-decorator'; import { renderFilterType } from '../search-filter-type-decorator';
import { FilterType } from '../../../search-service/filter-type.model'; import { FilterType } from '../../../search-service/filter-type.model';
import { SearchFilterConfig } from '../../../search-service/search-filter-config.model'; import { SearchFilterConfig } from '../../../search-service/search-filter-config.model';
import { GlobalConfig } from '../../../../../config/global-config.interface';
import { FILTER_CONFIG, SELECTED_VALUES } from '../search-filter.service'; import { FILTER_CONFIG, SELECTED_VALUES } from '../search-filter.service';
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs/Observable';

View File

@@ -2,7 +2,7 @@ import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { SearchFilterService } from '../search-filter.service'; import { FILTER_CONFIG, SearchFilterService, SELECTED_VALUES } from '../search-filter.service';
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 { FacetValue } from '../../../search-service/facet-value.model'; import { FacetValue } from '../../../search-service/facet-value.model';
@@ -17,7 +17,7 @@ import { RouterStub } from '../../../../shared/testing/router-stub';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { BehaviorSubject } from 'rxjs/BehaviorSubject'; import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { PageInfo } from '../../../../core/shared/page-info.model'; import { PageInfo } from '../../../../core/shared/page-info.model';
import { SearchFacetFilterComponent } from '../search-facet-filter/search-facet-filter.component'; import { SearchFacetFilterComponent } from './search-facet-filter.component';
describe('SearchFacetFilterComponent', () => { describe('SearchFacetFilterComponent', () => {
let comp: SearchFacetFilterComponent; let comp: SearchFacetFilterComponent;
@@ -64,6 +64,8 @@ describe('SearchFacetFilterComponent', () => {
providers: [ providers: [
{ provide: SearchService, useValue: new SearchServiceStub(searchLink) }, { provide: SearchService, useValue: new SearchServiceStub(searchLink) },
{ provide: Router, useValue: new RouterStub() }, { provide: Router, useValue: new RouterStub() },
{ provide: FILTER_CONFIG, useValue: new SearchFilterConfig()},
{ provide: SELECTED_VALUES, useValue: {} },
{ {
provide: SearchFilterService, useValue: { provide: SearchFilterService, useValue: {
isFilterActiveWithValue: (paramName: string, filterValue: string) => true, isFilterActiveWithValue: (paramName: string, filterValue: string) => true,

View File

@@ -1,7 +1,7 @@
<div> <div>
<div (click)="toggle()" class="filter-name"><h5 class="d-inline-block mb-0">{{'search.filters.filter.' + filter.name + '.head'| translate}}</h5> <span class="filter-toggle fa float-right" <div (click)="toggle()" class="filter-name"><h5 class="d-inline-block mb-0">{{'search.filters.filter.' + filter.name + '.head'| translate}}</h5> <span class="filter-toggle fa float-right"
[ngClass]="(isCollapsed() | async) ? 'fa-plus' : 'fa-minus'"></span></div> [ngClass]="(isCollapsed() | async) ? 'fa-plus' : 'fa-minus'"></span></div>
<div [@slide]="(isCollapsed() | async) ? 'collapsed' : 'expanded'" class="search-filter-wrapper"> <div [@slide]="(isCollapsed() | async) ? 'collapsed' : 'expanded'" class="search-filter-wrapper" [ngClass]="{'closed' : (isCollapsed() | async)}">
<ds-search-facet-filter-wrapper [filterConfig]="filter" [selectedValues]="getSelectedValues()"></ds-search-facet-filter-wrapper> <ds-search-facet-filter-wrapper [filterConfig]="filter" [selectedValues]="getSelectedValues()"></ds-search-facet-filter-wrapper>
</div> </div>
</div> </div>

View File

@@ -3,7 +3,7 @@
:host { :host {
border: 1px solid map-get($theme-colors, light); border: 1px solid map-get($theme-colors, light);
.search-filter-wrapper { .search-filter-wrapper.closed {
overflow: hidden; overflow: hidden;
} }
.filter-toggle { .filter-toggle {

View File

@@ -1,241 +0,0 @@
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 { SearchFilterService } from '../search-filter.service';
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 { Observable } from 'rxjs/Observable';
import { SearchService } from '../../../search-service/search.service';
import { SearchServiceStub } from '../../../../shared/testing/search-service-stub';
import { RemoteData } from '../../../../core/data/remote-data';
import { PaginatedList } from '../../../../core/data/paginated-list';
import { SearchOptions } from '../../../search-options.model';
import { RouterStub } from '../../../../shared/testing/router-stub';
import { Router } from '@angular/router';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { PageInfo } from '../../../../core/shared/page-info.model';
import { SearchFacetFilterComponent } from '../search-facet-filter/search-facet-filter.component';
describe('SearchFacetFilterComponent', () => {
let comp: SearchFacetFilterComponent;
let fixture: ComponentFixture<SearchFacetFilterComponent>;
const filterName1 = 'test name';
const value1 = 'testvalue1';
const value2 = 'test2';
const value3 = 'another value3';
const mockFilterConfig: SearchFilterConfig = Object.assign(new SearchFilterConfig(), {
name: filterName1,
type: FilterType.text,
hasFacets: false,
isOpenByDefault: false,
pageSize: 2
});
const values: FacetValue[] = [
{
value: value1,
count: 52,
search: ''
}, {
value: value2,
count: 20,
search: ''
}, {
value: value3,
count: 5,
search: ''
}
];
const searchLink = '/search';
const selectedValues = [value1, value2];
let filterService;
let searchService;
let router;
const page = Observable.of(0);
const mockValues = Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), values)));
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [TranslateModule.forRoot(), NoopAnimationsModule, FormsModule],
declarations: [SearchFacetFilterComponent],
providers: [
{ provide: SearchService, useValue: new SearchServiceStub(searchLink) },
{ provide: Router, useValue: new RouterStub() },
{
provide: SearchFilterService, useValue: {
isFilterActiveWithValue: (paramName: string, filterValue: string) => true,
getPage: (paramName: string) => page,
/* tslint:disable:no-empty */
incrementPage: (filterName: string) => {
},
resetPage: (filterName: string) => {
},
getSearchOptions: () => Observable.of({}),
/* tslint:enable:no-empty */
}
}
],
schemas: [NO_ERRORS_SCHEMA]
}).overrideComponent(SearchFacetFilterComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SearchFacetFilterComponent);
comp = fixture.componentInstance; // SearchPageComponent test instance
comp.filterConfig = mockFilterConfig;
comp.filterValues = [mockValues];
comp.filterValues$ = new BehaviorSubject(comp.filterValues);
comp.selectedValues = selectedValues;
filterService = (comp as any).filterService;
searchService = (comp as any).searchService;
spyOn(searchService, 'getFacetValuesFor').and.returnValue(mockValues);
router = (comp as any).router;
fixture.detectChanges();
});
describe('when the isChecked method is called with a value', () => {
beforeEach(() => {
spyOn(filterService, 'isFilterActiveWithValue');
comp.isChecked(values[1]);
});
it('should call isFilterActiveWithValue on the filterService with the correct filter parameter name and the passed value', () => {
expect(filterService.isFilterActiveWithValue).toHaveBeenCalledWith(mockFilterConfig.paramName, values[1].value)
});
});
describe('when the getSearchLink method is triggered', () => {
let link: string;
beforeEach(() => {
link = comp.getSearchLink();
});
it('should return the value of the searchLink variable in the filter service', () => {
expect(link).toEqual(searchLink);
});
});
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);
expect(result[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);
expect(result[mockFilterConfig.paramName]).toEqual([value2]);
});
});
describe('when the showMore method is called', () => {
beforeEach(() => {
spyOn(filterService, 'incrementPage');
comp.showMore();
});
it('should call incrementPage on the filterService with the correct filter parameter name', () => {
expect(filterService.incrementPage).toHaveBeenCalledWith(mockFilterConfig.name)
});
});
describe('when the showFirstPageOnly method is called', () => {
beforeEach(() => {
spyOn(filterService, 'resetPage');
comp.showFirstPageOnly();
});
it('should call resetPage on the filterService with the correct filter parameter name', () => {
expect(filterService.resetPage).toHaveBeenCalledWith(mockFilterConfig.name);
});
});
describe('when the getCurrentPage method is called', () => {
beforeEach(() => {
spyOn(filterService, 'getPage');
comp.getCurrentPage();
});
it('should call getPage on the filterService with the correct filter parameter name', () => {
expect(filterService.getPage).toHaveBeenCalledWith(mockFilterConfig.name)
});
});
describe('when the getCurrentUrl method is called', () => {
const url = 'test.url/test'
beforeEach(() => {
router.navigateByUrl(url);
});
it('should call getPage on the filterService with the correct filter parameter name', () => {
expect(router.url).toEqual(url);
});
});
describe('when the onSubmit method is called with data', () => {
const searchUrl = '/search/path';
const testValue = 'test';
const data = { [mockFilterConfig.paramName]: testValue };
beforeEach(() => {
spyOn(comp, 'getSearchLink').and.returnValue(searchUrl);
comp.onSubmit(data);
});
it('should call navigate on the router with the right searchlink and parameters', () => {
expect(router.navigate).toHaveBeenCalledWith([searchUrl], {
queryParams: { [mockFilterConfig.paramName]: [...selectedValues, testValue] },
queryParamsHandling: 'merge'
});
});
});
describe('when updateFilterValueList is called', () => {
const cPage = 10;
const searchOptions = new SearchOptions();
beforeEach(() => {
// spyOn(searchService, 'getFacetValuesFor'); Already spied upon
comp.currentPage = Observable.of(cPage);
comp.updateFilterValueList(searchOptions);
});
it('should call getFacetValuesFor on the searchService with the correct parameters', () => {
expect(searchService.getFacetValuesFor).toHaveBeenCalledWith(mockFilterConfig, cPage, searchOptions);
});
});
describe('when updateFilterValueList is called and pageChange is set to true', () => {
const searchOptions = new SearchOptions();
beforeEach(() => {
comp.pageChange = true;
spyOn(comp, 'showFirstPageOnly');
comp.updateFilterValueList(searchOptions);
});
it('should not call showFirstPageOnly on the component', () => {
expect(comp.showFirstPageOnly).not.toHaveBeenCalled();
});
it('should set pageChange to false', () => {
expect(comp.pageChange).toBeFalsy();
});
});
describe('when updateFilterValueList is called and pageChange is set to false', () => {
const searchOptions = new SearchOptions();
beforeEach(() => {
comp.pageChange = false;
spyOn(comp, 'showFirstPageOnly');
comp.updateFilterValueList(searchOptions);
});
it('should call showFirstPageOnly on the component', () => {
expect(comp.showFirstPageOnly).toHaveBeenCalled();
});
});
});

View File

@@ -20,6 +20,4 @@ import { SearchFacetFilterComponent } from '../search-facet-filter/search-facet-
@renderFacetFor(FilterType.hierarchy) @renderFacetFor(FilterType.hierarchy)
export class SearchHierarchyFilterComponent extends SearchFacetFilterComponent implements OnInit { export class SearchHierarchyFilterComponent extends SearchFacetFilterComponent implements OnInit {
currentPage: Observable<number>; currentPage: Observable<number>;
} }

View File

@@ -1,3 +1,4 @@
<script src="search-range-filter.component.ts"></script>
<div> <div>
<div class="filters"> <div class="filters">
<form #form="ngForm" (ngSubmit)="onSubmit(form.value)" class="add-filter row" <form #form="ngForm" (ngSubmit)="onSubmit(form.value)" class="add-filter row"
@@ -17,7 +18,7 @@
<input type="submit" class="d-none"/> <input type="submit" class="d-none"/>
</form> </form>
<nouislider [connect]="true" [min]="min" [max]="max" [step]="1" <nouislider [connect]="true" [min]="min" [max]="max" [step]="1"
[(ngModel)]="range" (mouseup)="onSubmit(form.value)"></nouislider> [(ngModel)]="range" (mouseup)="onSubmit(form.value)" ngDefaultControl></nouislider>
<ng-container *ngFor="let page of (filterValues$ | async)"> <ng-container *ngFor="let page of (filterValues$ | async)">
<ng-container *ngFor="let value of (page | async)?.payload.page"> <ng-container *ngFor="let value of (page | async)?.payload.page">

View File

@@ -1,14 +1,38 @@
@import '../../../../../styles/variables.scss'; @import '../../../../../styles/variables.scss';
@import '../../../../../styles/mixins.scss'; @import '../../../../../styles/mixins.scss';
.filters { .filters {
margin-top: $spacer/2;
margin-bottom: $spacer/2;
a {
color: $link-color;
&:hover {
text-decoration: underline;
color: $link-hover-color;
a { }
color: $link-color; }
&:hover { .toggle-more-filters a {
text-decoration: underline; color: $link-color;
color: $link-hover-color; text-decoration: underline;
cursor: pointer;
}
}
$slider-handle-width: 18px;
::ng-deep
{
html:not([dir=rtl]) .noUi-horizontal .noUi-handle {
right: -$slider-handle-width/2;
}
.noUi-horizontal .noUi-handle {
width: $slider-handle-width;
&:before {
left: ($slider-handle-width - 2)/2 - 2;
}
&:after {
left: ($slider-handle-width - 2)/2 + 2;
} }
} }
} }

View File

@@ -0,0 +1,139 @@
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 { FILTER_CONFIG, SearchFilterService, SELECTED_VALUES } from '../search-filter.service';
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 { Observable } from 'rxjs/Observable';
import { SearchService } from '../../../search-service/search.service';
import { SearchServiceStub } from '../../../../shared/testing/search-service-stub';
import { RemoteData } from '../../../../core/data/remote-data';
import { PaginatedList } from '../../../../core/data/paginated-list';
import { RouterStub } from '../../../../shared/testing/router-stub';
import { ActivatedRoute, Router } from '@angular/router';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { PageInfo } from '../../../../core/shared/page-info.model';
import { SearchRangeFilterComponent } from './search-range-filter.component';
import { MockActivatedRoute } from '../../../../shared/mocks/mock-active-router';
describe('SearchFacetFilterComponent', () => {
let comp: SearchRangeFilterComponent;
let fixture: ComponentFixture<SearchRangeFilterComponent>;
const minSuffix = '.min';
const maxSuffix = '.max';
const dateFormats = ['YYYY', 'YYYY-MM', 'YYYY-MM-DD'];
const filterName1 = 'test name';
const value1 = '2000 - 2012';
const value2 = '1992 - 2000';
const value3 = '1990 - 1992';
const mockFilterConfig: SearchFilterConfig = Object.assign(new SearchFilterConfig(), {
name: filterName1,
type: FilterType.range,
hasFacets: false,
isOpenByDefault: false,
pageSize: 2,
minValue: 200,
maxValue: 3000,
});
const values: FacetValue[] = [
{
value: value1,
count: 52,
search: ''
}, {
value: value2,
count: 20,
search: ''
}, {
value: value3,
count: 5,
search: ''
}
];
const searchLink = '/search';
const selectedValues = [value1];
let filterService;
let searchService;
let router;
const page = Observable.of(0);
const activatedRouteStub = new MockActivatedRoute();
const mockValues = Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), values)));
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [TranslateModule.forRoot(), NoopAnimationsModule, FormsModule],
declarations: [SearchRangeFilterComponent],
providers: [
{ provide: SearchService, useValue: new SearchServiceStub(searchLink) },
{ provide: Router, useValue: new RouterStub() },
{ provide: FILTER_CONFIG, useValue: mockFilterConfig},
{ provide: SELECTED_VALUES, useValue: selectedValues },
{ provide: ActivatedRoute, useValue: activatedRouteStub },
{
provide: SearchFilterService, useValue: {
isFilterActiveWithValue: (paramName: string, filterValue: string) => true,
getPage: (paramName: string) => page,
/* tslint:disable:no-empty */
incrementPage: (filterName: string) => {
},
resetPage: (filterName: string) => {
},
getSearchOptions: () => Observable.of({}),
/* tslint:enable:no-empty */
}
}
],
schemas: [NO_ERRORS_SCHEMA]
}).overrideComponent(SearchRangeFilterComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SearchRangeFilterComponent);
comp = fixture.componentInstance; // SearchPageComponent test instance
comp.filterValues = [mockValues];
comp.filterValues$ = new BehaviorSubject(comp.filterValues);
filterService = (comp as any).filterService;
searchService = (comp as any).searchService;
spyOn(searchService, 'getFacetValuesFor').and.returnValue(mockValues);
router = (comp as any).router;
fixture.detectChanges();
});
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);
expect(result[mockFilterConfig.paramName + minSuffix]).toEqual(['1990']);
expect(result[mockFilterConfig.paramName + maxSuffix]).toEqual(['1992']);
});
});
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);
expect(result[mockFilterConfig.paramName + minSuffix]).toBeNull();
expect(result[mockFilterConfig.paramName + maxSuffix]).toBeNull();
});
});
describe('when the onSubmit method is called with data', () => {
const searchUrl = '/search/path';
const data = { [mockFilterConfig.paramName + minSuffix]: '1900', [mockFilterConfig.paramName + maxSuffix]: '1950' };
beforeEach(() => {
spyOn(comp, 'getSearchLink').and.returnValue(searchUrl);
comp.onSubmit(data);
});
it('should call navigate on the router with the right searchlink and parameters', () => {
expect(router.navigate).toHaveBeenCalledWith([searchUrl], {
queryParams: { [mockFilterConfig.paramName + minSuffix]: ['1900'], [mockFilterConfig.paramName + maxSuffix]: ['1950']},
queryParamsHandling: 'merge'
});
});
});
});

View File

@@ -14,6 +14,10 @@ import * as moment from 'moment';
* The route parameter 'id' is used to request the item it represents. * The route parameter 'id' is used to request the item it represents.
* All fields of the item that should be displayed, are defined in its template. * All fields of the item that should be displayed, are defined in its template.
*/ */
const minSuffix = '.min';
const maxSuffix = '.max';
const dateFormats = ['YYYY', 'YYYY-MM', 'YYYY-MM-DD'];
const rangeDelimiter = '-';
@Component({ @Component({
selector: 'ds-search-range-filter', selector: 'ds-search-range-filter',
@@ -23,11 +27,9 @@ import * as moment from 'moment';
@renderFacetFor(FilterType.range) @renderFacetFor(FilterType.range)
export class SearchRangeFilterComponent extends SearchFacetFilterComponent implements OnInit { export class SearchRangeFilterComponent extends SearchFacetFilterComponent implements OnInit {
rangeDelimiter = '-';
min = 1950; min = 1950;
max = 2018; max = 2018;
range; range;
dateFormats = ['YYYY', 'YYYY-MM', 'YYYY-MM-DD']
constructor(protected searchService: SearchService, constructor(protected searchService: SearchService,
protected filterService: SearchFilterService, protected filterService: SearchFilterService,
@@ -40,29 +42,29 @@ export class SearchRangeFilterComponent extends SearchFacetFilterComponent imple
ngOnInit(): void { ngOnInit(): void {
super.ngOnInit(); super.ngOnInit();
this.min = moment(this.filterConfig.minValue, this.dateFormats).year() || this.min; this.min = moment(this.filterConfig.minValue, dateFormats).year() || this.min;
this.max = moment(this.filterConfig.maxValue, this.dateFormats).year() || this.max; this.max = moment(this.filterConfig.maxValue, dateFormats).year() || this.max;
const iniMin = this.route.snapshot.queryParams[this.filterConfig.paramName + '.min'] || this.min; const iniMin = this.route.snapshot.queryParams[this.filterConfig.paramName + minSuffix] || this.min;
const iniMax = this.route.snapshot.queryParams[this.filterConfig.paramName + '.max'] || this.max; const iniMax = this.route.snapshot.queryParams[this.filterConfig.paramName + maxSuffix] || this.max;
this.range = [iniMin, iniMax]; this.range = [iniMin, iniMax];
} }
getAddParams(value: string) { getAddParams(value: string) {
const parts = value.split(this.rangeDelimiter); const parts = value.split(rangeDelimiter);
const min = parts.length > 1 ? parts[0].trim() : value; const min = parts.length > 1 ? parts[0].trim() : value;
const max = parts.length > 1 ? parts[1].trim() : value; const max = parts.length > 1 ? parts[1].trim() : value;
return { return {
[this.filterConfig.paramName + '.min']: [min], [this.filterConfig.paramName + minSuffix]: [min],
[this.filterConfig.paramName + '.max']: [max], [this.filterConfig.paramName + maxSuffix]: [max],
page: 1 page: 1
}; };
} }
getRemoveParams(value: string) { getRemoveParams(value: string) {
return { return {
[this.filterConfig.paramName + '.min']: null, [this.filterConfig.paramName + minSuffix]: null,
[this.filterConfig.paramName + '.max']: null, [this.filterConfig.paramName + maxSuffix]: null,
page: 1 page: 1
}; };
} }
@@ -72,8 +74,8 @@ export class SearchRangeFilterComponent extends SearchFacetFilterComponent imple
this.router.navigate([this.getSearchLink()], { this.router.navigate([this.getSearchLink()], {
queryParams: queryParams:
{ {
[this.filterConfig.paramName + '.min']: [data[this.filterConfig.paramName + '.min']], [this.filterConfig.paramName + minSuffix]: [data[this.filterConfig.paramName + minSuffix]],
[this.filterConfig.paramName + '.max']: [data[this.filterConfig.paramName + '.max']] [this.filterConfig.paramName + maxSuffix]: [data[this.filterConfig.paramName + maxSuffix]]
}, },
queryParamsHandling: 'merge' queryParamsHandling: 'merge'
}); });

View File

@@ -1,242 +0,0 @@
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { TranslateModule } from '@ngx-translate/core';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { SearchFilterService } from '../search-filter.service';
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 { Observable } from 'rxjs/Observable';
import { SearchService } from '../../../search-service/search.service';
import { SearchServiceStub } from '../../../../shared/testing/search-service-stub';
import { RemoteData } from '../../../../core/data/remote-data';
import { PaginatedList } from '../../../../core/data/paginated-list';
import { SearchOptions } from '../../../search-options.model';
import { RouterStub } from '../../../../shared/testing/router-stub';
import { Router } from '@angular/router';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { PageInfo } from '../../../../core/shared/page-info.model';
import { SearchFacetFilterComponent } from '../search-facet-filter/search-facet-filter.component';
describe('SearchFacetFilterComponent', () => {
let comp: SearchFacetFilterComponent;
let fixture: ComponentFixture<SearchFacetFilterComponent>;
const filterName1 = 'test name';
const value1 = 'testvalue1';
const value2 = 'test2';
const value3 = 'another value3';
const mockFilterConfig: SearchFilterConfig = Object.assign(new SearchFilterConfig(), {
name: filterName1,
type: FilterType.text,
hasFacets: false,
isOpenByDefault: false,
pageSize: 2
});
const values: FacetValue[] = [
{
value: value1,
count: 52,
search: ''
}, {
value: value2,
count: 20,
search: ''
}, {
value: value3,
count: 5,
search: ''
}
];
const searchLink = '/search';
const selectedValues = [value1, value2];
let filterService;
let searchService;
let router;
const page = Observable.of(0);
const mockValues = Observable.of(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), values)));
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [TranslateModule.forRoot(), NoopAnimationsModule, FormsModule],
declarations: [SearchFacetFilterComponent],
providers: [
{ provide: SearchService, useValue: new SearchServiceStub(searchLink) },
{ provide: Router, useValue: new RouterStub() },
{
provide: SearchFilterService, useValue: {
isFilterActiveWithValue: (paramName: string, filterValue: string) => true,
getPage: (paramName: string) => page,
/* tslint:disable:no-empty */
incrementPage: (filterName: string) => {
},
resetPage: (filterName: string) => {
},
getSearchOptions: () => Observable.of({}),
/* tslint:enable:no-empty */
}
}
],
schemas: [NO_ERRORS_SCHEMA]
}).overrideComponent(SearchFacetFilterComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SearchFacetFilterComponent);
comp = fixture.componentInstance; // SearchPageComponent test instance
comp.filterConfig = mockFilterConfig;
comp.filterValues = [mockValues];
comp.filterValues$ = new BehaviorSubject(comp.filterValues);
comp.selectedValues = selectedValues;
filterService = (comp as any).filterService;
searchService = (comp as any).searchService;
spyOn(searchService, 'getFacetValuesFor').and.returnValue(mockValues);
router = (comp as any).router;
fixture.detectChanges();
});
describe('when the isChecked method is called with a value', () => {
beforeEach(() => {
spyOn(filterService, 'isFilterActiveWithValue');
comp.isChecked(values[1]);
});
it('should call isFilterActiveWithValue on the filterService with the correct filter parameter name and the passed value', () => {
expect(filterService.isFilterActiveWithValue).toHaveBeenCalledWith(mockFilterConfig.paramName, values[1].value)
});
});
describe('when the getSearchLink method is triggered', () => {
let link: string;
beforeEach(() => {
link = comp.getSearchLink();
});
it('should return the value of the searchLink variable in the filter service', () => {
expect(link).toEqual(searchLink);
});
});
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);
expect(result[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);
expect(result[mockFilterConfig.paramName]).toEqual([value2]);
});
});
describe('when the showMore method is called', () => {
beforeEach(() => {
spyOn(filterService, 'incrementPage');
comp.showMore();
});
it('should call incrementPage on the filterService with the correct filter parameter name', () => {
expect(filterService.incrementPage).toHaveBeenCalledWith(mockFilterConfig.name)
});
});
describe('when the showFirstPageOnly method is called', () => {
beforeEach(() => {
spyOn(filterService, 'resetPage');
comp.showFirstPageOnly();
});
it('should call resetPage on the filterService with the correct filter parameter name', () => {
expect(filterService.resetPage).toHaveBeenCalledWith(mockFilterConfig.name);
});
});
describe('when the getCurrentPage method is called', () => {
beforeEach(() => {
spyOn(filterService, 'getPage');
comp.getCurrentPage();
});
it('should call getPage on the filterService with the correct filter parameter name', () => {
expect(filterService.getPage).toHaveBeenCalledWith(mockFilterConfig.name)
});
});
describe('when the getCurrentUrl method is called', () => {
const url = 'test.url/test'
beforeEach(() => {
router.navigateByUrl(url);
});
it('should call getPage on the filterService with the correct filter parameter name', () => {
expect(router.url).toEqual(url);
});
});
describe('when the onSubmit method is called with data', () => {
const searchUrl = '/search/path';
const testValue = 'test';
const data = { [mockFilterConfig.paramName]: testValue };
beforeEach(() => {
spyOn(comp, 'getSearchLink').and.returnValue(searchUrl);
comp.onSubmit(data);
});
it('should call navigate on the router with the right searchlink and parameters', () => {
expect(router.navigate).toHaveBeenCalledWith([searchUrl], {
queryParams: { [mockFilterConfig.paramName]: [...selectedValues, testValue] },
queryParamsHandling: 'merge'
});
});
});
describe('when updateFilterValueList is called', () => {
const cPage = 10;
const searchOptions = new SearchOptions();
beforeEach(() => {
// spyOn(searchService, 'getFacetValuesFor'); Already spied upon
comp.currentPage = Observable.of(cPage);
comp.updateFilterValueList(searchOptions);
});
it('should call getFacetValuesFor on the searchService with the correct parameters', () => {
expect(searchService.getFacetValuesFor).toHaveBeenCalledWith(mockFilterConfig, cPage, searchOptions);
});
});
describe('when updateFilterValueList is called and pageChange is set to true', () => {
const searchOptions = new SearchOptions();
beforeEach(() => {
comp.pageChange = true;
spyOn(comp, 'showFirstPageOnly');
comp.updateFilterValueList(searchOptions);
});
it('should not call showFirstPageOnly on the component', () => {
expect(comp.showFirstPageOnly).not.toHaveBeenCalled();
});
it('should set pageChange to false', () => {
expect(comp.pageChange).toBeFalsy();
});
});
describe('when updateFilterValueList is called and pageChange is set to false', () => {
const searchOptions = new SearchOptions();
beforeEach(() => {
comp.pageChange = false;
spyOn(comp, 'showFirstPageOnly');
comp.updateFilterValueList(searchOptions);
});
it('should call showFirstPageOnly on the component', () => {
expect(comp.showFirstPageOnly).toHaveBeenCalled();
});
});
});

View File

@@ -24,7 +24,7 @@ describe('SearchFiltersComponent', () => {
/* tslint:enable:no-empty */ /* tslint:enable:no-empty */
}; };
const searchFilterServiceStub = jasmine.createSpyObj('SearchFilterService', { const searchFilterServiceStub = jasmine.createSpyObj('SearchFilterService', {
getCurrentFilters: Observable.of({}) getCurrentFrontendFilters: Observable.of({})
}); });
beforeEach(async(() => { beforeEach(async(() => {

View File

@@ -72,6 +72,7 @@ const effects = [
ItemSearchResultGridElementComponent, ItemSearchResultGridElementComponent,
CollectionSearchResultGridElementComponent, CollectionSearchResultGridElementComponent,
CommunitySearchResultGridElementComponent, CommunitySearchResultGridElementComponent,
SearchFacetFilterComponent,
SearchRangeFilterComponent, SearchRangeFilterComponent,
SearchTextFilterComponent, SearchTextFilterComponent,
SearchHierarchyFilterComponent, SearchHierarchyFilterComponent,

View File

@@ -17,7 +17,7 @@
@autoserialize @autoserialize
isOpenByDefault: boolean; isOpenByDefault: boolean;
@autoserialize @autoserialize
maxValue: string; maxValue: string;

View File

@@ -5,8 +5,7 @@ import { ResponseParsingService } from './parsing.service';
import { RestRequest } from './request.models'; import { RestRequest } from './request.models';
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model'; import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer'; import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
import { PageInfo } from '../shared/page-info.model'; import { hasValue } from '../../shared/empty.util';
import { hasValue, isNotEmpty } from '../../shared/empty.util';
import { SearchQueryResponse } from '../../+search-page/search-service/search-query-response.model'; import { SearchQueryResponse } from '../../+search-page/search-service/search-query-response.model';
import { Metadatum } from '../shared/metadatum.model'; import { Metadatum } from '../shared/metadatum.model';
@@ -17,7 +16,6 @@ export class SearchResponseParsingService implements ResponseParsingService {
parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse { parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse {
const payload = data.payload; const payload = data.payload;
console.log(payload);
const hitHighlights = payload._embedded.objects const hitHighlights = payload._embedded.objects
.map((object) => object.hitHighlights) .map((object) => object.hitHighlights)
.map((hhObject) => { .map((hhObject) => {

View File

@@ -29,6 +29,6 @@ export class MockActivatedRoute {
// ActivatedRoute.snapshot.params // ActivatedRoute.snapshot.params
get snapshot() { get snapshot() {
return { params: this.testParams }; return { params: this.testParams, queryParams: this.testParams };
} }
} }

View File

@@ -5591,9 +5591,9 @@ netmask@~1.0.4:
version "1.0.6" version "1.0.6"
resolved "https://registry.yarnpkg.com/netmask/-/netmask-1.0.6.tgz#20297e89d86f6f6400f250d9f4f6b4c1945fcd35" resolved "https://registry.yarnpkg.com/netmask/-/netmask-1.0.6.tgz#20297e89d86f6f6400f250d9f4f6b4c1945fcd35"
ng2-nouislider@^1.7.7: ng2-nouislider@1.7.8:
version "1.7.7" version "1.7.8"
resolved "https://registry.yarnpkg.com/ng2-nouislider/-/ng2-nouislider-1.7.7.tgz#b841f4b313c8c9c8a763c80f3a59d5aa4c3a70c8" resolved "https://registry.yarnpkg.com/ng2-nouislider/-/ng2-nouislider-1.7.8.tgz#5fd6de120f9ca5b5d9c4b377f884944b74c06855"
ngrx-store-freeze@^0.2.1: ngrx-store-freeze@^0.2.1:
version "0.2.1" version "0.2.1"
@@ -5814,9 +5814,9 @@ normalize-url@^1.4.0:
query-string "^4.1.0" query-string "^4.1.0"
sort-keys "^1.0.0" sort-keys "^1.0.0"
nouislider@^10.0.0: nouislider@^11.0.0:
version "10.1.0" version "11.1.0"
resolved "https://registry.yarnpkg.com/nouislider/-/nouislider-10.1.0.tgz#7bdd0411fd62d4584bfe88cb92bb8d06e64c6b47" resolved "https://registry.yarnpkg.com/nouislider/-/nouislider-11.1.0.tgz#1768eb5b854917325d41b96f2dc4eb3757d73381"
npm-run-all@4.1.2: npm-run-all@4.1.2:
version "4.1.2" version "4.1.2"