Merge remote-tracking branch 'refs/remotes/github/main' into task/main/DURACOM-263

This commit is contained in:
Andrea Barbasso
2024-05-22 11:51:23 +02:00
12 changed files with 132 additions and 81 deletions

View File

@@ -7,8 +7,10 @@
} }} } }}
</h1> </h1>
<div class="mb-3"> <div class="mb-3">
<ds-vocabulary-treeview [vocabularyOptions]=vocabularyOptions <ds-vocabulary-treeview [description]="description"
[vocabularyOptions]=vocabularyOptions
[multiSelect]="true" [multiSelect]="true"
[showAdd]="false"
(select)="onSelect($event)" (select)="onSelect($event)"
(deselect)="onDeselect($event)"> (deselect)="onDeselect($event)">
</ds-vocabulary-treeview> </ds-vocabulary-treeview>

View File

@@ -14,7 +14,10 @@ import {
Params, Params,
RouterLink, RouterLink,
} from '@angular/router'; } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core'; import {
TranslateModule,
TranslateService,
} from '@ngx-translate/core';
import { import {
BehaviorSubject, BehaviorSubject,
Observable, Observable,
@@ -124,6 +127,11 @@ export class BrowseByTaxonomyComponent implements OnInit, OnChanges, OnDestroy {
*/ */
browseDefinition$: Observable<BrowseDefinition>; browseDefinition$: Observable<BrowseDefinition>;
/**
* Browse description
*/
description: string;
/** /**
* Subscriptions to track * Subscriptions to track
*/ */
@@ -131,6 +139,7 @@ export class BrowseByTaxonomyComponent implements OnInit, OnChanges, OnDestroy {
public constructor( public constructor(
protected route: ActivatedRoute, protected route: ActivatedRoute,
protected translate: TranslateService,
) { ) {
} }
@@ -141,9 +150,11 @@ export class BrowseByTaxonomyComponent implements OnInit, OnChanges, OnDestroy {
}), }),
); );
this.subs.push(this.browseDefinition$.subscribe((browseDefinition: HierarchicalBrowseDefinition) => { this.subs.push(this.browseDefinition$.subscribe((browseDefinition: HierarchicalBrowseDefinition) => {
this.selectedItems = [];
this.facetType = browseDefinition.facetType; this.facetType = browseDefinition.facetType;
this.vocabularyName = browseDefinition.vocabulary; this.vocabularyName = browseDefinition.vocabulary;
this.vocabularyOptions = { name: this.vocabularyName, closed: true }; this.vocabularyOptions = { name: this.vocabularyName, closed: true };
this.description = this.translate.instant(`browse.metadata.${this.vocabularyName}.tree.descrption`);
})); }));
this.subs.push(this.scope$.subscribe(() => { this.subs.push(this.scope$.subscribe(() => {
this.updateQueryParams(); this.updateQueryParams();

View File

@@ -293,7 +293,7 @@ export class DsDynamicOneboxComponent extends DsDynamicVocabularyComponent imple
const modalRef: NgbModalRef = this.modalService.open(VocabularyTreeviewModalComponent, { size: 'lg', windowClass: 'treeview' }); const modalRef: NgbModalRef = this.modalService.open(VocabularyTreeviewModalComponent, { size: 'lg', windowClass: 'treeview' });
modalRef.componentInstance.vocabularyOptions = this.model.vocabularyOptions; modalRef.componentInstance.vocabularyOptions = this.model.vocabularyOptions;
modalRef.componentInstance.preloadLevel = preloadLevel; modalRef.componentInstance.preloadLevel = preloadLevel;
modalRef.componentInstance.selectedItems = this.currentValue ? [this.currentValue.value] : []; modalRef.componentInstance.selectedItems = this.currentValue ? [this.currentValue] : [];
modalRef.result.then((result: VocabularyEntryDetail) => { modalRef.result.then((result: VocabularyEntryDetail) => {
if (result) { if (result) {
this.currentValue = result; this.currentValue = result;

View File

@@ -7,9 +7,11 @@
<div class="modal-body"> <div class="modal-body">
<div class="p-3"> <div class="p-3">
<ds-vocabulary-treeview [vocabularyOptions]="vocabularyOptions" <ds-vocabulary-treeview [vocabularyOptions]="vocabularyOptions"
[description]="description"
[preloadLevel]="preloadLevel" [preloadLevel]="preloadLevel"
[selectedItems]="selectedItems" [selectedItems]="selectedItems"
[multiSelect]="multiSelect" [multiSelect]="multiSelect"
[showAdd]="showAdd"
(select)="onSelect($event)"> (select)="onSelect($event)">
</ds-vocabulary-treeview> </ds-vocabulary-treeview>
</div> </div>

View File

@@ -5,6 +5,7 @@ import {
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { VocabularyOptions } from '../../../core/submission/vocabularies/models/vocabulary-options.model';
import { VocabularyTreeviewComponent } from '../vocabulary-treeview/vocabulary-treeview.component'; import { VocabularyTreeviewComponent } from '../vocabulary-treeview/vocabulary-treeview.component';
import { VocabularyTreeviewModalComponent } from './vocabulary-treeview-modal.component'; import { VocabularyTreeviewModalComponent } from './vocabulary-treeview-modal.component';
@@ -13,6 +14,7 @@ describe('VocabularyTreeviewModalComponent', () => {
let fixture: ComponentFixture<VocabularyTreeviewModalComponent>; let fixture: ComponentFixture<VocabularyTreeviewModalComponent>;
const modalStub = jasmine.createSpyObj('modalStub', ['close']); const modalStub = jasmine.createSpyObj('modalStub', ['close']);
const vocabularyOptions = new VocabularyOptions('vocabularyTest', false);
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
@@ -32,10 +34,16 @@ describe('VocabularyTreeviewModalComponent', () => {
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(VocabularyTreeviewModalComponent); fixture = TestBed.createComponent(VocabularyTreeviewModalComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
component.vocabularyOptions = vocabularyOptions;
spyOn(component as any, 'setDescription').and.callThrough();
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
it('should init descrption message', () => {
expect((component as any).setDescription).toHaveBeenCalled();
});
}); });

View File

@@ -2,10 +2,14 @@ import {
Component, Component,
EventEmitter, EventEmitter,
Input, Input,
OnInit,
Output, Output,
} from '@angular/core'; } from '@angular/core';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { TranslateModule } from '@ngx-translate/core'; import {
TranslateModule,
TranslateService,
} from '@ngx-translate/core';
import { VocabularyEntryDetail } from '../../../core/submission/vocabularies/models/vocabulary-entry-detail.model'; import { VocabularyEntryDetail } from '../../../core/submission/vocabularies/models/vocabulary-entry-detail.model';
import { VocabularyOptions } from '../../../core/submission/vocabularies/models/vocabulary-options.model'; import { VocabularyOptions } from '../../../core/submission/vocabularies/models/vocabulary-options.model';
@@ -24,7 +28,7 @@ import { VocabularyTreeviewComponent } from '../vocabulary-treeview/vocabulary-t
/** /**
* Component that contains a modal to display a VocabularyTreeviewComponent * Component that contains a modal to display a VocabularyTreeviewComponent
*/ */
export class VocabularyTreeviewModalComponent { export class VocabularyTreeviewModalComponent implements OnInit {
/** /**
* The {@link VocabularyOptions} object * The {@link VocabularyOptions} object
@@ -39,13 +43,23 @@ export class VocabularyTreeviewModalComponent {
/** /**
* The vocabulary entries already selected, if any * The vocabulary entries already selected, if any
*/ */
@Input() selectedItems: string[] = []; @Input() selectedItems: VocabularyEntryDetail[] = [];
/** /**
* Whether to allow selecting multiple values with checkboxes * Whether to allow selecting multiple values with checkboxes
*/ */
@Input() multiSelect = false; @Input() multiSelect = false;
/**
* A boolean representing if to show the add button or not
*/
@Input() showAdd = true;
/**
* Contain a descriptive message for this vocabulary retrieved from i18n files
*/
description: string;
/** /**
* An event fired when a vocabulary entry is selected. * An event fired when a vocabulary entry is selected.
* Event's payload equals to {@link VocabularyEntryDetail} selected. * Event's payload equals to {@link VocabularyEntryDetail} selected.
@@ -56,11 +70,17 @@ export class VocabularyTreeviewModalComponent {
* Initialize instance variables * Initialize instance variables
* *
* @param {NgbActiveModal} activeModal * @param {NgbActiveModal} activeModal
* @param {TranslateService} translate
*/ */
constructor( constructor(
public activeModal: NgbActiveModal, public activeModal: NgbActiveModal,
protected translate: TranslateService,
) { } ) { }
ngOnInit(): void {
this.setDescription();
}
/** /**
* Method called on entry select * Method called on entry select
*/ */
@@ -68,4 +88,13 @@ export class VocabularyTreeviewModalComponent {
this.select.emit(item); this.select.emit(item);
this.activeModal.close(item); this.activeModal.close(item);
} }
/**
* Set the description message related to the given vocabulary
*/
private setDescription() {
const descriptionLabel = 'vocabulary-treeview.tree.description.' + this.vocabularyOptions.name;
this.description = this.translate.instant(descriptionLabel);
}
} }

View File

@@ -1,4 +1,4 @@
<ds-alert [content]="'vocabulary-treeview.info' | translate" [type]="AlertType.Info"></ds-alert> <ds-alert *ngIf="description" [content]="description" [type]="AlertType.Info"></ds-alert>
<div class="treeview-header row mb-1"> <div class="treeview-header row mb-1">
<div class="col-12"> <div class="col-12">
<div class="input-group"> <div class="input-group">
@@ -11,7 +11,7 @@
<button class="btn btn-outline-secondary" type="button" (click)="reset()"> <button class="btn btn-outline-secondary" type="button" (click)="reset()">
{{'vocabulary-treeview.search.form.reset' | translate}} {{'vocabulary-treeview.search.form.reset' | translate}}
</button> </button>
<button class="btn btn-outline-primary" type="button" (click)="add()" [disabled]="this.vocabularyOptions.closed"> <button *ngIf="showAdd && this.vocabularyOptions.closed" class="btn btn-outline-primary" type="button" (click)="add()">
{{'vocabulary-treeview.search.form.add' | translate}} {{'vocabulary-treeview.search.form.add' | translate}}
</button> </button>
<button class="btn btn-outline-primary" type="button" (click)="add()" [disabled]="this.vocabularyOptions.closed"> <button class="btn btn-outline-primary" type="button" (click)="add()" [disabled]="this.vocabularyOptions.closed">

View File

@@ -13,14 +13,9 @@ import {
import { By } from '@angular/platform-browser'; import { By } from '@angular/platform-browser';
import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { StoreModule } from '@ngrx/store';
import { provideMockStore } from '@ngrx/store/testing';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { of as observableOf } from 'rxjs'; import { of as observableOf } from 'rxjs';
import { storeModuleConfig } from '../../../app.reducer';
import { authReducer } from '../../../core/auth/auth.reducer';
import { AuthTokenInfo } from '../../../core/auth/models/auth-token-info.model';
import { PageInfo } from '../../../core/shared/page-info.model'; import { PageInfo } from '../../../core/shared/page-info.model';
import { VocabularyEntry } from '../../../core/submission/vocabularies/models/vocabulary-entry.model'; import { VocabularyEntry } from '../../../core/submission/vocabularies/models/vocabulary-entry.model';
import { VocabularyEntryDetail } from '../../../core/submission/vocabularies/models/vocabulary-entry-detail.model'; import { VocabularyEntryDetail } from '../../../core/submission/vocabularies/models/vocabulary-entry-detail.model';
@@ -40,7 +35,6 @@ describe('VocabularyTreeviewComponent test suite', () => {
let comp: VocabularyTreeviewComponent; let comp: VocabularyTreeviewComponent;
let compAsAny: any; let compAsAny: any;
let fixture: ComponentFixture<VocabularyTreeviewComponent>; let fixture: ComponentFixture<VocabularyTreeviewComponent>;
let initialState;
let de; let de;
const item = new VocabularyEntryDetail(); const item = new VocabularyEntryDetail();
@@ -71,25 +65,10 @@ describe('VocabularyTreeviewComponent test suite', () => {
clearSearchTopRequests: jasmine.createSpy('clearSearchTopRequests'), clearSearchTopRequests: jasmine.createSpy('clearSearchTopRequests'),
}); });
initialState = {
core: {
auth: {
authenticated: true,
loaded: true,
blocking: false,
loading: false,
authToken: new AuthTokenInfo('test_token'),
userId: 'testid',
authMethods: [],
},
},
};
beforeEach(waitForAsync(() => { beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ imports: [
CdkTreeModule, CdkTreeModule,
StoreModule.forRoot({ auth: authReducer }, storeModuleConfig),
TranslateModule.forRoot(), TranslateModule.forRoot(),
VocabularyTreeviewComponent, VocabularyTreeviewComponent,
TestComponent, TestComponent,
@@ -99,7 +78,6 @@ describe('VocabularyTreeviewComponent test suite', () => {
{ provide: VocabularyTreeviewService, useValue: vocabularyTreeviewServiceStub }, { provide: VocabularyTreeviewService, useValue: vocabularyTreeviewServiceStub },
{ provide: VocabularyService, useValue: vocabularyServiceStub }, { provide: VocabularyService, useValue: vocabularyServiceStub },
{ provide: NgbActiveModal, useValue: modalStub }, { provide: NgbActiveModal, useValue: modalStub },
provideMockStore({ initialState }),
ChangeDetectorRef, ChangeDetectorRef,
VocabularyTreeviewComponent, VocabularyTreeviewComponent,
], ],
@@ -155,10 +133,10 @@ describe('VocabularyTreeviewComponent test suite', () => {
currentValue.otherInformation = { currentValue.otherInformation = {
id: 'entryID', id: 'entryID',
}; };
comp.selectedItems = [currentValue.value]; comp.selectedItems = [currentValue];
fixture.detectChanges(); fixture.detectChanges();
expect(comp.dataSource.data).toEqual([]); expect(comp.dataSource.data).toEqual([]);
expect(vocabularyTreeviewServiceStub.initialize).toHaveBeenCalledWith(comp.vocabularyOptions, new PageInfo(), ['testValue'], null); expect(vocabularyTreeviewServiceStub.initialize).toHaveBeenCalledWith(comp.vocabularyOptions, new PageInfo(), ['entryID'], 'entryID');
}); });
it('should should init component properly with init value as VocabularyEntry', () => { it('should should init component properly with init value as VocabularyEntry', () => {
@@ -167,10 +145,20 @@ describe('VocabularyTreeviewComponent test suite', () => {
currentValue.otherInformation = { currentValue.otherInformation = {
id: 'entryID', id: 'entryID',
}; };
comp.selectedItems = [currentValue.value]; comp.selectedItems = [currentValue];
fixture.detectChanges(); fixture.detectChanges();
expect(comp.dataSource.data).toEqual([]); expect(comp.dataSource.data).toEqual([]);
expect(vocabularyTreeviewServiceStub.initialize).toHaveBeenCalledWith(comp.vocabularyOptions, new PageInfo(), ['testValue'], null); expect(vocabularyTreeviewServiceStub.initialize).toHaveBeenCalledWith(comp.vocabularyOptions, new PageInfo(), ['entryID'], 'entryID');
});
it('should should init component properly with init value as VocabularyEntryDetail', () => {
const currentValue = new VocabularyEntryDetail();
currentValue.value = 'testValue';
currentValue.id = 'entryID';
comp.selectedItems = [currentValue];
fixture.detectChanges();
expect(comp.dataSource.data).toEqual([]);
expect(vocabularyTreeviewServiceStub.initialize).toHaveBeenCalledWith(comp.vocabularyOptions, new PageInfo(), ['entryID'], 'entryID');
}); });
it('should call loadMore function', () => { it('should call loadMore function', () => {

View File

@@ -18,23 +18,16 @@ import {
} from '@angular/core'; } from '@angular/core';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'; import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap';
import { Store } from '@ngrx/store'; import { TranslateModule } from '@ngx-translate/core';
import {
TranslateModule,
TranslateService,
} from '@ngx-translate/core';
import { import {
Observable, Observable,
Subscription, Subscription,
} from 'rxjs'; } from 'rxjs';
import { CoreState } from '../../../core/core-state.model';
import { getFirstSucceededRemoteDataPayload } from '../../../core/shared/operators';
import { PageInfo } from '../../../core/shared/page-info.model'; import { PageInfo } from '../../../core/shared/page-info.model';
import { VocabularyEntry } from '../../../core/submission/vocabularies/models/vocabulary-entry.model'; import { VocabularyEntry } from '../../../core/submission/vocabularies/models/vocabulary-entry.model';
import { VocabularyEntryDetail } from '../../../core/submission/vocabularies/models/vocabulary-entry-detail.model'; import { VocabularyEntryDetail } from '../../../core/submission/vocabularies/models/vocabulary-entry-detail.model';
import { VocabularyOptions } from '../../../core/submission/vocabularies/models/vocabulary-options.model'; import { VocabularyOptions } from '../../../core/submission/vocabularies/models/vocabulary-options.model';
import { VocabularyService } from '../../../core/submission/vocabularies/vocabulary.service';
import { AlertComponent } from '../../alert/alert.component'; import { AlertComponent } from '../../alert/alert.component';
import { AlertType } from '../../alert/alert-type'; import { AlertType } from '../../alert/alert-type';
import { import {
@@ -43,6 +36,7 @@ import {
isNotEmpty, isNotEmpty,
} from '../../empty.util'; } from '../../empty.util';
import { ThemedLoadingComponent } from '../../loading/themed-loading.component'; import { ThemedLoadingComponent } from '../../loading/themed-loading.component';
import { FormFieldMetadataValueObject } from '../builder/models/form-field-metadata-value.model';
import { VocabularyTreeFlatDataSource } from './vocabulary-tree-flat-data-source'; import { VocabularyTreeFlatDataSource } from './vocabulary-tree-flat-data-source';
import { VocabularyTreeFlattener } from './vocabulary-tree-flattener'; import { VocabularyTreeFlattener } from './vocabulary-tree-flattener';
import { VocabularyTreeviewService } from './vocabulary-treeview.service'; import { VocabularyTreeviewService } from './vocabulary-treeview.service';
@@ -53,6 +47,8 @@ import {
TreeviewNode, TreeviewNode,
} from './vocabulary-treeview-node.model'; } from './vocabulary-treeview-node.model';
export type VocabularyTreeItemType = FormFieldMetadataValueObject | VocabularyEntry | VocabularyEntryDetail;
/** /**
* Component that shows a hierarchical vocabulary in a tree view * Component that shows a hierarchical vocabulary in a tree view
*/ */
@@ -85,15 +81,25 @@ export class VocabularyTreeviewComponent implements OnDestroy, OnInit, OnChanges
@Input() preloadLevel = 2; @Input() preloadLevel = 2;
/** /**
* The vocabulary entries already selected, if any * Contain a descriptive message for the tree
*/ */
@Input() selectedItems: string[] = []; @Input() description = '';
/** /**
* Whether to allow selecting multiple values with checkboxes * Whether to allow selecting multiple values with checkboxes
*/ */
@Input() multiSelect = false; @Input() multiSelect = false;
/**
* A boolean representing if to show the add button or not
*/
@Input() showAdd = true;
/**
* The vocabulary entries already selected, if any
*/
@Input() selectedItems: VocabularyTreeItemType[] = [];
/** /**
* A map containing the current node showed by the tree * A map containing the current node showed by the tree
*/ */
@@ -131,20 +137,15 @@ export class VocabularyTreeviewComponent implements OnDestroy, OnInit, OnChanges
/** /**
* An event fired when a vocabulary entry is selected. * An event fired when a vocabulary entry is selected.
* Event's payload equals to {@link VocabularyEntryDetail} selected. * Event's payload equals to {@link VocabularyTreeItemType} selected.
*/ */
@Output() select: EventEmitter<VocabularyEntryDetail> = new EventEmitter<VocabularyEntryDetail>(null); @Output() select: EventEmitter<VocabularyTreeItemType> = new EventEmitter<VocabularyTreeItemType>(null);
/** /**
* An event fired when a vocabulary entry is deselected. * An event fired when a vocabulary entry is deselected.
* Event's payload equals to {@link VocabularyEntryDetail} deselected. * Event's payload equals to {@link VocabularyTreeItemType} deselected.
*/ */
@Output() deselect: EventEmitter<VocabularyEntryDetail> = new EventEmitter<VocabularyEntryDetail>(null); @Output() deselect: EventEmitter<VocabularyTreeItemType> = new EventEmitter<VocabularyTreeItemType>(null);
/**
* A boolean representing if user is authenticated
*/
private isAuthenticated: Observable<boolean>;
/** /**
* Array to track all subscriptions and unsubscribe them onDestroy * Array to track all subscriptions and unsubscribe them onDestroy
@@ -157,15 +158,9 @@ export class VocabularyTreeviewComponent implements OnDestroy, OnInit, OnChanges
* Initialize instance variables * Initialize instance variables
* *
* @param {VocabularyTreeviewService} vocabularyTreeviewService * @param {VocabularyTreeviewService} vocabularyTreeviewService
* @param {vocabularyService} vocabularyService
* @param {Store<CoreState>} store
* @param {TranslateService} translate
*/ */
constructor( constructor(
private vocabularyTreeviewService: VocabularyTreeviewService, private vocabularyTreeviewService: VocabularyTreeviewService,
private vocabularyService: VocabularyService,
private store: Store<CoreState>,
private translate: TranslateService,
) { ) {
this.treeFlattener = new VocabularyTreeFlattener(this.transformer, this.getLevel, this.treeFlattener = new VocabularyTreeFlattener(this.transformer, this.getLevel,
this.isExpandable, this.getChildren); this.isExpandable, this.getChildren);
@@ -187,7 +182,8 @@ export class VocabularyTreeviewComponent implements OnDestroy, OnInit, OnChanges
* @param level The node level information * @param level The node level information
*/ */
transformer = (node: TreeviewNode, level: number) => { transformer = (node: TreeviewNode, level: number) => {
const existingNode = this.nodeMap.get(node.item.id); const entryId = this.getEntryId(node.item);
const existingNode = this.nodeMap.get(entryId);
if (existingNode && existingNode.item.id !== LOAD_MORE && existingNode.item.id !== LOAD_MORE_ROOT) { if (existingNode && existingNode.item.id !== LOAD_MORE && existingNode.item.id !== LOAD_MORE_ROOT) {
return existingNode; return existingNode;
@@ -204,7 +200,7 @@ export class VocabularyTreeviewComponent implements OnDestroy, OnInit, OnChanges
node.isInInitValueHierarchy, node.isInInitValueHierarchy,
node.isSelected, node.isSelected,
); );
this.nodeMap.set(node.item.id, newNode); this.nodeMap.set(entryId, newNode);
if ((((level + 1) < this.preloadLevel) && newNode.childrenLoaded) if ((((level + 1) < this.preloadLevel) && newNode.childrenLoaded)
|| (newNode.isSearchNode && newNode.childrenLoaded) || (newNode.isSearchNode && newNode.childrenLoaded)
@@ -259,7 +255,8 @@ export class VocabularyTreeviewComponent implements OnDestroy, OnInit, OnChanges
this.loading = this.vocabularyTreeviewService.isLoading(); this.loading = this.vocabularyTreeviewService.isLoading();
this.vocabularyTreeviewService.initialize(this.vocabularyOptions, new PageInfo(), this.selectedItems, null); const entryId: string = (this.selectedItems?.length > 0) ? this.getEntryId(this.selectedItems[0]) : null;
this.vocabularyTreeviewService.initialize(this.vocabularyOptions, new PageInfo(), this.getSelectedEntryIds(), entryId);
} }
/** /**
@@ -267,7 +264,7 @@ export class VocabularyTreeviewComponent implements OnDestroy, OnInit, OnChanges
* @param item The VocabularyEntryDetail for which to load more nodes * @param item The VocabularyEntryDetail for which to load more nodes
*/ */
loadMore(item: VocabularyEntryDetail) { loadMore(item: VocabularyEntryDetail) {
this.vocabularyTreeviewService.loadMore(item, this.selectedItems); this.vocabularyTreeviewService.loadMore(item, this.getSelectedEntryIds());
} }
/** /**
@@ -275,7 +272,7 @@ export class VocabularyTreeviewComponent implements OnDestroy, OnInit, OnChanges
* @param node The TreeviewFlatNode for which to load more nodes * @param node The TreeviewFlatNode for which to load more nodes
*/ */
loadMoreRoot(node: TreeviewFlatNode) { loadMoreRoot(node: TreeviewFlatNode) {
this.vocabularyTreeviewService.loadMoreRoot(node, this.selectedItems); this.vocabularyTreeviewService.loadMoreRoot(node, this.getSelectedEntryIds());
} }
/** /**
@@ -283,18 +280,18 @@ export class VocabularyTreeviewComponent implements OnDestroy, OnInit, OnChanges
* @param node The TreeviewFlatNode for which to load children nodes * @param node The TreeviewFlatNode for which to load children nodes
*/ */
loadChildren(node: TreeviewFlatNode) { loadChildren(node: TreeviewFlatNode) {
this.vocabularyTreeviewService.loadMore(node.item, this.selectedItems, true); this.vocabularyTreeviewService.loadMore(node.item, this.getSelectedEntryIds(), true);
} }
/** /**
* Method called on entry select/deselect * Method called on entry select/deselect
*/ */
onSelect(item: VocabularyEntryDetail) { onSelect(item: VocabularyEntryDetail) {
if (!this.selectedItems.includes(item.id)) { if (!this.getSelectedEntryIds().includes(this.getEntryId(item))) {
this.selectedItems.push(item.id); this.selectedItems.push(item);
this.select.emit(item); this.select.emit(item);
} else { } else {
this.selectedItems = this.selectedItems.filter((detail: string) => { return detail !== item.id; }); this.selectedItems = this.selectedItems.filter((detail: VocabularyTreeItemType) => this.getEntryId(detail) !== this.getEntryId(item));
this.deselect.emit(item); this.deselect.emit(item);
} }
} }
@@ -308,7 +305,7 @@ export class VocabularyTreeviewComponent implements OnDestroy, OnInit, OnChanges
this.storedNodeMap = this.nodeMap; this.storedNodeMap = this.nodeMap;
} }
this.nodeMap = new Map<string, TreeviewFlatNode>(); this.nodeMap = new Map<string, TreeviewFlatNode>();
this.vocabularyTreeviewService.searchByQuery(this.searchText, this.selectedItems); this.vocabularyTreeviewService.searchByQuery(this.searchText, this.getSelectedEntryIds());
} }
} }
@@ -325,12 +322,8 @@ export class VocabularyTreeviewComponent implements OnDestroy, OnInit, OnChanges
reset() { reset() {
this.searchText = ''; this.searchText = '';
for (const item of this.selectedItems) { for (const item of this.selectedItems) {
this.subs.push(this.vocabularyService.findEntryDetailById(item, this.vocabularyOptions.name, true, true, false).pipe( this.deselect.emit(item);
getFirstSucceededRemoteDataPayload(), this.nodeMap.get(this.getEntryId(item)).isSelected = false;
).subscribe((detail: VocabularyEntryDetail) => {
this.deselect.emit(detail);
}));
this.nodeMap.get(item).isSelected = false;
} }
this.selectedItems = []; this.selectedItems = [];
@@ -361,14 +354,27 @@ export class VocabularyTreeviewComponent implements OnDestroy, OnInit, OnChanges
} }
/** /**
* Return an id for a given {@link VocabularyEntry} * Return an id for a given {@link VocabularyTreeItemType}
*/ */
private getEntryId(entry: VocabularyEntry): string { private getEntryId(entry: VocabularyTreeItemType): string {
return entry.authority || entry.otherInformation.id || undefined; return entry?.authority || entry?.otherInformation?.id || (entry as any)?.id || undefined;
}
/**
* Return an ids for all selected entries
*/
private getSelectedEntryIds(): string[] {
return this.selectedItems
.map((entry: VocabularyTreeItemType) => this.getEntryId(entry))
.filter((value) => isNotEmpty(value));
} }
ngOnChanges(changes: SimpleChanges): void { ngOnChanges(changes: SimpleChanges): void {
this.reset(); if (!changes.vocabularyOptions.isFirstChange() && changes.vocabularyOptions.currentValue !== changes.vocabularyOptions.previousValue) {
this.vocabularyTreeviewService.initialize(this.vocabularyOptions, new PageInfo(), this.selectedItems, null); this.selectedItems = [];
this.searchText = '';
this.vocabularyTreeviewService.cleanTree();
this.vocabularyTreeviewService.initialize(this.vocabularyOptions, new PageInfo(), this.getSelectedEntryIds(), null);
}
} }
} }

View File

@@ -260,7 +260,7 @@ export class VocabularyTreeviewService {
const hasChildren = entry.hasOtherInformation() && (entry.otherInformation as any)!.hasChildren === 'true'; const hasChildren = entry.hasOtherInformation() && (entry.otherInformation as any)!.hasChildren === 'true';
const pageInfo: PageInfo = this.pageInfo; const pageInfo: PageInfo = this.pageInfo;
const isInInitValueHierarchy = this.initValueHierarchy.includes(entryId); const isInInitValueHierarchy = this.initValueHierarchy.includes(entryId);
const isSelected: boolean = selectedItems.some(() => selectedItems.includes(entry.id)); const isSelected: boolean = selectedItems.some(() => selectedItems.includes(entryId));
const result = new TreeviewNode( const result = new TreeviewNode(
entry, entry,
hasChildren, hasChildren,

View File

@@ -126,6 +126,7 @@ export class SearchHierarchyFilterComponent extends SearchFacetFilterComponent i
name: this.getVocabularyEntry(), name: this.getVocabularyEntry(),
closed: true, closed: true,
}; };
modalRef.componentInstance.showAdd = false;
this.subs.push(from(modalRef.result).pipe( this.subs.push(from(modalRef.result).pipe(
switchMap((detail: VocabularyEntryDetail) => this.searchConfigService.selectNewAppliedFilterParams(this.filterConfig.name, detail.value, 'equals')), switchMap((detail: VocabularyEntryDetail) => this.searchConfigService.selectNewAppliedFilterParams(this.filterConfig.name, detail.value, 'equals')),
take(1), take(1),

View File

@@ -1034,8 +1034,12 @@
"browse.metadata.srsc.breadcrumbs": "Browse by Subject Category", "browse.metadata.srsc.breadcrumbs": "Browse by Subject Category",
"browse.metadata.srsc.tree.descrption": "Select a subject to add as search filter",
"browse.metadata.nsi.breadcrumbs": "Browse by Norwegian Science Index", "browse.metadata.nsi.breadcrumbs": "Browse by Norwegian Science Index",
"browse.metadata.nsi.tree.descrption": "Select an index to add as search filter",
"browse.metadata.title.breadcrumbs": "Browse by Title", "browse.metadata.title.breadcrumbs": "Browse by Title",
"pagination.next.button": "Next", "pagination.next.button": "Next",