diff --git a/config/config.example.yml b/config/config.example.yml index f1e6be76aa..a62cce13eb 100644 --- a/config/config.example.yml +++ b/config/config.example.yml @@ -214,6 +214,9 @@ languages: - code: tr label: Türkçe active: true + - code: vi + label: Tiếng Việt + active: true - code: kk label: Қазақ active: true diff --git a/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.spec.ts b/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.spec.ts index e478aa3ef3..ee3de42131 100644 --- a/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.spec.ts +++ b/src/app/admin/admin-search-page/admin-search-results/admin-search-result-grid-element/item-search-result/item-admin-search-result-grid-element.component.spec.ts @@ -19,7 +19,7 @@ import { createSuccessfulRemoteDataObject$ } from '../../../../../shared/remote- import { getMockThemeService } from '../../../../../shared/mocks/theme-service.mock'; import { ThemeService } from '../../../../../shared/theme-support/theme.service'; import { AccessStatusDataService } from '../../../../../core/data/access-status-data.service'; -import { AccessStatusObject } from '../../../../../shared/object-list/access-status-badge/access-status.model'; +import { AccessStatusObject } from '../../../../../shared/object-collection/shared/badges/access-status-badge/access-status.model'; import { AuthService } from '../../../../../core/auth/auth.service'; import { AuthServiceStub } from '../../../../../shared/testing/auth-service.stub'; import { FileService } from '../../../../../core/shared/file.service'; diff --git a/src/app/admin/admin-search-page/admin-search-results/admin-search-result-list-element/item-search-result/item-admin-search-result-list-element.component.html b/src/app/admin/admin-search-page/admin-search-results/admin-search-result-list-element/item-search-result/item-admin-search-result-list-element.component.html index 259512552c..991508335f 100644 --- a/src/app/admin/admin-search-page/admin-search-results/admin-search-result-list-element/item-search-result/item-admin-search-result-list-element.component.html +++ b/src/app/admin/admin-search-page/admin-search-results/admin-search-result-list-element/item-search-result/item-admin-search-result-list-element.component.html @@ -2,6 +2,5 @@ [viewMode]="viewModes.ListElement" [index]="index" [linkType]="linkType" - [listID]="listID" - [hideBadges]="true"> + [listID]="listID"> diff --git a/src/app/community-list-page/community-list/community-list.component.html b/src/app/community-list-page/community-list/community-list.component.html index ea772bb891..ddda4df9fa 100644 --- a/src/app/community-list-page/community-list/community-list.component.html +++ b/src/app/community-list-page/community-list/community-list.component.html @@ -8,10 +8,10 @@
- {{ 'communityList.showMore' | translate }} - +
diff --git a/src/app/community-list-page/community-list/community-list.component.spec.ts b/src/app/community-list-page/community-list/community-list.component.spec.ts index 575edf14e8..2120df62fa 100644 --- a/src/app/community-list-page/community-list/community-list.component.spec.ts +++ b/src/app/community-list-page/community-list/community-list.component.spec.ts @@ -16,6 +16,7 @@ import { of as observableOf } from 'rxjs'; import { By } from '@angular/platform-browser'; import { isEmpty, isNotEmpty } from '../../shared/empty.util'; import { FlatNode } from '../flat-node.model'; +import { RouterLinkWithHref } from '@angular/router'; describe('CommunityListComponent', () => { let component: CommunityListComponent; @@ -194,7 +195,7 @@ describe('CommunityListComponent', () => { }), CdkTreeModule, RouterTestingModule], - declarations: [CommunityListComponent], + declarations: [CommunityListComponent, RouterLinkWithHref], providers: [CommunityListComponent, { provide: CommunityListService, useValue: communityListServiceStub },], schemas: [CUSTOM_ELEMENTS_SCHEMA], @@ -230,9 +231,14 @@ describe('CommunityListComponent', () => { expect(showMoreEl).toBeTruthy(); }); + it('should not render the show more button as an empty link', () => { + const debugElements = fixture.debugElement.queryAll(By.directive(RouterLinkWithHref)); + expect(debugElements).toBeTruthy(); + }); + describe('when show more of top communities is clicked', () => { beforeEach(fakeAsync(() => { - const showMoreLink = fixture.debugElement.query(By.css('.show-more-node a')); + const showMoreLink = fixture.debugElement.query(By.css('.show-more-node .btn-outline-primary')); showMoreLink.triggerEventHandler('click', { preventDefault: () => {/**/ } @@ -240,6 +246,7 @@ describe('CommunityListComponent', () => { tick(); fixture.detectChanges(); })); + it('tree contains maximum of currentPage (2) * (2) elementsPerPage of first top communities, or less if there are less communities (3)', () => { const expandableNodesFound = fixture.debugElement.queryAll(By.css('.expandable-node a')); const childlessNodesFound = fixture.debugElement.queryAll(By.css('.childless-node a')); diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index 319b42d58b..d35900ebe2 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -160,7 +160,7 @@ import { SubmissionAccessesModel } from './config/models/config-submission-acces import { RatingAdvancedWorkflowInfo } from './tasks/models/rating-advanced-workflow-info.model'; import { AdvancedWorkflowInfo } from './tasks/models/advanced-workflow-info.model'; import { SelectReviewerAdvancedWorkflowInfo } from './tasks/models/select-reviewer-advanced-workflow-info.model'; -import { AccessStatusObject } from '../shared/object-list/access-status-badge/access-status.model'; +import { AccessStatusObject } from '../shared/object-collection/shared/badges/access-status-badge/access-status.model'; import { AccessStatusDataService } from './data/access-status-data.service'; import { LinkHeadService } from './services/link-head.service'; import { ResearcherProfileDataService } from './profile/researcher-profile-data.service'; diff --git a/src/app/core/data/access-status-data.service.ts b/src/app/core/data/access-status-data.service.ts index 2f641456fa..e8b77245e8 100644 --- a/src/app/core/data/access-status-data.service.ts +++ b/src/app/core/data/access-status-data.service.ts @@ -3,8 +3,8 @@ import { RemoteDataBuildService } from '../cache/builders/remote-data-build.serv import { ObjectCacheService } from '../cache/object-cache.service'; import { HALEndpointService } from '../shared/hal-endpoint.service'; import { RequestService } from './request.service'; -import { AccessStatusObject } from 'src/app/shared/object-list/access-status-badge/access-status.model'; -import { ACCESS_STATUS } from 'src/app/shared/object-list/access-status-badge/access-status.resource-type'; +import { AccessStatusObject } from 'src/app/shared/object-collection/shared/badges/access-status-badge/access-status.model'; +import { ACCESS_STATUS } from 'src/app/shared/object-collection/shared/badges/access-status-badge/access-status.resource-type'; import { Observable } from 'rxjs'; import { RemoteData } from './remote-data'; import { Item } from '../shared/item.model'; diff --git a/src/app/core/data/base/base-data.service.spec.ts b/src/app/core/data/base/base-data.service.spec.ts index 17532f477a..098f075c10 100644 --- a/src/app/core/data/base/base-data.service.spec.ts +++ b/src/app/core/data/base/base-data.service.spec.ts @@ -641,6 +641,62 @@ describe('BaseDataService', () => { }); }); + describe('hasCachedResponse', () => { + it('should return false when the request will be dispatched', (done) => { + const result = service.hasCachedResponse('test-href'); + + result.subscribe((hasCachedResponse) => { + expect(hasCachedResponse).toBeFalse(); + done(); + }); + }); + + it('should return true when the request will not be dispatched', (done) => { + (requestService.shouldDispatchRequest as jasmine.Spy).and.returnValue(false); + const result = service.hasCachedResponse('test-href'); + + result.subscribe((hasCachedResponse) => { + expect(hasCachedResponse).toBeTrue(); + done(); + }); + }); + }); + + describe('hasCachedErrorResponse', () => { + it('should return false when no response is cached', (done) => { + spyOn(service,'hasCachedResponse').and.returnValue(observableOf(false)); + const result = service.hasCachedErrorResponse('test-href'); + + result.subscribe((hasCachedErrorResponse) => { + expect(hasCachedErrorResponse).toBeFalse(); + done(); + }); + }); + it('should return false when no error response is cached', (done) => { + spyOn(service,'hasCachedResponse').and.returnValue(observableOf(true)); + spyOn(rdbService,'buildSingle').and.returnValue(createSuccessfulRemoteDataObject$({})); + + const result = service.hasCachedErrorResponse('test-href'); + + result.subscribe((hasCachedErrorResponse) => { + expect(hasCachedErrorResponse).toBeFalse(); + done(); + }); + }); + + it('should return true when an error response is cached', (done) => { + spyOn(service,'hasCachedResponse').and.returnValue(observableOf(true)); + spyOn(rdbService,'buildSingle').and.returnValue(createFailedRemoteDataObject$()); + + const result = service.hasCachedErrorResponse('test-href'); + + result.subscribe((hasCachedErrorResponse) => { + expect(hasCachedErrorResponse).toBeTrue(); + done(); + }); + }); + }); + describe('addDependency', () => { let addDependencySpy; diff --git a/src/app/core/data/base/base-data.service.ts b/src/app/core/data/base/base-data.service.ts index 85603580a4..edd6d9e2a4 100644 --- a/src/app/core/data/base/base-data.service.ts +++ b/src/app/core/data/base/base-data.service.ts @@ -341,6 +341,48 @@ export class BaseDataService implements HALDataServic } } + /** + * Checks for the provided href whether a response is already cached + * @param href$ The url for which to check whether there is a cached response. + * Can be a string or an Observable + */ + hasCachedResponse(href$: string | Observable): Observable { + if (isNotEmpty(href$)) { + if (typeof href$ === 'string') { + href$ = observableOf(href$); + } + return href$.pipe( + isNotEmptyOperator(), + take(1), + map((href: string) => { + const requestId = this.requestService.generateRequestId(); + const request = new GetRequest(requestId, href); + return !this.requestService.shouldDispatchRequest(request, true); + }), + ); + } + throw new Error(`Can't check whether there is a cached response for an empty href$`); + } + + /** + * Checks for the provided href whether an ERROR response is currently cached + * @param href$ The url for which to check whether there is a cached ERROR response. + * Can be a string or an Observable + */ + hasCachedErrorResponse(href$: string | Observable): Observable { + return this.hasCachedResponse(href$).pipe( + switchMap((hasCachedResponse) => { + if (hasCachedResponse) { + return this.rdbService.buildSingle(href$).pipe( + getFirstCompletedRemoteData(), + map((rd => rd.hasFailed)) + ); + } + return observableOf(false); + }) + ); + } + /** * Return the links to traverse from the root of the api to the * endpoint this DataService represents diff --git a/src/app/core/data/external-source-data.service.spec.ts b/src/app/core/data/external-source-data.service.spec.ts index cdbdbaa006..723d7f9bed 100644 --- a/src/app/core/data/external-source-data.service.spec.ts +++ b/src/app/core/data/external-source-data.service.spec.ts @@ -5,6 +5,7 @@ import { ExternalSourceEntry } from '../shared/external-source-entry.model'; import { of as observableOf } from 'rxjs'; import { GetRequest } from './request.models'; import { testSearchDataImplementation } from './base/search-data.spec'; +import { take } from 'rxjs/operators'; describe('ExternalSourceService', () => { let service: ExternalSourceDataService; @@ -64,19 +65,42 @@ describe('ExternalSourceService', () => { }); describe('getExternalSourceEntries', () => { - let result; - beforeEach(() => { - result = service.getExternalSourceEntries('test'); + describe('when no error response is cached', () => { + let result; + beforeEach(() => { + spyOn(service, 'hasCachedErrorResponse').and.returnValue(observableOf(false)); + result = service.getExternalSourceEntries('test'); + }); + + it('should send a GetRequest', () => { + result.pipe(take(1)).subscribe(); + expect(requestService.send).toHaveBeenCalledWith(jasmine.any(GetRequest), true); + }); + + it('should return the entries', () => { + result.subscribe((resultRD) => { + expect(resultRD.payload.page).toBe(entries); + }); + }); }); - it('should send a GetRequest', () => { - expect(requestService.send).toHaveBeenCalledWith(jasmine.any(GetRequest), true); - }); + describe('when an error response is cached', () => { + let result; + beforeEach(() => { + spyOn(service, 'hasCachedErrorResponse').and.returnValue(observableOf(true)); + result = service.getExternalSourceEntries('test'); + }); - it('should return the entries', () => { - result.subscribe((resultRD) => { - expect(resultRD.payload.page).toBe(entries); + it('should send a GetRequest', () => { + result.pipe(take(1)).subscribe(); + expect(requestService.send).toHaveBeenCalledWith(jasmine.any(GetRequest), false); + }); + + it('should return the entries', () => { + result.subscribe((resultRD) => { + expect(resultRD.payload.page).toBe(entries); + }); }); }); }); diff --git a/src/app/core/data/external-source-data.service.ts b/src/app/core/data/external-source-data.service.ts index c0552aeaec..02c5e4a53c 100644 --- a/src/app/core/data/external-source-data.service.ts +++ b/src/app/core/data/external-source-data.service.ts @@ -74,7 +74,12 @@ export class ExternalSourceDataService extends IdentifiableDataService { + return this.findListByHref(href$, undefined, !hasCachedErrorResponse, reRequestOnStale, ...linksToFollow as any); + }) + ) as any; } /** diff --git a/src/app/core/shared/context.model.ts b/src/app/core/shared/context.model.ts index dbe5a64552..756fe5c673 100644 --- a/src/app/core/shared/context.model.ts +++ b/src/app/core/shared/context.model.ts @@ -3,17 +3,38 @@ */ export enum Context { + /** Default context */ Any = 'undefined', + + /** General item page context */ ItemPage = 'itemPage', + + /** General search page context */ Search = 'search', + Workflow = 'workflow', Workspace = 'workspace', SupervisedItems = 'supervisedWorkspace', + + /** Administrative menu context */ AdminMenu = 'adminMenu', + EntitySearchModalWithNameVariants = 'EntitySearchModalWithNameVariants', EntitySearchModal = 'EntitySearchModal', + + /** Administrative search page context */ AdminSearch = 'adminSearch', AdminWorkflowSearch = 'adminWorkflowSearch', + SideBarSearchModal = 'sideBarSearchModal', SideBarSearchModalCurrent = 'sideBarSearchModalCurrent', + + /** The MyDSpace* Context values below are used for badge display in MyDSpace. */ + MyDSpaceArchived = 'mydspaceArchived', + MyDSpaceWorkspace = 'mydspaceWorkspace', + MyDSpaceWorkflow = 'mydspaceWorkflow', + MyDSpaceDeclined = 'mydspaceDeclined', + MyDSpaceApproved = 'mydspaceApproved', + MyDSpaceWaitingController = 'mydspaceWaitingController', + MyDSpaceValidation = 'mydspaceValidation', } diff --git a/src/app/core/shared/item.model.ts b/src/app/core/shared/item.model.ts index 3441896ed1..20fc275ee2 100644 --- a/src/app/core/shared/item.model.ts +++ b/src/app/core/shared/item.model.ts @@ -21,8 +21,8 @@ import { Version } from './version.model'; import { VERSION } from './version.resource-type'; import { BITSTREAM } from './bitstream.resource-type'; import { Bitstream } from './bitstream.model'; -import { ACCESS_STATUS } from 'src/app/shared/object-list/access-status-badge/access-status.resource-type'; -import { AccessStatusObject } from 'src/app/shared/object-list/access-status-badge/access-status.model'; +import { ACCESS_STATUS } from 'src/app/shared/object-collection/shared/badges/access-status-badge/access-status.resource-type'; +import { AccessStatusObject } from 'src/app/shared/object-collection/shared/badges/access-status-badge/access-status.model'; import { HandleObject } from './handle-object.model'; import { IDENTIFIERS } from '../../shared/object-list/identifier-data/identifier-data.resource-type'; import { IdentifierData } from '../../shared/object-list/identifier-data/identifier-data.model'; diff --git a/src/app/curation-form/curation-form.component.ts b/src/app/curation-form/curation-form.component.ts index 4b67580e77..e8fc67c970 100644 --- a/src/app/curation-form/curation-form.component.ts +++ b/src/app/curation-form/curation-form.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; import { ScriptDataService } from '../core/data/processes/script-data.service'; import { FormControl, FormGroup } from '@angular/forms'; import { getFirstCompletedRemoteData } from '../core/shared/operators'; @@ -40,7 +40,8 @@ export class CurationFormComponent implements OnInit { private notificationsService: NotificationsService, private translateService: TranslateService, private handleService: HandleService, - private router: Router + private router: Router, + private cdr: ChangeDetectorRef ) { } @@ -59,6 +60,7 @@ export class CurationFormComponent implements OnInit { .filter((value) => isNotEmpty(value) && value.includes('=')) .map((value) => value.split('=')[1].trim()); this.form.get('task').patchValue(this.tasks[0]); + this.cdr.detectChanges(); }); } diff --git a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.html b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.html index f54dde4971..525b42610b 100644 --- a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.html +++ b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.html @@ -7,7 +7,7 @@ [dsDebounce]="300" (onDebounce)="confirm.emit(false)">
diff --git a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.spec.ts b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.spec.ts index 10b3016a52..67a6f98ac0 100644 --- a/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.spec.ts +++ b/src/app/dso-shared/dso-edit-metadata/dso-edit-metadata-value/dso-edit-metadata-value.component.spec.ts @@ -68,7 +68,7 @@ describe('DsoEditMetadataValueComponent', () => { }); it('should not show a badge', () => { - expect(fixture.debugElement.query(By.css('ds-type-badge'))).toBeNull(); + expect(fixture.debugElement.query(By.css('ds-themed-type-badge'))).toBeNull(); }); describe('when no changes have been made', () => { @@ -134,7 +134,7 @@ describe('DsoEditMetadataValueComponent', () => { }); it('should show a badge', () => { - expect(fixture.debugElement.query(By.css('ds-type-badge'))).toBeTruthy(); + expect(fixture.debugElement.query(By.css('ds-themed-type-badge'))).toBeTruthy(); }); assertButton(EDIT_BTN, true, true); diff --git a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-issue/journal-issue-search-result-grid-element.component.html b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-issue/journal-issue-search-result-grid-element.component.html index c6c29c2f1a..f379efbaf1 100644 --- a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-issue/journal-issue-search-result-grid-element.component.html +++ b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-issue/journal-issue-search-result-grid-element.component.html @@ -19,7 +19,7 @@
- +

diff --git a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-volume/journal-volume-search-result-grid-element.component.html b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-volume/journal-volume-search-result-grid-element.component.html index 5849105f96..0477ef2324 100644 --- a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-volume/journal-volume-search-result-grid-element.component.html +++ b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-volume/journal-volume-search-result-grid-element.component.html @@ -19,7 +19,7 @@
- +

diff --git a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal/journal-search-result-grid-element.component.html b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal/journal-search-result-grid-element.component.html index 9a3ea95c07..d994ae8411 100644 --- a/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal/journal-search-result-grid-element.component.html +++ b/src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal/journal-search-result-grid-element.component.html @@ -19,7 +19,7 @@
- +

diff --git a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.html b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.html index 36b7e98c51..9d02a5d837 100644 --- a/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.html +++ b/src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.html @@ -12,7 +12,7 @@
- +
- +
- +

diff --git a/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/person/person-search-result-grid-element.component.html b/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/person/person-search-result-grid-element.component.html index 853a717965..a11cd384e0 100644 --- a/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/person/person-search-result-grid-element.component.html +++ b/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/person/person-search-result-grid-element.component.html @@ -19,7 +19,7 @@
- +

diff --git a/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/project/project-search-result-grid-element.component.html b/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/project/project-search-result-grid-element.component.html index a54d136de2..60c9db31f9 100644 --- a/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/project/project-search-result-grid-element.component.html +++ b/src/app/entity-groups/research-entities/item-grid-elements/search-result-grid-elements/project/project-search-result-grid-element.component.html @@ -19,7 +19,7 @@
- +

diff --git a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.html b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.html index 0bba83a209..9495577c01 100644 --- a/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.html +++ b/src/app/entity-groups/research-entities/item-list-elements/search-result-list-elements/org-unit/org-unit-search-result-list-element.component.html @@ -18,7 +18,7 @@
- + diff --git a/src/app/home-page/home-page.module.ts b/src/app/home-page/home-page.module.ts index 3418437d3c..1681abd805 100644 --- a/src/app/home-page/home-page.module.ts +++ b/src/app/home-page/home-page.module.ts @@ -12,11 +12,13 @@ import { ThemedHomePageComponent } from './themed-home-page.component'; import { RecentItemListComponent } from './recent-item-list/recent-item-list.component'; import { JournalEntitiesModule } from '../entity-groups/journal-entities/journal-entities.module'; import { ResearchEntitiesModule } from '../entity-groups/research-entities/research-entities.module'; +import { ThemedTopLevelCommunityListComponent } from './top-level-community-list/themed-top-level-community-list.component'; const DECLARATIONS = [ HomePageComponent, ThemedHomePageComponent, TopLevelCommunityListComponent, + ThemedTopLevelCommunityListComponent, ThemedHomeNewsComponent, HomeNewsComponent, RecentItemListComponent diff --git a/src/app/home-page/top-level-community-list/themed-top-level-community-list.component.ts b/src/app/home-page/top-level-community-list/themed-top-level-community-list.component.ts new file mode 100644 index 0000000000..6eb74cc0a9 --- /dev/null +++ b/src/app/home-page/top-level-community-list/themed-top-level-community-list.component.ts @@ -0,0 +1,25 @@ +import { Component } from '@angular/core'; +import { TopLevelCommunityListComponent } from './top-level-community-list.component'; +import { ThemedComponent } from '../../shared/theme-support/themed.component'; + +@Component({ + selector: 'ds-themed-top-level-community-list', + styleUrls: [], + templateUrl: '../../shared/theme-support/themed.component.html', +}) +export class ThemedTopLevelCommunityListComponent extends ThemedComponent { + protected inAndOutputNames: (keyof TopLevelCommunityListComponent & keyof this)[]; + + protected getComponentName(): string { + return 'TopLevelCommunityListComponent'; + } + + protected importThemedComponent(themeName: string): Promise { + return import(`../../../themes/${themeName}/app/home-page/top-level-community-list/top-level-community-list.component`); + } + + protected importUnthemedComponent(): Promise { + return import(`./top-level-community-list.component`); + } + +} diff --git a/src/app/item-page/edit-item-page/edit-item-page.module.ts b/src/app/item-page/edit-item-page/edit-item-page.module.ts index 24d27b3340..7692df8924 100644 --- a/src/app/item-page/edit-item-page/edit-item-page.module.ts +++ b/src/app/item-page/edit-item-page/edit-item-page.module.ts @@ -38,7 +38,7 @@ import { IdentifierDataService } from '../../core/data/identifier-data.service'; import { IdentifierDataComponent } from '../../shared/object-list/identifier-data/identifier-data.component'; import { ItemRegisterDoiComponent } from './item-register-doi/item-register-doi.component'; import { DsoSharedModule } from '../../dso-shared/dso-shared.module'; - +import { ItemCurateComponent } from './item-curate/item-curate.component'; /** * Module that contains all components related to the Edit Item page administrator functionality @@ -81,7 +81,8 @@ import { DsoSharedModule } from '../../dso-shared/dso-shared.module'; VirtualMetadataComponent, ItemAuthorizationsComponent, IdentifierDataComponent, - ItemRegisterDoiComponent + ItemRegisterDoiComponent, + ItemCurateComponent ], providers: [ BundleDataService, diff --git a/src/app/item-page/edit-item-page/edit-item-page.routing.module.ts b/src/app/item-page/edit-item-page/edit-item-page.routing.module.ts index 88172e2620..9923748338 100644 --- a/src/app/item-page/edit-item-page/edit-item-page.routing.module.ts +++ b/src/app/item-page/edit-item-page/edit-item-page.routing.module.ts @@ -41,6 +41,7 @@ import { ItemPageVersionHistoryGuard } from './item-page-version-history.guard'; import { ItemPageCollectionMapperGuard } from './item-page-collection-mapper.guard'; import { ThemedDsoEditMetadataComponent } from '../../dso-shared/dso-edit-metadata/themed-dso-edit-metadata.component'; import { ItemPageRegisterDoiGuard } from './item-page-register-doi.guard'; +import { ItemCurateComponent } from './item-curate/item-curate.component'; /** * Routing module that handles the routing for the Edit Item page administrator functionality @@ -82,6 +83,11 @@ import { ItemPageRegisterDoiGuard } from './item-page-register-doi.guard'; data: { title: 'item.edit.tabs.metadata.title', showBreadcrumbs: true }, canActivate: [ItemPageMetadataGuard] }, + { + path: 'curate', + component: ItemCurateComponent, + data: { title: 'item.edit.tabs.curate.title', showBreadcrumbs: true } + }, { path: 'relationships', component: ItemRelationshipsComponent, diff --git a/src/app/item-page/edit-item-page/item-curate/item-curate.component.html b/src/app/item-page/edit-item-page/item-curate/item-curate.component.html new file mode 100644 index 0000000000..7c7ed41bd9 --- /dev/null +++ b/src/app/item-page/edit-item-page/item-curate/item-curate.component.html @@ -0,0 +1,7 @@ +
+

{{'item.edit.curate.title' |translate:{item: (itemName$ |async)} }}

+ +
diff --git a/src/app/item-page/edit-item-page/item-curate/item-curate.component.spec.ts b/src/app/item-page/edit-item-page/item-curate/item-curate.component.spec.ts new file mode 100644 index 0000000000..c104b4400b --- /dev/null +++ b/src/app/item-page/edit-item-page/item-curate/item-curate.component.spec.ts @@ -0,0 +1,75 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { TranslateModule } from '@ngx-translate/core'; +import { CUSTOM_ELEMENTS_SCHEMA, DebugElement } from '@angular/core'; +import { ItemCurateComponent } from './item-curate.component'; +import { of as observableOf } from 'rxjs'; +import { createSuccessfulRemoteDataObject } from '../../../shared/remote-data.utils'; +import { ActivatedRoute } from '@angular/router'; +import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; +import { Item } from '../../../core/shared/item.model'; + +describe('ItemCurateComponent', () => { + let comp: ItemCurateComponent; + let fixture: ComponentFixture; + let debugEl: DebugElement; + + let routeStub; + let dsoNameService; + + const item = Object.assign(new Item(), { + handle: '123456789/1', + metadata: {'dc.title': ['Item Name']} + }); + + beforeEach(waitForAsync(() => { + routeStub = { + parent: { + data: observableOf({ + dso: createSuccessfulRemoteDataObject(item) + }) + } + }; + + dsoNameService = jasmine.createSpyObj('dsoNameService', { + getName: 'Item Name' + }); + + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot()], + declarations: [ItemCurateComponent], + providers: [ + {provide: ActivatedRoute, useValue: routeStub}, + {provide: DSONameService, useValue: dsoNameService} + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ItemCurateComponent); + comp = fixture.componentInstance; + debugEl = fixture.debugElement; + + fixture.detectChanges(); + }); + describe('init', () => { + it('should initialise the comp', () => { + expect(comp).toBeDefined(); + expect(debugEl.nativeElement.innerHTML).toContain('ds-curation-form'); + }); + + it('should contain the item information provided in the route', (done) => { + comp.dsoRD$.subscribe((value) => { + expect(value.payload.handle).toEqual('123456789/1'); + done(); + }); + }); + + it('should contain the item name', (done) => { + comp.itemName$.subscribe((value) => { + expect(value).toEqual('Item Name'); + done(); + }); + }); + }); +}); diff --git a/src/app/item-page/edit-item-page/item-curate/item-curate.component.ts b/src/app/item-page/edit-item-page/item-curate/item-curate.component.ts new file mode 100644 index 0000000000..fa1e0287fa --- /dev/null +++ b/src/app/item-page/edit-item-page/item-curate/item-curate.component.ts @@ -0,0 +1,39 @@ +import { Component, OnInit } from '@angular/core'; +import { filter, map, take } from 'rxjs/operators'; +import { RemoteData } from '../../../core/data/remote-data'; +import { Observable } from 'rxjs'; +import { ActivatedRoute } from '@angular/router'; +import { DSONameService } from '../../../core/breadcrumbs/dso-name.service'; +import { hasValue } from '../../../shared/empty.util'; +import { Item } from '../../../core/shared/item.model'; + +/** + * Component for managing a collection's curation tasks + */ +@Component({ + selector: 'ds-item-curate', + templateUrl: './item-curate.component.html', +}) +export class ItemCurateComponent implements OnInit { + dsoRD$: Observable>; + itemName$: Observable; + + constructor( + private route: ActivatedRoute, + private dsoNameService: DSONameService, + ) {} + + ngOnInit(): void { + this.dsoRD$ = this.route.parent.data.pipe( + take(1), + map((data) => data.dso), + ); + + this.itemName$ = this.dsoRD$.pipe( + filter((rd: RemoteData) => hasValue(rd)), + map((rd: RemoteData) => { + return this.dsoNameService.getName(rd.payload); + }) + ); + } +} diff --git a/src/app/item-page/simple/field-components/file-section/file-section.component.html b/src/app/item-page/simple/field-components/file-section/file-section.component.html index 8e4f4dfcb0..82d934fe7b 100644 --- a/src/app/item-page/simple/field-components/file-section/file-section.component.html +++ b/src/app/item-page/simple/field-components/file-section/file-section.component.html @@ -8,10 +8,10 @@
- {{'item.page.bitstreams.view-more' | translate}} +
- {{'item.page.bitstreams.collapse' | translate}} +
diff --git a/src/app/item-page/simple/metadata-representation-list/metadata-representation-list.component.html b/src/app/item-page/simple/metadata-representation-list/metadata-representation-list.component.html index 65660eaa34..efbe9206d1 100644 --- a/src/app/item-page/simple/metadata-representation-list/metadata-representation-list.component.html +++ b/src/app/item-page/simple/metadata-representation-list/metadata-representation-list.component.html @@ -7,12 +7,12 @@
- {{'item.page.related-items.view-more' | - translate:{ amount: (total - (objects.length * incrementBy) < incrementBy) ? total - (objects.length * incrementBy) : incrementBy } }} +
- {{'item.page.related-items.view-less' | - translate:{ amount: representations?.length } }} +
diff --git a/src/app/item-page/simple/related-items/related-items.component.html b/src/app/item-page/simple/related-items/related-items.component.html index 0d1e14941d..bee1f345fd 100644 --- a/src/app/item-page/simple/related-items/related-items.component.html +++ b/src/app/item-page/simple/related-items/related-items.component.html @@ -7,12 +7,12 @@
- {{'item.page.related-items.view-more' | - translate:{ amount: (itemsRD?.payload?.totalElements - (incrementBy * objects.length) < incrementBy) ? itemsRD?.payload?.totalElements - (incrementBy * objects.length) : incrementBy } }} +
- {{'item.page.related-items.view-less' | - translate:{ amount: itemsRD?.payload?.page?.length } }} +
diff --git a/src/app/my-dspace-page/my-dspace-search.module.ts b/src/app/my-dspace-page/my-dspace-search.module.ts index f3775214d5..71d1343a30 100644 --- a/src/app/my-dspace-page/my-dspace-search.module.ts +++ b/src/app/my-dspace-page/my-dspace-search.module.ts @@ -23,7 +23,6 @@ import { ItemDetailPreviewComponent } from '../shared/object-detail/my-dspace-re import { ItemDetailPreviewFieldComponent } from '../shared/object-detail/my-dspace-result-detail-element/item-detail-preview/item-detail-preview-field/item-detail-preview-field.component'; import { ItemListPreviewComponent } from '../shared/object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component'; import { ThemedItemListPreviewComponent } from '../shared/object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component'; -import { MyDSpaceItemStatusComponent } from '../shared/object-collection/shared/mydspace-item-status/my-dspace-item-status.component'; import { JournalEntitiesModule } from '../entity-groups/journal-entities/journal-entities.module'; import { MyDSpaceActionsModule } from '../shared/mydspace-actions/mydspace-actions.module'; import { ClaimedDeclinedTaskSearchResultListElementComponent } from '../shared/object-list/my-dspace-result-list-element/claimed-search-result/claimed-declined-task-search-result/claimed-declined-task-search-result-list-element.component'; @@ -52,7 +51,6 @@ const DECLARATIONS = [ ItemDetailPreviewFieldComponent, ItemListPreviewComponent, ThemedItemListPreviewComponent, - MyDSpaceItemStatusComponent, ]; @NgModule({ diff --git a/src/app/process-page/form/process-parameters/process-parameters.component.html b/src/app/process-page/form/process-parameters/process-parameters.component.html index d7bbce6779..a937524e88 100644 --- a/src/app/process-page/form/process-parameters/process-parameters.component.html +++ b/src/app/process-page/form/process-parameters/process-parameters.component.html @@ -1,11 +1,11 @@ -
- - +
+ +
diff --git a/src/app/process-page/form/process-parameters/process-parameters.component.spec.ts b/src/app/process-page/form/process-parameters/process-parameters.component.spec.ts index 91bc61e02f..b44479c14a 100644 --- a/src/app/process-page/form/process-parameters/process-parameters.component.spec.ts +++ b/src/app/process-page/form/process-parameters/process-parameters.component.spec.ts @@ -14,14 +14,14 @@ import { TranslateLoaderMock } from '../../../shared/mocks/translate-loader.mock describe('ProcessParametersComponent', () => { let component: ProcessParametersComponent; let fixture: ComponentFixture; - let parameterValues; - let script; + let mockParameterValues: ProcessParameter[]; + let mockScript: Script; - function init() { + function initParametersAndScriptMockValues() { const param1 = new ScriptParameter(); const param2 = new ScriptParameter(); - script = Object.assign(new Script(), { parameters: [param1, param2] }); - parameterValues = [ + mockScript = Object.assign(new Script(), { parameters: [param1, param2] }); + mockParameterValues = [ Object.assign(new ProcessParameter(), { name: '-a', value: 'bla' }), Object.assign(new ProcessParameter(), { name: '-b', value: '123' }), Object.assign(new ProcessParameter(), { name: '-c', value: 'value' }), @@ -29,7 +29,6 @@ describe('ProcessParametersComponent', () => { } beforeEach(waitForAsync(() => { - init(); TestBed.configureTestingModule({ imports: [ FormsModule, @@ -48,17 +47,34 @@ describe('ProcessParametersComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(ProcessParametersComponent); component = fixture.componentInstance; - component.script = script; - component.parameterValues = parameterValues; - fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); - it('should render a ParameterSelectComponent for each parameter value of the component', () => { - const selectComponents = fixture.debugElement.queryAll(By.directive(ParameterSelectComponent)); - expect(selectComponents.length).toBe(parameterValues.length); + describe('when parameter values and script are initialized', () => { + + beforeEach(() => { + initParametersAndScriptMockValues(); + component.parameterValues = mockParameterValues; + component.script = mockScript; + + fixture.detectChanges(); + }); + + it(`should render a ${ParameterSelectComponent.name} for each parameter value`, () => { + const selectComponents = fixture.debugElement.queryAll(By.directive(ParameterSelectComponent)); + expect(selectComponents.length).toBe(mockParameterValues.length); + }); + + it('should not render a selector box if the parameter array is empty',() => { + fixture.componentInstance.script.parameters = []; + + fixture.detectChanges(); + + const formGroupComponent = fixture.debugElement.query(By.css('[data-testID=parameters-select-container]')); + expect(formGroupComponent).toBeFalsy(); + }); }); }); diff --git a/src/app/process-page/process-page-shared.module.ts b/src/app/process-page/process-page-shared.module.ts new file mode 100644 index 0000000000..e666283e03 --- /dev/null +++ b/src/app/process-page/process-page-shared.module.ts @@ -0,0 +1,48 @@ +import { NgModule } from '@angular/core'; +import { SharedModule } from '../shared/shared.module'; +import { NewProcessComponent } from './new/new-process.component'; +import { ScriptsSelectComponent } from './form/scripts-select/scripts-select.component'; +import { ScriptHelpComponent } from './form/script-help/script-help.component'; +import { ParameterSelectComponent } from './form/process-parameters/parameter-select/parameter-select.component'; +import { ProcessParametersComponent } from './form/process-parameters/process-parameters.component'; +import { StringValueInputComponent } from './form/process-parameters/parameter-value-input/string-value-input/string-value-input.component'; +import { ParameterValueInputComponent } from './form/process-parameters/parameter-value-input/parameter-value-input.component'; +import { FileValueInputComponent } from './form/process-parameters/parameter-value-input/file-value-input/file-value-input.component'; +import { BooleanValueInputComponent } from './form/process-parameters/parameter-value-input/boolean-value-input/boolean-value-input.component'; +import { DateValueInputComponent } from './form/process-parameters/parameter-value-input/date-value-input/date-value-input.component'; +import { ProcessOverviewComponent } from './overview/process-overview.component'; +import { ProcessDetailComponent } from './detail/process-detail.component'; +import { ProcessDetailFieldComponent } from './detail/process-detail-field/process-detail-field.component'; +import { ProcessBreadcrumbsService } from './process-breadcrumbs.service'; +import { ProcessBreadcrumbResolver } from './process-breadcrumb.resolver'; +import { ProcessFormComponent } from './form/process-form.component'; + +@NgModule({ + imports: [ + SharedModule, + ], + declarations: [ + NewProcessComponent, + ScriptsSelectComponent, + ScriptHelpComponent, + ParameterSelectComponent, + ProcessParametersComponent, + StringValueInputComponent, + ParameterValueInputComponent, + FileValueInputComponent, + BooleanValueInputComponent, + DateValueInputComponent, + ProcessOverviewComponent, + ProcessDetailComponent, + ProcessDetailFieldComponent, + ProcessFormComponent + ], + providers: [ + ProcessBreadcrumbResolver, + ProcessBreadcrumbsService + ] +}) + +export class ProcessPageSharedModule { + +} diff --git a/src/app/process-page/process-page.module.ts b/src/app/process-page/process-page.module.ts index ebe03e4537..2587813998 100644 --- a/src/app/process-page/process-page.module.ts +++ b/src/app/process-page/process-page.module.ts @@ -1,47 +1,17 @@ import { NgModule } from '@angular/core'; import { SharedModule } from '../shared/shared.module'; import { ProcessPageRoutingModule } from './process-page-routing.module'; -import { NewProcessComponent } from './new/new-process.component'; -import { ScriptsSelectComponent } from './form/scripts-select/scripts-select.component'; -import { ScriptHelpComponent } from './form/script-help/script-help.component'; -import { ParameterSelectComponent } from './form/process-parameters/parameter-select/parameter-select.component'; -import { ProcessParametersComponent } from './form/process-parameters/process-parameters.component'; -import { StringValueInputComponent } from './form/process-parameters/parameter-value-input/string-value-input/string-value-input.component'; -import { ParameterValueInputComponent } from './form/process-parameters/parameter-value-input/parameter-value-input.component'; -import { FileValueInputComponent } from './form/process-parameters/parameter-value-input/file-value-input/file-value-input.component'; -import { BooleanValueInputComponent } from './form/process-parameters/parameter-value-input/boolean-value-input/boolean-value-input.component'; -import { DateValueInputComponent } from './form/process-parameters/parameter-value-input/date-value-input/date-value-input.component'; -import { ProcessOverviewComponent } from './overview/process-overview.component'; -import { ProcessDetailComponent } from './detail/process-detail.component'; -import { ProcessDetailFieldComponent } from './detail/process-detail-field/process-detail-field.component'; -import { ProcessBreadcrumbsService } from './process-breadcrumbs.service'; -import { ProcessBreadcrumbResolver } from './process-breadcrumb.resolver'; -import { ProcessFormComponent } from './form/process-form.component'; +import { ProcessPageSharedModule } from './process-page-shared.module'; @NgModule({ imports: [ ProcessPageRoutingModule, SharedModule, + ProcessPageSharedModule, ], declarations: [ - NewProcessComponent, - ScriptsSelectComponent, - ScriptHelpComponent, - ParameterSelectComponent, - ProcessParametersComponent, - StringValueInputComponent, - ParameterValueInputComponent, - FileValueInputComponent, - BooleanValueInputComponent, - DateValueInputComponent, - ProcessOverviewComponent, - ProcessDetailComponent, - ProcessDetailFieldComponent, - ProcessFormComponent ], providers: [ - ProcessBreadcrumbResolver, - ProcessBreadcrumbsService ] }) diff --git a/src/app/search-navbar/search-navbar.component.html b/src/app/search-navbar/search-navbar.component.html index e1de59ce51..2b30507f3e 100644 --- a/src/app/search-navbar/search-navbar.component.html +++ b/src/app/search-navbar/search-navbar.component.html @@ -4,9 +4,9 @@ - +
diff --git a/src/app/search-navbar/search-navbar.component.scss b/src/app/search-navbar/search-navbar.component.scss index d5f3d8d615..cf46c25d91 100644 --- a/src/app/search-navbar/search-navbar.component.scss +++ b/src/app/search-navbar/search-navbar.component.scss @@ -1,13 +1,14 @@ input[type="text"] { margin-top: calc(-0.5 * var(--bs-font-size-base)); background-color: #fff !important; + border-color: var(--ds-header-icon-color); &.collapsed { opacity: 0; } } -a.submit-icon { +.submit-icon { cursor: pointer; position: sticky; top: 0; diff --git a/src/app/shared/auth-nav-menu/auth-nav-menu.component.html b/src/app/shared/auth-nav-menu/auth-nav-menu.component.html index 94cbd4368a..05f502afa1 100644 --- a/src/app/shared/auth-nav-menu/auth-nav-menu.component.html +++ b/src/app/shared/auth-nav-menu/auth-nav-menu.component.html @@ -19,7 +19,7 @@