mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
111731: Hide the search facets when there are no facet suggestions & the applied filters of that facet don't have the operator equals, authority or range (because those should be displayed in the facets)
This commit is contained in:
@@ -24,6 +24,8 @@ 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';
|
||||
import { cold } from 'jasmine-marbles';
|
||||
import { PageInfo } from '../../../../../core/shared/page-info.model';
|
||||
|
||||
describe('SearchFacetFilterComponent', () => {
|
||||
let comp: SearchFacetFilterComponent;
|
||||
@@ -32,6 +34,7 @@ describe('SearchFacetFilterComponent', () => {
|
||||
const value1 = 'testvalue1';
|
||||
const value2 = 'test2';
|
||||
const value3 = 'another value3';
|
||||
const value4 = '52d629dc-7d2f-47b9-aa2d-258b92e45ae1';
|
||||
const mockFilterConfig: SearchFilterConfig = Object.assign(new SearchFilterConfig(), {
|
||||
name: filterName1,
|
||||
filterType: FilterType.text,
|
||||
@@ -39,27 +42,39 @@ describe('SearchFacetFilterComponent', () => {
|
||||
isOpenByDefault: false,
|
||||
pageSize: 2
|
||||
});
|
||||
const values: Partial<FacetValues> = {
|
||||
appliedFilters: [
|
||||
{
|
||||
const appliedFilter1: AppliedFilter = Object.assign(new AppliedFilter(), {
|
||||
filter: filterName1,
|
||||
operator: 'equals',
|
||||
label: value1,
|
||||
value: value1,
|
||||
},
|
||||
{
|
||||
});
|
||||
const appliedFilter2: AppliedFilter = Object.assign(new AppliedFilter(), {
|
||||
filter: filterName1,
|
||||
operator: 'equals',
|
||||
label: value2,
|
||||
value: value2,
|
||||
},
|
||||
{
|
||||
});
|
||||
const appliedFilter3: AppliedFilter = Object.assign(new AppliedFilter(), {
|
||||
filter: filterName1,
|
||||
operator: 'equals',
|
||||
label: value3,
|
||||
value: value3,
|
||||
}
|
||||
]
|
||||
});
|
||||
const appliedFilter4: AppliedFilter = Object.assign(new AppliedFilter(), {
|
||||
filter: filterName1,
|
||||
operator: 'notauthority',
|
||||
label: value4,
|
||||
value: value4,
|
||||
});
|
||||
const values: Partial<FacetValues> = {
|
||||
appliedFilters: [
|
||||
appliedFilter1,
|
||||
appliedFilter2,
|
||||
appliedFilter3,
|
||||
],
|
||||
pageInfo: Object.assign(new PageInfo(), {
|
||||
currentPage: 0,
|
||||
}),
|
||||
};
|
||||
|
||||
const searchLink = '/search';
|
||||
@@ -205,4 +220,26 @@ describe('SearchFacetFilterComponent', () => {
|
||||
expect(comp.filter).toEqual('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when new values are detected for a filter', () => {
|
||||
let selectedValues$: BehaviorSubject<AppliedFilter[]>;
|
||||
|
||||
beforeEach(() => {
|
||||
selectedValues$ = new BehaviorSubject([appliedFilter1, appliedFilter2, appliedFilter3]);
|
||||
spyOn(searchService, 'getSelectedValuesForFilter').and.returnValue(selectedValues$);
|
||||
comp.ngOnInit();
|
||||
});
|
||||
|
||||
it('should updated the selectedAppliedFilters$ when they are AppliedFilters that should be displayed in the search facets', () => {
|
||||
expect(comp.selectedAppliedFilters$).toBeObservable(cold('a', {
|
||||
a: [appliedFilter1, appliedFilter2, appliedFilter3],
|
||||
}));
|
||||
|
||||
selectedValues$.next([appliedFilter1, appliedFilter2, appliedFilter3, appliedFilter4]);
|
||||
|
||||
expect(comp.selectedAppliedFilters$).toBeObservable(cold('a', {
|
||||
a: [appliedFilter1, appliedFilter2, appliedFilter3],
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -20,6 +20,15 @@ import { currentPath } from '../../../../utils/route.utils';
|
||||
import { FacetValues } from '../../../models/facet-values.model';
|
||||
import { AppliedFilter } from '../../../models/applied-filter.model';
|
||||
|
||||
/**
|
||||
* The operators the {@link AppliedFilter} should have in order to be shown in the facets
|
||||
*/
|
||||
export const FACET_OPERATORS: string[] = [
|
||||
'equals',
|
||||
'authority',
|
||||
'range',
|
||||
];
|
||||
|
||||
@Component({
|
||||
selector: 'ds-search-facet-filter',
|
||||
template: ``,
|
||||
@@ -104,7 +113,10 @@ export class SearchFacetFilterComponent implements OnInit, OnDestroy {
|
||||
this.searchOptions$.subscribe(() => this.updateFilterValueList()),
|
||||
this.retrieveFilterValues().subscribe(),
|
||||
);
|
||||
this.selectedAppliedFilters$ = this.searchService.getSelectedValuesForFilter(this.filterConfig.name);
|
||||
this.selectedAppliedFilters$ = this.searchService.getSelectedValuesForFilter(this.filterConfig.name).pipe(
|
||||
map((allAppliedFilters: AppliedFilter[]) => allAppliedFilters.filter((appliedFilter: AppliedFilter) => FACET_OPERATORS.includes(appliedFilter.operator))),
|
||||
distinctUntilChanged((previous: AppliedFilter[], next: AppliedFilter[]) => JSON.stringify(previous) === JSON.stringify(next)),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { ChangeDetectionStrategy, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { RouterModule } from '@angular/router';
|
||||
|
||||
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { Observable, of as observableOf } from 'rxjs';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
@@ -16,10 +16,22 @@ import { SequenceService } from '../../../../core/shared/sequence.service';
|
||||
import { BrowserOnlyMockPipe } from '../../../testing/browser-only-mock.pipe';
|
||||
import { SearchServiceStub } from '../../../testing/search-service.stub';
|
||||
import { SearchFilterServiceStub } from '../../../testing/search-filter-service.stub';
|
||||
import { cold } from 'jasmine-marbles';
|
||||
import { AppliedFilter } from '../../models/applied-filter.model';
|
||||
import { FacetValues } from '../../models/facet-values.model';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../../../remote-data.utils';
|
||||
|
||||
describe('SearchFilterComponent', () => {
|
||||
let comp: SearchFilterComponent;
|
||||
let fixture: ComponentFixture<SearchFilterComponent>;
|
||||
|
||||
const appliedFilter1: AppliedFilter = Object.assign(new AppliedFilter(), {
|
||||
operator: 'equals',
|
||||
});
|
||||
const appliedFilter2: AppliedFilter = Object.assign(new AppliedFilter(), {
|
||||
operator: 'notauthority',
|
||||
});
|
||||
|
||||
const filterName1 = 'test name';
|
||||
|
||||
const mockFilterConfig: SearchFilterConfig = Object.assign(new SearchFilterConfig(), {
|
||||
@@ -30,16 +42,21 @@ describe('SearchFilterComponent', () => {
|
||||
});
|
||||
let searchFilterService: SearchFilterServiceStub;
|
||||
let sequenceService;
|
||||
const mockResults = observableOf(['test', 'data']);
|
||||
let searchService: SearchServiceStub;
|
||||
let searchConfigurationService: SearchConfigurationServiceStub;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
searchFilterService = new SearchFilterServiceStub();
|
||||
searchService = new SearchServiceStub();
|
||||
searchConfigurationService = new SearchConfigurationServiceStub();
|
||||
sequenceService = jasmine.createSpyObj('sequenceService', { next: 17 });
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
imports: [TranslateModule.forRoot(), RouterTestingModule.withRoutes([]), NoopAnimationsModule],
|
||||
imports: [
|
||||
NoopAnimationsModule,
|
||||
RouterModule.forRoot([]),
|
||||
TranslateModule.forRoot(),
|
||||
],
|
||||
declarations: [
|
||||
SearchFilterComponent,
|
||||
BrowserOnlyMockPipe,
|
||||
@@ -47,7 +64,7 @@ describe('SearchFilterComponent', () => {
|
||||
providers: [
|
||||
{ provide: SearchService, useValue: searchService },
|
||||
{ provide: SearchFilterService, useValue: searchFilterService },
|
||||
{ provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() },
|
||||
{ provide: SEARCH_CONFIG_SERVICE, useValue: searchConfigurationService },
|
||||
{ provide: SequenceService, useValue: sequenceService },
|
||||
],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
@@ -57,7 +74,6 @@ describe('SearchFilterComponent', () => {
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
spyOn(searchService, 'getFacetValuesFor').and.returnValue(mockResults);
|
||||
fixture = TestBed.createComponent(SearchFilterComponent);
|
||||
comp = fixture.componentInstance; // SearchPageComponent test instance
|
||||
comp.filter = mockFilterConfig;
|
||||
@@ -121,4 +137,59 @@ describe('SearchFilterComponent', () => {
|
||||
sub.unsubscribe();
|
||||
});
|
||||
});
|
||||
|
||||
describe('isActive', () => {
|
||||
it('should return true when there are facet value suggestions & no valid applied values', () => {
|
||||
spyOn(searchService, 'getFacetValuesFor').and.returnValue(createSuccessfulRemoteDataObject$(Object.assign(new FacetValues(), {
|
||||
pageInfo: {
|
||||
totalElements: 5,
|
||||
},
|
||||
} as FacetValues)));
|
||||
comp.appliedFilters$ = observableOf([appliedFilter2]);
|
||||
|
||||
expect(comp.isActive()).toBeObservable(cold('(tt)', {
|
||||
t: true,
|
||||
}));
|
||||
});
|
||||
|
||||
it('should return false when there are no facet value suggestions & no valid applied values', () => {
|
||||
spyOn(searchService, 'getFacetValuesFor').and.returnValue(createSuccessfulRemoteDataObject$(Object.assign(new FacetValues(), {
|
||||
pageInfo: {
|
||||
totalElements: 0,
|
||||
},
|
||||
} as FacetValues)));
|
||||
comp.appliedFilters$ = observableOf([appliedFilter2]);
|
||||
|
||||
expect(comp.isActive()).toBeObservable(cold('(tf)', {
|
||||
t: true,
|
||||
f: false,
|
||||
}));
|
||||
});
|
||||
|
||||
it('should return true when there are no facet value suggestions & but there are valid applied values', () => {
|
||||
spyOn(searchService, 'getFacetValuesFor').and.returnValue(createSuccessfulRemoteDataObject$(Object.assign(new FacetValues(), {
|
||||
pageInfo: {
|
||||
totalElements: 0,
|
||||
},
|
||||
} as FacetValues)));
|
||||
comp.appliedFilters$ = observableOf([appliedFilter1, appliedFilter2]);
|
||||
|
||||
expect(comp.isActive()).toBeObservable(cold('(tt)', {
|
||||
t: true,
|
||||
}));
|
||||
});
|
||||
|
||||
it('should return true when there are facet value suggestions & there are valid applied values', () => {
|
||||
spyOn(searchService, 'getFacetValuesFor').and.returnValue(createSuccessfulRemoteDataObject$(Object.assign(new FacetValues(), {
|
||||
pageInfo: {
|
||||
totalElements: 5,
|
||||
},
|
||||
} as FacetValues)));
|
||||
comp.appliedFilters$ = observableOf([appliedFilter1, appliedFilter2]);
|
||||
|
||||
expect(comp.isActive()).toBeObservable(cold('(tt)', {
|
||||
t: true,
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -15,6 +15,7 @@ import { FacetValues } from '../../models/facet-values.model';
|
||||
import { RemoteData } from '../../../../core/data/remote-data';
|
||||
import { AppliedFilter } from '../../models/applied-filter.model';
|
||||
import { SearchOptions } from '../../models/search-options.model';
|
||||
import { FACET_OPERATORS } from './search-facet-filter/search-facet-filter.component';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-search-filter',
|
||||
@@ -166,13 +167,13 @@ export class SearchFilterComponent implements OnInit, OnDestroy {
|
||||
* Check if a given filter is supposed to be shown or not
|
||||
* @returns {Observable<boolean>} Emits true whenever a given filter config should be shown
|
||||
*/
|
||||
private isActive(): Observable<boolean> {
|
||||
isActive(): Observable<boolean> {
|
||||
return combineLatest([
|
||||
this.appliedFilters$,
|
||||
this.searchConfigService.searchOptions,
|
||||
]).pipe(
|
||||
switchMap(([selectedValues, options]: [AppliedFilter[], SearchOptions]) => {
|
||||
if (isNotEmpty(selectedValues)) {
|
||||
if (isNotEmpty(selectedValues.filter((appliedFilter: AppliedFilter) => FACET_OPERATORS.includes(appliedFilter.operator)))) {
|
||||
return observableOf(true);
|
||||
} else {
|
||||
return this.searchService.getFacetValuesFor(this.filter, 1, options).pipe(
|
||||
|
@@ -4,6 +4,8 @@ import {
|
||||
FilterConfig,
|
||||
SearchConfig,
|
||||
} from '../../core/shared/search/search-filters/search-config.model';
|
||||
import { SearchOptions } from '../search/models/search-options.model';
|
||||
import { PaginatedSearchOptions } from '../search/models/paginated-search-options.model';
|
||||
|
||||
/**
|
||||
* Stub class of {@link SearchConfigurationService}
|
||||
@@ -12,8 +14,8 @@ export class SearchConfigurationServiceStub {
|
||||
|
||||
public paginationID = 'test-id';
|
||||
|
||||
private searchOptions: BehaviorSubject<any> = new BehaviorSubject<any>({});
|
||||
private paginatedSearchOptions: BehaviorSubject<any> = new BehaviorSubject<any>({});
|
||||
public searchOptions: BehaviorSubject<SearchOptions> = new BehaviorSubject(new SearchOptions({}));
|
||||
public paginatedSearchOptions: BehaviorSubject<PaginatedSearchOptions> = new BehaviorSubject(new PaginatedSearchOptions({}));
|
||||
|
||||
getCurrentFrontendFilters() {
|
||||
return observableOf([]);
|
||||
|
Reference in New Issue
Block a user