mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
[CST-4591] Create entity scrollable dropdown
This commit is contained in:
@@ -0,0 +1,28 @@
|
|||||||
|
<div
|
||||||
|
class="scrollable-menu"
|
||||||
|
aria-labelledby="dropdownMenuButton"
|
||||||
|
(scroll)="onScroll($event)"
|
||||||
|
infiniteScroll
|
||||||
|
[infiniteScrollDistance]="5"
|
||||||
|
[infiniteScrollThrottle]="300"
|
||||||
|
[infiniteScrollUpDistance]="1.5"
|
||||||
|
[fromRoot]="true"
|
||||||
|
[scrollWindow]="false"
|
||||||
|
(scrolled)="onScrollDown()">
|
||||||
|
<button class="dropdown-item disabled" *ngIf="searchListEntity?.length == 0 && !(isLoadingList | async)">
|
||||||
|
{{'submission.sections.general.no-entity' | translate}}
|
||||||
|
</button>
|
||||||
|
<button *ngFor="let listItem of searchListEntity"
|
||||||
|
class="dropdown-item entity-item"
|
||||||
|
title="{{ listItem.label }}"
|
||||||
|
(click)="onSelect(listItem)">
|
||||||
|
<ul class="list-unstyled mb-0">
|
||||||
|
<li class="list-item text-truncate text-primary font-weight-bold">{{ listItem.label.toLowerCase() + '.listelement.badge' | translate }}</li>
|
||||||
|
</ul>
|
||||||
|
</button>
|
||||||
|
<button class="dropdown-item disabled" *ngIf="(isLoadingList | async)" >
|
||||||
|
<ds-loading message="{{'loading.default' | translate}}">
|
||||||
|
</ds-loading>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
@@ -0,0 +1,19 @@
|
|||||||
|
.list-item:active {
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scrollable-menu {
|
||||||
|
height: auto;
|
||||||
|
max-height: var(--ds-dropdown-menu-max-height);
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.entity-item {
|
||||||
|
border-bottom: var(--bs-dropdown-border-width) solid var(--bs-dropdown-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#entityControlsDropdownMenu {
|
||||||
|
outline: 0;
|
||||||
|
left: 0 !important;
|
||||||
|
box-shadow: var(--bs-btn-focus-box-shadow);
|
||||||
|
}
|
167
src/app/shared/entity-dropdown/entity-dropdown.component.spec.ts
Normal file
167
src/app/shared/entity-dropdown/entity-dropdown.component.spec.ts
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
|
||||||
|
import { EntityDropdownComponent } from './entity-dropdown.component';
|
||||||
|
import { getTestScheduler } from 'jasmine-marbles';
|
||||||
|
import { createSuccessfulRemoteDataObject$ } from '../remote-data.utils';
|
||||||
|
import { ItemType } from '../../core/shared/item-relationships/item-type.model';
|
||||||
|
import { ChangeDetectorRef, NO_ERRORS_SCHEMA, Pipe, PipeTransform } from '@angular/core';
|
||||||
|
import { EntityTypeService } from '../../core/data/entity-type.service';
|
||||||
|
import { TestScheduler } from 'rxjs/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { createPaginatedList } from '../testing/utils.test';
|
||||||
|
|
||||||
|
// tslint:disable-next-line:pipe-prefix
|
||||||
|
@Pipe({ name: 'translate' })
|
||||||
|
class MockTranslatePipe implements PipeTransform {
|
||||||
|
transform(value: string): string {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const entities: ItemType[] = [
|
||||||
|
Object.assign(new ItemType(), {
|
||||||
|
id: 'ce64f48e-2c9b-411a-ac36-ee429c0e6a88',
|
||||||
|
label: 'Entity_1',
|
||||||
|
uuid: 'UUID-ce64f48e-2c9b-411a-ac36-ee429c0e6a88'
|
||||||
|
}),
|
||||||
|
Object.assign(new ItemType(), {
|
||||||
|
id: '59ee713b-ee53-4220-8c3f-9860dc84fe33',
|
||||||
|
label: 'Entity_2',
|
||||||
|
uuid: 'UUID-59ee713b-ee53-4220-8c3f-9860dc84fe33'
|
||||||
|
}),
|
||||||
|
Object.assign(new ItemType(), {
|
||||||
|
id: 'e9dbf393-7127-415f-8919-55be34a6e9ed',
|
||||||
|
label: 'Entity_3',
|
||||||
|
uuid: 'UUID-7127-415f-8919-55be34a6e9ed'
|
||||||
|
}),
|
||||||
|
Object.assign(new ItemType(), {
|
||||||
|
id: '59da2ff0-9bf4-45bf-88be-e35abd33f304',
|
||||||
|
label: 'Entity_4',
|
||||||
|
uuid: 'UUID-59da2ff0-9bf4-45bf-88be-e35abd33f304'
|
||||||
|
}),
|
||||||
|
Object.assign(new ItemType(), {
|
||||||
|
id: 'a5159760-f362-4659-9e81-e3253ad91ede',
|
||||||
|
label: 'Entity_5',
|
||||||
|
uuid: 'UUID-a5159760-f362-4659-9e81-e3253ad91ede'
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
|
const listElementMock: ItemType = Object.assign(
|
||||||
|
new ItemType(), {
|
||||||
|
id: 'ce64f48e-2c9b-411a-ac36-ee429c0e6a88',
|
||||||
|
label: 'Entity_1',
|
||||||
|
uuid: 'UUID-ce64f48e-2c9b-411a-ac36-ee429c0e6a88'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
describe('EntityDropdownComponent', () => {
|
||||||
|
let component: EntityDropdownComponent;
|
||||||
|
let componentAsAny: any;
|
||||||
|
let fixture: ComponentFixture<EntityDropdownComponent>;
|
||||||
|
let scheduler: TestScheduler;
|
||||||
|
|
||||||
|
const entityTypeServiceMock: any = jasmine.createSpyObj('EntityTypeService', {
|
||||||
|
getAllAuthorizedRelationshipType: jasmine.createSpy('getAllAuthorizedRelationshipType'),
|
||||||
|
getAllAuthorizedRelationshipTypeImport: jasmine.createSpy('getAllAuthorizedRelationshipTypeImport')
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
let translatePipeSpy: jasmine.Spy;
|
||||||
|
|
||||||
|
const paginatedEntities = createPaginatedList(entities);
|
||||||
|
const paginatedEntitiesRD$ = createSuccessfulRemoteDataObject$(paginatedEntities);
|
||||||
|
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [],
|
||||||
|
declarations: [EntityDropdownComponent, MockTranslatePipe],
|
||||||
|
providers: [
|
||||||
|
{ provide: EntityTypeService, useValue: entityTypeServiceMock },
|
||||||
|
ChangeDetectorRef
|
||||||
|
],
|
||||||
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
scheduler = getTestScheduler();
|
||||||
|
fixture = TestBed.createComponent(EntityDropdownComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
componentAsAny = fixture.componentInstance;
|
||||||
|
componentAsAny.entityTypeService.getAllAuthorizedRelationshipType.and.returnValue(paginatedEntitiesRD$);
|
||||||
|
componentAsAny.entityTypeService.getAllAuthorizedRelationshipTypeImport.and.returnValue(paginatedEntitiesRD$);
|
||||||
|
component.isSubmission = true;
|
||||||
|
|
||||||
|
translatePipeSpy = spyOn(MockTranslatePipe.prototype, 'transform');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should translate entries', () => {
|
||||||
|
scheduler.schedule(() => fixture.detectChanges());
|
||||||
|
scheduler.flush();
|
||||||
|
|
||||||
|
expect(translatePipeSpy).toHaveBeenCalledWith('entity_1.listelement.badge');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should init component with entities list', () => {
|
||||||
|
spyOn(component.subs, 'push');
|
||||||
|
spyOn(component, 'resetPagination');
|
||||||
|
spyOn(component, 'populateEntityList').and.callThrough();
|
||||||
|
|
||||||
|
scheduler.schedule(() => fixture.detectChanges());
|
||||||
|
scheduler.flush();
|
||||||
|
const elements = fixture.debugElement.queryAll(By.css('.entity-item'));
|
||||||
|
|
||||||
|
expect(elements.length).toEqual(5);
|
||||||
|
expect(component.subs.push).toHaveBeenCalled();
|
||||||
|
expect(component.resetPagination).toHaveBeenCalled();
|
||||||
|
expect(component.populateEntityList).toHaveBeenCalled();
|
||||||
|
expect((component as any).entityTypeService.getAllAuthorizedRelationshipType).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should trigger onSelect method when select a new entity from list', () => {
|
||||||
|
scheduler.schedule(() => fixture.detectChanges());
|
||||||
|
scheduler.flush();
|
||||||
|
|
||||||
|
spyOn(component, 'onSelect');
|
||||||
|
const entityItem = fixture.debugElement.query(By.css('.entity-item:nth-child(2)'));
|
||||||
|
entityItem.triggerEventHandler('click', null);
|
||||||
|
|
||||||
|
scheduler.schedule(() => fixture.detectChanges());
|
||||||
|
scheduler.flush();
|
||||||
|
|
||||||
|
expect(component.onSelect).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should emit selectionChange event when selecting a new entity', () => {
|
||||||
|
spyOn(component.selectionChange, 'emit').and.callThrough();
|
||||||
|
component.ngOnInit();
|
||||||
|
component.onSelect(listElementMock as any);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(component.selectionChange.emit).toHaveBeenCalledWith(listElementMock as any);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should change loader status', () => {
|
||||||
|
spyOn(component.isLoadingList, 'next').and.callThrough();
|
||||||
|
component.hideShowLoader(true);
|
||||||
|
|
||||||
|
expect(component.isLoadingList.next).toHaveBeenCalledWith(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reset pagination fields', () => {
|
||||||
|
component.resetPagination();
|
||||||
|
|
||||||
|
expect(component.currentPage).toEqual(1);
|
||||||
|
expect(component.hasNextPage).toEqual(true);
|
||||||
|
expect(component.searchListEntity).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should invoke the method getAllAuthorizedRelationshipTypeImport of EntityTypeService when isSubmission is false', () => {
|
||||||
|
component.isSubmission = false;
|
||||||
|
|
||||||
|
scheduler.schedule(() => fixture.detectChanges());
|
||||||
|
scheduler.flush();
|
||||||
|
|
||||||
|
expect((component as any).entityTypeService.getAllAuthorizedRelationshipTypeImport).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
207
src/app/shared/entity-dropdown/entity-dropdown.component.ts
Normal file
207
src/app/shared/entity-dropdown/entity-dropdown.component.ts
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
import {
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Component,
|
||||||
|
ElementRef,
|
||||||
|
EventEmitter,
|
||||||
|
HostListener,
|
||||||
|
Input,
|
||||||
|
OnDestroy,
|
||||||
|
OnInit,
|
||||||
|
Output
|
||||||
|
} from '@angular/core';
|
||||||
|
import { BehaviorSubject, Observable, Subscription } from 'rxjs';
|
||||||
|
import { hasValue } from '../empty.util';
|
||||||
|
import { reduce, startWith, switchMap } from 'rxjs/operators';
|
||||||
|
import { RemoteData } from '../../core/data/remote-data';
|
||||||
|
import { FindListOptions } from '../../core/data/request.models';
|
||||||
|
import { PaginatedList } from '../../core/data/paginated-list.model';
|
||||||
|
import { EntityTypeService } from '../../core/data/entity-type.service';
|
||||||
|
import { ItemType } from '../../core/shared/item-relationships/item-type.model';
|
||||||
|
import { getFirstSucceededRemoteWithNotEmptyData } from '../../core/shared/operators';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-entity-dropdown',
|
||||||
|
templateUrl: './entity-dropdown.component.html',
|
||||||
|
styleUrls: ['./entity-dropdown.component.scss']
|
||||||
|
})
|
||||||
|
export class EntityDropdownComponent implements OnInit, OnDestroy {
|
||||||
|
/**
|
||||||
|
* The entity list obtained from a search
|
||||||
|
* @type {Observable<ItemType[]>}
|
||||||
|
*/
|
||||||
|
public searchListEntity$: Observable<ItemType[]>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if dropdown list is scrollable to the bottom
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
private scrollableBottom = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if dropdown list is scrollable to the top
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
private scrollableTop = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of entity to render
|
||||||
|
*/
|
||||||
|
public searchListEntity: ItemType[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TRUE if the parent operation is a 'new submission' operation, FALSE otherwise (eg.: is an 'Import metadata from an external source' operation).
|
||||||
|
*/
|
||||||
|
@Input() isSubmission: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The entity to output to the parent component
|
||||||
|
*/
|
||||||
|
@Output() selectionChange = new EventEmitter<ItemType>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if the loader is visible or not
|
||||||
|
*/
|
||||||
|
public isLoadingList: BehaviorSubject<boolean> = new BehaviorSubject(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A numeric representig current page
|
||||||
|
*/
|
||||||
|
public currentPage: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A boolean representing if exist another page to render
|
||||||
|
*/
|
||||||
|
public hasNextPage: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array to track all subscriptions and unsubscribe them onDestroy
|
||||||
|
* @type {Array}
|
||||||
|
*/
|
||||||
|
public subs: Subscription[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize instance variables
|
||||||
|
*
|
||||||
|
* @param {ChangeDetectorRef} changeDetectorRef
|
||||||
|
* @param {EntityTypeService} entityTypeService
|
||||||
|
* @param {ElementRef} el
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
private changeDetectorRef: ChangeDetectorRef,
|
||||||
|
private entityTypeService: EntityTypeService,
|
||||||
|
private el: ElementRef
|
||||||
|
) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method called on mousewheel event, it prevent the page scroll
|
||||||
|
* when arriving at the top/bottom of dropdown menu
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* mousewheel event
|
||||||
|
*/
|
||||||
|
@HostListener('mousewheel', ['$event']) onMousewheel(event) {
|
||||||
|
if (event.wheelDelta > 0 && this.scrollableTop) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
if (event.wheelDelta < 0 && this.scrollableBottom) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize entity list
|
||||||
|
*/
|
||||||
|
ngOnInit() {
|
||||||
|
this.resetPagination();
|
||||||
|
this.populateEntityList(this.currentPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if dropdown scrollbar is at the top or bottom of the dropdown list
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
public onScroll(event) {
|
||||||
|
this.scrollableBottom = (event.target.scrollTop + event.target.clientHeight === event.target.scrollHeight);
|
||||||
|
this.scrollableTop = (event.target.scrollTop === 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method used from infitity scroll for retrive more data on scroll down
|
||||||
|
*/
|
||||||
|
public onScrollDown() {
|
||||||
|
if ( this.hasNextPage ) {
|
||||||
|
this.populateEntityList(++this.currentPage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit a [selectionChange] event when a new entity is selected from list
|
||||||
|
*
|
||||||
|
* @param event
|
||||||
|
* the selected [ItemType]
|
||||||
|
*/
|
||||||
|
public onSelect(event: ItemType) {
|
||||||
|
this.selectionChange.emit(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method called for populate the entity list
|
||||||
|
* @param page page number
|
||||||
|
*/
|
||||||
|
public populateEntityList(page: number) {
|
||||||
|
this.isLoadingList.next(true);
|
||||||
|
// Set the pagination info
|
||||||
|
const findOptions: FindListOptions = {
|
||||||
|
elementsPerPage: 10,
|
||||||
|
currentPage: page
|
||||||
|
};
|
||||||
|
let searchListEntity$;
|
||||||
|
if (this.isSubmission) {
|
||||||
|
searchListEntity$ = this.entityTypeService.getAllAuthorizedRelationshipType(findOptions);
|
||||||
|
} else {
|
||||||
|
searchListEntity$ = this.entityTypeService.getAllAuthorizedRelationshipTypeImport(findOptions);
|
||||||
|
}
|
||||||
|
this.searchListEntity$ = searchListEntity$.pipe(
|
||||||
|
getFirstSucceededRemoteWithNotEmptyData(),
|
||||||
|
switchMap((entityType: RemoteData<PaginatedList<ItemType>>) => {
|
||||||
|
if ( (this.searchListEntity.length + findOptions.elementsPerPage) >= entityType.payload.totalElements ) {
|
||||||
|
this.hasNextPage = false;
|
||||||
|
}
|
||||||
|
return entityType.payload.page;
|
||||||
|
}),
|
||||||
|
reduce((acc: any, value: any) => [...acc, value], []),
|
||||||
|
startWith([])
|
||||||
|
);
|
||||||
|
this.subs.push(
|
||||||
|
this.searchListEntity$.subscribe(
|
||||||
|
(next) => { this.searchListEntity.push(...next); }, undefined,
|
||||||
|
() => { this.hideShowLoader(false); this.changeDetectorRef.detectChanges(); }
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset pagination values
|
||||||
|
*/
|
||||||
|
public resetPagination() {
|
||||||
|
this.currentPage = 1;
|
||||||
|
this.hasNextPage = true;
|
||||||
|
this.searchListEntity = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide/Show the entity list loader
|
||||||
|
* @param hideShow true for show, false otherwise
|
||||||
|
*/
|
||||||
|
public hideShowLoader(hideShow: boolean) {
|
||||||
|
this.isLoadingList.next(hideShow);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unsubscribe from all subscriptions
|
||||||
|
*/
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.subs.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe());
|
||||||
|
}
|
||||||
|
}
|
@@ -53,7 +53,8 @@ import { FormComponent } from './form/form.component';
|
|||||||
import { DsDynamicOneboxComponent } from './form/builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.component';
|
import { DsDynamicOneboxComponent } from './form/builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.component';
|
||||||
import { DsDynamicScrollableDropdownComponent } from './form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component';
|
import { DsDynamicScrollableDropdownComponent } from './form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.component';
|
||||||
import {
|
import {
|
||||||
DsDynamicFormControlContainerComponent, dsDynamicFormControlMapFn,
|
DsDynamicFormControlContainerComponent,
|
||||||
|
dsDynamicFormControlMapFn,
|
||||||
} from './form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component';
|
} from './form/builder/ds-dynamic-form-ui/ds-dynamic-form-control-container.component';
|
||||||
import { DsDynamicFormComponent } from './form/builder/ds-dynamic-form-ui/ds-dynamic-form.component';
|
import { DsDynamicFormComponent } from './form/builder/ds-dynamic-form-ui/ds-dynamic-form.component';
|
||||||
import { DragClickDirective } from './utils/drag-click.directive';
|
import { DragClickDirective } from './utils/drag-click.directive';
|
||||||
@@ -202,6 +203,7 @@ import { EpersonSearchBoxComponent } from './resource-policies/form/eperson-grou
|
|||||||
import { GroupSearchBoxComponent } from './resource-policies/form/eperson-group-list/group-search-box/group-search-box.component';
|
import { GroupSearchBoxComponent } from './resource-policies/form/eperson-group-list/group-search-box/group-search-box.component';
|
||||||
import { FileDownloadLinkComponent } from './file-download-link/file-download-link.component';
|
import { FileDownloadLinkComponent } from './file-download-link/file-download-link.component';
|
||||||
import { CollectionDropdownComponent } from './collection-dropdown/collection-dropdown.component';
|
import { CollectionDropdownComponent } from './collection-dropdown/collection-dropdown.component';
|
||||||
|
import { EntityDropdownComponent } from './entity-dropdown/entity-dropdown.component';
|
||||||
import { DsSelectComponent } from './ds-select/ds-select.component';
|
import { DsSelectComponent } from './ds-select/ds-select.component';
|
||||||
import { VocabularyTreeviewComponent } from './vocabulary-treeview/vocabulary-treeview.component';
|
import { VocabularyTreeviewComponent } from './vocabulary-treeview/vocabulary-treeview.component';
|
||||||
import { CurationFormComponent } from '../curation-form/curation-form.component';
|
import { CurationFormComponent } from '../curation-form/curation-form.component';
|
||||||
@@ -432,6 +434,7 @@ const COMPONENTS = [
|
|||||||
FileDownloadLinkComponent,
|
FileDownloadLinkComponent,
|
||||||
BitstreamDownloadPageComponent,
|
BitstreamDownloadPageComponent,
|
||||||
CollectionDropdownComponent,
|
CollectionDropdownComponent,
|
||||||
|
EntityDropdownComponent,
|
||||||
ExportMetadataSelectorComponent,
|
ExportMetadataSelectorComponent,
|
||||||
ConfirmationModalComponent,
|
ConfirmationModalComponent,
|
||||||
VocabularyTreeviewComponent,
|
VocabularyTreeviewComponent,
|
||||||
|
Reference in New Issue
Block a user