Skip to content

Commit

Permalink
feat(input): add native select to be form input
Browse files Browse the repository at this point in the history
  • Loading branch information
Vivian Hu committed Aug 27, 2018
1 parent 4c8448a commit 63f07ca
Show file tree
Hide file tree
Showing 15 changed files with 71 additions and 65 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ npm-debug.log
testem.log
/.chrome
/.git
/.firebase

# schematics
/src/lib/schematics/**/*.js
Expand Down
4 changes: 2 additions & 2 deletions src/demo-app/input/input-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<form>
<mat-form-field class="demo-full-width">
<mat-label>Company (disabled)</mat-label>
<input matInput disabled value="Google">
<input matNativeControl disabled value="Google">
</mat-form-field>

<table style="width: 100%" cellspacing="0"><tr>
Expand All @@ -28,7 +28,7 @@
</mat-form-field>
<mat-form-field class="demo-full-width">
<mat-label>Address 2</mat-label>
<textarea matInput></textarea>
<textarea matNativeControl></textarea>
</mat-form-field>
</p>
<table style="width: 100%" cellspacing="0"><tr>
Expand Down
28 changes: 14 additions & 14 deletions src/demo-app/select/select-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<h4>Basic</h4>
<mat-form-field class="demo-full-width">
<mat-label>Select your car</mat-label>
<select matControl id="mySelectId">
<select matNativeControl id="mySelectId">
<option value="" disabled selected></option>
<option value="volvo">Volvo</option>
<option value="saab" disabled>Saab</option>
Expand All @@ -18,7 +18,7 @@ <h4>Basic</h4>
<h4>Disabled and required</h4>
<mat-form-field class="demo-full-width">
<mat-label>Select your car (disabled)</mat-label>
<select matControl disabled required>
<select matNativeControl disabled required>
<option value="volvo">Volvo</option>
<option value="saab">Saab</option>
<option value="mercedes">Mercedes</option>
Expand All @@ -28,7 +28,7 @@ <h4>Disabled and required</h4>
<h4>Floating label</h4>
<mat-form-field>
<mat-label>Float with value</mat-label>
<select matControl>
<select matNativeControl>
<option value="volvo">Volvo</option>
<option value="saab">Saab</option>
<option value="mercedes">Mercedes</option>
Expand All @@ -37,7 +37,7 @@ <h4>Floating label</h4>
</mat-form-field>
<mat-form-field>
<mat-label>Not float when empty</mat-label>
<select matControl>
<select matNativeControl>
<option value="" selected></option>
<option value="saab">Saab</option>
<option value="mercedes">Mercedes</option>
Expand All @@ -46,7 +46,7 @@ <h4>Floating label</h4>
</mat-form-field>
<mat-form-field>
<mat-label>Float with no value, but with label</mat-label>
<select matControl>
<select matNativeControl>
<option value="" selected label="--select one--"></option>
<option value="saab">Saab</option>
<option value="mercedes">Mercedes</option>
Expand All @@ -55,7 +55,7 @@ <h4>Floating label</h4>
</mat-form-field>
<mat-form-field>
<mat-label>Float with no value, but with html</mat-label>
<select matControl>
<select matNativeControl>
<option value="" selected>--select one--</option>
<option value="saab">Saab</option>
<option value="mercedes">Mercedes</option>
Expand All @@ -65,7 +65,7 @@ <h4>Floating label</h4>
<h4>Looks</h4>
<mat-form-field appearance="legacy">
<mat-label>Legacy</mat-label>
<select matControl required>
<select matNativeControl required>
<option value="volvo">Volvo</option>
<option value="saab">Saab</option>
<option value="mercedes">Mercedes</option>
Expand All @@ -74,7 +74,7 @@ <h4>Looks</h4>
</mat-form-field>
<mat-form-field appearance="standard">
<mat-label>Standard</mat-label>
<select matControl required>
<select matNativeControl required>
<option value="volvo">Volvo</option>
<option value="saab">Saab</option>
<option value="mercedes">Mercedes</option>
Expand All @@ -83,7 +83,7 @@ <h4>Looks</h4>
</mat-form-field>
<mat-form-field appearance="fill">
<mat-label>Fill</mat-label>
<select matControl required>
<select matNativeControl required>
<option value="volvo">Volvo</option>
<option value="saab">Saab</option>
<option value="mercedes">Mercedes</option>
Expand All @@ -92,7 +92,7 @@ <h4>Looks</h4>
</mat-form-field>
<mat-form-field appearance="outline">
<mat-label>Outline</mat-label>
<select matControl>
<select matNativeControl>
<option value="volvo">volvo</option>
<option value="saab">Saab</option>
<option value="mercedes">Mercedes</option>
Expand All @@ -101,7 +101,7 @@ <h4>Looks</h4>
</mat-form-field>
<h4>Option group</h4>
<mat-form-field>
<select matControl>
<select matNativeControl>
<optgroup label="Swedish Cars">
<option value="volvo">volvo</option>
<option value="saab">Saab</option>
Expand All @@ -114,7 +114,7 @@ <h4>Option group</h4>
</mat-form-field>
<h4>Place holder</h4>
<mat-form-field class="demo-full-width">
<select matControl placeholder="place holder">
<select matNativeControl placeholder="place holder">
<option value="" disabled selected></option>
<option value="volvo">Volvo</option>
<option value="saab" disabled>Saab</option>
Expand All @@ -125,7 +125,7 @@ <h4>Place holder</h4>
<h4>Error message, hint, form sumbit</h4>
<mat-form-field class="demo-full-width">
<mat-label>Select your car (required)</mat-label>
<select matControl required [formControl]="selectFormControl">
<select matNativeControl required [formControl]="selectFormControl">
<option label="--select something --"></option>
<option value="saab">Saab</option>
<option value="mercedes">Mercedes</option>
Expand All @@ -140,7 +140,7 @@ <h4>Error message, hint, form sumbit</h4>
<h4>Error message with errorStateMatcher</h4>
<mat-form-field class="demo-full-width">
<mat-label>Select your car</mat-label>
<select matControl required [formControl]="selectFormControl" [errorStateMatcher]="matcher">
<select matNativeControl required [formControl]="selectFormControl" [errorStateMatcher]="matcher">
<option label="--select something --"></option>
<option value="saab">Saab</option>
<option value="mercedes">Mercedes</option>
Expand Down
6 changes: 3 additions & 3 deletions src/lib/form-field/form-field.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ In this document, "form field" refers to the wrapper component `<mat-form-field>
(e.g. the input, textarea, select, etc.)

The following Angular Material components are designed to work inside a `<mat-form-field>`:
* [`<input matInput>` &amp; `<textarea matInput>`](https://material.angular.io/components/input/overview)
* [`<select matInput>`](https://material.angular.io/components/select/overview)
* [`<input matNativeControl>` &amp; `<textarea matNativeControl>`](https://material.angular.io/components/input/overview)
* [`<select matNativeControl>`](https://material.angular.io/components/select/overview)
* [`<mat-select>`](https://material.angular.io/components/select/overview)
* [`<mat-chip-list>`](https://material.angular.io/components/chips/overview)

Expand Down Expand Up @@ -42,7 +42,7 @@ want a floating label, add a `<mat-label>` to the `mat-form-field`.
### Floating label

The floating label is a text label displayed on top of the form field control when
the control does not contain any text or when `<select matInput>` does not show any option text.
the control does not contain any text or when `<select matNativeControl>` does not show any option text.
By default, when text is present the floating label
floats above the form field control. The label for a form field can be specified by adding a
`mat-label` element.
Expand Down
11 changes: 5 additions & 6 deletions src/lib/input/input.scss
Original file line number Diff line number Diff line change
Expand Up @@ -122,17 +122,16 @@ textarea.mat-input-element {
margin: -2px 0;
}

// Remove select button from IE11
select.mat-input-element::-ms-expand {
display: none;
}

//encoded material design select arrow svg
// Encoded material design select arrow svg
/* stylelint-disable max-line-length */
$mat-native-select-arrow-svg: 'data:image/svg+xml;charset=utf8,%3Csvg%20width%3D%2210%22%20height%3D%225%22%20viewBox%3D%227%2010%2010%205%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill%3D%22%230%22%20fill-rule%3D%22evenodd%22%20opacity%3D%22.54%22%20d%3D%22M7%2010l5%205%205-5z%22%2F%3E%3C%2Fsvg%3E';
/* stylelint-enable */

// Remove the native select down arrow and replace it with material design arrow
select.mat-input-element {
&::-ms-expand {
display: none;
}
-moz-appearance: none;
-webkit-appearance: none;
position: relative;
Expand Down
43 changes: 22 additions & 21 deletions src/lib/input/input.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -448,19 +448,19 @@ describe('MatInput without forms', () => {
fixture.detectChanges();

const formFieldEl =
fixture.debugElement.query(By.css('.mat-form-field')).nativeElement;
const inputEl = fixture.debugElement.query(By.css('select')).nativeElement;
fixture.debugElement.query(By.css('.mat-form-field')).nativeElement;
const selectEl = fixture.debugElement.query(By.css('select')).nativeElement;

expect(formFieldEl.classList.contains('mat-form-field-disabled'))
.toBe(false, `Expected form field not to start out disabled.`);
expect(inputEl.disabled).toBe(false);
.toBe(false, `Expected form field not to start out disabled.`);
expect(selectEl.disabled).toBe(false);

fixture.componentInstance.disabled = true;
fixture.detectChanges();

expect(formFieldEl.classList.contains('mat-form-field-disabled'))
.toBe(true, `Expected form field to look disabled after property is set.`);
expect(inputEl.disabled).toBe(true);
.toBe(true, `Expected form field to look disabled after property is set.`);
expect(selectEl.disabled).toBe(true);
}));

it('supports the required attribute as binding', fakeAsync(() => {
Expand All @@ -481,14 +481,14 @@ describe('MatInput without forms', () => {
const fixture = createComponent(MatInputSelect);
fixture.detectChanges();

const inputEl = fixture.debugElement.query(By.css('select')).nativeElement;
const selectEl = fixture.debugElement.query(By.css('select')).nativeElement;

expect(inputEl.required).toBe(false);
expect(selectEl.required).toBe(false);

fixture.componentInstance.required = true;
fixture.detectChanges();

expect(inputEl.required).toBe(true);
expect(selectEl.required).toBe(true);
}));

it('supports the type attribute as binding', fakeAsync(() => {
Expand Down Expand Up @@ -646,14 +646,14 @@ describe('MatInput without forms', () => {
}));

it('should floating labels when select has no value but has option innerHTML',
fakeAsync(() => {
const fixture = createComponent(MatInputSelectWithInnerHtml);
fixture.detectChanges();
fakeAsync(() => {
const fixture = createComponent(MatInputSelectWithInnerHtml);
fixture.detectChanges();

const formFieldEl = fixture.debugElement.query(By.css('.mat-form-field'))
const formFieldEl = fixture.debugElement.query(By.css('.mat-form-field'))
.nativeElement;
expect(formFieldEl.classList).toContain('mat-form-field-should-float');
}));
expect(formFieldEl.classList).toContain('mat-form-field-should-float');
}));

it('should never float the label when floatLabel is set to false', fakeAsync(() => {
let fixture = createComponent(MatInputWithDynamicLabel);
Expand Down Expand Up @@ -1373,7 +1373,7 @@ function createComponent<T>(component: Type<T>,
@Component({
template: `
<mat-form-field>
<input matInput id="test-id" placeholder="test">
<input matNativeControl id="test-id" placeholder="test">
</mat-form-field>`
})
class MatInputWithId {}
Expand Down Expand Up @@ -1580,7 +1580,8 @@ class MatInputWithDynamicLabel {
@Component({
template: `
<mat-form-field>
<textarea matInput [rows]="rows" [cols]="cols" [wrap]="wrap" placeholder="Snacks"></textarea>
<textarea matNativeControl [rows]="rows" [cols]="cols" [wrap]="wrap" placeholder="Snacks">
</textarea>
</mat-form-field>`
})
class MatInputTextareaWithBindings {
Expand Down Expand Up @@ -1807,7 +1808,7 @@ class AutosizeTextareaInAStep {}
@Component({
template: `
<mat-form-field>
<select matInput id="test-id" [disabled]="disabled" [required]="required">
<select matNativeControl id="test-id" [disabled]="disabled" [required]="required">
<option value="volvo">Volvo</option>
<option value="saab">Saab</option>
<option value="mercedes">Mercedes</option>
Expand All @@ -1823,7 +1824,7 @@ class MatInputSelect {
@Component({
template: `
<mat-form-field>
<select matInput>
<select matNativeControl>
<option value="" disabled selected></option>
<option value="saab">Saab</option>
<option value="mercedes">Mercedes</option>
Expand All @@ -1836,7 +1837,7 @@ class MatInputSelectWithNoLabelNoValue {}
@Component({
template: `
<mat-form-field>
<select matInput>
<select matNativeControl>
<option value="" label="select a car"></option>
<option value="saab">Saab</option>
<option value="mercedes">Mercedes</option>
Expand All @@ -1849,7 +1850,7 @@ class MatInputSelectWithLabel {}
@Component({
template: `
<mat-form-field>
<select matInput>
<select matNativeControl>
<option value="">select a car</option>
<option value="saab">Saab</option>
<option value="mercedes">Mercedes</option>
Expand Down
16 changes: 9 additions & 7 deletions src/lib/input/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ export const _MatInputMixinBase = mixinErrorState(MatInputBase);

/** Directive that allows a native input to work inside a `MatFormField`. */
@Directive({
selector: `input[matInput], textarea[matInput], select[matControl]`,
selector: `input[matInput], textarea[matInput], select[matNativeControl],
input[matNativeControl], textarea[matNativeControl]`,
exportAs: 'matInput',
host: {
/**
Expand All @@ -72,6 +73,7 @@ export const _MatInputMixinBase = mixinErrorState(MatInputBase);
'[attr.placeholder]': 'placeholder',
'[disabled]': 'disabled',
'[required]': 'required',
'[attr.readonly]': '_isNativeSelect ? null : this._readonly',
'[attr.aria-describedby]': '_ariaDescribedby || null',
'[attr.aria-invalid]': 'errorState',
'[attr.aria-required]': 'required.toString()',
Expand Down Expand Up @@ -361,12 +363,12 @@ export class MatInput extends _MatInputMixinBase implements MatFormFieldControl<
*/
get shouldLabelFloat(): boolean {
if (this._isNativeSelect) {
// For multi select, float mat input label
// If single select has value or has a option label or html, float mat input label to avoid
// mat input label to overlap with the select content.
const selectElement = this._elementRef.nativeElement;
return selectElement.multiple || !this.empty || selectElement.options[0].label
|| this.focused;
// For a single-selection `<select>`, the label should float when the selected option has
// a non-empty display value. For a `<select multiple>`, the label *always* floats to avoid
// overlapping the label with the options.
const selectElement = this._elementRef.nativeElement;
return selectElement.multiple || !this.empty || selectElement.options[0].label ||
this.focused;
} else {
return this.focused || !this.empty;
}
Expand Down
13 changes: 8 additions & 5 deletions src/lib/select/select.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ To add options to the select, add `<mat-option>` elements to the `<mat-select>`.
has a `value` property that can be used to set the value that will be selected if the user chooses
this option. The content of the `<mat-option>` is what will be shown to the user.

Angular Material also supports use of native `<select>` element inside of
Angular Material also supports use of the native `<select>` element inside of
`<mat-form-field>`. The native control has several performance, accessibility,
and usability advantages. See [the documentation for
form-field](https://material.angular.io/components/form-field) for more information.

To use native select inside `<mat-form-field>`, add `matInput` as an attribute to native html select.
To use a native select inside `<mat-form-field>`, add the `matNativeControl` attribute
to the `<select>` element.

<!-- example(select-overview) -->

Expand Down Expand Up @@ -52,7 +53,7 @@ In some cases that `<mat-form-field>` may use the placeholder as the label (see
### Disabling the select or individual options

It is possible to disable the entire select or individual options in the select by using the
disabled property on the `<select>` or `<mat-select>` and the `<option>` or <mat-option>` components respectively.
disabled property on the `<select>` or `<mat-select>` and the `<option>` or <mat-option>` elements respectively.

<!-- example(select-disabled) -->

Expand All @@ -78,7 +79,9 @@ by setting the `multiple` property. This will allow the user to select multiple
using the `<mat-select>` in multiple selection mode, its value will be a sorted list of all selected
values rather than a single value.

Multiple select with `<select multiple>` is not recommended.
Using multiple selection with a native select element (`<select multiple>`) is discouraged
inside `<mat-form-field>`, as the inline listbox appearance is inconsistent with other
Material Design components.

<!-- example(select-multiple) -->

Expand Down Expand Up @@ -144,7 +147,7 @@ The `<mat-select>` component without text or label should be given a meaningful

The `<mat-select>` component has `role="listbox"` and options inside select have `role="option"`.

The `<select>` offers better accessibility by supporting all the native html accessibility capability.
The native `<select>` offers the best accessibility because it is supported directly by screen-readers.

### Troubleshooting

Expand Down
Loading

0 comments on commit 63f07ca

Please sign in to comment.