From e14e8fac3717a1168f83d5b10cfc070b1dad5f36 Mon Sep 17 00:00:00 2001 From: Lotte Hofstede Date: Fri, 3 Nov 2017 14:04:36 +0100 Subject: [PATCH 01/18] 45477: sidebar filter section --- resources/i18n/en.json | 6 +- .../search-filters.component.html | 7 ++ .../search-filters.component.scss | 2 + .../search-filters.component.ts | 24 +++++++ .../+search-page/search-page.component.scss | 2 +- src/app/+search-page/search-page.module.ts | 4 +- .../search-results.component.html | 2 +- .../search-service/search.service.ts | 68 +++++++++++-------- .../search-sidebar.component.html | 2 +- .../utils/scroll-and-stick.directive.ts | 10 +-- 10 files changed, 89 insertions(+), 38 deletions(-) create mode 100644 src/app/+search-page/search-filters/search-filters.component.html create mode 100644 src/app/+search-page/search-filters/search-filters.component.scss create mode 100644 src/app/+search-page/search-filters/search-filters.component.ts diff --git a/resources/i18n/en.json b/resources/i18n/en.json index 8f41b90b13..a1af4bcc6a 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -80,7 +80,7 @@ "search_dspace": "Search DSpace" }, "results": { - "title": "Search Results" + "head": "Search Results" }, "sidebar": { "close": "Back to results", @@ -90,6 +90,10 @@ "view-switch": { "show-list": "Show as list", "show-grid": "Show as grid" + }, + "filters": { + "head": "Filters", + "reset": "Reset filters" } }, "loading": { diff --git a/src/app/+search-page/search-filters/search-filters.component.html b/src/app/+search-page/search-filters/search-filters.component.html new file mode 100644 index 0000000000..f42ad620ed --- /dev/null +++ b/src/app/+search-page/search-filters/search-filters.component.html @@ -0,0 +1,7 @@ +

{{"search.filters.head" | translate}}

+
+
+ +
+
+{{"search.filters.reset" | translate}} \ No newline at end of file diff --git a/src/app/+search-page/search-filters/search-filters.component.scss b/src/app/+search-page/search-filters/search-filters.component.scss new file mode 100644 index 0000000000..628c8ed46f --- /dev/null +++ b/src/app/+search-page/search-filters/search-filters.component.scss @@ -0,0 +1,2 @@ +@import '../../../styles/variables.scss'; +@import '../../../styles/mixins.scss'; \ No newline at end of file diff --git a/src/app/+search-page/search-filters/search-filters.component.ts b/src/app/+search-page/search-filters/search-filters.component.ts new file mode 100644 index 0000000000..b1b8357d9f --- /dev/null +++ b/src/app/+search-page/search-filters/search-filters.component.ts @@ -0,0 +1,24 @@ +import { Component, Input } from '@angular/core'; +import { SearchService } from '../search-service/search.service'; +import { Observable } from 'rxjs/Observable'; + +/** + * This component renders a simple item page. + * 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. + */ + +@Component({ + selector: 'ds-search-filters', + styleUrls: ['./search-filters.component.scss'], + templateUrl: './search-filters.component.html', +}) + +export class SidebarFiltersComponent { + @Input() filters; + constructor(private searchService: SearchService) {} + + getClearFiltersLink(): Observable { + return this.searchService.getClearFiltersLink(); + } +} diff --git a/src/app/+search-page/search-page.component.scss b/src/app/+search-page/search-page.component.scss index 60a696a52a..b670549141 100644 --- a/src/app/+search-page/search-page.component.scss +++ b/src/app/+search-page/search-page.component.scss @@ -1,7 +1,7 @@ @import '../../styles/variables.scss'; @import '../../styles/mixins.scss'; -#search-body { +.container { position: relative; } diff --git a/src/app/+search-page/search-page.module.ts b/src/app/+search-page/search-page.module.ts index 1f295bc75f..be08e0c307 100644 --- a/src/app/+search-page/search-page.module.ts +++ b/src/app/+search-page/search-page.module.ts @@ -12,6 +12,7 @@ import { SearchSidebarComponent } from './search-sidebar/search-sidebar.componen import { SearchSidebarService } from './search-sidebar/search-sidebar.service'; import { SearchSidebarEffects } from './search-sidebar/search-sidebar.effects'; import { EffectsModule } from '@ngrx/effects'; +import { SidebarFiltersComponent } from './search-filters/search-filters.component'; const effects = [ SearchSidebarEffects @@ -30,7 +31,8 @@ const effects = [ SearchSidebarComponent, ItemSearchResultListElementComponent, CollectionSearchResultListElementComponent, - CommunitySearchResultListElementComponent + CommunitySearchResultListElementComponent, + SidebarFiltersComponent ], providers: [ SearchService, diff --git a/src/app/+search-page/search-results/search-results.component.html b/src/app/+search-page/search-results/search-results.component.html index 08e63d598d..233f22d850 100644 --- a/src/app/+search-page/search-results/search-results.component.html +++ b/src/app/+search-page/search-results/search-results.component.html @@ -1,4 +1,4 @@ -

{{ 'search.results.title' | translate }}

+

{{ 'search.results.head' | translate }}

{ + const url = '/search?'; + return this.route.queryParamMap + .map((map) => { return url.concat(map.keys + .filter((key) => this.config + .findIndex((conf: SearchFilterConfig) => conf.paramName === key) < 0) + .map((key) => { return key + '=' + map.get(key) }) + .join('&'))}) + .first(); + } } diff --git a/src/app/+search-page/search-sidebar/search-sidebar.component.html b/src/app/+search-page/search-sidebar/search-sidebar.component.html index 9a34e05dbc..4adca2907a 100644 --- a/src/app/+search-page/search-sidebar/search-sidebar.component.html +++ b/src/app/+search-page/search-sidebar/search-sidebar.component.html @@ -8,6 +8,6 @@
- Filters +
\ No newline at end of file diff --git a/src/app/shared/utils/scroll-and-stick.directive.ts b/src/app/shared/utils/scroll-and-stick.directive.ts index 4ec99d8db6..0c071ec6f3 100644 --- a/src/app/shared/utils/scroll-and-stick.directive.ts +++ b/src/app/shared/utils/scroll-and-stick.directive.ts @@ -1,7 +1,7 @@ - import { NativeWindowRef, NativeWindowService } from '../window.service'; import { Observable } from 'rxjs/Observable'; -import { AfterViewInit, Directive, ElementRef, Inject } from '@angular/core'; +import { AfterViewInit, Directive, ElementRef, Inject, PLATFORM_ID } from '@angular/core'; +import { isPlatformBrowser } from '@angular/common'; @Directive({ selector: '[dsStick]' @@ -10,8 +10,10 @@ export class ScrollAndStickDirective implements AfterViewInit { private initialY: number; - constructor(private _element: ElementRef, @Inject(NativeWindowService) private _window: NativeWindowRef) { - this.subscribeForScrollEvent(); + constructor(private _element: ElementRef, @Inject(NativeWindowService) private _window: NativeWindowRef, @Inject(PLATFORM_ID) private platformId) { + if (isPlatformBrowser(platformId)) { + this.subscribeForScrollEvent(); + } } ngAfterViewInit(): void { From 532741d073f954fc1ad0f94ee3794210e5c010f3 Mon Sep 17 00:00:00 2001 From: Lotte Hofstede Date: Mon, 6 Nov 2017 14:27:48 +0100 Subject: [PATCH 02/18] 45621: filter components for sidebar --- resources/i18n/en.json | 14 +++++- .../search-facet-filter.component.html | 8 +++ .../search-facet-filter.component.scss | 2 + .../search-facet-filter.component.ts | 41 +++++++++++++++ .../search-filter.component.html | 4 ++ .../search-filter.component.scss | 2 + .../search-filter/search-filter.component.ts | 34 +++++++++++++ .../search-filters.component.html | 8 +-- .../search-filters.component.ts | 17 +++++-- .../+search-page/search-page.component.html | 47 ++++++++--------- .../+search-page/search-page.component.scss | 22 +++----- src/app/+search-page/search-page.module.ts | 6 ++- .../search-service/search.service.spec.ts | 1 - .../search-service/search.service.ts | 50 ++++++++++++++----- .../search-form/search-form.component.html | 2 +- src/app/shared/shared.module.ts | 2 - .../utils/scroll-and-stick.directive.ts | 42 ---------------- 17 files changed, 195 insertions(+), 107 deletions(-) create mode 100644 src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.html create mode 100644 src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.scss create mode 100644 src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts create mode 100644 src/app/+search-page/search-filters/search-filter/search-filter.component.html create mode 100644 src/app/+search-page/search-filters/search-filter/search-filter.component.scss create mode 100644 src/app/+search-page/search-filters/search-filter/search-filter.component.ts delete mode 100644 src/app/shared/utils/scroll-and-stick.directive.ts diff --git a/resources/i18n/en.json b/resources/i18n/en.json index a1af4bcc6a..c803cda073 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -93,7 +93,19 @@ }, "filters": { "head": "Filters", - "reset": "Reset filters" + "reset": "Reset filters", + "facet-filter": { + "show-more": "Show more", + "author": { + "placeholder": "Author name" + }, + "scope": { + "placeholder": "Scope filter" + }, + "subject": { + "placeholder": "Subject" + } + } } }, "loading": { diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.html b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.html new file mode 100644 index 0000000000..7d1f7f2e89 --- /dev/null +++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.html @@ -0,0 +1,8 @@ + + + {{value.value}} + ({{value.count}}) + +{{"search.filters.facet-filter.show-more" | translate}} + + \ No newline at end of file diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.scss b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.scss new file mode 100644 index 0000000000..9ad844019b --- /dev/null +++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.scss @@ -0,0 +1,2 @@ +@import '../../../../../styles/variables.scss'; +@import '../../../../../styles/mixins.scss'; \ No newline at end of file diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts new file mode 100644 index 0000000000..2565c11945 --- /dev/null +++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts @@ -0,0 +1,41 @@ +import { Component, Input } from '@angular/core'; +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 { ActivatedRoute } from '@angular/router'; +import { Observable } from 'rxjs/Observable'; + +/** + * This component renders a simple item page. + * 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. + */ + +@Component({ + selector: 'ds-search-facet-filter', + styleUrls: ['./search-facet-filter.component.scss'], + templateUrl: './search-facet-filter.component.html', +}) + +export class SidebarFacetFilterComponent { + @Input() filterValues: FacetValue[]; + @Input() filterConfig: SearchFilterConfig; + + constructor(private searchService: SearchService, private route: ActivatedRoute) { + } + + isChecked(value: FacetValue) { + return this.searchService.isFilterActive(this.filterConfig.name, value.value); + } + + getSearchLink() { + return this.searchService.getSearchLink(); + } + + getQueryParams(value: FacetValue): Observable { + const params = {}; + params[this.filterConfig.paramName] = value.value; + return this.route.queryParams.map((p) => Object.assign({}, p, params)) + } + +} diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.component.html b/src/app/+search-page/search-filters/search-filter/search-filter.component.html new file mode 100644 index 0000000000..2279564359 --- /dev/null +++ b/src/app/+search-page/search-filters/search-filter/search-filter.component.html @@ -0,0 +1,4 @@ +
+
{{filter.name}}
+ +
\ No newline at end of file diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.component.scss b/src/app/+search-page/search-filters/search-filter/search-filter.component.scss new file mode 100644 index 0000000000..3e77826dc2 --- /dev/null +++ b/src/app/+search-page/search-filters/search-filter/search-filter.component.scss @@ -0,0 +1,2 @@ +@import '../../../../styles/variables.scss'; +@import '../../../../styles/mixins.scss'; \ No newline at end of file diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.component.ts b/src/app/+search-page/search-filters/search-filter/search-filter.component.ts new file mode 100644 index 0000000000..c26efd41ad --- /dev/null +++ b/src/app/+search-page/search-filters/search-filter/search-filter.component.ts @@ -0,0 +1,34 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { SearchFilterConfig } from '../../search-service/search-filter-config.model'; +import { SearchService } from '../../search-service/search.service'; +import { RemoteData } from '../../../core/data/remote-data'; +import { FacetValue } from '../../search-service/facet-value.model'; + +/** + * This component renders a simple item page. + * 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. + */ + +@Component({ + selector: 'ds-search-filter', + styleUrls: ['./search-filter.component.scss'], + templateUrl: './search-filter.component.html', +}) + +export class SidebarFilterComponent implements OnInit { + @Input() filter: SearchFilterConfig; + filterValues: RemoteData; + isCollapsed = false; + + constructor(private searchService: SearchService) { + } + + ngOnInit() { + this.filterValues = this.searchService.getFacetValuesFor(this.filter.name); + } + + toggle() { + this.isCollapsed = !this.isCollapsed; + } +} \ No newline at end of file diff --git a/src/app/+search-page/search-filters/search-filters.component.html b/src/app/+search-page/search-filters/search-filters.component.html index f42ad620ed..270649598f 100644 --- a/src/app/+search-page/search-filters/search-filters.component.html +++ b/src/app/+search-page/search-filters/search-filters.component.html @@ -1,7 +1,7 @@

{{"search.filters.head" | translate}}

-
-
- +
+
+
-{{"search.filters.reset" | translate}} \ No newline at end of file +{{"search.filters.reset" | translate}} \ No newline at end of file diff --git a/src/app/+search-page/search-filters/search-filters.component.ts b/src/app/+search-page/search-filters/search-filters.component.ts index b1b8357d9f..216e8c0832 100644 --- a/src/app/+search-page/search-filters/search-filters.component.ts +++ b/src/app/+search-page/search-filters/search-filters.component.ts @@ -1,6 +1,7 @@ import { Component, Input } from '@angular/core'; import { SearchService } from '../search-service/search.service'; -import { Observable } from 'rxjs/Observable'; +import { RemoteData } from '../../core/data/remote-data'; +import { SearchFilterConfig } from '../search-service/search-filter-config.model'; /** * This component renders a simple item page. @@ -15,10 +16,16 @@ import { Observable } from 'rxjs/Observable'; }) export class SidebarFiltersComponent { - @Input() filters; - constructor(private searchService: SearchService) {} + filters: RemoteData; + constructor(private searchService: SearchService) { + this.filters = searchService.getConfig(); + } - getClearFiltersLink(): Observable { - return this.searchService.getClearFiltersLink(); + getClearFiltersQueryParams(): any { + return this.searchService.getClearFiltersQueryParams(); + } + + getSearchLink() { + return this.searchService.getSearchLink(); } } diff --git a/src/app/+search-page/search-page.component.html b/src/app/+search-page/search-page.component.html index a6c9834e37..25bd741415 100644 --- a/src/app/+search-page/search-page.component.html +++ b/src/app/+search-page/search-page.component.html @@ -1,35 +1,36 @@
-
- + -
- + -
-
-
- -
-
- - - +
+
+ +
+
+ + +
+
-
diff --git a/src/app/+search-page/search-page.component.scss b/src/app/+search-page/search-page.component.scss index b670549141..2ca44644f8 100644 --- a/src/app/+search-page/search-page.component.scss +++ b/src/app/+search-page/search-page.component.scss @@ -5,12 +5,6 @@ position: relative; } -#search-content, #search-form { - display: block; - @include media-breakpoint-down(xs) { - margin-left: 0; - } -} /deep/ .search-controls { margin-bottom: $spacer; @@ -43,15 +37,15 @@ } } -.sidebar-sm-fixed { +.sidebar-sm-sticky{ @include media-breakpoint-up(sm) { - position: absolute; - margin-top: -$content-spacing; + position: sticky; + position: -webkit-sticky; + top: 0; + z-index: $zindex-sticky; padding-top: $content-spacing; - &.stick { - top: 0; - margin-top: 0px; - position: fixed; - } + margin-top: -$content-spacing; + align-self: flex-start; + display: block; } } \ No newline at end of file diff --git a/src/app/+search-page/search-page.module.ts b/src/app/+search-page/search-page.module.ts index be08e0c307..58e590d4dc 100644 --- a/src/app/+search-page/search-page.module.ts +++ b/src/app/+search-page/search-page.module.ts @@ -13,6 +13,8 @@ import { SearchSidebarService } from './search-sidebar/search-sidebar.service'; import { SearchSidebarEffects } from './search-sidebar/search-sidebar.effects'; import { EffectsModule } from '@ngrx/effects'; import { SidebarFiltersComponent } from './search-filters/search-filters.component'; +import { SidebarFilterComponent } from './search-filters/search-filter/search-filter.component'; +import { SidebarFacetFilterComponent } from './search-filters/search-filter/search-facet-filter/search-facet-filter.component'; const effects = [ SearchSidebarEffects @@ -32,7 +34,9 @@ const effects = [ ItemSearchResultListElementComponent, CollectionSearchResultListElementComponent, CommunitySearchResultListElementComponent, - SidebarFiltersComponent + SidebarFiltersComponent, + SidebarFilterComponent, + SidebarFacetFilterComponent ], providers: [ SearchService, diff --git a/src/app/+search-page/search-service/search.service.spec.ts b/src/app/+search-page/search-service/search.service.spec.ts index 489ac76763..f2564eed27 100644 --- a/src/app/+search-page/search-service/search.service.spec.ts +++ b/src/app/+search-page/search-service/search.service.spec.ts @@ -3,7 +3,6 @@ import { RouterTestingModule } from '@angular/router/testing'; import { CommonModule } from '@angular/common'; import { Component } from '@angular/core'; -import { Router } from '@angular/router'; import { SearchService } from './search.service'; import { ItemDataService } from './../../core/data/item-data.service'; diff --git a/src/app/+search-page/search-service/search.service.ts b/src/app/+search-page/search-service/search.service.ts index be82f3e2ee..9d65eb6c0e 100644 --- a/src/app/+search-page/search-service/search.service.ts +++ b/src/app/+search-page/search-service/search.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, OnDestroy } from '@angular/core'; import { RemoteData } from '../../core/data/remote-data'; import { Observable } from 'rxjs/Observable'; import { SearchResult } from '../search-result.model'; @@ -31,7 +31,7 @@ function shuffle(array: any[]) { } @Injectable() -export class SearchService { +export class SearchService implements OnDestroy { totalPages = 5; mockedHighlights: string[] = new Array( @@ -46,6 +46,8 @@ export class SearchService { 'This was blank in the actual item, no abstract', 'The QSAR DataBank (QsarDB) repository', ); + private sub; + searchLink = '/search'; config: SearchFilterConfig[] = [ Object.assign(new SearchFilterConfig(), @@ -170,13 +172,16 @@ export class SearchService { } getFacetValuesFor(searchFilterConfigName: string): RemoteData { + + const filterConfig = this.config.find((config: SearchFilterConfig) => config.name === searchFilterConfigName); + const values: FacetValue[] = []; for (let i = 0; i < 5; i++) { const value = searchFilterConfigName + ' ' + (i + 1); values.push({ value: value, count: Math.floor(Math.random() * 20) + 20 * (5 - i), // make sure first results have the highest (random) count - search: 'https://dspace7.4science.it/dspace-spring-rest/api/search?f.' + searchFilterConfigName + '=' + encodeURI(value) + search: decodeURI(this.router.url) + (this.router.url.includes('?') ? '&' : '?') + filterConfig.paramName + '=' + value }); } const requestPending = Observable.of(false); @@ -213,17 +218,36 @@ export class SearchService { queryParamsHandling: 'merge' }; - this.router.navigate(['/search'], navigationExtras); + this.router.navigate([this.searchLink], navigationExtras); } - getClearFiltersLink(): Observable { - const url = '/search?'; - return this.route.queryParamMap - .map((map) => { return url.concat(map.keys - .filter((key) => this.config - .findIndex((conf: SearchFilterConfig) => conf.paramName === key) < 0) - .map((key) => { return key + '=' + map.get(key) }) - .join('&'))}) - .first(); + getClearFiltersQueryParams(): any { + const params = {}; + this.sub = this.route.queryParamMap + .subscribe((map) => { + map.keys + .filter((key) => this.config + .findIndex((conf: SearchFilterConfig) => conf.paramName === key) < 0) + .forEach((key) => { + params[key] = map.get(key); + }) + }); + return params; } + + isFilterActive(filterName: string, filterValue: string): boolean { + const filterConfig = this.config.find((config: SearchFilterConfig) => config.name === filterName); + return isNotEmpty(this.router.url.match(filterConfig.paramName + '=' + encodeURI(filterValue) + '(&(.*))?$')); + } + + getSearchLink() { + return this.searchLink; + } + + ngOnDestroy(): void { + if (this.sub !== undefined) { + this.sub.unsubscribe(); + } + } + } diff --git a/src/app/shared/search-form/search-form.component.html b/src/app/shared/search-form/search-form.component.html index dfdc20024a..76b234ec0c 100644 --- a/src/app/shared/search-form/search-form.component.html +++ b/src/app/shared/search-form/search-form.component.html @@ -1,4 +1,4 @@ -
+
- {{value.value}} - ({{value.count}}) + + + + {{value.value}} + ({{value.count}}) + -{{"search.filters.facet-filter.show-more" | translate}} +{{"search.filters.facet-filter.show-more" + | translate}} +{{"search.filters.facet-filter.show-less" | + translate}} - \ No newline at end of file + \ No newline at end of file diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts index 2565c11945..a817eb0d9b 100644 --- a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts +++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts @@ -1,9 +1,10 @@ -import { Component, Input } from '@angular/core'; +import { Component, Input, OnInit } from '@angular/core'; 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 { ActivatedRoute } from '@angular/router'; +import { Params } from '@angular/router'; import { Observable } from 'rxjs/Observable'; +import { SearchFilterService } from '../search-filter.service'; /** * This component renders a simple item page. @@ -17,25 +18,48 @@ import { Observable } from 'rxjs/Observable'; templateUrl: './search-facet-filter.component.html', }) -export class SidebarFacetFilterComponent { +export class SidebarFacetFilterComponent implements OnInit { @Input() filterValues: FacetValue[]; @Input() filterConfig: SearchFilterConfig; + currentPage: Observable; - constructor(private searchService: SearchService, private route: ActivatedRoute) { + constructor(private filterService: SearchFilterService) { + } + + ngOnInit(): void { + this.currentPage = this.filterService.getPage(this.filterConfig.name); } isChecked(value: FacetValue) { - return this.searchService.isFilterActive(this.filterConfig.name, value.value); + return this.filterService.isFilterActive(this.filterConfig.name, value.value); } getSearchLink() { - return this.searchService.getSearchLink(); + return this.filterService.searchLink; } - getQueryParams(value: FacetValue): Observable { - const params = {}; - params[this.filterConfig.paramName] = value.value; - return this.route.queryParams.map((p) => Object.assign({}, p, params)) + getQueryParams(value: FacetValue): Params { + return this.filterService.switchFilterInURL(this.filterConfig, value.value); } + get facetCount(): Observable { + const resultCount = this.filterValues.length; + return this.currentPage.map((page: number) => { + const max = page * this.filterConfig.pageSize; + return max > resultCount ? resultCount : max; + }); + } + + showMore() { + this.filterService.increasePage(this.filterConfig.name); + } + + showLess() { + this.filterService.decreasePage(this.filterConfig.name); + } + + getCurrentPage(): Observable { + return this.filterService.getPage(this.filterConfig.name); + + } } diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.actions.ts b/src/app/+search-page/search-filters/search-filter/search-filter.actions.ts new file mode 100644 index 0000000000..89a29de527 --- /dev/null +++ b/src/app/+search-page/search-filters/search-filter/search-filter.actions.ts @@ -0,0 +1,58 @@ +import { Action } from '@ngrx/store'; + +import { type } from '../../../shared/ngrx/type'; + +/** + * For each action type in an action group, make a simple + * enum object for all of this group's action types. + * + * The 'type' utility function coerces strings into string + * literal types and runs a simple check to guarantee all + * action types in the application are unique. + */ +export const SearchFilterActionTypes = { + COLLAPSE: type('dspace/search-filter/COLLAPSE'), + INITIAL_COLLAPSE: type('dspace/search-filter/INITIAL_COLLAPSE'), + EXPAND: type('dspace/search-filter/EXPAND'), + INITIAL_EXPAND: type('dspace/search-filter/INITIAL_EXPAND'), + TOGGLE: type('dspace/search-filter/TOGGLE'), + DECREASE_PAGE: type('dspace/search-filter/DECREASE_PAGE'), + INCREASE_PAGE: type('dspace/search-filter/INCREASE_PAGE') +}; + +export class SearchFilterAction implements Action { + filterName: string; + type; + constructor(name: string) { + this.filterName = name; + } +} + +/* tslint:disable:max-classes-per-file */ +export class SearchFilterCollapseAction extends SearchFilterAction { + type = SearchFilterActionTypes.COLLAPSE; +} + +export class SearchFilterExpandAction extends SearchFilterAction { + type = SearchFilterActionTypes.EXPAND; +} + +export class SearchFilterToggleAction extends SearchFilterAction { + type = SearchFilterActionTypes.TOGGLE; +} + +export class SearchFilterInitialCollapseAction extends SearchFilterAction { + type = SearchFilterActionTypes.INITIAL_COLLAPSE; +} + +export class SearchFilterInitialExpandAction extends SearchFilterAction { + type = SearchFilterActionTypes.INITIAL_EXPAND; +} +export class SearchFilterDecreasePageAction extends SearchFilterAction { + type = SearchFilterActionTypes.DECREASE_PAGE; +} + +export class SearchFilterIncreasePageAction extends SearchFilterAction { + type = SearchFilterActionTypes.INCREASE_PAGE; +} +/* tslint:enable:max-classes-per-file */ diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.component.html b/src/app/+search-page/search-filters/search-filter/search-filter.component.html index 2279564359..c8fd3ff10c 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.component.html +++ b/src/app/+search-page/search-filters/search-filter/search-filter.component.html @@ -1,4 +1,8 @@
-
{{filter.name}}
- +
{{filter.name}}
+
+ +
\ No newline at end of file diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.component.scss b/src/app/+search-page/search-filters/search-filter/search-filter.component.scss index 3e77826dc2..93be5d1c05 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.component.scss +++ b/src/app/+search-page/search-filters/search-filter/search-filter.component.scss @@ -1,2 +1,6 @@ @import '../../../../styles/variables.scss'; -@import '../../../../styles/mixins.scss'; \ No newline at end of file +@import '../../../../styles/mixins.scss'; + +.search-filter-wrapper { + overflow: hidden; +} \ No newline at end of file diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.component.ts b/src/app/+search-page/search-filters/search-filter/search-filter.component.ts index c26efd41ad..cbffe6a8e9 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.component.ts +++ b/src/app/+search-page/search-filters/search-filter/search-filter.component.ts @@ -3,6 +3,9 @@ import { SearchFilterConfig } from '../../search-service/search-filter-config.mo import { SearchService } from '../../search-service/search.service'; import { RemoteData } from '../../../core/data/remote-data'; import { FacetValue } from '../../search-service/facet-value.model'; +import { SearchFilterService } from './search-filter.service'; +import { Observable } from 'rxjs/Observable'; +import { slide } from '../../../shared/animations/slide'; /** * This component renders a simple item page. @@ -14,21 +17,38 @@ import { FacetValue } from '../../search-service/facet-value.model'; selector: 'ds-search-filter', styleUrls: ['./search-filter.component.scss'], templateUrl: './search-filter.component.html', + animations: [slide] }) export class SidebarFilterComponent implements OnInit { @Input() filter: SearchFilterConfig; filterValues: RemoteData; - isCollapsed = false; - constructor(private searchService: SearchService) { + constructor(private searchService: SearchService, private filterService: SearchFilterService) { } ngOnInit() { this.filterValues = this.searchService.getFacetValuesFor(this.filter.name); + if (this.filter.isOpenByDefault) { + this.initialExpand(); + } else { + this.initialCollapse(); + } } toggle() { - this.isCollapsed = !this.isCollapsed; + this.filterService.toggle(this.filter.name); } -} \ No newline at end of file + + isCollapsed(): Observable { + return this.filterService.isCollapsed(this.filter.name); + } + + initialCollapse() { + this.filterService.initialCollapse(this.filter.name); + } + + initialExpand() { + this.filterService.initialExpand(this.filter.name); + } +} diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.reducer.ts b/src/app/+search-page/search-filters/search-filter/search-filter.reducer.ts new file mode 100644 index 0000000000..30892c4c97 --- /dev/null +++ b/src/app/+search-page/search-filters/search-filter/search-filter.reducer.ts @@ -0,0 +1,95 @@ +import { SearchFilterAction, SearchFilterActionTypes } from './search-filter.actions'; +import { isEmpty } from '../../../shared/empty.util'; + +export interface SearchFilterState { + filterCollapsed: boolean, + page: number +} + +export interface SearchFiltersState { + [name: string]: SearchFilterState +} + +const initialState: SearchFiltersState = Object.create(null); + +export function filterReducer(state = initialState, action: SearchFilterAction): SearchFiltersState { + + switch (action.type) { + + case SearchFilterActionTypes.INITIAL_COLLAPSE: { + if (isEmpty(state) || isEmpty(state[action.filterName])) { + return Object.assign({}, state, { + [action.filterName]: { + filterCollapsed: true, + page: 1 + } + }); + } + return state; + } + + case SearchFilterActionTypes.INITIAL_EXPAND: { + if (isEmpty(state) || isEmpty(state[action.filterName])) { + return Object.assign({}, state, { + [action.filterName]: { + filterCollapsed: false, + page: 1 + } + }); + } + return state; + } + + case SearchFilterActionTypes.COLLAPSE: { + return Object.assign({}, state, { + [action.filterName]: { + filterCollapsed: true, + page: state[action.filterName].page + } + }); + } + + case SearchFilterActionTypes.EXPAND: { + return Object.assign({}, state, { + [action.filterName]: { + filterCollapsed: false, + page: state[action.filterName].page + } + }); + + } + + case SearchFilterActionTypes.DECREASE_PAGE: { + return Object.assign({}, state, { + [action.filterName]: { + filterCollapsed: state[action.filterName].filterCollapsed, + page: state[action.filterName].page - 1 + } + }); + } + + case SearchFilterActionTypes.INCREASE_PAGE: { + return Object.assign({}, state, { + [action.filterName]: { + filterCollapsed: state[action.filterName].filterCollapsed, + page: state[action.filterName].page + 1 + } + }); + + } + + case SearchFilterActionTypes.TOGGLE: { + return Object.assign({}, state, { + [action.filterName]: { + filterCollapsed: !state[action.filterName].filterCollapsed, + page: state[action.filterName].page + } + }); + + } + + default: { + return state; + } + } +} diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.service.ts b/src/app/+search-page/search-filters/search-filter/search-filter.service.ts new file mode 100644 index 0000000000..258f54aa78 --- /dev/null +++ b/src/app/+search-page/search-filters/search-filter/search-filter.service.ts @@ -0,0 +1,136 @@ +import { Injectable, OnDestroy } from '@angular/core'; +import { SearchFiltersState, SearchFilterState } from './search-filter.reducer'; +import { createSelector, MemoizedSelector, Store } from '@ngrx/store'; +import { Observable } from 'rxjs/Observable'; +import { AppState } from '../../../app.reducer'; +import { + SearchFilterCollapseAction, SearchFilterDecreasePageAction, SearchFilterIncreasePageAction, + SearchFilterInitialCollapseAction, + SearchFilterInitialExpandAction, + SearchFilterToggleAction +} from './search-filter.actions'; +import { hasValue, isNotEmpty } from '../../../shared/empty.util'; +import { ActivatedRoute, Params, Router } from '@angular/router'; +import { SearchFilterConfig } from '../../search-service/search-filter-config.model'; +import { RemoteData } from '../../../core/data/remote-data'; +import { PageInfo } from '../../../core/shared/page-info.model'; +import { FacetValue } from '../../search-service/facet-value.model'; +import { FilterType } from '../../search-service/filter-type.model'; +import { SearchService } from '../../search-service/search.service'; + +const filterStateSelector = (state: SearchFiltersState) => state.searchFilter; + +@Injectable() +export class SearchFilterService implements OnDestroy { + private sub; + + constructor(private store: Store, + private route: ActivatedRoute, + private router: Router, + private searchService: SearchService) { + } + + isFilterActive(filterName: string, filterValue: string): boolean { + let filterConfig: SearchFilterConfig; + this.sub = this.searchService.getConfig().payload + .subscribe((configuration) => filterConfig = configuration + .find((config: SearchFilterConfig) => config.name === filterName)); + return isNotEmpty(this.route.snapshot.queryParams[filterConfig.paramName]) && [...this.route.snapshot.queryParams[filterConfig.paramName]].indexOf(filterValue, 0) > -1; + } + + switchFilterInURL(filterConfig: SearchFilterConfig, value: string) { + console.log(this.route.snapshot.queryParams); + if (this.isFilterActive(filterConfig.name, value)) { + return this.removeQueryParameter(filterConfig.paramName, value); + } else { + return this.addQueryParameter(filterConfig.paramName, value); + } + } + + addQueryParameter(paramName: string, value: string): Params { + const currentParams = this.route.snapshot.queryParams; + const newParam = {}; + if ((currentParams[paramName])) { + newParam[paramName] = [...currentParams[paramName], value]; + } else { + newParam[paramName] = [value]; + } + return Object.assign({}, currentParams, newParam); + } + + removeQueryParameter(paramName: string, value: string): Params { + const currentParams = this.route.snapshot.queryParams; + const newParam = {}; + let currentFilterParams = [...currentParams[paramName]]; + if (isNotEmpty(currentFilterParams)) { + const index = currentFilterParams.indexOf(value, 0); + if (index > -1) { + currentFilterParams = currentFilterParams.splice(index, 1); + } + newParam[paramName] = currentFilterParams; + } + return Object.assign({}, currentParams, newParam); + } + + get searchLink() { + return this.searchService.searchLink; + } + + isCollapsed(filterName: string): Observable { + return this.store.select(filterByNameSelector(filterName)) + .map((object: SearchFilterState) => object.filterCollapsed); + } + + getPage(filterName: string): Observable { + return this.store.select(filterByNameSelector(filterName)) + .map((object: SearchFilterState) => object.page); + } + + public collapse(filterName: string): void { + this.store.dispatch(new SearchFilterCollapseAction(filterName)); + } + + public expand(filterName: string): void { + this.store.dispatch(new SearchFilterCollapseAction(filterName)); + } + + public toggle(filterName: string): void { + this.store.dispatch(new SearchFilterToggleAction(filterName)); + } + + public initialCollapse(filterName: string): void { + this.store.dispatch(new SearchFilterInitialCollapseAction(filterName)); + } + + public initialExpand(filterName: string): void { + this.store.dispatch(new SearchFilterInitialExpandAction(filterName)); + } + + public decreasePage(filterName: string): void { + this.store.dispatch(new SearchFilterDecreasePageAction(filterName)); + } + + public increasePage(filterName: string): void { + this.store.dispatch(new SearchFilterIncreasePageAction(filterName)); + } + + ngOnDestroy(): void { + if (this.sub !== undefined) { + this.sub.unsubscribe(); + } + } +} + +function filterByNameSelector(name: string): MemoizedSelector { + return keySelector(name); +} + +export function keySelector(key: string): MemoizedSelector { + return createSelector(filterStateSelector, (state: SearchFilterState) => { + if (hasValue(state)) { + return state[key]; + } else { + return undefined; + } + }); +} diff --git a/src/app/+search-page/search-page.component.html b/src/app/+search-page/search-page.component.html index 25bd741415..881efbf0c1 100644 --- a/src/app/+search-page/search-page.component.html +++ b/src/app/+search-page/search-page.component.html @@ -13,7 +13,7 @@
+ [@pushInOut]="(isSidebarCollapsed() | async) ? 'collapsed' : 'expanded'"> config.name === filterName); - return isNotEmpty(this.router.url.match(filterConfig.paramName + '=' + encodeURI(filterValue) + '(&(.*))?$')); - } - getSearchLink() { return this.searchLink; } @@ -249,5 +244,4 @@ export class SearchService implements OnDestroy { this.sub.unsubscribe(); } } - } diff --git a/src/app/+search-page/search-sidebar/search-sidebar.effects.ts b/src/app/+search-page/search-sidebar/search-sidebar.effects.ts index 71d928bd5f..2894796695 100644 --- a/src/app/+search-page/search-sidebar/search-sidebar.effects.ts +++ b/src/app/+search-page/search-sidebar/search-sidebar.effects.ts @@ -2,7 +2,6 @@ import { Injectable } from '@angular/core'; import { Effect, Actions } from '@ngrx/effects' import * as fromRouter from '@ngrx/router-store'; -import { HostWindowActionTypes } from '../../shared/host-window.actions'; import { SearchSidebarCollapseAction } from './search-sidebar.actions'; @Injectable() diff --git a/src/app/app.reducer.ts b/src/app/app.reducer.ts index b01fd62f60..72b29519de 100644 --- a/src/app/app.reducer.ts +++ b/src/app/app.reducer.ts @@ -7,12 +7,17 @@ import { SearchSidebarState, sidebarReducer } from './+search-page/search-sidebar/search-sidebar.reducer'; +import { + filterReducer, + SearchFiltersState +} from './+search-page/search-filters/search-filter/search-filter.reducer'; export interface AppState { router: fromRouter.RouterReducerState; hostWindow: HostWindowState; header: HeaderState; searchSidebar: SearchSidebarState; + searchFilter: SearchFiltersState; } export const appReducers: ActionReducerMap = { @@ -20,4 +25,5 @@ export const appReducers: ActionReducerMap = { hostWindow: hostWindowReducer, header: headerReducer, searchSidebar: sidebarReducer, + searchFilter: filterReducer }; diff --git a/src/app/shared/animations/push.ts b/src/app/shared/animations/push.ts new file mode 100644 index 0000000000..124e3289e7 --- /dev/null +++ b/src/app/shared/animations/push.ts @@ -0,0 +1,16 @@ +import { animate, state, transition, trigger, style } from '@angular/animations'; + +export const pushInOut = trigger('pushInOut', [ + + /* + state('expanded', style({ right: '100%' })); + + state('collapsed', style({ right: 0 })); +*/ + + state('expanded', style({ left: '100%' })), + + state('collapsed', style({ left: 0 })), + + transition('expanded <=> collapsed', animate(250)), +]); diff --git a/src/app/shared/animations/slide.ts b/src/app/shared/animations/slide.ts index e4a1fcbb59..381f592168 100644 --- a/src/app/shared/animations/slide.ts +++ b/src/app/shared/animations/slide.ts @@ -1,16 +1,10 @@ import { animate, state, transition, trigger, style } from '@angular/animations'; -export const slideInOut = trigger('slideInOut', [ +export const slide = trigger('slide', [ - /* - state('expanded', style({ right: '100%' })); + state('expanded', style({ height: '*' })), - state('collapsed', style({ right: 0 })); -*/ - - state('expanded', style({ left: '100%' })), - - state('collapsed', style({ left: 0 })), + state('collapsed', style({ height: 0 })), transition('expanded <=> collapsed', animate(250)), ]); From 57c525d1788e8cdc753660f0b27434b43da267eb Mon Sep 17 00:00:00 2001 From: Lotte Hofstede Date: Wed, 8 Nov 2017 16:26:36 +0100 Subject: [PATCH 04/18] solved merge issues --- src/app/+search-page/search-page.component.ts | 1 - src/app/shared/shared.module.ts | 3 --- 2 files changed, 4 deletions(-) diff --git a/src/app/+search-page/search-page.component.ts b/src/app/+search-page/search-page.component.ts index 04d8f5ce3d..3026256ec8 100644 --- a/src/app/+search-page/search-page.component.ts +++ b/src/app/+search-page/search-page.component.ts @@ -1,6 +1,5 @@ import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { Observable } from 'rxjs/Observable'; import { SortOptions } from '../core/cache/models/sort-options.model'; import { CommunityDataService } from '../core/data/community-data.service'; import { RemoteData } from '../core/data/remote-data'; diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 85aeca5390..245d45ea4e 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -67,9 +67,6 @@ const COMPONENTS = [ ViewModeSwitchComponent ]; -const DIRECTIVES = [ -]; - const ENTRY_COMPONENTS = [ // put shared entry components (components that are created dynamically) here CollectionListElementComponent, From 569d7a634bc71d88c39e40466e5551813457b6b4 Mon Sep 17 00:00:00 2001 From: Lotte Hofstede Date: Thu, 9 Nov 2017 15:02:36 +0100 Subject: [PATCH 05/18] 45621: filters facet route service --- .../search-facet-filter.component.html | 4 +- .../search-facet-filter.component.ts | 8 +- .../search-filter.component.html | 2 +- .../search-filter/search-filter.component.ts | 2 +- .../search-filter/search-filter.service.ts | 82 ++++++++----------- .../search-filters.component.html | 4 +- .../search-filters.component.ts | 3 +- .../+search-page/search-page.component.html | 11 ++- .../search-page.component.spec.ts | 25 +++--- src/app/+search-page/search-page.component.ts | 2 +- .../search-sidebar/search-sidebar.service.ts | 3 +- src/app/app.module.ts | 2 +- src/app/shared/route.service.spec.ts | 51 ++++++++++++ src/app/shared/route.service.ts | 47 +++++++++++ 14 files changed, 171 insertions(+), 75 deletions(-) create mode 100644 src/app/shared/route.service.spec.ts create mode 100644 src/app/shared/route.service.ts diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.html b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.html index f54883011a..e253e65048 100644 --- a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.html +++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.html @@ -1,7 +1,7 @@ + [queryParams]="getQueryParams(value) | async"> - + {{value.value}} ({{value.count}}) diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts index a817eb0d9b..0c9eb06baa 100644 --- a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts +++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts @@ -30,16 +30,16 @@ export class SidebarFacetFilterComponent implements OnInit { this.currentPage = this.filterService.getPage(this.filterConfig.name); } - isChecked(value: FacetValue) { - return this.filterService.isFilterActive(this.filterConfig.name, value.value); + isChecked(value: FacetValue): Observable { + return this.filterService.isFilterActive(this.filterConfig.paramName, value.value); } getSearchLink() { return this.filterService.searchLink; } - getQueryParams(value: FacetValue): Params { - return this.filterService.switchFilterInURL(this.filterConfig, value.value); + getQueryParams(value: FacetValue): Observable { + return this.filterService.getFilterValueURL(this.filterConfig, value.value); } get facetCount(): Observable { diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.component.html b/src/app/+search-page/search-filters/search-filter/search-filter.component.html index c8fd3ff10c..c78ea9f676 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.component.html +++ b/src/app/+search-page/search-filters/search-filter/search-filter.component.html @@ -3,6 +3,6 @@ [ngClass]="(isCollapsed() | async) ? 'fa-plus' : 'fa-minus'">
+ [filterValues]="(filterValues | async)?.payload">
\ No newline at end of file diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.component.ts b/src/app/+search-page/search-filters/search-filter/search-filter.component.ts index cbffe6a8e9..211315e397 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.component.ts +++ b/src/app/+search-page/search-filters/search-filter/search-filter.component.ts @@ -22,7 +22,7 @@ import { slide } from '../../../shared/animations/slide'; export class SidebarFilterComponent implements OnInit { @Input() filter: SearchFilterConfig; - filterValues: RemoteData; + filterValues: Observable>; constructor(private searchService: SearchService, private filterService: SearchFilterService) { } diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.service.ts b/src/app/+search-page/search-filters/search-filter/search-filter.service.ts index 258f54aa78..098611bdae 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.service.ts +++ b/src/app/+search-page/search-filters/search-filter/search-filter.service.ts @@ -10,7 +10,7 @@ import { SearchFilterToggleAction } from './search-filter.actions'; import { hasValue, isNotEmpty } from '../../../shared/empty.util'; -import { ActivatedRoute, Params, Router } from '@angular/router'; +import { ActivatedRoute, convertToParamMap, Params, Router } from '@angular/router'; import { SearchFilterConfig } from '../../search-service/search-filter-config.model'; import { RemoteData } from '../../../core/data/remote-data'; import { PageInfo } from '../../../core/shared/page-info.model'; @@ -21,55 +21,49 @@ import { SearchService } from '../../search-service/search.service'; const filterStateSelector = (state: SearchFiltersState) => state.searchFilter; @Injectable() -export class SearchFilterService implements OnDestroy { - private sub; +export class SearchFilterService { constructor(private store: Store, private route: ActivatedRoute, - private router: Router, private searchService: SearchService) { + this.route.queryParams.subscribe((params) => { + console.log(params); + }) } - isFilterActive(filterName: string, filterValue: string): boolean { - let filterConfig: SearchFilterConfig; - this.sub = this.searchService.getConfig().payload - .subscribe((configuration) => filterConfig = configuration - .find((config: SearchFilterConfig) => config.name === filterName)); - return isNotEmpty(this.route.snapshot.queryParams[filterConfig.paramName]) && [...this.route.snapshot.queryParams[filterConfig.paramName]].indexOf(filterValue, 0) > -1; + isFilterActive(paramName: string, filterValue: string): Observable { + return this.route.queryParamMap.map((map) => map.getAll(paramName).indexOf(filterValue) > -1 ); } - switchFilterInURL(filterConfig: SearchFilterConfig, value: string) { - console.log(this.route.snapshot.queryParams); - if (this.isFilterActive(filterConfig.name, value)) { - return this.removeQueryParameter(filterConfig.paramName, value); - } else { - return this.addQueryParameter(filterConfig.paramName, value); - } - } - - addQueryParameter(paramName: string, value: string): Params { - const currentParams = this.route.snapshot.queryParams; - const newParam = {}; - if ((currentParams[paramName])) { - newParam[paramName] = [...currentParams[paramName], value]; - } else { - newParam[paramName] = [value]; - } - return Object.assign({}, currentParams, newParam); - } - - removeQueryParameter(paramName: string, value: string): Params { - const currentParams = this.route.snapshot.queryParams; - const newParam = {}; - let currentFilterParams = [...currentParams[paramName]]; - if (isNotEmpty(currentFilterParams)) { - const index = currentFilterParams.indexOf(value, 0); - if (index > -1) { - currentFilterParams = currentFilterParams.splice(index, 1); + getFilterValueURL(filterConfig: SearchFilterConfig, value: string): Observable { + return this.isFilterActive(filterConfig.paramName, value).flatMap((isActive) => { + if (isActive) { + return this.removeQueryParameter(filterConfig.paramName, value); + } else { + return this.addQueryParameter(filterConfig.paramName, value); } - newParam[paramName] = currentFilterParams; - } - return Object.assign({}, currentParams, newParam); + }) + } + + addQueryParameter(paramName: string, value: string): Observable { + return this.route.queryParams.map((currentParams) => { + const newParam = {}; + newParam[paramName] = [...convertToParamMap(currentParams).getAll(paramName), value]; + return Object.assign({}, currentParams, newParam); + }); + } + + removeQueryParameter(paramName: string, value: string): Observable { + return this.route.queryParams.map((currentParams) => { + const newParam = {}; + const currentFilterParams = convertToParamMap(currentParams).getAll(paramName); + if (isNotEmpty(currentFilterParams)) { + newParam[paramName] = currentFilterParams.filter((param) => (param !== value)); + } + console.log(Object.assign({}, currentParams, newParam)); + return Object.assign({}, currentParams, newParam); + }); + } get searchLink() { @@ -113,12 +107,6 @@ export class SearchFilterService implements OnDestroy { public increasePage(filterName: string): void { this.store.dispatch(new SearchFilterIncreasePageAction(filterName)); } - - ngOnDestroy(): void { - if (this.sub !== undefined) { - this.sub.unsubscribe(); - } - } } function filterByNameSelector(name: string): MemoizedSelector { diff --git a/src/app/+search-page/search-filters/search-filters.component.html b/src/app/+search-page/search-filters/search-filters.component.html index 270649598f..9d08545923 100644 --- a/src/app/+search-page/search-filters/search-filters.component.html +++ b/src/app/+search-page/search-filters/search-filters.component.html @@ -1,6 +1,6 @@

{{"search.filters.head" | translate}}

-
-
+
+
diff --git a/src/app/+search-page/search-filters/search-filters.component.ts b/src/app/+search-page/search-filters/search-filters.component.ts index 216e8c0832..8380df7e05 100644 --- a/src/app/+search-page/search-filters/search-filters.component.ts +++ b/src/app/+search-page/search-filters/search-filters.component.ts @@ -2,6 +2,7 @@ import { Component, Input } from '@angular/core'; import { SearchService } from '../search-service/search.service'; import { RemoteData } from '../../core/data/remote-data'; import { SearchFilterConfig } from '../search-service/search-filter-config.model'; +import { Observable } from 'rxjs/Observable'; /** * This component renders a simple item page. @@ -16,7 +17,7 @@ import { SearchFilterConfig } from '../search-service/search-filter-config.model }) export class SidebarFiltersComponent { - filters: RemoteData; + filters: Observable>; constructor(private searchService: SearchService) { this.filters = searchService.getConfig(); } diff --git a/src/app/+search-page/search-page.component.html b/src/app/+search-page/search-page.component.html index 56c1b51fed..7efa1077d3 100644 --- a/src/app/+search-page/search-page.component.html +++ b/src/app/+search-page/search-page.component.html @@ -2,7 +2,7 @@
+ resultCount="{{(results | async)?.pageInfo.totalElements}}">
+ [ngClass]="{'active': !(isSidebarCollapsed() | async)}"> + + + Not Active - Sidebar is collapsed + Active- Sidebar is not collapsed +
diff --git a/src/app/+search-page/search-page.component.spec.ts b/src/app/+search-page/search-page.component.spec.ts index f1b86e3270..a0b31d0ff2 100644 --- a/src/app/+search-page/search-page.component.spec.ts +++ b/src/app/+search-page/search-page.component.spec.ts @@ -1,6 +1,6 @@ import { NO_ERRORS_SCHEMA } from '@angular/core'; -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { Store } from '@ngrx/store'; import { TranslateModule } from '@ngx-translate/core'; @@ -18,7 +18,7 @@ import { By } from '@angular/platform-browser'; import { NgbCollapseModule } from '@ng-bootstrap/ng-bootstrap'; import { SearchSidebarService } from './search-sidebar/search-sidebar.service'; -describe('SearchPageComponent', () => { +fdescribe('SearchPageComponent', () => { let comp: SearchPageComponent; let fixture: ComponentFixture; let searchServiceObject: SearchService; @@ -41,10 +41,11 @@ describe('SearchPageComponent', () => { }) }; const sidebarService = { - isCollapsed: Observable.of(true), - collapse: () => this.isCollapsed = Observable.of(true), - expand: () => this.isCollapsed = Observable.of(false) - } + collapsed: Observable.of(true), + collapse: () => {this.collapsed = Observable.of(true)}, + expand: () => {console.log('expand'); this.collapsed = Observable.of(false)}, + isCollapsed: () => {if (this.collapse) {this.collapsed.subscribe(a => console.log(a))};return this.collapsed}, + }; const mockCommunityList = []; const communityDataServiceStub = { @@ -178,11 +179,12 @@ describe('SearchPageComponent', () => { beforeEach(() => { menu = fixture.debugElement.query(By.css('#search-sidebar-xs')).nativeElement; - comp.isSidebarCollapsed = () => Observable.of(true); - fixture.detectChanges(); + sidebarService.collapse(); }); it('should close the sidebar', () => { + sidebarService.isCollapsed().subscribe((v) => console.log('after closing, is collapsed is...: ', v)); + console.log(menu.classList); expect(menu.classList).not.toContain('active'); }); @@ -193,12 +195,13 @@ describe('SearchPageComponent', () => { beforeEach(() => { menu = fixture.debugElement.query(By.css('#search-sidebar-xs')).nativeElement; - comp.isSidebarCollapsed = () => Observable.of(false); - fixture.detectChanges(); + sidebarService.expand(); }); it('should open the menu', () => { - sidebarService.isCollapsed.subscribe((a) => {console.log(a)}) + sidebarService.isCollapsed().subscribe((v) => console.log('after opening, is collapsed is...: ', v)); + console.log(menu.classList); + debugger; expect(menu.classList).toContain('active'); }); diff --git a/src/app/+search-page/search-page.component.ts b/src/app/+search-page/search-page.component.ts index 3026256ec8..0b084a46d0 100644 --- a/src/app/+search-page/search-page.component.ts +++ b/src/app/+search-page/search-page.component.ts @@ -107,6 +107,6 @@ export class SearchPageComponent implements OnInit, OnDestroy { } public isSidebarCollapsed(): Observable { - return this.sidebarService.isCollapsed; + return this.sidebarService.isCollapsed(); } } diff --git a/src/app/+search-page/search-sidebar/search-sidebar.service.ts b/src/app/+search-page/search-sidebar/search-sidebar.service.ts index c3e10c8ddb..b474062e93 100644 --- a/src/app/+search-page/search-sidebar/search-sidebar.service.ts +++ b/src/app/+search-page/search-sidebar/search-sidebar.service.ts @@ -19,7 +19,8 @@ export class SearchSidebarService { this.isCollapsdeInStored = this.store.select(sidebarCollapsedSelector); } - get isCollapsed(): Observable { + isCollapsed(): Observable { + console.log('NEEN'); return Observable.combineLatest( this.isMobileView, this.isCollapsdeInStored, diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 14719ed266..8e74150cd6 100755 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -46,7 +46,7 @@ export function getMetaReducers(config: GlobalConfig): Array { + let service: RouteService; + const paramName1 = 'name'; + const paramValue1 = 'Test Name'; + const paramName2 = 'id'; + const paramValue2a = 'Test id'; + const paramValue2b = 'another id'; + const nonExistingParamName = 'non existing name'; + const nonExistingParamValue = 'non existing value'; + + const paramObject: Params = {}; + + paramObject[paramName1] = paramValue1; + paramObject[paramName2] = [paramValue2a, paramValue2b]; + + beforeEach(async(() => { + return TestBed.configureTestingModule({ + declarations: [RouteService], + providers: [ + { + provide: ActivatedRoute, + useValue: { + params: Observable.of([paramObject]), + }, + }, + ] + }); + })); + + beforeEach(() => { + service = new RouteService(TestBed.get(ActivatedRoute)); + }); + + describe('hasQueryParam', () => { + it(' should return true when the parameter name exists', () => { + service.hasQueryParam(paramName1).subscribe((status) => { + expect(status).toBeTruthy(); + }); + }); + it(' should return false when the parameter name does not exists', () => { + service.hasQueryParam(nonExistingParamName).subscribe((status) => { + expect(status).toBeFalsy(); + }); + }); + }); +}); diff --git a/src/app/shared/route.service.ts b/src/app/shared/route.service.ts new file mode 100644 index 0000000000..7f045afa9c --- /dev/null +++ b/src/app/shared/route.service.ts @@ -0,0 +1,47 @@ +import { Injectable, OnDestroy } from '@angular/core'; +import { Observable } from 'rxjs/Observable'; +import { ActivatedRoute, convertToParamMap, Params, } from '@angular/router'; +import { isNotEmpty } from './empty.util'; + +@Injectable() +export class RouteService { + + constructor(private route: ActivatedRoute) { + } + + hasQueryParam(paramName: string): Observable { + return this.route.queryParamMap.map((map) => map.has(paramName)); + } + + hasQueryParamWithValue(paramName: string, paramValue: string): Observable { + return this.route.queryParamMap.map((map) => map.getAll(paramName).indexOf(paramValue) > -1); + } + + addQueryParameterValue(paramName: string, paramValue: string): Observable { + return this.route.queryParams.map((currentParams) => { + const newParam = {}; + newParam[paramName] = [...convertToParamMap(currentParams).getAll(paramName), paramValue]; + return Object.assign({}, currentParams, newParam); + }); + } + + removeQueryParameterValue(paramName: string, paramValue: string): Observable { + return this.route.queryParams.map((currentParams) => { + const newParam = {}; + const currentFilterParams = convertToParamMap(currentParams).getAll(paramName); + if (isNotEmpty(currentFilterParams)) { + newParam[paramName] = currentFilterParams.filter((param) => (param !== paramValue)); + } + return Object.assign({}, currentParams, newParam); + }); + } + + removeQueryParameter(paramName: string): Observable { + return this.route.queryParams.map((currentParams) => { + const newParam = {}; + newParam[paramName] = {}; + return Object.assign({}, currentParams, newParam); + }); + + } +} From 4522bc5e16ebbd784b472d53c94265fb9629c916 Mon Sep 17 00:00:00 2001 From: Lotte Hofstede Date: Fri, 10 Nov 2017 11:11:47 +0100 Subject: [PATCH 06/18] 45621: added unit tests --- .../search-filter/search-filter.actions.ts | 12 +- .../search-filter.reducer.spec.ts | 157 ++++++++++++++++++ .../search-filter/search-filter.reducer.ts | 7 +- .../search-filter.service.spec.ts | 103 ++++++++++++ .../search-filter/search-filter.service.ts | 54 ++---- .../+search-page/search-page.component.html | 6 +- .../search-page.component.spec.ts | 30 ++-- src/app/+search-page/search-page.component.ts | 2 +- .../search-sidebar/search-sidebar.service.ts | 3 +- src/app/shared/route.service.spec.ts | 77 ++++++++- src/app/shared/route.service.ts | 2 +- 11 files changed, 374 insertions(+), 79 deletions(-) create mode 100644 src/app/+search-page/search-filters/search-filter/search-filter.reducer.spec.ts create mode 100644 src/app/+search-page/search-filters/search-filter/search-filter.service.spec.ts diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.actions.ts b/src/app/+search-page/search-filters/search-filter/search-filter.actions.ts index 89a29de527..eb5f0bd959 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.actions.ts +++ b/src/app/+search-page/search-filters/search-filter/search-filter.actions.ts @@ -16,8 +16,8 @@ export const SearchFilterActionTypes = { EXPAND: type('dspace/search-filter/EXPAND'), INITIAL_EXPAND: type('dspace/search-filter/INITIAL_EXPAND'), TOGGLE: type('dspace/search-filter/TOGGLE'), - DECREASE_PAGE: type('dspace/search-filter/DECREASE_PAGE'), - INCREASE_PAGE: type('dspace/search-filter/INCREASE_PAGE') + DECREMENT_PAGE: type('dspace/search-filter/DECREMENT_PAGE'), + INCREMENT_PAGE: type('dspace/search-filter/INCREMENT_PAGE') }; export class SearchFilterAction implements Action { @@ -48,11 +48,11 @@ export class SearchFilterInitialCollapseAction extends SearchFilterAction { export class SearchFilterInitialExpandAction extends SearchFilterAction { type = SearchFilterActionTypes.INITIAL_EXPAND; } -export class SearchFilterDecreasePageAction extends SearchFilterAction { - type = SearchFilterActionTypes.DECREASE_PAGE; +export class SearchFilterDecrementPageAction extends SearchFilterAction { + type = SearchFilterActionTypes.DECREMENT_PAGE; } -export class SearchFilterIncreasePageAction extends SearchFilterAction { - type = SearchFilterActionTypes.INCREASE_PAGE; +export class SearchFilterIncrementPageAction extends SearchFilterAction { + type = SearchFilterActionTypes.INCREMENT_PAGE; } /* tslint:enable:max-classes-per-file */ diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.reducer.spec.ts b/src/app/+search-page/search-filters/search-filter/search-filter.reducer.spec.ts new file mode 100644 index 0000000000..f2f5421c74 --- /dev/null +++ b/src/app/+search-page/search-filters/search-filter/search-filter.reducer.spec.ts @@ -0,0 +1,157 @@ +import * as deepFreeze from 'deep-freeze'; +import { + SearchFilterCollapseAction, SearchFilterExpandAction, SearchFilterIncrementPageAction, + SearchFilterInitialCollapseAction, + SearchFilterInitialExpandAction, + SearchFilterToggleAction, + SearchFilterDecrementPageAction +} from './search-filter.actions'; +import { filterReducer } from './search-filter.reducer'; + +const filterName1 = 'author'; +const filterName2 = 'scope'; + +class NullAction extends SearchFilterCollapseAction { + type = null; + + constructor() { + super(undefined); + } +} + +describe('filterReducer', () => { + + it('should return the current state when no valid actions have been made', () => { + const state = { author: { filterCollapsed: true, page: 1 } }; + const action = new NullAction(); + const newState = filterReducer(state, action); + + expect(newState).toEqual(state); + }); + + it('should start with an empty object', () => { + const state = Object.create({}); + const action = new NullAction(); + const initialState = filterReducer(undefined, action); + + // The search filter starts collapsed + expect(initialState).toEqual(state); + }); + + it('should set filterCollapsed to true in response to the COLLAPSE action', () => { + const state = {}; + state[filterName1] = { filterCollapsed: false, page: 1 }; + const action = new SearchFilterCollapseAction(filterName1); + const newState = filterReducer(state, action); + + expect(newState[filterName1].filterCollapsed).toEqual(true); + }); + + it('should perform the COLLAPSE action without affecting the previous state', () => { + const state = {}; + state[filterName1] = { filterCollapsed: false, page: 1 }; + deepFreeze([state]); + + const action = new SearchFilterCollapseAction(filterName1); + filterReducer(state, action); + + // no expect required, deepFreeze will ensure an exception is thrown if the state + // is mutated, and any uncaught exception will cause the test to fail + }); + + it('should set filterCollapsed to false in response to the EXPAND action', () => { + const state = {}; + state[filterName1] = { filterCollapsed: true, page: 1 }; + const action = new SearchFilterExpandAction(filterName1); + const newState = filterReducer(state, action); + + expect(newState[filterName1].filterCollapsed).toEqual(false); + }); + + it('should perform the EXPAND action without affecting the previous state', () => { + const state = {}; + state[filterName1] = { filterCollapsed: true, page: 1 }; + deepFreeze([state]); + + const action = new SearchFilterExpandAction(filterName1); + filterReducer(state, action); + }); + + it('should flip the value of filterCollapsed in response to the TOGGLE action', () => { + const state1 = {}; + state1[filterName1] = { filterCollapsed: true, page: 1 }; + const action = new SearchFilterToggleAction(filterName1); + + const state2 = filterReducer(state1, action); + const state3 = filterReducer(state2, action); + + expect(state2[filterName1].filterCollapsed).toEqual(false); + expect(state3[filterName1].filterCollapsed).toEqual(true); + }); + + it('should perform the TOGGLE action without affecting the previous state', () => { + const state = {}; + state[filterName1] = { filterCollapsed: true, page: 1 }; + deepFreeze([state]); + + const action = new SearchFilterToggleAction(filterName1); + 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', () => { + const state = {}; + state[filterName2] = { filterCollapsed: false, page: 1 }; + const action = new SearchFilterInitialCollapseAction(filterName1); + const newState = filterReducer(state, action); + + 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', () => { + const state = {}; + state[filterName2] = { filterCollapsed: true, page: 1 }; + const action = new SearchFilterInitialExpandAction(filterName1); + const newState = filterReducer(state, action); + 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', () => { + const state = {}; + state[filterName1] = { filterCollapsed: false, page: 1 }; + const action = new SearchFilterInitialCollapseAction(filterName1); + const newState = filterReducer(state, action); + 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', () => { + const state = {}; + state[filterName1] = { filterCollapsed: true, page: 1 }; + const action = new SearchFilterInitialExpandAction(filterName1); + const newState = filterReducer(state, action); + expect(newState).toEqual(state); + }); + + it('should increment with 1 for the specified filter in response to the INCREMENT_PAGE action', () => { + const state = {}; + state[filterName1] = { filterCollapsed: true, page: 5 }; + const action = new SearchFilterIncrementPageAction(filterName1); + const newState = filterReducer(state, action); + expect(newState[filterName1].page).toEqual(6); + }); + + it('should decrement with 1 for the specified filter in response to the DECREMENT_PAGE action', () => { + const state = {}; + state[filterName1] = { filterCollapsed: true, page: 12 }; + const action = new SearchFilterDecrementPageAction(filterName1); + const newState = filterReducer(state, action); + expect(newState[filterName1].page).toEqual(11); + }); + + it('should not decrement when page is 1 for the specified filter in response to the DECREMENT_PAGE action', () => { + const state = {}; + state[filterName1] = { filterCollapsed: true, page: 1 }; + const action = new SearchFilterDecrementPageAction(filterName1); + const newState = filterReducer(state, action); + expect(newState[filterName1].page).toEqual(1); + }); +}); diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.reducer.ts b/src/app/+search-page/search-filters/search-filter/search-filter.reducer.ts index 30892c4c97..fb1dc6802b 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.reducer.ts +++ b/src/app/+search-page/search-filters/search-filter/search-filter.reducer.ts @@ -59,16 +59,17 @@ export function filterReducer(state = initialState, action: SearchFilterAction): } - case SearchFilterActionTypes.DECREASE_PAGE: { + case SearchFilterActionTypes.DECREMENT_PAGE: { + const page = state[action.filterName].page - 1; return Object.assign({}, state, { [action.filterName]: { filterCollapsed: state[action.filterName].filterCollapsed, - page: state[action.filterName].page - 1 + page: (page >= 1 ? page : 1) } }); } - case SearchFilterActionTypes.INCREASE_PAGE: { + case SearchFilterActionTypes.INCREMENT_PAGE: { return Object.assign({}, state, { [action.filterName]: { filterCollapsed: state[action.filterName].filterCollapsed, diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.service.spec.ts b/src/app/+search-page/search-filters/search-filter/search-filter.service.spec.ts new file mode 100644 index 0000000000..0e90f3242a --- /dev/null +++ b/src/app/+search-page/search-filters/search-filter/search-filter.service.spec.ts @@ -0,0 +1,103 @@ +import { async, TestBed } from '@angular/core/testing'; +import { Observable } from 'rxjs/Observable'; +import { SearchFilterService } from './search-filter.service'; +import { Store } from '@ngrx/store'; +import { + SearchFilterCollapseAction, SearchFilterDecrementPageAction, SearchFilterExpandAction, + SearchFilterIncrementPageAction, + SearchFilterInitialCollapseAction, SearchFilterInitialExpandAction, SearchFilterToggleAction +} from './search-filter.actions'; +import { SearchService } from '../../search-service/search.service'; +import { SearchFiltersState } from './search-filter.reducer'; +import { RouteService } from '../../../shared/route.service'; + +describe('SearchFilterService', () => { + let service: SearchFilterService; + const filterName1 = 'author'; + // const filterName2 = 'scope'; + const store: Store = jasmine.createSpyObj('store', { + /* tslint:disable:no-empty */ + dispatch: {}, + /* tslint:enable:no-empty */ + select: Observable.of(true) + }); + const routeService = new RouteService(null); + const searchService = new SearchService(null, null, null) + + beforeEach(() => { + service = new SearchFilterService(store, routeService, searchService); + }) ; + + describe('when the initialCollapse method is triggered', () => { + beforeEach(() => { + service.initialCollapse(filterName1); + }); + + it('SearchFilterInitialCollapseAction should be dispatched to the store', () => { + expect(store.dispatch).toHaveBeenCalledWith(new SearchFilterInitialCollapseAction(filterName1)); + }); + }); + + describe('when the initialExpand method is triggered', () => { + beforeEach(() => { + service.initialExpand(filterName1); + }); + + it('SearchFilterInitialExpandAction should be dispatched to the store', () => { + expect(store.dispatch).toHaveBeenCalledWith(new SearchFilterInitialExpandAction(filterName1)); + }); + }); + + describe('when the collapse method is triggered', () => { + beforeEach(() => { + service.collapse(filterName1); + }); + + it('SearchFilterCollapseAction should be dispatched to the store', () => { + expect(store.dispatch).toHaveBeenCalledWith(new SearchFilterCollapseAction(filterName1)); + }); + + }); + + describe('when the toggle method is triggered', () => { + beforeEach(() => { + service.toggle(filterName1); + }); + + it('SearchFilterInitialExpandAction should be dispatched to the store', () => { + expect(store.dispatch).toHaveBeenCalledWith(new SearchFilterToggleAction(filterName1)); + }); + }); + + describe('when the decreasePage method is triggered', () => { + beforeEach(() => { + service.decreasePage(filterName1); + }); + + it('SearchFilterDecrementPageAction should be dispatched to the store', () => { + expect(store.dispatch).toHaveBeenCalledWith(new SearchFilterDecrementPageAction(filterName1)); + }); + + }); + + describe('when the increasePage method is triggered', () => { + beforeEach(() => { + service.increasePage(filterName1); + }); + + it('SearchFilterCollapseAction should be dispatched to the store', () => { + expect(store.dispatch).toHaveBeenCalledWith(new SearchFilterIncrementPageAction(filterName1)); + }); + + }); + + describe('when the expand method is triggered', () => { + beforeEach(() => { + service.expand(filterName1); + }); + + it('SearchSidebarExpandAction should be dispatched to the store', () => { + expect(store.dispatch).toHaveBeenCalledWith(new SearchFilterExpandAction(filterName1)); + }); + }); +}); diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.service.ts b/src/app/+search-page/search-filters/search-filter/search-filter.service.ts index 098611bdae..492eabdcf3 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.service.ts +++ b/src/app/+search-page/search-filters/search-filter/search-filter.service.ts @@ -1,22 +1,20 @@ -import { Injectable, OnDestroy } from '@angular/core'; +import { Injectable } from '@angular/core'; import { SearchFiltersState, SearchFilterState } from './search-filter.reducer'; import { createSelector, MemoizedSelector, Store } from '@ngrx/store'; import { Observable } from 'rxjs/Observable'; -import { AppState } from '../../../app.reducer'; import { - SearchFilterCollapseAction, SearchFilterDecreasePageAction, SearchFilterIncreasePageAction, + SearchFilterCollapseAction, + SearchFilterDecrementPageAction, SearchFilterExpandAction, + SearchFilterIncrementPageAction, SearchFilterInitialCollapseAction, SearchFilterInitialExpandAction, SearchFilterToggleAction } from './search-filter.actions'; -import { hasValue, isNotEmpty } from '../../../shared/empty.util'; -import { ActivatedRoute, convertToParamMap, Params, Router } from '@angular/router'; +import { hasValue, } from '../../../shared/empty.util'; +import { Params } from '@angular/router'; import { SearchFilterConfig } from '../../search-service/search-filter-config.model'; -import { RemoteData } from '../../../core/data/remote-data'; -import { PageInfo } from '../../../core/shared/page-info.model'; -import { FacetValue } from '../../search-service/facet-value.model'; -import { FilterType } from '../../search-service/filter-type.model'; import { SearchService } from '../../search-service/search.service'; +import { RouteService } from '../../../shared/route.service'; const filterStateSelector = (state: SearchFiltersState) => state.searchFilter; @@ -24,48 +22,24 @@ const filterStateSelector = (state: SearchFiltersState) => state.searchFilter; export class SearchFilterService { constructor(private store: Store, - private route: ActivatedRoute, + private routeService: RouteService, private searchService: SearchService) { - this.route.queryParams.subscribe((params) => { - console.log(params); - }) } isFilterActive(paramName: string, filterValue: string): Observable { - return this.route.queryParamMap.map((map) => map.getAll(paramName).indexOf(filterValue) > -1 ); + return this.routeService.hasQueryParamWithValue(paramName, filterValue); } getFilterValueURL(filterConfig: SearchFilterConfig, value: string): Observable { return this.isFilterActive(filterConfig.paramName, value).flatMap((isActive) => { if (isActive) { - return this.removeQueryParameter(filterConfig.paramName, value); + return this.routeService.removeQueryParameterValue(filterConfig.paramName, value); } else { - return this.addQueryParameter(filterConfig.paramName, value); + return this.routeService.addQueryParameterValue(filterConfig.paramName, value); } }) } - addQueryParameter(paramName: string, value: string): Observable { - return this.route.queryParams.map((currentParams) => { - const newParam = {}; - newParam[paramName] = [...convertToParamMap(currentParams).getAll(paramName), value]; - return Object.assign({}, currentParams, newParam); - }); - } - - removeQueryParameter(paramName: string, value: string): Observable { - return this.route.queryParams.map((currentParams) => { - const newParam = {}; - const currentFilterParams = convertToParamMap(currentParams).getAll(paramName); - if (isNotEmpty(currentFilterParams)) { - newParam[paramName] = currentFilterParams.filter((param) => (param !== value)); - } - console.log(Object.assign({}, currentParams, newParam)); - return Object.assign({}, currentParams, newParam); - }); - - } - get searchLink() { return this.searchService.searchLink; } @@ -85,7 +59,7 @@ export class SearchFilterService { } public expand(filterName: string): void { - this.store.dispatch(new SearchFilterCollapseAction(filterName)); + this.store.dispatch(new SearchFilterExpandAction(filterName)); } public toggle(filterName: string): void { @@ -101,11 +75,11 @@ export class SearchFilterService { } public decreasePage(filterName: string): void { - this.store.dispatch(new SearchFilterDecreasePageAction(filterName)); + this.store.dispatch(new SearchFilterDecrementPageAction(filterName)); } public increasePage(filterName: string): void { - this.store.dispatch(new SearchFilterIncreasePageAction(filterName)); + this.store.dispatch(new SearchFilterIncrementPageAction(filterName)); } } diff --git a/src/app/+search-page/search-page.component.html b/src/app/+search-page/search-page.component.html index 7efa1077d3..fe08336579 100644 --- a/src/app/+search-page/search-page.component.html +++ b/src/app/+search-page/search-page.component.html @@ -2,7 +2,7 @@
+ resultCount="{{(resultsRDObs | async)?.pageInfo?.totalElements}}">
- - Not Active - Sidebar is collapsed - Active- Sidebar is not collapsed -
diff --git a/src/app/+search-page/search-page.component.spec.ts b/src/app/+search-page/search-page.component.spec.ts index a0b31d0ff2..97ad826048 100644 --- a/src/app/+search-page/search-page.component.spec.ts +++ b/src/app/+search-page/search-page.component.spec.ts @@ -1,6 +1,6 @@ -import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; -import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { Store } from '@ngrx/store'; import { TranslateModule } from '@ngx-translate/core'; @@ -18,7 +18,7 @@ import { By } from '@angular/platform-browser'; import { NgbCollapseModule } from '@ng-bootstrap/ng-bootstrap'; import { SearchSidebarService } from './search-sidebar/search-sidebar.service'; -fdescribe('SearchPageComponent', () => { +describe('SearchPageComponent', () => { let comp: SearchPageComponent; let fixture: ComponentFixture; let searchServiceObject: SearchService; @@ -41,11 +41,10 @@ fdescribe('SearchPageComponent', () => { }) }; const sidebarService = { - collapsed: Observable.of(true), - collapse: () => {this.collapsed = Observable.of(true)}, - expand: () => {console.log('expand'); this.collapsed = Observable.of(false)}, - isCollapsed: () => {if (this.collapse) {this.collapsed.subscribe(a => console.log(a))};return this.collapsed}, - }; + isCollapsed: Observable.of(true), + collapse: () => this.isCollapsed = Observable.of(true), + expand: () => this.isCollapsed = Observable.of(false) + } const mockCommunityList = []; const communityDataServiceStub = { @@ -86,6 +85,8 @@ fdescribe('SearchPageComponent', () => { }, ], schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(SearchPageComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } }).compileComponents(); })); @@ -179,12 +180,11 @@ fdescribe('SearchPageComponent', () => { beforeEach(() => { menu = fixture.debugElement.query(By.css('#search-sidebar-xs')).nativeElement; - sidebarService.collapse(); + comp.isSidebarCollapsed = () => Observable.of(true); + fixture.detectChanges(); }); it('should close the sidebar', () => { - sidebarService.isCollapsed().subscribe((v) => console.log('after closing, is collapsed is...: ', v)); - console.log(menu.classList); expect(menu.classList).not.toContain('active'); }); @@ -195,15 +195,13 @@ fdescribe('SearchPageComponent', () => { beforeEach(() => { menu = fixture.debugElement.query(By.css('#search-sidebar-xs')).nativeElement; - sidebarService.expand(); + comp.isSidebarCollapsed = () => Observable.of(false); + fixture.detectChanges(); }); it('should open the menu', () => { - sidebarService.isCollapsed().subscribe((v) => console.log('after opening, is collapsed is...: ', v)); - console.log(menu.classList); - debugger; expect(menu.classList).toContain('active'); }); }); -}); +}); \ No newline at end of file diff --git a/src/app/+search-page/search-page.component.ts b/src/app/+search-page/search-page.component.ts index 0b084a46d0..3026256ec8 100644 --- a/src/app/+search-page/search-page.component.ts +++ b/src/app/+search-page/search-page.component.ts @@ -107,6 +107,6 @@ export class SearchPageComponent implements OnInit, OnDestroy { } public isSidebarCollapsed(): Observable { - return this.sidebarService.isCollapsed(); + return this.sidebarService.isCollapsed; } } diff --git a/src/app/+search-page/search-sidebar/search-sidebar.service.ts b/src/app/+search-page/search-sidebar/search-sidebar.service.ts index b474062e93..c3e10c8ddb 100644 --- a/src/app/+search-page/search-sidebar/search-sidebar.service.ts +++ b/src/app/+search-page/search-sidebar/search-sidebar.service.ts @@ -19,8 +19,7 @@ export class SearchSidebarService { this.isCollapsdeInStored = this.store.select(sidebarCollapsedSelector); } - isCollapsed(): Observable { - console.log('NEEN'); + get isCollapsed(): Observable { return Observable.combineLatest( this.isMobileView, this.isCollapsdeInStored, diff --git a/src/app/shared/route.service.spec.ts b/src/app/shared/route.service.spec.ts index 2be70b48a3..10bd147e1d 100644 --- a/src/app/shared/route.service.spec.ts +++ b/src/app/shared/route.service.spec.ts @@ -1,6 +1,6 @@ import { RouteService } from './route.service'; import { async, TestBed } from '@angular/core/testing'; -import { ActivatedRoute, Params } from '@angular/router'; +import { ActivatedRoute, convertToParamMap, Params } from '@angular/router'; import { Observable } from 'rxjs/Observable'; describe('RouteService', () => { @@ -20,12 +20,12 @@ describe('RouteService', () => { beforeEach(async(() => { return TestBed.configureTestingModule({ - declarations: [RouteService], providers: [ { provide: ActivatedRoute, useValue: { - params: Observable.of([paramObject]), + queryParams: Observable.of(paramObject), + queryParamMap: Observable.of(convertToParamMap(paramObject)) }, }, ] @@ -37,15 +37,82 @@ describe('RouteService', () => { }); describe('hasQueryParam', () => { - it(' should return true when the parameter name exists', () => { + it('should return true when the parameter name exists', () => { service.hasQueryParam(paramName1).subscribe((status) => { expect(status).toBeTruthy(); }); }); - it(' should return false when the parameter name does not exists', () => { + it('should return false when the parameter name does not exists', () => { service.hasQueryParam(nonExistingParamName).subscribe((status) => { expect(status).toBeFalsy(); }); }); }); + + describe('hasQueryParamWithValue', () => { + it('should return true when the parameter name exists and contains the specified value', () => { + service.hasQueryParamWithValue(paramName2, paramValue2a).subscribe((status) => { + expect(status).toBeTruthy(); + }); + }); + it('should return false when the parameter name exists and does not contain the specified value', () => { + service.hasQueryParamWithValue(paramName1, nonExistingParamValue).subscribe((status) => { + expect(status).toBeFalsy(); + }); + }); + it('should return false when the parameter name does not exists', () => { + service.hasQueryParamWithValue(nonExistingParamName, nonExistingParamValue).subscribe((status) => { + expect(status).toBeFalsy(); + }); + }); + }); + + describe('addQueryParameterValue', () => { + it('should return a list of values that contains the added value when a new value is added and the parameter did not exist yet', () => { + service.addQueryParameterValue(nonExistingParamName, nonExistingParamValue).subscribe((params) => { + expect(params[nonExistingParamName]).toContain(nonExistingParamValue); + }); + }); + it('should return a list of values that contains the existing values and the added value when a new value is added and the parameter already has values', () => { + service.addQueryParameterValue(paramName1, nonExistingParamValue).subscribe((params) => { + const values = params[paramName1]; + expect(values).toContain(paramValue1); + expect(values).toContain(nonExistingParamValue); + }); + }); + }); + + describe('removeQueryParameterValue', () => { + it('should return a list of values that does not contain the removed value when the parameter value exists', () => { + service.removeQueryParameterValue(paramName2, paramValue2a).subscribe((params) => { + const values = params[paramName2]; + expect(values).toContain(paramValue2b); + expect(values).not.toContain(paramValue2a); + }); + }); + + it('should return a list of values that does contain all existing values when the removed parameter does not exist', () => { + service.removeQueryParameterValue(paramName2, nonExistingParamValue).subscribe((params) => { + const values = params[paramName2]; + expect(values).toContain(paramValue2a); + expect(values).toContain(paramValue2b); + }); + }); + }); + + describe('removeQueryParameter', () => { + it('should return a list of values that does not contain any values for the parameter anymore when the parameter exists', () => { + service.removeQueryParameter(paramName2).subscribe((params) => { + const values = params[paramName2]; + expect(values).toEqual({}); + }); + }); + it('should return a list of values that does not contain any values for the parameter when the parameter does not exist', () => { + service.removeQueryParameter(nonExistingParamName).subscribe((params) => { + const values = params[nonExistingParamName]; + expect(values).toEqual({}); + }); + }); + }); + }); diff --git a/src/app/shared/route.service.ts b/src/app/shared/route.service.ts index 7f045afa9c..5a34e40779 100644 --- a/src/app/shared/route.service.ts +++ b/src/app/shared/route.service.ts @@ -10,7 +10,7 @@ export class RouteService { } hasQueryParam(paramName: string): Observable { - return this.route.queryParamMap.map((map) => map.has(paramName)); + return this.route.queryParamMap.map((map) => {return map.has(paramName);}); } hasQueryParamWithValue(paramName: string, paramValue: string): Observable { From 202b045009e2eff35d57dbe2303a737d481e44e3 Mon Sep 17 00:00:00 2001 From: Lotte Hofstede Date: Mon, 13 Nov 2017 13:52:21 +0100 Subject: [PATCH 07/18] 45621: finished filter facets for search sidebar --- resources/i18n/en.json | 8 +- .../search-facet-filter.component.html | 47 +++++--- .../search-facet-filter.component.scss | 18 ++- .../search-facet-filter.component.ts | 26 ++++- .../search-filter.component.html | 4 +- .../search-filter.component.scss | 10 +- .../search-filter/search-filter.component.ts | 7 +- .../search-filter/search-filter.service.ts | 19 +-- .../search-filters.component.html | 2 +- .../search-filters.component.ts | 2 +- .../+search-page/search-page.component.html | 5 +- src/app/+search-page/search-page.component.ts | 1 - .../search-filter-config.model.ts | 2 +- .../search-service/search.service.ts | 110 +++++++++--------- .../search-sidebar.component.scss | 3 + src/app/core/core.module.ts | 2 + src/app/shared/animations/slide.ts | 4 +- src/app/shared/route.service.ts | 10 +- 18 files changed, 182 insertions(+), 98 deletions(-) diff --git a/resources/i18n/en.json b/resources/i18n/en.json index 832fbb95aa..f835699087 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -94,9 +94,9 @@ "filters": { "head": "Filters", "reset": "Reset filters", - "facet-filter": { + "filter": { "show-more": "Show more", - "show-less": "Show less", + "show-less": "Collapse", "author": { "placeholder": "Author name", "head": "Author" @@ -108,6 +108,10 @@ "subject": { "placeholder": "Subject", "head": "Subject" + }, + "date": { + "placeholder": "Date", + "head": "Date" } } } diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.html b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.html index e253e65048..436f497bf5 100644 --- a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.html +++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.html @@ -1,15 +1,32 @@ - - - - {{value.value}} - ({{value.count}}) - - -{{"search.filters.facet-filter.show-more" - | translate}} -{{"search.filters.facet-filter.show-less" | - translate}} - - \ No newline at end of file + \ No newline at end of file diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.scss b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.scss index 9ad844019b..595b2aefb8 100644 --- a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.scss +++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.scss @@ -1,2 +1,18 @@ @import '../../../../../styles/variables.scss'; -@import '../../../../../styles/mixins.scss'; \ No newline at end of file +@import '../../../../../styles/mixins.scss'; + +.filters { + margin-top: $spacer/2; + margin-bottom: $spacer/2; + a { + color: $body-color; + &:hover { + text-decoration: none; + } + } + .toggle-more-filters a { + color: $link-color; + text-decoration: underline; + cursor: pointer; + } +} \ No newline at end of file diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts index 0c9eb06baa..3b3b7312e8 100644 --- a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts +++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts @@ -1,10 +1,10 @@ import { Component, Input, OnInit } from '@angular/core'; 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 { Params } from '@angular/router'; +import { Params, Router } from '@angular/router'; import { Observable } from 'rxjs/Observable'; import { SearchFilterService } from '../search-filter.service'; +import { isNotEmpty } from '../../../../shared/empty.util'; /** * This component renders a simple item page. @@ -21,9 +21,11 @@ import { SearchFilterService } from '../search-filter.service'; export class SidebarFacetFilterComponent implements OnInit { @Input() filterValues: FacetValue[]; @Input() filterConfig: SearchFilterConfig; + @Input() selectedValues: string[]; currentPage: Observable; + filter: string; - constructor(private filterService: SearchFilterService) { + constructor(private filterService: SearchFilterService, private router: Router) { } ngOnInit(): void { @@ -38,8 +40,12 @@ export class SidebarFacetFilterComponent implements OnInit { return this.filterService.searchLink; } - getQueryParams(value: FacetValue): Observable { - return this.filterService.getFilterValueURL(this.filterConfig, value.value); + getQueryParamsWith(value: string): Observable { + return this.filterService.getQueryParamsWith(this.filterConfig, value); + } + + getQueryParamsWithout(value: string): Observable { + return this.filterService.getQueryParamsWithout(this.filterConfig, value); } get facetCount(): Observable { @@ -60,6 +66,16 @@ export class SidebarFacetFilterComponent implements OnInit { getCurrentPage(): Observable { return this.filterService.getPage(this.filterConfig.name); + } + onSubmit(data: any) { + if (isNotEmpty(data.filter)) { + this.getQueryParamsWith(data.filter).first().subscribe((a) => { + this.router.navigate([this.getSearchLink()], { queryParams: a } + ); + } + ); + this.filter = ''; + } } } diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.component.html b/src/app/+search-page/search-filters/search-filter/search-filter.component.html index c78ea9f676..f5acb42b6d 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.component.html +++ b/src/app/+search-page/search-filters/search-filter/search-filter.component.html @@ -1,8 +1,8 @@
-
{{filter.name}}
{{'search.filters.filter.' + filter.name + '.head'| translate}}
+ [filterValues]="(filterValues | async)?.payload" [selectedValues]="getSelectedValues() | async">
\ No newline at end of file diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.component.scss b/src/app/+search-page/search-filters/search-filter/search-filter.component.scss index 93be5d1c05..f694e9e167 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.component.scss +++ b/src/app/+search-page/search-filters/search-filter/search-filter.component.scss @@ -1,6 +1,12 @@ @import '../../../../styles/variables.scss'; @import '../../../../styles/mixins.scss'; -.search-filter-wrapper { - overflow: hidden; +:host { + border: 1px solid map-get($theme-colors, light); + .search-filter-wrapper { + overflow: hidden; + } + .filter-toggle { + line-height: $line-height-base; + } } \ No newline at end of file diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.component.ts b/src/app/+search-page/search-filters/search-filter/search-filter.component.ts index 211315e397..e3f86c124c 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.component.ts +++ b/src/app/+search-page/search-filters/search-filter/search-filter.component.ts @@ -6,6 +6,7 @@ import { FacetValue } from '../../search-service/facet-value.model'; import { SearchFilterService } from './search-filter.service'; import { Observable } from 'rxjs/Observable'; import { slide } from '../../../shared/animations/slide'; +import { RouteService } from '../../../shared/route.service'; /** * This component renders a simple item page. @@ -17,7 +18,7 @@ import { slide } from '../../../shared/animations/slide'; selector: 'ds-search-filter', styleUrls: ['./search-filter.component.scss'], templateUrl: './search-filter.component.html', - animations: [slide] + animations: [slide], }) export class SidebarFilterComponent implements OnInit { @@ -51,4 +52,8 @@ export class SidebarFilterComponent implements OnInit { initialExpand() { this.filterService.initialExpand(this.filter.name); } + + getSelectedValues(): Observable { + return this.filterService.getSelectedValuesForFilter(this.filter); + } } diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.service.ts b/src/app/+search-page/search-filters/search-filter/search-filter.service.ts index 492eabdcf3..1a154e2808 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.service.ts +++ b/src/app/+search-page/search-filters/search-filter/search-filter.service.ts @@ -11,7 +11,6 @@ import { SearchFilterToggleAction } from './search-filter.actions'; import { hasValue, } from '../../../shared/empty.util'; -import { Params } from '@angular/router'; import { SearchFilterConfig } from '../../search-service/search-filter-config.model'; import { SearchService } from '../../search-service/search.service'; import { RouteService } from '../../../shared/route.service'; @@ -30,14 +29,16 @@ export class SearchFilterService { return this.routeService.hasQueryParamWithValue(paramName, filterValue); } - getFilterValueURL(filterConfig: SearchFilterConfig, value: string): Observable { - return this.isFilterActive(filterConfig.paramName, value).flatMap((isActive) => { - if (isActive) { - return this.routeService.removeQueryParameterValue(filterConfig.paramName, value); - } else { - return this.routeService.addQueryParameterValue(filterConfig.paramName, value); - } - }) + getQueryParamsWithout(filterConfig: SearchFilterConfig, value: string) { + return this.routeService.removeQueryParameterValue(filterConfig.paramName, value); + } + + getQueryParamsWith(filterConfig: SearchFilterConfig, value: string) { + return this.routeService.addQueryParameterValue(filterConfig.paramName, value); + } + + getSelectedValuesForFilter(filterConfig: SearchFilterConfig): Observable { + return this.routeService.getQueryParameterValues(filterConfig.paramName); } get searchLink() { diff --git a/src/app/+search-page/search-filters/search-filters.component.html b/src/app/+search-page/search-filters/search-filters.component.html index 9d08545923..5bd42928a1 100644 --- a/src/app/+search-page/search-filters/search-filters.component.html +++ b/src/app/+search-page/search-filters/search-filters.component.html @@ -1,7 +1,7 @@

{{"search.filters.head" | translate}}

- +
{{"search.filters.reset" | translate}} \ No newline at end of file diff --git a/src/app/+search-page/search-filters/search-filters.component.ts b/src/app/+search-page/search-filters/search-filters.component.ts index 8380df7e05..c06c35b372 100644 --- a/src/app/+search-page/search-filters/search-filters.component.ts +++ b/src/app/+search-page/search-filters/search-filters.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from '@angular/core'; +import { Component } from '@angular/core'; import { SearchService } from '../search-service/search.service'; import { RemoteData } from '../../core/data/remote-data'; import { SearchFilterConfig } from '../search-service/search-filter-config.model'; diff --git a/src/app/+search-page/search-page.component.html b/src/app/+search-page/search-page.component.html index fe08336579..e8f2fe5487 100644 --- a/src/app/+search-page/search-page.component.html +++ b/src/app/+search-page/search-page.component.html @@ -2,7 +2,7 @@
+ [resultCount]="(resultsRDObs | async)?.pageInfo?.totalElements">
@@ -37,3 +37,4 @@
+ diff --git a/src/app/+search-page/search-page.component.ts b/src/app/+search-page/search-page.component.ts index 4a7d6322b9..1d3b80e4e7 100644 --- a/src/app/+search-page/search-page.component.ts +++ b/src/app/+search-page/search-page.component.ts @@ -90,7 +90,6 @@ export class SearchPageComponent implements OnInit, OnDestroy { } private updateSearchResults(searchOptions) { - // Resolve search results this.resultsRDObs = this.service.search(this.query, this.scope, searchOptions); } diff --git a/src/app/+search-page/search-service/search-filter-config.model.ts b/src/app/+search-page/search-service/search-filter-config.model.ts index 476dd8438e..fd3223c7bc 100644 --- a/src/app/+search-page/search-service/search-filter-config.model.ts +++ b/src/app/+search-page/search-service/search-filter-config.model.ts @@ -5,7 +5,7 @@ export class SearchFilterConfig { name: string; type: FilterType; hasFacets: boolean; - pageSize = 3; + pageSize = 2; isOpenByDefault: boolean; /** * Name of this configuration that can be used in a url diff --git a/src/app/+search-page/search-service/search.service.ts b/src/app/+search-page/search-service/search.service.ts index 2d34b3a98f..20b64b2528 100644 --- a/src/app/+search-page/search-service/search.service.ts +++ b/src/app/+search-page/search-service/search.service.ts @@ -15,6 +15,7 @@ import { FilterType } from './filter-type.model'; import { FacetValue } from './facet-value.model'; import { ViewMode } from '../../+search-page/search-options.model'; import { Router, NavigationExtras, ActivatedRoute, Params } from '@angular/router'; +import { RouteService } from '../../shared/route.service'; function shuffle(array: any[]) { let i = 0; @@ -81,6 +82,7 @@ export class SearchService implements OnDestroy { ]; constructor(private itemDataService: ItemDataService, + private routeService: RouteService, private route: ActivatedRoute, private router: Router) { @@ -126,31 +128,31 @@ export class SearchService implements OnDestroy { .filter((rd: RemoteData) => rd.hasSucceeded) .map((rd: RemoteData) => { - const totalElements = rd.pageInfo.totalElements > 20 ? 20 : rd.pageInfo.totalElements; - const pageInfo = Object.assign({}, rd.pageInfo, { totalElements: totalElements }); + const totalElements = rd.pageInfo.totalElements > 20 ? 20 : rd.pageInfo.totalElements; + const pageInfo = Object.assign({}, rd.pageInfo, { totalElements: totalElements }); - const payload = shuffle(rd.payload) - .map((item: Item, index: number) => { - const mockResult: SearchResult = new ItemSearchResult(); - mockResult.dspaceObject = item; - const highlight = new Metadatum(); - highlight.key = 'dc.description.abstract'; - highlight.value = this.mockedHighlights[index % this.mockedHighlights.length]; - mockResult.hitHighlights = new Array(highlight); - return mockResult; - }); + const payload = shuffle(rd.payload) + .map((item: Item, index: number) => { + const mockResult: SearchResult = new ItemSearchResult(); + mockResult.dspaceObject = item; + const highlight = new Metadatum(); + highlight.key = 'dc.description.abstract'; + highlight.value = this.mockedHighlights[index % this.mockedHighlights.length]; + mockResult.hitHighlights = new Array(highlight); + return mockResult; + }); - return new RemoteData( - self, - rd.isRequestPending, - rd.isResponsePending, - rd.hasSucceeded, - errorMessage, - statusCode, - pageInfo, - payload - ) - }).startWith(new RemoteData( + return new RemoteData( + self, + rd.isRequestPending, + rd.isResponsePending, + rd.hasSucceeded, + errorMessage, + statusCode, + pageInfo, + payload + ) + }).startWith(new RemoteData( '', true, false, @@ -182,39 +184,43 @@ export class SearchService implements OnDestroy { } getFacetValuesFor(searchFilterConfigName: string): Observable> { - const filterConfig = this.config.find((config: SearchFilterConfig) => config.name === searchFilterConfigName); - const values: FacetValue[] = []; - for (let i = 0; i < 5; i++) { - const value = searchFilterConfigName + ' ' + (i + 1); - values.push({ - value: value, - count: Math.floor(Math.random() * 20) + 20 * (5 - i), // make sure first results have the highest (random) count - search: decodeURI(this.router.url) + (this.router.url.includes('?') ? '&' : '?') + filterConfig.paramName + '=' + value - }); - } - const requestPending = false; - const responsePending = false; - const isSuccessful = true; - const errorMessage = undefined; - const statusCode = '200'; - const returningPageInfo = new PageInfo(); - return Observable.of(new RemoteData( - 'https://dspace7.4science.it/dspace-spring-rest/api/search', - requestPending, - responsePending, - isSuccessful, - errorMessage, - statusCode, - returningPageInfo, - values - )); + return this.routeService.getQueryParameterValues(filterConfig.paramName).map((selectedValues: string[]) => { + const values: FacetValue[] = []; + for (let i = 0; i < 5; i++) { + const value = searchFilterConfigName + ' ' + (i + 1); + if (!selectedValues.includes(value)) { + values.push({ + value: value, + count: Math.floor(Math.random() * 20) + 20 * (5 - i), // make sure first results have the highest (random) count + search: decodeURI(this.router.url) + (this.router.url.includes('?') ? '&' : '?') + filterConfig.paramName + '=' + value + }); + } + } + const requestPending = false; + const responsePending = false; + const isSuccessful = true; + const errorMessage = undefined; + const statusCode = '200'; + const returningPageInfo = new PageInfo(); + return new RemoteData( + 'https://dspace7.4science.it/dspace-spring-rest/api/search', + requestPending, + responsePending, + isSuccessful, + errorMessage, + statusCode, + returningPageInfo, + values + ) + } + ) } getViewMode(): Observable { - return this.route.queryParams.map((params) => { - if (isNotEmpty(params.view) && hasValue(params.view)) { - return params.view; + return this.routeService.getQueryParameterValue('view').map((value) => { + if (hasValue(value)) { + return value as ViewMode; } else { return ViewMode.List; } diff --git a/src/app/+search-page/search-sidebar/search-sidebar.component.scss b/src/app/+search-page/search-sidebar/search-sidebar.component.scss index c8c0a60225..2b318454ed 100644 --- a/src/app/+search-page/search-sidebar/search-sidebar.component.scss +++ b/src/app/+search-page/search-sidebar/search-sidebar.component.scss @@ -5,4 +5,7 @@ .results { line-height: $button-height; } + ds-view-mode-switch { + margin-bottom: $spacer; + } } diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index bfd5cebcb6..39570f9aed 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -32,6 +32,7 @@ import { ServerResponseService } from '../shared/server-response.service'; import { NativeWindowFactory, NativeWindowService } from '../shared/window.service'; import { BrowseService } from './browse/browse.service'; import { BrowseResponseParsingService } from './data/browse-response-parsing.service'; +import { RouteService } from '../shared/route.service'; const IMPORTS = [ CommonModule, @@ -65,6 +66,7 @@ const PROVIDERS = [ ServerResponseService, BrowseResponseParsingService, BrowseService, + RouteService, { provide: NativeWindowService, useFactory: NativeWindowFactory } ]; diff --git a/src/app/shared/animations/slide.ts b/src/app/shared/animations/slide.ts index 381f592168..fa4a451863 100644 --- a/src/app/shared/animations/slide.ts +++ b/src/app/shared/animations/slide.ts @@ -1,4 +1,4 @@ -import { animate, state, transition, trigger, style } from '@angular/animations'; +import { animate, state, transition, trigger, style, stagger, query } from '@angular/animations'; export const slide = trigger('slide', [ @@ -6,5 +6,5 @@ export const slide = trigger('slide', [ state('collapsed', style({ height: 0 })), - transition('expanded <=> collapsed', animate(250)), + transition('expanded <=> collapsed', animate(250)) ]); diff --git a/src/app/shared/route.service.ts b/src/app/shared/route.service.ts index 5a34e40779..b11da41211 100644 --- a/src/app/shared/route.service.ts +++ b/src/app/shared/route.service.ts @@ -1,4 +1,4 @@ -import { Injectable, OnDestroy } from '@angular/core'; +import { Injectable } from '@angular/core'; import { Observable } from 'rxjs/Observable'; import { ActivatedRoute, convertToParamMap, Params, } from '@angular/router'; import { isNotEmpty } from './empty.util'; @@ -9,6 +9,14 @@ export class RouteService { constructor(private route: ActivatedRoute) { } + getQueryParameterValues(paramName: string): Observable { + return this.route.queryParamMap.map((map) => map.getAll(paramName)); + } + + getQueryParameterValue(paramName: string): Observable { + return this.route.queryParamMap.map((map) => map.get(paramName)); + } + hasQueryParam(paramName: string): Observable { return this.route.queryParamMap.map((map) => {return map.has(paramName);}); } From 152c79278a70d1a3f24698dbd86ff7aae68a60b9 Mon Sep 17 00:00:00 2001 From: Jonas Van Goolen Date: Thu, 9 Nov 2017 11:16:03 +0100 Subject: [PATCH 08/18] 195 Pre-test commit --- resources/i18n/en.json | 10 ++- .../+search-page/search-page.component.html | 9 ++- src/app/+search-page/search-page.component.ts | 7 +- src/app/+search-page/search-page.module.ts | 2 + .../search-results.component.html | 17 ++-- .../search-service/search.service.ts | 14 +++- .../search-settings.component.html | 25 ++++++ .../search-settings.component.scss | 0 .../search-settings.component.ts | 79 +++++++++++++++++++ .../search-sidebar.component.html | 3 +- .../pagination/pagination.component.html | 4 +- 11 files changed, 148 insertions(+), 22 deletions(-) create mode 100644 src/app/+search-page/search-settings/search-settings.component.html create mode 100644 src/app/+search-page/search-settings/search-settings.component.scss create mode 100644 src/app/+search-page/search-settings/search-settings.component.ts diff --git a/resources/i18n/en.json b/resources/i18n/en.json index f835699087..7b3d31c3e0 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -85,7 +85,15 @@ "sidebar": { "close": "Back to results", "open": "Search Tools", - "results": "results" + "results": "results", + "filters":{ + "title":"Filters" + }, + "settings":{ + "title":"Settings", + "sort-by":"Sort By", + "rpp":"Results per page" + } }, "view-switch": { "show-list": "Show as list", diff --git a/src/app/+search-page/search-page.component.html b/src/app/+search-page/search-page.component.html index e8f2fe5487..2eac36be2f 100644 --- a/src/app/+search-page/search-page.component.html +++ b/src/app/+search-page/search-page.component.html @@ -1,15 +1,19 @@
- + -
+
+
+
+
+
diff --git a/src/app/+search-page/search-page.component.ts b/src/app/+search-page/search-page.component.ts index 1d3b80e4e7..4a6f272580 100644 --- a/src/app/+search-page/search-page.component.ts +++ b/src/app/+search-page/search-page.component.ts @@ -49,12 +49,7 @@ export class SearchPageComponent implements OnInit, OnDestroy { this.isMobileView = this.windowService.isXs(); this.scopeListRDObs = communityService.findAll(); // Initial pagination config - const pagination: PaginationComponentOptions = new PaginationComponentOptions(); - pagination.id = 'search-results-pagination'; - pagination.currentPage = 1; - pagination.pageSize = 10; - const sort: SortOptions = new SortOptions(); - this.searchOptions = { pagination: pagination, sort: sort }; + this.searchOptions = this.service.searchOptions; } ngOnInit(): void { diff --git a/src/app/+search-page/search-page.module.ts b/src/app/+search-page/search-page.module.ts index 661399bab1..e940e4975d 100644 --- a/src/app/+search-page/search-page.module.ts +++ b/src/app/+search-page/search-page.module.ts @@ -11,6 +11,7 @@ import { SearchService } from './search-service/search.service'; import { SearchSidebarComponent } from './search-sidebar/search-sidebar.component'; import { SearchSidebarService } from './search-sidebar/search-sidebar.service'; import { SearchSidebarEffects } from './search-sidebar/search-sidebar.effects'; +import { SearchSettingsComponent } from './search-settings/search-settings.component'; import { EffectsModule } from '@ngrx/effects'; import { SidebarFiltersComponent } from './search-filters/search-filters.component'; import { SidebarFilterComponent } from './search-filters/search-filter/search-filter.component'; @@ -32,6 +33,7 @@ const effects = [ SearchPageComponent, SearchResultsComponent, SearchSidebarComponent, + SearchSettingsComponent, ItemSearchResultListElementComponent, CollectionSearchResultListElementComponent, CommunitySearchResultListElementComponent, diff --git a/src/app/+search-page/search-results/search-results.component.html b/src/app/+search-page/search-results/search-results.component.html index 20b2cb8699..70e315671b 100644 --- a/src/app/+search-page/search-results/search-results.component.html +++ b/src/app/+search-page/search-results/search-results.component.html @@ -1,11 +1,10 @@
-

{{ 'search.results.head' | translate }}

- - -
+

{{ 'search.results.head' | translate }}

+ +
- \ No newline at end of file + diff --git a/src/app/+search-page/search-service/search.service.ts b/src/app/+search-page/search-service/search.service.ts index 20b64b2528..cf1da04acc 100644 --- a/src/app/+search-page/search-service/search.service.ts +++ b/src/app/+search-page/search-service/search.service.ts @@ -14,8 +14,10 @@ import { SearchFilterConfig } from './search-filter-config.model'; import { FilterType } from './filter-type.model'; import { FacetValue } from './facet-value.model'; import { ViewMode } from '../../+search-page/search-options.model'; -import { Router, NavigationExtras, ActivatedRoute, Params } from '@angular/router'; +import { Router, NavigationExtras, ActivatedRoute } from '@angular/router'; import { RouteService } from '../../shared/route.service'; +import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; +import { SortOptions } from 'src/app/core/cache/models/sort-options.model'; function shuffle(array: any[]) { let i = 0; @@ -80,15 +82,25 @@ export class SearchService implements OnDestroy { isOpenByDefault: false }) ]; + // searchOptions: BehaviorSubject; + searchOptions: SearchOptions; constructor(private itemDataService: ItemDataService, private routeService: RouteService, private route: ActivatedRoute, private router: Router) { + const pagination: PaginationComponentOptions = new PaginationComponentOptions(); + pagination.id = 'search-results-pagination'; + pagination.currentPage = 1; + pagination.pageSize = 10; + const sort: SortOptions = new SortOptions(); + this.searchOptions = { pagination: pagination, sort: sort }; + // this.searchOptions = new BehaviorSubject(searchOptions); } search(query: string, scopeId?: string, searchOptions?: SearchOptions): Observable>>> { + this.searchOptions = this.searchOptions; let self = `https://dspace7.4science.it/dspace-spring-rest/api/search?query=${query}`; if (hasValue(scopeId)) { self += `&scope=${scopeId}`; diff --git a/src/app/+search-page/search-settings/search-settings.component.html b/src/app/+search-page/search-settings/search-settings.component.html new file mode 100644 index 0000000000..c53f0290a0 --- /dev/null +++ b/src/app/+search-page/search-settings/search-settings.component.html @@ -0,0 +1,25 @@ +

{{ 'search.sidebar.settings.title' | translate}}

+
+

{{ 'search.sidebar.settings.sort-by' | translate}}

+
+ +
+
+ +
+

{{ 'search.sidebar.settings.rpp' | translate}}

+ +
+ +
+
diff --git a/src/app/+search-page/search-settings/search-settings.component.scss b/src/app/+search-page/search-settings/search-settings.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/+search-page/search-settings/search-settings.component.ts b/src/app/+search-page/search-settings/search-settings.component.ts new file mode 100644 index 0000000000..864720a019 --- /dev/null +++ b/src/app/+search-page/search-settings/search-settings.component.ts @@ -0,0 +1,79 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { SearchService } from '../search-service/search.service'; +import { SearchOptions } from '../search-options.model'; +import { SortDirection } from '../../core/cache/models/sort-options.model'; +import { ActivatedRoute, NavigationExtras, Router } from '@angular/router'; + +@Component({ + selector: 'ds-search-settings', + styleUrls: ['./search-settings.component.scss'], + templateUrl: './search-settings.component.html', +}) +export class SearchSettingsComponent implements OnInit{ + + @Input() searchOptions: SearchOptions; + /** + * Declare SortDirection enumeration to use it in the template + */ + public sortDirections = SortDirection; + /** + * Number of items per page. + */ + public pageSize; + + private sub; + private scope: string; + query: string; + page: number; + direction: SortDirection; + currentParams = {}; + + constructor(private service: SearchService, + private route: ActivatedRoute, + private router: Router){ + + } + + ngOnInit(): void { + this.searchOptions = this.service.searchOptions; + this.pageSize = this.searchOptions.pagination.pageSize; + this.sub = this.route + .queryParams + .subscribe((params) => { + this.currentParams = params; + this.query = params.query || ''; + this.scope = params.scope; + this.page = +params.page || this.searchOptions.pagination.currentPage; + this.pageSize = +params.pageSize || this.searchOptions.pagination.pageSize; + this.direction = +params.sortDirection || this.searchOptions.sort.direction; + }); + } + reloadRPP(event:Event) { + let value = (event.target).value; + this.searchOptions.sort.direction; + const navigationExtras: NavigationExtras = { + queryParams: { + query:this.query, + pageSize:value, + scope: this.scope, + page:this.page, + sortDirection:this.direction + } + }; + this.router.navigate(['/search'], navigationExtras); + } + + reloadOrder(event:Event) { + let value = (event.target).value; + const navigationExtras: NavigationExtras = { + queryParams: { + query:this.query, + pageSize:this.pageSize, + scope: this.scope, + page:this.page, + sortDirection:value + } + }; + this.router.navigate(['/search'], navigationExtras); + } +} diff --git a/src/app/+search-page/search-sidebar/search-sidebar.component.html b/src/app/+search-page/search-sidebar/search-sidebar.component.html index 4adca2907a..325f557233 100644 --- a/src/app/+search-page/search-sidebar/search-sidebar.component.html +++ b/src/app/+search-page/search-sidebar/search-sidebar.component.html @@ -9,5 +9,6 @@
+
-
\ No newline at end of file +
diff --git a/src/app/shared/pagination/pagination.component.html b/src/app/shared/pagination/pagination.component.html index 25412b3195..062209f4bb 100644 --- a/src/app/shared/pagination/pagination.component.html +++ b/src/app/shared/pagination/pagination.component.html @@ -1,12 +1,12 @@
-
+
{{ 'pagination.showing.label' | translate }} {{ 'pagination.showing.detail' | translate:getShowingDetails(collectionSize)}}
-
+
From fccaea9c0889275d86c57b5a7fa03566a1624104 Mon Sep 17 00:00:00 2001 From: Jonas Van Goolen Date: Thu, 9 Nov 2017 13:30:27 +0100 Subject: [PATCH 09/18] 195 Initial testing + fixing previously broken test(s) --- .../search-page.component.spec.ts | 8 +- .../search-service/search.service.ts | 2 +- .../search-settings.component.html | 4 +- .../search-settings.component.spec.ts | 106 ++++++++++++++++++ 4 files changed, 116 insertions(+), 4 deletions(-) create mode 100644 src/app/+search-page/search-settings/search-settings.component.spec.ts diff --git a/src/app/+search-page/search-page.component.spec.ts b/src/app/+search-page/search-page.component.spec.ts index 97ad826048..442dbf2a49 100644 --- a/src/app/+search-page/search-page.component.spec.ts +++ b/src/app/+search-page/search-page.component.spec.ts @@ -28,8 +28,14 @@ describe('SearchPageComponent', () => { /* tslint:enable:no-empty */ select: Observable.of(true) }); + const pagination: PaginationComponentOptions = new PaginationComponentOptions(); + pagination.id = 'search-results-pagination'; + pagination.currentPage = 1; + pagination.pageSize = 10; + const sort: SortOptions = new SortOptions(); const mockResults = Observable.of(['test', 'data']); const searchServiceStub = { + searchOptions:{ pagination: pagination, sort: sort }, search: () => mockResults }; const queryParam = 'test query'; @@ -204,4 +210,4 @@ describe('SearchPageComponent', () => { }); }); -}); \ No newline at end of file +}); diff --git a/src/app/+search-page/search-service/search.service.ts b/src/app/+search-page/search-service/search.service.ts index cf1da04acc..548f400280 100644 --- a/src/app/+search-page/search-service/search.service.ts +++ b/src/app/+search-page/search-service/search.service.ts @@ -17,7 +17,7 @@ import { ViewMode } from '../../+search-page/search-options.model'; import { Router, NavigationExtras, ActivatedRoute } from '@angular/router'; import { RouteService } from '../../shared/route.service'; import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model'; -import { SortOptions } from 'src/app/core/cache/models/sort-options.model'; +import { SortOptions } from '../../core/cache/models/sort-options.model'; function shuffle(array: any[]) { let i = 0; diff --git a/src/app/+search-page/search-settings/search-settings.component.html b/src/app/+search-page/search-settings/search-settings.component.html index c53f0290a0..1e119474bb 100644 --- a/src/app/+search-page/search-settings/search-settings.component.html +++ b/src/app/+search-page/search-settings/search-settings.component.html @@ -1,5 +1,5 @@

{{ 'search.sidebar.settings.title' | translate}}

-
+

{{ 'search.sidebar.settings.sort-by' | translate}}

@@ -11,7 +11,7 @@
-
+

{{ 'search.sidebar.settings.rpp' | translate}}

diff --git a/src/app/+search-page/search-settings/search-settings.component.scss b/src/app/+search-page/search-settings/search-settings.component.scss index e69de29bb2..0a3824f815 100644 --- a/src/app/+search-page/search-settings/search-settings.component.scss +++ b/src/app/+search-page/search-settings/search-settings.component.scss @@ -0,0 +1,5 @@ +@import '../../../styles/variables.scss'; + +.setting-option { + border: 1px solid map-get($theme-colors, light); +} diff --git a/src/app/+search-page/search-settings/search-settings.component.spec.ts b/src/app/+search-page/search-settings/search-settings.component.spec.ts index fc9d28660b..504bfbc2bf 100644 --- a/src/app/+search-page/search-settings/search-settings.component.spec.ts +++ b/src/app/+search-page/search-settings/search-settings.component.spec.ts @@ -11,7 +11,6 @@ import { SearchSidebarService } from '../search-sidebar/search-sidebar.service'; import { NO_ERRORS_SCHEMA } from '@angular/core'; import { EnumKeysPipe } from '../../shared/utils/enum-keys-pipe'; import { By } from '@angular/platform-browser'; -import Any = jasmine.Any; describe('SearchSettingsComponent', () => { @@ -72,35 +71,33 @@ describe('SearchSettingsComponent', () => { spyOn(comp, 'reloadOrder'); spyOn(searchServiceObject, 'search').and.callThrough(); - }); it('it should show the order settings with the respective selectable options', () => { - let orderSetting = fixture.debugElement.query(By.css('div.result-order-settings')); + const orderSetting = fixture.debugElement.query(By.css('div.result-order-settings')); expect(orderSetting).toBeDefined(); - let childElements = orderSetting.query(By.css('.form-control')).children; + const childElements = orderSetting.query(By.css('.form-control')).children; expect(childElements.length).toEqual(2); }); it('it should show the size settings with the respective selectable options', () => { - let pageSizeSetting = fixture.debugElement.query(By.css('div.page-size-settings')); + const pageSizeSetting = fixture.debugElement.query(By.css('div.page-size-settings')); expect(pageSizeSetting).toBeDefined(); - let childElements = pageSizeSetting.query(By.css('.form-control')).children; + const childElements = pageSizeSetting.query(By.css('.form-control')).children; expect(childElements.length).toEqual(7); }); it('should have the proper order value selected by default', () => { - let orderSetting = fixture.debugElement.query(By.css('div.result-order-settings')); - let childElementToBeSelected = orderSetting.query(By.css('.form-control option[value="0"][selected="selected"]')) + const orderSetting = fixture.debugElement.query(By.css('div.result-order-settings')); + const childElementToBeSelected = orderSetting.query(By.css('.form-control option[value="0"][selected="selected"]')) expect(childElementToBeSelected).toBeDefined(); }); it('should have the proper rpp value selected by default', () => { - let pageSizeSetting = fixture.debugElement.query(By.css('div.page-size-settings')); - let childElementToBeSelected = pageSizeSetting.query(By.css('.form-control option[value="10"][selected="selected"]')) + const pageSizeSetting = fixture.debugElement.query(By.css('div.page-size-settings')); + const childElementToBeSelected = pageSizeSetting.query(By.css('.form-control option[value="10"][selected="selected"]')) expect(childElementToBeSelected).toBeDefined(); }); - }); diff --git a/src/app/+search-page/search-settings/search-settings.component.ts b/src/app/+search-page/search-settings/search-settings.component.ts index 864720a019..7b8bb8eb6e 100644 --- a/src/app/+search-page/search-settings/search-settings.component.ts +++ b/src/app/+search-page/search-settings/search-settings.component.ts @@ -9,7 +9,7 @@ import { ActivatedRoute, NavigationExtras, Router } from '@angular/router'; styleUrls: ['./search-settings.component.scss'], templateUrl: './search-settings.component.html', }) -export class SearchSettingsComponent implements OnInit{ +export class SearchSettingsComponent implements OnInit { @Input() searchOptions: SearchOptions; /** @@ -30,8 +30,7 @@ export class SearchSettingsComponent implements OnInit{ constructor(private service: SearchService, private route: ActivatedRoute, - private router: Router){ - + private router: Router) { } ngOnInit(): void { @@ -48,32 +47,24 @@ export class SearchSettingsComponent implements OnInit{ this.direction = +params.sortDirection || this.searchOptions.sort.direction; }); } - reloadRPP(event:Event) { - let value = (event.target).value; - this.searchOptions.sort.direction; + + reloadRPP(event: Event) { + const value = (event.target as HTMLInputElement).value; const navigationExtras: NavigationExtras = { - queryParams: { - query:this.query, - pageSize:value, - scope: this.scope, - page:this.page, - sortDirection:this.direction - } + queryParams: Object.assign({}, this.currentParams, { + pageSize: value + }) }; - this.router.navigate(['/search'], navigationExtras); + this.router.navigate([ '/search' ], navigationExtras); } - reloadOrder(event:Event) { - let value = (event.target).value; + reloadOrder(event: Event) { + const value = (event.target as HTMLInputElement).value; const navigationExtras: NavigationExtras = { - queryParams: { - query:this.query, - pageSize:this.pageSize, - scope: this.scope, - page:this.page, - sortDirection:value - } + queryParams: Object.assign({}, this.currentParams, { + sortDirection: value + }) }; - this.router.navigate(['/search'], navigationExtras); + this.router.navigate([ '/search' ], navigationExtras); } } From 1d795ccb168fab83783f71424813363e277a3096 Mon Sep 17 00:00:00 2001 From: Lotte Hofstede Date: Thu, 16 Nov 2017 11:29:34 +0100 Subject: [PATCH 11/18] 45621: open filter section on load when it's used --- .../search-facet-filter.component.html | 4 ++-- .../search-facet-filter.component.ts | 13 +++++++---- .../search-filter/search-filter.component.ts | 14 +++++++----- .../search-filter/search-filter.service.ts | 22 ++++++++++++++++--- src/app/shared/route.service.ts | 1 + 5 files changed, 40 insertions(+), 14 deletions(-) diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.html b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.html index 436f497bf5..a21289808a 100644 --- a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.html +++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.html @@ -23,8 +23,8 @@ | translate}}
-
- + diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts index 3b3b7312e8..159eecf698 100644 --- a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts +++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts @@ -33,7 +33,7 @@ export class SidebarFacetFilterComponent implements OnInit { } isChecked(value: FacetValue): Observable { - return this.filterService.isFilterActive(this.filterConfig.paramName, value.value); + return this.filterService.isFilterActiveWithValue(this.filterConfig.paramName, value.value); } getSearchLink() { @@ -68,14 +68,19 @@ export class SidebarFacetFilterComponent implements OnInit { return this.filterService.getPage(this.filterConfig.name); } + getCurrentUrl() { + return this.router.url; + } + onSubmit(data: any) { - if (isNotEmpty(data.filter)) { - this.getQueryParamsWith(data.filter).first().subscribe((a) => { - this.router.navigate([this.getSearchLink()], { queryParams: a } + if (isNotEmpty(data)) { + const sub = this.getQueryParamsWith(data[this.filterConfig.paramName]).first().subscribe((params) => { + this.router.navigate([this.getSearchLink()], { queryParams: params } ); } ); this.filter = ''; + sub.unsubscribe(); } } } diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.component.ts b/src/app/+search-page/search-filters/search-filter/search-filter.component.ts index e3f86c124c..57bff9af27 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.component.ts +++ b/src/app/+search-page/search-filters/search-filter/search-filter.component.ts @@ -7,6 +7,7 @@ import { SearchFilterService } from './search-filter.service'; import { Observable } from 'rxjs/Observable'; import { slide } from '../../../shared/animations/slide'; import { RouteService } from '../../../shared/route.service'; +import { first } from 'rxjs/operator/first'; /** * This component renders a simple item page. @@ -30,11 +31,14 @@ export class SidebarFilterComponent implements OnInit { ngOnInit() { this.filterValues = this.searchService.getFacetValuesFor(this.filter.name); - if (this.filter.isOpenByDefault) { - this.initialExpand(); - } else { - this.initialCollapse(); - } + const sub = this.filterService.isFilterActive(this.filter.paramName).first().subscribe((isActive) => { + if (this.filter.isOpenByDefault || isActive) { + this.initialExpand(); + } else { + this.initialCollapse(); + } + }); + sub.unsubscribe(); } toggle() { diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.service.ts b/src/app/+search-page/search-filters/search-filter/search-filter.service.ts index 1a154e2808..a35e0ded76 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.service.ts +++ b/src/app/+search-page/search-filters/search-filter/search-filter.service.ts @@ -25,10 +25,14 @@ export class SearchFilterService { private searchService: SearchService) { } - isFilterActive(paramName: string, filterValue: string): Observable { + isFilterActiveWithValue(paramName: string, filterValue: string): Observable { return this.routeService.hasQueryParamWithValue(paramName, filterValue); } + isFilterActive(paramName: string): Observable { + return this.routeService.hasQueryParam(paramName); + } + getQueryParamsWithout(filterConfig: SearchFilterConfig, value: string) { return this.routeService.removeQueryParameterValue(filterConfig.paramName, value); } @@ -47,12 +51,24 @@ export class SearchFilterService { isCollapsed(filterName: string): Observable { return this.store.select(filterByNameSelector(filterName)) - .map((object: SearchFilterState) => object.filterCollapsed); + .map((object: SearchFilterState) => { + if (object) { + return object.filterCollapsed; + } else { + return false; + } + }); } getPage(filterName: string): Observable { return this.store.select(filterByNameSelector(filterName)) - .map((object: SearchFilterState) => object.page); + .map((object: SearchFilterState) => { + if (object) { + return object.page; + } else { + return 1; + } + }); } public collapse(filterName: string): void { diff --git a/src/app/shared/route.service.ts b/src/app/shared/route.service.ts index b11da41211..25f65a65c9 100644 --- a/src/app/shared/route.service.ts +++ b/src/app/shared/route.service.ts @@ -25,6 +25,7 @@ export class RouteService { return this.route.queryParamMap.map((map) => map.getAll(paramName).indexOf(paramValue) > -1); } + addQueryParameterValue(paramName: string, paramValue: string): Observable { return this.route.queryParams.map((currentParams) => { const newParam = {}; From 889318f162ceecfc55cec7b37bdad6ead30aef13 Mon Sep 17 00:00:00 2001 From: Lotte Hofstede Date: Fri, 17 Nov 2017 11:28:48 +0100 Subject: [PATCH 12/18] 45621: commit before small sidebar changes --- .../search-facet-filter.component.html | 8 ++-- .../search-filters.component.html | 2 +- .../+search-page/search-page.component.html | 8 +--- .../search-settings.component.html | 29 +++++++------- .../search-sidebar.component.html | 9 +++-- .../search-sidebar.component.scss | 4 ++ .../search-sidebar/search-sidebar.effects.ts | 12 +++++- src/app/core/url-baser/url-baser.ts | 39 +++++++++++++++++++ src/app/shared/route.service.ts | 1 - 9 files changed, 80 insertions(+), 32 deletions(-) create mode 100644 src/app/core/url-baser/url-baser.ts diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.html b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.html index a21289808a..dec8ad4d12 100644 --- a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.html +++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.html @@ -6,13 +6,13 @@ {{value}} - - - {{value.value}} - ({{value.count}}) + + {{value.value}} + ({{value.count}})
diff --git a/src/app/+search-page/search-filters/search-filters.component.html b/src/app/+search-page/search-filters/search-filters.component.html index 5bd42928a1..7f375b1238 100644 --- a/src/app/+search-page/search-filters/search-filters.component.html +++ b/src/app/+search-page/search-filters/search-filters.component.html @@ -1,4 +1,4 @@ -

{{"search.filters.head" | translate}}

+

{{"search.filters.head" | translate}}

diff --git a/src/app/+search-page/search-page.component.html b/src/app/+search-page/search-page.component.html index a109950354..16280f0b49 100644 --- a/src/app/+search-page/search-page.component.html +++ b/src/app/+search-page/search-page.component.html @@ -1,19 +1,15 @@
-
- -
-
-
+
-
{{ 'search.sidebar.settings.title' | translate}} -
-

{{ 'search.sidebar.settings.sort-by' | translate}}

-
- + - -
+
-
-

{{ 'search.sidebar.settings.rpp' | translate}}

+
+
{{ 'search.sidebar.settings.rpp' | translate}}
-
- - -
+
diff --git a/src/app/+search-page/search-sidebar/search-sidebar.component.html b/src/app/+search-page/search-sidebar/search-sidebar.component.html index 325f557233..4c98caed0d 100644 --- a/src/app/+search-page/search-sidebar/search-sidebar.component.html +++ b/src/app/+search-page/search-sidebar/search-sidebar.component.html @@ -2,13 +2,16 @@
- - +
diff --git a/src/app/+search-page/search-sidebar/search-sidebar.component.scss b/src/app/+search-page/search-sidebar/search-sidebar.component.scss index 2b318454ed..b5bd6dd30d 100644 --- a/src/app/+search-page/search-sidebar/search-sidebar.component.scss +++ b/src/app/+search-page/search-sidebar/search-sidebar.component.scss @@ -8,4 +8,8 @@ ds-view-mode-switch { margin-bottom: $spacer; } + .sidebar-content > *:not(:last-child) { + margin-bottom: 4*$spacer; + display: block; + } } diff --git a/src/app/+search-page/search-sidebar/search-sidebar.effects.ts b/src/app/+search-page/search-sidebar/search-sidebar.effects.ts index 2894796695..b65010b6e0 100644 --- a/src/app/+search-page/search-sidebar/search-sidebar.effects.ts +++ b/src/app/+search-page/search-sidebar/search-sidebar.effects.ts @@ -3,16 +3,26 @@ import { Effect, Actions } from '@ngrx/effects' import * as fromRouter from '@ngrx/router-store'; import { SearchSidebarCollapseAction } from './search-sidebar.actions'; +import { URLBaser } from '../../core/url-baser/url-baser'; @Injectable() export class SearchSidebarEffects { - + private previousPath: string; @Effect() routeChange$ = this.actions$ .ofType(fromRouter.ROUTER_NAVIGATION) + .filter((action) => this.previousPath !== this.getBaseUrl(action)) + .do((action) => {this.previousPath = this.getBaseUrl(action)}) .map(() => new SearchSidebarCollapseAction()); constructor(private actions$: Actions) { } + getBaseUrl(action: any): string { + /* tslint:disable:no-string-literal */ + const url: string = action['payload'].routerState.url; + return new URLBaser(url).toString(); + /* tslint:enable:no-string-literal */ + } + } diff --git a/src/app/core/url-baser/url-baser.ts b/src/app/core/url-baser/url-baser.ts new file mode 100644 index 0000000000..290f91fe83 --- /dev/null +++ b/src/app/core/url-baser/url-baser.ts @@ -0,0 +1,39 @@ +import { isEmpty } from '../../shared/empty.util'; + +/** + * Extracts the base URL + * from a URL with query parameters + */ +export class URLBaser { + private original: string; + + /** + * Creates a new URLBaser + * + * @param originalURL + * a string representing the original URL with possible query parameters + */ + constructor(originalURL: string) { + this.original = originalURL; + } + + /** + * Removes the query parameters from the original URL of this URLBaser + * + * @return {string} + * The base URL + */ + toString(): string { + if (isEmpty(this.original)) { + return ''; + } else { + const index = this.original.indexOf('?'); + if (index < 0) { + return this.original; + } else { + return this.original.substring(0, index); + } + } + } + +} diff --git a/src/app/shared/route.service.ts b/src/app/shared/route.service.ts index 25f65a65c9..b11da41211 100644 --- a/src/app/shared/route.service.ts +++ b/src/app/shared/route.service.ts @@ -25,7 +25,6 @@ export class RouteService { return this.route.queryParamMap.map((map) => map.getAll(paramName).indexOf(paramValue) > -1); } - addQueryParameterValue(paramName: string, paramValue: string): Observable { return this.route.queryParams.map((currentParams) => { const newParam = {}; From 970e77aed3f8bb971ea714ae9ba818340dcac7e2 Mon Sep 17 00:00:00 2001 From: Lotte Hofstede Date: Fri, 17 Nov 2017 14:37:59 +0100 Subject: [PATCH 13/18] 45621: refactored to sm full width for search page --- .../search-facet-filter.component.html | 13 +++++++---- .../+search-page/search-page.component.html | 8 +++---- .../+search-page/search-page.component.scss | 23 +++++++++++-------- .../search-page.component.spec.ts | 6 ++--- src/app/+search-page/search-page.component.ts | 6 ++++- .../search-filter-config.model.ts | 2 +- .../search-service/search.service.ts | 5 ++-- .../search-sidebar.component.html | 4 ++-- .../search-sidebar/search-sidebar.service.ts | 7 +++++- 9 files changed, 45 insertions(+), 29 deletions(-) diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.html b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.html index dec8ad4d12..8550b04182 100644 --- a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.html +++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.html @@ -6,13 +6,15 @@ {{value}} - - - {{value.value}} - ({{value.count}}) + + {{value.value}} + + {{value.count}} +
@@ -23,7 +25,8 @@ | translate}}
- + diff --git a/src/app/+search-page/search-page.component.html b/src/app/+search-page/search-page.component.html index 16280f0b49..c4d679f72b 100644 --- a/src/app/+search-page/search-page.component.html +++ b/src/app/+search-page/search-page.component.html @@ -1,9 +1,9 @@
- -
+
-
+
- +
diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.spec.ts b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.spec.ts index 83ed1ca8cb..08ee0fd840 100644 --- a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.spec.ts +++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.spec.ts @@ -55,8 +55,10 @@ describe('SearchFacetFilterComponent', () => { getQueryParamsWithout: (paramName: string, filterValue: string) => '', getPage: (paramName: string) => page, /* tslint:disable:no-empty */ - increasePage: (filterName: string) => {}, - decreasePage: (filterName: string) => {}, + incrementPage: (filterName: string) => { + }, + resetPage: (filterName: string) => { + }, /* tslint:enable:no-empty */ searchLink: '/search', } @@ -156,12 +158,23 @@ describe('SearchFacetFilterComponent', () => { describe('when the showMore method is called', () => { beforeEach(() => { - spyOn(filterService, 'increasePage'); + spyOn(filterService, 'incrementPage'); comp.showMore(); }); - it('should call increasePage on the filterService with the correct filter parameter name', () => { - expect(filterService.increasePage).toHaveBeenCalledWith(mockFilterConfig.name) + 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) }); }); diff --git a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts index ccdab52c57..99501f346a 100644 --- a/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts +++ b/src/app/+search-page/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts @@ -57,11 +57,11 @@ export class SearchFacetFilterComponent implements OnInit { } showMore() { - this.filterService.increasePage(this.filterConfig.name); + this.filterService.incrementPage(this.filterConfig.name); } - showLess() { - this.filterService.decreasePage(this.filterConfig.name); + showFirstPageOnly() { + this.filterService.resetPage(this.filterConfig.name); } getCurrentPage(): Observable { diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.actions.ts b/src/app/+search-page/search-filters/search-filter/search-filter.actions.ts index eb5f0bd959..5c9803c7a9 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.actions.ts +++ b/src/app/+search-page/search-filters/search-filter/search-filter.actions.ts @@ -17,7 +17,8 @@ export const SearchFilterActionTypes = { INITIAL_EXPAND: type('dspace/search-filter/INITIAL_EXPAND'), TOGGLE: type('dspace/search-filter/TOGGLE'), DECREMENT_PAGE: type('dspace/search-filter/DECREMENT_PAGE'), - INCREMENT_PAGE: type('dspace/search-filter/INCREMENT_PAGE') + INCREMENT_PAGE: type('dspace/search-filter/INCREMENT_PAGE'), + RESET_PAGE: type('dspace/search-filter/RESET_PAGE') }; export class SearchFilterAction implements Action { @@ -55,4 +56,8 @@ export class SearchFilterDecrementPageAction extends SearchFilterAction { export class SearchFilterIncrementPageAction extends SearchFilterAction { type = SearchFilterActionTypes.INCREMENT_PAGE; } + +export class SearchFilterResetPageAction extends SearchFilterAction { + type = SearchFilterActionTypes.RESET_PAGE; +} /* tslint:enable:max-classes-per-file */ diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.reducer.spec.ts b/src/app/+search-page/search-filters/search-filter/search-filter.reducer.spec.ts index f2f5421c74..8fbfbf2e65 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.reducer.spec.ts +++ b/src/app/+search-page/search-filters/search-filter/search-filter.reducer.spec.ts @@ -4,7 +4,7 @@ import { SearchFilterInitialCollapseAction, SearchFilterInitialExpandAction, SearchFilterToggleAction, - SearchFilterDecrementPageAction + SearchFilterDecrementPageAction, SearchFilterResetPageAction } from './search-filter.actions'; import { filterReducer } from './search-filter.reducer'; @@ -154,4 +154,12 @@ describe('filterReducer', () => { const newState = filterReducer(state, action); expect(newState[filterName1].page).toEqual(1); }); + + it('should reset the page to 1 for the specified filter in response to the RESET_PAGE action', () => { + const state = {}; + state[filterName1] = { filterCollapsed: true, page: 20 }; + const action = new SearchFilterResetPageAction(filterName1); + const newState = filterReducer(state, action); + expect(newState[filterName1].page).toEqual(1); + }); }); diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.reducer.ts b/src/app/+search-page/search-filters/search-filter/search-filter.reducer.ts index fb1dc6802b..9b1a084462 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.reducer.ts +++ b/src/app/+search-page/search-filters/search-filter/search-filter.reducer.ts @@ -77,6 +77,15 @@ export function filterReducer(state = initialState, action: SearchFilterAction): } }); + } + case SearchFilterActionTypes.RESET_PAGE: { + return Object.assign({}, state, { + [action.filterName]: { + filterCollapsed: state[action.filterName].filterCollapsed, + page: 1 + } + }); + } case SearchFilterActionTypes.TOGGLE: { diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.service.spec.ts b/src/app/+search-page/search-filters/search-filter/search-filter.service.spec.ts index b7e0c4ed90..7371e55ee8 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.service.spec.ts +++ b/src/app/+search-page/search-filters/search-filter/search-filter.service.spec.ts @@ -4,7 +4,8 @@ import { Store } from '@ngrx/store'; import { SearchFilterCollapseAction, SearchFilterDecrementPageAction, SearchFilterExpandAction, SearchFilterIncrementPageAction, - SearchFilterInitialCollapseAction, SearchFilterInitialExpandAction, SearchFilterToggleAction + SearchFilterInitialCollapseAction, SearchFilterInitialExpandAction, SearchFilterResetPageAction, + SearchFilterToggleAction } from './search-filter.actions'; import { SearchFiltersState } from './search-filter.reducer'; import { SearchFilterConfig } from '../../search-service/search-filter-config.model'; @@ -95,7 +96,7 @@ describe('SearchFilterService', () => { describe('when the decreasePage method is triggered', () => { beforeEach(() => { - service.decreasePage(mockFilterConfig.name); + service.decrementPage(mockFilterConfig.name); }); it('SearchFilterDecrementPageAction should be dispatched to the store', () => { @@ -106,7 +107,7 @@ describe('SearchFilterService', () => { describe('when the increasePage method is triggered', () => { beforeEach(() => { - service.increasePage(mockFilterConfig.name); + service.incrementPage(mockFilterConfig.name); }); it('SearchFilterCollapseAction should be dispatched to the store', () => { @@ -115,6 +116,17 @@ describe('SearchFilterService', () => { }); + describe('when the resetPage method is triggered', () => { + beforeEach(() => { + service.resetPage(mockFilterConfig.name); + }); + + it('SearchFilterDecrementPageAction should be dispatched to the store', () => { + expect(store.dispatch).toHaveBeenCalledWith(new SearchFilterResetPageAction(mockFilterConfig.name)); + }); + + }); + describe('when the expand method is triggered', () => { beforeEach(() => { service.expand(mockFilterConfig.name); diff --git a/src/app/+search-page/search-filters/search-filter/search-filter.service.ts b/src/app/+search-page/search-filters/search-filter/search-filter.service.ts index a35e0ded76..f3efc19b86 100644 --- a/src/app/+search-page/search-filters/search-filter/search-filter.service.ts +++ b/src/app/+search-page/search-filters/search-filter/search-filter.service.ts @@ -7,7 +7,7 @@ import { SearchFilterDecrementPageAction, SearchFilterExpandAction, SearchFilterIncrementPageAction, SearchFilterInitialCollapseAction, - SearchFilterInitialExpandAction, + SearchFilterInitialExpandAction, SearchFilterResetPageAction, SearchFilterToggleAction } from './search-filter.actions'; import { hasValue, } from '../../../shared/empty.util'; @@ -91,13 +91,17 @@ export class SearchFilterService { this.store.dispatch(new SearchFilterInitialExpandAction(filterName)); } - public decreasePage(filterName: string): void { + public decrementPage(filterName: string): void { this.store.dispatch(new SearchFilterDecrementPageAction(filterName)); } - public increasePage(filterName: string): void { + public incrementPage(filterName: string): void { this.store.dispatch(new SearchFilterIncrementPageAction(filterName)); } + + public resetPage(filterName: string): void { + this.store.dispatch(new SearchFilterResetPageAction(filterName)); + } } function filterByNameSelector(name: string): MemoizedSelector { From 27239341d9c34d2e07df3a8bd13def5e9428c846 Mon Sep 17 00:00:00 2001 From: Lotte Hofstede Date: Tue, 21 Nov 2017 08:58:03 +0100 Subject: [PATCH 17/18] 45621: fixed e2e tests --- e2e/search-page/search-page.po.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/e2e/search-page/search-page.po.ts b/e2e/search-page/search-page.po.ts index 5ea9a0019b..fde3e68bf8 100644 --- a/e2e/search-page/search-page.po.ts +++ b/e2e/search-page/search-page.po.ts @@ -17,13 +17,13 @@ export class ProtractorPage { } getCurrentScope(): promise.Promise { - const scopeSelect = element(by.tagName('select')); + const scopeSelect = element(by.css('#search-form select')); browser.wait(protractor.ExpectedConditions.presenceOf(scopeSelect), 10000); return scopeSelect.getAttribute('value'); } getCurrentQuery(): promise.Promise { - return element(by.tagName('input')).getAttribute('value'); + return element(by.css('#search-form input')).getAttribute('value'); } setCurrentScope(scope: string) { From b257ca703364942e14ce91cb50d7d8acd4efe1c9 Mon Sep 17 00:00:00 2001 From: courtneypattison Date: Wed, 22 Nov 2017 12:23:11 -0800 Subject: [PATCH 18/18] re-export RouterModule in AppRoutingModule --- src/app/app-routing.module.ts | 1 + src/app/app.module.ts | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index b3b8eacfce..dc442cd485 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -15,6 +15,7 @@ import { PageNotFoundComponent } from './pagenotfound/pagenotfound.component'; { path: '**', pathMatch: 'full', component: PageNotFoundComponent }, ]) ], + exports: [RouterModule] }) export class AppRoutingModule { diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 14719ed266..7354a4537f 100755 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,7 +1,6 @@ import { NgModule } from '@angular/core'; import { CommonModule, APP_BASE_HREF } from '@angular/common'; import { HttpModule } from '@angular/http'; -import { RouterModule } from '@angular/router'; import { EffectsModule } from '@ngrx/effects'; import { StoreModule, MetaReducer, META_REDUCERS } from '@ngrx/store'; @@ -53,7 +52,6 @@ if (!ENV_CONFIG.production) { imports: [ CommonModule, HttpModule, - RouterModule, AppRoutingModule, CoreModule.forRoot(), NgbModule.forRoot(),