@instructure/ui-select 8.43.2-snapshot-13 → 8.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,9 +3,13 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
- ## [8.43.2-snapshot-13](https://github.com/instructure/instructure-ui/compare/v8.43.1...v8.43.2-snapshot-13) (2023-09-21)
6
+ # [8.44.0](https://github.com/instructure/instructure-ui/compare/v8.43.1...v8.44.0) (2023-09-21)
7
7
 
8
- **Note:** Version bump only for package @instructure/ui-select
8
+
9
+ ### Bug Fixes
10
+
11
+ * **ui-select,ui-simple-select:** add new tests ([e156baa](https://github.com/instructure/instructure-ui/commit/e156baa29c0d8fe70037057a500f1217bc2d51e5))
12
+ * **ui-select:** add workaround for safari combobox ([09987bb](https://github.com/instructure/instructure-ui/commit/09987bb97b49eb7dbef934eb1f8590ea2106e08b))
9
13
 
10
14
 
11
15
 
@@ -0,0 +1,74 @@
1
+ /*
2
+ * The MIT License (MIT)
3
+ *
4
+ * Copyright (c) 2015 - present Instructure, Inc.
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+ import React from 'react';
25
+ import { render } from '@testing-library/react';
26
+ import '@testing-library/jest-dom/extend-expect';
27
+ import Select from '../index';
28
+ import * as utils from '@instructure/ui-utils';
29
+ jest.mock('@instructure/ui-utils', () => {
30
+ const originalModule = jest.requireActual('@instructure/ui-utils');
31
+ return {
32
+ __esModule: true,
33
+ ...originalModule,
34
+ isSafari: jest.fn(() => true)
35
+ };
36
+ });
37
+ const mockUtils = utils;
38
+ describe('<Select />', () => {
39
+ const defaultOptions = ['foo', 'bar', 'baz'];
40
+ const getOptions = disabled => defaultOptions.map(opt => /*#__PURE__*/React.createElement(Select.Option, {
41
+ id: opt,
42
+ key: opt,
43
+ value: opt,
44
+ isDisabled: opt === disabled
45
+ }, opt));
46
+ it('should have role button in Safari without onInputChange', async () => {
47
+ const _render = render( /*#__PURE__*/React.createElement(Select, {
48
+ renderLabel: "Choose an option"
49
+ }, getOptions())),
50
+ container = _render.container;
51
+ const input = container.querySelector('input');
52
+ expect(input).toHaveAttribute('role', 'button');
53
+ });
54
+ it('should have role combobox in different browsers than Safari without onInputChange', async () => {
55
+ mockUtils.isSafari = jest.fn(() => false);
56
+ const _render2 = render( /*#__PURE__*/React.createElement(Select, {
57
+ renderLabel: "Choose an option"
58
+ }, getOptions())),
59
+ container = _render2.container;
60
+ const input = container.querySelector('input');
61
+ expect(input).toHaveAttribute('role', 'combobox');
62
+ });
63
+ it('should have role combobox with onInputChange', async () => {
64
+ const _render3 = render( /*#__PURE__*/React.createElement(Select, {
65
+ renderLabel: "Choose an option",
66
+ onInputChange: () => {}
67
+ }, getOptions())
68
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
69
+ ),
70
+ container = _render3.container;
71
+ const input = container.querySelector('input');
72
+ expect(input).toHaveAttribute('role', 'combobox');
73
+ });
74
+ });
@@ -29,7 +29,7 @@ var _dec, _dec2, _dec3, _class, _class2, _Options$Separator, _Options$Separator2
29
29
 
30
30
  /** @jsx jsx */
31
31
  import React, { Children, Component, memo } from 'react';
32
- import { createChainedFunction } from '@instructure/ui-utils';
32
+ import * as utils from '@instructure/ui-utils';
33
33
  import { testable } from '@instructure/ui-testable';
34
34
  import { matchComponentTypes, omitProps, getInteraction, withDeterministicId } from '@instructure/ui-react-utils';
35
35
  import { getBoundingClientRect, isActiveElement } from '@instructure/ui-dom-utils';
@@ -462,9 +462,13 @@ let Select = (_dec = withDeterministicId(), _dec2 = withStyle(generateStyle, gen
462
462
  // props to ensure screen readers treat uneditable selects as accessible
463
463
  // popup buttons rather than comboboxes.
464
464
  const overrideProps = !isEditable ? {
465
- role: 'button',
465
+ // Given that Safari with Voiceover does not support proper combobox
466
+ // handling, a button role is set as a workaround.
467
+ // See https://bugs.webkit.org/show_bug.cgi?id=236881
468
+ role: utils.isSafari() ? 'button' : 'combobox',
466
469
  title: inputValue,
467
- 'aria-autocomplete': void 0
470
+ 'aria-autocomplete': void 0,
471
+ 'aria-readonly': true
468
472
  } : {};
469
473
 
470
474
  // backdoor to autocomplete attr to work around chrome autofill issues
@@ -480,7 +484,7 @@ let Select = (_dec = withDeterministicId(), _dec2 = withStyle(generateStyle, gen
480
484
  htmlSize,
481
485
  messages,
482
486
  value: inputValue,
483
- inputRef: createChainedFunction(ref, this.handleInputRef),
487
+ inputRef: utils.createChainedFunction(ref, this.handleInputRef),
484
488
  inputContainerRef: this.handleInputContainerRef,
485
489
  interaction: interaction === 'enabled' && !isEditable ? 'readonly' // prevent keyboard cursor
486
490
  : interaction,
@@ -495,7 +499,7 @@ let Select = (_dec = withDeterministicId(), _dec2 = withStyle(generateStyle, gen
495
499
  // (before it was handled by TextInput's defaultProp)
496
500
  onChange: typeof onInputChange === 'function' ? onInputChange : inputValue ? () => {} : void 0,
497
501
  onFocus,
498
- onBlur: createChainedFunction(onBlur, onRequestHideOptions),
502
+ onBlur: utils.createChainedFunction(onBlur, onRequestHideOptions),
499
503
  ...overrideProps
500
504
  };
501
505
  return jsx(TextInput, Object.assign({}, triggerProps, getInputProps(inputProps)));
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+
3
+ var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
4
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
5
+ var _react = _interopRequireDefault(require("react"));
6
+ var _react2 = require("@testing-library/react");
7
+ require("@testing-library/jest-dom/extend-expect");
8
+ var _index = _interopRequireDefault(require("../index"));
9
+ var utils = _interopRequireWildcard(require("@instructure/ui-utils"));
10
+ /*
11
+ * The MIT License (MIT)
12
+ *
13
+ * Copyright (c) 2015 - present Instructure, Inc.
14
+ *
15
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
16
+ * of this software and associated documentation files (the "Software"), to deal
17
+ * in the Software without restriction, including without limitation the rights
18
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19
+ * copies of the Software, and to permit persons to whom the Software is
20
+ * furnished to do so, subject to the following conditions:
21
+ *
22
+ * The above copyright notice and this permission notice shall be included in all
23
+ * copies or substantial portions of the Software.
24
+ *
25
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31
+ * SOFTWARE.
32
+ */
33
+
34
+ jest.mock('@instructure/ui-utils', () => {
35
+ const originalModule = jest.requireActual('@instructure/ui-utils');
36
+ return {
37
+ __esModule: true,
38
+ ...originalModule,
39
+ isSafari: jest.fn(() => true)
40
+ };
41
+ });
42
+ const mockUtils = utils;
43
+ describe('<Select />', () => {
44
+ const defaultOptions = ['foo', 'bar', 'baz'];
45
+ const getOptions = disabled => defaultOptions.map(opt => /*#__PURE__*/_react.default.createElement(_index.default.Option, {
46
+ id: opt,
47
+ key: opt,
48
+ value: opt,
49
+ isDisabled: opt === disabled
50
+ }, opt));
51
+ it('should have role button in Safari without onInputChange', async () => {
52
+ const _render = (0, _react2.render)( /*#__PURE__*/_react.default.createElement(_index.default, {
53
+ renderLabel: "Choose an option"
54
+ }, getOptions())),
55
+ container = _render.container;
56
+ const input = container.querySelector('input');
57
+ expect(input).toHaveAttribute('role', 'button');
58
+ });
59
+ it('should have role combobox in different browsers than Safari without onInputChange', async () => {
60
+ mockUtils.isSafari = jest.fn(() => false);
61
+ const _render2 = (0, _react2.render)( /*#__PURE__*/_react.default.createElement(_index.default, {
62
+ renderLabel: "Choose an option"
63
+ }, getOptions())),
64
+ container = _render2.container;
65
+ const input = container.querySelector('input');
66
+ expect(input).toHaveAttribute('role', 'combobox');
67
+ });
68
+ it('should have role combobox with onInputChange', async () => {
69
+ const _render3 = (0, _react2.render)( /*#__PURE__*/_react.default.createElement(_index.default, {
70
+ renderLabel: "Choose an option",
71
+ onInputChange: () => {}
72
+ }, getOptions())
73
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
74
+ ),
75
+ container = _render3.container;
76
+ const input = container.querySelector('input');
77
+ expect(input).toHaveAttribute('role', 'combobox');
78
+ });
79
+ });
@@ -8,7 +8,7 @@ Object.defineProperty(exports, "__esModule", {
8
8
  exports.default = exports.Select = void 0;
9
9
  var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
10
10
  var _react = _interopRequireWildcard(require("react"));
11
- var _createChainedFunction = require("@instructure/ui-utils/lib/createChainedFunction.js");
11
+ var utils = _interopRequireWildcard(require("@instructure/ui-utils"));
12
12
  var _testable = require("@instructure/ui-testable/lib/testable.js");
13
13
  var _matchComponentTypes = require("@instructure/ui-react-utils/lib/matchComponentTypes.js");
14
14
  var _omitProps = require("@instructure/ui-react-utils/lib/omitProps.js");
@@ -474,9 +474,13 @@ let Select = (_dec = (0, _withDeterministicId.withDeterministicId)(), _dec2 = (0
474
474
  // props to ensure screen readers treat uneditable selects as accessible
475
475
  // popup buttons rather than comboboxes.
476
476
  const overrideProps = !isEditable ? {
477
- role: 'button',
477
+ // Given that Safari with Voiceover does not support proper combobox
478
+ // handling, a button role is set as a workaround.
479
+ // See https://bugs.webkit.org/show_bug.cgi?id=236881
480
+ role: utils.isSafari() ? 'button' : 'combobox',
478
481
  title: inputValue,
479
- 'aria-autocomplete': void 0
482
+ 'aria-autocomplete': void 0,
483
+ 'aria-readonly': true
480
484
  } : {};
481
485
 
482
486
  // backdoor to autocomplete attr to work around chrome autofill issues
@@ -492,7 +496,7 @@ let Select = (_dec = (0, _withDeterministicId.withDeterministicId)(), _dec2 = (0
492
496
  htmlSize,
493
497
  messages,
494
498
  value: inputValue,
495
- inputRef: (0, _createChainedFunction.createChainedFunction)(ref, this.handleInputRef),
499
+ inputRef: utils.createChainedFunction(ref, this.handleInputRef),
496
500
  inputContainerRef: this.handleInputContainerRef,
497
501
  interaction: interaction === 'enabled' && !isEditable ? 'readonly' // prevent keyboard cursor
498
502
  : interaction,
@@ -507,7 +511,7 @@ let Select = (_dec = (0, _withDeterministicId.withDeterministicId)(), _dec2 = (0
507
511
  // (before it was handled by TextInput's defaultProp)
508
512
  onChange: typeof onInputChange === 'function' ? onInputChange : inputValue ? () => {} : void 0,
509
513
  onFocus,
510
- onBlur: (0, _createChainedFunction.createChainedFunction)(onBlur, onRequestHideOptions),
514
+ onBlur: utils.createChainedFunction(onBlur, onRequestHideOptions),
511
515
  ...overrideProps
512
516
  };
513
517
  return (0, _emotion.jsx)(_TextInput.TextInput, Object.assign({}, triggerProps, getInputProps(inputProps)));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@instructure/ui-select",
3
- "version": "8.43.2-snapshot-13",
3
+ "version": "8.44.0",
4
4
  "description": "A component for select and autocomplete behavior.",
5
5
  "author": "Instructure, Inc. Engineering and Product Design",
6
6
  "module": "./es/index.js",
@@ -23,30 +23,32 @@
23
23
  },
24
24
  "license": "MIT",
25
25
  "devDependencies": {
26
- "@instructure/ui-babel-preset": "8.43.2-snapshot-13",
27
- "@instructure/ui-color-utils": "8.43.2-snapshot-13",
28
- "@instructure/ui-test-locator": "8.43.2-snapshot-13",
29
- "@instructure/ui-test-utils": "8.43.2-snapshot-13",
30
- "@instructure/ui-themes": "8.43.2-snapshot-13"
26
+ "@instructure/ui-babel-preset": "8.44.0",
27
+ "@instructure/ui-color-utils": "8.44.0",
28
+ "@instructure/ui-test-locator": "8.44.0",
29
+ "@instructure/ui-test-utils": "8.44.0",
30
+ "@instructure/ui-themes": "8.44.0",
31
+ "@testing-library/jest-dom": "^5.17.0",
32
+ "@testing-library/react": "^14.0.0"
31
33
  },
32
34
  "dependencies": {
33
35
  "@babel/runtime": "^7.22.15",
34
- "@instructure/emotion": "8.43.2-snapshot-13",
35
- "@instructure/shared-types": "8.43.2-snapshot-13",
36
- "@instructure/ui-dom-utils": "8.43.2-snapshot-13",
37
- "@instructure/ui-form-field": "8.43.2-snapshot-13",
38
- "@instructure/ui-icons": "8.43.2-snapshot-13",
39
- "@instructure/ui-options": "8.43.2-snapshot-13",
40
- "@instructure/ui-popover": "8.43.2-snapshot-13",
41
- "@instructure/ui-position": "8.43.2-snapshot-13",
42
- "@instructure/ui-prop-types": "8.43.2-snapshot-13",
43
- "@instructure/ui-react-utils": "8.43.2-snapshot-13",
44
- "@instructure/ui-selectable": "8.43.2-snapshot-13",
45
- "@instructure/ui-testable": "8.43.2-snapshot-13",
46
- "@instructure/ui-text-input": "8.43.2-snapshot-13",
47
- "@instructure/ui-utils": "8.43.2-snapshot-13",
48
- "@instructure/ui-view": "8.43.2-snapshot-13",
49
- "@instructure/uid": "8.43.2-snapshot-13",
36
+ "@instructure/emotion": "8.44.0",
37
+ "@instructure/shared-types": "8.44.0",
38
+ "@instructure/ui-dom-utils": "8.44.0",
39
+ "@instructure/ui-form-field": "8.44.0",
40
+ "@instructure/ui-icons": "8.44.0",
41
+ "@instructure/ui-options": "8.44.0",
42
+ "@instructure/ui-popover": "8.44.0",
43
+ "@instructure/ui-position": "8.44.0",
44
+ "@instructure/ui-prop-types": "8.44.0",
45
+ "@instructure/ui-react-utils": "8.44.0",
46
+ "@instructure/ui-selectable": "8.44.0",
47
+ "@instructure/ui-testable": "8.44.0",
48
+ "@instructure/ui-text-input": "8.44.0",
49
+ "@instructure/ui-utils": "8.44.0",
50
+ "@instructure/ui-view": "8.44.0",
51
+ "@instructure/uid": "8.44.0",
50
52
  "prop-types": "^15.8.1"
51
53
  },
52
54
  "peerDependencies": {
@@ -0,0 +1,86 @@
1
+ /*
2
+ * The MIT License (MIT)
3
+ *
4
+ * Copyright (c) 2015 - present Instructure, Inc.
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+ import React from 'react'
25
+ import { render } from '@testing-library/react'
26
+ import '@testing-library/jest-dom/extend-expect'
27
+ import Select from '../index'
28
+ import * as utils from '@instructure/ui-utils'
29
+
30
+ type ExampleOption = 'foo' | 'bar' | 'baz'
31
+ jest.mock('@instructure/ui-utils', () => {
32
+ const originalModule = jest.requireActual('@instructure/ui-utils')
33
+
34
+ return {
35
+ __esModule: true,
36
+ ...originalModule,
37
+ isSafari: jest.fn(() => true)
38
+ }
39
+ })
40
+
41
+ const mockUtils = utils as jest.Mocked<typeof utils>
42
+
43
+ describe('<Select />', () => {
44
+ const defaultOptions: ExampleOption[] = ['foo', 'bar', 'baz']
45
+
46
+ const getOptions = (disabled?: ExampleOption) =>
47
+ defaultOptions.map((opt) => (
48
+ <Select.Option
49
+ id={opt}
50
+ key={opt}
51
+ value={opt}
52
+ isDisabled={opt === disabled}
53
+ >
54
+ {opt}
55
+ </Select.Option>
56
+ ))
57
+
58
+ it('should have role button in Safari without onInputChange', async () => {
59
+ const { container } = render(
60
+ <Select renderLabel="Choose an option">{getOptions()}</Select>
61
+ )
62
+ const input = container.querySelector('input')
63
+ expect(input).toHaveAttribute('role', 'button')
64
+ })
65
+
66
+ it('should have role combobox in different browsers than Safari without onInputChange', async () => {
67
+ mockUtils.isSafari = jest.fn(() => false)
68
+
69
+ const { container } = render(
70
+ <Select renderLabel="Choose an option">{getOptions()}</Select>
71
+ )
72
+ const input = container.querySelector('input')
73
+ expect(input).toHaveAttribute('role', 'combobox')
74
+ })
75
+
76
+ it('should have role combobox with onInputChange', async () => {
77
+ const { container } = render(
78
+ <Select renderLabel="Choose an option" onInputChange={() => {}}>
79
+ {getOptions()}
80
+ </Select>
81
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
82
+ )
83
+ const input = container.querySelector('input')
84
+ expect(input).toHaveAttribute('role', 'combobox')
85
+ })
86
+ })
@@ -25,7 +25,7 @@
25
25
  /** @jsx jsx */
26
26
  import React, { Children, Component, memo } from 'react'
27
27
 
28
- import { createChainedFunction } from '@instructure/ui-utils'
28
+ import * as utils from '@instructure/ui-utils'
29
29
  import { testable } from '@instructure/ui-testable'
30
30
  import {
31
31
  matchComponentTypes,
@@ -607,9 +607,13 @@ class Select extends Component<SelectProps> {
607
607
  // popup buttons rather than comboboxes.
608
608
  const overrideProps: Partial<TextInputProps> = !isEditable
609
609
  ? {
610
- role: 'button',
610
+ // Given that Safari with Voiceover does not support proper combobox
611
+ // handling, a button role is set as a workaround.
612
+ // See https://bugs.webkit.org/show_bug.cgi?id=236881
613
+ role: utils.isSafari() ? 'button' : 'combobox',
611
614
  title: inputValue,
612
- 'aria-autocomplete': undefined
615
+ 'aria-autocomplete': undefined,
616
+ 'aria-readonly': true
613
617
  }
614
618
  : {}
615
619
 
@@ -627,7 +631,7 @@ class Select extends Component<SelectProps> {
627
631
  htmlSize,
628
632
  messages,
629
633
  value: inputValue,
630
- inputRef: createChainedFunction(ref, this.handleInputRef),
634
+ inputRef: utils.createChainedFunction(ref, this.handleInputRef),
631
635
  inputContainerRef: this.handleInputContainerRef,
632
636
  interaction:
633
637
  interaction === 'enabled' && !isEditable
@@ -651,7 +655,7 @@ class Select extends Component<SelectProps> {
651
655
  : undefined,
652
656
 
653
657
  onFocus,
654
- onBlur: createChainedFunction(onBlur, onRequestHideOptions),
658
+ onBlur: utils.createChainedFunction(onBlur, onRequestHideOptions),
655
659
  ...overrideProps
656
660
  }
657
661