Merge branch 'w2p-65195_dynamic-component-refactoring' into clean-relationships-in-submission

This commit is contained in:
lotte
2019-11-08 13:55:08 +01:00
22 changed files with 110 additions and 70 deletions

View File

@@ -237,7 +237,7 @@
"tslint": "5.11.0",
"typedoc": "^0.9.0",
"typescript": "^2.9.1",
"webdriver-manager": "^12.1.6",
"webdriver-manager": "^12.1.7",
"webpack": "^4.17.1",
"webpack-bundle-analyzer": "^3.3.2",
"webpack-dev-middleware": "3.2.0",

View File

@@ -39,7 +39,8 @@
</button>
</div>
<ds-my-dspace-results [searchResults]="resultsRD$ | async"
[searchConfig]="searchOptions$ | async"></ds-my-dspace-results>
[searchConfig]="searchOptions$ | async"
[context]="context$ | async"></ds-my-dspace-results>
</div>
</div>
</div>

View File

@@ -8,7 +8,7 @@ import {
} from '@angular/core';
import { BehaviorSubject, Observable, Subscription } from 'rxjs';
import { switchMap, tap, } from 'rxjs/operators';
import { map, switchMap, tap, } from 'rxjs/operators';
import { PaginatedList } from '../core/data/paginated-list';
import { RemoteData } from '../core/data/remote-data';
@@ -28,6 +28,7 @@ import { MyDSpaceConfigurationService } from './my-dspace-configuration.service'
import { ViewMode } from '../core/shared/view-mode.model';
import { MyDSpaceRequest } from '../core/data/request.models';
import { SearchResult } from '../shared/search/search-result.model';
import { Context } from '../core/shared/context.model';
export const MYDSPACE_ROUTE = '/mydspace';
export const SEARCH_CONFIG_SERVICE: InjectionToken<SearchConfigurationService> = new InjectionToken<SearchConfigurationService>('searchConfigurationService');
@@ -95,6 +96,11 @@ export class MyDSpacePageComponent implements OnInit {
*/
viewModeList = [ViewMode.ListElement, ViewMode.DetailedListElement];
/**
* The current context of this page: workspace or workflow
*/
context$: Observable<Context>;
constructor(private service: SearchService,
private sidebarService: SearchSidebarService,
private windowService: HostWindowService,
@@ -111,6 +117,9 @@ export class MyDSpacePageComponent implements OnInit {
*
* Listen to changes in the scope
* If something changes, update the list of scopes for the dropdown
*
* Listen to changes in the configuration
* If something changes, update the current context
*/
ngOnInit(): void {
this.configurationList$ = this.searchConfigService.getAvailableConfigurationOptions();
@@ -126,6 +135,17 @@ export class MyDSpacePageComponent implements OnInit {
switchMap((scopeId) => this.service.getScopes(scopeId))
);
this.context$ = this.searchConfigService.getCurrentConfiguration('workspace')
.pipe(
map((configuration: string) => {
if (configuration === 'workspace') {
return Context.Workspace
} else {
return Context.Workflow
}
})
);
}
/**

View File

@@ -37,7 +37,10 @@ export class MyDSpaceResultsComponent {
*/
@Input() viewMode: ViewMode;
context = Context.Submission;
/**
* The current context for the search results
*/
@Input() context: Context;
/**
* A boolean representing if search results entry are separated by a line
*/

View File

@@ -6,7 +6,8 @@ export enum Context {
Undefined = 'undefined',
ItemPage = 'itemPage',
Search = 'search',
Submission = 'submission',
Workflow = 'workflow',
Workspace = 'workspace',
AdminMenu = 'adminMenu',
SubmissionModal = 'submissionModal',
}

View File

@@ -1,21 +1,27 @@
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 { MetadataMap, MetadataValue } from '../../metadata.models';
import { MetadataValue } from '../../metadata.models';
describe('ItemMetadataRepresentation', () => {
const valuePrefix = 'Test value for ';
const item = new Item();
const itemType = 'Item Type';
let itemMetadataRepresentation: ItemMetadataRepresentation;
const metadataMap = new MetadataMap();
for (const key of Object.keys(ItemTypeToValue)) {
metadataMap[ItemTypeToValue[key]] = [Object.assign(new MetadataValue(), {
value: `${valuePrefix}${ItemTypeToValue[key]}`
})];
}
item.metadata = metadataMap;
item.metadata = {
'dc.title': [
{
value: `${valuePrefix}dc.title`
}
] 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`, () => {
beforeEach(() => {
item.metadata['relationship.type'] = [
@@ -23,8 +29,7 @@ describe('ItemMetadataRepresentation', () => {
value: itemType
})
];
itemMetadataRepresentation = Object.assign(new ItemMetadataRepresentation(), item);
itemMetadataRepresentation = Object.assign(new ItemMetadataRepresentation(item.metadata[metadataField][0]), item);
});
it('should have a representation type of item', () => {
@@ -32,7 +37,7 @@ describe('ItemMetadataRepresentation', () => {
});
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', () => {

View File

@@ -1,21 +1,22 @@
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',
OrgUnit: 'dc.title'
};
import { MetadataValue } from '../../metadata.models';
/**
* This class determines which fields to use when rendering an Item as a metadata value.
*/
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
*/
@@ -34,13 +35,7 @@ export class ItemMetadataRepresentation extends Item implements MetadataRepresen
* 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.firstMetadataValue(metadata);
return this.virtualMetadata.value;
}
}

View File

@@ -1,13 +1,8 @@
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 { JournalIssueComponent } from './journal-issue.component';
import { of as observableOf } from 'rxjs';
import {
createRelationshipsObservable,
getItemPageFieldsTest
} from '../../../../+item-page/simple/item-types/shared/item.component.spec';
import { createRelationshipsObservable, getItemPageFieldsTest } from '../../../../+item-page/simple/item-types/shared/item.component.spec';
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/testing/utils';
const mockItem: Item = Object.assign(new Item(), {

View File

@@ -8,6 +8,6 @@
</ng-template>
<ds-truncatable [id]="metadataRepresentation.id">
<a [routerLink]="['/items/' + metadataRepresentation.id]"
[innerHTML]="metadataRepresentation.firstMetadataValue('organization.legalName')"
[tooltip]="descTemplate"></a>
[innerHTML]="metadataRepresentation.getValue()"
[tooltip]="metadataRepresentation.allMetadata(['dc.description']).length > 0 ? descTemplate : null"></a>
</ds-truncatable>

View File

@@ -5,11 +5,13 @@ import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-rep
import { OrgUnitItemMetadataListElementComponent } from './org-unit-item-metadata-list-element.component';
import { Item } from '../../../../core/shared/item.model';
import { TooltipModule } from 'ngx-bootstrap';
import { MetadataValue } from '../../../../core/shared/metadata.models';
const description = 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.';
const organisation = 'Anonymous';
const mockItem = Object.assign(new Item(), { metadata: { 'dc.description': [{ value: description }], 'organization.legalName': [{ value: organisation }] } });
const mockItemMetadataRepresentation = Object.assign(new ItemMetadataRepresentation(), mockItem);
const virtMD = Object.assign(new MetadataValue(), { value: organisation });
const mockItemMetadataRepresentation = Object.assign(new ItemMetadataRepresentation(virtMD), mockItem);
describe('OrgUnitItemMetadataListElementComponent', () => {
let comp: OrgUnitItemMetadataListElementComponent;

View File

@@ -1,7 +1,7 @@
import { Component } from '@angular/core';
import { metadataRepresentationComponent } from '../../../../shared/metadata-representation/metadata-representation.decorator';
import { MetadataRepresentationType } from '../../../../core/shared/metadata-representation/metadata-representation.model';
import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-representation/item/item-metadata-representation.model';
import { ItemMetadataRepresentationListElementComponent } from '../../../../shared/object-list/metadata-representation-list-element/item/item-metadata-representation-list-element.component';
@metadataRepresentationComponent('OrgUnit', MetadataRepresentationType.Item)
@Component({
@@ -11,6 +11,5 @@ import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-rep
/**
* The component for displaying an item of the type OrgUnit as a metadata field
*/
export class OrgUnitItemMetadataListElementComponent {
metadataRepresentation: ItemMetadataRepresentation;
export class OrgUnitItemMetadataListElementComponent extends ItemMetadataRepresentationListElementComponent {
}

View File

@@ -10,6 +10,6 @@
</ng-template>
<ds-truncatable [id]="metadataRepresentation.id">
<a [routerLink]="['/items/' + metadataRepresentation.id]"
[innerHTML]="metadataRepresentation.firstMetadataValue('person.familyName') + ', ' + metadataRepresentation.firstMetadataValue('person.givenName')"
[tooltip]="descTemplate"></a>
[innerHTML]="metadataRepresentation.getValue()"
[tooltip]="metadataRepresentation.allMetadata(['person.jobTitle']).length > 0 ? descTemplate : null"></a>
</ds-truncatable>

View File

@@ -5,12 +5,15 @@ import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-rep
import { Item } from '../../../../core/shared/item.model';
import { PersonItemMetadataListElementComponent } from './person-item-metadata-list-element.component';
import { TooltipModule } from 'ngx-bootstrap';
import { MetadataValue } from '../../../../core/shared/metadata.models';
const jobTitle ='Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.';
const firstName = 'Joe';
const lastName = 'Anonymous';
const mockItem = Object.assign(new Item(), { metadata: { 'person.jobTitle': [{ value: jobTitle }], 'person.givenName': [{ value: firstName }], 'person.familyName': [{ value: lastName }] } });
const mockItemMetadataRepresentation = Object.assign(new ItemMetadataRepresentation(), mockItem);
const virtMD = Object.assign(new MetadataValue(), { value: lastName + ', ' + firstName });
const mockItemMetadataRepresentation = Object.assign(new ItemMetadataRepresentation(virtMD), mockItem);
describe('PersonItemMetadataListElementComponent', () => {
let comp: PersonItemMetadataListElementComponent;

View File

@@ -1,7 +1,7 @@
import { Component } from '@angular/core';
import { metadataRepresentationComponent } from '../../../../shared/metadata-representation/metadata-representation.decorator';
import { MetadataRepresentationType } from '../../../../core/shared/metadata-representation/metadata-representation.model';
import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-representation/item/item-metadata-representation.model';
import { ItemMetadataRepresentationListElementComponent } from '../../../../shared/object-list/metadata-representation-list-element/item/item-metadata-representation-list-element.component';
@metadataRepresentationComponent('Person', MetadataRepresentationType.Item)
@Component({
@@ -11,6 +11,5 @@ import { ItemMetadataRepresentation } from '../../../../core/shared/metadata-rep
/**
* The component for displaying an item of the type Person as a metadata field
*/
export class PersonItemMetadataListElementComponent {
metadataRepresentation: ItemMetadataRepresentation;
export class PersonItemMetadataListElementComponent extends ItemMetadataRepresentationListElementComponent {
}

View File

@@ -27,14 +27,14 @@ describe('MetadataRepresentation decorator function', () => {
metadataRepresentationComponent(key + type1, MetadataRepresentationType.AuthorityControlled)(Test1Authority);
metadataRepresentationComponent(key + type2, MetadataRepresentationType.Item)(Test2Item);
metadataRepresentationComponent(key + type2, MetadataRepresentationType.Item, Context.Submission)(Test2ItemSubmission);
metadataRepresentationComponent(key + type2, MetadataRepresentationType.Item, Context.Workspace)(Test2ItemSubmission);
metadataRepresentationComponent(key + type3, MetadataRepresentationType.Item, Context.Submission)(Test3ItemSubmission);
metadataRepresentationComponent(key + type3, MetadataRepresentationType.Item, Context.Workspace)(Test3ItemSubmission);
}
describe('If there\'s an exact match', () => {
it('should return the matching class', () => {
const component = getMetadataRepresentationComponent(prefix + type3, MetadataRepresentationType.Item, Context.Submission);
const component = getMetadataRepresentationComponent(prefix + type3, MetadataRepresentationType.Item, Context.Workspace);
expect(component).toEqual(Test3ItemSubmission);
});
});
@@ -42,7 +42,7 @@ describe('MetadataRepresentation decorator function', () => {
describe('If there isn\'nt an exact match', () => {
describe('If there is a match for the entity type and representation type', () => {
it('should return the class with the matching entity type and representation type and default context', () => {
const component = getMetadataRepresentationComponent(prefix + type1, MetadataRepresentationType.AuthorityControlled, Context.Submission);
const component = getMetadataRepresentationComponent(prefix + type1, MetadataRepresentationType.AuthorityControlled, Context.Workspace);
expect(component).toEqual(Test1Authority);
});
});

View File

@@ -22,10 +22,10 @@ describe('ListableObject decorator function', () => {
listableObjectComponent(type1, ViewMode.GridElement)(Test1Grid);
listableObjectComponent(type2, ViewMode.ListElement)(Test2List);
listableObjectComponent(type2, ViewMode.ListElement, Context.Submission)(Test2ListSubmission);
listableObjectComponent(type2, ViewMode.ListElement, Context.Workspace)(Test2ListSubmission);
listableObjectComponent(type3, ViewMode.ListElement)(Test3List);
listableObjectComponent(type3, ViewMode.DetailedListElement, Context.Submission)(Test3DetailedSubmission);
listableObjectComponent(type3, ViewMode.DetailedListElement, Context.Workspace)(Test3DetailedSubmission);
});
const gridDecorator = listableObjectComponent('Item', ViewMode.GridElement);
@@ -40,10 +40,10 @@ describe('ListableObject decorator function', () => {
describe('If there\'s an exact match', () => {
it('should return the matching class', () => {
const component = getListableObjectComponent([type3], ViewMode.DetailedListElement, Context.Submission);
const component = getListableObjectComponent([type3], ViewMode.DetailedListElement, Context.Workspace);
expect(component).toEqual(Test3DetailedSubmission);
const component2 = getListableObjectComponent([type3, type2], ViewMode.ListElement, Context.Submission);
const component2 = getListableObjectComponent([type3, type2], ViewMode.ListElement, Context.Workspace);
expect(component2).toEqual(Test2ListSubmission);
});
});
@@ -51,10 +51,10 @@ describe('ListableObject decorator function', () => {
describe('If there isn\'nt an exact match', () => {
describe('If there is a match for one of the entity types and the view mode', () => {
it('should return the class with the matching entity type and view mode and default context', () => {
const component = getListableObjectComponent([type3], ViewMode.ListElement, Context.Submission);
const component = getListableObjectComponent([type3], ViewMode.ListElement, Context.Workspace);
expect(component).toEqual(Test3List);
const component2 = getListableObjectComponent([type3, type1], ViewMode.GridElement, Context.Submission);
const component2 = getListableObjectComponent([type3, type1], ViewMode.GridElement, Context.Workspace);
expect(component2).toEqual(Test1Grid);
});
});

View File

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

View File

@@ -0,0 +1,14 @@
import { MetadataRepresentationListElementComponent } from '../metadata-representation-list-element.component';
import { Component } from '@angular/core';
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 {
metadataRepresentation: ItemMetadataRepresentation;
}

View File

@@ -17,7 +17,8 @@ import { SearchResultListElementComponent } from '../../search-result-list-eleme
templateUrl: './item-search-result-list-element-submission.component.html'
})
@listableObjectComponent(ItemSearchResult, ViewMode.ListElement, Context.Submission)
@listableObjectComponent(ItemSearchResult, ViewMode.ListElement, Context.Workspace)
@listableObjectComponent(ItemSearchResult, ViewMode.ListElement, Context.Workflow)
export class ItemSearchResultListElementSubmissionComponent extends SearchResultListElementComponent<ItemSearchResult, Item> implements OnInit {
/**
* Represent item's status

View File

@@ -72,7 +72,7 @@ export class SearchFiltersComponent implements OnInit {
/**
* @returns {string} The base path to the search page, or the current page when inPlaceSearch is true
*/
private getSearchLink(): string {
getSearchLink(): string {
if (this.inPlaceSearch) {
return currentPath(this.router);
}

View File

@@ -167,6 +167,7 @@ import { PublicationSearchResultListElementComponent } from './object-list/searc
import { PublicationSearchResultGridElementComponent } from './object-grid/search-result-grid-element/item-search-result/publication/publication-search-result-grid-element.component';
import { ListableObjectDirective } from './object-collection/shared/listable-object/listable-object.directive';
import { SearchLabelComponent } from './search/search-labels/search-label/search-label.component';
import { ItemMetadataRepresentationListElementComponent } from './object-list/metadata-representation-list-element/item/item-metadata-representation-list-element.component';
const MODULES = [
// Do NOT include UniversalModule, HttpModule, or JsonpModule here
@@ -365,6 +366,7 @@ const ENTRY_COMPONENTS = [
PlainTextMetadataListElementComponent,
ItemMetadataListElementComponent,
MetadataRepresentationListElementComponent,
ItemMetadataRepresentationListElementComponent,
SearchResultsComponent,
CollectionSearchResultGridElementComponent,
CommunitySearchResultGridElementComponent,

View File

@@ -11368,10 +11368,10 @@ webdriver-manager@^12.0.6:
semver "^5.3.0"
xml2js "^0.4.17"
webdriver-manager@^12.1.6:
version "12.1.6"
resolved "https://registry.yarnpkg.com/webdriver-manager/-/webdriver-manager-12.1.6.tgz#9e5410c506d1a7e0a7aa6af91ba3d5bb37f362b6"
integrity sha512-B1mOycNCrbk7xODw7Jgq/mdD3qzPxMaTsnKIQDy2nXlQoyjTrJTTD0vRpEZI9b8RibPEyQvh9zIZ0M1mpOxS3w==
webdriver-manager@^12.1.7:
version "12.1.7"
resolved "https://registry.yarnpkg.com/webdriver-manager/-/webdriver-manager-12.1.7.tgz#ed4eaee8f906b33c146e869b55e850553a1b1162"
integrity sha512-XINj6b8CYuUYC93SG3xPkxlyUc3IJbD6Vvo75CVGuG9uzsefDzWQrhz0Lq8vbPxtb4d63CZdYophF8k8Or/YiA==
dependencies:
adm-zip "^0.4.9"
chalk "^1.1.1"