feat(ui): Added textarea component to ui-libs. (TV-849)
Squashed commit of the following: commit 61ecaf467c63cdd10f4d66131b105d3b12d60e49 Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Wed Nov 3 11:14:19 2021 +0100 Removed unused input commit 2254c1dd547cbf3639e5232fe6d8e657491a369c Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Wed Nov 3 11:12:38 2021 +0100 Implemented textarea component inside ui-libs
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/* tslint:disable:no-unused-variable */
|
||||
import { FormCheckboxComponent } from './form-checkbox.component';
|
||||
import { CheckboxComponent } from './checkbox.component';
|
||||
|
||||
export class MockInjector {
|
||||
get = jest.fn();
|
||||
@@ -14,10 +14,10 @@ export class MockChangeDetectorRef {
|
||||
checkNoChanges = jest.fn();
|
||||
}
|
||||
|
||||
describe('FormCheckboxComponent', () => {
|
||||
let component: FormCheckboxComponent;
|
||||
describe('CheckboxComponent', () => {
|
||||
let component: CheckboxComponent;
|
||||
it('should create', () => {
|
||||
component = new FormCheckboxComponent(new MockInjector(), new MockChangeDetectorRef());
|
||||
component = new CheckboxComponent(new MockInjector(), new MockChangeDetectorRef());
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
Input,
|
||||
OnChanges,
|
||||
Output,
|
||||
SimpleChanges,
|
||||
} from '@angular/core';
|
||||
import { ControlValueAccessor, NgControl, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { uuid } from '@utils/uuid.util';
|
||||
@@ -42,7 +41,7 @@ export class CheckboxComponent implements AfterViewInit, ControlValueAccessor, O
|
||||
@Input() uiId: string = uuid();
|
||||
@Input() uiName: string;
|
||||
@Input() uiAnnounceIfOptional: boolean = false;
|
||||
@Output() uiOnChange: EventEmitter<any> = new EventEmitter();
|
||||
@Output() uiOnChange: EventEmitter<boolean> = new EventEmitter();
|
||||
|
||||
name: string | number;
|
||||
|
||||
@@ -79,7 +78,7 @@ export class CheckboxComponent implements AfterViewInit, ControlValueAccessor, O
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
ngOnChanges(): void {
|
||||
const ngControl: NgControl = this.injector.get(NgControl, null);
|
||||
if (ngControl) {
|
||||
this.name = ngControl.name;
|
||||
@@ -96,7 +95,7 @@ export class CheckboxComponent implements AfterViewInit, ControlValueAccessor, O
|
||||
}
|
||||
}
|
||||
|
||||
writeValue(value: any): void {
|
||||
writeValue(value: boolean): void {
|
||||
this._value = value;
|
||||
this.changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
6
libs/ui/src/textarea/textarea-variation.enum.ts
Normal file
6
libs/ui/src/textarea/textarea-variation.enum.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export enum UiTextAreaVariation {
|
||||
AUTO = 'auto',
|
||||
S = 's',
|
||||
M = 'm',
|
||||
L = 'l',
|
||||
}
|
||||
26
libs/ui/src/textarea/textarea.component.html
Normal file
26
libs/ui/src/textarea/textarea.component.html
Normal file
@@ -0,0 +1,26 @@
|
||||
<div
|
||||
class="ui-textarea"
|
||||
[ngClass]="{'ui-textarea--invalid': uiInvalid && uiValidationMessage, 'ui-textarea--with-counter': uiMaxLength}"
|
||||
>
|
||||
<digi-form-textarea
|
||||
class="ui-textarea__textarea"
|
||||
[afId]="uiId"
|
||||
[afName]="uiName"
|
||||
[afLabel]="uiLabel"
|
||||
[afLabelDescription]="uiDescription"
|
||||
[afVariation]="uiVariation"
|
||||
[afRequired]="uiRequired"
|
||||
[afAnnounceIfOptional]="uiAnnounceIfOptional"
|
||||
[afMaxlength]="uiMaxLength"
|
||||
[afMinlength]="uiMinLength"
|
||||
[afValue]="currentValue"
|
||||
[afValidation]="uiInvalid ? 'error' : 'neutral'"
|
||||
(afOnInput)="checkForChange($event.detail.target.value)"
|
||||
></digi-form-textarea>
|
||||
<span class="ui-textarea__counter" *ngIf="uiMaxLength">{{charactersLeft}} tecken kvar</span>
|
||||
<div class="ui-textarea__validation" aria-atomic="true" role="alert">
|
||||
<digi-form-validation-message *ngIf="uiInvalid && uiValidationMessage" af-variation="error"
|
||||
>{{uiValidationMessage}}</digi-form-validation-message
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
25
libs/ui/src/textarea/textarea.component.scss
Normal file
25
libs/ui/src/textarea/textarea.component.scss
Normal file
@@ -0,0 +1,25 @@
|
||||
.ui-textarea {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr max-content;
|
||||
grid-template-areas:
|
||||
'textarea textarea'
|
||||
'validation counter';
|
||||
|
||||
&--invalid,
|
||||
&--with-counter {
|
||||
gap: var(--digi--layout--gutter--s);
|
||||
}
|
||||
|
||||
&__textarea {
|
||||
grid-area: textarea;
|
||||
}
|
||||
|
||||
&__counter {
|
||||
grid-area: counter;
|
||||
color: var(--digi--typography--color--text--dimmed);
|
||||
font-size: var(--digi--typography--font-size--s);
|
||||
}
|
||||
&__validation {
|
||||
grid-area: validation;
|
||||
}
|
||||
}
|
||||
23
libs/ui/src/textarea/textarea.component.spec.ts
Normal file
23
libs/ui/src/textarea/textarea.component.spec.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
/* tslint:disable:no-unused-variable */
|
||||
import { TextareaComponent } from './textarea.component';
|
||||
|
||||
export class MockInjector {
|
||||
get = jest.fn();
|
||||
}
|
||||
|
||||
// tslint:disable-next-line: max-classes-per-file
|
||||
export class MockChangeDetectorRef {
|
||||
markForCheck = jest.fn();
|
||||
detach = jest.fn();
|
||||
detectChanges = jest.fn();
|
||||
reattach = jest.fn();
|
||||
checkNoChanges = jest.fn();
|
||||
}
|
||||
|
||||
describe('TextareaComponent', () => {
|
||||
let component: TextareaComponent;
|
||||
it('should create', () => {
|
||||
component = new TextareaComponent(new MockInjector(), new MockChangeDetectorRef());
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
16
libs/ui/src/textarea/textarea.component.stories.ts
Normal file
16
libs/ui/src/textarea/textarea.component.stories.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { TextareaComponent } from './textarea.component';
|
||||
import { UiTextareaModule } from './textarea.module';
|
||||
|
||||
export default { title: 'Textarea', component: TextareaComponent };
|
||||
|
||||
const componentModule = {
|
||||
moduleMetadata: {
|
||||
imports: [ReactiveFormsModule, UiTextareaModule],
|
||||
},
|
||||
};
|
||||
|
||||
export const standard = () => ({
|
||||
...componentModule,
|
||||
template: '<ui-textarea></ui-textarea>',
|
||||
});
|
||||
101
libs/ui/src/textarea/textarea.component.ts
Normal file
101
libs/ui/src/textarea/textarea.component.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
import {
|
||||
AfterViewInit,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
EventEmitter,
|
||||
Injector,
|
||||
Input,
|
||||
OnChanges,
|
||||
Output,
|
||||
} from '@angular/core';
|
||||
import { ControlValueAccessor, NgControl, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { uuid } from '@utils/uuid.util';
|
||||
import { UiTextAreaVariation } from './textarea-variation.enum';
|
||||
|
||||
/**
|
||||
* A textarea input. Implemented with control value accessor
|
||||
*
|
||||
* ## Usage
|
||||
* ``import {UiTextareaModule} from '@ui/textarea/textarea.module';``
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ui-textarea',
|
||||
templateUrl: './textarea.component.html',
|
||||
styleUrls: ['./textarea.component.scss'],
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: TextareaComponent,
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class TextareaComponent implements AfterViewInit, ControlValueAccessor, OnChanges {
|
||||
@Input() uiId: string = uuid();
|
||||
@Input() uiName: string;
|
||||
@Input() uiLabel: string = '';
|
||||
@Input() uiDescription: string;
|
||||
@Input() uiVariation: UiTextAreaVariation = UiTextAreaVariation.M;
|
||||
@Input() uiRequired: boolean;
|
||||
@Input() uiAnnounceIfOptional: boolean = false;
|
||||
@Input() uiMaxLength: number;
|
||||
@Input() uiMinLength: number;
|
||||
@Input() uiInvalid: boolean;
|
||||
@Input() uiValidationMessage: string;
|
||||
@Output() uiOnChange: EventEmitter<string> = new EventEmitter();
|
||||
|
||||
name: string | number;
|
||||
|
||||
onTouched: () => {};
|
||||
private onChange: (value: any) => {};
|
||||
private _value: string = '';
|
||||
|
||||
constructor(private injector: Injector, private changeDetectorRef: ChangeDetectorRef) {}
|
||||
|
||||
get currentValue(): string {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
get charactersLeft(): number {
|
||||
return this.uiMaxLength - this.currentValue.length;
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
const ngControl: NgControl = this.injector.get(NgControl, null);
|
||||
if (ngControl) {
|
||||
this.name = ngControl.name;
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(): void {
|
||||
const ngControl: NgControl = this.injector.get(NgControl, null);
|
||||
if (ngControl) {
|
||||
this.name = ngControl.name;
|
||||
}
|
||||
}
|
||||
|
||||
checkForChange(value: string): void {
|
||||
if (this._value !== value) {
|
||||
if (this.onChange) {
|
||||
this.onChange(value);
|
||||
}
|
||||
this._value = value;
|
||||
this.uiOnChange.emit(value);
|
||||
}
|
||||
}
|
||||
|
||||
writeValue(value: string): void {
|
||||
this._value = value;
|
||||
this.changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
registerOnChange(fn: (value: string) => {}) {
|
||||
this.onChange = fn;
|
||||
}
|
||||
|
||||
registerOnTouched(fn: () => {}) {
|
||||
this.onTouched = fn;
|
||||
}
|
||||
}
|
||||
11
libs/ui/src/textarea/textarea.module.ts
Normal file
11
libs/ui/src/textarea/textarea.module.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||
import { TextareaComponent } from './textarea.component';
|
||||
|
||||
@NgModule({
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
imports: [CommonModule],
|
||||
declarations: [TextareaComponent],
|
||||
exports: [TextareaComponent],
|
||||
})
|
||||
export class UiTextareaModule {}
|
||||
Reference in New Issue
Block a user