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}`;