Skip to content

Commit 83db19d

Browse files
authored
Merge pull request #1016 from adlius/fix-overview-collection-dropdown
feat(es2): Fix overview page dropdown
2 parents 3930350 + c2b7237 commit 83db19d

4 files changed

Lines changed: 88 additions & 49 deletions

File tree

src/app/features/project/overview/components/overview-collections/overview-collections.component.html

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -33,26 +33,19 @@ <h3 class="mb-2">{{ 'project.overview.metadata.collection' | translate }}</h3>
3333
@let cedarRecord = cedarRecordByTemplateId().get(templateId) ?? null;
3434
@let cedarTemplate = cedarTemplateById().get(templateId) ?? null;
3535

36-
@if (isCedarMode() && cedarRecord && cedarTemplate?.attributes?.template) {
37-
<div class="mt-2">
38-
<cedar-artifact-viewer
39-
[config]="cedarViewerConfig"
40-
[templateObject]="cedarTemplate!.attributes.template"
41-
[instanceObject]="cedarRecord.attributes.metadata"
42-
></cedar-artifact-viewer>
43-
</div>
44-
} @else {
45-
@let attributes = getSubmissionAttributes(submission);
36+
@let attributes =
37+
isCedarMode() && cedarRecord && cedarTemplate?.attributes?.template
38+
? getCedarAttributes(cedarRecord, cedarTemplate!)
39+
: getSubmissionAttributes(submission);
4640

47-
@if (attributes.length) {
48-
<div class="flex flex-column gap-2 mt-2">
49-
@for (attribute of attributes; track attribute.key) {
50-
<p class="font-normal">
51-
<span class="font-bold">{{ attribute.label }}:</span> {{ attribute.value }}
52-
</p>
53-
}
54-
</div>
55-
}
41+
@if (attributes.length) {
42+
<div class="flex flex-column gap-2 mt-2">
43+
@for (attribute of attributes; track attribute.key) {
44+
<p class="font-normal">
45+
<span class="font-bold">{{ attribute.label }}:</span> {{ attribute.value }}
46+
</p>
47+
}
48+
</div>
5649
}
5750
</p-accordion-content>
5851
</p-accordion-panel>

src/app/features/project/overview/components/overview-collections/overview-collections.component.spec.ts

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
22
import { provideRouter } from '@angular/router';
33

44
import { collectionFilterNames } from '@osf/features/collections/constants';
5-
import { CedarMetadataDataTemplateJsonApi, CedarMetadataRecordData } from '@osf/features/metadata/models';
5+
import { CedarMetadataDataTemplateJsonApi } from '@osf/features/metadata/models';
66
import { CollectionSubmission } from '@osf/shared/models/collections/collections.model';
77

88
import { CEDAR_METADATA_DATA_TEMPLATE_JSON_API_MOCK } from '@testing/mocks/cedar-metadata-data-template-json-api.mock';
@@ -96,47 +96,49 @@ describe('OverviewCollectionsComponent', () => {
9696
expect(typeof statusAttr?.value).toBe('string');
9797
});
9898

99-
it('should render cedar-artifact-viewer when isCedarMode is true with matching record and template', async () => {
99+
it('should display cedar attributes as key-value pairs when isCedarMode is true with matching record and template', async () => {
100100
const cedarSubmission: CollectionSubmission = {
101101
...MOCK_COLLECTION_SUBMISSION_EMPTY_FILTERS,
102102
requiredMetadataTemplateId: 'template-1',
103103
};
104-
const cedarRecord: CedarMetadataRecordData = MOCK_CEDAR_METADATA_RECORD_DATA;
105104
const cedarTemplate: CedarMetadataDataTemplateJsonApi =
106105
CEDAR_METADATA_DATA_TEMPLATE_JSON_API_MOCK as CedarMetadataDataTemplateJsonApi;
107106

108107
fixture.componentRef.setInput('projectSubmissions', [cedarSubmission]);
109108
fixture.componentRef.setInput('isCedarMode', true);
110-
fixture.componentRef.setInput('cedarRecords', [cedarRecord]);
109+
fixture.componentRef.setInput('cedarRecords', [MOCK_CEDAR_METADATA_RECORD_DATA]);
111110
fixture.componentRef.setInput('cedarTemplates', [cedarTemplate]);
112111
fixture.detectChanges();
113112
await fixture.whenStable();
114113

115-
const viewer = fixture.nativeElement.querySelector('cedar-artifact-viewer');
116-
expect(viewer).toBeTruthy();
114+
const paragraphs = fixture.nativeElement.querySelectorAll('p.font-normal');
115+
expect(paragraphs.length).toBeGreaterThan(0);
116+
expect(fixture.nativeElement.textContent).toContain('Project Name');
117+
expect(fixture.nativeElement.textContent).toContain('Test Project Name');
117118
});
118119

119-
it('should not render cedar-artifact-viewer when isCedarMode is false', async () => {
120+
it('should display submission attributes when isCedarMode is false', async () => {
120121
const cedarSubmission: CollectionSubmission = {
121122
...MOCK_COLLECTION_SUBMISSION_WITH_FILTERS,
122123
requiredMetadataTemplateId: 'template-1',
123124
};
124-
const cedarRecord: CedarMetadataRecordData = MOCK_CEDAR_METADATA_RECORD_DATA;
125125
const cedarTemplate: CedarMetadataDataTemplateJsonApi =
126126
CEDAR_METADATA_DATA_TEMPLATE_JSON_API_MOCK as CedarMetadataDataTemplateJsonApi;
127127

128128
fixture.componentRef.setInput('projectSubmissions', [cedarSubmission]);
129129
fixture.componentRef.setInput('isCedarMode', false);
130-
fixture.componentRef.setInput('cedarRecords', [cedarRecord]);
130+
fixture.componentRef.setInput('cedarRecords', [MOCK_CEDAR_METADATA_RECORD_DATA]);
131131
fixture.componentRef.setInput('cedarTemplates', [cedarTemplate]);
132132
fixture.detectChanges();
133133
await fixture.whenStable();
134134

135-
const viewer = fixture.nativeElement.querySelector('cedar-artifact-viewer');
136-
expect(viewer).toBeNull();
135+
const attributes = component.getSubmissionAttributes(cedarSubmission);
136+
expect(attributes.length).toBeGreaterThan(0);
137+
const paragraphs = fixture.nativeElement.querySelectorAll('p.font-normal');
138+
expect(paragraphs.length).toBe(attributes.length);
137139
});
138140

139-
it('should show traditional attributes when isCedarMode is true but no matching record', async () => {
141+
it('should fall back to submission attributes when isCedarMode is true but no matching record', async () => {
140142
const cedarSubmission: CollectionSubmission = {
141143
...MOCK_COLLECTION_SUBMISSION_WITH_FILTERS,
142144
requiredMetadataTemplateId: 'non-existent-template',
@@ -149,9 +151,19 @@ describe('OverviewCollectionsComponent', () => {
149151
fixture.detectChanges();
150152
await fixture.whenStable();
151153

152-
const viewer = fixture.nativeElement.querySelector('cedar-artifact-viewer');
153-
expect(viewer).toBeNull();
154-
expect(component.getSubmissionAttributes(cedarSubmission).length).toBeGreaterThan(0);
154+
const attributes = component.getSubmissionAttributes(cedarSubmission);
155+
expect(attributes.length).toBeGreaterThan(0);
156+
const paragraphs = fixture.nativeElement.querySelectorAll('p.font-normal');
157+
expect(paragraphs.length).toBe(attributes.length);
158+
});
159+
160+
it('should extract key-value pairs from a cedar record using template field order and labels', () => {
161+
const cedarTemplate: CedarMetadataDataTemplateJsonApi =
162+
CEDAR_METADATA_DATA_TEMPLATE_JSON_API_MOCK as CedarMetadataDataTemplateJsonApi;
163+
164+
const result = component.getCedarAttributes(MOCK_CEDAR_METADATA_RECORD_DATA, cedarTemplate);
165+
166+
expect(result).toContainEqual({ key: 'Project Name', label: 'Project Name', value: 'Test Project Name' });
155167
});
156168

157169
it('should compute empty cedarRecordByTemplateId map when cedarRecords is null', () => {

src/app/features/project/overview/components/overview-collections/overview-collections.component.ts

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,10 @@ import { Button } from 'primeng/button';
55
import { Skeleton } from 'primeng/skeleton';
66
import { Tag } from 'primeng/tag';
77

8-
import {
9-
ChangeDetectionStrategy,
10-
Component,
11-
computed,
12-
CUSTOM_ELEMENTS_SCHEMA,
13-
input,
14-
ViewEncapsulation,
15-
} from '@angular/core';
8+
import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
169
import { RouterLink } from '@angular/router';
1710

1811
import { collectionFilterNames } from '@osf/features/collections/constants';
19-
import { CEDAR_VIEWER_CONFIG } from '@osf/features/metadata/constants';
2012
import { CedarMetadataDataTemplateJsonApi, CedarMetadataRecordData } from '@osf/features/metadata/models';
2113
import { StopPropagationDirective } from '@osf/shared/directives/stop-propagation.directive';
2214
import { CollectionSubmission } from '@osf/shared/models/collections/collections.model';
@@ -41,8 +33,6 @@ import { CollectionStatusSeverityPipe } from '@osf/shared/pipes/collection-statu
4133
templateUrl: './overview-collections.component.html',
4234
styleUrl: './overview-collections.component.scss',
4335
changeDetection: ChangeDetectionStrategy.OnPush,
44-
schemas: [CUSTOM_ELEMENTS_SCHEMA],
45-
encapsulation: ViewEncapsulation.None,
4636
})
4737
export class OverviewCollectionsComponent {
4838
projectSubmissions = input<CollectionSubmission[] | null>(null);
@@ -51,8 +41,6 @@ export class OverviewCollectionsComponent {
5141
cedarRecords = input<CedarMetadataRecordData[] | null>(null);
5242
cedarTemplates = input<CedarMetadataDataTemplateJsonApi[] | null>(null);
5343

54-
cedarViewerConfig = CEDAR_VIEWER_CONFIG;
55-
5644
cedarRecordByTemplateId = computed(() => {
5745
const records = this.cedarRecords();
5846
return new Map(
@@ -85,4 +73,40 @@ export class OverviewCollectionsComponent {
8573

8674
return attributes;
8775
}
76+
77+
getCedarAttributes(record: CedarMetadataRecordData, template: CedarMetadataDataTemplateJsonApi): KeyValueModel[] {
78+
const { order, propertyLabels } = template.attributes.template._ui;
79+
const metadata = record.attributes.metadata as Record<string, unknown>;
80+
const attributes: KeyValueModel[] = [];
81+
82+
for (const key of order) {
83+
const label = propertyLabels[key];
84+
const value = this.formatCedarValue(metadata[key]);
85+
if (label && value) {
86+
attributes.push({ key, label, value });
87+
}
88+
}
89+
90+
return attributes;
91+
}
92+
93+
private formatCedarValue(value: unknown): string {
94+
if (value == null) return '';
95+
96+
if (Array.isArray(value)) {
97+
return value
98+
.map((item) => this.formatCedarValue(item))
99+
.filter(Boolean)
100+
.join(', ');
101+
}
102+
103+
if (typeof value === 'object') {
104+
const obj = value as Record<string, unknown>;
105+
if ('@value' in obj && obj['@value'] != null) return String(obj['@value']);
106+
if ('rdfs:label' in obj && obj['rdfs:label'] != null) return String(obj['rdfs:label']);
107+
return '';
108+
}
109+
110+
return String(value);
111+
}
88112
}

src/app/shared/stores/collections/collections.state.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Action, State, StateContext } from '@ngxs/store';
22

3-
import { catchError, forkJoin, of, switchMap, tap } from 'rxjs';
3+
import { catchError, forkJoin, map, of, switchMap, tap } from 'rxjs';
44

55
import { inject, Injectable } from '@angular/core';
66
import { Router } from '@angular/router';
@@ -116,7 +116,17 @@ export class CollectionsState {
116116
}
117117

118118
const submissionRequests = collections.map((collection) =>
119-
this.collectionsService.fetchCurrentSubmission(action.projectId, collection.id)
119+
this.collectionsService.fetchCurrentSubmission(action.projectId, collection.id).pipe(
120+
switchMap((submission) =>
121+
this.collectionsService.getCollectionProvider(submission.collectionId).pipe(
122+
map((provider) => ({
123+
...submission,
124+
requiredMetadataTemplateId: provider.requiredMetadataTemplate?.id ?? null,
125+
})),
126+
catchError(() => of(submission))
127+
)
128+
)
129+
)
120130
);
121131

122132
return forkJoin(submissionRequests);

0 commit comments

Comments
 (0)