Skip to content
This repository has been archived by the owner on Oct 7, 2020. It is now read-only.

Commit

Permalink
feat(textfield): Add setValid method to set a custom validity (#190)
Browse files Browse the repository at this point in the history
* feat(textfield): Add `setValid` method, so developers can set custom validity.
* test(textfield): Add initial test coverage
* test: Add test coverage for mdc-textfield-box
* feat(textfield): Add return type to each property

BREAKING CHANGE: `updateErrorState` method was renamed to `setValid` keeping with MDC foundation naming.
  • Loading branch information
trimox authored Sep 16, 2017
1 parent b2ee5c9 commit 8dee576
Show file tree
Hide file tree
Showing 6 changed files with 284 additions and 34 deletions.
4 changes: 2 additions & 2 deletions src/demo-app/components/dialog-demo/dialog-demo.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,13 @@ export class DialogDemoComponent {

showDialogForm() {
// reset error state
this.input.updateErrorState(true);
this.input.setValid(true);
this.dialogForm.show();
}

updateForm() {
// reset error state
this.input.updateErrorState(false);
this.input.setValid(false);
if (!this.userForm.valid) {
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,55 +30,55 @@ <h1 mdc-typography-display1>Text Field</h1>
</thead>
<tbody>
<tr>
<td>@Input() id: string</td>
<td>id: string</td>
<td>Unique id of the element.</td>
</tr>
<tr>
<td>@Input() type: string = 'text'</td>
<td>type: string = 'text'</td>
<td>Input type of textfield (e.g. email, password, url).</td>
</tr>
<tr>
<td>@Input() name: string</td>
<td>name: string</td>
<td>Name of the textfield.</td>
</tr>
<tr>
<td>@Input() value: string</td>
<td>value: string</td>
<td>The input element's value.</td>
</tr>
<tr>
<td>@Input() dense: boolean</td>
<td>dense: boolean</td>
<td>Shrinks the font size and height of the input.</td>
</tr>
<tr>
<td>@Input() fullwidth: boolean</td>
<td>fullwidth: boolean</td>
<td>Use to change element to fullwidth textfield. Not usable with mdc-textfield-box.</td>
</tr>
<tr>
<td>@Input() multiline: boolean</td>
<td>multiline: boolean</td>
<td>Use to allow multiple lines inside the textfield. Not usable with mdc-textfield-box.</td>
</tr>
<tr>
<td>@Input() disabled: boolean</td>
<td>disabled: boolean</td>
<td>Disables the component.</td>
</tr>
<tr>
<td>@Input() required: boolean</td>
<td>required: boolean</td>
<td>Whether the element is required.</td>
</tr>
<tr>
<td>@Input() label: string</td>
<td>label: string</td>
<td>Shown to the user when there's no focus or values.</td>
</tr>
<tr>
<td>@Input() placeholder: string</td>
<td>placeholder: string</td>
<td>Placeholder attribute of the element.</td>
</tr>
<tr>
<td>@Input() tabindex: number</td>
<td>tabindex: number</td>
<td>Tab index of the text element.</td>
</tr>
<tr>
<td>@Input() rows: number</td>
<td>rows: number</td>
<td>Number of rows for this textarea. Not usable with mdc-textfield-box.</td>
</tr>
<tr>
Expand All @@ -98,8 +98,8 @@ <h1 mdc-typography-display1>Text Field</h1>
</thead>
<tbody>
<tr>
<td>updateErrorState(value?: boolean)</td>
<td>Updates the error state either by value passed, or by checking the validity of the input.</td>
<td>setValid(value?: boolean)</td>
<td>Updates input validity using passed value, or if left empty checking the validity of the input.</td>
</tr>
</tbody>
</table>
Expand Down
27 changes: 10 additions & 17 deletions src/lib/textfield/textfield.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ export class MdcTextfieldLabelDirective {
})
export class MdcTextfieldComponent implements AfterViewInit, OnDestroy, ControlValueAccessor {
private type_ = 'text';
private disabled_ = false;
private required_ = false;
private disabled_: boolean = false;
private required_: boolean = false;
private controlValueAccessorChangeFn_: (value: any) => void = (value) => { };
onChange = (_: any) => { };
onTouched = () => { };
Expand All @@ -121,7 +121,7 @@ export class MdcTextfieldComponent implements AfterViewInit, OnDestroy, ControlV
this.required_ = value != null && `${value}` !== 'false';
}
@Input()
get type() { return this.type_; }
get type(): string { return this.type_; }
set type(value: string) {
this.type_ = value || 'text';
this.validateType_();
Expand All @@ -131,7 +131,7 @@ export class MdcTextfieldComponent implements AfterViewInit, OnDestroy, ControlV
}
}
@Input()
get value() { return this.inputText.elementRef.nativeElement.value; }
get value(): string { return this.inputText.elementRef.nativeElement.value; }
set value(value: string) {
if (value !== this.value) {
this.inputText.elementRef.nativeElement.value = value;
Expand Down Expand Up @@ -234,6 +234,7 @@ export class MdcTextfieldComponent implements AfterViewInit, OnDestroy, ControlV
destroy: Function,
isDisabled: Function,
setDisabled: Function,
setValid: Function,
} = new MDCTextfieldFoundation(this._mdcAdapter);

constructor(
Expand Down Expand Up @@ -280,32 +281,24 @@ export class MdcTextfieldComponent implements AfterViewInit, OnDestroy, ControlV
this.onTouched();
}

isDisabled() {
isDisabled(): boolean {
return this._foundation.isDisabled();
}

isBadInput() {
isBadInput(): boolean {
return (this.inputText.elementRef.nativeElement as HTMLInputElement).validity.badInput;
}

focus() {
focus(): void {
this.inputText.focus();
}

updateErrorState(value?: boolean) {
if (value || this.valid) {
this._mdcAdapter.removeClass('mdc-textfield--invalid');
} else {
this._mdcAdapter.addClass('mdc-textfield--invalid');
}
setValid(value?: boolean): void {
this._foundation.setValid(value ? value : this.valid);
}

private isTextarea_() {
let nativeElement = this._root.nativeElement;

// In Universal, we don't have access to `nodeName`, but the same can be achieved with `name`.
// Note that this shouldn't be necessary once Angular switches to an API that resembles the
// DOM closer.
let nodeName = isBrowser ? nativeElement.nodeName : nativeElement.name;
return nodeName ? nodeName.toLowerCase() === 'textarea' : false;
}
Expand Down
70 changes: 70 additions & 0 deletions test/unit/textfield/textarea.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { Component, DebugElement } from '@angular/core';
import { async, ComponentFixture, fakeAsync, flushMicrotasks, TestBed } from '@angular/core/testing';
import { FormControl, FormsModule, NgModel, ReactiveFormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser';

import {
MdcTextareaComponent,
MdcTextfieldModule
} from '../../../src/lib/public_api';

describe('MdcTextareaComponent', () => {
let fixture: ComponentFixture<any>;

beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [MdcTextfieldModule, FormsModule, ReactiveFormsModule],
declarations: [
SimpleTextfield,
]
});
TestBed.compileComponents();
}));

describe('basic behaviors', () => {
let textFieldDebugElement: DebugElement;
let textFieldNativeElement: HTMLElement;
let textFieldInstance: MdcTextareaComponent;
let testComponent: SimpleTextfield;
let inputElement: HTMLInputElement;

beforeEach(() => {
fixture = TestBed.createComponent(SimpleTextfield);
fixture.detectChanges();

textFieldDebugElement = fixture.debugElement.query(By.directive(MdcTextareaComponent));
textFieldNativeElement = textFieldDebugElement.nativeElement;
textFieldInstance = textFieldDebugElement.componentInstance;
testComponent = fixture.debugElement.componentInstance;
});

it('#should apply class multiline on property', () => {
testComponent.isMultiline = true;
fixture.detectChanges();
expect(textFieldDebugElement.nativeElement.classList.contains('mdc-textfield--multiline')).toBe(true);
});
});
});

/** Simple component for testing. */
@Component({
template:
`
<mdc-textarea
[(ngModel)]="comments"
label="Comments"
[required]="isRequired"
[multiline]="isMultiline"
[disabled]="isDisabled">
</mdc-textarea>
<p mdc-textfield-helptext
[validation]="true"
[persistent]="false">Comments are required</p>
`,
})
class SimpleTextfield {
username: string = '';
isDisabled: boolean = false;
isRequired: boolean = false;
isMultiline: boolean = false;
}
76 changes: 76 additions & 0 deletions test/unit/textfield/textfield-box.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { Component, DebugElement } from '@angular/core';
import { async, ComponentFixture, fakeAsync, flushMicrotasks, TestBed } from '@angular/core/testing';
import { FormControl, FormsModule, NgModel, ReactiveFormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser';

import {
MdcTextfieldBoxComponent,
MdcTextfieldInputDirective,
MdcTextfieldModule
} from '../../../src/lib/public_api';

describe('MdcTextfieldBoxComponent', () => {
let fixture: ComponentFixture<any>;

beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [MdcTextfieldModule, FormsModule, ReactiveFormsModule],
declarations: [
SimpleTextfield,
]
});
TestBed.compileComponents();
}));

describe('basic behaviors', () => {
let textFieldDebugElement: DebugElement;
let textFieldNativeElement: HTMLElement;
let textFieldInstance: MdcTextfieldBoxComponent;
let testComponent: SimpleTextfield;
let inputElement: HTMLInputElement;

beforeEach(() => {
fixture = TestBed.createComponent(SimpleTextfield);
fixture.detectChanges();

textFieldDebugElement = fixture.debugElement.query(By.directive(MdcTextfieldBoxComponent));
textFieldNativeElement = textFieldDebugElement.nativeElement;
textFieldInstance = textFieldDebugElement.componentInstance;
testComponent = fixture.debugElement.componentInstance;
inputElement = textFieldInstance.inputText.elementRef.nativeElement;
});

it('#should have mdc-textfield--box by default', () => {
expect(textFieldDebugElement.nativeElement.classList)
.toContain('mdc-textfield--box', 'Expected to have mdc-textfield--box class');
});

it('#should preserve the user-provided id', () => {
fixture.detectChanges();
expect(inputElement.id).toBe('simple-check');
});
});
});

/** Simple component for testing. */
@Component({
template:
`
<mdc-textfield-box
[id]="boxId"
[(ngModel)]="username"
label="Username"
[required]="isRequired"
[disabled]="isDisabled">
</mdc-textfield-box>
<p mdc-textfield-helptext
[validation]="true"
[persistent]="false">Username is required</p>
`,
})
class SimpleTextfield {
boxId: string = 'simple-check';
username: string = '';
isDisabled: boolean = false;
isRequired: boolean = false;
}
Loading

0 comments on commit 8dee576

Please sign in to comment.