Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(input): option to imperatively float placeholder #2585

Merged
merged 4 commits into from
Feb 3, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions src/demo-app/input/input-container-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -180,9 +180,16 @@ <h4>Textarea</h4>
</md-input-container>
</p>
<p>
<md-checkbox [(ngModel)]="floatingLabel"> Check to make floating label:</md-checkbox>
<md-input-container [floatingPlaceholder]="floatingLabel">
<input mdInput [placeholder]="floatingLabel ? 'Floating label' : 'Not floating label'">
<md-button-toggle-group [(ngModel)]="floatingLabel">
<md-button-toggle value="auto">Auto Float</md-button-toggle>
<md-button-toggle value="always">Always Float</md-button-toggle>
<md-button-toggle value="never">Never Float</md-button-toggle>
</md-button-toggle-group>
</p>

<p>
<md-input-container [floatPlaceholder]="floatingLabel">
<input mdInput placeholder="Placeholder">
</md-input-container>
</p>

Expand Down
2 changes: 1 addition & 1 deletion src/demo-app/input/input-container-demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions src/lib/input/input-container.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@

<label class="md-input-placeholder"
[attr.for]="_mdInputChild.id"
[class.md-empty]="_mdInputChild.empty"
[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()">
Expand Down
91 changes: 87 additions & 4 deletions src/lib/input/input-container.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ describe('MdInputContainer', function () {
MdInputContainerWithStaticPlaceholder,
MdInputContainerMissingMdInputTestController,
MdInputContainerMultipleHintTestController,
MdInputContainerMultipleHintMixedTestController
MdInputContainerMultipleHintMixedTestController,
MdInputContainerWithDynamicPlaceholder
],
});

Expand All @@ -61,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.floatPlaceholder).toBe('auto',
'Expected MdInputContainer to set floatingLabel to auto by default.');
});

it('should not be treated as empty if type is date',
Expand Down Expand Up @@ -477,6 +478,78 @@ describe('MdInputContainer', function () {

expect(ariaValue).toBe(`${hintLabel.getAttribute('id')} ${endLabel.getAttribute('id')}`);
});

it('should float when floatPlaceholder is set to default and text is entered', () => {
let fixture = TestBed.createComponent(MdInputContainerWithDynamicPlaceholder);
fixture.detectChanges();

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 = 'auto';
fixture.detectChanges();

expect(labelEl.classList).toContain('md-empty');
expect(labelEl.classList).toContain('md-float');

// Update the value of the input.
inputEl.value = 'Text';

// Fake behavior of the `(input)` event which should trigger a change detection.
fixture.detectChanges();

expect(labelEl.classList).not.toContain('md-empty');
expect(labelEl.classList).toContain('md-float');
});

it('should always float the placeholder when floatPlaceholder is set to true', () => {
let fixture = TestBed.createComponent(MdInputContainerWithDynamicPlaceholder);
fixture.detectChanges();

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.detectChanges();

// Update the value of the input.
inputEl.value = 'Text';

// Fake behavior of the `(input)` event which should trigger a change detection.
fixture.detectChanges();

expect(labelEl.classList).not.toContain('md-empty');
expect(labelEl.classList).toContain('md-float');
});


it('should never float the placeholder when floatPlaceholder is set to false', () => {
let fixture = TestBed.createComponent(MdInputContainerWithDynamicPlaceholder);

fixture.componentInstance.shouldFloat = 'never';
fixture.detectChanges();

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.value = 'Text';

// Fake behavior of the `(input)` event which should trigger a change detection.
fixture.detectChanges();

expect(labelEl.classList).not.toContain('md-empty');
expect(labelEl.classList).not.toContain('md-float');
});

});

@Component({
Expand Down Expand Up @@ -668,13 +741,23 @@ class MdInputContainerWithValueBinding {

@Component({
template: `
<md-input-container [floatingPlaceholder]="false">
<md-input-container floatPlaceholder="never">
<input mdInput placeholder="Label">
</md-input-container>
`
})
class MdInputContainerWithStaticPlaceholder {}

@Component({
template: `
<md-input-container [floatPlaceholder]="shouldFloat">
<input mdInput placeholder="Label">
</md-input-container>`
})
class MdInputContainerWithDynamicPlaceholder {
shouldFloat: string = 'always';
}

@Component({
template: `
<md-input-container>
Expand Down
18 changes: 14 additions & 4 deletions src/lib/input/input-container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ const MD_INPUT_INVALID_TYPES = [
'submit'
];

/** Type for the available floatPlaceholder values. */
export type FloatPlaceholderType = 'always' | 'never' | 'auto';

let nextUniqueId = 0;

Expand Down Expand Up @@ -253,6 +255,12 @@ export class MdInputContainer implements AfterContentInit {
/** Color of the input divider, based on the theme. */
@Input() dividerColor: 'primary' | 'accent' | 'warn' = 'primary';

/** Whether the floating label should always float or not. */
get _shouldAlwaysFloat() { return this._floatPlaceholder === 'always'; };

/** Whether the placeholder can float or not. */
get _canPlaceholderFloat() { return this._floatPlaceholder !== 'never'; }

/** Text for the input hint. */
@Input()
get hintLabel() { return this._hintLabel; }
Expand All @@ -265,11 +273,13 @@ export class MdInputContainer implements AfterContentInit {
// Unique id for the hint label.
_hintLabelId: string = `md-input-hint-${nextUniqueId++}`;

/** Text or the floating placeholder. */
/** Whether the placeholder should always float, never float or float as the user types. */
@Input()
get floatingPlaceholder(): boolean { return this._floatingPlaceholder; }
set floatingPlaceholder(value) { this._floatingPlaceholder = coerceBooleanProperty(value); }
private _floatingPlaceholder: boolean = true;
get floatPlaceholder() { return this._floatPlaceholder; }
set floatPlaceholder(value: FloatPlaceholderType) {
this._floatPlaceholder = value || 'auto';
}
private _floatPlaceholder: FloatPlaceholderType = 'auto';

@ContentChild(MdInputDirective) _mdInputChild: MdInputDirective;

Expand Down
4 changes: 3 additions & 1 deletion src/lib/input/input.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 `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 `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
`md-input-container`. Using both will raise an error.
Expand Down