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:
Erik Tiekstra
2021-11-03 12:04:15 +01:00
parent 37cdf9285a
commit e51e13dfa8
17 changed files with 256 additions and 63 deletions

View File

@@ -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();
});
});

View File

@@ -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();
}

View File

@@ -0,0 +1,6 @@
export enum UiTextAreaVariation {
AUTO = 'auto',
S = 's',
M = 'm',
L = 'l',
}

View 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>

View 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;
}
}

View 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();
});
});

View 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>',
});

View 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;
}
}

View 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 {}