mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-18 07:23:03 +00:00
Merge pull request #354 from atmire/mixing-text-authority-entities
[Configurable Entities] Mixing text fields, authority controlled fields and typed items
This commit is contained in:
@@ -346,7 +346,7 @@
|
||||
"f.dateIssued.max": "End date",
|
||||
"f.subject": "Subject",
|
||||
"f.has_content_in_original_bundle": "Has files",
|
||||
"f.entityType": "Entity Type"
|
||||
"f.entityType": "Item Type"
|
||||
},
|
||||
"filter": {
|
||||
"show-more": "Show more",
|
||||
@@ -376,8 +376,8 @@
|
||||
"head": "Has files"
|
||||
},
|
||||
"entityType": {
|
||||
"placeholder": "Entity Type",
|
||||
"head": "Entity Type"
|
||||
"placeholder": "Item Type",
|
||||
"head": "Item Type"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { MockTranslateLoader } from '../../../shared/mocks/mock-translate-loader';
|
||||
import { MetadataValuesComponent } from './metadata-values.component';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { Metadatum } from '../../../core/shared/metadatum.model';
|
||||
|
||||
let comp: MetadataValuesComponent;
|
||||
let fixture: ComponentFixture<MetadataValuesComponent>;
|
||||
@@ -23,7 +24,7 @@ const mockMetadata = [
|
||||
key: 'journal.identifier.description',
|
||||
language: 'en_US',
|
||||
value: 'desc'
|
||||
}];
|
||||
}] as Metadatum[];
|
||||
const mockSeperator = '<br/>';
|
||||
const mockLabel = 'fake.message';
|
||||
|
||||
|
@@ -19,17 +19,18 @@ import { FileSectionComponent } from './simple/field-components/file-section/fil
|
||||
import { CollectionsComponent } from './field-components/collections/collections.component';
|
||||
import { FullItemPageComponent } from './full/full-item-page.component';
|
||||
import { FullFileSectionComponent } from './full/field-components/file-section/full-file-section.component';
|
||||
import { RelatedEntitiesComponent } from './simple/related-entities/related-entities-component';
|
||||
import { RelatedItemsComponent } from './simple/related-items/related-items-component';
|
||||
import { SearchPageModule } from '../+search-page/search-page.module';
|
||||
import { PublicationComponent } from './simple/entity-types/publication/publication.component';
|
||||
import { PersonComponent } from './simple/entity-types/person/person.component';
|
||||
import { OrgunitComponent } from './simple/entity-types/orgunit/orgunit.component';
|
||||
import { ProjectComponent } from './simple/entity-types/project/project.component';
|
||||
import { JournalComponent } from './simple/entity-types/journal/journal.component';
|
||||
import { JournalVolumeComponent } from './simple/entity-types/journal-volume/journal-volume.component';
|
||||
import { JournalIssueComponent } from './simple/entity-types/journal-issue/journal-issue.component';
|
||||
import { EntityComponent } from './simple/entity-types/shared/entity.component';
|
||||
import { PublicationComponent } from './simple/item-types/publication/publication.component';
|
||||
import { PersonComponent } from './simple/item-types/person/person.component';
|
||||
import { OrgunitComponent } from './simple/item-types/orgunit/orgunit.component';
|
||||
import { ProjectComponent } from './simple/item-types/project/project.component';
|
||||
import { JournalComponent } from './simple/item-types/journal/journal.component';
|
||||
import { JournalVolumeComponent } from './simple/item-types/journal-volume/journal-volume.component';
|
||||
import { JournalIssueComponent } from './simple/item-types/journal-issue/journal-issue.component';
|
||||
import { ItemComponent } from './simple/item-types/shared/item.component';
|
||||
import { EditItemPageModule } from './edit-item-page/edit-item-page.module';
|
||||
import { MetadataRepresentationListComponent } from './simple/metadata-representation-list/metadata-representation-list.component';
|
||||
import { RelatedEntitiesSearchComponent } from './simple/related-entities/related-entities-search/related-entities-search.component';
|
||||
|
||||
@NgModule({
|
||||
@@ -59,12 +60,13 @@ import { RelatedEntitiesSearchComponent } from './simple/related-entities/relate
|
||||
ProjectComponent,
|
||||
OrgunitComponent,
|
||||
PersonComponent,
|
||||
RelatedEntitiesComponent,
|
||||
EntityComponent,
|
||||
RelatedItemsComponent,
|
||||
ItemComponent,
|
||||
GenericItemPageFieldComponent,
|
||||
JournalComponent,
|
||||
JournalIssueComponent,
|
||||
JournalVolumeComponent,
|
||||
MetadataRepresentationListComponent,
|
||||
RelatedEntitiesSearchComponent
|
||||
],
|
||||
entryComponents: [
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<div class="container" *ngVar="(itemRD$ | async) as itemRD">
|
||||
<div class="item-page" *ngIf="itemRD?.hasSucceeded" @fadeInOut>
|
||||
<div *ngIf="itemRD?.payload as item">
|
||||
<ds-entity-type-switcher [object]="item" [viewMode]="ElementViewMode.Full"></ds-entity-type-switcher>
|
||||
<ds-item-type-switcher [object]="item" [viewMode]="viewMode"></ds-item-type-switcher>
|
||||
</div>
|
||||
</div>
|
||||
<ds-error *ngIf="itemRD?.hasFailed" message="{{'error.item' | translate}}"></ds-error>
|
||||
|
@@ -8,14 +8,13 @@ import { ActivatedRoute } from '@angular/router';
|
||||
import { ActivatedRouteStub } from '../../shared/testing/active-router-stub';
|
||||
import { MetadataService } from '../../core/metadata/metadata.service';
|
||||
import { VarDirective } from '../../shared/utils/var.directive';
|
||||
import { Observable } from 'rxjs';
|
||||
import { RemoteData } from '../../core/data/remote-data';
|
||||
import { Item } from '../../core/shared/item.model';
|
||||
import { PaginatedList } from '../../core/data/paginated-list';
|
||||
import { PageInfo } from '../../core/shared/page-info.model';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { createRelationshipsObservable } from './entity-types/shared/entity.component.spec';
|
||||
import { createRelationshipsObservable } from './item-types/shared/item.component.spec';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
|
||||
const mockItem: Item = Object.assign(new Item(), {
|
||||
|
@@ -15,6 +15,8 @@ import { fadeInOut } from '../../shared/animations/fade';
|
||||
import { hasValue } from '../../shared/empty.util';
|
||||
import * as viewMode from '../../shared/view-mode';
|
||||
|
||||
export const VIEW_MODE_FULL = 'full';
|
||||
|
||||
/**
|
||||
* This component renders a simple item page.
|
||||
* The route parameter 'id' is used to request the item it represents.
|
||||
@@ -46,9 +48,8 @@ export class ItemPageComponent implements OnInit {
|
||||
|
||||
/**
|
||||
* The view-mode we're currently on
|
||||
* @type {ElementViewMode}
|
||||
*/
|
||||
ElementViewMode = viewMode.ElementViewMode;
|
||||
viewMode = VIEW_MODE_FULL;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
|
@@ -24,15 +24,15 @@
|
||||
</ds-generic-item-page-field>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<ds-related-entities
|
||||
[entities]="volumes$ | async"
|
||||
<ds-related-items
|
||||
[items]="volumes$ | async"
|
||||
[label]="'relationships.isSingleVolumeOf' | translate">
|
||||
</ds-related-entities>
|
||||
<ds-related-entities
|
||||
</ds-related-items>
|
||||
<ds-related-items
|
||||
class="mb-1 mt-1"
|
||||
[entities]="publications$ | async"
|
||||
[items]="publications$ | async"
|
||||
[label]="'relationships.isPublicationOfJournalIssue' | translate">
|
||||
</ds-related-entities>
|
||||
</ds-related-items>
|
||||
<ds-generic-item-page-field [item]="item"
|
||||
[fields]="['journalissue.identifier.description']"
|
||||
[label]="'journalissue.page.description'">
|
@@ -1,9 +1,8 @@
|
||||
import { Observable } from 'rxjs';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { RemoteData } from '../../../../core/data/remote-data';
|
||||
import { PaginatedList } from '../../../../core/data/paginated-list';
|
||||
import { PageInfo } from '../../../../core/shared/page-info.model';
|
||||
import { createRelationshipsObservable, getEntityPageFieldsTest } from '../shared/entity.component.spec';
|
||||
import { createRelationshipsObservable, getItemPageFieldsTest } from '../shared/item.component.spec';
|
||||
import { JournalIssueComponent } from './journal-issue.component';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
|
||||
@@ -33,4 +32,4 @@ const mockItem: Item = Object.assign(new Item(), {
|
||||
relationships: createRelationshipsObservable()
|
||||
});
|
||||
|
||||
describe('JournalIssueComponent', getEntityPageFieldsTest(mockItem, JournalIssueComponent));
|
||||
describe('JournalIssueComponent', getItemPageFieldsTest(mockItem, JournalIssueComponent));
|
@@ -2,22 +2,22 @@ import { Component, Inject } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { ItemDataService } from '../../../../core/data/item-data.service';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator';
|
||||
import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component';
|
||||
import { ElementViewMode } from '../../../../shared/view-mode';
|
||||
import { rendersItemType } from '../../../../shared/items/item-type-decorator';
|
||||
import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component';
|
||||
import { isNotEmpty } from '../../../../shared/empty.util';
|
||||
import { EntityComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/entity.component';
|
||||
import { ItemComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/item.component';
|
||||
import { VIEW_MODE_FULL } from '../../item-page.component';
|
||||
|
||||
@rendersEntityType('JournalIssue', ElementViewMode.Full)
|
||||
@rendersItemType('JournalIssue', VIEW_MODE_FULL)
|
||||
@Component({
|
||||
selector: 'ds-journal-issue',
|
||||
styleUrls: ['./journal-issue.component.scss'],
|
||||
templateUrl: './journal-issue.component.html'
|
||||
})
|
||||
/**
|
||||
* The component for displaying metadata and relations of an item with entity type Journal Issue
|
||||
* The component for displaying metadata and relations of an item of the type Journal Issue
|
||||
*/
|
||||
export class JournalIssueComponent extends EntityComponent {
|
||||
export class JournalIssueComponent extends ItemComponent {
|
||||
/**
|
||||
* The volumes related to this journal issue
|
||||
*/
|
@@ -16,14 +16,14 @@
|
||||
</ds-generic-item-page-field>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<ds-related-entities
|
||||
[entities]="journals$ | async"
|
||||
<ds-related-items
|
||||
[items]="journals$ | async"
|
||||
[label]="'relationships.isSingleJournalOf' | translate">
|
||||
</ds-related-entities>
|
||||
<ds-related-entities
|
||||
[entities]="issues$ | async"
|
||||
</ds-related-items>
|
||||
<ds-related-items
|
||||
[items]="issues$ | async"
|
||||
[label]="'relationships.isIssueOf' | translate">
|
||||
</ds-related-entities>
|
||||
</ds-related-items>
|
||||
<ds-generic-item-page-field [item]="item"
|
||||
[fields]="['journalvolume.identifier.description']"
|
||||
[label]="'journalvolume.page.description'">
|
@@ -1,9 +1,8 @@
|
||||
import { Observable } from 'rxjs';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { RemoteData } from '../../../../core/data/remote-data';
|
||||
import { PaginatedList } from '../../../../core/data/paginated-list';
|
||||
import { PageInfo } from '../../../../core/shared/page-info.model';
|
||||
import { createRelationshipsObservable, getEntityPageFieldsTest } from '../shared/entity.component.spec';
|
||||
import { createRelationshipsObservable, getItemPageFieldsTest } from '../shared/item.component.spec';
|
||||
import { JournalVolumeComponent } from './journal-volume.component';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
|
||||
@@ -28,4 +27,4 @@ const mockItem: Item = Object.assign(new Item(), {
|
||||
relationships: createRelationshipsObservable()
|
||||
});
|
||||
|
||||
describe('JournalVolumeComponent', getEntityPageFieldsTest(mockItem, JournalVolumeComponent));
|
||||
describe('JournalVolumeComponent', getItemPageFieldsTest(mockItem, JournalVolumeComponent));
|
@@ -2,22 +2,22 @@ import { Component, Inject } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { ItemDataService } from '../../../../core/data/item-data.service';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator';
|
||||
import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component';
|
||||
import { ElementViewMode } from '../../../../shared/view-mode';
|
||||
import { rendersItemType } from '../../../../shared/items/item-type-decorator';
|
||||
import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component';
|
||||
import { isNotEmpty } from '../../../../shared/empty.util';
|
||||
import { EntityComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/entity.component';
|
||||
import { ItemComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/item.component';
|
||||
import { VIEW_MODE_FULL } from '../../item-page.component';
|
||||
|
||||
@rendersEntityType('JournalVolume', ElementViewMode.Full)
|
||||
@rendersItemType('JournalVolume', VIEW_MODE_FULL)
|
||||
@Component({
|
||||
selector: 'ds-journal-volume',
|
||||
styleUrls: ['./journal-volume.component.scss'],
|
||||
templateUrl: './journal-volume.component.html'
|
||||
})
|
||||
/**
|
||||
* The component for displaying metadata and relations of an item with entity type Journal Volume
|
||||
* The component for displaying metadata and relations of an item of the type Journal Volume
|
||||
*/
|
||||
export class JournalVolumeComponent extends EntityComponent {
|
||||
export class JournalVolumeComponent extends ItemComponent {
|
||||
/**
|
||||
* The journals related to this journal volume
|
||||
*/
|
@@ -20,10 +20,10 @@
|
||||
</ds-generic-item-page-field>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<ds-related-entities
|
||||
[entities]="volumes$ | async"
|
||||
<ds-related-items
|
||||
[items]="volumes$ | async"
|
||||
[label]="'relationships.isVolumeOf' | translate">
|
||||
</ds-related-entities>
|
||||
</ds-related-items>
|
||||
<ds-generic-item-page-field class="item-page-fields" [item]="item"
|
||||
[fields]="['journal.identifier.description']"
|
||||
[label]="'journal.page.description'">
|
@@ -1,10 +1,9 @@
|
||||
import { ChangeDetectionStrategy, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { TruncatableService } from '../../../../shared/truncatable/truncatable.service';
|
||||
import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component';
|
||||
import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component';
|
||||
import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
|
||||
import { ItemDataService } from '../../../../core/data/item-data.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
@@ -2,22 +2,22 @@ import { Component, Inject } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { ItemDataService } from '../../../../core/data/item-data.service';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator';
|
||||
import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component';
|
||||
import { ElementViewMode } from '../../../../shared/view-mode';
|
||||
import { rendersItemType } from '../../../../shared/items/item-type-decorator';
|
||||
import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component';
|
||||
import { isNotEmpty } from '../../../../shared/empty.util';
|
||||
import { EntityComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/entity.component';
|
||||
import { ItemComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/item.component';
|
||||
import { VIEW_MODE_FULL } from '../../item-page.component';
|
||||
|
||||
@rendersEntityType('Journal', ElementViewMode.Full)
|
||||
@rendersItemType('Journal', VIEW_MODE_FULL)
|
||||
@Component({
|
||||
selector: 'ds-journal',
|
||||
styleUrls: ['./journal.component.scss'],
|
||||
templateUrl: './journal.component.html'
|
||||
})
|
||||
/**
|
||||
* The component for displaying metadata and relations of an item with entity type Journal
|
||||
* The component for displaying metadata and relations of an item of the type Journal
|
||||
*/
|
||||
export class JournalComponent extends EntityComponent {
|
||||
export class JournalComponent extends ItemComponent {
|
||||
/**
|
||||
* The volumes related to this journal
|
||||
*/
|
@@ -24,18 +24,18 @@
|
||||
</ds-generic-item-page-field>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<ds-related-entities
|
||||
[entities]="people$ | async"
|
||||
<ds-related-items
|
||||
[items]="people$ | async"
|
||||
[label]="'relationships.isPersonOf' | translate">
|
||||
</ds-related-entities>
|
||||
<ds-related-entities
|
||||
[entities]="projects$ | async"
|
||||
</ds-related-items>
|
||||
<ds-related-items
|
||||
[items]="projects$ | async"
|
||||
[label]="'relationships.isProjectOf' | translate">
|
||||
</ds-related-entities>
|
||||
<ds-related-entities
|
||||
[entities]="publications$ | async"
|
||||
</ds-related-items>
|
||||
<ds-related-items
|
||||
[items]="publications$ | async"
|
||||
[label]="'relationships.isPublicationOf' | translate">
|
||||
</ds-related-entities>
|
||||
</ds-related-items>
|
||||
<ds-generic-item-page-field [item]="item"
|
||||
[fields]="['orgunit.identifier.description']"
|
||||
[label]="'orgunit.page.description'">
|
@@ -1,9 +1,8 @@
|
||||
import { Observable } from 'rxjs';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { RemoteData } from '../../../../core/data/remote-data';
|
||||
import { PaginatedList } from '../../../../core/data/paginated-list';
|
||||
import { PageInfo } from '../../../../core/shared/page-info.model';
|
||||
import { createRelationshipsObservable, getEntityPageFieldsTest } from '../shared/entity.component.spec';
|
||||
import { createRelationshipsObservable, getItemPageFieldsTest } from '../shared/item.component.spec';
|
||||
import { OrgunitComponent } from './orgunit.component';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
|
||||
@@ -38,4 +37,4 @@ const mockItem: Item = Object.assign(new Item(), {
|
||||
relationships: createRelationshipsObservable()
|
||||
});
|
||||
|
||||
describe('OrgUnitComponent', getEntityPageFieldsTest(mockItem, OrgunitComponent));
|
||||
describe('OrgUnitComponent', getItemPageFieldsTest(mockItem, OrgunitComponent));
|
@@ -2,22 +2,22 @@ import { Component, Inject, OnInit } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { ItemDataService } from '../../../../core/data/item-data.service';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator';
|
||||
import { ElementViewMode } from '../../../../shared/view-mode';
|
||||
import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component';
|
||||
import { rendersItemType } from '../../../../shared/items/item-type-decorator';
|
||||
import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component';
|
||||
import { isNotEmpty } from '../../../../shared/empty.util';
|
||||
import { EntityComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/entity.component';
|
||||
import { ItemComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/item.component';
|
||||
import { VIEW_MODE_FULL } from '../../item-page.component';
|
||||
|
||||
@rendersEntityType('OrgUnit', ElementViewMode.Full)
|
||||
@rendersItemType('OrgUnit', VIEW_MODE_FULL)
|
||||
@Component({
|
||||
selector: 'ds-orgunit',
|
||||
styleUrls: ['./orgunit.component.scss'],
|
||||
templateUrl: './orgunit.component.html'
|
||||
})
|
||||
/**
|
||||
* The component for displaying metadata and relations of an item with entity type Organisation Unit
|
||||
* The component for displaying metadata and relations of an item of the type Organisation Unit
|
||||
*/
|
||||
export class OrgunitComponent extends EntityComponent implements OnInit {
|
||||
export class OrgunitComponent extends ItemComponent implements OnInit {
|
||||
/**
|
||||
* The people related to this organisation unit
|
||||
*/
|
@@ -24,14 +24,14 @@
|
||||
</ds-generic-item-page-field>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<ds-related-entities
|
||||
[entities]="projects$ | async"
|
||||
<ds-related-items
|
||||
[items]="projects$ | async"
|
||||
[label]="'relationships.isProjectOf' | translate">
|
||||
</ds-related-entities>
|
||||
<ds-related-entities
|
||||
[entities]="orgUnits$ | async"
|
||||
</ds-related-items>
|
||||
<ds-related-items
|
||||
[items]="orgUnits$ | async"
|
||||
[label]="'relationships.isOrgUnitOf' | translate">
|
||||
</ds-related-entities>
|
||||
</ds-related-items>
|
||||
<ds-generic-item-page-field [item]="item"
|
||||
[fields]="['person.identifier.jobtitle']"
|
||||
[label]="'person.page.jobtitle'">
|
@@ -1,9 +1,8 @@
|
||||
import { Observable } from 'rxjs';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { RemoteData } from '../../../../core/data/remote-data';
|
||||
import { PaginatedList } from '../../../../core/data/paginated-list';
|
||||
import { PageInfo } from '../../../../core/shared/page-info.model';
|
||||
import { createRelationshipsObservable, getEntityPageFieldsTest } from '../shared/entity.component.spec';
|
||||
import { createRelationshipsObservable, getItemPageFieldsTest } from '../shared/item.component.spec';
|
||||
import { PersonComponent } from './person.component';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
|
||||
@@ -48,4 +47,4 @@ const mockItem: Item = Object.assign(new Item(), {
|
||||
relationships: createRelationshipsObservable()
|
||||
});
|
||||
|
||||
describe('PersonComponent', getEntityPageFieldsTest(mockItem, PersonComponent));
|
||||
describe('PersonComponent', getItemPageFieldsTest(mockItem, PersonComponent));
|
@@ -2,23 +2,23 @@ import { Component, Inject } from '@angular/core';
|
||||
import { Observable , of as observableOf } from 'rxjs';
|
||||
import { ItemDataService } from '../../../../core/data/item-data.service';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator';
|
||||
import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component';
|
||||
import { ElementViewMode } from '../../../../shared/view-mode';
|
||||
import { rendersItemType } from '../../../../shared/items/item-type-decorator';
|
||||
import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component';
|
||||
import { SearchFixedFilterService } from '../../../../+search-page/search-filters/search-filter/search-fixed-filter.service';
|
||||
import { isNotEmpty } from '../../../../shared/empty.util';
|
||||
import { EntityComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/entity.component';
|
||||
import { ItemComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/item.component';
|
||||
import { VIEW_MODE_FULL } from '../../item-page.component';
|
||||
|
||||
@rendersEntityType('Person', ElementViewMode.Full)
|
||||
@rendersItemType('Person', VIEW_MODE_FULL)
|
||||
@Component({
|
||||
selector: 'ds-person',
|
||||
styleUrls: ['./person.component.scss'],
|
||||
templateUrl: './person.component.html'
|
||||
})
|
||||
/**
|
||||
* The component for displaying metadata and relations of an item with entity type Person
|
||||
* The component for displaying metadata and relations of an item of the type Person
|
||||
*/
|
||||
export class PersonComponent extends EntityComponent {
|
||||
export class PersonComponent extends ItemComponent {
|
||||
/**
|
||||
* The publications related to this person
|
||||
*/
|
@@ -20,18 +20,18 @@
|
||||
</ds-generic-item-page-field>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<ds-related-entities
|
||||
[entities]="people$ | async"
|
||||
<ds-related-items
|
||||
[items]="people$ | async"
|
||||
[label]="'relationships.isPersonOf' | translate">
|
||||
</ds-related-entities>
|
||||
<ds-related-entities
|
||||
[entities]="publications$ | async"
|
||||
</ds-related-items>
|
||||
<ds-related-items
|
||||
[items]="publications$ | async"
|
||||
[label]="'relationships.isPublicationOf' | translate">
|
||||
</ds-related-entities>
|
||||
<ds-related-entities
|
||||
[entities]="orgUnits$ | async"
|
||||
</ds-related-items>
|
||||
<ds-related-items
|
||||
[items]="orgUnits$ | async"
|
||||
[label]="'relationships.isOrgUnitOf' | translate">
|
||||
</ds-related-entities>
|
||||
</ds-related-items>
|
||||
<ds-generic-item-page-field [item]="item"
|
||||
[fields]="['project.identifier.description']"
|
||||
[label]="'project.page.description'">
|
@@ -1,9 +1,8 @@
|
||||
import { Observable } from 'rxjs';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { RemoteData } from '../../../../core/data/remote-data';
|
||||
import { PaginatedList } from '../../../../core/data/paginated-list';
|
||||
import { PageInfo } from '../../../../core/shared/page-info.model';
|
||||
import { createRelationshipsObservable, getEntityPageFieldsTest } from '../shared/entity.component.spec';
|
||||
import { createRelationshipsObservable, getItemPageFieldsTest } from '../shared/item.component.spec';
|
||||
import { ProjectComponent } from './project.component';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
|
||||
@@ -38,4 +37,4 @@ const mockItem: Item = Object.assign(new Item(), {
|
||||
relationships: createRelationshipsObservable()
|
||||
});
|
||||
|
||||
describe('ProjectComponent', getEntityPageFieldsTest(mockItem, ProjectComponent));
|
||||
describe('ProjectComponent', getItemPageFieldsTest(mockItem, ProjectComponent));
|
@@ -2,22 +2,22 @@ import { Component, Inject, OnInit } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { ItemDataService } from '../../../../core/data/item-data.service';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { rendersEntityType } from '../../../../shared/entities/entity-type-decorator';
|
||||
import { ElementViewMode } from '../../../../shared/view-mode';
|
||||
import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component';
|
||||
import { rendersItemType } from '../../../../shared/items/item-type-decorator';
|
||||
import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component';
|
||||
import { isNotEmpty } from '../../../../shared/empty.util';
|
||||
import { EntityComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/entity.component';
|
||||
import { ItemComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/item.component';
|
||||
import { VIEW_MODE_FULL } from '../../item-page.component';
|
||||
|
||||
@rendersEntityType('Project', ElementViewMode.Full)
|
||||
@rendersItemType('Project', VIEW_MODE_FULL)
|
||||
@Component({
|
||||
selector: 'ds-project',
|
||||
styleUrls: ['./project.component.scss'],
|
||||
templateUrl: './project.component.html'
|
||||
})
|
||||
/**
|
||||
* The component for displaying metadata and relations of an item with entity type Project
|
||||
* The component for displaying metadata and relations of an item of the type Project
|
||||
*/
|
||||
export class ProjectComponent extends EntityComponent implements OnInit {
|
||||
export class ProjectComponent extends ItemComponent implements OnInit {
|
||||
/**
|
||||
* The people related to this project
|
||||
*/
|
@@ -21,22 +21,22 @@
|
||||
</ds-generic-item-page-field>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<ds-related-entities
|
||||
[entities]="authors$ | async"
|
||||
[label]="'relationships.isAuthorOf' | translate">
|
||||
</ds-related-entities>
|
||||
<ds-related-entities
|
||||
[entities]="projects$ | async"
|
||||
<ds-metadata-representation-list
|
||||
[label]="'relationships.isAuthorOf' | translate"
|
||||
[representations]="authors$ | async">
|
||||
</ds-metadata-representation-list>
|
||||
<ds-related-items
|
||||
[items]="projects$ | async"
|
||||
[label]="'relationships.isProjectOf' | translate">
|
||||
</ds-related-entities>
|
||||
<ds-related-entities
|
||||
[entities]="orgUnits$ | async"
|
||||
</ds-related-items>
|
||||
<ds-related-items
|
||||
[items]="orgUnits$ | async"
|
||||
[label]="'relationships.isOrgUnitOf' | translate">
|
||||
</ds-related-entities>
|
||||
<ds-related-entities
|
||||
[entities]="journalIssues$ | async"
|
||||
</ds-related-items>
|
||||
<ds-related-items
|
||||
[items]="journalIssues$ | async"
|
||||
[label]="'relationships.isJournalIssueOf' | translate">
|
||||
</ds-related-entities>
|
||||
</ds-related-items>
|
||||
<ds-item-page-abstract-field [item]="item"></ds-item-page-abstract-field>
|
||||
<ds-generic-item-page-field [item]="item"
|
||||
[fields]="['dc.subject']"
|
@@ -3,18 +3,17 @@ import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loader';
|
||||
import { GenericItemPageFieldComponent } from '../../field-components/specific-field/generic/generic-item-page-field.component';
|
||||
import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
|
||||
import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component';
|
||||
import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component';
|
||||
import { ItemDataService } from '../../../../core/data/item-data.service';
|
||||
import { SearchFixedFilterService } from '../../../../+search-page/search-filters/search-filter/search-fixed-filter.service';
|
||||
import { TruncatableService } from '../../../../shared/truncatable/truncatable.service';
|
||||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { Observable } from 'rxjs';
|
||||
import { RemoteData } from '../../../../core/data/remote-data';
|
||||
import { PaginatedList } from '../../../../core/data/paginated-list';
|
||||
import { PageInfo } from '../../../../core/shared/page-info.model';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { createRelationshipsObservable } from '../shared/entity.component.spec';
|
||||
import { createRelationshipsObservable } from '../shared/item.component.spec';
|
||||
import { PublicationComponent } from './publication.component';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
|
@@ -3,26 +3,27 @@ import { Observable } from 'rxjs';
|
||||
import { ItemDataService } from '../../../../core/data/item-data.service';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import {
|
||||
DEFAULT_ENTITY_TYPE,
|
||||
rendersEntityType
|
||||
} from '../../../../shared/entities/entity-type-decorator';
|
||||
import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component';
|
||||
import { ElementViewMode } from '../../../../shared/view-mode';
|
||||
import { EntityComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/entity.component';
|
||||
DEFAULT_ITEM_TYPE,
|
||||
rendersItemType
|
||||
} from '../../../../shared/items/item-type-decorator';
|
||||
import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component';
|
||||
import { ItemComponent, filterRelationsByTypeLabel, relationsToItems } from '../shared/item.component';
|
||||
import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model';
|
||||
import { VIEW_MODE_FULL } from '../../item-page.component';
|
||||
|
||||
@rendersEntityType('Publication', ElementViewMode.Full)
|
||||
@rendersEntityType(DEFAULT_ENTITY_TYPE, ElementViewMode.Full)
|
||||
@rendersItemType('Publication', VIEW_MODE_FULL)
|
||||
@rendersItemType(DEFAULT_ITEM_TYPE, VIEW_MODE_FULL)
|
||||
@Component({
|
||||
selector: 'ds-publication',
|
||||
styleUrls: ['./publication.component.scss'],
|
||||
templateUrl: './publication.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class PublicationComponent extends EntityComponent implements OnInit {
|
||||
export class PublicationComponent extends ItemComponent implements OnInit {
|
||||
/**
|
||||
* The authors related to this publication
|
||||
*/
|
||||
authors$: Observable<Item[]>;
|
||||
authors$: Observable<MetadataRepresentation[]>;
|
||||
|
||||
/**
|
||||
* The projects related to this publication
|
||||
@@ -51,10 +52,7 @@ export class PublicationComponent extends EntityComponent implements OnInit {
|
||||
|
||||
if (this.resolvedRelsAndTypes$) {
|
||||
|
||||
this.authors$ = this.resolvedRelsAndTypes$.pipe(
|
||||
filterRelationsByTypeLabel('isAuthorOfPublication'),
|
||||
relationsToItems(this.item.id, this.ids)
|
||||
);
|
||||
this.authors$ = this.buildRepresentations('Person', 'dc.contributor.author', this.ids);
|
||||
|
||||
this.projects$ = this.resolvedRelsAndTypes$.pipe(
|
||||
filterRelationsByTypeLabel('isProjectOfPublication'),
|
@@ -7,27 +7,36 @@ import { ItemDataService } from '../../../../core/data/item-data.service';
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loader';
|
||||
import { ChangeDetectionStrategy, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component';
|
||||
import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component';
|
||||
import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
|
||||
import { isNotEmpty } from '../../../../shared/empty.util';
|
||||
import { SearchFixedFilterService } from '../../../../+search-page/search-filters/search-filter/search-fixed-filter.service';
|
||||
import { RelationshipType } from '../../../../core/shared/entities/relationship-type.model';
|
||||
import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model';
|
||||
import { PaginatedList } from '../../../../core/data/paginated-list';
|
||||
import { RemoteData } from '../../../../core/data/remote-data';
|
||||
import { Relationship } from '../../../../core/shared/entities/relationship.model';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
|
||||
import { PageInfo } from '../../../../core/shared/page-info.model';
|
||||
import { compareArraysUsing, compareArraysUsingIds } from './entity.component';
|
||||
import { compareArraysUsing, compareArraysUsingIds, ItemComponent } from './item.component';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { ItemPageComponent } from '../../item-page.component';
|
||||
import { VarDirective } from '../../../../shared/utils/var.directive';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { MetadataService } from '../../../../core/metadata/metadata.service';
|
||||
import { of } from 'rxjs/internal/observable/of';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model';
|
||||
import { MetadatumRepresentation } from '../../../../core/shared/metadata-representation/metadatum/metadatum-representation.model';
|
||||
import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-representation/item/item-metadata-representation.model';
|
||||
|
||||
/**
|
||||
* Create a generic test for an entity-page-fields component using a mockItem and the type of component
|
||||
* Create a generic test for an item-page-fields component using a mockItem and the type of component
|
||||
* @param {Item} mockItem The item to use for testing. The item needs to contain just the metadata necessary to
|
||||
* execute the tests for it's component.
|
||||
* @param component The type of component to create test cases for.
|
||||
* @returns {() => void} Returns a specDefinition for the test.
|
||||
*/
|
||||
export function getEntityPageFieldsTest(mockItem: Item, component) {
|
||||
export function getItemPageFieldsTest(mockItem: Item, component) {
|
||||
return () => {
|
||||
let comp: any;
|
||||
let fixture: ComponentFixture<any>;
|
||||
@@ -101,7 +110,7 @@ export function createRelationshipsObservable() {
|
||||
})
|
||||
])));
|
||||
}
|
||||
describe('EntityComponent', () => {
|
||||
describe('ItemComponent', () => {
|
||||
const arr1 = [
|
||||
{
|
||||
id: 1,
|
||||
@@ -307,4 +316,116 @@ describe('EntityComponent', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('when calling buildRepresentations', () => {
|
||||
let comp: ItemComponent;
|
||||
let fixture: ComponentFixture<ItemComponent>;
|
||||
|
||||
const metadataField = 'dc.contributor.author';
|
||||
const mockItem = Object.assign(new Item(), {
|
||||
id: '1',
|
||||
uuid: '1',
|
||||
metadata: [
|
||||
{
|
||||
key: metadataField,
|
||||
value: 'Second value',
|
||||
place: 1
|
||||
},
|
||||
{
|
||||
key: metadataField,
|
||||
value: 'Third value',
|
||||
place: 2,
|
||||
authority: 'virtual::123'
|
||||
},
|
||||
{
|
||||
key: metadataField,
|
||||
value: 'First value',
|
||||
place: 0
|
||||
},
|
||||
{
|
||||
key: metadataField,
|
||||
value: 'Fourth value',
|
||||
place: 3,
|
||||
authority: '123'
|
||||
}
|
||||
],
|
||||
relationships: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), [
|
||||
Object.assign(new Relationship(), {
|
||||
uuid: '123',
|
||||
id: '123',
|
||||
leftId: '1',
|
||||
rightId: '2',
|
||||
relationshipType: observableOf(new RemoteData(false, false, true, null, new RelationshipType()))
|
||||
})
|
||||
])))
|
||||
});
|
||||
const relatedItem = Object.assign(new Item(), {
|
||||
id: '2',
|
||||
metadata: [
|
||||
{
|
||||
key: 'dc.title',
|
||||
value: 'related item'
|
||||
}
|
||||
]
|
||||
});
|
||||
const mockItemDataService = {
|
||||
findById: (id) => {
|
||||
if (id === relatedItem.id) {
|
||||
return observableOf(new RemoteData(false, false, true, null, relatedItem))
|
||||
}
|
||||
}
|
||||
} as ItemDataService;
|
||||
|
||||
let representations: Observable<MetadataRepresentation[]>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useClass: MockTranslateLoader
|
||||
}
|
||||
}), BrowserAnimationsModule],
|
||||
declarations: [ItemComponent, VarDirective],
|
||||
providers: [
|
||||
{provide: ITEM, useValue: mockItem}
|
||||
],
|
||||
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).overrideComponent(ItemComponent, {
|
||||
set: {changeDetection: ChangeDetectionStrategy.Default}
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(async(() => {
|
||||
fixture = TestBed.createComponent(ItemComponent);
|
||||
comp = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
representations = comp.buildRepresentations('bogus', metadataField, mockItemDataService);
|
||||
}));
|
||||
|
||||
it('should contain exactly 4 metadata-representations', () => {
|
||||
representations.subscribe((reps: MetadataRepresentation[]) => {
|
||||
expect(reps.length).toEqual(4);
|
||||
});
|
||||
});
|
||||
|
||||
it('should have all the representations in the correct order', () => {
|
||||
representations.subscribe((reps: MetadataRepresentation[]) => {
|
||||
expect(reps[0].getValue()).toEqual('First value');
|
||||
expect(reps[1].getValue()).toEqual('Second value');
|
||||
expect(reps[2].getValue()).toEqual('related item');
|
||||
expect(reps[3].getValue()).toEqual('Fourth value');
|
||||
});
|
||||
});
|
||||
|
||||
it('should have created the correct MetadatumRepresentation and ItemMetadataRepresentation objects for the correct Metadata', () => {
|
||||
representations.subscribe((reps: MetadataRepresentation[]) => {
|
||||
expect(reps[0] instanceof MetadatumRepresentation).toEqual(true);
|
||||
expect(reps[1] instanceof MetadatumRepresentation).toEqual(true);
|
||||
expect(reps[2] instanceof ItemMetadataRepresentation).toEqual(true);
|
||||
expect(reps[3] instanceof MetadatumRepresentation).toEqual(true);
|
||||
});
|
||||
});
|
||||
})
|
||||
|
||||
});
|
@@ -4,12 +4,17 @@ import { distinctUntilChanged, filter, flatMap, map } from 'rxjs/operators';
|
||||
import { ItemDataService } from '../../../../core/data/item-data.service';
|
||||
import { PaginatedList } from '../../../../core/data/paginated-list';
|
||||
import { RemoteData } from '../../../../core/data/remote-data';
|
||||
import { RelationshipType } from '../../../../core/shared/entities/relationship-type.model';
|
||||
import { Relationship } from '../../../../core/shared/entities/relationship.model';
|
||||
import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model';
|
||||
import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
|
||||
import { Item } from '../../../../core/shared/item.model';
|
||||
import { getRemoteDataPayload } from '../../../../core/shared/operators';
|
||||
import { hasValue } from '../../../../shared/empty.util';
|
||||
import { ITEM } from '../../../../shared/entities/switcher/entity-type-switcher.component';
|
||||
import { getRemoteDataPayload, getSucceededRemoteData } from '../../../../core/shared/operators';
|
||||
import { hasNoValue, hasValue } from '../../../../shared/empty.util';
|
||||
import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component';
|
||||
import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model';
|
||||
import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-representation/item/item-metadata-representation.model';
|
||||
import { Metadatum } from '../../../../core/shared/metadatum.model';
|
||||
import { MetadatumRepresentation } from '../../../../core/shared/metadata-representation/metadatum/metadatum-representation.model';
|
||||
import { of } from 'rxjs/internal/observable/of';
|
||||
|
||||
/**
|
||||
* Operator for comparing arrays using a mapping function
|
||||
@@ -82,18 +87,54 @@ export const relationsToItems = (thisId: string, ids: ItemDataService) =>
|
||||
distinctUntilChanged(compareArraysUsingIds()),
|
||||
);
|
||||
|
||||
/**
|
||||
* Operator for turning a list of relationships into a list of metadatarepresentations given the original metadata
|
||||
* @param thisId The id of the parent item
|
||||
* @param itemType The type of relation this list resembles (for creating representations)
|
||||
* @param metadata The list of original Metadatum objects
|
||||
* @param ids The ItemDataService to use for fetching Items from the Rest API
|
||||
*/
|
||||
export const relationsToRepresentations = (thisId: string, itemType: string, metadata: Metadatum[], ids: ItemDataService) =>
|
||||
(source: Observable<Relationship[]>): Observable<MetadataRepresentation[]> =>
|
||||
source.pipe(
|
||||
flatMap((rels: Relationship[]) =>
|
||||
observableZip(
|
||||
...metadata
|
||||
.map((metadatum: any) => Object.assign(new Metadatum(), metadatum))
|
||||
.map((metadatum: Metadatum) => {
|
||||
if (metadatum.isVirtual) {
|
||||
const matchingRels = rels.filter((rel: Relationship) => ('' + rel.id) === metadatum.virtualValue);
|
||||
if (matchingRels.length > 0) {
|
||||
const matchingRel = matchingRels[0];
|
||||
let queryId = matchingRel.leftId;
|
||||
if (matchingRel.leftId === thisId) {
|
||||
queryId = matchingRel.rightId;
|
||||
}
|
||||
return ids.findById(queryId).pipe(
|
||||
getSucceededRemoteData(),
|
||||
map((d: RemoteData<Item>) => Object.assign(new ItemMetadataRepresentation(itemType), d.payload))
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return of(Object.assign(new MetadatumRepresentation(itemType), metadatum));
|
||||
}
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
@Component({
|
||||
selector: 'ds-entity',
|
||||
selector: 'ds-item',
|
||||
template: ''
|
||||
})
|
||||
/**
|
||||
* A generic component for displaying metadata and relations of an item
|
||||
*/
|
||||
export class EntityComponent implements OnInit {
|
||||
export class ItemComponent implements OnInit {
|
||||
/**
|
||||
* Resolved relationships and types together in one observable
|
||||
*/
|
||||
resolvedRelsAndTypes$: Observable<[Relationship[], RelationshipType[]]>
|
||||
resolvedRelsAndTypes$: Observable<[Relationship[], RelationshipType[]]>;
|
||||
|
||||
constructor(
|
||||
@Inject(ITEM) public item: Item
|
||||
@@ -125,4 +166,25 @@ export class EntityComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a list of MetadataRepresentations for the current item. This combines all metadata and relationships of a
|
||||
* certain type.
|
||||
* @param itemType The type of item we're building representations of. Used for matching templates.
|
||||
* @param metadataField The metadata field that resembles the item type.
|
||||
* @param itemDataService ItemDataService to turn relations into items.
|
||||
*/
|
||||
buildRepresentations(itemType: string, metadataField: string, itemDataService: ItemDataService): Observable<MetadataRepresentation[]> {
|
||||
const metadata = this.item.findMetadataSortedByPlace(metadataField);
|
||||
const relsCurrentPage$ = this.item.relationships.pipe(
|
||||
getSucceededRemoteData(),
|
||||
getRemoteDataPayload(),
|
||||
map((pl: PaginatedList<Relationship>) => pl.page),
|
||||
distinctUntilChanged(compareArraysUsingIds())
|
||||
);
|
||||
|
||||
return relsCurrentPage$.pipe(
|
||||
relationsToRepresentations(this.item.id, itemType, metadata, itemDataService)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
<ds-metadata-field-wrapper *ngIf="representations && representations.length > 0" [label]="label">
|
||||
<ds-item-type-switcher *ngFor="let rep of representations"
|
||||
[object]="rep" [viewMode]="viewMode">
|
||||
</ds-item-type-switcher>
|
||||
</ds-metadata-field-wrapper>
|
@@ -0,0 +1,40 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { MetadataRepresentationListComponent } from './metadata-representation-list.component';
|
||||
import { MetadatumRepresentation } from '../../../core/shared/metadata-representation/metadatum/metadatum-representation.model';
|
||||
import { ItemMetadataRepresentation } from '../../../core/shared/metadata-representation/item/item-metadata-representation.model';
|
||||
|
||||
const itemType = 'type';
|
||||
const metadataRepresentation1 = new MetadatumRepresentation(itemType);
|
||||
const metadataRepresentation2 = new ItemMetadataRepresentation(itemType);
|
||||
const representations = [metadataRepresentation1, metadataRepresentation2];
|
||||
|
||||
describe('MetadataRepresentationListComponent', () => {
|
||||
let comp: MetadataRepresentationListComponent;
|
||||
let fixture: ComponentFixture<MetadataRepresentationListComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [],
|
||||
declarations: [MetadataRepresentationListComponent],
|
||||
providers: [],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).overrideComponent(MetadataRepresentationListComponent, {
|
||||
set: {changeDetection: ChangeDetectionStrategy.Default}
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(async(() => {
|
||||
fixture = TestBed.createComponent(MetadataRepresentationListComponent);
|
||||
comp = fixture.componentInstance;
|
||||
comp.representations = representations;
|
||||
fixture.detectChanges();
|
||||
}));
|
||||
|
||||
it(`should load ${representations.length} item-type-switcher components`, () => {
|
||||
const fields = fixture.debugElement.queryAll(By.css('ds-item-type-switcher'));
|
||||
expect(fields.length).toBe(representations.length);
|
||||
});
|
||||
|
||||
});
|
@@ -0,0 +1,31 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import * as viewMode from '../../../shared/view-mode';
|
||||
import { MetadataRepresentation } from '../../../core/shared/metadata-representation/metadata-representation.model';
|
||||
|
||||
export const VIEW_MODE_METADATA = 'metadata';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-metadata-representation-list',
|
||||
templateUrl: './metadata-representation-list.component.html'
|
||||
})
|
||||
/**
|
||||
* This component is used for displaying metadata
|
||||
* It expects a list of MetadataRepresentation objects and a label to put on top of the list
|
||||
*/
|
||||
export class MetadataRepresentationListComponent {
|
||||
/**
|
||||
* A list of metadata-representations to display
|
||||
*/
|
||||
@Input() representations: MetadataRepresentation[];
|
||||
|
||||
/**
|
||||
* An i18n label to use as a title for the list
|
||||
*/
|
||||
@Input() label: string;
|
||||
|
||||
/**
|
||||
* The view-mode we're currently on
|
||||
* @type {ElementViewMode}
|
||||
*/
|
||||
viewMode = VIEW_MODE_METADATA;
|
||||
}
|
@@ -1,30 +0,0 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import * as viewMode from '../../../shared/view-mode';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-related-entities',
|
||||
styleUrls: ['./related-entities.component.scss'],
|
||||
templateUrl: './related-entities.component.html'
|
||||
})
|
||||
/**
|
||||
* This component is used for displaying relations between entities
|
||||
* It expects a list of entities to display and a label to put on top
|
||||
*/
|
||||
export class RelatedEntitiesComponent {
|
||||
/**
|
||||
* A list of entities to display
|
||||
*/
|
||||
@Input() entities: Item[];
|
||||
|
||||
/**
|
||||
* An i18n label to use as a title for the list (usually describes the relation)
|
||||
*/
|
||||
@Input() label: string;
|
||||
|
||||
/**
|
||||
* The view-mode we're currently on
|
||||
* @type {ElementViewMode}
|
||||
*/
|
||||
ElementViewMode = viewMode.ElementViewMode
|
||||
}
|
@@ -1,5 +0,0 @@
|
||||
<ds-metadata-field-wrapper *ngIf="entities && entities.length > 0" [label]="label">
|
||||
<ds-entity-type-switcher *ngFor="let entity of entities"
|
||||
[object]="entity" [viewMode]="ElementViewMode.SetElement">
|
||||
</ds-entity-type-switcher>
|
||||
</ds-metadata-field-wrapper>
|
@@ -0,0 +1,31 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
|
||||
export const VIEW_MODE_ELEMENT = 'element';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-related-items',
|
||||
styleUrls: ['./related-items.component.scss'],
|
||||
templateUrl: './related-items.component.html'
|
||||
})
|
||||
/**
|
||||
* This component is used for displaying relations between items
|
||||
* It expects a list of items to display and a label to put on top
|
||||
*/
|
||||
export class RelatedItemsComponent {
|
||||
/**
|
||||
* A list of items to display
|
||||
*/
|
||||
@Input() items: Item[];
|
||||
|
||||
/**
|
||||
* An i18n label to use as a title for the list (usually describes the relation)
|
||||
*/
|
||||
@Input() label: string;
|
||||
|
||||
/**
|
||||
* The view-mode we're currently on
|
||||
* @type {ElementViewMode}
|
||||
*/
|
||||
viewMode = VIEW_MODE_ELEMENT;
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
<ds-metadata-field-wrapper *ngIf="items && items.length > 0" [label]="label">
|
||||
<ds-item-type-switcher *ngFor="let item of items"
|
||||
[object]="item" [viewMode]="viewMode">
|
||||
</ds-item-type-switcher>
|
||||
</ds-metadata-field-wrapper>
|
@@ -1,12 +1,12 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { RelatedEntitiesComponent } from './related-entities-component';
|
||||
import { RelatedItemsComponent } from './related-items-component';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { PaginatedList } from '../../../core/data/paginated-list';
|
||||
import { PageInfo } from '../../../core/shared/page-info.model';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { createRelationshipsObservable } from '../entity-types/shared/entity.component.spec';
|
||||
import { createRelationshipsObservable } from '../item-types/shared/item.component.spec';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
|
||||
const mockItem1: Item = Object.assign(new Item(), {
|
||||
@@ -19,33 +19,33 @@ const mockItem2: Item = Object.assign(new Item(), {
|
||||
metadata: [],
|
||||
relationships: createRelationshipsObservable()
|
||||
});
|
||||
const mockEntities = [mockItem1, mockItem2];
|
||||
const mockItems = [mockItem1, mockItem2];
|
||||
|
||||
describe('RelatedEntitiesComponent', () => {
|
||||
let comp: RelatedEntitiesComponent;
|
||||
let fixture: ComponentFixture<RelatedEntitiesComponent>;
|
||||
describe('RelatedItemsComponent', () => {
|
||||
let comp: RelatedItemsComponent;
|
||||
let fixture: ComponentFixture<RelatedItemsComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [],
|
||||
declarations: [RelatedEntitiesComponent],
|
||||
declarations: [RelatedItemsComponent],
|
||||
providers: [],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).overrideComponent(RelatedEntitiesComponent, {
|
||||
}).overrideComponent(RelatedItemsComponent, {
|
||||
set: {changeDetection: ChangeDetectionStrategy.Default}
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(async(() => {
|
||||
fixture = TestBed.createComponent(RelatedEntitiesComponent);
|
||||
fixture = TestBed.createComponent(RelatedItemsComponent);
|
||||
comp = fixture.componentInstance;
|
||||
comp.entities = mockEntities;
|
||||
comp.items = mockItems;
|
||||
fixture.detectChanges();
|
||||
}));
|
||||
|
||||
it(`should load ${mockEntities.length} entity-type-switcher components`, () => {
|
||||
const fields = fixture.debugElement.queryAll(By.css('ds-entity-type-switcher'));
|
||||
expect(fields.length).toBe(mockEntities.length);
|
||||
it(`should load ${mockItems.length} item-type-switcher components`, () => {
|
||||
const fields = fixture.debugElement.queryAll(By.css('ds-item-type-switcher'));
|
||||
expect(fields.length).toBe(mockItems.length);
|
||||
});
|
||||
|
||||
});
|
@@ -1,32 +1,32 @@
|
||||
import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize';
|
||||
import { EntityType } from '../../../shared/entities/entity-type.model';
|
||||
import { ItemType } from '../../../shared/item-relationships/item-type.model';
|
||||
import { ResourceType } from '../../../shared/resource-type';
|
||||
import { mapsTo } from '../../builders/build-decorators';
|
||||
import { NormalizedObject } from '../normalized-object.model';
|
||||
import { IDToUUIDSerializer } from '../../id-to-uuid-serializer';
|
||||
|
||||
/**
|
||||
* Normalized model class for a DSpace EntityType
|
||||
* Normalized model class for a DSpace ItemType
|
||||
*/
|
||||
@mapsTo(EntityType)
|
||||
@mapsTo(ItemType)
|
||||
@inheritSerialization(NormalizedObject)
|
||||
export class NormalizedEntityType extends NormalizedObject {
|
||||
export class NormalizedItemType extends NormalizedObject {
|
||||
|
||||
/**
|
||||
* The label that describes the ResourceType of the Entity
|
||||
* The label that describes the ResourceType of the Item
|
||||
*/
|
||||
@autoserialize
|
||||
label: string;
|
||||
|
||||
/**
|
||||
* The identifier of this EntityType
|
||||
* The identifier of this ItemType
|
||||
*/
|
||||
@autoserialize
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* The universally unique identifier of this EntityType
|
||||
* The universally unique identifier of this ItemType
|
||||
*/
|
||||
@autoserializeAs(new IDToUUIDSerializer(ResourceType.EntityType), 'id')
|
||||
@autoserializeAs(new IDToUUIDSerializer(ResourceType.ItemType), 'id')
|
||||
uuid: string;
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize';
|
||||
import { RelationshipType } from '../../../shared/entities/relationship-type.model';
|
||||
import { RelationshipType } from '../../../shared/item-relationships/relationship-type.model';
|
||||
import { ResourceType } from '../../../shared/resource-type';
|
||||
import { mapsTo, relationship } from '../../builders/build-decorators';
|
||||
import { NormalizedDSpaceObject } from '../normalized-dspace-object.model';
|
||||
@@ -56,17 +56,17 @@ export class NormalizedRelationshipType extends NormalizedObject {
|
||||
rightMinCardinality: number;
|
||||
|
||||
/**
|
||||
* The type of Entity found to the left of this RelationshipType
|
||||
* The type of Item found to the left of this RelationshipType
|
||||
*/
|
||||
@autoserialize
|
||||
@relationship(ResourceType.EntityType, false)
|
||||
@relationship(ResourceType.ItemType, false)
|
||||
leftType: string;
|
||||
|
||||
/**
|
||||
* The type of Entity found to the right of this RelationshipType
|
||||
* The type of Item found to the right of this RelationshipType
|
||||
*/
|
||||
@autoserialize
|
||||
@relationship(ResourceType.EntityType, false)
|
||||
@relationship(ResourceType.ItemType, false)
|
||||
rightType: string;
|
||||
|
||||
/**
|
@@ -1,5 +1,5 @@
|
||||
import { autoserialize, autoserializeAs, inheritSerialization } from 'cerialize';
|
||||
import { Relationship } from '../../../shared/entities/relationship.model';
|
||||
import { Relationship } from '../../../shared/item-relationships/relationship.model';
|
||||
import { ResourceType } from '../../../shared/resource-type';
|
||||
import { mapsTo, relationship } from '../../builders/build-decorators';
|
||||
import { NormalizedObject } from '../normalized-object.model';
|
||||
@@ -19,25 +19,25 @@ export class NormalizedRelationship extends NormalizedObject {
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* The identifier of the Entity to the left side of this Relationship
|
||||
* The identifier of the Item to the left side of this Relationship
|
||||
*/
|
||||
@autoserialize
|
||||
leftId: string;
|
||||
|
||||
/**
|
||||
* The identifier of the Entity to the right side of this Relationship
|
||||
* The identifier of the Item to the right side of this Relationship
|
||||
*/
|
||||
@autoserialize
|
||||
rightId: string;
|
||||
|
||||
/**
|
||||
* The place of the Entity to the left side of this Relationship
|
||||
* The place of the Item to the left side of this Relationship
|
||||
*/
|
||||
@autoserialize
|
||||
leftPlace: number;
|
||||
|
||||
/**
|
||||
* The place of the Entity to the right side of this Relationship
|
||||
* The place of the Item to the right side of this Relationship
|
||||
*/
|
||||
@autoserialize
|
||||
rightPlace: number;
|
@@ -1,6 +1,6 @@
|
||||
import { NormalizedEntityType } from './entities/normalized-entity-type.model';
|
||||
import { NormalizedRelationshipType } from './entities/normalized-relationship-type.model';
|
||||
import { NormalizedRelationship } from './entities/normalized-relationship.model';
|
||||
import { NormalizedItemType } from './items/normalized-item-type.model';
|
||||
import { NormalizedRelationshipType } from './items/normalized-relationship-type.model';
|
||||
import { NormalizedRelationship } from './items/normalized-relationship.model';
|
||||
import { NormalizedBitstream } from './normalized-bitstream.model';
|
||||
import { NormalizedBundle } from './normalized-bundle.model';
|
||||
import { NormalizedItem } from './normalized-item.model';
|
||||
@@ -44,8 +44,8 @@ export class NormalizedObjectFactory {
|
||||
case ResourceType.RelationshipType: {
|
||||
return NormalizedRelationshipType
|
||||
}
|
||||
case ResourceType.EntityType: {
|
||||
return NormalizedEntityType
|
||||
case ResourceType.ItemType: {
|
||||
return NormalizedItemType
|
||||
}
|
||||
case ResourceType.EPerson: {
|
||||
return NormalizedEPerson
|
||||
|
@@ -33,6 +33,7 @@ import { MockTranslateLoader } from '../../shared/mocks/mock-translate-loader';
|
||||
import { BrowseService } from '../browse/browse.service';
|
||||
import { HALEndpointService } from '../shared/hal-endpoint.service';
|
||||
import { EmptyError } from 'rxjs/internal-compatibility';
|
||||
import { Metadatum } from '../shared/metadatum.model';
|
||||
|
||||
/* tslint:disable:max-classes-per-file */
|
||||
@Component({
|
||||
@@ -223,7 +224,7 @@ describe('MetadataService', () => {
|
||||
key: 'dc.publisher',
|
||||
language: 'en_US',
|
||||
value: 'Mock Publisher'
|
||||
});
|
||||
} as Metadatum);
|
||||
return publishedMockItem;
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { Metadatum } from './metadatum.model'
|
||||
import { isEmpty, isNotEmpty } from '../../shared/empty.util';
|
||||
import { hasNoValue, isEmpty, isNotEmpty } from '../../shared/empty.util';
|
||||
import { CacheableObject } from '../cache/object-cache.reducer';
|
||||
import { RemoteData } from '../data/remote-data';
|
||||
import { ResourceType } from './resource-type';
|
||||
@@ -91,4 +91,23 @@ export class DSpaceObject implements CacheableObject, ListableObject {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Find metadata on a specific field and order all of them using their "place" property.
|
||||
* @param key
|
||||
*/
|
||||
findMetadataSortedByPlace(key: string): Metadatum[] {
|
||||
return this.filterMetadata([key]).sort((a: Metadatum, b: Metadatum) => {
|
||||
if (hasNoValue(a.place) && hasNoValue(b.place)) {
|
||||
return 0;
|
||||
}
|
||||
if (hasNoValue(a.place)) {
|
||||
return -1;
|
||||
}
|
||||
if (hasNoValue(b.place)) {
|
||||
return 1;
|
||||
}
|
||||
return a.place - b.place;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -2,11 +2,11 @@ import { CacheableObject } from '../../cache/object-cache.reducer';
|
||||
import { ResourceType } from '../resource-type';
|
||||
|
||||
/**
|
||||
* Describes a type of Entity
|
||||
* Describes a type of Item
|
||||
*/
|
||||
export class EntityType implements CacheableObject {
|
||||
export class ItemType implements CacheableObject {
|
||||
/**
|
||||
* The identifier of this EntityType
|
||||
* The identifier of this ItemType
|
||||
*/
|
||||
id: string;
|
||||
|
||||
@@ -21,7 +21,7 @@ export class EntityType implements CacheableObject {
|
||||
type: ResourceType;
|
||||
|
||||
/**
|
||||
* The universally unique identifier of this EntityType
|
||||
* The universally unique identifier of this ItemType
|
||||
*/
|
||||
uuid: string;
|
||||
}
|
@@ -2,10 +2,10 @@ import { Observable } from 'rxjs';
|
||||
import { CacheableObject } from '../../cache/object-cache.reducer';
|
||||
import { RemoteData } from '../../data/remote-data';
|
||||
import { ResourceType } from '../resource-type';
|
||||
import { EntityType } from './entity-type.model';
|
||||
import { ItemType } from './item-type.model';
|
||||
|
||||
/**
|
||||
* Describes a type of Relationship between multiple possible Entities
|
||||
* Describes a type of Relationship between multiple possible Items
|
||||
*/
|
||||
export class RelationshipType implements CacheableObject {
|
||||
/**
|
||||
@@ -64,12 +64,12 @@ export class RelationshipType implements CacheableObject {
|
||||
rightMinCardinality: number;
|
||||
|
||||
/**
|
||||
* The type of Entity found to the left of this RelationshipType
|
||||
* The type of Item found to the left of this RelationshipType
|
||||
*/
|
||||
leftType: Observable<RemoteData<EntityType>>;
|
||||
leftType: Observable<RemoteData<ItemType>>;
|
||||
|
||||
/**
|
||||
* The type of Entity found to the right of this RelationshipType
|
||||
* The type of Item found to the right of this RelationshipType
|
||||
*/
|
||||
rightType: Observable<RemoteData<EntityType>>;
|
||||
rightType: Observable<RemoteData<ItemType>>;
|
||||
}
|
@@ -5,7 +5,7 @@ import { ResourceType } from '../resource-type';
|
||||
import { RelationshipType } from './relationship-type.model';
|
||||
|
||||
/**
|
||||
* Describes a Relationship between two Entities
|
||||
* Describes a Relationship between two Items
|
||||
*/
|
||||
export class Relationship implements CacheableObject {
|
||||
/**
|
||||
@@ -29,22 +29,22 @@ export class Relationship implements CacheableObject {
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* The identifier of the Entity to the left side of this Relationship
|
||||
* The identifier of the Item to the left side of this Relationship
|
||||
*/
|
||||
leftId: string;
|
||||
|
||||
/**
|
||||
* The identifier of the Entity to the right side of this Relationship
|
||||
* The identifier of the Item to the right side of this Relationship
|
||||
*/
|
||||
rightId: string;
|
||||
|
||||
/**
|
||||
* The place of the Entity to the left side of this Relationship
|
||||
* The place of the Item to the left side of this Relationship
|
||||
*/
|
||||
leftPlace: number;
|
||||
|
||||
/**
|
||||
* The place of the Entity to the right side of this Relationship
|
||||
* The place of the Item to the right side of this Relationship
|
||||
*/
|
||||
rightPlace: number;
|
||||
|
@@ -7,7 +7,7 @@ import { RemoteData } from '../data/remote-data';
|
||||
import { Bitstream } from './bitstream.model';
|
||||
import { hasValue, isNotEmpty } from '../../shared/empty.util';
|
||||
import { PaginatedList } from '../data/paginated-list';
|
||||
import { Relationship } from './entities/relationship.model';
|
||||
import { Relationship } from './item-relationships/relationship.model';
|
||||
|
||||
export class Item extends DSpaceObject {
|
||||
|
||||
|
@@ -0,0 +1,36 @@
|
||||
import { MetadataRepresentationType } from '../metadata-representation.model';
|
||||
import { ItemMetadataRepresentation, ItemTypeToValue } from './item-metadata-representation.model';
|
||||
import { Item } from '../../item.model';
|
||||
import { Metadatum } from '../../metadatum.model';
|
||||
|
||||
describe('ItemMetadataRepresentation', () => {
|
||||
const valuePrefix = 'Test value for ';
|
||||
const item = new Item();
|
||||
let itemMetadataRepresentation: ItemMetadataRepresentation;
|
||||
item.metadata = Object.keys(ItemTypeToValue).map((key: string) => {
|
||||
return Object.assign(new Metadatum(), {
|
||||
key: ItemTypeToValue[key],
|
||||
value: `${valuePrefix}${ItemTypeToValue[key]}`
|
||||
});
|
||||
});
|
||||
|
||||
for (const itemType of Object.keys(ItemTypeToValue)) {
|
||||
describe(`when creating an ItemMetadataRepresentation with item-type "${itemType}"`, () => {
|
||||
beforeEach(() => {
|
||||
itemMetadataRepresentation = Object.assign(new ItemMetadataRepresentation(itemType), item);
|
||||
});
|
||||
|
||||
it('should have a representation type of item', () => {
|
||||
expect(itemMetadataRepresentation.representationType).toEqual(MetadataRepresentationType.Item);
|
||||
});
|
||||
|
||||
it('should return the correct value when calling getValue', () => {
|
||||
expect(itemMetadataRepresentation.getValue()).toEqual(`${valuePrefix}${ItemTypeToValue[itemType]}`);
|
||||
});
|
||||
|
||||
it('should return the correct item type', () => {
|
||||
expect(itemMetadataRepresentation.itemType).toEqual(itemType);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
@@ -0,0 +1,48 @@
|
||||
import { Item } from '../../item.model';
|
||||
import { MetadataRepresentation, MetadataRepresentationType } from '../metadata-representation.model';
|
||||
import { hasValue } from '../../../../shared/empty.util';
|
||||
|
||||
/**
|
||||
* An object to convert item types into the metadata field it should render for the item's value
|
||||
*/
|
||||
export const ItemTypeToValue = {
|
||||
Default: 'dc.title',
|
||||
Person: 'dc.contributor.author'
|
||||
};
|
||||
|
||||
/**
|
||||
* This class determines which fields to use when rendering an Item as a metadata value.
|
||||
*/
|
||||
export class ItemMetadataRepresentation extends Item implements MetadataRepresentation {
|
||||
|
||||
/**
|
||||
* The type of item this item can be represented as
|
||||
*/
|
||||
itemType: string;
|
||||
|
||||
constructor(itemType: string) {
|
||||
super();
|
||||
this.itemType = itemType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the way this item should be rendered as in a list
|
||||
*/
|
||||
get representationType(): MetadataRepresentationType {
|
||||
return MetadataRepresentationType.Item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value to display, depending on the itemType
|
||||
*/
|
||||
getValue(): string {
|
||||
let metadata;
|
||||
if (hasValue(ItemTypeToValue[this.itemType])) {
|
||||
metadata = ItemTypeToValue[this.itemType];
|
||||
} else {
|
||||
metadata = ItemTypeToValue.Default;
|
||||
}
|
||||
return this.findMetadata(metadata);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* An Enum defining the representation type of metadata
|
||||
*/
|
||||
export enum MetadataRepresentationType {
|
||||
None = 'none',
|
||||
Item = 'item',
|
||||
AuthorityControlled = 'authority_controlled',
|
||||
PlainText = 'plain_text'
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface containing information about how we should represent certain metadata
|
||||
*/
|
||||
export interface MetadataRepresentation {
|
||||
/**
|
||||
* The type of item this metadata is representing
|
||||
* e.g. 'Person'
|
||||
* This can be used for template matching
|
||||
*/
|
||||
itemType: string;
|
||||
|
||||
/**
|
||||
* How we should render the metadata in a list
|
||||
*/
|
||||
representationType: MetadataRepresentationType,
|
||||
|
||||
/**
|
||||
* Fetches the value to be displayed
|
||||
*/
|
||||
getValue(): string
|
||||
}
|
@@ -0,0 +1,54 @@
|
||||
import { Metadatum } from '../../metadatum.model';
|
||||
import { MetadatumRepresentation } from './metadatum-representation.model';
|
||||
import { MetadataRepresentationType } from '../metadata-representation.model';
|
||||
|
||||
describe('MetadatumRepresentation', () => {
|
||||
const itemType = 'Person';
|
||||
const normalMetadatum = Object.assign(new Metadatum(), {
|
||||
key: 'dc.contributor.author',
|
||||
value: 'Test Author'
|
||||
});
|
||||
const authorityMetadatum = Object.assign(new Metadatum(), {
|
||||
key: 'dc.contributor.author',
|
||||
value: 'Test Authority Author',
|
||||
authority: '1234'
|
||||
});
|
||||
|
||||
let metadatumRepresentation: MetadatumRepresentation;
|
||||
|
||||
describe('when creating a MetadatumRepresentation based on a standard Metadatum object', () => {
|
||||
beforeEach(() => {
|
||||
metadatumRepresentation = Object.assign(new MetadatumRepresentation(itemType), normalMetadatum);
|
||||
});
|
||||
|
||||
it('should have a representation type of plain text', () => {
|
||||
expect(metadatumRepresentation.representationType).toEqual(MetadataRepresentationType.PlainText);
|
||||
});
|
||||
|
||||
it('should return the correct value when calling getPrimaryValue', () => {
|
||||
expect(metadatumRepresentation.getValue()).toEqual(normalMetadatum.value);
|
||||
});
|
||||
|
||||
it('should return the correct item type', () => {
|
||||
expect(metadatumRepresentation.itemType).toEqual(itemType);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when creating a MetadatumRepresentation based on an authority controlled Metadatum object', () => {
|
||||
beforeEach(() => {
|
||||
metadatumRepresentation = Object.assign(new MetadatumRepresentation(itemType), authorityMetadatum);
|
||||
});
|
||||
|
||||
it('should have a representation type of plain text', () => {
|
||||
expect(metadatumRepresentation.representationType).toEqual(MetadataRepresentationType.AuthorityControlled);
|
||||
});
|
||||
|
||||
it('should return the correct value when calling getValue', () => {
|
||||
expect(metadatumRepresentation.getValue()).toEqual(authorityMetadatum.value);
|
||||
});
|
||||
|
||||
it('should return the correct item type', () => {
|
||||
expect(metadatumRepresentation.itemType).toEqual(itemType);
|
||||
});
|
||||
});
|
||||
});
|
@@ -0,0 +1,38 @@
|
||||
import { Metadatum } from '../../metadatum.model';
|
||||
import { MetadataRepresentation, MetadataRepresentationType } from '../metadata-representation.model';
|
||||
import { hasValue } from '../../../../shared/empty.util';
|
||||
|
||||
/**
|
||||
* This class defines the way the metadatum it extends should be represented
|
||||
*/
|
||||
export class MetadatumRepresentation extends Metadatum implements MetadataRepresentation {
|
||||
|
||||
/**
|
||||
* The type of item this metadatum can be represented as
|
||||
*/
|
||||
itemType: string;
|
||||
|
||||
constructor(itemType: string) {
|
||||
super();
|
||||
this.itemType = itemType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the way this metadatum should be rendered as in a list
|
||||
*/
|
||||
get representationType(): MetadataRepresentationType {
|
||||
if (hasValue(this.authority)) {
|
||||
return MetadataRepresentationType.AuthorityControlled;
|
||||
} else {
|
||||
return MetadataRepresentationType.PlainText;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value to display
|
||||
*/
|
||||
getValue(): string {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
}
|
67
src/app/core/shared/metadatum.model.spec.ts
Normal file
67
src/app/core/shared/metadatum.model.spec.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { Metadatum } from './metadatum.model';
|
||||
|
||||
describe('Metadatum', () => {
|
||||
let metadatum: Metadatum ;
|
||||
|
||||
beforeEach(() => {
|
||||
metadatum = new Metadatum();
|
||||
});
|
||||
|
||||
describe('isVirtual', () => {
|
||||
describe('when the metadatum has no authority key', () => {
|
||||
beforeEach(() => {
|
||||
metadatum.authority = undefined;
|
||||
});
|
||||
|
||||
it('should return false', () => {
|
||||
expect(metadatum.isVirtual).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the metadatum has an authority key', () => {
|
||||
describe('but it doesn\'t start with the virtual prefix', () => {
|
||||
beforeEach(() => {
|
||||
metadatum.authority = 'value';
|
||||
});
|
||||
|
||||
it('should return false', () => {
|
||||
expect(metadatum.isVirtual).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('and it starts with the virtual prefix', () => {
|
||||
beforeEach(() => {
|
||||
metadatum.authority = 'virtual::value';
|
||||
});
|
||||
|
||||
it('should return true', () => {
|
||||
expect(metadatum.isVirtual).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('virtualValue', () => {
|
||||
describe('when the metadatum isn\'t virtual', () => {
|
||||
beforeEach(() => {
|
||||
metadatum.authority = 'value';
|
||||
});
|
||||
|
||||
it('should return undefined', () => {
|
||||
expect(metadatum.virtualValue).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the metadatum is virtual', () => {
|
||||
beforeEach(() => {
|
||||
metadatum.authority = 'virtual::value';
|
||||
});
|
||||
|
||||
it('should return everything in the authority key after virtual::', () => {
|
||||
expect(metadatum.virtualValue).toBe('value');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,4 +1,7 @@
|
||||
import { autoserialize } from 'cerialize';
|
||||
import { hasValue } from '../../shared/empty.util';
|
||||
|
||||
const VIRTUAL_METADATA_PREFIX = 'virtual::';
|
||||
|
||||
export class Metadatum {
|
||||
|
||||
@@ -20,4 +23,42 @@ export class Metadatum {
|
||||
@autoserialize
|
||||
value: string;
|
||||
|
||||
/**
|
||||
* The place of this Metadatum within his list of metadata
|
||||
* This is used to render metadata in a specific custom order
|
||||
*/
|
||||
@autoserialize
|
||||
place: number;
|
||||
|
||||
/**
|
||||
* The authority key used for authority-controlled metadata
|
||||
*/
|
||||
@autoserialize
|
||||
authority: string;
|
||||
|
||||
/**
|
||||
* The authority confidence value
|
||||
*/
|
||||
@autoserialize
|
||||
confidence: number;
|
||||
|
||||
/**
|
||||
* Returns true if this Metadatum's authority key starts with 'virtual::'
|
||||
*/
|
||||
get isVirtual(): boolean {
|
||||
return hasValue(this.authority) && this.authority.startsWith(VIRTUAL_METADATA_PREFIX);
|
||||
}
|
||||
|
||||
/**
|
||||
* If this is a virtual Metadatum, it returns everything in the authority key after 'virtual::'.
|
||||
* Returns undefined otherwise.
|
||||
*/
|
||||
get virtualValue(): string {
|
||||
if (this.isVirtual) {
|
||||
return this.authority.substring(this.authority.indexOf(VIRTUAL_METADATA_PREFIX) + VIRTUAL_METADATA_PREFIX.length);
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -11,5 +11,5 @@ export enum ResourceType {
|
||||
ResourcePolicy = 'resourcePolicy',
|
||||
Relationship = 'relationship',
|
||||
RelationshipType = 'relationshiptype',
|
||||
EntityType = 'entitytype',
|
||||
ItemType = 'entitytype',
|
||||
}
|
||||
|
@@ -1,36 +0,0 @@
|
||||
import { hasNoValue, hasValue } from '../empty.util';
|
||||
import { ElementViewMode } from '../view-mode';
|
||||
|
||||
export const DEFAULT_ENTITY_TYPE = 'Default';
|
||||
|
||||
const map = new Map();
|
||||
|
||||
/**
|
||||
* Decorator used for rendering simple item pages for an Entity by type and viewMode
|
||||
* @param type
|
||||
* @param viewMode
|
||||
*/
|
||||
export function rendersEntityType(type: string, viewMode: ElementViewMode) {
|
||||
return function decorator(component: any) {
|
||||
if (hasNoValue(map.get(viewMode))) {
|
||||
map.set(viewMode, new Map());
|
||||
}
|
||||
if (hasValue(map.get(viewMode).get(type))) {
|
||||
throw new Error(`There can't be more than one component to render Items of type "${type}" in view mode "${viewMode}"`);
|
||||
}
|
||||
map.get(viewMode).set(type, component);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the component used for rendering an entity by type and viewMode
|
||||
* @param type
|
||||
* @param viewMode
|
||||
*/
|
||||
export function getComponentByEntityType(type: string, viewMode: ElementViewMode) {
|
||||
let component = map.get(viewMode).get(type);
|
||||
if (hasNoValue(component)) {
|
||||
component = map.get(viewMode).get(DEFAULT_ENTITY_TYPE);
|
||||
}
|
||||
return component;
|
||||
}
|
@@ -1,60 +0,0 @@
|
||||
import { EntityTypeSwitcherComponent } from './entity-type-switcher.component';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { PageInfo } from '../../../core/shared/page-info.model';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { PaginatedList } from '../../../core/data/paginated-list';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import * as decorator from '../entity-type-decorator';
|
||||
import { getComponentByEntityType } from '../entity-type-decorator';
|
||||
import { ElementViewMode } from '../../view-mode';
|
||||
import createSpy = jasmine.createSpy;
|
||||
|
||||
const relationType = 'type';
|
||||
const mockItem: Item = Object.assign(new Item(), {
|
||||
bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))),
|
||||
metadata: [
|
||||
{
|
||||
key: 'dc.title',
|
||||
language: 'en_US',
|
||||
value: 'test item'
|
||||
},
|
||||
{
|
||||
key: 'relationship.type',
|
||||
language: 'en_US',
|
||||
value: relationType
|
||||
}]
|
||||
});
|
||||
const viewMode = ElementViewMode.Full;
|
||||
|
||||
describe('EntityTypeSwitcherComponent', () => {
|
||||
let comp: EntityTypeSwitcherComponent;
|
||||
let fixture: ComponentFixture<EntityTypeSwitcherComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ EntityTypeSwitcherComponent ],
|
||||
schemas: [ NO_ERRORS_SCHEMA ]
|
||||
}).compileComponents(); // compile template and css
|
||||
}));
|
||||
|
||||
beforeEach(async(() => {
|
||||
fixture = TestBed.createComponent(EntityTypeSwitcherComponent);
|
||||
comp = fixture.componentInstance;
|
||||
comp.object = mockItem;
|
||||
comp.viewMode = viewMode;
|
||||
spyOnProperty(decorator, 'getComponentByEntityType').and.returnValue(createSpy('getComponentByEntityType'))
|
||||
}));
|
||||
|
||||
describe('when calling getComponent', () => {
|
||||
beforeEach(() => {
|
||||
comp.getComponent();
|
||||
});
|
||||
|
||||
it('should call getComponentByEntityType with parameters type and viewMode', () => {
|
||||
expect(decorator.getComponentByEntityType).toHaveBeenCalledWith(relationType, viewMode);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
60
src/app/shared/items/item-type-decorator.ts
Normal file
60
src/app/shared/items/item-type-decorator.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { hasNoValue, hasValue } from '../empty.util';
|
||||
import { MetadataRepresentationType } from '../../core/shared/metadata-representation/metadata-representation.model';
|
||||
import { VIEW_MODE_ELEMENT } from '../../+item-page/simple/related-items/related-items-component';
|
||||
|
||||
export const DEFAULT_ITEM_TYPE = 'Default';
|
||||
export const DEFAULT_VIEW_MODE = VIEW_MODE_ELEMENT;
|
||||
export const NO_REPRESENTATION_TYPE = MetadataRepresentationType.None;
|
||||
export const DEFAULT_REPRESENTATION_TYPE = MetadataRepresentationType.PlainText;
|
||||
|
||||
const map = new Map();
|
||||
|
||||
/**
|
||||
* Decorator used for rendering simple item pages by type and viewMode (and optionally a representationType)
|
||||
* @param type
|
||||
* @param viewMode
|
||||
* @param representationType
|
||||
*/
|
||||
export function rendersItemType(type: string, viewMode: string, representationType?: MetadataRepresentationType) {
|
||||
return function decorator(component: any) {
|
||||
if (hasNoValue(map.get(viewMode))) {
|
||||
map.set(viewMode, new Map());
|
||||
}
|
||||
if (hasNoValue(map.get(viewMode).get(type))) {
|
||||
map.get(viewMode).set(type, new Map());
|
||||
}
|
||||
if (hasNoValue(representationType)) {
|
||||
representationType = NO_REPRESENTATION_TYPE;
|
||||
}
|
||||
if (hasValue(map.get(viewMode).get(type).get(representationType))) {
|
||||
throw new Error(`There can't be more than one component to render Metadata of type "${type}" in view mode "${viewMode}" with representation type "${representationType}"`);
|
||||
}
|
||||
map.get(viewMode).get(type).set(representationType, component);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the component used for rendering an item by type and viewMode (and optionally a representationType)
|
||||
* @param type
|
||||
* @param viewMode
|
||||
* @param representationType
|
||||
*/
|
||||
export function getComponentByItemType(type: string, viewMode: string, representationType?: MetadataRepresentationType) {
|
||||
if (hasNoValue(representationType)) {
|
||||
representationType = NO_REPRESENTATION_TYPE;
|
||||
}
|
||||
if (hasNoValue(map.get(viewMode))) {
|
||||
viewMode = DEFAULT_VIEW_MODE;
|
||||
}
|
||||
if (hasNoValue(map.get(viewMode).get(type))) {
|
||||
type = DEFAULT_ITEM_TYPE;
|
||||
}
|
||||
let representationComponent = map.get(viewMode).get(type).get(representationType);
|
||||
if (hasNoValue(representationComponent)) {
|
||||
representationComponent = map.get(viewMode).get(type).get(DEFAULT_REPRESENTATION_TYPE);
|
||||
}
|
||||
if (hasNoValue(representationComponent)) {
|
||||
representationComponent = map.get(viewMode).get(type).get(NO_REPRESENTATION_TYPE);
|
||||
}
|
||||
return representationComponent;
|
||||
}
|
@@ -0,0 +1,89 @@
|
||||
import { ItemTypeSwitcherComponent } from './item-type-switcher.component';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { PageInfo } from '../../../core/shared/page-info.model';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { PaginatedList } from '../../../core/data/paginated-list';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import * as decorator from '../item-type-decorator';
|
||||
import { getComponentByItemType } from '../item-type-decorator';
|
||||
import { ItemMetadataRepresentation } from '../../../core/shared/metadata-representation/item/item-metadata-representation.model';
|
||||
import createSpy = jasmine.createSpy;
|
||||
import { VIEW_MODE_FULL } from '../../../+item-page/simple/item-page.component';
|
||||
import { VIEW_MODE_METADATA } from '../../../+item-page/simple/metadata-representation-list/metadata-representation-list.component';
|
||||
|
||||
const relationType = 'type';
|
||||
const mockItem: Item = Object.assign(new Item(), {
|
||||
bitstreams: observableOf(new RemoteData(false, false, true, null, new PaginatedList(new PageInfo(), []))),
|
||||
metadata: [
|
||||
{
|
||||
key: 'dc.title',
|
||||
language: 'en_US',
|
||||
value: 'test item'
|
||||
},
|
||||
{
|
||||
key: 'relationship.type',
|
||||
language: 'en_US',
|
||||
value: relationType
|
||||
}]
|
||||
});
|
||||
const mockItemMetadataRepresentation = Object.assign(new ItemMetadataRepresentation(relationType), mockItem);
|
||||
let viewMode = VIEW_MODE_FULL;
|
||||
|
||||
describe('ItemTypeSwitcherComponent', () => {
|
||||
let comp: ItemTypeSwitcherComponent;
|
||||
let fixture: ComponentFixture<ItemTypeSwitcherComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ ItemTypeSwitcherComponent ],
|
||||
schemas: [ NO_ERRORS_SCHEMA ]
|
||||
}).compileComponents(); // compile template and css
|
||||
}));
|
||||
|
||||
beforeEach(async(() => {
|
||||
fixture = TestBed.createComponent(ItemTypeSwitcherComponent);
|
||||
comp = fixture.componentInstance;
|
||||
comp.object = mockItem;
|
||||
comp.viewMode = viewMode;
|
||||
spyOnProperty(decorator, 'getComponentByItemType').and.returnValue(createSpy('getComponentByItemType'))
|
||||
}));
|
||||
|
||||
describe('when the injected object is of type Item', () => {
|
||||
beforeEach(() => {
|
||||
viewMode = VIEW_MODE_FULL;
|
||||
comp.object = mockItem;
|
||||
comp.viewMode = viewMode;
|
||||
});
|
||||
|
||||
describe('when calling getComponent', () => {
|
||||
beforeEach(() => {
|
||||
comp.getComponent();
|
||||
});
|
||||
|
||||
it('should call getComponentByItemType with parameters type and viewMode', () => {
|
||||
expect(decorator.getComponentByItemType).toHaveBeenCalledWith(relationType, viewMode);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the injected object is of type MetadataRepresentation', () => {
|
||||
beforeEach(() => {
|
||||
viewMode = VIEW_MODE_METADATA;
|
||||
comp.object = mockItemMetadataRepresentation;
|
||||
comp.viewMode = viewMode;
|
||||
});
|
||||
|
||||
describe('when calling getComponent', () => {
|
||||
beforeEach(() => {
|
||||
comp.getComponent();
|
||||
});
|
||||
|
||||
it('should call getComponentByItemType with parameters type, viewMode and representationType', () => {
|
||||
expect(decorator.getComponentByItemType).toHaveBeenCalledWith(relationType, viewMode, mockItemMetadataRepresentation.representationType);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
@@ -3,29 +3,29 @@ import { SearchResult } from '../../../+search-page/search-result.model';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { hasValue } from '../../empty.util';
|
||||
import { ItemSearchResult } from '../../object-collection/shared/item-search-result.model';
|
||||
import { getComponentByEntityType } from '../entity-type-decorator';
|
||||
import { ElementViewMode } from '../../view-mode';
|
||||
import { getComponentByItemType } from '../item-type-decorator';
|
||||
import { MetadataRepresentation } from '../../../core/shared/metadata-representation/metadata-representation.model';
|
||||
|
||||
export const ITEM: InjectionToken<string> = new InjectionToken<string>('item');
|
||||
|
||||
@Component({
|
||||
selector: 'ds-entity-type-switcher',
|
||||
styleUrls: ['./entity-type-switcher.component.scss'],
|
||||
templateUrl: './entity-type-switcher.component.html'
|
||||
selector: 'ds-item-type-switcher',
|
||||
styleUrls: ['./item-type-switcher.component.scss'],
|
||||
templateUrl: './item-type-switcher.component.html'
|
||||
})
|
||||
/**
|
||||
* Component for determining what component to use depending on the item's relationship type (relationship.type)
|
||||
*/
|
||||
export class EntityTypeSwitcherComponent implements OnInit {
|
||||
export class ItemTypeSwitcherComponent implements OnInit {
|
||||
/**
|
||||
* The item to determine the component for
|
||||
* The item or metadata to determine the component for
|
||||
*/
|
||||
@Input() object: Item | SearchResult<Item>;
|
||||
@Input() object: Item | SearchResult<Item> | MetadataRepresentation;
|
||||
|
||||
/**
|
||||
* The preferred view-mode to display
|
||||
*/
|
||||
@Input() viewMode: ElementViewMode;
|
||||
@Input() viewMode: string;
|
||||
|
||||
/**
|
||||
* The object injector used to inject the item into the child component
|
||||
@@ -48,6 +48,11 @@ export class EntityTypeSwitcherComponent implements OnInit {
|
||||
* @returns {string}
|
||||
*/
|
||||
getComponent(): string {
|
||||
if (hasValue((this.object as any).representationType)) {
|
||||
const metadataRepresentation = this.object as MetadataRepresentation;
|
||||
return getComponentByItemType(metadataRepresentation.itemType, this.viewMode, metadataRepresentation.representationType);
|
||||
}
|
||||
|
||||
let item: Item;
|
||||
if (hasValue((this.object as any).dspaceObject)) {
|
||||
const searchResult = this.object as ItemSearchResult;
|
||||
@@ -57,6 +62,6 @@ export class EntityTypeSwitcherComponent implements OnInit {
|
||||
}
|
||||
|
||||
const type = item.findMetadata('relationship.type');
|
||||
return getComponentByEntityType(type, this.viewMode);
|
||||
return getComponentByItemType(type, this.viewMode);
|
||||
}
|
||||
}
|
@@ -1 +0,0 @@
|
||||
<ds-entity-type-switcher [object]="object" [viewMode]="ElementViewMode.SetElement"></ds-entity-type-switcher>
|
@@ -1,22 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import * as viewMode from '../../../shared/view-mode';
|
||||
import { renderElementsFor } from '../../object-collection/shared/dso-element-decorator';
|
||||
import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
|
||||
import { SetViewMode } from '../../view-mode';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-entity-list-element',
|
||||
styleUrls: ['./entity-list-element.component.scss'],
|
||||
templateUrl: './entity-list-element.component.html'
|
||||
})
|
||||
|
||||
/**
|
||||
* The component used to list entities depending on type
|
||||
* Uses entity-type-switcher to determine which components to use for displaying the list
|
||||
*/
|
||||
@renderElementsFor(Item, SetViewMode.List)
|
||||
export class EntityListElementComponent extends AbstractListableElementComponent<Item> {
|
||||
ElementViewMode = viewMode.ElementViewMode;
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { rendersEntityType } from '../../../../entities/entity-type-decorator';
|
||||
import { ElementViewMode } from '../../../../view-mode';
|
||||
import { EntitySearchResultComponent } from '../entity-search-result-component';
|
||||
|
||||
@rendersEntityType('JournalIssue', ElementViewMode.SetElement)
|
||||
@Component({
|
||||
selector: 'ds-journal-issue-list-element',
|
||||
styleUrls: ['./journal-issue-list-element.component.scss'],
|
||||
templateUrl: './journal-issue-list-element.component.html'
|
||||
})
|
||||
/**
|
||||
* The component for displaying a list element for an item with entity type Journal Issue
|
||||
*/
|
||||
export class JournalIssueListElementComponent extends EntitySearchResultComponent {
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { rendersEntityType } from '../../../../entities/entity-type-decorator';
|
||||
import { ElementViewMode } from '../../../../view-mode';
|
||||
import { EntitySearchResultComponent } from '../entity-search-result-component';
|
||||
|
||||
@rendersEntityType('JournalVolume', ElementViewMode.SetElement)
|
||||
@Component({
|
||||
selector: 'ds-journal-volume-list-element',
|
||||
styleUrls: ['./journal-volume-list-element.component.scss'],
|
||||
templateUrl: './journal-volume-list-element.component.html'
|
||||
})
|
||||
/**
|
||||
* The component for displaying a list element for an item with entity type Journal Volume
|
||||
*/
|
||||
export class JournalVolumeListElementComponent extends EntitySearchResultComponent {
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { rendersEntityType } from '../../../../entities/entity-type-decorator';
|
||||
import { ElementViewMode } from '../../../../view-mode';
|
||||
import { EntitySearchResultComponent } from '../entity-search-result-component';
|
||||
|
||||
@rendersEntityType('Journal', ElementViewMode.SetElement)
|
||||
@Component({
|
||||
selector: 'ds-journal-list-element',
|
||||
styleUrls: ['./journal-list-element.component.scss'],
|
||||
templateUrl: './journal-list-element.component.html'
|
||||
})
|
||||
/**
|
||||
* The component for displaying a list element for an item with entity type Journal
|
||||
*/
|
||||
export class JournalListElementComponent extends EntitySearchResultComponent {
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { rendersEntityType } from '../../../../entities/entity-type-decorator';
|
||||
import { ElementViewMode } from '../../../../view-mode';
|
||||
import { EntitySearchResultComponent } from '../entity-search-result-component';
|
||||
|
||||
@rendersEntityType('OrgUnit', ElementViewMode.SetElement)
|
||||
@Component({
|
||||
selector: 'ds-orgunit-list-element',
|
||||
styleUrls: ['./orgunit-list-element.component.scss'],
|
||||
templateUrl: './orgunit-list-element.component.html'
|
||||
})
|
||||
/**
|
||||
* The component for displaying a list element for an item with entity type Organisation Unit
|
||||
*/
|
||||
export class OrgUnitListElementComponent extends EntitySearchResultComponent {
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { rendersEntityType } from '../../../../entities/entity-type-decorator';
|
||||
import { ElementViewMode } from '../../../../view-mode';
|
||||
import { EntitySearchResultComponent } from '../entity-search-result-component';
|
||||
|
||||
@rendersEntityType('Person', ElementViewMode.SetElement)
|
||||
@Component({
|
||||
selector: 'ds-person-list-element',
|
||||
styleUrls: ['./person-list-element.component.scss'],
|
||||
templateUrl: './person-list-element.component.html'
|
||||
})
|
||||
/**
|
||||
* The component for displaying a list element for an item with entity type Person
|
||||
*/
|
||||
export class PersonListElementComponent extends EntitySearchResultComponent {
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { rendersEntityType } from '../../../../entities/entity-type-decorator';
|
||||
import { ElementViewMode } from '../../../../view-mode';
|
||||
import { EntitySearchResultComponent } from '../entity-search-result-component';
|
||||
|
||||
@rendersEntityType('Project', ElementViewMode.SetElement)
|
||||
@Component({
|
||||
selector: 'ds-project-list-element',
|
||||
styleUrls: ['./project-list-element.component.scss'],
|
||||
templateUrl: './project-list-element.component.html'
|
||||
})
|
||||
/**
|
||||
* The component for displaying a list element for an item with entity type Project
|
||||
*/
|
||||
export class ProjectListElementComponent extends EntitySearchResultComponent {
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { DEFAULT_ENTITY_TYPE, rendersEntityType } from '../../../../entities/entity-type-decorator';
|
||||
import { ElementViewMode } from '../../../../view-mode';
|
||||
import { EntitySearchResultComponent } from '../entity-search-result-component';
|
||||
|
||||
@rendersEntityType('Publication', ElementViewMode.SetElement)
|
||||
@rendersEntityType(DEFAULT_ENTITY_TYPE, ElementViewMode.SetElement)
|
||||
@Component({
|
||||
selector: 'ds-publication-list-element',
|
||||
styleUrls: ['./publication-list-element.component.scss'],
|
||||
templateUrl: './publication-list-element.component.html'
|
||||
})
|
||||
/**
|
||||
* The component for displaying a list element for an item with entity type Publication
|
||||
*/
|
||||
export class PublicationListElementComponent extends EntitySearchResultComponent {
|
||||
}
|
@@ -0,0 +1 @@
|
||||
<ds-item-type-switcher [object]="object" [viewMode]="viewMode"></ds-item-type-switcher>
|
@@ -1,13 +1,12 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { EntityListElementComponent } from './entity-list-element.component';
|
||||
import { ItemListElementComponent } from './item-list-element.component';
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { Observable } from 'rxjs';
|
||||
import { RemoteData } from '../../../core/data/remote-data';
|
||||
import { PaginatedList } from '../../../core/data/paginated-list';
|
||||
import { PageInfo } from '../../../core/shared/page-info.model';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { createRelationshipsObservable } from '../../../+item-page/simple/entity-types/shared/entity.component.spec';
|
||||
import { createRelationshipsObservable } from '../../../+item-page/simple/item-types/shared/item.component.spec';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
|
||||
const mockItem: Item = Object.assign(new Item(), {
|
||||
@@ -16,32 +15,32 @@ const mockItem: Item = Object.assign(new Item(), {
|
||||
relationships: createRelationshipsObservable()
|
||||
});
|
||||
|
||||
describe('EntityListElementComponent', () => {
|
||||
let comp: EntityListElementComponent;
|
||||
let fixture: ComponentFixture<EntityListElementComponent>;
|
||||
describe('ItemListElementComponent', () => {
|
||||
let comp: ItemListElementComponent;
|
||||
let fixture: ComponentFixture<ItemListElementComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [],
|
||||
declarations: [EntityListElementComponent],
|
||||
declarations: [ItemListElementComponent],
|
||||
providers: [
|
||||
{ provide: 'objectElementProvider', useValue: mockItem }
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).overrideComponent(EntityListElementComponent, {
|
||||
}).overrideComponent(ItemListElementComponent, {
|
||||
set: {changeDetection: ChangeDetectionStrategy.Default}
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(async(() => {
|
||||
fixture = TestBed.createComponent(EntityListElementComponent);
|
||||
fixture = TestBed.createComponent(ItemListElementComponent);
|
||||
comp = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
}));
|
||||
|
||||
it('should call an entity-type-switcher component and pass the item', () => {
|
||||
const entityTypeSwitcher = fixture.debugElement.query(By.css('ds-entity-type-switcher')).componentInstance;
|
||||
expect(entityTypeSwitcher.object).toBe(mockItem);
|
||||
it('should call an item-type-switcher component and pass the item', () => {
|
||||
const itemTypeSwitcher = fixture.debugElement.query(By.css('ds-item-type-switcher')).componentInstance;
|
||||
expect(itemTypeSwitcher.object).toBe(mockItem);
|
||||
});
|
||||
|
||||
});
|
@@ -0,0 +1,22 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
import { Item } from '../../../core/shared/item.model';
|
||||
import { renderElementsFor } from '../../object-collection/shared/dso-element-decorator';
|
||||
import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
|
||||
import { SetViewMode } from '../../view-mode';
|
||||
import { VIEW_MODE_ELEMENT } from '../../../+item-page/simple/related-items/related-items-component';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-item-list-element',
|
||||
styleUrls: ['./item-list-element.component.scss'],
|
||||
templateUrl: './item-list-element.component.html'
|
||||
})
|
||||
|
||||
/**
|
||||
* The component used to list items depending on type
|
||||
* Uses item-type-switcher to determine which components to use for displaying the list
|
||||
*/
|
||||
@renderElementsFor(Item, SetViewMode.List)
|
||||
export class ItemListElementComponent extends AbstractListableElementComponent<Item> {
|
||||
viewMode = VIEW_MODE_ELEMENT;
|
||||
}
|
@@ -1,11 +1,10 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Item } from '../../../../../core/shared/item.model';
|
||||
import { TruncatePipe } from '../../../../utils/truncate.pipe';
|
||||
import { TruncatableService } from '../../../../truncatable/truncatable.service';
|
||||
import { ITEM } from '../../../../entities/switcher/entity-type-switcher.component';
|
||||
import { ITEM } from '../../../../items/switcher/item-type-switcher.component';
|
||||
import { JournalIssueListElementComponent } from './journal-issue-list-element.component';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
|
@@ -0,0 +1,16 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { rendersItemType } from '../../../../items/item-type-decorator';
|
||||
import { TypedItemSearchResultListElementComponent } from '../typed-item-search-result-list-element.component';
|
||||
import { VIEW_MODE_ELEMENT } from '../../../../../+item-page/simple/related-items/related-items-component';
|
||||
|
||||
@rendersItemType('JournalIssue', VIEW_MODE_ELEMENT)
|
||||
@Component({
|
||||
selector: 'ds-journal-issue-list-element',
|
||||
styleUrls: ['./journal-issue-list-element.component.scss'],
|
||||
templateUrl: './journal-issue-list-element.component.html'
|
||||
})
|
||||
/**
|
||||
* The component for displaying a list element for an item of the type Journal Issue
|
||||
*/
|
||||
export class JournalIssueListElementComponent extends TypedItemSearchResultListElementComponent {
|
||||
}
|
@@ -1,11 +1,10 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Item } from '../../../../../core/shared/item.model';
|
||||
import { TruncatePipe } from '../../../../utils/truncate.pipe';
|
||||
import { TruncatableService } from '../../../../truncatable/truncatable.service';
|
||||
import { ITEM } from '../../../../entities/switcher/entity-type-switcher.component';
|
||||
import { ITEM } from '../../../../items/switcher/item-type-switcher.component';
|
||||
import { JournalVolumeListElementComponent } from './journal-volume-list-element.component';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
|
@@ -0,0 +1,16 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { rendersItemType } from '../../../../items/item-type-decorator';
|
||||
import { TypedItemSearchResultListElementComponent } from '../typed-item-search-result-list-element.component';
|
||||
import { VIEW_MODE_ELEMENT } from '../../../../../+item-page/simple/related-items/related-items-component';
|
||||
|
||||
@rendersItemType('JournalVolume', VIEW_MODE_ELEMENT)
|
||||
@Component({
|
||||
selector: 'ds-journal-volume-list-element',
|
||||
styleUrls: ['./journal-volume-list-element.component.scss'],
|
||||
templateUrl: './journal-volume-list-element.component.html'
|
||||
})
|
||||
/**
|
||||
* The component for displaying a list element for an item of the type Journal Volume
|
||||
*/
|
||||
export class JournalVolumeListElementComponent extends TypedItemSearchResultListElementComponent {
|
||||
}
|
@@ -1,11 +1,10 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Item } from '../../../../../core/shared/item.model';
|
||||
import { TruncatePipe } from '../../../../utils/truncate.pipe';
|
||||
import { TruncatableService } from '../../../../truncatable/truncatable.service';
|
||||
import { ITEM } from '../../../../entities/switcher/entity-type-switcher.component';
|
||||
import { ITEM } from '../../../../items/switcher/item-type-switcher.component';
|
||||
import { JournalListElementComponent } from './journal-list-element.component';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
|
@@ -0,0 +1,16 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { rendersItemType } from '../../../../items/item-type-decorator';
|
||||
import { TypedItemSearchResultListElementComponent } from '../typed-item-search-result-list-element.component';
|
||||
import { VIEW_MODE_ELEMENT } from '../../../../../+item-page/simple/related-items/related-items-component';
|
||||
|
||||
@rendersItemType('Journal', VIEW_MODE_ELEMENT)
|
||||
@Component({
|
||||
selector: 'ds-journal-list-element',
|
||||
styleUrls: ['./journal-list-element.component.scss'],
|
||||
templateUrl: './journal-list-element.component.html'
|
||||
})
|
||||
/**
|
||||
* The component for displaying a list element for an item of the type Journal
|
||||
*/
|
||||
export class JournalListElementComponent extends TypedItemSearchResultListElementComponent {
|
||||
}
|
@@ -1,11 +1,10 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Item } from '../../../../../core/shared/item.model';
|
||||
import { TruncatePipe } from '../../../../utils/truncate.pipe';
|
||||
import { TruncatableService } from '../../../../truncatable/truncatable.service';
|
||||
import { ITEM } from '../../../../entities/switcher/entity-type-switcher.component';
|
||||
import { ITEM } from '../../../../items/switcher/item-type-switcher.component';
|
||||
import { OrgUnitListElementComponent } from './orgunit-list-element.component';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
|
@@ -0,0 +1,16 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { rendersItemType } from '../../../../items/item-type-decorator';
|
||||
import { TypedItemSearchResultListElementComponent } from '../typed-item-search-result-list-element.component';
|
||||
import { VIEW_MODE_ELEMENT } from '../../../../../+item-page/simple/related-items/related-items-component';
|
||||
|
||||
@rendersItemType('OrgUnit', VIEW_MODE_ELEMENT)
|
||||
@Component({
|
||||
selector: 'ds-orgunit-list-element',
|
||||
styleUrls: ['./orgunit-list-element.component.scss'],
|
||||
templateUrl: './orgunit-list-element.component.html'
|
||||
})
|
||||
/**
|
||||
* The component for displaying a list element for an item of the type Organisation Unit
|
||||
*/
|
||||
export class OrgUnitListElementComponent extends TypedItemSearchResultListElementComponent {
|
||||
}
|
@@ -13,4 +13,3 @@
|
||||
</ds-truncatable-part>
|
||||
</span>
|
||||
</ds-truncatable>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user