Merge pull request #497 from atmire/w2p-65195_dynamic-component-refactoring

Dynamic component loading refactoring
This commit is contained in:
Tim Donohue
2019-11-14 09:58:49 -06:00
committed by GitHub
350 changed files with 4619 additions and 3402 deletions

View File

@@ -1,6 +1,6 @@
<div class="row" *ngIf="item">
<div class="col-10 relationship">
<ds-item-type-switcher [object]="item" [viewMode]="viewMode"></ds-item-type-switcher>
<ds-listable-object-component-loader [object]="item" [viewMode]="viewMode"></ds-listable-object-component-loader>
</div>
<div class="col-2">
<div class="btn-group relationship-action-buttons">

View File

@@ -5,7 +5,6 @@ import { ObjectUpdatesService } from '../../../../core/data/object-updates/objec
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { EditRelationshipComponent } from './edit-relationship.component';
import { RelationshipType } from '../../../../core/shared/item-relationships/relationship-type.model';
import { ResourceType } from '../../../../core/shared/resource-type';
import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
import { RemoteData } from '../../../../core/data/remote-data';
import { Item } from '../../../../core/shared/item.model';

View File

@@ -4,7 +4,7 @@ import { cloneDeep } from 'lodash';
import { Item } from '../../../../core/shared/item.model';
import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service';
import { FieldChangeType } from '../../../../core/data/object-updates/object-updates.actions';
import { ItemViewMode } from '../../../../shared/items/item-type-decorator';
import { ViewMode } from '../../../../core/shared/view-mode.model';
@Component({
// tslint:disable-next-line:component-selector
@@ -31,7 +31,7 @@ export class EditRelationshipComponent implements OnChanges {
/**
* The view-mode we're currently on
*/
viewMode = ItemViewMode.Element;
viewMode = ViewMode.ListElement;
constructor(private objectUpdatesService: ObjectUpdatesService) {
}

View File

@@ -10,7 +10,7 @@ import { ItemPageFieldComponent } from '../item-page-field.component';
/**
* This component can be used to represent metadata on a simple item page.
* It is the most generic way of displaying metadata values
* It expects 4 parameters: The item, a seperator, the metadata keys and an i18n key
* It expects 4 parameters: The item, a separator, the metadata keys and an i18n key
*/
export class GenericItemPageFieldComponent extends ItemPageFieldComponent {

View File

@@ -4,12 +4,9 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { Item } from '../../../../core/shared/item.model';
import { PaginatedList } from '../../../../core/data/paginated-list';
import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loader';
import { Observable } from 'rxjs';
import { PageInfo } from '../../../../core/shared/page-info.model';
import { RemoteData } from '../../../../core/data/remote-data';
import { ItemPageFieldComponent } from './item-page-field.component';
import { MetadataValuesComponent } from '../../../field-components/metadata-values/metadata-values.component';
import { of as observableOf } from 'rxjs';
import { MetadataMap, MetadataValue } from '../../../../core/shared/metadata.models';
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/testing/utils';

View File

@@ -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-item-type-switcher [object]="item" [viewMode]="viewMode"></ds-item-type-switcher>
<ds-listable-object-component-loader [object]="item" [viewMode]="viewMode"></ds-listable-object-component-loader>
</div>
</div>
<ds-error *ngIf="itemRD?.hasFailed" message="{{'error.item' | translate}}"></ds-error>

View File

@@ -1,21 +1,18 @@
import { mergeMap, filter, map, take, tap } from 'rxjs/operators';
import { map } from 'rxjs/operators';
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { Observable } from 'rxjs';
import { ItemDataService } from '../../core/data/item-data.service';
import { RemoteData } from '../../core/data/remote-data';
import { Bitstream } from '../../core/shared/bitstream.model';
import { Item } from '../../core/shared/item.model';
import { MetadataService } from '../../core/metadata/metadata.service';
import { fadeInOut } from '../../shared/animations/fade';
import { hasValue } from '../../shared/empty.util';
import { redirectToPageNotFoundOn404 } from '../../core/shared/operators';
import { ItemViewMode } from '../../shared/items/item-type-decorator';
import { ViewMode } from '../../core/shared/view-mode.model';
/**
* This component renders a simple item page.
@@ -44,7 +41,7 @@ export class ItemPageComponent implements OnInit {
/**
* The view-mode we're currently on
*/
viewMode = ItemViewMode.Full;
viewMode = ViewMode.StandalonePage;
constructor(
private route: ActivatedRoute,
@@ -53,6 +50,9 @@ export class ItemPageComponent implements OnInit {
private metadataService: MetadataService,
) { }
/**
* Initialize instance variables
*/
ngOnInit(): void {
this.itemRD$ = this.route.data.pipe(
map((data) => data.item as RemoteData<Item>),

View File

@@ -1,72 +1,72 @@
<h2 class="item-page-title-field">
{{'publication.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="item?.allMetadata(['dc.title'])"></ds-metadata-values>
{{'publication.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
</h2>
<div class="row">
<div class="col-xs-12 col-md-4">
<ds-metadata-field-wrapper>
<ds-thumbnail [thumbnail]="this.item.getThumbnail() | async"></ds-thumbnail>
<ds-thumbnail [thumbnail]="object.getThumbnail() | async"></ds-thumbnail>
</ds-metadata-field-wrapper>
<ds-item-page-file-section [item]="item"></ds-item-page-file-section>
<ds-item-page-date-field [item]="item"></ds-item-page-date-field>
<ds-item-page-author-field [item]="item"></ds-item-page-author-field>
<ds-generic-item-page-field [item]="item"
<ds-item-page-file-section [item]="object"></ds-item-page-file-section>
<ds-item-page-date-field [item]="object"></ds-item-page-date-field>
<ds-item-page-author-field [item]="object"></ds-item-page-author-field>
<ds-generic-item-page-field [item]="object"
[fields]="['journal.title']"
[label]="'publication.page.journal-title'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
<ds-generic-item-page-field [item]="object"
[fields]="['journal.identifier.issn']"
[label]="'publication.page.journal-issn'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
<ds-generic-item-page-field [item]="object"
[fields]="['journalvolume.identifier.name']"
[label]="'publication.page.volume-title'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
<ds-generic-item-page-field [item]="object"
[fields]="['dc.publisher']"
[label]="'publication.page.publisher'">
</ds-generic-item-page-field>
</div>
<div class="col-xs-12 col-md-6">
<ds-metadata-representation-list
[parentItem]="item"
[parentItem]="object"
[itemType]="'Person'"
[metadataField]="'dc.contributor.author'"
[label]="'relationships.isAuthorOf' | translate">
</ds-metadata-representation-list>
<ds-related-items
[parentItem]="item"
[parentItem]="object"
[relationType]="'isProjectOfPublication'"
[label]="'relationships.isProjectOf' | translate">
</ds-related-items>
<ds-related-items
[parentItem]="item"
[parentItem]="object"
[relationType]="'isOrgUnitOfPublication'"
[label]="'relationships.isOrgUnitOf' | translate">
</ds-related-items>
<ds-related-items
[parentItem]="item"
[parentItem]="object"
[relationType]="'isJournalIssueOfPublication'"
[label]="'relationships.isJournalIssueOf' | translate">
</ds-related-items>
<ds-item-page-abstract-field [item]="item"></ds-item-page-abstract-field>
<ds-generic-item-page-field [item]="item"
<ds-item-page-abstract-field [item]="object"></ds-item-page-abstract-field>
<ds-generic-item-page-field [item]="object"
[fields]="['dc.description']"
[label]="'publication.page.description'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
<ds-generic-item-page-field [item]="object"
[fields]="['dc.subject']"
[separator]="','"
[label]="'item.page.subject'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
<ds-generic-item-page-field [item]="object"
[fields]="['dc.identifier.citation']"
[label]="'item.page.citation'">
</ds-generic-item-page-field>
<ds-item-page-uri-field [item]="item"></ds-item-page-uri-field>
<ds-item-page-collections [item]="item"></ds-item-page-collections>
<ds-item-page-uri-field [item]="object"></ds-item-page-uri-field>
<ds-item-page-collections [item]="object"></ds-item-page-collections>
<div>
<a class="btn btn-outline-primary" [routerLink]="['/items/' + item.id + '/full']">
<a class="btn btn-outline-primary" [routerLink]="['/items/' + object.id + '/full']">
{{"item.page.link.full" | translate}}
</a>
</div>

View File

@@ -3,19 +3,16 @@ 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/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 { 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/item.component.spec';
import { PublicationComponent } from './publication.component';
import { of as observableOf } from 'rxjs';
import { MetadataMap } from '../../../../core/shared/metadata.models';
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/testing/utils';
@@ -45,7 +42,6 @@ describe('PublicationComponent', () => {
})],
declarations: [PublicationComponent, GenericItemPageFieldComponent, TruncatePipe],
providers: [
{provide: ITEM, useValue: mockItem},
{provide: ItemDataService, useValue: {}},
{provide: SearchFixedFilterService, useValue: searchFixedFilterServiceStub},
{provide: TruncatableService, useValue: {}}
@@ -60,6 +56,7 @@ describe('PublicationComponent', () => {
beforeEach(async(() => {
fixture = TestBed.createComponent(PublicationComponent);
comp = fixture.componentInstance;
comp.object = mockItem;
fixture.detectChanges();
}));

View File

@@ -1,12 +1,15 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import {
DEFAULT_ITEM_TYPE, ItemViewMode,
rendersItemType
} from '../../../../shared/items/item-type-decorator';
import { ItemComponent } from '../shared/item.component';
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { Item } from '../../../../core/shared/item.model';
import { ViewMode } from '../../../../core/shared/view-mode.model';
@rendersItemType('Publication', ItemViewMode.Full)
@rendersItemType(DEFAULT_ITEM_TYPE, ItemViewMode.Full)
/**
* Component that represents a publication Item page
*/
@listableObjectComponent('Publication', ViewMode.StandalonePage)
@listableObjectComponent(Item, ViewMode.StandalonePage)
@Component({
selector: 'ds-publication',
styleUrls: ['./publication.component.scss'],

View File

@@ -7,17 +7,14 @@ 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/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/item-relationships/relationship-type.model';
import { PaginatedList } from '../../../../core/data/paginated-list';
import { RemoteData } from '../../../../core/data/remote-data';
import { Relationship } from '../../../../core/shared/item-relationships/relationship.model';
import { PageInfo } from '../../../../core/shared/page-info.model';
import { ItemComponent } from './item.component';
import { of as observableOf } from 'rxjs';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { VarDirective } from '../../../../shared/utils/var.directive';
import { Observable } from 'rxjs/internal/Observable';
@@ -56,7 +53,6 @@ export function getItemPageFieldsTest(mockItem: Item, component) {
})],
declarations: [component, GenericItemPageFieldComponent, TruncatePipe],
providers: [
{provide: ITEM, useValue: mockItem},
{provide: ItemDataService, useValue: {}},
{provide: SearchFixedFilterService, useValue: searchFixedFilterServiceStub},
{provide: TruncatableService, useValue: {}}
@@ -71,6 +67,7 @@ export function getItemPageFieldsTest(mockItem: Item, component) {
beforeEach(async(() => {
fixture = TestBed.createComponent(component);
comp = fixture.componentInstance;
comp.object = mockItem;
fixture.detectChanges();
}));

View File

@@ -1,6 +1,5 @@
import { Component, Inject } from '@angular/core';
import { Component, Inject, Input } from '@angular/core';
import { Item } from '../../../../core/shared/item.model';
import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component';
@Component({
selector: 'ds-item',
@@ -10,9 +9,5 @@ import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.compo
* A generic component for displaying metadata and relations of an item
*/
export class ItemComponent {
constructor(
@Inject(ITEM) public item: Item
) {}
@Input() object: Item;
}

View File

@@ -1,7 +1,7 @@
<ds-metadata-field-wrapper *ngIf="representations$ && (representations$ | async)?.length > 0" [label]="label">
<ds-item-type-switcher *ngFor="let rep of (representations$ | async)"
[object]="rep" [viewMode]="viewMode">
</ds-item-type-switcher>
<ds-metadata-representation-loader *ngFor="let rep of (representations$ | async)"
[mdRepresentation]="rep">
</ds-metadata-representation-loader>
<div *ngIf="(representations$ | async)?.length < total" class="mt-2">
<a [routerLink]="" (click)="viewMore()">{{'item.page.related-items.view-more' | translate}}</a>
</div>

View File

@@ -83,8 +83,8 @@ describe('MetadataRepresentationListComponent', () => {
fixture.detectChanges();
}));
it('should load 2 item-type-switcher components', () => {
const fields = fixture.debugElement.queryAll(By.css('ds-item-type-switcher'));
it('should load 2 ds-metadata-representation-loader components', () => {
const fields = fixture.debugElement.queryAll(By.css('ds-metadata-representation-loader'));
expect(fields.length).toBe(2);
});

View File

@@ -1,11 +1,10 @@
import { Component, Input, OnInit } from '@angular/core';
import { MetadataRepresentation } from '../../../core/shared/metadata-representation/metadata-representation.model';
import { ItemViewMode } from '../../../shared/items/item-type-decorator';
import { Observable } from 'rxjs/internal/Observable';
import { RemoteData } from '../../../core/data/remote-data';
import { RelationshipService } from '../../../core/data/relationship.service';
import { Item } from '../../../core/shared/item.model';
import { zip as observableZip, combineLatest as observableCombineLatest, of as observableOf } from 'rxjs';
import { combineLatest as observableCombineLatest, of as observableOf, zip as observableZip } from 'rxjs';
import { MetadataValue } from '../../../core/shared/metadata.models';
import { MetadatumRepresentation } from '../../../core/shared/metadata-representation/metadatum/metadatum-representation.model';
import { filter, map, switchMap } from 'rxjs/operators';
@@ -56,12 +55,6 @@ export class MetadataRepresentationListComponent implements OnInit {
*/
representations$: Observable<MetadataRepresentation[]>;
/**
* The view-mode we're currently on
* @type {ElementViewMode}
*/
viewMode = ItemViewMode.Metadata;
/**
* The originally provided limit
* Used for resetting the limit to the original value when collapsing the list

View File

@@ -1,14 +1,12 @@
import { Component, HostBinding, Input, OnDestroy, OnInit } from '@angular/core';
import { Item } from '../../../core/shared/item.model';
import { ItemViewMode } from '../../../shared/items/item-type-decorator';
import { Observable } from 'rxjs/internal/Observable';
import { RemoteData } from '../../../core/data/remote-data';
import { PaginatedList } from '../../../core/data/paginated-list';
import { RelationshipService } from '../../../core/data/relationship.service';
import { FindAllOptions } from '../../../core/data/request.models';
import { getRemoteDataPayload, getSucceededRemoteData } from '../../../core/shared/operators';
import { hasNoValue, hasValueOperator } from '../../../shared/empty.util';
import { Subscription } from 'rxjs/internal/Subscription';
import { ViewMode } from '../../../core/shared/view-mode.model';
@Component({
selector: 'ds-related-items',
@@ -61,7 +59,7 @@ export class RelatedItemsComponent implements OnInit, OnDestroy {
* The view-mode we're currently on
* @type {ElementViewMode}
*/
viewMode = ItemViewMode.Element;
viewMode = ViewMode.ListElement;
/**
* Whether or not the list is currently expanded to show all related items

View File

@@ -1,7 +1,7 @@
<ds-metadata-field-wrapper *ngIf="(items$ | async)?.payload?.page?.length > 0" [label]="label">
<ds-item-type-switcher *ngFor="let item of (items$ | async)?.payload?.page"
<ds-listable-object-component-loader *ngFor="let item of (items$ | async)?.payload?.page"
[object]="item" [viewMode]="viewMode">
</ds-item-type-switcher>
</ds-listable-object-component-loader>
<div *ngIf="(items$ | async)?.payload?.page?.length < (items$ | async)?.payload?.totalElements" class="mt-2" id="view-more">
<a [routerLink]="" (click)="viewMore()">{{'item.page.related-items.view-more' | translate}}</a>
</div>

View File

@@ -61,7 +61,7 @@ describe('RelatedItemsComponent', () => {
}));
it(`should load ${mockItems.length} item-type-switcher components`, () => {
const fields = fixture.debugElement.queryAll(By.css('ds-item-type-switcher'));
const fields = fixture.debugElement.queryAll(By.css('ds-listable-object-component-loader'));
expect(fields.length).toBe(mockItems.length);
});

View File

@@ -7,7 +7,6 @@ import { TranslateService } from '@ngx-translate/core';
import { SubmissionState } from '../../submission/submission.reducers';
import { AuthService } from '../../core/auth/auth.service';
import { MyDSpaceResult } from '../my-dspace-result.model';
import { DSpaceObject } from '../../core/shared/dspace-object.model';
import { NotificationsService } from '../../shared/notifications/notifications.service';
import { NotificationOptions } from '../../shared/notifications/models/notification-options.model';
@@ -15,6 +14,7 @@ import { UploaderOptions } from '../../shared/uploader/uploader-options.model';
import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
import { NotificationType } from '../../shared/notifications/models/notification-type';
import { hasValue } from '../../shared/empty.util';
import { SearchResult } from '../../+search-page/search-result.model';
/**
* This component represents the whole mydspace page header
@@ -25,7 +25,10 @@ import { hasValue } from '../../shared/empty.util';
templateUrl: './my-dspace-new-submission.component.html'
})
export class MyDSpaceNewSubmissionComponent implements OnDestroy, OnInit {
@Output() uploadEnd = new EventEmitter<Array<MyDSpaceResult<DSpaceObject>>>();
/**
* Output that emits the workspace item when the upload has completed
*/
@Output() uploadEnd = new EventEmitter<Array<SearchResult<DSpaceObject>>>();
/**
* The UploaderOptions object

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';
@@ -20,7 +20,6 @@ import { SearchService } from '../+search-page/search-service/search.service';
import { SearchSidebarService } from '../+search-page/search-sidebar/search-sidebar.service';
import { hasValue } from '../shared/empty.util';
import { getSucceededRemoteData } from '../core/shared/operators';
import { MyDSpaceResult } from './my-dspace-result.model';
import { MyDSpaceResponseParsingService } from '../core/data/mydspace-response-parsing.service';
import { SearchConfigurationOption } from '../+search-page/search-switch-configuration/search-configuration-option.model';
import { RoleType } from '../core/roles/role-types';
@@ -28,6 +27,8 @@ import { SearchConfigurationService } from '../+search-page/search-service/searc
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 '../+search-page/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');
@@ -63,7 +64,7 @@ export class MyDSpacePageComponent implements OnInit {
/**
* The current search results
*/
resultsRD$: BehaviorSubject<RemoteData<PaginatedList<MyDSpaceResult<DSpaceObject>>>> = new BehaviorSubject(null);
resultsRD$: BehaviorSubject<RemoteData<PaginatedList<SearchResult<DSpaceObject>>>> = new BehaviorSubject(null);
/**
* The current paginated search options
@@ -93,7 +94,12 @@ export class MyDSpacePageComponent implements OnInit {
/**
* List of available view mode
*/
viewModeList = [ViewMode.List, ViewMode.Detail];
viewModeList = [ViewMode.ListElement, ViewMode.DetailedListElement];
/**
* The current context of this page: workspace or workflow
*/
context$: Observable<Context>;
constructor(private service: SearchService,
private sidebarService: SearchSidebarService,
@@ -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

@@ -7,19 +7,20 @@ import { MyDspacePageRoutingModule } from './my-dspace-page-routing.module';
import { MyDSpacePageComponent } from './my-dspace-page.component';
import { SearchPageModule } from '../+search-page/search-page.module';
import { MyDSpaceResultsComponent } from './my-dspace-results/my-dspace-results.component';
import { WorkspaceitemMyDSpaceResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-list-element.component';
import { ItemMyDSpaceResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/item-my-dspace-result/item-my-dspace-result-list-element.component';
import { WorkflowitemMyDSpaceResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-list-element.component';
import { ClaimedMyDSpaceResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/claimed-my-dspace-result/claimed-my-dspace-result-list-element.component';
import { PoolMyDSpaceResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/pool-my-dspace-result/pool-my-dspace-result-list-element.component';
import { WorkspaceItemSearchResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/workspace-item-search-result/workspace-item-search-result-list-element.component';
import { ClaimedSearchResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-search-result-list-element.component';
import { PoolSearchResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/pool-search-result/pool-search-result-list-element.component';
import { MyDSpaceNewSubmissionComponent } from './my-dspace-new-submission/my-dspace-new-submission.component';
import { ItemMyDSpaceResultDetailElementComponent } from '../shared/object-detail/my-dspace-result-detail-element/item-my-dspace-result/item-my-dspace-result-detail-element.component';
import { WorkspaceitemMyDSpaceResultDetailElementComponent } from '../shared/object-detail/my-dspace-result-detail-element/workspaceitem-my-dspace-result/workspaceitem-my-dspace-result-detail-element.component';
import { WorkflowitemMyDSpaceResultDetailElementComponent } from '../shared/object-detail/my-dspace-result-detail-element/workflowitem-my-dspace-result/workflowitem-my-dspace-result-detail-element.component';
import { ClaimedMyDSpaceResultDetailElementComponent } from '../shared/object-detail/my-dspace-result-detail-element/claimed-my-dspace-result/claimed-my-dspace-result-detail-element.component';
import { PoolMyDSpaceResultDetailElementComponent } from '../shared/object-detail/my-dspace-result-detail-element/pool-my-dspace-result/pool-my-dspace-result-detail-lement.component';
import { ItemSearchResultDetailElementComponent } from '../shared/object-detail/my-dspace-result-detail-element/item-search-result/item-search-result-detail-element.component';
import { WorkspaceItemSearchResultDetailElementComponent } from '../shared/object-detail/my-dspace-result-detail-element/workspace-item-search-result/workspace-item-search-result-detail-element.component';
import { WorkflowItemSearchResultDetailElementComponent } from '../shared/object-detail/my-dspace-result-detail-element/workflow-item-search-result/workflow-item-search-result-detail-element.component';
import { ClaimedTaskSearchResultDetailElementComponent } from '../shared/object-detail/my-dspace-result-detail-element/claimed-task-search-result/claimed-task-search-result-detail-element.component';
import { MyDSpaceGuard } from './my-dspace.guard';
import { MyDSpaceConfigurationService } from './my-dspace-configuration.service';
import { SearchResultListElementComponent } from '../shared/object-list/search-result-list-element/search-result-list-element.component';
import { ItemSearchResultListElementSubmissionComponent } from '../shared/object-list/my-dspace-result-list-element/item-search-result/item-search-result-list-element-submission.component';
import { WorkflowItemSearchResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/workflow-item-search-result/workflow-item-search-result-list-element.component';
import { PoolSearchResultDetailElementComponent } from '../shared/object-detail/my-dspace-result-detail-element/pool-search-result/pool-search-result-detail-element.component';
@NgModule({
imports: [
@@ -31,33 +32,34 @@ import { MyDSpaceConfigurationService } from './my-dspace-configuration.service'
declarations: [
MyDSpacePageComponent,
MyDSpaceResultsComponent,
ItemMyDSpaceResultListElementComponent,
WorkspaceitemMyDSpaceResultListElementComponent,
WorkflowitemMyDSpaceResultListElementComponent,
ClaimedMyDSpaceResultListElementComponent,
PoolMyDSpaceResultListElementComponent,
ItemMyDSpaceResultDetailElementComponent,
WorkspaceitemMyDSpaceResultDetailElementComponent,
WorkflowitemMyDSpaceResultDetailElementComponent,
ClaimedMyDSpaceResultDetailElementComponent,
PoolMyDSpaceResultDetailElementComponent,
MyDSpaceNewSubmissionComponent
WorkspaceItemSearchResultListElementComponent,
WorkflowItemSearchResultListElementComponent,
ClaimedSearchResultListElementComponent,
PoolSearchResultListElementComponent,
ItemSearchResultDetailElementComponent,
WorkspaceItemSearchResultDetailElementComponent,
WorkflowItemSearchResultDetailElementComponent,
ClaimedTaskSearchResultDetailElementComponent,
PoolSearchResultDetailElementComponent,
MyDSpaceNewSubmissionComponent,
ItemSearchResultListElementSubmissionComponent
],
providers: [
MyDSpaceGuard,
MyDSpaceConfigurationService
],
entryComponents: [
ItemMyDSpaceResultListElementComponent,
WorkspaceitemMyDSpaceResultListElementComponent,
WorkflowitemMyDSpaceResultListElementComponent,
ClaimedMyDSpaceResultListElementComponent,
PoolMyDSpaceResultListElementComponent,
ItemMyDSpaceResultDetailElementComponent,
WorkspaceitemMyDSpaceResultDetailElementComponent,
WorkflowitemMyDSpaceResultDetailElementComponent,
ClaimedMyDSpaceResultDetailElementComponent,
PoolMyDSpaceResultDetailElementComponent
SearchResultListElementComponent,
WorkspaceItemSearchResultListElementComponent,
WorkflowItemSearchResultListElementComponent,
ClaimedSearchResultListElementComponent,
PoolSearchResultListElementComponent,
ItemSearchResultDetailElementComponent,
WorkspaceItemSearchResultDetailElementComponent,
WorkflowItemSearchResultDetailElementComponent,
ClaimedTaskSearchResultDetailElementComponent,
PoolSearchResultDetailElementComponent,
ItemSearchResultListElementSubmissionComponent
]
})

View File

@@ -1,19 +0,0 @@
import { DSpaceObject } from '../core/shared/dspace-object.model';
import { MetadataMap } from '../core/shared/metadata.models';
import { ListableObject } from '../shared/object-collection/shared/listable-object.model';
/**
* Represents a search result object of a certain (<T>) DSpaceObject
*/
export class MyDSpaceResult<T extends DSpaceObject> implements ListableObject {
/**
* The DSpaceObject that was found
*/
indexableObject: T;
/**
* The metadata that was used to find this item, hithighlighted
*/
hitHighlights: MetadataMap;
}

View File

@@ -4,7 +4,8 @@
[hasBorder]="hasBorder"
[sortConfig]="searchConfig.sort"
[objects]="searchResults"
[hideGear]="true">
[hideGear]="true"
[context]="context">
</ds-viewable-collection>
</div>
<ds-loading *ngIf="isLoading()" message="{{'loading.mydspace-results' | translate}}"></ds-loading>

View File

@@ -1,13 +1,13 @@
import { Component, Input } from '@angular/core';
import { RemoteData } from '../../core/data/remote-data';
import { DSpaceObject } from '../../core/shared/dspace-object.model';
import { fadeIn, fadeInOut } from '../../shared/animations/fade';
import { MyDSpaceResult } from '../my-dspace-result.model';
import { SearchOptions } from '../../+search-page/search-options.model';
import { PaginatedList } from '../../core/data/paginated-list';
import { ViewMode } from '../../core/shared/view-mode.model';
import { isEmpty } from '../../shared/empty.util';
import { SearchResult } from '../../+search-page/search-result.model';
import { Context } from '../../core/shared/context.model';
/**
* Component that represents all results for mydspace page
@@ -25,7 +25,7 @@ export class MyDSpaceResultsComponent {
/**
* The actual search result objects
*/
@Input() searchResults: RemoteData<PaginatedList<MyDSpaceResult<DSpaceObject>>>;
@Input() searchResults: RemoteData<PaginatedList<SearchResult<DSpaceObject>>>;
/**
* The current configuration of the search
@@ -37,6 +37,10 @@ export class MyDSpaceResultsComponent {
*/
@Input() viewMode: ViewMode;
/**
* The current context for the search results
*/
@Input() context: Context;
/**
* A boolean representing if search results entry are separated by a line
*/

View File

@@ -1,13 +1,12 @@
import { autoserialize, inheritSerialization } from 'cerialize';
import { MetadataMap } from '../core/shared/metadata.models';
import { ListableObject } from '../shared/object-collection/shared/listable-object.model';
import { NormalizedObject } from '../core/cache/models/normalized-object.model';
/**
* Represents a normalized version of a search result object of a certain DSpaceObject
*/
@inheritSerialization(NormalizedObject)
export class NormalizedSearchResult implements ListableObject {
export class NormalizedSearchResult {
/**
* The UUID of the DSpaceObject that was found
*/
@@ -19,5 +18,4 @@ export class NormalizedSearchResult implements ListableObject {
*/
@autoserialize
hitHighlights: MetadataMap;
}

View File

@@ -71,7 +71,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 './';
}

View File

@@ -3,14 +3,14 @@ import { URLCombiner } from '../core/url-combiner/url-combiner';
import 'core-js/library/fn/object/entries';
import { SearchFilter } from './search-filter.model';
import { DSpaceObjectType } from '../core/shared/dspace-object-type.model';
import { SetViewMode } from '../shared/view-mode';
import { ViewMode } from '../core/shared/view-mode.model';
/**
* This model class represents all parameters needed to request information about a certain search request
*/
export class SearchOptions {
configuration?: string;
view?: SetViewMode = SetViewMode.List;
view?: ViewMode = ViewMode.ListElement;
scope?: string;
query?: string;
dsoType?: DSpaceObjectType;

View File

@@ -5,7 +5,6 @@ import { SharedModule } from '../shared/shared.module';
import { SearchPageRoutingModule } from './search-page-routing.module';
import { SearchPageComponent } from './search-page.component';
import { SearchResultsComponent } from './search-results/search-results.component';
import { ItemSearchResultGridElementComponent } from '../shared/object-grid/search-result-grid-element/item-search-result/item-search-result-grid-element.component';
import { CommunitySearchResultGridElementComponent } from '../shared/object-grid/search-result-grid-element/community-search-result/community-search-result-grid-element.component'
import { CollectionSearchResultGridElementComponent } from '../shared/object-grid/search-result-grid-element/collection-search-result/collection-search-result-grid-element.component';
import { SearchSidebarComponent } from './search-sidebar/search-sidebar.component';
@@ -44,9 +43,6 @@ const components = [
SearchResultsComponent,
SearchSidebarComponent,
SearchSettingsComponent,
ItemSearchResultGridElementComponent,
CollectionSearchResultGridElementComponent,
CommunitySearchResultGridElementComponent,
SearchFiltersComponent,
SearchFilterComponent,
SearchFacetFilterComponent,
@@ -84,9 +80,6 @@ const components = [
SearchConfigurationService
],
entryComponents: [
ItemSearchResultGridElementComponent,
CollectionSearchResultGridElementComponent,
CommunitySearchResultGridElementComponent,
SearchFacetFilterComponent,
SearchRangeFilterComponent,
SearchTextFilterComponent,

View File

@@ -1,6 +1,7 @@
import { DSpaceObject } from '../core/shared/dspace-object.model';
import { MetadataMap } from '../core/shared/metadata.models';
import { ListableObject } from '../shared/object-collection/shared/listable-object.model';
import { GenericConstructor } from '../core/shared/generic-constructor';
/**
* Represents a search result object of a certain (<T>) DSpaceObject
@@ -16,4 +17,10 @@ export class SearchResult<T extends DSpaceObject> implements ListableObject {
*/
hitHighlights: MetadataMap;
/**
* Method that returns as which type of object this object should be rendered
*/
getRenderTypes(): Array<string | GenericConstructor<ListableObject>> {
return [this.constructor as GenericConstructor<ListableObject>];
}
}

View File

@@ -4,6 +4,7 @@
[config]="searchConfig.pagination"
[sortConfig]="searchConfig.sort"
[objects]="searchResults"
[linkType]="linkType"
[hideGear]="true">
</ds-viewable-collection></div>
<ds-loading *ngIf="hasNoValue(searchResults) || hasNoValue(searchResults.payload) || searchResults.isLoading" message="{{'loading.search-results' | translate}}"></ds-loading>

View File

@@ -2,12 +2,13 @@ import { Component, Input } from '@angular/core';
import { RemoteData } from '../../core/data/remote-data';
import { DSpaceObject } from '../../core/shared/dspace-object.model';
import { fadeIn, fadeInOut } from '../../shared/animations/fade';
import { SetViewMode } from '../../shared/view-mode';
import { SearchOptions } from '../search-options.model';
import { SearchResult } from '../search-result.model';
import { PaginatedList } from '../../core/data/paginated-list';
import { hasNoValue, isNotEmpty } from '../../shared/empty.util';
import { SortOptions } from '../../core/cache/models/sort-options.model';
import { ViewMode } from '../../core/shared/view-mode.model';
import { CollectionElementLinkType } from '../../shared/object-collection/collection-element-link.type';
@Component({
selector: 'ds-search-results',
@@ -24,6 +25,11 @@ import { SortOptions } from '../../core/cache/models/sort-options.model';
export class SearchResultsComponent {
hasNoValue = hasNoValue;
/**
* The link type of the listed search results
*/
@Input() linkType: CollectionElementLinkType;
/**
* The actual search result objects
*/
@@ -42,7 +48,7 @@ export class SearchResultsComponent {
/**
* The current view-mode of the list
*/
@Input() viewMode: SetViewMode;
@Input() viewMode: ViewMode;
/**
* An optional configuration to filter the result on one type

View File

@@ -12,19 +12,12 @@ const searchResultMap = new Map();
* @param {GenericConstructor<ListableObject>} domainConstructor The constructor of the DSpaceObject
* @returns Decorator function that performs the actual mapping on initialization of the component
*/
export function searchResultFor(domainConstructor: GenericConstructor<ListableObject>, configuration: string = null) {
export function searchResultFor(domainConstructor: GenericConstructor<ListableObject>) {
return function decorator(searchResult: any) {
if (!searchResult) {
return;
}
if (isNull(configuration)) {
searchResultMap.set(domainConstructor, searchResult);
} else {
if (!searchResultMap.get(configuration)) {
searchResultMap.set(configuration, new Map());
}
searchResultMap.get(configuration).set(domainConstructor, searchResult);
}
};
}
@@ -33,10 +26,6 @@ export function searchResultFor(domainConstructor: GenericConstructor<ListableOb
* @param {GenericConstructor<ListableObject>} domainConstructor The DSpaceObject's constructor for which the search result component is requested
* @returns The component's constructor that matches the given DSpaceObject
*/
export function getSearchResultFor(domainConstructor: GenericConstructor<ListableObject>, configuration: string = null) {
if (isNull(configuration) || configuration === 'default' || hasNoValue(searchResultMap.get(configuration))) {
export function getSearchResultFor(domainConstructor: GenericConstructor<ListableObject>) {
return searchResultMap.get(domainConstructor);
} else {
return searchResultMap.get(configuration).get(domainConstructor);
}
}

View File

@@ -5,9 +5,6 @@ import { CommonModule } from '@angular/common';
import { Component } from '@angular/core';
import { SearchService } from './search.service';
import { ItemDataService } from './../../core/data/item-data.service';
import { SetViewMode } from '../../shared/view-mode';
import { GLOBAL_CONFIG } from '../../../config';
import { RemoteDataBuildService } from '../../core/cache/builders/remote-data-build.service';
import { Router, UrlTree } from '@angular/router';
import { RequestService } from '../../core/data/request.service';
@@ -66,7 +63,7 @@ describe('SearchService', () => {
it('should return list view mode', () => {
searchService.getViewMode().subscribe((viewMode) => {
expect(viewMode).toBe(ViewMode.List);
expect(viewMode).toBe(ViewMode.ListElement);
});
});
});
@@ -125,38 +122,38 @@ describe('SearchService', () => {
});
it('should call the navigate method on the Router with view mode list parameter as a parameter when setViewMode is called', () => {
searchService.setViewMode(ViewMode.List);
searchService.setViewMode(ViewMode.ListElement);
expect(router.navigate).toHaveBeenCalledWith(['/search'], {
queryParams: { view: ViewMode.List, page: 1 },
queryParams: { view: ViewMode.ListElement, page: 1 },
queryParamsHandling: 'merge'
});
});
it('should call the navigate method on the Router with view mode grid parameter as a parameter when setViewMode is called', () => {
searchService.setViewMode(ViewMode.Grid);
searchService.setViewMode(ViewMode.GridElement);
expect(router.navigate).toHaveBeenCalledWith(['/search'], {
queryParams: { view: ViewMode.Grid, page: 1 },
queryParams: { view: ViewMode.GridElement, page: 1 },
queryParamsHandling: 'merge'
});
});
it('should return ViewMode.List when the viewMode is set to ViewMode.List in the ActivatedRoute', () => {
let viewMode = ViewMode.Grid;
let viewMode = ViewMode.GridElement;
spyOn(routeService, 'getQueryParamMap').and.returnValue(observableOf(new Map([
[ 'view', ViewMode.List ],
[ 'view', ViewMode.ListElement ],
])));
searchService.getViewMode().subscribe((mode) => viewMode = mode);
expect(viewMode).toEqual(ViewMode.List);
expect(viewMode).toEqual(ViewMode.ListElement);
});
it('should return ViewMode.Grid when the viewMode is set to ViewMode.Grid in the ActivatedRoute', () => {
let viewMode = ViewMode.List;
let viewMode = ViewMode.ListElement;
spyOn(routeService, 'getQueryParamMap').and.returnValue(observableOf(new Map([
[ 'view', ViewMode.Grid ],
[ 'view', ViewMode.GridElement ],
])));
searchService.getViewMode().subscribe((mode) => viewMode = mode);
expect(viewMode).toEqual(ViewMode.Grid);
expect(viewMode).toEqual(ViewMode.GridElement);
});
describe('when search is called', () => {

View File

@@ -160,7 +160,7 @@ export class SearchService implements OnDestroy {
let co = DSpaceObject;
if (dsos.payload[index]) {
const constructor: GenericConstructor<ListableObject> = dsos.payload[index].constructor as GenericConstructor<ListableObject>;
co = getSearchResultFor(constructor, searchOptions.configuration);
co = getSearchResultFor(constructor);
return Object.assign(new co(), object, {
indexableObject: dsos.payload[index]
});
@@ -341,7 +341,7 @@ export class SearchService implements OnDestroy {
if (isNotEmpty(params.get('view')) && hasValue(params.get('view'))) {
return params.get('view');
} else {
return ViewMode.List;
return ViewMode.ListElement;
}
}));
}
@@ -354,7 +354,7 @@ export class SearchService implements OnDestroy {
this.routeService.getQueryParameterValue('pageSize').pipe(first())
.subscribe((pageSize) => {
let queryParams = { view: viewMode, page: 1 };
if (viewMode === ViewMode.Detail) {
if (viewMode === ViewMode.DetailedListElement) {
queryParams = Object.assign(queryParams, {pageSize: '1'});
} else if (pageSize === '1') {
queryParams = Object.assign(queryParams, {pageSize: '10'});

View File

@@ -1,7 +1,6 @@
import { autoserialize, deserialize, inheritSerialization } from 'cerialize';
import { CacheableObject } from '../../cache/object-cache.reducer';
import { ListableObject } from '../../../shared/object-collection/shared/listable-object.model';
import { NormalizedDSpaceObject } from '../../cache/models/normalized-dspace-object.model';
import { EPerson } from './eperson.model';
import { mapsTo, relationship } from '../../cache/builders/build-decorators';
@@ -9,7 +8,7 @@ import { Group } from './group.model';
@mapsTo(EPerson)
@inheritSerialization(NormalizedDSpaceObject)
export class NormalizedEPerson extends NormalizedDSpaceObject<EPerson> implements CacheableObject, ListableObject {
export class NormalizedEPerson extends NormalizedDSpaceObject<EPerson> implements CacheableObject {
/**
* A string representing the unique handle of this EPerson
*/

View File

@@ -1,14 +1,13 @@
import { autoserialize, deserialize, inheritSerialization } from 'cerialize';
import { CacheableObject } from '../../cache/object-cache.reducer';
import { ListableObject } from '../../../shared/object-collection/shared/listable-object.model';
import { NormalizedDSpaceObject } from '../../cache/models/normalized-dspace-object.model';
import { mapsTo, relationship } from '../../cache/builders/build-decorators';
import { Group } from './group.model';
@mapsTo(Group)
@inheritSerialization(NormalizedDSpaceObject)
export class NormalizedGroup extends NormalizedDSpaceObject<Group> implements CacheableObject, ListableObject {
export class NormalizedGroup extends NormalizedDSpaceObject<Group> implements CacheableObject {
/**
* List of Groups that this Group belong to

View File

@@ -2,6 +2,7 @@ import { ListableObject } from '../../shared/object-collection/shared/listable-o
import { isNotEmpty } from '../../shared/empty.util';
import { MetadataSchema } from './metadata-schema.model';
import { ResourceType } from '../shared/resource-type';
import { GenericConstructor } from '../shared/generic-constructor';
/**
* Class the represents a metadata field
@@ -50,4 +51,11 @@ export class MetadataField implements ListableObject {
}
return key;
}
/**
* Method that returns as which type of object this object should be rendered
*/
getRenderTypes(): Array<string | GenericConstructor<ListableObject>> {
return [this.constructor as GenericConstructor<ListableObject>];
}
}

View File

@@ -1,5 +1,6 @@
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
import { ResourceType } from '../shared/resource-type';
import { GenericConstructor } from '../shared/generic-constructor';
/**
* Class that represents a metadata schema
@@ -26,4 +27,11 @@ export class MetadataSchema implements ListableObject {
* The namespace of this metadata schema
*/
namespace: string;
/**
* Method that returns as which type of object this object should be rendered
*/
getRenderTypes(): Array<string | GenericConstructor<ListableObject>> {
return [this.constructor as GenericConstructor<ListableObject>];
}
}

View File

@@ -10,7 +10,7 @@ import { MetadataSchema } from './metadata-schema.model';
*/
@mapsTo(MetadataField)
@inheritSerialization(NormalizedObject)
export class NormalizedMetadataField extends NormalizedObject<MetadataField> implements ListableObject {
export class NormalizedMetadataField extends NormalizedObject<MetadataField> {
/**
* The identifier of this normalized metadata field

View File

@@ -9,7 +9,7 @@ import { MetadataSchema } from './metadata-schema.model';
*/
@mapsTo(MetadataSchema)
@inheritSerialization(NormalizedObject)
export class NormalizedMetadataSchema extends NormalizedObject<MetadataSchema> implements ListableObject {
export class NormalizedMetadataSchema extends NormalizedObject<MetadataSchema> {
/**
* The unique identifier for this schema
*/

View File

@@ -55,22 +55,24 @@ describe('RegistryService', () => {
});
const mockSchemasList = [
{
Object.assign(new MetadataSchema(), {
id: 1,
self: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadataschemas/1',
prefix: 'dc',
namespace: 'http://dublincore.org/documents/dcmi-terms/',
type: MetadataSchema.type
},
{
}),
Object.assign(new MetadataSchema(), {
id: 2,
self: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadataschemas/2',
prefix: 'mock',
namespace: 'http://dspace.org/mockschema',
type: MetadataSchema.type
}
})
];
const mockFieldsList = [
Object.assign(new MetadataField(),
{
id: 1,
self: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadatafields/8',
@@ -79,7 +81,8 @@ describe('RegistryService', () => {
scopeNote: null,
schema: mockSchemasList[0],
type: MetadataField.type
},
}),
Object.assign(new MetadataField(),
{
id: 2,
self: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadatafields/9',
@@ -88,7 +91,8 @@ describe('RegistryService', () => {
scopeNote: null,
schema: mockSchemasList[0],
type: MetadataField.type
},
}),
Object.assign(new MetadataField(),
{
id: 3,
self: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadatafields/10',
@@ -97,7 +101,8 @@ describe('RegistryService', () => {
scopeNote: 'test scope note',
schema: mockSchemasList[1],
type: MetadataField.type
},
}),
Object.assign(new MetadataField(),
{
id: 4,
self: 'https://dspace7.4science.it/dspace-spring-rest/api/core/metadatafields/11',
@@ -106,7 +111,7 @@ describe('RegistryService', () => {
scopeNote: null,
schema: mockSchemasList[1],
type: MetadataField.type
}
})
];
const pageInfo = new PageInfo();

View File

@@ -400,7 +400,7 @@ export class RegistryService {
distinctUntilChanged()
);
const serializedSchema = new DSpaceRESTv2Serializer(getMapsToType(MetadataSchema.type)).serialize(schema as NormalizedMetadataSchema);
const serializedSchema = new DSpaceRESTv2Serializer(getMapsToType(MetadataSchema.type)).serialize(schema);
const request$ = endpoint$.pipe(
take(1),

View File

@@ -1,12 +1,13 @@
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
import { TypedObject } from '../cache/object-cache.reducer';
import { ResourceType } from './resource-type';
import { GenericConstructor } from './generic-constructor';
/**
* Class object representing a browse entry
* This class is not normalized because browse entries do not have self links
*/
export class BrowseEntry implements ListableObject, TypedObject {
export class BrowseEntry implements ListableObject {
static type = new ResourceType('browseEntry');
/**
@@ -28,4 +29,11 @@ export class BrowseEntry implements ListableObject, TypedObject {
* The count of this browse entry
*/
count: number;
/**
* Method that returns as which type of object this object should be rendered
*/
getRenderTypes(): Array<string | GenericConstructor<ListableObject>> {
return [this.constructor as GenericConstructor<ListableObject>];
}
}

View File

@@ -0,0 +1,13 @@
/**
* This enumeration represents all possible ways of representing a group of objects in the UI
*/
export enum Context {
Undefined = 'undefined',
ItemPage = 'itemPage',
Search = 'search',
Workflow = 'workflow',
Workspace = 'workspace',
AdminMenu = 'adminMenu',
SubmissionModal = 'submissionModal',
}

View File

@@ -12,6 +12,7 @@ import { CacheableObject } from '../cache/object-cache.reducer';
import { RemoteData } from '../data/remote-data';
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
import { ResourceType } from './resource-type';
import { GenericConstructor } from './generic-constructor';
/**
* An abstract model class for a DSpaceObject.
@@ -109,7 +110,7 @@ export class DSpaceObject implements CacheableObject, ListableObject {
* Like [[firstMetadata]], but only returns a string value, or `undefined`.
*
* @param {string|string[]} keyOrKeys The metadata key(s) in scope. Wildcards are supported; see [[Metadata]].
* @param {MetadataValueFilter} filter The value filter to use. If unspecified, no filtering will be done.
* @param {MetadataValueFilter} valueFilter The value filter to use. If unspecified, no filtering will be done.
* @returns {string} the first matching string value, or `undefined`.
*/
firstMetadataValue(keyOrKeys: string | string[], valueFilter?: MetadataValueFilter): string {
@@ -146,4 +147,10 @@ export class DSpaceObject implements CacheableObject, ListableObject {
});
}
/**
* Method that returns as which type of object this object should be rendered
*/
getRenderTypes(): Array<string | GenericConstructor<ListableObject>> {
return [this.constructor as GenericConstructor<ListableObject>];
}
}

View File

@@ -5,12 +5,18 @@ import { DSpaceObject } from './dspace-object.model';
import { Collection } from './collection.model';
import { RemoteData } from '../data/remote-data';
import { Bitstream } from './bitstream.model';
import { hasValue, isNotEmpty } from '../../shared/empty.util';
import { hasValue, isEmpty, isNotEmpty } from '../../shared/empty.util';
import { PaginatedList } from '../data/paginated-list';
import { Relationship } from './item-relationships/relationship.model';
import { ResourceType } from './resource-type';
import { getSucceededRemoteData } from './operators';
import { GenericConstructor } from './generic-constructor';
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
import { DEFAULT_ENTITY_TYPE } from '../../shared/metadata-representation/metadata-representation.decorator';
/**
* Class representing a DSpace Item
*/
export class Item extends DSpaceObject {
static type = new ResourceType('item');
@@ -110,4 +116,14 @@ export class Item extends DSpaceObject {
}));
}
/**
* Method that returns as which type of object this object should be rendered
*/
getRenderTypes(): Array<string | GenericConstructor<ListableObject>> {
let entityType = this.firstMetadataValue('relationship.type');
if (isEmpty(entityType)) {
entityType = DEFAULT_ENTITY_TYPE;
}
return [entityType, ...super.getRenderTypes()];
}
}

View File

@@ -3,7 +3,8 @@
*/
export enum ViewMode {
List = 'list',
Grid = 'grid',
Detail = 'detail'
ListElement = 'listElement',
GridElement = 'gridElement',
DetailedListElement = 'detailedListElement',
StandalonePage = 'standalonePage',
}

View File

@@ -1,30 +1 @@
<ds-truncatable [id]="dso.id">
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
<a [routerLink]="['/items/' + dso.id]" class="card-img-top full-width">
<div>
<ds-grid-thumbnail [thumbnail]="this.item.getThumbnail() | async">
</ds-grid-thumbnail>
</div>
</a>
<div class="card-body">
<ds-item-type-badge [object]="object"></ds-item-type-badge>
<ds-truncatable-part [id]="dso.id" [minLines]="3" type="h4">
<h4 class="card-title" [innerHTML]="dso.firstMetadataValue('dc.title')"></h4>
</ds-truncatable-part>
<p *ngIf="dso.hasMetadata('creativework.datePublished')" class="item-date card-text text-muted">
<ds-truncatable-part [id]="dso.id" [minLines]="1">
<span [innerHTML]="firstMetadataValue('creativework.datePublished')"></span>
</ds-truncatable-part>
</p>
<p *ngIf="dso.hasMetadata('journal.title')" class="item-journal-title card-text">
<ds-truncatable-part [id]="dso.id" [minLines]="3">
<span [innerHTML]="firstMetadataValue('journal.title')"></span>
</ds-truncatable-part>
</p>
<div class="text-center">
<a [routerLink]="['/items/' + dso.id]"
class="lead btn btn-primary viewButton">View</a>
</div>
</div>
</div>
</ds-truncatable>
<ds-journal-issue-search-result-grid-element [object]="{ indexableObject: object, hitHighlights: {} }" [linkType]="linkType"></ds-journal-issue-search-result-grid-element>

View File

@@ -1,15 +1,17 @@
import { ItemSearchResult } from '../../../../shared/object-collection/shared/item-search-result.model';
import { Item } from '../../../../core/shared/item.model';
import { of as observableOf } from 'rxjs/internal/observable/of';
import { getEntityGridElementTestComponent } from '../../../../shared/object-grid/item-grid-element/item-types/publication/publication-grid-element.component.spec';
import { JournalIssueGridElementComponent } from './journal-issue-grid-element.component';
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/testing/utils';
import { PaginatedList } from '../../../../core/data/paginated-list';
import { PageInfo } from '../../../../core/shared/page-info.model';
import { of as observableOf } from 'rxjs';
import { async, TestBed } from '@angular/core/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
import { TruncatableService } from '../../../../shared/truncatable/truncatable.service';
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
import { By } from '@angular/platform-browser';
const mockItemWithMetadata: ItemSearchResult = new ItemSearchResult();
mockItemWithMetadata.hitHighlights = {};
mockItemWithMetadata.indexableObject = Object.assign(new Item(), {
const mockItem = Object.assign(new Item(), {
bitstreams: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
metadata: {
'dc.title': [
@@ -33,18 +35,41 @@ mockItemWithMetadata.indexableObject = Object.assign(new Item(), {
}
});
const mockItemWithoutMetadata: ItemSearchResult = new ItemSearchResult();
mockItemWithoutMetadata.hitHighlights = {};
mockItemWithoutMetadata.indexableObject = Object.assign(new Item(), {
bitstreams: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
metadata: {
'dc.title': [
{
language: 'en_US',
value: 'This is just another title'
}
]
}
describe('JournalIssueGridElementComponent', () => {
let comp;
let fixture;
const truncatableServiceStub: any = {
isCollapsed: (id: number) => observableOf(true),
};
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [NoopAnimationsModule],
declarations: [JournalIssueGridElementComponent, TruncatePipe],
providers: [
{ provide: TruncatableService, useValue: truncatableServiceStub },
],
schemas: [NO_ERRORS_SCHEMA]
}).overrideComponent(JournalIssueGridElementComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
}).compileComponents();
}));
beforeEach(async(() => {
fixture = TestBed.createComponent(JournalIssueGridElementComponent);
comp = fixture.componentInstance;
}));
describe(`when the journal issue is rendered`, () => {
beforeEach(() => {
comp.object = mockItem;
fixture.detectChanges();
});
describe('JournalIssueGridElementComponent', getEntityGridElementTestComponent(JournalIssueGridElementComponent, mockItemWithMetadata, mockItemWithoutMetadata, ['date', 'journal-title']));
it(`should contain a JournalIssueSearchResultGridElementComponent`, () => {
const journalIssueGridElement = fixture.debugElement.query(By.css(`ds-journal-issue-search-result-grid-element`));
expect(journalIssueGridElement).not.toBeNull();
});
});
});

View File

@@ -1,17 +1,17 @@
import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
import { Component } from '@angular/core';
import { focusShadow } from '../../../../shared/animations/focus';
import { TypedItemSearchResultGridElementComponent } from '../../../../shared/object-grid/item-grid-element/item-types/typed-item-search-result-grid-element.component';
import { ViewMode } from '../../../../core/shared/view-mode.model';
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { AbstractListableElementComponent } from '../../../../shared/object-collection/shared/object-collection-element/abstract-listable-element.component';
import { Item } from '../../../../core/shared/item.model';
@rendersItemType('JournalIssue', ItemViewMode.Card)
@listableObjectComponent('JournalIssue', ViewMode.GridElement)
@Component({
selector: 'ds-journal-issue-grid-element',
styleUrls: ['./journal-issue-grid-element.component.scss'],
templateUrl: './journal-issue-grid-element.component.html',
animations: [focusShadow]
})
/**
* The component for displaying a grid element for an item of the type Journal Issue
*/
export class JournalIssueGridElementComponent extends TypedItemSearchResultGridElementComponent {
export class JournalIssueGridElementComponent extends AbstractListableElementComponent<Item> {
}

View File

@@ -1,30 +1 @@
<ds-truncatable [id]="dso.id">
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
<a [routerLink]="['/items/' + dso.id]" class="card-img-top full-width">
<div>
<ds-grid-thumbnail [thumbnail]="this.item.getThumbnail() | async">
</ds-grid-thumbnail>
</div>
</a>
<div class="card-body">
<ds-item-type-badge [object]="object"></ds-item-type-badge>
<ds-truncatable-part [id]="dso.id" [minLines]="3" type="h4">
<h4 class="card-title" [innerHTML]="dso.firstMetadataValue('dc.title')"></h4>
</ds-truncatable-part>
<p *ngIf="dso.hasMetadata('creativework.datePublished')" class="item-date card-text text-muted">
<ds-truncatable-part [id]="dso.id" [minLines]="1">
<span [innerHTML]="firstMetadataValue('creativework.datePublished')"></span>
</ds-truncatable-part>
</p>
<p *ngIf="dso.hasMetadata('dc.description')" class="item-description card-text">
<ds-truncatable-part [id]="dso.id" [minLines]="3">
<span [innerHTML]="firstMetadataValue('dc.description')"></span>
</ds-truncatable-part>
</p>
<div class="text-center">
<a [routerLink]="['/items/' + dso.id]"
class="lead btn btn-primary viewButton">View</a>
</div>
</div>
</div>
</ds-truncatable>
<ds-journal-volume-search-result-grid-element [object]="{ indexableObject: object, hitHighlights: {} }" [linkType]="linkType"></ds-journal-volume-search-result-grid-element>

View File

@@ -1,15 +1,17 @@
import { ItemSearchResult } from '../../../../shared/object-collection/shared/item-search-result.model';
import { Item } from '../../../../core/shared/item.model';
import { of as observableOf } from 'rxjs/internal/observable/of';
import { getEntityGridElementTestComponent } from '../../../../shared/object-grid/item-grid-element/item-types/publication/publication-grid-element.component.spec';
import { JournalVolumeGridElementComponent } from './journal-volume-grid-element.component';
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/testing/utils';
import { PaginatedList } from '../../../../core/data/paginated-list';
import { PageInfo } from '../../../../core/shared/page-info.model';
import { of as observableOf } from 'rxjs';
import { async, TestBed } from '@angular/core/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
import { TruncatableService } from '../../../../shared/truncatable/truncatable.service';
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
import { By } from '@angular/platform-browser';
const mockItemWithMetadata: ItemSearchResult = new ItemSearchResult();
mockItemWithMetadata.hitHighlights = {};
mockItemWithMetadata.indexableObject = Object.assign(new Item(), {
const mockItem = Object.assign(new Item(), {
bitstreams: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
metadata: {
'dc.title': [
@@ -33,18 +35,41 @@ mockItemWithMetadata.indexableObject = Object.assign(new Item(), {
}
});
const mockItemWithoutMetadata: ItemSearchResult = new ItemSearchResult();
mockItemWithoutMetadata.hitHighlights = {};
mockItemWithoutMetadata.indexableObject = Object.assign(new Item(), {
bitstreams: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
metadata: {
'dc.title': [
{
language: 'en_US',
value: 'This is just another title'
}
]
}
describe('JournalVolumeGridElementComponent', () => {
let comp;
let fixture;
const truncatableServiceStub: any = {
isCollapsed: (id: number) => observableOf(true),
};
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [NoopAnimationsModule],
declarations: [JournalVolumeGridElementComponent, TruncatePipe],
providers: [
{ provide: TruncatableService, useValue: truncatableServiceStub },
],
schemas: [NO_ERRORS_SCHEMA]
}).overrideComponent(JournalVolumeGridElementComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
}).compileComponents();
}));
beforeEach(async(() => {
fixture = TestBed.createComponent(JournalVolumeGridElementComponent);
comp = fixture.componentInstance;
}));
describe(`when the journal volume is rendered`, () => {
beforeEach(() => {
comp.object = mockItem;
fixture.detectChanges();
});
describe('JournalVolumeGridElementComponent', getEntityGridElementTestComponent(JournalVolumeGridElementComponent, mockItemWithMetadata, mockItemWithoutMetadata, ['date', 'description']));
it(`should contain a JournalVolumeSearchResultGridElementComponent`, () => {
const journalVolumeGridElement = fixture.debugElement.query(By.css(`ds-journal-volume-search-result-grid-element`));
expect(journalVolumeGridElement).not.toBeNull();
});
});
});

View File

@@ -1,17 +1,17 @@
import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
import { Component } from '@angular/core';
import { focusShadow } from '../../../../shared/animations/focus';
import { TypedItemSearchResultGridElementComponent } from '../../../../shared/object-grid/item-grid-element/item-types/typed-item-search-result-grid-element.component';
import { ViewMode } from '../../../../core/shared/view-mode.model';
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { AbstractListableElementComponent } from '../../../../shared/object-collection/shared/object-collection-element/abstract-listable-element.component';
import { Item } from '../../../../core/shared/item.model';
@rendersItemType('JournalVolume', ItemViewMode.Card)
@listableObjectComponent('JournalVolume', ViewMode.GridElement)
@Component({
selector: 'ds-journal-volume-grid-element',
styleUrls: ['./journal-volume-grid-element.component.scss'],
templateUrl: './journal-volume-grid-element.component.html',
animations: [focusShadow]
})
/**
* The component for displaying a grid element for an item of the type Journal Volume
*/
export class JournalVolumeGridElementComponent extends TypedItemSearchResultGridElementComponent {
export class JournalVolumeGridElementComponent extends AbstractListableElementComponent<Item> {
}

View File

@@ -1,35 +1 @@
<ds-truncatable [id]="dso.id">
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
<a [routerLink]="['/items/' + dso.id]" class="card-img-top full-width">
<div>
<ds-grid-thumbnail [thumbnail]="this.item.getThumbnail() | async">
</ds-grid-thumbnail>
</div>
</a>
<div class="card-body">
<ds-item-type-badge [object]="object"></ds-item-type-badge>
<ds-truncatable-part [id]="dso.id" [minLines]="3" type="h4">
<h4 class="card-title" [innerHTML]="dso.firstMetadataValue('dc.title')"></h4>
</ds-truncatable-part>
<p *ngIf="dso.hasMetadata('creativework.editor')"
class="item-publisher card-text text-muted">
<ds-truncatable-part [id]="dso.id" [minLines]="1">
<span class="item-editor">{{dso.firstMetadataValue('creativework.editor')}}</span>
<span *ngIf="dso.hasMetadata('creativework.publisher')" class="item-publisher">
<span>, </span>
{{dso.firstMetadataValue('creativework.publisher')}}
</span>
</ds-truncatable-part>
</p>
<p *ngIf="dso.hasMetadata('dc.description')" class="item-description card-text">
<ds-truncatable-part [id]="dso.id" [minLines]="3">
<span [innerHTML]="firstMetadataValue('dc.description')"></span>
</ds-truncatable-part>
</p>
<div class="text-center">
<a [routerLink]="['/items/' + dso.id]"
class="lead btn btn-primary viewButton">View</a>
</div>
</div>
</div>
</ds-truncatable>
<ds-journal-search-result-grid-element [object]="{ indexableObject: object, hitHighlights: {} }" [linkType]="linkType"></ds-journal-search-result-grid-element>

View File

@@ -1,15 +1,17 @@
import { ItemSearchResult } from '../../../../shared/object-collection/shared/item-search-result.model';
import { Item } from '../../../../core/shared/item.model';
import { of as observableOf } from 'rxjs/internal/observable/of';
import { getEntityGridElementTestComponent } from '../../../../shared/object-grid/item-grid-element/item-types/publication/publication-grid-element.component.spec';
import { JournalGridElementComponent } from './journal-grid-element.component';
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/testing/utils';
import { PaginatedList } from '../../../../core/data/paginated-list';
import { PageInfo } from '../../../../core/shared/page-info.model';
import { async, TestBed } from '@angular/core/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
import { TruncatableService } from '../../../../shared/truncatable/truncatable.service';
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
import { By } from '@angular/platform-browser';
const mockItemWithMetadata: ItemSearchResult = new ItemSearchResult();
mockItemWithMetadata.hitHighlights = {};
mockItemWithMetadata.indexableObject = Object.assign(new Item(), {
const mockItem = Object.assign(new Item(), {
bitstreams: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
metadata: {
'dc.title': [
@@ -39,18 +41,41 @@ mockItemWithMetadata.indexableObject = Object.assign(new Item(), {
}
});
const mockItemWithoutMetadata: ItemSearchResult = new ItemSearchResult();
mockItemWithoutMetadata.hitHighlights = {};
mockItemWithoutMetadata.indexableObject = Object.assign(new Item(), {
bitstreams: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
metadata: {
'dc.title': [
{
language: 'en_US',
value: 'This is just another title'
}
]
}
describe('JournalGridElementComponent', () => {
let comp;
let fixture;
const truncatableServiceStub: any = {
isCollapsed: (id: number) => observableOf(true),
};
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [NoopAnimationsModule],
declarations: [JournalGridElementComponent, TruncatePipe],
providers: [
{ provide: TruncatableService, useValue: truncatableServiceStub },
],
schemas: [NO_ERRORS_SCHEMA]
}).overrideComponent(JournalGridElementComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
}).compileComponents();
}));
beforeEach(async(() => {
fixture = TestBed.createComponent(JournalGridElementComponent);
comp = fixture.componentInstance;
}));
describe(`when the journal is rendered`, () => {
beforeEach(() => {
comp.object = mockItem;
fixture.detectChanges();
});
describe('JournalGridElementComponent', getEntityGridElementTestComponent(JournalGridElementComponent, mockItemWithMetadata, mockItemWithoutMetadata, ['editor', 'publisher', 'description']));
it(`should contain a JournalGridElementComponent`, () => {
const journalGridElement = fixture.debugElement.query(By.css(`ds-journal-search-result-grid-element`));
expect(journalGridElement).not.toBeNull();
});
});
});

View File

@@ -1,17 +1,17 @@
import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
import { Component } from '@angular/core';
import { focusShadow } from '../../../../shared/animations/focus';
import { TypedItemSearchResultGridElementComponent } from '../../../../shared/object-grid/item-grid-element/item-types/typed-item-search-result-grid-element.component';
import { ViewMode } from '../../../../core/shared/view-mode.model';
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { AbstractListableElementComponent } from '../../../../shared/object-collection/shared/object-collection-element/abstract-listable-element.component';
import { Item } from '../../../../core/shared/item.model';
@rendersItemType('Journal', ItemViewMode.Card)
@listableObjectComponent('Journal', ViewMode.GridElement)
@Component({
selector: 'ds-journal-grid-element',
styleUrls: ['./journal-grid-element.component.scss'],
templateUrl: './journal-grid-element.component.html',
animations: [focusShadow]
})
/**
* The component for displaying a grid element for an item of the type Journal
*/
export class JournalGridElementComponent extends TypedItemSearchResultGridElementComponent {
export class JournalGridElementComponent extends AbstractListableElementComponent<Item> {
}

View File

@@ -0,0 +1,36 @@
<ds-truncatable [id]="dso.id">
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" class="card-img-top full-width">
<div>
<ds-grid-thumbnail [thumbnail]="dso.getThumbnail() | async">
</ds-grid-thumbnail>
</div>
</a>
<span *ngIf="linkType == linkTypes.None" class="card-img-top full-width">
<div>
<ds-grid-thumbnail [thumbnail]="dso.getThumbnail() | async">
</ds-grid-thumbnail>
</div>
</span>
<div class="card-body">
<ds-item-type-badge [object]="dso"></ds-item-type-badge>
<ds-truncatable-part [id]="dso.id" [minLines]="3" type="h4">
<h4 class="card-title" [innerHTML]="firstMetadataValue('dc.title')"></h4>
</ds-truncatable-part>
<p *ngIf="dso.hasMetadata('creativework.datePublished')" class="item-date card-text text-muted">
<ds-truncatable-part [id]="dso.id" [minLines]="1">
<span [innerHTML]="firstMetadataValue('creativework.datePublished')"></span>
</ds-truncatable-part>
</p>
<p *ngIf="dso.hasMetadata('journal.title')" class="item-journal-title card-text">
<ds-truncatable-part [id]="dso.id" [minLines]="3">
<span [innerHTML]="firstMetadataValue('journal.title')"></span>
</ds-truncatable-part>
</p>
<div *ngIf="linkType != linkTypes.None" class="text-center">
<a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]"
class="lead btn btn-primary viewButton">View</a>
</div>
</div>
</div>
</ds-truncatable>

View File

@@ -0,0 +1,49 @@
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { Item } from '../../../../../core/shared/item.model';
import { createSuccessfulRemoteDataObject$ } from '../../../../../shared/testing/utils';
import { PaginatedList } from '../../../../../core/data/paginated-list';
import { PageInfo } from '../../../../../core/shared/page-info.model';
import { getEntityGridElementTestComponent } from '../../../../../shared/object-grid/search-result-grid-element/item-search-result/publication/publication-search-result-grid-element.component.spec';
import { JournalIssueSearchResultGridElementComponent } from './journal-issue-search-result-grid-element.component';
const mockItemWithMetadata: ItemSearchResult = new ItemSearchResult();
mockItemWithMetadata.hitHighlights = {};
mockItemWithMetadata.indexableObject = Object.assign(new Item(), {
bitstreams: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
metadata: {
'dc.title': [
{
language: 'en_US',
value: 'This is just another title'
}
],
'creativework.datePublished': [
{
language: null,
value: '2015-06-26'
}
],
'journal.title': [
{
language: 'en_US',
value: 'The journal title'
}
]
}
});
const mockItemWithoutMetadata: ItemSearchResult = new ItemSearchResult();
mockItemWithoutMetadata.hitHighlights = {};
mockItemWithoutMetadata.indexableObject = Object.assign(new Item(), {
bitstreams: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
metadata: {
'dc.title': [
{
language: 'en_US',
value: 'This is just another title'
}
]
}
});
describe('JournalIssueSearchResultGridElementComponent', getEntityGridElementTestComponent(JournalIssueSearchResultGridElementComponent, mockItemWithMetadata, mockItemWithoutMetadata, ['date', 'journal-title']));

View File

@@ -0,0 +1,20 @@
import { Component } from '@angular/core';
import { ViewMode } from '../../../../../core/shared/view-mode.model';
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { focusShadow } from '../../../../../shared/animations/focus';
import { SearchResultGridElementComponent } from '../../../../../shared/object-grid/search-result-grid-element/search-result-grid-element.component';
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { Item } from '../../../../../core/shared/item.model';
@listableObjectComponent('JournalIssueSearchResult', ViewMode.GridElement)
@Component({
selector: 'ds-journal-issue-search-result-grid-element',
styleUrls: ['./journal-issue-search-result-grid-element.component.scss'],
templateUrl: './journal-issue-search-result-grid-element.component.html',
animations: [focusShadow]
})
/**
* The component for displaying a grid element for an item search result of the type Journal Issue
*/
export class JournalIssueSearchResultGridElementComponent extends SearchResultGridElementComponent<ItemSearchResult, Item> {
}

View File

@@ -0,0 +1,36 @@
<ds-truncatable [id]="dso.id">
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" class="card-img-top full-width">
<div>
<ds-grid-thumbnail [thumbnail]="dso.getThumbnail() | async">
</ds-grid-thumbnail>
</div>
</a>
<span *ngIf="linkType == linkTypes.None" class="card-img-top full-width">
<div>
<ds-grid-thumbnail [thumbnail]="dso.getThumbnail() | async">
</ds-grid-thumbnail>
</div>
</span>
<div class="card-body">
<ds-item-type-badge [object]="dso"></ds-item-type-badge>
<ds-truncatable-part [id]="dso.id" [minLines]="3" type="h4">
<h4 class="card-title" [innerHTML]="dso.firstMetadataValue('dc.title')"></h4>
</ds-truncatable-part>
<p *ngIf="dso.hasMetadata('creativework.datePublished')" class="item-date card-text text-muted">
<ds-truncatable-part [id]="dso.id" [minLines]="1">
<span [innerHTML]="firstMetadataValue('creativework.datePublished')"></span>
</ds-truncatable-part>
</p>
<p *ngIf="dso.hasMetadata('dc.description')" class="item-description card-text">
<ds-truncatable-part [id]="dso.id" [minLines]="3">
<span [innerHTML]="firstMetadataValue('dc.description')"></span>
</ds-truncatable-part>
</p>
<div *ngIf="linkType != linkTypes.None" class="text-center">
<a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]"
class="lead btn btn-primary viewButton">View</a>
</div>
</div>
</div>
</ds-truncatable>

View File

@@ -0,0 +1,49 @@
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { Item } from '../../../../../core/shared/item.model';
import { createSuccessfulRemoteDataObject$ } from '../../../../../shared/testing/utils';
import { PaginatedList } from '../../../../../core/data/paginated-list';
import { PageInfo } from '../../../../../core/shared/page-info.model';
import { getEntityGridElementTestComponent } from '../../../../../shared/object-grid/search-result-grid-element/item-search-result/publication/publication-search-result-grid-element.component.spec';
import { JournalVolumeSearchResultGridElementComponent } from './journal-volume-search-result-grid-element.component';
const mockItemWithMetadata: ItemSearchResult = new ItemSearchResult();
mockItemWithMetadata.hitHighlights = {};
mockItemWithMetadata.indexableObject = Object.assign(new Item(), {
bitstreams: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
metadata: {
'dc.title': [
{
language: 'en_US',
value: 'This is just another title'
}
],
'creativework.datePublished': [
{
language: null,
value: '2015-06-26'
}
],
'dc.description': [
{
language: 'en_US',
value: 'A description for the journal volume'
}
]
}
});
const mockItemWithoutMetadata: ItemSearchResult = new ItemSearchResult();
mockItemWithoutMetadata.hitHighlights = {};
mockItemWithoutMetadata.indexableObject = Object.assign(new Item(), {
bitstreams: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
metadata: {
'dc.title': [
{
language: 'en_US',
value: 'This is just another title'
}
]
}
});
describe('JournalVolumeSearchResultGridElementComponent', getEntityGridElementTestComponent(JournalVolumeSearchResultGridElementComponent, mockItemWithMetadata, mockItemWithoutMetadata, ['date', 'description']));

View File

@@ -0,0 +1,20 @@
import { Component } from '@angular/core';
import { SearchResultGridElementComponent } from '../../../../../shared/object-grid/search-result-grid-element/search-result-grid-element.component';
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { Item } from '../../../../../core/shared/item.model';
import { ViewMode } from '../../../../../core/shared/view-mode.model';
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { focusShadow } from '../../../../../shared/animations/focus';
@listableObjectComponent('JournalVolumeSearchResult', ViewMode.GridElement)
@Component({
selector: 'ds-journal-volume-search-result-grid-element',
styleUrls: ['./journal-volume-search-result-grid-element.component.scss'],
templateUrl: './journal-volume-search-result-grid-element.component.html',
animations: [focusShadow]
})
/**
* The component for displaying a grid element for an item search result of the type Journal Volume
*/
export class JournalVolumeSearchResultGridElementComponent extends SearchResultGridElementComponent<ItemSearchResult, Item> {
}

View File

@@ -0,0 +1,41 @@
<ds-truncatable [id]="dso.id">
<div class="card" [@focusShadow]="(isCollapsed$ | async)?'blur':'focus'">
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]" class="card-img-top full-width">
<div>
<ds-grid-thumbnail [thumbnail]="dso.getThumbnail() | async">
</ds-grid-thumbnail>
</div>
</a>
<span *ngIf="linkType == linkTypes.None" class="card-img-top full-width">
<div>
<ds-grid-thumbnail [thumbnail]="dso.getThumbnail() | async">
</ds-grid-thumbnail>
</div>
</span>
<div class="card-body">
<ds-item-type-badge [object]="dso"></ds-item-type-badge>
<ds-truncatable-part [id]="dso.id" [minLines]="3" type="h4">
<h4 class="card-title" [innerHTML]="firstMetadataValue('dc.title')"></h4>
</ds-truncatable-part>
<p *ngIf="dso.hasMetadata('creativework.editor')"
class="item-publisher card-text text-muted">
<ds-truncatable-part [id]="dso.id" [minLines]="1">
<span class="item-editor">{{firstMetadataValue('creativework.editor')}}</span>
<span *ngIf="dso.hasMetadata('creativework.publisher')" class="item-publisher">
<span>, </span>
{{firstMetadataValue('creativework.publisher')}}
</span>
</ds-truncatable-part>
</p>
<p *ngIf="dso.hasMetadata('dc.description')" class="item-description card-text">
<ds-truncatable-part [id]="dso.id" [minLines]="3">
<span [innerHTML]="firstMetadataValue('dc.description')"></span>
</ds-truncatable-part>
</p>
<div *ngIf="linkType != linkTypes.None" class="text-center">
<a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer" [routerLink]="['/items/' + dso.id]"
class="lead btn btn-primary viewButton">View</a>
</div>
</div>
</div>
</ds-truncatable>

View File

@@ -0,0 +1,55 @@
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { Item } from '../../../../../core/shared/item.model';
import { createSuccessfulRemoteDataObject$ } from '../../../../../shared/testing/utils';
import { PaginatedList } from '../../../../../core/data/paginated-list';
import { PageInfo } from '../../../../../core/shared/page-info.model';
import { getEntityGridElementTestComponent } from '../../../../../shared/object-grid/search-result-grid-element/item-search-result/publication/publication-search-result-grid-element.component.spec';
import { JournalSearchResultGridElementComponent } from './journal-search-result-grid-element.component';
const mockItemWithMetadata: ItemSearchResult = new ItemSearchResult();
mockItemWithMetadata.hitHighlights = {};
mockItemWithMetadata.indexableObject = Object.assign(new Item(), {
bitstreams: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
metadata: {
'dc.title': [
{
language: 'en_US',
value: 'This is just another title'
}
],
'creativework.editor': [
{
language: 'en_US',
value: 'Smith, Donald'
}
],
'creativework.publisher': [
{
language: 'en_US',
value: 'A company'
}
],
'dc.description': [
{
language: 'en_US',
value: 'This is the description'
}
]
}
});
const mockItemWithoutMetadata: ItemSearchResult = new ItemSearchResult();
mockItemWithoutMetadata.hitHighlights = {};
mockItemWithoutMetadata.indexableObject = Object.assign(new Item(), {
bitstreams: createSuccessfulRemoteDataObject$(new PaginatedList(new PageInfo(), [])),
metadata: {
'dc.title': [
{
language: 'en_US',
value: 'This is just another title'
}
]
}
});
describe('JournalSearchResultGridElementComponent', getEntityGridElementTestComponent(JournalSearchResultGridElementComponent, mockItemWithMetadata, mockItemWithoutMetadata, ['editor', 'publisher', 'description']));

View File

@@ -0,0 +1,20 @@
import { Component } from '@angular/core';
import { focusShadow } from '../../../../../shared/animations/focus';
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { ViewMode } from '../../../../../core/shared/view-mode.model';
import { SearchResultGridElementComponent } from '../../../../../shared/object-grid/search-result-grid-element/search-result-grid-element.component';
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { Item } from '../../../../../core/shared/item.model';
@listableObjectComponent('JournalSearchResult', ViewMode.GridElement)
@Component({
selector: 'ds-journal-search-result-grid-element',
styleUrls: ['./journal-search-result-grid-element.component.scss'],
templateUrl: './journal-search-result-grid-element.component.html',
animations: [focusShadow]
})
/**
* The component for displaying a grid element for an item search result of the type Journal
*/
export class JournalSearchResultGridElementComponent extends SearchResultGridElementComponent<ItemSearchResult, Item> {
}

View File

@@ -1,21 +1 @@
<ds-truncatable [id]="item.id">
<a
[routerLink]="['/items/' + item.id]" class="lead"
[innerHTML]="firstMetadataValue('dc.title')"></a>
<span class="text-muted">
<ds-truncatable-part [id]="item.id" [minLines]="1">
<span *ngIf="item.allMetadata(['publicationvolume.volumeNumber']).length > 0"
class="item-list-journal-issues">
<span *ngFor="let value of allMetadataValues(['publicationvolume.volumeNumber']); let last=last;">
<span [innerHTML]="value"><span [innerHTML]="value"></span></span>
</span>
<span *ngIf="item.allMetadata(['publicationissue.issueNumber']).length > 0"
class="item-list-journal-issue-numbers">
<span *ngFor="let value of allMetadataValues(['publicationissue.issueNumber']); let last=last;">
<span> - </span><span [innerHTML]="value"><span [innerHTML]="value"></span></span>
</span>
</span>
</span>
</ds-truncatable-part>
</span>
</ds-truncatable>
<ds-journal-issue-search-result-list-element [object]="{ indexableObject: object, hitHighlights: {} }" [linkType]="linkType"></ds-journal-issue-search-result-list-element>

View File

@@ -1,17 +1,13 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { async, TestBed } from '@angular/core/testing';
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
import { By } from '@angular/platform-browser';
import { JournalIssueListElementComponent } from './journal-issue-list-element.component';
import { of as observableOf } from 'rxjs';
import { Item } from '../../../../core/shared/item.model';
import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component';
import { TruncatableService } from '../../../../shared/truncatable/truncatable.service';
let journalIssueListElementComponent: JournalIssueListElementComponent;
let fixture: ComponentFixture<JournalIssueListElementComponent>;
const mockItemWithMetadata: Item = Object.assign(new Item(), {
const mockItem: Item = Object.assign(new Item(), {
bitstreams: observableOf({}),
metadata: {
'dc.title': [
@@ -34,27 +30,21 @@ const mockItemWithMetadata: Item = Object.assign(new Item(), {
]
}
});
const mockItemWithoutMetadata: Item = Object.assign(new Item(), {
bitstreams: observableOf({}),
metadata: {
'dc.title': [
{
language: 'en_US',
value: 'This is just another title'
}
]
}
});
describe('JournalIssueListElementComponent', () => {
let comp;
let fixture;
const truncatableServiceStub: any = {
isCollapsed: (id: number) => observableOf(true),
};
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [JournalIssueListElementComponent, TruncatePipe],
providers: [
{ provide: ITEM, useValue: mockItemWithMetadata},
{ provide: TruncatableService, useValue: {} }
{ provide: TruncatableService, useValue: truncatableServiceStub },
],
schemas: [NO_ERRORS_SCHEMA]
}).overrideComponent(JournalIssueListElementComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
@@ -63,55 +53,19 @@ describe('JournalIssueListElementComponent', () => {
beforeEach(async(() => {
fixture = TestBed.createComponent(JournalIssueListElementComponent);
journalIssueListElementComponent = fixture.componentInstance;
comp = fixture.componentInstance;
}));
describe('When the item has a journal identifier', () => {
describe(`when the journal issue is rendered`, () => {
beforeEach(() => {
journalIssueListElementComponent.item = mockItemWithMetadata;
comp.object = mockItem;
fixture.detectChanges();
});
it('should show the journal issues span', () => {
const journalIdentifierField = fixture.debugElement.query(By.css('span.item-list-journal-issues'));
expect(journalIdentifierField).not.toBeNull();
it(`should contain a JournalIssueListElementComponent`, () => {
const journalIssueListElement = fixture.debugElement.query(By.css(`ds-journal-issue-search-result-list-element`));
expect(journalIssueListElement).not.toBeNull();
});
});
describe('When the item has no journal identifier', () => {
beforeEach(() => {
journalIssueListElementComponent.item = mockItemWithoutMetadata;
fixture.detectChanges();
});
it('should not show the journal issues span', () => {
const journalIdentifierField = fixture.debugElement.query(By.css('span.item-list-journal-issues'));
expect(journalIdentifierField).toBeNull();
});
});
describe('When the item has a journal number', () => {
beforeEach(() => {
journalIssueListElementComponent.item = mockItemWithMetadata;
fixture.detectChanges();
});
it('should show the journal issue numbers span', () => {
const journalNumberField = fixture.debugElement.query(By.css('span.item-list-journal-issue-numbers'));
expect(journalNumberField).not.toBeNull();
});
});
describe('When the item has no journal number', () => {
beforeEach(() => {
journalIssueListElementComponent.item = mockItemWithoutMetadata;
fixture.detectChanges();
});
it('should not show the journal issue numbers span', () => {
const journalNumberField = fixture.debugElement.query(By.css('span.item-list-journal-issue-numbers'));
expect(journalNumberField).toBeNull();
});
});
});

View File

@@ -1,8 +1,10 @@
import { Component } from '@angular/core';
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 { ViewMode } from '../../../../core/shared/view-mode.model';
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { AbstractListableElementComponent } from '../../../../shared/object-collection/shared/object-collection-element/abstract-listable-element.component';
import { Item } from '../../../../core/shared/item.model';
@rendersItemType('JournalIssue', ItemViewMode.Element)
@listableObjectComponent('JournalIssue', ViewMode.ListElement)
@Component({
selector: 'ds-journal-issue-list-element',
styleUrls: ['./journal-issue-list-element.component.scss'],
@@ -11,5 +13,5 @@ import { TypedItemSearchResultListElementComponent } from '../../../../shared/ob
/**
* The component for displaying a list element for an item of the type Journal Issue
*/
export class JournalIssueListElementComponent extends TypedItemSearchResultListElementComponent {
export class JournalIssueListElementComponent extends AbstractListableElementComponent<Item> {
}

View File

@@ -1,21 +1 @@
<ds-truncatable [id]="item.id">
<a
[routerLink]="['/items/' + item.id]" class="lead"
[innerHTML]="firstMetadataValue('dc.title')"></a>
<span class="text-muted">
<ds-truncatable-part [id]="item.id" [minLines]="1">
<span *ngIf="item.allMetadata(['journal.title']).length > 0"
class="item-list-journal-volumes">
<span *ngFor="let value of allMetadataValues(['journal.title']); let last=last;">
<span [innerHTML]="value"><span [innerHTML]="value"></span></span>
</span>
</span>
<span *ngIf="item.allMetadata(['publicationvolume.volumeNumber']).length > 0"
class="item-list-journal-volume-identifiers">
<span *ngFor="let value of allMetadataValues(['publicationvolume.volumeNumber']); let last=last;">
<span> (</span><span [innerHTML]="value"><span [innerHTML]="value"></span></span><span>)</span>
</span>
</span>
</ds-truncatable-part>
</span>
</ds-truncatable>
<ds-journal-volume-search-result-list-element [object]="{ indexableObject: object, hitHighlights: {} }" [linkType]="linkType"></ds-journal-volume-search-result-list-element>

View File

@@ -1,17 +1,13 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { async, TestBed } from '@angular/core/testing';
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
import { By } from '@angular/platform-browser';
import { JournalVolumeListElementComponent } from './journal-volume-list-element.component';
import { of as observableOf } from 'rxjs';
import { Item } from '../../../../core/shared/item.model';
import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component';
import { TruncatableService } from '../../../../shared/truncatable/truncatable.service';
let journalVolumeListElementComponent: JournalVolumeListElementComponent;
let fixture: ComponentFixture<JournalVolumeListElementComponent>;
const mockItemWithMetadata: Item = Object.assign(new Item(), {
const mockItem: Item = Object.assign(new Item(), {
bitstreams: observableOf({}),
metadata: {
'dc.title': [
@@ -34,27 +30,21 @@ const mockItemWithMetadata: Item = Object.assign(new Item(), {
]
}
});
const mockItemWithoutMetadata: Item = Object.assign(new Item(), {
bitstreams: observableOf({}),
metadata: {
'dc.title': [
{
language: 'en_US',
value: 'This is just another title'
}
]
}
});
describe('JournalVolumeListElementComponent', () => {
let comp;
let fixture;
const truncatableServiceStub: any = {
isCollapsed: (id: number) => observableOf(true),
};
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [JournalVolumeListElementComponent, TruncatePipe],
providers: [
{ provide: ITEM, useValue: mockItemWithMetadata},
{ provide: TruncatableService, useValue: {} }
{ provide: TruncatableService, useValue: truncatableServiceStub },
],
schemas: [NO_ERRORS_SCHEMA]
}).overrideComponent(JournalVolumeListElementComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
@@ -63,55 +53,18 @@ describe('JournalVolumeListElementComponent', () => {
beforeEach(async(() => {
fixture = TestBed.createComponent(JournalVolumeListElementComponent);
journalVolumeListElementComponent = fixture.componentInstance;
comp = fixture.componentInstance;
}));
describe('When the item has a journal title', () => {
describe(`when the journal volume is rendered`, () => {
beforeEach(() => {
journalVolumeListElementComponent.item = mockItemWithMetadata;
comp.object = mockItem;
fixture.detectChanges();
});
it('should show the journal title span', () => {
const journalTitleField = fixture.debugElement.query(By.css('span.item-list-journal-volumes'));
expect(journalTitleField).not.toBeNull();
});
});
describe('When the item has no journal title', () => {
beforeEach(() => {
journalVolumeListElementComponent.item = mockItemWithoutMetadata;
fixture.detectChanges();
});
it('should not show the journal title span', () => {
const journalTitleField = fixture.debugElement.query(By.css('span.item-list-journal-volumes'));
expect(journalTitleField).toBeNull();
});
});
describe('When the item has a journal identifier', () => {
beforeEach(() => {
journalVolumeListElementComponent.item = mockItemWithMetadata;
fixture.detectChanges();
});
it('should show the journal identifiers span', () => {
const journalIdentifierField = fixture.debugElement.query(By.css('span.item-list-journal-volume-identifiers'));
expect(journalIdentifierField).not.toBeNull();
});
});
describe('When the item has no journal identifier', () => {
beforeEach(() => {
journalVolumeListElementComponent.item = mockItemWithoutMetadata;
fixture.detectChanges();
});
it('should not show the journal identifiers span', () => {
const journalIdentifierField = fixture.debugElement.query(By.css('span.item-list-journal-volume-identifiers'));
expect(journalIdentifierField).toBeNull();
it(`should contain a JournalVolumeListElementComponent`, () => {
const journalVolumeListElement = fixture.debugElement.query(By.css(`ds-journal-volume-search-result-list-element`));
expect(journalVolumeListElement).not.toBeNull();
});
});
});

View File

@@ -1,8 +1,10 @@
import { Component } from '@angular/core';
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 { ViewMode } from '../../../../core/shared/view-mode.model';
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { AbstractListableElementComponent } from '../../../../shared/object-collection/shared/object-collection-element/abstract-listable-element.component';
import { Item } from '../../../../core/shared/item.model';
@rendersItemType('JournalVolume', ItemViewMode.Element)
@listableObjectComponent('JournalVolume', ViewMode.ListElement)
@Component({
selector: 'ds-journal-volume-list-element',
styleUrls: ['./journal-volume-list-element.component.scss'],
@@ -11,5 +13,5 @@ import { TypedItemSearchResultListElementComponent } from '../../../../shared/ob
/**
* The component for displaying a list element for an item of the type Journal Volume
*/
export class JournalVolumeListElementComponent extends TypedItemSearchResultListElementComponent {
export class JournalVolumeListElementComponent extends AbstractListableElementComponent<Item> {
}

View File

@@ -1,15 +1 @@
<ds-truncatable [id]="item.id">
<a
[routerLink]="['/items/' + item.id]" class="lead"
[innerHTML]="firstMetadataValue('dc.title')"></a>
<span class="text-muted">
<ds-truncatable-part [id]="item.id" [minLines]="1">
<span *ngIf="item.allMetadata(['creativeworkseries.issn']).length > 0"
class="item-list-journals">
<span *ngFor="let value of allMetadataValues(['creativeworkseries.issn']); let last=last;">
<span [innerHTML]="value"><span [innerHTML]="value"></span></span>
</span>
</span>
</ds-truncatable-part>
</span>
</ds-truncatable>
<ds-journal-search-result-list-element [object]="{ indexableObject: object, hitHighlights: {} }" [linkType]="linkType"></ds-journal-search-result-list-element>

View File

@@ -1,17 +1,13 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { async, TestBed } from '@angular/core/testing';
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
import { By } from '@angular/platform-browser';
import { JournalListElementComponent } from './journal-list-element.component';
import { of as observableOf } from 'rxjs';
import { Item } from '../../../../core/shared/item.model';
import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
import { ITEM } from '../../../../shared/items/switcher/item-type-switcher.component';
import { TruncatableService } from '../../../../shared/truncatable/truncatable.service';
let journalListElementComponent: JournalListElementComponent;
let fixture: ComponentFixture<JournalListElementComponent>;
const mockItemWithMetadata: Item = Object.assign(new Item(), {
const mockItem: Item = Object.assign(new Item(), {
bitstreams: observableOf({}),
metadata: {
'dc.title': [
@@ -28,27 +24,21 @@ const mockItemWithMetadata: Item = Object.assign(new Item(), {
]
}
});
const mockItemWithoutMetadata: Item = Object.assign(new Item(), {
bitstreams: observableOf({}),
metadata: {
'dc.title': [
{
language: 'en_US',
value: 'This is just another title'
}
]
}
});
describe('JournalListElementComponent', () => {
let comp;
let fixture;
const truncatableServiceStub: any = {
isCollapsed: (id: number) => observableOf(true),
};
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [JournalListElementComponent, TruncatePipe],
providers: [
{ provide: ITEM, useValue: mockItemWithMetadata},
{ provide: TruncatableService, useValue: {} }
{ provide: TruncatableService, useValue: truncatableServiceStub },
],
schemas: [NO_ERRORS_SCHEMA]
}).overrideComponent(JournalListElementComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
@@ -57,31 +47,18 @@ describe('JournalListElementComponent', () => {
beforeEach(async(() => {
fixture = TestBed.createComponent(JournalListElementComponent);
journalListElementComponent = fixture.componentInstance;
comp = fixture.componentInstance;
}));
describe('When the item has an issn', () => {
describe(`when the journal is rendered`, () => {
beforeEach(() => {
journalListElementComponent.item = mockItemWithMetadata;
comp.object = mockItem;
fixture.detectChanges();
});
it('should show the journals span', () => {
const issnField = fixture.debugElement.query(By.css('span.item-list-journals'));
expect(issnField).not.toBeNull();
});
});
describe('When the item has no issn', () => {
beforeEach(() => {
journalListElementComponent.item = mockItemWithoutMetadata;
fixture.detectChanges();
});
it('should not show the journals span', () => {
const issnField = fixture.debugElement.query(By.css('span.item-list-journals'));
expect(issnField).toBeNull();
it(`should contain a JournalListElementComponent`, () => {
const journalListElement = fixture.debugElement.query(By.css(`ds-journal-search-result-list-element`));
expect(journalListElement).not.toBeNull();
});
});
});

View File

@@ -1,8 +1,10 @@
import { Component } from '@angular/core';
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 { ViewMode } from '../../../../core/shared/view-mode.model';
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { AbstractListableElementComponent } from '../../../../shared/object-collection/shared/object-collection-element/abstract-listable-element.component';
import { Item } from '../../../../core/shared/item.model';
@rendersItemType('Journal', ItemViewMode.Element)
@listableObjectComponent('Journal', ViewMode.ListElement)
@Component({
selector: 'ds-journal-list-element',
styleUrls: ['./journal-list-element.component.scss'],
@@ -11,5 +13,5 @@ import { TypedItemSearchResultListElementComponent } from '../../../../shared/ob
/**
* The component for displaying a list element for an item of the type Journal
*/
export class JournalListElementComponent extends TypedItemSearchResultListElementComponent {
export class JournalListElementComponent extends AbstractListableElementComponent<Item> {
}

View File

@@ -0,0 +1,25 @@
<ds-item-type-badge [object]="dso"></ds-item-type-badge>
<ds-truncatable [id]="dso.id">
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer"
[routerLink]="['/items/' + dso.id]" class="lead"
[innerHTML]="firstMetadataValue('dc.title')"></a>
<span *ngIf="linkType == linkTypes.None"
class="lead"
[innerHTML]="firstMetadataValue('dc.title')"></span>
<span class="text-muted">
<ds-truncatable-part [id]="dso.id" [minLines]="1">
<span *ngIf="dso.allMetadata(['publicationvolume.volumeNumber']).length > 0"
class="item-list-journal-issues">
<span *ngFor="let value of allMetadataValues(['publicationvolume.volumeNumber']); let last=last;">
<span [innerHTML]="value"><span [innerHTML]="value"></span></span>
</span>
<span *ngIf="dso.allMetadata(['publicationissue.issueNumber']).length > 0"
class="item-list-journal-issue-numbers">
<span *ngFor="let value of allMetadataValues(['publicationissue.issueNumber']); let last=last;">
<span> - </span><span [innerHTML]="value"><span [innerHTML]="value"></span></span>
</span>
</span>
</span>
</ds-truncatable-part>
</span>
</ds-truncatable>

View File

@@ -0,0 +1,125 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
import { By } from '@angular/platform-browser';
import { of as observableOf } from 'rxjs';
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { JournalIssueSearchResultListElementComponent } from './journal-issue-search-result-list-element.component';
import { Item } from '../../../../../core/shared/item.model';
import { TruncatePipe } from '../../../../../shared/utils/truncate.pipe';
import { TruncatableService } from '../../../../../shared/truncatable/truncatable.service';
let journalIssueListElementComponent: JournalIssueSearchResultListElementComponent;
let fixture: ComponentFixture<JournalIssueSearchResultListElementComponent>;
const mockItemWithMetadata: ItemSearchResult = Object.assign(
new ItemSearchResult(),
{
indexableObject: Object.assign(new Item(), {
bitstreams: observableOf({}),
metadata: {
'dc.title': [
{
language: 'en_US',
value: 'This is just another title'
}
],
'publicationvolume.volumeNumber': [
{
language: 'en_US',
value: '1234'
}
],
'publicationissue.issueNumber': [
{
language: 'en_US',
value: '5678'
}
]
}
})
});
const mockItemWithoutMetadata: ItemSearchResult = Object.assign(
new ItemSearchResult(),
{
indexableObject: Object.assign(new Item(), {
bitstreams: observableOf({}),
metadata: {
'dc.title': [
{
language: 'en_US',
value: 'This is just another title'
}
]
}
})
});
describe('JournalIssueSearchResultListElementComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [JournalIssueSearchResultListElementComponent, TruncatePipe],
providers: [
{ provide: TruncatableService, useValue: {} }
],
schemas: [NO_ERRORS_SCHEMA]
}).overrideComponent(JournalIssueSearchResultListElementComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
}).compileComponents();
}));
beforeEach(async(() => {
fixture = TestBed.createComponent(JournalIssueSearchResultListElementComponent);
journalIssueListElementComponent = fixture.componentInstance;
}));
describe('When the item has a journal identifier', () => {
beforeEach(() => {
journalIssueListElementComponent.object = mockItemWithMetadata;
fixture.detectChanges();
});
it('should show the journal issues span', () => {
const journalIdentifierField = fixture.debugElement.query(By.css('span.item-list-journal-issues'));
expect(journalIdentifierField).not.toBeNull();
});
});
describe('When the item has no journal identifier', () => {
beforeEach(() => {
journalIssueListElementComponent.object = mockItemWithoutMetadata;
fixture.detectChanges();
});
it('should not show the journal issues span', () => {
const journalIdentifierField = fixture.debugElement.query(By.css('span.item-list-journal-issues'));
expect(journalIdentifierField).toBeNull();
});
});
describe('When the item has a journal number', () => {
beforeEach(() => {
journalIssueListElementComponent.object = mockItemWithMetadata;
fixture.detectChanges();
});
it('should show the journal issue numbers span', () => {
const journalNumberField = fixture.debugElement.query(By.css('span.item-list-journal-issue-numbers'));
expect(journalNumberField).not.toBeNull();
});
});
describe('When the item has no journal number', () => {
beforeEach(() => {
journalIssueListElementComponent.object = mockItemWithoutMetadata;
fixture.detectChanges();
});
it('should not show the journal issue numbers span', () => {
const journalNumberField = fixture.debugElement.query(By.css('span.item-list-journal-issue-numbers'));
expect(journalNumberField).toBeNull();
});
});
});

View File

@@ -0,0 +1,18 @@
import { Component } from '@angular/core';
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { ViewMode } from '../../../../../core/shared/view-mode.model';
import { SearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/search-result-list-element.component';
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { Item } from '../../../../../core/shared/item.model';
@listableObjectComponent('JournalIssueSearchResult', ViewMode.ListElement)
@Component({
selector: 'ds-journal-issue-search-result-list-element',
styleUrls: ['./journal-issue-search-result-list-element.component.scss'],
templateUrl: './journal-issue-search-result-list-element.component.html'
})
/**
* The component for displaying a list element for an item search result of the type Journal Issue
*/
export class JournalIssueSearchResultListElementComponent extends SearchResultListElementComponent<ItemSearchResult, Item> {
}

View File

@@ -0,0 +1,25 @@
<ds-item-type-badge [object]="dso"></ds-item-type-badge>
<ds-truncatable [id]="dso.id">
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer"
[routerLink]="['/items/' + dso.id]" class="lead"
[innerHTML]="firstMetadataValue('dc.title')"></a>
<span *ngIf="linkType == linkTypes.None"
class="lead"
[innerHTML]="firstMetadataValue('dc.title')"></span>
<span class="text-muted">
<ds-truncatable-part [id]="dso.id" [minLines]="1">
<span *ngIf="dso.allMetadata(['journal.title']).length > 0"
class="item-list-journal-volumes">
<span *ngFor="let value of allMetadataValues(['journal.title']); let last=last;">
<span [innerHTML]="value"><span [innerHTML]="value"></span></span>
</span>
</span>
<span *ngIf="dso.allMetadata(['publicationvolume.volumeNumber']).length > 0"
class="item-list-journal-volume-identifiers">
<span *ngFor="let value of allMetadataValues(['publicationvolume.volumeNumber']); let last=last;">
<span> (</span><span [innerHTML]="value"><span [innerHTML]="value"></span></span><span>)</span>
</span>
</span>
</ds-truncatable-part>
</span>
</ds-truncatable>

View File

@@ -0,0 +1,124 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
import { By } from '@angular/platform-browser';
import { of as observableOf } from 'rxjs';
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { Item } from '../../../../../core/shared/item.model';
import { JournalVolumeSearchResultListElementComponent } from './journal-volume-search-result-list-element.component';
import { TruncatePipe } from '../../../../../shared/utils/truncate.pipe';
import { TruncatableService } from '../../../../../shared/truncatable/truncatable.service';
let journalVolumeListElementComponent: JournalVolumeSearchResultListElementComponent;
let fixture: ComponentFixture<JournalVolumeSearchResultListElementComponent>;
const mockItemWithMetadata: ItemSearchResult = Object.assign(
new ItemSearchResult(),
{
indexableObject: Object.assign(new Item(), {
bitstreams: observableOf({}),
metadata: {
'dc.title': [
{
language: 'en_US',
value: 'This is just another title'
}
],
'journal.title': [
{
language: 'en_US',
value: 'This is just another journal title'
}
],
'publicationvolume.volumeNumber': [
{
language: 'en_US',
value: '1234'
}
]
}
})
});
const mockItemWithoutMetadata: ItemSearchResult = Object.assign(
new ItemSearchResult(),
{
indexableObject: Object.assign(new Item(), {
bitstreams: observableOf({}),
metadata: {
'dc.title': [
{
language: 'en_US',
value: 'This is just another title'
}
]
}
})
});
describe('JournalVolumeSearchResultListElementComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [JournalVolumeSearchResultListElementComponent, TruncatePipe],
providers: [
{ provide: TruncatableService, useValue: {} }
],
schemas: [NO_ERRORS_SCHEMA]
}).overrideComponent(JournalVolumeSearchResultListElementComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
}).compileComponents();
}));
beforeEach(async(() => {
fixture = TestBed.createComponent(JournalVolumeSearchResultListElementComponent);
journalVolumeListElementComponent = fixture.componentInstance;
}));
describe('When the item has a journal title', () => {
beforeEach(() => {
journalVolumeListElementComponent.object = mockItemWithMetadata;
fixture.detectChanges();
});
it('should show the journal title span', () => {
const journalTitleField = fixture.debugElement.query(By.css('span.item-list-journal-volumes'));
expect(journalTitleField).not.toBeNull();
});
});
describe('When the item has no journal title', () => {
beforeEach(() => {
journalVolumeListElementComponent.object = mockItemWithoutMetadata;
fixture.detectChanges();
});
it('should not show the journal title span', () => {
const journalTitleField = fixture.debugElement.query(By.css('span.item-list-journal-volumes'));
expect(journalTitleField).toBeNull();
});
});
describe('When the item has a journal identifier', () => {
beforeEach(() => {
journalVolumeListElementComponent.object = mockItemWithMetadata;
fixture.detectChanges();
});
it('should show the journal identifiers span', () => {
const journalIdentifierField = fixture.debugElement.query(By.css('span.item-list-journal-volume-identifiers'));
expect(journalIdentifierField).not.toBeNull();
});
});
describe('When the item has no journal identifier', () => {
beforeEach(() => {
journalVolumeListElementComponent.object = mockItemWithoutMetadata;
fixture.detectChanges();
});
it('should not show the journal identifiers span', () => {
const journalIdentifierField = fixture.debugElement.query(By.css('span.item-list-journal-volume-identifiers'));
expect(journalIdentifierField).toBeNull();
});
});
});

View File

@@ -0,0 +1,18 @@
import { Component } from '@angular/core';
import { SearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/search-result-list-element.component';
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { Item } from '../../../../../core/shared/item.model';
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { ViewMode } from '../../../../../core/shared/view-mode.model';
@listableObjectComponent('JournalVolumeSearchResult', ViewMode.ListElement)
@Component({
selector: 'ds-journal-volume-search-result-list-element',
styleUrls: ['./journal-volume-search-result-list-element.component.scss'],
templateUrl: './journal-volume-search-result-list-element.component.html'
})
/**
* The component for displaying a list element for an item search result of the type Journal Volume
*/
export class JournalVolumeSearchResultListElementComponent extends SearchResultListElementComponent<ItemSearchResult, Item> {
}

View File

@@ -0,0 +1,19 @@
<ds-item-type-badge [object]="dso"></ds-item-type-badge>
<ds-truncatable [id]="dso.id">
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'" rel="noopener noreferrer"
[routerLink]="['/items/' + dso.id]" class="lead"
[innerHTML]="firstMetadataValue('dc.title')"></a>
<span *ngIf="linkType == linkTypes.None"
class="lead"
[innerHTML]="firstMetadataValue('dc.title')"></span>
<span class="text-muted">
<ds-truncatable-part [id]="dso.id" [minLines]="1">
<span *ngIf="dso.allMetadata(['creativeworkseries.issn']).length > 0"
class="item-list-journals">
<span *ngFor="let value of allMetadataValues(['creativeworkseries.issn']); let last=last;">
<span [innerHTML]="value"><span [innerHTML]="value"></span></span>
</span>
</span>
</ds-truncatable-part>
</span>
</ds-truncatable>

View File

@@ -0,0 +1,96 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core';
import { By } from '@angular/platform-browser';
import { of as observableOf } from 'rxjs';
import { JournalSearchResultListElementComponent } from './journal-search-result-list-element.component';
import { Item } from '../../../../../core/shared/item.model';
import { TruncatePipe } from '../../../../../shared/utils/truncate.pipe';
import { TruncatableService } from '../../../../../shared/truncatable/truncatable.service';
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
let journalListElementComponent: JournalSearchResultListElementComponent;
let fixture: ComponentFixture<JournalSearchResultListElementComponent>;
const mockItemWithMetadata: ItemSearchResult = Object.assign(
new ItemSearchResult(),
{
indexableObject: Object.assign(new Item(), {
bitstreams: observableOf({}),
metadata: {
'dc.title': [
{
language: 'en_US',
value: 'This is just another title'
}
],
'creativeworkseries.issn': [
{
language: 'en_US',
value: '1234'
}
]
}
})
});
const mockItemWithoutMetadata: ItemSearchResult = Object.assign(
new ItemSearchResult(),
{
indexableObject: Object.assign(new Item(), {
bitstreams: observableOf({}),
metadata: {
'dc.title': [
{
language: 'en_US',
value: 'This is just another title'
}
]
}
})
}
);
describe('JournalSearchResultListElementComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [JournalSearchResultListElementComponent, TruncatePipe],
providers: [
{ provide: TruncatableService, useValue: {} }
],
schemas: [NO_ERRORS_SCHEMA]
}).overrideComponent(JournalSearchResultListElementComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
}).compileComponents();
}));
beforeEach(async(() => {
fixture = TestBed.createComponent(JournalSearchResultListElementComponent);
journalListElementComponent = fixture.componentInstance;
}));
describe('When the item has an issn', () => {
beforeEach(() => {
journalListElementComponent.object = mockItemWithMetadata;
fixture.detectChanges();
});
it('should show the journals span', () => {
const issnField = fixture.debugElement.query(By.css('span.item-list-journals'));
expect(issnField).not.toBeNull();
});
});
describe('When the item has no issn', () => {
beforeEach(() => {
journalListElementComponent.object = mockItemWithoutMetadata;
fixture.detectChanges();
});
it('should not show the journals span', () => {
const issnField = fixture.debugElement.query(By.css('span.item-list-journals'));
expect(issnField).toBeNull();
});
});
});

View File

@@ -0,0 +1,18 @@
import { Component } from '@angular/core';
import { SearchResultListElementComponent } from '../../../../../shared/object-list/search-result-list-element/search-result-list-element.component';
import { ItemSearchResult } from '../../../../../shared/object-collection/shared/item-search-result.model';
import { Item } from '../../../../../core/shared/item.model';
import { listableObjectComponent } from '../../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { ViewMode } from '../../../../../core/shared/view-mode.model';
@listableObjectComponent('JournalSearchResult', ViewMode.ListElement)
@Component({
selector: 'ds-journal-search-result-list-element',
styleUrls: ['./journal-search-result-list-element.component.scss'],
templateUrl: './journal-search-result-list-element.component.html'
})
/**
* The component for displaying a list element for an item search result of the type Journal
*/
export class JournalSearchResultListElementComponent extends SearchResultListElementComponent<ItemSearchResult, Item> {
}

View File

@@ -1,54 +1,54 @@
<h2 class="item-page-title-field">
{{'journalissue.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="item?.allMetadata(['dc.title'])"></ds-metadata-values>
{{'journalissue.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
</h2>
<div class="row">
<div class="col-xs-12 col-md-4">
<ds-metadata-field-wrapper>
<ds-thumbnail [thumbnail]="this.item.getThumbnail() | async"></ds-thumbnail>
<ds-thumbnail [thumbnail]="object.getThumbnail() | async"></ds-thumbnail>
</ds-metadata-field-wrapper>
<ds-generic-item-page-field [item]="item"
<ds-generic-item-page-field [item]="object"
[fields]="['publicationvolume.volumeNumber']"
[label]="'journalvolume.page.volume'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
<ds-generic-item-page-field [item]="object"
[fields]="['publicationissue.issueNumber']"
[label]="'journalissue.page.number'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
<ds-generic-item-page-field [item]="object"
[fields]="['creativework.datePublished']"
[label]="'journalissue.page.issuedate'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
<ds-generic-item-page-field [item]="object"
[fields]="['journal.title']"
[label]="'journalissue.page.journal-title'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
<ds-generic-item-page-field [item]="object"
[fields]="['creativeworkseries.issn']"
[label]="'journalissue.page.journal-issn'">
</ds-generic-item-page-field>
</div>
<div class="col-xs-12 col-md-6">
<ds-related-items
[parentItem]="item"
[parentItem]="object"
[relationType]="'isJournalVolumeOfIssue'"
[label]="'relationships.isSingleVolumeOf' | translate">
</ds-related-items>
<ds-related-items
class="mb-1 mt-1"
[parentItem]="item"
[parentItem]="object"
[relationType]="'isPublicationOfJournalIssue'"
[label]="'relationships.isPublicationOfJournalIssue' | translate">
</ds-related-items>
<ds-generic-item-page-field [item]="item"
<ds-generic-item-page-field [item]="object"
[fields]="['dc.description']"
[label]="'journalissue.page.description'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
<ds-generic-item-page-field [item]="object"
[fields]="['creativework.keywords']"
[label]="'journalissue.page.keyword'">
</ds-generic-item-page-field>
<div>
<a class="btn btn-outline-primary" [routerLink]="['/items/' + item.id + '/full']">
<a class="btn btn-outline-primary" [routerLink]="['/items/' + object.id + '/full']">
{{"item.page.link.full" | translate}}
</a>
</div>

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

@@ -1,8 +1,9 @@
import { Component } from '@angular/core';
import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/item.component';
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { ViewMode } from '../../../../core/shared/view-mode.model';
@rendersItemType('JournalIssue', ItemViewMode.Full)
@listableObjectComponent('JournalIssue', ViewMode.StandalonePage)
@Component({
selector: 'ds-journal-issue',
styleUrls: ['./journal-issue.component.scss'],

View File

@@ -1,37 +1,37 @@
<h2 class="item-page-title-field">
{{'journalvolume.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="item?.allMetadata(['dc.title'])"></ds-metadata-values>
{{'journalvolume.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
</h2>
<div class="row">
<div class="col-xs-12 col-md-4">
<ds-metadata-field-wrapper>
<ds-thumbnail [thumbnail]="this.item.getThumbnail() | async"></ds-thumbnail>
<ds-thumbnail [thumbnail]="object.getThumbnail() | async"></ds-thumbnail>
</ds-metadata-field-wrapper>
<ds-generic-item-page-field [item]="item"
<ds-generic-item-page-field [item]="object"
[fields]="['publicationvolume.volumeNumber']"
[label]="'journalvolume.page.volume'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
<ds-generic-item-page-field [item]="object"
[fields]="['creativework.datePublished']"
[label]="'journalvolume.page.issuedate'">
</ds-generic-item-page-field>
</div>
<div class="col-xs-12 col-md-6">
<ds-related-items
[parentItem]="item"
[parentItem]="object"
[relationType]="'isJournalOfVolume'"
[label]="'relationships.isSingleJournalOf' | translate">
</ds-related-items>
<ds-related-items
[parentItem]="item"
[parentItem]="object"
[relationType]="'isIssueOfJournalVolume'"
[label]="'relationships.isIssueOf' | translate">
</ds-related-items>
<ds-generic-item-page-field [item]="item"
<ds-generic-item-page-field [item]="object"
[fields]="['dc.description']"
[label]="'journalvolume.page.description'">
</ds-generic-item-page-field>
<div>
<a class="btn btn-outline-primary" [routerLink]="['/items/' + item.id + '/full']">
<a class="btn btn-outline-primary" [routerLink]="['/items/' + object.id + '/full']">
{{"item.page.link.full" | translate}}
</a>
</div>

View File

@@ -1,8 +1,9 @@
import { Component } from '@angular/core';
import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/item.component';
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { ViewMode } from '../../../../core/shared/view-mode.model';
@rendersItemType('JournalVolume', ItemViewMode.Full)
@listableObjectComponent('JournalVolume', ViewMode.StandalonePage)
@Component({
selector: 'ds-journal-volume',
styleUrls: ['./journal-volume.component.scss'],

View File

@@ -1,42 +1,42 @@
<h2 class="item-page-title-field">
{{'journal.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="item?.allMetadata(['dc.title'])"></ds-metadata-values>
{{'journal.page.titleprefix' | translate}}<ds-metadata-values [mdValues]="object?.allMetadata(['dc.title'])"></ds-metadata-values>
</h2>
<div class="row">
<div class="col-xs-12 col-md-4">
<ds-metadata-field-wrapper>
<ds-thumbnail [thumbnail]="this.item.getThumbnail() | async"></ds-thumbnail>
<ds-thumbnail [thumbnail]="object.getThumbnail() | async"></ds-thumbnail>
</ds-metadata-field-wrapper>
<ds-generic-item-page-field class="item-page-fields" [item]="item"
<ds-generic-item-page-field class="item-page-fields" [item]="object"
[fields]="['creativeworkseries.issn']"
[label]="'journal.page.issn'">
</ds-generic-item-page-field>
<ds-generic-item-page-field class="item-page-fields" [item]="item"
<ds-generic-item-page-field class="item-page-fields" [item]="object"
[fields]="['creativework.publisher']"
[label]="'journal.page.publisher'">
</ds-generic-item-page-field>
<ds-generic-item-page-field [item]="item"
<ds-generic-item-page-field [item]="object"
[fields]="['creativework.editor']"
[label]="'journal.page.editor'">
</ds-generic-item-page-field>
</div>
<div class="col-xs-12 col-md-6">
<ds-related-items
[parentItem]="item"
[parentItem]="object"
[relationType]="'isVolumeOfJournal'"
[label]="'relationships.isVolumeOf' | translate">
</ds-related-items>
<ds-generic-item-page-field class="item-page-fields" [item]="item"
<ds-generic-item-page-field class="item-page-fields" [item]="object"
[fields]="['dc.description']"
[label]="'journal.page.description'">
</ds-generic-item-page-field>
<div>
<a class="btn btn-outline-primary" [routerLink]="['/items/' + item.id + '/full']">
<a class="btn btn-outline-primary" [routerLink]="['/items/' + object.id + '/full']">
{{"item.page.link.full" | translate}}
</a>
</div>
</div>
<div class="mt-5 w-100">
<ds-related-entities-search [item]="item"
<ds-related-entities-search [item]="object"
[relationType]="'isJournalOfPublication'">
</ds-related-entities-search>
</div>

View File

@@ -1,19 +1,16 @@
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/items/switcher/item-type-switcher.component';
import { TruncatePipe } from '../../../../shared/utils/truncate.pipe';
import { ItemDataService } from '../../../../core/data/item-data.service';
import { Item } from '../../../../core/shared/item.model';
import { By } from '@angular/platform-browser';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { MockTranslateLoader } from '../../../../shared/mocks/mock-translate-loader';
import { RemoteData } from '../../../../core/data/remote-data';
import { PaginatedList } from '../../../../core/data/paginated-list';
import { PageInfo } from '../../../../core/shared/page-info.model';
import { isNotEmpty } from '../../../../shared/empty.util';
import { JournalComponent } from './journal.component';
import { of as observableOf } from 'rxjs';
import { GenericItemPageFieldComponent } from '../../../../+item-page/simple/field-components/specific-field/generic/generic-item-page-field.component';
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/testing/utils';
@@ -55,7 +52,6 @@ describe('JournalComponent', () => {
})],
declarations: [JournalComponent, GenericItemPageFieldComponent, TruncatePipe],
providers: [
{provide: ITEM, useValue: mockItem},
{provide: ItemDataService, useValue: {}},
{provide: TruncatableService, useValue: {}}
],
@@ -69,6 +65,7 @@ describe('JournalComponent', () => {
beforeEach(async(() => {
fixture = TestBed.createComponent(JournalComponent);
comp = fixture.componentInstance;
comp.object = mockItem;
fixture.detectChanges();
}));

View File

@@ -1,8 +1,9 @@
import { Component } from '@angular/core';
import { ItemViewMode, rendersItemType } from '../../../../shared/items/item-type-decorator';
import { ItemComponent } from '../../../../+item-page/simple/item-types/shared/item.component';
import { listableObjectComponent } from '../../../../shared/object-collection/shared/listable-object/listable-object.decorator';
import { ViewMode } from '../../../../core/shared/view-mode.model';
@rendersItemType('Journal', ItemViewMode.Full)
@listableObjectComponent('Journal', ViewMode.StandalonePage)
@Component({
selector: 'ds-journal',
styleUrls: ['./journal.component.scss'],

View File

@@ -12,6 +12,12 @@ import { TooltipModule } from 'ngx-bootstrap';
import { JournalIssueGridElementComponent } from './item-grid-elements/journal-issue/journal-issue-grid-element.component';
import { JournalVolumeGridElementComponent } from './item-grid-elements/journal-volume/journal-volume-grid-element.component';
import { JournalGridElementComponent } from './item-grid-elements/journal/journal-grid-element.component';
import { JournalSearchResultListElementComponent } from './item-list-elements/search-result-list-elements/journal/journal-search-result-list-element.component';
import { JournalSearchResultGridElementComponent } from './item-grid-elements/search-result-grid-elements/journal/journal-search-result-grid-element.component';
import { JournalIssueSearchResultListElementComponent } from './item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component';
import { JournalVolumeSearchResultListElementComponent } from './item-list-elements/search-result-list-elements/journal-volume/journal-volume-search-result-list-element.component';
import { JournalIssueSearchResultGridElementComponent } from './item-grid-elements/search-result-grid-elements/journal-issue/journal-issue-search-result-grid-element.component';
import { JournalVolumeSearchResultGridElementComponent } from './item-grid-elements/search-result-grid-elements/journal-volume/journal-volume-search-result-grid-element.component';
const ENTRY_COMPONENTS = [
JournalComponent,
@@ -22,7 +28,13 @@ const ENTRY_COMPONENTS = [
JournalVolumeListElementComponent,
JournalIssueGridElementComponent,
JournalVolumeGridElementComponent,
JournalGridElementComponent
JournalGridElementComponent,
JournalSearchResultListElementComponent,
JournalIssueSearchResultListElementComponent,
JournalVolumeSearchResultListElementComponent,
JournalIssueSearchResultGridElementComponent,
JournalVolumeSearchResultGridElementComponent,
JournalSearchResultGridElementComponent
];
@NgModule({

View File

@@ -0,0 +1 @@
<ds-org-unit-search-result-grid-element [object]="{ indexableObject: object, hitHighlights: {} }" [linkType]="linkType"></ds-org-unit-search-result-grid-element>

Some files were not shown because too many files have changed in this diff Show More