Skip to content

Commit ab93d56

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 7863e38 commit ab93d56

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

@@ -383,6 +384,78 @@ describe('MdInputContainer', function () {
383384
const textarea: HTMLTextAreaElement = fixture.nativeElement.querySelector('textarea');
384385
expect(textarea).not.toBeNull();
385386
});
387+
388+
it('should float when floatingPlaceholder is set to default and text is entered', () => {
389+
let fixture = TestBed.createComponent(MdInputContainerWithDynamicPlaceholder);
390+
fixture.detectChanges();
391+
392+
let inputEl = fixture.debugElement.query(By.css('input'));
393+
let labelEl = fixture.debugElement.query(By.css('label')).nativeElement;
394+
395+
expect(labelEl.classList).not.toContain('md-empty');
396+
expect(labelEl.classList).toContain('md-float');
397+
398+
fixture.componentInstance.shouldFloat = null;
399+
fixture.detectChanges();
400+
401+
expect(labelEl.classList).toContain('md-empty');
402+
expect(labelEl.classList).toContain('md-float');
403+
404+
// Update the value of the input.
405+
inputEl.nativeElement.value = 'Text';
406+
407+
// Fake behavior of the `(input)` event which should trigger a change detection.
408+
fixture.detectChanges();
409+
410+
expect(labelEl.classList).not.toContain('md-empty');
411+
expect(labelEl.classList).toContain('md-float');
412+
});
413+
414+
it('should always float the placeholder when floatingPlaceholder is set to true', () => {
415+
let fixture = TestBed.createComponent(MdInputContainerWithDynamicPlaceholder);
416+
fixture.detectChanges();
417+
418+
let inputEl = fixture.debugElement.query(By.css('input'));
419+
let labelEl = fixture.debugElement.query(By.css('label')).nativeElement;
420+
421+
expect(labelEl.classList).not.toContain('md-empty');
422+
expect(labelEl.classList).toContain('md-float');
423+
424+
fixture.detectChanges();
425+
426+
// Update the value of the input.
427+
inputEl.nativeElement.value = 'Text';
428+
429+
// Fake behavior of the `(input)` event which should trigger a change detection.
430+
fixture.detectChanges();
431+
432+
expect(labelEl.classList).not.toContain('md-empty');
433+
expect(labelEl.classList).toContain('md-float');
434+
});
435+
436+
437+
it('should never float the placeholder when floatingPlaceholder is set to false', () => {
438+
let fixture = TestBed.createComponent(MdInputContainerWithDynamicPlaceholder);
439+
440+
fixture.componentInstance.shouldFloat = false;
441+
fixture.detectChanges();
442+
443+
let inputEl = fixture.debugElement.query(By.css('input'));
444+
let labelEl = fixture.debugElement.query(By.css('label')).nativeElement;
445+
446+
expect(labelEl.classList).toContain('md-empty');
447+
expect(labelEl.classList).not.toContain('md-float');
448+
449+
// Update the value of the input.
450+
inputEl.nativeElement.value = 'Text';
451+
452+
// Fake behavior of the `(input)` event which should trigger a change detection.
453+
fixture.detectChanges();
454+
455+
expect(labelEl.classList).not.toContain('md-empty');
456+
expect(labelEl.classList).not.toContain('md-float');
457+
});
458+
386459
});
387460

388461
@Component({
@@ -559,6 +632,16 @@ class MdInputContainerWithValueBinding {
559632
})
560633
class MdInputContainerWithStaticPlaceholder {}
561634

635+
@Component({
636+
template: `
637+
<md-input-container [floatingPlaceholder]="shouldFloat">
638+
<input md-input placeholder="Label">
639+
</md-input-container>`
640+
})
641+
class MdInputContainerWithDynamicPlaceholder {
642+
shouldFloat: boolean = true;
643+
}
644+
562645
@Component({
563646
template: `
564647
<md-input-container>

src/lib/input/input-container.ts

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

235+
/** Whether the floating label should always float or not. */
236+
_shouldAlwaysFloat: boolean = false;
237+
238+
/** Whether the placeholder can float or not. */
239+
_floatingPlaceholder: boolean = true;
240+
241+
235242
/** Text for the input hint. */
236243
@Input()
237244
get hintLabel() { return this._hintLabel; }
@@ -241,11 +248,16 @@ export class MdInputContainer implements AfterContentInit {
241248
}
242249
private _hintLabel = '';
243250

244-
/** Text or the floating placeholder. */
251+
/**
252+
* Whether the placeholder should always float or just show the placeholder when empty.
253+
* If the value is set to null the placeholder will float if text is entered.
254+
*/
245255
@Input()
246-
get floatingPlaceholder(): boolean { return this._floatingPlaceholder; }
247-
set floatingPlaceholder(value) { this._floatingPlaceholder = coerceBooleanProperty(value); }
248-
private _floatingPlaceholder: boolean = true;
256+
get floatingPlaceholder() { return this._floatingPlaceholder; }
257+
set floatingPlaceholder(value: boolean) {
258+
this._floatingPlaceholder = value == null || coerceBooleanProperty(value);
259+
this._shouldAlwaysFloat = coerceBooleanProperty(value);
260+
}
249261

250262
@ContentChild(MdInputDirective) _mdInputChild: MdInputDirective;
251263

0 commit comments

Comments
 (0)