diff --git a/alfa-client/libs/design-component/src/lib/form/multi-file-upload-editor/multi-file-upload-editor.component.html b/alfa-client/libs/design-component/src/lib/form/multi-file-upload-editor/multi-file-upload-editor.component.html new file mode 100644 index 0000000000000000000000000000000000000000..4bb9d09a52d824d83d6501a8c8c5513d855e81ae --- /dev/null +++ b/alfa-client/libs/design-component/src/lib/form/multi-file-upload-editor/multi-file-upload-editor.component.html @@ -0,0 +1,37 @@ +<!-- + + Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den + Ministerpräsidenten des Landes Schleswig-Holstein + Staatskanzlei + Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + + Lizenziert unter der EUPL, Version 1.2 oder - sobald + diese von der Europäischen Kommission genehmigt wurden - + Folgeversionen der EUPL ("Lizenz"); + Sie dürfen dieses Werk ausschließlich gemäß + dieser Lizenz nutzen. + Eine Kopie der Lizenz finden Sie hier: + + https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + + Sofern nicht durch anwendbare Rechtsvorschriften + gefordert oder in schriftlicher Form vereinbart, wird + die unter der Lizenz verbreitete Software "so wie sie + ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + ausdrücklich oder stillschweigend - verbreitet. + Die sprachspezifischen Genehmigungen und Beschränkungen + unter der Lizenz sind dem Lizenztext zu entnehmen. + +--> +<ods-file-upload-button + [id]="uploadButtonId" + [accept]="accept" + [attr.data-test-id]="(label | convertForDataTest) + '-file-upload-button'" + [multi]="true" + [isLoading]="false" + class="relative w-72" +> + <ods-spinner-icon spinner size="medium" /> + <ods-attachment-icon icon size="medium" /> + <p text class="text-center">{{ label }}</p> +</ods-file-upload-button> diff --git a/alfa-client/libs/design-component/src/lib/form/multi-file-upload-editor/multi-file-upload-editor.component.spec.ts b/alfa-client/libs/design-component/src/lib/form/multi-file-upload-editor/multi-file-upload-editor.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..6b066bf109720fc66a914f64c86cfe26cdf7928b --- /dev/null +++ b/alfa-client/libs/design-component/src/lib/form/multi-file-upload-editor/multi-file-upload-editor.component.spec.ts @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +import { BinaryFileService, ToUploadFile } from '@alfa-client/binary-file-shared'; +import { ConvertForDataTestPipe } from '@alfa-client/tech-shared'; +import { existsAsHtmlElement, getElementComponentFromFixtureByCss, mock, Mock } from '@alfa-client/test-utils'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { expect } from '@jest/globals'; +import { getUrl, Resource } from '@ngxp/rest'; +import { FileUploadButtonComponent, SpinnerIconComponent } from '@ods/system'; +import { getDataTestIdOf } from 'libs/tech-shared/test/data-test'; +import { MockComponent } from 'ng-mocks'; +import { createFileList } from '../../../../../tech-shared/test/file'; +import { createDummyResource } from '../../../../../tech-shared/test/resource'; +import { MultiFileUploadEditorComponent } from './multi-file-upload-editor.component'; + +describe('MultiFileUploadEditorComponent', () => { + let component: MultiFileUploadEditorComponent; + let fixture: ComponentFixture<MultiFileUploadEditorComponent>; + + const uploadLinkRel: string = 'upload'; + const uploadResource: Resource = createDummyResource([uploadLinkRel]); + + const buttonTestId: string = getDataTestIdOf('Ein_Label-file-upload-button'); + + let binaryFileService: Mock<BinaryFileService>; + + beforeEach(() => { + binaryFileService = mock(BinaryFileService); + }); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ + MultiFileUploadEditorComponent, + MockComponent(SpinnerIconComponent), + MockComponent(FileUploadButtonComponent), + ConvertForDataTestPipe, + ], + providers: [ + { + provide: BinaryFileService, + useValue: binaryFileService, + }, + ], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(MultiFileUploadEditorComponent); + component = fixture.componentInstance; + component.uploadResource = uploadResource; + component.uploadLinkRelation = uploadLinkRel; + component.label = 'Ein Label'; + fixture.detectChanges(); + }); + + describe('component', () => { + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('onFilesUpload', () => { + beforeEach(() => { + component._uploadFiles = jest.fn(); + component.setErrors = jest.fn(); + }); + + it('should upload files', () => { + const fileList: FileList = createFileList(); + + component.onFilesUpload(fileList); + + expect(component._uploadFiles).toHaveBeenCalledWith(fileList); + }); + + it('should set errors', () => { + component.onFilesUpload(createFileList()); + + expect(component.setErrors).toHaveBeenCalled(); + }); + }); + + describe('_uploadFiles', () => { + it('should call binary file service', () => { + const fileList: FileList = createFileList(); + + component._uploadFiles(fileList); + + expect(binaryFileService.uploadFileNew).toHaveBeenCalledWith({ + file: fileList.item(0), + type: component.fileUploadType, + uploadUrl: getUrl(uploadResource, uploadLinkRel), + } as ToUploadFile); + }); + }); + }); + + describe('template', () => { + it('should have upload button', () => { + existsAsHtmlElement(fixture, buttonTestId); + }); + + it('should have inputs', () => { + const fileButtonComponent: FileUploadButtonComponent = getElementComponentFromFixtureByCss(fixture, buttonTestId); + + expect(fileButtonComponent.id).toEqual(component.uploadButtonId); + expect(fileButtonComponent.accept).toEqual(component.accept); + expect(fileButtonComponent.multi).toEqual(true); + expect(fileButtonComponent.isLoading).toEqual(false); + }); + }); +}); diff --git a/alfa-client/libs/design-component/src/lib/form/multi-file-upload-editor/multi-file-upload-editor.component.ts b/alfa-client/libs/design-component/src/lib/form/multi-file-upload-editor/multi-file-upload-editor.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..cd8fa52d652eeb371b9c3c89a5ba89c18e46393c --- /dev/null +++ b/alfa-client/libs/design-component/src/lib/form/multi-file-upload-editor/multi-file-upload-editor.component.ts @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +import { BinaryFileModule } from '@alfa-client/binary-file'; +import { BinaryFileResource, BinaryFileService, FileUploadType } from '@alfa-client/binary-file-shared'; +import { StateResource, TechSharedModule } from '@alfa-client/tech-shared'; +import { Component, HostListener, inject, Input } from '@angular/core'; +import { ControlContainer, FormGroupDirective, ReactiveFormsModule } from '@angular/forms'; +import { getUrl, Resource } from '@ngxp/rest'; +import { AttachmentIconComponent, FileUploadButtonComponent, SpinnerIconComponent } from '@ods/system'; +import { uniqueId } from 'lodash-es'; +import { Observable } from 'rxjs'; +import { FormControlEditorAbstractComponent } from '../formcontrol-editor.abstract.component'; + +export interface MultiUploadItem { + file?: File; + uploadStateResource: Observable<StateResource<BinaryFileResource>>; +} + +@Component({ + selector: 'ods-multi-file-upload-editor', + templateUrl: './multi-file-upload-editor.component.html', + viewProviders: [{ provide: ControlContainer, useExisting: FormGroupDirective }], + standalone: true, + imports: [ + FileUploadButtonComponent, + AttachmentIconComponent, + SpinnerIconComponent, + ReactiveFormsModule, + TechSharedModule, + BinaryFileModule, + ], +}) +export class MultiFileUploadEditorComponent extends FormControlEditorAbstractComponent { + @Input() label: string = ''; + @Input() accept: string = '*/*'; + @Input() fileUploadType: FileUploadType; + @Input() uploadResource: Resource; + @Input() uploadLinkRelation: string; + + private readonly binaryFileService: BinaryFileService = inject(BinaryFileService); + + public readonly uploadButtonId: string = uniqueId(); + + @HostListener('change', ['$event.target.files']) onFilesUpload(fileList: FileList): void { + this._uploadFiles(fileList); + this.setErrors(); + } + + _uploadFiles(fileList: FileList) { + for (let i = 0; i < fileList.length; i++) { + this.binaryFileService.uploadFileNew({ + file: fileList.item(i), + type: this.fileUploadType, + uploadUrl: getUrl(this.uploadResource, this.uploadLinkRelation), + }); + } + } +}