diff --git a/src/@types/cedar.d.ts b/src/@types/cedar.d.ts
new file mode 100644
index 000000000..7416041cb
--- /dev/null
+++ b/src/@types/cedar.d.ts
@@ -0,0 +1,2 @@
+declare module 'cedar-artifact-viewer';
+declare module 'cedar-embeddable-editor';
diff --git a/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.html b/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.html
index 396b9a96e..3e6baa3d8 100644
--- a/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.html
+++ b/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.html
@@ -54,23 +54,27 @@
{{ 'project.metadata.addMetadata.notPublishedTe
}
- @if (readonly()) {
-
+ @if (!cedarLoaded()) {
+
} @else {
-
+ @if (readonly()) {
+
+ } @else {
+
+ }
}
diff --git a/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.spec.ts b/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.spec.ts
index 15b023b33..e6fb6f0ed 100644
--- a/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.spec.ts
+++ b/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.spec.ts
@@ -1,7 +1,11 @@
+import { MockComponent } from 'ng-mocks';
+
+import { PLATFORM_ID } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CedarMetadataHelper } from '@osf/features/metadata/helpers';
import { CedarMetadataDataTemplateJsonApi } from '@osf/features/metadata/models';
+import { LoadingSpinnerComponent } from '@osf/shared/components/loading-spinner/loading-spinner.component';
import { CedarTemplateFormComponent } from './cedar-template-form.component';
@@ -16,7 +20,8 @@ describe('CedarTemplateFormComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
- imports: [CedarTemplateFormComponent, OSFTestingModule],
+ imports: [CedarTemplateFormComponent, OSFTestingModule, MockComponent(LoadingSpinnerComponent)],
+ providers: [{ provide: PLATFORM_ID, useValue: 'browser' }],
}).compileComponents();
fixture = TestBed.createComponent(CedarTemplateFormComponent);
@@ -75,10 +80,14 @@ describe('CedarTemplateFormComponent', () => {
expect(emitSpy).toHaveBeenCalled();
});
- it('should initialize form data with empty metadata when no existing record', () => {
+ it('should initialize form data with empty metadata when no existing record', async () => {
fixture.componentRef.setInput('existingRecord', null);
fixture.detectChanges();
+ await (component as any).loadCedarLibraries();
+ (component as any).initializeCedar();
+ fixture.detectChanges();
+
const expectedEmptyMetadata = CedarMetadataHelper.buildEmptyMetadata();
expect(component.formData()).toEqual(expectedEmptyMetadata);
});
diff --git a/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.ts b/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.ts
index 16a562e69..6ce949170 100644
--- a/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.ts
+++ b/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.ts
@@ -6,7 +6,7 @@ import { Tooltip } from 'primeng/tooltip';
import { map, of } from 'rxjs';
-import { CommonModule } from '@angular/common';
+import { CommonModule, isPlatformBrowser } from '@angular/common';
import {
ChangeDetectionStrategy,
Component,
@@ -16,6 +16,7 @@ import {
inject,
input,
output,
+ PLATFORM_ID,
signal,
viewChild,
ViewEncapsulation,
@@ -24,9 +25,7 @@ import { toSignal } from '@angular/core/rxjs-interop';
import { ActivatedRoute } from '@angular/router';
import { ENVIRONMENT } from '@core/provider/environment.provider';
-
-import 'cedar-artifact-viewer';
-import 'cedar-embeddable-editor';
+import { LoadingSpinnerComponent } from '@osf/shared/components/loading-spinner/loading-spinner.component';
import { CEDAR_CONFIG, CEDAR_VIEWER_CONFIG } from '../../constants';
import { CedarMetadataHelper } from '../../helpers';
@@ -39,7 +38,7 @@ import {
@Component({
selector: 'osf-cedar-template-form',
- imports: [CommonModule, Button, TranslatePipe, Tooltip, Menu],
+ imports: [CommonModule, Button, TranslatePipe, Tooltip, Menu, LoadingSpinnerComponent],
templateUrl: './cedar-template-form.component.html',
styleUrl: './cedar-template-form.component.scss',
schemas: [CUSTOM_ELEMENTS_SCHEMA],
@@ -66,11 +65,15 @@ export class CedarTemplateFormComponent {
private route = inject(ActivatedRoute);
readonly environment = inject(ENVIRONMENT);
+ private platformId = inject(PLATFORM_ID);
+ readonly cedarLoaded = signal(false);
readonly recordId = signal('');
readonly downloadUrl = signal('');
readonly schemaName = signal('');
+ readonly fileGuid = toSignal(this.route.params.pipe(map((params) => params['fileGuid'])) ?? of(undefined));
+
shareItems = [
{
label: 'files.detail.actions.share.email',
@@ -90,7 +93,7 @@ export class CedarTemplateFormComponent {
effect(() => {
const tpl = this.template();
if (tpl?.attributes?.template) {
- this.initializeCedar();
+ this.loadCedarLibraries().then(() => this.initializeCedar());
}
});
@@ -98,7 +101,7 @@ export class CedarTemplateFormComponent {
const record = this.existingRecord();
this.schemaName.set(record?.embeds?.template.data.attributes.schema_name || '');
if (record) {
- this.initializeCedar();
+ this.loadCedarLibraries().then(() => this.initializeCedar());
}
});
}
@@ -123,7 +126,30 @@ export class CedarTemplateFormComponent {
this.validateCedarMetadata();
}
- readonly fileGuid = toSignal(this.route.params.pipe(map((params) => params['fileGuid'])) ?? of(undefined));
+ private initializeFormData(): void {
+ const template = this.template()?.attributes?.template;
+ if (!template) return;
+ const metadata = this.existingRecord()?.attributes?.metadata;
+ if (this.existingRecord()) {
+ const structuredMetadata = CedarMetadataHelper.buildStructuredMetadata(metadata);
+ this.formData.set(structuredMetadata);
+ } else {
+ this.formData.set(CedarMetadataHelper.buildEmptyMetadata());
+ }
+ }
+
+ private async loadCedarLibraries(): Promise {
+ if (!isPlatformBrowser(this.platformId) || this.cedarLoaded()) {
+ return;
+ }
+
+ try {
+ await Promise.all([import('cedar-artifact-viewer'), import('cedar-embeddable-editor')]);
+ this.cedarLoaded.set(true);
+ } catch {
+ this.cedarLoaded.set(false);
+ }
+ }
downloadMetadadaRecord() {
if (this.fileGuid()) {
@@ -173,19 +199,6 @@ export class CedarTemplateFormComponent {
this.emitData.emit(finalData as CedarRecordDataBinding);
}
}
-
- private initializeFormData(): void {
- const template = this.template()?.attributes?.template;
- if (!template) return;
- const metadata = this.existingRecord()?.attributes?.metadata;
- if (this.existingRecord()) {
- const structuredMetadata = CedarMetadataHelper.buildStructuredMetadata(metadata);
- this.formData.set(structuredMetadata);
- } else {
- this.formData.set(CedarMetadataHelper.buildEmptyMetadata());
- }
- }
-
handleEmailShare(): void {
const url = window.location.href;
window.location.href = `mailto:?subject=${this.schemaName()}&body=${url}`;