Skip to content

Commit ae3a89a

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 angular#2466
1 parent 4ab6f30 commit ae3a89a

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
@@ -46,7 +46,8 @@ describe('MdInputContainer', function () {
4646
MdInputContainerWithValueBinding,
4747
MdInputContainerWithFormControl,
4848
MdInputContainerWithStaticPlaceholder,
49-
MdInputContainerMissingMdInputTestController
49+
MdInputContainerMissingMdInputTestController,
50+
MdInputContainerWithDynamicPlaceholder
5051
],
5152
});
5253

@@ -404,6 +405,78 @@ describe('MdInputContainer', function () {
404405
const textarea: HTMLTextAreaElement = fixture.nativeElement.querySelector('textarea');
405406
expect(textarea).not.toBeNull();
406407
});
408+
409+
it('should float when floatingPlaceholder is set to default and text is entered', () => {
410+
let fixture = TestBed.createComponent(MdInputContainerWithDynamicPlaceholder);
411+
fixture.detectChanges();
412+
413+
let inputEl = fixture.debugElement.query(By.css('input'));
414+
let labelEl = fixture.debugElement.query(By.css('label')).nativeElement;
415+
416+
expect(labelEl.classList).not.toContain('md-empty');
417+
expect(labelEl.classList).toContain('md-float');
418+
419+
fixture.componentInstance.shouldFloat = null;
420+
fixture.detectChanges();
421+
422+
expect(labelEl.classList).toContain('md-empty');
423+
expect(labelEl.classList).toContain('md-float');
424+
425+
// Update the value of the input.
426+
inputEl.nativeElement.value = 'Text';
427+
428+
// Fake behavior of the `(input)` event which should trigger a change detection.
429+
fixture.detectChanges();
430+
431+
expect(labelEl.classList).not.toContain('md-empty');
432+
expect(labelEl.classList).toContain('md-float');
433+
});
434+
435+
it('should always float the placeholder when floatingPlaceholder is set to true', () => {
436+
let fixture = TestBed.createComponent(MdInputContainerWithDynamicPlaceholder);
437+
fixture.detectChanges();
438+
439+
let inputEl = fixture.debugElement.query(By.css('input'));
440+
let labelEl = fixture.debugElement.query(By.css('label')).nativeElement;
441+
442+
expect(labelEl.classList).not.toContain('md-empty');
443+
expect(labelEl.classList).toContain('md-float');
444+
445+
fixture.detectChanges();
446+
447+
// Update the value of the input.
448+
inputEl.nativeElement.value = 'Text';
449+
450+
// Fake behavior of the `(input)` event which should trigger a change detection.
451+
fixture.detectChanges();
452+
453+
expect(labelEl.classList).not.toContain('md-empty');
454+
expect(labelEl.classList).toContain('md-float');
455+
});
456+
457+
458+
it('should never float the placeholder when floatingPlaceholder is set to false', () => {
459+
let fixture = TestBed.createComponent(MdInputContainerWithDynamicPlaceholder);
460+
461+
fixture.componentInstance.shouldFloat = false;
462+
fixture.detectChanges();
463+
464+
let inputEl = fixture.debugElement.query(By.css('input'));
465+
let labelEl = fixture.debugElement.query(By.css('label')).nativeElement;
466+
467+
expect(labelEl.classList).toContain('md-empty');
468+
expect(labelEl.classList).not.toContain('md-float');
469+
470+
// Update the value of the input.
471+
inputEl.nativeElement.value = 'Text';
472+
473+
// Fake behavior of the `(input)` event which should trigger a change detection.
474+
fixture.detectChanges();
475+
476+
expect(labelEl.classList).not.toContain('md-empty');
477+
expect(labelEl.classList).not.toContain('md-float');
478+
});
479+
407480
});
408481

409482
@Component({
@@ -580,6 +653,16 @@ class MdInputContainerWithValueBinding {
580653
})
581654
class MdInputContainerWithStaticPlaceholder {}
582655

656+
@Component({
657+
template: `
658+
<md-input-container [floatingPlaceholder]="shouldFloat">
659+
<input md-input placeholder="Label">
660+
</md-input-container>`
661+
})
662+
class MdInputContainerWithDynamicPlaceholder {
663+
shouldFloat: boolean = true;
664+
}
665+
583666
@Component({
584667
template: `
585668
<md-input-container>

src/lib/input/input-container.ts

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

251+
/** Whether the floating label should always float or not. */
252+
_shouldAlwaysFloat: boolean = false;
253+
254+
/** Whether the placeholder can float or not. */
255+
_floatingPlaceholder: boolean = true;
256+
257+
251258
/** Text for the input hint. */
252259
@Input()
253260
get hintLabel() { return this._hintLabel; }
@@ -257,11 +264,16 @@ export class MdInputContainer implements AfterContentInit {
257264
}
258265
private _hintLabel = '';
259266

260-
/** Text or the floating placeholder. */
267+
/**
268+
* Whether the placeholder should always float or just show the placeholder when empty.
269+
* If the value is set to null the placeholder will float if text is entered.
270+
*/
261271
@Input()
262-
get floatingPlaceholder(): boolean { return this._floatingPlaceholder; }
263-
set floatingPlaceholder(value) { this._floatingPlaceholder = coerceBooleanProperty(value); }
264-
private _floatingPlaceholder: boolean = true;
272+
get floatingPlaceholder() { return this._floatingPlaceholder; }
273+
set floatingPlaceholder(value: boolean) {
274+
this._floatingPlaceholder = value == null || coerceBooleanProperty(value);
275+
this._shouldAlwaysFloat = coerceBooleanProperty(value);
276+
}
265277

266278
@ContentChild(MdInputDirective) _mdInputChild: MdInputDirective;
267279

0 commit comments

Comments
 (0)