Skip to content

Commit

Permalink
Merge pull request #3027 from alexandrevryghem/discovery-backports_co…
Browse files Browse the repository at this point in the history
…ntribute-7_x

[Port dspace-7_x] Multiple discovery backports
  • Loading branch information
tdonohue authored May 13, 2024
2 parents 1aa6d35 + ae8e0f9 commit 36fa4d4
Show file tree
Hide file tree
Showing 12 changed files with 191 additions and 63 deletions.
7 changes: 5 additions & 2 deletions src/app/search-page/configuration-search-page.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { SearchConfigurationService } from '../core/shared/search/search-configu
import { RouteService } from '../core/services/route.service';
import { SearchService } from '../core/shared/search/search.service';
import { Router } from '@angular/router';
import { APP_CONFIG, AppConfig } from '../../config/app-config.interface';

/**
* This component renders a search page using a configuration as input.
Expand All @@ -32,7 +33,9 @@ export class ConfigurationSearchPageComponent extends SearchComponent {
protected windowService: HostWindowService,
@Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService,
protected routeService: RouteService,
protected router: Router) {
super(service, sidebarService, windowService, searchConfigService, routeService, router);
protected router: Router,
@Inject(APP_CONFIG) protected appConfig: AppConfig,
) {
super(service, sidebarService, windowService, searchConfigService, routeService, router, appConfig);
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,31 @@
import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing';
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
import { ListableObjectComponentLoaderComponent } from './listable-object-component-loader.component';
import { ListableObject } from '../listable-object.model';
import { GenericConstructor } from '../../../../core/shared/generic-constructor';
import { Context } from '../../../../core/shared/context.model';
import { ViewMode } from '../../../../core/shared/view-mode.model';
import {
ItemListElementComponent
} from '../../../object-list/item-list-element/item-types/item/item-list-element.component';
import { ListableObjectDirective } from './listable-object.directive';
import { TranslateModule } from '@ngx-translate/core';
import { By } from '@angular/platform-browser';
import { provideMockStore } from '@ngrx/store/testing';
import { ThemeService } from '../../../theme-support/theme.service';
import { ItemSearchResultListElementComponent } from '../../../object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component';
import { ActivatedRouteStub } from '../../../testing/active-router.stub';
import { AuthServiceStub } from '../../../testing/auth-service.stub';
import { AuthorizationDataServiceStub } from '../../../testing/authorization-service.stub';
import { FileServiceStub } from '../../../testing/file-service.stub';
import { TruncatableServiceStub } from '../../../testing/truncatable-service.stub';
import { getMockThemeService } from '../../../mocks/theme-service.mock';
import { APP_CONFIG } from '../../../../../config/app-config.interface';
import { environment } from '../../../../../environments/environment.test';
import { ActivatedRoute } from '@angular/router';
import { AuthService } from '../../../../core/auth/auth.service';
import { AuthorizationDataService } from '../../../../core/data/feature-authorization/authorization-data.service';
import { DSONameService } from '../../../../core/breadcrumbs/dso-name.service';
import { DSONameServiceMock } from '../../../mocks/dso-name.service.mock';
import { FileService } from '../../../../core/shared/file.service';
import { TruncatableService } from '../../../truncatable/truncatable.service';
import { ChangeDetectionStrategy } from '@angular/core';
import { SearchResultListElementComponent } from '../../../object-list/search-result-list-element/search-result-list-element.component';

const testType = 'TestType';
const testContext = Context.Search;
Expand All @@ -28,24 +41,41 @@ describe('ListableObjectComponentLoaderComponent', () => {
let comp: ListableObjectComponentLoaderComponent;
let fixture: ComponentFixture<ListableObjectComponentLoaderComponent>;

let activatedRoute: ActivatedRouteStub;
let authService: AuthServiceStub;
let authorizationService: AuthorizationDataServiceStub;
let fileService: FileServiceStub;
let themeService: ThemeService;
let truncatableService: TruncatableServiceStub;

beforeEach(waitForAsync(() => {
themeService = jasmine.createSpyObj('themeService', {
getThemeName: 'dspace',
});
TestBed.configureTestingModule({
activatedRoute = new ActivatedRouteStub();
authService = new AuthServiceStub();
authorizationService = new AuthorizationDataServiceStub();
fileService = new FileServiceStub();
themeService = getMockThemeService();
truncatableService = new TruncatableServiceStub();

void TestBed.configureTestingModule({
imports: [TranslateModule.forRoot()],
declarations: [ListableObjectComponentLoaderComponent, ItemListElementComponent, ListableObjectDirective],
schemas: [NO_ERRORS_SCHEMA],
declarations: [
ItemSearchResultListElementComponent,
ListableObjectComponentLoaderComponent,
ListableObjectDirective,
],
providers: [
provideMockStore({}),
{ provide: APP_CONFIG, useValue: environment },
{ provide: ActivatedRoute, useValue: activatedRoute },
{ provide: AuthService, useValue: authService },
{ provide: AuthorizationDataService, useValue: authorizationService },
{ provide: DSONameService, useValue: new DSONameServiceMock() },
{ provide: FileService, useValue: fileService },
{ provide: ThemeService, useValue: themeService },
{ provide: TruncatableService, useValue: truncatableService },
]
}).overrideComponent(ListableObjectComponentLoaderComponent, {
set: {
changeDetection: ChangeDetectionStrategy.Default,
entryComponents: [ItemListElementComponent]
}
}).compileComponents();
}));
Expand All @@ -57,7 +87,7 @@ describe('ListableObjectComponentLoaderComponent', () => {
comp.object = new TestType();
comp.viewMode = testViewMode;
comp.context = testContext;
spyOn(comp, 'getComponent').and.returnValue(ItemListElementComponent as any);
spyOn(comp, 'getComponent').and.returnValue(SearchResultListElementComponent as any);
spyOn(comp as any, 'connectInputsAndOutputs').and.callThrough();
fixture.detectChanges();

Expand All @@ -81,7 +111,7 @@ describe('ListableObjectComponentLoaderComponent', () => {
spyOn((comp as any), 'instantiateComponent').and.returnValue(null);
spyOn((comp as any).contentChange, 'emit').and.returnValue(null);

listableComponent = fixture.debugElement.query(By.css('ds-item-list-element')).componentInstance;
listableComponent = fixture.debugElement.query(By.css('ds-search-result-list-element')).componentInstance;
reloadedObject = 'object';
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
<ds-item-search-result-list-element [showLabel]="showLabel" [object]="{ indexableObject: object, hitHighlights: {} }" [linkType]="linkType"></ds-item-search-result-list-element>
<ds-listable-object-component-loader
[object]="itemSearchResult"
[viewMode]="viewMode"
[linkType]="linkType">
</ds-listable-object-component-loader>
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
import { TestBed, waitForAsync } from '@angular/core/testing';
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
import { TestBed, waitForAsync, ComponentFixture } from '@angular/core/testing';
import { ChangeDetectionStrategy } from '@angular/core';
import { By } from '@angular/platform-browser';
import { ItemListElementComponent } from './item-list-element.component';
import { Item } from '../../../../../core/shared/item.model';
import { TruncatePipe } from '../../../../utils/truncate.pipe';
import { TruncatableService } from '../../../../truncatable/truncatable.service';
import { of as observableOf } from 'rxjs';
import { DSONameService } from '../../../../../core/breadcrumbs/dso-name.service';
import { DSONameServiceMock } from '../../../../mocks/dso-name.service.mock';
import { ListableObjectComponentLoaderComponent } from '../../../../object-collection/shared/listable-object/listable-object-component-loader.component';
import { getMockThemeService } from '../../../../mocks/theme-service.mock';
import { ThemeService } from '../../../../theme-support/theme.service';
import { ListableObjectDirective } from '../../../../object-collection/shared/listable-object/listable-object.directive';
import { APP_CONFIG } from '../../../../../../config/app-config.interface';
import { environment } from '../../../../../../environments/environment.test';
import { TranslateModule } from '@ngx-translate/core';
import { ActivatedRouteStub } from '../../../../testing/active-router.stub';
import { ActivatedRoute } from '@angular/router';
import { AuthService } from '../../../../../core/auth/auth.service';
import { AuthServiceStub } from '../../../../testing/auth-service.stub';
import { AuthorizationDataService } from '../../../../../core/data/feature-authorization/authorization-data.service';
import { AuthorizationDataServiceStub } from '../../../../testing/authorization-service.stub';
import { FileService } from '../../../../../core/shared/file.service';
import { FileServiceStub } from '../../../../testing/file-service.stub';
import { TruncatableServiceStub } from '../../../../testing/truncatable-service.stub';

const mockItem: Item = Object.assign(new Item(), {
bundles: observableOf({}),
Expand Down Expand Up @@ -46,21 +59,42 @@ const mockItem: Item = Object.assign(new Item(), {
});

describe('ItemListElementComponent', () => {
let comp;
let fixture;
let comp: ItemListElementComponent;
let fixture: ComponentFixture<ItemListElementComponent>;

const truncatableServiceStub: any = {
isCollapsed: (id: number) => observableOf(true),
};
let activatedRoute: ActivatedRouteStub;
let authService: AuthServiceStub;
let authorizationService: AuthorizationDataServiceStub;
let fileService: FileServiceStub;
let themeService: ThemeService;
let truncatableService: TruncatableServiceStub;

beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ItemListElementComponent, TruncatePipe],
activatedRoute = new ActivatedRouteStub();
authService = new AuthServiceStub();
authorizationService = new AuthorizationDataServiceStub();
fileService = new FileServiceStub();
themeService = getMockThemeService();
truncatableService = new TruncatableServiceStub();

void TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
],
declarations: [
ItemListElementComponent,
ListableObjectComponentLoaderComponent,
ListableObjectDirective,
],
providers: [
{ provide: DSONameService, useValue: new DSONameServiceMock() },
{ provide: TruncatableService, useValue: truncatableServiceStub },
{ provide: APP_CONFIG, useValue: environment },
{ provide: ActivatedRoute, useValue: activatedRoute },
{ provide: AuthService, useValue: authService },
{ provide: AuthorizationDataService, useValue: authorizationService },
{ provide: FileService, useValue: fileService },
{ provide: ThemeService, useValue: themeService },
{ provide: TruncatableService, useValue: truncatableService },
],
schemas: [NO_ERRORS_SCHEMA]
}).overrideComponent(ItemListElementComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
}).compileComponents();
Expand All @@ -74,6 +108,7 @@ describe('ItemListElementComponent', () => {
describe(`when the publication is rendered`, () => {
beforeEach(() => {
comp.object = mockItem;
comp.ngOnChanges();
fixture.detectChanges();
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { Component } from '@angular/core';
import {
Component,
OnChanges,
} from '@angular/core';

import { ViewMode } from '../../../../../core/shared/view-mode.model';
import { listableObjectComponent } from '../../../../object-collection/shared/listable-object/listable-object.decorator';
import { AbstractListableElementComponent } from '../../../../object-collection/shared/object-collection-element/abstract-listable-element.component';
import { Item } from '../../../../../core/shared/item.model';
import { ItemSearchResult } from '../../../../object-collection/shared/item-search-result.model';
import { hasValue } from '../../../../empty.util';

@listableObjectComponent('Publication', ViewMode.ListElement)
@listableObjectComponent(Item, ViewMode.ListElement)
Expand All @@ -14,5 +20,16 @@ import { Item } from '../../../../../core/shared/item.model';
/**
* The component for displaying a list element for an item of the type Publication
*/
export class ItemListElementComponent extends AbstractListableElementComponent<Item> {
export class ItemListElementComponent extends AbstractListableElementComponent<Item> implements OnChanges {

itemSearchResult: ItemSearchResult;

ngOnChanges(): void {
if (hasValue(this.object)) {
this.itemSearchResult = Object.assign(new ItemSearchResult(), {
indexableObject: this.object,
});
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,16 @@ import { SearchConfigurationServiceStub } from '../../../../testing/search-confi
import { VocabularyEntryDetail } from '../../../../../core/submission/vocabularies/models/vocabulary-entry-detail.model';
import { FacetValue} from '../../../models/facet-value.model';
import { SearchFilterConfig } from '../../../models/search-filter-config.model';
import { APP_CONFIG } from '../../../../../../config/app-config.interface';
import { environment } from '../../../../../../environments/environment.test';

describe('SearchHierarchyFilterComponent', () => {

let fixture: ComponentFixture<SearchHierarchyFilterComponent>;
let showVocabularyTreeLink: DebugElement;

const testSearchLink = 'test-search';
const testSearchFilter = 'test-search-filter';
const testSearchFilter = 'subject';
const VocabularyTreeViewComponent = {
select: new EventEmitter<VocabularyEntryDetail>(),
};
Expand Down Expand Up @@ -73,6 +75,7 @@ describe('SearchHierarchyFilterComponent', () => {
{ provide: Router, useValue: router },
{ provide: NgbModal, useValue: ngbModal },
{ provide: VocabularyService, useValue: vocabularyService },
{ provide: APP_CONFIG, useValue: environment },
{ provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() },
{ provide: IN_PLACE_SEARCH, useValue: false },
{ provide: FILTER_CONFIG, useValue: Object.assign(new SearchFilterConfig(), { name: testSearchFilter }) },
Expand All @@ -86,7 +89,7 @@ describe('SearchHierarchyFilterComponent', () => {
function init() {
fixture = TestBed.createComponent(SearchHierarchyFilterComponent);
fixture.detectChanges();
showVocabularyTreeLink = fixture.debugElement.query(By.css('a#show-test-search-filter-tree'));
showVocabularyTreeLink = fixture.debugElement.query(By.css(`a#show-${testSearchFilter}-tree`));
}

describe('if the vocabulary doesn\'t exist', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@ import { filter, map, take } from 'rxjs/operators';
import { VocabularyService } from '../../../../../core/submission/vocabularies/vocabulary.service';
import { Observable, BehaviorSubject } from 'rxjs';
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 { hasValue } from '../../../../empty.util';
import { APP_CONFIG, AppConfig } from '../../../../../../config/app-config.interface';
import { FilterVocabularyConfig } from '../../../../../../config/filter-vocabulary-config';

@Component({
selector: 'ds-search-hierarchy-filter',
Expand All @@ -47,6 +49,7 @@ export class SearchHierarchyFilterComponent extends SearchFacetFilterComponent i
protected router: Router,
protected modalService: NgbModal,
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,
Expand All @@ -67,17 +70,20 @@ export class SearchHierarchyFilterComponent extends SearchFacetFilterComponent i
super.onSubmit(addOperatorToFilterValue(data, 'query'));
}

ngOnInit() {
ngOnInit(): void {
super.ngOnInit();
this.vocabularyExists$ = this.vocabularyService.searchTopEntries(
this.getVocabularyEntry(), new PageInfo(), true, false,
).pipe(
filter(rd => rd.hasCompleted),
take(1),
map(rd => {
return rd.hasSucceeded;
}),
);
const vocabularyName: string = this.getVocabularyEntry();
if (hasValue(vocabularyName)) {
this.vocabularyExists$ = this.vocabularyService.searchTopEntries(
vocabularyName, new PageInfo(), true, false,
).pipe(
filter(rd => rd.hasCompleted),
take(1),
map(rd => {
return rd.hasSucceeded;
}),
);
}
}

/**
Expand All @@ -93,11 +99,11 @@ export class SearchHierarchyFilterComponent extends SearchFacetFilterComponent i
name: this.getVocabularyEntry(),
closed: true
};
modalRef.result.then((detail: VocabularyEntryDetail) => {
this.selectedValues$
void modalRef.result.then((detail: VocabularyEntryDetail) => {
this.subs.push(this.selectedValues$
.pipe(take(1))
.subscribe((selectedValues) => {
this.router.navigate(
void this.router.navigate(
[this.searchService.getSearchLink()],
{
queryParams: {
Expand All @@ -107,16 +113,16 @@ export class SearchHierarchyFilterComponent extends SearchFacetFilterComponent i
queryParamsHandling: 'merge',
},
);
});
}).catch();
}));
});
}

/**
* Returns the matching vocabulary entry for the given search filter.
* These are configurable in the config file.
*/
getVocabularyEntry() {
const foundVocabularyConfig = environment.vocabularies.filter((v) => v.filter === this.filterConfig.name);
getVocabularyEntry(): string {
const foundVocabularyConfig: FilterVocabularyConfig[] = this.appConfig.vocabularies.filter((v: FilterVocabularyConfig) => v.filter === this.filterConfig.name);
if (foundVocabularyConfig.length > 0 && foundVocabularyConfig[0].enabled === true) {
return foundVocabularyConfig[0].vocabulary;
}
Expand Down
Loading

0 comments on commit 36fa4d4

Please sign in to comment.