@atlaskit/react-select 2.0.5 → 2.0.7

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
@@ -1,5 +1,21 @@
1
1
  # @atlaskit/react-select
2
2
 
3
+ ## 2.0.7
4
+
5
+ ### Patch Changes
6
+
7
+ - [#131835](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/pull-requests/131835)
8
+ [`11b3a9f6a407e`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/11b3a9f6a407e) -
9
+ [Popup Select] Improve assistive technology support by reducing reliance on live regions.
10
+
11
+ ## 2.0.6
12
+
13
+ ### Patch Changes
14
+
15
+ - [#123298](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/pull-requests/123298)
16
+ [`8a9b860d5da36`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/8a9b860d5da36) -
17
+ [ux] Improved accessibility for selected values
18
+
3
19
  ## 2.0.5
4
20
 
5
21
  ### Patch Changes
@@ -1,20 +1,24 @@
1
1
  "use strict";
2
2
 
3
3
  var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ var _typeof = require("@babel/runtime/helpers/typeof");
4
5
  Object.defineProperty(exports, "__esModule", {
5
6
  value: true
6
7
  });
7
8
  exports.default = void 0;
8
9
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
- var _react = require("react");
10
+ var _react = _interopRequireWildcard(require("react"));
10
11
  var _react2 = require("@emotion/react");
11
12
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
12
13
  var _accessibility = require("../accessibility");
13
14
  var _a11yText = _interopRequireDefault(require("./internal/a11y-text"));
15
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
16
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
14
17
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
15
18
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } /* eslint-disable @atlaskit/platform/ensure-feature-flag-prefix */ /**
16
19
  * @jsxRuntime classic
17
20
  * @jsx jsx
21
+ * @jsxFrag React.Fragment
18
22
  */
19
23
  // ==============================
20
24
  // Root Container
@@ -123,17 +127,13 @@ var LiveRegion = function LiveRegion(props) {
123
127
  var ariaResults = (0, _react.useMemo)(function () {
124
128
  var resultsMsg = '';
125
129
  if (menuIsOpen && options.length && !isLoading && messages.onFilter) {
126
- var shouldAnnounceAvailableResults = !focusableOptions.length;
127
- if (shouldAnnounceAvailableResults && (0, _platformFeatureFlags.fg)('design_system_select-a11y-improvement') || !(0, _platformFeatureFlags.fg)('design_system_select-a11y-improvement')) {
128
- // only announce no option results when ff is on
129
- var resultsMessage = screenReaderStatus({
130
- count: focusableOptions.length
131
- });
132
- resultsMsg = messages.onFilter({
133
- inputValue: inputValue,
134
- resultsMessage: resultsMessage
135
- });
136
- }
130
+ var resultsMessage = screenReaderStatus({
131
+ count: focusableOptions.length
132
+ });
133
+ resultsMsg = messages.onFilter({
134
+ inputValue: inputValue,
135
+ resultsMessage: resultsMessage
136
+ });
137
137
  }
138
138
  return resultsMsg;
139
139
  }, [focusableOptions, inputValue, menuIsOpen, messages, options, screenReaderStatus, isLoading]);
@@ -161,12 +161,12 @@ var LiveRegion = function LiveRegion(props) {
161
161
  var ScreenReaderText = (0, _react2.jsx)(_react.Fragment, null, (0, _react2.jsx)("span", {
162
162
  id: "aria-selection"
163
163
  }, ariaSelected), (0, _react2.jsx)("span", {
164
+ id: "aria-results"
165
+ }, ariaResults), !(0, _platformFeatureFlags.fg)('design_system_select-a11y-improvement') && (0, _react2.jsx)(_react.default.Fragment, null, (0, _react2.jsx)("span", {
164
166
  id: "aria-focused"
165
167
  }, ariaFocused), (0, _react2.jsx)("span", {
166
- id: "aria-results"
167
- }, ariaResults), !(0, _platformFeatureFlags.fg)('design_system_select-a11y-improvement') && (0, _react2.jsx)("span", {
168
168
  id: "aria-guidance"
169
- }, ariaGuidance));
169
+ }, ariaGuidance)));
170
170
  return (0, _react2.jsx)(_react.Fragment, null, (0, _react2.jsx)(_a11yText.default, {
171
171
  id: id
172
172
  }, isInitialFocus && ScreenReaderText), (0, _react2.jsx)(_a11yText.default, {
@@ -7,7 +7,6 @@ Object.defineProperty(exports, "__esModule", {
7
7
  exports.MultiValueLabel = exports.MultiValueContainer = void 0;
8
8
  exports.MultiValueRemove = MultiValueRemove;
9
9
  exports.multiValueRemoveCSS = exports.multiValueLabelCSS = exports.multiValueCSS = exports.default = void 0;
10
- var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
11
10
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
12
11
  var _react = require("@emotion/react");
13
12
  var _selectClear = _interopRequireDefault(require("@atlaskit/icon/glyph/select-clear"));
@@ -147,7 +146,7 @@ var renderIcon = function renderIcon() {
147
146
  // eslint-disable-next-line @atlaskit/platform/ensure-feature-flag-prefix
148
147
  if ((0, _platformFeatureFlags.fg)('platform-component-visual-refresh')) {
149
148
  return (0, _react.jsx)(_cross.default, {
150
- label: "Clear",
149
+ label: "",
151
150
  color: "currentColor"
152
151
  });
153
152
  }
@@ -157,14 +156,14 @@ var renderIcon = function renderIcon() {
157
156
  return (0, _react.jsx)(_primitives.Inline, {
158
157
  xcss: iconWrapperStyles
159
158
  }, (0, _react.jsx)(_cross.default, {
160
- label: "Clear",
159
+ label: "",
161
160
  color: "currentColor"
162
161
  }));
163
162
  }
164
163
  return (
165
164
  // eslint-disable-next-line @atlaskit/design-system/no-legacy-icons
166
165
  (0, _react.jsx)(_selectClear.default, {
167
- label: "Clear",
166
+ label: "",
168
167
  primaryColor: "transparent",
169
168
  size: "small",
170
169
  secondaryColor: "inherit"
@@ -176,9 +175,7 @@ function MultiValueRemove(_ref5) {
176
175
  innerProps = _ref5.innerProps;
177
176
  return (
178
177
  // The Remove button is intentionally excluded from the tab order, please avoid assigning a non-negative tabIndex to it. Context: https://hello.atlassian.net/wiki/spaces/A11YKB/pages/3031993460/Clear+Options+on+an+Input+Field
179
- (0, _react.jsx)("div", (0, _extends2.default)({
180
- role: "button"
181
- }, innerProps), (0, _react.jsx)("div", {
178
+ (0, _react.jsx)("div", innerProps, (0, _react.jsx)("div", {
182
179
  css: isDisabled ? disabledStyles : enabledStyles,
183
180
  "data-testid": isDisabled ? 'hide-clear-icon' : 'show-clear-icon'
184
181
  }, renderIcon()))
@@ -214,7 +211,9 @@ var MultiValue = function MultiValue(props) {
214
211
  innerProps: _objectSpread(_objectSpread({}, (0, _utils.getStyleProps)(props, 'multiValueRemove', {
215
212
  'multi-value__remove': true
216
213
  })), {}, {
217
- 'aria-label': "Remove ".concat(ariaLabel || 'option')
214
+ role: 'button',
215
+ tabIndex: -1,
216
+ 'aria-label': "".concat(ariaLabel || 'option', ", remove")
218
217
  }, removeProps),
219
218
  isDisabled: isDisabled,
220
219
  selectProps: selectProps
@@ -239,6 +239,7 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
239
239
  focusedOptionId: null,
240
240
  focusableOptionsWithIds: [],
241
241
  focusedValue: null,
242
+ focusedValueId: null,
242
243
  inputIsHidden: false,
243
244
  isFocused: false,
244
245
  selectValue: [],
@@ -740,6 +741,9 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
740
741
  // ref. https://www.w3.org/TR/uievents/#determine-keydown-keyup-keyCode
741
742
  break;
742
743
  }
744
+ if (focusedValue) {
745
+ _this.removeValue(focusedValue);
746
+ }
743
747
  if (menuIsOpen) {
744
748
  if (!focusedOption) {
745
749
  return;
@@ -825,7 +829,7 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
825
829
  _this.state.instancePrefix = 'react-select-' + (_this.props.instanceId || ++instanceId);
826
830
  _this.state.selectValue = (0, _utils.cleanValue)(_props.value);
827
831
  // Set focusedOption if menuIsOpen is set on init (e.g. defaultMenuIsOpen)
828
- if (_props.menuIsOpen && _this.state.selectValue.length) {
832
+ if (_props.menuIsOpen) {
829
833
  var focusableOptionsWithIds = _this.getFocusableOptionsWithIds();
830
834
  var focusableOptions = _this.buildFocusableOptions();
831
835
  var optionIndex = focusableOptions.indexOf(_this.state.selectValue[0]);
@@ -1010,7 +1014,10 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
1010
1014
  }
1011
1015
  this.setState({
1012
1016
  inputIsHidden: nextFocus !== -1,
1013
- focusedValue: selectValue[nextFocus]
1017
+ focusedValue: selectValue[nextFocus],
1018
+ focusedValueId: "".concat(this.getElementId('selected-value'), "-").concat(nextFocus),
1019
+ focusedOption: null,
1020
+ focusedOptionId: null
1014
1021
  });
1015
1022
  }
1016
1023
  }, {
@@ -1049,6 +1056,7 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
1049
1056
  this.setState({
1050
1057
  focusedOption: options[nextFocus],
1051
1058
  focusedValue: null,
1059
+ focusedValueId: null,
1052
1060
  focusedOptionId: this.getFocusedOptionId(options[nextFocus])
1053
1061
  });
1054
1062
  }
@@ -1261,7 +1269,7 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
1261
1269
  'aria-labelledby': this.props['aria-labelledby'] || labelId,
1262
1270
  'aria-required': required || isRequired,
1263
1271
  role: 'combobox',
1264
- 'aria-activedescendant': this.state.focusedOptionId || undefined
1272
+ 'aria-activedescendant': this.state.focusedOptionId || this.state.focusedValueId || undefined
1265
1273
  }, menuIsOpen && {
1266
1274
  'aria-controls': this.getElementId('listbox')
1267
1275
  }), !isSearchable && {
@@ -1360,11 +1368,14 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
1360
1368
  },
1361
1369
  onMouseDown: function onMouseDown(e) {
1362
1370
  e.preventDefault();
1363
- }
1371
+ },
1372
+ 'data-testid': "".concat(testId, "-select--multivalue-").concat(index, "-remove")
1364
1373
  },
1365
1374
  data: opt,
1366
- innerProps: _objectSpread({}, testId && {
1367
- 'data-testid': "".concat(testId, "-select--multivalue")
1375
+ innerProps: _objectSpread(_objectSpread({}, testId && {
1376
+ 'data-testid': "".concat(testId, "-select--multivalue-").concat(index)
1377
+ }), {}, {
1378
+ id: "".concat(_this3.getElementId('selected-value'), "-").concat(index)
1368
1379
  })
1369
1380
  }), _this3.formatOptionLabel(opt, 'value'));
1370
1381
  });
@@ -1603,6 +1614,26 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
1603
1614
  menuPosition: menuPosition,
1604
1615
  menuShouldScrollIntoView: menuShouldScrollIntoView
1605
1616
  };
1617
+ var calculateListboxLabel = function calculateListboxLabel() {
1618
+ var _this4$inputRef;
1619
+ // First in name calculation, overwrites aria-label
1620
+ if (labelId) {
1621
+ return {
1622
+ 'aria-labelledby': labelId
1623
+ };
1624
+ }
1625
+ // Second in name calcuation, overwrites everything else except aria-labelledby
1626
+ if (label) {
1627
+ return {
1628
+ 'aria-label': label
1629
+ };
1630
+ }
1631
+ // Fallback if no label or labelId is provided, might catch label via <label for> otherwise
1632
+ // will most likely not have an accessible name
1633
+ return {
1634
+ 'aria-labelledby': ((_this4$inputRef = _this4.inputRef) === null || _this4$inputRef === void 0 ? void 0 : _this4$inputRef.id) || _this4.getElementId('input')
1635
+ };
1636
+ };
1606
1637
  var menuElement = /*#__PURE__*/_react.default.createElement(_menu.MenuPlacer, (0, _extends2.default)({}, commonProps, menuPlacementProps), function (_ref4) {
1607
1638
  var ref = _ref4.ref,
1608
1639
  _ref4$placerProps = _ref4.placerProps,
@@ -1625,7 +1656,6 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
1625
1656
  onBottomArrive: onMenuScrollToBottom,
1626
1657
  lockEnabled: menuShouldBlockScroll
1627
1658
  }, function (scrollTargetRef) {
1628
- var _this4$inputRef;
1629
1659
  return /*#__PURE__*/_react.default.createElement(MenuList, (0, _extends2.default)({}, commonProps, {
1630
1660
  innerRef: function innerRef(instance) {
1631
1661
  _this4.getMenuListRef(instance);
@@ -1637,10 +1667,7 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
1637
1667
  id: _this4.getElementId('listbox')
1638
1668
  }, testId && {
1639
1669
  'data-testid': "".concat(testId, "-select--listbox")
1640
- }), (0, _platformFeatureFlags.fg)('design_system_select-a11y-improvement') && {
1641
- 'aria-label': label,
1642
- 'aria-labelledby': "".concat(labelId || ((_this4$inputRef = _this4.inputRef) === null || _this4$inputRef === void 0 ? void 0 : _this4$inputRef.id) || _this4.getElementId('input'))
1643
- }),
1670
+ }), (0, _platformFeatureFlags.fg)('design_system_select-a11y-improvement') && calculateListboxLabel()),
1644
1671
  isLoading: isLoading,
1645
1672
  maxHeight: maxHeight,
1646
1673
  focusedOption: focusedOption
@@ -2,8 +2,9 @@
2
2
  /**
3
3
  * @jsxRuntime classic
4
4
  * @jsx jsx
5
+ * @jsxFrag React.Fragment
5
6
  */
6
- import { Fragment, useMemo, useRef } from 'react';
7
+ import React, { Fragment, useMemo, useRef } from 'react';
7
8
  import { jsx } from '@emotion/react';
8
9
  import { fg } from '@atlaskit/platform-feature-flags';
9
10
  import { defaultAriaLiveMessages } from '../accessibility';
@@ -122,17 +123,13 @@ const LiveRegion = props => {
122
123
  const ariaResults = useMemo(() => {
123
124
  let resultsMsg = '';
124
125
  if (menuIsOpen && options.length && !isLoading && messages.onFilter) {
125
- const shouldAnnounceAvailableResults = !focusableOptions.length;
126
- if (shouldAnnounceAvailableResults && fg('design_system_select-a11y-improvement') || !fg('design_system_select-a11y-improvement')) {
127
- // only announce no option results when ff is on
128
- const resultsMessage = screenReaderStatus({
129
- count: focusableOptions.length
130
- });
131
- resultsMsg = messages.onFilter({
132
- inputValue,
133
- resultsMessage
134
- });
135
- }
126
+ const resultsMessage = screenReaderStatus({
127
+ count: focusableOptions.length
128
+ });
129
+ resultsMsg = messages.onFilter({
130
+ inputValue,
131
+ resultsMessage
132
+ });
136
133
  }
137
134
  return resultsMsg;
138
135
  }, [focusableOptions, inputValue, menuIsOpen, messages, options, screenReaderStatus, isLoading]);
@@ -160,12 +157,12 @@ const LiveRegion = props => {
160
157
  const ScreenReaderText = jsx(Fragment, null, jsx("span", {
161
158
  id: "aria-selection"
162
159
  }, ariaSelected), jsx("span", {
160
+ id: "aria-results"
161
+ }, ariaResults), !fg('design_system_select-a11y-improvement') && jsx(React.Fragment, null, jsx("span", {
163
162
  id: "aria-focused"
164
163
  }, ariaFocused), jsx("span", {
165
- id: "aria-results"
166
- }, ariaResults), !fg('design_system_select-a11y-improvement') && jsx("span", {
167
164
  id: "aria-guidance"
168
- }, ariaGuidance));
165
+ }, ariaGuidance)));
169
166
  return jsx(Fragment, null, jsx(A11yText, {
170
167
  id: id
171
168
  }, isInitialFocus && ScreenReaderText), jsx(A11yText, {
@@ -1,4 +1,3 @@
1
- import _extends from "@babel/runtime/helpers/extends";
2
1
  /**
3
2
  * @jsxRuntime classic
4
3
  * @jsx jsx
@@ -138,7 +137,7 @@ const renderIcon = () => {
138
137
  // eslint-disable-next-line @atlaskit/platform/ensure-feature-flag-prefix
139
138
  if (fg('platform-component-visual-refresh')) {
140
139
  return jsx(CrossIcon, {
141
- label: "Clear",
140
+ label: "",
142
141
  color: "currentColor"
143
142
  });
144
143
  }
@@ -148,14 +147,14 @@ const renderIcon = () => {
148
147
  return jsx(Inline, {
149
148
  xcss: iconWrapperStyles
150
149
  }, jsx(CrossIcon, {
151
- label: "Clear",
150
+ label: "",
152
151
  color: "currentColor"
153
152
  }));
154
153
  }
155
154
  return (
156
155
  // eslint-disable-next-line @atlaskit/design-system/no-legacy-icons
157
156
  jsx(LegacySelectClearIcon, {
158
- label: "Clear",
157
+ label: "",
159
158
  primaryColor: "transparent",
160
159
  size: "small",
161
160
  secondaryColor: "inherit"
@@ -168,9 +167,7 @@ export function MultiValueRemove({
168
167
  }) {
169
168
  return (
170
169
  // The Remove button is intentionally excluded from the tab order, please avoid assigning a non-negative tabIndex to it. Context: https://hello.atlassian.net/wiki/spaces/A11YKB/pages/3031993460/Clear+Options+on+an+Input+Field
171
- jsx("div", _extends({
172
- role: "button"
173
- }, innerProps), jsx("div", {
170
+ jsx("div", innerProps, jsx("div", {
174
171
  css: isDisabled ? disabledStyles : enabledStyles,
175
172
  "data-testid": isDisabled ? 'hide-clear-icon' : 'show-clear-icon'
176
173
  }, renderIcon()))
@@ -216,7 +213,9 @@ const MultiValue = props => {
216
213
  ...getStyleProps(props, 'multiValueRemove', {
217
214
  'multi-value__remove': true
218
215
  }),
219
- 'aria-label': `Remove ${ariaLabel || 'option'}`,
216
+ role: 'button',
217
+ tabIndex: -1,
218
+ 'aria-label': `${ariaLabel || 'option'}, remove`,
220
219
  ...removeProps
221
220
  },
222
221
  isDisabled: isDisabled,
@@ -209,6 +209,7 @@ export default class Select extends Component {
209
209
  focusedOptionId: null,
210
210
  focusableOptionsWithIds: [],
211
211
  focusedValue: null,
212
+ focusedValueId: null,
212
213
  inputIsHidden: false,
213
214
  isFocused: false,
214
215
  selectValue: [],
@@ -716,6 +717,9 @@ export default class Select extends Component {
716
717
  // ref. https://www.w3.org/TR/uievents/#determine-keydown-keyup-keyCode
717
718
  break;
718
719
  }
720
+ if (focusedValue) {
721
+ this.removeValue(focusedValue);
722
+ }
719
723
  if (menuIsOpen) {
720
724
  if (!focusedOption) {
721
725
  return;
@@ -801,7 +805,7 @@ export default class Select extends Component {
801
805
  this.state.instancePrefix = 'react-select-' + (this.props.instanceId || ++instanceId);
802
806
  this.state.selectValue = cleanValue(_props.value);
803
807
  // Set focusedOption if menuIsOpen is set on init (e.g. defaultMenuIsOpen)
804
- if (_props.menuIsOpen && this.state.selectValue.length) {
808
+ if (_props.menuIsOpen) {
805
809
  const focusableOptionsWithIds = this.getFocusableOptionsWithIds();
806
810
  const focusableOptions = this.buildFocusableOptions();
807
811
  const optionIndex = focusableOptions.indexOf(this.state.selectValue[0]);
@@ -1033,7 +1037,10 @@ export default class Select extends Component {
1033
1037
  }
1034
1038
  this.setState({
1035
1039
  inputIsHidden: nextFocus !== -1,
1036
- focusedValue: selectValue[nextFocus]
1040
+ focusedValue: selectValue[nextFocus],
1041
+ focusedValueId: `${this.getElementId('selected-value')}-${nextFocus}`,
1042
+ focusedOption: null,
1043
+ focusedOptionId: null
1037
1044
  });
1038
1045
  }
1039
1046
  focusOption(direction = 'first') {
@@ -1073,6 +1080,7 @@ export default class Select extends Component {
1073
1080
  this.setState({
1074
1081
  focusedOption: options[nextFocus],
1075
1082
  focusedValue: null,
1083
+ focusedValueId: null,
1076
1084
  focusedOptionId: this.getFocusedOptionId(options[nextFocus])
1077
1085
  });
1078
1086
  }
@@ -1271,7 +1279,7 @@ export default class Select extends Component {
1271
1279
  'aria-labelledby': this.props['aria-labelledby'] || labelId,
1272
1280
  'aria-required': required || isRequired,
1273
1281
  role: 'combobox',
1274
- 'aria-activedescendant': this.state.focusedOptionId || undefined,
1282
+ 'aria-activedescendant': this.state.focusedOptionId || this.state.focusedValueId || undefined,
1275
1283
  ...(menuIsOpen && {
1276
1284
  'aria-controls': this.getElementId('listbox')
1277
1285
  }),
@@ -1373,13 +1381,15 @@ export default class Select extends Component {
1373
1381
  onTouchEnd: () => this.removeValue(opt),
1374
1382
  onMouseDown: e => {
1375
1383
  e.preventDefault();
1376
- }
1384
+ },
1385
+ 'data-testid': `${testId}-select--multivalue-${index}-remove`
1377
1386
  },
1378
1387
  data: opt,
1379
1388
  innerProps: {
1380
1389
  ...(testId && {
1381
- 'data-testid': `${testId}-select--multivalue`
1382
- })
1390
+ 'data-testid': `${testId}-select--multivalue-${index}`
1391
+ }),
1392
+ id: `${this.getElementId('selected-value')}-${index}`
1383
1393
  }
1384
1394
  }), this.formatOptionLabel(opt, 'value'));
1385
1395
  });
@@ -1637,6 +1647,26 @@ export default class Select extends Component {
1637
1647
  menuPosition,
1638
1648
  menuShouldScrollIntoView
1639
1649
  };
1650
+ const calculateListboxLabel = () => {
1651
+ var _this$inputRef;
1652
+ // First in name calculation, overwrites aria-label
1653
+ if (labelId) {
1654
+ return {
1655
+ 'aria-labelledby': labelId
1656
+ };
1657
+ }
1658
+ // Second in name calcuation, overwrites everything else except aria-labelledby
1659
+ if (label) {
1660
+ return {
1661
+ 'aria-label': label
1662
+ };
1663
+ }
1664
+ // Fallback if no label or labelId is provided, might catch label via <label for> otherwise
1665
+ // will most likely not have an accessible name
1666
+ return {
1667
+ 'aria-labelledby': ((_this$inputRef = this.inputRef) === null || _this$inputRef === void 0 ? void 0 : _this$inputRef.id) || this.getElementId('input')
1668
+ };
1669
+ };
1640
1670
  const menuElement = /*#__PURE__*/React.createElement(MenuPlacer, _extends({}, commonProps, menuPlacementProps), ({
1641
1671
  ref,
1642
1672
  placerProps: {
@@ -1660,31 +1690,25 @@ export default class Select extends Component {
1660
1690
  onTopArrive: onMenuScrollToTop,
1661
1691
  onBottomArrive: onMenuScrollToBottom,
1662
1692
  lockEnabled: menuShouldBlockScroll
1663
- }, scrollTargetRef => {
1664
- var _this$inputRef;
1665
- return /*#__PURE__*/React.createElement(MenuList, _extends({}, commonProps, {
1666
- innerRef: instance => {
1667
- this.getMenuListRef(instance);
1668
- scrollTargetRef(instance);
1669
- },
1670
- innerProps: {
1671
- role: 'listbox',
1672
- 'aria-multiselectable': (this.isVoiceOver || !commonProps.isMulti) && fg('design_system_select-a11y-improvement') ? undefined : commonProps.isMulti,
1673
- id: this.getElementId('listbox'),
1674
- ...(testId && {
1675
- 'data-testid': `${testId}-select--listbox`
1676
- }),
1677
- // add aditional label on listbox when ff is on
1678
- ...(fg('design_system_select-a11y-improvement') && {
1679
- 'aria-label': label,
1680
- 'aria-labelledby': `${labelId || ((_this$inputRef = this.inputRef) === null || _this$inputRef === void 0 ? void 0 : _this$inputRef.id) || this.getElementId('input')}`
1681
- })
1682
- },
1683
- isLoading: isLoading,
1684
- maxHeight: maxHeight,
1685
- focusedOption: focusedOption
1686
- }), menuUI);
1687
- })));
1693
+ }, scrollTargetRef => /*#__PURE__*/React.createElement(MenuList, _extends({}, commonProps, {
1694
+ innerRef: instance => {
1695
+ this.getMenuListRef(instance);
1696
+ scrollTargetRef(instance);
1697
+ },
1698
+ innerProps: {
1699
+ role: 'listbox',
1700
+ 'aria-multiselectable': (this.isVoiceOver || !commonProps.isMulti) && fg('design_system_select-a11y-improvement') ? undefined : commonProps.isMulti,
1701
+ id: this.getElementId('listbox'),
1702
+ ...(testId && {
1703
+ 'data-testid': `${testId}-select--listbox`
1704
+ }),
1705
+ // add aditional label on listbox when ff is on
1706
+ ...(fg('design_system_select-a11y-improvement') && calculateListboxLabel())
1707
+ },
1708
+ isLoading: isLoading,
1709
+ maxHeight: maxHeight,
1710
+ focusedOption: focusedOption
1711
+ }), menuUI))));
1688
1712
 
1689
1713
  // positioning behaviour is almost identical for portalled and fixed,
1690
1714
  // so we use the same component. the actual portalling logic is forked
@@ -5,8 +5,9 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
5
5
  /**
6
6
  * @jsxRuntime classic
7
7
  * @jsx jsx
8
+ * @jsxFrag React.Fragment
8
9
  */
9
- import { Fragment, useMemo, useRef } from 'react';
10
+ import React, { Fragment, useMemo, useRef } from 'react';
10
11
  import { jsx } from '@emotion/react';
11
12
  import { fg } from '@atlaskit/platform-feature-flags';
12
13
  import { defaultAriaLiveMessages } from '../accessibility';
@@ -119,17 +120,13 @@ var LiveRegion = function LiveRegion(props) {
119
120
  var ariaResults = useMemo(function () {
120
121
  var resultsMsg = '';
121
122
  if (menuIsOpen && options.length && !isLoading && messages.onFilter) {
122
- var shouldAnnounceAvailableResults = !focusableOptions.length;
123
- if (shouldAnnounceAvailableResults && fg('design_system_select-a11y-improvement') || !fg('design_system_select-a11y-improvement')) {
124
- // only announce no option results when ff is on
125
- var resultsMessage = screenReaderStatus({
126
- count: focusableOptions.length
127
- });
128
- resultsMsg = messages.onFilter({
129
- inputValue: inputValue,
130
- resultsMessage: resultsMessage
131
- });
132
- }
123
+ var resultsMessage = screenReaderStatus({
124
+ count: focusableOptions.length
125
+ });
126
+ resultsMsg = messages.onFilter({
127
+ inputValue: inputValue,
128
+ resultsMessage: resultsMessage
129
+ });
133
130
  }
134
131
  return resultsMsg;
135
132
  }, [focusableOptions, inputValue, menuIsOpen, messages, options, screenReaderStatus, isLoading]);
@@ -157,12 +154,12 @@ var LiveRegion = function LiveRegion(props) {
157
154
  var ScreenReaderText = jsx(Fragment, null, jsx("span", {
158
155
  id: "aria-selection"
159
156
  }, ariaSelected), jsx("span", {
157
+ id: "aria-results"
158
+ }, ariaResults), !fg('design_system_select-a11y-improvement') && jsx(React.Fragment, null, jsx("span", {
160
159
  id: "aria-focused"
161
160
  }, ariaFocused), jsx("span", {
162
- id: "aria-results"
163
- }, ariaResults), !fg('design_system_select-a11y-improvement') && jsx("span", {
164
161
  id: "aria-guidance"
165
- }, ariaGuidance));
162
+ }, ariaGuidance)));
166
163
  return jsx(Fragment, null, jsx(A11yText, {
167
164
  id: id
168
165
  }, isInitialFocus && ScreenReaderText), jsx(A11yText, {
@@ -1,4 +1,3 @@
1
- import _extends from "@babel/runtime/helpers/extends";
2
1
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
3
2
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
4
3
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
@@ -140,7 +139,7 @@ var renderIcon = function renderIcon() {
140
139
  // eslint-disable-next-line @atlaskit/platform/ensure-feature-flag-prefix
141
140
  if (fg('platform-component-visual-refresh')) {
142
141
  return jsx(CrossIcon, {
143
- label: "Clear",
142
+ label: "",
144
143
  color: "currentColor"
145
144
  });
146
145
  }
@@ -150,14 +149,14 @@ var renderIcon = function renderIcon() {
150
149
  return jsx(Inline, {
151
150
  xcss: iconWrapperStyles
152
151
  }, jsx(CrossIcon, {
153
- label: "Clear",
152
+ label: "",
154
153
  color: "currentColor"
155
154
  }));
156
155
  }
157
156
  return (
158
157
  // eslint-disable-next-line @atlaskit/design-system/no-legacy-icons
159
158
  jsx(LegacySelectClearIcon, {
160
- label: "Clear",
159
+ label: "",
161
160
  primaryColor: "transparent",
162
161
  size: "small",
163
162
  secondaryColor: "inherit"
@@ -169,9 +168,7 @@ export function MultiValueRemove(_ref5) {
169
168
  innerProps = _ref5.innerProps;
170
169
  return (
171
170
  // The Remove button is intentionally excluded from the tab order, please avoid assigning a non-negative tabIndex to it. Context: https://hello.atlassian.net/wiki/spaces/A11YKB/pages/3031993460/Clear+Options+on+an+Input+Field
172
- jsx("div", _extends({
173
- role: "button"
174
- }, innerProps), jsx("div", {
171
+ jsx("div", innerProps, jsx("div", {
175
172
  css: isDisabled ? disabledStyles : enabledStyles,
176
173
  "data-testid": isDisabled ? 'hide-clear-icon' : 'show-clear-icon'
177
174
  }, renderIcon()))
@@ -207,7 +204,9 @@ var MultiValue = function MultiValue(props) {
207
204
  innerProps: _objectSpread(_objectSpread({}, getStyleProps(props, 'multiValueRemove', {
208
205
  'multi-value__remove': true
209
206
  })), {}, {
210
- 'aria-label': "Remove ".concat(ariaLabel || 'option')
207
+ role: 'button',
208
+ tabIndex: -1,
209
+ 'aria-label': "".concat(ariaLabel || 'option', ", remove")
211
210
  }, removeProps),
212
211
  isDisabled: isDisabled,
213
212
  selectProps: selectProps
@@ -230,6 +230,7 @@ var Select = /*#__PURE__*/function (_Component) {
230
230
  focusedOptionId: null,
231
231
  focusableOptionsWithIds: [],
232
232
  focusedValue: null,
233
+ focusedValueId: null,
233
234
  inputIsHidden: false,
234
235
  isFocused: false,
235
236
  selectValue: [],
@@ -731,6 +732,9 @@ var Select = /*#__PURE__*/function (_Component) {
731
732
  // ref. https://www.w3.org/TR/uievents/#determine-keydown-keyup-keyCode
732
733
  break;
733
734
  }
735
+ if (focusedValue) {
736
+ _this.removeValue(focusedValue);
737
+ }
734
738
  if (menuIsOpen) {
735
739
  if (!focusedOption) {
736
740
  return;
@@ -816,7 +820,7 @@ var Select = /*#__PURE__*/function (_Component) {
816
820
  _this.state.instancePrefix = 'react-select-' + (_this.props.instanceId || ++instanceId);
817
821
  _this.state.selectValue = cleanValue(_props.value);
818
822
  // Set focusedOption if menuIsOpen is set on init (e.g. defaultMenuIsOpen)
819
- if (_props.menuIsOpen && _this.state.selectValue.length) {
823
+ if (_props.menuIsOpen) {
820
824
  var focusableOptionsWithIds = _this.getFocusableOptionsWithIds();
821
825
  var focusableOptions = _this.buildFocusableOptions();
822
826
  var optionIndex = focusableOptions.indexOf(_this.state.selectValue[0]);
@@ -1001,7 +1005,10 @@ var Select = /*#__PURE__*/function (_Component) {
1001
1005
  }
1002
1006
  this.setState({
1003
1007
  inputIsHidden: nextFocus !== -1,
1004
- focusedValue: selectValue[nextFocus]
1008
+ focusedValue: selectValue[nextFocus],
1009
+ focusedValueId: "".concat(this.getElementId('selected-value'), "-").concat(nextFocus),
1010
+ focusedOption: null,
1011
+ focusedOptionId: null
1005
1012
  });
1006
1013
  }
1007
1014
  }, {
@@ -1040,6 +1047,7 @@ var Select = /*#__PURE__*/function (_Component) {
1040
1047
  this.setState({
1041
1048
  focusedOption: options[nextFocus],
1042
1049
  focusedValue: null,
1050
+ focusedValueId: null,
1043
1051
  focusedOptionId: this.getFocusedOptionId(options[nextFocus])
1044
1052
  });
1045
1053
  }
@@ -1252,7 +1260,7 @@ var Select = /*#__PURE__*/function (_Component) {
1252
1260
  'aria-labelledby': this.props['aria-labelledby'] || labelId,
1253
1261
  'aria-required': required || isRequired,
1254
1262
  role: 'combobox',
1255
- 'aria-activedescendant': this.state.focusedOptionId || undefined
1263
+ 'aria-activedescendant': this.state.focusedOptionId || this.state.focusedValueId || undefined
1256
1264
  }, menuIsOpen && {
1257
1265
  'aria-controls': this.getElementId('listbox')
1258
1266
  }), !isSearchable && {
@@ -1351,11 +1359,14 @@ var Select = /*#__PURE__*/function (_Component) {
1351
1359
  },
1352
1360
  onMouseDown: function onMouseDown(e) {
1353
1361
  e.preventDefault();
1354
- }
1362
+ },
1363
+ 'data-testid': "".concat(testId, "-select--multivalue-").concat(index, "-remove")
1355
1364
  },
1356
1365
  data: opt,
1357
- innerProps: _objectSpread({}, testId && {
1358
- 'data-testid': "".concat(testId, "-select--multivalue")
1366
+ innerProps: _objectSpread(_objectSpread({}, testId && {
1367
+ 'data-testid': "".concat(testId, "-select--multivalue-").concat(index)
1368
+ }), {}, {
1369
+ id: "".concat(_this3.getElementId('selected-value'), "-").concat(index)
1359
1370
  })
1360
1371
  }), _this3.formatOptionLabel(opt, 'value'));
1361
1372
  });
@@ -1594,6 +1605,26 @@ var Select = /*#__PURE__*/function (_Component) {
1594
1605
  menuPosition: menuPosition,
1595
1606
  menuShouldScrollIntoView: menuShouldScrollIntoView
1596
1607
  };
1608
+ var calculateListboxLabel = function calculateListboxLabel() {
1609
+ var _this4$inputRef;
1610
+ // First in name calculation, overwrites aria-label
1611
+ if (labelId) {
1612
+ return {
1613
+ 'aria-labelledby': labelId
1614
+ };
1615
+ }
1616
+ // Second in name calcuation, overwrites everything else except aria-labelledby
1617
+ if (label) {
1618
+ return {
1619
+ 'aria-label': label
1620
+ };
1621
+ }
1622
+ // Fallback if no label or labelId is provided, might catch label via <label for> otherwise
1623
+ // will most likely not have an accessible name
1624
+ return {
1625
+ 'aria-labelledby': ((_this4$inputRef = _this4.inputRef) === null || _this4$inputRef === void 0 ? void 0 : _this4$inputRef.id) || _this4.getElementId('input')
1626
+ };
1627
+ };
1597
1628
  var menuElement = /*#__PURE__*/React.createElement(MenuPlacer, _extends({}, commonProps, menuPlacementProps), function (_ref4) {
1598
1629
  var ref = _ref4.ref,
1599
1630
  _ref4$placerProps = _ref4.placerProps,
@@ -1616,7 +1647,6 @@ var Select = /*#__PURE__*/function (_Component) {
1616
1647
  onBottomArrive: onMenuScrollToBottom,
1617
1648
  lockEnabled: menuShouldBlockScroll
1618
1649
  }, function (scrollTargetRef) {
1619
- var _this4$inputRef;
1620
1650
  return /*#__PURE__*/React.createElement(MenuList, _extends({}, commonProps, {
1621
1651
  innerRef: function innerRef(instance) {
1622
1652
  _this4.getMenuListRef(instance);
@@ -1628,10 +1658,7 @@ var Select = /*#__PURE__*/function (_Component) {
1628
1658
  id: _this4.getElementId('listbox')
1629
1659
  }, testId && {
1630
1660
  'data-testid': "".concat(testId, "-select--listbox")
1631
- }), fg('design_system_select-a11y-improvement') && {
1632
- 'aria-label': label,
1633
- 'aria-labelledby': "".concat(labelId || ((_this4$inputRef = _this4.inputRef) === null || _this4$inputRef === void 0 ? void 0 : _this4$inputRef.id) || _this4.getElementId('input'))
1634
- }),
1661
+ }), fg('design_system_select-a11y-improvement') && calculateListboxLabel()),
1635
1662
  isLoading: isLoading,
1636
1663
  maxHeight: maxHeight,
1637
1664
  focusedOption: focusedOption
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * @jsxRuntime classic
3
3
  * @jsx jsx
4
+ * @jsxFrag React.Fragment
4
5
  */
5
6
  import { type ReactNode } from 'react';
6
7
  import { jsx } from '@emotion/react';
@@ -475,6 +475,7 @@ interface State<Option, IsMulti extends boolean, Group extends GroupBase<Option>
475
475
  focusedOptionId: string | null;
476
476
  focusableOptionsWithIds: FocusableOptionWithId<Option>[];
477
477
  focusedValue: Option | null;
478
+ focusedValueId: string | null;
478
479
  selectValue: Options<Option>;
479
480
  clearFocusValueOnUpdate: boolean;
480
481
  prevWasFocused: boolean;
@@ -618,7 +619,7 @@ export default class Select<Option = unknown, IsMulti extends boolean = false, G
618
619
  getOptionValue: (data: Option) => string;
619
620
  getStyles: <Key extends keyof StylesProps<Option, IsMulti, Group>>(key: Key, props: StylesProps<Option, IsMulti, Group>[Key]) => import("./types").CSSObjectWithLabel;
620
621
  getClassNames: <Key extends keyof StylesProps<Option, IsMulti, Group>>(key: Key, props: StylesProps<Option, IsMulti, Group>[Key]) => string | undefined;
621
- getElementId: (element: 'group' | 'input' | 'listbox' | 'option' | 'placeholder' | 'live-region' | 'multi-message') => string;
622
+ getElementId: (element: 'group' | 'input' | 'listbox' | 'option' | 'placeholder' | 'live-region' | 'multi-message' | 'selected-value') => string;
622
623
  getComponents: () => {
623
624
  ClearIndicator: <Option_1, IsMulti_1 extends boolean, Group_1 extends GroupBase<Option_1>>(props: import(".").ClearIndicatorProps<Option_1, IsMulti_1, Group_1>) => import("@emotion/react").jsx.JSX.Element;
624
625
  Control: <Option_2, IsMulti_2 extends boolean, Group_2 extends GroupBase<Option_2>>(props: import(".").ControlProps<Option_2, IsMulti_2, Group_2>) => import("@emotion/react").jsx.JSX.Element;
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * @jsxRuntime classic
3
3
  * @jsx jsx
4
+ * @jsxFrag React.Fragment
4
5
  */
5
6
  import { type ReactNode } from 'react';
6
7
  import { jsx } from '@emotion/react';
@@ -475,6 +475,7 @@ interface State<Option, IsMulti extends boolean, Group extends GroupBase<Option>
475
475
  focusedOptionId: string | null;
476
476
  focusableOptionsWithIds: FocusableOptionWithId<Option>[];
477
477
  focusedValue: Option | null;
478
+ focusedValueId: string | null;
478
479
  selectValue: Options<Option>;
479
480
  clearFocusValueOnUpdate: boolean;
480
481
  prevWasFocused: boolean;
@@ -618,7 +619,7 @@ export default class Select<Option = unknown, IsMulti extends boolean = false, G
618
619
  getOptionValue: (data: Option) => string;
619
620
  getStyles: <Key extends keyof StylesProps<Option, IsMulti, Group>>(key: Key, props: StylesProps<Option, IsMulti, Group>[Key]) => import("./types").CSSObjectWithLabel;
620
621
  getClassNames: <Key extends keyof StylesProps<Option, IsMulti, Group>>(key: Key, props: StylesProps<Option, IsMulti, Group>[Key]) => string | undefined;
621
- getElementId: (element: 'group' | 'input' | 'listbox' | 'option' | 'placeholder' | 'live-region' | 'multi-message') => string;
622
+ getElementId: (element: 'group' | 'input' | 'listbox' | 'option' | 'placeholder' | 'live-region' | 'multi-message' | 'selected-value') => string;
622
623
  getComponents: () => {
623
624
  ClearIndicator: <Option_1, IsMulti_1 extends boolean, Group_1 extends GroupBase<Option_1>>(props: import(".").ClearIndicatorProps<Option_1, IsMulti_1, Group_1>) => import("@emotion/react").jsx.JSX.Element;
624
625
  Control: <Option_2, IsMulti_2 extends boolean, Group_2 extends GroupBase<Option_2>>(props: import(".").ControlProps<Option_2, IsMulti_2, Group_2>) => import("@emotion/react").jsx.JSX.Element;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/react-select",
3
- "version": "2.0.5",
3
+ "version": "2.0.7",
4
4
  "description": "A forked version of react-select to only be used in atlaskit/select",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -30,11 +30,11 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "@atlaskit/ds-lib": "^4.0.0",
33
- "@atlaskit/icon": "^25.0.0",
33
+ "@atlaskit/icon": "^25.1.0",
34
34
  "@atlaskit/platform-feature-flags": "^1.1.0",
35
- "@atlaskit/primitives": "^14.1.0",
35
+ "@atlaskit/primitives": "^14.2.0",
36
36
  "@atlaskit/spinner": "^18.0.0",
37
- "@atlaskit/tokens": "^4.4.0",
37
+ "@atlaskit/tokens": "^4.5.0",
38
38
  "@babel/runtime": "^7.0.0",
39
39
  "@emotion/react": "^11.7.1",
40
40
  "@floating-ui/dom": "^1.0.1",