Skip to content

Commit 61e9466

Browse files
111731: Display the authority label in the search labels
1 parent b34d90d commit 61e9466

19 files changed

Lines changed: 166 additions & 182 deletions

src/app/core/shared/search/search-filter.service.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { BehaviorSubject, combineLatest as observableCombineLatest, Observable } from 'rxjs';
22
import { distinctUntilChanged, map } from 'rxjs/operators';
3-
import { Injectable, InjectionToken } from '@angular/core';
3+
import { Injectable, InjectionToken, EventEmitter } from '@angular/core';
44
import {
55
SearchFiltersState,
66
SearchFilterState
@@ -21,12 +21,14 @@ import { SortDirection, SortOptions } from '../../cache/models/sort-options.mode
2121
import { RouteService } from '../../services/route.service';
2222
import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model';
2323
import { Params } from '@angular/router';
24+
import { AppliedFilter } from '../../../shared/search/models/applied-filter.model';
2425

2526
const filterStateSelector = (state: SearchFiltersState) => state.searchFilter;
2627

2728
export const FILTER_CONFIG: InjectionToken<SearchFilterConfig> = new InjectionToken<SearchFilterConfig>('filterConfig');
2829
export const IN_PLACE_SEARCH: InjectionToken<boolean> = new InjectionToken<boolean>('inPlaceSearch');
2930
export const REFRESH_FILTER: InjectionToken<BehaviorSubject<any>> = new InjectionToken<boolean>('refreshFilters');
31+
export const CHANGE_APPLIED_FILTERS: InjectionToken<EventEmitter<AppliedFilter[]>> = new InjectionToken('changeAppliedFilters');
3032

3133
/**
3234
* Service that performs all actions that have to do with search filters and facets

src/app/shared/search/search-filters/search-filter/search-facet-filter-wrapper/search-facet-filter-wrapper.component.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
import { Component, Injector, Input, OnInit } from '@angular/core';
1+
import { Component, Injector, Input, OnInit, Output, EventEmitter } from '@angular/core';
22
import { renderFilterType } from '../search-filter-type-decorator';
33
import { FilterType } from '../../../models/filter-type.model';
44
import { SearchFilterConfig } from '../../../models/search-filter-config.model';
55
import {
66
FILTER_CONFIG,
77
IN_PLACE_SEARCH,
8-
REFRESH_FILTER
8+
REFRESH_FILTER, CHANGE_APPLIED_FILTERS
99
} from '../../../../../core/shared/search/search-filter.service';
1010
import { GenericConstructor } from '../../../../../core/shared/generic-constructor';
1111
import { SearchFacetFilterComponent } from '../search-facet-filter/search-facet-filter.component';
1212
import { BehaviorSubject } from 'rxjs';
13+
import { AppliedFilter } from '../../../models/applied-filter.model';
1314

1415
@Component({
1516
selector: 'ds-search-facet-filter-wrapper',
@@ -35,6 +36,11 @@ export class SearchFacetFilterWrapperComponent implements OnInit {
3536
*/
3637
@Input() refreshFilters: BehaviorSubject<boolean>;
3738

39+
/**
40+
* Emits the {@link AppliedFilter}s of this search filter
41+
*/
42+
@Output() changeAppliedFilters: EventEmitter<AppliedFilter[]> = new EventEmitter();
43+
3844
/**
3945
* The constructor of the search facet filter that should be rendered, based on the filter config's type
4046
*/
@@ -56,7 +62,8 @@ export class SearchFacetFilterWrapperComponent implements OnInit {
5662
providers: [
5763
{ provide: FILTER_CONFIG, useFactory: () => (this.filterConfig), deps: [] },
5864
{ provide: IN_PLACE_SEARCH, useFactory: () => (this.inPlaceSearch), deps: [] },
59-
{ provide: REFRESH_FILTER, useFactory: () => (this.refreshFilters), deps: [] }
65+
{ provide: REFRESH_FILTER, useFactory: () => (this.refreshFilters), deps: [] },
66+
{ provide: CHANGE_APPLIED_FILTERS, useFactory: () => this.changeAppliedFilters },
6067
],
6168
parent: this.injector
6269
});

src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.spec.ts

Lines changed: 30 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,29 @@
1-
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
1+
import { ChangeDetectionStrategy, EventEmitter, NO_ERRORS_SCHEMA } from '@angular/core';
22
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
33
import { TranslateModule } from '@ngx-translate/core';
44
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
55
import {
6+
CHANGE_APPLIED_FILTERS,
67
FILTER_CONFIG,
78
IN_PLACE_SEARCH,
89
REFRESH_FILTER,
910
SearchFilterService
1011
} from '../../../../../core/shared/search/search-filter.service';
1112
import { SearchFilterConfig } from '../../../models/search-filter-config.model';
1213
import { FilterType } from '../../../models/filter-type.model';
13-
import { FacetValue } from '../../../models/facet-value.model';
1414
import { FormsModule } from '@angular/forms';
1515
import { BehaviorSubject, of as observableOf } from 'rxjs';
1616
import { SearchService } from '../../../../../core/shared/search/search.service';
1717
import { SearchServiceStub } from '../../../../testing/search-service.stub';
18-
import { buildPaginatedList } from '../../../../../core/data/paginated-list.model';
1918
import { RouterStub } from '../../../../testing/router.stub';
2019
import { Router } from '@angular/router';
21-
import { PageInfo } from '../../../../../core/shared/page-info.model';
2220
import { SearchFacetFilterComponent } from './search-facet-filter.component';
2321
import { RemoteDataBuildService } from '../../../../../core/cache/builders/remote-data-build.service';
2422
import { SearchConfigurationServiceStub } from '../../../../testing/search-configuration-service.stub';
2523
import { SEARCH_CONFIG_SERVICE } from '../../../../../my-dspace-page/my-dspace-page.component';
2624
import { createSuccessfulRemoteDataObject$ } from '../../../../remote-data.utils';
25+
import { AppliedFilter } from '../../../models/applied-filter.model';
26+
import { FacetValues } from '../../../models/facet-values.model';
2727

2828
describe('SearchFacetFilterComponent', () => {
2929
let comp: SearchFacetFilterComponent;
@@ -39,45 +39,28 @@ describe('SearchFacetFilterComponent', () => {
3939
isOpenByDefault: false,
4040
pageSize: 2
4141
});
42-
const values: FacetValue[] = [
43-
{
44-
label: value1,
45-
value: value1,
46-
count: 52,
47-
_links: {
48-
self: {
49-
href: ''
50-
},
51-
search: {
52-
href: ''
53-
}
54-
}
55-
}, {
56-
label: value2,
57-
value: value2,
58-
count: 20,
59-
_links: {
60-
self: {
61-
href: ''
62-
},
63-
search: {
64-
href: ''
65-
}
42+
const values: Partial<FacetValues> = {
43+
appliedFilters: [
44+
{
45+
filter: filterName1,
46+
operator: 'equals',
47+
label: value1,
48+
value: value1,
49+
},
50+
{
51+
filter: filterName1,
52+
operator: 'equals',
53+
label: value2,
54+
value: value2,
55+
},
56+
{
57+
filter: filterName1,
58+
operator: 'equals',
59+
label: value3,
60+
value: value3,
6661
}
67-
}, {
68-
label: value3,
69-
value: value3,
70-
count: 5,
71-
_links: {
72-
self: {
73-
href: ''
74-
},
75-
search: {
76-
href: ''
77-
}
78-
}
79-
}
80-
];
62+
]
63+
};
8164

8265
const searchLink = '/search';
8366
const selectedValues = [value1, value2];
@@ -86,7 +69,6 @@ describe('SearchFacetFilterComponent', () => {
8669
let router;
8770
const page = observableOf(0);
8871

89-
const mockValues = createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), values));
9072
beforeEach(waitForAsync(() => {
9173
TestBed.configureTestingModule({
9274
imports: [TranslateModule.forRoot(), NoopAnimationsModule, FormsModule],
@@ -99,6 +81,7 @@ describe('SearchFacetFilterComponent', () => {
9981
{ provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() },
10082
{ provide: IN_PLACE_SEARCH, useValue: false },
10183
{ provide: REFRESH_FILTER, useValue: new BehaviorSubject<boolean>(false) },
84+
{ provide: CHANGE_APPLIED_FILTERS, useValue: new EventEmitter() },
10285
{
10386
provide: SearchFilterService, useValue: {
10487
getSelectedValuesForFilter: () => observableOf(selectedValues),
@@ -125,22 +108,11 @@ describe('SearchFacetFilterComponent', () => {
125108
comp.filterConfig = mockFilterConfig;
126109
filterService = (comp as any).filterService;
127110
searchService = (comp as any).searchService;
128-
spyOn(searchService, 'getFacetValuesFor').and.returnValue(mockValues);
111+
spyOn(searchService, 'getFacetValuesFor').and.returnValue(createSuccessfulRemoteDataObject$(values));
129112
router = (comp as any).router;
130113
fixture.detectChanges();
131114
});
132115

133-
describe('when the isChecked method is called with a value', () => {
134-
beforeEach(() => {
135-
spyOn(filterService, 'isFilterActiveWithValue');
136-
comp.isChecked(values[1]);
137-
});
138-
139-
it('should call isFilterActiveWithValue on the filterService with the correct filter parameter name and the passed value', () => {
140-
expect(filterService.isFilterActiveWithValue).toHaveBeenCalledWith(mockFilterConfig.paramName, values[1].value);
141-
});
142-
});
143-
144116
describe('when the getSearchLink method is triggered', () => {
145117
let link: string;
146118
beforeEach(() => {
@@ -202,7 +174,9 @@ describe('SearchFacetFilterComponent', () => {
202174

203175
beforeEach(() => {
204176
comp.selectedValues$ = observableOf(selectedValues.map((value) =>
205-
Object.assign(new FacetValue(), {
177+
Object.assign(new AppliedFilter(), {
178+
filter: filterName1,
179+
operator: 'equals',
206180
label: value,
207181
value: value
208182
})));

src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { animate, state, style, transition, trigger } from '@angular/animations';
2-
import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
2+
import { Component, Inject, OnDestroy, OnInit, EventEmitter } from '@angular/core';
33
import { Router } from '@angular/router';
44

55
import { BehaviorSubject, combineLatest as observableCombineLatest, Observable, of as observableOf, Subject, Subscription } from 'rxjs';
@@ -13,14 +13,14 @@ import { EmphasizePipe } from '../../../../utils/emphasize.pipe';
1313
import { FacetValue } from '../../../models/facet-value.model';
1414
import { SearchFilterConfig } from '../../../models/search-filter-config.model';
1515
import { SearchService } from '../../../../../core/shared/search/search.service';
16-
import { FILTER_CONFIG, IN_PLACE_SEARCH, REFRESH_FILTER, SearchFilterService } from '../../../../../core/shared/search/search-filter.service';
16+
import { FILTER_CONFIG, IN_PLACE_SEARCH, REFRESH_FILTER, SearchFilterService, CHANGE_APPLIED_FILTERS } from '../../../../../core/shared/search/search-filter.service';
1717
import { SearchConfigurationService } from '../../../../../core/shared/search/search-configuration.service';
1818
import { getFirstSucceededRemoteData } from '../../../../../core/shared/operators';
1919
import { InputSuggestion } from '../../../../input-suggestions/input-suggestions.model';
2020
import { SearchOptions } from '../../../models/search-options.model';
2121
import { SEARCH_CONFIG_SERVICE } from '../../../../../my-dspace-page/my-dspace-page.component';
2222
import { currentPath } from '../../../../utils/route.utils';
23-
import { getFacetValueForType, stripOperatorFromFilterValue } from '../../../search.utils';
23+
import { getFacetValueForType, stripOperatorFromFilterValue, addOperatorToFilterValue } from '../../../search.utils';
2424
import { createPendingRemoteDataObject } from '../../../../remote-data.utils';
2525
import { FacetValues } from '../../../models/facet-values.model';
2626
import { AppliedFilter } from '../../../models/applied-filter.model';
@@ -93,7 +93,9 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy {
9393
@Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService,
9494
@Inject(IN_PLACE_SEARCH) public inPlaceSearch: boolean,
9595
@Inject(FILTER_CONFIG) public filterConfig: SearchFilterConfig,
96-
@Inject(REFRESH_FILTER) public refreshFilters: BehaviorSubject<boolean>) {
96+
@Inject(REFRESH_FILTER) public refreshFilters: BehaviorSubject<boolean>,
97+
@Inject(CHANGE_APPLIED_FILTERS) public changeAppliedFilters: EventEmitter<AppliedFilter[]>,
98+
) {
9799
}
98100

99101
/**
@@ -126,13 +128,6 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy {
126128
this.filter = '';
127129
}
128130

129-
/**
130-
* Checks if a value for this filter is currently active
131-
*/
132-
isChecked(value: FacetValue): Observable<boolean> {
133-
return this.filterService.isFilterActiveWithValue(this.filterConfig.paramName, value.value);
134-
}
135-
136131
/**
137132
* @returns {string} The base path to the search page, or the current page when inPlaceSearch is true
138133
*/
@@ -243,13 +238,13 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy {
243238
*/
244239
protected applyFilterValue(data) {
245240
if (data.match(new RegExp(`^.+,(equals|query|authority)$`))) {
246-
this.selectedValues$.pipe(take(1)).subscribe((selectedValues) => {
241+
this.selectedValues$.pipe(take(1)).subscribe((selectedValues: AppliedFilter[]) => {
247242
if (isNotEmpty(data)) {
248-
this.router.navigate(this.getSearchLinkParts(), {
243+
void this.router.navigate(this.getSearchLinkParts(), {
249244
queryParams:
250245
{
251246
[this.filterConfig.paramName]: [
252-
...selectedValues.map((appliedFilter: AppliedFilter) => appliedFilter.value),
247+
...selectedValues.map((appliedFilter: AppliedFilter) => addOperatorToFilterValue(appliedFilter.value, appliedFilter.operator)),
253248
data
254249
]
255250
},
@@ -306,13 +301,15 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy {
306301
return this.rdbs.aggregate(filterValues);
307302
}),
308303
tap((rd: RemoteData<FacetValues[]>) => {
309-
const appliedFilters: AppliedFilter[] = [].concat(...rd.payload.map((facetValues: FacetValues) => facetValues.appliedFilters))
304+
const allAppliedFilters: AppliedFilter[] = [].concat(...rd.payload.map((facetValues: FacetValues) => facetValues.appliedFilters))
310305
.filter((appliedFilter: AppliedFilter) => hasValue(appliedFilter));
311306
this.selectedValues$ = this.filterService.getSelectedValuesForFilter(this.filterConfig).pipe(
312307
map((selectedValues: string[]) => {
313-
return selectedValues.map((value: string) => {
314-
return appliedFilters.find((appliedFilter: AppliedFilter) => appliedFilter.value === stripOperatorFromFilterValue(value));
308+
const appliedFilters: AppliedFilter[] = selectedValues.map((value: string) => {
309+
return allAppliedFilters.find((appliedFilter: AppliedFilter) => appliedFilter.value === stripOperatorFromFilterValue(value));
315310
}).filter((appliedFilter: AppliedFilter) => hasValue(appliedFilter));
311+
this.changeAppliedFilters.emit(appliedFilters);
312+
return appliedFilters;
316313
}),
317314
);
318315
this.animationState = 'ready';

src/app/shared/search/search-filters/search-filter/search-filter.component.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ <h5 class="d-inline-block mb-0">
2020
<ds-search-facet-filter-wrapper
2121
[filterConfig]="filter"
2222
[inPlaceSearch]="inPlaceSearch"
23-
[refreshFilters]="refreshFilters" >
23+
[refreshFilters]="refreshFilters"
24+
(changeAppliedFilters)="changeAppliedFilters.emit($event)">
2425
</ds-search-facet-filter-wrapper>
2526
</div>
2627
</div>

src/app/shared/search/search-filters/search-filter/search-filter.component.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, Inject, Input, OnInit } from '@angular/core';
1+
import { Component, Inject, Input, OnInit, Output, EventEmitter } from '@angular/core';
22

33
import { BehaviorSubject, Observable, of as observableOf } from 'rxjs';
44
import { filter, map, startWith, switchMap, take } from 'rxjs/operators';
@@ -11,6 +11,7 @@ import { SearchService } from '../../../../core/shared/search/search.service';
1111
import { SearchConfigurationService } from '../../../../core/shared/search/search-configuration.service';
1212
import { SEARCH_CONFIG_SERVICE } from '../../../../my-dspace-page/my-dspace-page.component';
1313
import { SequenceService } from '../../../../core/shared/sequence.service';
14+
import { AppliedFilter } from '../../models/applied-filter.model';
1415

1516
@Component({
1617
selector: 'ds-search-filter',
@@ -38,6 +39,11 @@ export class SearchFilterComponent implements OnInit {
3839
*/
3940
@Input() refreshFilters: BehaviorSubject<boolean>;
4041

42+
/**
43+
* Emits the {@link AppliedFilter}s of this search filter
44+
*/
45+
@Output() changeAppliedFilters: EventEmitter<AppliedFilter[]> = new EventEmitter();
46+
4147
/**
4248
* True when the filter is 100% collapsed in the UI
4349
*/

0 commit comments

Comments
 (0)