-
Notifications
You must be signed in to change notification settings - Fork 467
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix #25782 Edit Contentlet: Create dropzone component
* dev: create dot-drop-zone component * dev: create dot drop zone value accessor directive * dev: allow drop one file * test: setup tests * test: dot-drop-zone component * dev: prevent DragOver behavior to allow drop event * test: fix tests * dev: emit dragStart and DrahStop events * test: add tests for directive * dev: create dotDropZone Value Accesor Story * dev: fix CodeQL warning * dev: fix mimeType Validation * dev: add addon-actions to story-book * feedback: unify allowedExtensions & allowedMimeTypes * feedback: emit error on drop * feedback: rename dot-drop-zone @output * feedback: rename @Ouput fileDrop to fileDropped * dev: add validity object * dev: add NG_VALIDATORS to the DotDropZoneValueAccessorDirective
- Loading branch information
Showing
11 changed files
with
754 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
55 changes: 55 additions & 0 deletions
55
...zone/directive/dot-drop-zone-value-accesor/dot-drop-zone-value-accessor.directive.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import { createDirectiveFactory, SpectatorDirective } from '@ngneat/spectator'; | ||
import { MockComponent } from 'ng-mocks'; | ||
|
||
import { forwardRef } from '@angular/core'; | ||
import { NG_VALUE_ACCESSOR } from '@angular/forms'; | ||
|
||
import { DotDropZoneValueAccessorDirective } from './dot-drop-zone-value-accessor.directive'; | ||
|
||
import { DotDropZoneComponent } from '../../dot-drop-zone.component'; | ||
|
||
describe('DotDropZoneValueAccessorDirective', () => { | ||
let spectator: SpectatorDirective<DotDropZoneValueAccessorDirective>; | ||
|
||
const createDirective = createDirectiveFactory({ | ||
directive: DotDropZoneValueAccessorDirective, | ||
declarations: [MockComponent(DotDropZoneComponent)], | ||
providers: [ | ||
{ | ||
multi: true, | ||
provide: NG_VALUE_ACCESSOR, | ||
useExisting: forwardRef(() => DotDropZoneValueAccessorDirective) | ||
} | ||
] | ||
}); | ||
|
||
describe('Used with dot-drop-zone component', () => { | ||
beforeEach(() => { | ||
spectator = createDirective(` | ||
<dot-drop-zone dotDropZoneValueAccessor> | ||
<div id="dot-drop-zone__content" class="dot-drop-zone__content"> | ||
Content | ||
</div> | ||
</dot-drop-zone>`); | ||
}); | ||
|
||
it('should create', () => { | ||
expect(spectator.directive).toBeTruthy(); | ||
}); | ||
}); | ||
|
||
describe('Used without dot-drop-zone component', () => { | ||
it('should throw error if not inside dot-drop-zone', () => { | ||
expect(() => { | ||
spectator = createDirective(` | ||
<div dotDropZoneValueAccessor> | ||
<div id="dot-drop-zone__content" class="dot-drop-zone__content"> | ||
Content | ||
</div> | ||
</div>`); | ||
}).toThrowError( | ||
'dot-drop-zone-value-accessor can only be used inside of a dot-drop-zone' | ||
); | ||
}); | ||
}); | ||
}); |
122 changes: 122 additions & 0 deletions
122
...e/directive/dot-drop-zone-value-accesor/dot-drop-zone-value-accessor.directive.stories.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import { action } from '@storybook/addon-actions'; | ||
import { moduleMetadata, Story, Meta } from '@storybook/angular'; | ||
|
||
import { CommonModule } from '@angular/common'; | ||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; | ||
import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; | ||
|
||
import { DotDropZoneValueAccessorDirective } from './dot-drop-zone-value-accessor.directive'; | ||
|
||
import { DotDropZoneComponent } from '../../dot-drop-zone.component'; | ||
|
||
/** | ||
* This component is used to test the value accessor directive on Storybook | ||
* | ||
* @class DotDropZoneValueAccessorTestComponent | ||
* @implements {OnInit} | ||
*/ | ||
@Component({ | ||
selector: 'dot-drop-zone-value-accessor', | ||
styles: [ | ||
` | ||
.dot-drop-zone__content { | ||
width: 100%; | ||
height: 200px; | ||
background: #f8f9fa; | ||
display: flex; | ||
justify-content: center; | ||
flex-direction: column; | ||
gap: 1rem; | ||
align-items: center; | ||
border: 1px dashed #ced4da; | ||
border-radius: 5px; | ||
} | ||
` | ||
], | ||
template: ` | ||
<form [formGroup]="myForm"> | ||
<dot-drop-zone | ||
[accept]="accept" | ||
[maxFileSize]="maxFileSize" | ||
formControlName="file" | ||
dotDropZoneValueAccessor | ||
> | ||
<div class="dot-drop-zone__content" id="dot-drop-zone__content"> | ||
Drop files here. | ||
<div *ngIf="accept.length"><strong>Allowed Type:</strong> {{ accept }}</div> | ||
<div *ngIf="maxFileSize"><strong>Max File Size:</strong> {{ maxFileSize }}</div> | ||
</div> | ||
</dot-drop-zone> | ||
</form> | ||
` | ||
}) | ||
class DotDropZoneValueAccessorTestComponent implements OnInit { | ||
@Input() accept: string[]; | ||
@Input() maxFileSize: number; | ||
|
||
@Output() formChanged = new EventEmitter(); | ||
@Output() formErrors = new EventEmitter(); | ||
|
||
myForm: FormGroup; | ||
|
||
constructor(private fb: FormBuilder) {} | ||
|
||
ngOnInit() { | ||
this.myForm = this.fb.group({ | ||
file: null | ||
}); | ||
|
||
this.myForm.valueChanges.subscribe((value) => { | ||
// eslint-disable-next-line no-console | ||
this.formChanged.emit(value); | ||
|
||
if (this.myForm.invalid) { | ||
this.formErrors.emit(this.myForm.errors); | ||
} | ||
}); | ||
} | ||
} | ||
|
||
export default { | ||
title: 'Library/ui/Components/DropZone/ValueAccessor', | ||
component: DotDropZoneValueAccessorTestComponent, | ||
decorators: [ | ||
moduleMetadata({ | ||
imports: [FormsModule, ReactiveFormsModule, CommonModule, DotDropZoneComponent], | ||
declarations: [DotDropZoneValueAccessorDirective] | ||
}) | ||
], | ||
parameters: { | ||
// https://storybook.js.org/docs/6.5/angular/essentials/actions#action-event-handlers | ||
actions: { | ||
// detect if the component is emitting the correct HTML events | ||
handles: ['formChanged', 'formErrors'] | ||
} | ||
} | ||
} as Meta<DotDropZoneComponent>; | ||
|
||
const Template: Story<DotDropZoneComponent> = (args: DotDropZoneComponent) => ({ | ||
props: { | ||
...args, | ||
// https://storybook.js.org/docs/6.5/angular/essentials/actions#action-args | ||
formChanged: action('formChanged'), | ||
formErrors: action('formErrors') | ||
}, | ||
template: ` | ||
<dot-drop-zone-value-accessor | ||
[accept]="accept" | ||
[maxFileSize]="maxFileSize" | ||
(formChanged)="formChanged($event)" | ||
(formErrors)="formErrors($event)" | ||
></dot-drop-zone-value-accessor> | ||
` | ||
}); | ||
|
||
export const Base = Template.bind({}); | ||
|
||
Base.args = { | ||
accept: [], | ||
maxFileSize: 1000000 | ||
}; |
78 changes: 78 additions & 0 deletions
78
...drop-zone/directive/dot-drop-zone-value-accesor/dot-drop-zone-value-accessor.directive.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import { Directive, Host, OnInit, Optional, forwardRef } from '@angular/core'; | ||
import { | ||
AbstractControl, | ||
ControlValueAccessor, | ||
NG_VALUE_ACCESSOR, | ||
Validator, | ||
NG_VALIDATORS, | ||
ValidationErrors | ||
} from '@angular/forms'; | ||
|
||
import { DotDropZoneComponent, DropZoneFileEvent } from '../../dot-drop-zone.component'; | ||
|
||
@Directive({ | ||
selector: '[dotDropZoneValueAccessor]', | ||
providers: [ | ||
{ | ||
provide: NG_VALUE_ACCESSOR, | ||
useExisting: forwardRef(() => DotDropZoneValueAccessorDirective), | ||
multi: true | ||
}, | ||
{ | ||
provide: NG_VALIDATORS, | ||
useExisting: forwardRef(() => DotDropZoneValueAccessorDirective), | ||
multi: true | ||
} | ||
] | ||
}) | ||
export class DotDropZoneValueAccessorDirective implements ControlValueAccessor, Validator, OnInit { | ||
private onChange: (value: File) => void; | ||
private onTouched: () => void; | ||
|
||
constructor(@Optional() @Host() private _dotDropZone: DotDropZoneComponent) { | ||
if (!this._dotDropZone) { | ||
throw new Error( | ||
'dot-drop-zone-value-accessor can only be used inside of a dot-drop-zone' | ||
); | ||
} | ||
} | ||
|
||
ngOnInit() { | ||
this._dotDropZone.fileDropped.subscribe(({ file }: DropZoneFileEvent) => { | ||
this.onChange(file); // Only File | ||
this.onTouched(); | ||
}); | ||
} | ||
|
||
writeValue(_value: unknown) { | ||
/* | ||
We can set a value here by doing this._dotDropZone.setFile(value), if needed | ||
*/ | ||
} | ||
|
||
registerOnChange(fn: (value: unknown) => void) { | ||
this.onChange = fn; | ||
} | ||
|
||
registerOnTouched(fn: () => void) { | ||
this.onTouched = fn; | ||
} | ||
|
||
validate(_control: AbstractControl): ValidationErrors | null { | ||
const validity = this._dotDropZone.validity; | ||
|
||
if (validity.valid) { | ||
return null; | ||
} | ||
|
||
const errors = Object.entries(validity).reduce((acc, [key, value]) => { | ||
if (value === true) { | ||
acc[key] = value; | ||
} | ||
|
||
return acc; | ||
}, {}); | ||
|
||
return errors; | ||
} | ||
} |
1 change: 1 addition & 0 deletions
1
core-web/libs/ui/src/lib/components/dot-drop-zone/dot-drop-zone.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
<ng-content></ng-content> |
Empty file.
Oops, something went wrong.