Merge branch 'develop' into feature/AAE-36582-forms-misalignment

This commit is contained in:
Tomasz Nastaly
2025-07-31 23:22:09 +02:00
committed by GitHub
38 changed files with 1051 additions and 1697 deletions
@@ -17,13 +17,12 @@
import { EventEmitter } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { AppConfigModule, AppConfigService, AppConfigServiceMock } from '@alfresco/adf-core';
import { AppConfigService, AppConfigServiceMock } from '@alfresco/adf-core';
import { UploadService } from './upload.service';
import { RepositoryInfo } from '@alfresco/js-api';
import { BehaviorSubject } from 'rxjs';
import { DiscoveryApiService } from '../../common/services/discovery-api.service';
import { FileModel, FileUploadStatus } from '../../common/models/file.model';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { AlfrescoApiService } from '../../services';
import { AlfrescoApiServiceMock } from '../../mock';
@@ -38,7 +37,7 @@ describe('UploadService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [AppConfigModule, HttpClientTestingModule],
imports: [],
providers: [
UploadService,
{ provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock },
+1 -2
View File
@@ -26,8 +26,7 @@ module.exports = function (config) {
included: false,
served: true,
watched: false
},
{ pattern: 'node_modules/resize-observer-polyfill/dist/ResizeObserver.global.js', included: true, watched: false }
}
],
frameworks: ['jasmine-ajax', 'jasmine', '@angular-devkit/build-angular'],
@@ -20,5 +20,4 @@ export * from './debug-app-config.service';
export * from './app-config.pipe';
export * from './app-config-storage-prefix.factory';
export * from './app-config.module';
export * from './provide-app-config';
@@ -1,7 +1,7 @@
<div
role="grid"
*ngIf="data"
class="adf-full-width adf-datatable-list"
class="adf-datatable-list"
[class.adf-sticky-header]="isStickyHeaderEnabled()"
[class.adf-datatable--empty]="(isEmpty() && !isHeaderVisible()) || loading"
[class.adf-datatable--empty--header-visible]="isEmpty() && isHeaderVisible()"
@@ -19,6 +19,7 @@ $data-table-cell-min-width-file-size: $data-table-cell-min-width-1 !default;
.adf-datatable {
overflow-y: scroll;
height: 100%;
display: block;
.adf-full-width {
width: 100%;
@@ -60,6 +61,8 @@ $data-table-cell-min-width-file-size: $data-table-cell-min-width-1 !default;
border: 1px solid var(--adf-theme-foreground-text-color-007);
box-sizing: border-box;
overflow-x: auto;
min-width: 100%;
width: fit-content;
@media screen and (-ms-high-contrast: active), screen and (-ms-high-contrast: none) {
.adf-datatable-center-size-column-ie {
@@ -671,7 +674,7 @@ $data-table-cell-min-width-file-size: $data-table-cell-min-width-1 !default;
.adf-datatable-body {
display: block;
flex: 1;
overflow-y: scroll;
overflow: hidden auto;
margin-top: -1px;
}
}
@@ -16,10 +16,11 @@
*/
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { UnitTestingUtils } from '../../../testing';
import { UnitTestingUtils, NoopTranslateModule } from '../../../testing';
import { FormFieldModel, FormModel } from '../widgets';
import { FormSectionComponent } from './form-section.component';
import { mockSectionWithFields } from '../mock/form-renderer.component.mock';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
describe('FormSectionComponent', () => {
let fixture: ComponentFixture<FormSectionComponent>;
@@ -28,7 +29,7 @@ describe('FormSectionComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [FormSectionComponent]
imports: [FormSectionComponent, NoopTranslateModule, NoopAnimationsModule]
});
fixture = TestBed.createComponent(FormSectionComponent);
testingUtils = new UnitTestingUtils(fixture.debugElement);
@@ -59,4 +60,87 @@ describe('FormSectionComponent', () => {
const sectionFields = testingUtils.getAllByCSS('.adf-grid-list-section-column-view-item adf-form-field');
expect(sectionFields.length).toBe(2);
});
describe('getSectionColumnWidth', () => {
it('should cap width at 100% when numberOfColumns is not a number', () => {
const columnField = { colspan: 2 } as FormFieldModel;
const width = component.getSectionColumnWidth('invalid' as unknown as number, [columnField]);
expect(width).toBe('100');
});
it('should cap width at 100% when numberOfColumns is null', () => {
const columnField = { colspan: 3 } as FormFieldModel;
const width = component.getSectionColumnWidth(null as unknown as number, [columnField]);
expect(width).toBe('100');
});
it('should return 100% when numberOfColumns is undefined', () => {
const columnField = { colspan: 1 } as FormFieldModel;
const width = component.getSectionColumnWidth(undefined as unknown as number, [columnField]);
expect(width).toBe('100');
});
it('should cap width at 100% when numberOfColumns is 0', () => {
const columnField = { colspan: 2 } as FormFieldModel;
const width = component.getSectionColumnWidth(0, [columnField]);
expect(width).toBe('100');
});
it('should cap width at 100% when numberOfColumns is negative', () => {
const columnField = { colspan: 3 } as FormFieldModel;
const width = component.getSectionColumnWidth(-1, [columnField]);
expect(width).toBe('100');
});
it('should return 100 when numberOfColumns is falsy and no colspan is defined', () => {
const columnField = {} as FormFieldModel;
const width = component.getSectionColumnWidth(null as unknown as number, [columnField]);
expect(width).toBe('100');
});
it('should calculate percentage width when numberOfColumns is a valid number', () => {
const numberOfColumns = 4;
const columnField = { colspan: 2 } as FormFieldModel;
const width = component.getSectionColumnWidth(numberOfColumns, [columnField]);
expect(width).toBe('50');
});
it('should cap width at 100% when colspan exceeds numberOfColumns', () => {
const numberOfColumns = 2;
const columnField = { colspan: 5 } as FormFieldModel;
const width = component.getSectionColumnWidth(numberOfColumns, [columnField]);
expect(width).toBe('100');
});
it('should use default colspan of 1 when field has no colspan and numberOfColumns is valid', () => {
const numberOfColumns = 5;
const columnField = {} as FormFieldModel;
const width = component.getSectionColumnWidth(numberOfColumns, [columnField]);
expect(width).toBe('20');
});
it('should handle empty columnFields array', () => {
const numberOfColumns = 3;
const width = component.getSectionColumnWidth(numberOfColumns, []);
expect(parseFloat(width)).toBeCloseTo(33.33);
});
it('should use first field colspan when multiple fields are provided', () => {
const numberOfColumns = 2;
const columnFields = [{ colspan: 1 } as FormFieldModel, { colspan: 3 } as FormFieldModel];
const width = component.getSectionColumnWidth(numberOfColumns, columnFields);
expect(width).toBe('50');
});
});
});
@@ -43,6 +43,10 @@ export class FormSectionComponent implements OnInit {
const defaultColspan = 1;
const fieldColspan = columnFields[firstColumnFieldIndex]?.colspan ?? defaultColspan;
return (100 / numberOfColumns) * fieldColspan + '';
if (typeof numberOfColumns !== 'number' || !numberOfColumns || numberOfColumns <= 0) {
numberOfColumns = 1;
}
return Math.min(100, (100 / numberOfColumns) * fieldColspan) + '';
}
}
@@ -5,4 +5,6 @@
color: var(--adf-readonly-text-color, var(--adf-form-label-color, var(--theme-text-color)));
line-height: normal;
word-break: break-word;
position: relative;
top: 3px;
}
+13 -1
View File
@@ -292,7 +292,19 @@
},
"SEARCH": {
"TOGGLE_ASC_DESC_ORDER": "Umschalten zwischen auf- und absteigender Reihenfolge der Ergebnisse",
"SORT_BY": "Sortieren nach"
"SORT_BY": "Sortieren nach",
"BUTTON": {
"TOOLTIP": "Search",
"ARIA-LABEL": "Search button"
},
"INPUT": {
"ARIA-LABEL": "Search input"
},
"FILTER": {
"BUTTONS": {
"CLOSE": "Close"
}
}
},
"FEATURE-FLAGS": {
"OVERRIDES": "Feature-Flags überschreibt",
+13 -1
View File
@@ -292,7 +292,19 @@
},
"SEARCH": {
"TOGGLE_ASC_DESC_ORDER": "Toggle results between ascending and descending order",
"SORT_BY": "Sort by"
"SORT_BY": "Sort by",
"BUTTON": {
"TOOLTIP": "Search",
"ARIA-LABEL": "Search button"
},
"INPUT": {
"ARIA-LABEL": "Search input"
},
"FILTER": {
"BUTTONS": {
"CLOSE": "Close"
}
}
},
"FEATURE-FLAGS": {
"OVERRIDES": "Feature flag overrides",
+13 -1
View File
@@ -292,7 +292,19 @@
},
"SEARCH": {
"TOGGLE_ASC_DESC_ORDER": "Cambiar entre orden ascendente y descendente de resultados",
"SORT_BY": "Clasificar por"
"SORT_BY": "Clasificar por",
"BUTTON": {
"TOOLTIP": "Search",
"ARIA-LABEL": "Search button"
},
"INPUT": {
"ARIA-LABEL": "Search input"
},
"FILTER": {
"BUTTONS": {
"CLOSE": "Close"
}
}
},
"FEATURE-FLAGS": {
"OVERRIDES": "Anulación de indicador de características",
+13 -1
View File
@@ -292,7 +292,19 @@
},
"SEARCH": {
"TOGGLE_ASC_DESC_ORDER": "Afficher les résultats dans l'ordre croissant ou décroissant",
"SORT_BY": "Trier par"
"SORT_BY": "Trier par",
"BUTTON": {
"TOOLTIP": "Search",
"ARIA-LABEL": "Search button"
},
"INPUT": {
"ARIA-LABEL": "Search input"
},
"FILTER": {
"BUTTONS": {
"CLOSE": "Close"
}
}
},
"FEATURE-FLAGS": {
"OVERRIDES": "Remplacements dindicateur de fonctionnalité",
+13 -1
View File
@@ -292,7 +292,19 @@
},
"SEARCH": {
"TOGGLE_ASC_DESC_ORDER": "Ordina i risultati in modo crescente o decrescente",
"SORT_BY": "Ordina per"
"SORT_BY": "Ordina per",
"BUTTON": {
"TOOLTIP": "Search",
"ARIA-LABEL": "Search button"
},
"INPUT": {
"ARIA-LABEL": "Search input"
},
"FILTER": {
"BUTTONS": {
"CLOSE": "Close"
}
}
},
"FEATURE-FLAGS": {
"OVERRIDES": "Ridefinizioni flag funzione",
+13 -1
View File
@@ -292,7 +292,19 @@
},
"SEARCH": {
"TOGGLE_ASC_DESC_ORDER": "Przełącz kolejność sortowania wyników między rosnącą a malejącą",
"SORT_BY": "Sortuj wg"
"SORT_BY": "Sortuj wg",
"BUTTON": {
"TOOLTIP": "Search",
"ARIA-LABEL": "Search button"
},
"INPUT": {
"ARIA-LABEL": "Search input"
},
"FILTER": {
"BUTTONS": {
"CLOSE": "Close"
}
}
},
"FEATURE-FLAGS": {
"OVERRIDES": "Zastąpienie flagi funkcji",
+13 -1
View File
@@ -292,7 +292,19 @@
},
"SEARCH": {
"TOGGLE_ASC_DESC_ORDER": "Alternar resultados entre ordem crescente e decrescente",
"SORT_BY": "Ordenar por:"
"SORT_BY": "Ordenar por:",
"BUTTON": {
"TOOLTIP": "Search",
"ARIA-LABEL": "Search button"
},
"INPUT": {
"ARIA-LABEL": "Search input"
},
"FILTER": {
"BUTTONS": {
"CLOSE": "Close"
}
}
},
"FEATURE-FLAGS": {
"OVERRIDES": "Substituições do sinalizador de caraterística",
@@ -7,16 +7,16 @@
id="adf-search-button"
class="adf-search-button"
[ngClass]="{'adf-search-button-inactive': subscriptAnimationState.value === 'inactive'}"
[title]="'SEARCH.BUTTON.TOOLTIP' | translate"
[title]="'CORE.SEARCH.BUTTON.TOOLTIP' | translate"
(click)="toggleSearchBar()"
(keyup.enter)="toggleSearchBar()">
<mat-icon [attr.aria-label]="'SEARCH.BUTTON.ARIA-LABEL' | translate">search</mat-icon>
<mat-icon [attr.aria-label]="'CORE.SEARCH.BUTTON.ARIA-LABEL' | translate">search</mat-icon>
</button>
<mat-form-field class="adf-input-form-field-divider" [hintLabel]="hintLabel">
<mat-label *ngIf='label'>{{label}}</mat-label>
<input matInput
#searchInput
[attr.aria-label]="'SEARCH.INPUT.ARIA-LABEL' | translate"
[attr.aria-label]="'CORE.SEARCH.INPUT.ARIA-LABEL' | translate"
[attr.type]="inputType"
[autocomplete]="getAutoComplete()"
id="adf-control-input"
@@ -34,7 +34,7 @@
matSuffix
data-automation-id="adf-clear-search-button"
class="adf-clear-search-button"
[title]="'SEARCH.FILTER.BUTTONS.CLOSE' | translate"
[title]="'CORE.SEARCH.FILTER.BUTTONS.CLOSE' | translate"
(click)="resetSearch()"
(keyup.enter)="resetSearch()">
<mat-icon>close</mat-icon>
@@ -21,6 +21,8 @@ import { DebugElement } from '@angular/core';
import { Subject } from 'rxjs';
import { UserPreferencesService } from '../common/services/user-preferences.service';
import { UnitTestingUtils } from '../testing/unit-testing-utils';
import { NoopTranslateModule } from '../testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
describe('SearchTextInputComponent', () => {
let fixture: ComponentFixture<SearchTextInputComponent>;
@@ -31,7 +33,7 @@ describe('SearchTextInputComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [SearchTextInputComponent]
imports: [NoopAnimationsModule, SearchTextInputComponent, NoopTranslateModule]
});
fixture = TestBed.createComponent(SearchTextInputComponent);
component = fixture.componentInstance;
@@ -356,4 +358,37 @@ describe('SearchTextInputComponent', () => {
});
});
});
describe('Translations', () => {
beforeEach(fakeAsync(() => {
component.expandable = true;
component.showClearButton = true;
fixture.detectChanges();
component.subscriptAnimationState.value = 'active';
fixture.detectChanges();
tick(200);
}));
it('should contain correct translation key for search button tooltip', () => {
const searchButton = testingUtils.getByCSS('#adf-search-button');
expect(searchButton.nativeElement.getAttribute('title')).toBe('CORE.SEARCH.BUTTON.TOOLTIP');
});
it('should contain correct translation key for search button aria-label', () => {
const searchButton = testingUtils.getByCSS('#adf-search-button');
// eslint-disable-next-line @alfresco/eslint-angular/no-angular-material-selectors
const searchIcon = searchButton.nativeElement.querySelector('mat-icon');
expect(searchIcon.getAttribute('aria-label')).toBe('CORE.SEARCH.BUTTON.ARIA-LABEL');
});
it('should contain correct translation key for search input aria-label', () => {
const searchInput = testingUtils.getByCSS('#adf-control-input');
expect(searchInput.nativeElement.getAttribute('aria-label')).toBe('CORE.SEARCH.INPUT.ARIA-LABEL');
});
it('should contain correct translation key for clear button title', () => {
const clearButton = testingUtils.getByDataAutomationId('adf-clear-search-button');
expect(clearButton.nativeElement.getAttribute('title')).toBe('CORE.SEARCH.FILTER.BUTTONS.CLOSE');
});
});
});
@@ -1,110 +1,118 @@
<div *ngIf="isLoading" class="adf-viewer-render-main-loader">
<div class="adf-viewer-render-layout-content adf-viewer__fullscreen-container">
<div class="adf-viewer-render-content-container">
<div class="adf-viewer-render__loading-screen">
<h2 id="loading-spinner-label">{{ 'ADF_VIEWER.LOADING' | translate }}</h2>
<div>
<mat-spinner aria-labelledby="loading-spinner-label"
class="adf-viewer-render__loading-screen__spinner" />
@if (isLoading) {
<div class="adf-viewer-render-main-loader">
<div class="adf-viewer-render-layout-content adf-viewer__fullscreen-container">
<div class="adf-viewer-render-content-container">
<div class="adf-viewer-render__loading-screen">
<h2 id="loading-spinner-label">{{ 'ADF_VIEWER.LOADING' | translate }}</h2>
<div>
<mat-spinner aria-labelledby="loading-spinner-label" class="adf-viewer-render__loading-screen__spinner" />
</div>
</div>
</div>
</div>
</div>
</div>
}
@if (urlFile || blobFile) {
<div [style.visibility]="isLoading ? 'hidden' : 'visible'" class="adf-viewer-render-main">
<div class="adf-viewer-render-layout-content adf-viewer__fullscreen-container">
<div class="adf-viewer-render-content-container" [ngSwitch]="viewerType">
<ng-container *ngSwitchCase="'external'">
<adf-preview-extension
*ngIf="!!externalViewer"
[id]="externalViewer.component"
[url]="urlFile"
[extension]="externalViewer.fileExtension"
[nodeId]="nodeId"
[attr.data-automation-id]="externalViewer.component"
(contentLoaded)="markAsLoaded()"
/>
</ng-container>
<ng-container *ngSwitchCase="'pdf'">
<adf-pdf-viewer
[thumbnailsTemplate]="thumbnailsTemplate"
[allowThumbnails]="allowThumbnails"
[blobFile]="blobFile"
[urlFile]="urlFile"
[fileName]="internalFileName"
[cacheType]="cacheTypeForContent"
(pagesLoaded)="markAsLoaded()"
(close)="onClose()"
(error)="onUnsupportedFile()"
/>
</ng-container>
<ng-container *ngSwitchCase="'image'">
<adf-img-viewer
[urlFile]="urlFile"
[readOnly]="readOnly"
[fileName]="internalFileName"
[allowedEditActions]="allowedEditActions"
[blobFile]="blobFile"
(error)="onUnsupportedFile()"
(submit)="onSubmitFile($event)"
(imageLoaded)="markAsLoaded()"
(isSaving)="isSaving.emit($event)"
/>
</ng-container>
<ng-container *ngSwitchCase="'media'">
<adf-media-player
id="adf-mdedia-player"
[urlFile]="urlFile"
[tracks]="tracks"
[mimeType]="mimeType"
[blobFile]="blobFile"
[fileName]="internalFileName"
(error)="onUnsupportedFile()"
(canPlay)="markAsLoaded()"
/>
</ng-container>
<ng-container *ngSwitchCase="'text'">
<adf-txt-viewer [urlFile]="urlFile" [blobFile]="blobFile" (contentLoaded)="markAsLoaded()" />
</ng-container>
<ng-container *ngSwitchCase="'custom'">
<ng-container *ngFor="let ext of viewerExtensions">
<adf-preview-extension
*ngIf="checkExtensions(ext.fileExtension)"
[id]="ext.component"
[url]="urlFile"
[extension]="extension"
[nodeId]="nodeId"
[attr.data-automation-id]="ext.component"
(contentLoaded)="markAsLoaded()"
/>
</ng-container>
<ng-container *ngFor="let extensionTemplate of extensionTemplates">
<span *ngIf="extensionTemplate.isVisible" class="adf-viewer-render-custom-content">
<ng-template
[ngTemplateOutlet]="extensionTemplate.template"
[ngTemplateOutletContext]="{ urlFile: urlFile, extension: extension, markAsLoaded: markAsLoaded.bind(this) }"
<div class="adf-viewer-render-content-container">
@switch (viewerType) {
@case ('external') {
@if (!!externalViewer) {
<adf-preview-extension
[id]="externalViewer.component"
[url]="urlFile"
[extension]="externalViewer.fileExtension"
[nodeId]="nodeId"
[attr.data-automation-id]="externalViewer.component"
(contentLoaded)="markAsLoaded()"
/>
</span>
</ng-container>
</ng-container>
}
}
<ng-container *ngSwitchDefault>
<adf-viewer-unknown-format [customError]="customError" />
</ng-container>
@case ('pdf') {
<adf-pdf-viewer
[thumbnailsTemplate]="thumbnailsTemplate"
[allowThumbnails]="allowThumbnails"
[blobFile]="blobFile"
[urlFile]="urlFile"
[fileName]="internalFileName"
[cacheType]="cacheTypeForContent"
(pagesLoaded)="markAsLoaded()"
(close)="onClose()"
(error)="onUnsupportedFile()"
/>
}
@case ('image') {
<adf-img-viewer
[urlFile]="urlFile"
[readOnly]="readOnly"
[fileName]="internalFileName"
[allowedEditActions]="allowedEditActions"
[blobFile]="blobFile"
(error)="onUnsupportedFile()"
(submit)="onSubmitFile($event)"
(imageLoaded)="markAsLoaded()"
(isSaving)="isSaving.emit($event)"
/>
}
@case ('media') {
<adf-media-player
id="adf-mdedia-player"
[urlFile]="urlFile"
[tracks]="tracks"
[mimeType]="mimeType"
[blobFile]="blobFile"
[fileName]="internalFileName"
(error)="onUnsupportedFile()"
(canPlay)="markAsLoaded()"
/>
}
@case ('text') {
<adf-txt-viewer [urlFile]="urlFile" [blobFile]="blobFile" (contentLoaded)="markAsLoaded()" />
}
@case ('custom') {
@for (ext of viewerExtensions; track ext.id) {
@if (checkExtensions(ext.fileExtension)) {
<adf-preview-extension
[id]="ext.component"
[url]="urlFile"
[extension]="extension"
[nodeId]="nodeId"
[attr.data-automation-id]="ext.component"
(contentLoaded)="markAsLoaded()"
/>
}
}
<ng-container *ngFor="let extensionTemplate of extensionTemplates">
@if (extensionTemplate.isVisible) {
<span class="adf-viewer-render-custom-content">
<ng-template
[ngTemplateOutlet]="extensionTemplate.template"
[ngTemplateOutletContext]="{ urlFile: urlFile, extension: extension, markAsLoaded: markAsLoaded.bind(this) }"
/>
</span>
}
</ng-container>
}
@default {
<adf-viewer-unknown-format [customError]="customError" />
}
}
</div>
</div>
</div>
}
<ng-container *ngIf="viewerTemplateExtensions">
@if (viewerTemplateExtensions) {
<ng-template [ngTemplateOutlet]="viewerTemplateExtensions"
[ngTemplateOutletContext]="{ urlFile: urlFile, extension: extension, markAsLoaded: markAsLoaded.bind(this) }"
[ngTemplateOutletInjector]="injector" />
</ng-container>
}
@@ -15,8 +15,8 @@
* limitations under the License.
*/
import { AppExtensionService, ExtensionsModule, ViewerExtensionRef } from '@alfresco/adf-extensions';
import { NgForOf, NgIf, NgSwitch, NgSwitchCase, NgSwitchDefault, NgTemplateOutlet } from '@angular/common';
import { AppExtensionService, ExtensionsModule, ViewerExtensionRef, PreviewExtensionComponent } from '@alfresco/adf-extensions';
import { NgForOf, NgTemplateOutlet } from '@angular/common';
import { Component, EventEmitter, Injector, Input, OnChanges, OnInit, Output, TemplateRef, ViewEncapsulation } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
@@ -38,9 +38,6 @@ import { UnknownFormatComponent } from '../unknown-format/unknown-format.compone
imports: [
TranslatePipe,
MatProgressSpinnerModule,
NgSwitch,
NgSwitchCase,
NgIf,
PdfViewerComponent,
ImgViewerComponent,
MediaPlayerComponent,
@@ -49,7 +46,7 @@ import { UnknownFormatComponent } from '../unknown-format/unknown-format.compone
UnknownFormatComponent,
ExtensionsModule,
NgForOf,
NgSwitchDefault
PreviewExtensionComponent
],
providers: [ViewUtilService]
})
+5 -15
View File
@@ -15,36 +15,26 @@
* limitations under the License.
*/
import { DynamicExtensionComponent } from './components/dynamic-component/dynamic.component';
import { DynamicTabComponent } from './components/dynamic-tab/dynamic-tab.component';
import { DynamicColumnComponent } from './components/dynamic-column/dynamic-column.component';
import { PreviewExtensionComponent } from './components/viewer/preview-extension.component';
import { NgModule, ModuleWithProviders, inject, provideAppInitializer } from '@angular/core';
import { AppExtensionService } from './services/app-extension.service';
import { setupExtensions } from './services/startup-extension-factory';
export const EXTENSION_DIRECTIVES = [DynamicExtensionComponent, DynamicTabComponent, DynamicColumnComponent, PreviewExtensionComponent] as const;
/** @deprecated import EXTENSION_DIRECTIVES or standalone components instead */
@NgModule({
imports: [...EXTENSION_DIRECTIVES],
exports: [...EXTENSION_DIRECTIVES]
})
/** @deprecated use provideAppExtensions() api instead */
@NgModule()
export class ExtensionsModule {
static forRoot(): ModuleWithProviders<ExtensionsModule> {
return {
ngModule: ExtensionsModule,
providers: [
provideAppInitializer(() => {
const initializerFn = setupExtensions(inject(AppExtensionService));
return initializerFn();
const appExtensionService = inject(AppExtensionService);
return appExtensionService.load();
})
]
};
}
/**
* @deprecated use `ExtensionsModule` instead, `EXTENSION_DIRECTIVES` or direct standalone components
* @deprecated use provideAppExtensions() api instead
* @returns Module with providers
*/
static forChild(): ModuleWithProviders<ExtensionsModule> {
@@ -15,12 +15,18 @@
* limitations under the License.
*/
import { NgModule } from '@angular/core';
import { AppConfigPipe } from './app-config.pipe';
import { EnvironmentProviders, inject, provideAppInitializer, Provider } from '@angular/core';
import { AppExtensionService } from './services/app-extension.service';
/** @deprecated This module is deprecated, consider importing AppConfigPipe directly */
@NgModule({
imports: [AppConfigPipe],
exports: [AppConfigPipe]
})
export class AppConfigModule {}
/**
* Provides all necessary entries for the app extensibility
* @returns list of providers
*/
export function provideAppExtensions(): (Provider | EnvironmentProviders)[] {
return [
provideAppInitializer(() => {
const appExtensionService = inject(AppExtensionService);
return appExtensionService.load();
})
];
}
@@ -1,20 +0,0 @@
/*!
* @license
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { AppExtensionService } from './app-extension.service';
export const setupExtensions = (appExtensionService: AppExtensionService) => () => appExtensionService.load();
+1
View File
@@ -41,3 +41,4 @@ export * from './lib/store/states/repository.state';
export * from './lib/components/public-api';
export * from './lib/extensions.module';
export * from './lib/providers';
+1 -1
View File
@@ -5,7 +5,7 @@ export default {
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
coverageDirectory: '../../../coverage/libs/js-api',
moduleMNameMapper: {
moduleNameMapper: {
'^pdfjs-dist$': 'pdfjs-dist/legacy/build/pdf'
},
transform: {
+2 -2
View File
@@ -15,5 +15,5 @@
* limitations under the License.
*/
import 'jest-preset-angular/setup-jest';
import 'resize-observer-polyfill/dist/ResizeObserver.global';
import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone';
setupZoneTestEnv();
@@ -13,7 +13,6 @@
overflow-x: auto;
resize: vertical;
border-radius: 3px;
padding: 10px 12px;
outline: none;
width: 100%;
}