From 9ff1a6a642930fd6852079f3c5f777471a9a92e7 Mon Sep 17 00:00:00 2001 From: Samuel Date: Thu, 16 Dec 2021 09:51:40 +0100 Subject: [PATCH 01/27] taskid 85843 Add a tree to browse hierarchical facets on the search page - overrides --- .../vocabulary-treeview.component.ts | 10 ++-- .../okr-vocabulary-treeview.component.scss | 0 .../okr-vocabulary-treeview.component.ts | 47 +++++++++++++++++++ 3 files changed, 52 insertions(+), 5 deletions(-) create mode 100644 src/themes/okr/app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component.scss create mode 100644 src/themes/okr/app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component.ts diff --git a/src/app/shared/vocabulary-treeview/vocabulary-treeview.component.ts b/src/app/shared/vocabulary-treeview/vocabulary-treeview.component.ts index 41ba775171..f9e4d04f3b 100644 --- a/src/app/shared/vocabulary-treeview/vocabulary-treeview.component.ts +++ b/src/app/shared/vocabulary-treeview/vocabulary-treeview.component.ts @@ -98,7 +98,7 @@ export class VocabularyTreeviewComponent implements OnDestroy, OnInit { /** * Array to track all subscriptions and unsubscribe them onDestroy */ - private subs: Subscription[] = []; + protected subs: Subscription[] = []; /** * Initialize instance variables @@ -110,9 +110,9 @@ export class VocabularyTreeviewComponent implements OnDestroy, OnInit { */ constructor( public activeModal: NgbActiveModal, - private vocabularyTreeviewService: VocabularyTreeviewService, - private store: Store, - private translate: TranslateService + protected vocabularyTreeviewService: VocabularyTreeviewService, + protected store: Store, + protected translate: TranslateService ) { this.treeFlattener = new VocabularyTreeFlattener(this.transformer, this.getLevel, this.isExpandable, this.getChildren); @@ -301,7 +301,7 @@ export class VocabularyTreeviewComponent implements OnDestroy, OnInit { /** * Return an id for a given {@link VocabularyEntry} */ - private getEntryId(entry: VocabularyEntry): string { + protected getEntryId(entry: VocabularyEntry): string { return entry.authority || entry.otherInformation.id || undefined; } } diff --git a/src/themes/okr/app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component.scss b/src/themes/okr/app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/themes/okr/app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component.ts b/src/themes/okr/app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component.ts new file mode 100644 index 0000000000..f340cdfccf --- /dev/null +++ b/src/themes/okr/app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component.ts @@ -0,0 +1,47 @@ +import { Component } from '@angular/core'; +import { VocabularyTreeviewComponent } from '../../../../../app/shared/vocabulary-treeview/vocabulary-treeview.component'; +import { filter, find, startWith } from 'rxjs/operators'; +import { PageInfo } from '../../../../../app/core/shared/page-info.model'; + +/** + * Component that show a hierarchical vocabulary in a tree view + */ +@Component({ + selector: 'ds-okr-vocabulary-treeview', + templateUrl: '../../../../../app/shared/vocabulary-treeview/vocabulary-treeview.component.html', + styleUrls: [ + './okr-vocabulary-treeview.component.scss', + '../../../../../app/shared/vocabulary-treeview/vocabulary-treeview.component.scss', + ] +}) +export class OkrVocabularyTreeviewComponent extends VocabularyTreeviewComponent { + + /** + * Initialize the component, setting up the data to build the tree + */ + ngOnInit(): void { + this.subs.push( + this.vocabularyTreeviewService.getData().subscribe((data) => { + this.dataSource.data = data; + }) + ); + + const descriptionLabel = 'vocabulary-treeview.tree.description.' + this.vocabularyOptions.name; + this.description = this.translate.get(descriptionLabel).pipe( + filter((msg) => msg !== descriptionLabel), + startWith('') + ); + + // set isAuthenticated + this.isAuthenticated = this.store.pipe(select(isAuthenticated)); + + this.loading = this.vocabularyTreeviewService.isLoading(); + + this.isAuthenticated.pipe( + find((isAuth) => isAuth) + ).subscribe(() => { + const entryId: string = (this.selectedItem) ? this.getEntryId(this.selectedItem) : null; + this.vocabularyTreeviewService.initialize(this.vocabularyOptions, new PageInfo(), entryId); + }); + } +} From d5578accea27a941b7d11c9f26b8fa7398e8519c Mon Sep 17 00:00:00 2001 From: Samuel Date: Thu, 16 Dec 2021 09:52:43 +0100 Subject: [PATCH 02/27] taskid 85843 Add a tree to browse hierarchical facets on the search page --- .../search-hierarchy-filter.component.ts | 2 +- .../okr-vocabulary-treeview.component.ts | 15 +- ...okr-search-hierarchy-filter.component.html | 7 + ...okr-search-hierarchy-filter.component.scss | 0 ...-search-hierarchy-filter.component.spec.ts | 156 ++++++++++++++++++ .../okr-search-hierarchy-filter.component.ts | 106 ++++++++++++ src/themes/okr/entry-components.ts | 63 +++++++ 7 files changed, 337 insertions(+), 12 deletions(-) create mode 100644 src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.html create mode 100644 src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.scss create mode 100644 src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.spec.ts create mode 100644 src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.ts create mode 100644 src/themes/okr/entry-components.ts 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 d2f3de2dc3..7aa81116de 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 @@ -13,6 +13,6 @@ import { facetLoad, SearchFacetFilterComponent } from '../search-facet-filter/se /** * Component that represents a hierarchy facet for a specific filter configuration */ -@renderFacetFor(FilterType.hierarchy) +// @renderFacetFor(FilterType.hierarchy) export class SearchHierarchyFilterComponent extends SearchFacetFilterComponent implements OnInit { } diff --git a/src/themes/okr/app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component.ts b/src/themes/okr/app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component.ts index f340cdfccf..f33f7d89e9 100644 --- a/src/themes/okr/app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component.ts +++ b/src/themes/okr/app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component.ts @@ -1,10 +1,11 @@ import { Component } from '@angular/core'; import { VocabularyTreeviewComponent } from '../../../../../app/shared/vocabulary-treeview/vocabulary-treeview.component'; -import { filter, find, startWith } from 'rxjs/operators'; +import { filter, startWith } from 'rxjs/operators'; import { PageInfo } from '../../../../../app/core/shared/page-info.model'; /** - * Component that show a hierarchical vocabulary in a tree view + * Component that show a hierarchical vocabulary in a tree view. + * Worldbank customization which omits the authentication check. */ @Component({ selector: 'ds-okr-vocabulary-treeview', @@ -32,16 +33,8 @@ export class OkrVocabularyTreeviewComponent extends VocabularyTreeviewComponent startWith('') ); - // set isAuthenticated - this.isAuthenticated = this.store.pipe(select(isAuthenticated)); - this.loading = this.vocabularyTreeviewService.isLoading(); - this.isAuthenticated.pipe( - find((isAuth) => isAuth) - ).subscribe(() => { - const entryId: string = (this.selectedItem) ? this.getEntryId(this.selectedItem) : null; - this.vocabularyTreeviewService.initialize(this.vocabularyOptions, new PageInfo(), entryId); - }); + this.vocabularyTreeviewService.initialize(this.vocabularyOptions, new PageInfo(), null); } } diff --git a/src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.html b/src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.html new file mode 100644 index 0000000000..e62006538b --- /dev/null +++ b/src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.html @@ -0,0 +1,7 @@ + + +
+ {{'search.filters.filter.show-tree' | translate: {name: filterConfig.name} }} +
diff --git a/src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.scss b/src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.spec.ts b/src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.spec.ts new file mode 100644 index 0000000000..ce4caa23eb --- /dev/null +++ b/src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.spec.ts @@ -0,0 +1,156 @@ +import { OkrSearchHierarchyFilterComponent } from './okr-search-hierarchy-filter.component'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { DebugElement, EventEmitter, NO_ERRORS_SCHEMA } from '@angular/core'; +import { By } from '@angular/platform-browser'; +import { VocabularyService } from '../../../../../../../../app/core/submission/vocabularies/vocabulary.service'; +import { of as observableOf } from 'rxjs'; +import { RemoteData } from '../../../../../../../../app/core/data/remote-data'; +import { RequestEntryState } from '../../../../../../../../app/core/data/request.reducer'; +import { TranslateModule } from '@ngx-translate/core'; +import { RouterStub } from '../../../../../../../../app/shared/testing/router.stub'; +import { buildPaginatedList } from '../../../../../../../../app/core/data/paginated-list.model'; +import { PageInfo } from '../../../../../../../../app/core/shared/page-info.model'; +import { CommonModule } from '@angular/common'; +import { SearchService } from '../../../../../../../../app/core/shared/search/search.service'; +import { + FILTER_CONFIG, + IN_PLACE_SEARCH, + SearchFilterService +} from '../../../../../../../../app/core/shared/search/search-filter.service'; +import { RemoteDataBuildService } from '../../../../../../../../app/core/cache/builders/remote-data-build.service'; +import { Router } from '@angular/router'; +import { NgbModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { SEARCH_CONFIG_SERVICE } from '../../../../../../../../app/my-dspace-page/my-dspace-page.component'; +import { SearchConfigurationServiceStub } from '../../../../../../../../app/shared/testing/search-configuration-service.stub'; +import { VocabularyEntryDetail } from '../../../../../../../../app/core/submission/vocabularies/models/vocabulary-entry-detail.model'; +import { FacetValue } from '../../../../../../../../app/shared/search/facet-value.model'; +import { SearchFilterConfig } from '../../../../../../../../app/shared/search/search-filter-config.model'; + +describe('OkrSearchHierarchyFilterComponent', () => { + + let fixture: ComponentFixture; + let showVocabularyTreeLink: DebugElement; + + const testSearchLink = 'test-search'; + const testSearchFilter = 'test-search-filter'; + const okrVocabularyTreeViewComponent = { + select: new EventEmitter(), + }; + + const searchService = { + getSearchLink: () => testSearchLink, + getFacetValuesFor: () => observableOf([]), + }; + const searchFilterService = { + getPage: () => observableOf(0), + }; + const router = new RouterStub(); + const ngbModal = jasmine.createSpyObj('modal', { + open: { + componentInstance: okrVocabularyTreeViewComponent, + } + }); + const vocabularyService = { + searchTopEntries: () => undefined, + }; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + CommonModule, + NgbModule, + TranslateModule.forRoot(), + ], + declarations: [ + OkrSearchHierarchyFilterComponent, + ], + providers: [ + { provide: SearchService, useValue: searchService }, + { provide: SearchFilterService, useValue: searchFilterService }, + { provide: RemoteDataBuildService, useValue: {} }, + { provide: Router, useValue: router }, + { provide: NgbModal, useValue: ngbModal }, + { provide: VocabularyService, useValue: vocabularyService }, + { provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() }, + { provide: IN_PLACE_SEARCH, useValue: false }, + { provide: FILTER_CONFIG, useValue: Object.assign(new SearchFilterConfig(), { name: testSearchFilter }) }, + ], + schemas: [NO_ERRORS_SCHEMA], + }).compileComponents(); + }); + + function init() { + fixture = TestBed.createComponent(OkrSearchHierarchyFilterComponent); + fixture.detectChanges(); + showVocabularyTreeLink = fixture.debugElement.query(By.css('div#show-test-search-filter-tree')); + } + + describe('if the vocabulary doesn\'t exist', () => { + + beforeEach(() => { + spyOn(vocabularyService, 'searchTopEntries').and.returnValue(observableOf(new RemoteData( + undefined, 0, 0, RequestEntryState.Error, undefined, undefined, 404 + ))); + init(); + }); + + it('should not show the vocabulary tree link', () => { + expect(showVocabularyTreeLink).toBeNull(); + }); + }); + + describe('if the vocabulary exists', () => { + + beforeEach(() => { + spyOn(vocabularyService, 'searchTopEntries').and.returnValue(observableOf(new RemoteData( + undefined, 0, 0, RequestEntryState.Success, undefined, buildPaginatedList(new PageInfo(), []), 200 + ))); + init(); + }); + + it('should show the vocabulary tree link', () => { + expect(showVocabularyTreeLink).toBeTruthy(); + }); + + describe('when clicking the vocabulary tree link', () => { + + beforeEach(async () => { + showVocabularyTreeLink.nativeElement.click(); + }); + + it('should open the vocabulary tree modal', () => { + expect(ngbModal.open).toHaveBeenCalled(); + }); + + describe('when selecting a value from the vocabulary tree', () => { + + const alreadySelectedValues = [ + 'already-selected-value-1', + 'already-selected-value-2', + ]; + const newSelectedValue = 'new-selected-value'; + + beforeEach(() => { + fixture.componentInstance.selectedValues$ = observableOf( + alreadySelectedValues.map(value => Object.assign(new FacetValue(), { value })) + ); + okrVocabularyTreeViewComponent.select.emit(Object.assign(new VocabularyEntryDetail(), { + value: newSelectedValue, + })); + }); + + it('should add a new search filter to the existing search filters', () => { + expect(router.navigate).toHaveBeenCalledWith([testSearchLink], { + queryParams: { + [`f.${testSearchFilter}`]: [ + ...alreadySelectedValues, + newSelectedValue, + ].map((value => `${value},equals`)), + }, + queryParamsHandling: 'merge', + }); + }); + }); + }); + }); +}); diff --git a/src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.ts b/src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.ts new file mode 100644 index 0000000000..c637f576eb --- /dev/null +++ b/src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.ts @@ -0,0 +1,106 @@ +import { Component, Inject } from '@angular/core'; +import { renderFacetFor } from 'src/app/shared/search/search-filters/search-filter/search-filter-type-decorator'; +import { FilterType } from '../../../../../../../../app/shared/search/filter-type.model'; +import { facetLoad } from '../../../../../../../../app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component'; +import { SearchHierarchyFilterComponent } from '../../../../../../../../app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { OkrVocabularyTreeviewComponent } from '../../../../okr-vocabulary-treeview/okr-vocabulary-treeview.component'; +import { VocabularyEntryDetail } from '../../../../../../../../app/core/submission/vocabularies/models/vocabulary-entry-detail.model'; +import { SearchService } from '../../../../../../../../app/core/shared/search/search.service'; +import { + FILTER_CONFIG, + IN_PLACE_SEARCH, + SearchFilterService +} from '../../../../../../../../app/core/shared/search/search-filter.service'; +import { Router } from '@angular/router'; +import { RemoteDataBuildService } from '../../../../../../../../app/core/cache/builders/remote-data-build.service'; +import { SEARCH_CONFIG_SERVICE } from '../../../../../../../../app/my-dspace-page/my-dspace-page.component'; +import { SearchConfigurationService } from '../../../../../../../../app/core/shared/search/search-configuration.service'; +import { SearchFilterConfig } from '../../../../../../../../app/shared/search/search-filter-config.model'; +import { FacetValue } from '../../../../../../../../app/shared/search/facet-value.model'; +import { getFacetValueForType } from '../../../../../../../../app/shared/search/search.utils'; +import { filter, map, take } from 'rxjs/operators'; +import { VocabularyService } from '../../../../../../../../app/core/submission/vocabularies/vocabulary.service'; +import { Observable } from 'rxjs'; +import { PageInfo } from '../../../../../../../../app/core/shared/page-info.model'; + +/** + * Component that represents a hierarchy facet for a specific filter configuration. + * Worldbank customization which features a link at the bottom to open a vocabulary popup, + */ +@Component({ + selector: 'ds-okr-search-hierarchy-filter', + styleUrls: ['./okr-search-hierarchy-filter.component.scss'], + templateUrl: './okr-search-hierarchy-filter.component.html', + animations: [facetLoad] +}) +@renderFacetFor(FilterType.hierarchy) +export class OkrSearchHierarchyFilterComponent extends SearchHierarchyFilterComponent { + + constructor(protected searchService: SearchService, + protected filterService: SearchFilterService, + protected rdbs: RemoteDataBuildService, + protected router: Router, + protected modalService: NgbModal, + protected vocabularyService: VocabularyService, + @Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService, + @Inject(IN_PLACE_SEARCH) public inPlaceSearch: boolean, + @Inject(FILTER_CONFIG) public filterConfig: SearchFilterConfig, + ) { + super( + searchService, + filterService, + rdbs, + router, + searchConfigService, + inPlaceSearch, + filterConfig, + ); + } + + vocabularyExists$: Observable; + + ngOnInit() { + super.ngOnInit(); + this.vocabularyExists$ = this.vocabularyService.searchTopEntries( + this.filterConfig.name, new PageInfo(), true, false, + ).pipe( + filter(rd => rd.hasCompleted), + take(1), + map(rd => { + return rd.hasSucceeded; + }), + ); + } + + /** + * Open the vocabulary tree modal popup. + * When an entry is selected, add the filter query to the search options. + */ + showVocabularyTree() { + const modalRef: NgbModalRef = this.modalService.open(OkrVocabularyTreeviewComponent, { + size: 'lg', + windowClass: 'treeview' + }); + modalRef.componentInstance.vocabularyOptions = { + name: this.filterConfig.name, + closed: true + }; + modalRef.componentInstance.select.subscribe((detail: VocabularyEntryDetail) => { + this.selectedValues$ + .pipe(take(1)) + .subscribe((selectedValues) => { + this.router.navigate( + [this.searchService.getSearchLink()], + { + queryParams: { + [this.filterConfig.paramName]: [...selectedValues, {value: detail.value}] + .map((facetValue: FacetValue) => getFacetValueForType(facetValue, this.filterConfig)), + }, + queryParamsHandling: 'merge', + }, + ); + }); + }); + } +} diff --git a/src/themes/okr/entry-components.ts b/src/themes/okr/entry-components.ts new file mode 100644 index 0000000000..bfc93ad216 --- /dev/null +++ b/src/themes/okr/entry-components.ts @@ -0,0 +1,63 @@ +import { PublicationComponent } from './app/item-page/simple/item-types/publication/publication.component'; +import { ItemSearchResultListElementComponent } from './app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component'; +import { PersonSearchResultListElementComponent } from './app/entity-groups/research-entities/item-list-elements/search-result-list-elements/person/person-search-result-list-element.component'; +import { JournalSearchResultListElementComponent } from './app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal/journal-search-result-list-element.component'; +import { JournalIssueSearchResultListElementComponent } from './app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component'; +import { JournalVolumeSearchResultListElementComponent } from './app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-volume/journal-volume-search-result-list-element.component'; +import { CitationsSectionComponent } from './app/item-page/field-components/citation/citations-section.component'; +import { CcIconsComponent } from './app/item-page/field-components/cc-icons/cc-icons.component'; +import { AltmetricDonutComponent } from './app/item-page/field-components/citation/altmetric-donut/altmetric-donut.component'; +import { ItemPageWbDateFieldComponent } from './app/item-page/field-components/specific-field/wb-date/item-page-wb-date-field.component'; +import { ItemPageWbGenericWithFallbackComponent } from './app/item-page/field-components/specific-field/wb-generic-with-fallback/item-page-wb-generic-with-fallback.component'; +import { ItemPageWbExternalContentComponent } from './app/item-page/field-components/specific-field/wb-external-content/item-page-wb-external-content.component'; +import { UntypedItemComponent } from './app/item-page/simple/item-types/untyped-item/untyped-item.component'; +import { JournalComponent } from './app/entity-groups/journal-entities/item-pages/journal/journal.component'; +import { JournalIssueComponent } from './app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component'; +import { JournalVolumeComponent } from './app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component'; +import { JournalVolumeGridElementComponent } from './app/entity-groups/journal-entities/item-grid-elements/journal-volume/journal-volume-grid-element.component'; +import { JournalVolumeSearchResultGridElementComponent } from './app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-volume/journal-volume-search-result-grid-element.component'; +import { JournalIssueSearchResultGridElementComponent } from './app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-issue/journal-issue-search-result-grid-element.component'; +import { JournalIssueGridElementComponent } from './app/entity-groups/journal-entities/item-grid-elements/journal-issue/journal-issue-grid-element.component'; +import { JournalVolumeSidebarSearchListElementComponent } from './app/entity-groups/journal-entities/item-list-elements/sidebar-search-list-elements/journal-volume/journal-volume-sidebar-search-list-element.component'; +import { JournalIssueSidebarSearchListElementComponent } from './app/entity-groups/journal-entities/item-list-elements/sidebar-search-list-elements/journal-issue/journal-issue-sidebar-search-list-element.component'; +import { JournalIssueListElementComponent } from './app/entity-groups/journal-entities/item-list-elements/journal-issue/journal-issue-list-element.component'; +import { JournalVolumeListElementComponent } from './app/entity-groups/journal-entities/item-list-elements/journal-volume/journal-volume-list-element.component'; +import { FeaturedPublicationsListElementComponent } from './app/featured-publications/featured-publications-list-element/featured-publications-list-element.component'; +import { ClaimedTaskActionsMarkDuplicateComponent } from './app/shared/mydspace-actions/claimed-task/mark-duplicate/claimed-task-actions-mark-duplicate.component'; +import { ClaimedTaskActionsReportProblemComponent } from './app/shared/mydspace-actions/claimed-task/report-problem/claimed-task-actions-report-problem.component'; +import { SingleStatletTableComponent } from './app/atmire-cua/statlets/shared/single-statlet/graph-types/single-statlet-table/single-statlet-table.component'; +import { OkrSearchHierarchyFilterComponent } from './app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component'; +import { OkrVocabularyTreeviewComponent } from './app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component'; + +export const ENTRY_COMPONENTS = [ + PublicationComponent, + ItemSearchResultListElementComponent, + PersonSearchResultListElementComponent, + JournalSearchResultListElementComponent, + JournalVolumeSearchResultListElementComponent, + JournalIssueSearchResultListElementComponent, + UntypedItemComponent, + AltmetricDonutComponent, + CitationsSectionComponent, + CcIconsComponent, + ItemPageWbDateFieldComponent, + ItemPageWbGenericWithFallbackComponent, + ItemPageWbExternalContentComponent, + JournalComponent, + JournalIssueComponent, + JournalVolumeComponent, + JournalVolumeGridElementComponent, + JournalVolumeSearchResultGridElementComponent, + JournalIssueSearchResultGridElementComponent, + JournalIssueGridElementComponent, + JournalVolumeSidebarSearchListElementComponent, + JournalIssueSidebarSearchListElementComponent, + JournalIssueListElementComponent, + JournalVolumeListElementComponent, + FeaturedPublicationsListElementComponent, + ClaimedTaskActionsMarkDuplicateComponent, + ClaimedTaskActionsReportProblemComponent, + SingleStatletTableComponent, + OkrSearchHierarchyFilterComponent, + OkrVocabularyTreeviewComponent, +]; From bc3ab46e8eae70e2a3b25fe1bbddd5f8c51fecf2 Mon Sep 17 00:00:00 2001 From: Samuel Date: Thu, 16 Dec 2021 13:14:13 +0100 Subject: [PATCH 03/27] taskid 85843 Add a tree to browse hierarchical facets on the search page - fix import issue --- .../okr-search-hierarchy-filter.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.ts b/src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.ts index c637f576eb..7d4e41894c 100644 --- a/src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.ts +++ b/src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.ts @@ -1,5 +1,5 @@ import { Component, Inject } from '@angular/core'; -import { renderFacetFor } from 'src/app/shared/search/search-filters/search-filter/search-filter-type-decorator'; +import { renderFacetFor } from '../../../../../../../../app/shared/search/search-filters/search-filter/search-filter-type-decorator'; import { FilterType } from '../../../../../../../../app/shared/search/filter-type.model'; import { facetLoad } from '../../../../../../../../app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component'; import { SearchHierarchyFilterComponent } from '../../../../../../../../app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component'; From 2fab8415626e0e96ed9019d9ce390c70ccff09f6 Mon Sep 17 00:00:00 2001 From: Samuel Date: Thu, 16 Dec 2021 14:12:27 +0100 Subject: [PATCH 04/27] taskid 85843 Add a tree to browse hierarchical facets on the search page - i18n updates --- .../okr-search-hierarchy-filter.component.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.html b/src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.html index e62006538b..dd50528bb6 100644 --- a/src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.html +++ b/src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.html @@ -2,6 +2,7 @@
- {{'search.filters.filter.show-tree' | translate: {name: filterConfig.name} }} + {{'search.filters.filter.show-tree' | translate: {name: ('search.filters.filter.' + filterConfig.name + '.head' | translate)} }}
From 3b7d962671deb967241e52071a0f8b6d8d0e756e Mon Sep 17 00:00:00 2001 From: Samuel Date: Thu, 23 Dec 2021 11:43:59 +0100 Subject: [PATCH 05/27] taskid 85843 Add a tree to browse hierarchical facets on the search page - feedback --- .../okr-vocabulary-treeview.component.ts | 11 ++++++----- .../okr-search-hierarchy-filter.component.html | 8 ++++---- .../okr-search-hierarchy-filter.component.spec.ts | 2 +- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/themes/okr/app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component.ts b/src/themes/okr/app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component.ts index f33f7d89e9..c9ef159f81 100644 --- a/src/themes/okr/app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component.ts +++ b/src/themes/okr/app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component.ts @@ -1,7 +1,8 @@ import { Component } from '@angular/core'; import { VocabularyTreeviewComponent } from '../../../../../app/shared/vocabulary-treeview/vocabulary-treeview.component'; -import { filter, startWith } from 'rxjs/operators'; +import { filter, map, startWith } from 'rxjs/operators'; import { PageInfo } from '../../../../../app/core/shared/page-info.model'; +import { lowerCase } from 'lodash'; /** * Component that show a hierarchical vocabulary in a tree view. @@ -27,10 +28,10 @@ export class OkrVocabularyTreeviewComponent extends VocabularyTreeviewComponent }) ); - const descriptionLabel = 'vocabulary-treeview.tree.description.' + this.vocabularyOptions.name; - this.description = this.translate.get(descriptionLabel).pipe( - filter((msg) => msg !== descriptionLabel), - startWith('') + this.translate.get(`search.filters.filter.${this.vocabularyOptions.name}.head`).pipe( + map((type) => lowerCase(type)), + ).subscribe( + (type) => this.description = this.translate.get('okr-vocabulary-treeview.info', { type }) ); this.loading = this.vocabularyTreeviewService.isLoading(); diff --git a/src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.html b/src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.html index dd50528bb6..731b5045bf 100644 --- a/src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.html +++ b/src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.html @@ -1,8 +1,8 @@ -
- {{'search.filters.filter.show-tree' | translate: {name: ('search.filters.filter.' + filterConfig.name + '.head' | translate)} }} -
+ {{'search.filters.filter.show-tree' | translate: {name: ('search.filters.filter.' + filterConfig.name + '.head' | translate | lowercase )} }} + diff --git a/src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.spec.ts b/src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.spec.ts index ce4caa23eb..ac0669cbf9 100644 --- a/src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.spec.ts +++ b/src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.spec.ts @@ -82,7 +82,7 @@ describe('OkrSearchHierarchyFilterComponent', () => { function init() { fixture = TestBed.createComponent(OkrSearchHierarchyFilterComponent); fixture.detectChanges(); - showVocabularyTreeLink = fixture.debugElement.query(By.css('div#show-test-search-filter-tree')); + showVocabularyTreeLink = fixture.debugElement.query(By.css('a#show-test-search-filter-tree')); } describe('if the vocabulary doesn\'t exist', () => { From 4afa5181a6ffae1b94ca946abad88a2c5fdbe199 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Thu, 1 Dec 2022 13:34:56 +0100 Subject: [PATCH 06/27] refac --- .../okr-vocabulary-treeview.component.scss | 0 .../okr-vocabulary-treeview.component.ts | 8 +-- ...okr-search-hierarchy-filter.component.html | 0 ...okr-search-hierarchy-filter.component.scss | 0 ...-search-hierarchy-filter.component.spec.ts | 24 +++---- .../okr-search-hierarchy-filter.component.ts | 30 ++++----- src/app/shared/search/search.module.ts | 6 +- src/app/shared/shared.module.ts | 5 ++ src/assets/i18n/en.json5 | 26 +++++++- src/themes/okr/entry-components.ts | 63 ------------------- 10 files changed, 66 insertions(+), 96 deletions(-) rename src/{themes/okr => }/app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component.scss (100%) rename src/{themes/okr => }/app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component.ts (74%) rename src/{themes/okr => }/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.html (100%) rename src/{themes/okr => }/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.scss (100%) rename src/{themes/okr => }/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.spec.ts (80%) rename src/{themes/okr => }/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.ts (64%) delete mode 100644 src/themes/okr/entry-components.ts diff --git a/src/themes/okr/app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component.scss b/src/app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component.scss similarity index 100% rename from src/themes/okr/app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component.scss rename to src/app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component.scss diff --git a/src/themes/okr/app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component.ts b/src/app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component.ts similarity index 74% rename from src/themes/okr/app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component.ts rename to src/app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component.ts index c9ef159f81..80dcb1f739 100644 --- a/src/themes/okr/app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component.ts +++ b/src/app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core'; -import { VocabularyTreeviewComponent } from '../../../../../app/shared/vocabulary-treeview/vocabulary-treeview.component'; +import { VocabularyTreeviewComponent } from '../vocabulary-treeview/vocabulary-treeview.component'; import { filter, map, startWith } from 'rxjs/operators'; -import { PageInfo } from '../../../../../app/core/shared/page-info.model'; +import { PageInfo } from '../../core/shared/page-info.model'; import { lowerCase } from 'lodash'; /** @@ -10,10 +10,10 @@ import { lowerCase } from 'lodash'; */ @Component({ selector: 'ds-okr-vocabulary-treeview', - templateUrl: '../../../../../app/shared/vocabulary-treeview/vocabulary-treeview.component.html', + templateUrl: '../vocabulary-treeview/vocabulary-treeview.component.html', styleUrls: [ './okr-vocabulary-treeview.component.scss', - '../../../../../app/shared/vocabulary-treeview/vocabulary-treeview.component.scss', + '../vocabulary-treeview/vocabulary-treeview.component.scss', ] }) export class OkrVocabularyTreeviewComponent extends VocabularyTreeviewComponent { diff --git a/src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.html b/src/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.html similarity index 100% rename from src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.html rename to src/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.html diff --git a/src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.scss b/src/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.scss similarity index 100% rename from src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.scss rename to src/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.scss diff --git a/src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.spec.ts b/src/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.spec.ts similarity index 80% rename from src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.spec.ts rename to src/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.spec.ts index ac0669cbf9..fe0866468a 100644 --- a/src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.spec.ts +++ b/src/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.spec.ts @@ -2,27 +2,27 @@ import { OkrSearchHierarchyFilterComponent } from './okr-search-hierarchy-filter import { ComponentFixture, TestBed } from '@angular/core/testing'; import { DebugElement, EventEmitter, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; -import { VocabularyService } from '../../../../../../../../app/core/submission/vocabularies/vocabulary.service'; +import { VocabularyService } from '../../../../../core/submission/vocabularies/vocabulary.service'; import { of as observableOf } from 'rxjs'; -import { RemoteData } from '../../../../../../../../app/core/data/remote-data'; -import { RequestEntryState } from '../../../../../../../../app/core/data/request.reducer'; +import { RemoteData } from '../../../../../core/data/remote-data'; +import { RequestEntryState } from '../../../../../core/data/request.reducer'; import { TranslateModule } from '@ngx-translate/core'; -import { RouterStub } from '../../../../../../../../app/shared/testing/router.stub'; -import { buildPaginatedList } from '../../../../../../../../app/core/data/paginated-list.model'; -import { PageInfo } from '../../../../../../../../app/core/shared/page-info.model'; +import { RouterStub } from '../../../../testing/router.stub'; +import { buildPaginatedList } from '../../../../../core/data/paginated-list.model'; +import { PageInfo } from '../../../../../core/shared/page-info.model'; import { CommonModule } from '@angular/common'; -import { SearchService } from '../../../../../../../../app/core/shared/search/search.service'; +import { SearchService } from '../../../../../core/shared/search/search.service'; import { FILTER_CONFIG, IN_PLACE_SEARCH, SearchFilterService -} from '../../../../../../../../app/core/shared/search/search-filter.service'; -import { RemoteDataBuildService } from '../../../../../../../../app/core/cache/builders/remote-data-build.service'; +} from '../../../../../core/shared/search/search-filter.service'; +import { RemoteDataBuildService } from '../../../../../core/cache/builders/remote-data-build.service'; import { Router } from '@angular/router'; import { NgbModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'; -import { SEARCH_CONFIG_SERVICE } from '../../../../../../../../app/my-dspace-page/my-dspace-page.component'; -import { SearchConfigurationServiceStub } from '../../../../../../../../app/shared/testing/search-configuration-service.stub'; -import { VocabularyEntryDetail } from '../../../../../../../../app/core/submission/vocabularies/models/vocabulary-entry-detail.model'; +import { SEARCH_CONFIG_SERVICE } from '../../../../../my-dspace-page/my-dspace-page.component'; +import { SearchConfigurationServiceStub } from '../../../../testing/search-configuration-service.stub'; +import { VocabularyEntryDetail } from '../../../../../core/submission/vocabularies/models/vocabulary-entry-detail.model'; import { FacetValue } from '../../../../../../../../app/shared/search/facet-value.model'; import { SearchFilterConfig } from '../../../../../../../../app/shared/search/search-filter-config.model'; diff --git a/src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.ts b/src/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.ts similarity index 64% rename from src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.ts rename to src/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.ts index 7d4e41894c..530b3d8d2b 100644 --- a/src/themes/okr/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.ts +++ b/src/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.ts @@ -1,28 +1,28 @@ import { Component, Inject } from '@angular/core'; -import { renderFacetFor } from '../../../../../../../../app/shared/search/search-filters/search-filter/search-filter-type-decorator'; -import { FilterType } from '../../../../../../../../app/shared/search/filter-type.model'; -import { facetLoad } from '../../../../../../../../app/shared/search/search-filters/search-filter/search-facet-filter/search-facet-filter.component'; -import { SearchHierarchyFilterComponent } from '../../../../../../../../app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component'; +import { renderFacetFor } from '../search-filter-type-decorator'; +import { FilterType } from '../../../models/filter-type.model'; +import { facetLoad } from '../search-facet-filter/search-facet-filter.component'; +import { SearchHierarchyFilterComponent } from '../search-hierarchy-filter/search-hierarchy-filter.component'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { OkrVocabularyTreeviewComponent } from '../../../../okr-vocabulary-treeview/okr-vocabulary-treeview.component'; -import { VocabularyEntryDetail } from '../../../../../../../../app/core/submission/vocabularies/models/vocabulary-entry-detail.model'; -import { SearchService } from '../../../../../../../../app/core/shared/search/search.service'; +import { VocabularyEntryDetail } from '../../../../../core/submission/vocabularies/models/vocabulary-entry-detail.model'; +import { SearchService } from '../../../../../core/shared/search/search.service'; import { FILTER_CONFIG, IN_PLACE_SEARCH, SearchFilterService -} from '../../../../../../../../app/core/shared/search/search-filter.service'; +} from '../../../../../core/shared/search/search-filter.service'; import { Router } from '@angular/router'; -import { RemoteDataBuildService } from '../../../../../../../../app/core/cache/builders/remote-data-build.service'; -import { SEARCH_CONFIG_SERVICE } from '../../../../../../../../app/my-dspace-page/my-dspace-page.component'; -import { SearchConfigurationService } from '../../../../../../../../app/core/shared/search/search-configuration.service'; -import { SearchFilterConfig } from '../../../../../../../../app/shared/search/search-filter-config.model'; -import { FacetValue } from '../../../../../../../../app/shared/search/facet-value.model'; -import { getFacetValueForType } from '../../../../../../../../app/shared/search/search.utils'; +import { RemoteDataBuildService } from '../../../../../core/cache/builders/remote-data-build.service'; +import { SEARCH_CONFIG_SERVICE } from '../../../../../my-dspace-page/my-dspace-page.component'; +import { SearchConfigurationService } from '../../../../../core/shared/search/search-configuration.service'; +import { SearchFilterConfig } from '../../../models/search-filter-config.model'; +import { FacetValue } from '../../../models/facet-value.model'; +import { getFacetValueForType } from '../../../search.utils'; import { filter, map, take } from 'rxjs/operators'; -import { VocabularyService } from '../../../../../../../../app/core/submission/vocabularies/vocabulary.service'; +import { VocabularyService } from '../../../../../core/submission/vocabularies/vocabulary.service'; import { Observable } from 'rxjs'; -import { PageInfo } from '../../../../../../../../app/core/shared/page-info.model'; +import { PageInfo } from '../../../../../core/shared/page-info.model'; /** * Component that represents a hierarchy facet for a specific filter configuration. diff --git a/src/app/shared/search/search.module.ts b/src/app/shared/search/search.module.ts index 668d260c23..e9c0b3e121 100644 --- a/src/app/shared/search/search.module.ts +++ b/src/app/shared/search/search.module.ts @@ -28,6 +28,9 @@ import { MissingTranslationHelper } from '../translate/missing-translation.helpe import { SharedModule } from '../shared.module'; import { SearchResultsComponent } from './search-results/search-results.component'; import { SearchComponent } from './search.component'; +import { + OkrSearchHierarchyFilterComponent +} from './search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component'; const COMPONENTS = [ SearchComponent, @@ -50,7 +53,8 @@ const COMPONENTS = [ SearchAuthorityFilterComponent, SearchSwitchConfigurationComponent, ConfigurationSearchPageComponent, - ThemedConfigurationSearchPageComponent + ThemedConfigurationSearchPageComponent, + OkrSearchHierarchyFilterComponent ]; const ENTRY_COMPONENTS = [ diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 715ee66a99..6f4091f465 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -177,6 +177,10 @@ import { ScopeSelectorModalComponent } from './search-form/scope-selector-modal/ import { BitstreamRequestACopyPageComponent } from './bitstream-request-a-copy-page/bitstream-request-a-copy-page.component'; import { DsSelectComponent } from './ds-select/ds-select.component'; import { LogInOidcComponent } from './log-in/methods/oidc/log-in-oidc.component'; +import { OkrVocabularyTreeviewComponent } from './okr-vocabulary-treeview/okr-vocabulary-treeview.component'; +import { + OkrSearchHierarchyFilterComponent +} from './search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component'; const MODULES = [ // Do NOT include UniversalModule, HttpModule, or JsonpModule here @@ -346,6 +350,7 @@ const COMPONENTS = [ CommunitySidebarSearchListElementComponent, SearchNavbarComponent, ScopeSelectorModalComponent, + OkrVocabularyTreeviewComponent ]; const ENTRY_COMPONENTS = [ diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index f742273edb..3ebe8006ef 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -4162,5 +4162,29 @@ "idle-modal.log-out": "Log out", - "idle-modal.extend-session": "Extend session" + "idle-modal.extend-session": "Extend session", + + "search.filters.filter.show-tree": "Browse {{ name }} tree", + + "search.filters.filter.supportedlanguage.placeholder": "Supported Language", + + "search.filters.filter.supportedlanguage.label": "Search supported Language", + + "search.filters.filter.topic.placeholder": "Topic", + + "search.filters.filter.topic.label": "Search topic", + + "search.filters.filter.doctype.placeholder": "Document type", + + "search.filters.filter.doctype.label": "Search document type", + + "search.filters.filter.region.placeholder": "Region", + + "search.filters.filter.region.label": "Search region", + + "search.filters.filter.country.placeholder": "Country", + + "search.filters.filter.country.label": "Search country", + + "okr-vocabulary-treeview.info": "Click a {{ type }} to add a search filter", } diff --git a/src/themes/okr/entry-components.ts b/src/themes/okr/entry-components.ts deleted file mode 100644 index bfc93ad216..0000000000 --- a/src/themes/okr/entry-components.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { PublicationComponent } from './app/item-page/simple/item-types/publication/publication.component'; -import { ItemSearchResultListElementComponent } from './app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component'; -import { PersonSearchResultListElementComponent } from './app/entity-groups/research-entities/item-list-elements/search-result-list-elements/person/person-search-result-list-element.component'; -import { JournalSearchResultListElementComponent } from './app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal/journal-search-result-list-element.component'; -import { JournalIssueSearchResultListElementComponent } from './app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component'; -import { JournalVolumeSearchResultListElementComponent } from './app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-volume/journal-volume-search-result-list-element.component'; -import { CitationsSectionComponent } from './app/item-page/field-components/citation/citations-section.component'; -import { CcIconsComponent } from './app/item-page/field-components/cc-icons/cc-icons.component'; -import { AltmetricDonutComponent } from './app/item-page/field-components/citation/altmetric-donut/altmetric-donut.component'; -import { ItemPageWbDateFieldComponent } from './app/item-page/field-components/specific-field/wb-date/item-page-wb-date-field.component'; -import { ItemPageWbGenericWithFallbackComponent } from './app/item-page/field-components/specific-field/wb-generic-with-fallback/item-page-wb-generic-with-fallback.component'; -import { ItemPageWbExternalContentComponent } from './app/item-page/field-components/specific-field/wb-external-content/item-page-wb-external-content.component'; -import { UntypedItemComponent } from './app/item-page/simple/item-types/untyped-item/untyped-item.component'; -import { JournalComponent } from './app/entity-groups/journal-entities/item-pages/journal/journal.component'; -import { JournalIssueComponent } from './app/entity-groups/journal-entities/item-pages/journal-issue/journal-issue.component'; -import { JournalVolumeComponent } from './app/entity-groups/journal-entities/item-pages/journal-volume/journal-volume.component'; -import { JournalVolumeGridElementComponent } from './app/entity-groups/journal-entities/item-grid-elements/journal-volume/journal-volume-grid-element.component'; -import { JournalVolumeSearchResultGridElementComponent } from './app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-volume/journal-volume-search-result-grid-element.component'; -import { JournalIssueSearchResultGridElementComponent } from './app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-issue/journal-issue-search-result-grid-element.component'; -import { JournalIssueGridElementComponent } from './app/entity-groups/journal-entities/item-grid-elements/journal-issue/journal-issue-grid-element.component'; -import { JournalVolumeSidebarSearchListElementComponent } from './app/entity-groups/journal-entities/item-list-elements/sidebar-search-list-elements/journal-volume/journal-volume-sidebar-search-list-element.component'; -import { JournalIssueSidebarSearchListElementComponent } from './app/entity-groups/journal-entities/item-list-elements/sidebar-search-list-elements/journal-issue/journal-issue-sidebar-search-list-element.component'; -import { JournalIssueListElementComponent } from './app/entity-groups/journal-entities/item-list-elements/journal-issue/journal-issue-list-element.component'; -import { JournalVolumeListElementComponent } from './app/entity-groups/journal-entities/item-list-elements/journal-volume/journal-volume-list-element.component'; -import { FeaturedPublicationsListElementComponent } from './app/featured-publications/featured-publications-list-element/featured-publications-list-element.component'; -import { ClaimedTaskActionsMarkDuplicateComponent } from './app/shared/mydspace-actions/claimed-task/mark-duplicate/claimed-task-actions-mark-duplicate.component'; -import { ClaimedTaskActionsReportProblemComponent } from './app/shared/mydspace-actions/claimed-task/report-problem/claimed-task-actions-report-problem.component'; -import { SingleStatletTableComponent } from './app/atmire-cua/statlets/shared/single-statlet/graph-types/single-statlet-table/single-statlet-table.component'; -import { OkrSearchHierarchyFilterComponent } from './app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component'; -import { OkrVocabularyTreeviewComponent } from './app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component'; - -export const ENTRY_COMPONENTS = [ - PublicationComponent, - ItemSearchResultListElementComponent, - PersonSearchResultListElementComponent, - JournalSearchResultListElementComponent, - JournalVolumeSearchResultListElementComponent, - JournalIssueSearchResultListElementComponent, - UntypedItemComponent, - AltmetricDonutComponent, - CitationsSectionComponent, - CcIconsComponent, - ItemPageWbDateFieldComponent, - ItemPageWbGenericWithFallbackComponent, - ItemPageWbExternalContentComponent, - JournalComponent, - JournalIssueComponent, - JournalVolumeComponent, - JournalVolumeGridElementComponent, - JournalVolumeSearchResultGridElementComponent, - JournalIssueSearchResultGridElementComponent, - JournalIssueGridElementComponent, - JournalVolumeSidebarSearchListElementComponent, - JournalIssueSidebarSearchListElementComponent, - JournalIssueListElementComponent, - JournalVolumeListElementComponent, - FeaturedPublicationsListElementComponent, - ClaimedTaskActionsMarkDuplicateComponent, - ClaimedTaskActionsReportProblemComponent, - SingleStatletTableComponent, - OkrSearchHierarchyFilterComponent, - OkrVocabularyTreeviewComponent, -]; From edc73d35852bc70ac930546d344e0c250f79d1aa Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Thu, 1 Dec 2022 15:01:11 +0100 Subject: [PATCH 07/27] 97049: Refactor how to obtain vocabularies for filter (configurable) --- config/config.yml | 5 +++++ .../okr-search-hierarchy-filter.component.ts | 16 ++++++++++++++-- src/config/FilterVocabularyConfig.ts | 7 +++++++ src/config/app-config.interface.ts | 2 ++ src/config/default-app-config.ts | 9 +++++++++ src/environments/environment.test.ts | 10 +++++++++- 6 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 src/config/FilterVocabularyConfig.ts diff --git a/config/config.yml b/config/config.yml index b5eecd112f..5b171d9cc4 100644 --- a/config/config.yml +++ b/config/config.yml @@ -3,3 +3,8 @@ rest: host: api7.dspace.org port: 443 nameSpace: /server + +vocabularies: + - filter: 'subject' + vocabulary: 'srsc' + enabled: true diff --git a/src/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.ts b/src/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.ts index 530b3d8d2b..f3a1a41355 100644 --- a/src/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.ts +++ b/src/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.ts @@ -23,6 +23,7 @@ import { filter, map, take } from 'rxjs/operators'; import { VocabularyService } from '../../../../../core/submission/vocabularies/vocabulary.service'; import { Observable } from 'rxjs'; import { PageInfo } from '../../../../../core/shared/page-info.model'; +import { environment } from '../../../../../../environments/environment'; /** * Component that represents a hierarchy facet for a specific filter configuration. @@ -63,7 +64,7 @@ export class OkrSearchHierarchyFilterComponent extends SearchHierarchyFilterComp ngOnInit() { super.ngOnInit(); this.vocabularyExists$ = this.vocabularyService.searchTopEntries( - this.filterConfig.name, new PageInfo(), true, false, + this.getVocabularyEntry(), new PageInfo(), true, false, ).pipe( filter(rd => rd.hasCompleted), take(1), @@ -83,7 +84,7 @@ export class OkrSearchHierarchyFilterComponent extends SearchHierarchyFilterComp windowClass: 'treeview' }); modalRef.componentInstance.vocabularyOptions = { - name: this.filterConfig.name, + name: this.getVocabularyEntry(), closed: true }; modalRef.componentInstance.select.subscribe((detail: VocabularyEntryDetail) => { @@ -103,4 +104,15 @@ export class OkrSearchHierarchyFilterComponent extends SearchHierarchyFilterComp }); }); } + + /** + * 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); + if (foundVocabularyConfig.length > 0 && foundVocabularyConfig[0].enabled === true) { + return foundVocabularyConfig[0].vocabulary; + } + } } diff --git a/src/config/FilterVocabularyConfig.ts b/src/config/FilterVocabularyConfig.ts new file mode 100644 index 0000000000..a9b37af2d8 --- /dev/null +++ b/src/config/FilterVocabularyConfig.ts @@ -0,0 +1,7 @@ +import { Config } from './config.interface'; + +export interface FilterVocabularyConfig extends Config { + filter: string; + vocabulary: string; + enabled: boolean; +} diff --git a/src/config/app-config.interface.ts b/src/config/app-config.interface.ts index 62d0be7216..21682e7c45 100644 --- a/src/config/app-config.interface.ts +++ b/src/config/app-config.interface.ts @@ -15,6 +15,7 @@ import { AuthConfig } from './auth-config.interfaces'; import { UIServerConfig } from './ui-server-config.interface'; import { MediaViewerConfig } from './media-viewer-config.interface'; import { BrowseByConfig } from './browse-by-config.interface'; +import { FilterVocabularyConfig } from './FilterVocabularyConfig'; interface AppConfig extends Config { ui: UIServerConfig; @@ -34,6 +35,7 @@ interface AppConfig extends Config { collection: CollectionPageConfig; themes: ThemeConfig[]; mediaViewer: MediaViewerConfig; + vocabularies: FilterVocabularyConfig[]; } const APP_CONFIG = new InjectionToken('APP_CONFIG'); diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts index df9bbe9369..65d8691421 100644 --- a/src/config/default-app-config.ts +++ b/src/config/default-app-config.ts @@ -15,6 +15,7 @@ import { SubmissionConfig } from './submission-config.interface'; import { ThemeConfig } from './theme.model'; import { UIServerConfig } from './ui-server-config.interface'; import { UniversalConfig } from './universal-config.interface'; +import { FilterVocabularyConfig } from './FilterVocabularyConfig'; export class DefaultAppConfig implements AppConfig { production = false; @@ -314,4 +315,12 @@ export class DefaultAppConfig implements AppConfig { image: false, video: false }; + + vocabularies: FilterVocabularyConfig[] = [ + { + filter: 'subject', + vocabulary: 'srsc', + enabled: true + } + ]; } diff --git a/src/environments/environment.test.ts b/src/environments/environment.test.ts index f8a3248837..0ba688fac5 100644 --- a/src/environments/environment.test.ts +++ b/src/environments/environment.test.ts @@ -227,5 +227,13 @@ export const environment: AppConfig = { mediaViewer: { image: true, video: true - } + }, + + vocabularies: [ + { + filter: 'subject', + vocabulary: 'srsc', + enabled: true + } + ] }; From 85ccfc6ea749d214a43972728be8309cdcbe442b Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Thu, 1 Dec 2022 15:30:30 +0100 Subject: [PATCH 08/27] 97049: Remove client structure --- .../okr-vocabulary-treeview.component.scss | 0 .../okr-vocabulary-treeview.component.ts | 41 ------ ...okr-search-hierarchy-filter.component.html | 8 -- ...okr-search-hierarchy-filter.component.scss | 0 .../okr-search-hierarchy-filter.component.ts | 118 ------------------ .../search-hierarchy-filter.component.html | 7 ++ ...search-hierarchy-filter.component.spec.ts} | 12 +- .../search-hierarchy-filter.component.ts | 106 ++++++++++++++-- src/app/shared/search/search.module.ts | 6 +- src/app/shared/shared.module.ts | 5 - .../vocabulary-treeview.component.ts | 31 ++--- 11 files changed, 125 insertions(+), 209 deletions(-) delete mode 100644 src/app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component.scss delete mode 100644 src/app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component.ts delete mode 100644 src/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.html delete mode 100644 src/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.scss delete mode 100644 src/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.ts rename src/app/shared/search/search-filters/search-filter/{okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.spec.ts => search-hierarchy-filter/search-hierarchy-filter.component.spec.ts} (92%) diff --git a/src/app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component.scss b/src/app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component.scss deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component.ts b/src/app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component.ts deleted file mode 100644 index 80dcb1f739..0000000000 --- a/src/app/shared/okr-vocabulary-treeview/okr-vocabulary-treeview.component.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Component } from '@angular/core'; -import { VocabularyTreeviewComponent } from '../vocabulary-treeview/vocabulary-treeview.component'; -import { filter, map, startWith } from 'rxjs/operators'; -import { PageInfo } from '../../core/shared/page-info.model'; -import { lowerCase } from 'lodash'; - -/** - * Component that show a hierarchical vocabulary in a tree view. - * Worldbank customization which omits the authentication check. - */ -@Component({ - selector: 'ds-okr-vocabulary-treeview', - templateUrl: '../vocabulary-treeview/vocabulary-treeview.component.html', - styleUrls: [ - './okr-vocabulary-treeview.component.scss', - '../vocabulary-treeview/vocabulary-treeview.component.scss', - ] -}) -export class OkrVocabularyTreeviewComponent extends VocabularyTreeviewComponent { - - /** - * Initialize the component, setting up the data to build the tree - */ - ngOnInit(): void { - this.subs.push( - this.vocabularyTreeviewService.getData().subscribe((data) => { - this.dataSource.data = data; - }) - ); - - this.translate.get(`search.filters.filter.${this.vocabularyOptions.name}.head`).pipe( - map((type) => lowerCase(type)), - ).subscribe( - (type) => this.description = this.translate.get('okr-vocabulary-treeview.info', { type }) - ); - - this.loading = this.vocabularyTreeviewService.isLoading(); - - this.vocabularyTreeviewService.initialize(this.vocabularyOptions, new PageInfo(), null); - } -} diff --git a/src/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.html b/src/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.html deleted file mode 100644 index 731b5045bf..0000000000 --- a/src/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.html +++ /dev/null @@ -1,8 +0,0 @@ - - - - {{'search.filters.filter.show-tree' | translate: {name: ('search.filters.filter.' + filterConfig.name + '.head' | translate | lowercase )} }} - diff --git a/src/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.scss b/src/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.scss deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.ts b/src/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.ts deleted file mode 100644 index f3a1a41355..0000000000 --- a/src/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { Component, Inject } from '@angular/core'; -import { renderFacetFor } from '../search-filter-type-decorator'; -import { FilterType } from '../../../models/filter-type.model'; -import { facetLoad } from '../search-facet-filter/search-facet-filter.component'; -import { SearchHierarchyFilterComponent } from '../search-hierarchy-filter/search-hierarchy-filter.component'; -import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; -import { OkrVocabularyTreeviewComponent } from '../../../../okr-vocabulary-treeview/okr-vocabulary-treeview.component'; -import { VocabularyEntryDetail } from '../../../../../core/submission/vocabularies/models/vocabulary-entry-detail.model'; -import { SearchService } from '../../../../../core/shared/search/search.service'; -import { - FILTER_CONFIG, - IN_PLACE_SEARCH, - SearchFilterService -} from '../../../../../core/shared/search/search-filter.service'; -import { Router } from '@angular/router'; -import { RemoteDataBuildService } from '../../../../../core/cache/builders/remote-data-build.service'; -import { SEARCH_CONFIG_SERVICE } from '../../../../../my-dspace-page/my-dspace-page.component'; -import { SearchConfigurationService } from '../../../../../core/shared/search/search-configuration.service'; -import { SearchFilterConfig } from '../../../models/search-filter-config.model'; -import { FacetValue } from '../../../models/facet-value.model'; -import { getFacetValueForType } from '../../../search.utils'; -import { filter, map, take } from 'rxjs/operators'; -import { VocabularyService } from '../../../../../core/submission/vocabularies/vocabulary.service'; -import { Observable } from 'rxjs'; -import { PageInfo } from '../../../../../core/shared/page-info.model'; -import { environment } from '../../../../../../environments/environment'; - -/** - * Component that represents a hierarchy facet for a specific filter configuration. - * Worldbank customization which features a link at the bottom to open a vocabulary popup, - */ -@Component({ - selector: 'ds-okr-search-hierarchy-filter', - styleUrls: ['./okr-search-hierarchy-filter.component.scss'], - templateUrl: './okr-search-hierarchy-filter.component.html', - animations: [facetLoad] -}) -@renderFacetFor(FilterType.hierarchy) -export class OkrSearchHierarchyFilterComponent extends SearchHierarchyFilterComponent { - - constructor(protected searchService: SearchService, - protected filterService: SearchFilterService, - protected rdbs: RemoteDataBuildService, - protected router: Router, - protected modalService: NgbModal, - protected vocabularyService: VocabularyService, - @Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService, - @Inject(IN_PLACE_SEARCH) public inPlaceSearch: boolean, - @Inject(FILTER_CONFIG) public filterConfig: SearchFilterConfig, - ) { - super( - searchService, - filterService, - rdbs, - router, - searchConfigService, - inPlaceSearch, - filterConfig, - ); - } - - vocabularyExists$: Observable; - - ngOnInit() { - 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; - }), - ); - } - - /** - * Open the vocabulary tree modal popup. - * When an entry is selected, add the filter query to the search options. - */ - showVocabularyTree() { - const modalRef: NgbModalRef = this.modalService.open(OkrVocabularyTreeviewComponent, { - size: 'lg', - windowClass: 'treeview' - }); - modalRef.componentInstance.vocabularyOptions = { - name: this.getVocabularyEntry(), - closed: true - }; - modalRef.componentInstance.select.subscribe((detail: VocabularyEntryDetail) => { - this.selectedValues$ - .pipe(take(1)) - .subscribe((selectedValues) => { - this.router.navigate( - [this.searchService.getSearchLink()], - { - queryParams: { - [this.filterConfig.paramName]: [...selectedValues, {value: detail.value}] - .map((facetValue: FacetValue) => getFacetValueForType(facetValue, this.filterConfig)), - }, - queryParamsHandling: 'merge', - }, - ); - }); - }); - } - - /** - * 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); - if (foundVocabularyConfig.length > 0 && foundVocabularyConfig[0].enabled === true) { - return foundVocabularyConfig[0].vocabulary; - } - } -} 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 49ca6fe3fd..eb49235641 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 @@ -29,3 +29,10 @@ ngDefaultControl > + + + {{'search.filters.filter.show-tree' | translate: {name: ('search.filters.filter.' + filterConfig.name + '.head' | translate | lowercase )} }} + diff --git a/src/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.spec.ts b/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.spec.ts similarity index 92% rename from src/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.spec.ts rename to src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.spec.ts index fe0866468a..41ab296c18 100644 --- a/src/app/shared/search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component.spec.ts +++ b/src/app/shared/search/search-filters/search-filter/search-hierarchy-filter/search-hierarchy-filter.component.spec.ts @@ -1,4 +1,4 @@ -import { OkrSearchHierarchyFilterComponent } from './okr-search-hierarchy-filter.component'; +import { SearchHierarchyFilterComponent } from './search-hierarchy-filter.component'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { DebugElement, EventEmitter, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; @@ -23,12 +23,12 @@ import { NgbModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { SEARCH_CONFIG_SERVICE } from '../../../../../my-dspace-page/my-dspace-page.component'; import { SearchConfigurationServiceStub } from '../../../../testing/search-configuration-service.stub'; import { VocabularyEntryDetail } from '../../../../../core/submission/vocabularies/models/vocabulary-entry-detail.model'; -import { FacetValue } from '../../../../../../../../app/shared/search/facet-value.model'; -import { SearchFilterConfig } from '../../../../../../../../app/shared/search/search-filter-config.model'; +import { FacetValue} from '../../../models/facet-value.model'; +import { SearchFilterConfig } from '../../../models/search-filter-config.model'; describe('OkrSearchHierarchyFilterComponent', () => { - let fixture: ComponentFixture; + let fixture: ComponentFixture; let showVocabularyTreeLink: DebugElement; const testSearchLink = 'test-search'; @@ -62,7 +62,7 @@ describe('OkrSearchHierarchyFilterComponent', () => { TranslateModule.forRoot(), ], declarations: [ - OkrSearchHierarchyFilterComponent, + SearchHierarchyFilterComponent, ], providers: [ { provide: SearchService, useValue: searchService }, @@ -80,7 +80,7 @@ describe('OkrSearchHierarchyFilterComponent', () => { }); function init() { - fixture = TestBed.createComponent(OkrSearchHierarchyFilterComponent); + fixture = TestBed.createComponent(SearchHierarchyFilterComponent); fixture.detectChanges(); showVocabularyTreeLink = fixture.debugElement.query(By.css('a#show-test-search-filter-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 7aa81116de..e24e6c6d02 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,18 +1,110 @@ -import { Component, OnInit } from '@angular/core'; -import { FilterType } from '../../../models/filter-type.model'; +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'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { VocabularyTreeviewComponent } from '../../../../vocabulary-treeview/vocabulary-treeview.component'; +import { + VocabularyEntryDetail +} from '../../../../../core/submission/vocabularies/models/vocabulary-entry-detail.model'; +import { SearchService } from '../../../../../core/shared/search/search.service'; +import { + FILTER_CONFIG, + IN_PLACE_SEARCH, + SearchFilterService +} from '../../../../../core/shared/search/search-filter.service'; +import { Router } from '@angular/router'; +import { RemoteDataBuildService } from '../../../../../core/cache/builders/remote-data-build.service'; +import { SEARCH_CONFIG_SERVICE } from '../../../../../my-dspace-page/my-dspace-page.component'; +import { SearchConfigurationService } from '../../../../../core/shared/search/search-configuration.service'; +import { SearchFilterConfig } from '../../../models/search-filter-config.model'; +import { FacetValue } from '../../../models/facet-value.model'; +import { getFacetValueForType } from '../../../search.utils'; +import { filter, map, take } from 'rxjs/operators'; +import { VocabularyService } from '../../../../../core/submission/vocabularies/vocabulary.service'; +import { Observable } from 'rxjs'; +import { PageInfo } from '../../../../../core/shared/page-info.model'; +import { environment } from '../../../../../../environments/environment'; +/** + * Component that represents a hierarchy facet for a specific filter configuration. + */ @Component({ selector: 'ds-search-hierarchy-filter', styleUrls: ['./search-hierarchy-filter.component.scss'], templateUrl: './search-hierarchy-filter.component.html', animations: [facetLoad] }) - -/** - * Component that represents a hierarchy facet for a specific filter configuration - */ -// @renderFacetFor(FilterType.hierarchy) +@renderFacetFor(FilterType.hierarchy) export class SearchHierarchyFilterComponent extends SearchFacetFilterComponent implements OnInit { + + constructor(protected searchService: SearchService, + protected filterService: SearchFilterService, + protected rdbs: RemoteDataBuildService, + protected router: Router, + protected modalService: NgbModal, + protected vocabularyService: VocabularyService, + @Inject(SEARCH_CONFIG_SERVICE) public searchConfigService: SearchConfigurationService, + @Inject(IN_PLACE_SEARCH) public inPlaceSearch: boolean, + @Inject(FILTER_CONFIG) public filterConfig: SearchFilterConfig, + ) { + super(searchService, filterService, rdbs, router, searchConfigService, inPlaceSearch, filterConfig); + } + + vocabularyExists$: Observable; + + ngOnInit() { + 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; + }), + ); + } + + /** + * Open the vocabulary tree modal popup. + * When an entry is selected, add the filter query to the search options. + */ + showVocabularyTree() { + const modalRef: NgbModalRef = this.modalService.open(VocabularyTreeviewComponent, { + size: 'lg', + windowClass: 'treeview' + }); + modalRef.componentInstance.vocabularyOptions = { + name: this.getVocabularyEntry(), + closed: true + }; + modalRef.componentInstance.select.subscribe((detail: VocabularyEntryDetail) => { + this.selectedValues$ + .pipe(take(1)) + .subscribe((selectedValues) => { + this.router.navigate( + [this.searchService.getSearchLink()], + { + queryParams: { + [this.filterConfig.paramName]: [...selectedValues, {value: detail.value}] + .map((facetValue: FacetValue) => getFacetValueForType(facetValue, this.filterConfig)), + }, + queryParamsHandling: 'merge', + }, + ); + }); + }); + } + + /** + * 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); + if (foundVocabularyConfig.length > 0 && foundVocabularyConfig[0].enabled === true) { + return foundVocabularyConfig[0].vocabulary; + } + } } diff --git a/src/app/shared/search/search.module.ts b/src/app/shared/search/search.module.ts index e9c0b3e121..668d260c23 100644 --- a/src/app/shared/search/search.module.ts +++ b/src/app/shared/search/search.module.ts @@ -28,9 +28,6 @@ import { MissingTranslationHelper } from '../translate/missing-translation.helpe import { SharedModule } from '../shared.module'; import { SearchResultsComponent } from './search-results/search-results.component'; import { SearchComponent } from './search.component'; -import { - OkrSearchHierarchyFilterComponent -} from './search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component'; const COMPONENTS = [ SearchComponent, @@ -53,8 +50,7 @@ const COMPONENTS = [ SearchAuthorityFilterComponent, SearchSwitchConfigurationComponent, ConfigurationSearchPageComponent, - ThemedConfigurationSearchPageComponent, - OkrSearchHierarchyFilterComponent + ThemedConfigurationSearchPageComponent ]; const ENTRY_COMPONENTS = [ diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 6f4091f465..715ee66a99 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -177,10 +177,6 @@ import { ScopeSelectorModalComponent } from './search-form/scope-selector-modal/ import { BitstreamRequestACopyPageComponent } from './bitstream-request-a-copy-page/bitstream-request-a-copy-page.component'; import { DsSelectComponent } from './ds-select/ds-select.component'; import { LogInOidcComponent } from './log-in/methods/oidc/log-in-oidc.component'; -import { OkrVocabularyTreeviewComponent } from './okr-vocabulary-treeview/okr-vocabulary-treeview.component'; -import { - OkrSearchHierarchyFilterComponent -} from './search/search-filters/search-filter/okr-search-hierarchy-filter/okr-search-hierarchy-filter.component'; const MODULES = [ // Do NOT include UniversalModule, HttpModule, or JsonpModule here @@ -350,7 +346,6 @@ const COMPONENTS = [ CommunitySidebarSearchListElementComponent, SearchNavbarComponent, ScopeSelectorModalComponent, - OkrVocabularyTreeviewComponent ]; const ENTRY_COMPONENTS = [ diff --git a/src/app/shared/vocabulary-treeview/vocabulary-treeview.component.ts b/src/app/shared/vocabulary-treeview/vocabulary-treeview.component.ts index f9e4d04f3b..b62c4a2274 100644 --- a/src/app/shared/vocabulary-treeview/vocabulary-treeview.component.ts +++ b/src/app/shared/vocabulary-treeview/vocabulary-treeview.component.ts @@ -2,7 +2,7 @@ import { FlatTreeControl } from '@angular/cdk/tree'; import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; -import { filter, find, startWith } from 'rxjs/operators'; +import { filter, find, startWith, map } from 'rxjs/operators'; import { Observable, Subscription } from 'rxjs'; import { select, Store } from '@ngrx/store'; import { TranslateService } from '@ngx-translate/core'; @@ -18,6 +18,7 @@ import { PageInfo } from '../../core/shared/page-info.model'; import { VocabularyEntry } from '../../core/submission/vocabularies/models/vocabulary-entry.model'; import { VocabularyTreeFlattener } from './vocabulary-tree-flattener'; import { VocabularyTreeFlatDataSource } from './vocabulary-tree-flat-data-source'; +import { lowerCase } from 'lodash'; /** * Component that show a hierarchical vocabulary in a tree view @@ -98,7 +99,7 @@ export class VocabularyTreeviewComponent implements OnDestroy, OnInit { /** * Array to track all subscriptions and unsubscribe them onDestroy */ - protected subs: Subscription[] = []; + private subs: Subscription[] = []; /** * Initialize instance variables @@ -110,9 +111,9 @@ export class VocabularyTreeviewComponent implements OnDestroy, OnInit { */ constructor( public activeModal: NgbActiveModal, - protected vocabularyTreeviewService: VocabularyTreeviewService, - protected store: Store, - protected translate: TranslateService + private vocabularyTreeviewService: VocabularyTreeviewService, + private store: Store, + private translate: TranslateService ) { this.treeFlattener = new VocabularyTreeFlattener(this.transformer, this.getLevel, this.isExpandable, this.getChildren); @@ -203,23 +204,15 @@ export class VocabularyTreeviewComponent implements OnDestroy, OnInit { }) ); - const descriptionLabel = 'vocabulary-treeview.tree.description.' + this.vocabularyOptions.name; - this.description = this.translate.get(descriptionLabel).pipe( - filter((msg) => msg !== descriptionLabel), - startWith('') + this.translate.get(`search.filters.filter.${this.vocabularyOptions.name}.head`).pipe( + map((type) => lowerCase(type)), + ).subscribe( + (type) => this.description = this.translate.get('okr-vocabulary-treeview.info', { type }) ); - // set isAuthenticated - this.isAuthenticated = this.store.pipe(select(isAuthenticated)); - this.loading = this.vocabularyTreeviewService.isLoading(); - this.isAuthenticated.pipe( - find((isAuth) => isAuth) - ).subscribe(() => { - const entryId: string = (this.selectedItem) ? this.getEntryId(this.selectedItem) : null; - this.vocabularyTreeviewService.initialize(this.vocabularyOptions, new PageInfo(), entryId); - }); + this.vocabularyTreeviewService.initialize(this.vocabularyOptions, new PageInfo(), null); } /** @@ -301,7 +294,7 @@ export class VocabularyTreeviewComponent implements OnDestroy, OnInit { /** * Return an id for a given {@link VocabularyEntry} */ - protected getEntryId(entry: VocabularyEntry): string { + private getEntryId(entry: VocabularyEntry): string { return entry.authority || entry.otherInformation.id || undefined; } } From 472c2a1dd1da96b509bd75688914caea786156f1 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Thu, 1 Dec 2022 15:34:41 +0100 Subject: [PATCH 09/27] 97049: Remove client structure --- .../search-hierarchy-filter.component.spec.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 41ab296c18..88cb0400e2 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 @@ -26,14 +26,14 @@ import { VocabularyEntryDetail } from '../../../../../core/submission/vocabulari import { FacetValue} from '../../../models/facet-value.model'; import { SearchFilterConfig } from '../../../models/search-filter-config.model'; -describe('OkrSearchHierarchyFilterComponent', () => { +fdescribe('SearchHierarchyFilterComponent', () => { let fixture: ComponentFixture; let showVocabularyTreeLink: DebugElement; const testSearchLink = 'test-search'; const testSearchFilter = 'test-search-filter'; - const okrVocabularyTreeViewComponent = { + const VocabularyTreeViewComponent = { select: new EventEmitter(), }; @@ -47,7 +47,7 @@ describe('OkrSearchHierarchyFilterComponent', () => { const router = new RouterStub(); const ngbModal = jasmine.createSpyObj('modal', { open: { - componentInstance: okrVocabularyTreeViewComponent, + componentInstance: VocabularyTreeViewComponent, } }); const vocabularyService = { @@ -134,7 +134,7 @@ describe('OkrSearchHierarchyFilterComponent', () => { fixture.componentInstance.selectedValues$ = observableOf( alreadySelectedValues.map(value => Object.assign(new FacetValue(), { value })) ); - okrVocabularyTreeViewComponent.select.emit(Object.assign(new VocabularyEntryDetail(), { + VocabularyTreeViewComponent.select.emit(Object.assign(new VocabularyEntryDetail(), { value: newSelectedValue, })); }); From dd3a528c485a9f1644fe61525cea0dc81c4367f7 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Thu, 1 Dec 2022 16:52:50 +0100 Subject: [PATCH 10/27] 97049: Remove accidental focused describe --- .../search-hierarchy-filter.component.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 88cb0400e2..306d252395 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 @@ -26,7 +26,7 @@ import { VocabularyEntryDetail } from '../../../../../core/submission/vocabulari import { FacetValue} from '../../../models/facet-value.model'; import { SearchFilterConfig } from '../../../models/search-filter-config.model'; -fdescribe('SearchHierarchyFilterComponent', () => { +describe('SearchHierarchyFilterComponent', () => { let fixture: ComponentFixture; let showVocabularyTreeLink: DebugElement; From 6ed762a8ac1fcbe206b0f656b18457577cce49d8 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Thu, 1 Dec 2022 17:12:14 +0100 Subject: [PATCH 11/27] 97049: alter tests --- .../vocabulary-treeview/vocabulary-treeview.component.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/shared/vocabulary-treeview/vocabulary-treeview.component.spec.ts b/src/app/shared/vocabulary-treeview/vocabulary-treeview.component.spec.ts index 1bdf4b9df7..d965bc7f4b 100644 --- a/src/app/shared/vocabulary-treeview/vocabulary-treeview.component.spec.ts +++ b/src/app/shared/vocabulary-treeview/vocabulary-treeview.component.spec.ts @@ -141,7 +141,7 @@ describe('VocabularyTreeviewComponent test suite', () => { comp.selectedItem = currentValue; fixture.detectChanges(); expect(comp.dataSource.data).toEqual([]); - expect(vocabularyTreeviewServiceStub.initialize).toHaveBeenCalledWith(comp.vocabularyOptions, new PageInfo(), 'entryID'); + expect(vocabularyTreeviewServiceStub.initialize).toHaveBeenCalledWith(comp.vocabularyOptions, new PageInfo(), null); }); it('should should init component properly with init value as VocabularyEntry', () => { @@ -153,7 +153,7 @@ describe('VocabularyTreeviewComponent test suite', () => { comp.selectedItem = currentValue; fixture.detectChanges(); expect(comp.dataSource.data).toEqual([]); - expect(vocabularyTreeviewServiceStub.initialize).toHaveBeenCalledWith(comp.vocabularyOptions, new PageInfo(), 'entryID'); + expect(vocabularyTreeviewServiceStub.initialize).toHaveBeenCalledWith(comp.vocabularyOptions, new PageInfo(), null); }); it('should call loadMore function', () => { From aac8fea225949e199271efb9fc9c28bb7aa47716 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Fri, 2 Dec 2022 13:43:22 +0100 Subject: [PATCH 12/27] 97049: Fix constructor issue + rework test for new functionality --- .../search-hierarchy-filter.component.spec.ts | 237 +++++++++--------- .../search-hierarchy-filter.component.ts | 7 +- 2 files changed, 124 insertions(+), 120 deletions(-) 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 9302e66d98..872d0772bd 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 @@ -1,155 +1,158 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; import { SearchHierarchyFilterComponent } from './search-hierarchy-filter.component'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { DebugElement, EventEmitter, NO_ERRORS_SCHEMA } from '@angular/core'; +import { By } from '@angular/platform-browser'; +import { VocabularyService } from '../../../../../core/submission/vocabularies/vocabulary.service'; +import { of as observableOf, BehaviorSubject } from 'rxjs'; +import { RemoteData } from '../../../../../core/data/remote-data'; +import { RequestEntryState } from '../../../../../core/data/request-entry-state.model'; +import { TranslateModule } from '@ngx-translate/core'; +import { RouterStub } from '../../../../testing/router.stub'; +import { buildPaginatedList } from '../../../../../core/data/paginated-list.model'; +import { PageInfo } from '../../../../../core/shared/page-info.model'; +import { CommonModule } from '@angular/common'; import { SearchService } from '../../../../../core/shared/search/search.service'; import { FILTER_CONFIG, IN_PLACE_SEARCH, - REFRESH_FILTER, - SearchFilterService + SearchFilterService, + REFRESH_FILTER } from '../../../../../core/shared/search/search-filter.service'; import { RemoteDataBuildService } from '../../../../../core/cache/builders/remote-data-build.service'; -import { SearchFiltersComponent } from '../../search-filters.component'; import { Router } from '@angular/router'; -import { RouterStub } from '../../../../testing/router.stub'; -import { SearchServiceStub } from '../../../../testing/search-service.stub'; -import { BehaviorSubject, Observable, of as observableOf } from 'rxjs'; +import { NgbModal, NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { SEARCH_CONFIG_SERVICE } from '../../../../../my-dspace-page/my-dspace-page.component'; import { SearchConfigurationServiceStub } from '../../../../testing/search-configuration-service.stub'; +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 { TranslateModule } from '@ngx-translate/core'; -import { - FilterInputSuggestionsComponent -} from '../../../../input-suggestions/filter-suggestions/filter-input-suggestions.component'; -import { FormsModule } from '@angular/forms'; -import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; -import { createSuccessfulRemoteDataObject$ } from '../../../../remote-data.utils'; -import { FacetValue } from '../../../models/facet-value.model'; -import { FilterType } from '../../../models/filter-type.model'; -import { createPaginatedList } from '../../../../testing/utils.test'; -import { RemoteData } from '../../../../../core/data/remote-data'; -import { PaginatedList } from '../../../../../core/data/paginated-list.model'; describe('SearchHierarchyFilterComponent', () => { - let comp: SearchHierarchyFilterComponent; + let fixture: ComponentFixture; - let searchService: SearchService; - let router; + let showVocabularyTreeLink: DebugElement; - const value1 = 'testvalue1'; - const value2 = 'test2'; - const value3 = 'another value3'; - const values: FacetValue[] = [ - { - label: value1, - value: value1, - count: 52, - _links: { - self: { - href: '' - }, - search: { - href: '' - } - } - }, { - label: value2, - value: value2, - count: 20, - _links: { - self: { - href: '' - }, - search: { - href: '' - } - } - }, { - label: value3, - value: value3, - count: 5, - _links: { - self: { - href: '' - }, - search: { - href: '' - } - } - } - ]; - const mockValues = createSuccessfulRemoteDataObject$(createPaginatedList(values)); - - const searchFilterServiceStub = { - getSelectedValuesForFilter(_filterConfig: SearchFilterConfig): Observable { - return observableOf(values.map((value: FacetValue) => value.value)); - }, - getPage(_paramName: string): Observable { - return observableOf(0); - }, - resetPage(_filterName: string): void { - // empty - } + const testSearchLink = 'test-search'; + const testSearchFilter = 'test-search-filter'; + const VocabularyTreeViewComponent = { + select: new EventEmitter(), }; - const remoteDataBuildServiceStub = { - aggregate(_input: Observable>[]): Observable[]>> { - return createSuccessfulRemoteDataObject$([createPaginatedList(values)]); + const searchService = { + getSearchLink: () => testSearchLink, + getFacetValuesFor: () => observableOf([]), + }; + const searchFilterService = { + getPage: () => observableOf(0), + }; + const router = new RouterStub(); + const ngbModal = jasmine.createSpyObj('modal', { + open: { + componentInstance: VocabularyTreeViewComponent, } + }); + const vocabularyService = { + searchTopEntries: () => undefined, }; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [TranslateModule.forRoot(), NoopAnimationsModule, FormsModule], + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + CommonModule, + NgbModule, + TranslateModule.forRoot(), + ], declarations: [ SearchHierarchyFilterComponent, - SearchFiltersComponent, - FilterInputSuggestionsComponent ], providers: [ - { provide: SearchService, useValue: new SearchServiceStub() }, - { provide: SearchFilterService, useValue: searchFilterServiceStub }, - { provide: RemoteDataBuildService, useValue: remoteDataBuildServiceStub }, - { provide: Router, useValue: new RouterStub() }, + { provide: SearchService, useValue: searchService }, + { provide: SearchFilterService, useValue: searchFilterService }, + { provide: RemoteDataBuildService, useValue: {} }, + { provide: Router, useValue: router }, + { provide: NgbModal, useValue: ngbModal }, + { provide: VocabularyService, useValue: vocabularyService }, { provide: SEARCH_CONFIG_SERVICE, useValue: new SearchConfigurationServiceStub() }, { provide: IN_PLACE_SEARCH, useValue: false }, - { provide: FILTER_CONFIG, useValue: new SearchFilterConfig() }, - { provide: REFRESH_FILTER, useValue: new BehaviorSubject(false) } + { provide: FILTER_CONFIG, useValue: Object.assign(new SearchFilterConfig(), { name: testSearchFilter }) }, + { provide: REFRESH_FILTER, useValue: new BehaviorSubject(false)} ], - schemas: [NO_ERRORS_SCHEMA] - }).overrideComponent(SearchHierarchyFilterComponent, { - set: { changeDetection: ChangeDetectionStrategy.Default } + schemas: [NO_ERRORS_SCHEMA], }).compileComponents(); - }) - ; - const mockFilterConfig: SearchFilterConfig = Object.assign(new SearchFilterConfig(), { - name: 'filterName1', - filterType: FilterType.text, - hasFacets: false, - isOpenByDefault: false, - pageSize: 2 }); - beforeEach(async () => { + function init() { fixture = TestBed.createComponent(SearchHierarchyFilterComponent); - comp = fixture.componentInstance; // SearchHierarchyFilterComponent test instance - comp.filterConfig = mockFilterConfig; - searchService = (comp as any).searchService; - // @ts-ignore - spyOn(searchService, 'getFacetValuesFor').and.returnValue(mockValues); - router = (comp as any).router; fixture.detectChanges(); + showVocabularyTreeLink = fixture.debugElement.query(By.css('a#show-test-search-filter-tree')); + } + + describe('if the vocabulary doesn\'t exist', () => { + + beforeEach(() => { + spyOn(vocabularyService, 'searchTopEntries').and.returnValue(observableOf(new RemoteData( + undefined, 0, 0, RequestEntryState.Error, undefined, undefined, 404 + ))); + init(); + }); + + it('should not show the vocabulary tree link', () => { + expect(showVocabularyTreeLink).toBeNull(); + }); }); - it('should navigate to the correct filter with the query operator', () => { - expect((comp as any).searchService.getFacetValuesFor).toHaveBeenCalledWith(comp.filterConfig, 0, {}, null, true); + describe('if the vocabulary exists', () => { - const searchQuery = 'MARVEL'; - comp.onSubmit(searchQuery); + beforeEach(() => { + spyOn(vocabularyService, 'searchTopEntries').and.returnValue(observableOf(new RemoteData( + undefined, 0, 0, RequestEntryState.Success, undefined, buildPaginatedList(new PageInfo(), []), 200 + ))); + init(); + }); - expect(router.navigate).toHaveBeenCalledWith(['', 'search'], Object({ - queryParams: Object({ [mockFilterConfig.paramName]: [...values.map((value: FacetValue) => `${value.value},equals`), `${searchQuery},query`] }), - queryParamsHandling: 'merge' - })); + it('should show the vocabulary tree link', () => { + expect(showVocabularyTreeLink).toBeTruthy(); + }); + + describe('when clicking the vocabulary tree link', () => { + + beforeEach(async () => { + showVocabularyTreeLink.nativeElement.click(); + }); + + it('should open the vocabulary tree modal', () => { + expect(ngbModal.open).toHaveBeenCalled(); + }); + + describe('when selecting a value from the vocabulary tree', () => { + + const alreadySelectedValues = [ + 'already-selected-value-1', + 'already-selected-value-2', + ]; + const newSelectedValue = 'new-selected-value'; + + beforeEach(() => { + fixture.componentInstance.selectedValues$ = observableOf( + alreadySelectedValues.map(value => Object.assign(new FacetValue(), { value })) + ); + VocabularyTreeViewComponent.select.emit(Object.assign(new VocabularyEntryDetail(), { + value: newSelectedValue, + })); + }); + + it('should add a new search filter to the existing search filters', () => { + expect(router.navigate).toHaveBeenCalledWith([testSearchLink], { + queryParams: { + [`f.${testSearchFilter}`]: [ + ...alreadySelectedValues, + newSelectedValue, + ].map((value => `${value},equals`)), + }, + queryParamsHandling: 'merge', + }); + }); + }); + }); }); }); 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 f47e652f3a..3dd866c5b1 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 @@ -11,7 +11,7 @@ import { SearchService } from '../../../../../core/shared/search/search.service' import { FILTER_CONFIG, IN_PLACE_SEARCH, - SearchFilterService + SearchFilterService, REFRESH_FILTER } from '../../../../../core/shared/search/search-filter.service'; import { Router } from '@angular/router'; import { RemoteDataBuildService } from '../../../../../core/cache/builders/remote-data-build.service'; @@ -22,7 +22,7 @@ import { FacetValue } from '../../../models/facet-value.model'; import { getFacetValueForType } from '../../../search.utils'; import { filter, map, take } from 'rxjs/operators'; import { VocabularyService } from '../../../../../core/submission/vocabularies/vocabulary.service'; -import { Observable } from 'rxjs'; +import { Observable, BehaviorSubject } from 'rxjs'; import { PageInfo } from '../../../../../core/shared/page-info.model'; import { environment } from '../../../../../../environments/environment'; import { addOperatorToFilterValue } from '../../../search.utils'; @@ -49,8 +49,9 @@ export class SearchHierarchyFilterComponent extends SearchFacetFilterComponent i @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 ) { - super(searchService, filterService, rdbs, router, searchConfigService, inPlaceSearch, filterConfig); + super(searchService, filterService, rdbs, router, searchConfigService, inPlaceSearch, filterConfig, refreshFilters); } vocabularyExists$: Observable; From 099debf6663ab4e45f939bdaddf71121999f8259 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Fri, 2 Dec 2022 14:11:41 +0100 Subject: [PATCH 13/27] 97049: Resolve lint issues --- .../vocabulary-treeview/vocabulary-treeview.component.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/app/shared/vocabulary-treeview/vocabulary-treeview.component.ts b/src/app/shared/vocabulary-treeview/vocabulary-treeview.component.ts index 369c4ac817..548c3d89be 100644 --- a/src/app/shared/vocabulary-treeview/vocabulary-treeview.component.ts +++ b/src/app/shared/vocabulary-treeview/vocabulary-treeview.component.ts @@ -2,14 +2,13 @@ import { FlatTreeControl } from '@angular/cdk/tree'; import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; -import { filter, find, startWith, map } from 'rxjs/operators'; +import { map } from 'rxjs/operators'; import { Observable, Subscription } from 'rxjs'; -import { select, Store } from '@ngrx/store'; +import { Store } from '@ngrx/store'; import { TranslateService } from '@ngx-translate/core'; import { VocabularyEntryDetail } from '../../core/submission/vocabularies/models/vocabulary-entry-detail.model'; import { hasValue, isEmpty, isNotEmpty } from '../empty.util'; -import { isAuthenticated } from '../../core/auth/selectors'; import { VocabularyTreeviewService } from './vocabulary-treeview.service'; import { LOAD_MORE, LOAD_MORE_ROOT, TreeviewFlatNode, TreeviewNode } from './vocabulary-treeview-node.model'; import { VocabularyOptions } from '../../core/submission/vocabularies/models/vocabulary-options.model'; @@ -18,7 +17,7 @@ import { VocabularyEntry } from '../../core/submission/vocabularies/models/vocab import { VocabularyTreeFlattener } from './vocabulary-tree-flattener'; import { VocabularyTreeFlatDataSource } from './vocabulary-tree-flat-data-source'; import { CoreState } from '../../core/core-state.model'; -import { lowerCase } from 'lodash'; +import { lowerCase } from 'lodash/string'; /** * Component that show a hierarchical vocabulary in a tree view From eb1536819733e6e770dfa8fb58c4c5f1e7245d9e Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Mon, 5 Dec 2022 09:58:38 +0100 Subject: [PATCH 14/27] 97049: Clean up strings --- .../vocabulary-treeview.component.ts | 2 +- src/assets/i18n/en.json5 | 22 +------------------ 2 files changed, 2 insertions(+), 22 deletions(-) diff --git a/src/app/shared/vocabulary-treeview/vocabulary-treeview.component.ts b/src/app/shared/vocabulary-treeview/vocabulary-treeview.component.ts index 548c3d89be..deffc11e88 100644 --- a/src/app/shared/vocabulary-treeview/vocabulary-treeview.component.ts +++ b/src/app/shared/vocabulary-treeview/vocabulary-treeview.component.ts @@ -206,7 +206,7 @@ export class VocabularyTreeviewComponent implements OnDestroy, OnInit { this.translate.get(`search.filters.filter.${this.vocabularyOptions.name}.head`).pipe( map((type) => lowerCase(type)), ).subscribe( - (type) => this.description = this.translate.get('okr-vocabulary-treeview.info', { type }) + (type) => this.description = this.translate.get('vocabulary-treeview.info', { type }) ); this.loading = this.vocabularyTreeviewService.isLoading(); diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 32b8c4c265..84bbc04af3 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -4828,25 +4828,5 @@ "search.filters.filter.show-tree": "Browse {{ name }} tree", - "search.filters.filter.supportedlanguage.placeholder": "Supported Language", - - "search.filters.filter.supportedlanguage.label": "Search supported Language", - - "search.filters.filter.topic.placeholder": "Topic", - - "search.filters.filter.topic.label": "Search topic", - - "search.filters.filter.doctype.placeholder": "Document type", - - "search.filters.filter.doctype.label": "Search document type", - - "search.filters.filter.region.placeholder": "Region", - - "search.filters.filter.region.label": "Search region", - - "search.filters.filter.country.placeholder": "Country", - - "search.filters.filter.country.label": "Search country", - - "okr-vocabulary-treeview.info": "Click a {{ type }} to add a search filter" + "vocabulary-treeview.info": "Select a subject to add as search filter" } From acbff2e6b42e15284e53ee522f6cc1ed807424ca Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Mon, 5 Dec 2022 15:13:13 +0100 Subject: [PATCH 15/27] 97049: Minor refactor // adding docs --- config/config.example.yml | 10 +++++++++- config/config.yml | 5 ----- src/config/FilterVocabularyConfig.ts | 7 ------- src/config/app-config.interface.ts | 2 +- src/config/default-app-config.ts | 5 ++++- src/config/filter-vocabulary-config.ts | 22 ++++++++++++++++++++++ 6 files changed, 36 insertions(+), 15 deletions(-) delete mode 100644 src/config/FilterVocabularyConfig.ts create mode 100644 src/config/filter-vocabulary-config.ts diff --git a/config/config.example.yml b/config/config.example.yml index 27400f0041..7bc96b8e4a 100644 --- a/config/config.example.yml +++ b/config/config.example.yml @@ -295,4 +295,12 @@ info: # display in supported metadata fields. By default, only dc.description.abstract is supported. markdown: enabled: false - mathjax: false \ No newline at end of file + mathjax: false + +# Which vocabularies should be used for which search filters +# and whether to show the filter in the search sidebar +# Take a look at the filter-vocabulary-config.ts file for documentation on how the options are obtained +vocabularies: + - filter: 'subject' + vocabulary: 'srsc' + enabled: true diff --git a/config/config.yml b/config/config.yml index 5b171d9cc4..b5eecd112f 100644 --- a/config/config.yml +++ b/config/config.yml @@ -3,8 +3,3 @@ rest: host: api7.dspace.org port: 443 nameSpace: /server - -vocabularies: - - filter: 'subject' - vocabulary: 'srsc' - enabled: true diff --git a/src/config/FilterVocabularyConfig.ts b/src/config/FilterVocabularyConfig.ts deleted file mode 100644 index a9b37af2d8..0000000000 --- a/src/config/FilterVocabularyConfig.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Config } from './config.interface'; - -export interface FilterVocabularyConfig extends Config { - filter: string; - vocabulary: string; - enabled: boolean; -} diff --git a/src/config/app-config.interface.ts b/src/config/app-config.interface.ts index bba7c66168..d62b9e5bcb 100644 --- a/src/config/app-config.interface.ts +++ b/src/config/app-config.interface.ts @@ -20,7 +20,7 @@ import { InfoConfig } from './info-config.interface'; import { CommunityListConfig } from './community-list-config.interface'; import { HomeConfig } from './homepage-config.interface'; import { MarkdownConfig } from './markdown-config.interface'; -import { FilterVocabularyConfig } from './FilterVocabularyConfig'; +import { FilterVocabularyConfig } from './filter-vocabulary-config'; interface AppConfig extends Config { ui: UIServerConfig; diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts index e78ef0a221..c4ab741103 100644 --- a/src/config/default-app-config.ts +++ b/src/config/default-app-config.ts @@ -20,7 +20,7 @@ import { InfoConfig } from './info-config.interface'; import { CommunityListConfig } from './community-list-config.interface'; import { HomeConfig } from './homepage-config.interface'; import { MarkdownConfig } from './markdown-config.interface'; -import { FilterVocabularyConfig } from './FilterVocabularyConfig'; +import { FilterVocabularyConfig } from './filter-vocabulary-config'; export class DefaultAppConfig implements AppConfig { production = false; @@ -378,6 +378,9 @@ export class DefaultAppConfig implements AppConfig { mathjax: false, }; + // Which vocabularies should be used for which search filters + // and whether to show the filter in the search sidebar + // Take a look at the filter-vocabulary-config.ts file for documentation on how the options are obtained vocabularies: FilterVocabularyConfig[] = [ { filter: 'subject', diff --git a/src/config/filter-vocabulary-config.ts b/src/config/filter-vocabulary-config.ts new file mode 100644 index 0000000000..54e57090c8 --- /dev/null +++ b/src/config/filter-vocabulary-config.ts @@ -0,0 +1,22 @@ +import { Config } from './config.interface'; + +/** + * Configuration that can be used to enable a vocabulary tree to be used as search filter + */ +export interface FilterVocabularyConfig extends Config { + /** + * The name of the filter where the vocabulary tree should be used + * This is the name of the filter as it's configured in the facet in discovery.xml + * (can also be seen on the /server/api/discover/facets endpoint) + */ + filter: string; + /** + * name of the vocabulary tree to use + * ( name of the file as stored in the dspace/config/controlled-vocabularies folder without file extension ) + */ + vocabulary: string; + /** + * Whether to show the vocabulary tree in the sidebar + */ + enabled: boolean; +} From 9cde4c119e22c69e749895f351e4eb033394261b Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Thu, 8 Dec 2022 17:52:49 +0100 Subject: [PATCH 16/27] 97049: Fixing inconsistent tests --- .../search-hierarchy-filter.component.spec.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 872d0772bd..29d033fb17 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 @@ -1,5 +1,5 @@ import { SearchHierarchyFilterComponent } from './search-hierarchy-filter.component'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { DebugElement, EventEmitter, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; import { VocabularyService } from '../../../../../core/submission/vocabularies/vocabulary.service'; @@ -89,12 +89,12 @@ describe('SearchHierarchyFilterComponent', () => { describe('if the vocabulary doesn\'t exist', () => { - beforeEach(() => { + beforeEach(waitForAsync(() => { spyOn(vocabularyService, 'searchTopEntries').and.returnValue(observableOf(new RemoteData( undefined, 0, 0, RequestEntryState.Error, undefined, undefined, 404 ))); init(); - }); + })); it('should not show the vocabulary tree link', () => { expect(showVocabularyTreeLink).toBeNull(); @@ -103,12 +103,12 @@ describe('SearchHierarchyFilterComponent', () => { describe('if the vocabulary exists', () => { - beforeEach(() => { + beforeEach(waitForAsync(() => { spyOn(vocabularyService, 'searchTopEntries').and.returnValue(observableOf(new RemoteData( undefined, 0, 0, RequestEntryState.Success, undefined, buildPaginatedList(new PageInfo(), []), 200 ))); init(); - }); + })); it('should show the vocabulary tree link', () => { expect(showVocabularyTreeLink).toBeTruthy(); From 105308cdf8bc814b5c37df667cccbfb97d60593a Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Mon, 12 Dec 2022 11:18:31 +0100 Subject: [PATCH 17/27] 97049: Fixing inconsistent tests --- .../search-hierarchy-filter.component.spec.ts | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) 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 29d033fb17..add8061e58 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 @@ -89,29 +89,31 @@ describe('SearchHierarchyFilterComponent', () => { describe('if the vocabulary doesn\'t exist', () => { - beforeEach(waitForAsync(() => { + beforeEach(() => { spyOn(vocabularyService, 'searchTopEntries').and.returnValue(observableOf(new RemoteData( undefined, 0, 0, RequestEntryState.Error, undefined, undefined, 404 ))); init(); - })); + }); - it('should not show the vocabulary tree link', () => { + it('should not show the vocabulary tree link', (done) => { expect(showVocabularyTreeLink).toBeNull(); + done(); }); }); describe('if the vocabulary exists', () => { - beforeEach(waitForAsync(() => { + beforeEach(() => { spyOn(vocabularyService, 'searchTopEntries').and.returnValue(observableOf(new RemoteData( undefined, 0, 0, RequestEntryState.Success, undefined, buildPaginatedList(new PageInfo(), []), 200 ))); init(); - })); + }); - it('should show the vocabulary tree link', () => { + it('should show the vocabulary tree link', (done) => { expect(showVocabularyTreeLink).toBeTruthy(); + done(); }); describe('when clicking the vocabulary tree link', () => { @@ -120,8 +122,9 @@ describe('SearchHierarchyFilterComponent', () => { showVocabularyTreeLink.nativeElement.click(); }); - it('should open the vocabulary tree modal', () => { + it('should open the vocabulary tree modal', (done) => { expect(ngbModal.open).toHaveBeenCalled(); + done(); }); describe('when selecting a value from the vocabulary tree', () => { @@ -141,7 +144,7 @@ describe('SearchHierarchyFilterComponent', () => { })); }); - it('should add a new search filter to the existing search filters', () => { + it('should add a new search filter to the existing search filters', (done) => { expect(router.navigate).toHaveBeenCalledWith([testSearchLink], { queryParams: { [`f.${testSearchFilter}`]: [ @@ -151,6 +154,7 @@ describe('SearchHierarchyFilterComponent', () => { }, queryParamsHandling: 'merge', }); + done(); }); }); }); From 1af1d511d0ebd7e6315cbcf648dc1a00f8c1d524 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Mon, 12 Dec 2022 11:23:19 +0100 Subject: [PATCH 18/27] 97049: Lint issue --- .../search-hierarchy-filter.component.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 add8061e58..165db5e43b 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 @@ -1,5 +1,5 @@ import { SearchHierarchyFilterComponent } from './search-hierarchy-filter.component'; -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; import { DebugElement, EventEmitter, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; import { VocabularyService } from '../../../../../core/submission/vocabularies/vocabulary.service'; From 04e5510c6b1fc168b8d0225b70880cc618d9e9fb Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Tue, 13 Dec 2022 15:01:56 +0100 Subject: [PATCH 19/27] 97049: fix wrong import after merge --- .../search-hierarchy-filter.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 3dd866c5b1..8504237f09 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 @@ -3,7 +3,7 @@ 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'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; -import { VocabularyTreeviewComponent } from '../../../../vocabulary-treeview/vocabulary-treeview.component'; +import { VocabularyTreeviewComponent } from '../../../../form/vocabulary-treeview/vocabulary-treeview.component'; import { VocabularyEntryDetail } from '../../../../../core/submission/vocabularies/models/vocabulary-entry-detail.model'; From c2ef6893d37c3c031cb4df698a34f51f81a7fe87 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Tue, 13 Dec 2022 15:12:28 +0100 Subject: [PATCH 20/27] 97049: fix lint issue after merge --- .../form/vocabulary-treeview/vocabulary-treeview.component.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.ts b/src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.ts index 4b03a8c2b3..56909090c7 100644 --- a/src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.ts +++ b/src/app/shared/form/vocabulary-treeview/vocabulary-treeview.component.ts @@ -9,7 +9,6 @@ import { TranslateService } from '@ngx-translate/core'; import { VocabularyEntryDetail } from '../../../core/submission/vocabularies/models/vocabulary-entry-detail.model'; import { hasValue, isEmpty, isNotEmpty } from '../../empty.util'; -import { isAuthenticated } from '../../../core/auth/selectors'; import { VocabularyTreeviewService } from './vocabulary-treeview.service'; import { LOAD_MORE, LOAD_MORE_ROOT, TreeviewFlatNode, TreeviewNode } from './vocabulary-treeview-node.model'; import { VocabularyOptions } from '../../../core/submission/vocabularies/models/vocabulary-options.model'; From b04c8025dc96ca01dd1a06c0897d108e26ae69e1 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Wed, 14 Dec 2022 11:35:19 +0100 Subject: [PATCH 21/27] 97049: Attempt to fix aSync test --- .../search-hierarchy-filter.component.spec.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) 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 165db5e43b..a7ee15db1a 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 @@ -1,5 +1,5 @@ import { SearchHierarchyFilterComponent } from './search-hierarchy-filter.component'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { DebugElement, EventEmitter, NO_ERRORS_SCHEMA } from '@angular/core'; import { By } from '@angular/platform-browser'; import { VocabularyService } from '../../../../../core/submission/vocabularies/vocabulary.service'; @@ -144,8 +144,8 @@ describe('SearchHierarchyFilterComponent', () => { })); }); - it('should add a new search filter to the existing search filters', (done) => { - expect(router.navigate).toHaveBeenCalledWith([testSearchLink], { + it('should add a new search filter to the existing search filters', () => { + waitForAsync(() => expect(router.navigate).toHaveBeenCalledWith([testSearchLink], { queryParams: { [`f.${testSearchFilter}`]: [ ...alreadySelectedValues, @@ -153,8 +153,7 @@ describe('SearchHierarchyFilterComponent', () => { ].map((value => `${value},equals`)), }, queryParamsHandling: 'merge', - }); - done(); + })); }); }); }); From 07fe4f261f1fc35c75a4f8d6952caedc497b7106 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Wed, 14 Dec 2022 11:52:00 +0100 Subject: [PATCH 22/27] 97049: Attempt to fix aSync test --- .../search-hierarchy-filter.component.spec.ts | 1 + 1 file changed, 1 insertion(+) 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 a7ee15db1a..7fc21a3c31 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 @@ -154,6 +154,7 @@ describe('SearchHierarchyFilterComponent', () => { }, queryParamsHandling: 'merge', })); + fixture.destroy(); }); }); }); From ec89addf17ebbd0af9d99205f979a9343ee8dcdf Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Mon, 2 Jan 2023 12:14:33 +0100 Subject: [PATCH 23/27] 97049: Missing provided service after merge --- src/modules/app/browser-app.module.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/modules/app/browser-app.module.ts b/src/modules/app/browser-app.module.ts index 6baf339003..4cdf7fbe2f 100644 --- a/src/modules/app/browser-app.module.ts +++ b/src/modules/app/browser-app.module.ts @@ -31,6 +31,7 @@ import { GoogleAnalyticsService } from '../../app/statistics/google-analytics.se import { AuthRequestService } from '../../app/core/auth/auth-request.service'; import { BrowserAuthRequestService } from '../../app/core/auth/browser-auth-request.service'; import { BrowserInitService } from './browser-init.service'; +import { VocabularyTreeviewService } from 'src/app/shared/form/vocabulary-treeview/vocabulary-treeview.service'; export const REQ_KEY = makeStateKey('req'); @@ -111,6 +112,10 @@ export function getRequest(transferState: TransferState): any { provide: LocationToken, useFactory: locationProvider, }, + { + provide: VocabularyTreeviewService, + useClass: VocabularyTreeviewService, + } ] }) export class BrowserAppModule { From 6012c66d1cf0f05b88b76cf1f1983d5798c25cfb Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Mon, 2 Jan 2023 13:48:50 +0100 Subject: [PATCH 24/27] 97049: fix tests --- .../search-hierarchy-filter.component.spec.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 7fc21a3c31..7ae4545da9 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 @@ -87,6 +87,10 @@ describe('SearchHierarchyFilterComponent', () => { showVocabularyTreeLink = fixture.debugElement.query(By.css('a#show-test-search-filter-tree')); } + afterEach(() => { + fixture.destroy(); + }); + describe('if the vocabulary doesn\'t exist', () => { beforeEach(() => { @@ -154,7 +158,6 @@ describe('SearchHierarchyFilterComponent', () => { }, queryParamsHandling: 'merge', })); - fixture.destroy(); }); }); }); From 3e09653f0b4078eaffd5e01e8a80136469b559f0 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Mon, 2 Jan 2023 14:38:20 +0100 Subject: [PATCH 25/27] 97049: Nest before each statements --- .../search-hierarchy-filter.component.spec.ts | 40 +++++++------------ 1 file changed, 15 insertions(+), 25 deletions(-) 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 7ae4545da9..e6c74d8047 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 @@ -87,10 +87,6 @@ describe('SearchHierarchyFilterComponent', () => { showVocabularyTreeLink = fixture.debugElement.query(By.css('a#show-test-search-filter-tree')); } - afterEach(() => { - fixture.destroy(); - }); - describe('if the vocabulary doesn\'t exist', () => { beforeEach(() => { @@ -100,9 +96,8 @@ describe('SearchHierarchyFilterComponent', () => { init(); }); - it('should not show the vocabulary tree link', (done) => { + it('should not show the vocabulary tree link', () => { expect(showVocabularyTreeLink).toBeNull(); - done(); }); }); @@ -115,39 +110,34 @@ describe('SearchHierarchyFilterComponent', () => { init(); }); - it('should show the vocabulary tree link', (done) => { + it('should show the vocabulary tree link', () => { expect(showVocabularyTreeLink).toBeTruthy(); - done(); }); describe('when clicking the vocabulary tree link', () => { + const alreadySelectedValues = [ + 'already-selected-value-1', + 'already-selected-value-2', + ]; + const newSelectedValue = 'new-selected-value'; + beforeEach(async () => { showVocabularyTreeLink.nativeElement.click(); + fixture.componentInstance.selectedValues$ = observableOf( + alreadySelectedValues.map(value => Object.assign(new FacetValue(), { value })) + ); + VocabularyTreeViewComponent.select.emit(Object.assign(new VocabularyEntryDetail(), { + value: newSelectedValue, + })); }); - it('should open the vocabulary tree modal', (done) => { + it('should open the vocabulary tree modal', () => { expect(ngbModal.open).toHaveBeenCalled(); - done(); }); describe('when selecting a value from the vocabulary tree', () => { - const alreadySelectedValues = [ - 'already-selected-value-1', - 'already-selected-value-2', - ]; - const newSelectedValue = 'new-selected-value'; - - beforeEach(() => { - fixture.componentInstance.selectedValues$ = observableOf( - alreadySelectedValues.map(value => Object.assign(new FacetValue(), { value })) - ); - VocabularyTreeViewComponent.select.emit(Object.assign(new VocabularyEntryDetail(), { - value: newSelectedValue, - })); - }); - it('should add a new search filter to the existing search filters', () => { waitForAsync(() => expect(router.navigate).toHaveBeenCalledWith([testSearchLink], { queryParams: { From e0cadec2c21b44587945edd08e17c3fda1532ed0 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Fri, 13 Jan 2023 12:14:43 +0100 Subject: [PATCH 26/27] 97049: Group keys together --- src/assets/i18n/en.json5 | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 94fa99455e..584cb6d53f 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -3640,6 +3640,7 @@ "search.filters.filter.submitter.label": "Search submitter", + "search.filters.filter.show-tree": "Browse {{ name }} tree", "search.filters.entityType.JournalIssue": "Journal Issue", @@ -4514,7 +4515,7 @@ "vocabulary-treeview.tree.description.srsc": "Research Subject Categories", - + "vocabulary-treeview.info": "Select a subject to add as search filter", "uploader.browse": "browse", @@ -4841,8 +4842,4 @@ "home.recent-submissions.head": "Recent Submissions", "listable-notification-object.default-message": "This object couldn't be retrieved", - - "search.filters.filter.show-tree": "Browse {{ name }} tree", - - "vocabulary-treeview.info": "Select a subject to add as search filter" } From e40cd202878d9ae8dfd1645f1ecf015263dabf14 Mon Sep 17 00:00:00 2001 From: Jens Vannerum Date: Mon, 16 Jan 2023 09:55:52 +0100 Subject: [PATCH 27/27] 97049: Disable the vocabulary by default --- src/config/default-app-config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts index aab5b9df4e..d7b3efc2ed 100644 --- a/src/config/default-app-config.ts +++ b/src/config/default-app-config.ts @@ -394,7 +394,7 @@ export class DefaultAppConfig implements AppConfig { { filter: 'subject', vocabulary: 'srsc', - enabled: true + enabled: false } ]; }