From ec1da473811ae5582e6f78103eb8ca6cda4990f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Jolin-Nicol?= Date: Tue, 2 Aug 2022 13:38:58 -0400 Subject: [PATCH 1/6] Creating a Resource Type entity based on COAR Resource Types 3.0. New components and module to display the resource type details and search results. Metadata locale translation component and service. English and french i18n labels. --- .../admin-search-page/admin-search.module.ts | 4 +- .../metadata-translation.service.spec.ts | 94 ++++++++++++++++ .../metadata-translation.service.ts | 65 +++++++++++ .../resource-type-grid-element.component.html | 2 + .../resource-type-grid-element.component.scss | 0 ...source-type-grid-element.component.spec.ts | 104 +++++++++++++++++ .../resource-type-grid-element.component.ts | 17 +++ .../resource-type-list-element.component.html | 5 + .../resource-type-list-element.component.scss | 0 ...source-type-list-element.component.spec.ts | 104 +++++++++++++++++ .../resource-type-list-element.component.ts | 17 +++ .../item-page/resource-type.component.html | 74 ++++++++++++ .../item-page/resource-type.component.scss | 0 .../item-page/resource-type.component.spec.ts | 40 +++++++ .../item-page/resource-type.component.ts | 19 ++++ .../resource-type/resource-type.module.ts | 53 +++++++++ ...-search-result-grid-element.component.html | 46 ++++++++ ...-search-result-grid-element.component.scss | 0 ...arch-result-grid-element.component.spec.ts | 77 +++++++++++++ ...pe-search-result-grid-element.component.ts | 33 ++++++ ...-search-result-list-element.component.html | 15 +++ ...-search-result-list-element.component.scss | 0 ...arch-result-list-element.component.spec.ts | 89 +++++++++++++++ ...pe-search-result-list-element.component.ts | 31 ++++++ ...e-sidebar-search-list-element.component.ts | 21 ++++ .../import-external-page.module.ts | 4 +- .../metadata-values-locale.component.html | 5 + .../metadata-values-locale.component.scss | 0 .../metadata-values-locale.component.spec.ts | 105 ++++++++++++++++++ .../metadata-values-locale.component.ts | 74 ++++++++++++ src/app/item-page/item-page.module.ts | 6 +- .../item-page-field-locale.component.html | 3 + .../item-page-field-locale.component.spec.ts | 66 +++++++++++ .../item-page-field-locale.component.ts | 44 ++++++++ src/app/search-page/search-page.module.ts | 4 +- src/app/shared/shared.module.ts | 7 +- src/app/submission/submission.module.ts | 2 + src/assets/i18n/en.json5 | 53 +++++++++ src/assets/i18n/fr.json5 | 86 +++++++++++++- src/themes/custom/lazy-theme.module.ts | 2 + src/themes/dspace/lazy-theme.module.ts | 2 + 41 files changed, 1363 insertions(+), 10 deletions(-) create mode 100644 src/app/core/locale/metadata-translation/metadata-translation.service.spec.ts create mode 100644 src/app/core/locale/metadata-translation/metadata-translation.service.ts create mode 100644 src/app/entity-groups/resource-type/item-grid-elements/resource-type-grid-element.component.html create mode 100644 src/app/entity-groups/resource-type/item-grid-elements/resource-type-grid-element.component.scss create mode 100644 src/app/entity-groups/resource-type/item-grid-elements/resource-type-grid-element.component.spec.ts create mode 100644 src/app/entity-groups/resource-type/item-grid-elements/resource-type-grid-element.component.ts create mode 100644 src/app/entity-groups/resource-type/item-list-elements/resource-type-list-element.component.html create mode 100644 src/app/entity-groups/resource-type/item-list-elements/resource-type-list-element.component.scss create mode 100644 src/app/entity-groups/resource-type/item-list-elements/resource-type-list-element.component.spec.ts create mode 100644 src/app/entity-groups/resource-type/item-list-elements/resource-type-list-element.component.ts create mode 100644 src/app/entity-groups/resource-type/item-page/resource-type.component.html create mode 100644 src/app/entity-groups/resource-type/item-page/resource-type.component.scss create mode 100644 src/app/entity-groups/resource-type/item-page/resource-type.component.spec.ts create mode 100644 src/app/entity-groups/resource-type/item-page/resource-type.component.ts create mode 100644 src/app/entity-groups/resource-type/resource-type.module.ts create mode 100644 src/app/entity-groups/resource-type/search-result-grid-elements/resource-type-search-result-grid-element.component.html create mode 100644 src/app/entity-groups/resource-type/search-result-grid-elements/resource-type-search-result-grid-element.component.scss create mode 100644 src/app/entity-groups/resource-type/search-result-grid-elements/resource-type-search-result-grid-element.component.spec.ts create mode 100644 src/app/entity-groups/resource-type/search-result-grid-elements/resource-type-search-result-grid-element.component.ts create mode 100644 src/app/entity-groups/resource-type/search-result-list-elements/resource-type-search-result-list-element.component.html create mode 100644 src/app/entity-groups/resource-type/search-result-list-elements/resource-type-search-result-list-element.component.scss create mode 100644 src/app/entity-groups/resource-type/search-result-list-elements/resource-type-search-result-list-element.component.spec.ts create mode 100644 src/app/entity-groups/resource-type/search-result-list-elements/resource-type-search-result-list-element.component.ts create mode 100644 src/app/entity-groups/resource-type/sidebar-search-list-elements/resource-type-sidebar-search-list-element.component.ts create mode 100644 src/app/item-page/field-components/metadata-value-locale/metadata-values/metadata-values-locale.component.html create mode 100644 src/app/item-page/field-components/metadata-value-locale/metadata-values/metadata-values-locale.component.scss create mode 100644 src/app/item-page/field-components/metadata-value-locale/metadata-values/metadata-values-locale.component.spec.ts create mode 100644 src/app/item-page/field-components/metadata-value-locale/metadata-values/metadata-values-locale.component.ts create mode 100644 src/app/item-page/simple/field-components/specific-field/metadata-value-locale/item-page-field-locale.component.html create mode 100644 src/app/item-page/simple/field-components/specific-field/metadata-value-locale/item-page-field-locale.component.spec.ts create mode 100644 src/app/item-page/simple/field-components/specific-field/metadata-value-locale/item-page-field-locale.component.ts diff --git a/src/app/admin/admin-search-page/admin-search.module.ts b/src/app/admin/admin-search-page/admin-search.module.ts index 353d6dd4985..f75d56ec87e 100644 --- a/src/app/admin/admin-search-page/admin-search.module.ts +++ b/src/app/admin/admin-search-page/admin-search.module.ts @@ -10,6 +10,7 @@ import { CollectionAdminSearchResultGridElementComponent } from './admin-search- import { ItemAdminSearchResultActionsComponent } from './admin-search-results/item-admin-search-result-actions.component'; import { JournalEntitiesModule } from '../../entity-groups/journal-entities/journal-entities.module'; import { ResearchEntitiesModule } from '../../entity-groups/research-entities/research-entities.module'; +import { ResourceTypeModule } from '../../entity-groups/resource-type/resource-type.module'; import { SearchModule } from '../../shared/search/search.module'; const ENTRY_COMPONENTS = [ @@ -28,7 +29,8 @@ const ENTRY_COMPONENTS = [ SearchModule, SharedModule.withEntryComponents(), JournalEntitiesModule.withEntryComponents(), - ResearchEntitiesModule.withEntryComponents() + ResearchEntitiesModule.withEntryComponents(), + ResourceTypeModule.withEntryComponents() ], declarations: [ AdminSearchPageComponent, diff --git a/src/app/core/locale/metadata-translation/metadata-translation.service.spec.ts b/src/app/core/locale/metadata-translation/metadata-translation.service.spec.ts new file mode 100644 index 00000000000..2e69e6b0f1b --- /dev/null +++ b/src/app/core/locale/metadata-translation/metadata-translation.service.spec.ts @@ -0,0 +1,94 @@ +import { MetadataTranslationService } from './metadata-translation.service'; +import { DSpaceObject } from '../../shared/dspace-object.model'; + +describe('MetadataTranslationService', () => { + let testedService: MetadataTranslationService; + + const dcKey = 'dc.title'; + const englishTitle = 'english title'; + const frenchTitle = 'titre français'; + const spanishTitle = 'titulo español'; + const emptyLanguageTitle = 'empty language title'; + + const dso = Object.assign(new DSpaceObject(), { + metadata: { + 'dc.title': [ + { value: englishTitle, language: 'en' }, + { value: frenchTitle, language: 'fr' }, + { value: spanishTitle, language: 'es' }, + { value: emptyLanguageTitle } + ], + 'other.key': [{ value: 'other value' }], + 'other.en.key': [{ value: 'other en value', language: 'en' }] + } + }); + + const dsoNoEnglishNoFrench = Object.assign(new DSpaceObject(), { + metadata: { + 'dc.title': [ + { value: spanishTitle, language: 'es' }, + { value: emptyLanguageTitle } + ], + 'other.key': [{ value: 'other value' }], + 'other.en.key': [{ value: 'other en value', language: 'en' }] + } + }); + + const localServiceStubFrench: any = { + getCurrentLanguageCode(): string { return 'fr'; }, + }; + const localServiceStubEnglish: any = { + getCurrentLanguageCode(): string { return 'en'; }, + }; + const localServiceStubGerman: any = { + getCurrentLanguageCode(): string { return 'de'; }, + }; + + beforeEach(() => { + testedService = new MetadataTranslationService(localServiceStubFrench); + }); + + describe('currentLanguageValue', () => { + it('should return metadata value for the current language code', () => { + let value = testedService.currentLanguageValue(dso, dcKey) + expect(value).toEqual(frenchTitle); + }); + }); + + describe('defaultLanguageValue', () => { + it('should return metadata value for the default language code', () => { + let value = testedService.defaultLanguageValue(dso, dcKey) + expect(value).toEqual(englishTitle); + }); + }); + + describe('emptyLanguageValue', () => { + it('should return metadata value without language code', () => { + let value = testedService.emptyLanguageValue(dso, dcKey) + expect(value).toEqual(emptyLanguageTitle); + }); + }); + + describe('currentLanguageValueOrDefault', () => { + it('should return english metadata value with english locale', () => { + testedService = new MetadataTranslationService(localServiceStubEnglish); + let value = testedService.currentLanguageValueOrDefault(dso, dcKey) + expect(value).toEqual(englishTitle); + }); + it('should return french metadata value with french locale', () => { + let value = testedService.currentLanguageValueOrDefault(dso, dcKey) + expect(value).toEqual(frenchTitle); + }); + it('should display default language metadata value if locale is not available', () => { + testedService = new MetadataTranslationService(localServiceStubGerman); + let value = testedService.currentLanguageValueOrDefault(dso, dcKey) + expect(value).toEqual(englishTitle); + }); + it('should display empty language metadata value if both locale and default language are not availables', () => { + let value = testedService.currentLanguageValueOrDefault(dsoNoEnglishNoFrench, dcKey) + expect(value).toEqual(emptyLanguageTitle); + }); + + }); + +}); diff --git a/src/app/core/locale/metadata-translation/metadata-translation.service.ts b/src/app/core/locale/metadata-translation/metadata-translation.service.ts new file mode 100644 index 00000000000..c3a6aef6cf6 --- /dev/null +++ b/src/app/core/locale/metadata-translation/metadata-translation.service.ts @@ -0,0 +1,65 @@ +import { Inject, Injectable } from '@angular/core'; +import { isEmpty, isNotEmpty } from '../../../shared/empty.util'; +import { LocaleService } from '../locale.service'; +import { MetadataValue, MetadataValueFilter } from '../../shared/metadata.models'; +import { DSpaceObject } from '../../shared/dspace-object.model'; +import { isUndefined } from '../../../shared/empty.util'; + +@Injectable({ + providedIn: 'root' +}) +/** + * Translate metadata from a DSpaceObject using the current language code from the locale service. + */ +export class MetadataTranslationService { + + /** + * The filtered metadata language + */ + language: string; + + private currentLanguageFilter: MetadataValueFilter; + + readonly defaultFilter: MetadataValueFilter = { language: 'en' }; + + constructor( + protected localeService: LocaleService, + ) { + this.currentLanguageFilter = { language: this.localeService.getCurrentLanguageCode() }; + } + + /* First metadata value for the current language from the locale service */ + currentLanguageValue(dso: DSpaceObject, keyOrKeys: string | string[]): string { + const md = dso.firstMetadata(keyOrKeys, this.currentLanguageFilter); + return this.getValue(md); + } + + /* First metadata value for the defaultFilter defined language (en) */ + defaultLanguageValue(dso: DSpaceObject, keyOrKeys: string | string[]): string { + const md = dso.firstMetadata(keyOrKeys, this.defaultFilter); + return this.getValue(md); + } + + /* First metadata value without language defined */ + emptyLanguageValue(dso: DSpaceObject, keyOrKeys: string | string[]): string { + const md = dso.allMetadata(keyOrKeys).find(val => isEmpty(val.language)); + return this.getValue(md); + } + + /* First metadata value for the current language. Then falls back on default then empty language value. */ + currentLanguageValueOrDefault(dso: DSpaceObject, keyOrKeys: string | string[]): string { + let mdValue = this.currentLanguageValue(dso, keyOrKeys); + if (!mdValue) { + mdValue = this.defaultLanguageValue(dso, keyOrKeys); + } + if (!mdValue) { + mdValue = this.emptyLanguageValue(dso, keyOrKeys); + } + return String(mdValue); + } + + private getValue(mdValue: MetadataValue): string { + return isUndefined(mdValue) ? undefined : mdValue.value; + } + +} diff --git a/src/app/entity-groups/resource-type/item-grid-elements/resource-type-grid-element.component.html b/src/app/entity-groups/resource-type/item-grid-elements/resource-type-grid-element.component.html new file mode 100644 index 00000000000..324c90af483 --- /dev/null +++ b/src/app/entity-groups/resource-type/item-grid-elements/resource-type-grid-element.component.html @@ -0,0 +1,2 @@ + + diff --git a/src/app/entity-groups/resource-type/item-grid-elements/resource-type-grid-element.component.scss b/src/app/entity-groups/resource-type/item-grid-elements/resource-type-grid-element.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/app/entity-groups/resource-type/item-grid-elements/resource-type-grid-element.component.spec.ts b/src/app/entity-groups/resource-type/item-grid-elements/resource-type-grid-element.component.spec.ts new file mode 100644 index 00000000000..4ccf4f8d185 --- /dev/null +++ b/src/app/entity-groups/resource-type/item-grid-elements/resource-type-grid-element.component.spec.ts @@ -0,0 +1,104 @@ +import { ResourceTypeGridElementComponent } from './resource-type-grid-element.component'; + +import { Item } from '../../../core/shared/item.model'; +import { of as observableOf } from 'rxjs'; +import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; +import { buildPaginatedList } from '../../../core/data/paginated-list.model'; +import { PageInfo } from '../../../core/shared/page-info.model'; +import { TestBed, waitForAsync } 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 mockItem = Object.assign(new Item(), { + bundles: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])), + metadata: { + 'resourcetypes.​name': [ + { + language: null, + value: 'doctoral thesis' + } + ], + 'resourcetypes.definition': [ + { + language: null, + value: 'A thesis reporting the research undertaken during a period of graduate study leading to a doctoral degree.' + } + ], + 'resourcetypes.preferredLabels': [ + { + language: 'en', + value: 'doctoral thesis' + }, + { + language: 'es', + value: 'tesis doctoral' + }, + { + language: 'fr', + value: 'thèse de doctorat' + }, + { + language: 'de', + value: 'Dissertation' + }, + { + language: 'it', + value: 'tesi di dottorato' + }, + ], + 'resourcetypes.​relatedTerms': [ + { + language: null, + value: 'Broad Match: http://purl.org/eprint/type/Thesis' + } + ], + 'resourcetypes.uri': [ + { + language: null, + value: 'http://purl.org/coar/resource_type/c_db06' + } + ] + } +}); + +describe('ResourceTypeGridElementComponent', () => { + let comp; + let fixture; + + const truncatableServiceStub: any = { + isCollapsed: (id: number) => observableOf(true), + }; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [NoopAnimationsModule], + declarations: [ResourceTypeGridElementComponent, TruncatePipe], + providers: [ + { provide: TruncatableService, useValue: truncatableServiceStub }, + ], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(ResourceTypeGridElementComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(waitForAsync(() => { + fixture = TestBed.createComponent(ResourceTypeGridElementComponent); + comp = fixture.componentInstance; + })); + + describe(`when the resource type is rendered`, () => { + beforeEach(() => { + comp.object = mockItem; + fixture.detectChanges(); + }); + + it(`should contain a ResourceTypeGridElementComponent`, () => { + const resourceTypeGridElement = fixture.debugElement.query(By.css(`ds-resource-type-search-result-grid-element`)); + expect(resourceTypeGridElement).not.toBeNull(); + }); + }); +}); diff --git a/src/app/entity-groups/resource-type/item-grid-elements/resource-type-grid-element.component.ts b/src/app/entity-groups/resource-type/item-grid-elements/resource-type-grid-element.component.ts new file mode 100644 index 00000000000..720bd1c2184 --- /dev/null +++ b/src/app/entity-groups/resource-type/item-grid-elements/resource-type-grid-element.component.ts @@ -0,0 +1,17 @@ +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 { AbstractListableElementComponent } from '../../../shared/object-collection/shared/object-collection-element/abstract-listable-element.component'; +import { Item } from '../../../core/shared/item.model'; + +@listableObjectComponent('ResourceType', ViewMode.GridElement) +@Component({ + selector: 'ds-resource-type-grid-element', + styleUrls: ['./resource-type-grid-element.component.scss'], + templateUrl: './resource-type-grid-element.component.html', +}) +/** + * The component for displaying a grid element for an item of the type ResourceType + */ +export class ResourceTypeGridElementComponent extends AbstractListableElementComponent { +} diff --git a/src/app/entity-groups/resource-type/item-list-elements/resource-type-list-element.component.html b/src/app/entity-groups/resource-type/item-list-elements/resource-type-list-element.component.html new file mode 100644 index 00000000000..fc5c6c0e74d --- /dev/null +++ b/src/app/entity-groups/resource-type/item-list-elements/resource-type-list-element.component.html @@ -0,0 +1,5 @@ + + diff --git a/src/app/entity-groups/resource-type/item-list-elements/resource-type-list-element.component.scss b/src/app/entity-groups/resource-type/item-list-elements/resource-type-list-element.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/app/entity-groups/resource-type/item-list-elements/resource-type-list-element.component.spec.ts b/src/app/entity-groups/resource-type/item-list-elements/resource-type-list-element.component.spec.ts new file mode 100644 index 00000000000..0c173ac59b9 --- /dev/null +++ b/src/app/entity-groups/resource-type/item-list-elements/resource-type-list-element.component.spec.ts @@ -0,0 +1,104 @@ +import { ResourceTypeListElementComponent } from './resource-type-list-element.component'; + +import { Item } from '../../../core/shared/item.model'; +import { of as observableOf } from 'rxjs'; +import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; +import { buildPaginatedList } from '../../../core/data/paginated-list.model'; +import { PageInfo } from '../../../core/shared/page-info.model'; +import { TestBed, waitForAsync } 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 mockItem = Object.assign(new Item(), { + bundles: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])), + metadata: { + 'resourcetypes.​name': [ + { + language: null, + value: 'doctoral thesis' + } + ], + 'resourcetypes.definition': [ + { + language: null, + value: 'A thesis reporting the research undertaken during a period of graduate study leading to a doctoral degree.' + } + ], + 'resourcetypes.preferredLabels': [ + { + language: 'en', + value: 'doctoral thesis' + }, + { + language: 'es', + value: 'tesis doctoral' + }, + { + language: 'fr', + value: 'thèse de doctorat' + }, + { + language: 'de', + value: 'Dissertation' + }, + { + language: 'it', + value: 'tesi di dottorato' + }, + ], + 'resourcetypes.​relatedTerms': [ + { + language: null, + value: 'Broad Match: http://purl.org/eprint/type/Thesis' + } + ], + 'resourcetypes.uri': [ + { + language: null, + value: 'http://purl.org/coar/resource_type/c_db06' + } + ] + } +}); + +describe('ResourceTypeListElementComponent', () => { + let comp; + let fixture; + + const truncatableServiceStub: any = { + isCollapsed: (id: number) => observableOf(true), + }; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [NoopAnimationsModule], + declarations: [ResourceTypeListElementComponent, TruncatePipe], + providers: [ + { provide: TruncatableService, useValue: truncatableServiceStub }, + ], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(ResourceTypeListElementComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(waitForAsync(() => { + fixture = TestBed.createComponent(ResourceTypeListElementComponent); + comp = fixture.componentInstance; + })); + + describe(`when the resource type is rendered`, () => { + beforeEach(() => { + comp.object = mockItem; + fixture.detectChanges(); + }); + + it(`should contain a ResourceTypeListElementComponent`, () => { + const resourceTypeListElement = fixture.debugElement.query(By.css(`ds-resource-type-search-result-list-element`)); + expect(resourceTypeListElement).not.toBeNull(); + }); + }); +}); diff --git a/src/app/entity-groups/resource-type/item-list-elements/resource-type-list-element.component.ts b/src/app/entity-groups/resource-type/item-list-elements/resource-type-list-element.component.ts new file mode 100644 index 00000000000..7d4d5beb75a --- /dev/null +++ b/src/app/entity-groups/resource-type/item-list-elements/resource-type-list-element.component.ts @@ -0,0 +1,17 @@ +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 { AbstractListableElementComponent } from '../../../shared/object-collection/shared/object-collection-element/abstract-listable-element.component'; +import { Item } from '../../../core/shared/item.model'; + +@listableObjectComponent('ResourceType', ViewMode.ListElement) +@Component({ + selector: 'ds-resource-type-list-element', + styleUrls: ['./resource-type-list-element.component.scss'], + templateUrl: './resource-type-list-element.component.html' +}) +/** + * The component for displaying a list element for an item of the type ResourceType + */ +export class ResourceTypeListElementComponent extends AbstractListableElementComponent { +} diff --git a/src/app/entity-groups/resource-type/item-page/resource-type.component.html b/src/app/entity-groups/resource-type/item-page/resource-type.component.html new file mode 100644 index 00000000000..6da898140db --- /dev/null +++ b/src/app/entity-groups/resource-type/item-page/resource-type.component.html @@ -0,0 +1,74 @@ +
+

+ {{'resourcetype.page.titleprefix' | translate}} + +

+
+ +
+
+ +
+
+ + + + + + + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+ +
+ + +
+
diff --git a/src/app/entity-groups/resource-type/item-page/resource-type.component.scss b/src/app/entity-groups/resource-type/item-page/resource-type.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/app/entity-groups/resource-type/item-page/resource-type.component.spec.ts b/src/app/entity-groups/resource-type/item-page/resource-type.component.spec.ts new file mode 100644 index 00000000000..29abc7fbdf9 --- /dev/null +++ b/src/app/entity-groups/resource-type/item-page/resource-type.component.spec.ts @@ -0,0 +1,40 @@ +import { + createRelationshipsObservable, + getItemPageFieldsTest +} from '../../../item-page/simple/item-types/shared/item.component.spec'; +import { buildPaginatedList } from '../../../core/data/paginated-list.model'; +import { Item } from '../../../core/shared/item.model'; +import { PageInfo } from '../../../core/shared/page-info.model'; +import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; +import { ResourceTypeComponent } from './resource-type.component'; + +const mockItem: Item = Object.assign(new Item(), { + bundles: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])), + metadata: { + 'resourcetypes.preferredLabels': [ + { + language: 'en', + value: 'doctoral thesis' + }, + { + language: 'es', + value: 'tesis doctoral' + }, + { + language: 'fr', + value: 'thèse de doctorat' + }, + { + language: 'de', + value: 'Dissertation' + }, + { + language: 'it', + value: 'tesi di dottorato' + }, + ], + }, + relationships: createRelationshipsObservable() +}); + +describe('ResourceTypeComponent', getItemPageFieldsTest(mockItem, ResourceTypeComponent)); diff --git a/src/app/entity-groups/resource-type/item-page/resource-type.component.ts b/src/app/entity-groups/resource-type/item-page/resource-type.component.ts new file mode 100644 index 00000000000..e9996620589 --- /dev/null +++ b/src/app/entity-groups/resource-type/item-page/resource-type.component.ts @@ -0,0 +1,19 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { ItemComponent } from '../../../item-page/simple/item-types/shared/item.component'; +import { ViewMode } from '../../../core/shared/view-mode.model'; +import { listableObjectComponent } from '../../../shared/object-collection/shared/listable-object/listable-object.decorator'; +import { Context } from 'src/app/core/shared/context.model'; + +/** + * Component that represents a resource type Item page + */ +@listableObjectComponent('ResourceType', ViewMode.StandalonePage, Context.Any, 'ul') +@Component({ + selector: 'ds-resource-type', + styleUrls: ['./resource-type.component.scss'], + templateUrl: './resource-type.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ResourceTypeComponent extends ItemComponent { + +} diff --git a/src/app/entity-groups/resource-type/resource-type.module.ts b/src/app/entity-groups/resource-type/resource-type.module.ts new file mode 100644 index 00000000000..0821de6f683 --- /dev/null +++ b/src/app/entity-groups/resource-type/resource-type.module.ts @@ -0,0 +1,53 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'; +import { SharedModule } from '../../shared/shared.module'; +import { ItemSharedModule } from '../../item-page/item-shared.module'; +import { ResourceTypeListElementComponent } from './item-list-elements/resource-type-list-element.component'; +import { ResourceTypeSearchResultListElementComponent } from './search-result-list-elements/resource-type-search-result-list-element.component'; +import { ResourceTypeSidebarSearchListElementComponent } from './sidebar-search-list-elements/resource-type-sidebar-search-list-element.component'; +import { ResourceTypeGridElementComponent } from './item-grid-elements/resource-type-grid-element.component'; +import { ResourceTypeSearchResultGridElementComponent } from './search-result-grid-elements/resource-type-search-result-grid-element.component'; +import { ResourceTypeComponent } from './item-page/resource-type.component'; +import { ItemPageFieldLocaleComponent } from '../../item-page/simple/field-components/specific-field/metadata-value-locale/item-page-field-locale.component'; +import { MetadataValuesLocaleComponent } from '../../item-page/field-components/metadata-value-locale/metadata-values/metadata-values-locale.component'; + +const ENTRY_COMPONENTS = [ +// put only entry components that use custom decorator + ResourceTypeComponent, + ResourceTypeListElementComponent, + ResourceTypeSearchResultListElementComponent, + ResourceTypeSidebarSearchListElementComponent, + ResourceTypeGridElementComponent, + ResourceTypeSearchResultGridElementComponent, + ItemPageFieldLocaleComponent, + MetadataValuesLocaleComponent, +]; + +const COMPONENTS = [ + ...ENTRY_COMPONENTS +]; + +@NgModule({ + imports: [ + CommonModule, + ItemSharedModule, + SharedModule, + NgbTooltipModule, + ], + declarations: [ + ...COMPONENTS, + ] +}) +export class ResourceTypeModule { + /** + * NOTE: this method allows to resolve issue with components that using a custom decorator + * which are not loaded during SSR otherwise + */ + static withEntryComponents() { + return { + ngModule: ResourceTypeModule, + providers: ENTRY_COMPONENTS.map((component) => ({ provide: component })) + }; + } +} diff --git a/src/app/entity-groups/resource-type/search-result-grid-elements/resource-type-search-result-grid-element.component.html b/src/app/entity-groups/resource-type/search-result-grid-elements/resource-type-search-result-grid-element.component.html new file mode 100644 index 00000000000..a4700bf9dc0 --- /dev/null +++ b/src/app/entity-groups/resource-type/search-result-grid-elements/resource-type-search-result-grid-element.component.html @@ -0,0 +1,46 @@ +
+ +
+ +
+ + +
+ + + +

+
+
+

+ + + +

+
+ View +
+
+
+ +
diff --git a/src/app/entity-groups/resource-type/search-result-grid-elements/resource-type-search-result-grid-element.component.scss b/src/app/entity-groups/resource-type/search-result-grid-elements/resource-type-search-result-grid-element.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/app/entity-groups/resource-type/search-result-grid-elements/resource-type-search-result-grid-element.component.spec.ts b/src/app/entity-groups/resource-type/search-result-grid-elements/resource-type-search-result-grid-element.component.spec.ts new file mode 100644 index 00000000000..06fe8b4d239 --- /dev/null +++ b/src/app/entity-groups/resource-type/search-result-grid-elements/resource-type-search-result-grid-element.component.spec.ts @@ -0,0 +1,77 @@ +import { ItemSearchResult } from '../../../shared/object-collection/shared/item-search-result.model'; +import { Item } from '../../../core/shared/item.model'; +import { createSuccessfulRemoteDataObject$ } from '../../../shared/remote-data.utils'; +import { buildPaginatedList } from '../../../core/data/paginated-list.model'; +import { PageInfo } from '../../../core/shared/page-info.model'; +import { ResourceTypeSearchResultGridElementComponent } from './resource-type-search-result-grid-element.component'; +import { getEntityGridElementTestComponent } from '../../../shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.spec'; + +const mockItemWithMetadata: ItemSearchResult = new ItemSearchResult(); +mockItemWithMetadata.hitHighlights = {}; +mockItemWithMetadata.indexableObject = Object.assign(new Item(), { + bundles: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])), + metadata: { + 'resourcetypes.name': [ + { + language: null, + value: 'doctoral thesis' + } + ], + 'resourcetypes.definition': [ + { + language: null, + value: 'A thesis reporting the research undertaken during a period of graduate study leading to a doctoral degree.' + } + ], + 'resourcetypes.preferredLabels': [ + { + language: 'en', + value: 'doctoral thesis' + }, + { + language: 'es', + value: 'tesis doctoral' + }, + { + language: 'fr', + value: 'thèse de doctorat' + }, + { + language: 'de', + value: 'Dissertation' + }, + { + language: 'it', + value: 'tesi di dottorato' + }, + ], + 'resourcetypes.relatedTerms': [ + { + language: null, + value: 'Broad Match: http://purl.org/eprint/type/Thesis' + } + ], + 'resourcetypes.uri': [ + { + language: null, + value: 'http://purl.org/coar/resource_type/c_db06' + } + ] + } +}); + +const mockItemWithoutMetadata: ItemSearchResult = new ItemSearchResult(); +mockItemWithoutMetadata.hitHighlights = {}; +mockItemWithoutMetadata.indexableObject = Object.assign(new Item(), { + bundles: createSuccessfulRemoteDataObject$(buildPaginatedList(new PageInfo(), [])), + metadata: { + 'resourcetypes.name': [ + { + language: null, + value: 'doctoral thesis' + } + ], + } +}); + +describe('ResourceTypeSearchResultGridElementComponent', getEntityGridElementTestComponent(ResourceTypeSearchResultGridElementComponent, mockItemWithMetadata, mockItemWithoutMetadata, ['definition'])); diff --git a/src/app/entity-groups/resource-type/search-result-grid-elements/resource-type-search-result-grid-element.component.ts b/src/app/entity-groups/resource-type/search-result-grid-elements/resource-type-search-result-grid-element.component.ts new file mode 100644 index 00000000000..564590c4eb6 --- /dev/null +++ b/src/app/entity-groups/resource-type/search-result-grid-elements/resource-type-search-result-grid-element.component.ts @@ -0,0 +1,33 @@ +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 { focusShadow } from '../../../shared/animations/focus'; +import { ItemSearchResultGridElementComponent } from '../../../shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component'; +import { TruncatableService } from '../../../shared/truncatable/truncatable.service'; +import { MetadataTranslationService } from '../../../core/locale/metadata-translation/metadata-translation.service'; +import { BitstreamDataService } from '../../../core/data/bitstream-data.service'; + +@listableObjectComponent('ResourceTypeSearchResult', ViewMode.GridElement) +@Component({ + selector: 'ds-resource-type-search-result-grid-element', + styleUrls: ['./resource-type-search-result-grid-element.component.scss'], + templateUrl: './resource-type-search-result-grid-element.component.html', + animations: [focusShadow] +}) +/** + * The component for displaying a grid element for an item search result of the type ResourceType + */ +export class ResourceTypeSearchResultGridElementComponent extends ItemSearchResultGridElementComponent { + + public constructor( + protected truncatableService: TruncatableService, + protected bitstreamDataService: BitstreamDataService, + protected metadataTranslationService: MetadataTranslationService) { + super(truncatableService, bitstreamDataService); + } + + currentLanguageValueOrDefault(keyOrKeys: string | string[]): string { + return this.metadataTranslationService.currentLanguageValueOrDefault(this.dso, keyOrKeys); + } + +} diff --git a/src/app/entity-groups/resource-type/search-result-list-elements/resource-type-search-result-list-element.component.html b/src/app/entity-groups/resource-type/search-result-list-elements/resource-type-search-result-list-element.component.html new file mode 100644 index 00000000000..3fb732ef69f --- /dev/null +++ b/src/app/entity-groups/resource-type/search-result-list-elements/resource-type-search-result-list-element.component.html @@ -0,0 +1,15 @@ + + + + + + + + + + + diff --git a/src/app/entity-groups/resource-type/search-result-list-elements/resource-type-search-result-list-element.component.scss b/src/app/entity-groups/resource-type/search-result-list-elements/resource-type-search-result-list-element.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/app/entity-groups/resource-type/search-result-list-elements/resource-type-search-result-list-element.component.spec.ts b/src/app/entity-groups/resource-type/search-result-list-elements/resource-type-search-result-list-element.component.spec.ts new file mode 100644 index 00000000000..63bde10a39a --- /dev/null +++ b/src/app/entity-groups/resource-type/search-result-list-elements/resource-type-search-result-list-element.component.spec.ts @@ -0,0 +1,89 @@ +import { waitForAsync, 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 { ResourceTypeSearchResultListElementComponent } from './resource-type-search-result-list-element.component'; +import { TruncatePipe } from '../../../shared/utils/truncate.pipe'; +import { TruncatableService } from '../../../shared/truncatable/truncatable.service'; +import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; +import { DSONameServiceMock } from '../../../shared/mocks/dso-name.service.mock'; +import { LocaleService } from '../../../core/locale/locale.service'; + + +let resourceTypeSearchResultListElementComponent: ResourceTypeSearchResultListElementComponent; +let fixture: ComponentFixture; + +const englishLabel = 'doctoral thesis'; +const frenchLabel = 'doctoral thesis'; +const spanishLabel = 'tesis doctoral'; +const englishDefinition = 'A thesis reporting the research undertaken during a period of graduate study leading to a doctoral degree.'; + +const localServiceStubSpanish: any = { + getCurrentLanguageCode(): string { return 'es'; }, +}; + +const mockItem: ItemSearchResult = Object.assign( + new ItemSearchResult(), + { + indexableObject: Object.assign(new Item(), { + bundles: observableOf({}), + metadata: { + 'resourcetypes.preferredLabels': [ + { + language: 'en', + value: englishLabel + }, + { + language: 'fr', + value: frenchLabel + }, + { + language: 'es', + value: spanishLabel + }, + ], + 'resourcetypes.definition': [ + { + language: 'en', + value: englishDefinition + } + ] + } + }) + }); + +describe('ResourceTypeSearchResultListElementComponent', () => { + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [ResourceTypeSearchResultListElementComponent, TruncatePipe], + providers: [ + { provide: TruncatableService, useValue: {} }, + { provide: DSONameService, useClass: DSONameServiceMock }, + { provide: LocaleService, useValue: localServiceStubSpanish }, + ], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(ResourceTypeSearchResultListElementComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(waitForAsync(() => { + fixture = TestBed.createComponent(ResourceTypeSearchResultListElementComponent); + resourceTypeSearchResultListElementComponent = fixture.componentInstance; + resourceTypeSearchResultListElementComponent.object = mockItem; + fixture.detectChanges(); + })); + + it('should show the resource type prefered label of the current language code when availlable', () => { + const innerHTML = fixture.nativeElement.innerHTML; + expect(innerHTML).toContain(spanishLabel); + }); + + it('should show the resource type definition of the default language code when current language code is unavaillable', () => { + const innerHTML = fixture.nativeElement.innerHTML; + expect(innerHTML).toContain(englishDefinition); + }); + +}); diff --git a/src/app/entity-groups/resource-type/search-result-list-elements/resource-type-search-result-list-element.component.ts b/src/app/entity-groups/resource-type/search-result-list-elements/resource-type-search-result-list-element.component.ts new file mode 100644 index 00000000000..528f47ad130 --- /dev/null +++ b/src/app/entity-groups/resource-type/search-result-list-elements/resource-type-search-result-list-element.component.ts @@ -0,0 +1,31 @@ +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 { ItemSearchResultListElementComponent } from '../../../shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component'; +import { TruncatableService } from '../../../shared/truncatable/truncatable.service'; +import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; +import { MetadataTranslationService } from '../../../core/locale/metadata-translation/metadata-translation.service'; + +@listableObjectComponent('ResourceTypeSearchResult', ViewMode.ListElement) +@Component({ + selector: 'ds-resource-type-search-result-list-element', + styleUrls: ['./resource-type-search-result-list-element.component.scss'], + templateUrl: './resource-type-search-result-list-element.component.html' +}) +/** + * The component for displaying a list element for an item search result of the type ResourceType + */ +export class ResourceTypeSearchResultListElementComponent extends ItemSearchResultListElementComponent { + + public constructor( + protected truncatableService: TruncatableService, + protected dsoNameService: DSONameService, + protected metadataTranslationService: MetadataTranslationService) { + super(truncatableService, dsoNameService); + } + + currentLanguageValueOrDefault(keyOrKeys: string | string[]): string { + return this.metadataTranslationService.currentLanguageValueOrDefault(this.dso, keyOrKeys); + } + +} diff --git a/src/app/entity-groups/resource-type/sidebar-search-list-elements/resource-type-sidebar-search-list-element.component.ts b/src/app/entity-groups/resource-type/sidebar-search-list-elements/resource-type-sidebar-search-list-element.component.ts new file mode 100644 index 00000000000..26e27c4b3cb --- /dev/null +++ b/src/app/entity-groups/resource-type/sidebar-search-list-elements/resource-type-sidebar-search-list-element.component.ts @@ -0,0 +1,21 @@ +import { listableObjectComponent } from '../../../shared/object-collection/shared/listable-object/listable-object.decorator'; +import { ViewMode } from '../../../core/shared/view-mode.model'; +import { Context } from '../../../core/shared/context.model'; +import { ItemSearchResult } from '../../../shared/object-collection/shared/item-search-result.model'; +import { Component } from '@angular/core'; +import { SidebarSearchListElementComponent } from '../../../shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component'; +import { Item } from '../../../core/shared/item.model'; +import { isNotEmpty } from '../../../shared/empty.util'; + +@listableObjectComponent('ResourceTypeSearchResult', ViewMode.ListElement, Context.SideBarSearchModal) +@listableObjectComponent('ResourceTypeSearchResult', ViewMode.ListElement, Context.SideBarSearchModalCurrent) +@Component({ + selector: 'ds-resource-type-sidebar-search-list-element', + templateUrl: '../../../shared/object-list/sidebar-search-list-element/sidebar-search-list-element.component.html' +}) +/** + * Component displaying a list element for a {@link ItemSearchResult} of type "ResourceType" within the context of + * a sidebar search modal + */ +export class ResourceTypeSidebarSearchListElementComponent extends SidebarSearchListElementComponent { +} diff --git a/src/app/import-external-page/import-external-page.module.ts b/src/app/import-external-page/import-external-page.module.ts index 1a0fd9e3604..37b593aaec3 100644 --- a/src/app/import-external-page/import-external-page.module.ts +++ b/src/app/import-external-page/import-external-page.module.ts @@ -8,6 +8,7 @@ import { SubmissionModule } from '../submission/submission.module'; import { ImportExternalPageComponent } from './import-external-page.component'; import { JournalEntitiesModule } from '../entity-groups/journal-entities/journal-entities.module'; import { ResearchEntitiesModule } from '../entity-groups/research-entities/research-entities.module'; +import { ResourceTypeModule } from '../entity-groups/resource-type/resource-type.module'; @NgModule({ imports: [ @@ -17,7 +18,8 @@ import { ResearchEntitiesModule } from '../entity-groups/research-entities/resea ImportExternalRoutingModule, SubmissionModule, JournalEntitiesModule.withEntryComponents(), - ResearchEntitiesModule.withEntryComponents() + ResearchEntitiesModule.withEntryComponents(), + ResourceTypeModule.withEntryComponents() ], declarations: [ ImportExternalPageComponent diff --git a/src/app/item-page/field-components/metadata-value-locale/metadata-values/metadata-values-locale.component.html b/src/app/item-page/field-components/metadata-value-locale/metadata-values/metadata-values-locale.component.html new file mode 100644 index 00000000000..9e1b7148208 --- /dev/null +++ b/src/app/item-page/field-components/metadata-value-locale/metadata-values/metadata-values-locale.component.html @@ -0,0 +1,5 @@ + + + {{mdValue.value}} + + diff --git a/src/app/item-page/field-components/metadata-value-locale/metadata-values/metadata-values-locale.component.scss b/src/app/item-page/field-components/metadata-value-locale/metadata-values/metadata-values-locale.component.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/app/item-page/field-components/metadata-value-locale/metadata-values/metadata-values-locale.component.spec.ts b/src/app/item-page/field-components/metadata-value-locale/metadata-values/metadata-values-locale.component.spec.ts new file mode 100644 index 00000000000..7e9fb66f77b --- /dev/null +++ b/src/app/item-page/field-components/metadata-value-locale/metadata-values/metadata-values-locale.component.spec.ts @@ -0,0 +1,105 @@ +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; +import { TranslateLoaderMock } from '../../../../shared/mocks/translate-loader.mock'; +import { MetadataValuesLocaleComponent } from './metadata-values-locale.component'; +import { MetadataValue } from '../../../../core/shared/metadata.models'; +import { LocaleService } from '../../../../core/locale/locale.service'; + +let comp: MetadataValuesLocaleComponent; +let fixture: ComponentFixture; + +const englishValue = 'in english'; +const frenchValue = 'en français'; +const spanishValue = 'en español'; +const defaultValue = 'by default'; + +const mockMetadata = [ + { + language: 'en', + value: englishValue + }, + { + language: 'fr', + value: frenchValue + }, + { + language: 'es', + value: spanishValue + }, + { + language: '', + value: defaultValue + }, + ] as MetadataValue[]; +const mockSeperator = '
'; +const mockLabel = 'fake.message'; + + +const localServiceStubEnglish: any = { + getCurrentLanguageCode(): string { return 'en'; }, +}; + +const localServiceStubFrench: any = { + getCurrentLanguageCode(): string { return 'fr'; }, +}; + +const localServiceStubGerman: any = { + getCurrentLanguageCode(): string { return 'de'; }, +}; + +export function setFixtureComponentWithLocalService(localeServiceStub: any, defaultLanguage: string = 'en') { + TestBed.overrideProvider(LocaleService, {useValue: localeServiceStub}); + fixture = TestBed.createComponent(MetadataValuesLocaleComponent); + comp = fixture.componentInstance; + comp.mdValues = mockMetadata; + comp.separator = mockSeperator; + comp.label = mockLabel; + comp.defaultLanguage = defaultLanguage; + fixture.detectChanges(); +} + +describe('MetadataValuesLocaleComponent', () => { + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateLoaderMock + } + })], + declarations: [MetadataValuesLocaleComponent], + providers: [{ provide: LocaleService, useValue: {} },], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(MetadataValuesLocaleComponent, { + set: {changeDetection: ChangeDetectionStrategy.Default} + }).compileComponents(); + })); + + it('should display english metadata value with english locale', () => { + setFixtureComponentWithLocalService(localServiceStubEnglish); + const innerHTML = fixture.nativeElement.innerHTML; + expect(innerHTML).toContain(englishValue); + }); + + it('should display french metadata value with french locale', () => { + setFixtureComponentWithLocalService(localServiceStubFrench); + const innerHTML = fixture.nativeElement.innerHTML; + expect(innerHTML).toContain(frenchValue); + }); + + it('should display default language metadata value if locale is not available', () => { + setFixtureComponentWithLocalService(localServiceStubGerman, 'en'); + const innerHTML = fixture.nativeElement.innerHTML; + expect(innerHTML).toContain(englishValue); + }); + + it('should display empty language metadata value if both locale and default language are not availables', () => { + setFixtureComponentWithLocalService(localServiceStubGerman, 'fi'); + const innerHTML = fixture.nativeElement.innerHTML; + expect(innerHTML).toContain(defaultValue); + }); + +}); + + diff --git a/src/app/item-page/field-components/metadata-value-locale/metadata-values/metadata-values-locale.component.ts b/src/app/item-page/field-components/metadata-value-locale/metadata-values/metadata-values-locale.component.ts new file mode 100644 index 00000000000..89cf2829b9e --- /dev/null +++ b/src/app/item-page/field-components/metadata-value-locale/metadata-values/metadata-values-locale.component.ts @@ -0,0 +1,74 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { isEmpty } from 'src/app/shared/empty.util'; +import { LocaleService } from '../../../../core/locale/locale.service'; +import { MetadataValue } from '../../../../core/shared/metadata.models'; + +/** + * This component renders the configured 'values' filtering with the user's language. + * It puts the given 'separator' between each two values. + */ +@Component({ + selector: 'ds-metadata-values-locale', + styleUrls: ['./metadata-values-locale.component.scss'], + templateUrl: './metadata-values-locale.component.html' +}) +export class MetadataValuesLocaleComponent implements OnInit { + + /** + * The metadata values to display + */ + @Input() mdValues: MetadataValue[]; + + /** + * The seperator used to split the metadata values (can contain HTML) + */ + @Input() separator: string; + + /** + * The label for this iteration of metadata values + */ + @Input() label: string; + + /** + * The filtered metadata language + */ + @Input() language: string; + + /** + * Default fallback language + */ + @Input() defaultLanguage = 'en'; + + constructor( + private localeService: LocaleService + ) { + } + + ngOnInit(): void { + this.language = this.localeService.getCurrentLanguageCode(); + } + + currentLanguageValues(): MetadataValue[] { + return this.mdValues.filter(value => value.language === this.language); + } + + defaultLanguageValues(): MetadataValue[] { + return this.mdValues.filter(value => value.language === this.defaultLanguage); + } + + emptyLanguageValues(): MetadataValue[] { + return this.mdValues.filter(val => isEmpty(val.language)); + } + + currentLanguageValueOrDefault(): MetadataValue[] { + let mdValues = this.currentLanguageValues(); + if (!mdValues.length) { + mdValues = this.defaultLanguageValues(); + } + if (!mdValues.length) { + mdValues = this.emptyLanguageValues(); + } + return mdValues; + } + +} diff --git a/src/app/item-page/item-page.module.ts b/src/app/item-page/item-page.module.ts index cbb9f3299ea..56b5bf71c5f 100644 --- a/src/app/item-page/item-page.module.ts +++ b/src/app/item-page/item-page.module.ts @@ -5,7 +5,6 @@ import { SharedModule } from '../shared/shared.module'; import { ItemPageComponent } from './simple/item-page.component'; import { ItemPageRoutingModule } from './item-page-routing.module'; -import { MetadataUriValuesComponent } from './field-components/metadata-uri-values/metadata-uri-values.component'; import { ItemPageAuthorFieldComponent } from './simple/field-components/specific-field/author/item-page-author-field.component'; @@ -15,7 +14,6 @@ import { import { ItemPageAbstractFieldComponent } from './simple/field-components/specific-field/abstract/item-page-abstract-field.component'; -import { ItemPageUriFieldComponent } from './simple/field-components/specific-field/uri/item-page-uri-field.component'; import { ItemPageTitleFieldComponent } from './simple/field-components/specific-field/title/item-page-title-field.component'; @@ -34,6 +32,7 @@ import { import { UntypedItemComponent } from './simple/item-types/untyped-item/untyped-item.component'; import { JournalEntitiesModule } from '../entity-groups/journal-entities/journal-entities.module'; import { ResearchEntitiesModule } from '../entity-groups/research-entities/research-entities.module'; +import { ResourceTypeModule } from '../entity-groups/resource-type/resource-type.module'; import { ThemedItemPageComponent } from './simple/themed-item-page.component'; import { ThemedFullItemPageComponent } from './full/themed-full-item-page.component'; import { MediaViewerComponent } from './media-viewer/media-viewer.component'; @@ -63,11 +62,9 @@ const DECLARATIONS = [ ThemedItemPageComponent, FullItemPageComponent, ThemedFullItemPageComponent, - MetadataUriValuesComponent, ItemPageAuthorFieldComponent, ItemPageDateFieldComponent, ItemPageAbstractFieldComponent, - ItemPageUriFieldComponent, ItemPageTitleFieldComponent, ItemPageFieldComponent, CollectionsComponent, @@ -97,6 +94,7 @@ const DECLARATIONS = [ StatisticsModule.forRoot(), JournalEntitiesModule.withEntryComponents(), ResearchEntitiesModule.withEntryComponents(), + ResourceTypeModule.withEntryComponents(), NgxGalleryModule, NgbAccordionModule ], diff --git a/src/app/item-page/simple/field-components/specific-field/metadata-value-locale/item-page-field-locale.component.html b/src/app/item-page/simple/field-components/specific-field/metadata-value-locale/item-page-field-locale.component.html new file mode 100644 index 00000000000..927d6659996 --- /dev/null +++ b/src/app/item-page/simple/field-components/specific-field/metadata-value-locale/item-page-field-locale.component.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/src/app/item-page/simple/field-components/specific-field/metadata-value-locale/item-page-field-locale.component.spec.ts b/src/app/item-page/simple/field-components/specific-field/metadata-value-locale/item-page-field-locale.component.spec.ts new file mode 100644 index 00000000000..7891c7a3bc7 --- /dev/null +++ b/src/app/item-page/simple/field-components/specific-field/metadata-value-locale/item-page-field-locale.component.spec.ts @@ -0,0 +1,66 @@ +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { Item } from '../../../../../core/shared/item.model'; +import { TranslateLoaderMock } from '../../../../../shared/mocks/translate-loader.mock'; +import { ItemPageFieldLocaleComponent } from './item-page-field-locale.component'; +import { MetadataValuesLocaleComponent } from '../../../../field-components/metadata-value-locale/metadata-values/metadata-values-locale.component'; +import { MetadataMap, MetadataValue } from '../../../../../core/shared/metadata.models'; +import { createSuccessfulRemoteDataObject$ } from '../../../../../shared/remote-data.utils'; +import { createPaginatedList } from '../../../../../shared/testing/utils.test'; +import { LocaleService } from '../../../../../core/locale/locale.service'; + +let comp: ItemPageFieldLocaleComponent; +let fixture: ComponentFixture; + +const mockValue = 'test value'; +const mockField = 'dc.test'; +const mockLabel = 'test label'; +const mockFields = [mockField]; + +const localServiceStubEnglish: any = { + getCurrentLanguageCode() { return 'en'; }, +}; + +describe('ItemPageFieldLocaleComponent', () => { + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateLoaderMock + } + })], + declarations: [ItemPageFieldLocaleComponent, MetadataValuesLocaleComponent], + providers: [{ provide: LocaleService, useValue: localServiceStubEnglish },], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(ItemPageFieldLocaleComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(waitForAsync(() => { + fixture = TestBed.createComponent(ItemPageFieldLocaleComponent); + comp = fixture.componentInstance; + comp.item = mockItemWithMetadataFieldAndValue(mockField, mockValue); + comp.fields = mockFields; + comp.label = mockLabel; + fixture.detectChanges(); + })); + + it('should display the correct metadata value', () => { + expect(fixture.nativeElement.innerHTML).toContain(mockValue); + }); +}); + +export function mockItemWithMetadataFieldAndValue(field: string, value: string): Item { + const item = Object.assign(new Item(), { + bundles: createSuccessfulRemoteDataObject$(createPaginatedList([])), + metadata: new MetadataMap() + }); + item.metadata[field] = [{ + language: 'en', + value: value + }] as MetadataValue[]; + return item; +} diff --git a/src/app/item-page/simple/field-components/specific-field/metadata-value-locale/item-page-field-locale.component.ts b/src/app/item-page/simple/field-components/specific-field/metadata-value-locale/item-page-field-locale.component.ts new file mode 100644 index 00000000000..940f3a7666a --- /dev/null +++ b/src/app/item-page/simple/field-components/specific-field/metadata-value-locale/item-page-field-locale.component.ts @@ -0,0 +1,44 @@ +import { Component, Input } from '@angular/core'; +import { ItemPageFieldComponent } from '../item-page-field.component'; +import { Item } from '../../../../../core/shared/item.model'; + + +/** + * This component can be used to represent metadata on a simple item page. + * It expects one input parameter of type Item to which the metadata belongs. + * This class can be extended to print certain metadata. + */ + +@Component({ + selector: 'ds-item-page-field-locale', + templateUrl: './item-page-field-locale.component.html' +}) +export class ItemPageFieldLocaleComponent extends ItemPageFieldComponent { + + /** + * The item to display metadata for + */ + @Input() item: Item; + + /** + * Separator string between multiple values of the metadata fields defined + * @type {string} + */ + @Input() separator: string; + + /** + * Fields (schema.element.qualifier) used to render their values. + */ + @Input() fields: string[]; + + /** + * Label i18n key for the rendered metadata + */ + @Input() label: string; + + /** + * The filtered metadata language + */ + @Input() language: string; + +} diff --git a/src/app/search-page/search-page.module.ts b/src/app/search-page/search-page.module.ts index 758eca15c09..ad34f38bbae 100644 --- a/src/app/search-page/search-page.module.ts +++ b/src/app/search-page/search-page.module.ts @@ -12,6 +12,7 @@ import { SearchFilterService } from '../core/shared/search/search-filter.service import { SearchConfigurationService } from '../core/shared/search/search-configuration.service'; import { JournalEntitiesModule } from '../entity-groups/journal-entities/journal-entities.module'; import { ResearchEntitiesModule } from '../entity-groups/research-entities/research-entities.module'; +import { ResourceTypeModule } from '../entity-groups/resource-type/resource-type.module'; import { ThemedSearchPageComponent } from './themed-search-page.component'; import { SearchModule } from '../shared/search/search.module'; @@ -29,7 +30,8 @@ const components = [ CoreModule.forRoot(), StatisticsModule.forRoot(), JournalEntitiesModule.withEntryComponents(), - ResearchEntitiesModule.withEntryComponents() + ResearchEntitiesModule.withEntryComponents(), + ResourceTypeModule.withEntryComponents() ], declarations: components, providers: [ diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index f40ddd5b900..51199e7024f 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -293,6 +293,8 @@ import { BrowserOnlyPipe } from './utils/browser-only.pipe'; import { ThemedLoadingComponent } from './loading/themed-loading.component'; import { PersonPageClaimButtonComponent } from './dso-page/person-page-claim-button/person-page-claim-button.component'; import { SearchExportCsvComponent } from './search/search-export-csv/search-export-csv.component'; +import { ItemPageUriFieldComponent } from '../item-page/simple/field-components/specific-field/uri/item-page-uri-field.component'; +import { MetadataUriValuesComponent } from '../item-page/field-components/metadata-uri-values/metadata-uri-values.component'; const MODULES = [ CommonModule, @@ -538,8 +540,9 @@ const SHARED_ITEM_PAGE_COMPONENTS = [ GenericItemPageFieldComponent, MetadataRepresentationListComponent, RelatedItemsComponent, - DsoPageOrcidButtonComponent - + DsoPageOrcidButtonComponent, + ItemPageUriFieldComponent, + MetadataUriValuesComponent ]; const PROVIDERS = [ diff --git a/src/app/submission/submission.module.ts b/src/app/submission/submission.module.ts index 97ffb7ffcdb..033bc86d48c 100644 --- a/src/app/submission/submission.module.ts +++ b/src/app/submission/submission.module.ts @@ -46,6 +46,7 @@ import { import { SubmissionSectionCcLicensesComponent } from './sections/cc-license/submission-section-cc-licenses.component'; import { JournalEntitiesModule } from '../entity-groups/journal-entities/journal-entities.module'; import { ResearchEntitiesModule } from '../entity-groups/research-entities/research-entities.module'; +import { ResourceTypeModule } from '../entity-groups/resource-type/resource-type.module'; import { ThemedSubmissionEditComponent } from './edit/themed-submission-edit.component'; import { ThemedSubmissionSubmitComponent } from './submit/themed-submission-submit.component'; import { ThemedSubmissionImportExternalComponent } from './import-external/themed-submission-import-external.component'; @@ -111,6 +112,7 @@ const DECLARATIONS = [ EffectsModule.forFeature(submissionEffects), JournalEntitiesModule.withEntryComponents(), ResearchEntitiesModule.withEntryComponents(), + ResourceTypeModule.withEntryComponents(), FormModule, NgbModalModule, NgbCollapseModule, diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 7d9a7373545..299f6c3ae5a 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -2475,6 +2475,9 @@ "iiif.page.description": "Description: ", + "isResourceTypedAs": "Is Document Type of", + + "loading.bitstream": "Loading bitstream...", "loading.bitstreams": "Loading bitstreams...", @@ -3229,6 +3232,10 @@ "relationships.isFundingAgencyOf.OrgUnit": "Funder", + "relationships.isResourceTypeOf": "Document Type", + + "relationships.isResourceTypedAs": "Publications", + "repository.image.logo": "Repository logo", @@ -3352,6 +3359,36 @@ "resource-policies.table.headers.title.for.collection": "Policies for Collection", + "resourcetype.listelement.badge": "Document Type", + + "resourcetype.listelement.no-title": "No document of this type found", + + "resourcetype.page.edit": "Edit this item", + + "resourcetype.page.link.full": "Show all metadata", + + "resourcetype.page.titleprefix": "Document Type : ", + + "resourcetype.search.results.head": "Document Types search results", + + "resourcetype.search.title": "Document Type search", + + "resourcetypes.page.name": "Name", + + "resourcetypes.page.definition": "Definition", + + "resourcetypes.page.uri": "URI", + + "resourcetypes.page.preferredLabels": "Preferred Labels", + + "resourcetypes.page.alternateLabels": "Alternate Labels", + + "resourcetypes.page.narrowerConcepts": "Narrower Concepts", + + "resourcetypes.page.broaderConcepts": "Broader Concepts", + + "resourcetypes.page.relatedTerms": "Related terms", + "search.description": "", @@ -3562,6 +3599,19 @@ "search.filters.search.submit": "Submit", + "search.filters.filter.resourceType.label": "Document Type", + + "search.filters.filter.resourceType.head": "Document Type", + + "search.filters.filter.resourceType.placeholder": "Document Type", + + "search.filters.filter.resourceTypeName.label": "Document Type", + + "search.filters.filter.resourceTypeName.head": "Document Type", + + "search.filters.filter.resourceTypeName.placeholder": "Document Type", + + "search.form.search": "Search", @@ -3982,6 +4032,9 @@ "submission.sections.describe.relationship-lookup.title.isChildOrgUnitOf": "Parent Organizational Unit", + "submission.sections.describe.relationship-lookup.title.isResourceTypeOfPublication": "Document Type", + + "submission.sections.describe.relationship-lookup.search-tab.toggle-dropdown": "Toggle dropdown", "submission.sections.describe.relationship-lookup.selection-tab.settings": "Settings", diff --git a/src/assets/i18n/fr.json5 b/src/assets/i18n/fr.json5 index 5cfb7ff0995..0ca6dc85142 100644 --- a/src/assets/i18n/fr.json5 +++ b/src/assets/i18n/fr.json5 @@ -2302,11 +2302,16 @@ "info.feedback.error.message.required": "Un contenu est requis", // "info.feedback.page-label": "Page", - "info.feedback.page-label": "Page", + "info.feedback.page-label": "Page", // "info.feedback.page_help": "The page related to your feedback", "info.feedback.page_help": "La page relative à vos commentaires", + + // "isResourceTypedAs": "Is Document Type of", + "isResourceTypedAs": "Documents de ce type", + + // "item.alerts.private": "This item is private", "item.alerts.private": "Cet Item est privé", @@ -4320,6 +4325,12 @@ // "relationships.isFundingAgencyOf.OrgUnit": "Funder", "relationships.isFundingAgencyOf.OrgUnit": "Bailleur de fonds", + // "relationships.isResourceTypeOf": "Document Type", + "relationships.isResourceTypeOf": "Type de document", + + // "relationships.isResourceTypedAs": "Publications", + "relationships.isResourceTypedAs": "Documents", + // "repository.image.logo": "Repository logo", "repository.image.logo": "Logo du dépôt", @@ -4479,6 +4490,53 @@ // "resource-policies.table.headers.title.for.collection": "Policies for Collection", "resource-policies.table.headers.title.for.collection": "Règles de collection", + + // "resourcetype.listelement.badge": "Document Type", + "resourcetype.listelement.badge": "Type de document", + + // "resourcetype.listelement.no-title": "No document of this type found", + "resourcetype.listelement.no-title": "Aucun type de document trouvé", + + // "resourcetype.page.edit": "Edit this item", + "resourcetype.page.edit": "Éditer cet item", + + // "resourcetype.page.link.full": "Show all metadata", + "resourcetype.page.link.full": "Afficher toutes les métadonnées", + + // "resourcetype.page.titleprefix": "Document Type : ", + "resourcetype.page.titleprefix": "Type de document : ", + + // "resourcetype.search.results.head": "Document Types search results", + "resourcetype.search.results.head": "Résultats de recherche Type de document", + + // "resourcetype.search.title": "Document Type search", + "resourcetype.search.title": "Recherche Type de document", + + // "resourcetypes.page.name": "Name", + "resourcetypes.page.name": "Nom", + + // "resourcetypes.page.definition": "Definition", + "resourcetypes.page.definition": "Définition", + + // "resourcetypes.page.uri": "URI", + "resourcetypes.page.uri": "URI du type de document", + + // "resourcetypes.page.preferredLabels": "Preferred Labels", + "resourcetypes.page.preferredLabels": "Terme retenu", + + // "resourcetypes.page.alternateLabels": "Alternate Labels", + "resourcetypes.page.alternateLabels": "Terme alternatif", + + // "resourcetypes.page.narrowerConcepts": "Narrower Concepts", + "resourcetypes.page.narrowerConcepts": "Terme spécifique", + + // "resourcetypes.page.broaderConcepts": "Broader Concepts", + "resourcetypes.page.broaderConcepts": "Terme générique", + + // "resourcetypes.page.relatedTerms": "Related terms", + "resourcetypes.page.relatedTerms": "Terme relié", + + // "search.description": "", "search.description": "", @@ -4782,6 +4840,28 @@ // "search.filters.search.submit": "Submit", "search.filters.search.submit": "Soumettre", + + + // "search.filters.filter.resourceType.label": "Document Type", + "search.filters.filter.resourceType.label": "Type de document", + + // "search.filters.filter.resourceType.head": "Document Type", + "search.filters.filter.resourceType.head": "Type de document", + + // "search.filters.filter.resourceType.placeholder": "Document Type", + "search.filters.filter.resourceType.placeholder": "Type de document", + + // "search.filters.filter.resourceTypeName.label": "Document Type", + "search.filters.filter.resourceTypeName.label": "Type de document", + + // "search.filters.filter.resourceTypeName.head": "Document Type", + "search.filters.filter.resourceTypeName.head": "Type de document", + + // "search.filters.filter.resourceTypeName.placeholder": "Document Type", + "search.filters.filter.resourceTypeName.placeholder": "Type de document", + + + // "search.form.search": "Search", "search.form.search": "Recherche", @@ -5316,6 +5396,10 @@ // "submission.sections.describe.relationship-lookup.title.isChildOrgUnitOf": "Parent Organizational Unit", "submission.sections.describe.relationship-lookup.title.isChildOrgUnitOf": "Structure organisationnelle mère", + // "submission.sections.describe.relationship-lookup.title.isResourceTypeOfPublication": "Document Type", + "submission.sections.describe.relationship-lookup.title.isResourceTypeOfPublication": "Type de document", + + // "submission.sections.describe.relationship-lookup.search-tab.toggle-dropdown": "Toggle dropdown", "submission.sections.describe.relationship-lookup.search-tab.toggle-dropdown": "Dérouler le menu", diff --git a/src/themes/custom/lazy-theme.module.ts b/src/themes/custom/lazy-theme.module.ts index 5b9d5b7230d..e3caa70a9fa 100644 --- a/src/themes/custom/lazy-theme.module.ts +++ b/src/themes/custom/lazy-theme.module.ts @@ -14,6 +14,7 @@ import { FormsModule } from '@angular/forms'; import { HttpClientModule } from '@angular/common/http'; import { IdlePreloadModule } from 'angular-idle-preload'; import { JournalEntitiesModule } from '../../app/entity-groups/journal-entities/journal-entities.module'; +import { ResourceTypeModule } from '../../app/entity-groups/resource-type/resource-type.module'; import { MyDspaceSearchModule } from '../../app/my-dspace-page/my-dspace-search.module'; import { MenuModule } from '../../app/shared/menu/menu.module'; import { NavbarModule } from '../../app/navbar/navbar.module'; @@ -171,6 +172,7 @@ const DECLARATIONS = [ IdlePreloadModule, InfoModule, JournalEntitiesModule, + ResourceTypeModule, MenuModule, MyDspaceSearchModule, NavbarModule, diff --git a/src/themes/dspace/lazy-theme.module.ts b/src/themes/dspace/lazy-theme.module.ts index a4e8027a159..84d8de4d7dd 100644 --- a/src/themes/dspace/lazy-theme.module.ts +++ b/src/themes/dspace/lazy-theme.module.ts @@ -22,6 +22,7 @@ import { IdlePreloadModule } from 'angular-idle-preload'; import { JournalEntitiesModule } from '../../app/entity-groups/journal-entities/journal-entities.module'; +import { ResourceTypeModule } from '../../app/entity-groups/resource-type/resource-type.module'; import { MyDspaceSearchModule } from '../../app/my-dspace-page/my-dspace-search.module'; import { MenuModule } from '../../app/shared/menu/menu.module'; import { NavbarModule } from '../../app/navbar/navbar.module'; @@ -84,6 +85,7 @@ const DECLARATIONS = [ IdlePreloadModule, InfoModule, JournalEntitiesModule, + ResourceTypeModule, MenuModule, MyDspaceSearchModule, NavbarModule, From 5abc6d52e3f743547e2e4dc4b3dcdb5121add98d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Jolin-Nicol?= Date: Tue, 2 Aug 2022 14:33:25 -0400 Subject: [PATCH 2/6] Removed theme specific parameter to the listable object component --- .../resource-type/item-page/resource-type.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/entity-groups/resource-type/item-page/resource-type.component.ts b/src/app/entity-groups/resource-type/item-page/resource-type.component.ts index e9996620589..8d44e86fd3f 100644 --- a/src/app/entity-groups/resource-type/item-page/resource-type.component.ts +++ b/src/app/entity-groups/resource-type/item-page/resource-type.component.ts @@ -7,7 +7,7 @@ import { Context } from 'src/app/core/shared/context.model'; /** * Component that represents a resource type Item page */ -@listableObjectComponent('ResourceType', ViewMode.StandalonePage, Context.Any, 'ul') +@listableObjectComponent('ResourceType', ViewMode.StandalonePage) @Component({ selector: 'ds-resource-type', styleUrls: ['./resource-type.component.scss'], From fb690a6fa9cb28bb8e171e016ca301dd7e55510c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Jolin-Nicol?= Date: Tue, 2 Aug 2022 15:54:35 -0400 Subject: [PATCH 3/6] Corrected a wrong value in a unit test --- .../resource-type-search-result-list-element.component.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/entity-groups/resource-type/search-result-list-elements/resource-type-search-result-list-element.component.spec.ts b/src/app/entity-groups/resource-type/search-result-list-elements/resource-type-search-result-list-element.component.spec.ts index 63bde10a39a..39c38ad5d71 100644 --- a/src/app/entity-groups/resource-type/search-result-list-elements/resource-type-search-result-list-element.component.spec.ts +++ b/src/app/entity-groups/resource-type/search-result-list-elements/resource-type-search-result-list-element.component.spec.ts @@ -16,7 +16,7 @@ let resourceTypeSearchResultListElementComponent: ResourceTypeSearchResultListEl let fixture: ComponentFixture; const englishLabel = 'doctoral thesis'; -const frenchLabel = 'doctoral thesis'; +const frenchLabel = 'thèse de doctorat'; const spanishLabel = 'tesis doctoral'; const englishDefinition = 'A thesis reporting the research undertaken during a period of graduate study leading to a doctoral degree.'; From e2e688c9a395472a97d4ebe42189bbb06cd68c1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Jolin-Nicol?= Date: Tue, 2 Aug 2022 16:53:40 -0400 Subject: [PATCH 4/6] Display resource type on the publication as a comment --- .../item-types/publication/publication.component.html | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/app/item-page/simple/item-types/publication/publication.component.html b/src/app/item-page/simple/item-types/publication/publication.component.html index d83202ce12a..ec17c117c66 100644 --- a/src/app/item-page/simple/item-types/publication/publication.component.html +++ b/src/app/item-page/simple/item-types/publication/publication.component.html @@ -89,6 +89,12 @@

[label]="'item.page.uri'"> + +
{{"item.page.link.full" | translate}} From 6037eb861947dd5fe924d67aaf144aece1e1a9ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Jolin-Nicol?= Date: Tue, 2 Aug 2022 17:13:15 -0400 Subject: [PATCH 5/6] Lint found some problems in two unit test files --- .../metadata-translation.service.spec.ts | 14 +++++++------- ...pe-search-result-list-element.component.spec.ts | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/app/core/locale/metadata-translation/metadata-translation.service.spec.ts b/src/app/core/locale/metadata-translation/metadata-translation.service.spec.ts index 2e69e6b0f1b..cbb897f521e 100644 --- a/src/app/core/locale/metadata-translation/metadata-translation.service.spec.ts +++ b/src/app/core/locale/metadata-translation/metadata-translation.service.spec.ts @@ -50,21 +50,21 @@ describe('MetadataTranslationService', () => { describe('currentLanguageValue', () => { it('should return metadata value for the current language code', () => { - let value = testedService.currentLanguageValue(dso, dcKey) + let value = testedService.currentLanguageValue(dso, dcKey); expect(value).toEqual(frenchTitle); }); }); describe('defaultLanguageValue', () => { it('should return metadata value for the default language code', () => { - let value = testedService.defaultLanguageValue(dso, dcKey) + let value = testedService.defaultLanguageValue(dso, dcKey); expect(value).toEqual(englishTitle); }); }); describe('emptyLanguageValue', () => { it('should return metadata value without language code', () => { - let value = testedService.emptyLanguageValue(dso, dcKey) + let value = testedService.emptyLanguageValue(dso, dcKey); expect(value).toEqual(emptyLanguageTitle); }); }); @@ -72,20 +72,20 @@ describe('MetadataTranslationService', () => { describe('currentLanguageValueOrDefault', () => { it('should return english metadata value with english locale', () => { testedService = new MetadataTranslationService(localServiceStubEnglish); - let value = testedService.currentLanguageValueOrDefault(dso, dcKey) + let value = testedService.currentLanguageValueOrDefault(dso, dcKey); expect(value).toEqual(englishTitle); }); it('should return french metadata value with french locale', () => { - let value = testedService.currentLanguageValueOrDefault(dso, dcKey) + let value = testedService.currentLanguageValueOrDefault(dso, dcKey); expect(value).toEqual(frenchTitle); }); it('should display default language metadata value if locale is not available', () => { testedService = new MetadataTranslationService(localServiceStubGerman); - let value = testedService.currentLanguageValueOrDefault(dso, dcKey) + let value = testedService.currentLanguageValueOrDefault(dso, dcKey); expect(value).toEqual(englishTitle); }); it('should display empty language metadata value if both locale and default language are not availables', () => { - let value = testedService.currentLanguageValueOrDefault(dsoNoEnglishNoFrench, dcKey) + let value = testedService.currentLanguageValueOrDefault(dsoNoEnglishNoFrench, dcKey); expect(value).toEqual(emptyLanguageTitle); }); diff --git a/src/app/entity-groups/resource-type/search-result-list-elements/resource-type-search-result-list-element.component.spec.ts b/src/app/entity-groups/resource-type/search-result-list-elements/resource-type-search-result-list-element.component.spec.ts index 39c38ad5d71..e7b78522c37 100644 --- a/src/app/entity-groups/resource-type/search-result-list-elements/resource-type-search-result-list-element.component.spec.ts +++ b/src/app/entity-groups/resource-type/search-result-list-elements/resource-type-search-result-list-element.component.spec.ts @@ -53,7 +53,7 @@ const mockItem: ItemSearchResult = Object.assign( } }) }); - + describe('ResourceTypeSearchResultListElementComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ From 4bb19b070aac0dfed8c7d116b55ccbffec4c5c2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Jolin-Nicol?= Date: Wed, 3 Aug 2022 09:24:10 -0400 Subject: [PATCH 6/6] Repaired a broken unit test by providing an extra stub. --- .../item/item-search-result-grid-element.component.spec.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.spec.ts b/src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.spec.ts index 57b863a1b15..f00d550ec03 100644 --- a/src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.spec.ts +++ b/src/app/shared/object-grid/search-result-grid-element/item-search-result/item/item-search-result-grid-element.component.spec.ts @@ -6,6 +6,7 @@ import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { Store } from '@ngrx/store'; import { TranslateModule } from '@ngx-translate/core'; import { Observable, of as observableOf } from 'rxjs'; +import { LocaleService } from 'src/app/core/locale/locale.service'; import { RemoteDataBuildService } from '../../../../../core/cache/builders/remote-data-build.service'; import { ObjectCacheService } from '../../../../../core/cache/object-cache.service'; import { BitstreamDataService } from '../../../../../core/data/bitstream-data.service'; @@ -98,6 +99,10 @@ export function getEntityGridElementTestComponent(component, searchResultWithMet } }; + const localeServiceStub: any = { + getCurrentLanguageCode(): string { return 'en'; }, + }; + beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ imports: [ @@ -118,6 +123,7 @@ export function getEntityGridElementTestComponent(component, searchResultWithMet { provide: NotificationsService, useValue: {} }, { provide: DefaultChangeAnalyzer, useValue: {} }, { provide: BitstreamDataService, useValue: mockBitstreamDataService }, + { provide: LocaleService, useValue: localeServiceStub} ], schemas: [NO_ERRORS_SCHEMA] }).overrideComponent(component, {