From 846ff3748baaffcc2f2337f686da54646f8b51eb Mon Sep 17 00:00:00 2001 From: nsemets Date: Tue, 25 Nov 2025 18:18:06 +0200 Subject: [PATCH 1/4] fix(cedar): load cedar dynamically --- src/@types/cedar.d.ts | 2 + .../cedar-template-form.component.html | 36 ++++++------ .../cedar-template-form.component.spec.ts | 13 ++++- .../cedar-template-form.component.ts | 55 ++++++++++++------- 4 files changed, 67 insertions(+), 39 deletions(-) create mode 100644 src/@types/cedar.d.ts 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}`; From c837b2967ab10a499e59a6d7a7fcb24e5de9363f Mon Sep 17 00:00:00 2001 From: nsemets Date: Tue, 9 Dec 2025 11:09:54 +0200 Subject: [PATCH 2/4] fix(cedar-template): updated load of cedar libraries --- .../cedar-template-form.component.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) 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 6ce949170..e371b5f68 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 @@ -15,6 +15,7 @@ import { ElementRef, inject, input, + NgZone, output, PLATFORM_ID, signal, @@ -66,6 +67,7 @@ export class CedarTemplateFormComponent { private route = inject(ActivatedRoute); readonly environment = inject(ENVIRONMENT); private platformId = inject(PLATFORM_ID); + private ngZone = inject(NgZone); readonly cedarLoaded = signal(false); readonly recordId = signal(''); @@ -144,8 +146,13 @@ export class CedarTemplateFormComponent { } try { - await Promise.all([import('cedar-artifact-viewer'), import('cedar-embeddable-editor')]); - this.cedarLoaded.set(true); + await this.ngZone.runOutsideAngular(async () => { + await Promise.all([import('cedar-artifact-viewer'), import('cedar-embeddable-editor')]); + }); + + this.ngZone.run(() => { + this.cedarLoaded.set(true); + }); } catch { this.cedarLoaded.set(false); } From 716cca31a1d000e74e1206ce5ef5754a3658fc47 Mon Sep 17 00:00:00 2001 From: nsemets Date: Wed, 10 Dec 2025 15:39:32 +0200 Subject: [PATCH 3/4] fix(cedar-libraries): reverted changes to working state --- .../cedar-template-form.component.ts | 35 ++++--------------- src/main.ts | 2 ++ 2 files changed, 9 insertions(+), 28 deletions(-) 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 e371b5f68..4faf1a9cd 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, isPlatformBrowser } from '@angular/common'; +import { CommonModule } from '@angular/common'; import { ChangeDetectionStrategy, Component, @@ -15,9 +15,7 @@ import { ElementRef, inject, input, - NgZone, output, - PLATFORM_ID, signal, viewChild, ViewEncapsulation, @@ -26,7 +24,8 @@ import { toSignal } from '@angular/core/rxjs-interop'; import { ActivatedRoute } from '@angular/router'; import { ENVIRONMENT } from '@core/provider/environment.provider'; -import { LoadingSpinnerComponent } from '@osf/shared/components/loading-spinner/loading-spinner.component'; + +import 'cedar-artifact-viewer'; 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, LoadingSpinnerComponent], + imports: [CommonModule, Button, TranslatePipe, Tooltip, Menu], templateUrl: './cedar-template-form.component.html', styleUrl: './cedar-template-form.component.scss', schemas: [CUSTOM_ELEMENTS_SCHEMA], @@ -66,9 +65,6 @@ export class CedarTemplateFormComponent { private route = inject(ActivatedRoute); readonly environment = inject(ENVIRONMENT); - private platformId = inject(PLATFORM_ID); - private ngZone = inject(NgZone); - readonly cedarLoaded = signal(false); readonly recordId = signal(''); readonly downloadUrl = signal(''); @@ -95,7 +91,7 @@ export class CedarTemplateFormComponent { effect(() => { const tpl = this.template(); if (tpl?.attributes?.template) { - this.loadCedarLibraries().then(() => this.initializeCedar()); + this.initializeCedar(); } }); @@ -103,7 +99,7 @@ export class CedarTemplateFormComponent { const record = this.existingRecord(); this.schemaName.set(record?.embeds?.template.data.attributes.schema_name || ''); if (record) { - this.loadCedarLibraries().then(() => this.initializeCedar()); + this.initializeCedar(); } }); } @@ -140,24 +136,6 @@ export class CedarTemplateFormComponent { } } - private async loadCedarLibraries(): Promise { - if (!isPlatformBrowser(this.platformId) || this.cedarLoaded()) { - return; - } - - try { - await this.ngZone.runOutsideAngular(async () => { - await Promise.all([import('cedar-artifact-viewer'), import('cedar-embeddable-editor')]); - }); - - this.ngZone.run(() => { - this.cedarLoaded.set(true); - }); - } catch { - this.cedarLoaded.set(false); - } - } - downloadMetadadaRecord() { if (this.fileGuid()) { window.open(`${this.environment.webUrl}/metadata/${this.fileGuid()}`)?.focus(); @@ -206,6 +184,7 @@ export class CedarTemplateFormComponent { this.emitData.emit(finalData as CedarRecordDataBinding); } } + handleEmailShare(): void { const url = window.location.href; window.location.href = `mailto:?subject=${this.schemaName()}&body=${url}`; diff --git a/src/main.ts b/src/main.ts index e005d74a2..1285e5af0 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,6 +3,8 @@ import { bootstrapApplication } from '@angular/platform-browser'; import { AppComponent } from '@osf/app.component'; import { appConfig } from '@osf/app.config'; +import 'cedar-embeddable-editor'; + bootstrapApplication(AppComponent, { providers: [...appConfig.providers], }).catch((err) => From c9944b66571afbcd521daa73207d8b1795159858 Mon Sep 17 00:00:00 2001 From: nsemets Date: Wed, 10 Dec 2025 15:52:03 +0200 Subject: [PATCH 4/4] fix(cedar): removed unused code --- .../cedar-template-form.component.html | 36 +++++++++---------- 1 file changed, 16 insertions(+), 20 deletions(-) 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 3e6baa3d8..396b9a96e 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,27 +54,23 @@

{{ 'project.metadata.addMetadata.notPublishedTe }
- @if (!cedarLoaded()) { - + @if (readonly()) { + } @else { - @if (readonly()) { - - } @else { - - } + }