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(form-field): support native select element #12707

Merged
merged 3 commits into from
Aug 30, 2018

Conversation

vivian-hu-zz
Copy link
Contributor

No description provided.

@googlebot googlebot added the cla: yes PR author has agreed to Google's Contributor License Agreement label Aug 16, 2018
@vivian-hu-zz vivian-hu-zz changed the title List2 feat(input): add native select to be form input. Aug 16, 2018
@@ -8,6 +8,7 @@ In this document, "form field" refers to the wrapper component `<mat-form-field>

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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be <select matNativeSelect>?

(here and elsewhere)

Now that I'm thinking about it, we could deprecate matInput and make the selector matControl work for both... @mmalerba thoughts?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe matFormControl for consistency with mat-form-field?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like it to be something short, matInput,matSelect, and matControl are all fine by me

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

after team discussion, decided to go with matNativeControl

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still need to update these lines

// Remove select button from IE11
select::-ms-expand {
display: none;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will target all <select> elements; it should be restricted to a CSS class that's applied by the directive

display: none;
}

select[matInput] {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer CSS class selectors to attribute selectors

-webkit-appearance: none;
position: relative;
background-color: transparent;
background-image: url('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');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you extract the uri portion of this into a sass variable with a comment explaining what it is?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK we can't use data URIs in CSS. See #8898.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We couldn't think of another way to do this, so we checked directly with Google's security team. They said that they see no reason to ban data: URIs, so we can relax this restriction. I do want to add documentation that talks about our CSP support.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security team approved using data URI for image.

background-repeat: no-repeat;
display: inline-flex;
box-sizing: border-box;
background-position: right center;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be flipped in RTL?

}
}

private shouldLabelFloatForSelect(selectElement:HTMLSelectElement): boolean {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Space after colon

}
}

private shouldLabelFloatForSelect(selectElement:HTMLSelectElement): boolean {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method should be prefixed with an underscore (applies to any non-public api)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should have another class only with the select functionality that extends MatInput? Otherwise we'll end up sprinkling if (isSelect) checks everywhere.

@@ -1,8 +1,11 @@
Angular material supports both native html select and `<mat-select>` to work inside a
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would leave the original text for this file the same, and then add a new paragraph underneath that says something like

Angular Material also supports use of native the `<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.

@@ -71,11 +74,13 @@ 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>` is not recommended.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would specifically use <select multiple> here

@@ -8,6 +8,7 @@ In this document, "form field" refers to the wrapper component `<mat-form-field>

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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe matFormControl for consistency with mat-form-field?

-webkit-appearance: none;
position: relative;
background-color: transparent;
background-image: url('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');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK we can't use data URIs in CSS. See #8898.

@@ -572,6 +614,41 @@ describe('MatInput without forms', () => {
expect(formFieldEl.classList).toContain('mat-form-field-should-float');
}));

it('should floating labels when select has value', fakeAsync(() => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"should floating" -> "should float"

expect(formFieldEl.classList).toContain('mat-form-field-should-float');
}));

it('should not floating labels when select has no value, no option label, ' +
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

floating -> float

@@ -351,7 +350,26 @@ export class MatInput extends _MatInputMixinBase implements MatFormFieldControl<
* Implemented as part of MatFormFieldControl.
* @docs-private
*/
get shouldLabelFloat(): boolean { return this.focused || !this.empty; }
get shouldLabelFloat(): boolean {
if (this._elementRef.nativeElement.nodeName.toLowerCase() === 'select') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of doing this in a getter, the check can be done in the constructor since the element's nodeName won't change.

}
}

private shouldLabelFloatForSelect(selectElement:HTMLSelectElement): boolean {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should have another class only with the select functionality that extends MatInput? Otherwise we'll end up sprinkling if (isSelect) checks everywhere.

if (selectElement.multiple) {
// For multi select, float mat input label
return true;
} else if (!this.empty || selectElement.options[0].label || selectElement.options[0].innerHTML) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is being called in a getter, it can be very expensive to keep checking innerHTML. We should either move away from checking the DOM or check textContent.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can just use label to cover both. from the documentation "label
This attribute is text for the label indicating the meaning of the option. If the label attribute isn't defined, its value is that of the element text content."
And testing confirmed that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this method can be simplified into one statement:

return selectedElement.multiple || this.focused || !this.empty ||
    selectElement.options[0].label || selectElement.options[0].innerHTML;

Angular material supports both native html select and `<mat-select>` to work inside a
[`<mat-form-field>`] element.(https://material.angular.io/components/form-field/overview) element.

To use native select inside `<mat-form-field>`, add `matInput` as a attribute to native html select.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"as a" -> "as an"

@josephperrott josephperrott added the target: minor This PR is targeted for the next minor release label Aug 17, 2018
Copy link
Contributor Author

@vivian-hu-zz vivian-hu-zz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed review comments.

After team discussion, decided to go with matNativeControl for input, textarea and select. Changed the existing input and textarea to use matNativeControl in a few places to make sure both matInput and matNativeControl works for input/textarea.

@ngbot
Copy link

ngbot bot commented Aug 20, 2018

Hi @vivian-hu! This PR has merge conflicts due to recent upstream merges.
Please help to unblock it by resolving these conflicts. Thanks!

display: none;
}

//encoded material design select arrow svg
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment should have a spacer after // and start with a capital letter:

// Drowndown arrow svg icon, url-encoded.

$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 */

select.mat-input-element {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment here that explains what this class is doing.

// 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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra space

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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line-break and capitalization is off. Also, should this say "For native select" instead of "For multi select"?

// 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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer putting the operator on the previous line:

return selectElement.multiple || !this.empty || selectElement.options[0].label ||
    this.focused;

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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update selector here

The select component has `role="listbox"` and options inside select have `role="option"`.
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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rephrase this

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

Copy link
Contributor Author

@vivian-hu-zz vivian-hu-zz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed code review comments.

@@ -8,6 +8,7 @@ In this document, "form field" refers to the wrapper component `<mat-form-field>

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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still need to update these lines

@@ -41,7 +42,8 @@ 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. By default, when text is present the floating label
the control does not contain any text or when `<select matInput>` does not show any option text.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still needs updated selector

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can move this into the rule below like this:

select.mat-input-element {
  &::-ms-expand {
    display: none;
  }

  -moz-appearance: none;
  ...
}


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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inputEl -> selectElement (or selectEl) here and other places where it is a select element and not an input

if (this._isNativeSelect) {
// If multi select, float mat-label.
// If single select with value, with an option label or html, float mat-label to avoid
// it overlapping with select content.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about

// 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.

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 `matNativeControl` as an attribute to native html select.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@@ -45,7 +52,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 `<mat-select>` and the `<mat-option>` components respectively.
disabled property on the `<select>` or `<mat-select>` and the `<option>` or <mat-option>` components respectively.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change components here to elements since <option> is not a component.

@@ -71,11 +78,13 @@ 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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about

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.

Copy link
Contributor Author

@vivian-hu-zz vivian-hu-zz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update based on review feedback

@@ -8,6 +8,7 @@ In this document, "form field" refers to the wrapper component `<mat-form-field>

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 matNativeControl>`](https://material.angular.io/components/select/overview)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should also update the line above:
<input matNativeControl> and <textarea matNativeControl>

fixture.detectChanges();

const formFieldEl =
fixture.debugElement.query(By.css('.mat-form-field')).nativeElement;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just noticed this, but line overflow indent should be +4 rather than +2
(here and elsewhere)

@@ -8,6 +8,14 @@ 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

of native `<select>` element -> of the native `<select>` element

@vivian-hu-zz vivian-hu-zz force-pushed the list2 branch 2 times, most recently from 75b57d4 to 47fcaf8 Compare August 22, 2018 21:39
Copy link
Contributor Author

@vivian-hu-zz vivian-hu-zz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed indentation and updated selector name for input and textarea.

Copy link
Member

@jelbourn jelbourn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@jelbourn jelbourn added pr: lgtm action: merge The PR is ready for merge by the caretaker target: major This PR is targeted for the next major release merge: fix commit message When the PR is merged, rewrites/fixups of the commit messages are needed and removed target: minor This PR is targeted for the next minor release labels Aug 22, 2018
@jelbourn
Copy link
Member

Commit message should be:

feat(form-field): support native select element

@vivian-hu-zz vivian-hu-zz changed the title feat(input): add native select to be form input. feat(form-field): support native select element Aug 23, 2018
@ngbot
Copy link

ngbot bot commented Aug 23, 2018

Hi @vivian-hu! This PR has merge conflicts due to recent upstream merges.
Please help to unblock it by resolving these conflicts. Thanks!

@vivian-hu-zz vivian-hu-zz force-pushed the list2 branch 2 times, most recently from 63f07ca to 4e04c81 Compare August 27, 2018 23:55
Copy link
Contributor Author

@vivian-hu-zz vivian-hu-zz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed the way to set "Readonly" attribute.

@vivian-hu-zz vivian-hu-zz added the P2 The issue is important to a large percentage of users, with a workaround label Aug 28, 2018
@jelbourn jelbourn removed the merge: fix commit message When the PR is merged, rewrites/fixups of the commit messages are needed label Aug 30, 2018
@jelbourn jelbourn merged commit 4e41985 into angular:master Aug 30, 2018
@vivian-hu-zz vivian-hu-zz deleted the list2 branch January 23, 2019 23:00
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 10, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
action: merge The PR is ready for merge by the caretaker cla: yes PR author has agreed to Google's Contributor License Agreement P2 The issue is important to a large percentage of users, with a workaround target: major This PR is targeted for the next major release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants