Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,30 @@ <h3 class="mb-2">{{ 'project.overview.metadata.collection' | translate }}</h3>
</p-accordion-header>

<p-accordion-content>
@let attributes = getSubmissionAttributes(submission);
@let templateId = submission.requiredMetadataTemplateId ?? '';
@let cedarRecord = cedarRecordByTemplateId().get(templateId) ?? null;
@let cedarTemplate = cedarTemplateById().get(templateId) ?? null;

@if (attributes.length) {
<div class="flex flex-column gap-2 mt-2">
@for (attribute of attributes; track attribute.key) {
<p class="font-normal">
<span class="font-bold">{{ attribute.label }}:</span> {{ attribute.value }}
</p>
}
@if (isCedarMode() && cedarRecord && cedarTemplate?.attributes?.template) {
<div class="mt-2">
<cedar-artifact-viewer
[config]="cedarViewerConfig"
[templateObject]="cedarTemplate!.attributes.template"
[instanceObject]="cedarRecord.attributes.metadata"
></cedar-artifact-viewer>
</div>
} @else {
@let attributes = getSubmissionAttributes(submission);

@if (attributes.length) {
<div class="flex flex-column gap-2 mt-2">
@for (attribute of attributes; track attribute.key) {
<p class="font-normal">
<span class="font-bold">{{ attribute.label }}:</span> {{ attribute.value }}
</p>
}
</div>
}
}
</p-accordion-content>
</p-accordion-panel>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { provideRouter } from '@angular/router';

import { collectionFilterNames } from '@osf/features/collections/constants';
import { CedarMetadataDataTemplateJsonApi, CedarMetadataRecordData } from '@osf/features/metadata/models';
import { CollectionSubmission } from '@osf/shared/models/collections/collections.model';

import { CEDAR_METADATA_DATA_TEMPLATE_JSON_API_MOCK } from '@testing/mocks/cedar-metadata-data-template-json-api.mock';
import { MOCK_CEDAR_METADATA_RECORD_DATA } from '@testing/mocks/cedar-metadata-record.mock';
import {
MOCK_COLLECTION_SUBMISSION_EMPTY_FILTERS,
MOCK_COLLECTION_SUBMISSION_SINGLE_FILTER,
Expand Down Expand Up @@ -92,4 +95,74 @@ describe('OverviewCollectionsComponent', () => {
expect(statusAttr?.value).toBe('1');
expect(typeof statusAttr?.value).toBe('string');
});

it('should render cedar-artifact-viewer when isCedarMode is true with matching record and template', async () => {
const cedarSubmission: CollectionSubmission = {
...MOCK_COLLECTION_SUBMISSION_EMPTY_FILTERS,
requiredMetadataTemplateId: 'template-1',
};
const cedarRecord: CedarMetadataRecordData = MOCK_CEDAR_METADATA_RECORD_DATA;
const cedarTemplate: CedarMetadataDataTemplateJsonApi =
CEDAR_METADATA_DATA_TEMPLATE_JSON_API_MOCK as CedarMetadataDataTemplateJsonApi;

fixture.componentRef.setInput('projectSubmissions', [cedarSubmission]);
fixture.componentRef.setInput('isCedarMode', true);
fixture.componentRef.setInput('cedarRecords', [cedarRecord]);
fixture.componentRef.setInput('cedarTemplates', [cedarTemplate]);
fixture.detectChanges();
await fixture.whenStable();

const viewer = fixture.nativeElement.querySelector('cedar-artifact-viewer');
expect(viewer).toBeTruthy();
});

it('should not render cedar-artifact-viewer when isCedarMode is false', async () => {
const cedarSubmission: CollectionSubmission = {
...MOCK_COLLECTION_SUBMISSION_WITH_FILTERS,
requiredMetadataTemplateId: 'template-1',
};
const cedarRecord: CedarMetadataRecordData = MOCK_CEDAR_METADATA_RECORD_DATA;
const cedarTemplate: CedarMetadataDataTemplateJsonApi =
CEDAR_METADATA_DATA_TEMPLATE_JSON_API_MOCK as CedarMetadataDataTemplateJsonApi;

fixture.componentRef.setInput('projectSubmissions', [cedarSubmission]);
fixture.componentRef.setInput('isCedarMode', false);
fixture.componentRef.setInput('cedarRecords', [cedarRecord]);
fixture.componentRef.setInput('cedarTemplates', [cedarTemplate]);
fixture.detectChanges();
await fixture.whenStable();

const viewer = fixture.nativeElement.querySelector('cedar-artifact-viewer');
expect(viewer).toBeNull();
});

it('should show traditional attributes when isCedarMode is true but no matching record', async () => {
const cedarSubmission: CollectionSubmission = {
...MOCK_COLLECTION_SUBMISSION_WITH_FILTERS,
requiredMetadataTemplateId: 'non-existent-template',
};

fixture.componentRef.setInput('projectSubmissions', [cedarSubmission]);
fixture.componentRef.setInput('isCedarMode', true);
fixture.componentRef.setInput('cedarRecords', []);
fixture.componentRef.setInput('cedarTemplates', []);
fixture.detectChanges();
await fixture.whenStable();

const viewer = fixture.nativeElement.querySelector('cedar-artifact-viewer');
expect(viewer).toBeNull();
expect(component.getSubmissionAttributes(cedarSubmission).length).toBeGreaterThan(0);
});

it('should compute empty cedarRecordByTemplateId map when cedarRecords is null', () => {
fixture.componentRef.setInput('cedarRecords', null);
fixture.detectChanges();
expect(component.cedarRecordByTemplateId().size).toBe(0);
});

it('should compute empty cedarTemplateById map when cedarTemplates is null', () => {
fixture.componentRef.setInput('cedarTemplates', null);
fixture.detectChanges();
expect(component.cedarTemplateById().size).toBe(0);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,19 @@ import { Button } from 'primeng/button';
import { Skeleton } from 'primeng/skeleton';
import { Tag } from 'primeng/tag';

import { ChangeDetectionStrategy, Component, input } from '@angular/core';
import {
ChangeDetectionStrategy,
Component,
computed,
CUSTOM_ELEMENTS_SCHEMA,
input,
ViewEncapsulation,
} from '@angular/core';
import { RouterLink } from '@angular/router';

import { collectionFilterNames } from '@osf/features/collections/constants';
import { CEDAR_VIEWER_CONFIG } from '@osf/features/metadata/constants';
import { CedarMetadataDataTemplateJsonApi, CedarMetadataRecordData } from '@osf/features/metadata/models';
import { StopPropagationDirective } from '@osf/shared/directives/stop-propagation.directive';
import { CollectionSubmission } from '@osf/shared/models/collections/collections.model';
import { KeyValueModel } from '@osf/shared/models/common/key-value.model';
Expand All @@ -32,10 +41,32 @@ import { CollectionStatusSeverityPipe } from '@osf/shared/pipes/collection-statu
templateUrl: './overview-collections.component.html',
styleUrl: './overview-collections.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
schemas: [CUSTOM_ELEMENTS_SCHEMA],
encapsulation: ViewEncapsulation.None,
})
export class OverviewCollectionsComponent {
projectSubmissions = input<CollectionSubmission[] | null>(null);
isProjectSubmissionsLoading = input<boolean>(false);
isCedarMode = input<boolean>(false);
cedarRecords = input<CedarMetadataRecordData[] | null>(null);
cedarTemplates = input<CedarMetadataDataTemplateJsonApi[] | null>(null);

cedarViewerConfig = CEDAR_VIEWER_CONFIG;

cedarRecordByTemplateId = computed(() => {
const records = this.cedarRecords();
return new Map(
records?.flatMap((record) => {
const templateId = record.relationships?.template?.data?.id;
return templateId ? [[templateId, record] as const] : [];
}) ?? []
);
});

cedarTemplateById = computed(() => {
const templates = this.cedarTemplates();
return new Map(templates?.map((t) => [t.id, t] as const) ?? []);
});

getSubmissionAttributes(submission: CollectionSubmission): KeyValueModel[] {
const attributes: KeyValueModel[] = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ <h3>{{ 'common.labels.affiliatedInstitutions' | translate }}</h3>
<osf-overview-collections
[projectSubmissions]="projectSubmissions()"
[isProjectSubmissionsLoading]="isProjectSubmissionsLoading()"
[isCedarMode]="isCedarMode()"
[cedarRecords]="cedarRecords()"
[cedarTemplates]="cedarTemplates()"
></osf-overview-collections>

<div class="flex flex-column gap-2">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { Mock } from 'vitest';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Router } from '@angular/router';

import { UserSelectors } from '@core/store/user';
import { GetCedarMetadataRecords, GetCedarMetadataTemplates, MetadataSelectors } from '@osf/features/metadata/store';
import { AffiliatedInstitutionsViewComponent } from '@osf/shared/components/affiliated-institutions-view/affiliated-institutions-view.component';
import { ContributorsListComponent } from '@osf/shared/components/contributors-list/contributors-list.component';
import { ResourceCitationsComponent } from '@osf/shared/components/resource-citations/resource-citations.component';
Expand Down Expand Up @@ -94,6 +96,9 @@ describe('ProjectOverviewMetadataComponent', () => {
{ selector: ContributorsSelectors.hasMoreBibliographicContributors, value: false },
{ selector: CollectionsSelectors.getCurrentProjectSubmissions, value: [] },
{ selector: CollectionsSelectors.getCurrentProjectSubmissionsLoading, value: false },
{ selector: UserSelectors.getActiveFlags, value: [] },
{ selector: MetadataSelectors.getCedarRecords, value: [] },
{ selector: MetadataSelectors.getCedarTemplates, value: null },
],
}),
],
Expand Down Expand Up @@ -122,6 +127,8 @@ describe('ProjectOverviewMetadataComponent', () => {
expect(dispatchMock).toHaveBeenCalledWith(new FetchSelectedSubjects('project-1', ResourceType.Project));
expect(dispatchMock).toHaveBeenCalledWith(new GetProjectSubmissions('project-1'));
expect(dispatchMock).toHaveBeenCalledWith(new GetProjectLicense(MOCK_PROJECT_OVERVIEW.licenseId));
expect(dispatchMock).toHaveBeenCalledWith(new GetCedarMetadataRecords('project-1', ResourceType.Project));
expect(dispatchMock).toHaveBeenCalledWith(new GetCedarMetadataTemplates());
});

it('should not dispatch init actions when project is null', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import { TranslatePipe } from '@ngx-translate/core';
import { Button } from 'primeng/button';

import { DatePipe } from '@angular/common';
import { ChangeDetectionStrategy, Component, effect, inject } from '@angular/core';
import { ChangeDetectionStrategy, Component, computed, effect, inject } from '@angular/core';
import { Router, RouterLink } from '@angular/router';

import { UserSelectors } from '@core/store/user';
import { GetCedarMetadataRecords, GetCedarMetadataTemplates, MetadataSelectors } from '@osf/features/metadata/store';
import { AffiliatedInstitutionsViewComponent } from '@osf/shared/components/affiliated-institutions-view/affiliated-institutions-view.component';
import { ContributorsListComponent } from '@osf/shared/components/contributors-list/contributors-list.component';
import { ResourceCitationsComponent } from '@osf/shared/components/resource-citations/resource-citations.component';
Expand All @@ -24,6 +26,7 @@ import {
LoadMoreBibliographicContributors,
} from '@osf/shared/stores/contributors';
import { FetchSelectedSubjects, SubjectsSelectors } from '@osf/shared/stores/subjects';
import { COLLECTION_SUBMISSION_WITH_CEDAR } from '@shared/constants/feature-flags.const';

import {
GetProjectIdentifiers,
Expand Down Expand Up @@ -79,6 +82,11 @@ export class ProjectOverviewMetadataComponent {
readonly hasMoreBibliographicContributors = select(ContributorsSelectors.hasMoreBibliographicContributors);
readonly projectSubmissions = select(CollectionsSelectors.getCurrentProjectSubmissions);
readonly isProjectSubmissionsLoading = select(CollectionsSelectors.getCurrentProjectSubmissionsLoading);
readonly activeFlags = select(UserSelectors.getActiveFlags);
readonly cedarRecords = select(MetadataSelectors.getCedarRecords);
private readonly cedarTemplatesResponse = select(MetadataSelectors.getCedarTemplates);
readonly cedarTemplates = computed(() => this.cedarTemplatesResponse()?.data ?? null);
readonly isCedarMode = computed(() => this.activeFlags().includes(COLLECTION_SUBMISSION_WITH_CEDAR));

readonly resourceType = CurrentResourceType.Projects;
readonly dateFormat = 'MMM d, y, h:mm a';
Expand All @@ -93,6 +101,8 @@ export class ProjectOverviewMetadataComponent {
getProjectSubmissions: GetProjectSubmissions,
getBibliographicContributors: GetBibliographicContributors,
loadMoreBibliographicContributors: LoadMoreBibliographicContributors,
getCedarRecords: GetCedarMetadataRecords,
getCedarTemplates: GetCedarMetadataTemplates,
});

constructor() {
Expand All @@ -107,6 +117,8 @@ export class ProjectOverviewMetadataComponent {
this.actions.getSubjects(project.id, ResourceType.Project);
this.actions.getProjectSubmissions(project.id);
this.actions.getLicense(project.licenseId);
this.actions.getCedarRecords(project.id, ResourceType.Project);
this.actions.getCedarTemplates();
}
});
}
Expand Down
Loading