From 2055653bba31c490f724ffe9c0bbfd773b1dc5f8 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Tue, 10 Jan 2017 14:08:02 +0100 Subject: [PATCH 1/4] feat(input): option to imperatively float placeholder Refactors the `[floatingPlaceholder]` input to be able to specifiy whether the label should always float or not. There are three options for the `floatingPlaceholder` input binding now - If set to `true`, the placeholder will *always* float - If set to `false`, the placeholder will *never* float - If set to `null`, the placeholder will float if text is entered. Closes #2466 --- src/demo-app/input/input-container-demo.html | 3 +- src/lib/input/input-container.html | 2 +- src/lib/input/input-container.spec.ts | 85 +++++++++++++++++++- src/lib/input/input-container.ts | 20 ++++- 4 files changed, 103 insertions(+), 7 deletions(-) diff --git a/src/demo-app/input/input-container-demo.html b/src/demo-app/input/input-container-demo.html index d393a5b6459e..007a583dc878 100644 --- a/src/demo-app/input/input-container-demo.html +++ b/src/demo-app/input/input-container-demo.html @@ -180,7 +180,8 @@

Textarea

- Check to make floating label: + Toggle Floating Label + diff --git a/src/lib/input/input-container.html b/src/lib/input/input-container.html index 50b76067a9c2..ce2bc748ff30 100644 --- a/src/lib/input/input-container.html +++ b/src/lib/input/input-container.html @@ -7,7 +7,7 @@

Textarea

- Toggle Floating Label - + + Auto Float + Always Float + Never Float + +

+ +

- +

diff --git a/src/demo-app/input/input-container-demo.ts b/src/demo-app/input/input-container-demo.ts index 38edcda046a2..9c773f00e5ee 100644 --- a/src/demo-app/input/input-container-demo.ts +++ b/src/demo-app/input/input-container-demo.ts @@ -11,9 +11,9 @@ let max = 5; styleUrls: ['input-container-demo.css'], }) export class InputContainerDemo { + floatingLabel: string = 'auto'; dividerColor: boolean; requiredField: boolean; - floatingLabel: boolean; ctrlDisabled = false; name: string; diff --git a/src/lib/input/input-container-errors.ts b/src/lib/input/input-container-errors.ts index 90a9337b2a65..00864cdfec94 100644 --- a/src/lib/input/input-container-errors.ts +++ b/src/lib/input/input-container-errors.ts @@ -28,3 +28,9 @@ export class MdInputContainerMissingMdInputError extends MdError { 'to the native input or textarea element?'); } } + +export class MdInputContainerFloatingPlaceholderInvalidError extends MdError { + constructor(value: string) { + super(`The value "${value}" for the floatingPlaceholder input is not valid.`); + } +} diff --git a/src/lib/input/input-container.html b/src/lib/input/input-container.html index ce2bc748ff30..c457a7d8173c 100644 --- a/src/lib/input/input-container.html +++ b/src/lib/input/input-container.html @@ -9,7 +9,7 @@ [attr.for]="_mdInputChild.id" [class.md-empty]="_mdInputChild.empty && !_shouldAlwaysFloat" [class.md-focused]="_mdInputChild.focused" - [class.md-float]="floatingPlaceholder" + [class.md-float]="_canPlaceholderFloat" [class.md-accent]="dividerColor == 'accent'" [class.md-warn]="dividerColor == 'warn'" *ngIf="_hasPlaceholder()"> diff --git a/src/lib/input/input-container.spec.ts b/src/lib/input/input-container.spec.ts index 0b244626850e..eea31125d4e8 100644 --- a/src/lib/input/input-container.spec.ts +++ b/src/lib/input/input-container.spec.ts @@ -62,8 +62,8 @@ describe('MdInputContainer', function () { let inputContainer = fixture.debugElement.query(By.directive(MdInputContainer)) .componentInstance as MdInputContainer; - expect(inputContainer.floatingPlaceholder).toBe(true, - 'Expected MdInputContainer to default to having floating placeholders turned on'); + expect(inputContainer.floatingPlaceholder).toBe('auto', + 'Expected MdInputContainer to set floatingLabel to auto by default.'); }); it('should not be treated as empty if type is date', @@ -483,20 +483,20 @@ describe('MdInputContainer', function () { let fixture = TestBed.createComponent(MdInputContainerWithDynamicPlaceholder); fixture.detectChanges(); - let inputEl = fixture.debugElement.query(By.css('input')); + let inputEl = fixture.debugElement.query(By.css('input')).nativeElement; let labelEl = fixture.debugElement.query(By.css('label')).nativeElement; expect(labelEl.classList).not.toContain('md-empty'); expect(labelEl.classList).toContain('md-float'); - fixture.componentInstance.shouldFloat = null; + fixture.componentInstance.shouldFloat = 'auto'; fixture.detectChanges(); expect(labelEl.classList).toContain('md-empty'); expect(labelEl.classList).toContain('md-float'); // Update the value of the input. - inputEl.nativeElement.value = 'Text'; + inputEl.value = 'Text'; // Fake behavior of the `(input)` event which should trigger a change detection. fixture.detectChanges(); @@ -509,7 +509,7 @@ describe('MdInputContainer', function () { let fixture = TestBed.createComponent(MdInputContainerWithDynamicPlaceholder); fixture.detectChanges(); - let inputEl = fixture.debugElement.query(By.css('input')); + let inputEl = fixture.debugElement.query(By.css('input')).nativeElement; let labelEl = fixture.debugElement.query(By.css('label')).nativeElement; expect(labelEl.classList).not.toContain('md-empty'); @@ -518,7 +518,7 @@ describe('MdInputContainer', function () { fixture.detectChanges(); // Update the value of the input. - inputEl.nativeElement.value = 'Text'; + inputEl.value = 'Text'; // Fake behavior of the `(input)` event which should trigger a change detection. fixture.detectChanges(); @@ -531,17 +531,17 @@ describe('MdInputContainer', function () { it('should never float the placeholder when floatingPlaceholder is set to false', () => { let fixture = TestBed.createComponent(MdInputContainerWithDynamicPlaceholder); - fixture.componentInstance.shouldFloat = false; + fixture.componentInstance.shouldFloat = 'never'; fixture.detectChanges(); - let inputEl = fixture.debugElement.query(By.css('input')); + let inputEl = fixture.debugElement.query(By.css('input')).nativeElement; let labelEl = fixture.debugElement.query(By.css('label')).nativeElement; expect(labelEl.classList).toContain('md-empty'); expect(labelEl.classList).not.toContain('md-float'); // Update the value of the input. - inputEl.nativeElement.value = 'Text'; + inputEl.value = 'Text'; // Fake behavior of the `(input)` event which should trigger a change detection. fixture.detectChanges(); @@ -741,7 +741,7 @@ class MdInputContainerWithValueBinding { @Component({ template: ` - + ` @@ -755,7 +755,7 @@ class MdInputContainerWithStaticPlaceholder {} ` }) class MdInputContainerWithDynamicPlaceholder { - shouldFloat: boolean = true; + shouldFloat: string = 'always'; } @Component({ diff --git a/src/lib/input/input-container.ts b/src/lib/input/input-container.ts index 814b754e15b9..9e3f59cf4566 100644 --- a/src/lib/input/input-container.ts +++ b/src/lib/input/input-container.ts @@ -20,7 +20,7 @@ import { MdInputContainerUnsupportedTypeError, MdInputContainerPlaceholderConflictError, MdInputContainerDuplicatedHintError, - MdInputContainerMissingMdInputError + MdInputContainerMissingMdInputError, MdInputContainerFloatingPlaceholderInvalidError } from './input-container-errors'; @@ -38,6 +38,9 @@ const MD_INPUT_INVALID_TYPES = [ 'submit' ]; +/** Valid options for the floatingPlaceholder input binding. */ +export type MD_INPUT_PLACEHOLDER_TYPES = 'always' | 'never' | 'auto'; +const MD_INPUT_PLACEHOLDER_VALUES = ['always', 'never', 'auto']; let nextUniqueId = 0; @@ -254,11 +257,10 @@ export class MdInputContainer implements AfterContentInit { @Input() dividerColor: 'primary' | 'accent' | 'warn' = 'primary'; /** Whether the floating label should always float or not. */ - _shouldAlwaysFloat: boolean = false; + get _shouldAlwaysFloat() { return this._floatingPlaceholder === 'always'; }; /** Whether the placeholder can float or not. */ - _floatingPlaceholder: boolean = true; - + get _canPlaceholderFloat() { return this._floatingPlaceholder !== 'never'; } /** Text for the input hint. */ @Input() @@ -273,15 +275,17 @@ export class MdInputContainer implements AfterContentInit { _hintLabelId: string = `md-input-hint-${nextUniqueId++}`; /** - * Whether the placeholder should always float or just show the placeholder when empty. - * If the value is set to null the placeholder will float if text is entered. + * Whether the placeholder should always float, never float or float as the user types. */ @Input() get floatingPlaceholder() { return this._floatingPlaceholder; } - set floatingPlaceholder(value: boolean) { - this._floatingPlaceholder = value == null || coerceBooleanProperty(value); - this._shouldAlwaysFloat = coerceBooleanProperty(value); + set floatingPlaceholder(value: MD_INPUT_PLACEHOLDER_TYPES) { + if (value && MD_INPUT_PLACEHOLDER_VALUES.indexOf(value) === -1) { + throw new MdInputContainerFloatingPlaceholderInvalidError(value); + } + this._floatingPlaceholder = value || 'auto'; } + private _floatingPlaceholder: MD_INPUT_PLACEHOLDER_TYPES = 'auto'; @ContentChild(MdInputDirective) _mdInputChild: MdInputDirective; diff --git a/src/lib/input/input.md b/src/lib/input/input.md index ebd0750035f8..40a768863c51 100644 --- a/src/lib/input/input.md +++ b/src/lib/input/input.md @@ -38,9 +38,11 @@ be used with `md-input-container`: A placeholder is an indicative text displayed in the input zone when the input does not contain text. When text is present, the indicative text will float above this input zone. -The `floatingPlaceholder` attribute of `md-input-container` can be set to `false` to hide the +The `floatingPlaceholder` attribute of `md-input-container` can be set to `never` to hide the indicative text instead when text is present in the input. +When setting `floatingPlaceholder` to `always` the floating label will always show above the input. + A placeholder for the input can be specified in one of two ways: either using the `placeholder` attribute on the `input` or `textarea`, or using an `md-placeholder` element in the `md-input-container`. Using both will raise an error. From 066c3d641155116b22468420b0d2f63b6aa489fb Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Sat, 14 Jan 2017 16:12:54 +0100 Subject: [PATCH 3/4] Single line comment --- src/lib/input/input-container.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/lib/input/input-container.ts b/src/lib/input/input-container.ts index 9e3f59cf4566..84b221eea87f 100644 --- a/src/lib/input/input-container.ts +++ b/src/lib/input/input-container.ts @@ -274,9 +274,7 @@ export class MdInputContainer implements AfterContentInit { // Unique id for the hint label. _hintLabelId: string = `md-input-hint-${nextUniqueId++}`; - /** - * Whether the placeholder should always float, never float or float as the user types. - */ + /** Whether the placeholder should always float, never float or float as the user types. */ @Input() get floatingPlaceholder() { return this._floatingPlaceholder; } set floatingPlaceholder(value: MD_INPUT_PLACEHOLDER_TYPES) { From 025fae47a7387a0aebc6fb000a1c3401b45ddda2 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Fri, 27 Jan 2017 22:20:19 +0100 Subject: [PATCH 4/4] Address comments --- src/demo-app/input/input-container-demo.html | 2 +- src/lib/input/input-container-errors.ts | 6 ------ src/lib/input/input-container.spec.ts | 14 ++++++------- src/lib/input/input-container.ts | 22 ++++++++------------ src/lib/input/input.md | 4 ++-- 5 files changed, 19 insertions(+), 29 deletions(-) diff --git a/src/demo-app/input/input-container-demo.html b/src/demo-app/input/input-container-demo.html index 3d29303032b5..d73c0cbb0aa6 100644 --- a/src/demo-app/input/input-container-demo.html +++ b/src/demo-app/input/input-container-demo.html @@ -188,7 +188,7 @@

Textarea

- +

diff --git a/src/lib/input/input-container-errors.ts b/src/lib/input/input-container-errors.ts index 00864cdfec94..90a9337b2a65 100644 --- a/src/lib/input/input-container-errors.ts +++ b/src/lib/input/input-container-errors.ts @@ -28,9 +28,3 @@ export class MdInputContainerMissingMdInputError extends MdError { 'to the native input or textarea element?'); } } - -export class MdInputContainerFloatingPlaceholderInvalidError extends MdError { - constructor(value: string) { - super(`The value "${value}" for the floatingPlaceholder input is not valid.`); - } -} diff --git a/src/lib/input/input-container.spec.ts b/src/lib/input/input-container.spec.ts index eea31125d4e8..953c7e57c353 100644 --- a/src/lib/input/input-container.spec.ts +++ b/src/lib/input/input-container.spec.ts @@ -62,7 +62,7 @@ describe('MdInputContainer', function () { let inputContainer = fixture.debugElement.query(By.directive(MdInputContainer)) .componentInstance as MdInputContainer; - expect(inputContainer.floatingPlaceholder).toBe('auto', + expect(inputContainer.floatPlaceholder).toBe('auto', 'Expected MdInputContainer to set floatingLabel to auto by default.'); }); @@ -479,7 +479,7 @@ describe('MdInputContainer', function () { expect(ariaValue).toBe(`${hintLabel.getAttribute('id')} ${endLabel.getAttribute('id')}`); }); - it('should float when floatingPlaceholder is set to default and text is entered', () => { + it('should float when floatPlaceholder is set to default and text is entered', () => { let fixture = TestBed.createComponent(MdInputContainerWithDynamicPlaceholder); fixture.detectChanges(); @@ -505,7 +505,7 @@ describe('MdInputContainer', function () { expect(labelEl.classList).toContain('md-float'); }); - it('should always float the placeholder when floatingPlaceholder is set to true', () => { + it('should always float the placeholder when floatPlaceholder is set to true', () => { let fixture = TestBed.createComponent(MdInputContainerWithDynamicPlaceholder); fixture.detectChanges(); @@ -528,7 +528,7 @@ describe('MdInputContainer', function () { }); - it('should never float the placeholder when floatingPlaceholder is set to false', () => { + it('should never float the placeholder when floatPlaceholder is set to false', () => { let fixture = TestBed.createComponent(MdInputContainerWithDynamicPlaceholder); fixture.componentInstance.shouldFloat = 'never'; @@ -741,7 +741,7 @@ class MdInputContainerWithValueBinding { @Component({ template: ` - + ` @@ -750,8 +750,8 @@ class MdInputContainerWithStaticPlaceholder {} @Component({ template: ` - - + + ` }) class MdInputContainerWithDynamicPlaceholder { diff --git a/src/lib/input/input-container.ts b/src/lib/input/input-container.ts index 84b221eea87f..070cb8203263 100644 --- a/src/lib/input/input-container.ts +++ b/src/lib/input/input-container.ts @@ -20,7 +20,7 @@ import { MdInputContainerUnsupportedTypeError, MdInputContainerPlaceholderConflictError, MdInputContainerDuplicatedHintError, - MdInputContainerMissingMdInputError, MdInputContainerFloatingPlaceholderInvalidError + MdInputContainerMissingMdInputError } from './input-container-errors'; @@ -38,9 +38,8 @@ const MD_INPUT_INVALID_TYPES = [ 'submit' ]; -/** Valid options for the floatingPlaceholder input binding. */ -export type MD_INPUT_PLACEHOLDER_TYPES = 'always' | 'never' | 'auto'; -const MD_INPUT_PLACEHOLDER_VALUES = ['always', 'never', 'auto']; +/** Type for the available floatPlaceholder values. */ +export type FloatPlaceholderType = 'always' | 'never' | 'auto'; let nextUniqueId = 0; @@ -257,10 +256,10 @@ export class MdInputContainer implements AfterContentInit { @Input() dividerColor: 'primary' | 'accent' | 'warn' = 'primary'; /** Whether the floating label should always float or not. */ - get _shouldAlwaysFloat() { return this._floatingPlaceholder === 'always'; }; + get _shouldAlwaysFloat() { return this._floatPlaceholder === 'always'; }; /** Whether the placeholder can float or not. */ - get _canPlaceholderFloat() { return this._floatingPlaceholder !== 'never'; } + get _canPlaceholderFloat() { return this._floatPlaceholder !== 'never'; } /** Text for the input hint. */ @Input() @@ -276,14 +275,11 @@ export class MdInputContainer implements AfterContentInit { /** Whether the placeholder should always float, never float or float as the user types. */ @Input() - get floatingPlaceholder() { return this._floatingPlaceholder; } - set floatingPlaceholder(value: MD_INPUT_PLACEHOLDER_TYPES) { - if (value && MD_INPUT_PLACEHOLDER_VALUES.indexOf(value) === -1) { - throw new MdInputContainerFloatingPlaceholderInvalidError(value); - } - this._floatingPlaceholder = value || 'auto'; + get floatPlaceholder() { return this._floatPlaceholder; } + set floatPlaceholder(value: FloatPlaceholderType) { + this._floatPlaceholder = value || 'auto'; } - private _floatingPlaceholder: MD_INPUT_PLACEHOLDER_TYPES = 'auto'; + private _floatPlaceholder: FloatPlaceholderType = 'auto'; @ContentChild(MdInputDirective) _mdInputChild: MdInputDirective; diff --git a/src/lib/input/input.md b/src/lib/input/input.md index 40a768863c51..9e17b3baf7bb 100644 --- a/src/lib/input/input.md +++ b/src/lib/input/input.md @@ -38,10 +38,10 @@ be used with `md-input-container`: A placeholder is an indicative text displayed in the input zone when the input does not contain text. When text is present, the indicative text will float above this input zone. -The `floatingPlaceholder` attribute of `md-input-container` can be set to `never` to hide the +The `floatPlaceholder` attribute of `md-input-container` can be set to `never` to hide the indicative text instead when text is present in the input. -When setting `floatingPlaceholder` to `always` the floating label will always show above the input. +When setting `floatPlaceholder` to `always` the floating label will always show above the input. A placeholder for the input can be specified in one of two ways: either using the `placeholder` attribute on the `input` or `textarea`, or using an `md-placeholder` element in the