Skip to content

Commit

Permalink
fix(textarea): fix textarea update issue (#107)
Browse files Browse the repository at this point in the history
Closes #106
  • Loading branch information
kyubisation authored Jul 16, 2019
1 parent 16010ad commit 103923f
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { dispatchFakeEvent } from './dispatch-events';
* @param value Value to be set on the input.
* @param element Element onto which to set the value.
*/
export function typeInElement(value: string, element: HTMLInputElement) {
export function typeInElement(value: string, element: HTMLInputElement | HTMLTextAreaElement) {
element.focus();
element.value = value;
dispatchFakeEvent(element, 'input');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<textarea
#textarea
[id]="inputId"
[value]="textContent"
[disabled]="disabled"
[readonly]="readonly"
[placeholder]="placeholder"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { TextFieldModule } from '@angular/cdk/text-field';
import { ChangeDetectionStrategy, Component, DebugElement } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser';
import { configureTestSuite } from 'ng-bullet';
Expand All @@ -11,9 +11,16 @@ import { TextareaComponent } from './textarea.component';

@Component({
selector: 'sbb-textarea-test',
template:
'<sbb-textarea [(ngModel)]="textArea1" [minlength]="minlength" [maxlength]="maxlength"' +
'[required]="required" [readonly]="readonly" [disabled]="disabled"></sbb-textarea>'
template: `
<sbb-textarea
[(ngModel)]="textArea1"
[minlength]="minlength"
[maxlength]="maxlength"
[required]="required"
[readonly]="readonly"
[disabled]="disabled"
></sbb-textarea>
`
})
class TextareaTestComponent {
required: boolean;
Expand Down Expand Up @@ -57,8 +64,6 @@ describe('TextareaComponent behaviour', () => {
TestBed.configureTestingModule({
imports: [TextFieldModule, FormsModule],
declarations: [TextareaTestComponent, TextareaComponent]
}).overrideComponent(TextareaComponent, {
set: { changeDetection: ChangeDetectionStrategy.Default }
});
});

Expand All @@ -71,9 +76,7 @@ describe('TextareaComponent behaviour', () => {

it('should be required', () => {
component.required = true;
const textarea = innerComponent.query(
e => e.nativeElement.nodeName.toLowerCase() === 'textarea'
);
const textarea = innerComponent.query(By.css('textarea'));
typeInElement('', textarea.nativeElement);
fixture.detectChanges();
expect(innerComponent.classes['ng-invalid'] && innerComponent.classes['ng-dirty']).toBeTruthy();
Expand Down Expand Up @@ -114,6 +117,26 @@ describe('TextareaComponent behaviour', () => {
.borderTopColor
).toBe('rgb(235, 0, 0)');
});

// See https://github.com/SchweizerischeBundesbahnen/sbb-angular/issues/106
it('should update the inner value twice', async () => {
const textarea = fixture.debugElement.query(By.css('textarea'))
.nativeElement as HTMLTextAreaElement;
typeInElement('test1', textarea);
fixture.detectChanges();
expect(component.textArea1).toEqual('test1');
component.textArea1 = 'test2';
fixture.detectChanges();
await fixture.whenStable();
expect(textarea.value).toEqual('test2');
typeInElement('test3', textarea);
fixture.detectChanges();
expect(component.textArea1).toEqual('test3');
component.textArea1 = 'test4';
fixture.detectChanges();
await fixture.whenStable();
expect(textarea.value).toEqual('test4');
});
});

describe('TextareaComponent digits counter', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';
import { CdkTextareaAutosize } from '@angular/cdk/text-field';
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ElementRef,
forwardRef,
HostBinding,
Input,
Expand All @@ -29,64 +31,92 @@ let nextId = 0;
changeDetection: ChangeDetectionStrategy.OnPush
})
export class TextareaComponent implements ControlValueAccessor {
/** The value of the textarea. */
@Input()
get value() {
return this._textarea ? this._textarea.nativeElement.value : '';
}
set value(value: string) {
if (this._textarea) {
this._textarea.nativeElement.value = value;
}
}

/**
* Text content in a textarea
*/
textContent: string;
/**
* Class property that represents the autosize textarea
* Text content in a textarea.
* @deprecated Use value instead.
*/
get textContent(): string {
return this.value;
}
set textContent(value: string) {
this.value = value;
}
/** Class property that represents the autosize textarea. */
matTextareaAutosize = true;

/**
* Class property that disables the textarea status
*/
/** Class property that disables the textarea status. */
@HostBinding('class.disabled')
@Input()
disabled: boolean;
/**
* Class property that sets readonly the textarea content
*/
@Input() readonly: boolean;
/**
* Class property that sets the maxlength of the textarea content
*/
@Input() maxlength: number;
/**
* Class property that represents an observer on the number of digits in a textarea
*/
counterObserver$: BehaviorSubject<number> = new BehaviorSubject<number>(this.maxlength);
/**
* Class property that sets the minlength of the textarea content
*/
@Input() minlength: number;
/**
* Class property that sets required the textarea
*/
@Input() required = false;
/**
* Placeholder value for the textarea.
*/
get disabled(): boolean {
return this._disabled;
}
set disabled(value: boolean) {
this._disabled = coerceBooleanProperty(value);
this._changeDetector.markForCheck();
}
private _disabled = false;
/** Class property that sets readonly the textarea content. */
@Input()
get readonly(): boolean {
return this._readonly;
}
set readonly(value: boolean) {
this._readonly = coerceBooleanProperty(value);
}
private _readonly = false;
/** Class property that sets the maxlength of the textarea content. */
@Input()
get maxlength(): number {
return this._maxlength;
}
set maxlength(value: number) {
this._maxlength = coerceNumberProperty(value);
}
private _maxlength: number;
/** Class property that sets the minlength of the textarea content. */
@Input()
get minlength(): number {
return this._minlength;
}
set minlength(value: number) {
this._minlength = coerceNumberProperty(value);
}
private _minlength: number;
/** Class property that sets required the textarea. */
@Input()
get required(): boolean {
return this._required;
}
set required(value: boolean) {
this._required = coerceBooleanProperty(value);
}
private _required = false;
/** Placeholder value for the textarea. */
@Input() placeholder = '';
/**
* Identifier of textarea
*/
/** Identifier of textarea. */
@Input() inputId = `sbb-textarea-input-id-${++nextId}`;
/**
* Class property that automatically resize a textarea to fit its content
*/
/** @docs-private */
@ViewChild('textarea', { static: true }) _textarea: ElementRef<HTMLTextAreaElement>;
/** Class property that automatically resize a textarea to fit its content. */
@ViewChild('autosize', { static: true }) autosize: CdkTextareaAutosize;
/**
* Class property that represents the focused class status
*/
/** Class property that represents the focused class status. */
@HostBinding('class.focused') focusedClass: boolean;
/**
* Class property that represents a change caused by a new digit in a textarea
*/
/** Class property that represents an observer on the number of digits in a textarea. */
counterObserver$: BehaviorSubject<number> = new BehaviorSubject<number>(this.maxlength);
/** Class property that represents a change caused by a new digit in a textarea. */
propagateChange: any = () => {};
/**
* The registered callback function called when a blur event occurs on the input element.
*/
/** The registered callback function called when a blur event occurs on the input element. */
onTouched = () => {};

constructor(private _changeDetector: ChangeDetectorRef, private _ngZone: NgZone) {}
Expand All @@ -112,9 +142,8 @@ export class TextareaComponent implements ControlValueAccessor {
}

writeValue(newValue: any) {
this.textContent = newValue == null ? '' : newValue;
this.updateDigitsCounter(this.textContent);
this._changeDetector.markForCheck();
this.value = newValue == null ? '' : newValue;
this.updateDigitsCounter(this.value);
}

registerOnChange(fn: (_: any) => void) {
Expand All @@ -135,7 +164,6 @@ export class TextareaComponent implements ControlValueAccessor {

setDisabledState(disabled: boolean) {
this.disabled = disabled;
this._changeDetector.markForCheck();
}
/**
* Method that updates the max number of digits available in the textarea content
Expand Down

0 comments on commit 103923f

Please sign in to comment.