Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 12 additions & 11 deletions src/app/core/shared/search/search-configuration.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ describe('SearchConfigurationService', () => {
}));

service = new SearchConfigurationService(routeService, paginationService as any, activatedRoute as any, linkService, halService, requestService, rdb, environment);
service.searchInstanceId = defaults.pagination.id;
});

describe('when the scope is called', () => {
Expand Down Expand Up @@ -181,7 +182,7 @@ describe('SearchConfigurationService', () => {

describe('when subscribeToSearchOptions is called', () => {
beforeEach(() => {
(service as any).subscribeToSearchOptions(defaults);
(service as any).subscribeToSearchOptions(defaults.pagination.id, defaults);
});
it('should call all getters it needs, but not call any others', () => {
expect(service.getCurrentPagination).not.toHaveBeenCalled();
Expand All @@ -198,14 +199,14 @@ describe('SearchConfigurationService', () => {
beforeEach(() => {
(service as any).subscribeToPaginatedSearchOptions(defaults.pagination.id, defaults);
});
it('should call all getters it needs', () => {
it('should call the pagination-specific getters it needs', () => {
expect(service.getCurrentPagination).toHaveBeenCalled();
expect(service.getCurrentSort).toHaveBeenCalled();
expect(service.getCurrentScope).toHaveBeenCalled();
expect(service.getCurrentConfiguration).toHaveBeenCalled();
expect(service.getCurrentQuery).toHaveBeenCalled();
expect(service.getCurrentDSOType).toHaveBeenCalled();
expect(service.getCurrentFilters).toHaveBeenCalled();
expect(service.getCurrentScope).not.toHaveBeenCalled();
expect(service.getCurrentConfiguration).not.toHaveBeenCalled();
expect(service.getCurrentQuery).not.toHaveBeenCalled();
expect(service.getCurrentDSOType).not.toHaveBeenCalled();
expect(service.getCurrentFilters).not.toHaveBeenCalled();
});
});
});
Expand Down Expand Up @@ -314,13 +315,13 @@ describe('SearchConfigurationService', () => {
it('should return all params except the applied filter', () => {
service.unselectAppliedFilterParams(appliedFilter.filter, appliedFilter.value, appliedFilter.operator);

expect(routeService.getParamsExceptValue).toHaveBeenCalledWith('f.author', '1282121b-5394-4689-ab93-78d537764052,authority');
expect(routeService.getParamsExceptValue).toHaveBeenCalledWith(`${defaults.pagination.id}.f.author`, '1282121b-5394-4689-ab93-78d537764052,authority');
});

it('should be able to remove AppliedFilter without operator', () => {
service.unselectAppliedFilterParams('dateIssued.max', '2000');

expect(routeService.getParamsExceptValue).toHaveBeenCalledWith('f.dateIssued.max', '2000');
expect(routeService.getParamsExceptValue).toHaveBeenCalledWith(`${defaults.pagination.id}.f.dateIssued.max`, '2000');
});

it('should reset the page to 1', (done: DoneFn) => {
Expand All @@ -346,13 +347,13 @@ describe('SearchConfigurationService', () => {
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');
expect(routeService.getParamsWithAdditionalValue).toHaveBeenCalledWith(`${defaults.pagination.id}.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');
expect(routeService.getParamsWithAdditionalValue).toHaveBeenCalledWith(`${defaults.pagination.id}.f.dateIssued.max`, '2000');
});

it('should reset the page to 1', (done: DoneFn) => {
Expand Down
227 changes: 144 additions & 83 deletions src/app/core/shared/search/search-configuration.service.ts

Large diffs are not rendered by default.

53 changes: 43 additions & 10 deletions src/app/core/shared/search/search-filter.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Injectable } from '@angular/core';
import { Params } from '@angular/router';
import {
createSelector,
MemoizedSelector,
Expand Down Expand Up @@ -75,7 +74,14 @@ export class SearchFilterService {
* @param {string} filterValue The value for which to search
* @returns {Observable<boolean>} Emit true when the filter is active with the given value
*/
isFilterActiveWithValue(paramName: string, filterValue: string): Observable<boolean> {
isFilterActiveWithValue(paramName: string, filterValue: string, searchInstanceId?: string): Observable<boolean> {
if (hasValue(searchInstanceId)) {
return observableCombineLatest(
this.routeService.hasQueryParamWithValue(this.getSearchInstanceParam(searchInstanceId, paramName), filterValue),
this.routeService.getQueryParamsWithPrefix(this.getSearchInstanceParam(searchInstanceId, 'f.')),
this.routeService.hasQueryParamWithValue(paramName, filterValue),
).pipe(map(([instanceActive, instanceFilters, legacyActive]) => instanceActive || (!isNotEmpty(instanceFilters) && legacyActive)));
}
return this.routeService.hasQueryParamWithValue(paramName, filterValue);
}

Expand All @@ -84,24 +90,31 @@ export class SearchFilterService {
* @param {string} paramName The parameter name of the filter's configuration for which to search
* @returns {Observable<boolean>} Emit true when the filter is active with any value
*/
isFilterActive(paramName: string): Observable<boolean> {
isFilterActive(paramName: string, searchInstanceId?: string): Observable<boolean> {
if (hasValue(searchInstanceId)) {
return observableCombineLatest(
this.routeService.hasQueryParam(this.getSearchInstanceParam(searchInstanceId, paramName)),
this.routeService.getQueryParamsWithPrefix(this.getSearchInstanceParam(searchInstanceId, 'f.')),
this.routeService.hasQueryParam(paramName),
).pipe(map(([instanceActive, instanceFilters, legacyActive]) => instanceActive || (!isNotEmpty(instanceFilters) && legacyActive)));
}
return this.routeService.hasQueryParam(paramName);
}

/**
* Fetch the current active scope from the query parameters
* @returns {Observable<string>}
*/
getCurrentScope(): Observable<string> {
return this.routeService.getQueryParameterValue('scope');
getCurrentScope(searchInstanceId?: string) {
return this.getCurrentSearchInstanceQueryParam(searchInstanceId, 'scope');
}

/**
* Fetch the current query from the query parameters
* @returns {Observable<string>}
*/
getCurrentQuery(): Observable<string> {
return this.routeService.getQueryParameterValue('query');
getCurrentQuery(searchInstanceId?: string) {
return this.getCurrentSearchInstanceQueryParam(searchInstanceId, 'query');
}

/**
Expand Down Expand Up @@ -142,16 +155,22 @@ export class SearchFilterService {
* Fetch the current active filters from the query parameters
* @returns {Observable<Params>}
*/
getCurrentFilters(): Observable<Params> {
getCurrentFilters(searchInstanceId?: string) {
if (hasValue(searchInstanceId)) {
return observableCombineLatest(
this.routeService.getQueryParamsWithPrefix(this.getSearchInstanceParam(searchInstanceId, 'f.')),
this.routeService.getQueryParamsWithPrefix('f.'),
).pipe(map(([instanceFilters, legacyFilters]) => isNotEmpty(instanceFilters) ? instanceFilters : legacyFilters));
}
return this.routeService.getQueryParamsWithPrefix('f.');
}

/**
* Fetch the current view from the query parameters
* @returns {Observable<string>}
*/
getCurrentView(): Observable<string> {
return this.routeService.getQueryParameterValue('view');
getCurrentView(searchInstanceId?: string) {
return this.getCurrentSearchInstanceQueryParam(searchInstanceId, 'view');
}

/**
Expand Down Expand Up @@ -190,6 +209,20 @@ export class SearchFilterService {
return `${new EmphasizePipe().transform(facet.value, query)} (${facet.count})`;
}

private getSearchInstanceParam(searchInstanceId: string, parameterName: string): string {
return `${searchInstanceId}.${parameterName}`;
}

private getCurrentSearchInstanceQueryParam(searchInstanceId: string | undefined, parameterName: string): Observable<string> {
if (hasValue(searchInstanceId)) {
return observableCombineLatest(
this.routeService.getQueryParameterValue(this.getSearchInstanceParam(searchInstanceId, parameterName)),
this.routeService.getQueryParameterValue(parameterName),
).pipe(map(([instanceValue, legacyValue]) => hasValue(instanceValue) ? instanceValue : legacyValue));
}
return this.routeService.getQueryParameterValue(parameterName);
}

/**
* 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
Expand Down
4 changes: 2 additions & 2 deletions src/app/core/shared/search/search.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,13 @@ describe('SearchService', () => {
it('should call the navigate method on the Router with view mode list parameter as a parameter when setViewMode is called', () => {
service.setViewMode(ViewMode.ListElement);

expect(paginationService.updateRouteWithUrl).toHaveBeenCalledWith('test-id', ['/search'], { page: 1 }, { view: ViewMode.ListElement });
expect(paginationService.updateRouteWithUrl).toHaveBeenCalledWith('test-id', ['/search'], { page: 1 }, { 'test-id.view': ViewMode.ListElement });
});

it('should call the navigate method on the Router with view mode grid parameter as a parameter when setViewMode is called', () => {
service.setViewMode(ViewMode.GridElement);

expect(paginationService.updateRouteWithUrl).toHaveBeenCalledWith('test-id', ['/search'], { page: 1 }, { view: ViewMode.GridElement });
expect(paginationService.updateRouteWithUrl).toHaveBeenCalledWith('test-id', ['/search'], { page: 1 }, { 'test-id.view': ViewMode.GridElement });
});
});

Expand Down
12 changes: 7 additions & 5 deletions src/app/core/shared/search/search.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -342,8 +342,10 @@ export class SearchService {
*/
getViewMode(): Observable<ViewMode> {
return this.routeService.getQueryParamMap().pipe(map((params) => {
if (isNotEmpty(params.get('view')) && hasValue(params.get('view'))) {
return params.get('view');
const viewParam = this.searchConfigurationService.getCurrentSearchInstanceParam('view');
const view = hasValue(params.get(viewParam)) ? params.get(viewParam) : params.get('view');
if (isNotEmpty(view) && hasValue(view)) {
return view;
} else {
return ViewMode.ListElement;
}
Expand All @@ -356,16 +358,16 @@ export class SearchService {
* @param {string[]} searchLinkParts
*/
setViewMode(viewMode: ViewMode, searchLinkParts?: string[]) {
this.paginationService.getCurrentPagination(this.searchConfigurationService.paginationID, new PaginationComponentOptions()).pipe(take(1))
this.paginationService.getCurrentPagination(this.searchConfigurationService.searchInstanceId, new PaginationComponentOptions()).pipe(take(1))
.subscribe((config) => {
let pageParams = { page: 1 };
const queryParams = { view: viewMode };
const queryParams = { [this.searchConfigurationService.getCurrentSearchInstanceParam('view')]: viewMode };
if (viewMode === ViewMode.DetailedListElement) {
pageParams = Object.assign(pageParams, { pageSize: 1 });
} else if (config.pageSize === 1) {
pageParams = Object.assign(pageParams, { pageSize: 10 });
}
this.paginationService.updateRouteWithUrl(this.searchConfigurationService.paginationID, hasValue(searchLinkParts) ? searchLinkParts : [this.getSearchLink()], pageParams, queryParams);
this.paginationService.updateRouteWithUrl(this.searchConfigurationService.searchInstanceId, hasValue(searchLinkParts) ? searchLinkParts : [this.getSearchLink()], pageParams, queryParams);
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ export class RecentItemListComponent implements OnInit, OnDestroy {
}

onLoadMore(): void {
this.paginationService.updateRouteWithUrl(this.searchConfigurationService.paginationID, ['search'], {
this.paginationService.updateRouteWithUrl(this.searchConfigurationService.searchInstanceId, ['search'], {
sortField: environment.homePage.recentSubmissions.sortField,
sortDirection: 'DESC' as SortDirection,
page: 1,
Expand Down
14 changes: 7 additions & 7 deletions src/app/my-dspace-page/my-dspace-configuration.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ describe('MyDSpaceConfigurationService', () => {

describe('when subscribeToSearchOptions is called', () => {
beforeEach(() => {
(service as any).subscribeToSearchOptions(defaults);
(service as any).subscribeToSearchOptions(defaults.pagination.id, defaults);
});
it('should call all getters it needs, but not call any others', () => {
expect(service.getCurrentPagination).not.toHaveBeenCalled();
Expand All @@ -168,14 +168,14 @@ describe('MyDSpaceConfigurationService', () => {
beforeEach(() => {
(service as any).subscribeToPaginatedSearchOptions('id', defaults);
});
it('should call all getters it needs', () => {
it('should call the pagination-specific getters it needs', () => {
expect(service.getCurrentPagination).toHaveBeenCalled();
expect(service.getCurrentSort).toHaveBeenCalled();
expect(service.getCurrentScope).toHaveBeenCalled();
expect(service.getCurrentConfiguration).toHaveBeenCalled();
expect(service.getCurrentQuery).toHaveBeenCalled();
expect(service.getCurrentDSOType).toHaveBeenCalled();
expect(service.getCurrentFilters).toHaveBeenCalled();
expect(service.getCurrentScope).not.toHaveBeenCalled();
expect(service.getCurrentConfiguration).not.toHaveBeenCalled();
expect(service.getCurrentQuery).not.toHaveBeenCalled();
expect(service.getCurrentDSOType).not.toHaveBeenCalled();
expect(service.getCurrentFilters).not.toHaveBeenCalled();
});
});
});
Expand Down
5 changes: 5 additions & 0 deletions src/app/my-dspace-page/my-dspace-configuration.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ export const SEARCH_CONFIG_SERVICE: InjectionToken<SearchConfigurationService> =
*/
@Injectable({ providedIn: 'root' })
export class MyDSpaceConfigurationService extends SearchConfigurationService {
/**
* Search instance id used for the MyDSpace search component.
*/
public searchInstanceId = 'mydspace-page';

/**
* Default pagination settings
*/
Expand Down
12 changes: 9 additions & 3 deletions src/app/my-dspace-page/my-dspace.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,28 +26,34 @@ export const myDSpaceGuard: CanActivateFn = (
configurationService: MyDSpaceConfigurationService = inject(MyDSpaceConfigurationService),
router: Router = inject(Router),
): Observable<boolean> => {
const configurationParam = configurationService.getCurrentSearchInstanceParam('configuration');
const configuration = route.queryParamMap.get(configurationParam) || route.queryParamMap.get('configuration');
return configurationService.getAvailableConfigurationTypes().pipe(
first(),
map((configurationList) => validateConfigurationParam(router, route.queryParamMap.get('configuration'), configurationList)));
map((configurationList) => validateConfigurationParam(router, configurationService, configuration, configurationList)));
};

/**
* Check if the given configuration is present in the list of those available
*
* @param router
* the service router
* @param configurationService
* the MyDSpace configuration service
* @param configuration
* the configuration to validate
* @param configurationList
* the list of available configuration
*
*/
function validateConfigurationParam(router: Router, configuration: string, configurationList: MyDSpaceConfigurationValueType[]): boolean {
function validateConfigurationParam(router: Router, configurationService: MyDSpaceConfigurationService, configuration: string, configurationList: MyDSpaceConfigurationValueType[]): boolean {
const configurationDefault: string = configurationList[0];
if (isEmpty(configuration) || !configurationList.includes(configuration as MyDSpaceConfigurationValueType)) {
// If configuration param is empty or is not included in available configurations redirect to a default configuration value
const navigationExtras: NavigationExtras = {
queryParams: { configuration: configurationDefault },
queryParams: {
[configurationService.getCurrentSearchInstanceParam('configuration')]: configurationDefault,
},
};

router.navigate([MYDSPACE_ROUTE], navigationExtras);
Expand Down
14 changes: 12 additions & 2 deletions src/app/search-navbar/search-navbar.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ import {
TranslateModule,
} from '@ngx-translate/core';

import { PaginationService } from '../core/pagination/pagination.service';
import { SearchService } from '../core/shared/search/search.service';
import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';
import { TranslateLoaderMock } from '../shared/mocks/translate-loader.mock';
import { SearchNavbarComponent } from './search-navbar.component';

Expand Down Expand Up @@ -54,6 +56,14 @@ describe('SearchNavbarComponent', () => {
],
providers: [
{ provide: SearchService, useValue: mockSearchService },
{ provide: PaginationService, useValue: { getPageParam: (id: string) => `${id}.page` } },
{
provide: SearchConfigurationService,
useValue: {
searchInstanceId: 'spc',
getCurrentSearchInstanceParam: (param: string) => `spc.${param}`,
},
},
],
})
.compileComponents();
Expand Down Expand Up @@ -100,7 +110,7 @@ describe('SearchNavbarComponent', () => {
fixture.detectChanges();
}));
it('to search page with empty query', () => {
const extras: NavigationExtras = { queryParams: { query: '' } };
const extras: NavigationExtras = { queryParams: { 'spc.query': '', 'spc.page': 1 } };
expect(component.onSubmit).toHaveBeenCalledWith({ query: '' });
expect(router.navigate).toHaveBeenCalledWith(['search'], extras);
});
Expand All @@ -125,7 +135,7 @@ describe('SearchNavbarComponent', () => {
fixture.detectChanges();
}));
it('to search page with query', async () => {
const extras: NavigationExtras = { queryParams: { query: 'test' } };
const extras: NavigationExtras = { queryParams: { 'spc.query': 'test', 'spc.page': 1 } };
expect(component.onSubmit).toHaveBeenCalledWith({ query: 'test' });

expect(router.navigate).toHaveBeenCalledWith(['search'], extras);
Expand Down
13 changes: 11 additions & 2 deletions src/app/search-navbar/search-navbar.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ import {
import { Router } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core';

import { PaginationService } from '../core/pagination/pagination.service';
import { SearchService } from '../core/shared/search/search.service';
import { SearchConfigurationService } from '../core/shared/search/search-configuration.service';
import { expandSearchInput } from '../shared/animations/slide';
import { BrowserOnlyPipe } from '../shared/utils/browser-only.pipe';
import { ClickOutsideDirective } from '../shared/utils/click-outside.directive';
Expand Down Expand Up @@ -43,7 +45,11 @@ export class SearchNavbarComponent {
// Search input field
@ViewChild('searchInput') searchField: ElementRef;

constructor(private formBuilder: UntypedFormBuilder, private router: Router, private searchService: SearchService) {
constructor(private formBuilder: UntypedFormBuilder,
private router: Router,
private searchService: SearchService,
private searchConfigurationService: SearchConfigurationService,
private paginationService: PaginationService) {
this.searchForm = this.formBuilder.group(({
query: '',
}));
Expand Down Expand Up @@ -80,7 +86,10 @@ export class SearchNavbarComponent {
*/
onSubmit(data: any) {
this.collapse();
const queryParams = Object.assign({}, data);
const queryParams = {
[this.searchConfigurationService.getCurrentSearchInstanceParam('query')]: data.query,
[this.paginationService.getPageParam(this.searchConfigurationService.searchInstanceId)]: 1,
};
const linkToNavigateTo = [this.searchService.getSearchLink().replace('/', '')];
this.searchForm.reset();

Expand Down
Loading
Loading