From 459e7d33fb7ce5fe671c049d95f0dd1ff765e6e5 Mon Sep 17 00:00:00 2001 From: Sandhya Raja Sabeson Date: Fri, 20 Dec 2024 12:21:26 -0500 Subject: [PATCH 01/10] working --- .../file-attachment.component.html | 56 ++++++------ .../file-attachment.component.ts | 13 +++ .../file-attachment.component.ts | 6 ++ .../file-attachment.service.ts | 87 +++++++++++++------ .../file-drop/file-drop.component.ts | 38 +++++++- 5 files changed, 140 insertions(+), 60 deletions(-) diff --git a/apps/playground/src/app/components/forms/file-attachment/file-attachment.component.html b/apps/playground/src/app/components/forms/file-attachment/file-attachment.component.html index d89e5228ad..ffe800935a 100644 --- a/apps/playground/src/app/components/forms/file-attachment/file-attachment.component.html +++ b/apps/playground/src/app/components/forms/file-attachment/file-attachment.component.html @@ -1,35 +1,35 @@ - -@for (file of allItems; track file) { - -} -
- + +@for (file of allItems; track file) { + +} + +Values +
Touched: {{ attachment.touched }}
+
Dirty: {{ attachment.dirty }}
+ + + diff --git a/apps/playground/src/app/components/forms/file-attachment/file-attachment.component.ts b/apps/playground/src/app/components/forms/file-attachment/file-attachment.component.ts index 3245c96bd7..40bc55e945 100644 --- a/apps/playground/src/app/components/forms/file-attachment/file-attachment.component.ts +++ b/apps/playground/src/app/components/forms/file-attachment/file-attachment.component.ts @@ -92,4 +92,17 @@ export class FileAttachmentComponent { } } } + + public markTouched(): void { + this.attachment.markAsTouched(); + } + + public loadFile(): void { + const file: SkyFileItem = { + file: new File([], 'foo.bar', { type: 'image/png' }), + url: 'foo.bar.bar', + }; + + this.attachment.setValue(file); + } } diff --git a/libs/components/forms/src/lib/modules/file-attachment/file-attachment/file-attachment.component.ts b/libs/components/forms/src/lib/modules/file-attachment/file-attachment/file-attachment.component.ts index 41cd92e48b..23f8efec20 100644 --- a/libs/components/forms/src/lib/modules/file-attachment/file-attachment/file-attachment.component.ts +++ b/libs/components/forms/src/lib/modules/file-attachment/file-attachment/file-attachment.component.ts @@ -23,6 +23,7 @@ import { } from '@angular/core'; import { ControlValueAccessor, + NG_VALUE_ACCESSOR, NgControl, ValidationErrors, Validators, @@ -85,6 +86,11 @@ const MIN_FILE_SIZE_DEFAULT = 0; providers: [ SkyFileAttachmentService, { provide: SKY_FORM_ERRORS_ENABLED, useValue: true }, + { + provide: NG_VALUE_ACCESSOR, + useExisting: SkyFileAttachmentComponent, + multi: true, + }, ], hostDirectives: [SkyThemeComponentClassDirective], selector: 'sky-file-attachment', diff --git a/libs/components/forms/src/lib/modules/file-attachment/file-attachment/file-attachment.service.ts b/libs/components/forms/src/lib/modules/file-attachment/file-attachment/file-attachment.service.ts index b703289b87..4b30db92c2 100644 --- a/libs/components/forms/src/lib/modules/file-attachment/file-attachment/file-attachment.service.ts +++ b/libs/components/forms/src/lib/modules/file-attachment/file-attachment/file-attachment.service.ts @@ -8,8 +8,9 @@ import { SkyFileValidateFn } from '../shared/file-validate-function'; */ @Injectable() export class SkyFileAttachmentService { + // make it an array of SkyFileItem public checkFiles( - files: FileList, + files: FileList | SkyFileItem, minFileSize: number, maxFileSize: number, acceptedTypes?: string, @@ -17,38 +18,68 @@ export class SkyFileAttachmentService { ): SkyFileItem[] { const fileResults: SkyFileItem[] = []; - for (let index = 0; index < files.length; index++) { - const fileItem = { - file: files.item(index), - } as SkyFileItem; - - if (fileItem.file.size < minFileSize) { - fileItem.errorType = 'minFileSize'; - fileItem.errorParam = minFileSize.toString(); - fileResults.push(fileItem); - } else if (fileItem.file.size > maxFileSize) { - fileItem.errorType = 'maxFileSize'; - fileItem.errorParam = maxFileSize.toString(); - fileResults.push(fileItem); - } else if (this.fileTypeRejected(fileItem.file.type, acceptedTypes)) { - fileItem.errorType = 'fileType'; - fileItem.errorParam = this.#getAcceptedTypesList(acceptedTypes); - fileResults.push(fileItem); - } else if (validateFn) { - const errorParam = validateFn(fileItem); - - if (errorParam) { - fileItem.errorType = 'validate'; - fileItem.errorParam = errorParam; - } - fileResults.push(fileItem); - } else { - fileResults.push(fileItem); + if (files instanceof FileList) { + for (let index = 0; index < files.length; index++) { + const fileItem = { + file: files.item(index), + } as SkyFileItem; + + fileResults.push( + this.checkFile( + fileItem, + minFileSize, + maxFileSize, + acceptedTypes, + validateFn, + ), + ); } + } else { + fileResults.push( + this.checkFile( + files as SkyFileItem, + minFileSize, + maxFileSize, + acceptedTypes, + validateFn, + ), + ); } return fileResults; } + public checkFile( + fileItem: SkyFileItem, + minFileSize: number, + maxFileSize: number, + acceptedTypes?: string, + validateFn?: SkyFileValidateFn, + ): SkyFileItem { + if (fileItem.file.size < minFileSize) { + fileItem.errorType = 'minFileSize'; + fileItem.errorParam = minFileSize.toString(); + return fileItem; + } else if (fileItem.file.size > maxFileSize) { + fileItem.errorType = 'maxFileSize'; + fileItem.errorParam = maxFileSize.toString(); + return fileItem; + } else if (this.fileTypeRejected(fileItem.file.type, acceptedTypes)) { + fileItem.errorType = 'fileType'; + fileItem.errorParam = this.#getAcceptedTypesList(acceptedTypes); + return fileItem; + } else if (validateFn) { + const errorParam = validateFn(fileItem); + + if (errorParam) { + fileItem.errorType = 'validate'; + fileItem.errorParam = errorParam; + } + return fileItem; + } else { + return fileItem; + } + } + /** * Returns `true` if a directory is found in the provided `files` parameter. */ diff --git a/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.ts b/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.ts index 84b4200f36..88cf975974 100644 --- a/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.ts +++ b/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.ts @@ -12,7 +12,11 @@ import { booleanAttribute, inject, } from '@angular/core'; -import { FormsModule } from '@angular/forms'; +import { + ControlValueAccessor, + FormsModule, + NG_VALUE_ACCESSOR, +} from '@angular/forms'; import { SkyIdModule, SkyLiveAnnouncerService } from '@skyux/core'; import { SkyIdService } from '@skyux/core'; import { SkyHelpInlineModule } from '@skyux/help-inline'; @@ -55,6 +59,11 @@ const MIN_FILE_SIZE_DEFAULT = 0; providers: [ SkyFileAttachmentService, { provide: SKY_FORM_ERRORS_ENABLED, useValue: true }, + { + provide: NG_VALUE_ACCESSOR, + useExisting: SkyFileDropComponent, + multi: true, + }, ], standalone: true, imports: [ @@ -71,7 +80,22 @@ const MIN_FILE_SIZE_DEFAULT = 0; SkyThemeModule, ], }) -export class SkyFileDropComponent implements OnDestroy { +export class SkyFileDropComponent implements OnDestroy, ControlValueAccessor { + public writeValue(value: any): void { + this.#handleFiles(value); + } + + public registerOnChange(fn: any): void { + this.#notifyChange = fn; + } + + #notifyTouched: (() => void) | undefined; + #notifyChange: ((_: any) => void) | undefined; + + public registerOnTouched(fn: () => void): void { + this.#notifyTouched = fn; + } + /** * Fires when users add or remove files. */ @@ -270,6 +294,7 @@ export class SkyFileDropComponent implements OnDestroy { public dropClicked(): void { if (!this.noClick && this.inputEl) { + this.#notifyTouched?.(); this.inputEl.nativeElement.click(); } } @@ -329,6 +354,8 @@ export class SkyFileDropComponent implements OnDestroy { dropEvent.stopPropagation(); dropEvent.preventDefault(); + this.#notifyTouched?.(); + this.#enterEventTarget = undefined; this.rejectedOver = false; this.acceptedOver = false; @@ -372,6 +399,7 @@ export class SkyFileDropComponent implements OnDestroy { } public onLinkBlur(): void { + this.#notifyTouched?.(); this.linkInputBlur.emit(); } @@ -398,6 +426,8 @@ export class SkyFileDropComponent implements OnDestroy { if (this.inputEl) { this.inputEl.nativeElement.value = ''; } + + this.#notifyChange?.(undefined); } } @@ -455,11 +485,11 @@ export class SkyFileDropComponent implements OnDestroy { reader.readAsDataURL(file.file); } - #handleFiles(files?: FileList | null): void { + #handleFiles(files?: FileList | null | SkyFileItem): void { if (files) { const validFileArray: SkyFileItem[] = []; const rejectedFileArray: SkyFileItem[] = []; - const totalFiles = files.length; + const totalFiles = files instanceof FileList ? files.length : 1; const processedFiles = this.#fileAttachmentService.checkFiles( files, From 4fa47c90386dcce14ca4d0a10082d4ac7e991c7a Mon Sep 17 00:00:00 2001 From: Sandhya Raja Sabeson Date: Fri, 3 Jan 2025 15:54:46 -0500 Subject: [PATCH 02/10] write working but only with skyfileitem --- .../file-attachment/basic/demo.component.ts | 11 ++- .../file-attachment.component.html | 14 ++-- .../file-attachment.component.ts | 68 +++---------------- .../file-attachment/file-attachment.module.ts | 2 + .../file-attachment.component.html | 2 +- .../file-attachment.component.ts | 6 -- .../file-drop/file-drop.component.html | 8 ++- .../file-drop/file-drop.component.ts | 41 +++++++---- 8 files changed, 62 insertions(+), 90 deletions(-) diff --git a/apps/code-examples/src/app/code-examples/forms/file-attachment/basic/demo.component.ts b/apps/code-examples/src/app/code-examples/forms/file-attachment/basic/demo.component.ts index ad57411cfe..8a6ac8bf96 100644 --- a/apps/code-examples/src/app/code-examples/forms/file-attachment/basic/demo.component.ts +++ b/apps/code-examples/src/app/code-examples/forms/file-attachment/basic/demo.component.ts @@ -41,7 +41,7 @@ export class DemoComponent { attachment: FormControl; }>; - protected maxFileSize = 4000000; + protected maxFileSize = 40; constructor() { this.attachment = new FormControl(undefined, { @@ -51,6 +51,15 @@ export class DemoComponent { this.formGroup = inject(FormBuilder).group({ attachment: this.attachment, }); + + this.attachment.setValue({ + file: { + name: 'aFile.txt', + type: '', + size: 100000000, + }, + url: './file/path', + } as SkyFileItem); } protected onFileClick($event: SkyFileAttachmentClick): void { diff --git a/apps/playground/src/app/components/forms/file-attachment/file-attachment.component.html b/apps/playground/src/app/components/forms/file-attachment/file-attachment.component.html index ffe800935a..bab307ca44 100644 --- a/apps/playground/src/app/components/forms/file-attachment/file-attachment.component.html +++ b/apps/playground/src/app/components/forms/file-attachment/file-attachment.component.html @@ -8,28 +8,24 @@ [acceptedTypes]="acceptedTypes" [allowLinks]="true" [maxFileSize]="maxFileSize" - [minFileSize]="minFileSize" [required]="required" [validateFn]="validateFile" [stacked]="true" - (filesChanged)="filesUpdated($event)" - (linkChanged)="linkAdded($event)" /> -@for (file of allItems; track file) { - +@for (file of attachment.value; track file) { + } Values
Touched: {{ attachment.touched }}
Dirty: {{ attachment.dirty }}
+
Value: {{ attachment.value | json }}
- diff --git a/apps/playground/src/app/components/forms/file-attachment/file-attachment.component.ts b/apps/playground/src/app/components/forms/file-attachment/file-attachment.component.ts index 40bc55e945..14ec1a4f1e 100644 --- a/apps/playground/src/app/components/forms/file-attachment/file-attachment.component.ts +++ b/apps/playground/src/app/components/forms/file-attachment/file-attachment.component.ts @@ -1,17 +1,11 @@ import { Component, inject } from '@angular/core'; import { - AbstractControl, FormBuilder, FormControl, FormGroup, Validators, } from '@angular/forms'; -import { - SkyFileAttachmentChange, - SkyFileDropChange, - SkyFileItem, - SkyFileLink, -} from '@skyux/forms'; +import { SkyFileItem, SkyFileLink } from '@skyux/forms'; @Component({ selector: 'app-file-attachment-demo', @@ -22,28 +16,15 @@ export class FileAttachmentComponent { public allItems: (SkyFileItem | SkyFileLink)[]; - public filesToUpload: SkyFileItem[]; - - public linksToUpload: SkyFileLink[]; - public maxFileSize = 4000000; public minFileSize = 300000; - public rejectedFiles: SkyFileItem[]; - - protected attachment: FormControl; + protected attachment: FormControl<(SkyFileItem | SkyFileLink)[]>; protected formGroup: FormGroup; protected required = true; - get #reactiveFile(): AbstractControl | null { - return this.formGroup.get('attachment'); - } - constructor() { - this.filesToUpload = []; - this.rejectedFiles = []; this.allItems = []; - this.linksToUpload = []; this.attachment = new FormControl(undefined, Validators.required); this.formGroup = inject(FormBuilder).group({ attachment: this.attachment, @@ -51,20 +32,13 @@ export class FileAttachmentComponent { } public deleteFile(file: SkyFileItem | SkyFileLink): void { - this.#removeFromArray(this.allItems, file); - this.#removeFromArray(this.filesToUpload, file); - this.#removeFromArray(this.linksToUpload, file); - } - - public filesUpdated(result: SkyFileDropChange): void { - this.filesToUpload = this.filesToUpload.concat(result.files); - this.rejectedFiles = result.rejectedFiles; - this.allItems = this.allItems.concat(result.files); - } - - public linkAdded(result: SkyFileLink): void { - this.linksToUpload = this.linksToUpload.concat(result); - this.allItems = this.allItems.concat(result); + const index = this.attachment.value.indexOf(file); + if (index !== -1) { + this.attachment.value.splice(index, 1); + } + if (this.attachment.value.length === 0) { + this.attachment.setValue(null); + } } public validateFile(file: SkyFileItem): string { @@ -73,36 +47,16 @@ export class FileAttachmentComponent { } } - protected onFileChange(result: SkyFileAttachmentChange): void { - const file = result.file; - - if (file && file.errorType) { - this.#reactiveFile?.setValue(undefined); - } else { - this.#reactiveFile?.setValue(file); - } - } - - #removeFromArray(items: any[], obj: SkyFileItem | SkyFileLink): void { - if (items) { - const index = items.indexOf(obj); - - if (index !== -1) { - items.splice(index, 1); - } - } - } - public markTouched(): void { this.attachment.markAsTouched(); } - public loadFile(): void { + public setFiles(): void { const file: SkyFileItem = { file: new File([], 'foo.bar', { type: 'image/png' }), url: 'foo.bar.bar', }; - this.attachment.setValue(file); + this.attachment.setValue([file]); } } diff --git a/apps/playground/src/app/components/forms/file-attachment/file-attachment.module.ts b/apps/playground/src/app/components/forms/file-attachment/file-attachment.module.ts index bf034078c1..f63d201864 100644 --- a/apps/playground/src/app/components/forms/file-attachment/file-attachment.module.ts +++ b/apps/playground/src/app/components/forms/file-attachment/file-attachment.module.ts @@ -1,3 +1,4 @@ +import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { SkyFileAttachmentsModule } from '@skyux/forms'; @@ -11,6 +12,7 @@ import { FileAttachmentComponent } from './file-attachment.component'; @NgModule({ imports: [ + CommonModule, FileAttachmentRoutingModule, FormsModule, ReactiveFormsModule, diff --git a/libs/components/forms/src/lib/modules/file-attachment/file-attachment/file-attachment.component.html b/libs/components/forms/src/lib/modules/file-attachment/file-attachment/file-attachment.component.html index 76055adb1e..c3f6fcd713 100644 --- a/libs/components/forms/src/lib/modules/file-attachment/file-attachment/file-attachment.component.html +++ b/libs/components/forms/src/lib/modules/file-attachment/file-attachment/file-attachment.component.html @@ -167,7 +167,7 @@ {{ labelText }} - @if (required) { + @if (isRequired) { {{ 'skyux_file_attachment_required' | skyLibResources }} @@ -198,7 +198,9 @@ @for (rejectedFile of rejectedFiles; track rejectedFile) {
diff --git a/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.ts b/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.ts index 88cf975974..7c58b6baec 100644 --- a/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.ts +++ b/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.ts @@ -15,7 +15,8 @@ import { import { ControlValueAccessor, FormsModule, - NG_VALUE_ACCESSOR, + NgControl, + Validators, } from '@angular/forms'; import { SkyIdModule, SkyLiveAnnouncerService } from '@skyux/core'; import { SkyIdService } from '@skyux/core'; @@ -59,11 +60,6 @@ const MIN_FILE_SIZE_DEFAULT = 0; providers: [ SkyFileAttachmentService, { provide: SKY_FORM_ERRORS_ENABLED, useValue: true }, - { - provide: NG_VALUE_ACCESSOR, - useExisting: SkyFileDropComponent, - multi: true, - }, ], standalone: true, imports: [ @@ -81,21 +77,38 @@ const MIN_FILE_SIZE_DEFAULT = 0; ], }) export class SkyFileDropComponent implements OnDestroy, ControlValueAccessor { - public writeValue(value: any): void { - this.#handleFiles(value); + #notifyTouched: (() => void) | undefined; + #notifyChange: + | ((_: (SkyFileItem | SkyFileLink)[] | undefined | null) => void) + | undefined; + #_uploadedFiles: (SkyFileItem | SkyFileLink)[] | undefined | null = []; + + protected ngControl = inject(NgControl); + protected get isRequired(): boolean { + return ( + this.required || + (this.ngControl.control?.hasValidator(Validators.required) ?? false) + ); + } + + public writeValue(value: SkyFileItem[]): void { + value.forEach((file) => { + this.#handleFiles(file); + }); } public registerOnChange(fn: any): void { this.#notifyChange = fn; } - #notifyTouched: (() => void) | undefined; - #notifyChange: ((_: any) => void) | undefined; - public registerOnTouched(fn: () => void): void { this.#notifyTouched = fn; } + constructor() { + this.ngControl.valueAccessor = this; + } + /** * Fires when users add or remove files. */ @@ -391,6 +404,8 @@ export class SkyFileDropComponent implements OnDestroy, ControlValueAccessor { public addLink(event: Event): void { event.preventDefault(); this.linkChanged.emit({ url: this.linkUrl } as SkyFileLink); + this.#_uploadedFiles?.push({ url: this.linkUrl } as SkyFileLink); + this.#notifyChange?.(this.#_uploadedFiles); this.#announceState( 'skyux_file_attachment_file_upload_link_added', this.linkUrl, @@ -426,8 +441,6 @@ export class SkyFileDropComponent implements OnDestroy, ControlValueAccessor { if (this.inputEl) { this.inputEl.nativeElement.value = ''; } - - this.#notifyChange?.(undefined); } } @@ -453,6 +466,8 @@ export class SkyFileDropComponent implements OnDestroy, ControlValueAccessor { reader.addEventListener('load', (event: any) => { file.url = event.target.result; validFileArray.push(file); + this.#_uploadedFiles?.push(file); + this.#notifyChange?.(this.#_uploadedFiles); fileDrop.#emitFileChangeEvent( totalFiles, rejectedFileArray, From 5178eeb09c7e41e5c83b41e56d50686a3c8e12b5 Mon Sep 17 00:00:00 2001 From: Sandhya Raja Sabeson Date: Mon, 6 Jan 2025 09:19:34 -0500 Subject: [PATCH 03/10] write value proper --- .../file-attachment.component.ts | 6 +++- .../file-drop/file-drop.component.ts | 28 +++++++++++++------ 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/apps/playground/src/app/components/forms/file-attachment/file-attachment.component.ts b/apps/playground/src/app/components/forms/file-attachment/file-attachment.component.ts index 14ec1a4f1e..e2eeb61f37 100644 --- a/apps/playground/src/app/components/forms/file-attachment/file-attachment.component.ts +++ b/apps/playground/src/app/components/forms/file-attachment/file-attachment.component.ts @@ -57,6 +57,10 @@ export class FileAttachmentComponent { url: 'foo.bar.bar', }; - this.attachment.setValue([file]); + const link: SkyFileLink = { + url: 'foo.foo', + }; + + this.attachment.setValue([file, link]); } } diff --git a/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.ts b/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.ts index 7c58b6baec..87faf595e3 100644 --- a/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.ts +++ b/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.ts @@ -91,10 +91,18 @@ export class SkyFileDropComponent implements OnDestroy, ControlValueAccessor { ); } - public writeValue(value: SkyFileItem[]): void { - value.forEach((file) => { - this.#handleFiles(file); - }); + public writeValue(value: any): void { + if (value instanceof Array) { + value.forEach((file) => { + if ('url' in file) { + if ('file' in file) { + this.#handleFiles(file as SkyFileItem); + } else { + this.uploadLink(file as SkyFileLink); + } + } + }); + } } public registerOnChange(fn: any): void { @@ -403,14 +411,18 @@ export class SkyFileDropComponent implements OnDestroy, ControlValueAccessor { public addLink(event: Event): void { event.preventDefault(); - this.linkChanged.emit({ url: this.linkUrl } as SkyFileLink); - this.#_uploadedFiles?.push({ url: this.linkUrl } as SkyFileLink); + this.uploadLink({ url: this.linkUrl } as SkyFileLink); + this.linkUrl = undefined; + } + + protected uploadLink(file: SkyFileLink): void { + this.linkChanged.emit(file); + this.#_uploadedFiles?.push(file); this.#notifyChange?.(this.#_uploadedFiles); this.#announceState( 'skyux_file_attachment_file_upload_link_added', - this.linkUrl, + file.url, ); - this.linkUrl = undefined; } public onLinkBlur(): void { From 4c0d79cfd304766c2880173b000dc2dca24f7690 Mon Sep 17 00:00:00 2001 From: Sandhya Raja Sabeson Date: Mon, 6 Jan 2025 16:21:45 -0500 Subject: [PATCH 04/10] old unit tests working --- .../file-attachment.component.ts | 12 ++- .../file-attachment.service.ts | 85 ++++++------------- .../file-drop/file-drop.component.html | 4 +- .../file-drop/file-drop.component.spec.ts | 1 + .../file-drop/file-drop.component.ts | 34 +++++--- 5 files changed, 62 insertions(+), 74 deletions(-) diff --git a/libs/components/forms/src/lib/modules/file-attachment/file-attachment/file-attachment.component.ts b/libs/components/forms/src/lib/modules/file-attachment/file-attachment/file-attachment.component.ts index 41cd92e48b..d442dd1e1a 100644 --- a/libs/components/forms/src/lib/modules/file-attachment/file-attachment/file-attachment.component.ts +++ b/libs/components/forms/src/lib/modules/file-attachment/file-attachment/file-attachment.component.ts @@ -594,8 +594,16 @@ export class SkyFileAttachmentComponent } } - #handleFiles(files?: FileList | null): void { - if (files) { + #handleFiles(fileList?: FileList | null): void { + if (fileList) { + const files: SkyFileItem[] = []; + + for (let index = 0; index < files.length; index++) { + files.push({ + file: fileList.item(index), + } as SkyFileItem); + } + const processedFiles = this.#fileAttachmentService.checkFiles( files, this.minFileSize, diff --git a/libs/components/forms/src/lib/modules/file-attachment/file-attachment/file-attachment.service.ts b/libs/components/forms/src/lib/modules/file-attachment/file-attachment/file-attachment.service.ts index 4b30db92c2..d62efa9c46 100644 --- a/libs/components/forms/src/lib/modules/file-attachment/file-attachment/file-attachment.service.ts +++ b/libs/components/forms/src/lib/modules/file-attachment/file-attachment/file-attachment.service.ts @@ -8,9 +8,8 @@ import { SkyFileValidateFn } from '../shared/file-validate-function'; */ @Injectable() export class SkyFileAttachmentService { - // make it an array of SkyFileItem public checkFiles( - files: FileList | SkyFileItem, + files: SkyFileItem[], minFileSize: number, maxFileSize: number, acceptedTypes?: string, @@ -18,68 +17,34 @@ export class SkyFileAttachmentService { ): SkyFileItem[] { const fileResults: SkyFileItem[] = []; - if (files instanceof FileList) { - for (let index = 0; index < files.length; index++) { - const fileItem = { - file: files.item(index), - } as SkyFileItem; - - fileResults.push( - this.checkFile( - fileItem, - minFileSize, - maxFileSize, - acceptedTypes, - validateFn, - ), - ); + files.forEach((fileItem) => { + if (fileItem.file.size < minFileSize) { + fileItem.errorType = 'minFileSize'; + fileItem.errorParam = minFileSize.toString(); + fileResults.push(fileItem); + } else if (fileItem.file.size > maxFileSize) { + fileItem.errorType = 'maxFileSize'; + fileItem.errorParam = maxFileSize.toString(); + fileResults.push(fileItem); + } else if (this.fileTypeRejected(fileItem.file.type, acceptedTypes)) { + fileItem.errorType = 'fileType'; + fileItem.errorParam = this.#getAcceptedTypesList(acceptedTypes); + fileResults.push(fileItem); + } else if (validateFn) { + const errorParam = validateFn(fileItem); + + if (errorParam) { + fileItem.errorType = 'validate'; + fileItem.errorParam = errorParam; + } + fileResults.push(fileItem); + } else { + fileResults.push(fileItem); } - } else { - fileResults.push( - this.checkFile( - files as SkyFileItem, - minFileSize, - maxFileSize, - acceptedTypes, - validateFn, - ), - ); - } + }); return fileResults; } - public checkFile( - fileItem: SkyFileItem, - minFileSize: number, - maxFileSize: number, - acceptedTypes?: string, - validateFn?: SkyFileValidateFn, - ): SkyFileItem { - if (fileItem.file.size < minFileSize) { - fileItem.errorType = 'minFileSize'; - fileItem.errorParam = minFileSize.toString(); - return fileItem; - } else if (fileItem.file.size > maxFileSize) { - fileItem.errorType = 'maxFileSize'; - fileItem.errorParam = maxFileSize.toString(); - return fileItem; - } else if (this.fileTypeRejected(fileItem.file.type, acceptedTypes)) { - fileItem.errorType = 'fileType'; - fileItem.errorParam = this.#getAcceptedTypesList(acceptedTypes); - return fileItem; - } else if (validateFn) { - const errorParam = validateFn(fileItem); - - if (errorParam) { - fileItem.errorType = 'validate'; - fileItem.errorParam = errorParam; - } - return fileItem; - } else { - return fileItem; - } - } - /** * Returns `true` if a directory is found in the provided `files` parameter. */ diff --git a/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.html b/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.html index 8a699de361..93a7d13688 100644 --- a/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.html +++ b/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.html @@ -198,8 +198,8 @@ @for (rejectedFile of rejectedFiles; track rejectedFile) { diff --git a/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.spec.ts b/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.spec.ts index 56cc720f74..5a22d08b65 100644 --- a/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.spec.ts +++ b/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.spec.ts @@ -1,3 +1,4 @@ +/* eslint-disable complexity */ import { Component, DebugElement } from '@angular/core'; import { ComponentFixture, TestBed, fakeAsync } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; diff --git a/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.ts b/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.ts index 87faf595e3..5daf3f7d35 100644 --- a/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.ts +++ b/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.ts @@ -83,25 +83,25 @@ export class SkyFileDropComponent implements OnDestroy, ControlValueAccessor { | undefined; #_uploadedFiles: (SkyFileItem | SkyFileLink)[] | undefined | null = []; - protected ngControl = inject(NgControl); + protected ngControl = inject(NgControl, { optional: true }); protected get isRequired(): boolean { return ( this.required || - (this.ngControl.control?.hasValidator(Validators.required) ?? false) + (this.ngControl?.control?.hasValidator(Validators.required) ?? false) ); } public writeValue(value: any): void { if (value instanceof Array) { - value.forEach((file) => { + value.forEach((file, index) => { if ('url' in file) { - if ('file' in file) { - this.#handleFiles(file as SkyFileItem); - } else { + if (!('file' in file)) { this.uploadLink(file as SkyFileLink); + value.splice(index, 1); } } }); + this.#handleFiles(value as SkyFileItem[]); } } @@ -114,7 +114,9 @@ export class SkyFileDropComponent implements OnDestroy, ControlValueAccessor { } constructor() { - this.ngControl.valueAccessor = this; + if (this.ngControl) { + this.ngControl.valueAccessor = this; + } } /** @@ -512,11 +514,23 @@ export class SkyFileDropComponent implements OnDestroy, ControlValueAccessor { reader.readAsDataURL(file.file); } - #handleFiles(files?: FileList | null | SkyFileItem): void { - if (files) { + #handleFiles(fileList?: FileList | null | SkyFileItem[]): void { + if (fileList) { const validFileArray: SkyFileItem[] = []; const rejectedFileArray: SkyFileItem[] = []; - const totalFiles = files instanceof FileList ? files.length : 1; + const totalFiles = fileList.length; + + let files: SkyFileItem[] = []; + + if ('item' in fileList) { + for (let index = 0; index < fileList.length; index++) { + files.push({ + file: fileList.item(index), + } as SkyFileItem); + } + } else { + files = fileList; + } const processedFiles = this.#fileAttachmentService.checkFiles( files, From a01f43bd5176c40537748bdafc5a98fe5916b0cb Mon Sep 17 00:00:00 2001 From: Sandhya Raja Sabeson Date: Tue, 7 Jan 2025 11:36:17 -0500 Subject: [PATCH 05/10] file attachment working --- .../file-attachment.component.ts | 10 +++-- .../reactive-file-drop.component.fixture.html | 7 ++++ .../reactive-file-drop.component.fixture.ts | 42 +++++++++++++++++++ 3 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 libs/components/forms/src/lib/modules/file-attachment/file-drop/fixtures/reactive-file-drop.component.fixture.html create mode 100644 libs/components/forms/src/lib/modules/file-attachment/file-drop/fixtures/reactive-file-drop.component.fixture.ts diff --git a/libs/components/forms/src/lib/modules/file-attachment/file-attachment/file-attachment.component.ts b/libs/components/forms/src/lib/modules/file-attachment/file-attachment/file-attachment.component.ts index d442dd1e1a..ce95ec63ca 100644 --- a/libs/components/forms/src/lib/modules/file-attachment/file-attachment/file-attachment.component.ts +++ b/libs/components/forms/src/lib/modules/file-attachment/file-attachment/file-attachment.component.ts @@ -598,10 +598,12 @@ export class SkyFileAttachmentComponent if (fileList) { const files: SkyFileItem[] = []; - for (let index = 0; index < files.length; index++) { - files.push({ - file: fileList.item(index), - } as SkyFileItem); + if ('item' in fileList) { + for (let index = 0; index < fileList.length; index++) { + files.push({ + file: fileList.item(index), + } as SkyFileItem); + } } const processedFiles = this.#fileAttachmentService.checkFiles( diff --git a/libs/components/forms/src/lib/modules/file-attachment/file-drop/fixtures/reactive-file-drop.component.fixture.html b/libs/components/forms/src/lib/modules/file-attachment/file-drop/fixtures/reactive-file-drop.component.fixture.html new file mode 100644 index 0000000000..812957fab6 --- /dev/null +++ b/libs/components/forms/src/lib/modules/file-attachment/file-drop/fixtures/reactive-file-drop.component.fixture.html @@ -0,0 +1,7 @@ +
+ + + +@for (file of fileDrop.value; track file) { + +} diff --git a/libs/components/forms/src/lib/modules/file-attachment/file-drop/fixtures/reactive-file-drop.component.fixture.ts b/libs/components/forms/src/lib/modules/file-attachment/file-drop/fixtures/reactive-file-drop.component.fixture.ts new file mode 100644 index 0000000000..e0120919e0 --- /dev/null +++ b/libs/components/forms/src/lib/modules/file-attachment/file-drop/fixtures/reactive-file-drop.component.fixture.ts @@ -0,0 +1,42 @@ +import { Component } from '@angular/core'; +import { + FormBuilder, + FormControl, + FormGroup, + FormsModule, + ReactiveFormsModule, + Validators, +} from '@angular/forms'; + +import { SkyFileItem } from '../../shared/file-item'; +import { SkyFileDropModule } from '../file-drop.module'; +import { SkyFileLink } from '../file-link'; + +@Component({ + imports: [SkyFileDropModule, FormsModule, ReactiveFormsModule], + selector: 'sky-file-drop-reactive-test', + standalone: true, + templateUrl: './reactive-file-drop.component.fixture.html', +}) +export class ReactiveFileDropTestComponent { + public formGroup: FormGroup; + public fileDrop: FormControl; + public labelText: string | undefined; + + constructor(formBuilder: FormBuilder) { + this.fileDrop = new FormControl(undefined, Validators.required); + this.formGroup = formBuilder.group({ + fileDrop: this.fileDrop, + }); + } + + public deleteFile(file: SkyFileItem | SkyFileLink): void { + const index = this.fileDrop.value.indexOf(file); + if (index !== -1) { + this.fileDrop.value?.splice(index, 1); + } + if (this.fileDrop.value.length === 0) { + this.fileDrop.setValue(null); + } + } +} From dbee98772688d19a3cb365b7a5f2e72a555365a5 Mon Sep 17 00:00:00 2001 From: Sandhya Raja Sabeson Date: Wed, 8 Jan 2025 10:16:16 -0500 Subject: [PATCH 06/10] midway --- .../file-drop/file-drop.component.spec.ts | 155 +++++++++++++++--- .../file-drop/file-drop.component.ts | 39 ++--- .../reactive-file-drop.component.fixture.html | 6 +- 3 files changed, 157 insertions(+), 43 deletions(-) diff --git a/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.spec.ts b/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.spec.ts index 5a22d08b65..4910d48dfe 100644 --- a/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.spec.ts +++ b/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.spec.ts @@ -1,6 +1,13 @@ +/* eslint-disable @typescript-eslint/no-empty-function */ + +/* eslint-disable @typescript-eslint/explicit-function-return-type */ + +/* eslint-disable @typescript-eslint/no-explicit-any */ + /* eslint-disable complexity */ import { Component, DebugElement } from '@angular/core'; import { ComponentFixture, TestBed, fakeAsync } from '@angular/core/testing'; +import { Validators } from '@angular/forms'; import { By } from '@angular/platform-browser'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { SkyAppTestUtility, expect, expectAsync } from '@skyux-sdk/testing'; @@ -16,8 +23,9 @@ import { SkyFileDropChange } from './file-drop-change'; import { SkyFileDropComponent } from './file-drop.component'; import { SkyFileDropModule } from './file-drop.module'; import { SkyFileLink } from './file-link'; +import { ReactiveFileDropTestComponent } from './fixtures/reactive-file-drop.component.fixture'; -describe('File drop component', () => { +fdescribe('File drop component', () => { /** Simple test component with tabIndex */ @Component({ imports: [SkyFileDropModule], @@ -323,7 +331,11 @@ describe('File drop component', () => { expect(dragOverPropStopped).toBe(true); } - function triggerDrop(files: any, dropDebugEl: DebugElement): void { + function triggerDrop( + fixture: ComponentFixture, + files: any, + dropDebugEl: DebugElement, + ): void { let dropPropStopped = false; let dropPreventDefault = false; const fileLength = files ? files.length : 0; @@ -505,7 +517,7 @@ describe('File drop component', () => { testClick(false); }); - it('should load and emit files on file change event', () => { + it('should load and emit files on file change event', async () => { let filesChangedActual: SkyFileDropChange | undefined; componentInstance.filesChanged.subscribe( @@ -513,6 +525,7 @@ describe('File drop component', () => { ); setupStandardFileChangeEvent(); + await fixture.whenStable(); expect(filesChangedActual?.files.length).toBe(2); expect(filesChangedActual?.files[0].url).toBe('url'); @@ -527,7 +540,7 @@ describe('File drop component', () => { expect(liveAnnouncerSpy.calls.count()).toBe(2); }); - it('should load and emit files on file change event when file reader has an error and aborts', () => { + fit('should load and emit files on file change event when file reader has an error and aborts', async () => { let filesChangedActual: SkyFileDropChange | undefined; componentInstance.filesChanged.subscribe( @@ -550,20 +563,26 @@ describe('File drop component', () => { size: 3000, }, ]); - fixture.detectChanges(); + await fixture.whenStable(); fileReaderSpy.abortCallbacks[0](); + fixture.detectChanges(); + await fixture.whenStable(); + fileReaderSpy.loadCallbacks[1]({ target: { result: 'anotherUrl', }, }); + fixture.detectChanges(); + await fixture.whenStable(); fileReaderSpy.errorCallbacks[2](); fixture.detectChanges(); + await fixture.whenStable(); expect(filesChangedActual?.files.length).toBe(1); expect(filesChangedActual?.files[0].url).toBe('anotherUrl'); @@ -602,7 +621,7 @@ describe('File drop component', () => { expect(inputEl.nativeElement.getAttribute('accept')).toBe('image/png'); }); - it('should allow the user to specify a min file size', () => { + it('should allow the user to specify a min file size', async () => { let filesChangedActual: SkyFileDropChange | undefined; componentInstance.filesChanged.subscribe( @@ -613,6 +632,7 @@ describe('File drop component', () => { fixture.detectChanges(); setupStandardFileChangeEvent(); + await fixture.whenStable(); expect(filesChangedActual?.rejectedFiles.length).toBe(1); expect(filesChangedActual?.rejectedFiles[0].file.name).toBe('foo.txt'); @@ -629,7 +649,7 @@ describe('File drop component', () => { expect(liveAnnouncerSpy.calls.count()).toBe(1); }); - it('should respect a default min file size of 0', () => { + it('should respect a default min file size of 0', async () => { let filesChangedActual: SkyFileDropChange | undefined; componentInstance.filesChanged.subscribe( @@ -637,6 +657,7 @@ describe('File drop component', () => { ); const spy = setupStandardFileChangeEvent(); + await fixture.whenStable(); expect(filesChangedActual?.rejectedFiles.length).toBe(0); expect(filesChangedActual?.files.length).toBe(2); @@ -660,6 +681,7 @@ describe('File drop component', () => { fixture.detectChanges(); setupStandardFileChangeEvent(undefined, spy); + await fixture.whenStable(); expect(filesChangedActual?.rejectedFiles.length).toBe(1); expect(filesChangedActual?.rejectedFiles[0].file.name).toBe('foo.txt'); @@ -683,6 +705,7 @@ describe('File drop component', () => { fixture.detectChanges(); setupStandardFileChangeEvent(undefined, spy); + await fixture.whenStable(); expect(filesChangedActual?.rejectedFiles.length).toBe(0); expect(filesChangedActual?.files.length).toBe(2); @@ -699,7 +722,7 @@ describe('File drop component', () => { expect(liveAnnouncerSpy.calls.count()).toBe(2); }); - it('should allow the user to specify a max file size', () => { + it('should allow the user to specify a max file size', async () => { let filesChangedActual: SkyFileDropChange | undefined; componentInstance.filesChanged.subscribe( @@ -710,6 +733,7 @@ describe('File drop component', () => { fixture.detectChanges(); setupStandardFileChangeEvent(); + await fixture.whenStable(); expect(filesChangedActual?.rejectedFiles.length).toBe(1); expect(filesChangedActual?.rejectedFiles[0].file.name).toBe('woo.txt'); @@ -726,7 +750,7 @@ describe('File drop component', () => { expect(liveAnnouncerSpy.calls.count()).toBe(1); }); - it('should respect a default max file size of 500000', () => { + it('should respect a default max file size of 500000', async () => { let filesChangedActual: SkyFileDropChange | undefined; componentInstance.filesChanged.subscribe( @@ -734,6 +758,7 @@ describe('File drop component', () => { ); const spy = setupStandardFileChangeEvent(); + await fixture.whenStable(); expect(filesChangedActual?.rejectedFiles.length).toBe(0); expect(filesChangedActual?.files.length).toBe(2); @@ -757,6 +782,7 @@ describe('File drop component', () => { fixture.detectChanges(); setupStandardFileChangeEvent(undefined, spy); + await fixture.whenStable(); expect(filesChangedActual?.rejectedFiles.length).toBe(1); expect(filesChangedActual?.rejectedFiles[0].file.name).toBe('woo.txt'); @@ -780,6 +806,7 @@ describe('File drop component', () => { fixture.detectChanges(); setupStandardFileChangeEvent(undefined, spy); + await fixture.whenStable(); expect(filesChangedActual?.rejectedFiles.length).toBe(0); expect(filesChangedActual?.files.length).toBe(2); @@ -796,7 +823,7 @@ describe('File drop component', () => { expect(liveAnnouncerSpy.calls.count()).toBe(2); }); - it('should allow the user to specify a validation function', () => { + it('should allow the user to specify a validation function', async () => { let filesChangedActual: SkyFileDropChange | undefined; componentInstance.filesChanged.subscribe( @@ -818,6 +845,7 @@ describe('File drop component', () => { fixture.detectChanges(); setupStandardFileChangeEvent(); + await fixture.whenStable(); expect(filesChangedActual?.rejectedFiles.length).toBe(1); expect(filesChangedActual?.rejectedFiles[0].file.name).toBe('woo.txt'); @@ -834,7 +862,7 @@ describe('File drop component', () => { expect(liveAnnouncerSpy.calls.count()).toBe(1); }); - it('should allow the user to specify accepted types', () => { + it('should allow the user to specify accepted types', async () => { let filesChangedActual: SkyFileDropChange | undefined; componentInstance.filesChanged.subscribe( @@ -846,6 +874,7 @@ describe('File drop component', () => { fixture.detectChanges(); setupStandardFileChangeEvent(); + await fixture.whenStable(); expect(filesChangedActual?.rejectedFiles.length).toBe(1); expect(filesChangedActual?.rejectedFiles[0].file.name).toBe('woo.txt'); @@ -862,7 +891,7 @@ describe('File drop component', () => { expect(liveAnnouncerSpy.calls.count()).toBe(1); }); - it('should reject a file with no type when accepted types are defined', () => { + it('should reject a file with no type when accepted types are defined', async () => { let filesChangedActual: SkyFileDropChange | undefined; componentInstance.filesChanged.subscribe( @@ -886,6 +915,7 @@ describe('File drop component', () => { ]; setupStandardFileChangeEvent(files); + await fixture.whenStable(); expect(filesChangedActual?.rejectedFiles.length).toBe(2); expect(filesChangedActual?.rejectedFiles[1].file.name).toBe('woo.txt'); @@ -901,7 +931,7 @@ describe('File drop component', () => { expect(liveAnnouncerSpy.calls.count()).toBe(0); }); - it('should allow the user to specify accepted type with wildcards', () => { + it('should allow the user to specify accepted type with wildcards', async () => { let filesChangedActual: SkyFileDropChange | undefined; componentInstance.filesChanged.subscribe( @@ -913,6 +943,7 @@ describe('File drop component', () => { fixture.detectChanges(); setupStandardFileChangeEvent(); + await fixture.whenStable(); expect(filesChangedActual?.rejectedFiles.length).toBe(0); @@ -929,7 +960,7 @@ describe('File drop component', () => { expect(liveAnnouncerSpy.calls.count()).toBe(2); }); - it('should load files and set classes on drag and drop', () => { + it('should load files and set classes on drag and drop', async () => { let filesChangedActual: SkyFileDropChange | undefined; componentInstance.filesChanged.subscribe( @@ -966,7 +997,7 @@ describe('File drop component', () => { validateDropClasses(true, false, dropElWrapper); - triggerDrop(files, dropDebugEl); + triggerDrop(fixture, files, dropDebugEl); validateDropClasses(false, false, dropElWrapper); @@ -977,6 +1008,7 @@ describe('File drop component', () => { }); fixture.detectChanges(); + await fixture.whenStable(); expect(filesChangedActual?.rejectedFiles.length).toBe(0); expect(filesChangedActual?.files.length).toBe(1); @@ -1044,7 +1076,7 @@ describe('File drop component', () => { triggerDragOver(undefined, dropDebugEl); validateDropClasses(true, false, dropElWrapper); - triggerDrop(invalidFiles, dropDebugEl); + triggerDrop(fixture, invalidFiles, dropDebugEl); validateDropClasses(false, false, dropElWrapper); }, ); @@ -1072,7 +1104,7 @@ describe('File drop component', () => { triggerDragEnter('sky-drop', dropDebugEl); triggerDragOver(files, dropDebugEl); - triggerDrop(files, dropDebugEl); + triggerDrop(fixture, files, dropDebugEl); expect(fileReaderSpy.loadCallbacks.length).toBe(2); }); @@ -1099,7 +1131,7 @@ describe('File drop component', () => { triggerDragEnter('sky-drop', dropDebugEl); triggerDragOver(files, dropDebugEl); - triggerDrop(files, dropDebugEl); + triggerDrop(fixture, files, dropDebugEl); expect(fileReaderSpy.loadCallbacks.length).toBe(0); }); @@ -1124,7 +1156,7 @@ describe('File drop component', () => { triggerDragEnter('sky-drop', dropDebugEl); triggerDragOver(files, dropDebugEl); - triggerDrop(files, dropDebugEl); + triggerDrop(fixture, files, dropDebugEl); expect(fileReaderSpy.loadCallbacks.length).toBe(0); }); @@ -1361,4 +1393,89 @@ describe('File drop component', () => { helpController.expectCurrentHelpKey('helpKey.html'); }); + + describe('File drop reactive component', () => { + let fixture: ComponentFixture; + + beforeEach(() => { + fixture = TestBed.createComponent(ReactiveFileDropTestComponent); + fixture.detectChanges(); + }); + + it('should mark control as touched on file `drop` event', () => { + expect(fixture.componentInstance.fileDrop.touched).toBeFalse(); + const dropEl = fixture.debugElement.query(By.css('.sky-file-drop')); + + triggerDrop(fixture, [], dropEl); + expect(fixture.componentInstance.fileDrop.touched).toBeTrue(); + }); + + it('should mark control as touched on file drop clicked', () => { + expect(fixture.componentInstance.fileDrop.touched).toBeFalse(); + const dropEl = fixture.nativeElement.querySelector('.sky-file-drop'); + + dropEl.click(); + fixture.detectChanges(); + + expect(fixture.componentInstance.fileDrop.touched).toBeTrue(); + }); + + it('should mark control as touched on link added', () => { + expect(fixture.componentInstance.fileDrop.touched).toBeFalse(); + const linkButton = fixture.debugElement.query( + By.css('.sky-file-drop-link button'), + ); + const linkEl = fixture.debugElement.query( + By.css('.sky-file-drop-link input'), + ); + + linkEl.triggerEventHandler('input', { target: { value: 'link.url' } }); + fixture.detectChanges(); + + linkButton.nativeElement.click(); + fixture.detectChanges(); + + expect(fixture.componentInstance.fileDrop.touched).toBeTrue(); + }); + + it('should mark control as touched on link blur', () => { + expect(fixture.componentInstance.fileDrop.touched).toBeFalse(); + const linkEl = fixture.nativeElement.querySelector( + '.sky-file-drop-link input', + ); + + SkyAppTestUtility.fireDomEvent(linkEl, 'blur'); + fixture.detectChanges(); + + expect(fixture.componentInstance.fileDrop.touched).toBeTrue(); + }); + + it('should set file drop to required using form control', () => { + fixture.componentInstance.labelText = 'File Drop'; + fixture.componentInstance.fileDrop.addValidators(Validators.required); + fixture.detectChanges(); + + const label = fixture.nativeElement.querySelector( + '.sky-file-drop-label-text', + ); + + expect(label.classList.contains('sky-control-label-required')).toBeTrue(); + }); + + it('should set file drop value using form control', () => { + const file: SkyFileItem = { + file: new File([], 'foo.bar', { type: 'image/png' }), + url: 'foo.bar.bar', + }; + + const link: SkyFileLink = { + url: 'foo.foo', + }; + + fixture.componentInstance.fileDrop.setValue([file, link]); + fixture.detectChanges(); + + expect(fixture.componentInstance.fileDrop.value.length).toBe(2); + }); + }); }); diff --git a/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.ts b/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.ts index 5daf3f7d35..e5931731f5 100644 --- a/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.ts +++ b/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.ts @@ -18,7 +18,11 @@ import { NgControl, Validators, } from '@angular/forms'; -import { SkyIdModule, SkyLiveAnnouncerService } from '@skyux/core'; +import { + SkyFileReaderService, + SkyIdModule, + SkyLiveAnnouncerService, +} from '@skyux/core'; import { SkyIdService } from '@skyux/core'; import { SkyHelpInlineModule } from '@skyux/help-inline'; import { SkyLibResourcesService } from '@skyux/i18n'; @@ -415,6 +419,7 @@ export class SkyFileDropComponent implements OnDestroy, ControlValueAccessor { event.preventDefault(); this.uploadLink({ url: this.linkUrl } as SkyFileLink); this.linkUrl = undefined; + this.#notifyTouched?.(); } protected uploadLink(file: SkyFileLink): void { @@ -468,20 +473,21 @@ export class SkyFileDropComponent implements OnDestroy, ControlValueAccessor { this.#emitFileChangeEvent(totalFiles, rejectedFileArray, validFileArray); } - #loadFile( + readonly #fileReaderSvc = inject(SkyFileReaderService); + + async #loadFile( fileDrop: SkyFileDropComponent, file: SkyFileItem, validFileArray: SkyFileItem[], rejectedFileArray: SkyFileItem[], totalFiles: number, - ): void { - const reader = new FileReader(); - - reader.addEventListener('load', (event: any) => { - file.url = event.target.result; + ): Promise { + try { validFileArray.push(file); this.#_uploadedFiles?.push(file); this.#notifyChange?.(this.#_uploadedFiles); + file.url = await this.#fileReaderSvc.readAsDataURL(file.file); + fileDrop.#emitFileChangeEvent( totalFiles, rejectedFileArray, @@ -491,27 +497,14 @@ export class SkyFileDropComponent implements OnDestroy, ControlValueAccessor { 'skyux_file_attachment_file_upload_file_added', file.file.name, ); - }); - - reader.addEventListener('error', () => { - fileDrop.#filesRejected( - file, - validFileArray, - rejectedFileArray, - totalFiles, - ); - }); - - reader.addEventListener('abort', () => { + } catch { fileDrop.#filesRejected( file, validFileArray, rejectedFileArray, totalFiles, ); - }); - - reader.readAsDataURL(file.file); + } } #handleFiles(fileList?: FileList | null | SkyFileItem[]): void { @@ -549,7 +542,7 @@ export class SkyFileDropComponent implements OnDestroy, ControlValueAccessor { totalFiles, ); } else { - this.#loadFile( + void this.#loadFile( this, file, validFileArray, diff --git a/libs/components/forms/src/lib/modules/file-attachment/file-drop/fixtures/reactive-file-drop.component.fixture.html b/libs/components/forms/src/lib/modules/file-attachment/file-drop/fixtures/reactive-file-drop.component.fixture.html index 812957fab6..fface28401 100644 --- a/libs/components/forms/src/lib/modules/file-attachment/file-drop/fixtures/reactive-file-drop.component.fixture.html +++ b/libs/components/forms/src/lib/modules/file-attachment/file-drop/fixtures/reactive-file-drop.component.fixture.html @@ -1,5 +1,9 @@
- + @for (file of fileDrop.value; track file) { From 5167763a6f59a2e7068dc6ea60d8a65adcf13c2a Mon Sep 17 00:00:00 2001 From: Sandhya Raja Sabeson Date: Wed, 8 Jan 2025 11:36:09 -0500 Subject: [PATCH 07/10] unit tests whoop --- .../file-attachment.component.spec.ts | 1 + .../file-drop/file-drop.component.spec.ts | 164 ++++++++++-------- .../file-drop/file-drop.component.ts | 6 +- 3 files changed, 95 insertions(+), 76 deletions(-) diff --git a/libs/components/forms/src/lib/modules/file-attachment/file-attachment/file-attachment.component.spec.ts b/libs/components/forms/src/lib/modules/file-attachment/file-attachment/file-attachment.component.spec.ts index ddf247ffe3..7116bfada7 100644 --- a/libs/components/forms/src/lib/modules/file-attachment/file-attachment/file-attachment.component.spec.ts +++ b/libs/components/forms/src/lib/modules/file-attachment/file-attachment/file-attachment.component.spec.ts @@ -1,3 +1,4 @@ +/* eslint-disable complexity */ import { DebugElement } from '@angular/core'; import { ComponentFixture, diff --git a/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.spec.ts b/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.spec.ts index 4910d48dfe..339f6d54d5 100644 --- a/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.spec.ts +++ b/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.spec.ts @@ -15,6 +15,7 @@ import { SkyIdService, SkyLiveAnnouncerService } from '@skyux/core'; import { SkyHelpTestingController, SkyHelpTestingModule, + provideSkyFileReaderTesting, } from '@skyux/core/testing'; import { SkyFileItem } from '../shared/file-item'; @@ -25,7 +26,7 @@ import { SkyFileDropModule } from './file-drop.module'; import { SkyFileLink } from './file-link'; import { ReactiveFileDropTestComponent } from './fixtures/reactive-file-drop.component.fixture'; -fdescribe('File drop component', () => { +describe('File drop component', () => { /** Simple test component with tabIndex */ @Component({ imports: [SkyFileDropModule], @@ -540,7 +541,7 @@ fdescribe('File drop component', () => { expect(liveAnnouncerSpy.calls.count()).toBe(2); }); - fit('should load and emit files on file change event when file reader has an error and aborts', async () => { + it('should load and emit files on file change event when file reader has an error and aborts', async () => { let filesChangedActual: SkyFileDropChange | undefined; componentInstance.filesChanged.subscribe( @@ -563,22 +564,13 @@ fdescribe('File drop component', () => { size: 3000, }, ]); - fixture.detectChanges(); - await fixture.whenStable(); fileReaderSpy.abortCallbacks[0](); - - fixture.detectChanges(); - await fixture.whenStable(); - fileReaderSpy.loadCallbacks[1]({ target: { result: 'anotherUrl', }, }); - fixture.detectChanges(); - await fixture.whenStable(); - fileReaderSpy.errorCallbacks[2](); fixture.detectChanges(); @@ -1393,89 +1385,115 @@ fdescribe('File drop component', () => { helpController.expectCurrentHelpKey('helpKey.html'); }); +}); - describe('File drop reactive component', () => { - let fixture: ComponentFixture; +describe('File drop reactive component', () => { + let fixture: ComponentFixture; - beforeEach(() => { - fixture = TestBed.createComponent(ReactiveFileDropTestComponent); - fixture.detectChanges(); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [SkyFileDropModule], + providers: [provideSkyFileReaderTesting()], }); + fixture = TestBed.createComponent(ReactiveFileDropTestComponent); + fixture.detectChanges(); + }); - it('should mark control as touched on file `drop` event', () => { - expect(fixture.componentInstance.fileDrop.touched).toBeFalse(); - const dropEl = fixture.debugElement.query(By.css('.sky-file-drop')); + it('should mark control as touched on file `drop` event', () => { + expect(fixture.componentInstance.fileDrop.touched).toBeFalse(); + const dropEl = fixture.debugElement.query(By.css('.sky-file-drop')); - triggerDrop(fixture, [], dropEl); - expect(fixture.componentInstance.fileDrop.touched).toBeTrue(); - }); + const dropEvent = { + dataTransfer: {}, + stopPropagation: function (): void {}, + preventDefault: function (): void {}, + }; - it('should mark control as touched on file drop clicked', () => { - expect(fixture.componentInstance.fileDrop.touched).toBeFalse(); - const dropEl = fixture.nativeElement.querySelector('.sky-file-drop'); + dropEl.triggerEventHandler('drop', dropEvent); + expect(fixture.componentInstance.fileDrop.touched).toBeTrue(); + }); - dropEl.click(); - fixture.detectChanges(); + it('should mark control as touched on file drop clicked', () => { + expect(fixture.componentInstance.fileDrop.touched).toBeFalse(); + const dropEl = fixture.nativeElement.querySelector('.sky-file-drop'); - expect(fixture.componentInstance.fileDrop.touched).toBeTrue(); - }); + dropEl.click(); + fixture.detectChanges(); - it('should mark control as touched on link added', () => { - expect(fixture.componentInstance.fileDrop.touched).toBeFalse(); - const linkButton = fixture.debugElement.query( - By.css('.sky-file-drop-link button'), - ); - const linkEl = fixture.debugElement.query( - By.css('.sky-file-drop-link input'), - ); + expect(fixture.componentInstance.fileDrop.touched).toBeTrue(); + }); - linkEl.triggerEventHandler('input', { target: { value: 'link.url' } }); - fixture.detectChanges(); + it('should mark control as touched on link added', () => { + expect(fixture.componentInstance.fileDrop.touched).toBeFalse(); + const linkButton = fixture.debugElement.query( + By.css('.sky-file-drop-link button'), + ); + const linkEl = fixture.debugElement.query( + By.css('.sky-file-drop-link input'), + ); - linkButton.nativeElement.click(); - fixture.detectChanges(); + linkEl.triggerEventHandler('input', { target: { value: 'link.url' } }); + fixture.detectChanges(); - expect(fixture.componentInstance.fileDrop.touched).toBeTrue(); - }); + linkButton.nativeElement.click(); + fixture.detectChanges(); - it('should mark control as touched on link blur', () => { - expect(fixture.componentInstance.fileDrop.touched).toBeFalse(); - const linkEl = fixture.nativeElement.querySelector( - '.sky-file-drop-link input', - ); + expect(fixture.componentInstance.fileDrop.touched).toBeTrue(); + }); - SkyAppTestUtility.fireDomEvent(linkEl, 'blur'); - fixture.detectChanges(); + it('should mark control as touched on link blur', () => { + expect(fixture.componentInstance.fileDrop.touched).toBeFalse(); + const linkEl = fixture.nativeElement.querySelector( + '.sky-file-drop-link input', + ); - expect(fixture.componentInstance.fileDrop.touched).toBeTrue(); - }); + SkyAppTestUtility.fireDomEvent(linkEl, 'blur'); + fixture.detectChanges(); - it('should set file drop to required using form control', () => { - fixture.componentInstance.labelText = 'File Drop'; - fixture.componentInstance.fileDrop.addValidators(Validators.required); - fixture.detectChanges(); + expect(fixture.componentInstance.fileDrop.touched).toBeTrue(); + }); - const label = fixture.nativeElement.querySelector( - '.sky-file-drop-label-text', - ); + it('should set file drop to required using form control', () => { + fixture.componentInstance.labelText = 'File Drop'; + fixture.componentInstance.fileDrop.addValidators(Validators.required); + fixture.detectChanges(); - expect(label.classList.contains('sky-control-label-required')).toBeTrue(); - }); + const label = fixture.nativeElement.querySelector( + '.sky-file-drop-label-text', + ); - it('should set file drop value using form control', () => { - const file: SkyFileItem = { - file: new File([], 'foo.bar', { type: 'image/png' }), - url: 'foo.bar.bar', - }; + expect(label.classList.contains('sky-control-label-required')).toBeTrue(); + }); - const link: SkyFileLink = { - url: 'foo.foo', - }; + it('should set file drop value using form control', async () => { + const file: SkyFileItem = { + file: new File([], 'foo.bar', { type: 'image/png' }), + url: 'foo.bar.bar', + }; - fixture.componentInstance.fileDrop.setValue([file, link]); - fixture.detectChanges(); + const link: SkyFileLink = { + url: 'foo.foo', + }; - expect(fixture.componentInstance.fileDrop.value.length).toBe(2); - }); + fixture.componentInstance.fileDrop.setValue([file, link]); + fixture.detectChanges(); + await fixture.whenStable(); + + expect(fixture.componentInstance.fileDrop.value.length).toBe(2); + }); + + it('should show required error', () => { + fixture.componentInstance.labelText = 'testing'; + fixture.detectChanges(); + const linkInput = fixture.nativeElement.querySelector( + '.sky-file-drop-link input', + ); + SkyAppTestUtility.fireDomEvent(linkInput, 'blur'); + fixture.detectChanges(); + + const requiredError = fixture.nativeElement.querySelector( + "sky-form-error[errorName='required']", + ); + expect(requiredError).toBeVisible(); }); }); diff --git a/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.ts b/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.ts index e5931731f5..3bb783b57d 100644 --- a/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.ts +++ b/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.ts @@ -483,11 +483,9 @@ export class SkyFileDropComponent implements OnDestroy, ControlValueAccessor { totalFiles: number, ): Promise { try { - validFileArray.push(file); - this.#_uploadedFiles?.push(file); - this.#notifyChange?.(this.#_uploadedFiles); file.url = await this.#fileReaderSvc.readAsDataURL(file.file); + validFileArray.push(file); fileDrop.#emitFileChangeEvent( totalFiles, rejectedFileArray, @@ -497,6 +495,8 @@ export class SkyFileDropComponent implements OnDestroy, ControlValueAccessor { 'skyux_file_attachment_file_upload_file_added', file.file.name, ); + this.#_uploadedFiles?.push(file); + this.#notifyChange?.(this.#_uploadedFiles); } catch { fileDrop.#filesRejected( file, From bb9fc167cb03f4148852339f187cabd470806be0 Mon Sep 17 00:00:00 2001 From: Sandhya Raja Sabeson Date: Wed, 8 Jan 2025 11:42:48 -0500 Subject: [PATCH 08/10] cleanup --- .../file-attachment.component.spec.ts | 1 - .../file-drop/file-drop.component.spec.ts | 7 -- .../file-drop/file-drop.component.ts | 86 ++++++++++--------- 3 files changed, 44 insertions(+), 50 deletions(-) diff --git a/libs/components/forms/src/lib/modules/file-attachment/file-attachment/file-attachment.component.spec.ts b/libs/components/forms/src/lib/modules/file-attachment/file-attachment/file-attachment.component.spec.ts index 7116bfada7..ddf247ffe3 100644 --- a/libs/components/forms/src/lib/modules/file-attachment/file-attachment/file-attachment.component.spec.ts +++ b/libs/components/forms/src/lib/modules/file-attachment/file-attachment/file-attachment.component.spec.ts @@ -1,4 +1,3 @@ -/* eslint-disable complexity */ import { DebugElement } from '@angular/core'; import { ComponentFixture, diff --git a/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.spec.ts b/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.spec.ts index 339f6d54d5..8b09c113b1 100644 --- a/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.spec.ts +++ b/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.spec.ts @@ -1,10 +1,3 @@ -/* eslint-disable @typescript-eslint/no-empty-function */ - -/* eslint-disable @typescript-eslint/explicit-function-return-type */ - -/* eslint-disable @typescript-eslint/no-explicit-any */ - -/* eslint-disable complexity */ import { Component, DebugElement } from '@angular/core'; import { ComponentFixture, TestBed, fakeAsync } from '@angular/core/testing'; import { Validators } from '@angular/forms'; diff --git a/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.ts b/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.ts index 3bb783b57d..fdf6085a6c 100644 --- a/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.ts +++ b/libs/components/forms/src/lib/modules/file-attachment/file-drop/file-drop.component.ts @@ -81,48 +81,6 @@ const MIN_FILE_SIZE_DEFAULT = 0; ], }) export class SkyFileDropComponent implements OnDestroy, ControlValueAccessor { - #notifyTouched: (() => void) | undefined; - #notifyChange: - | ((_: (SkyFileItem | SkyFileLink)[] | undefined | null) => void) - | undefined; - #_uploadedFiles: (SkyFileItem | SkyFileLink)[] | undefined | null = []; - - protected ngControl = inject(NgControl, { optional: true }); - protected get isRequired(): boolean { - return ( - this.required || - (this.ngControl?.control?.hasValidator(Validators.required) ?? false) - ); - } - - public writeValue(value: any): void { - if (value instanceof Array) { - value.forEach((file, index) => { - if ('url' in file) { - if (!('file' in file)) { - this.uploadLink(file as SkyFileLink); - value.splice(index, 1); - } - } - }); - this.#handleFiles(value as SkyFileItem[]); - } - } - - public registerOnChange(fn: any): void { - this.#notifyChange = fn; - } - - public registerOnTouched(fn: () => void): void { - this.#notifyTouched = fn; - } - - constructor() { - if (this.ngControl) { - this.ngControl.valueAccessor = this; - } - } - /** * Fires when users add or remove files. */ @@ -305,20 +263,57 @@ export class SkyFileDropComponent implements OnDestroy, ControlValueAccessor { #_minFileSize = MIN_FILE_SIZE_DEFAULT; + #notifyTouched: (() => void) | undefined; + #notifyChange: + | ((_: (SkyFileItem | SkyFileLink)[] | undefined | null) => void) + | undefined; + #_uploadedFiles: (SkyFileItem | SkyFileLink)[] | undefined | null = []; + readonly #fileAttachmentService = inject(SkyFileAttachmentService); readonly #liveAnnouncerSvc = inject(SkyLiveAnnouncerService); readonly #resourcesSvc = inject(SkyLibResourcesService); readonly #idSvc = inject(SkyIdService); protected errorId = this.#idSvc.generateId(); + + protected ngControl = inject(NgControl, { optional: true }); + protected rejectedFiles: SkyFileItem[] = []; + constructor() { + if (this.ngControl) { + this.ngControl.valueAccessor = this; + } + } + public ngOnDestroy(): void { this.filesChanged.complete(); this.linkChanged.complete(); this.linkInputBlur.complete(); } + public writeValue(value: any): void { + if (value instanceof Array) { + value.forEach((file, index) => { + if ('url' in file) { + if (!('file' in file)) { + this.uploadLink(file as SkyFileLink); + value.splice(index, 1); + } + } + }); + this.#handleFiles(value as SkyFileItem[]); + } + } + + public registerOnChange(fn: any): void { + this.#notifyChange = fn; + } + + public registerOnTouched(fn: () => void): void { + this.#notifyTouched = fn; + } + public dropClicked(): void { if (!this.noClick && this.inputEl) { this.#notifyTouched?.(); @@ -437,6 +432,13 @@ export class SkyFileDropComponent implements OnDestroy, ControlValueAccessor { this.linkInputBlur.emit(); } + protected get isRequired(): boolean { + return ( + this.required || + (this.ngControl?.control?.hasValidator(Validators.required) ?? false) + ); + } + #announceState(resourceString: string, ...args: any[]): void { this.#resourcesSvc .getString(resourceString, ...args) From 19bb797d23f0ac88d1e0c07d26363ebd6c37aaf9 Mon Sep 17 00:00:00 2001 From: Sandhya Raja Sabeson Date: Wed, 8 Jan 2025 12:20:04 -0500 Subject: [PATCH 09/10] code example basic --- .../forms/file-drop/basic/demo.component.html | 33 ++++----- .../forms/file-drop/basic/demo.component.ts | 67 +++++++++---------- 2 files changed, 51 insertions(+), 49 deletions(-) diff --git a/apps/code-examples/src/app/code-examples/forms/file-drop/basic/demo.component.html b/apps/code-examples/src/app/code-examples/forms/file-drop/basic/demo.component.html index 2b8efa66d6..b3c416fba9 100644 --- a/apps/code-examples/src/app/code-examples/forms/file-drop/basic/demo.component.html +++ b/apps/code-examples/src/app/code-examples/forms/file-drop/basic/demo.component.html @@ -1,17 +1,20 @@ - -@for (file of allItems; track file) { +
+ + + +@for (file of fileDrop.value; track file) { } + +
Value: {{ fileDrop.value | json }}
diff --git a/apps/code-examples/src/app/code-examples/forms/file-drop/basic/demo.component.ts b/apps/code-examples/src/app/code-examples/forms/file-drop/basic/demo.component.ts index a02c10b309..9d14114d50 100644 --- a/apps/code-examples/src/app/code-examples/forms/file-drop/basic/demo.component.ts +++ b/apps/code-examples/src/app/code-examples/forms/file-drop/basic/demo.component.ts @@ -1,17 +1,27 @@ +import { CommonModule } from '@angular/common'; import { Component } from '@angular/core'; import { - SkyFileDropChange, - SkyFileDropModule, - SkyFileItem, - SkyFileLink, -} from '@skyux/forms'; + FormBuilder, + FormControl, + FormGroup, + FormsModule, + ReactiveFormsModule, + Validators, +} from '@angular/forms'; +import { SkyFileDropModule, SkyFileItem, SkyFileLink } from '@skyux/forms'; import { SkyStatusIndicatorModule } from '@skyux/indicators'; @Component({ standalone: true, selector: 'app-demo', templateUrl: './demo.component.html', - imports: [SkyFileDropModule, SkyStatusIndicatorModule], + imports: [ + SkyFileDropModule, + SkyStatusIndicatorModule, + CommonModule, + FormsModule, + ReactiveFormsModule, + ], }) export class DemoComponent { protected acceptedTypes = 'image/png,image/jpeg'; @@ -22,27 +32,29 @@ export class DemoComponent { protected labelText = 'Logo image'; protected maxFileSize = 5242880; protected rejectedFiles: SkyFileItem[] = []; - protected required = true; protected stacked = 'true'; - #filesToUpload: SkyFileItem[] = []; - #linksToUpload: SkyFileLink[] = []; + protected formGroup: FormGroup; + protected fileDrop: FormControl< + (SkyFileItem | SkyFileLink)[] | null | undefined + >; - protected deleteFile(file: SkyFileItem | SkyFileLink): void { - this.#removeFromArray(this.allItems, file); - this.#removeFromArray(this.#filesToUpload, file); - this.#removeFromArray(this.#linksToUpload, file); + constructor(formBuilder: FormBuilder) { + this.fileDrop = new FormControl(undefined, Validators.required); + this.formGroup = formBuilder.group({ + fileDrop: this.fileDrop, + }); } - protected onFilesChanged(change: SkyFileDropChange): void { - this.#filesToUpload = this.#filesToUpload.concat(change.files); - this.rejectedFiles = change.rejectedFiles; - this.allItems = this.allItems.concat(change.files); - } + protected deleteFile(file: SkyFileItem | SkyFileLink): void { + const index = this.fileDrop.value?.indexOf(file); - protected onLinkChanged(change: SkyFileLink): void { - this.#linksToUpload = this.#linksToUpload.concat(change); - this.allItems = this.allItems.concat(change); + if (index !== undefined && index !== -1) { + this.fileDrop.value?.splice(index, 1); + } + if (this.fileDrop.value?.length === 0) { + this.fileDrop.setValue(null); + } } protected validateFile(file: SkyFileItem): string | undefined { @@ -50,17 +62,4 @@ export class DemoComponent { ? 'Upload a file that does not begin with the letter "a"' : undefined; } - - #removeFromArray( - items: (SkyFileItem | SkyFileLink)[], - obj: SkyFileItem | SkyFileLink, - ): void { - if (items) { - const index = items.indexOf(obj); - - if (index !== -1) { - items.splice(index, 1); - } - } - } } From d5064c9e5dba98312c827547dd6927a22c9bf8b0 Mon Sep 17 00:00:00 2001 From: Sandhya Raja Sabeson Date: Wed, 8 Jan 2025 12:51:06 -0500 Subject: [PATCH 10/10] code examples cleanup --- .../forms/file-attachment/basic/demo.component.ts | 11 +---------- .../forms/file-drop/basic/demo.component.html | 2 -- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/apps/code-examples/src/app/code-examples/forms/file-attachment/basic/demo.component.ts b/apps/code-examples/src/app/code-examples/forms/file-attachment/basic/demo.component.ts index 8a6ac8bf96..ad57411cfe 100644 --- a/apps/code-examples/src/app/code-examples/forms/file-attachment/basic/demo.component.ts +++ b/apps/code-examples/src/app/code-examples/forms/file-attachment/basic/demo.component.ts @@ -41,7 +41,7 @@ export class DemoComponent { attachment: FormControl; }>; - protected maxFileSize = 40; + protected maxFileSize = 4000000; constructor() { this.attachment = new FormControl(undefined, { @@ -51,15 +51,6 @@ export class DemoComponent { this.formGroup = inject(FormBuilder).group({ attachment: this.attachment, }); - - this.attachment.setValue({ - file: { - name: 'aFile.txt', - type: '', - size: 100000000, - }, - url: './file/path', - } as SkyFileItem); } protected onFileClick($event: SkyFileAttachmentClick): void { diff --git a/apps/code-examples/src/app/code-examples/forms/file-drop/basic/demo.component.html b/apps/code-examples/src/app/code-examples/forms/file-drop/basic/demo.component.html index b3c416fba9..a2ca0b83cb 100644 --- a/apps/code-examples/src/app/code-examples/forms/file-drop/basic/demo.component.html +++ b/apps/code-examples/src/app/code-examples/forms/file-drop/basic/demo.component.html @@ -16,5 +16,3 @@ @for (file of fileDrop.value; track file) { } - -
Value: {{ fileDrop.value | json }}