feat(placeholder): add global placeholder options (#4681) (#4681)

* Implement global placeholder options so the
default float option can be set to something
other than 'auto'

Fixes #4311
This commit is contained in:
Rob Jacobs
2017-06-08 14:22:31 -04:00
committed by Andrew Seguin
parent 66629cf1ed
commit d0d79fd93a
8 changed files with 149 additions and 26 deletions
+7
View File
@@ -103,6 +103,13 @@ export {MdCommonModule} from './common-behaviors/common-module';
// Datetime
export * from './datetime/index';
// Placeholder
export {
FloatPlaceholderType,
PlaceholderOptions,
MD_PLACEHOLDER_GLOBAL_OPTIONS
} from './placeholder/placeholder-options';
@NgModule({
imports: [
MdLineModule,
@@ -0,0 +1,12 @@
import { InjectionToken } from '@angular/core';
/** InjectionToken that can be used to specify the global placeholder options. */
export const MD_PLACEHOLDER_GLOBAL_OPTIONS =
new InjectionToken<PlaceholderOptions>('md-placeholder-global-options');
/** Type for the available floatPlaceholder values. */
export type FloatPlaceholderType = 'always' | 'never' | 'auto';
export interface PlaceholderOptions {
float?: FloatPlaceholderType;
}
+24
View File
@@ -22,6 +22,7 @@ import {
getMdInputContainerMissingMdInputError,
getMdInputContainerPlaceholderConflictError
} from './input-container-errors';
import {MD_PLACEHOLDER_GLOBAL_OPTIONS} from '../core/placeholder/placeholder-options';
describe('MdInputContainer', function () {
@@ -82,6 +83,29 @@ describe('MdInputContainer', function () {
'Expected MdInputContainer to set floatingLabel to auto by default.');
});
it('should default to global floating placeholder type', () => {
TestBed.resetTestingModule();
TestBed.configureTestingModule({
imports: [
FormsModule,
MdInputModule,
NoopAnimationsModule
],
declarations: [
MdInputContainerBaseTestController
],
providers: [{ provide: MD_PLACEHOLDER_GLOBAL_OPTIONS, useValue: { float: 'always' } }]
});
let fixture = TestBed.createComponent(MdInputContainerBaseTestController);
fixture.detectChanges();
let inputContainer = fixture.debugElement.query(By.directive(MdInputContainer))
.componentInstance as MdInputContainer;
expect(inputContainer.floatPlaceholder).toBe('always',
'Expected MdInputContainer to set floatingLabel to always from global option.');
});
it('should not be treated as empty if type is date',
inject([Platform], (platform: Platform) => {
if (!(platform.TRIDENT || platform.FIREFOX)) {
+19 -8
View File
@@ -16,7 +16,8 @@ import {
Renderer2,
Self,
ViewChild,
ViewEncapsulation
ViewEncapsulation,
Inject
} from '@angular/core';
import {animate, state, style, transition, trigger} from '@angular/animations';
import {coerceBooleanProperty} from '../core';
@@ -28,7 +29,11 @@ import {
getMdInputContainerPlaceholderConflictError,
getMdInputContainerUnsupportedTypeError
} from './input-container-errors';
import {
FloatPlaceholderType,
PlaceholderOptions,
MD_PLACEHOLDER_GLOBAL_OPTIONS
} from '../core/placeholder/placeholder-options';
// Invalid input type. Using one of these will throw an MdInputContainerUnsupportedTypeError.
const MD_INPUT_INVALID_TYPES = [
@@ -44,9 +49,6 @@ const MD_INPUT_INVALID_TYPES = [
'submit'
];
/** Type for the available floatPlaceholder values. */
export type FloatPlaceholderType = 'always' | 'never' | 'auto';
let nextUniqueId = 0;
@@ -302,7 +304,10 @@ export class MdInputDirective {
},
encapsulation: ViewEncapsulation.None,
})
export class MdInputContainer implements AfterViewInit, AfterContentInit, AfterContentChecked {
private _placeholderOptions: PlaceholderOptions;
/** Alignment of the input container's content. */
@Input() align: 'start' | 'end' = 'start';
@@ -347,9 +352,9 @@ export class MdInputContainer implements AfterViewInit, AfterContentInit, AfterC
@Input()
get floatPlaceholder() { return this._floatPlaceholder; }
set floatPlaceholder(value: FloatPlaceholderType) {
this._floatPlaceholder = value || 'auto';
this._floatPlaceholder = value || this._placeholderOptions.float || 'auto';
}
private _floatPlaceholder: FloatPlaceholderType = 'auto';
private _floatPlaceholder: FloatPlaceholderType;
/** Reference to the input's underline element. */
@ViewChild('underline') underlineRef: ElementRef;
@@ -368,7 +373,13 @@ export class MdInputContainer implements AfterViewInit, AfterContentInit, AfterC
constructor(
public _elementRef: ElementRef,
private _changeDetectorRef: ChangeDetectorRef) { }
private _changeDetectorRef: ChangeDetectorRef,
@Optional() private _parentForm: NgForm,
@Optional() private _parentFormGroup: FormGroupDirective,
@Optional() @Inject(MD_PLACEHOLDER_GLOBAL_OPTIONS) placeholderOptions: PlaceholderOptions) {
this._placeholderOptions = placeholderOptions ? placeholderOptions : {};
this.floatPlaceholder = this._placeholderOptions.float || 'auto';
}
ngAfterContentInit() {
this._validateInputChild();
+16
View File
@@ -60,6 +60,22 @@ A placeholder for the input can be specified in one of two ways: either using th
attribute on the `input` or `textarea`, or using an `md-placeholder` element in the
`md-input-container`. Using both will raise an error.
Global default placeholder options can be specified by setting the `MD_PLACEHOLDER_GLOBAL_OPTIONS` provider. This setting will apply to all components that support the floating placeholder.
```ts
@NgModule({
providers: [
{provide: MD_PLACEHOLDER_GLOBAL_OPTIONS, useValue: { float: 'always' }}
]
})
```
Here are the available global options:
| Name | Type | Values | Description |
| --------------- | ------- | ------------------- | ----------------------------------------- |
| float | string | auto, always, never | The default placeholder float behavior. |
### Prefix and Suffix
HTML can be included before, and after the input tag, as prefix or suffix. It will be underlined as
+16
View File
@@ -62,6 +62,22 @@ It's possible to turn off the placeholder's floating animation using the `floatP
</md-select>
```
Global default placeholder options can be specified by setting the `MD_PLACEHOLDER_GLOBAL_OPTIONS` provider. This setting will apply to all components that support the floating placeholder.
```ts
@NgModule({
providers: [
{provide: MD_PLACEHOLDER_GLOBAL_OPTIONS, useValue: { float: 'always' }}
]
})
```
Here are the available global options:
| Name | Type | Values | Description |
| --------------- | ------- | ------------------- | ----------------------------------------- |
| float | string | auto, always, never | The default placeholder float behavior. |
#### Keyboard interaction:
- <kbd>DOWN_ARROW</kbd>: Focus next option
- <kbd>UP_ARROW</kbd>: Focus previous option
+30 -2
View File
@@ -12,7 +12,7 @@ import {
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
import {MdSelectModule} from './index';
import {OverlayContainer} from '../core/overlay/overlay-container';
import {MdSelect, MdSelectFloatPlaceholderType} from './select';
import {MdSelect} from './select';
import {getMdSelectDynamicMultipleError, getMdSelectNonArrayValueError} from './select-errors';
import {MdOption} from '../core/option/option';
import {Dir} from '../core/rtl/dir';
@@ -25,6 +25,10 @@ import {ViewportRuler} from '../core/overlay/position/viewport-ruler';
import {dispatchFakeEvent, dispatchKeyboardEvent} from '../core/testing/dispatch-events';
import {wrappedErrorMessage} from '../core/testing/wrapped-error-message';
import {ScrollDispatcher} from '../core/overlay/scroll/scroll-dispatcher';
import {
FloatPlaceholderType,
MD_PLACEHOLDER_GLOBAL_OPTIONS
} from '../core/placeholder/placeholder-options';
describe('MdSelect', () => {
@@ -1798,6 +1802,30 @@ describe('MdSelect', () => {
expect(fixture.componentInstance.select._getPlaceholderAnimationState()).toBe('floating-ltr');
});
it ('should default to global floating placeholder type', () => {
fixture.destroy();
TestBed.resetTestingModule();
TestBed.configureTestingModule({
imports: [
MdSelectModule,
ReactiveFormsModule,
FormsModule,
NoopAnimationsModule
],
declarations: [
FloatPlaceholderSelect
],
providers: [{ provide: MD_PLACEHOLDER_GLOBAL_OPTIONS, useValue: { float: 'always' } }]
});
fixture = TestBed.createComponent(FloatPlaceholderSelect);
fixture.componentInstance.floatPlaceholder = null;
fixture.detectChanges();
expect(fixture.componentInstance.select.floatPlaceholder).toBe('always');
});
});
describe('with OnPush change detection', () => {
@@ -2419,7 +2447,7 @@ class BasicSelectOnPushPreselected {
`,
})
class FloatPlaceholderSelect {
floatPlaceholder: MdSelectFloatPlaceholderType = 'auto';
floatPlaceholder: FloatPlaceholderType = 'auto';
control = new FormControl();
foods: any[] = [
{ value: 'steak-0', viewValue: 'Steak' },
+25 -16
View File
@@ -16,6 +16,7 @@ import {
ChangeDetectorRef,
Attribute,
OnInit,
Inject
} from '@angular/core';
import {MdOption, MdOptionSelectionChange} from '../core/option/option';
import {ENTER, SPACE, UP_ARROW, DOWN_ARROW, HOME, END} from '../core/keyboard/keycodes';
@@ -36,7 +37,11 @@ import 'rxjs/add/operator/startWith';
import 'rxjs/add/operator/filter';
import {CanColor, mixinColor} from '../core/common-behaviors/color';
import {CanDisable} from '../core/common-behaviors/disabled';
import {
FloatPlaceholderType,
PlaceholderOptions,
MD_PLACEHOLDER_GLOBAL_OPTIONS}
from '../core/placeholder/placeholder-options';
/**
* The following style constants are necessary to save here in order
@@ -94,16 +99,12 @@ export class MdSelectChange {
constructor(public source: MdSelect, public value: any) { }
}
/** Allowed values for the floatPlaceholder option. */
export type MdSelectFloatPlaceholderType = 'always' | 'never' | 'auto';
// Boilerplate for applying mixins to MdSelect.
export class MdSelectBase {
constructor(public _renderer: Renderer2, public _elementRef: ElementRef) {}
}
export const _MdSelectMixinBase = mixinColor(MdSelectBase, 'primary');
@Component({
moduleId: module.id,
selector: 'md-select, mat-select',
@@ -170,6 +171,9 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On
/** Tab index for the element. */
private _tabIndex: number;
/** Deals with configuring placeholder options */
private _placeholderOptions: PlaceholderOptions;
/**
* The width of the trigger. Must be saved to set the min width of the overlay panel
* and the width of the selected value.
@@ -275,11 +279,11 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On
/** Whether to float the placeholder text. */
@Input()
get floatPlaceholder(): MdSelectFloatPlaceholderType { return this._floatPlaceholder; }
set floatPlaceholder(value: MdSelectFloatPlaceholderType) {
this._floatPlaceholder = value || 'auto';
get floatPlaceholder(): FloatPlaceholderType { return this._floatPlaceholder; }
set floatPlaceholder(value: FloatPlaceholderType) {
this._floatPlaceholder = value || this._placeholderOptions.float || 'auto';
}
private _floatPlaceholder: MdSelectFloatPlaceholderType = 'auto';
private _floatPlaceholder: FloatPlaceholderType;
/** Tab index for the select element. */
@Input()
@@ -310,13 +314,15 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On
/** Event emitted when the selected value has been changed by the user. */
@Output() change: EventEmitter<MdSelectChange> = new EventEmitter<MdSelectChange>();
constructor(private _viewportRuler: ViewportRuler,
private _changeDetectorRef: ChangeDetectorRef,
renderer: Renderer2,
elementRef: ElementRef,
@Optional() private _dir: Dir,
@Self() @Optional() public _control: NgControl,
@Attribute('tabindex') tabIndex: string) {
constructor(
private _viewportRuler: ViewportRuler,
private _changeDetectorRef: ChangeDetectorRef,
renderer: Renderer2,
elementRef: ElementRef,
@Optional() private _dir: Dir,
@Self() @Optional() public _control: NgControl,
@Attribute('tabindex') tabIndex: string,
@Optional() @Inject(MD_PLACEHOLDER_GLOBAL_OPTIONS) placeholderOptions: PlaceholderOptions) {
super(renderer, elementRef);
if (this._control) {
@@ -324,6 +330,9 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On
}
this._tabIndex = parseInt(tabIndex) || 0;
this._placeholderOptions = placeholderOptions ? placeholderOptions : {};
this.floatPlaceholder = this._placeholderOptions.float || 'auto';
}
ngOnInit() {