From 911bfae4acca0a2204d3b9b31d6a2151022dca08 Mon Sep 17 00:00:00 2001 From: Matthew de Nobrega Date: Wed, 18 May 2016 17:27:26 +0200 Subject: [PATCH] feat(input): add support for more input attributes (#447) --- src/components/input/input.html | 15 +- src/components/input/input.spec.ts | 371 +++++++++++++++++++++++++++++ src/components/input/input.ts | 16 +- 3 files changed, 396 insertions(+), 6 deletions(-) diff --git a/src/components/input/input.html b/src/components/input/input.html index 5f0f79086fbf..e76d6f8f15eb 100644 --- a/src/components/input/input.html +++ b/src/components/input/input.html @@ -12,11 +12,20 @@ [attr.aria-disabled]="ariaDisabled" [attr.aria-required]="ariaRequired" [attr.aria-invalid]="ariaInvalid" - [id]="inputId" + [attr.autocomplete]="autoComplete" + [autofocus]="autoFocus" [disabled]="disabled" - [required]="required" - [spellcheck]="spellcheck" + [id]="inputId" + [attr.list]="list" + [attr.max]="max" [attr.maxlength]="maxLength" + [attr.min]="min" + [attr.minlength]="minLength" + [readonly]="readOnly" + [required]="required" + [spellcheck]="spellCheck" + [attr.step]="step" + [attr.tabindex]="tabIndex" [type]="type" (focus)="handleFocus($event)" (blur)="handleBlur($event)" diff --git a/src/components/input/input.spec.ts b/src/components/input/input.spec.ts index 11246dd183c9..0b29ba0e72e0 100644 --- a/src/components/input/input.spec.ts +++ b/src/components/input/input.spec.ts @@ -283,6 +283,368 @@ export function main() { })(); }); }); + + it('supports the autoComplete attribute', () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then(fixture => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('autocomplete')).toBeNull(); + + fixture.componentInstance.autoComplete = 'on'; + fixture.detectChanges(); + expect(el.getAttribute('autocomplete')).toEqual('on'); + })(); + }); + }); + + it('supports the autoComplete attribute as an unbound attribute', () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then(fixture => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('autocomplete')).toEqual(''); + })(); + }); + }); + + it('supports the autoComplete attribute as an unbound value attribute', () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then(fixture => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('autocomplete')).toEqual('name'); + })(); + }); + }); + + it('supports the autoFocus attribute', () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then(fixture => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('autofocus')).toBeNull(); + + fixture.componentInstance.autoFocus = true; + fixture.detectChanges(); + expect(el.getAttribute('autofocus')).toEqual(''); + })(); + }); + }); + + it('supports the autoFocus attribute as an unbound attribute', () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then(fixture => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('autofocus')).toEqual(''); + })(); + }); + }); + + it('supports the disabled attribute', () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then(fixture => { + fakeAsync(() => { + fixture.componentInstance.disabled = false; + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('disabled')).toEqual(null); + + fixture.componentInstance.disabled = true; + fixture.detectChanges(); + expect(el.getAttribute('disabled')).toEqual(''); + })(); + }); + }); + + it('supports the disabled attribute as an unbound attribute', () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then(fixture => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('disabled')).toEqual(''); + })(); + }); + }); + + it('supports the list attribute', () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then(fixture => { + fakeAsync(() => { + fixture.componentInstance.disabled = false; + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('list')).toEqual(null); + + fixture.componentInstance.list = 'datalist-id'; + fixture.detectChanges(); + expect(el.getAttribute('list')).toEqual('datalist-id'); + })(); + }); + }); + + it('supports the max attribute', () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then(fixture => { + fakeAsync(() => { + fixture.componentInstance.disabled = false; + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('max')).toEqual(null); + + fixture.componentInstance.max = 10; + fixture.detectChanges(); + expect(el.getAttribute('max')).toEqual('10'); + + fixture.componentInstance.max = '2000-01-02'; + fixture.detectChanges(); + expect(el.getAttribute('max')).toEqual('2000-01-02'); + })(); + }); + }); + + it('supports the min attribute', () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then(fixture => { + fakeAsync(() => { + fixture.componentInstance.disabled = false; + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('min')).toEqual(null); + + fixture.componentInstance.min = 10; + fixture.detectChanges(); + expect(el.getAttribute('min')).toEqual('10'); + + fixture.componentInstance.min = '2000-01-02'; + fixture.detectChanges(); + expect(el.getAttribute('min')).toEqual('2000-01-02'); + })(); + }); + }); + + it('supports the readOnly attribute', () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then(fixture => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('readonly')).toBeNull(); + + fixture.componentInstance.readOnly = true; + fixture.detectChanges(); + expect(el.getAttribute('readonly')).toEqual(''); + })(); + }); + }); + + it('supports the readOnly attribute as an unbound attribute', () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then(fixture => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('readonly')).toEqual(''); + })(); + }); + }); + + it('supports the required attribute', () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then(fixture => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('required')).toBeNull(); + + fixture.componentInstance.required = true; + fixture.detectChanges(); + expect(el.getAttribute('required')).toEqual(''); + })(); + }); + }); + + it('supports the required attribute as an unbound attribute', () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then(fixture => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('required')).toEqual(''); + })(); + }); + }); + + it('supports the spellCheck attribute', () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then(fixture => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('spellcheck')).toEqual('false'); + + fixture.componentInstance.spellCheck = true; + fixture.detectChanges(); + expect(el.getAttribute('spellcheck')).toEqual('true'); + })(); + }); + }); + + it('supports the spellCheck attribute as an unbound attribute', () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then(fixture => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('spellcheck')).toEqual('true'); + })(); + }); + }); + + it('supports the step attribute', () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then(fixture => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('step')).toEqual(null); + + fixture.componentInstance.step = 0.5; + fixture.detectChanges(); + expect(el.getAttribute('step')).toEqual('0.5'); + })(); + }); + }); + + it('supports the tabIndex attribute', () => { + var template = ''; + + return builder.overrideTemplate(MdInputOptionalAttributeController, template) + .createAsync(MdInputOptionalAttributeController) + .then(fixture => { + fakeAsync(() => { + fixture.detectChanges(); + + let el: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement; + + expect(el).not.toBeNull(); + expect(el.getAttribute('tabindex')).toEqual(null); + + fixture.componentInstance.tabIndex = 1; + fixture.detectChanges(); + expect(el.getAttribute('tabindex')).toEqual('1'); + })(); + }); + }); }); } @@ -443,3 +805,12 @@ class MdInputWithBlurAndFocusEvents { onBlur(event: FocusEvent) {} onFocus(event: FocusEvent) {} } + +@Component({ + selector: 'test-input-controller', + template: ` + + `, + directives: [MdInput] +}) +class MdInputOptionalAttributeController {} diff --git a/src/components/input/input.ts b/src/components/input/input.ts index 33de725e0b65..0b92e5570bb4 100644 --- a/src/components/input/input.ts +++ b/src/components/input/input.ts @@ -135,14 +135,24 @@ export class MdInput implements ControlValueAccessor, AfterContentInit, OnChange */ @Input() align: 'start' | 'end' = 'start'; @Input() dividerColor: 'primary' | 'accent' | 'warn' = 'primary'; - @Input() @BooleanFieldValue() disabled: boolean = false; @Input() @BooleanFieldValue() floatingPlaceholder: boolean = true; @Input() hintLabel: string = ''; + + @Input() autoComplete: string; + @Input() @BooleanFieldValue() autoFocus: boolean = false; + @Input() @BooleanFieldValue() disabled: boolean = false; @Input() id: string = `md-input-${nextUniqueId++}`; - @Input() maxLength: number = null; + @Input() list: string; + @Input() max: string; + @Input() maxLength: number = -1; + @Input() min: string; + @Input() minLength: number; @Input() placeholder: string; + @Input() @BooleanFieldValue() readOnly: boolean = false; @Input() @BooleanFieldValue() required: boolean = false; - @Input() @BooleanFieldValue() spellcheck: boolean = false; + @Input() @BooleanFieldValue() spellCheck: boolean = false; + @Input() step: number; + @Input() tabIndex: number; @Input() type: string = 'text'; private _blurEmitter: EventEmitter = new EventEmitter();