Skip to content

Commit 2055653

Browse files
committed
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
1 parent e783494 commit 2055653

File tree

4 files changed

+103
-7
lines changed

4 files changed

+103
-7
lines changed

src/demo-app/input/input-container-demo.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,8 @@ <h4>Textarea</h4>
180180
</md-input-container>
181181
</p>
182182
<p>
183-
<md-checkbox [(ngModel)]="floatingLabel"> Check to make floating label:</md-checkbox>
183+
<md-checkbox [(ngModel)]="floatingLabel">Toggle Floating Label</md-checkbox>
184+
<button md-button (click)="floatingLabel = null">Reset Floating Label</button>
184185
<md-input-container [floatingPlaceholder]="floatingLabel">
185186
<input mdInput [placeholder]="floatingLabel ? 'Floating label' : 'Not floating label'">
186187
</md-input-container>

src/lib/input/input-container.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
<label class="md-input-placeholder"
99
[attr.for]="_mdInputChild.id"
10-
[class.md-empty]="_mdInputChild.empty"
10+
[class.md-empty]="_mdInputChild.empty && !_shouldAlwaysFloat"
1111
[class.md-focused]="_mdInputChild.focused"
1212
[class.md-float]="floatingPlaceholder"
1313
[class.md-accent]="dividerColor == 'accent'"

src/lib/input/input-container.spec.ts

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ describe('MdInputContainer', function () {
4848
MdInputContainerWithStaticPlaceholder,
4949
MdInputContainerMissingMdInputTestController,
5050
MdInputContainerMultipleHintTestController,
51-
MdInputContainerMultipleHintMixedTestController
51+
MdInputContainerMultipleHintMixedTestController,
52+
MdInputContainerWithDynamicPlaceholder
5253
],
5354
});
5455

@@ -477,6 +478,78 @@ describe('MdInputContainer', function () {
477478

478479
expect(ariaValue).toBe(`${hintLabel.getAttribute('id')} ${endLabel.getAttribute('id')}`);
479480
});
481+
482+
it('should float when floatingPlaceholder is set to default and text is entered', () => {
483+
let fixture = TestBed.createComponent(MdInputContainerWithDynamicPlaceholder);
484+
fixture.detectChanges();
485+
486+
let inputEl = fixture.debugElement.query(By.css('input'));
487+
let labelEl = fixture.debugElement.query(By.css('label')).nativeElement;
488+
489+
expect(labelEl.classList).not.toContain('md-empty');
490+
expect(labelEl.classList).toContain('md-float');
491+
492+
fixture.componentInstance.shouldFloat = null;
493+
fixture.detectChanges();
494+
495+
expect(labelEl.classList).toContain('md-empty');
496+
expect(labelEl.classList).toContain('md-float');
497+
498+
// Update the value of the input.
499+
inputEl.nativeElement.value = 'Text';
500+
501+
// Fake behavior of the `(input)` event which should trigger a change detection.
502+
fixture.detectChanges();
503+
504+
expect(labelEl.classList).not.toContain('md-empty');
505+
expect(labelEl.classList).toContain('md-float');
506+
});
507+
508+
it('should always float the placeholder when floatingPlaceholder is set to true', () => {
509+
let fixture = TestBed.createComponent(MdInputContainerWithDynamicPlaceholder);
510+
fixture.detectChanges();
511+
512+
let inputEl = fixture.debugElement.query(By.css('input'));
513+
let labelEl = fixture.debugElement.query(By.css('label')).nativeElement;
514+
515+
expect(labelEl.classList).not.toContain('md-empty');
516+
expect(labelEl.classList).toContain('md-float');
517+
518+
fixture.detectChanges();
519+
520+
// Update the value of the input.
521+
inputEl.nativeElement.value = 'Text';
522+
523+
// Fake behavior of the `(input)` event which should trigger a change detection.
524+
fixture.detectChanges();
525+
526+
expect(labelEl.classList).not.toContain('md-empty');
527+
expect(labelEl.classList).toContain('md-float');
528+
});
529+
530+
531+
it('should never float the placeholder when floatingPlaceholder is set to false', () => {
532+
let fixture = TestBed.createComponent(MdInputContainerWithDynamicPlaceholder);
533+
534+
fixture.componentInstance.shouldFloat = false;
535+
fixture.detectChanges();
536+
537+
let inputEl = fixture.debugElement.query(By.css('input'));
538+
let labelEl = fixture.debugElement.query(By.css('label')).nativeElement;
539+
540+
expect(labelEl.classList).toContain('md-empty');
541+
expect(labelEl.classList).not.toContain('md-float');
542+
543+
// Update the value of the input.
544+
inputEl.nativeElement.value = 'Text';
545+
546+
// Fake behavior of the `(input)` event which should trigger a change detection.
547+
fixture.detectChanges();
548+
549+
expect(labelEl.classList).not.toContain('md-empty');
550+
expect(labelEl.classList).not.toContain('md-float');
551+
});
552+
480553
});
481554

482555
@Component({
@@ -675,6 +748,16 @@ class MdInputContainerWithValueBinding {
675748
})
676749
class MdInputContainerWithStaticPlaceholder {}
677750

751+
@Component({
752+
template: `
753+
<md-input-container [floatingPlaceholder]="shouldFloat">
754+
<input md-input placeholder="Label">
755+
</md-input-container>`
756+
})
757+
class MdInputContainerWithDynamicPlaceholder {
758+
shouldFloat: boolean = true;
759+
}
760+
678761
@Component({
679762
template: `
680763
<md-input-container>

src/lib/input/input-container.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,13 @@ export class MdInputContainer implements AfterContentInit {
253253
/** Color of the input divider, based on the theme. */
254254
@Input() dividerColor: 'primary' | 'accent' | 'warn' = 'primary';
255255

256+
/** Whether the floating label should always float or not. */
257+
_shouldAlwaysFloat: boolean = false;
258+
259+
/** Whether the placeholder can float or not. */
260+
_floatingPlaceholder: boolean = true;
261+
262+
256263
/** Text for the input hint. */
257264
@Input()
258265
get hintLabel() { return this._hintLabel; }
@@ -265,11 +272,16 @@ export class MdInputContainer implements AfterContentInit {
265272
// Unique id for the hint label.
266273
_hintLabelId: string = `md-input-hint-${nextUniqueId++}`;
267274

268-
/** Text or the floating placeholder. */
275+
/**
276+
* Whether the placeholder should always float or just show the placeholder when empty.
277+
* If the value is set to null the placeholder will float if text is entered.
278+
*/
269279
@Input()
270-
get floatingPlaceholder(): boolean { return this._floatingPlaceholder; }
271-
set floatingPlaceholder(value) { this._floatingPlaceholder = coerceBooleanProperty(value); }
272-
private _floatingPlaceholder: boolean = true;
280+
get floatingPlaceholder() { return this._floatingPlaceholder; }
281+
set floatingPlaceholder(value: boolean) {
282+
this._floatingPlaceholder = value == null || coerceBooleanProperty(value);
283+
this._shouldAlwaysFloat = coerceBooleanProperty(value);
284+
}
273285

274286
@ContentChild(MdInputDirective) _mdInputChild: MdInputDirective;
275287

0 commit comments

Comments
 (0)