diff --git a/packages/mdc-select/README.md b/packages/mdc-select/README.md
index e98f42c7cea..34ba672d442 100644
--- a/packages/mdc-select/README.md
+++ b/packages/mdc-select/README.md
@@ -49,8 +49,9 @@ The select requires that you set the `width` of the `mdc-select__anchor` element
```html
+
+
-
Pick a Food Group
@@ -115,9 +116,12 @@ The following is an example of the select component with all of the necessary ar
```html
-
+
+
-
Vegetables
Pick a Food Group
@@ -155,9 +159,9 @@ same.
```html
-
+
+
-
@@ -183,8 +187,8 @@ to set the selected item. The select also needs the text from the selected eleme
```html
+
-
Vegetables
Pick a Food Group
@@ -225,13 +229,13 @@ the list with an empty `data-value` attribute.
#### Required select
To style a select menu as required and enable validation, add the `mdc-select--required` class to the `mdc-select` element
-and set the `aria-required` attribute on the `mdc-select__selected-text` element to be `"true"`.
+and set the `aria-required` attribute on the `mdc-select__anchor` element to be `"true"`.
```html
-
+
+
-
Pick a Food Group
@@ -251,9 +255,9 @@ Add the `mdc-select--disabled` class to the `mdc-select` element and and set the
```html
-
+
+
-
Pick a Food Group
@@ -323,8 +327,8 @@ structure.
```html
+
-
@@ -356,8 +360,8 @@ structure.
```html
+
-
@@ -399,13 +403,14 @@ Mixin | Description
`label-color($color)` | Customizes the label color of the select in the unfocused state.
`focused-label-color($color)` | Customizes the label color of the select when focused.
`bottom-line-color($color)` | Customizes the color of the default bottom line of the select.
+`hover-bottom-line-color($color)` | Customizes the color of the bottom line when the select is hovered.
`focused-bottom-line-color($color)` | Customizes the color of the bottom line of the select when focused.
`shape-radius($radius, $rtl-reflexive)` | Sets rounded shape to boxed select variant with given radius size. Set `$rtl-reflexive` to true to flip radius values in RTL context, defaults to false.
-`hover-bottom-line-color($color)` | Customizes the color of the bottom line when the select is hovered.
`outline-color($color)` | Customizes the color of the notched outline.
`outline-shape-radius($radius, $rtl-reflexive)` | Sets the border radius of of the outlined select variant. Set `$rtl-reflexive` to true to flip radius values in RTL context, defaults to false.
`focused-outline-color($color)` | Customizes the color of the outline of the select when focused.
`hover-outline-color($color)` | Customizes the color of the outline when the select is hovered.
+`height($height)` | Sets height of the filled select variant.
> _NOTE_: To further customize the floating label, please see the [floating label documentation](./../mdc-floating-label/README.md).
@@ -455,9 +460,9 @@ If you are using a JavaScript framework, such as React or Angular, you can creat
| `setRippleCenter(normalizedX: number) => void` | Sets the line ripple center to the provided normalizedX value. |
| `notifyChange(value: string) => void` | Emits the `MDCSelect:change` event when an element is selected. |
| `setSelectedText(text: string) => void` | Sets the text content of the selectedText element to the given string. |
-| `isSelectedTextFocused() => boolean` | Returns whether the selected text element is focused. |
-| `getSelectedTextAttr(attr: string) => string` | Gets the given attribute on the selected text element. |
-| `setSelectedTextAttr(attr: string, value: string) => void` | Sets the given attribute on the selected text element. |
+| `isSelectAnchorFocused() => boolean` | Returns whether the select anchor element is focused. |
+| `getSelectAnchorAttr(attr: string) => string` | Gets the given attribute on the select anchor element. |
+| `setSelectAnchorAttr(attr: string, value: string) => void` | Sets the given attribute on the select anchor element. |
| `openMenu() => void` | Causes the menu element in the select to open. |
| `closeMenu() => void` | Causes the menu element in the select to close. |
| `getAnchorElement() => Element` | Returns the select anchor element. |
diff --git a/packages/mdc-select/_mixins.scss b/packages/mdc-select/_mixins.scss
index fd4c7197118..67d09520db1 100644
--- a/packages/mdc-select/_mixins.scss
+++ b/packages/mdc-select/_mixins.scss
@@ -38,7 +38,7 @@
@use "@material/theme/variables" as theme-variables;
@use "@material/typography/mixins" as typography-mixins;
@use "./helper-text/mixins" as helper-text-mixins;
-@use "./icon/mixins";
+@use "./icon/variables" as icon-variables;
@use "./variables";
@mixin core-styles($query: feature-targeting-functions.all()) {
@@ -57,7 +57,6 @@
@include label-color(variables.$label-color, $query: $query);
@include bottom-line-color(variables.$bottom-line-idle-color, $query: $query);
@include helper-text-mixins.helper-text-color(variables.$helper-text-color, $query: $query);
- @include shape-radius(small, $query: $query);
// Focused state colors
@include focused-bottom-line-color(primary, $query: $query);
@@ -69,6 +68,15 @@
// Floating label private mixin
@include floating-label_($query: $query);
+ // structural
+ @include shape-radius(small, $query: $query);
+ @include padding-horizontal_(
+ $left: variables.$anchor-padding-left,
+ $left-with-leading-icon: variables.$anchor-padding-left-with-leading-icon,
+ $right: variables.$anchor-padding-right,
+ $query: $query
+ );
+
@include feature-targeting-mixins.targets($feat-structure) {
position: relative; // Menu is absolutely positioned relative to this.
}
@@ -77,11 +85,13 @@
@include dd-arrow-svg-bg_(variables.$dropdown-color, variables.$dropdown-opacity, $query: $query);
@include feature-targeting-mixins.targets($feat-structure) {
- @include rtl-mixins.reflexive(left, auto, right, 8px);
- position: absolute;
- bottom: 16px;
+ @include rtl-mixins.reflexive-property(margin,
+ icon-variables.$icon-horizontal-margin,
+ icon-variables.$icon-horizontal-margin);
width: 24px;
height: 24px;
+ align-self: center;
+ flex-shrink: 0;
pointer-events: none;
}
@@ -106,16 +116,17 @@
}
.mdc-select__anchor {
+ @include height(variables.$height, $query: $query);
@include floating-label-mixins.float-position(variables.$label-position-y, $query: $query);
@include feature-targeting-mixins.targets($feat-structure) {
display: inline-flex;
position: relative;
box-sizing: border-box;
- height: variables.$height;
overflow: hidden;
- /* @alternate */
- will-change: opacity, transform, color;
+ outline: none;
+ cursor: pointer;
+ min-width: 200px;
}
@include focused-line-ripple_ {
@@ -176,10 +187,6 @@
@include disabled_($query: $query);
}
- .mdc-select--no-label {
- @include no-label_($query: $query);
- }
-
.mdc-select--with-leading-icon {
@include with-leading-icon_($query: $query);
}
@@ -206,14 +213,21 @@
@mixin ripple($query: feature-targeting-functions.all()) {
.mdc-select__anchor {
- @include ripple-mixins.surface($query: $query);
- @include ripple-mixins.radius-bounded($query: $query);
- @include ripple-mixins.states-base-color(variables.$ink-color, $query: $query);
+ @include ripple-mixins.surface($ripple-target: variables.$ripple-target, $query: $query);
+ @include ripple-mixins.radius-bounded($ripple-target: variables.$ripple-target, $query: $query);
+ @include ripple-mixins.states-base-color(variables.$ink-color, $ripple-target: variables.$ripple-target, $query: $query);
@include ripple-mixins.states-opacities(
(
hover: ripple-functions.states-opacity(variables.$ink-color, hover),
focus: ripple-functions.states-opacity(variables.$ink-color, focus),
- ), $query: $query);
+ ),
+ $ripple-target: variables.$ripple-target,
+ $query: $query
+ );
+
+ #{variables.$ripple-target} {
+ @include ripple-mixins.target-common($query: $query);
+ }
}
.mdc-select__menu .mdc-list .mdc-list-item--selected {
@@ -314,12 +328,12 @@
}
@if ($resolved-radius > notched-outline-variables.$leading-width) {
- .mdc-select__selected-text {
+ .mdc-select__anchor {
@include feature-targeting-mixins.targets($feat-structure) {
@include rtl-mixins.reflexive-property(
padding,
$resolved-radius + notched-outline-variables.$padding,
- variables.$arrow-padding
+ 0
);
}
}
@@ -423,59 +437,54 @@
}
@mixin floating-label_($query: feature-targeting-functions.all()) {
- $feat-structure: feature-targeting-functions.create-target($query, structure);
+ $feat-structure: feature-targeting-functions.create-target($query, structure);
.mdc-floating-label {
@include feature-targeting-mixins.targets($feat-structure) {
@include rtl-mixins.reflexive-position(left, variables.$outline-label-offset);
- top: 21px;
+ top: 50%;
+ transform: translateY(-50%);
pointer-events: none;
}
}
- &.mdc-select--with-leading-icon {
+ &.mdc-select--outlined {
.mdc-floating-label {
@include feature-targeting-mixins.targets($feat-structure) {
- @include rtl-mixins.reflexive-position(left, variables.$icon-padding);
+ @include rtl-mixins.reflexive-position(left, notched-outline-variables.$padding);
}
}
}
+}
+
+@mixin with-leading-icon_($query: feature-targeting-functions.all()) {
+ $feat-structure: feature-targeting-functions.create-target($query, structure);
+
+ $icon-total-width: icon-variables.$icon-size + 2 * icon-variables.$icon-horizontal-margin;
+
+ .mdc-floating-label {
+ @include feature-targeting-mixins.targets($feat-structure) {
+ @include rtl-mixins.reflexive-position(left, $icon-total-width);
+ }
+ }
&.mdc-select--outlined {
.mdc-floating-label {
@include feature-targeting-mixins.targets($feat-structure) {
- @include rtl-mixins.reflexive-position(left, notched-outline-variables.$padding);
-
- top: 17px;
+ @include rtl-mixins.reflexive-position(left, $icon-total-width - notched-outline-variables.$leading-width);
}
- }
- &.mdc-select--with-leading-icon {
- .mdc-floating-label {
+ &--float-above {
@include feature-targeting-mixins.targets($feat-structure) {
- @include rtl-mixins.reflexive-position(left, variables.$icon-padding - notched-outline-variables.$leading-width);
- }
-
- &--float-above {
- @include feature-targeting-mixins.targets($feat-structure) {
- @include rtl-mixins.reflexive-position(left, variables.$icon-padding - notched-outline-variables.$leading-width);
- }
+ @include rtl-mixins.reflexive-position(left, $icon-total-width - notched-outline-variables.$leading-width);
}
}
}
- }
-}
-@mixin with-leading-icon_($query: feature-targeting-functions.all()) {
- $feat-structure: feature-targeting-functions.create-target($query, structure);
-
- @include mixins.icon-horizontal-position_(16px, variables.$icon-padding, $query: $query);
-
- &.mdc-select--outlined {
- @include notched-outline-mixins.floating-label-float-position(
+ @include notched-outline-mixins.floating-label-float-position-absolute(
variables.$outlined-label-position-y,
- variables.$outlined-with-leading-icon-label-position-x,
+ $icon-total-width - icon-variables.$icon-horizontal-margin - notched-outline-variables.$notch-gutter-size,
$query: $query
);
@include floating-label-mixins.shake-animation(select-outlined-leading-icon, $query: $query);
@@ -500,8 +509,6 @@
@include typography-mixins.typography(subtitle1, $query: $query);
@include feature-targeting-mixins.targets($feat-structure) {
- @include rtl-mixins.reflexive-property(padding, variables.$label-padding, variables.$arrow-padding);
-
&::-ms-expand {
display: none;
}
@@ -511,23 +518,17 @@
color: inherit;
}
- // counteracts the extra text padding that Firefox adds by default
- // stylelint-disable-next-line function-url-quotes
- @-moz-document url-prefix("") {
- text-indent: -2px;
- }
-
box-sizing: border-box;
- width: 100%;
- min-width: 200px;
- height: variables.$height;
- padding-top: 20px;
- padding-bottom: 4px;
+ width: 0;
+ flex-grow: 1;
+ height: variables.$selected-text-height;
border: none;
outline: none;
+ padding: 0;
white-space: nowrap;
- cursor: pointer;
appearance: none;
+ pointer-events: none;
+ text-overflow: ellipsis;
}
@include feature-targeting-mixins.targets($feat-color) {
@@ -589,18 +590,6 @@
}
}
-@mixin no-label_($query: feature-targeting-functions.all()) {
- $feat-structure: feature-targeting-functions.create-target($query, structure);
-
- &:not(.mdc-select--outlined) {
- .mdc-select__anchor .mdc-select__selected-text {
- @include feature-targeting-mixins.targets($feat-structure) {
- padding-top: 14px;
- }
- }
- }
-}
-
@mixin outlined_($query: feature-targeting-functions.all()) {
$feat-structure: feature-targeting-functions.create-target($query, structure);
$feat-color: feature-targeting-functions.create-target($query, color);
@@ -618,8 +607,8 @@
.mdc-select__anchor {
@include ripple-mixins.states-base-color(transparent, $query: $query);
- @include floating-label-mixins.shake-animation(text-field-outlined, $query: $query);
- @include notched-outline-mixins.floating-label-float-position(variables.$outlined-label-position-y, 0, $query: $query);
+ @include floating-label-mixins.shake-animation(select-outlined, $query: $query);
+ @include notched-outline-mixins.floating-label-float-position-absolute(variables.$outlined-label-position-y, 0, $query: $query);
@include feature-targeting-mixins.targets($feat-structure) {
overflow: visible;
@@ -628,11 +617,7 @@
.mdc-select__selected-text {
@include feature-targeting-mixins.targets($feat-structure) {
- @include rtl-mixins.reflexive-property(padding, variables.$label-padding, variables.$arrow-padding);
-
display: flex;
- padding-top: 14px;
- padding-bottom: 12px;
border: none;
z-index: 1;
}
@@ -662,19 +647,11 @@
.mdc-select-helper-text {
// stylelint-disable plugin/selector-bem-pattern
.mdc-select__anchor + & {
- @include feature-targeting-mixins.targets($feat-structure) {
- margin-right: 12px;
- margin-left: 12px;
- }
- }
-
- .mdc-select--outlined .mdc-select__anchor + & {
@include feature-targeting-mixins.targets($feat-structure) {
margin-right: 16px;
margin-left: 16px;
}
}
- // stylelint-enable plugin/selector-bem-pattern
}
.mdc-select--focused .mdc-select__anchor + .mdc-select-helper-text:not(.mdc-select-helper-text--validation-msg) {
@@ -683,3 +660,122 @@
}
}
}
+
+/// Adds horizontal padding to the selected text
+///
+/// @param {Number} $left - left side padding
+/// @param {Number} $left-with-leading-icon - left-side padding when a leading
+/// icon is present
+/// @param {Number} $right - right-side padding; note that a trailing icon is
+/// always present.
+@mixin padding-horizontal_(
+ $left,
+ $left-with-leading-icon,
+ $right,
+ $query: feature-targeting-functions.all()
+) {
+ $feat-structure: feature-targeting-functions.create-target($query, structure);
+
+ .mdc-select__anchor {
+ @include feature-targeting-mixins.targets($feat-structure) {
+ @include rtl-mixins.reflexive-property(
+ padding,
+ $left,
+ $right
+ );
+ }
+ }
+
+ &.mdc-select--with-leading-icon .mdc-select__anchor {
+ @include feature-targeting-mixins.targets($feat-structure) {
+ @include rtl-mixins.reflexive-property(
+ padding,
+ $left-with-leading-icon,
+ $right
+ );
+ }
+ }
+}
+
+///
+/// Sets height of default select variant.
+///
+/// @param {Number} $height
+/// @param {Number} $minimum-height-for-filled-label Sets the minimum height for
+/// filled selects at which to allow floating labels.
+/// @param {Number} $filled-baseline-top The baseline from the top of the anchor
+/// that the input should be aligned to for a filled variant with a label
+/// @access public
+///
+@mixin height(
+ $height,
+ $minimum-height-for-filled-label: variables.$minimum-height-for-filled-label,
+ $filled-baseline-top: variables.$filled-baseline-top,
+ $query: feature-targeting-functions.all()
+) {
+ $feat-structure: feature-targeting-functions.create-target($query, structure);
+ @include feature-targeting-mixins.targets($feat-structure) {
+ height: $height;
+ }
+
+ // Filled variant is aligned to baseline...
+ @include typography-mixins.baseline($top: $filled-baseline-top, $display: inline-flex, $query: $query);
+ // ...unless it is too small to display a label
+ @if $height < $minimum-height-for-filled-label {
+ @include center-aligned_($query: $query);
+
+ @include feature-targeting-mixins.targets($feat-structure) {
+ &:not(.mdc-select--outlined) {
+ .mdc-floating-label {
+ display: none;
+ }
+ }
+ }
+ }
+
+ // Outlined and no-label variants are always centered
+ .mdc-select--outlined &,
+ .mdc-select--no-label & {
+ @include center-aligned_($query: $query);
+ }
+}
+
+// Removes filled baseline alignment
+@mixin center-aligned_($query: feature-targeting-functions.all()) {
+ $feat-structure: feature-targeting-functions.create-target($query, structure);
+
+ @include feature-targeting-mixins.targets($feat-structure) {
+ // In order for a flexbox container to participate in baseline alignment,
+ // it follows these rules to determine where its baseline is:
+ // https://www.w3.org/TR/css-flexbox-1/#flex-baselines
+ //
+ // In order to avoid leading icons "controlling" the baseline (since they
+ // are the first child), flexbox will generate a baseline from any child
+ // flex items that participate in baseline alignment.
+ //
+ // Icons are set to "align-self: center", while all other children are
+ // aligned to baseline. The next problem is deciding which child is
+ // used to determine the baseline.
+ //
+ // According to spec, the item with the largest distance between its
+ // baseline and the edge of the cross axis is placed flush with that edge,
+ // making it the baseline of the container.
+ // https://www.w3.org/TR/css-flexbox-1/#baseline-participation
+ //
+ // For the filled variant, the pseudo ::before strut is the "largest"
+ // child since the input has a height of 28px and the strut is 40px. We
+ // can emulate center alignment and force the baseline to use the input
+ // text by making the input the full height of the container and removing
+ // the baseline strut.
+
+ // TODO: IE11 does not respect this, and makes the leading icon (if present)
+ // the baseline.
+ .mdc-select__selected-text {
+ height: 100%;
+ }
+
+ &::before {
+ display: none;
+ }
+ }
+}
diff --git a/packages/mdc-select/_variables.scss b/packages/mdc-select/_variables.scss
index b7e350bbb4d..93dec888e8d 100644
--- a/packages/mdc-select/_variables.scss
+++ b/packages/mdc-select/_variables.scss
@@ -21,13 +21,22 @@
//
@use "sass:color";
-@use "@material/animation/variables" as variables2;
+@use "@material/notched-outline/variables" as notched-outline-variables;
@use "@material/theme/variables";
+@function get-outlined-label-position-y($select-anchor-height) {
+ @return $select-anchor-height / 2 + notched-outline-variables.$label-box-height / 2;
+}
+$ripple-target: '.mdc-select__ripple';
$arrow-padding: 52px !default;
$label-padding: 16px !default;
$height: 56px !default;
-$icon-padding: 48px !default;
+$minimum-height-for-filled-label: 52px !default;
+$filled-baseline-top: 40px !default;
+$selected-text-height: 28px !default;
+$anchor-padding-left: 16px !default;
+$anchor-padding-left-with-leading-icon: 0 !default;
+$anchor-padding-right: 0 !default;
$ink-color: rgba(variables.prop-value(on-surface), .87) !default;
$dropdown-color: variables.prop-value(on-surface) !default;
@@ -55,10 +64,9 @@ $outlined-hover-border: rgba(variables.prop-value(on-surface), .87) !default;
// should be .06 after mdc-select opacity is applied
$outlined-disabled-border: rgba(variables.prop-value(on-surface), .16) !default;
-$label-position-y: 70% !default;
+$label-position-y: 106% !default;
$outline-label-offset: 16px !default;
-$outlined-label-position-y: 130% !default;
-$outlined-dense-label-position-y: 110% !default;
+$outlined-label-position-y: get-outlined-label-position-y($height) !default;
$outlined-with-leading-icon-label-position-x: 32px !default;
$dropdown-transition-duration: 150ms !default;
diff --git a/packages/mdc-select/adapter.ts b/packages/mdc-select/adapter.ts
index 6824108ba09..6ae758ba04d 100644
--- a/packages/mdc-select/adapter.ts
+++ b/packages/mdc-select/adapter.ts
@@ -107,19 +107,19 @@ export interface MDCSelectAdapter {
setSelectedText(text: string): void;
/**
- * Returns whether the selected text element is focused.
+ * Returns whether the select anchor is focused.
*/
- isSelectedTextFocused(): boolean;
+ isSelectAnchorFocused(): boolean;
/**
- * Gets the given attribute on the selected text element.
+ * Gets the given attribute on the select anchor element.
*/
- getSelectedTextAttr(attr: string): string | null;
+ getSelectAnchorAttr(attr: string): string|null;
/**
- * Sets the given attribute on the selected text element.
+ * Sets the given attribute on the select anchor element.
*/
- setSelectedTextAttr(attr: string, value: string): void;
+ setSelectAnchorAttr(attr: string, value: string): void;
// Menu-related methods ======================================================
/**
diff --git a/packages/mdc-select/component.ts b/packages/mdc-select/component.ts
index 80130ef58a9..aad267a7456 100644
--- a/packages/mdc-select/component.ts
+++ b/packages/mdc-select/component.ts
@@ -53,7 +53,7 @@ export class MDCSelect extends MDCComponent {
private menu_!: MDCMenu; // assigned in menuSetup_()
private selectAnchor_!: HTMLElement; // assigned in initialize()
- private selectedText_!: HTMLElement; // assigned in initialize()
+ private selectedText_!: HTMLInputElement; // assigned in initialize()
private menuElement_!: Element; // assigned in menuSetup_()
private leadingIcon_?: MDCSelectIcon; // assigned in initialize()
@@ -79,7 +79,9 @@ export class MDCSelect extends MDCComponent {
helperTextFactory: MDCSelectHelperTextFactory = (el) => new MDCSelectHelperText(el),
) {
this.selectAnchor_ = this.root_.querySelector(strings.SELECT_ANCHOR_SELECTOR) as HTMLElement;
- this.selectedText_ = this.root_.querySelector(strings.SELECTED_TEXT_SELECTOR) as HTMLElement;
+ this.selectedText_ =
+ this.root_.querySelector(strings.SELECTED_TEXT_SELECTOR) as
+ HTMLInputElement;
if (!this.selectedText_) {
throw new Error(
@@ -88,8 +90,9 @@ export class MDCSelect extends MDCComponent {
);
}
- if (this.selectedText_.hasAttribute(strings.ARIA_CONTROLS)) {
- const helperTextElement = document.getElementById(this.selectedText_.getAttribute(strings.ARIA_CONTROLS)!);
+ if (this.selectAnchor_.hasAttribute(strings.ARIA_CONTROLS)) {
+ const helperTextElement = document.getElementById(
+ this.selectAnchor_.getAttribute(strings.ARIA_CONTROLS)!);
if (helperTextElement) {
this.helperText_ = helperTextFactory(helperTextElement);
}
@@ -125,7 +128,7 @@ export class MDCSelect extends MDCComponent {
this.handleFocus_ = () => this.foundation_.handleFocus();
this.handleBlur_ = () => this.foundation_.handleBlur();
this.handleClick_ = (evt) => {
- this.selectedText_.focus();
+ this.selectAnchor_.focus();
this.foundation_.handleClick(this.getNormalizedXCoordinate_(evt));
};
this.handleKeydown_ = (evt) => this.foundation_.handleKeydown(evt);
@@ -133,27 +136,26 @@ export class MDCSelect extends MDCComponent {
this.handleMenuOpened_ = () => this.foundation_.handleMenuOpened();
this.handleMenuClosed_ = () => this.foundation_.handleMenuClosed();
- this.selectedText_.addEventListener('focus', this.handleFocus_);
- this.selectedText_.addEventListener('blur', this.handleBlur_);
+ this.selectAnchor_.addEventListener('focus', this.handleFocus_);
+ this.selectAnchor_.addEventListener('blur', this.handleBlur_);
- this.selectedText_.addEventListener('click', this.handleClick_ as EventListener);
+ this.selectAnchor_.addEventListener(
+ 'click', this.handleClick_ as EventListener);
- this.selectedText_!.addEventListener('keydown', this.handleKeydown_);
+ this.selectAnchor_.addEventListener('keydown', this.handleKeydown_);
this.menu_!.listen(menuSurfaceConstants.strings.CLOSED_EVENT, this.handleMenuClosed_);
this.menu_!.listen(menuSurfaceConstants.strings.OPENED_EVENT, this.handleMenuOpened_);
this.menu_!.listen(menuConstants.strings.SELECTED_EVENT, this.handleMenuItemAction_);
this.foundation_.init();
-
- // Sets disabled state in foundation
- this.disabled = this.root_.classList.contains(cssClasses.DISABLED);
}
destroy() {
- this.selectedText_.removeEventListener('change', this.handleChange_);
- this.selectedText_.removeEventListener('focus', this.handleFocus_);
- this.selectedText_.removeEventListener('blur', this.handleBlur_);
- this.selectedText_.removeEventListener('keydown', this.handleKeydown_);
- this.selectedText_.removeEventListener('click', this.handleClick_ as EventListener);
+ this.selectAnchor_.removeEventListener('change', this.handleChange_);
+ this.selectAnchor_.removeEventListener('focus', this.handleFocus_);
+ this.selectAnchor_.removeEventListener('blur', this.handleBlur_);
+ this.selectAnchor_.removeEventListener('keydown', this.handleKeydown_);
+ this.selectAnchor_.removeEventListener(
+ 'click', this.handleClick_ as EventListener);
this.menu_.unlisten(menuSurfaceConstants.strings.CLOSED_EVENT, this.handleMenuClosed_);
this.menu_.unlisten(menuSurfaceConstants.strings.OPENED_EVENT, this.handleMenuOpened_);
@@ -279,8 +281,12 @@ export class MDCSelect extends MDCComponent {
// tslint:disable:object-literal-sort-keys Methods should be in the same order as the adapter interface.
const adapter: MDCRippleAdapter = {
...MDCRipple.createAdapter({root_: this.selectAnchor_}),
- registerInteractionHandler: (evtType, handler) => this.selectedText_.addEventListener(evtType, handler),
- deregisterInteractionHandler: (evtType, handler) => this.selectedText_.removeEventListener(evtType, handler),
+ registerInteractionHandler: (evtType, handler) => {
+ this.selectAnchor_.addEventListener(evtType, handler);
+ },
+ deregisterInteractionHandler: (evtType, handler) => {
+ this.selectAnchor_.removeEventListener(evtType, handler);
+ },
};
// tslint:enable:object-literal-sort-keys
return new MDCRipple(this.selectAnchor_, new MDCRippleFoundation(adapter));
@@ -289,28 +295,58 @@ export class MDCSelect extends MDCComponent {
private getSelectAdapterMethods_() {
// tslint:disable:object-literal-sort-keys Methods should be in the same order as the adapter interface.
return {
- getSelectedMenuItem: () => this.menuElement_!.querySelector(strings.SELECTED_ITEM_SELECTOR),
- getMenuItemAttr: (menuItem: Element, attr: string) => menuItem.getAttribute(attr),
- setSelectedText: (text: string) => this.selectedText_.textContent = text,
- isSelectedTextFocused: () => document.activeElement === this.selectedText_,
- getSelectedTextAttr: (attr: string) => this.selectedText_.getAttribute(attr),
- setSelectedTextAttr: (attr: string, value: string) => this.selectedText_.setAttribute(attr, value),
- openMenu: () => this.menu_.open = true,
- closeMenu: () => this.menu_.open = false,
- getAnchorElement: () => this.root_.querySelector(strings.SELECT_ANCHOR_SELECTOR)!,
- setMenuAnchorElement: (anchorEl: HTMLElement) => this.menu_.setAnchorElement(anchorEl),
- setMenuAnchorCorner: (anchorCorner: menuSurfaceConstants.Corner) => this.menu_.setAnchorCorner(anchorCorner),
- setMenuWrapFocus: (wrapFocus: boolean) => this.menu_.wrapFocus = wrapFocus,
- setAttributeAtIndex: (index: number, attributeName: string, attributeValue: string) =>
- this.menu_.items[index].setAttribute(attributeName, attributeValue),
- removeAttributeAtIndex: (index: number, attributeName: string) =>
- this.menu_.items[index].removeAttribute(attributeName),
- focusMenuItemAtIndex: (index: number) => (this.menu_.items[index] as HTMLElement).focus(),
+ getSelectedMenuItem: () =>
+ this.menuElement_!.querySelector(strings.SELECTED_ITEM_SELECTOR),
+ getMenuItemAttr: (menuItem: Element, attr: string) =>
+ menuItem.getAttribute(attr),
+ setSelectedText: (text: string) => {
+ this.selectedText_.value = text;
+ },
+ isSelectAnchorFocused: () =>
+ document.activeElement === this.selectAnchor_,
+ getSelectAnchorAttr: (attr: string) =>
+ this.selectAnchor_.getAttribute(attr),
+ setSelectAnchorAttr: (attr: string, value: string) => {
+ this.selectAnchor_.setAttribute(attr, value);
+ },
+ openMenu: () => {
+ this.menu_.open = true;
+ },
+ closeMenu: () => {
+ this.menu_.open = false;
+ },
+ getAnchorElement: () =>
+ this.root_.querySelector(strings.SELECT_ANCHOR_SELECTOR)!,
+ setMenuAnchorElement: (anchorEl: HTMLElement) => {
+ this.menu_.setAnchorElement(anchorEl);
+ },
+ setMenuAnchorCorner: (anchorCorner: menuSurfaceConstants.Corner) => {
+ this.menu_.setAnchorCorner(anchorCorner);
+ },
+ setMenuWrapFocus: (wrapFocus: boolean) => {
+ this.menu_.wrapFocus = wrapFocus;
+ },
+ setAttributeAtIndex:
+ (index: number, attributeName: string, attributeValue: string) => {
+ this.menu_.items[index].setAttribute(attributeName, attributeValue);
+ },
+ removeAttributeAtIndex: (index: number, attributeName: string) => {
+ this.menu_.items[index].removeAttribute(attributeName);
+ },
+ focusMenuItemAtIndex: (index: number) => {
+ (this.menu_.items[index] as HTMLElement).focus();
+ },
getMenuItemCount: () => this.menu_.items.length,
- getMenuItemValues: () => this.menu_.items.map((el) => el.getAttribute(strings.VALUE_ATTR) || ''),
- getMenuItemTextAtIndex: (index: number) => this.menu_.items[index].textContent as string,
- addClassAtIndex: (index: number, className: string) => this.menu_.items[index].classList.add(className),
- removeClassAtIndex: (index: number, className: string) => this.menu_.items[index].classList.remove(className),
+ getMenuItemValues: () => this.menu_.items.map(
+ (el) => el.getAttribute(strings.VALUE_ATTR) || ''),
+ getMenuItemTextAtIndex: (index: number) =>
+ this.menu_.items[index].textContent as string,
+ addClassAtIndex: (index: number, className: string) => {
+ this.menu_.items[index].classList.add(className);
+ },
+ removeClassAtIndex: (index: number, className: string) => {
+ this.menu_.items[index].classList.remove(className);
+ },
};
// tslint:enable:object-literal-sort-keys
}
diff --git a/packages/mdc-select/foundation.ts b/packages/mdc-select/foundation.ts
index e93bce09b7f..53ab745d0d4 100644
--- a/packages/mdc-select/foundation.ts
+++ b/packages/mdc-select/foundation.ts
@@ -64,9 +64,9 @@ export class MDCSelectFoundation extends MDCFoundation {
setRippleCenter: () => undefined,
notifyChange: () => undefined,
setSelectedText: () => undefined,
- isSelectedTextFocused: () => false,
- getSelectedTextAttr: () => '',
- setSelectedTextAttr: () => undefined,
+ isSelectAnchorFocused: () => false,
+ getSelectAnchorAttr: () => '',
+ setSelectAnchorAttr: () => undefined,
openMenu: () => undefined,
closeMenu: () => undefined,
getAnchorElement: () => null,
@@ -112,6 +112,7 @@ export class MDCSelectFoundation extends MDCFoundation {
this.helperText_ = foundationMap.helperText;
this.menuItemValues_ = this.adapter_.getMenuItemValues();
+ this.setDisabled(this.adapter_.hasClass(cssClasses.DISABLED));
}
/** Returns the index of the currently selected menu item, or -1 if none. */
@@ -180,8 +181,9 @@ export class MDCSelectFoundation extends MDCFoundation {
this.leadingIcon_.setDisabled(this.disabled_);
}
- this.adapter_.setSelectedTextAttr('tabindex', this.disabled_ ? '-1' : '0');
- this.adapter_.setSelectedTextAttr('aria-disabled', this.disabled_.toString());
+ this.adapter_.setSelectAnchorAttr('tabindex', this.disabled_ ? '-1' : '0');
+ this.adapter_.setSelectAnchorAttr(
+ 'aria-disabled', this.disabled_.toString());
}
/**
@@ -215,10 +217,10 @@ export class MDCSelectFoundation extends MDCFoundation {
handleMenuClosed() {
this.adapter_.removeClass(cssClasses.ACTIVATED);
this.isMenuOpen_ = false;
- this.adapter_.setSelectedTextAttr('aria-expanded', 'false');
+ this.adapter_.setSelectAnchorAttr('aria-expanded', 'false');
// Unfocus the select if menu is closed without a selection
- if (!this.adapter_.isSelectedTextFocused()) {
+ if (!this.adapter_.isSelectAnchorFocused()) {
this.blur_();
}
}
@@ -278,7 +280,7 @@ export class MDCSelectFoundation extends MDCFoundation {
this.adapter_.openMenu();
this.isMenuOpen_ = true;
- this.adapter_.setSelectedTextAttr('aria-expanded', 'true');
+ this.adapter_.setSelectAnchorAttr('aria-expanded', 'true');
}
handleKeydown(event: KeyboardEvent) {
@@ -294,7 +296,7 @@ export class MDCSelectFoundation extends MDCFoundation {
if (this.adapter_.hasClass(cssClasses.FOCUSED) && (isEnter || isSpace || arrowUp || arrowDown)) {
this.adapter_.openMenu();
this.isMenuOpen_ = true;
- this.adapter_.setSelectedTextAttr('aria-expanded', 'true');
+ this.adapter_.setSelectAnchorAttr('aria-expanded', 'true');
event.preventDefault();
}
}
@@ -336,7 +338,7 @@ export class MDCSelectFoundation extends MDCFoundation {
}
setValid(isValid: boolean) {
- this.adapter_.setSelectedTextAttr('aria-invalid', (!isValid).toString());
+ this.adapter_.setSelectAnchorAttr('aria-invalid', (!isValid).toString());
if (isValid) {
this.adapter_.removeClass(cssClasses.INVALID);
} else {
@@ -360,11 +362,11 @@ export class MDCSelectFoundation extends MDCFoundation {
} else {
this.adapter_.removeClass(cssClasses.REQUIRED);
}
- this.adapter_.setSelectedTextAttr('aria-required', isRequired.toString());
+ this.adapter_.setSelectAnchorAttr('aria-required', isRequired.toString());
}
getRequired() {
- return this.adapter_.getSelectedTextAttr('aria-required') === 'true';
+ return this.adapter_.getSelectAnchorAttr('aria-required') === 'true';
}
init() {
diff --git a/packages/mdc-select/helper-text/README.md b/packages/mdc-select/helper-text/README.md
index 3fede98768d..48f3f4a6d41 100644
--- a/packages/mdc-select/helper-text/README.md
+++ b/packages/mdc-select/helper-text/README.md
@@ -61,9 +61,7 @@ the display of the helper text is dependent on the interaction with the MDCSelec
Pick a Food Group