From b9fa4795192e375253e27ba84a65482ea29e5cc7 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Fri, 12 Apr 2024 11:54:45 +0200 Subject: [PATCH 01/24] 111731: Created new advanced search component --- .../advanced-search.component.html | 14 ++++++ .../advanced-search.component.scss | 0 .../advanced-search.component.spec.ts | 36 ++++++++++++++ .../advanced-search.component.ts | 47 +++++++++++++++++++ .../search-sidebar.component.html | 2 + src/app/shared/search/search.module.ts | 2 + .../search-configuration-service.stub.ts | 5 ++ src/assets/i18n/en.json5 | 42 +++++++++++++++++ src/themes/dspace/styles/_global-styles.scss | 2 +- 9 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 src/app/shared/search/advanced-search/advanced-search.component.html create mode 100644 src/app/shared/search/advanced-search/advanced-search.component.scss create mode 100644 src/app/shared/search/advanced-search/advanced-search.component.spec.ts create mode 100644 src/app/shared/search/advanced-search/advanced-search.component.ts diff --git a/src/app/shared/search/advanced-search/advanced-search.component.html b/src/app/shared/search/advanced-search/advanced-search.component.html new file mode 100644 index 00000000000..d9633c7eda5 --- /dev/null +++ b/src/app/shared/search/advanced-search/advanced-search.component.html @@ -0,0 +1,14 @@ +

{{ 'search.sidebar.advanced-search.title' | translate }}

+ diff --git a/src/app/shared/search/advanced-search/advanced-search.component.scss b/src/app/shared/search/advanced-search/advanced-search.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/app/shared/search/advanced-search/advanced-search.component.spec.ts b/src/app/shared/search/advanced-search/advanced-search.component.spec.ts new file mode 100644 index 00000000000..a8f4811d283 --- /dev/null +++ b/src/app/shared/search/advanced-search/advanced-search.component.spec.ts @@ -0,0 +1,36 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { AdvancedSearchComponent } from './advanced-search.component'; +import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service'; +import { SearchConfigurationServiceStub } from '../../testing/search-configuration-service.stub'; +import { TranslateModule } from '@ngx-translate/core'; + +describe('AdvancedSearchComponent', () => { + let component: AdvancedSearchComponent; + let fixture: ComponentFixture; + + let searchConfigurationService: SearchConfigurationServiceStub; + + beforeEach(async () => { + searchConfigurationService = new SearchConfigurationServiceStub(); + + await TestBed.configureTestingModule({ + imports: [ + TranslateModule.forRoot(), + ], + declarations: [ + AdvancedSearchComponent, + ], + providers: [ + { provide: SearchConfigurationService, useValue: searchConfigurationService }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(AdvancedSearchComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/search/advanced-search/advanced-search.component.ts b/src/app/shared/search/advanced-search/advanced-search.component.ts new file mode 100644 index 00000000000..6607cef6e4d --- /dev/null +++ b/src/app/shared/search/advanced-search/advanced-search.component.ts @@ -0,0 +1,47 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { map, Observable } from 'rxjs'; +import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service'; +import { FilterConfig, SearchConfig } from '../../../core/shared/search/search-filters/search-config.model'; + +@Component({ + selector: 'ds-advanced-search', + templateUrl: './advanced-search.component.html', + styleUrls: ['./advanced-search.component.scss'], +}) +export class AdvancedSearchComponent implements OnInit { + + @Input() configuration: string; + + advancedFilters$: Observable; + + advancedFilterMap$: Observable>; + + currentFilter: string; + + currentOperator: string; + + constructor( + protected searchConfigurationService: SearchConfigurationService, + ) { + } + + ngOnInit(): void { + this.advancedFilters$ = this.searchConfigurationService.getConfigurationSearchConfig(this.configuration).pipe( + map((searchConfiguration: SearchConfig) => searchConfiguration.filters), + ); + this.advancedFilterMap$ = this.advancedFilters$.pipe( + map((filters: FilterConfig[]) => { + const filterMap: Map = new Map(); + if (filters.length > 0) { + this.currentFilter = filters[0].filter; + this.currentOperator = filters[0].operators[0].operator; + for (const filter of filters) { + filterMap.set(filter.filter, filter); + } + } + return filterMap; + }), + ); + } + +} diff --git a/src/app/shared/search/search-sidebar/search-sidebar.component.html b/src/app/shared/search/search-sidebar/search-sidebar.component.html index c59617bafb9..4a9c810a6f0 100644 --- a/src/app/shared/search/search-sidebar/search-sidebar.component.html +++ b/src/app/shared/search/search-sidebar/search-sidebar.component.html @@ -24,6 +24,8 @@ [inPlaceSearch]="inPlaceSearch" (changeAppliedFilters)="changeAppliedFilters.emit($event)"> + + diff --git a/src/app/shared/search/search.module.ts b/src/app/shared/search/search.module.ts index 261d16774d7..131c59bc5db 100644 --- a/src/app/shared/search/search.module.ts +++ b/src/app/shared/search/search.module.ts @@ -37,6 +37,7 @@ import { ThemedSearchSettingsComponent } from './search-settings/themed-search-s import { NouisliderModule } from 'ng2-nouislider'; import { ThemedSearchFiltersComponent } from './search-filters/themed-search-filters.component'; import { ThemedSearchSidebarComponent } from './search-sidebar/themed-search-sidebar.component'; +import { AdvancedSearchComponent } from './advanced-search/advanced-search.component'; const ENTRY_COMPONENTS = [ SearchFacetFilterComponent, @@ -66,6 +67,7 @@ const COMPONENTS = [ SearchFacetFilterWrapperComponent, SearchSwitchConfigurationComponent, ConfigurationSearchPageComponent, + AdvancedSearchComponent, ThemedConfigurationSearchPageComponent, ThemedSearchResultsComponent, ThemedSearchSettingsComponent, diff --git a/src/app/shared/testing/search-configuration-service.stub.ts b/src/app/shared/testing/search-configuration-service.stub.ts index fb72310417b..fff5fff7016 100644 --- a/src/app/shared/testing/search-configuration-service.stub.ts +++ b/src/app/shared/testing/search-configuration-service.stub.ts @@ -1,5 +1,6 @@ import { BehaviorSubject, of as observableOf, Observable } from 'rxjs'; import { Params } from '@angular/router'; +import { SearchConfig } from '../../core/shared/search/search-filters/search-config.model'; export class SearchConfigurationServiceStub { @@ -28,6 +29,10 @@ export class SearchConfigurationServiceStub { return observableOf({ hasSucceeded: true, payload: [] }); } + getConfigurationSearchConfig(_configuration: string, _scope?: string): Observable { + return observableOf(new SearchConfig()); + } + getAvailableConfigurationOptions() { return observableOf([{value: 'test', label: 'test'}]); } diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 6c91bae4c16..83feee8f83f 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -3620,6 +3620,8 @@ "search.search-form.placeholder": "Search the repository ...", + "search.filters.applied.f.title": "Title", + "search.filters.applied.f.author": "Author", "search.filters.applied.f.dateIssued.max": "End date", @@ -3634,6 +3636,10 @@ "search.filters.applied.f.has_content_in_original_bundle": "Has files", + "search.filters.applied.f.original_bundle_filenames": "File name", + + "search.filters.applied.f.original_bundle_descriptions": "File description", + "search.filters.applied.f.itemtype": "Type", "search.filters.applied.f.namedresourcetype": "Status", @@ -3652,6 +3658,12 @@ "search.filters.applied.f.withdrawn": "Withdrawn", + "search.filters.filter.title.head": "Title", + + "search.filters.filter.title.placeholder": "Title", + + "search.filters.filter.title.label": "Search Title", + "search.filters.filter.author.head": "Author", "search.filters.filter.author.placeholder": "Author name", @@ -3720,6 +3732,18 @@ "search.filters.filter.has_content_in_original_bundle.head": "Has files", + "search.filters.filter.original_bundle_filenames.head": "File name", + + "search.filters.filter.original_bundle_filenames.placeholder": "File name", + + "search.filters.filter.original_bundle_filenames.label": "Search File name", + + "search.filters.filter.original_bundle_descriptions.head": "File description", + + "search.filters.filter.original_bundle_descriptions.placeholder": "File description", + + "search.filters.filter.original_bundle_descriptions.label": "Search File description", + "search.filters.filter.itemtype.head": "Type", "search.filters.filter.itemtype.placeholder": "Type", @@ -3822,6 +3846,20 @@ "search.filters.search.submit": "Submit", + "search.filters.operator.equals.text": "Equals", + + "search.filters.operator.notequals.text": "Not Equals", + + "search.filters.operator.authority.text": "Authority", + + "search.filters.operator.notauthority.text": "Not Authority", + + "search.filters.operator.contains.text": "Contains", + + "search.filters.operator.notcontains.text": "Not Contains", + + "search.filters.operator.query.text": "Query", + "search.form.search": "Search", "search.form.search_dspace": "All repository", @@ -3856,6 +3894,10 @@ "search.sidebar.settings.sort-by": "Sort By", + "search.sidebar.advanced-search.title": "Advanced Search", + + "search.sidebar.advanced-search.filter-by": "Filter by", + "search.sidebar.settings.title": "Settings", "search.view-switch.show-detail": "Show detail", diff --git a/src/themes/dspace/styles/_global-styles.scss b/src/themes/dspace/styles/_global-styles.scss index e41dae0e3f2..5e3b95b5e37 100644 --- a/src/themes/dspace/styles/_global-styles.scss +++ b/src/themes/dspace/styles/_global-styles.scss @@ -3,7 +3,7 @@ // imports the base global style @import '../../../styles/_global-styles.scss'; -.facet-filter, .setting-option { +.facet-filter, .setting-option, .advanced-search { background-color: var(--bs-light); border-radius: var(--bs-border-radius); From 89eb314fe7e9449474008aca45d18c5b677b9891 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Fri, 12 Apr 2024 13:46:42 +0200 Subject: [PATCH 02/24] 111731: Moved findSuggestions logic to SearchFilterService in order to reuse it in the advanced search component --- .../search/search-filter.service.spec.ts | 68 ++++++++++--------- .../shared/search/search-filter.service.ts | 54 ++++++++++++++- .../search-authority-filter.component.html | 2 +- .../search-facet-filter.component.spec.ts | 11 --- .../search-facet-filter.component.ts | 58 ++-------------- .../search-hierarchy-filter.component.html | 2 +- .../search-text-filter.component.html | 2 +- 7 files changed, 96 insertions(+), 101 deletions(-) diff --git a/src/app/core/shared/search/search-filter.service.spec.ts b/src/app/core/shared/search/search-filter.service.spec.ts index f18999bbe66..435e452549b 100644 --- a/src/app/core/shared/search/search-filter.service.spec.ts +++ b/src/app/core/shared/search/search-filter.service.spec.ts @@ -1,3 +1,4 @@ +import { waitForAsync, TestBed } from '@angular/core/testing'; import { SearchFilterService } from './search-filter.service'; import { Store } from '@ngrx/store'; import { @@ -12,9 +13,13 @@ import { import { SearchFiltersState } from '../../../shared/search/search-filters/search-filter/search-filter.reducer'; import { SearchFilterConfig } from '../../../shared/search/models/search-filter-config.model'; import { FilterType } from '../../../shared/search/models/filter-type.model'; -import { ActivatedRouteStub } from '../../../shared/testing/active-router.stub'; import { of as observableOf } from 'rxjs'; import { SortDirection, SortOptions } from '../../cache/models/sort-options.model'; +import { SearchServiceStub } from '../../../shared/testing/search-service.stub'; +import { SearchService } from './search.service'; +import { RouteService } from '../../services/route.service'; +import { routeServiceStub } from '../../../shared/testing/route-service.stub'; +import { SearchOptions } from '../../../shared/search/models/search-options.model'; describe('SearchFilterService', () => { let service: SearchFilterService; @@ -28,7 +33,8 @@ describe('SearchFilterService', () => { }); const value1 = 'random value'; - // const value2 = 'another value'; + + let searchService: SearchServiceStub; const store: Store = jasmine.createSpyObj('store', { /* eslint-disable no-empty,@typescript-eslint/no-empty-function */ dispatch: {}, @@ -36,36 +42,20 @@ describe('SearchFilterService', () => { select: observableOf(true) }); - const routeServiceStub: any = { - /* eslint-disable no-empty,@typescript-eslint/no-empty-function */ - hasQueryParamWithValue: (param: string, value: string) => { - }, - hasQueryParam: (param: string) => { - }, - removeQueryParameterValue: (param: string, value: string) => { - }, - addQueryParameterValue: (param: string, value: string) => { - }, - getQueryParameterValue: (param: string) => { - }, - getQueryParameterValues: (param: string) => { - return observableOf({}); - }, - getQueryParamsWithPrefix: (param: string) => { - return observableOf({}); - }, - getRouteParameterValue: (param: string) => { - } - /* eslint-enable no-empty, @typescript-eslint/no-empty-function */ - }; - const activatedRoute: any = new ActivatedRouteStub(); - const searchServiceStub: any = { - uiSearchRoute: '/search' - }; - - beforeEach(() => { - service = new SearchFilterService(store, routeServiceStub); - }); + beforeEach(waitForAsync(() => { + searchService = new SearchServiceStub(); + + TestBed.configureTestingModule({ + providers: [ + SearchFilterService, + { provide: SearchService, useValue: searchService }, + { provide: Store, useValue: store }, + { provide: RouteService, useValue: routeServiceStub }, + ], + }); + + service = TestBed.inject(SearchFilterService); + })); describe('when the initializeFilter method is triggered', () => { beforeEach(() => { @@ -269,4 +259,18 @@ describe('SearchFilterService', () => { }); }); + + describe('when findSuggestions is called with query \'test\'', () => { + const query = 'test'; + const searchOptions = new SearchOptions({}); + + beforeEach(() => { + spyOn(searchService, 'getFacetValuesFor').and.returnValue(observableOf()); + service.findSuggestions(mockFilterConfig, searchOptions, query); + }); + + it('should call getFacetValuesFor on the component\'s SearchService with the right query', () => { + expect(searchService.getFacetValuesFor).toHaveBeenCalledWith(mockFilterConfig, 1, searchOptions, query); + }); + }); }); diff --git a/src/app/core/shared/search/search-filter.service.ts b/src/app/core/shared/search/search-filter.service.ts index 2eafdf268af..14892bc78e5 100644 --- a/src/app/core/shared/search/search-filter.service.ts +++ b/src/app/core/shared/search/search-filter.service.ts @@ -1,4 +1,4 @@ -import { BehaviorSubject, combineLatest as observableCombineLatest, Observable } from 'rxjs'; +import { BehaviorSubject, combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs'; import { distinctUntilChanged, map } from 'rxjs/operators'; import { Injectable, InjectionToken, EventEmitter } from '@angular/core'; import { @@ -22,6 +22,15 @@ import { RouteService } from '../../services/route.service'; import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; import { Params } from '@angular/router'; import { AppliedFilter } from '../../../shared/search/models/applied-filter.model'; +import { SearchOptions } from '../../../shared/search/models/search-options.model'; +import { getFirstSucceededRemoteData } from '../operators'; +import { FacetValue } from '../../../shared/search/models/facet-value.model'; +import { PaginatedList } from '../../data/paginated-list.model'; +import { RemoteData } from '../../data/remote-data'; +import { stripOperatorFromFilterValue, getFacetValueForType } from '../../../shared/search/search.utils'; +import { EmphasizePipe } from '../../../shared/utils/emphasize.pipe'; +import { SearchService } from './search.service'; +import { InputSuggestion } from '../../../shared/input-suggestions/input-suggestions.model'; const filterStateSelector = (state: SearchFiltersState) => state.searchFilter; @@ -36,8 +45,11 @@ export const CHANGE_APPLIED_FILTERS: InjectionToken, - private routeService: RouteService) { + constructor( + protected searchService: SearchService, + protected store: Store, + protected routeService: RouteService, + ) { } /** @@ -146,6 +158,42 @@ export class SearchFilterService { ); } + /** + * Updates the found facet value suggestions for a given query + * Transforms the found values into display values + * + * @param searchFilterConfig The search filter config + * @param searchOptions The search options + * @param query The query for which is being searched + */ + findSuggestions(searchFilterConfig: SearchFilterConfig, searchOptions: SearchOptions, query: string): Observable { + if (isNotEmpty(query)) { + return this.searchService.getFacetValuesFor(searchFilterConfig, 1, searchOptions, query.toLowerCase()).pipe( + getFirstSucceededRemoteData(), + map((rd: RemoteData>) => rd.payload.page.map((facet) => { + return { + displayValue: this.getDisplayValue(facet, query), + query: getFacetValueForType(facet, searchFilterConfig), + value: stripOperatorFromFilterValue(getFacetValueForType(facet, searchFilterConfig)) + }; + })), + ); + } else { + return observableOf([]); + } + } + + /** + * Transforms the facet value string, so if the query matches part of the value, it's emphasized in the value + * + * @param facet The value of the facet as returned by the server + * @param query The query that was used to search facet values + * @returns {string} The facet value with the query part emphasized + */ + getDisplayValue(facet: FacetValue, query: string): string { + return `${new EmphasizePipe().transform(facet.value, query)} (${facet.count})`; + } + /** * Checks if the state of a given filter is currently collapsed or not * @param {string} filterName The filtername for which the collapsed state is checked diff --git a/src/app/shared/search/search-filters/search-filter/search-authority-filter/search-authority-filter.component.html b/src/app/shared/search/search-filters/search-filter/search-authority-filter/search-authority-filter.component.html index e008226d19a..698b8427d5b 100644 --- a/src/app/shared/search/search-filters/search-filter/search-authority-filter/search-authority-filter.component.html +++ b/src/app/shared/search/search-filters/search-filter/search-authority-filter/search-authority-filter.component.html @@ -17,7 +17,7 @@ - { expect(comp.filter).toEqual(''); }); }); - - describe('when findSuggestions is called with query \'test\'', () => { - const query = 'test'; - beforeEach(() => { - comp.findSuggestions(query); - }); - - it('should call getFacetValuesFor on the component\'s SearchService with the right query', () => { - expect((comp as any).searchService.getFacetValuesFor).toHaveBeenCalledWith(comp.filterConfig, 1, {}, query); - }); - }); }); diff --git a/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts b/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts index 6bf97538d2a..c9115a97038 100644 --- a/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts +++ b/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts @@ -6,21 +6,18 @@ import { BehaviorSubject, combineLatest as observableCombineLatest, Observable, import { debounceTime, distinctUntilChanged, filter, map, mergeMap, switchMap, take, tap } from 'rxjs/operators'; import { RemoteDataBuildService } from '../../../../../core/cache/builders/remote-data-build.service'; -import { PaginatedList } from '../../../../../core/data/paginated-list.model'; -import { RemoteData } from '../../../../../core/data/remote-data'; import { hasNoValue, hasValue, isNotEmpty } from '../../../../empty.util'; -import { EmphasizePipe } from '../../../../utils/emphasize.pipe'; import { FacetValue } from '../../../models/facet-value.model'; import { SearchFilterConfig } from '../../../models/search-filter-config.model'; import { SearchService } from '../../../../../core/shared/search/search.service'; import { FILTER_CONFIG, IN_PLACE_SEARCH, REFRESH_FILTER, SearchFilterService, CHANGE_APPLIED_FILTERS } from '../../../../../core/shared/search/search-filter.service'; import { SearchConfigurationService } from '../../../../../core/shared/search/search-configuration.service'; -import { getFirstSucceededRemoteData, getFirstSucceededRemoteDataPayload } from '../../../../../core/shared/operators'; +import { getFirstSucceededRemoteDataPayload } from '../../../../../core/shared/operators'; import { InputSuggestion } from '../../../../input-suggestions/input-suggestions.model'; import { SearchOptions } from '../../../models/search-options.model'; import { SEARCH_CONFIG_SERVICE } from '../../../../../my-dspace-page/my-dspace-page.component'; import { currentPath } from '../../../../utils/route.utils'; -import { getFacetValueForType, stripOperatorFromFilterValue, addOperatorToFilterValue } from '../../../search.utils'; +import { stripOperatorFromFilterValue, addOperatorToFilterValue } from '../../../search.utils'; import { FacetValues } from '../../../models/facet-values.model'; import { AppliedFilter } from '../../../models/applied-filter.model'; @@ -61,7 +58,7 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy { /** * Emits the result values for this filter found by the current filter query */ - filterSearchResults: Observable = observableOf([]); + filterSearchResults$: Observable = observableOf([]); /** * Emits the active values for this filter @@ -200,34 +197,8 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy { .forEach((sub) => sub.unsubscribe()); } - /** - * Updates the found facet value suggestions for a given query - * Transforms the found values into display values - * @param data The query for which is being searched - */ - findSuggestions(data): void { - if (isNotEmpty(data)) { - this.searchOptions$.pipe(take(1)).subscribe( - (options) => { - this.filterSearchResults = this.searchService.getFacetValuesFor(this.filterConfig, 1, options, data.toLowerCase()) - .pipe( - getFirstSucceededRemoteData(), - map( - (rd: RemoteData>) => { - return rd.payload.page.map((facet) => { - return { - displayValue: this.getDisplayValue(facet, data), - query: this.getFacetValue(facet), - value: stripOperatorFromFilterValue(this.getFacetValue(facet)) - }; - }); - } - )); - } - ); - } else { - this.filterSearchResults = observableOf([]); - } + findSuggestions(query: string): void { + this.filterSearchResults$ = this.filterService.findSuggestions(this.filterConfig, this.searchOptions$.value, query); } /** @@ -250,18 +221,11 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy { }); this.filter = ''; } - this.filterSearchResults = observableOf([]); + this.filterSearchResults$ = observableOf([]); }); } } - /** - * Retrieve facet value - */ - protected getFacetValue(facet: FacetValue): string { - return getFacetValueForType(facet, this.filterConfig); - } - protected retrieveFilterValues(useCachedVersionIfAvailable = true): Observable { return observableCombineLatest([this.searchOptions$, this.currentPage]).pipe( switchMap(([options, page]: [SearchOptions, number]) => this.searchService.getFacetValuesFor(this.filterConfig, page, options, null, useCachedVersionIfAvailable).pipe( @@ -309,16 +273,6 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy { ); } - /** - * Transforms the facet value string, so if the query matches part of the value, it's emphasized in the value - * @param {FacetValue} facet The value of the facet as returned by the server - * @param {string} query The query that was used to search facet values - * @returns {string} The facet value with the query part emphasized - */ - getDisplayValue(facet: FacetValue, query: string): string { - return new EmphasizePipe().transform(facet.value, query) + ' (' + facet.count + ')'; - } - /** * Prevent unnecessary rerendering */ diff --git a/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.html b/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.html index 1875cc8076a..37ddeb081b6 100644 --- a/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.html +++ b/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.html @@ -17,7 +17,7 @@ - - Date: Fri, 12 Apr 2024 15:51:45 +0200 Subject: [PATCH 03/24] 111731: Integrated the FilterInputSuggestionsComponent in the advanced search facets to show input suggestions (only works for filters that are also part of the facets) --- .../filter-input-suggestions.component.html | 2 +- .../advanced-search.component.html | 12 +++++++ .../advanced-search.component.scss | 5 +++ .../advanced-search.component.spec.ts | 10 ++++++ .../advanced-search.component.ts | 31 ++++++++++++++++++- .../search-sidebar.component.html | 3 +- src/assets/i18n/en.json5 | 2 ++ 7 files changed, 62 insertions(+), 3 deletions(-) diff --git a/src/app/shared/input-suggestions/filter-suggestions/filter-input-suggestions.component.html b/src/app/shared/input-suggestions/filter-suggestions/filter-input-suggestions.component.html index 726dc9ca0e6..1d6bf64c0b0 100644 --- a/src/app/shared/input-suggestions/filter-suggestions/filter-input-suggestions.component.html +++ b/src/app/shared/input-suggestions/filter-suggestions/filter-input-suggestions.component.html @@ -4,7 +4,7 @@ (keydown.arrowup)="shiftFocusUp($event)" (keydown.esc)="close()" (dsClickOutside)="close();">
-
diff --git a/src/app/shared/search/advanced-search/advanced-search.component.scss b/src/app/shared/search/advanced-search/advanced-search.component.scss index e69de29bb2d..09c5a9fa9da 100644 --- a/src/app/shared/search/advanced-search/advanced-search.component.scss +++ b/src/app/shared/search/advanced-search/advanced-search.component.scss @@ -0,0 +1,5 @@ +:host ::ng-deep { + ds-filter-input-suggestions label span { + display: none; + } +} diff --git a/src/app/shared/search/advanced-search/advanced-search.component.spec.ts b/src/app/shared/search/advanced-search/advanced-search.component.spec.ts index a8f4811d283..cbda0cf3c06 100644 --- a/src/app/shared/search/advanced-search/advanced-search.component.spec.ts +++ b/src/app/shared/search/advanced-search/advanced-search.component.spec.ts @@ -1,17 +1,25 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { AdvancedSearchComponent } from './advanced-search.component'; +import { Router } from '@angular/router'; +import { RouterStub } from '../../testing/router.stub'; import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service'; +import { SearchFilterService } from '../../../core/shared/search/search-filter.service'; import { SearchConfigurationServiceStub } from '../../testing/search-configuration-service.stub'; +import { SearchFilterServiceStub } from '../../testing/search-filter-service.stub'; import { TranslateModule } from '@ngx-translate/core'; describe('AdvancedSearchComponent', () => { let component: AdvancedSearchComponent; let fixture: ComponentFixture; + let router: RouterStub; let searchConfigurationService: SearchConfigurationServiceStub; + let searchFilterService: SearchFilterServiceStub; beforeEach(async () => { + router = new RouterStub(); searchConfigurationService = new SearchConfigurationServiceStub(); + searchFilterService = new SearchFilterServiceStub(); await TestBed.configureTestingModule({ imports: [ @@ -21,7 +29,9 @@ describe('AdvancedSearchComponent', () => { AdvancedSearchComponent, ], providers: [ + { provide: Router, useValue: router }, { provide: SearchConfigurationService, useValue: searchConfigurationService }, + { provide: SearchFilterService, useValue: searchFilterService }, ], }).compileComponents(); diff --git a/src/app/shared/search/advanced-search/advanced-search.component.ts b/src/app/shared/search/advanced-search/advanced-search.component.ts index 6607cef6e4d..220649fb035 100644 --- a/src/app/shared/search/advanced-search/advanced-search.component.ts +++ b/src/app/shared/search/advanced-search/advanced-search.component.ts @@ -1,7 +1,12 @@ import { Component, Input, OnInit } from '@angular/core'; -import { map, Observable } from 'rxjs'; +import { map, Observable, of as observableOf } from 'rxjs'; import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service'; import { FilterConfig, SearchConfig } from '../../../core/shared/search/search-filters/search-config.model'; +import { SearchFilterService } from '../../../core/shared/search/search-filter.service'; +import { SearchFilterConfig } from '../models/search-filter-config.model'; +import { Router } from '@angular/router'; +import { InputSuggestion } from '../../input-suggestions/input-suggestions.model'; +import { hasValue } from '../../empty.util'; @Component({ selector: 'ds-advanced-search', @@ -12,6 +17,8 @@ export class AdvancedSearchComponent implements OnInit { @Input() configuration: string; + @Input() filtersConfig: SearchFilterConfig[]; + advancedFilters$: Observable; advancedFilterMap$: Observable>; @@ -20,8 +27,20 @@ export class AdvancedSearchComponent implements OnInit { currentOperator: string; + /** + * The value of the input field that is used to query for possible values for this filter + */ + currentValue = ''; + + /** + * Emits the result values for this filter found by the current filter query + */ + filterSearchResults$: Observable = observableOf([]); + constructor( + protected router: Router, protected searchConfigurationService: SearchConfigurationService, + protected searchFilterService: SearchFilterService, ) { } @@ -44,4 +63,14 @@ export class AdvancedSearchComponent implements OnInit { ); } + findSuggestions(query: string): void { + if (hasValue(this.filtersConfig)) { + for (const filterConfig of this.filtersConfig) { + if (filterConfig.name === this.currentFilter) { + this.filterSearchResults$ = this.searchFilterService.findSuggestions(filterConfig, this.searchConfigurationService.searchOptions.value, query); + } + } + } + } + } diff --git a/src/app/shared/search/search-sidebar/search-sidebar.component.html b/src/app/shared/search/search-sidebar/search-sidebar.component.html index 4a9c810a6f0..ad1c5745976 100644 --- a/src/app/shared/search/search-sidebar/search-sidebar.component.html +++ b/src/app/shared/search/search-sidebar/search-sidebar.component.html @@ -24,7 +24,8 @@ [inPlaceSearch]="inPlaceSearch" (changeAppliedFilters)="changeAppliedFilters.emit($event)"> - + diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 83feee8f83f..2fcc7c9e2a6 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -3898,6 +3898,8 @@ "search.sidebar.advanced-search.filter-by": "Filter by", + "search.sidebar.advanced-search.add": "Add", + "search.sidebar.settings.title": "Settings", "search.view-switch.show-detail": "Show detail", From 3cdf42659497e23f5ca2ed883d60affd93084741 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Fri, 12 Apr 2024 20:09:04 +0200 Subject: [PATCH 04/24] 111731: Fixed search filter input suggestions not resetting the page number by moving that logic to the search configuration service --- .../search-configuration.service.spec.ts | 49 ++++++++++++++++++- .../search/search-configuration.service.ts | 16 ++++-- .../search-facet-option.component.spec.ts | 21 +------- .../search-facet-option.component.ts | 8 +-- ...ch-facet-selected-option.component.spec.ts | 20 ++------ .../search-facet-selected-option.component.ts | 9 +--- .../search-facet-filter.component.spec.ts | 40 ++++++--------- .../search-facet-filter.component.ts | 32 +++++------- .../search-label-range.component.spec.ts | 15 +----- .../search-label-range.component.ts | 9 +--- .../search-label.component.spec.ts | 15 +----- .../search-label/search-label.component.ts | 9 +--- 12 files changed, 101 insertions(+), 142 deletions(-) diff --git a/src/app/core/shared/search/search-configuration.service.spec.ts b/src/app/core/shared/search/search-configuration.service.spec.ts index c13ada13b4e..cdb3247549a 100644 --- a/src/app/core/shared/search/search-configuration.service.spec.ts +++ b/src/app/core/shared/search/search-configuration.service.spec.ts @@ -1,4 +1,5 @@ /* eslint-disable no-empty, @typescript-eslint/no-empty-function */ +import { Params } from '@angular/router'; import { SearchConfigurationService } from './search-configuration.service'; import { ActivatedRouteStub } from '../../../shared/testing/active-router.stub'; import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; @@ -41,9 +42,10 @@ describe('SearchConfigurationService', () => { getQueryParamsWithPrefix: observableOf(prefixFilter), getRouteParameterValue: observableOf(''), getParamsExceptValue: observableOf({}), + getParamsWithAdditionalValue: observableOf({}), }); - const paginationService = new PaginationServiceStub(); + let paginationService: PaginationServiceStub; const activatedRoute: ActivatedRouteStub = new ActivatedRouteStub(); @@ -72,6 +74,12 @@ describe('SearchConfigurationService', () => { } }; beforeEach(() => { + paginationService = new PaginationServiceStub(Object.assign(new PaginationComponentOptions(), { + id: defaults.pagination.id, + currentPage: 1, + pageSize: 20, + })); + service = new SearchConfigurationService(routeService, paginationService as any, activatedRoute as any, linkService, halService, requestService, rdb); }); @@ -305,5 +313,44 @@ describe('SearchConfigurationService', () => { expect(routeService.getParamsExceptValue).toHaveBeenCalledWith('f.dateIssued.max', '2000'); }); + + it('should reset the page to 1', (done: DoneFn) => { + service.unselectAppliedFilterParams('dateIssued.max', '2000').subscribe((params: Params) => { + expect(params[`${defaults.pagination.id}.page`]).toBe(1); + done(); + }); + }); + }); + + describe('selectNewAppliedFilterParams', () => { + let appliedFilter: AppliedFilter; + + beforeEach(() => { + appliedFilter = Object.assign(new AppliedFilter(), { + filter: 'author', + operator: 'authority', + value: '1282121b-5394-4689-ab93-78d537764052', + label: 'Odinson, Thor', + }); + }); + + it('should return all params with the applied filter', () => { + service.selectNewAppliedFilterParams(appliedFilter.filter, appliedFilter.value, appliedFilter.operator); + + expect(routeService.getParamsWithAdditionalValue).toHaveBeenCalledWith('f.author', '1282121b-5394-4689-ab93-78d537764052,authority'); + }); + + it('should be able to add AppliedFilter without operator', () => { + service.selectNewAppliedFilterParams('dateIssued.max', '2000'); + + expect(routeService.getParamsWithAdditionalValue).toHaveBeenCalledWith('f.dateIssued.max', '2000'); + }); + + it('should reset the page to 1', (done: DoneFn) => { + service.selectNewAppliedFilterParams('dateIssued.max', '2000').subscribe((params: Params) => { + expect(params[`${defaults.pagination.id}.page`]).toBe(1); + done(); + }); + }); }); }); diff --git a/src/app/core/shared/search/search-configuration.service.ts b/src/app/core/shared/search/search-configuration.service.ts index eb88c9e7345..968e41b1cde 100644 --- a/src/app/core/shared/search/search-configuration.service.ts +++ b/src/app/core/shared/search/search-configuration.service.ts @@ -527,25 +527,33 @@ export class SearchConfigurationService implements OnDestroy { } /** - * Calculates the {@link Params} of the search after removing a filter with a certain value + * Calculates the {@link Params} of the search after removing a filter with a certain value and resets the page number. * * @param filterName The {@link AppliedFilter}'s name * @param value The {@link AppliedFilter}'s value * @param operator The {@link AppliedFilter}'s optional operator */ unselectAppliedFilterParams(filterName: string, value: string, operator?: string): Observable { - return this.routeService.getParamsExceptValue(`f.${filterName}`, hasValue(operator) ? addOperatorToFilterValue(value, operator) : value); + return this.routeService.getParamsExceptValue(`f.${filterName}`, hasValue(operator) ? addOperatorToFilterValue(value, operator) : value).pipe( + map((params: Params) => Object.assign(params, { + [this.paginationService.getPageParam(this.paginationID)]: 1, + })), + ); } /** - * Calculates the {@link Params} of the search after removing a filter with a certain value + * Calculates the {@link Params} of the search after adding a filter with a certain value and resets the page number. * * @param filterName The {@link AppliedFilter}'s name * @param value The {@link AppliedFilter}'s value * @param operator The {@link AppliedFilter}'s optional operator */ selectNewAppliedFilterParams(filterName: string, value: string, operator?: string): Observable { - return this.routeService.getParamsWithAdditionalValue(`f.${filterName}`, hasValue(operator) ? addOperatorToFilterValue(value, operator) : value); + return this.routeService.getParamsWithAdditionalValue(`f.${filterName}`, hasValue(operator) ? addOperatorToFilterValue(value, operator) : value).pipe( + map((params: Params) => Object.assign(params, { + [this.paginationService.getPageParam(this.paginationID)]: 1, + })), + ); } /** diff --git a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.spec.ts b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.spec.ts index 83f3a6e77c5..de56d8ce895 100644 --- a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.spec.ts +++ b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.spec.ts @@ -3,9 +3,9 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { FormsModule } from '@angular/forms'; import { By } from '@angular/platform-browser'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { Router, Params } from '@angular/router'; +import { Router } from '@angular/router'; import { TranslateModule } from '@ngx-translate/core'; -import { of as observableOf, take } from 'rxjs'; +import { of as observableOf } from 'rxjs'; import { SearchConfigurationService } from '../../../../../../core/shared/search/search-configuration.service'; import { SearchFilterService } from '../../../../../../core/shared/search/search-filter.service'; import { SearchService } from '../../../../../../core/shared/search/search.service'; @@ -87,23 +87,6 @@ describe('SearchFacetOptionComponent', () => { fixture.detectChanges(); }); - describe('updateAddParams', () => { - it('should always reset the page to 1', (done: DoneFn) => { - spyOn(searchConfigurationService, 'selectNewAppliedFilterParams').and.returnValue(observableOf({ - [mockFilterConfig.paramName]: [`${facetValue.value},equals`], - ['test-id.page']: 5, - })); - - comp.updateAddParams().pipe(take(1)).subscribe((params: Params) => { - expect(params).toEqual({ - [mockFilterConfig.paramName]: [`${facetValue.value},equals`], - ['test-id.page']: 1, - }); - done(); - }); - }); - }); - describe('when isVisible emits true', () => { it('the facet option should be visible', () => { comp.isVisible = observableOf(true); diff --git a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.ts b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.ts index db3e729b894..ea780be41b8 100644 --- a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.ts +++ b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-option/search-facet-option.component.ts @@ -92,13 +92,7 @@ export class SearchFacetOptionComponent implements OnInit { * Calculates the parameters that should change if this {@link filterValue} would be added to the active filters */ updateAddParams(): Observable { - const page: string = this.paginationService.getPageParam(this.searchConfigService.paginationID); - return this.searchConfigService.selectNewAppliedFilterParams(this.filterConfig.name, this.getFacetValue()).pipe( - map((params: Params) => ({ - ...params, - [page]: 1, - })), - ); + return this.searchConfigService.selectNewAppliedFilterParams(this.filterConfig.name, this.getFacetValue()); } /** diff --git a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.spec.ts b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.spec.ts index 2cf7b934901..0ef804d12bd 100644 --- a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.spec.ts +++ b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.spec.ts @@ -2,9 +2,8 @@ import { NO_ERRORS_SCHEMA } from '@angular/core'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { FormsModule } from '@angular/forms'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { Params, Router } from '@angular/router'; +import { Router } from '@angular/router'; import { TranslateModule } from '@ngx-translate/core'; -import { of as observableOf, take } from 'rxjs'; import { SearchConfigurationService } from '../../../../../../core/shared/search/search-configuration.service'; import { SearchFilterService } from '../../../../../../core/shared/search/search-filter.service'; import { SearchService } from '../../../../../../core/shared/search/search.service'; @@ -81,20 +80,7 @@ describe('SearchFacetSelectedOptionComponent', () => { fixture.detectChanges(); }); - describe('updateRemoveParams', () => { - it('should always reset the page to 1', (done: DoneFn) => { - spyOn(searchConfigurationService, 'unselectAppliedFilterParams').and.returnValue(observableOf({ - [mockFilterConfig.paramName]: [`${value1},equals`], - ['page-id.page']: 5, - })); - - comp.updateRemoveParams().pipe(take(1)).subscribe((params: Params) => { - expect(params).toEqual({ - [mockFilterConfig.paramName]: [`${value1},equals`], - ['page-id.page']: 1 - }); - done(); - }); - }); + it('should create', () => { + expect(comp).toBeTruthy(); }); }); diff --git a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.ts b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.ts index 43f5f4c4ddc..fa0d8691746 100644 --- a/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.ts +++ b/src/app/shared/search/search-filters/search-filter/search-facet-filter-options/search-facet-selected-option/search-facet-selected-option.component.ts @@ -6,7 +6,6 @@ import { SearchService } from '../../../../../../core/shared/search/search.servi import { SearchConfigurationService } from '../../../../../../core/shared/search/search-configuration.service'; import { currentPath } from '../../../../../utils/route.utils'; import { AppliedFilter } from '../../../../models/applied-filter.model'; -import { map } from 'rxjs/operators'; import { PaginationService } from '../../../../../../core/pagination/pagination.service'; @Component({ @@ -64,13 +63,7 @@ export class SearchFacetSelectedOptionComponent implements OnInit { * Calculates the parameters that should change if this {@link selectedValue} would be removed from the active filters */ updateRemoveParams(): Observable { - const page: string = this.paginationService.getPageParam(this.searchConfigService.paginationID); - return this.searchConfigService.unselectAppliedFilterParams(this.selectedValue.filter, this.selectedValue.value, this.selectedValue.operator).pipe( - map((params: Params) => ({ - ...params, - [page]: 1, - })), - ); + return this.searchConfigService.unselectAppliedFilterParams(this.selectedValue.filter, this.selectedValue.value, this.selectedValue.operator); } /** diff --git a/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.spec.ts b/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.spec.ts index fa6b4538f65..343f0de6c73 100644 --- a/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.spec.ts +++ b/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.spec.ts @@ -24,6 +24,7 @@ import { SEARCH_CONFIG_SERVICE } from '../../../../../my-dspace-page/my-dspace-p import { createSuccessfulRemoteDataObject$ } from '../../../../remote-data.utils'; import { AppliedFilter } from '../../../models/applied-filter.model'; import { FacetValues } from '../../../models/facet-values.model'; +import { SearchFilterServiceStub } from '../../../../testing/search-filter-service.stub'; describe('SearchFacetFilterComponent', () => { let comp: SearchFacetFilterComponent; @@ -64,37 +65,30 @@ describe('SearchFacetFilterComponent', () => { const searchLink = '/search'; const selectedValues = [value1, value2]; - let filterService; - let searchService; - let router; - const page = observableOf(0); + let filterService: SearchFilterServiceStub; + let searchService: SearchServiceStub; + let router: RouterStub; + let searchConfigService: SearchConfigurationServiceStub; beforeEach(waitForAsync(() => { + searchService = new SearchServiceStub(searchLink); + filterService = new SearchFilterServiceStub(); + router = new RouterStub(); + searchConfigService = new SearchConfigurationServiceStub(); + TestBed.configureTestingModule({ imports: [TranslateModule.forRoot(), NoopAnimationsModule, FormsModule], declarations: [SearchFacetFilterComponent], providers: [ - { provide: SearchService, useValue: new SearchServiceStub(searchLink) }, - { provide: Router, useValue: new RouterStub() }, + { provide: SearchService, useValue: searchService }, + { provide: SearchFilterService, useValue: filterService }, + { provide: Router, useValue: router }, { provide: FILTER_CONFIG, useValue: new SearchFilterConfig() }, { provide: RemoteDataBuildService, useValue: { aggregate: () => observableOf({}) } }, - { provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() }, + { provide: SEARCH_CONFIG_SERVICE, useValue: searchConfigService }, { provide: IN_PLACE_SEARCH, useValue: false }, { provide: REFRESH_FILTER, useValue: new BehaviorSubject(false) }, { provide: CHANGE_APPLIED_FILTERS, useValue: new EventEmitter() }, - { - provide: SearchFilterService, useValue: { - getSelectedValuesForFilter: () => observableOf(selectedValues), - isFilterActiveWithValue: (paramName: string, filterValue: string) => true, - getPage: (paramName: string) => page, - /* eslint-disable no-empty,@typescript-eslint/no-empty-function */ - incrementPage: (filterName: string) => { - }, - resetPage: (filterName: string) => { - } - /* eslint-enable no-empty, @typescript-eslint/no-empty-function */ - } - } ], schemas: [NO_ERRORS_SCHEMA] }).overrideComponent(SearchFacetFilterComponent, { @@ -106,10 +100,7 @@ describe('SearchFacetFilterComponent', () => { fixture = TestBed.createComponent(SearchFacetFilterComponent); comp = fixture.componentInstance; // SearchPageComponent test instance comp.filterConfig = mockFilterConfig; - filterService = (comp as any).filterService; - searchService = (comp as any).searchService; spyOn(searchService, 'getFacetValuesFor').and.returnValue(createSuccessfulRemoteDataObject$(values)); - router = (comp as any).router; fixture.detectChanges(); }); @@ -182,13 +173,14 @@ describe('SearchFacetFilterComponent', () => { }))); fixture.detectChanges(); spyOn(comp, 'getSearchLink').and.returnValue(searchUrl); + spyOn(searchConfigService, 'selectNewAppliedFilterParams').and.returnValue(observableOf({ [mockFilterConfig.paramName]: [...selectedValues.map((value) => `${value},equals`), `${testValue},equals`] })); }); it('should call navigate on the router with the right searchlink and parameters when the filter is provided with a valid operator', () => { comp.onSubmit(testValue + ',equals'); + expect(searchConfigService.selectNewAppliedFilterParams).toHaveBeenCalledWith(filterName1, testValue, 'equals'); expect(router.navigate).toHaveBeenCalledWith(searchUrl.split('/'), { queryParams: { [mockFilterConfig.paramName]: [...selectedValues.map((value) => `${value},equals`), `${testValue},equals`] }, - queryParamsHandling: 'merge' }); }); diff --git a/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts b/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts index c9115a97038..381d194917c 100644 --- a/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts +++ b/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts @@ -1,12 +1,12 @@ import { animate, state, style, transition, trigger } from '@angular/animations'; import { Component, Inject, OnDestroy, OnInit, EventEmitter } from '@angular/core'; -import { Router } from '@angular/router'; +import { Router, Params } from '@angular/router'; import { BehaviorSubject, combineLatest as observableCombineLatest, Observable, of as observableOf, Subscription } from 'rxjs'; import { debounceTime, distinctUntilChanged, filter, map, mergeMap, switchMap, take, tap } from 'rxjs/operators'; import { RemoteDataBuildService } from '../../../../../core/cache/builders/remote-data-build.service'; -import { hasNoValue, hasValue, isNotEmpty } from '../../../../empty.util'; +import { hasNoValue, hasValue } from '../../../../empty.util'; import { FacetValue } from '../../../models/facet-value.model'; import { SearchFilterConfig } from '../../../models/search-filter-config.model'; import { SearchService } from '../../../../../core/shared/search/search.service'; @@ -17,7 +17,7 @@ import { InputSuggestion } from '../../../../input-suggestions/input-suggestions import { SearchOptions } from '../../../models/search-options.model'; import { SEARCH_CONFIG_SERVICE } from '../../../../../my-dspace-page/my-dspace-page.component'; import { currentPath } from '../../../../utils/route.utils'; -import { stripOperatorFromFilterValue, addOperatorToFilterValue } from '../../../search.utils'; +import { stripOperatorFromFilterValue } from '../../../search.utils'; import { FacetValues } from '../../../models/facet-values.model'; import { AppliedFilter } from '../../../models/applied-filter.model'; @@ -203,26 +203,18 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy { /** * Build the filter query using the value given and apply to the search. - * @param data The string from the input field + * @param data The string from the input field (containing operator) */ - protected applyFilterValue(data) { + protected applyFilterValue(data: string): void { if (data.match(new RegExp(`^.+,(equals|query|authority)$`))) { - this.selectedAppliedFilters$.pipe(take(1)).subscribe((selectedValues: AppliedFilter[]) => { - if (isNotEmpty(data)) { - void this.router.navigate(this.getSearchLinkParts(), { - queryParams: - { - [this.filterConfig.paramName]: [ - ...selectedValues.map((appliedFilter: AppliedFilter) => addOperatorToFilterValue(appliedFilter.value, appliedFilter.operator)), - data - ] - }, - queryParamsHandling: 'merge' - }); - this.filter = ''; - } + const valueParts = data.split(','); + this.subs.push(this.searchConfigService.selectNewAppliedFilterParams(this.filterConfig.name, valueParts.slice(0, valueParts.length - 1).join(), valueParts[valueParts.length - 1]).pipe(take(1)).subscribe((params: Params) => { + void this.router.navigate(this.getSearchLinkParts(), { + queryParams: params, + }); + this.filter = ''; this.filterSearchResults$ = observableOf([]); - }); + })); } } diff --git a/src/app/shared/search/search-labels/search-label-range/search-label-range.component.spec.ts b/src/app/shared/search/search-labels/search-label-range/search-label-range.component.spec.ts index 658bf46e514..ca835f2bbff 100644 --- a/src/app/shared/search/search-labels/search-label-range/search-label-range.component.spec.ts +++ b/src/app/shared/search/search-labels/search-label-range/search-label-range.component.spec.ts @@ -12,8 +12,6 @@ import { SearchConfigurationService } from '../../../../core/shared/search/searc import { SearchConfigurationServiceStub } from '../../../testing/search-configuration-service.stub'; import { PaginationService } from '../../../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../../testing/pagination-service.stub'; -import { take } from 'rxjs/operators'; -import { of as observableOf } from 'rxjs'; import { PaginationComponentOptions } from '../../../pagination/pagination-component-options.model'; describe('SearchLabelRangeComponent', () => { @@ -79,16 +77,7 @@ describe('SearchLabelRangeComponent', () => { fixture.detectChanges(); }); - describe('updateRemoveParams', () => { - it('should always reset the page to 1', (done: DoneFn) => { - spyOn(searchConfigurationService, 'unselectAppliedFilterParams').and.returnValue(observableOf(initialRouteParams)); - - comp.updateRemoveParams('f.dateIssued.max', '2000').pipe(take(1)).subscribe((params: Params) => { - expect(params).toEqual(Object.assign({}, initialRouteParams, { - 'page-id.page': 1, - })); - done(); - }); - }); + it('should create', () => { + expect(comp).toBeTruthy(); }); }); diff --git a/src/app/shared/search/search-labels/search-label-range/search-label-range.component.ts b/src/app/shared/search/search-labels/search-label-range/search-label-range.component.ts index 838a42cbfec..593dc2f9e19 100644 --- a/src/app/shared/search/search-labels/search-label-range/search-label-range.component.ts +++ b/src/app/shared/search/search-labels/search-label-range/search-label-range.component.ts @@ -6,7 +6,6 @@ import { currentPath } from '../../../utils/route.utils'; import { AppliedFilter } from '../../models/applied-filter.model'; import { renderSearchLabelFor } from '../search-label-loader/search-label-loader.decorator'; import { SearchConfigurationService } from '../../../../core/shared/search/search-configuration.service'; -import { map } from 'rxjs/operators'; import { PaginationService } from '../../../../core/pagination/pagination.service'; /** @@ -57,13 +56,7 @@ export class SearchLabelRangeComponent implements OnInit { * @param operator The {@link AppliedFilter}'s optional operator */ updateRemoveParams(filterName: string, value: string, operator?: string): Observable { - const page: string = this.paginationService.getPageParam(this.searchConfigurationService.paginationID); - return this.searchConfigurationService.unselectAppliedFilterParams(filterName, value, operator).pipe( - map((params: Params) => ({ - ...params, - [page]: 1, - })), - ); + return this.searchConfigurationService.unselectAppliedFilterParams(filterName, value, operator); } /** diff --git a/src/app/shared/search/search-labels/search-label/search-label.component.spec.ts b/src/app/shared/search/search-labels/search-label/search-label.component.spec.ts index 96e4e9823b3..657f9ae4a1a 100644 --- a/src/app/shared/search/search-labels/search-label/search-label.component.spec.ts +++ b/src/app/shared/search/search-labels/search-label/search-label.component.spec.ts @@ -13,8 +13,6 @@ import { SearchConfigurationServiceStub } from '../../../testing/search-configur import { PaginationService } from '../../../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../../testing/pagination-service.stub'; import { PaginationComponentOptions } from '../../../pagination/pagination-component-options.model'; -import { of as observableOf } from 'rxjs'; -import { take } from 'rxjs/operators'; describe('SearchLabelComponent', () => { let comp: SearchLabelComponent; @@ -79,16 +77,7 @@ describe('SearchLabelComponent', () => { fixture.detectChanges(); }); - describe('updateRemoveParams', () => { - it('should always reset the page to 1', (done: DoneFn) => { - spyOn(searchConfigurationService, 'unselectAppliedFilterParams').and.returnValue(observableOf(initialRouteParams)); - - comp.updateRemoveParams().pipe(take(1)).subscribe((params: Params) => { - expect(params).toEqual(Object.assign({}, initialRouteParams, { - 'page-id.page': 1, - })); - done(); - }); - }); + it('should create', () => { + expect(comp).toBeTruthy(); }); }); diff --git a/src/app/shared/search/search-labels/search-label/search-label.component.ts b/src/app/shared/search/search-labels/search-label/search-label.component.ts index 524296b1d54..5563d124e88 100644 --- a/src/app/shared/search/search-labels/search-label/search-label.component.ts +++ b/src/app/shared/search/search-labels/search-label/search-label.component.ts @@ -6,7 +6,6 @@ import { currentPath } from '../../../utils/route.utils'; import { AppliedFilter } from '../../models/applied-filter.model'; import { SearchConfigurationService } from '../../../../core/shared/search/search-configuration.service'; import { renderSearchLabelFor } from '../search-label-loader/search-label-loader.decorator'; -import { map } from 'rxjs/operators'; import { PaginationService } from '../../../../core/pagination/pagination.service'; /** @@ -49,13 +48,7 @@ export class SearchLabelComponent implements OnInit { * Calculates the parameters that should change if this {@link appliedFilter} would be removed from the active filters */ updateRemoveParams(): Observable { - const page: string = this.paginationService.getPageParam(this.searchConfigurationService.paginationID); - return this.searchConfigurationService.unselectAppliedFilterParams(this.appliedFilter.filter, this.appliedFilter.value, this.appliedFilter.operator).pipe( - map((params: Params) => ({ - ...params, - [page]: 1, - })), - ); + return this.searchConfigurationService.unselectAppliedFilterParams(this.appliedFilter.filter, this.appliedFilter.value, this.appliedFilter.operator); } /** From 7eb26582db7643ca79d6a36c7011ab52eb99b504 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Fri, 12 Apr 2024 22:23:14 +0200 Subject: [PATCH 05/24] 111731: Added apply filter functionality --- .../advanced-search.component.html | 7 +++-- .../advanced-search.component.spec.ts | 5 ++++ .../advanced-search.component.ts | 30 +++++++++++++++---- 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/src/app/shared/search/advanced-search/advanced-search.component.html b/src/app/shared/search/advanced-search/advanced-search.component.html index f6948b455ae..a3e5db61388 100644 --- a/src/app/shared/search/advanced-search/advanced-search.component.html +++ b/src/app/shared/search/advanced-search/advanced-search.component.html @@ -1,12 +1,12 @@

{{ 'search.sidebar.advanced-search.title' | translate }}

diff --git a/src/app/shared/search/advanced-search/advanced-search.component.spec.ts b/src/app/shared/search/advanced-search/advanced-search.component.spec.ts index cbda0cf3c06..3b80c6abeec 100644 --- a/src/app/shared/search/advanced-search/advanced-search.component.spec.ts +++ b/src/app/shared/search/advanced-search/advanced-search.component.spec.ts @@ -2,8 +2,10 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { AdvancedSearchComponent } from './advanced-search.component'; import { Router } from '@angular/router'; import { RouterStub } from '../../testing/router.stub'; +import { SearchService } from '../../../core/shared/search/search.service'; import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service'; import { SearchFilterService } from '../../../core/shared/search/search-filter.service'; +import { SearchServiceStub } from '../../testing/search-service.stub'; import { SearchConfigurationServiceStub } from '../../testing/search-configuration-service.stub'; import { SearchFilterServiceStub } from '../../testing/search-filter-service.stub'; import { TranslateModule } from '@ngx-translate/core'; @@ -13,11 +15,13 @@ describe('AdvancedSearchComponent', () => { let fixture: ComponentFixture; let router: RouterStub; + let searchService: SearchServiceStub; let searchConfigurationService: SearchConfigurationServiceStub; let searchFilterService: SearchFilterServiceStub; beforeEach(async () => { router = new RouterStub(); + searchService = new SearchServiceStub(); searchConfigurationService = new SearchConfigurationServiceStub(); searchFilterService = new SearchFilterServiceStub(); @@ -30,6 +34,7 @@ describe('AdvancedSearchComponent', () => { ], providers: [ { provide: Router, useValue: router }, + { provide: SearchService, useValue: searchService }, { provide: SearchConfigurationService, useValue: searchConfigurationService }, { provide: SearchFilterService, useValue: searchFilterService }, ], diff --git a/src/app/shared/search/advanced-search/advanced-search.component.ts b/src/app/shared/search/advanced-search/advanced-search.component.ts index 220649fb035..2864ba02eb1 100644 --- a/src/app/shared/search/advanced-search/advanced-search.component.ts +++ b/src/app/shared/search/advanced-search/advanced-search.component.ts @@ -1,19 +1,21 @@ -import { Component, Input, OnInit } from '@angular/core'; -import { map, Observable, of as observableOf } from 'rxjs'; +import { Component, Input, OnInit, OnDestroy } from '@angular/core'; +import { map, Observable, of as observableOf, Subscription } from 'rxjs'; +import { take } from 'rxjs/operators'; import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service'; import { FilterConfig, SearchConfig } from '../../../core/shared/search/search-filters/search-config.model'; import { SearchFilterService } from '../../../core/shared/search/search-filter.service'; import { SearchFilterConfig } from '../models/search-filter-config.model'; -import { Router } from '@angular/router'; +import { Router, Params } from '@angular/router'; import { InputSuggestion } from '../../input-suggestions/input-suggestions.model'; -import { hasValue } from '../../empty.util'; +import { hasValue, isNotEmpty } from '../../empty.util'; +import { SearchService } from '../../../core/shared/search/search.service'; @Component({ selector: 'ds-advanced-search', templateUrl: './advanced-search.component.html', styleUrls: ['./advanced-search.component.scss'], }) -export class AdvancedSearchComponent implements OnInit { +export class AdvancedSearchComponent implements OnInit, OnDestroy { @Input() configuration: string; @@ -37,8 +39,11 @@ export class AdvancedSearchComponent implements OnInit { */ filterSearchResults$: Observable = observableOf([]); + subs: Subscription[] = []; + constructor( protected router: Router, + protected searchService: SearchService, protected searchConfigurationService: SearchConfigurationService, protected searchFilterService: SearchFilterService, ) { @@ -63,6 +68,10 @@ export class AdvancedSearchComponent implements OnInit { ); } + ngOnDestroy(): void { + this.subs.forEach((sub: Subscription) => sub.unsubscribe()); + } + findSuggestions(query: string): void { if (hasValue(this.filtersConfig)) { for (const filterConfig of this.filtersConfig) { @@ -73,4 +82,15 @@ export class AdvancedSearchComponent implements OnInit { } } + applyFilter(): void { + if (isNotEmpty(this.currentValue)) { + this.subs.push(this.searchConfigurationService.selectNewAppliedFilterParams(this.currentFilter, this.currentValue.trim(), this.currentOperator).pipe(take(1)).subscribe((params: Params) => { + void this.router.navigate([this.searchService.getSearchLink()], { + queryParams: params, + }); + this.currentValue = ''; + })); + } + } + } From 8d1d04ed5891f6c4182bb1a8a037370114c78b7b Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Sat, 13 Apr 2024 17:26:57 +0200 Subject: [PATCH 06/24] 111731: Added theme support to renderSearchLabelFor --- .../search-label-loader.component.ts | 9 +++--- .../search-label-loader.decorator.ts | 32 +++++++++++++++---- .../search-label/search-label.component.ts | 8 +---- 3 files changed, 30 insertions(+), 19 deletions(-) diff --git a/src/app/shared/search/search-labels/search-label-loader/search-label-loader.component.ts b/src/app/shared/search/search-labels/search-label-loader/search-label-loader.component.ts index 763dc1b0323..f4682ecee96 100644 --- a/src/app/shared/search/search-labels/search-label-loader/search-label-loader.component.ts +++ b/src/app/shared/search/search-labels/search-label-loader/search-label-loader.component.ts @@ -1,6 +1,5 @@ -import { Component, ComponentRef, OnChanges, OnDestroy, OnInit, ViewChild, ViewContainerRef, SimpleChanges, Input } from '@angular/core'; +import { Component, ComponentRef, OnChanges, OnDestroy, OnInit, ViewChild, ViewContainerRef, SimpleChanges, Input, Type } from '@angular/core'; import { Subscription } from 'rxjs'; -import { GenericConstructor } from 'src/app/core/shared/generic-constructor'; import { hasValue, isNotEmpty } from 'src/app/shared/empty.util'; import { ThemeService } from '../../../theme-support/theme.service'; import { SearchLabelLoaderDirective } from './search-label-loader-directive.directive'; @@ -85,7 +84,7 @@ export class SearchLabelLoaderComponent implements OnInit, OnChanges, OnDestroy * Creates the component and connects the @Input() & @Output() from the ThemedComponent to its child Component. */ public instantiateComponent(): void { - const component: GenericConstructor = this.getComponent(); + const component: Type = this.getComponent(); const viewContainerRef: ViewContainerRef = this.componentDirective.viewContainerRef; viewContainerRef.clear(); @@ -113,8 +112,8 @@ export class SearchLabelLoaderComponent implements OnInit, OnChanges, OnDestroy /** * Fetch the component depending on the item's entity type, metadata representation type and context */ - public getComponent(): GenericConstructor { - return getSearchLabelByOperator(this.appliedFilter.operator); + public getComponent(): Type { + return getSearchLabelByOperator(this.appliedFilter.operator, this.themeService.getThemeName()); } /** diff --git a/src/app/shared/search/search-labels/search-label-loader/search-label-loader.decorator.ts b/src/app/shared/search/search-labels/search-label-loader/search-label-loader.decorator.ts index ffd73af6ba1..5315d83479f 100644 --- a/src/app/shared/search/search-labels/search-label-loader/search-label-loader.decorator.ts +++ b/src/app/shared/search/search-labels/search-label-loader/search-label-loader.decorator.ts @@ -1,17 +1,35 @@ -import { Component } from '@angular/core'; -import { GenericConstructor } from '../../../../core/shared/generic-constructor'; +import { Component, Type } from '@angular/core'; +import { DEFAULT_THEME } from '../../../object-collection/shared/listable-object/listable-object.decorator'; +import { hasNoValue } from '../../../empty.util'; -export const map: Map> = new Map(); +export const DEFAULT_LABEL_OPERATOR = undefined; -export function renderSearchLabelFor(operator: string) { +const map: Map>> = new Map(); + +export function renderSearchLabelFor(operator: string = DEFAULT_LABEL_OPERATOR, theme = DEFAULT_THEME) { return function decorator(objectElement: any) { if (!objectElement) { return; } - map.set(operator, objectElement); + if (hasNoValue(map.get(operator))) { + map.set(operator, new Map()); + } + if (hasNoValue(map.get(operator).get(theme))) { + map.get(operator).set(theme, objectElement); + } else { + throw new Error(`There can't be more than one component to render Label with operator "${operator}" and theme "${theme}"`); + } }; } -export function getSearchLabelByOperator(operator: string): GenericConstructor { - return map.get(operator); +export function getSearchLabelByOperator(operator: string, theme: string): Type { + let themeMap: Map> = map.get(operator); + if (hasNoValue(themeMap)) { + themeMap = map.get(DEFAULT_LABEL_OPERATOR); + } + const comp: Type = themeMap.get(theme); + if (hasNoValue(comp)) { + return themeMap.get(DEFAULT_THEME); + } + return comp; } diff --git a/src/app/shared/search/search-labels/search-label/search-label.component.ts b/src/app/shared/search/search-labels/search-label/search-label.component.ts index 5563d124e88..ff891de12b0 100644 --- a/src/app/shared/search/search-labels/search-label/search-label.component.ts +++ b/src/app/shared/search/search-labels/search-label/search-label.component.ts @@ -15,13 +15,7 @@ import { PaginationService } from '../../../../core/pagination/pagination.servic selector: 'ds-search-label', templateUrl: './search-label.component.html', }) -@renderSearchLabelFor('equals') -@renderSearchLabelFor('notequals') -@renderSearchLabelFor('authority') -@renderSearchLabelFor('notauthority') -@renderSearchLabelFor('contains') -@renderSearchLabelFor('notcontains') -@renderSearchLabelFor('query') +@renderSearchLabelFor() export class SearchLabelComponent implements OnInit { @Input() inPlaceSearch: boolean; @Input() appliedFilter: AppliedFilter; From d40d0b7c9ccaec87673d87b782923453de7767c1 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Tue, 23 Apr 2024 17:46:08 +0200 Subject: [PATCH 07/24] 111731: Fixed selected filters not being displayed as labels --- .../shared/search/search-filter.service.ts | 4 +-- src/app/core/shared/search/search.service.ts | 21 +++++++++++-- .../search-facet-filter-wrapper.component.ts | 3 +- .../search-facet-filter.component.spec.ts | 4 +-- .../search-facet-filter.component.ts | 6 ++-- .../search-filter.component.html | 3 +- .../search-filter/search-filter.component.ts | 8 +---- .../search-hierarchy-filter.component.spec.ts | 2 -- .../search-hierarchy-filter.component.ts | 8 ++--- .../search-range-filter.component.spec.ts | 5 ++-- .../search-range-filter.component.ts | 7 ++--- .../search-filters.component.html | 2 +- .../search-filters.component.ts | 30 ++----------------- .../themed-search-filters.component.ts | 5 +--- .../search-labels.component.html | 10 +++---- .../search-labels/search-labels.component.ts | 20 +++++++++---- .../search-sidebar.component.html | 3 +- .../search-sidebar.component.ts | 6 ---- .../themed-search-sidebar.component.ts | 3 -- src/app/shared/search/search.component.html | 4 +-- src/app/shared/search/search.component.ts | 6 ---- 21 files changed, 56 insertions(+), 104 deletions(-) diff --git a/src/app/core/shared/search/search-filter.service.ts b/src/app/core/shared/search/search-filter.service.ts index 14892bc78e5..de09ef67e09 100644 --- a/src/app/core/shared/search/search-filter.service.ts +++ b/src/app/core/shared/search/search-filter.service.ts @@ -1,6 +1,6 @@ import { BehaviorSubject, combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs'; import { distinctUntilChanged, map } from 'rxjs/operators'; -import { Injectable, InjectionToken, EventEmitter } from '@angular/core'; +import { Injectable, InjectionToken } from '@angular/core'; import { SearchFiltersState, SearchFilterState @@ -21,7 +21,6 @@ import { SortDirection, SortOptions } from '../../cache/models/sort-options.mode import { RouteService } from '../../services/route.service'; import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; import { Params } from '@angular/router'; -import { AppliedFilter } from '../../../shared/search/models/applied-filter.model'; import { SearchOptions } from '../../../shared/search/models/search-options.model'; import { getFirstSucceededRemoteData } from '../operators'; import { FacetValue } from '../../../shared/search/models/facet-value.model'; @@ -37,7 +36,6 @@ const filterStateSelector = (state: SearchFiltersState) => state.searchFilter; export const FILTER_CONFIG: InjectionToken = new InjectionToken('filterConfig'); export const IN_PLACE_SEARCH: InjectionToken = new InjectionToken('inPlaceSearch'); export const REFRESH_FILTER: InjectionToken> = new InjectionToken('refreshFilters'); -export const CHANGE_APPLIED_FILTERS: InjectionToken> = new InjectionToken('changeAppliedFilters'); /** * Service that performs all actions that have to do with search filters and facets diff --git a/src/app/core/shared/search/search.service.ts b/src/app/core/shared/search/search.service.ts index a88a8b0d16b..0a69fbd667d 100644 --- a/src/app/core/shared/search/search.service.ts +++ b/src/app/core/shared/search/search.service.ts @@ -1,7 +1,7 @@ /* eslint-disable max-classes-per-file */ -import { combineLatest as observableCombineLatest, Observable } from 'rxjs'; +import { combineLatest as observableCombineLatest, Observable, BehaviorSubject } from 'rxjs'; import { Injectable, OnDestroy } from '@angular/core'; -import { map, switchMap, take } from 'rxjs/operators'; +import { map, switchMap, take, tap } from 'rxjs/operators'; import { FollowLinkConfig } from '../../../shared/utils/follow-link-config.model'; import { ResponseParsingService } from '../../data/parsing.service'; import { RemoteData } from '../../data/remote-data'; @@ -32,6 +32,7 @@ import { PaginationComponentOptions } from '../../../shared/pagination/paginatio import { RestRequest } from '../../data/rest-request.model'; import { BaseDataService } from '../../data/base/base-data.service'; import { Angulartics2 } from 'angulartics2'; +import { AppliedFilter } from '../../../shared/search/models/applied-filter.model'; /** * A limited data service implementation for the 'discover' endpoint @@ -87,6 +88,8 @@ export class SearchService implements OnDestroy { */ private searchDataService: SearchDataService; + public appliedFilters$: BehaviorSubject = new BehaviorSubject([]); + constructor( private routeService: RouteService, protected requestService: RequestService, @@ -293,7 +296,19 @@ export class SearchService implements OnDestroy { }); this.requestService.send(request, useCachedVersionIfAvailable); - return this.rdb.buildFromHref(href); + return this.rdb.buildFromHref(href).pipe( + tap((facetValuesRD: RemoteData) => { + if (facetValuesRD.hasSucceeded) { + const appliedFilters: AppliedFilter[] = (facetValuesRD.payload.appliedFilters ?? []) + .filter((appliedFilter: AppliedFilter) => hasValue(appliedFilter)) + // TODO this should ideally be fixed in the backend + .map((appliedFilter: AppliedFilter) => Object.assign({}, appliedFilter, { + operator: hasValue(appliedFilter.value.match(/\[\s*(\*|\d+)\s*TO\s*(\*|\d+)\s*]/)) ? 'range' : appliedFilter.operator, + })); + this.appliedFilters$.next(appliedFilters); + } + }), + ); } /** diff --git a/src/app/shared/search/search-filters/search-filter/search-facet-filter-wrapper/search-facet-filter-wrapper.component.ts b/src/app/shared/search/search-filters/search-filter/search-facet-filter-wrapper/search-facet-filter-wrapper.component.ts index 92add2ab7b1..e8cf0d693ed 100644 --- a/src/app/shared/search/search-filters/search-filter/search-facet-filter-wrapper/search-facet-filter-wrapper.component.ts +++ b/src/app/shared/search/search-filters/search-filter/search-facet-filter-wrapper/search-facet-filter-wrapper.component.ts @@ -5,7 +5,7 @@ import { SearchFilterConfig } from '../../../models/search-filter-config.model'; import { FILTER_CONFIG, IN_PLACE_SEARCH, - REFRESH_FILTER, CHANGE_APPLIED_FILTERS + REFRESH_FILTER, } from '../../../../../core/shared/search/search-filter.service'; import { GenericConstructor } from '../../../../../core/shared/generic-constructor'; import { SearchFacetFilterComponent } from '../search-facet-filter/search-facet-filter.component'; @@ -63,7 +63,6 @@ export class SearchFacetFilterWrapperComponent implements OnInit { { provide: FILTER_CONFIG, useFactory: () => (this.filterConfig), deps: [] }, { provide: IN_PLACE_SEARCH, useFactory: () => (this.inPlaceSearch), deps: [] }, { provide: REFRESH_FILTER, useFactory: () => (this.refreshFilters), deps: [] }, - { provide: CHANGE_APPLIED_FILTERS, useFactory: () => this.changeAppliedFilters }, ], parent: this.injector }); diff --git a/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.spec.ts b/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.spec.ts index 343f0de6c73..61c9f50c92c 100644 --- a/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.spec.ts +++ b/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.spec.ts @@ -1,9 +1,8 @@ -import { ChangeDetectionStrategy, EventEmitter, NO_ERRORS_SCHEMA } from '@angular/core'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { TranslateModule } from '@ngx-translate/core'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { - CHANGE_APPLIED_FILTERS, FILTER_CONFIG, IN_PLACE_SEARCH, REFRESH_FILTER, @@ -88,7 +87,6 @@ describe('SearchFacetFilterComponent', () => { { provide: SEARCH_CONFIG_SERVICE, useValue: searchConfigService }, { provide: IN_PLACE_SEARCH, useValue: false }, { provide: REFRESH_FILTER, useValue: new BehaviorSubject(false) }, - { provide: CHANGE_APPLIED_FILTERS, useValue: new EventEmitter() }, ], schemas: [NO_ERRORS_SCHEMA] }).overrideComponent(SearchFacetFilterComponent, { diff --git a/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts b/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts index 381d194917c..8af3aac7be9 100644 --- a/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts +++ b/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts @@ -1,5 +1,5 @@ import { animate, state, style, transition, trigger } from '@angular/animations'; -import { Component, Inject, OnDestroy, OnInit, EventEmitter } from '@angular/core'; +import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; import { Router, Params } from '@angular/router'; import { BehaviorSubject, combineLatest as observableCombineLatest, Observable, of as observableOf, Subscription } from 'rxjs'; @@ -10,7 +10,7 @@ import { hasNoValue, hasValue } from '../../../../empty.util'; import { FacetValue } from '../../../models/facet-value.model'; import { SearchFilterConfig } from '../../../models/search-filter-config.model'; import { SearchService } from '../../../../../core/shared/search/search.service'; -import { FILTER_CONFIG, IN_PLACE_SEARCH, REFRESH_FILTER, SearchFilterService, CHANGE_APPLIED_FILTERS } from '../../../../../core/shared/search/search-filter.service'; +import { FILTER_CONFIG, IN_PLACE_SEARCH, REFRESH_FILTER, SearchFilterService } from '../../../../../core/shared/search/search-filter.service'; import { SearchConfigurationService } from '../../../../../core/shared/search/search-configuration.service'; import { getFirstSucceededRemoteDataPayload } from '../../../../../core/shared/operators'; import { InputSuggestion } from '../../../../input-suggestions/input-suggestions.model'; @@ -90,7 +90,6 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy { @Inject(IN_PLACE_SEARCH) public inPlaceSearch: boolean, @Inject(FILTER_CONFIG) public filterConfig: SearchFilterConfig, @Inject(REFRESH_FILTER) public refreshFilters: BehaviorSubject, - @Inject(CHANGE_APPLIED_FILTERS) public changeAppliedFilters: EventEmitter, ) { } @@ -259,7 +258,6 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy { const appliedFilters: AppliedFilter[] = selectedValues.map((value: string) => { return allAppliedFilters.find((appliedFilter: AppliedFilter) => appliedFilter.value === stripOperatorFromFilterValue(value)); }).filter((appliedFilter: AppliedFilter) => hasValue(appliedFilter)); - this.changeAppliedFilters.emit(appliedFilters); return appliedFilters; }), ); diff --git a/src/app/shared/search/search-filters/search-filter/search-filter.component.html b/src/app/shared/search/search-filters/search-filter/search-filter.component.html index 3de173b4ac8..6387ed9c1ff 100644 --- a/src/app/shared/search/search-filters/search-filter/search-filter.component.html +++ b/src/app/shared/search/search-filters/search-filter/search-filter.component.html @@ -20,8 +20,7 @@
+ [refreshFilters]="refreshFilters"> diff --git a/src/app/shared/search/search-filters/search-filter/search-filter.component.ts b/src/app/shared/search/search-filters/search-filter/search-filter.component.ts index f41c61342c9..d1d3bd729d4 100644 --- a/src/app/shared/search/search-filters/search-filter/search-filter.component.ts +++ b/src/app/shared/search/search-filters/search-filter/search-filter.component.ts @@ -1,4 +1,4 @@ -import { Component, Inject, Input, OnInit, Output, EventEmitter } from '@angular/core'; +import { Component, Inject, Input, OnInit } from '@angular/core'; import { BehaviorSubject, Observable, of as observableOf } from 'rxjs'; import { filter, map, startWith, switchMap, take } from 'rxjs/operators'; @@ -11,7 +11,6 @@ import { SearchService } from '../../../../core/shared/search/search.service'; import { SearchConfigurationService } from '../../../../core/shared/search/search-configuration.service'; import { SEARCH_CONFIG_SERVICE } from '../../../../my-dspace-page/my-dspace-page.component'; import { SequenceService } from '../../../../core/shared/sequence.service'; -import { AppliedFilter } from '../../models/applied-filter.model'; @Component({ selector: 'ds-search-filter', @@ -39,11 +38,6 @@ export class SearchFilterComponent implements OnInit { */ @Input() refreshFilters: BehaviorSubject; - /** - * Emits the {@link AppliedFilter}s of this search filter - */ - @Output() changeAppliedFilters: EventEmitter = new EventEmitter(); - /** * True when the filter is 100% collapsed in the UI */ diff --git a/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.spec.ts b/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.spec.ts index 949fc33892e..c3f73aa8997 100644 --- a/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.spec.ts +++ b/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.spec.ts @@ -13,7 +13,6 @@ import { PageInfo } from '../../../../../core/shared/page-info.model'; import { CommonModule } from '@angular/common'; import { SearchService } from '../../../../../core/shared/search/search.service'; import { - CHANGE_APPLIED_FILTERS, FILTER_CONFIG, IN_PLACE_SEARCH, SearchFilterService, @@ -77,7 +76,6 @@ describe('SearchHierarchyFilterComponent', () => { { provide: IN_PLACE_SEARCH, useValue: false }, { provide: FILTER_CONFIG, useValue: Object.assign(new SearchFilterConfig(), { name: testSearchFilter }) }, { provide: REFRESH_FILTER, useValue: new BehaviorSubject(false) }, - { provide: CHANGE_APPLIED_FILTERS, useValue: new EventEmitter() }, ], schemas: [NO_ERRORS_SCHEMA], }).compileComponents(); diff --git a/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.ts b/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.ts index 649624bcb4b..a5328a779ca 100644 --- a/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.ts +++ b/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.ts @@ -1,4 +1,4 @@ -import { Component, Inject, OnInit, EventEmitter } from '@angular/core'; +import { Component, Inject, OnInit } from '@angular/core'; import { renderFacetFor } from '../search-filter-type-decorator'; import { FilterType } from '../../../models/filter-type.model'; import { facetLoad, SearchFacetFilterComponent } from '../search-facet-filter/search-facet-filter.component'; @@ -10,7 +10,7 @@ import { SearchService } from '../../../../../core/shared/search/search.service' import { FILTER_CONFIG, IN_PLACE_SEARCH, - SearchFilterService, REFRESH_FILTER, CHANGE_APPLIED_FILTERS + SearchFilterService, REFRESH_FILTER, } from '../../../../../core/shared/search/search-filter.service'; import { Params, Router } from '@angular/router'; import { RemoteDataBuildService } from '../../../../../core/cache/builders/remote-data-build.service'; @@ -24,7 +24,6 @@ import { PageInfo } from '../../../../../core/shared/page-info.model'; import { environment } from '../../../../../../environments/environment'; import { addOperatorToFilterValue } from '../../../search.utils'; import { VocabularyTreeviewModalComponent } from '../../../../form/vocabulary-treeview-modal/vocabulary-treeview-modal.component'; -import { AppliedFilter } from '../../../models/applied-filter.model'; @Component({ selector: 'ds-search-hierarchy-filter', @@ -49,9 +48,8 @@ export class SearchHierarchyFilterComponent extends SearchFacetFilterComponent i @Inject(IN_PLACE_SEARCH) public inPlaceSearch: boolean, @Inject(FILTER_CONFIG) public filterConfig: SearchFilterConfig, @Inject(REFRESH_FILTER) public refreshFilters: BehaviorSubject, - @Inject(CHANGE_APPLIED_FILTERS) public changeAppliedFilters: EventEmitter, ) { - super(searchService, filterService, rdbs, router, searchConfigService, inPlaceSearch, filterConfig, refreshFilters, changeAppliedFilters); + super(searchService, filterService, rdbs, router, searchConfigService, inPlaceSearch, filterConfig, refreshFilters); } vocabularyExists$: Observable; diff --git a/src/app/shared/search/search-filters/search-filter/search-range-filter/search-range-filter.component.spec.ts b/src/app/shared/search/search-filters/search-filter/search-range-filter/search-range-filter.component.spec.ts index 509ef48bf86..9562c7e991f 100644 --- a/src/app/shared/search/search-filters/search-filter/search-range-filter/search-range-filter.component.spec.ts +++ b/src/app/shared/search/search-filters/search-filter/search-range-filter/search-range-filter.component.spec.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, EventEmitter, NO_ERRORS_SCHEMA } from '@angular/core'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { TranslateModule } from '@ngx-translate/core'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; @@ -6,7 +6,7 @@ import { FILTER_CONFIG, IN_PLACE_SEARCH, REFRESH_FILTER, - SearchFilterService, CHANGE_APPLIED_FILTERS + SearchFilterService, } from '../../../../../core/shared/search/search-filter.service'; import { SearchFilterConfig } from '../../../models/search-filter-config.model'; import { FilterType } from '../../../models/filter-type.model'; @@ -105,7 +105,6 @@ describe('SearchRangeFilterComponent', () => { { provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() }, { provide: IN_PLACE_SEARCH, useValue: false }, { provide: REFRESH_FILTER, useValue: new BehaviorSubject(false) }, - { provide: CHANGE_APPLIED_FILTERS, useValue: new EventEmitter() }, { provide: SearchFilterService, useValue: { getSelectedValuesForFilter: () => selectedValues, diff --git a/src/app/shared/search/search-filters/search-filter/search-range-filter/search-range-filter.component.ts b/src/app/shared/search/search-filters/search-filter/search-range-filter/search-range-filter.component.ts index 48c039c538f..9cc9e88660f 100644 --- a/src/app/shared/search/search-filters/search-filter/search-range-filter/search-range-filter.component.ts +++ b/src/app/shared/search/search-filters/search-filter/search-range-filter/search-range-filter.component.ts @@ -1,14 +1,13 @@ import { BehaviorSubject, combineLatest as observableCombineLatest, of as observableOf , Subscription } from 'rxjs'; import { map, startWith } from 'rxjs/operators'; import { isPlatformBrowser } from '@angular/common'; -import { Component, Inject, OnDestroy, OnInit, PLATFORM_ID, EventEmitter } from '@angular/core'; +import { Component, Inject, OnDestroy, OnInit, PLATFORM_ID } from '@angular/core'; import { RemoteDataBuildService } from '../../../../../core/cache/builders/remote-data-build.service'; import { FilterType } from '../../../models/filter-type.model'; import { renderFacetFor } from '../search-filter-type-decorator'; import { facetLoad, SearchFacetFilterComponent } from '../search-facet-filter/search-facet-filter.component'; import { SearchFilterConfig } from '../../../models/search-filter-config.model'; import { - CHANGE_APPLIED_FILTERS, FILTER_CONFIG, IN_PLACE_SEARCH, REFRESH_FILTER, @@ -86,9 +85,8 @@ export class SearchRangeFilterComponent extends SearchFacetFilterComponent imple @Inject(FILTER_CONFIG) public filterConfig: SearchFilterConfig, @Inject(PLATFORM_ID) private platformId: any, @Inject(REFRESH_FILTER) public refreshFilters: BehaviorSubject, - @Inject(CHANGE_APPLIED_FILTERS) public changeAppliedFilters: EventEmitter, private route: RouteService) { - super(searchService, filterService, rdbs, router, searchConfigService, inPlaceSearch, filterConfig, refreshFilters, changeAppliedFilters); + super(searchService, filterService, rdbs, router, searchConfigService, inPlaceSearch, filterConfig, refreshFilters); } @@ -121,7 +119,6 @@ export class SearchRangeFilterComponent extends SearchFacetFilterComponent imple })); this.selectedAppliedFilters$ = observableOf(appliedFilters); - this.changeAppliedFilters.emit(appliedFilters); } /** diff --git a/src/app/shared/search/search-filters/search-filters.component.html b/src/app/shared/search/search-filters/search-filters.component.html index 820be2ebdf3..e392cd2663e 100644 --- a/src/app/shared/search/search-filters/search-filters.component.html +++ b/src/app/shared/search/search-filters/search-filters.component.html @@ -1,7 +1,7 @@

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

- +
{{"search.filters.reset" | translate}} diff --git a/src/app/shared/search/search-filters/search-filters.component.ts b/src/app/shared/search/search-filters/search-filters.component.ts index 5cf98db2df8..65f3029a6ff 100644 --- a/src/app/shared/search/search-filters/search-filters.component.ts +++ b/src/app/shared/search/search-filters/search-filters.component.ts @@ -1,4 +1,4 @@ -import { Component, Inject, Input, OnDestroy, OnInit, Output, EventEmitter } from '@angular/core'; +import { Component, Inject, Input, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { BehaviorSubject, Observable } from 'rxjs'; @@ -10,7 +10,6 @@ import { SearchFilterConfig } from '../models/search-filter-config.model'; import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service'; import { SEARCH_CONFIG_SERVICE } from '../../../my-dspace-page/my-dspace-page.component'; import { currentPath } from '../../utils/route.utils'; -import { hasValue } from '../../empty.util'; import { AppliedFilter } from '../models/applied-filter.model'; @Component({ @@ -23,7 +22,7 @@ import { AppliedFilter } from '../models/applied-filter.model'; /** * This component represents the part of the search sidebar that contains filters. */ -export class SearchFiltersComponent implements OnInit, OnDestroy { +export class SearchFiltersComponent implements OnInit { /** * An observable containing configuration about which filters are shown and how they are shown */ @@ -55,11 +54,6 @@ export class SearchFiltersComponent implements OnInit, OnDestroy { */ @Input() refreshFilters: BehaviorSubject; - /** - * Emits the {@link AppliedFilter}s by search filter name - */ - @Output() changeAppliedFilters: EventEmitter> = new EventEmitter(); - appliedFilters: Map = new Map(); /** @@ -67,8 +61,6 @@ export class SearchFiltersComponent implements OnInit, OnDestroy { */ searchLink: string; - subs = []; - /** * Initialize instance variables * @param {SearchService} searchService @@ -107,22 +99,4 @@ export class SearchFiltersComponent implements OnInit, OnDestroy { return config ? config.name : undefined; } - /** - * Updates the map of {@link AppliedFilter}s and emits it to it's parent component - * - * @param filterName - * @param appliedFilters - */ - updateAppliedFilters(filterName: string, appliedFilters: AppliedFilter[]): void { - this.appliedFilters.set(filterName, appliedFilters); - this.changeAppliedFilters.emit(this.appliedFilters); - } - - ngOnDestroy() { - this.subs.forEach((sub) => { - if (hasValue(sub)) { - sub.unsubscribe(); - } - }); - } } diff --git a/src/app/shared/search/search-filters/themed-search-filters.component.ts b/src/app/shared/search/search-filters/themed-search-filters.component.ts index d520ac2b0ab..e935c2f2f12 100644 --- a/src/app/shared/search/search-filters/themed-search-filters.component.ts +++ b/src/app/shared/search/search-filters/themed-search-filters.component.ts @@ -1,10 +1,9 @@ -import { Component, Input, Output, EventEmitter } from '@angular/core'; +import { Component, Input } from '@angular/core'; import { ThemedComponent } from '../../theme-support/themed.component'; import { SearchFiltersComponent } from './search-filters.component'; import { Observable } from 'rxjs/internal/Observable'; import { RemoteData } from '../../../core/data/remote-data'; import { SearchFilterConfig } from '../models/search-filter-config.model'; -import { AppliedFilter } from '../models/applied-filter.model'; /** * Themed wrapper for SearchFiltersComponent @@ -20,11 +19,9 @@ export class ThemedSearchFiltersComponent extends ThemedComponent; @Input() filters: Observable>; - @Output() changeAppliedFilters: EventEmitter> = new EventEmitter(); protected inAndOutputNames: (keyof SearchFiltersComponent & keyof this)[] = [ 'filters', 'currentConfiguration', 'currentScope', 'inPlaceSearch', 'refreshFilters', - 'changeAppliedFilters', ]; protected getComponentName(): string { diff --git a/src/app/shared/search/search-labels/search-labels.component.html b/src/app/shared/search/search-labels/search-labels.component.html index 1dbf910dfb1..4e84de0ceb2 100644 --- a/src/app/shared/search/search-labels/search-labels.component.html +++ b/src/app/shared/search/search-labels/search-labels.component.html @@ -1,8 +1,6 @@
- - - - + +
diff --git a/src/app/shared/search/search-labels/search-labels.component.ts b/src/app/shared/search/search-labels/search-labels.component.ts index 388ce08e46e..9d011bcd2d2 100644 --- a/src/app/shared/search/search-labels/search-labels.component.ts +++ b/src/app/shared/search/search-labels/search-labels.component.ts @@ -1,5 +1,7 @@ -import { Component, Input } from '@angular/core'; +import { Component, Input, OnInit } from '@angular/core'; import { AppliedFilter } from '../models/applied-filter.model'; +import { SearchService } from '../../../core/shared/search/search.service'; +import { BehaviorSubject } from 'rxjs'; @Component({ selector: 'ds-search-labels', @@ -10,16 +12,22 @@ import { AppliedFilter } from '../models/applied-filter.model'; /** * Component that represents the labels containing the currently active filters */ -export class SearchLabelsComponent { +export class SearchLabelsComponent implements OnInit { /** * True when the search component should show results on the current page */ @Input() inPlaceSearch: boolean; - /** - * The {@link AppliedFilter}s by filter name - */ - @Input() appliedFilters: Map; + appliedFilters$: BehaviorSubject; + + constructor( + protected searchService: SearchService, + ) { + } + + ngOnInit(): void { + this.appliedFilters$ = this.searchService.appliedFilters$; + } } diff --git a/src/app/shared/search/search-sidebar/search-sidebar.component.html b/src/app/shared/search/search-sidebar/search-sidebar.component.html index ad1c5745976..5faba679eaf 100644 --- a/src/app/shared/search/search-sidebar/search-sidebar.component.html +++ b/src/app/shared/search/search-sidebar/search-sidebar.component.html @@ -21,8 +21,7 @@ [currentConfiguration]="configuration" [filters]="filters" [refreshFilters]="refreshFilters" - [inPlaceSearch]="inPlaceSearch" - (changeAppliedFilters)="changeAppliedFilters.emit($event)"> + [inPlaceSearch]="inPlaceSearch"> diff --git a/src/app/shared/search/search-sidebar/search-sidebar.component.ts b/src/app/shared/search/search-sidebar/search-sidebar.component.ts index 8f7e3236e9b..9eb31cffe15 100644 --- a/src/app/shared/search/search-sidebar/search-sidebar.component.ts +++ b/src/app/shared/search/search-sidebar/search-sidebar.component.ts @@ -7,7 +7,6 @@ import { SortOptions } from '../../../core/cache/models/sort-options.model'; import { ViewMode } from '../../../core/shared/view-mode.model'; import { RemoteData } from '../../../core/data/remote-data'; import { SearchFilterConfig } from '../models/search-filter-config.model'; -import { AppliedFilter } from '../models/applied-filter.model'; /** * This component renders a simple item page. @@ -96,11 +95,6 @@ export class SearchSidebarComponent { */ @Output() changeConfiguration: EventEmitter = new EventEmitter(); - /** - * Emits the {@link AppliedFilter}s by search filter name - */ - @Output() changeAppliedFilters: EventEmitter> = new EventEmitter(); - /** * Emits event when the user select a new view mode */ diff --git a/src/app/shared/search/search-sidebar/themed-search-sidebar.component.ts b/src/app/shared/search/search-sidebar/themed-search-sidebar.component.ts index 2ffb1f8060d..ce9f5800003 100644 --- a/src/app/shared/search/search-sidebar/themed-search-sidebar.component.ts +++ b/src/app/shared/search/search-sidebar/themed-search-sidebar.component.ts @@ -8,7 +8,6 @@ import { PaginatedSearchOptions } from '../models/paginated-search-options.model import { BehaviorSubject, Observable } from 'rxjs'; import { RemoteData } from '../../../core/data/remote-data'; import { SearchFilterConfig } from '../models/search-filter-config.model'; -import { AppliedFilter } from '../models/applied-filter.model'; /** * Themed wrapper for SearchSidebarComponent @@ -33,14 +32,12 @@ export class ThemedSearchSidebarComponent extends ThemedComponent; @Output() toggleSidebar: EventEmitter = new EventEmitter(); @Output() changeConfiguration: EventEmitter = new EventEmitter(); - @Output() changeAppliedFilters: EventEmitter> = new EventEmitter(); @Output() changeViewMode: EventEmitter = new EventEmitter(); protected inAndOutputNames: (keyof SearchSidebarComponent & keyof this)[] = [ 'configuration', 'configurationList', 'currentScope', 'currentSortOption', 'resultCount', 'filters', 'viewModeList', 'showViewModes', 'inPlaceSearch', 'searchOptions', 'sortOptionsList', 'refreshFilters', 'toggleSidebar', 'changeConfiguration', - 'changeAppliedFilters', 'changeViewMode', ]; diff --git a/src/app/shared/search/search.component.html b/src/app/shared/search/search.component.html index e65d5858e49..01192411617 100644 --- a/src/app/shared/search/search.component.html +++ b/src/app/shared/search/search.component.html @@ -61,7 +61,6 @@ [viewModeList]="viewModeList" [showViewModes]="showViewModes" (changeConfiguration)="changeContext($event.context)" - (changeAppliedFilters)="appliedFilters = $event" (changeViewMode)="changeViewMode()"> @@ -93,7 +91,7 @@
- +
diff --git a/src/app/shared/search/search.component.ts b/src/app/shared/search/search.component.ts index ff7b7fc6528..df8d1e4d64a 100644 --- a/src/app/shared/search/search.component.ts +++ b/src/app/shared/search/search.component.ts @@ -38,7 +38,6 @@ import { WorkspaceItem } from '../../core/submission/models/workspaceitem.model' import { ITEM_MODULE_PATH } from '../../item-page/item-page-routing-paths'; import { COLLECTION_MODULE_PATH } from '../../collection-page/collection-page-routing-paths'; import { COMMUNITY_MODULE_PATH } from '../../community-page/community-page-routing-paths'; -import { AppliedFilter } from './models/applied-filter.model'; @Component({ selector: 'ds-search', @@ -269,11 +268,6 @@ export class SearchComponent implements OnInit { */ @Output() selectObject: EventEmitter = new EventEmitter(); - /** - * The {@link AppliedFilter}s by filter name - */ - appliedFilters: Map = new Map(); - constructor(protected service: SearchService, protected sidebarService: SidebarService, protected windowService: HostWindowService, From b7711222977035364d61632de9d274c56c4d4d96 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Tue, 23 Apr 2024 18:49:08 +0200 Subject: [PATCH 08/24] 111731: Reset the advanced search's operator to the first available operator on filter change --- .../advanced-search.component.html | 6 +++-- .../advanced-search.component.ts | 24 +++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/app/shared/search/advanced-search/advanced-search.component.html b/src/app/shared/search/advanced-search/advanced-search.component.html index a3e5db61388..edcb09d0ac9 100644 --- a/src/app/shared/search/advanced-search/advanced-search.component.html +++ b/src/app/shared/search/advanced-search/advanced-search.component.html @@ -1,7 +1,9 @@

{{ 'search.sidebar.advanced-search.title' | translate }}

- - {{"search.filters.reset" | translate}} + diff --git a/src/app/shared/search/search-filters/search-filters.component.ts b/src/app/shared/search/search-filters/search-filters.component.ts index 65f3029a6ff..0d67e4c79e9 100644 --- a/src/app/shared/search/search-filters/search-filters.component.ts +++ b/src/app/shared/search/search-filters/search-filters.component.ts @@ -11,6 +11,7 @@ import { SearchConfigurationService } from '../../../core/shared/search/search-c import { SEARCH_CONFIG_SERVICE } from '../../../my-dspace-page/my-dspace-page.component'; import { currentPath } from '../../utils/route.utils'; import { AppliedFilter } from '../models/applied-filter.model'; +import { SearchFilterService } from '../../../core/shared/search/search-filter.service'; @Component({ selector: 'ds-search-filters', @@ -69,9 +70,11 @@ export class SearchFiltersComponent implements OnInit { * @param {SearchConfigurationService} searchConfigService */ constructor( - private searchService: SearchService, - private router: Router, - @Inject(SEARCH_CONFIG_SERVICE) private searchConfigService: SearchConfigurationService) { + protected searchService: SearchService, + protected searchFilterService: SearchFilterService, + protected router: Router, + @Inject(SEARCH_CONFIG_SERVICE) protected searchConfigService: SearchConfigurationService, + ) { } ngOnInit(): void { @@ -99,4 +102,9 @@ export class SearchFiltersComponent implements OnInit { return config ? config.name : undefined; } + minimizeFilters(): void { + if (this.searchService.appliedFilters$.value.length > 0) { + this.searchFilterService.minimizeAll(); + } + } } diff --git a/src/app/shared/search/search-labels/search-label-range/search-label-range.component.html b/src/app/shared/search/search-labels/search-label-range/search-label-range.component.html index 84c6c75ebc3..fc1d2312a6c 100644 --- a/src/app/shared/search/search-labels/search-label-range/search-label-range.component.html +++ b/src/app/shared/search/search-labels/search-label-range/search-label-range.component.html @@ -1,6 +1,7 @@ {{('search.filters.applied.f.' + appliedFilter.filter + '.min') | translate}}: {{ min }} × @@ -8,6 +9,7 @@ {{('search.filters.applied.f.' + appliedFilter.filter + '.max') | translate}}: {{ max }} × diff --git a/src/app/shared/search/search-labels/search-label-range/search-label-range.component.spec.ts b/src/app/shared/search/search-labels/search-label-range/search-label-range.component.spec.ts index ca835f2bbff..d0e09f891be 100644 --- a/src/app/shared/search/search-labels/search-label-range/search-label-range.component.spec.ts +++ b/src/app/shared/search/search-labels/search-label-range/search-label-range.component.spec.ts @@ -13,6 +13,8 @@ import { SearchConfigurationServiceStub } from '../../../testing/search-configur import { PaginationService } from '../../../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../../testing/pagination-service.stub'; import { PaginationComponentOptions } from '../../../pagination/pagination-component-options.model'; +import { SearchFilterService } from '../../../../core/shared/search/search-filter.service'; +import { SearchFilterServiceStub } from '../../../testing/search-filter-service.stub'; describe('SearchLabelRangeComponent', () => { let comp: SearchLabelRangeComponent; @@ -20,6 +22,7 @@ describe('SearchLabelRangeComponent', () => { let route: ActivatedRouteStub; let searchConfigurationService: SearchConfigurationServiceStub; + let searchFilterService: SearchFilterServiceStub; let paginationService: PaginationServiceStub; const searchLink = '/search'; @@ -51,6 +54,7 @@ describe('SearchLabelRangeComponent', () => { init(); route = new ActivatedRouteStub(initialRouteParams); searchConfigurationService = new SearchConfigurationServiceStub(); + searchFilterService = new SearchFilterServiceStub(); paginationService = new PaginationServiceStub(pagination); await TestBed.configureTestingModule({ @@ -65,6 +69,7 @@ describe('SearchLabelRangeComponent', () => { { provide: PaginationService, useValue: paginationService }, { provide: SearchConfigurationService, useValue: searchConfigurationService }, { provide: SearchService, useValue: new SearchServiceStub(searchLink) }, + { provide: SearchFilterService, useValue: searchFilterService }, { provide: ActivatedRoute, useValue: route }, ], }).compileComponents(); diff --git a/src/app/shared/search/search-labels/search-label-range/search-label-range.component.ts b/src/app/shared/search/search-labels/search-label-range/search-label-range.component.ts index 593dc2f9e19..5842358ce1f 100644 --- a/src/app/shared/search/search-labels/search-label-range/search-label-range.component.ts +++ b/src/app/shared/search/search-labels/search-label-range/search-label-range.component.ts @@ -7,6 +7,7 @@ import { AppliedFilter } from '../../models/applied-filter.model'; import { renderSearchLabelFor } from '../search-label-loader/search-label-loader.decorator'; import { SearchConfigurationService } from '../../../../core/shared/search/search-configuration.service'; import { PaginationService } from '../../../../core/pagination/pagination.service'; +import { SearchFilterService } from '../../../../core/shared/search/search-filter.service'; /** * Component that represents the label containing the currently active filters @@ -37,6 +38,7 @@ export class SearchLabelRangeComponent implements OnInit { protected router: Router, protected searchConfigurationService: SearchConfigurationService, protected searchService: SearchService, + protected searchFilterService: SearchFilterService, ) { } diff --git a/src/app/shared/search/search-labels/search-label/search-label.component.html b/src/app/shared/search/search-labels/search-label/search-label.component.html index c2caa9d4320..e3078a23c7d 100644 --- a/src/app/shared/search/search-labels/search-label/search-label.component.html +++ b/src/app/shared/search/search-labels/search-label/search-label.component.html @@ -1,6 +1,7 @@ + [queryParams]="(removeParameters$ | async)" + (click)="searchFilterService.minimizeAll()"> {{('search.filters.applied.f.' + appliedFilter.filter) | translate}}{{'search.filters.applied.operator.' + appliedFilter.operator | translate}}: {{'search.filters.' + appliedFilter.filter + '.' + appliedFilter.label | translate: {default: appliedFilter.label} }} × diff --git a/src/app/shared/search/search-labels/search-label/search-label.component.spec.ts b/src/app/shared/search/search-labels/search-label/search-label.component.spec.ts index 657f9ae4a1a..905726901e9 100644 --- a/src/app/shared/search/search-labels/search-label/search-label.component.spec.ts +++ b/src/app/shared/search/search-labels/search-label/search-label.component.spec.ts @@ -13,6 +13,8 @@ import { SearchConfigurationServiceStub } from '../../../testing/search-configur import { PaginationService } from '../../../../core/pagination/pagination.service'; import { PaginationServiceStub } from '../../../testing/pagination-service.stub'; import { PaginationComponentOptions } from '../../../pagination/pagination-component-options.model'; +import { SearchFilterServiceStub } from '../../../testing/search-filter-service.stub'; +import { SearchFilterService } from '../../../../core/shared/search/search-filter.service'; describe('SearchLabelComponent', () => { let comp: SearchLabelComponent; @@ -20,6 +22,7 @@ describe('SearchLabelComponent', () => { let route: ActivatedRouteStub; let searchConfigurationService: SearchConfigurationServiceStub; + let searchFilterService: SearchFilterServiceStub; let paginationService: PaginationServiceStub; const searchLink = '/search'; @@ -51,6 +54,7 @@ describe('SearchLabelComponent', () => { init(); route = new ActivatedRouteStub(initialRouteParams); searchConfigurationService = new SearchConfigurationServiceStub(); + searchFilterService = new SearchFilterServiceStub(); paginationService = new PaginationServiceStub(pagination); await TestBed.configureTestingModule({ @@ -64,6 +68,7 @@ describe('SearchLabelComponent', () => { providers: [ { provide: PaginationService, useValue: paginationService }, { provide: SearchConfigurationService, useValue: searchConfigurationService }, + { provide: SearchFilterService, useValue: searchFilterService }, { provide: SearchService, useValue: new SearchServiceStub(searchLink) }, { provide: ActivatedRoute, useValue: route }, ], diff --git a/src/app/shared/search/search-labels/search-label/search-label.component.ts b/src/app/shared/search/search-labels/search-label/search-label.component.ts index ff891de12b0..12b8bb15b81 100644 --- a/src/app/shared/search/search-labels/search-label/search-label.component.ts +++ b/src/app/shared/search/search-labels/search-label/search-label.component.ts @@ -7,6 +7,7 @@ import { AppliedFilter } from '../../models/applied-filter.model'; import { SearchConfigurationService } from '../../../../core/shared/search/search-configuration.service'; import { renderSearchLabelFor } from '../search-label-loader/search-label-loader.decorator'; import { PaginationService } from '../../../../core/pagination/pagination.service'; +import { SearchFilterService } from '../../../../core/shared/search/search-filter.service'; /** * Component that represents the label containing the currently active filters @@ -30,6 +31,7 @@ export class SearchLabelComponent implements OnInit { protected router: Router, protected searchConfigurationService: SearchConfigurationService, protected searchService: SearchService, + protected searchFilterService: SearchFilterService, ) { } From 25fd737800209d252d8b44b7eb58eeb2407c7d10 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Tue, 30 Apr 2024 14:38:31 +0200 Subject: [PATCH 12/24] 111731: Added missing border to advanced search section --- .../search/advanced-search/advanced-search.component.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/app/shared/search/advanced-search/advanced-search.component.scss b/src/app/shared/search/advanced-search/advanced-search.component.scss index 09c5a9fa9da..f6a2c0879bb 100644 --- a/src/app/shared/search/advanced-search/advanced-search.component.scss +++ b/src/app/shared/search/advanced-search/advanced-search.component.scss @@ -3,3 +3,7 @@ display: none; } } + +.advanced-search { + border: 1px solid var(--bs-light); +} From bc35bfb3cd87b08712f454099d8e9f8eadeab7dc Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Thu, 2 May 2024 00:00:47 +0200 Subject: [PATCH 13/24] 111731: Refactored SearchFacetFilterWrapperComponent to extend AbstractComponentLoaderComponent --- .../shared/search/search-filter.service.ts | 11 +--- ...search-facet-filter-wrapper.component.html | 1 - .../search-facet-filter-wrapper.component.ts | 66 ++++++------------- .../search-facet-filter.component.spec.ts | 14 +--- .../search-facet-filter.component.ts | 34 ++++++---- .../search-hierarchy-filter.component.spec.ts | 18 ++--- .../search-hierarchy-filter.component.ts | 22 +++---- .../search-range-filter.component.spec.ts | 15 ++--- .../search-range-filter.component.ts | 23 +++---- 9 files changed, 73 insertions(+), 131 deletions(-) delete mode 100644 src/app/shared/search/search-filters/search-filter/search-facet-filter-wrapper/search-facet-filter-wrapper.component.html diff --git a/src/app/core/shared/search/search-filter.service.ts b/src/app/core/shared/search/search-filter.service.ts index c8d71fc5a3c..5b971d26798 100644 --- a/src/app/core/shared/search/search-filter.service.ts +++ b/src/app/core/shared/search/search-filter.service.ts @@ -1,7 +1,4 @@ -import { - Injectable, - InjectionToken, -} from '@angular/core'; +import { Injectable } from '@angular/core'; import { Params } from '@angular/router'; import { createSelector, @@ -10,7 +7,6 @@ import { Store, } from '@ngrx/store'; import { - BehaviorSubject, combineLatest as observableCombineLatest, Observable, of as observableOf, @@ -60,11 +56,6 @@ import { SearchService } from './search.service'; const filterStateSelector = (state: SearchFiltersState) => state.searchFilter; -export const FILTER_CONFIG: InjectionToken = new InjectionToken('filterConfig'); -export const IN_PLACE_SEARCH: InjectionToken = new InjectionToken('inPlaceSearch'); -export const REFRESH_FILTER: InjectionToken> = new InjectionToken('refreshFilters'); -export const SCOPE: InjectionToken = new InjectionToken('scope'); - /** * Service that performs all actions that have to do with search filters and facets */ diff --git a/src/app/shared/search/search-filters/search-filter/search-facet-filter-wrapper/search-facet-filter-wrapper.component.html b/src/app/shared/search/search-filters/search-filter/search-facet-filter-wrapper/search-facet-filter-wrapper.component.html deleted file mode 100644 index 4a325d9b3ca..00000000000 --- a/src/app/shared/search/search-filters/search-filter/search-facet-filter-wrapper/search-facet-filter-wrapper.component.html +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/app/shared/search/search-filters/search-filter/search-facet-filter-wrapper/search-facet-filter-wrapper.component.ts b/src/app/shared/search/search-filters/search-filter/search-facet-filter-wrapper/search-facet-filter-wrapper.component.ts index 76a3c625363..ce8064b23d3 100644 --- a/src/app/shared/search/search-filters/search-filter/search-facet-filter-wrapper/search-facet-filter-wrapper.component.ts +++ b/src/app/shared/search/search-filters/search-filter/search-facet-filter-wrapper/search-facet-filter-wrapper.component.ts @@ -1,35 +1,29 @@ -import { NgComponentOutlet } from '@angular/common'; import { Component, - Injector, Input, - OnInit, } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; import { GenericConstructor } from '../../../../../core/shared/generic-constructor'; -import { - FILTER_CONFIG, - IN_PLACE_SEARCH, - REFRESH_FILTER, - SCOPE, -} from '../../../../../core/shared/search/search-filter.service'; -import { FilterType } from '../../../models/filter-type.model'; +import { AbstractComponentLoaderComponent } from '../../../../abstract-component-loader/abstract-component-loader.component'; +import { DynamicComponentLoaderDirective } from '../../../../abstract-component-loader/dynamic-component-loader.directive'; import { SearchFilterConfig } from '../../../models/search-filter-config.model'; -import { SearchFacetFilterComponent } from '../search-facet-filter/search-facet-filter.component'; import { renderFilterType } from '../search-filter-type-decorator'; @Component({ selector: 'ds-search-facet-filter-wrapper', - templateUrl: './search-facet-filter-wrapper.component.html', + templateUrl: '../../../../abstract-component-loader/abstract-component-loader.component.html', standalone: true, - imports: [NgComponentOutlet], + imports: [ + DynamicComponentLoaderDirective, + ], }) /** * Wrapper component that renders a specific facet filter based on the filter config's type */ -export class SearchFacetFilterWrapperComponent implements OnInit { +export class SearchFacetFilterWrapperComponent extends AbstractComponentLoaderComponent { + /** * Configuration for the filter of this wrapper component */ @@ -38,7 +32,7 @@ export class SearchFacetFilterWrapperComponent implements OnInit { /** * True when the search component should show results on the current page */ - @Input() inPlaceSearch; + @Input() inPlaceSearch: boolean; /** * Emits when the search filters values may be stale, and so they must be refreshed. @@ -50,39 +44,19 @@ export class SearchFacetFilterWrapperComponent implements OnInit { */ @Input() scope: string; - /** - * The constructor of the search facet filter that should be rendered, based on the filter config's type - */ - searchFilter: GenericConstructor; - /** - * Injector to inject a child component with the @Input parameters - */ - objectInjector: Injector; + inputNamesDependentForComponent: (keyof SearchFacetFilterWrapperComponent & string)[] = [ + 'filterConfig', + ]; - constructor(private injector: Injector) { - } + inputNames: (keyof SearchFacetFilterWrapperComponent & string)[] = [ + 'filterConfig', + 'inPlaceSearch', + 'refreshFilters', + 'scope', + ]; - /** - * Initialize and add the filter config to the injector - */ - ngOnInit(): void { - this.searchFilter = this.getSearchFilter(); - this.objectInjector = Injector.create({ - providers: [ - { provide: FILTER_CONFIG, useFactory: () => (this.filterConfig), deps: [] }, - { provide: IN_PLACE_SEARCH, useFactory: () => (this.inPlaceSearch), deps: [] }, - { provide: REFRESH_FILTER, useFactory: () => (this.refreshFilters), deps: [] }, - { provide: SCOPE, useFactory: () => (this.scope), deps: [] }, - ], - parent: this.injector, - }); + public getComponent(): GenericConstructor { + return renderFilterType(this.filterConfig.filterType); } - /** - * Find the correct component based on the filter config's type - */ - private getSearchFilter() { - const type: FilterType = this.filterConfig.filterType; - return renderFilterType(type); - } } diff --git a/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.spec.ts b/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.spec.ts index 78d6fa8d372..e90b7527a55 100644 --- a/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.spec.ts +++ b/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.spec.ts @@ -18,13 +18,7 @@ import { import { RemoteDataBuildService } from '../../../../../core/cache/builders/remote-data-build.service'; import { SearchService } from '../../../../../core/shared/search/search.service'; -import { - FILTER_CONFIG, - IN_PLACE_SEARCH, - REFRESH_FILTER, - SCOPE, - SearchFilterService, -} from '../../../../../core/shared/search/search-filter.service'; +import { SearchFilterService } from '../../../../../core/shared/search/search-filter.service'; import { SEARCH_CONFIG_SERVICE } from '../../../../../my-dspace-page/my-dspace-configuration.service'; import { createSuccessfulRemoteDataObject$ } from '../../../../remote-data.utils'; import { RouterStub } from '../../../../testing/router.stub'; @@ -93,12 +87,8 @@ describe('SearchFacetFilterComponent', () => { { provide: SearchService, useValue: searchService }, { provide: SearchFilterService, useValue: filterService }, { provide: Router, useValue: router }, - { provide: FILTER_CONFIG, useValue: new SearchFilterConfig() }, { provide: RemoteDataBuildService, useValue: { aggregate: () => observableOf({}) } }, { provide: SEARCH_CONFIG_SERVICE, useValue: searchConfigService }, - { provide: IN_PLACE_SEARCH, useValue: false }, - { provide: REFRESH_FILTER, useValue: new BehaviorSubject(false) }, - { provide: SCOPE, useValue: undefined }, ], schemas: [NO_ERRORS_SCHEMA], }).overrideComponent(SearchFacetFilterComponent, { @@ -110,6 +100,8 @@ describe('SearchFacetFilterComponent', () => { fixture = TestBed.createComponent(SearchFacetFilterComponent); comp = fixture.componentInstance; // SearchPageComponent test instance comp.filterConfig = mockFilterConfig; + comp.inPlaceSearch = false; + comp.refreshFilters = new BehaviorSubject(false); spyOn(searchService, 'getFacetValuesFor').and.returnValue(createSuccessfulRemoteDataObject$(values)); fixture.detectChanges(); }); diff --git a/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts b/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts index 505501bfccf..c2e5e4c0f04 100644 --- a/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts +++ b/src/app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component.ts @@ -8,6 +8,7 @@ import { import { Component, Inject, + Input, OnDestroy, OnInit, } from '@angular/core'; @@ -37,13 +38,7 @@ import { RemoteDataBuildService } from '../../../../../core/cache/builders/remot import { getFirstSucceededRemoteDataPayload } from '../../../../../core/shared/operators'; import { SearchService } from '../../../../../core/shared/search/search.service'; import { SearchConfigurationService } from '../../../../../core/shared/search/search-configuration.service'; -import { - FILTER_CONFIG, - IN_PLACE_SEARCH, - REFRESH_FILTER, - SCOPE, - SearchFilterService, -} from '../../../../../core/shared/search/search-filter.service'; +import { SearchFilterService } from '../../../../../core/shared/search/search-filter.service'; import { SEARCH_CONFIG_SERVICE } from '../../../../../my-dspace-page/my-dspace-configuration.service'; import { hasNoValue, @@ -68,6 +63,27 @@ import { stripOperatorFromFilterValue } from '../../../search.utils'; * Super class for all different representations of facets */ export class SearchFacetFilterComponent implements OnInit, OnDestroy { + + /** + * Configuration for the filter of this wrapper component + */ + @Input() filterConfig: SearchFilterConfig; + + /** + * True when the search component should show results on the current page + */ + @Input() inPlaceSearch: boolean; + + /** + * Emits when the search filters values may be stale, and so they must be refreshed. + */ + @Input() refreshFilters: BehaviorSubject; + + /** + * The current scope + */ + @Input() scope: string; + /** * Emits an array of pages with values found for this facet */ @@ -125,10 +141,6 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy { protected rdbs: RemoteDataBuildService, protected router: Router, @Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService, - @Inject(IN_PLACE_SEARCH) public inPlaceSearch: boolean, - @Inject(FILTER_CONFIG) public filterConfig: SearchFilterConfig, - @Inject(REFRESH_FILTER) public refreshFilters: BehaviorSubject, - @Inject(SCOPE) public scope: string, ) { } diff --git a/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.spec.ts b/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.spec.ts index 977edbcba72..a13d0a8ae46 100644 --- a/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.spec.ts +++ b/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.spec.ts @@ -30,13 +30,7 @@ import { RemoteData } from '../../../../../core/data/remote-data'; import { RequestEntryState } from '../../../../../core/data/request-entry-state.model'; import { PageInfo } from '../../../../../core/shared/page-info.model'; import { SearchService } from '../../../../../core/shared/search/search.service'; -import { - FILTER_CONFIG, - IN_PLACE_SEARCH, - REFRESH_FILTER, - SCOPE, - SearchFilterService, -} from '../../../../../core/shared/search/search-filter.service'; +import { SearchFilterService } from '../../../../../core/shared/search/search-filter.service'; import { VocabularyEntryDetail } from '../../../../../core/submission/vocabularies/models/vocabulary-entry-detail.model'; import { VocabularyService } from '../../../../../core/submission/vocabularies/vocabulary.service'; import { SEARCH_CONFIG_SERVICE } from '../../../../../my-dspace-page/my-dspace-configuration.service'; @@ -46,7 +40,7 @@ import { SearchFilterConfig } from '../../../models/search-filter-config.model'; import { SearchHierarchyFilterComponent } from './search-hierarchy-filter.component'; describe('SearchHierarchyFilterComponent', () => { - + let comp: SearchHierarchyFilterComponent; let fixture: ComponentFixture; let showVocabularyTreeLink: DebugElement; @@ -97,10 +91,6 @@ describe('SearchHierarchyFilterComponent', () => { { provide: VocabularyService, useValue: vocabularyService }, { provide: APP_CONFIG, useValue: environment }, { provide: SEARCH_CONFIG_SERVICE, useValue: searchConfigService }, - { provide: IN_PLACE_SEARCH, useValue: false }, - { provide: FILTER_CONFIG, useValue: Object.assign(new SearchFilterConfig(), { name: testSearchFilter }) }, - { provide: REFRESH_FILTER, useValue: new BehaviorSubject(false) }, - { provide: SCOPE, useValue: undefined }, ], schemas: [CUSTOM_ELEMENTS_SCHEMA], }).compileComponents(); @@ -108,6 +98,10 @@ describe('SearchHierarchyFilterComponent', () => { function init() { fixture = TestBed.createComponent(SearchHierarchyFilterComponent); + comp = fixture.componentInstance; + comp.inPlaceSearch = false; + comp.filterConfig = Object.assign(new SearchFilterConfig(), { name: testSearchFilter }); + comp.refreshFilters = new BehaviorSubject(false); fixture.detectChanges(); showVocabularyTreeLink = fixture.debugElement.query(By.css(`a#show-${testSearchFilter}-tree`)); } diff --git a/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.ts b/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.ts index fc12b7fabe6..a4c105b21f8 100644 --- a/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.ts +++ b/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.ts @@ -21,7 +21,6 @@ import { } from '@ng-bootstrap/ng-bootstrap'; import { TranslateModule } from '@ngx-translate/core'; import { - BehaviorSubject, from, Observable, } from 'rxjs'; @@ -41,20 +40,13 @@ import { RemoteDataBuildService } from '../../../../../core/cache/builders/remot import { PageInfo } from '../../../../../core/shared/page-info.model'; import { SearchService } from '../../../../../core/shared/search/search.service'; import { SearchConfigurationService } from '../../../../../core/shared/search/search-configuration.service'; -import { - FILTER_CONFIG, - IN_PLACE_SEARCH, - REFRESH_FILTER, - SCOPE, - SearchFilterService, -} from '../../../../../core/shared/search/search-filter.service'; +import { SearchFilterService } from '../../../../../core/shared/search/search-filter.service'; import { VocabularyEntryDetail } from '../../../../../core/submission/vocabularies/models/vocabulary-entry-detail.model'; import { VocabularyService } from '../../../../../core/submission/vocabularies/vocabulary.service'; import { SEARCH_CONFIG_SERVICE } from '../../../../../my-dspace-page/my-dspace-configuration.service'; import { hasValue } from '../../../../empty.util'; import { VocabularyTreeviewModalComponent } from '../../../../form/vocabulary-treeview-modal/vocabulary-treeview-modal.component'; import { FilterInputSuggestionsComponent } from '../../../../input-suggestions/filter-suggestions/filter-input-suggestions.component'; -import { SearchFilterConfig } from '../../../models/search-filter-config.model'; import { addOperatorToFilterValue } from '../../../search.utils'; import { facetLoad, @@ -85,12 +77,14 @@ export class SearchHierarchyFilterComponent extends SearchFacetFilterComponent i protected vocabularyService: VocabularyService, @Inject(APP_CONFIG) protected appConfig: AppConfig, @Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService, - @Inject(IN_PLACE_SEARCH) public inPlaceSearch: boolean, - @Inject(FILTER_CONFIG) public filterConfig: SearchFilterConfig, - @Inject(REFRESH_FILTER) public refreshFilters: BehaviorSubject, - @Inject(SCOPE) public scope: string, ) { - super(searchService, filterService, rdbs, router, searchConfigService, inPlaceSearch, filterConfig, refreshFilters, scope); + super( + searchService, + filterService, + rdbs, + router, + searchConfigService, + ); } vocabularyExists$: Observable; diff --git a/src/app/shared/search/search-filters/search-filter/search-range-filter/search-range-filter.component.spec.ts b/src/app/shared/search/search-filters/search-filter/search-range-filter/search-range-filter.component.spec.ts index 11fa74efee4..263861d017f 100644 --- a/src/app/shared/search/search-filters/search-filter/search-range-filter/search-range-filter.component.spec.ts +++ b/src/app/shared/search/search-filters/search-filter/search-range-filter/search-range-filter.component.spec.ts @@ -21,13 +21,7 @@ import { buildPaginatedList } from '../../../../../core/data/paginated-list.mode import { RouteService } from '../../../../../core/services/route.service'; import { PageInfo } from '../../../../../core/shared/page-info.model'; import { SearchService } from '../../../../../core/shared/search/search.service'; -import { - FILTER_CONFIG, - IN_PLACE_SEARCH, - REFRESH_FILTER, - SCOPE, - SearchFilterService, -} from '../../../../../core/shared/search/search-filter.service'; +import { SearchFilterService } from '../../../../../core/shared/search/search-filter.service'; import { SEARCH_CONFIG_SERVICE } from '../../../../../my-dspace-page/my-dspace-configuration.service'; import { createSuccessfulRemoteDataObject$ } from '../../../../remote-data.utils'; import { ActivatedRouteStub } from '../../../../testing/active-router.stub'; @@ -119,11 +113,7 @@ describe('SearchRangeFilterComponent', () => { { provide: Router, useValue: router }, { provide: RouteService, useValue: routeServiceStub }, { provide: RemoteDataBuildService, useValue: { aggregate: () => observableOf({}) } }, - { provide: FILTER_CONFIG, useValue: mockFilterConfig }, { provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() }, - { provide: IN_PLACE_SEARCH, useValue: false }, - { provide: REFRESH_FILTER, useValue: new BehaviorSubject(false) }, - { provide: SCOPE, useValue: undefined }, { provide: ActivatedRoute, useValue: new ActivatedRouteStub() }, ], schemas: [CUSTOM_ELEMENTS_SCHEMA], @@ -139,6 +129,9 @@ describe('SearchRangeFilterComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(SearchRangeFilterComponent); comp = fixture.componentInstance; // SearchPageComponent test instance + comp.filterConfig = mockFilterConfig; + comp.inPlaceSearch = false; + comp.refreshFilters = new BehaviorSubject(false); spyOn(searchService, 'getFacetValuesFor').and.returnValue(mockValues); fixture.detectChanges(); }); diff --git a/src/app/shared/search/search-filters/search-filter/search-range-filter/search-range-filter.component.ts b/src/app/shared/search/search-filters/search-filter/search-range-filter/search-range-filter.component.ts index 63fa2af3214..17931280ec9 100644 --- a/src/app/shared/search/search-filters/search-filter/search-range-filter/search-range-filter.component.ts +++ b/src/app/shared/search/search-filters/search-filter/search-range-filter/search-range-filter.component.ts @@ -19,7 +19,6 @@ import { } from '@ngx-translate/core'; import { NouisliderComponent } from 'ng2-nouislider'; import { - BehaviorSubject, combineLatest as observableCombineLatest, of as observableOf, Subscription, @@ -34,19 +33,12 @@ import { RemoteDataBuildService } from '../../../../../core/cache/builders/remot import { RouteService } from '../../../../../core/services/route.service'; import { SearchService } from '../../../../../core/shared/search/search.service'; import { SearchConfigurationService } from '../../../../../core/shared/search/search-configuration.service'; -import { - FILTER_CONFIG, - IN_PLACE_SEARCH, - REFRESH_FILTER, - SCOPE, - SearchFilterService, -} from '../../../../../core/shared/search/search-filter.service'; +import { SearchFilterService } from '../../../../../core/shared/search/search-filter.service'; import { SEARCH_CONFIG_SERVICE } from '../../../../../my-dspace-page/my-dspace-configuration.service'; import { hasValue } from '../../../../empty.util'; import { DebounceDirective } from '../../../../utils/debounce.directive'; import { AppliedFilter } from '../../../models/applied-filter.model'; import { FacetValues } from '../../../models/facet-values.model'; -import { SearchFilterConfig } from '../../../models/search-filter-config.model'; import { facetLoad, SearchFacetFilterComponent, @@ -124,14 +116,15 @@ export class SearchRangeFilterComponent extends SearchFacetFilterComponent imple protected rdbs: RemoteDataBuildService, private translateService: TranslateService, @Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService, - @Inject(IN_PLACE_SEARCH) public inPlaceSearch: boolean, - @Inject(FILTER_CONFIG) public filterConfig: SearchFilterConfig, @Inject(PLATFORM_ID) private platformId: any, - @Inject(REFRESH_FILTER) public refreshFilters: BehaviorSubject, - @Inject(SCOPE) public scope: string, ) { - super(searchService, filterService, rdbs, router, searchConfigService, inPlaceSearch, filterConfig, refreshFilters, scope); - + super( + searchService, + filterService, + rdbs, + router, + searchConfigService, + ); } /** From 92cc112ed6878d3bbd39691bc16e84228428eb75 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Thu, 2 May 2024 11:15:53 +0200 Subject: [PATCH 14/24] 111731: Also minimize the search facets while switching scopes --- src/app/shared/search-form/search-form.component.spec.ts | 8 ++++++-- src/app/shared/search-form/search-form.component.ts | 3 +++ src/app/shared/search/search.component.ts | 1 - 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/app/shared/search-form/search-form.component.spec.ts b/src/app/shared/search-form/search-form.component.spec.ts index 584b7c5584c..64bbbae154d 100644 --- a/src/app/shared/search-form/search-form.component.spec.ts +++ b/src/app/shared/search-form/search-form.component.spec.ts @@ -17,15 +17,17 @@ import { BrowserOnlyMockPipe } from '../testing/browser-only-mock.pipe'; import { SearchServiceStub } from '../testing/search-service.stub'; import { Router } from '@angular/router'; import { RouterStub } from '../testing/router.stub'; +import { SearchFilterService } from '../../core/shared/search/search-filter.service'; +import { SearchFilterServiceStub } from '../testing/search-filter-service.stub'; describe('SearchFormComponent', () => { let comp: SearchFormComponent; let fixture: ComponentFixture; let de: DebugElement; - let el: HTMLElement; const router = new RouterStub(); const searchService = new SearchServiceStub(); + let searchFilterService: SearchFilterServiceStub; const paginationService = new PaginationServiceStub(); const searchConfigService = { paginationID: 'test-id' }; const dspaceObjectService = { @@ -33,11 +35,14 @@ describe('SearchFormComponent', () => { }; beforeEach(waitForAsync(() => { + searchFilterService = new SearchFilterServiceStub(); + return TestBed.configureTestingModule({ imports: [FormsModule, RouterTestingModule, TranslateModule.forRoot()], providers: [ { provide: Router, useValue: router }, { provide: SearchService, useValue: searchService }, + { provide: SearchFilterService, useValue: searchFilterService }, { provide: PaginationService, useValue: paginationService }, { provide: SearchConfigurationService, useValue: searchConfigService }, { provide: DSpaceObjectDataService, useValue: dspaceObjectService }, @@ -53,7 +58,6 @@ describe('SearchFormComponent', () => { fixture = TestBed.createComponent(SearchFormComponent); comp = fixture.componentInstance; // SearchFormComponent test instance de = fixture.debugElement.query(By.css('form')); - el = de.nativeElement; }); it('should not display scopes when showScopeSelector is false', fakeAsync(() => { diff --git a/src/app/shared/search-form/search-form.component.ts b/src/app/shared/search-form/search-form.component.ts index 151ded6f9e9..7474c1cca81 100644 --- a/src/app/shared/search-form/search-form.component.ts +++ b/src/app/shared/search-form/search-form.component.ts @@ -13,6 +13,7 @@ import { BehaviorSubject } from 'rxjs'; import { DSpaceObjectDataService } from '../../core/data/dspace-object-data.service'; import { getFirstSucceededRemoteDataPayload } from '../../core/shared/operators'; import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; +import { SearchFilterService } from '../../core/shared/search/search-filter.service'; @Component({ selector: 'ds-search-form', @@ -71,6 +72,7 @@ export class SearchFormComponent implements OnChanges { constructor( protected router: Router, protected searchService: SearchService, + protected searchFilterService: SearchFilterService, protected paginationService: PaginationService, protected searchConfig: SearchConfigurationService, protected modalService: NgbModal, @@ -107,6 +109,7 @@ export class SearchFormComponent implements OnChanges { */ onScopeChange(scope: DSpaceObject) { this.updateSearch({ scope: scope ? scope.uuid : undefined }); + this.searchFilterService.minimizeAll(); } /** diff --git a/src/app/shared/search/search.component.ts b/src/app/shared/search/search.component.ts index df8d1e4d64a..60d3d8a4dea 100644 --- a/src/app/shared/search/search.component.ts +++ b/src/app/shared/search/search.component.ts @@ -416,7 +416,6 @@ export class SearchComponent implements OnInit { * @private */ private retrieveFilters(searchOptions: PaginatedSearchOptions) { - this.filtersRD$.next(null); this.searchConfigService.getConfig(searchOptions.scope, searchOptions.configuration).pipe( getFirstCompletedRemoteData(), ).subscribe((filtersRD: RemoteData) => { From 561ccc398e100e29b2d999cea5ded777c377f43f Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Thu, 2 May 2024 21:59:10 +0200 Subject: [PATCH 15/24] 111731: Used yml config to configure the visibility advanced search component & its filters --- .../advanced-search.component.spec.ts | 3 ++ .../advanced-search.component.ts | 12 ++++- .../search-filters.component.spec.ts | 46 ++++++++----------- .../search-filters.component.ts | 8 +--- .../search-label/search-label.component.html | 2 +- .../search-sidebar.component.html | 3 +- .../search-sidebar.component.spec.ts | 5 ++ .../search-sidebar.component.ts | 10 ++++ src/assets/i18n/en.json5 | 26 ----------- 9 files changed, 52 insertions(+), 63 deletions(-) diff --git a/src/app/shared/search/advanced-search/advanced-search.component.spec.ts b/src/app/shared/search/advanced-search/advanced-search.component.spec.ts index 6289b0e5346..ea3394c4aeb 100644 --- a/src/app/shared/search/advanced-search/advanced-search.component.spec.ts +++ b/src/app/shared/search/advanced-search/advanced-search.component.spec.ts @@ -5,6 +5,8 @@ import { import { Router } from '@angular/router'; import { TranslateModule } from '@ngx-translate/core'; +import { APP_CONFIG } from '../../../../config/app-config.interface'; +import { environment } from '../../../../environments/environment.test'; import { SearchService } from '../../../core/shared/search/search.service'; import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service'; import { SearchFilterService } from '../../../core/shared/search/search-filter.service'; @@ -39,6 +41,7 @@ describe('AdvancedSearchComponent', () => { { provide: SearchService, useValue: searchService }, { provide: SearchConfigurationService, useValue: searchConfigurationService }, { provide: SearchFilterService, useValue: searchFilterService }, + { provide: APP_CONFIG, useValue: environment }, ], }).compileComponents(); diff --git a/src/app/shared/search/advanced-search/advanced-search.component.ts b/src/app/shared/search/advanced-search/advanced-search.component.ts index 22488e40462..92e43983c0e 100644 --- a/src/app/shared/search/advanced-search/advanced-search.component.ts +++ b/src/app/shared/search/advanced-search/advanced-search.component.ts @@ -5,6 +5,7 @@ import { } from '@angular/common'; import { Component, + Inject, Input, OnDestroy, OnInit, @@ -23,6 +24,10 @@ import { } from 'rxjs'; import { take } from 'rxjs/operators'; +import { + APP_CONFIG, + AppConfig, +} from '../../../../config/app-config.interface'; import { SearchService } from '../../../core/shared/search/search.service'; import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service'; import { SearchFilterService } from '../../../core/shared/search/search-filter.service'; @@ -84,12 +89,17 @@ export class AdvancedSearchComponent implements OnInit, OnDestroy { protected searchService: SearchService, protected searchConfigurationService: SearchConfigurationService, protected searchFilterService: SearchFilterService, + @Inject(APP_CONFIG) protected appConfig: AppConfig, ) { } ngOnInit(): void { this.advancedFilters$ = this.searchConfigurationService.getConfigurationSearchConfig(this.configuration).pipe( - map((searchConfiguration: SearchConfig) => searchConfiguration.filters.filter((filter: FilterConfig) => filter.type !== FilterType.range)), + map((searchConfiguration: SearchConfig) => { + return searchConfiguration.filters + .filter((filter: FilterConfig) => this.appConfig.search.advancedFilters.filter.includes(filter.filter)) + .filter((filter: FilterConfig) => filter.type !== FilterType.range); + }), ); this.subs.push(this.advancedFilters$.subscribe((filters: FilterConfig[]) => { const filterMap: Map = new Map(); diff --git a/src/app/shared/search/search-filters/search-filters.component.spec.ts b/src/app/shared/search/search-filters/search-filters.component.spec.ts index e81da7a8654..0277551eea1 100644 --- a/src/app/shared/search/search-filters/search-filters.component.spec.ts +++ b/src/app/shared/search/search-filters/search-filters.component.spec.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, - NO_ERRORS_SCHEMA, + CUSTOM_ELEMENTS_SCHEMA, } from '@angular/core'; import { ComponentFixture, @@ -8,48 +8,41 @@ import { waitForAsync, } from '@angular/core/testing'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { RouterTestingModule } from '@angular/router/testing'; +import { RouterModule } from '@angular/router'; import { TranslateModule } from '@ngx-translate/core'; -import { APP_CONFIG } from 'src/config/app-config.interface'; -import { environment } from 'src/environments/environment'; import { SearchService } from '../../../core/shared/search/search.service'; import { SearchFilterService } from '../../../core/shared/search/search-filter.service'; import { SEARCH_CONFIG_SERVICE } from '../../../my-dspace-page/my-dspace-configuration.service'; import { SearchConfigurationServiceStub } from '../../testing/search-configuration-service.stub'; +import { SearchFilterServiceStub } from '../../testing/search-filter-service.stub'; +import { SearchServiceStub } from '../../testing/search-service.stub'; import { SearchFiltersComponent } from './search-filters.component'; describe('SearchFiltersComponent', () => { let comp: SearchFiltersComponent; let fixture: ComponentFixture; - let searchService: SearchService; - const searchServiceStub = { - /* eslint-disable no-empty,@typescript-eslint/no-empty-function */ - getClearFiltersQueryParams: () => { - }, - getSearchLink: () => { - }, - getConfigurationSearchConfig: () => { }, - /* eslint-enable no-empty, @typescript-eslint/no-empty-function */ - }; - - const searchFiltersStub = { - getSelectedValuesForFilter: (filter) => - [], - }; + let searchService: SearchServiceStub; + let searchFilters: SearchFilterServiceStub; beforeEach(waitForAsync(() => { + searchService = new SearchServiceStub(); + searchFilters = new SearchFilterServiceStub(); + TestBed.configureTestingModule({ - imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NoopAnimationsModule, SearchFiltersComponent], + imports: [ + TranslateModule.forRoot(), + RouterModule.forRoot([]), + NoopAnimationsModule, + SearchFiltersComponent, + ], providers: [ - { provide: SearchService, useValue: searchServiceStub }, + { provide: SearchService, useValue: searchService }, { provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() }, - { provide: SearchFilterService, useValue: searchFiltersStub }, - { provide: APP_CONFIG, useValue: environment }, - + { provide: SearchFilterService, useValue: searchFilters }, ], - schemas: [NO_ERRORS_SCHEMA], + schemas: [CUSTOM_ELEMENTS_SCHEMA], }).overrideComponent(SearchFiltersComponent, { set: { changeDetection: ChangeDetectionStrategy.Default }, }).compileComponents(); @@ -59,13 +52,12 @@ describe('SearchFiltersComponent', () => { fixture = TestBed.createComponent(SearchFiltersComponent); comp = fixture.componentInstance; // SearchFiltersComponent test instance fixture.detectChanges(); - searchService = (comp as any).searchService; }); describe('when the getSearchLink method is called', () => { beforeEach(() => { spyOn(searchService, 'getSearchLink'); - (comp as any).getSearchLink(); + comp.getSearchLink(); }); it('should call getSearchLink on the searchService', () => { diff --git a/src/app/shared/search/search-filters/search-filters.component.ts b/src/app/shared/search/search-filters/search-filters.component.ts index bbd02979213..f8d3f574237 100644 --- a/src/app/shared/search/search-filters/search-filters.component.ts +++ b/src/app/shared/search/search-filters/search-filters.component.ts @@ -19,10 +19,6 @@ import { Observable, } from 'rxjs'; import { map } from 'rxjs/operators'; -import { - APP_CONFIG, - AppConfig, -} from 'src/config/app-config.interface'; import { RemoteData } from '../../../core/data/remote-data'; import { SearchService } from '../../../core/shared/search/search.service'; @@ -32,7 +28,6 @@ import { SEARCH_CONFIG_SERVICE } from '../../../my-dspace-page/my-dspace-configu import { currentPath } from '../../utils/route.utils'; import { AdvancedSearchComponent } from '../advanced-search/advanced-search.component'; import { AppliedFilter } from '../models/applied-filter.model'; -import { PaginatedSearchOptions } from '../models/paginated-search-options.model'; import { SearchFilterConfig } from '../models/search-filter-config.model'; import { SearchFilterComponent } from './search-filter/search-filter.component'; @@ -52,7 +47,7 @@ export class SearchFiltersComponent implements OnInit { * An observable containing configuration about which filters are shown and how they are shown */ @Input() filters: Observable>; - @Input() searchOptions: PaginatedSearchOptions; + /** * List of all filters that are currently active with their value set to null. * Used to reset all filters at once @@ -90,7 +85,6 @@ export class SearchFiltersComponent implements OnInit { filterLabel = 'search'; constructor( - @Inject(APP_CONFIG) protected appConfig: AppConfig, protected searchService: SearchService, protected searchFilterService: SearchFilterService, protected router: Router, diff --git a/src/app/shared/search/search-labels/search-label/search-label.component.html b/src/app/shared/search/search-labels/search-label/search-label.component.html index 24e8b0dc15b..58308d49280 100644 --- a/src/app/shared/search/search-labels/search-label/search-label.component.html +++ b/src/app/shared/search/search-labels/search-label/search-label.component.html @@ -4,7 +4,7 @@ [queryParams]="(removeParameters$ | async)" (click)="searchFilterService.minimizeAll()"> - {{ ('search.filters.applied.f.' + appliedFilter.filter) | translate}}: {{'search.filters.' + appliedFilter.filter + '.' + appliedFilter.label | translate: { default: appliedFilter.label } }} + {{ ('search.filters.applied.f.' + appliedFilter.filter) | translate}}{{'search.filters.applied.operator.' + appliedFilter.operator | translate}}: {{'search.filters.' + appliedFilter.filter + '.' + appliedFilter.label | translate: { default: appliedFilter.label } }} diff --git a/src/app/shared/search/search-sidebar/search-sidebar.component.html b/src/app/shared/search/search-sidebar/search-sidebar.component.html index ecd9dd9492a..b2ba43869de 100644 --- a/src/app/shared/search/search-sidebar/search-sidebar.component.html +++ b/src/app/shared/search/search-sidebar/search-sidebar.component.html @@ -24,7 +24,8 @@ [refreshFilters]="refreshFilters" [inPlaceSearch]="inPlaceSearch"> - { NgbCollapseModule, SearchSidebarComponent, ], + providers: [ + { provide: APP_CONFIG, useValue: environment }, + ], schemas: [CUSTOM_ELEMENTS_SCHEMA], }) .overrideComponent(SearchSidebarComponent, { diff --git a/src/app/shared/search/search-sidebar/search-sidebar.component.ts b/src/app/shared/search/search-sidebar/search-sidebar.component.ts index ef45e581ca1..b9166e54ad7 100644 --- a/src/app/shared/search/search-sidebar/search-sidebar.component.ts +++ b/src/app/shared/search/search-sidebar/search-sidebar.component.ts @@ -5,6 +5,7 @@ import { import { Component, EventEmitter, + Inject, Input, Output, } from '@angular/core'; @@ -14,6 +15,10 @@ import { Observable, } from 'rxjs'; +import { + APP_CONFIG, + AppConfig, +} from '../../../../config/app-config.interface'; import { SortOptions } from '../../../core/cache/models/sort-options.model'; import { RemoteData } from '../../../core/data/remote-data'; import { ViewMode } from '../../../core/shared/view-mode.model'; @@ -120,4 +125,9 @@ export class SearchSidebarComponent { */ @Output() changeViewMode: EventEmitter = new EventEmitter(); + constructor( + @Inject(APP_CONFIG) protected appConfig: AppConfig, + ) { + } + } diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index e19e20d2d84..8e2f90f39e7 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -6058,24 +6058,6 @@ "admin.notifications.publicationclaim.page.title": "Publication Claim", - "filter.search.operator.placeholder": "Operator", - - "search.filters.filter.entityType.text": "Item Type", - - "search.filters.operator.equals.text": "Equals", - - "search.filters.operator.notequals.text": "Not Equals", - - "search.filters.operator.notcontains.text": "Not Contains", - - "search.filters.operator.contains.text": "Contains", - - "search.filters.filter.title.text": "Title", - - "search.filters.applied.f.title": "Title", - - "search.filters.filter.author.text": "Author", - "coar-notify-support.title": "COAR Notify Protocol", "coar-notify-support-title.content": "Here, we fully support the COAR Notify protocol, which is designed to enhance the communication between repositories. To learn more about the COAR Notify protocol, visit the COAR Notify website.", @@ -6370,14 +6352,6 @@ "type-equals-journal-article_condition.label": "Type equals Journal Article", - "search.filters.filter.subject.text": "Subject", - - "search.advanced.filters.head": "Advanced Search", - - "filter.search.text.placeholder": "Search text", - - "advancesearch.form.submit": "Add", - "ldn.no-filter.label": "None", "admin.notify.dashboard": "Dashboard", From 029359bace033d3c9c97f25e3d9cda9dcefd0277 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Sat, 4 May 2024 02:52:06 +0200 Subject: [PATCH 16/24] 111731: Search page UI fixes - Hide advanced search when inPlaceSearch is false - Fixed the search settings being invisible on small screens in the sidebar - Fixed the view mode buttons not being selected unless the parameter view was defined in the url --- .../search/search-sidebar/search-sidebar.component.html | 2 +- .../search/search-sidebar/search-sidebar.component.ts | 2 +- src/app/shared/search/search.component.html | 3 ++- .../view-mode-switch/view-mode-switch.component.html | 7 +++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/app/shared/search/search-sidebar/search-sidebar.component.html b/src/app/shared/search/search-sidebar/search-sidebar.component.html index f10eb8009cb..9d6b4c4f955 100644 --- a/src/app/shared/search/search-sidebar/search-sidebar.component.html +++ b/src/app/shared/search/search-sidebar/search-sidebar.component.html @@ -24,7 +24,7 @@ [refreshFilters]="refreshFilters" [inPlaceSearch]="inPlaceSearch"> - diff --git a/src/app/shared/search/search-sidebar/search-sidebar.component.ts b/src/app/shared/search/search-sidebar/search-sidebar.component.ts index 8cae88ba033..1da7bd6a6ef 100644 --- a/src/app/shared/search/search-sidebar/search-sidebar.component.ts +++ b/src/app/shared/search/search-sidebar/search-sidebar.component.ts @@ -93,7 +93,7 @@ export class SearchSidebarComponent { /** * True when the search component should show results on the current page */ - @Input() inPlaceSearch: boolean; + @Input() inPlaceSearch = true; /** * The configuration for the current paginated search results diff --git a/src/app/shared/search/search.component.html b/src/app/shared/search/search.component.html index 640dd36ed15..e0902806920 100644 --- a/src/app/shared/search/search.component.html +++ b/src/app/shared/search/search.component.html @@ -74,10 +74,11 @@ [currentScope]="(currentScope$ | async)" [filters]="filtersRD$.asObservable()" [refreshFilters]="refreshFilters" - [resultCount]="(resultsRD$ | async)?.payload.totalElements" + [resultCount]="(resultsRD$ | async)?.payload?.totalElements" [searchOptions]="(searchOptions$ | async)" [sortOptionsList]="(sortOptionsList$ | async)" [currentSortOption]="(currentSortOptions$ | async)" + [inPlaceSearch]="inPlaceSearch" [viewModeList]="viewModeList" [showViewModes]="showViewModes" (toggleSidebar)="closeSidebar()" diff --git a/src/app/shared/view-mode-switch/view-mode-switch.component.html b/src/app/shared/view-mode-switch/view-mode-switch.component.html index 5d29562a766..74ee22533d4 100644 --- a/src/app/shared/view-mode-switch/view-mode-switch.component.html +++ b/src/app/shared/view-mode-switch/view-mode-switch.component.html @@ -6,7 +6,6 @@ [queryParams]="{view: 'list'}" queryParamsHandling="merge" (click)="switchViewTo(viewModeEnum.ListElement)" - routerLinkActive="active" [class.active]="currentMode === viewModeEnum.ListElement" class="btn btn-secondary" [attr.data-test]="'list-view' | dsBrowserOnly"> @@ -19,21 +18,21 @@ [queryParams]="{view: 'grid'}" queryParamsHandling="merge" (click)="switchViewTo(viewModeEnum.GridElement)" - routerLinkActive="active" [class.active]="currentMode === viewModeEnum.GridElement" class="btn btn-secondary" [attr.data-test]="'grid-view' | dsBrowserOnly"> From 806430064f0bb13a1c849e5870c4e85a0d8b8d39 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Sun, 5 May 2024 05:19:58 +0200 Subject: [PATCH 17/24] 111731: Hide advanced search section when no filters are available and hide it on the home page --- .../search/search-configuration.service.ts | 17 ++++++++++++++ .../advanced-search.component.ts | 22 ++++++++++++++---- .../search-sidebar.component.html | 6 +++-- .../search-sidebar.component.spec.ts | 23 ++++++++++++++----- .../search-sidebar.component.ts | 20 ++++++++++++++-- src/app/shared/search/search.component.html | 1 + .../search-configuration-service.stub.ts | 14 +++++++++-- 7 files changed, 86 insertions(+), 17 deletions(-) diff --git a/src/app/core/shared/search/search-configuration.service.ts b/src/app/core/shared/search/search-configuration.service.ts index 968e41b1cde..ee559049fd3 100644 --- a/src/app/core/shared/search/search-configuration.service.ts +++ b/src/app/core/shared/search/search-configuration.service.ts @@ -29,6 +29,8 @@ import { ViewMode } from '../view-mode.model'; import { SearchFilterConfig } from '../../../shared/search/models/search-filter-config.model'; import { FacetConfigResponse } from '../../../shared/search/models/facet-config-response.model'; import { addOperatorToFilterValue } from '../../../shared/search/search.utils'; +import { FilterConfig } from './search-filters/search-config.model'; +import { FilterType } from '../../../shared/search/models/filter-type.model'; /** * Service that performs all actions that have to do with the current search configuration @@ -251,6 +253,21 @@ export class SearchConfigurationService implements OnDestroy { })); } + /** + * Return the {@link FilterConfig}s of the filters that should be displayed for the current configuration/scope + * + * @param configuration The search configuration + * @param scope The scope if exists + */ + public getConfigurationAdvancedSearchFilters(configuration: string, scope?: string): Observable { + return this.getConfigurationSearchConfig(configuration, scope).pipe( + map((searchConfiguration: SearchConfig) => { + return searchConfiguration.filters + .filter((filterConfig: FilterConfig) => filterConfig.type !== FilterType.range); + }), + ); + } + setPaginationId(paginationId): void { if (isNotEmpty(paginationId)) { const currentValue: PaginatedSearchOptions = this.paginatedSearchOptions.getValue(); diff --git a/src/app/shared/search/advanced-search/advanced-search.component.ts b/src/app/shared/search/advanced-search/advanced-search.component.ts index 020027ea106..ef9b36ba4d2 100644 --- a/src/app/shared/search/advanced-search/advanced-search.component.ts +++ b/src/app/shared/search/advanced-search/advanced-search.component.ts @@ -1,8 +1,8 @@ import { Component, Input, OnInit, OnDestroy } from '@angular/core'; -import { map, Observable, of as observableOf, Subscription } from 'rxjs'; +import { Observable, of as observableOf, Subscription } from 'rxjs'; import { take } from 'rxjs/operators'; import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service'; -import { FilterConfig, SearchConfig } from '../../../core/shared/search/search-filters/search-config.model'; +import { FilterConfig } from '../../../core/shared/search/search-filters/search-config.model'; import { SearchFilterService } from '../../../core/shared/search/search-filter.service'; import { SearchFilterConfig } from '../models/search-filter-config.model'; import { Router, Params } from '@angular/router'; @@ -11,6 +11,9 @@ import { hasValue, isNotEmpty } from '../../empty.util'; import { SearchService } from '../../../core/shared/search/search.service'; import { FilterType } from '../models/filter-type.model'; +/** + * This component represents the advanced search in the search sidebar. + */ @Component({ selector: 'ds-advanced-search', templateUrl: './advanced-search.component.html', @@ -18,10 +21,21 @@ import { FilterType } from '../models/filter-type.model'; }) export class AdvancedSearchComponent implements OnInit, OnDestroy { + /** + * The current search configuration + */ @Input() configuration: string; + /** + * The facet configurations, used to determine if suggestions should be retrieved for the selected search filter + */ @Input() filtersConfig: SearchFilterConfig[]; + /** + * The current search scope + */ + @Input() scope: string; + advancedFilters$: Observable; advancedFilterMap: Map = new Map(); @@ -51,9 +65,7 @@ export class AdvancedSearchComponent implements OnInit, OnDestroy { } ngOnInit(): void { - this.advancedFilters$ = this.searchConfigurationService.getConfigurationSearchConfig(this.configuration).pipe( - map((searchConfiguration: SearchConfig) => searchConfiguration.filters.filter((filter: FilterConfig) => filter.type !== FilterType.range)), - ); + this.advancedFilters$ = this.searchConfigurationService.getConfigurationAdvancedSearchFilters(this.configuration, this.scope); this.subs.push(this.advancedFilters$.subscribe((filters: FilterConfig[]) => { const filterMap: Map = new Map(); if (filters.length > 0) { diff --git a/src/app/shared/search/search-sidebar/search-sidebar.component.html b/src/app/shared/search/search-sidebar/search-sidebar.component.html index 5faba679eaf..a4e840612e2 100644 --- a/src/app/shared/search/search-sidebar/search-sidebar.component.html +++ b/src/app/shared/search/search-sidebar/search-sidebar.component.html @@ -23,8 +23,10 @@ [refreshFilters]="refreshFilters" [inPlaceSearch]="inPlaceSearch"> - + diff --git a/src/app/shared/search/search-sidebar/search-sidebar.component.spec.ts b/src/app/shared/search/search-sidebar/search-sidebar.component.spec.ts index a16387cf51c..3276940a159 100644 --- a/src/app/shared/search/search-sidebar/search-sidebar.component.spec.ts +++ b/src/app/shared/search/search-sidebar/search-sidebar.component.spec.ts @@ -3,19 +3,30 @@ import { TranslateModule } from '@ngx-translate/core'; import { SearchSidebarComponent } from './search-sidebar.component'; import { NgbCollapseModule } from '@ng-bootstrap/ng-bootstrap'; import { By } from '@angular/platform-browser'; -import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { SearchConfigurationServiceStub } from '../../testing/search-configuration-service.stub'; +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service'; describe('SearchSidebarComponent', () => { let comp: SearchSidebarComponent; let fixture: ComponentFixture; - // waitForAsync beforeEach + + let searchConfigurationService: SearchConfigurationServiceStub; + beforeEach(waitForAsync(() => { + searchConfigurationService = new SearchConfigurationServiceStub(); + TestBed.configureTestingModule({ - imports: [TranslateModule.forRoot(), NgbCollapseModule], + imports: [ + TranslateModule.forRoot(), + NgbCollapseModule, + ], declarations: [SearchSidebarComponent], - schemas: [NO_ERRORS_SCHEMA], - }) - .compileComponents(); // compile template and css + providers: [ + { provide: SearchConfigurationService, useValue: searchConfigurationService }, + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + }).compileComponents(); })); // synchronous beforeEach diff --git a/src/app/shared/search/search-sidebar/search-sidebar.component.ts b/src/app/shared/search/search-sidebar/search-sidebar.component.ts index 9eb31cffe15..ed182335e07 100644 --- a/src/app/shared/search/search-sidebar/search-sidebar.component.ts +++ b/src/app/shared/search/search-sidebar/search-sidebar.component.ts @@ -1,4 +1,5 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { Component, EventEmitter, Input, Output, OnInit } from '@angular/core'; +import { map } from 'rxjs/operators'; import { SearchConfigurationOption } from '../search-switch-configuration/search-configuration-option.model'; import { BehaviorSubject, Observable } from 'rxjs'; @@ -7,6 +8,8 @@ import { SortOptions } from '../../../core/cache/models/sort-options.model'; import { ViewMode } from '../../../core/shared/view-mode.model'; import { RemoteData } from '../../../core/data/remote-data'; import { SearchFilterConfig } from '../models/search-filter-config.model'; +import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service'; +import { FilterConfig } from '../../../core/shared/search/search-filters/search-config.model'; /** * This component renders a simple item page. @@ -23,7 +26,7 @@ import { SearchFilterConfig } from '../models/search-filter-config.model'; /** * Component representing the sidebar on the search page */ -export class SearchSidebarComponent { +export class SearchSidebarComponent implements OnInit { /** * The configuration to use for the search options @@ -100,4 +103,17 @@ export class SearchSidebarComponent { */ @Output() changeViewMode: EventEmitter = new EventEmitter(); + showAdvancedSearch$: Observable; + + constructor( + protected searchConfigurationService: SearchConfigurationService, + ) { + } + + ngOnInit(): void { + this.showAdvancedSearch$ = this.searchConfigurationService.getConfigurationAdvancedSearchFilters(this.configuration, this.currentScope).pipe( + map((advancedFilters: FilterConfig[]) => advancedFilters.length > 0), + ); + } + } diff --git a/src/app/shared/search/search.component.html b/src/app/shared/search/search.component.html index 01192411617..792868f2ae6 100644 --- a/src/app/shared/search/search.component.html +++ b/src/app/shared/search/search.component.html @@ -72,6 +72,7 @@ [searchOptions]="(searchOptions$ | async)" [sortOptionsList]="(sortOptionsList$ | async)" [currentSortOption]="(currentSortOptions$ | async)" + [inPlaceSearch]="inPlaceSearch" [viewModeList]="viewModeList" [showViewModes]="showViewModes" (toggleSidebar)="closeSidebar()" diff --git a/src/app/shared/testing/search-configuration-service.stub.ts b/src/app/shared/testing/search-configuration-service.stub.ts index fff5fff7016..cc874a0e4fb 100644 --- a/src/app/shared/testing/search-configuration-service.stub.ts +++ b/src/app/shared/testing/search-configuration-service.stub.ts @@ -1,7 +1,13 @@ import { BehaviorSubject, of as observableOf, Observable } from 'rxjs'; import { Params } from '@angular/router'; -import { SearchConfig } from '../../core/shared/search/search-filters/search-config.model'; - +import { + FilterConfig, + SearchConfig, +} from '../../core/shared/search/search-filters/search-config.model'; + +/** + * Stub class of {@link SearchConfigurationService} + */ export class SearchConfigurationServiceStub { public paginationID = 'test-id'; @@ -25,6 +31,10 @@ export class SearchConfigurationServiceStub { return observableOf(a); } + getConfigurationAdvancedSearchFilters(_configuration: string, _scope?: string): Observable { + return observableOf([]); + } + getConfig () { return observableOf({ hasSucceeded: true, payload: [] }); } From 806b07c084b8a16f6ae5993fa073f0c5544704ac Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Sun, 5 May 2024 15:28:54 +0200 Subject: [PATCH 18/24] 111731: Fixed tests not mocking SearchConfigurationService --- .../groups-registry.component.spec.ts | 22 ++-- .../groups-registry.component.ts | 6 +- .../admin-notify-incoming.component.spec.ts | 47 ++----- ...admin-notify-logs-result.component.spec.ts | 45 +++---- .../admin-notify-logs-result.component.ts | 10 +- .../admin-notify-outgoing.component.spec.ts | 51 +++----- ...min-notify-search-result.component.spec.ts | 82 ++++-------- .../bitstream-formats.component.spec.ts | 119 +++++------------- .../bitstream-formats.component.ts | 6 +- .../search-configuration.service.spec.ts | 3 +- .../search/search-configuration.service.ts | 28 ++--- .../my-dspace-configuration.service.spec.ts | 3 +- .../my-dspace-configuration.service.ts | 33 ++--- .../search-sidebar.component.spec.ts | 37 +++--- 14 files changed, 161 insertions(+), 331 deletions(-) diff --git a/src/app/access-control/group-registry/groups-registry.component.spec.ts b/src/app/access-control/group-registry/groups-registry.component.spec.ts index 9f847faf0b0..43c72843e5b 100644 --- a/src/app/access-control/group-registry/groups-registry.component.spec.ts +++ b/src/app/access-control/group-registry/groups-registry.component.spec.ts @@ -22,10 +22,7 @@ import { } from '@angular/router'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { provideMockStore } from '@ngrx/store/testing'; -import { - TranslateLoader, - TranslateModule, -} from '@ngx-translate/core'; +import { TranslateModule } from '@ngx-translate/core'; import { Observable, of as observableOf, @@ -59,6 +56,7 @@ import { } from '../../shared/mocks/dso-name.service.mock'; import { RouterMock } from '../../shared/mocks/router.mock'; import { NotificationsService } from '../../shared/notifications/notifications.service'; +import { PaginationComponent } from '../../shared/pagination/pagination.component'; import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils'; import { ActivatedRouteStub } from '../../shared/testing/active-router.stub'; import { @@ -72,7 +70,6 @@ import { import { NotificationsServiceStub } from '../../shared/testing/notifications-service.stub'; import { PaginationServiceStub } from '../../shared/testing/pagination-service.stub'; import { routeServiceStub } from '../../shared/testing/route-service.stub'; -import { TranslateLoaderMock } from '../../shared/testing/translate-loader.mock'; import { GroupsRegistryComponent } from './groups-registry.component'; describe('GroupsRegistryComponent', () => { @@ -209,12 +206,9 @@ describe('GroupsRegistryComponent', () => { paginationService = new PaginationServiceStub(); return TestBed.configureTestingModule({ imports: [CommonModule, NgbModule, FormsModule, ReactiveFormsModule, BrowserModule, - TranslateModule.forRoot({ - loader: { - provide: TranslateLoader, - useClass: TranslateLoaderMock, - }, - }), GroupsRegistryComponent], + TranslateModule.forRoot(), + GroupsRegistryComponent, + ], providers: [GroupsRegistryComponent, { provide: DSONameService, useValue: new DSONameServiceMock() }, { provide: EPersonDataService, useValue: ePersonDataServiceStub }, @@ -232,6 +226,12 @@ describe('GroupsRegistryComponent', () => { provideMockStore(), ], schemas: [NO_ERRORS_SCHEMA], + }).overrideComponent(GroupsRegistryComponent, { + remove: { + imports: [ + PaginationComponent, + ], + }, }).compileComponents(); })); diff --git a/src/app/access-control/group-registry/groups-registry.component.ts b/src/app/access-control/group-registry/groups-registry.component.ts index d97284488a9..dec3dc955d9 100644 --- a/src/app/access-control/group-registry/groups-registry.component.ts +++ b/src/app/access-control/group-registry/groups-registry.component.ts @@ -14,10 +14,7 @@ import { ReactiveFormsModule, UntypedFormBuilder, } from '@angular/forms'; -import { - Router, - RouterLink, -} from '@angular/router'; +import { RouterLink } from '@angular/router'; import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'; import { TranslateModule, @@ -149,7 +146,6 @@ export class GroupsRegistryComponent implements OnInit, OnDestroy { private notificationsService: NotificationsService, private formBuilder: UntypedFormBuilder, protected routeService: RouteService, - private router: Router, private authorizationService: AuthorizationDataService, private paginationService: PaginationService, public requestService: RequestService, diff --git a/src/app/admin/admin-notify-dashboard/admin-notify-logs/admin-notify-incoming/admin-notify-incoming.component.spec.ts b/src/app/admin/admin-notify-dashboard/admin-notify-logs/admin-notify-incoming/admin-notify-incoming.component.spec.ts index 054dac3218a..4c99c117e88 100644 --- a/src/app/admin/admin-notify-dashboard/admin-notify-logs/admin-notify-incoming/admin-notify-incoming.component.spec.ts +++ b/src/app/admin/admin-notify-dashboard/admin-notify-logs/admin-notify-incoming/admin-notify-incoming.component.spec.ts @@ -2,57 +2,34 @@ import { ComponentFixture, TestBed, } from '@angular/core/testing'; -import { ActivatedRoute } from '@angular/router'; -import { provideMockStore } from '@ngrx/store/testing'; +import { RouterModule } from '@angular/router'; import { TranslateModule } from '@ngx-translate/core'; -import { APP_DATA_SERVICES_MAP } from '../../../../../config/app-config.interface'; -import { RemoteDataBuildService } from '../../../../core/cache/builders/remote-data-build.service'; -import { RequestService } from '../../../../core/data/request.service'; -import { RouteService } from '../../../../core/services/route.service'; -import { HALEndpointService } from '../../../../core/shared/hal-endpoint.service'; -import { SearchConfigurationService } from '../../../../core/shared/search/search-configuration.service'; import { SEARCH_CONFIG_SERVICE } from '../../../../my-dspace-page/my-dspace-configuration.service'; -import { MockActivatedRoute } from '../../../../shared/mocks/active-router.mock'; -import { getMockRemoteDataBuildService } from '../../../../shared/mocks/remote-data-build.service.mock'; -import { routeServiceStub } from '../../../../shared/testing/route-service.stub'; +import { SearchConfigurationServiceStub } from '../../../../shared/testing/search-configuration-service.stub'; import { AdminNotifyLogsResultComponent } from '../admin-notify-logs-result/admin-notify-logs-result.component'; import { AdminNotifyIncomingComponent } from './admin-notify-incoming.component'; describe('AdminNotifyIncomingComponent', () => { let component: AdminNotifyIncomingComponent; let fixture: ComponentFixture; - let halService: HALEndpointService; - let requestService: RequestService; - let rdbService: RemoteDataBuildService; - + let searchConfigurationService: SearchConfigurationServiceStub; beforeEach(async () => { - rdbService = getMockRemoteDataBuildService(); - halService = jasmine.createSpyObj('halService', { - 'getRootHref': '/api', - }); - requestService = jasmine.createSpyObj('requestService', { - 'generateRequestId': 'client/1234', - 'send': '', - }); + searchConfigurationService = new SearchConfigurationServiceStub(); + await TestBed.configureTestingModule({ - imports: [TranslateModule.forRoot(), AdminNotifyIncomingComponent], - providers: [ - { provide: SEARCH_CONFIG_SERVICE, useValue: SearchConfigurationService }, - { provide: RouteService, useValue: routeServiceStub }, - { provide: ActivatedRoute, useValue: new MockActivatedRoute() }, - { provide: HALEndpointService, useValue: halService }, - { provide: RequestService, useValue: requestService }, - { provide: RemoteDataBuildService, useValue: rdbService }, - { provide: APP_DATA_SERVICES_MAP, useValue: {} }, - provideMockStore({}), + imports: [ + AdminNotifyIncomingComponent, + RouterModule.forRoot([]), + TranslateModule.forRoot(), ], + }).overrideProvider(SEARCH_CONFIG_SERVICE, { + useValue: searchConfigurationService, }).overrideComponent(AdminNotifyIncomingComponent, { remove: { imports: [AdminNotifyLogsResultComponent] }, - }) - .compileComponents(); + }).compileComponents(); fixture = TestBed.createComponent(AdminNotifyIncomingComponent); component = fixture.componentInstance; diff --git a/src/app/admin/admin-notify-dashboard/admin-notify-logs/admin-notify-logs-result/admin-notify-logs-result.component.spec.ts b/src/app/admin/admin-notify-dashboard/admin-notify-logs/admin-notify-logs-result/admin-notify-logs-result.component.spec.ts index 985e58bfcba..7d996898316 100644 --- a/src/app/admin/admin-notify-dashboard/admin-notify-logs/admin-notify-logs-result/admin-notify-logs-result.component.spec.ts +++ b/src/app/admin/admin-notify-dashboard/admin-notify-logs/admin-notify-logs-result/admin-notify-logs-result.component.spec.ts @@ -6,54 +6,41 @@ import { ActivatedRoute, Router, } from '@angular/router'; -import { provideMockStore } from '@ngrx/store/testing'; import { TranslateModule } from '@ngx-translate/core'; -import { APP_DATA_SERVICES_MAP } from '../../../../../config/app-config.interface'; -import { RemoteDataBuildService } from '../../../../core/cache/builders/remote-data-build.service'; -import { ObjectCacheService } from '../../../../core/cache/object-cache.service'; -import { RequestService } from '../../../../core/data/request.service'; -import { RouteService } from '../../../../core/services/route.service'; -import { HALEndpointService } from '../../../../core/shared/hal-endpoint.service'; +import { SEARCH_CONFIG_SERVICE } from '../../../../my-dspace-page/my-dspace-configuration.service'; import { MockActivatedRoute } from '../../../../shared/mocks/active-router.mock'; import { SearchLabelsComponent } from '../../../../shared/search/search-labels/search-labels.component'; import { ThemedSearchComponent } from '../../../../shared/search/themed-search.component'; -import { routeServiceStub } from '../../../../shared/testing/route-service.stub'; import { RouterStub } from '../../../../shared/testing/router.stub'; +import { SearchConfigurationServiceStub } from '../../../../shared/testing/search-configuration-service.stub'; import { AdminNotifyLogsResultComponent } from './admin-notify-logs-result.component'; describe('AdminNotifyLogsResultComponent', () => { let component: AdminNotifyLogsResultComponent; let fixture: ComponentFixture; - let objectCache: ObjectCacheService; - let requestService: RequestService; - let halService: HALEndpointService; - let rdbService: RemoteDataBuildService; + + let searchConfigurationService: SearchConfigurationServiceStub; beforeEach(async () => { + searchConfigurationService = new SearchConfigurationServiceStub(); + await TestBed.configureTestingModule({ imports: [TranslateModule.forRoot(), AdminNotifyLogsResultComponent], providers: [ - { provide: RouteService, useValue: routeServiceStub }, { provide: Router, useValue: new RouterStub() }, { provide: ActivatedRoute, useValue: new MockActivatedRoute() }, - { provide: HALEndpointService, useValue: halService }, - { provide: ObjectCacheService, useValue: objectCache }, - { provide: RequestService, useValue: requestService }, - { provide: APP_DATA_SERVICES_MAP, useValue: {} }, - { provide: RemoteDataBuildService, useValue: rdbService }, - provideMockStore({}), ], - }) - .overrideComponent(AdminNotifyLogsResultComponent, { - remove: { - imports: [ - SearchLabelsComponent, - ThemedSearchComponent, - ], - }, - }) - .compileComponents(); + }).overrideProvider(SEARCH_CONFIG_SERVICE, { + useValue: searchConfigurationService, + }).overrideComponent(AdminNotifyLogsResultComponent, { + remove: { + imports: [ + SearchLabelsComponent, + ThemedSearchComponent, + ], + }, + }).compileComponents(); fixture = TestBed.createComponent(AdminNotifyLogsResultComponent); component = fixture.componentInstance; diff --git a/src/app/admin/admin-notify-dashboard/admin-notify-logs/admin-notify-logs-result/admin-notify-logs-result.component.ts b/src/app/admin/admin-notify-dashboard/admin-notify-logs/admin-notify-logs-result/admin-notify-logs-result.component.ts index 33ac2b4cb18..2c6164ecda3 100644 --- a/src/app/admin/admin-notify-dashboard/admin-notify-logs/admin-notify-logs-result/admin-notify-logs-result.component.ts +++ b/src/app/admin/admin-notify-dashboard/admin-notify-logs/admin-notify-logs-result/admin-notify-logs-result.component.ts @@ -3,7 +3,6 @@ import { NgIf, } from '@angular/common'; import { - ChangeDetectorRef, Component, Inject, Input, @@ -59,10 +58,11 @@ export class AdminNotifyLogsResultComponent implements OnInit { protected readonly context = Context.CoarNotify; - constructor(@Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService, - private router: Router, - private route: ActivatedRoute, - protected cdRef: ChangeDetectorRef) { + constructor( + @Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService, + protected router: Router, + protected route: ActivatedRoute, + ) { } ngOnInit() { diff --git a/src/app/admin/admin-notify-dashboard/admin-notify-logs/admin-notify-outgoing/admin-notify-outgoing.component.spec.ts b/src/app/admin/admin-notify-dashboard/admin-notify-logs/admin-notify-outgoing/admin-notify-outgoing.component.spec.ts index 0cdb87ac17b..7eb19a649ba 100644 --- a/src/app/admin/admin-notify-dashboard/admin-notify-logs/admin-notify-outgoing/admin-notify-outgoing.component.spec.ts +++ b/src/app/admin/admin-notify-dashboard/admin-notify-logs/admin-notify-outgoing/admin-notify-outgoing.component.spec.ts @@ -2,57 +2,34 @@ import { ComponentFixture, TestBed, } from '@angular/core/testing'; -import { ActivatedRoute } from '@angular/router'; -import { provideMockStore } from '@ngrx/store/testing'; +import { RouterModule } from '@angular/router'; import { TranslateModule } from '@ngx-translate/core'; -import { APP_DATA_SERVICES_MAP } from '../../../../../config/app-config.interface'; -import { RemoteDataBuildService } from '../../../../core/cache/builders/remote-data-build.service'; -import { RequestService } from '../../../../core/data/request.service'; -import { RouteService } from '../../../../core/services/route.service'; -import { HALEndpointService } from '../../../../core/shared/hal-endpoint.service'; -import { SearchConfigurationService } from '../../../../core/shared/search/search-configuration.service'; import { SEARCH_CONFIG_SERVICE } from '../../../../my-dspace-page/my-dspace-configuration.service'; -import { MockActivatedRoute } from '../../../../shared/mocks/active-router.mock'; -import { getMockRemoteDataBuildService } from '../../../../shared/mocks/remote-data-build.service.mock'; -import { routeServiceStub } from '../../../../shared/testing/route-service.stub'; +import { SearchConfigurationServiceStub } from '../../../../shared/testing/search-configuration-service.stub'; import { AdminNotifyLogsResultComponent } from '../admin-notify-logs-result/admin-notify-logs-result.component'; import { AdminNotifyOutgoingComponent } from './admin-notify-outgoing.component'; describe('AdminNotifyOutgoingComponent', () => { let component: AdminNotifyOutgoingComponent; let fixture: ComponentFixture; - let halService: HALEndpointService; - let requestService: RequestService; - let rdbService: RemoteDataBuildService; + let searchConfigurationService: SearchConfigurationServiceStub; beforeEach(async () => { - rdbService = getMockRemoteDataBuildService(); - requestService = jasmine.createSpyObj('requestService', { - 'generateRequestId': 'client/1234', - 'send': '', - }); - halService = jasmine.createSpyObj('halService', { - 'getRootHref': '/api', - }); + searchConfigurationService = new SearchConfigurationServiceStub(); + await TestBed.configureTestingModule({ - imports: [TranslateModule.forRoot()], - providers: [ - { provide: SEARCH_CONFIG_SERVICE, useValue: SearchConfigurationService }, - { provide: APP_DATA_SERVICES_MAP, useValue: {} }, - { provide: RouteService, useValue: routeServiceStub }, - { provide: ActivatedRoute, useValue: new MockActivatedRoute() }, - { provide: HALEndpointService, useValue: halService }, - { provide: RequestService, useValue: requestService }, - { provide: RemoteDataBuildService, useValue: rdbService }, - provideMockStore({}), + imports: [ + AdminNotifyOutgoingComponent, + RouterModule.forRoot([]), + TranslateModule.forRoot(), ], - }) - .overrideComponent(AdminNotifyOutgoingComponent, { - remove: { imports: [AdminNotifyLogsResultComponent] }, - }) - .compileComponents(); + }).overrideProvider(SEARCH_CONFIG_SERVICE, { + useValue: searchConfigurationService, + }).overrideComponent(AdminNotifyOutgoingComponent, { + remove: { imports: [AdminNotifyLogsResultComponent] }, + }).compileComponents(); fixture = TestBed.createComponent(AdminNotifyOutgoingComponent); component = fixture.componentInstance; diff --git a/src/app/admin/admin-notify-dashboard/admin-notify-search-result/admin-notify-search-result.component.spec.ts b/src/app/admin/admin-notify-dashboard/admin-notify-search-result/admin-notify-search-result.component.spec.ts index 3bab1ab5bbd..02f4f024e85 100644 --- a/src/app/admin/admin-notify-dashboard/admin-notify-search-result/admin-notify-search-result.component.spec.ts +++ b/src/app/admin/admin-notify-dashboard/admin-notify-search-result/admin-notify-search-result.component.spec.ts @@ -1,29 +1,19 @@ import { DatePipe } from '@angular/common'; -import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { ComponentFixture, TestBed, } from '@angular/core/testing'; -import { ActivatedRoute } from '@angular/router'; +import { RouterModule } from '@angular/router'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { TranslateModule } from '@ngx-translate/core'; -import { cold } from 'jasmine-marbles'; import { of as observableOf, of, } from 'rxjs'; -import { APP_DATA_SERVICES_MAP } from '../../../../config/app-config.interface'; -import { RemoteDataBuildService } from '../../../core/cache/builders/remote-data-build.service'; -import { ObjectCacheService } from '../../../core/cache/object-cache.service'; -import { RequestService } from '../../../core/data/request.service'; -import { RouteService } from '../../../core/services/route.service'; -import { ConfigurationProperty } from '../../../core/shared/configuration-property.model'; -import { HALEndpointService } from '../../../core/shared/hal-endpoint.service'; import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service'; import { SEARCH_CONFIG_SERVICE } from '../../../my-dspace-page/my-dspace-configuration.service'; -import { routeServiceStub } from '../../../shared/testing/route-service.stub'; -import { RouterStub } from '../../../shared/testing/router.stub'; import { TruncatableComponent } from '../../../shared/truncatable/truncatable.component'; import { TruncatablePartComponent } from '../../../shared/truncatable/truncatable-part/truncatable-part.component'; import { AdminNotifyDetailModalComponent } from '../admin-notify-detail-modal/admin-notify-detail-modal.component'; @@ -90,76 +80,46 @@ export const mockAdminNotifyMessages = [ describe('AdminNotifySearchResultComponent', () => { let component: AdminNotifySearchResultComponent; let fixture: ComponentFixture; - let objectCache: ObjectCacheService; - let requestService: RequestService; - let halService: HALEndpointService; - let rdbService: RemoteDataBuildService; + let adminNotifyMessageService: AdminNotifyMessagesService; let searchConfigService: SearchConfigurationService; let modalService: NgbModal; - const requestUUID = '34cfed7c-f597-49ef-9cbe-ea351f0023c2'; - const testObject = { - uuid: 'test-property', - name: 'test-property', - values: ['value-1', 'value-2'], - } as ConfigurationProperty; beforeEach(async () => { - halService = jasmine.createSpyObj('halService', { - getEndpoint: cold('a', { a: '' }), - }); adminNotifyMessageService = jasmine.createSpyObj('adminNotifyMessageService', { getDetailedMessages: of(mockAdminNotifyMessages), reprocessMessage: of(mockAdminNotifyMessages), }); - requestService = jasmine.createSpyObj('requestService', { - generateRequestId: requestUUID, - send: true, - }); - rdbService = jasmine.createSpyObj('rdbService', { - buildSingle: cold('a', { - a: { - payload: testObject, - }, - }), - }); - searchConfigService = jasmine.createSpyObj('searchConfigService', { getCurrentConfiguration: of('NOTIFY.outgoing'), }); - objectCache = {} as ObjectCacheService; - await TestBed.configureTestingModule({ - imports: [TranslateModule.forRoot(), AdminNotifySearchResultComponent, AdminNotifyDetailModalComponent], + imports: [ + AdminNotifyDetailModalComponent, + AdminNotifySearchResultComponent, + RouterModule.forRoot([]), + TranslateModule.forRoot(), + ], providers: [ { provide: AdminNotifyMessagesService, useValue: adminNotifyMessageService }, - { provide: RouteService, useValue: routeServiceStub }, - { provide: ActivatedRoute, useValue: new RouterStub() }, - { provide: HALEndpointService, useValue: halService }, - { provide: ObjectCacheService, useValue: objectCache }, - { provide: RequestService, useValue: requestService }, - { provide: RemoteDataBuildService, useValue: rdbService }, - { provide: SEARCH_CONFIG_SERVICE, useValue: searchConfigService }, - { provide: APP_DATA_SERVICES_MAP, useValue: {} }, DatePipe, ], - schemas: [NO_ERRORS_SCHEMA], - }) - .overrideComponent(AdminNotifySearchResultComponent, { - remove: { - imports: [ - TruncatableComponent, - TruncatablePartComponent, - ], - }, - }) - .compileComponents(); + schemas: [CUSTOM_ELEMENTS_SCHEMA], + }).overrideProvider(SEARCH_CONFIG_SERVICE, { + useValue: searchConfigService, + }).overrideComponent(AdminNotifySearchResultComponent, { + remove: { + imports: [ + TruncatableComponent, + TruncatablePartComponent, + ], + }, + }).compileComponents(); fixture = TestBed.createComponent(AdminNotifySearchResultComponent); component = fixture.componentInstance; - component.searchConfigService = searchConfigService; - modalService = (component as any).modalService; + modalService = TestBed.inject(NgbModal); spyOn(modalService, 'open').and.returnValue(Object.assign({ componentInstance: Object.assign({ response: observableOf(true) }) })); fixture.detectChanges(); }); diff --git a/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.spec.ts b/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.spec.ts index 9ee767cda05..248e60ca4b0 100644 --- a/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.spec.ts +++ b/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.spec.ts @@ -1,38 +1,23 @@ -import { CommonModule } from '@angular/common'; -import { - Component, - NO_ERRORS_SCHEMA, -} from '@angular/core'; +import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { ComponentFixture, TestBed, waitForAsync, } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; -import { RouterTestingModule } from '@angular/router/testing'; -import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { RouterModule } from '@angular/router'; import { provideMockStore } from '@ngrx/store/testing'; import { TranslateModule } from '@ngx-translate/core'; import { cold, - getTestScheduler, hot, } from 'jasmine-marbles'; -import { - of as observableOf, - of, -} from 'rxjs'; -import { TestScheduler } from 'rxjs/testing'; +import { of as observableOf } from 'rxjs'; -import { APP_DATA_SERVICES_MAP } from '../../../../config/app-config.interface'; import { BitstreamFormatDataService } from '../../../core/data/bitstream-format-data.service'; -import { ConfigurationDataService } from '../../../core/data/configuration-data.service'; -import { GroupDataService } from '../../../core/eperson/group-data.service'; import { PaginationService } from '../../../core/pagination/pagination.service'; import { BitstreamFormat } from '../../../core/shared/bitstream-format.model'; import { BitstreamFormatSupportLevel } from '../../../core/shared/bitstream-format-support-level'; -import { XSRFService } from '../../../core/xsrf/xsrf.service'; -import { HostWindowService } from '../../../shared/host-window.service'; import { NotificationsService } from '../../../shared/notifications/notifications.service'; import { PaginationComponent } from '../../../shared/pagination/pagination.component'; import { @@ -41,7 +26,6 @@ import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$, } from '../../../shared/remote-data.utils'; -import { HostWindowServiceStub } from '../../../shared/testing/host-window-service.stub'; import { NotificationsServiceStub } from '../../../shared/testing/notifications-service.stub'; import { PaginationServiceStub } from '../../../shared/testing/pagination-service.stub'; import { createPaginatedList } from '../../../shared/testing/utils.test'; @@ -52,7 +36,6 @@ describe('BitstreamFormatsComponent', () => { let comp: BitstreamFormatsComponent; let fixture: ComponentFixture; let bitstreamFormatService; - let scheduler: TestScheduler; let notificationsServiceStub; let paginationService; @@ -107,8 +90,6 @@ describe('BitstreamFormatsComponent', () => { const initAsync = () => { notificationsServiceStub = new NotificationsServiceStub(); - scheduler = getTestScheduler(); - bitstreamFormatService = jasmine.createSpyObj('bitstreamFormatService', { findAll: observableOf(mockFormatsRD), find: createSuccessfulRemoteDataObject$(mockFormatsList[0]), @@ -120,33 +101,28 @@ describe('BitstreamFormatsComponent', () => { clearBitStreamFormatRequests: observableOf('cleared'), }); - const groupDataService = jasmine.createSpyObj('groupsDataService', { - findListByHref: createSuccessfulRemoteDataObject$(createPaginatedList([])), - getGroupRegistryRouterLink: '', - getUUIDFromString: '', - }); - - - const configurationDataService = jasmine.createSpyObj('ConfigurationDataService', { - findByPropertyName: of({ payload: { value: 'test' } }), - }); - paginationService = new PaginationServiceStub(); TestBed.configureTestingModule({ - imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule, BitstreamFormatsComponent, PaginationComponent, EnumKeysPipe], + imports: [ + BitstreamFormatsComponent, + EnumKeysPipe, + RouterModule.forRoot([]), + TranslateModule.forRoot(), + ], providers: [ provideMockStore(), - { provide: APP_DATA_SERVICES_MAP, useValue: {} }, { provide: BitstreamFormatDataService, useValue: bitstreamFormatService }, - { provide: HostWindowService, useValue: new HostWindowServiceStub(0) }, { provide: NotificationsService, useValue: notificationsServiceStub }, { provide: PaginationService, useValue: paginationService }, - { provide: GroupDataService, useValue: groupDataService }, - { provide: ConfigurationDataService, useValue: configurationDataService }, - { provide: XSRFService, useValue: {} }, ], - schemas: [NO_ERRORS_SCHEMA], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + }).overrideComponent(BitstreamFormatsComponent, { + remove: { + imports: [ + PaginationComponent, + ], + }, }).compileComponents(); }; @@ -251,8 +227,6 @@ describe('BitstreamFormatsComponent', () => { beforeEach(waitForAsync(() => { notificationsServiceStub = new NotificationsServiceStub(); - scheduler = getTestScheduler(); - bitstreamFormatService = jasmine.createSpyObj('bitstreamFormatService', { findAll: observableOf(mockFormatsRD), find: createSuccessfulRemoteDataObject$(mockFormatsList[0]), @@ -264,37 +238,26 @@ describe('BitstreamFormatsComponent', () => { clearBitStreamFormatRequests: observableOf('cleared'), }); - const groupDataService = jasmine.createSpyObj('groupsDataService', { - findListByHref: createSuccessfulRemoteDataObject$(createPaginatedList([])), - getGroupRegistryRouterLink: '', - getUUIDFromString: '', - }); - - const configurationDataService = jasmine.createSpyObj('ConfigurationDataService', { - findByPropertyName: of({ payload: { value: 'test' } }), - }); - paginationService = new PaginationServiceStub(); TestBed.configureTestingModule({ imports: [ - CommonModule, - RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule, - BitstreamFormatsComponent, PaginationComponent, EnumKeysPipe], + BitstreamFormatsComponent, + EnumKeysPipe, + PaginationComponent, + RouterModule.forRoot([]), + TranslateModule.forRoot(), + ], providers: [ provideMockStore(), { provide: BitstreamFormatDataService, useValue: bitstreamFormatService }, - { provide: HostWindowService, useValue: new HostWindowServiceStub(0) }, { provide: NotificationsService, useValue: notificationsServiceStub }, { provide: PaginationService, useValue: paginationService }, - { provide: GroupDataService, useValue: groupDataService }, - { provide: ConfigurationDataService, useValue: configurationDataService }, ], - schemas: [NO_ERRORS_SCHEMA], + schemas: [CUSTOM_ELEMENTS_SCHEMA], }) .overrideComponent(BitstreamFormatsComponent, { remove: { imports: [PaginationComponent] }, - add: { imports: [TestPaginationComponent] }, }) .compileComponents(); }, @@ -321,8 +284,6 @@ describe('BitstreamFormatsComponent', () => { beforeEach(waitForAsync(() => { notificationsServiceStub = new NotificationsServiceStub(); - scheduler = getTestScheduler(); - bitstreamFormatService = jasmine.createSpyObj('bitstreamFormatService', { findAll: observableOf(mockFormatsRD), find: createSuccessfulRemoteDataObject$(mockFormatsList[0]), @@ -334,35 +295,24 @@ describe('BitstreamFormatsComponent', () => { clearBitStreamFormatRequests: observableOf('cleared'), }); - const groupDataService = jasmine.createSpyObj('groupsDataService', { - findListByHref: createSuccessfulRemoteDataObject$(createPaginatedList([])), - getGroupRegistryRouterLink: '', - getUUIDFromString: '', - }); - - const configurationDataService = jasmine.createSpyObj('ConfigurationDataService', { - findByPropertyName: of({ payload: { value: 'test' } }), - }); - paginationService = new PaginationServiceStub(); TestBed.configureTestingModule({ - imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgbModule, BitstreamFormatsComponent, PaginationComponent, EnumKeysPipe], + imports: [ + BitstreamFormatsComponent, + EnumKeysPipe, + RouterModule.forRoot([]), + TranslateModule.forRoot(), + ], providers: [ - provideMockStore(), { provide: BitstreamFormatDataService, useValue: bitstreamFormatService }, - { provide: HostWindowService, useValue: new HostWindowServiceStub(0) }, { provide: NotificationsService, useValue: notificationsServiceStub }, { provide: PaginationService, useValue: paginationService }, - { provide: PaginationService, useValue: paginationService }, - { provide: GroupDataService, useValue: groupDataService }, - { provide: ConfigurationDataService, useValue: configurationDataService }, ], - schemas: [NO_ERRORS_SCHEMA], + schemas: [CUSTOM_ELEMENTS_SCHEMA], }) .overrideComponent(BitstreamFormatsComponent, { remove: { imports: [PaginationComponent] }, - add: { imports: [TestPaginationComponent] }, }) .compileComponents(); }, @@ -384,12 +334,3 @@ describe('BitstreamFormatsComponent', () => { }); }); }); - -@Component({ - exportAs: 'paginationComponent', - selector: 'ds-pagination', - template: ``, - standalone: true, -}) -export class TestPaginationComponent { -} diff --git a/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.ts b/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.ts index 72739507be3..72da2fadfd1 100644 --- a/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.ts +++ b/src/app/admin/admin-registries/bitstream-formats/bitstream-formats.component.ts @@ -8,10 +8,7 @@ import { OnDestroy, OnInit, } from '@angular/core'; -import { - Router, - RouterLink, -} from '@angular/router'; +import { RouterLink } from '@angular/router'; import { TranslateModule, TranslateService, @@ -74,7 +71,6 @@ export class BitstreamFormatsComponent implements OnInit, OnDestroy { }); constructor(private notificationsService: NotificationsService, - private router: Router, private translateService: TranslateService, private bitstreamFormatService: BitstreamFormatDataService, private paginationService: PaginationService, diff --git a/src/app/core/shared/search/search-configuration.service.spec.ts b/src/app/core/shared/search/search-configuration.service.spec.ts index c820e5bac0a..d56ee8f756c 100644 --- a/src/app/core/shared/search/search-configuration.service.spec.ts +++ b/src/app/core/shared/search/search-configuration.service.spec.ts @@ -7,6 +7,7 @@ import { } from 'rxjs'; import { map } from 'rxjs/operators'; +import { environment } from '../../../../environments/environment.test'; import { getMockRequestService } from '../../../shared/mocks/request.service.mock'; import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; @@ -88,7 +89,7 @@ describe('SearchConfigurationService', () => { pageSize: 20, })); - service = new SearchConfigurationService(routeService, paginationService as any, activatedRoute as any, linkService, halService, requestService, rdb); + service = new SearchConfigurationService(routeService, paginationService as any, activatedRoute as any, linkService, halService, requestService, rdb, environment); }); describe('when the scope is called', () => { diff --git a/src/app/core/shared/search/search-configuration.service.ts b/src/app/core/shared/search/search-configuration.service.ts index b128e457f70..b9e0207cc30 100644 --- a/src/app/core/shared/search/search-configuration.service.ts +++ b/src/app/core/shared/search/search-configuration.service.ts @@ -1,4 +1,5 @@ import { + Inject, Injectable, OnDestroy, } from '@angular/core'; @@ -20,6 +21,10 @@ import { take, } from 'rxjs/operators'; +import { + APP_CONFIG, + AppConfig, +} from '../../../../config/app-config.interface'; import { hasNoValue, hasValue, @@ -29,6 +34,7 @@ import { import { PaginationComponentOptions } from '../../../shared/pagination/pagination-component-options.model'; import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; import { FacetConfigResponse } from '../../../shared/search/models/facet-config-response.model'; +import { FilterType } from '../../../shared/search/models/filter-type.model'; import { PaginatedSearchOptions } from '../../../shared/search/models/paginated-search-options.model'; import { SearchFilter } from '../../../shared/search/models/search-filter.model'; import { SearchFilterConfig } from '../../../shared/search/models/search-filter-config.model'; @@ -56,12 +62,8 @@ import { getFirstSucceededRemoteData, } from '../operators'; import { ViewMode } from '../view-mode.model'; -import { SearchFilterConfig } from '../../../shared/search/models/search-filter-config.model'; -import { FacetConfigResponse } from '../../../shared/search/models/facet-config-response.model'; -import { addOperatorToFilterValue } from '../../../shared/search/search.utils'; -import { FilterConfig } from './search-filters/search-config.model'; -import { FilterType } from '../../../shared/search/models/filter-type.model'; import { + FilterConfig, SearchConfig, SortConfig, } from './search-filters/search-config.model'; @@ -126,24 +128,15 @@ export class SearchConfigurationService implements OnDestroy { */ protected subs: Map = new Map(null); - /** - * Initialize the search options - * @param {RouteService} routeService - * @param {PaginationService} paginationService - * @param {ActivatedRoute} route - * @param linkService - * @param halService - * @param requestService - * @param rdb - */ constructor(protected routeService: RouteService, protected paginationService: PaginationService, protected route: ActivatedRoute, protected linkService: LinkService, protected halService: HALEndpointService, protected requestService: RequestService, - protected rdb: RemoteDataBuildService) { - + protected rdb: RemoteDataBuildService, + @Inject(APP_CONFIG) protected appConfig: AppConfig, + ) { this.initDefaults(); } @@ -297,6 +290,7 @@ export class SearchConfigurationService implements OnDestroy { return this.getConfigurationSearchConfig(configuration, scope).pipe( map((searchConfiguration: SearchConfig) => { return searchConfiguration.filters + .filter((filterConfig: FilterConfig) => this.appConfig.search.advancedFilters.filter.includes(filterConfig.filter)) .filter((filterConfig: FilterConfig) => filterConfig.type !== FilterType.range); }), ); diff --git a/src/app/my-dspace-page/my-dspace-configuration.service.spec.ts b/src/app/my-dspace-page/my-dspace-configuration.service.spec.ts index e7bc4226651..fd6096b8472 100644 --- a/src/app/my-dspace-page/my-dspace-configuration.service.spec.ts +++ b/src/app/my-dspace-page/my-dspace-configuration.service.spec.ts @@ -4,6 +4,7 @@ import { } from 'jasmine-marbles'; import { of as observableOf } from 'rxjs'; +import { environment } from '../../environments/environment.test'; import { SortDirection, SortOptions, @@ -58,7 +59,7 @@ describe('MyDSpaceConfigurationService', () => { const rdb: any = getMockRemoteDataBuildService(); beforeEach(() => { - service = new MyDSpaceConfigurationService(roleService, spy, paginationService as any, activatedRoute, linkService, halService, requestService, rdb); + service = new MyDSpaceConfigurationService(roleService, spy, paginationService as any, activatedRoute, linkService, halService, requestService, rdb, environment); }); describe('when the scope is called', () => { diff --git a/src/app/my-dspace-page/my-dspace-configuration.service.ts b/src/app/my-dspace-page/my-dspace-configuration.service.ts index 2a324d7e137..9a7f3764046 100644 --- a/src/app/my-dspace-page/my-dspace-configuration.service.ts +++ b/src/app/my-dspace-page/my-dspace-configuration.service.ts @@ -1,4 +1,5 @@ import { + Inject, Injectable, InjectionToken, } from '@angular/core'; @@ -12,6 +13,10 @@ import { map, } from 'rxjs/operators'; +import { + APP_CONFIG, + AppConfig, +} from '../../config/app-config.interface'; import { LinkService } from '../core/cache/builders/link.service'; import { RemoteDataBuildService } from '../core/cache/builders/remote-data-build.service'; import { @@ -75,18 +80,6 @@ export class MyDSpaceConfigurationService extends SearchConfigurationService { private isController$: Observable; private isSubmitter$: Observable; - /** - * Initialize class - * - * @param {roleService} roleService - * @param {RouteService} routeService - * @param {PaginationService} paginationService - * @param {ActivatedRoute} route - * @param linkService - * @param halService - * @param requestService - * @param rdb - */ constructor(protected roleService: RoleService, protected routeService: RouteService, protected paginationService: PaginationService, @@ -94,9 +87,19 @@ export class MyDSpaceConfigurationService extends SearchConfigurationService { protected linkService: LinkService, protected halService: HALEndpointService, protected requestService: RequestService, - protected rdb: RemoteDataBuildService) { - - super(routeService, paginationService, route, linkService, halService, requestService, rdb); + protected rdb: RemoteDataBuildService, + @Inject(APP_CONFIG) protected appConfig: AppConfig, + ) { + super( + routeService, + paginationService, + route, + linkService, + halService, + requestService, + rdb, + appConfig, + ); // override parent class initialization this._defaults = null; diff --git a/src/app/shared/search/search-sidebar/search-sidebar.component.spec.ts b/src/app/shared/search/search-sidebar/search-sidebar.component.spec.ts index befa8b25bf9..b9c61629fcf 100644 --- a/src/app/shared/search/search-sidebar/search-sidebar.component.spec.ts +++ b/src/app/shared/search/search-sidebar/search-sidebar.component.spec.ts @@ -13,15 +13,12 @@ import { TranslateModule } from '@ngx-translate/core'; import { APP_CONFIG } from '../../../../config/app-config.interface'; import { environment } from '../../../../environments/environment.test'; +import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service'; +import { SearchConfigurationServiceStub } from '../../testing/search-configuration-service.stub'; import { AdvancedSearchComponent } from '../advanced-search/advanced-search.component'; import { ThemedSearchFiltersComponent } from '../search-filters/themed-search-filters.component'; import { ThemedSearchSettingsComponent } from '../search-settings/themed-search-settings.component'; import { SearchSidebarComponent } from './search-sidebar.component'; -import { NgbCollapseModule } from '@ng-bootstrap/ng-bootstrap'; -import { By } from '@angular/platform-browser'; -import { SearchConfigurationServiceStub } from '../../testing/search-configuration-service.stub'; -import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service'; describe('SearchSidebarComponent', () => { let comp: SearchSidebarComponent; @@ -39,23 +36,23 @@ describe('SearchSidebarComponent', () => { SearchSidebarComponent, ], providers: [ - { provide: APP_CONFIG, useValue: environment }, + { provide: SearchConfigurationService, useValue: searchConfigurationService }, ], schemas: [CUSTOM_ELEMENTS_SCHEMA], - }) - .overrideComponent(SearchSidebarComponent, { - remove:{ - imports: [ - AdvancedSearchComponent, - ThemedSearchFiltersComponent, - ThemedSearchSettingsComponent, - ], - }, - add: { - changeDetection: ChangeDetectionStrategy.Default, - }, - }) - .compileComponents(); // compile template and css + }).overrideProvider(APP_CONFIG, { + useValue: environment, + }).overrideComponent(SearchSidebarComponent, { + remove:{ + imports: [ + AdvancedSearchComponent, + ThemedSearchFiltersComponent, + ThemedSearchSettingsComponent, + ], + }, + add: { + changeDetection: ChangeDetectionStrategy.Default, + }, + }).compileComponents(); })); // synchronous beforeEach From 58d73875b8f02514a3f0963f1a57b5b2f7bb89b7 Mon Sep 17 00:00:00 2001 From: Alexandre Vryghem Date: Sun, 5 May 2024 15:59:31 +0200 Subject: [PATCH 19/24] 111731: Fixed accessibility issues --- .../advanced-search/advanced-search.component.html | 13 ++++++++++--- src/assets/i18n/en.json5 | 4 ++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/app/shared/search/advanced-search/advanced-search.component.html b/src/app/shared/search/advanced-search/advanced-search.component.html index edcb09d0ac9..bd8ad0dfc91 100644 --- a/src/app/shared/search/advanced-search/advanced-search.component.html +++ b/src/app/shared/search/advanced-search/advanced-search.component.html @@ -1,14 +1,21 @@

{{ 'search.sidebar.advanced-search.title' | translate }}