Merge pull request #496 from atmire/Name-variants-on-item-pages

Name variants on item pages
This commit is contained in:
Tim Donohue
2019-11-05 09:55:13 -06:00
committed by GitHub
14 changed files with 73 additions and 98 deletions

View File

@@ -117,9 +117,8 @@ export const getRelatedItemsByTypeLabel = (thisId: string, label: string) =>
* @param parentId The id of the parent item * @param parentId The id of the parent item
* @param itemType The type of relation this list resembles (for creating representations) * @param itemType The type of relation this list resembles (for creating representations)
* @param metadata The list of original Metadatum objects * @param metadata The list of original Metadatum objects
* @param ids The ItemDataService to use for fetching Items from the Rest API
*/ */
export const relationsToRepresentations = (parentId: string, itemType: string, metadata: MetadataValue[], ids: ItemDataService) => export const relationsToRepresentations = (parentId: string, itemType: string, metadata: MetadataValue[]) =>
(source: Observable<Relationship[]>): Observable<MetadataRepresentation[]> => (source: Observable<Relationship[]>): Observable<MetadataRepresentation[]> =>
source.pipe( source.pipe(
flatMap((rels: Relationship[]) => flatMap((rels: Relationship[]) =>
@@ -139,7 +138,7 @@ export const relationsToRepresentations = (parentId: string, itemType: string, m
return leftItem.payload; return leftItem.payload;
} }
}), }),
map((item: Item) => Object.assign(new ItemMetadataRepresentation(), item)) map((item: Item) => Object.assign(new ItemMetadataRepresentation(metadatum), item))
); );
} }
} else { } else {

View File

@@ -413,7 +413,7 @@ describe('ItemComponent', () => {
representations.subscribe((reps: MetadataRepresentation[]) => { representations.subscribe((reps: MetadataRepresentation[]) => {
expect(reps[0].getValue()).toEqual('First value'); expect(reps[0].getValue()).toEqual('First value');
expect(reps[1].getValue()).toEqual('Second value'); expect(reps[1].getValue()).toEqual('Second value');
expect(reps[2].getValue()).toEqual('related item'); expect(reps[2].getValue()).toEqual('Third value');
expect(reps[3].getValue()).toEqual('Fourth value'); expect(reps[3].getValue()).toEqual('Fourth value');
}); });
}); });

View File

@@ -1,7 +1,6 @@
import { Component, Inject, OnInit } from '@angular/core'; import { Component, Inject, OnInit } from '@angular/core';
import { Observable , zip as observableZip, combineLatest as observableCombineLatest } from 'rxjs'; import { Observable , zip as observableZip, combineLatest as observableCombineLatest } from 'rxjs';
import { distinctUntilChanged, filter, flatMap, map } from 'rxjs/operators'; import { distinctUntilChanged, filter, flatMap, map } from 'rxjs/operators';
import { ItemDataService } from '../../../../core/data/item-data.service';
import { PaginatedList } from '../../../../core/data/paginated-list'; import { PaginatedList } from '../../../../core/data/paginated-list';
import { RemoteData } from '../../../../core/data/remote-data'; import { RemoteData } from '../../../../core/data/remote-data';
import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model'; import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model';
@@ -10,49 +9,7 @@ import { Item } from '../../../../core/shared/item.model';
import { getRemoteDataPayload, getSucceededRemoteData } from '../../../../core/shared/operators'; import { getRemoteDataPayload, getSucceededRemoteData } from '../../../../core/shared/operators';
import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component'; import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component';
import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model'; import { MetadataRepresentation } from '../../../../core/shared/metadata-representation/metadata-representation.model';
import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-representation/item/item-metadata-representation.model'; import { compareArraysUsingIds, relationsToRepresentations } from './item-relationships-utils';
import { MetadatumRepresentation } from '../../../../core/shared/metadata-representation/metadatum/metadatum-representation.model';
import { of } from 'rxjs/internal/observable/of';
import { MetadataValue } from '../../../../core/shared/metadata.models';
import { compareArraysUsingIds } from './item-relationships-utils';
/**
* 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
*/
export const relationsToRepresentations = (thisId: string, itemType: string, metadata: MetadataValue[]) =>
(source: Observable<Relationship[]>): Observable<MetadataRepresentation[]> =>
source.pipe(
flatMap((rels: Relationship[]) =>
observableZip(
...metadata
.map((metadatum: any) => Object.assign(new MetadataValue(), metadatum))
.map((metadatum: MetadataValue) => {
if (metadatum.isVirtual) {
const matchingRels = rels.filter((rel: Relationship) => ('' + rel.id) === metadatum.virtualValue);
if (matchingRels.length > 0) {
const matchingRel = matchingRels[0];
return observableCombineLatest(matchingRel.leftItem, matchingRel.rightItem).pipe(
filter(([leftItem, rightItem]) => leftItem.hasSucceeded && rightItem.hasSucceeded),
map(([leftItem, rightItem]) => {
if (leftItem.payload.id === thisId) {
return rightItem.payload;
} else if (rightItem.payload.id === thisId) {
return leftItem.payload;
}
}),
map((item: Item) => Object.assign(new ItemMetadataRepresentation(), item))
);
}
} else {
return of(Object.assign(new MetadatumRepresentation(itemType), metadatum));
}
})
)
)
);
@Component({ @Component({
selector: 'ds-item', selector: 'ds-item',

View File

@@ -7,7 +7,7 @@ import { ItemMetadataRepresentation } from '../../../core/shared/metadata-repres
const itemType = 'type'; const itemType = 'type';
const metadataRepresentation1 = new MetadatumRepresentation(itemType); const metadataRepresentation1 = new MetadatumRepresentation(itemType);
const metadataRepresentation2 = new ItemMetadataRepresentation(); const metadataRepresentation2 = new ItemMetadataRepresentation(Object.assign({}));
const representations = [metadataRepresentation1, metadataRepresentation2]; const representations = [metadataRepresentation1, metadataRepresentation2];
describe('MetadataRepresentationListComponent', () => { describe('MetadataRepresentationListComponent', () => {

View File

@@ -1,21 +1,27 @@
import { MetadataRepresentationType } from '../metadata-representation.model'; import { MetadataRepresentationType } from '../metadata-representation.model';
import { ItemMetadataRepresentation, ItemTypeToValue } from './item-metadata-representation.model'; import { ItemMetadataRepresentation } from './item-metadata-representation.model';
import { Item } from '../../item.model'; import { Item } from '../../item.model';
import { MetadataMap, MetadataValue } from '../../metadata.models'; import { MetadataValue } from '../../metadata.models';
describe('ItemMetadataRepresentation', () => { describe('ItemMetadataRepresentation', () => {
const valuePrefix = 'Test value for '; const valuePrefix = 'Test value for ';
const item = new Item(); const item = new Item();
const itemType = 'Item Type';
let itemMetadataRepresentation: ItemMetadataRepresentation; let itemMetadataRepresentation: ItemMetadataRepresentation;
const metadataMap = new MetadataMap(); item.metadata = {
for (const key of Object.keys(ItemTypeToValue)) { 'dc.title': [
metadataMap[ItemTypeToValue[key]] = [Object.assign(new MetadataValue(), { {
value: `${valuePrefix}${ItemTypeToValue[key]}` value: `${valuePrefix}dc.title`
})];
} }
item.metadata = metadataMap; ] as MetadataValue[],
'dc.contributor.author': [
{
value: `${valuePrefix}dc.contributor.author`
}
] as MetadataValue[]
};
for (const itemType of Object.keys(ItemTypeToValue)) { for (const metadataField of Object.keys(item.metadata)) {
describe(`when creating an ItemMetadataRepresentation`, () => { describe(`when creating an ItemMetadataRepresentation`, () => {
beforeEach(() => { beforeEach(() => {
item.metadata['relationship.type'] = [ item.metadata['relationship.type'] = [
@@ -23,8 +29,7 @@ describe('ItemMetadataRepresentation', () => {
value: itemType value: itemType
}) })
]; ];
itemMetadataRepresentation = Object.assign(new ItemMetadataRepresentation(item.metadata[metadataField][0]), item);
itemMetadataRepresentation = Object.assign(new ItemMetadataRepresentation(), item);
}); });
it('should have a representation type of item', () => { it('should have a representation type of item', () => {
@@ -32,7 +37,7 @@ describe('ItemMetadataRepresentation', () => {
}); });
it('should return the correct value when calling getValue', () => { it('should return the correct value when calling getValue', () => {
expect(itemMetadataRepresentation.getValue()).toEqual(`${valuePrefix}${ItemTypeToValue[itemType]}`); expect(itemMetadataRepresentation.getValue()).toEqual(`${valuePrefix}${metadataField}`);
}); });
it('should return the correct item type', () => { it('should return the correct item type', () => {

View File

@@ -1,21 +1,22 @@
import { Item } from '../../item.model'; import { Item } from '../../item.model';
import { MetadataRepresentation, MetadataRepresentationType } from '../metadata-representation.model'; import { MetadataRepresentation, MetadataRepresentationType } from '../metadata-representation.model';
import { hasValue } from '../../../../shared/empty.util'; import { MetadataValue } from '../../metadata.models';
/**
* 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',
OrgUnit: 'dc.title'
};
/** /**
* This class determines which fields to use when rendering an Item as a metadata value. * This class determines which fields to use when rendering an Item as a metadata value.
*/ */
export class ItemMetadataRepresentation extends Item implements MetadataRepresentation { export class ItemMetadataRepresentation extends Item implements MetadataRepresentation {
/**
* The virtual metadata value representing this item
*/
virtualMetadata: MetadataValue;
constructor(virtualMetadata: MetadataValue) {
super();
this.virtualMetadata = virtualMetadata;
}
/** /**
* The type of item this item can be represented as * The type of item this item can be represented as
*/ */
@@ -34,13 +35,7 @@ export class ItemMetadataRepresentation extends Item implements MetadataRepresen
* Get the value to display, depending on the itemType * Get the value to display, depending on the itemType
*/ */
getValue(): string { getValue(): string {
let metadata; return this.virtualMetadata.value;
if (hasValue(ItemTypeToValue[this.itemType])) {
metadata = ItemTypeToValue[this.itemType];
} else {
metadata = ItemTypeToValue.Default;
}
return this.firstMetadataValue(metadata);
} }
} }

View File

@@ -1,13 +1,13 @@
<ng-template #descTemplate> <ng-template #descTemplate>
<span class="text-muted"> <span class="text-muted">
<span *ngIf="item.allMetadata(['dc.description']).length > 0" <span *ngIf="metadataRepresentation.allMetadata(['dc.description']).length > 0"
class="item-list-job-title"> class="item-list-job-title">
<span [innerHTML]="firstMetadataValue(['dc.description'])"></span> <span [innerHTML]="metadataRepresentation.firstMetadataValue(['dc.description'])"></span>
</span> </span>
</span> </span>
</ng-template> </ng-template>
<ds-truncatable [id]="item.id"> <ds-truncatable [id]="metadataRepresentation.id">
<a [routerLink]="['/items/' + item.id]" <a [routerLink]="['/items/' + metadataRepresentation.id]"
[innerHTML]="firstMetadataValue('organization.legalName')" [innerHTML]="metadataRepresentation.getValue()"
[tooltip]="descTemplate"></a> [tooltip]="metadataRepresentation.allMetadata(['dc.description']).length > 0 ? descTemplate : null"></a>
</ds-truncatable> </ds-truncatable>

View File

@@ -1,7 +1,7 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { MetadataRepresentationType } from '../../../../core/shared/metadata-representation/metadata-representation.model'; import { MetadataRepresentationType } from '../../../../core/shared/metadata-representation/metadata-representation.model';
import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator'; import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
import { TypedItemSearchResultListElementComponent } from '../../../../shared/object-list/item-list-element/item-types/typed-item-search-result-list-element.component'; import { ItemMetadataRepresentationListElementComponent } from '../../../../shared/object-list/metadata-representation-list-element/item/item-metadata-representation-list-element.component';
@rendersItemType('OrgUnit', ItemViewMode.Element, MetadataRepresentationType.Item) @rendersItemType('OrgUnit', ItemViewMode.Element, MetadataRepresentationType.Item)
@Component({ @Component({
@@ -11,5 +11,5 @@ import { TypedItemSearchResultListElementComponent } from '../../../../shared/ob
/** /**
* The component for displaying a list element for an item of the type OrgUnit * The component for displaying a list element for an item of the type OrgUnit
*/ */
export class OrgUnitMetadataListElementComponent extends TypedItemSearchResultListElementComponent { export class OrgUnitMetadataListElementComponent extends ItemMetadataRepresentationListElementComponent {
} }

View File

@@ -1,15 +1,15 @@
<ng-template #descTemplate> <ng-template #descTemplate>
<span class="text-muted"> <span class="text-muted">
<span *ngIf="item.allMetadata(['person.jobTitle']).length > 0" <span *ngIf="metadataRepresentation.allMetadata(['person.jobTitle']).length > 0"
class="item-list-job-title"> class="item-list-job-title">
<span *ngFor="let value of allMetadataValues(['person.jobTitle']); let last=last;"> <span *ngFor="let value of metadataRepresentation.allMetadataValues(['person.jobTitle']); let last=last;">
<span [innerHTML]="value"><span [innerHTML]="value"></span></span> <span [innerHTML]="value"><span [innerHTML]="value"></span></span>
</span> </span>
</span> </span>
</span> </span>
</ng-template> </ng-template>
<ds-truncatable [id]="item.id"> <ds-truncatable [id]="metadataRepresentation.id">
<a [routerLink]="['/items/' + item.id]" <a [routerLink]="['/items/' + metadataRepresentation.id]"
[innerHTML]="firstMetadataValue('person.familyName') + ', ' + firstMetadataValue('person.givenName')" [innerHTML]="metadataRepresentation.getValue()"
[tooltip]="descTemplate"></a> [tooltip]="metadataRepresentation.allMetadata(['person.jobTitle']).length > 0 ? descTemplate : null"></a>
</ds-truncatable> </ds-truncatable>

View File

@@ -1,7 +1,7 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator'; import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
import { MetadataRepresentationType } from '../../../../core/shared/metadata-representation/metadata-representation.model'; import { MetadataRepresentationType } from '../../../../core/shared/metadata-representation/metadata-representation.model';
import { TypedItemSearchResultListElementComponent } from '../../../../shared/object-list/item-list-element/item-types/typed-item-search-result-list-element.component'; import { ItemMetadataRepresentationListElementComponent } from '../../../../shared/object-list/metadata-representation-list-element/item/item-metadata-representation-list-element.component';
@rendersItemType('Person', ItemViewMode.Element, MetadataRepresentationType.Item) @rendersItemType('Person', ItemViewMode.Element, MetadataRepresentationType.Item)
@Component({ @Component({
@@ -11,5 +11,5 @@ import { TypedItemSearchResultListElementComponent } from '../../../../shared/ob
/** /**
* The component for displaying a list element for an item of the type Person * The component for displaying a list element for an item of the type Person
*/ */
export class PersonMetadataListElementComponent extends TypedItemSearchResultListElementComponent { export class PersonMetadataListElementComponent extends ItemMetadataRepresentationListElementComponent {
} }

View File

@@ -28,7 +28,7 @@ const mockItem: Item = Object.assign(new Item(), {
] ]
} }
}); });
const mockItemMetadataRepresentation = Object.assign(new ItemMetadataRepresentation(), mockItem); const mockItemMetadataRepresentation = Object.assign(new ItemMetadataRepresentation(Object.assign({})), mockItem);
let viewMode = ItemViewMode.Full; let viewMode = ItemViewMode.Full;
describe('ItemTypeSwitcherComponent', () => { describe('ItemTypeSwitcherComponent', () => {

View File

@@ -5,7 +5,7 @@ import { ItemMetadataListElementComponent } from './item-metadata-list-element.c
import { By } from '@angular/platform-browser'; import { By } from '@angular/platform-browser';
import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-representation/item/item-metadata-representation.model'; import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-representation/item/item-metadata-representation.model';
const mockItemMetadataRepresentation = new ItemMetadataRepresentation(); const mockItemMetadataRepresentation = new ItemMetadataRepresentation(Object.assign({}));
describe('ItemMetadataListElementComponent', () => { describe('ItemMetadataListElementComponent', () => {
let comp: ItemMetadataListElementComponent; let comp: ItemMetadataListElementComponent;

View File

@@ -0,0 +1,17 @@
import { MetadataRepresentationListElementComponent } from '../metadata-representation-list-element.component';
import { Component, Inject } from '@angular/core';
import { ITEM } from '../../../items/switcher/item-type-switcher.component';
import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-representation/item/item-metadata-representation.model';
@Component({
selector: 'ds-item-metadata-representation-list-element',
template: ''
})
/**
* An abstract class for displaying a single ItemMetadataRepresentation
*/
export class ItemMetadataRepresentationListElementComponent extends MetadataRepresentationListElementComponent {
constructor(@Inject(ITEM) public metadataRepresentation: ItemMetadataRepresentation) {
super(metadataRepresentation);
}
}

View File

@@ -146,6 +146,7 @@ import { DsoInputSuggestionsComponent } from './input-suggestions/dso-input-sugg
import { TypedItemSearchResultGridElementComponent } from './object-grid/item-grid-element/item-types/typed-item-search-result-grid-element.component'; import { TypedItemSearchResultGridElementComponent } from './object-grid/item-grid-element/item-types/typed-item-search-result-grid-element.component';
import { PublicationGridElementComponent } from './object-grid/item-grid-element/item-types/publication/publication-grid-element.component'; import { PublicationGridElementComponent } from './object-grid/item-grid-element/item-types/publication/publication-grid-element.component';
import { ItemTypeBadgeComponent } from './object-list/item-type-badge/item-type-badge.component'; import { ItemTypeBadgeComponent } from './object-list/item-type-badge/item-type-badge.component';
import { ItemMetadataRepresentationListElementComponent } from './object-list/metadata-representation-list-element/item/item-metadata-representation-list-element.component';
const MODULES = [ const MODULES = [
// Do NOT include UniversalModule, HttpModule, or JsonpModule here // Do NOT include UniversalModule, HttpModule, or JsonpModule here
@@ -316,7 +317,8 @@ const ENTRY_COMPONENTS = [
StartsWithTextComponent, StartsWithTextComponent,
PlainTextMetadataListElementComponent, PlainTextMetadataListElementComponent,
ItemMetadataListElementComponent, ItemMetadataListElementComponent,
MetadataRepresentationListElementComponent MetadataRepresentationListElementComponent,
ItemMetadataRepresentationListElementComponent
]; ];
const SHARED_ITEM_PAGE_COMPONENTS = [ const SHARED_ITEM_PAGE_COMPONENTS = [