From b0435e28216967b07f8260a09b08dc19635a3533 Mon Sep 17 00:00:00 2001 From: Matt Goo Date: Mon, 19 Mar 2018 14:02:49 -0700 Subject: [PATCH 01/19] WIP: select is workig without menu --- demos/select.html | 55 ++++--- package-lock.json | 6 +- packages/mdc-select/_mixins.scss | 5 +- packages/mdc-select/constants.js | 1 + packages/mdc-select/foundation.js | 216 +++++++--------------------- packages/mdc-select/index.js | 49 +++---- packages/mdc-select/mdc-select.scss | 21 +-- 7 files changed, 127 insertions(+), 226 deletions(-) diff --git a/demos/select.html b/demos/select.html index c83d6b1314f..4307996b8fe 100644 --- a/demos/select.html +++ b/demos/select.html @@ -60,6 +60,7 @@
+

Fully-Featured JS Component

-
-
Food Group
-
-
-
-
-
    -
  • - Fruit Roll Ups -
  • -
  • - Candy (cotton)‎ -
  • -
  • - Vegetables -
  • -
  • - Noodles -
  • -
-
+ +
Food Group
+
+

Currently selected: (none)

@@ -136,7 +142,7 @@

Fully-Featured JS Component

- +
diff --git a/packages/mdc-select/README.md b/packages/mdc-select/README.md index 4339a8aa585..c1315423b35 100644 --- a/packages/mdc-select/README.md +++ b/packages/mdc-select/README.md @@ -15,9 +15,8 @@ path: /catalog/input-controls/select-menus/ --> -MDC Select provides Material Design single-option select menus. It functions analogously to the -browser's native `` element. It is fully accessible, and fully RTL-aware. ## Design & API Documentation @@ -25,9 +24,6 @@ in conjunction with the browser's native element. Both are fully accessible, and
  • Material Design guidelines: Text Fields
  • -
  • - Material Design guidelines: Menus -
  • @@ -44,42 +40,44 @@ npm install @material/select ### Using the full-fidelity JS component ```html -
    -
    -
    Pick a Food Group
    -
    -
    -
    -
    -
      -
    • - Bread, Cereal, Rice, and Pasta -
    • -
    • - Vegetables -
    • -
    • - Fruit -
    • -
    • - Milk, Yogurt, and Cheese -
    • -
    • - Meat, Poultry, Fish, Dry Beans, Eggs, and Nuts -
    • -
    • - Fats, Oils, and Sweets -
    • -
    -
    +
    + +
    Pick a Food Group
    +
    ``` +Use the ` +``` + Then with JS ```js const select = new mdc.select.MDCSelect(document.querySelector('.mdc-select')); -select.listen('MDCSelect:change', () => { +select.listen('change', () => { alert(`Selected "${select.selectedOptions[0].textContent}" at index ${select.selectedIndex} ` + `with value "${select.value}"`); }); @@ -87,121 +85,77 @@ select.listen('MDCSelect:change', () => { See [Importing the JS component](../../docs/importing-js.md) for more information on how to import JavaScript. -Note that you can include mdc-select via a UMD bundle, which will be available post-alpha. - -> Note that the full-fidelity version of MDC Select requires you to manually include the styles for -[mdc-menu](../mdc-menu) and [mdc-list](../mdc-list). If you are using the -[material-components-web](../material-components-web) package, this is taken care of for you and you -simply need to import the main stylesheet. Otherwise, _you must ensure that you manually include the -style dependencies for both the mdc-list and mdc-menu for this component to function properly._ - #### Select with pre-selected option When dealing with the select component that has pre-selected values, you'll want to ensure that you render `mdc-select__label` with the `mdc-select__label--float-above` modifier class and the selected -option with `aria-selected`. This will ensure that the label moves out of the way of the select's value -and prevents a Flash Of Un-styled Content (**FOUC**). +option with `aria-selected` and the `selected` attribute. This will ensure that the label moves out +of the way of the select's value and prevents a Flash Of Un-styled Content (**FOUC**). ```html -
    -
    -
    Pick a Food Group
    -
    -
    -
    -
    -
      -
    • - Bread, Cereal, Rice, and Pasta -
    • -
    • - Vegetables -
    • -
    • - Fruit -
    • -
    • - Milk, Yogurt, and Cheese -
    • -
    • - Meat, Poultry, Fish, Dry Beans, Eggs, and Nuts -
    • -
    • - Fats, Oils, and Sweets -
    • -
    -
    +
    + +
    Pick a Food Group
    +
    ``` #### Disabled select +Add the `mdc-select--disabled` class and the `aria-disabled` attribute to the `mdc-select` +element. Also add the `disabled` attribute to the ` + + + + +
    Pick a Food Group
    +
    ``` #### Disabled options -When used in components such as MDC Select, `mdc-list-item`s can be disabled. -To disable a list item, set `aria-disabled` to `"true"`, and set `tabindex` to `"-1"`. +When used in components such as MDC Select, `mdc-select__option`s can be disabled. +To disable a list item, set `aria-disabled` to `"true"` and add the `disabled` attribute. ```html -
    -
    -
    Pick a Food Group
    -
    -
    -
    -
    -
      -
    • - Bread, Cereal, Rice, and Pasta -
    • -
    • - Vegetables -
    • -
    • - Fruit -
    • -
    • - Milk, Yogurt, and Cheese -
    • -
    • - Meat, Poultry, Fish, Dry Beans, Eggs, and Nuts -
    • -
    • - Fats, Oils, and Sweets -
    • -
    -
    +
    + +
    Pick a Food Group
    +
    ``` @@ -211,11 +165,7 @@ To disable a list item, set `aria-disabled` to `"true"`, and set `tabindex` to ` | ------------------------ | ----------------------------------------------- | | `mdc-select` | Mandatory. | | `mdc-select--box` | Styles the select as a box select. | -| `mdc-list-group` | A group of options. | -| `mdc-list-item` | A list item. | -| `mdc-list-divider` | A divider. | - -It is advised that dividers also set `role="presentation"` to disable selection and not cloud accessibility. +| `mdc-select__option` | A select option. | ### Sass Mixins @@ -226,13 +176,14 @@ Mixin | Description --- | --- `mdc-select-ink-color($color)` | Customizes the color of the selected item displayed in the select. `mdc-select-container-fill-color($color)` | Customizes the background color of the select. -`mdc-select-label-color($color)` | Customizes the label color of the select in the unfocused state. `mdc-select-focused-label-color($color, $opacity: 0.87)` | Customizes the label color of the select when focused. Changing opacity for the label when floating is optional. `mdc-select-bottom-line-color($color)` | Customizes the color of the default bottom line of the select. `mdc-select-focused-bottom-line-color($color)` | Customizes the color of the bottom line of the select when focused. To customize the color of the list items, refer to the [List documentation](../mdc-list/README.md). +> NOTE: To customize label color please see [label readme](./label/README.md). + ### MDC Select Component API The MDC Select component API is modeled after a subset of the `HTMLSelectElement` functionality, and @@ -242,113 +193,53 @@ is outlined below. | Property Name | Type | Description | | --- | --- | --- | -| `value` | `string` | _(read-only)_ The `id` of the currently selected option. If no `id` is present on the selected option, its `textContent` is used. Returns an empty string when no option is selected. | -| `options` | `HTMLElement[]` | _(read-only)_ An _array_ of menu items comprising the select's options. | +| `value` | `string` | The `value` of the currently selected option. | +| `options` | `[]` | _(read-only)_ An _array_ of `` comprising the select's options. | | `selectedIndex` | `number` | The index of the currently selected option. Set to -1 if no option is currently selected. Changing this property will update the select element. | -| `selectedOptions` | `HTMLElement[]` | _(read-only)_ A NodeList of either the currently selected option, or no elements if nothing is selected. | +| `selectedOptions` | `[]` | _(read-only)_ An _array_ of `` either the currently selected option, or no elements if nothing is selected. | | `disabled` | `boolean` | Whether or not the component is disabled. Settings this sets the disabled state on the component. | -#### Methods +### `MDCSelect` | Method Signature | Description | | --- | --- | | `item(index: number) => HTMLElement?` | Analogous to `HTMLSelectElement.prototype.item`. Returns the option at the specified index, or `null` if the index is out of bounds. } | `nameditem(key: string) => HTMLElement?` | Analogous to `HTMLSelectElement.prototype.nameditem`. Returns the options either whose `id` equals the given `key`, or whose `name` attribute equals the given `key`. Returns `null` if no item with an `id` or `name` attribute of the specified key is found. | +| `initialSyncWithDOM() => void` | Syncs the component with the current state of the HTML markup. | #### Events -The MDC Select JS component emits an `MDCSelect:change` event when the selected option changes as +The MDC Select JS component emits an `change` event when the selected option changes as the result of a user action. -## Using the foundation class - -MDC Select ships with a foundation class that framework authors can use to integrate MDC Select -into their custom components. Note that due to the nature of MDC Select, the adapter is quite -complex. We try to provide as much guidance as possible, but we encourage developers to reach out -to us via GH Issues if they run into problems. - -### Notes for component implementors - -The `MDCSelectFoundation` expects that the select component conforms to the following two requirements: - -1. The component owns an element that's used as its select menu, e.g. its **menu element**. - -2. The component controls an instance of `MDCMenu`, which is attached to its menu element. - -We achieve this by accepting a `menuFactory` optional constructor parameter, which is a function -which is passed our menu element, and is expected to return an `MDCMenu` component instance. -If you are attempting to implement mdc-select for your framework, and you find that this approach -does not work for you, and there is no suitable way to satisfy the above two requirements, please -[open an issue](https://github.com/material-components/material-components-web/issues/new). - -`MDCSelectFoundation` also has the ability to resize itself whenever its options change, via the -`resize()` method. We recommend calling this method on initialization, or when the menu items are -modified. For example, if building a react component, it may be appropriate to call `resize()` -within `componentDidUpdate`. - -### Adapter API +### `MDCSelectAdapter` | Method Signature | Description | | --- | --- | | `addClass(className: string) => void` | Adds a class to the root element. | | `removeClass(className: string) => void` | Removes a class from the root element. | -| `floatLabel(value: boolean) => void` | Float or defloats label as necessary | -| `addClassToBottomLine(className: string) => void` | Adds a class to the bottom line | -| `removeClassFromBottomLine(className: string) => void` | Removes a class from the bottom line | -| `setBottomLineAttr(attr: string, value: string) => void` | Adds an attribute to the bottom line | -| `addBodyClass(className: string) => void` | Adds a class to the body. | -| `removeBodyClass(className: string) => void` | Removes a class from the body. | +| `floatLabel(value: boolean) => void` | Float or defloats label as necessary. | +| `activateBottomLine() => void` | Activates bottom line as focused. | +| `deactivateBottomLine() => void` | Deactivates bottom line as root element loses focus. | | `setAttr(attr: string, value: string) => void` | Sets attribute `attr` to value `value` on the root element. | | `rmAttr(attr: string) => void` | Removes attribute `attr` from the root element. | -| `computeBoundingRect() => {left: number, top: number}` | Returns an object with a shape similar to a `ClientRect` object, with a `left` and `top` property specifying the element's position on the page relative to the viewport. The easiest way to achieve this is by calling `getBoundingClientRect()` on the surface element. | +| `setDisabled(disabled: boolean) => void` | Sets the ` + + + + + + + + +
    Pick a Food Group
    +
    ``` diff --git a/packages/mdc-select/constants.js b/packages/mdc-select/constants.js index efbfa65f073..a60243f0629 100644 --- a/packages/mdc-select/constants.js +++ b/packages/mdc-select/constants.js @@ -28,7 +28,6 @@ export const strings = { CHANGE_EVENT: 'MDCSelect:change', BOTTOM_LINE_SELECTOR: '.mdc-select__bottom-line', LABEL_SELECTOR: '.mdc-select__label', - MENU_SELECTOR: '.mdc-select__menu', OPTION_SELECTOR: '.mdc-select__option', SURFACE_SELECTOR: '.mdc-select__surface', }; diff --git a/packages/mdc-select/foundation.js b/packages/mdc-select/foundation.js index b9975a6b59e..eb148d824dd 100644 --- a/packages/mdc-select/foundation.js +++ b/packages/mdc-select/foundation.js @@ -16,7 +16,6 @@ import {MDCFoundation} from '@material/base/index'; import {cssClasses, numbers, strings} from './constants'; -import {MDCMenuFoundation} from '@material/menu/index'; export default class MDCSelectFoundation extends MDCFoundation { static get cssClasses() { @@ -31,6 +30,23 @@ export default class MDCSelectFoundation extends MDCFoundation { return strings; } + get disabled() { + return this.disabled_; + } + + set disabled(disabled) { + const {DISABLED} = MDCSelectFoundation.cssClasses; + this.disabled_ = disabled; + this.adapter_.setDisabled(disabled); + if (this.disabled_) { + this.adapter_.addClass(DISABLED); + this.adapter_.setAttr('aria-disabled', 'true'); + } else { + this.adapter_.removeClass(DISABLED); + this.adapter_.rmAttr('aria-disabled'); + } + } + static get defaultAdapter() { return { addClass: (/* className: string */) => {}, @@ -45,6 +61,8 @@ export default class MDCSelectFoundation extends MDCFoundation { getNumberOfOptions: () => /* number */ 0, getIndexForOptionValue: (/* value: string */) => /* number */ -1, getValueForOptionAtIndex: (/* index: number */) => /* index */ '', + getValue: () => /* string */ '', + setValue: (/* value: string */) => {}, setAttrForOptionAtIndex: (/* index: number, attr: string, value: string */) => {}, rmAttrForOptionAtIndex: (/* index: number, attr: string */) => {}, }; @@ -103,16 +121,16 @@ export default class MDCSelectFoundation extends MDCFoundation { }, SELECT_TEXT_TRANSITION_TIME); } - setDisabled(disabled) { - const {DISABLED} = MDCSelectFoundation.cssClasses; - this.disabled_ = disabled; - if (this.disabled_) { - this.adapter_.addClass(DISABLED); - this.adapter_.setAttr('aria-disabled', 'true'); - } else { - this.adapter_.removeClass(DISABLED); - this.adapter_.rmAttr('aria-disabled'); - } + getValue() { + return this.adapter_.getValue(); + } + + setValue(value) { + let index = this.adapter_.getIndexForOptionValue(value); + index = !index && null !== 0 ? -1 : index; + + this.adapter_.setValue(value); + this.setSelectedIndex(index); } handleFocus_() { diff --git a/packages/mdc-select/index.js b/packages/mdc-select/index.js index 655328859d1..56b8d1ccdeb 100644 --- a/packages/mdc-select/index.js +++ b/packages/mdc-select/index.js @@ -16,7 +16,6 @@ import {MDCComponent} from '@material/base/index'; import {MDCRipple} from '@material/ripple/index'; -import {MDCMenu} from '@material/menu/index'; import {MDCSelectBottomLine} from './bottom-line/index'; import {MDCSelectLabel} from './label/index'; @@ -31,7 +30,7 @@ export class MDCSelect extends MDCComponent { } get value() { - return this.surface_.value; + return this.foundation_.getValue(); } set value(value) { @@ -55,12 +54,11 @@ export class MDCSelect extends MDCComponent { } get disabled() { - return this.surface_.disabled; + return this.foundation_.disabled; } set disabled(disabled) { - this.surface_.disabled = disabled; - this.foundation_.setDisabled(disabled); + this.foundation_.disabled = disabled; } item(index) { @@ -116,12 +114,14 @@ export class MDCSelect extends MDCComponent { deactivateBottomLine: () => this.bottomLine_.deactivate(), setAttr: (attr, value) => this.root_.setAttribute(attr, value), rmAttr: (attr, value) => this.root_.removeAttribute(attr, value), + setDisabled: (disabled) => this.surface_.disabled = disabled, registerInteractionHandler: (type, handler) => this.surface_.addEventListener(type, handler), deregisterInteractionHandler: (type, handler) => this.surface_.removeEventListener(type, handler), getNumberOfOptions: () => this.options.length, getIndexForOptionValue: (value) => this.indexByValue_(value), getValueForOptionAtIndex: (index) => this.options[index].value, setSelectedIndex: (index) => this.surface_.selectedIndex = index, + getValue: () => this.surface_.value, setValue: (value) => this.surface_.value = value, setAttrForOptionAtIndex: (index, attr, value) => this.options[index].setAttribute(attr, value), rmAttrForOptionAtIndex: (index, attr) => this.options[index].removeAttribute(attr), diff --git a/packages/mdc-select/label/README.md b/packages/mdc-select/label/README.md index 103f031be21..285f3dc13ef 100644 --- a/packages/mdc-select/label/README.md +++ b/packages/mdc-select/label/README.md @@ -23,9 +23,6 @@ Select labels display the type of input a field requires. Every select should ha
  • Material Design guidelines: Text Fields
  • -
  • - Material Design guidelines: Menus -
  • @@ -42,25 +39,30 @@ Select labels display the type of input a field requires. Every select should ha ### Usage within `mdc-select` ```html -
    -
    -
    Pick a Food Group
    -
    -
    -
    -
    -
      -
    • - Dairy -
    • -
    • - Vegetables -
    • -
    • - Fruit -
    • -
    -
    +
    + +
    Pick a Food Group
    +
    ``` diff --git a/packages/mdc-select/mdc-select.scss b/packages/mdc-select/mdc-select.scss index f0537de1bde..1ebd7275b27 100644 --- a/packages/mdc-select/mdc-select.scss +++ b/packages/mdc-select/mdc-select.scss @@ -48,11 +48,11 @@ display: inline-flex; position: relative; - overflow: hidden; box-sizing: border-box; height: 48px; background-repeat: no-repeat; background-position: right 8px bottom 8px; + overflow: hidden; @include mdc-rtl { background-position: left 8px bottom 8px; @@ -65,20 +65,20 @@ display: none; } - @-moz-document url-prefix() { + @-moz-document url-prefix("") { text-indent: -2px; } width: 100%; - background-color: transparent; - padding-bottom: 8px; padding-top: 22px; + padding-bottom: 8px; border: none; outline: none; - overflow: hidden; - appearance: none; - cursor: pointer; + background-color: transparent; white-space: nowrap; + cursor: pointer; + appearance: none; + overflow: hidden; } @include mdc-select-focused-bottom-line_ { diff --git a/packages/mdc-theme/_color-palette.scss b/packages/mdc-theme/_color-palette.scss index ef5122b5a0d..ed52a9bf024 100644 --- a/packages/mdc-theme/_color-palette.scss +++ b/packages/mdc-theme/_color-palette.scss @@ -59,7 +59,7 @@ $material-color-purple-900: #4a148c; $material-color-purple-a100: #ea80fc; $material-color-purple-a200: #e040fb; $material-color-purple-a400: #d500f9; -$material-color-purple-a700: #aa00ff; +$material-color-purple-a700: #a0f; $material-color-deep-purple-50: #ede7f6; $material-color-deep-purple-100: #d1c4e9; @@ -207,7 +207,7 @@ $material-color-yellow-700: #fbc02d; $material-color-yellow-800: #f9a825; $material-color-yellow-900: #f57f17; $material-color-yellow-a100: #ffff8d; -$material-color-yellow-a200: #ffff00; +$material-color-yellow-a200: #ff0; $material-color-yellow-a400: #ffea00; $material-color-yellow-a700: #ffd600; @@ -269,7 +269,7 @@ $material-color-brown-900: #3e2723; $material-color-grey-50: #fafafa; $material-color-grey-100: #f5f5f5; -$material-color-grey-200: #eeeeee; +$material-color-grey-200: #eee; $material-color-grey-300: #e0e0e0; $material-color-grey-400: #bdbdbd; $material-color-grey-500: #9e9e9e; From 06ace26df35f9434fbc62fa29b14bd79483887e1 Mon Sep 17 00:00:00 2001 From: Matt Goo Date: Mon, 26 Mar 2018 14:43:23 -0700 Subject: [PATCH 05/19] fix(select): updated tests --- packages/mdc-select/foundation.js | 6 +- packages/mdc-select/index.js | 10 - .../unit/mdc-select/foundation-events.test.js | 281 ++------------- test/unit/mdc-select/foundation.test.js | 340 +++++++----------- test/unit/mdc-select/mdc-select.test.js | 322 ++++------------- 5 files changed, 223 insertions(+), 736 deletions(-) diff --git a/packages/mdc-select/foundation.js b/packages/mdc-select/foundation.js index eb148d824dd..1365aa78cb6 100644 --- a/packages/mdc-select/foundation.js +++ b/packages/mdc-select/foundation.js @@ -65,6 +65,8 @@ export default class MDCSelectFoundation extends MDCFoundation { setValue: (/* value: string */) => {}, setAttrForOptionAtIndex: (/* index: number, attr: string, value: string */) => {}, rmAttrForOptionAtIndex: (/* index: number, attr: string */) => {}, + setSelectedIndex: (/* index: number */) => {}, + setDisabled: (/* disabled: boolean */) => {}, }; } @@ -130,7 +132,9 @@ export default class MDCSelectFoundation extends MDCFoundation { index = !index && null !== 0 ? -1 : index; this.adapter_.setValue(value); - this.setSelectedIndex(index); + if (index !== this.selectedIndex_) { + this.setSelectedIndex(index); + } } handleFocus_() { diff --git a/packages/mdc-select/index.js b/packages/mdc-select/index.js index 56b8d1ccdeb..bb35953cfd4 100644 --- a/packages/mdc-select/index.js +++ b/packages/mdc-select/index.js @@ -65,16 +65,6 @@ export class MDCSelect extends MDCComponent { return this.surface_.item(index); } - nameditem(key) { - // NOTE: IE11 precludes us from using Array.prototype.find - for (let i = 0, options = this.options, option; (option = options[i]); i++) { - if (option.id === key || option.getAttribute('name') === key) { - return option; - } - } - return null; - } - indexByValue_(value) { // NOTE: IE11 precludes us from using Array.prototype.find for (let i = 0, options = this.options, option; (option = options[i]); i++) { diff --git a/test/unit/mdc-select/foundation-events.test.js b/test/unit/mdc-select/foundation-events.test.js index 805eb73c2fe..9f14113728a 100644 --- a/test/unit/mdc-select/foundation-events.test.js +++ b/test/unit/mdc-select/foundation-events.test.js @@ -14,38 +14,22 @@ * limitations under the License. */ -import {assert} from 'chai'; import td from 'testdouble'; +import lolex from 'lolex'; import {setupFoundationTest} from '../helpers/setup'; import {captureHandlers} from '../helpers/foundation'; -import {strings as menuStrings} from '../../../packages/mdc-menu/constants'; -import {createMockRaf} from '../helpers/raf'; import MDCSelectFoundation from '../../../packages/mdc-select/foundation'; -const {cssClasses} = MDCSelectFoundation; - function setupTest() { const {foundation, mockAdapter} = setupFoundationTest(MDCSelectFoundation); td.when(mockAdapter.getNumberOfOptions()).thenReturn(2); - td.when(mockAdapter.getTextForOptionAtIndex(td.matchers.isA(Number))).thenReturn('text'); - td.when(mockAdapter.create2dRenderingContext()).thenReturn({ - font: '', - measureText: () => ({width: 100}), - }); - td.when(mockAdapter.getComputedStyleValue('font')).thenReturn('16px Times'); - td.when(mockAdapter.computeBoundingRect()).thenReturn({ - left: 100, - top: 100, - }); - td.when(mockAdapter.getWindowInnerHeight()).thenReturn(500); - td.when(mockAdapter.getMenuElOffsetHeight()).thenReturn(100); + const handlers = captureHandlers(mockAdapter, 'registerInteractionHandler'); - const menuHandlers = captureHandlers(mockAdapter, 'registerMenuInteractionHandler'); foundation.init(); - return {foundation, mockAdapter, handlers, menuHandlers}; + return {foundation, mockAdapter, handlers}; } function createEvent(data) { @@ -54,247 +38,34 @@ function createEvent(data) { suite('MDCSelectFoundation - Events'); -test('on click opens the menu', () => { - const {mockAdapter, handlers} = setupTest(); - const raf = createMockRaf(); - handlers.click(createEvent()); - td.verify(mockAdapter.addClass(cssClasses.OPEN)); - raf.flush(); - td.verify(mockAdapter.openMenu(0)); -}); - -test('on click does not open the menu if it is already open', () => { +test('on focus activates bottom line', () => { const {mockAdapter, handlers} = setupTest(); - const raf = createMockRaf(); - td.when(mockAdapter.isMenuOpen()).thenReturn(true); - handlers.click(createEvent()); - td.verify(mockAdapter.addClass(cssClasses.OPEN), {times: 0}); - raf.flush(); - td.verify(mockAdapter.openMenu(0), {times: 0}); -}); - -test('on click opens the menu focused at the selected index, if any', () => { - const {foundation, mockAdapter, handlers} = setupTest(); - const raf = createMockRaf(); - foundation.setSelectedIndex(1); - handlers.click(createEvent()); - raf.flush(); - td.verify(mockAdapter.openMenu(1)); -}); - -test('on click cancels the event to prevent it from propagating', () => { - const {handlers} = setupTest(); - const evt = createEvent(); - handlers.click(evt); - td.verify(evt.preventDefault()); + handlers.focus(createEvent()); + td.verify(mockAdapter.activateBottomLine(), {times: 1}); }); -test('on ArrowUp keydown on the select itself opens the menu', () => { +test('on blur deactivates bottom line', () => { const {mockAdapter, handlers} = setupTest(); - const evt = createEvent({key: 'ArrowUp', eventPhase: Event.AT_TARGET}); - const raf = createMockRaf(); - handlers.keydown(evt); - td.verify(mockAdapter.addClass(cssClasses.OPEN)); - raf.flush(); - td.verify(mockAdapter.openMenu(0)); - td.verify(evt.preventDefault()); + handlers.blur(createEvent()); + td.verify(mockAdapter.deactivateBottomLine(), {times: 1}); }); -test('on ArrowUp keydown works with keyCode', () => { +test('on select value change with option value', () => { + const clock = lolex.install(); const {mockAdapter, handlers} = setupTest(); - const evt = createEvent({keyCode: 38, eventPhase: Event.AT_TARGET}); - handlers.keydown(evt); - td.verify(mockAdapter.addClass(cssClasses.OPEN)); -}); - -test('on ArrowUp keydown does not open the menu on bubbled events', () => { - const {mockAdapter, handlers} = setupTest(); - const evt = createEvent({key: 'ArrowUp', eventPhase: Event.BUBBLING_PHASE}); - handlers.keydown(evt); - td.verify(mockAdapter.addClass(cssClasses.OPEN), {times: 0}); -}); - -test('on ArrowDown keydown on the select itself opens the menu', () => { - const {mockAdapter, handlers} = setupTest(); - const evt = createEvent({key: 'ArrowDown', eventPhase: Event.AT_TARGET}); - const raf = createMockRaf(); - handlers.keydown(evt); - td.verify(mockAdapter.addClass(cssClasses.OPEN)); - raf.flush(); - td.verify(mockAdapter.openMenu(0)); - td.verify(evt.preventDefault()); -}); - -test('on ArrowDown keydown works with keyCode', () => { - const {mockAdapter, handlers} = setupTest(); - const evt = createEvent({keyCode: 40, eventPhase: Event.AT_TARGET}); - handlers.keydown(evt); - td.verify(mockAdapter.addClass(cssClasses.OPEN)); -}); - -test('on ArrowDown keydown does not open the menu on bubbled events', () => { - const {mockAdapter, handlers} = setupTest(); - const evt = createEvent({key: 'ArrowDown', eventPhase: Event.BUBBLING_PHASE}); - handlers.keydown(evt); - td.verify(mockAdapter.addClass(cssClasses.OPEN), {times: 0}); -}); - -test('on Space keydown prevents default to prevent page from scrolling', () => { - const {handlers} = setupTest(); - const evt = createEvent({key: 'Space', eventPhase: Event.AT_TARGET}); - handlers.keydown(evt); - td.verify(evt.preventDefault()); -}); - -test('on Space keydown works with keyCode', () => { - const {handlers} = setupTest(); - const evt = createEvent({keyCode: 32, eventPhase: Event.AT_TARGET}); - handlers.keydown(evt); - td.verify(evt.preventDefault()); -}); - -test('on Space keyup on the select itself opens the menu', () => { - const {mockAdapter, handlers} = setupTest(); - const evt = createEvent({key: 'Space', eventPhase: Event.AT_TARGET}); - const raf = createMockRaf(); - handlers.keyup(evt); - td.verify(mockAdapter.addClass(cssClasses.OPEN)); - raf.flush(); - td.verify(mockAdapter.openMenu(0)); - td.verify(evt.preventDefault()); -}); - -test('on Space keyup works with keyCode', () => { - const {mockAdapter, handlers} = setupTest(); - const evt = createEvent({keyCode: 32, eventPhase: Event.AT_TARGET}); - handlers.keyup(evt); - td.verify(mockAdapter.addClass(cssClasses.OPEN)); -}); - -test('on Space keyup does not open the menu on bubbled events', () => { - const {mockAdapter, handlers} = setupTest(); - const evt = createEvent({key: 'Space', eventPhase: Event.BUBBLING_PHASE}); - handlers.keydown(evt); - td.verify(mockAdapter.addClass(cssClasses.OPEN), {times: 0}); -}); - -test(`on ${menuStrings.SELECTED_EVENT} updates the selected index to that given by the event`, () => { - const {foundation, menuHandlers} = setupTest(); - const selected = menuHandlers[menuStrings.SELECTED_EVENT]; - selected(createEvent({detail: {index: 1}})); - assert.equal(foundation.getSelectedIndex(), 1); -}); - -test(`on ${menuStrings.SELECTED_EVENT} fires a change event`, () => { - const {mockAdapter, menuHandlers} = setupTest(); - const selected = menuHandlers[menuStrings.SELECTED_EVENT]; - selected(createEvent({detail: {index: 1}})); - td.verify(mockAdapter.notifyChange()); -}); - -test(`on ${menuStrings.SELECTED_EVENT} does not fire change event if the index is already the selected index`, () => { - const {foundation, mockAdapter, menuHandlers} = setupTest(); - const selected = menuHandlers[menuStrings.SELECTED_EVENT]; - foundation.setSelectedIndex(1); - selected({detail: {index: 1}}); - td.verify(mockAdapter.notifyChange(), {times: 0}); -}); - -test(`on ${menuStrings.SELECTED_EVENT} closes the menu`, () => { - const {mockAdapter, menuHandlers} = setupTest(); - const selected = menuHandlers[menuStrings.SELECTED_EVENT]; - selected(createEvent({detail: {index: 1}})); - td.verify(mockAdapter.removeClass(cssClasses.OPEN)); -}); - -test(`on ${menuStrings.SELECTED_EVENT} refocuses on the select element`, () => { - const {mockAdapter, menuHandlers} = setupTest(); - const selected = menuHandlers[menuStrings.SELECTED_EVENT]; - selected(createEvent({detail: {index: 1}})); - td.verify(mockAdapter.focus()); -}); - -test(`on ${menuStrings.CANCEL_EVENT} closes the menu`, () => { - const {mockAdapter, menuHandlers} = setupTest(); - const cancel = menuHandlers[menuStrings.CANCEL_EVENT]; - cancel(createEvent()); - td.verify(mockAdapter.removeClass(cssClasses.OPEN)); -}); - -test(`on ${menuStrings.CANCEL_EVENT} re-focuses the select element`, () => { - const {mockAdapter, menuHandlers} = setupTest(); - const cancel = menuHandlers[menuStrings.CANCEL_EVENT]; - cancel(createEvent()); - td.verify(mockAdapter.removeClass(cssClasses.OPEN)); -}); - -// NOTE: For purposes of brevity, positioning tests are only done for the click handler. -test('when opened the select positions the menu such that the selected option is over the select', () => { - const {foundation, mockAdapter, handlers} = setupTest(); - const mockLocation = mockAdapter.computeBoundingRect(); - foundation.setSelectedIndex(1); - td.when(mockAdapter.getOffsetTopForOptionAtIndex(1)).thenReturn(20); - handlers.click(createEvent()); - - td.verify(mockAdapter.setMenuElStyle('left', `${mockLocation.left}px`)); - td.verify(mockAdapter.setMenuElStyle('top', `${mockLocation.top - 20}px`)); -}); - -test('when opened sets the transform-origin y-coord to be the offset top of the selected item', () => { - const {foundation, mockAdapter, handlers} = setupTest(); - foundation.setSelectedIndex(1); - td.when(mockAdapter.getOffsetTopForOptionAtIndex(1)).thenReturn(20); - handlers.click(createEvent()); - - td.verify(mockAdapter.setMenuElStyle('transform-origin', 'center 20px')); -}); - -test('when opened clamps the menu position to the top of the window if it would be ' + - 'positioned above the window', () => { - const {foundation, mockAdapter, handlers} = setupTest(); - const mockLocation = mockAdapter.computeBoundingRect(); - foundation.setSelectedIndex(1); - td.when(mockAdapter.getOffsetTopForOptionAtIndex(1)).thenReturn(mockLocation.top + 20); - handlers.click(createEvent()); - - td.verify(mockAdapter.setMenuElStyle('left', `${mockLocation.left}px`)); - td.verify(mockAdapter.setMenuElStyle('top', '0px')); -}); - -test('when opened clamps the menu position to the bottom of the window if it would be ' + - 'positioned below the window', () => { - const {foundation, mockAdapter, handlers} = setupTest(); - const mockInnerHeight = mockAdapter.getWindowInnerHeight(); - const mockMenuHeight = mockAdapter.getMenuElOffsetHeight(); - - foundation.setSelectedIndex(1); - td.when(mockAdapter.computeBoundingRect()).thenReturn({ - left: 100, - top: mockInnerHeight-40, - }); - td.when(mockAdapter.getOffsetTopForOptionAtIndex(1)).thenReturn(20); - handlers.click(createEvent()); - - const mockLocation = mockAdapter.computeBoundingRect(); - td.verify(mockAdapter.setMenuElStyle('left', `${mockLocation.left}px`)); - td.verify( - mockAdapter.setMenuElStyle('top', `${mockInnerHeight - mockMenuHeight}px`) - ); -}); - -test('when opened clamps the menu position to the top of the window if it cannot ' + - 'find a suitable menu position', () => { - const {foundation, mockAdapter, handlers} = setupTest(); - const mockMenuHeight = mockAdapter.getMenuElOffsetHeight(); - - foundation.setSelectedIndex(1); - td.when(mockAdapter.getWindowInnerHeight()).thenReturn(mockMenuHeight - 10); - // Bump off offsetHeight to simulate no good possible placement - td.when(mockAdapter.getMenuElOffsetHeight()).thenReturn(mockMenuHeight + 10); - td.when(mockAdapter.getOffsetTopForOptionAtIndex(1)).thenReturn(20); - handlers.click(createEvent()); - - const mockLocation = mockAdapter.computeBoundingRect(); - td.verify(mockAdapter.setMenuElStyle('left', `${mockLocation.left}px`)); - td.verify(mockAdapter.setMenuElStyle('top', '0px')); + td.when(mockAdapter.getIndexForOptionValue('abc')).thenReturn(1); + td.when(mockAdapter.getValueForOptionAtIndex(1)).thenReturn('value'); + td.when(mockAdapter.setSelectedIndex(1)).thenReturn(); + handlers.change(createEvent({ + target: {value: 'abc'}, + })); + td.verify(mockAdapter.getIndexForOptionValue('abc'), {times: 1}); + td.verify(mockAdapter.getNumberOfOptions(), {times: 1}); + td.verify(mockAdapter.setSelectedIndex(1), {times: 1}); + td.verify(mockAdapter.addClass(MDCSelectFoundation.cssClasses.IS_CHANGING), {times: 1}); + td.verify(mockAdapter.getValueForOptionAtIndex(1), {times: 1}); + td.verify(mockAdapter.setAttrForOptionAtIndex(1, 'aria-selected', 'true'), {times: 1}); + td.verify(mockAdapter.floatLabel(true), {times: 1}); + clock.tick(MDCSelectFoundation.numbers.SELECT_TEXT_TRANSITION_TIME); + td.verify(mockAdapter.removeClass(MDCSelectFoundation.cssClasses.IS_CHANGING), {times: 1}); }); diff --git a/test/unit/mdc-select/foundation.test.js b/test/unit/mdc-select/foundation.test.js index 4d5ed77e247..ddf1aad1627 100644 --- a/test/unit/mdc-select/foundation.test.js +++ b/test/unit/mdc-select/foundation.test.js @@ -15,10 +15,11 @@ */ import {assert} from 'chai'; +import lolex from 'lolex'; import td from 'testdouble'; import {setupFoundationTest} from '../helpers/setup'; -import {captureHandlers, verifyDefaultAdapter} from '../helpers/foundation'; +import {verifyDefaultAdapter} from '../helpers/foundation'; import MDCSelectFoundation from '../../../packages/mdc-select/foundation'; @@ -28,6 +29,10 @@ test('exports cssClasses', () => { assert.isOk('cssClasses' in MDCSelectFoundation); }); +test('exports numbers', () => { + assert.isOk('numbers' in MDCSelectFoundation); +}); + test('exports strings', () => { assert.isOk('strings' in MDCSelectFoundation); }); @@ -35,271 +40,178 @@ test('exports strings', () => { test('default adapter returns a complete adapter implementation', () => { verifyDefaultAdapter(MDCSelectFoundation, [ 'addClass', 'removeClass', 'floatLabel', 'activateBottomLine', - 'deactivateBottomLine', 'addBodyClass', 'removeBodyClass', - 'setAttr', 'rmAttr', 'computeBoundingRect', - 'registerInteractionHandler', 'deregisterInteractionHandler', 'focus', 'makeTabbable', - 'makeUntabbable', 'getComputedStyleValue', 'setStyle', 'create2dRenderingContext', - 'setMenuElStyle', 'setMenuElAttr', 'rmMenuElAttr', 'getMenuElOffsetHeight', 'openMenu', - 'isMenuOpen', 'setSelectedTextContent', 'getNumberOfOptions', 'getTextForOptionAtIndex', - 'getValueForOptionAtIndex', 'setAttrForOptionAtIndex', 'rmAttrForOptionAtIndex', - 'getOffsetTopForOptionAtIndex', 'registerMenuInteractionHandler', 'deregisterMenuInteractionHandler', - 'notifyChange', 'getWindowInnerHeight', + 'deactivateBottomLine', 'setAttr', 'rmAttr', + 'registerInteractionHandler', 'deregisterInteractionHandler', + 'getNumberOfOptions', 'getIndexForOptionValue', 'getValueForOptionAtIndex', + 'getValue', 'setValue', 'setAttrForOptionAtIndex', 'rmAttrForOptionAtIndex', + 'setSelectedIndex', 'setDisabled', ]); - - const renderingContext = MDCSelectFoundation.defaultAdapter.create2dRenderingContext(); - const renderingContextMethods = - Object.keys(renderingContext).filter((k) => typeof renderingContext[k] === 'function'); - assert.deepEqual(renderingContextMethods.slice().sort(), ['measureText'].slice().sort()); - renderingContextMethods.forEach((m) => assert.doesNotThrow(renderingContext[m])); }); function setupTest() { - return setupFoundationTest(MDCSelectFoundation); + const {mockAdapter, foundation} = setupFoundationTest(MDCSelectFoundation); + td.when(mockAdapter.getNumberOfOptions()).thenReturn(2); + return {mockAdapter, foundation}; } -test('#getSelectedIndex returns -1 if never set', () => { +test('get disabled', () => { const {foundation} = setupTest(); - assert.equal(foundation.getSelectedIndex(), -1); + assert.isFalse(foundation.disabled); }); -test('#setSelectedIndex updates the selected index', () => { - const {foundation, mockAdapter} = setupTest(); - td.when(mockAdapter.getNumberOfOptions()).thenReturn(2); - td.when(mockAdapter.getTextForOptionAtIndex(1)).thenReturn(''); - foundation.setSelectedIndex(1); - assert.equal(foundation.getSelectedIndex(), 1); +test('set disabled to true calls adapter.setDisabled', () => { + const {mockAdapter, foundation} = setupTest(); + foundation.disabled = true; + td.verify(mockAdapter.setDisabled(true)); }); -test('#setSelectedIndex sets the trimmed text content of the selected item as selected text content', () => { - const {foundation, mockAdapter} = setupTest(); - td.when(mockAdapter.getNumberOfOptions()).thenReturn(2); - td.when(mockAdapter.getTextForOptionAtIndex(1)).thenReturn(' \nselected text '); - foundation.setSelectedIndex(1); - td.verify(mockAdapter.setSelectedTextContent('selected text')); +test('set disabled to true calls adapter.addClass', () => { + const {mockAdapter, foundation} = setupTest(); + foundation.disabled = true; + td.verify(mockAdapter.addClass(MDCSelectFoundation.cssClasses.DISABLED)); }); -test('#setSelectedIndex sets aria-selected to "true" on the selected item', () => { - const {foundation, mockAdapter} = setupTest(); - td.when(mockAdapter.getNumberOfOptions()).thenReturn(2); - td.when(mockAdapter.getTextForOptionAtIndex(1)).thenReturn(''); - foundation.setSelectedIndex(1); - td.verify(mockAdapter.setAttrForOptionAtIndex(1, 'aria-selected', 'true')); -}); -test('#setSelectedIndex removes aria-selected from the previously selected item, if any', () => { - const {foundation, mockAdapter} = setupTest(); - td.when(mockAdapter.getNumberOfOptions()).thenReturn(2); - td.when(mockAdapter.getTextForOptionAtIndex(0)).thenReturn(''); - td.when(mockAdapter.getTextForOptionAtIndex(1)).thenReturn(''); - foundation.setSelectedIndex(0); - foundation.setSelectedIndex(1); - td.verify(mockAdapter.rmAttrForOptionAtIndex(0, 'aria-selected')); +test('set disabled to true sets aria-disabled to true', () => { + const {mockAdapter, foundation} = setupTest(); + foundation.disabled = true; + td.verify(mockAdapter.setAttr('aria-disabled', 'true')); }); -test('#setSelectedIndex clears the select if given index is < 0', () => { - const {foundation, mockAdapter} = setupTest(); - td.when(mockAdapter.getNumberOfOptions()).thenReturn(2); - foundation.setSelectedIndex(-15); - td.verify(mockAdapter.setSelectedTextContent('')); - assert.equal(foundation.getSelectedIndex(), -1); +test('set disabled to true and calling get disabled returns true', () => { + const {foundation} = setupTest(); + foundation.disabled = true; + assert.isTrue(foundation.disabled); }); -test('#setSelectedIndex clears the select if given index is >= the number of options', () => { - const {foundation, mockAdapter} = setupTest(); - td.when(mockAdapter.getNumberOfOptions()).thenReturn(2); - foundation.setSelectedIndex(2); - td.verify(mockAdapter.setSelectedTextContent('')); - assert.equal(foundation.getSelectedIndex(), -1); +test('set disabled to false calls adapter.setDisabled false', () => { + const {mockAdapter, foundation} = setupTest(); + foundation.disabled = false; + td.verify(mockAdapter.setDisabled(false)); }); -test('#setSelectedIndex with valid index causes the label to float', () => { - const {foundation, mockAdapter} = setupTest(); - td.when(mockAdapter.getNumberOfOptions()).thenReturn(2); - td.when(mockAdapter.getTextForOptionAtIndex(td.matchers.anything())).thenReturn(''); - - foundation.setSelectedIndex(0); - - td.verify(mockAdapter.floatLabel(true)); +test('set disabled to false removes disabled class', () => { + const {mockAdapter, foundation} = setupTest(); + foundation.disabled = false; + td.verify(mockAdapter.removeClass(MDCSelectFoundation.cssClasses.DISABLED)); }); -test('#setSelectedIndex with -1 and already floating label causes the label to dock', () => { - const {foundation, mockAdapter} = setupTest(); - td.when(mockAdapter.getNumberOfOptions()).thenReturn(2); - td.when(mockAdapter.isMenuOpen()).thenReturn(false); - td.when(mockAdapter.getTextForOptionAtIndex(td.matchers.anything())).thenReturn(''); - - foundation.setSelectedIndex(-1); - - td.verify(mockAdapter.setSelectedTextContent('')); - td.verify(mockAdapter.floatLabel(false)); +test('set disabled to false removes aria-disabled attr', () => { + const {mockAdapter, foundation} = setupTest(); + foundation.disabled = false; + td.verify(mockAdapter.rmAttr('aria-disabled')); }); -test('#setSelectedIndex with -1 and menu open does not cause the label to dock', () => { - const {foundation, mockAdapter} = setupTest(); - td.when(mockAdapter.getNumberOfOptions()).thenReturn(2); - td.when(mockAdapter.isMenuOpen()).thenReturn(true); - td.when(mockAdapter.getTextForOptionAtIndex(td.matchers.anything())).thenReturn(''); - - foundation.setSelectedIndex(-1); +test('set disabled to false and get disabled returns false', () => { + const {foundation} = setupTest(); + foundation.disabled = false; + assert.isFalse(foundation.disabled); +}); - td.verify(mockAdapter.floatLabel(td.matchers.anything()), {times: 0}); +test('#init registers focus, blur, and change handler', () => { + const {mockAdapter, foundation} = setupTest(); + foundation.init(); + td.verify(mockAdapter.registerInteractionHandler('focus', foundation.focusHandler_)); + td.verify(mockAdapter.registerInteractionHandler('blur', foundation.blurHandler_)); + td.verify(mockAdapter.registerInteractionHandler('change', foundation.selectionHandler_)); }); -test('#isDisabled returns false by default', () => { - const {foundation} = setupTest(); - assert.isNotOk(foundation.isDisabled()); + +test('#destroy deregisters focus, blur, and change handler', () => { + const {mockAdapter, foundation} = setupTest(); + foundation.destroy(); + td.verify(mockAdapter.deregisterInteractionHandler('focus', foundation.focusHandler_)); + td.verify(mockAdapter.deregisterInteractionHandler('blur', foundation.blurHandler_)); + td.verify(mockAdapter.deregisterInteractionHandler('change', foundation.selectionHandler_)); }); -test('#setDisabled sets disabled to true when true', () => { +test('#getSelectedIndex returns correct default value of -1', () => { const {foundation} = setupTest(); - foundation.setDisabled(true); - assert.isOk(foundation.isDisabled()); + assert.equal(foundation.getSelectedIndex(), -1); }); -test('#setDisabled adds the disabled class when true', () => { - const {foundation, mockAdapter} = setupTest(); - foundation.setDisabled(true); - td.verify(mockAdapter.addClass(MDCSelectFoundation.cssClasses.DISABLED)); +test('#setSelectedIndex removes aria-selected from a previous option', () => { + const {mockAdapter, foundation} = setupTest(); + foundation.selectedIndex_ = 2; + foundation.setSelectedIndex(1); + td.verify(mockAdapter.rmAttrForOptionAtIndex(2, 'aria-selected')); }); -test('#setDisabled adds aria-disabled="true" when true', () => { - const {foundation, mockAdapter} = setupTest(); - foundation.setDisabled(true); - td.verify(mockAdapter.setAttr('aria-disabled', 'true')); +test('#setSelectedIndex does not remove aria-selected if there is no previous selected option', () => { + const {mockAdapter, foundation} = setupTest(); + foundation.setSelectedIndex(1); + td.verify(mockAdapter.rmAttrForOptionAtIndex(td.matchers.number, 'aria-selected'), {times: 0}); }); -test('#setDisabled makes the select unfocusable when true', () => { - const {foundation, mockAdapter} = setupTest(); - foundation.setDisabled(true); - td.verify(mockAdapter.makeUntabbable()); +test('#setSelectedIndex calls setSelectedIndex', () => { + const {mockAdapter, foundation} = setupTest(); + foundation.setSelectedIndex(1); + td.verify(mockAdapter.setSelectedIndex(1)); }); -test('#setDisabled sets disabled to false when false', () => { - const {foundation} = setupTest(); - foundation.setDisabled(false); - assert.isNotOk(foundation.isDisabled()); +test(`#setSelectedIndex calls addClass ${MDCSelectFoundation.cssClasses.IS_CHANGING}`, () => { + const {mockAdapter, foundation} = setupTest(); + foundation.setSelectedIndex(1); + td.verify(mockAdapter.addClass(MDCSelectFoundation.cssClasses.IS_CHANGING)); }); -test('#setDisabled removes the disabled class when false', () => { - const {foundation, mockAdapter} = setupTest(); - foundation.setDisabled(false); - td.verify(mockAdapter.removeClass(MDCSelectFoundation.cssClasses.DISABLED)); +test('#setSelectedIndex adds aria-selected to selected option and floats label', () => { + const {mockAdapter, foundation} = setupTest(); + td.when(mockAdapter.getValueForOptionAtIndex(1)).thenReturn('value'); + foundation.setSelectedIndex(1); + td.verify(mockAdapter.setAttrForOptionAtIndex(1, 'aria-selected', 'true')); + td.verify(mockAdapter.floatLabel(true)); }); -test('#setDisabled removes the aria-disabled attr when false', () => { - const {foundation, mockAdapter} = setupTest(); - foundation.setDisabled(false); - td.verify(mockAdapter.rmAttr('aria-disabled')); +test('#setSelectedIndex does not add aria-selected to selected option, but defloats label', () => { + const {mockAdapter, foundation} = setupTest(); + td.when(mockAdapter.getValueForOptionAtIndex(1)).thenReturn(''); + foundation.setSelectedIndex(1); + td.verify(mockAdapter.setAttrForOptionAtIndex(1, 'aria-selected', 'true'), {times: 0}); + td.verify(mockAdapter.floatLabel(false)); }); -test('#setDisabled makes the select focusable when false', () => { - const {foundation, mockAdapter} = setupTest(); - foundation.setDisabled(false); - td.verify(mockAdapter.makeTabbable()); +test('#setSelectedIndex defloats label if called with a number out of range', () => { + const {mockAdapter, foundation} = setupTest(); + td.when(mockAdapter.getValueForOptionAtIndex(1)).thenReturn('value'); + foundation.setSelectedIndex(4); + td.verify(mockAdapter.setAttrForOptionAtIndex(td.matchers.number, 'aria-selected', 'true'), {times: 0}); + td.verify(mockAdapter.floatLabel(false)); }); -test('#resize resizes the element to the longest-length option', () => { - const {foundation, mockAdapter} = setupTest(); - const ctx = td.object({ - font: 'default font', - measureText: () => {}, - }); - const paddingRight = 16; - const paddingLeft = 20; - - td.when(mockAdapter.create2dRenderingContext()).thenReturn(ctx); - td.when(mockAdapter.getComputedStyleValue('font')).thenReturn('16px Roboto'); - td.when(mockAdapter.getComputedStyleValue('letter-spacing')).thenReturn('2.5px'); - td.when(mockAdapter.getComputedStyleValue('padding-right')).thenReturn(`${paddingRight}px`); - td.when(mockAdapter.getComputedStyleValue('padding-left')).thenReturn(`${paddingLeft}px`); - - // Add space on last option to test trimming - const opts = ['longer', 'longest', ' short ']; - const widths = [100, 200, 50]; - td.when(mockAdapter.getNumberOfOptions()).thenReturn(opts.length); - opts.forEach((txt, i) => { - td.when(mockAdapter.getTextForOptionAtIndex(i)).thenReturn(txt); - td.when(ctx.measureText(txt.trim())).thenReturn({width: widths[i]}); - }); - - foundation.init(); - foundation.resize(); - assert.equal(ctx.font, '16px Roboto'); - // ceil(letter-spacing * 'longest'.length + longest measured width + extra padding) - const expectedWidth = Math.ceil((2.5 * 7) + Math.max(...widths) + - paddingRight + paddingLeft); - - td.verify(mockAdapter.setStyle('width', `${expectedWidth}px`)); +test(`#setSelectedIndex removesClass ${MDCSelectFoundation.cssClasses.IS_CHANGING}`, () => { + const {mockAdapter, foundation} = setupTest(); + const clock = lolex.install(); + foundation.setSelectedIndex(1); + clock.tick(MDCSelectFoundation.numbers.SELECT_TEXT_TRANSITION_TIME); + td.verify(mockAdapter.removeClass(MDCSelectFoundation.cssClasses.IS_CHANGING)); }); -test('#resize falls back to font-{family,size} if shorthand is not supported', () => { - const {foundation, mockAdapter} = setupTest(); - const ctx = td.object({ - font: 'default font', - measureText: () => {}, - }); - const paddingRight = 16; - const paddingLeft = 20; - - td.when(mockAdapter.create2dRenderingContext()).thenReturn(ctx); - td.when(mockAdapter.getComputedStyleValue('font')).thenReturn(null); - td.when(mockAdapter.getComputedStyleValue('font-size')).thenReturn('16px'); - td.when(mockAdapter.getComputedStyleValue('font-family')).thenReturn('Roboto,sans-serif'); - td.when(mockAdapter.getComputedStyleValue('letter-spacing')).thenReturn('2.5px'); - td.when(mockAdapter.getComputedStyleValue('padding-right')).thenReturn(`${paddingRight}px`); - td.when(mockAdapter.getComputedStyleValue('padding-left')).thenReturn(`${paddingLeft}px`); - - // Add space on last option to test trimming - const opts = ['longer', 'longest', ' short ']; - const widths = [100, 200, 50]; - td.when(mockAdapter.getNumberOfOptions()).thenReturn(opts.length); - opts.forEach((txt, i) => { - td.when(mockAdapter.getTextForOptionAtIndex(i)).thenReturn(txt); - td.when(ctx.measureText(txt.trim())).thenReturn({width: widths[i]}); - }); - - foundation.init(); - foundation.resize(); - assert.equal(ctx.font, '16px Roboto'); - - // ceil(letter-spacing * 'longest'.length + longest measured width) - const expectedWidth = Math.ceil((2.5 * 7) + Math.max(...widths) + - paddingRight + paddingLeft); - - td.verify(mockAdapter.setStyle('width', `${expectedWidth}px`)); +test('#getValue will return the adapter getValue', () => { + const {mockAdapter, foundation} = setupTest(); + assert.equal(foundation.getValue(), mockAdapter.getValue()); }); -test('#destroy deregisters all events registered within init()', () => { - const {foundation, mockAdapter} = setupTest(); - td.when(mockAdapter.create2dRenderingContext()).thenReturn({}); - td.when(mockAdapter.getComputedStyleValue('font')).thenReturn('16px Times'); - const handlers = captureHandlers(mockAdapter, 'registerInteractionHandler'); - const menuHandlers = captureHandlers(mockAdapter, 'registerMenuInteractionHandler'); - foundation.init(); - foundation.destroy(); - Object.keys(handlers).forEach((type) => { - td.verify(mockAdapter.deregisterInteractionHandler(type, td.matchers.isA(Function))); - }); - Object.keys(menuHandlers).forEach((type) => { - td.verify( - mockAdapter.deregisterMenuInteractionHandler(type, td.matchers.isA(Function)) - ); - }); +test('#setValue will call setValue on adapter', () => { + const {mockAdapter, foundation} = setupTest(); + td.when(mockAdapter.getIndexForOptionValue('value')).thenReturn(1); + foundation.setValue('value'); + td.verify(mockAdapter.setValue('value')); }); -test('#getValue() returns the value of the option at the selected index', () => { - const {foundation, mockAdapter} = setupTest(); - const opts = ['a', 'SELECTED', 'b']; - const selectedIndex = 1; - td.when(mockAdapter.getNumberOfOptions()).thenReturn(opts.length); - td.when(mockAdapter.getValueForOptionAtIndex(selectedIndex)).thenReturn(opts[selectedIndex]); - td.when(mockAdapter.getTextForOptionAtIndex(selectedIndex)).thenReturn(`${opts[selectedIndex]} text`); - - foundation.setSelectedIndex(selectedIndex); - assert.equal(foundation.getValue(), opts[selectedIndex]); +test('#setValue calls setSelectedIndex, which calls setAttrForOptionAtIndex with the index', () => { + const {mockAdapter, foundation} = setupTest(); + td.when(mockAdapter.getValueForOptionAtIndex(1)).thenReturn('value'); + td.when(mockAdapter.getIndexForOptionValue('value')).thenReturn(1); + foundation.setValue('value'); + td.verify(mockAdapter.setAttrForOptionAtIndex(1, 'aria-selected', 'true')); }); -test('#getValue() returns an empty string if selected index < 0', () => { - const {foundation} = setupTest(); - assert.equal(foundation.getValue(), ''); +test('#setValue does not call setSelectedIndex if the option is already selected', () => { + const {mockAdapter, foundation} = setupTest(); + foundation.selectedIndex_ = 1; + td.when(mockAdapter.getValueForOptionAtIndex(1)).thenReturn('value'); + td.when(mockAdapter.getIndexForOptionValue('value')).thenReturn(1); + foundation.setValue('value'); + td.verify(mockAdapter.setAttrForOptionAtIndex(1, 'aria-selected', 'true'), {times: 0}); }); diff --git a/test/unit/mdc-select/mdc-select.test.js b/test/unit/mdc-select/mdc-select.test.js index 7a1a47ecdb8..f0407da848e 100644 --- a/test/unit/mdc-select/mdc-select.test.js +++ b/test/unit/mdc-select/mdc-select.test.js @@ -20,23 +20,6 @@ import domEvents from 'dom-events'; import td from 'testdouble'; import {MDCSelect} from '../../../packages/mdc-select'; -import {strings} from '../../../packages/mdc-select/constants'; - -class FakeMenu { - constructor() { - this.items = [ - bel`
    Item 1
    `, - bel`
    Item 2
    `, - bel`
    Item 3
    `, - bel`
    Item 4 no id
    `, - ]; - this.listen = td.func('menu.listen'); - this.unlisten = td.func('menu.unlisten'); - this.show = td.func('menu.show'); - this.hide = td.func('menu.hide'); - this.open = false; - } -} class FakeLabel { constructor() { @@ -53,19 +36,20 @@ class FakeBottomLine { function getFixture() { return bel` -
    -
    -
    Pick a Food Group
    -
    -
    -
    -
    -
      -
    • - Pick a food group -
    • -
    -
    +
    +
    Pick a Food Group
    +
    +
    `; } @@ -79,31 +63,15 @@ test('attachTo returns a component instance', () => { function setupTest() { const bottomLine = new FakeBottomLine(); const label = new FakeLabel(); - const menu = new FakeMenu(); const fixture = getFixture(); const surface = fixture.querySelector('.mdc-select__surface'); const labelEl = fixture.querySelector('.mdc-select__label'); const bottomLineEl = fixture.querySelector('.mdc-select__bottom-line'); - const menuEl = fixture.querySelector('.mdc-select__menu'); - const component = new MDCSelect(fixture, /* foundation */ undefined, () => menu, () => label, () => bottomLine); + const component = new MDCSelect(fixture, /* foundation */ undefined, () => label, () => bottomLine); - return {menu, menuEl, fixture, surface, label, labelEl, bottomLine, bottomLineEl, component}; + return {fixture, surface, label, labelEl, bottomLine, bottomLineEl, component}; } -test('options returns the menu items', () => { - const {menu, component} = setupTest(); - assert.equal(component.options, menu.items); -}); - -test('selectOptions returns a NodeList containing the node with "aria-selected" as an attr', () => { - const {component, fixture} = setupTest(); - const option = fixture.querySelector('[role="option"]'); - assert.equal(component.selectedOptions.length, 0, 'No selected options when none selected'); - option.setAttribute('aria-selected', 'true'); - assert.equal(component.selectedOptions.length, 1, 'One selected option when element has aria-selected'); - assert.equal(component.selectedOptions[0], option, 'Option within selected options is the selected option'); -}); - test('#get/setSelectedIndex', () => { const {component} = setupTest(); assert.equal(component.selectedIndex, -1); @@ -115,21 +83,16 @@ test('#get/setDisabled', () => { const {component} = setupTest(); assert.equal(component.disabled, false); component.disabled = true; - assert.isOk(component.disabled); + assert.isTrue(component.disabled); }); test('#get value', () => { const {component} = setupTest(); assert.equal(component.value, ''); + component.selectedIndex = 2; + assert.equal(component.value, 'apple'); component.selectedIndex = 1; - assert.equal(component.value, 'item-2'); - component.selectedIndex = 3; - assert.equal(component.value, 'Item 4 no id'); -}); - -test('#item returns the menu item at the specified index', () => { - const {menu, component} = setupTest(); - assert.equal(component.item(1), menu.items[1]); + assert.equal(component.value, 'orange'); }); test('#item returns null if index out of bounds', () => { @@ -137,38 +100,26 @@ test('#item returns null if index out of bounds', () => { assert.equal(component.item(100), null); }); -test('#nameditem returns the item whose id matches the given key', () => { - const {menu, component} = setupTest(); - assert.equal(component.nameditem('item-1'), menu.items[0]); -}); - -test('#nameditem returns the item whose "name" key matches the given key', () => { - const {menu, component} = setupTest(); - const item = menu.items[0]; - const name = item.id; - item.id = ''; - item.removeAttribute('id'); - item.setAttribute('name', name); - assert.equal(component.nameditem(name), menu.items[0]); -}); - -test('#nameditem returns null when no id or name matches the given key', () => { - const {component} = setupTest(); - assert.equal(component.nameditem('nonexistant'), null); -}); - -test('#initialSyncWithDOM sets the selected index if a menu item contains an aria-selected attribute', () => { - const menu = new FakeMenu(); - const fixture = getFixture(); - // Insert menu item into fixture to pretend like the fake menu items are part of the component under test. - menu.items[1].setAttribute('aria-selected', 'true'); - fixture.appendChild(menu.items[1]); - - const component = new MDCSelect(fixture, /* foundation */ undefined, () => menu); +test('#initialSyncWithDOM sets the selected index if an option has the selected attr', () => { + const fixture = bel` +
    +
    Pick a Food Group
    +
    + +
    + `; + const component = new MDCSelect(fixture, /* foundation */ undefined); assert.equal(component.selectedIndex, 1); }); -test('#initialSyncWithDOM disables the menu if aria-disabled="true" is found on the element', () => { +test('#initialSyncWithDOM disables the select if aria-disabled="true" is found on the element', () => { const fixture = getFixture(); fixture.setAttribute('aria-disabled', 'true'); const component = new MDCSelect(fixture); @@ -178,14 +129,14 @@ test('#initialSyncWithDOM disables the menu if aria-disabled="true" is found on test('adapter#addClass adds a class to the root element', () => { const {component, fixture} = setupTest(); component.getDefaultFoundation().adapter_.addClass('foo'); - assert.isOk(fixture.classList.contains('foo')); + assert.isTrue(fixture.classList.contains('foo')); }); test('adapter#removeClass removes a class from the root element', () => { const {component, fixture} = setupTest(); fixture.classList.add('foo'); component.getDefaultFoundation().adapter_.removeClass('foo'); - assert.isNotOk(fixture.classList.contains('foo')); + assert.isFalse(fixture.classList.contains('foo')); }); test('adapter#floatLabel adds a class to the label', () => { @@ -219,15 +170,7 @@ test('adapter#rmAttr removes an attribute from the root element', () => { const {component, fixture} = setupTest(); fixture.setAttribute('aria-disabled', 'true'); component.getDefaultFoundation().adapter_.rmAttr('aria-disabled'); - assert.isNotOk(fixture.hasAttribute('aria-disabled')); -}); - -test('adapter#computeBoundingRect returns the result of getBoundingClientRect() on the root element', () => { - const {component, fixture} = setupTest(); - assert.deepEqual( - component.getDefaultFoundation().adapter_.computeBoundingRect(), - fixture.getBoundingClientRect() - ); + assert.isFalse(fixture.hasAttribute('aria-disabled')); }); test('adapter#registerInteractionHandler adds an event listener to the surface element', () => { @@ -247,123 +190,11 @@ test('adapter#deregisterInteractionHandler removes an event listener from the su td.verify(listener(td.matchers.anything()), {times: 0}); }); -test('adapter#focus focuses on the surface element', () => { - const {component, fixture, surface} = setupTest(); - document.body.appendChild(fixture); - - component.getDefaultFoundation().adapter_.focus(); - assert.equal(document.activeElement, surface); - - document.body.removeChild(fixture); -}); - -test('adapter#makeTabbable sets the surface element\'s tabindex to 0', () => { - const {component, surface} = setupTest(); - surface.tabIndex = -1; - component.getDefaultFoundation().adapter_.makeTabbable(); - assert.equal(surface.tabIndex, 0); -}); - -test('adapter#makeUntabbable sets the surface element\'s tabindex to -1', () => { - const {component, surface} = setupTest(); - surface.tabIndex = 0; - component.getDefaultFoundation().adapter_.makeUntabbable(); - assert.equal(surface.tabIndex, -1); -}); - -test('adapter#getComputedStyleValue gets the computed style value of the prop from the surface element', () => { - const {component, fixture, surface} = setupTest(); - document.body.appendChild(fixture); - surface.style.width = '500px'; - assert.equal( - component.getDefaultFoundation().adapter_.getComputedStyleValue('width'), - getComputedStyle(surface).getPropertyValue('width') - ); - document.body.removeChild(fixture); -}); - -test('adapter#setStyle sets the given style propertyName to the given value', () => { - const {component, surface} = setupTest(); - component.getDefaultFoundation().adapter_.setStyle('font-size', '13px'); - assert.equal(surface.style.getPropertyValue('font-size'), '13px'); -}); - -test('adapter#create2dRenderingContext returns a CanvasRenderingContext2d instance', () => { - const {component} = setupTest(); - const fakeCtx = {}; - const fakeCanvas = { - getContext: td.func('canvas.getContext'), - }; - const origCreateElement = document.createElement; - document.createElement = td.func('document.createElement'); - - td.when(fakeCanvas.getContext('2d')).thenReturn(fakeCtx); - td.when(document.createElement('canvas')).thenReturn(fakeCanvas); - - const ctx = component.getDefaultFoundation().adapter_.create2dRenderingContext(); - assert.equal(ctx, fakeCtx); - - document.createElement = origCreateElement; -}); - -test('adapter#setMenuElStyle sets a style property on the menu element', () => { - const {component, menuEl} = setupTest(); - component.getDefaultFoundation().adapter_.setMenuElStyle('font-size', '10px'); - assert.equal(menuEl.style.fontSize, '10px'); -}); - -test('adapter#setMenuElAttr sets an attribute on the menu element', () => { - const {component, menuEl} = setupTest(); - component.getDefaultFoundation().adapter_.setMenuElAttr('aria-hidden', 'true'); - assert.equal(menuEl.getAttribute('aria-hidden'), 'true'); -}); - -test('adapter#rmMenuElAttr removes an attribute from the menu element', () => { - const {component, menuEl} = setupTest(); - menuEl.setAttribute('aria-hidden', 'true'); - component.getDefaultFoundation().adapter_.rmMenuElAttr('aria-hidden'); - assert.isNotOk(menuEl.hasAttribute('aria-hidden')); -}); - -test('adapter#getMenuElOffsetHeight returns the menu element\'s offsetHeight', () => { - const {component, menuEl} = setupTest(); - assert.equal(component.getDefaultFoundation().adapter_.getMenuElOffsetHeight(), menuEl.offsetHeight); -}); - -test('adapter#openMenu shows the menu with the given focusIndex', () => { - const {component, menu} = setupTest(); - component.getDefaultFoundation().adapter_.openMenu(1); - td.verify(menu.show({focusIndex: 1})); -}); - -test('adapter#isMenuOpen returns whether or not the menu is open', () => { - const {component, menu} = setupTest(); - const {adapter_: adapter} = component.getDefaultFoundation(); - menu.open = true; - assert.isOk(adapter.isMenuOpen()); - menu.open = false; - assert.isNotOk(adapter.isMenuOpen()); -}); - -test('adapter#setSelectedTextContent sets the textContent of the selected text el', () => { - const {component, fixture} = setupTest(); - component.getDefaultFoundation().adapter_.setSelectedTextContent('content'); - assert.equal(fixture.querySelector('.mdc-select__selected-text').textContent, 'content'); -}); - test('adapter#getNumberOfOptions returns the length of the component\'s options property', () => { const {component} = setupTest(); assert.equal(component.getDefaultFoundation().adapter_.getNumberOfOptions(), component.options.length); }); -test('adapter#getTextForOptionAtIndex gets the text content for the option at the given index', () => { - const {component} = setupTest(); - assert.equal( - component.getDefaultFoundation().adapter_.getTextForOptionAtIndex(1), - component.options[1].textContent - ); -}); - test('adapter#setAttrForOptionAtIndex sets an attribute to the given value for the option at the ' + 'given index', () => { const {component} = setupTest(); @@ -375,66 +206,45 @@ test('adapter#rmAttrForOptionAtIndex removes the given attribute for the option const {component} = setupTest(); component.options[1].setAttribute('aria-disabled', 'true'); component.getDefaultFoundation().adapter_.rmAttrForOptionAtIndex(1, 'aria-disabled'); - assert.isNotOk(component.options[1].hasAttribute('aria-disabled')); + assert.isFalse(component.options[1].hasAttribute('aria-disabled')); }); -test('adapter#getOffsetTopForOptionAtIndex returns the offsetTop for the option at the given index', () => { - const {component, menu} = setupTest(); - assert.equal( - component.getDefaultFoundation().adapter_.getOffsetTopForOptionAtIndex(1), - menu.items[1].offsetTop - ); -}); - -test('adapter#registerMenuInteractionHandler listens for an interaction handler on the menu', () => { - const {component, menu} = setupTest(); - const handler = () => {}; - component.getDefaultFoundation().adapter_.registerMenuInteractionHandler('evt', handler); - td.verify(menu.listen('evt', handler)); -}); - -test('adapter#deregisterMenuInteractionHandler unlistens for an interaction handler on the menu', () => { - const {component, menu} = setupTest(); - const handler = () => {}; - component.getDefaultFoundation().adapter_.deregisterMenuInteractionHandler('evt', handler); - td.verify(menu.unlisten('evt', handler)); -}); - -test(`adapter#notifyChange emits an ${strings.CHANGE_EVENT} custom event from the root element`, () => { - const {component, fixture} = setupTest(); - const handler = td.func('change handler'); - fixture.addEventListener(strings.CHANGE_EVENT, handler); - component.getDefaultFoundation().adapter_.notifyChange(); - td.verify(handler(td.matchers.anything())); +test('adapter#getValueForOptionAtIndex returns the id of the option at the given index', () => { + const {component} = setupTest(); + assert.equal(component.getDefaultFoundation().adapter_.getValueForOptionAtIndex(1), 'orange'); }); -test('adapter#getWindowInnerHeight returns window.innerHeight', () => { +test('adapter#getIndexForOptionValue returns the index of the option with a value', () => { const {component} = setupTest(); - assert.equal(component.getDefaultFoundation().adapter_.getWindowInnerHeight(), window.innerHeight); + assert.equal(component.getDefaultFoundation().adapter_.getIndexForOptionValue('apple'), 2); }); -test('adapter#getValueForOptionAtIndex returns the id of the option at the given index', () => { +test('adapter#getIndexForOptionValue returns null if there is no option with the specific value', () => { const {component} = setupTest(); - assert.equal(component.getDefaultFoundation().adapter_.getValueForOptionAtIndex(1), 'item-2'); + assert.equal(component.getDefaultFoundation().adapter_.getIndexForOptionValue('grape'), null); }); -test('adapter#getValueForOptionAtIndex returns the textContent of the option at given index when ' + - 'no id value present', () => { - const {component} = setupTest(); - assert.equal(component.getDefaultFoundation().adapter_.getValueForOptionAtIndex(3), 'Item 4 no id'); +test('adapter#getValue returns the value of the select element', () => { + const {surface, component} = setupTest(); + surface.selectedIndex = 1; + assert.equal(component.getDefaultFoundation().adapter_.getValue(), 'orange'); }); -test('adapter#addBodyClass adds a class to the body', () => { - const {component} = setupTest(); - component.getDefaultFoundation().adapter_.addBodyClass('mdc-select-scroll-lock'); - assert.isOk(document.querySelector('body').classList.contains('mdc-select-scroll-lock')); +test('adapter#getValue returns empty string if there is no selected value', () => { + const {surface, component} = setupTest(); + surface.selectedIndex = -1; + assert.equal(component.getDefaultFoundation().adapter_.getValue(), ''); }); -test('adapter#removeBodyClass remove a class from the body', () => { - const {component} = setupTest(); - const body = document.querySelector('body'); +test('adapter#setValue sets the value of the select to the correct option', () => { + const {surface, component} = setupTest(); + component.getDefaultFoundation().adapter_.setValue('orange'); + assert.equal(surface.selectedIndex, 1); +}); - body.classList.add('mdc-select-scroll-lock'); - component.getDefaultFoundation().adapter_.removeBodyClass('mdc-select-scroll-lock'); - assert.isNotOk(body.classList.contains('mdc-select-scroll-lock')); +test('adapter#setValue sets the selectedIndex of the select to -1 if there is ' + + 'no option with the specified value', () => { + const {surface, component} = setupTest(); + component.getDefaultFoundation().adapter_.setValue('grape'); + assert.equal(surface.selectedIndex, -1); }); From 440a65225480965e91b2d86486dd960d57401b36 Mon Sep 17 00:00:00 2001 From: Matt Goo Date: Mon, 26 Mar 2018 14:53:23 -0700 Subject: [PATCH 06/19] WIP: test update --- test/unit/mdc-select/mdc-select.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unit/mdc-select/mdc-select.test.js b/test/unit/mdc-select/mdc-select.test.js index f0407da848e..c3d07fdeb19 100644 --- a/test/unit/mdc-select/mdc-select.test.js +++ b/test/unit/mdc-select/mdc-select.test.js @@ -37,8 +37,6 @@ class FakeBottomLine { function getFixture() { return bel`
    -
    Pick a Food Group
    -
    +
    Pick a Food Group
    +
    `; } From 47ac8112c346a07ac82162ba48b55b7722a78b74 Mon Sep 17 00:00:00 2001 From: Matt Goo Date: Mon, 26 Mar 2018 15:12:40 -0700 Subject: [PATCH 07/19] WIP: removed unintentional changes --- package-lock.json | 6 ++---- packages/mdc-theme/_color-palette.scss | 6 +++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1d095bdc231..42b1c8bd508 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4849,8 +4849,7 @@ "jsbn": { "version": "0.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "json-schema": { "version": "0.2.3", @@ -5268,8 +5267,7 @@ "tweetnacl": { "version": "0.14.5", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "uid-number": { "version": "0.0.6", diff --git a/packages/mdc-theme/_color-palette.scss b/packages/mdc-theme/_color-palette.scss index ed52a9bf024..ef5122b5a0d 100644 --- a/packages/mdc-theme/_color-palette.scss +++ b/packages/mdc-theme/_color-palette.scss @@ -59,7 +59,7 @@ $material-color-purple-900: #4a148c; $material-color-purple-a100: #ea80fc; $material-color-purple-a200: #e040fb; $material-color-purple-a400: #d500f9; -$material-color-purple-a700: #a0f; +$material-color-purple-a700: #aa00ff; $material-color-deep-purple-50: #ede7f6; $material-color-deep-purple-100: #d1c4e9; @@ -207,7 +207,7 @@ $material-color-yellow-700: #fbc02d; $material-color-yellow-800: #f9a825; $material-color-yellow-900: #f57f17; $material-color-yellow-a100: #ffff8d; -$material-color-yellow-a200: #ff0; +$material-color-yellow-a200: #ffff00; $material-color-yellow-a400: #ffea00; $material-color-yellow-a700: #ffd600; @@ -269,7 +269,7 @@ $material-color-brown-900: #3e2723; $material-color-grey-50: #fafafa; $material-color-grey-100: #f5f5f5; -$material-color-grey-200: #eee; +$material-color-grey-200: #eeeeee; $material-color-grey-300: #e0e0e0; $material-color-grey-400: #bdbdbd; $material-color-grey-500: #9e9e9e; From 8eafe05656570c83d4469981194c34fc213c7992 Mon Sep 17 00:00:00 2001 From: Matt Goo Date: Mon, 26 Mar 2018 17:08:31 -0700 Subject: [PATCH 08/19] WIP: removed aria selected and aria disabled --- demos/select.html | 14 +++--- packages/mdc-select/README.md | 20 ++++----- packages/mdc-select/foundation.js | 12 ----- packages/mdc-select/index.js | 6 +-- .../unit/mdc-select/foundation-events.test.js | 1 - test/unit/mdc-select/foundation.test.js | 44 +++---------------- test/unit/mdc-select/mdc-select.test.js | 36 +++------------ 7 files changed, 29 insertions(+), 104 deletions(-) diff --git a/demos/select.html b/demos/select.html index 357a195f51c..ffaebdfa096 100644 --- a/demos/select.html +++ b/demos/select.html @@ -62,10 +62,10 @@
    -
    Pick a Food Group
    +
    Pick a Food Group
    @@ -96,7 +96,7 @@

    Fully-Featured JS Component

    - -