@atlaskit/react-select 1.1.0 → 1.2.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 +8 -0
- package/dist/cjs/accessibility/helpers.js +5 -0
- package/dist/cjs/accessibility/index.js +7 -3
- package/dist/cjs/components/live-region.js +32 -14
- package/dist/cjs/components/menu.js +3 -1
- package/dist/cjs/components/option.js +3 -1
- package/dist/cjs/select.js +22 -12
- package/dist/es2019/accessibility/helpers.js +4 -0
- package/dist/es2019/accessibility/index.js +7 -3
- package/dist/es2019/components/live-region.js +33 -15
- package/dist/es2019/components/menu.js +3 -1
- package/dist/es2019/components/option.js +3 -1
- package/dist/es2019/select.js +23 -12
- package/dist/esm/accessibility/helpers.js +4 -0
- package/dist/esm/accessibility/index.js +7 -3
- package/dist/esm/components/live-region.js +33 -15
- package/dist/esm/components/menu.js +3 -1
- package/dist/esm/components/option.js +3 -1
- package/dist/esm/select.js +23 -13
- package/dist/types/accessibility/helpers.d.ts +1 -0
- package/dist/types/accessibility/index.d.ts +4 -0
- package/dist/types-ts4.5/accessibility/helpers.d.ts +1 -0
- package/dist/types-ts4.5/accessibility/index.d.ts +4 -0
- package/package.json +7 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @atlaskit/react-select
|
|
2
2
|
|
|
3
|
+
## 1.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#157818](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/157818)
|
|
8
|
+
[`87c14ad1a3efa`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/87c14ad1a3efa) -
|
|
9
|
+
Use semantic tags and arias for combobox and listbox and reduce aria-live
|
|
10
|
+
|
|
3
11
|
## 1.1.0
|
|
4
12
|
|
|
5
13
|
### Minor Changes
|
|
@@ -8,6 +8,7 @@ exports.isIOS = isIOS;
|
|
|
8
8
|
exports.isIPad = isIPad;
|
|
9
9
|
exports.isIPhone = isIPhone;
|
|
10
10
|
exports.isMac = isMac;
|
|
11
|
+
exports.isSafari = isSafari;
|
|
11
12
|
function testPlatform(re) {
|
|
12
13
|
var _window$navigator$use;
|
|
13
14
|
return typeof window !== 'undefined' && window.navigator != null ?
|
|
@@ -17,6 +18,10 @@ function testPlatform(re) {
|
|
|
17
18
|
function isIPhone() {
|
|
18
19
|
return testPlatform(/^iPhone/i);
|
|
19
20
|
}
|
|
21
|
+
function isSafari() {
|
|
22
|
+
var ua = navigator.userAgent.toLowerCase();
|
|
23
|
+
return ua.includes('safari') && !ua.includes('chrome');
|
|
24
|
+
}
|
|
20
25
|
function isMac() {
|
|
21
26
|
return testPlatform(/^Mac/i);
|
|
22
27
|
}
|
|
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.defaultAriaLiveMessages = void 0;
|
|
7
|
+
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
7
8
|
var defaultAriaLiveMessages = exports.defaultAriaLiveMessages = {
|
|
8
9
|
guidance: function guidance(props) {
|
|
9
10
|
var isSearchable = props.isSearchable,
|
|
@@ -50,19 +51,22 @@ var defaultAriaLiveMessages = exports.defaultAriaLiveMessages = {
|
|
|
50
51
|
_props$label2 = props.label,
|
|
51
52
|
label = _props$label2 === void 0 ? '' : _props$label2,
|
|
52
53
|
selectValue = props.selectValue,
|
|
54
|
+
isMulti = props.isMulti,
|
|
53
55
|
isDisabled = props.isDisabled,
|
|
54
56
|
isSelected = props.isSelected,
|
|
55
57
|
isAppleDevice = props.isAppleDevice;
|
|
56
58
|
var getArrayIndex = function getArrayIndex(arr, item) {
|
|
57
|
-
return arr && arr.length ? "".concat(arr.indexOf(item) + 1, " of ").concat(arr.length) : '';
|
|
59
|
+
return arr && arr.length ? "(".concat(arr.indexOf(item) + 1, " of ").concat(arr.length, ")") : '';
|
|
58
60
|
};
|
|
59
61
|
if (context === 'value' && selectValue) {
|
|
60
62
|
return "value ".concat(label, " focused, ").concat(getArrayIndex(selectValue, focused), ".");
|
|
61
63
|
}
|
|
62
64
|
if (context === 'menu' && isAppleDevice) {
|
|
63
65
|
var disabled = isDisabled ? ' disabled' : '';
|
|
64
|
-
|
|
65
|
-
|
|
66
|
+
// don't announce not selected for single selection
|
|
67
|
+
var notSelectedStatus = !isMulti && (0, _platformFeatureFlags.fg)('design_system_select-a11y-improvement') ? '' : ' not selected';
|
|
68
|
+
var status = "".concat(isSelected ? ' selected' : notSelectedStatus).concat(disabled);
|
|
69
|
+
return "".concat(label).concat(status, ", ").concat(getArrayIndex(options, focused), ", completion selected");
|
|
66
70
|
}
|
|
67
71
|
return '';
|
|
68
72
|
},
|
|
@@ -8,6 +8,7 @@ exports.default = void 0;
|
|
|
8
8
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
9
9
|
var _react = require("react");
|
|
10
10
|
var _react2 = require("@emotion/react");
|
|
11
|
+
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
11
12
|
var _accessibility = require("../accessibility");
|
|
12
13
|
var _a11yText = _interopRequireDefault(require("./internal/a11y-text"));
|
|
13
14
|
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; }
|
|
@@ -45,6 +46,9 @@ var LiveRegion = function LiveRegion(props) {
|
|
|
45
46
|
var ariaLabel = selectProps['aria-label'] || label;
|
|
46
47
|
var ariaLive = selectProps['aria-live'];
|
|
47
48
|
|
|
49
|
+
// for safari, we will use minimum support from aria-live region
|
|
50
|
+
var isA11yImprovementEnabled = (0, _platformFeatureFlags.fg)('design_system_select-a11y-improvement') && !isAppleDevice;
|
|
51
|
+
|
|
48
52
|
// Update aria live message configuration when prop changes
|
|
49
53
|
var messages = (0, _react.useMemo)(function () {
|
|
50
54
|
return _objectSpread(_objectSpread({}, _accessibility.defaultAriaLiveMessages), ariaLiveMessages || {});
|
|
@@ -82,10 +86,17 @@ var LiveRegion = function LiveRegion(props) {
|
|
|
82
86
|
}
|
|
83
87
|
return message;
|
|
84
88
|
}, [ariaSelection, messages, isOptionDisabled, selectValue, getOptionLabel]);
|
|
89
|
+
var prevInputValue = (0, _react.useRef)('');
|
|
85
90
|
var ariaFocused = (0, _react.useMemo)(function () {
|
|
86
91
|
var focusMsg = '';
|
|
87
92
|
var focused = focusedOption || focusedValue;
|
|
88
93
|
var isSelected = !!(focusedOption && selectValue && selectValue.includes(focusedOption));
|
|
94
|
+
if ((!inputValue || inputValue === prevInputValue.current) && isA11yImprovementEnabled) {
|
|
95
|
+
// only announce focus option when searching when ff is on,
|
|
96
|
+
// for safari, we will announce for all
|
|
97
|
+
return '';
|
|
98
|
+
}
|
|
99
|
+
prevInputValue.current = inputValue;
|
|
89
100
|
if (focused && messages.onFocus) {
|
|
90
101
|
var onFocusProps = {
|
|
91
102
|
focused: focused,
|
|
@@ -95,27 +106,36 @@ var LiveRegion = function LiveRegion(props) {
|
|
|
95
106
|
options: focusableOptions,
|
|
96
107
|
context: focused === focusedOption ? 'menu' : 'value',
|
|
97
108
|
selectValue: selectValue,
|
|
98
|
-
isAppleDevice: isAppleDevice
|
|
109
|
+
isAppleDevice: isAppleDevice,
|
|
110
|
+
isMulti: isMulti
|
|
99
111
|
};
|
|
100
112
|
focusMsg = messages.onFocus(onFocusProps);
|
|
101
113
|
}
|
|
102
114
|
return focusMsg;
|
|
103
|
-
}, [focusedOption, focusedValue, getOptionLabel, isOptionDisabled, messages, focusableOptions, selectValue, isAppleDevice]);
|
|
115
|
+
}, [inputValue, focusedOption, focusedValue, getOptionLabel, isOptionDisabled, messages, focusableOptions, selectValue, isAppleDevice, isA11yImprovementEnabled, isMulti]);
|
|
104
116
|
var ariaResults = (0, _react.useMemo)(function () {
|
|
105
117
|
var resultsMsg = '';
|
|
106
118
|
if (menuIsOpen && options.length && !isLoading && messages.onFilter) {
|
|
107
|
-
var
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
119
|
+
var shouldAnnounceAvailableResults = !focusableOptions.length;
|
|
120
|
+
if (shouldAnnounceAvailableResults && (0, _platformFeatureFlags.fg)('design_system_select-a11y-improvement') || !(0, _platformFeatureFlags.fg)('design_system_select-a11y-improvement')) {
|
|
121
|
+
// only announce no option results when ff is on
|
|
122
|
+
var resultsMessage = screenReaderStatus({
|
|
123
|
+
count: focusableOptions.length
|
|
124
|
+
});
|
|
125
|
+
resultsMsg = messages.onFilter({
|
|
126
|
+
inputValue: inputValue,
|
|
127
|
+
resultsMessage: resultsMessage
|
|
128
|
+
});
|
|
129
|
+
}
|
|
114
130
|
}
|
|
115
131
|
return resultsMsg;
|
|
116
132
|
}, [focusableOptions, inputValue, menuIsOpen, messages, options, screenReaderStatus, isLoading]);
|
|
117
133
|
var isInitialFocus = (ariaSelection === null || ariaSelection === void 0 ? void 0 : ariaSelection.action) === 'initial-input-focus';
|
|
118
134
|
var ariaGuidance = (0, _react.useMemo)(function () {
|
|
135
|
+
if ((0, _platformFeatureFlags.fg)('design_system_select-a11y-improvement')) {
|
|
136
|
+
// don't announce guidance at all when ff is on
|
|
137
|
+
return '';
|
|
138
|
+
}
|
|
119
139
|
var guidanceMsg = '';
|
|
120
140
|
if (messages.guidance) {
|
|
121
141
|
var context = focusedValue ? 'value' : menuIsOpen ? 'menu' : 'input';
|
|
@@ -137,16 +157,14 @@ var LiveRegion = function LiveRegion(props) {
|
|
|
137
157
|
id: "aria-focused"
|
|
138
158
|
}, ariaFocused), (0, _react2.jsx)("span", {
|
|
139
159
|
id: "aria-results"
|
|
140
|
-
}, ariaResults), (0, _react2.jsx)("span", {
|
|
160
|
+
}, ariaResults), !(0, _platformFeatureFlags.fg)('design_system_select-a11y-improvement') && (0, _react2.jsx)("span", {
|
|
141
161
|
id: "aria-guidance"
|
|
142
162
|
}, ariaGuidance));
|
|
143
163
|
return (0, _react2.jsx)(_react.Fragment, null, (0, _react2.jsx)(_a11yText.default, {
|
|
144
164
|
id: id
|
|
145
165
|
}, isInitialFocus && ScreenReaderText), (0, _react2.jsx)(_a11yText.default, {
|
|
146
|
-
"aria-live": ariaLive,
|
|
147
|
-
|
|
148
|
-
"aria-relevant": "additions text",
|
|
149
|
-
role: "log"
|
|
166
|
+
"aria-live": isA11yImprovementEnabled ? 'polite' : ariaLive,
|
|
167
|
+
role: isA11yImprovementEnabled ? 'status' : 'log'
|
|
150
168
|
}, isFocused && !isInitialFocus && ScreenReaderText));
|
|
151
169
|
};
|
|
152
170
|
|
|
@@ -311,7 +311,9 @@ var MenuList = exports.MenuList = function MenuList(props) {
|
|
|
311
311
|
'menu-list--is-multi': isMulti
|
|
312
312
|
}), {
|
|
313
313
|
ref: innerRef
|
|
314
|
-
}, innerProps
|
|
314
|
+
}, innerProps, {
|
|
315
|
+
tabIndex: -1
|
|
316
|
+
}), children);
|
|
315
317
|
};
|
|
316
318
|
|
|
317
319
|
// ==============================
|
package/dist/cjs/select.js
CHANGED
|
@@ -16,6 +16,7 @@ var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/ge
|
|
|
16
16
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
17
17
|
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
|
|
18
18
|
var _react = _interopRequireWildcard(require("react"));
|
|
19
|
+
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
19
20
|
var _helpers = require("./accessibility/helpers");
|
|
20
21
|
var _builtins = require("./builtins");
|
|
21
22
|
var _components = require("./components");
|
|
@@ -258,7 +259,7 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
|
|
|
258
259
|
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "initialTouchY", 0);
|
|
259
260
|
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "openAfterFocus", false);
|
|
260
261
|
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "scrollToFocusedOptionOnUpdate", false);
|
|
261
|
-
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "isAppleDevice", (0, _helpers.isAppleDevice)());
|
|
262
|
+
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "isAppleDevice", (0, _platformFeatureFlags.fg)('design_system_select-a11y-improvement') ? (0, _helpers.isSafari)() : (0, _helpers.isAppleDevice)());
|
|
262
263
|
// Refs
|
|
263
264
|
// ------------------------------
|
|
264
265
|
(0, _defineProperty2.default)((0, _assertThisInitialized2.default)(_this), "controlRef", null);
|
|
@@ -1237,7 +1238,8 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
|
|
|
1237
1238
|
labelId = _this$props8.labelId,
|
|
1238
1239
|
menuIsOpen = _this$props8.menuIsOpen,
|
|
1239
1240
|
required = _this$props8.required,
|
|
1240
|
-
tabIndex = _this$props8.tabIndex
|
|
1241
|
+
_this$props8$tabIndex = _this$props8.tabIndex,
|
|
1242
|
+
tabIndex = _this$props8$tabIndex === void 0 ? 0 : _this$props8$tabIndex;
|
|
1241
1243
|
var _this$getComponents = this.getComponents(),
|
|
1242
1244
|
Input = _this$getComponents.Input;
|
|
1243
1245
|
var _this$state4 = this.state,
|
|
@@ -1249,7 +1251,7 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
|
|
|
1249
1251
|
|
|
1250
1252
|
// aria attributes makes the JSX "noisy", separated for clarity
|
|
1251
1253
|
var ariaAttributes = _objectSpread(_objectSpread(_objectSpread({
|
|
1252
|
-
'aria-autocomplete': '
|
|
1254
|
+
'aria-autocomplete': 'both',
|
|
1253
1255
|
'aria-errormessage': this.props['aria-errormessage'],
|
|
1254
1256
|
'aria-expanded': menuIsOpen,
|
|
1255
1257
|
'aria-haspopup': 'listbox',
|
|
@@ -1259,7 +1261,7 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
|
|
|
1259
1261
|
'aria-labelledby': this.props['aria-labelledby'] || labelId,
|
|
1260
1262
|
'aria-required': required || isRequired,
|
|
1261
1263
|
role: 'combobox',
|
|
1262
|
-
'aria-activedescendant': this.
|
|
1264
|
+
'aria-activedescendant': this.state.focusedOptionId || undefined
|
|
1263
1265
|
}, menuIsOpen && {
|
|
1264
1266
|
'aria-controls': this.getElementId('listbox')
|
|
1265
1267
|
}), !isSearchable && {
|
|
@@ -1491,13 +1493,15 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
|
|
|
1491
1493
|
menuShouldScrollIntoView = _this$props12.menuShouldScrollIntoView,
|
|
1492
1494
|
noOptionsMessage = _this$props12.noOptionsMessage,
|
|
1493
1495
|
onMenuScrollToTop = _this$props12.onMenuScrollToTop,
|
|
1494
|
-
onMenuScrollToBottom = _this$props12.onMenuScrollToBottom
|
|
1496
|
+
onMenuScrollToBottom = _this$props12.onMenuScrollToBottom,
|
|
1497
|
+
labelId = _this$props12.labelId,
|
|
1498
|
+
label = _this$props12.label;
|
|
1495
1499
|
if (!menuIsOpen) {
|
|
1496
1500
|
return null;
|
|
1497
1501
|
}
|
|
1498
1502
|
|
|
1499
1503
|
// TODO: Internal Option Type here
|
|
1500
|
-
var render = function render(props, id) {
|
|
1504
|
+
var render = function render(props, id, headingId) {
|
|
1501
1505
|
var type = props.type,
|
|
1502
1506
|
data = props.data,
|
|
1503
1507
|
isDisabled = props.isDisabled,
|
|
@@ -1518,7 +1522,8 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
|
|
|
1518
1522
|
onMouseMove: onHover,
|
|
1519
1523
|
onMouseOver: onHover,
|
|
1520
1524
|
role: 'option',
|
|
1521
|
-
'aria-selected':
|
|
1525
|
+
'aria-selected': !commonProps.isMulti && (0, _platformFeatureFlags.fg)('design_system_select-a11y-improvement') ? isSelected || undefined : isSelected,
|
|
1526
|
+
'aria-describedby': (0, _platformFeatureFlags.fg)('design_system_select-a11y-improvement') ? headingId : undefined
|
|
1522
1527
|
};
|
|
1523
1528
|
return /*#__PURE__*/_react.default.createElement(Option, (0, _extends2.default)({}, commonProps, {
|
|
1524
1529
|
innerProps: innerProps,
|
|
@@ -1535,7 +1540,8 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
|
|
|
1535
1540
|
};
|
|
1536
1541
|
var menuUI;
|
|
1537
1542
|
if (this.hasOptions()) {
|
|
1538
|
-
|
|
1543
|
+
var items = this.getCategorizedOptions();
|
|
1544
|
+
menuUI = items.map(function (item) {
|
|
1539
1545
|
if (item.type === 'group') {
|
|
1540
1546
|
var data = item.data,
|
|
1541
1547
|
options = item.options,
|
|
@@ -1553,7 +1559,7 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
|
|
|
1553
1559
|
},
|
|
1554
1560
|
label: _this4.formatGroupLabel(item.data)
|
|
1555
1561
|
}), item.options.map(function (option) {
|
|
1556
|
-
return render(option, "".concat(groupIndex, "-").concat(option.index));
|
|
1562
|
+
return render(option, "".concat(groupIndex, "-").concat(option.index), headingId);
|
|
1557
1563
|
}));
|
|
1558
1564
|
} else if (item.type === 'option') {
|
|
1559
1565
|
return render(item, "".concat(item.index));
|
|
@@ -1608,11 +1614,15 @@ var Select = exports.default = /*#__PURE__*/function (_Component) {
|
|
|
1608
1614
|
_this4.getMenuListRef(instance);
|
|
1609
1615
|
scrollTargetRef(instance);
|
|
1610
1616
|
},
|
|
1611
|
-
innerProps: {
|
|
1617
|
+
innerProps: _objectSpread({
|
|
1612
1618
|
role: 'listbox',
|
|
1613
|
-
'aria-multiselectable
|
|
1619
|
+
// don't add aria-multiselectable when ff is on and the value is false
|
|
1620
|
+
'aria-multiselectable': (0, _platformFeatureFlags.fg)('design_system_select-a11y-improvement') ? commonProps.isMulti || undefined : commonProps.isMulti,
|
|
1614
1621
|
id: _this4.getElementId('listbox')
|
|
1615
|
-
},
|
|
1622
|
+
}, (0, _platformFeatureFlags.fg)('design_system_select-a11y-improvement') && {
|
|
1623
|
+
'aria-label': label,
|
|
1624
|
+
'aria-labelledby': labelId
|
|
1625
|
+
}),
|
|
1616
1626
|
isLoading: isLoading,
|
|
1617
1627
|
maxHeight: maxHeight,
|
|
1618
1628
|
focusedOption: focusedOption
|
|
@@ -7,6 +7,10 @@ function testPlatform(re) {
|
|
|
7
7
|
export function isIPhone() {
|
|
8
8
|
return testPlatform(/^iPhone/i);
|
|
9
9
|
}
|
|
10
|
+
export function isSafari() {
|
|
11
|
+
const ua = navigator.userAgent.toLowerCase();
|
|
12
|
+
return ua.includes('safari') && !ua.includes('chrome');
|
|
13
|
+
}
|
|
10
14
|
export function isMac() {
|
|
11
15
|
return testPlatform(/^Mac/i);
|
|
12
16
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
1
2
|
export const defaultAriaLiveMessages = {
|
|
2
3
|
guidance: props => {
|
|
3
4
|
const {
|
|
@@ -47,18 +48,21 @@ export const defaultAriaLiveMessages = {
|
|
|
47
48
|
options,
|
|
48
49
|
label = '',
|
|
49
50
|
selectValue,
|
|
51
|
+
isMulti,
|
|
50
52
|
isDisabled,
|
|
51
53
|
isSelected,
|
|
52
54
|
isAppleDevice
|
|
53
55
|
} = props;
|
|
54
|
-
const getArrayIndex = (arr, item) => arr && arr.length ?
|
|
56
|
+
const getArrayIndex = (arr, item) => arr && arr.length ? `(${arr.indexOf(item) + 1} of ${arr.length})` : '';
|
|
55
57
|
if (context === 'value' && selectValue) {
|
|
56
58
|
return `value ${label} focused, ${getArrayIndex(selectValue, focused)}.`;
|
|
57
59
|
}
|
|
58
60
|
if (context === 'menu' && isAppleDevice) {
|
|
59
61
|
const disabled = isDisabled ? ' disabled' : '';
|
|
60
|
-
|
|
61
|
-
|
|
62
|
+
// don't announce not selected for single selection
|
|
63
|
+
const notSelectedStatus = !isMulti && fg('design_system_select-a11y-improvement') ? '' : ' not selected';
|
|
64
|
+
const status = `${isSelected ? ' selected' : notSelectedStatus}${disabled}`;
|
|
65
|
+
return `${label}${status}, ${getArrayIndex(options, focused)}, completion selected`;
|
|
62
66
|
}
|
|
63
67
|
return '';
|
|
64
68
|
},
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
* @jsxRuntime classic
|
|
3
3
|
* @jsx jsx
|
|
4
4
|
*/
|
|
5
|
-
import { Fragment, useMemo } from 'react';
|
|
5
|
+
import { Fragment, useMemo, useRef } from 'react';
|
|
6
6
|
import { jsx } from '@emotion/react';
|
|
7
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
7
8
|
import { defaultAriaLiveMessages } from '../accessibility';
|
|
8
9
|
import A11yText from './internal/a11y-text';
|
|
9
10
|
|
|
@@ -41,6 +42,9 @@ const LiveRegion = props => {
|
|
|
41
42
|
const ariaLabel = selectProps['aria-label'] || label;
|
|
42
43
|
const ariaLive = selectProps['aria-live'];
|
|
43
44
|
|
|
45
|
+
// for safari, we will use minimum support from aria-live region
|
|
46
|
+
const isA11yImprovementEnabled = fg('design_system_select-a11y-improvement') && !isAppleDevice;
|
|
47
|
+
|
|
44
48
|
// Update aria live message configuration when prop changes
|
|
45
49
|
const messages = useMemo(() => ({
|
|
46
50
|
...defaultAriaLiveMessages,
|
|
@@ -80,10 +84,17 @@ const LiveRegion = props => {
|
|
|
80
84
|
}
|
|
81
85
|
return message;
|
|
82
86
|
}, [ariaSelection, messages, isOptionDisabled, selectValue, getOptionLabel]);
|
|
87
|
+
const prevInputValue = useRef('');
|
|
83
88
|
const ariaFocused = useMemo(() => {
|
|
84
89
|
let focusMsg = '';
|
|
85
90
|
const focused = focusedOption || focusedValue;
|
|
86
91
|
const isSelected = !!(focusedOption && selectValue && selectValue.includes(focusedOption));
|
|
92
|
+
if ((!inputValue || inputValue === prevInputValue.current) && isA11yImprovementEnabled) {
|
|
93
|
+
// only announce focus option when searching when ff is on,
|
|
94
|
+
// for safari, we will announce for all
|
|
95
|
+
return '';
|
|
96
|
+
}
|
|
97
|
+
prevInputValue.current = inputValue;
|
|
87
98
|
if (focused && messages.onFocus) {
|
|
88
99
|
const onFocusProps = {
|
|
89
100
|
focused,
|
|
@@ -93,27 +104,36 @@ const LiveRegion = props => {
|
|
|
93
104
|
options: focusableOptions,
|
|
94
105
|
context: focused === focusedOption ? 'menu' : 'value',
|
|
95
106
|
selectValue,
|
|
96
|
-
isAppleDevice
|
|
107
|
+
isAppleDevice,
|
|
108
|
+
isMulti
|
|
97
109
|
};
|
|
98
110
|
focusMsg = messages.onFocus(onFocusProps);
|
|
99
111
|
}
|
|
100
112
|
return focusMsg;
|
|
101
|
-
}, [focusedOption, focusedValue, getOptionLabel, isOptionDisabled, messages, focusableOptions, selectValue, isAppleDevice]);
|
|
113
|
+
}, [inputValue, focusedOption, focusedValue, getOptionLabel, isOptionDisabled, messages, focusableOptions, selectValue, isAppleDevice, isA11yImprovementEnabled, isMulti]);
|
|
102
114
|
const ariaResults = useMemo(() => {
|
|
103
115
|
let resultsMsg = '';
|
|
104
116
|
if (menuIsOpen && options.length && !isLoading && messages.onFilter) {
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
117
|
+
const shouldAnnounceAvailableResults = !focusableOptions.length;
|
|
118
|
+
if (shouldAnnounceAvailableResults && fg('design_system_select-a11y-improvement') || !fg('design_system_select-a11y-improvement')) {
|
|
119
|
+
// only announce no option results when ff is on
|
|
120
|
+
const resultsMessage = screenReaderStatus({
|
|
121
|
+
count: focusableOptions.length
|
|
122
|
+
});
|
|
123
|
+
resultsMsg = messages.onFilter({
|
|
124
|
+
inputValue,
|
|
125
|
+
resultsMessage
|
|
126
|
+
});
|
|
127
|
+
}
|
|
112
128
|
}
|
|
113
129
|
return resultsMsg;
|
|
114
130
|
}, [focusableOptions, inputValue, menuIsOpen, messages, options, screenReaderStatus, isLoading]);
|
|
115
131
|
const isInitialFocus = (ariaSelection === null || ariaSelection === void 0 ? void 0 : ariaSelection.action) === 'initial-input-focus';
|
|
116
132
|
const ariaGuidance = useMemo(() => {
|
|
133
|
+
if (fg('design_system_select-a11y-improvement')) {
|
|
134
|
+
// don't announce guidance at all when ff is on
|
|
135
|
+
return '';
|
|
136
|
+
}
|
|
117
137
|
let guidanceMsg = '';
|
|
118
138
|
if (messages.guidance) {
|
|
119
139
|
const context = focusedValue ? 'value' : menuIsOpen ? 'menu' : 'input';
|
|
@@ -135,16 +155,14 @@ const LiveRegion = props => {
|
|
|
135
155
|
id: "aria-focused"
|
|
136
156
|
}, ariaFocused), jsx("span", {
|
|
137
157
|
id: "aria-results"
|
|
138
|
-
}, ariaResults), jsx("span", {
|
|
158
|
+
}, ariaResults), !fg('design_system_select-a11y-improvement') && jsx("span", {
|
|
139
159
|
id: "aria-guidance"
|
|
140
160
|
}, ariaGuidance));
|
|
141
161
|
return jsx(Fragment, null, jsx(A11yText, {
|
|
142
162
|
id: id
|
|
143
163
|
}, isInitialFocus && ScreenReaderText), jsx(A11yText, {
|
|
144
|
-
"aria-live": ariaLive,
|
|
145
|
-
|
|
146
|
-
"aria-relevant": "additions text",
|
|
147
|
-
role: "log"
|
|
164
|
+
"aria-live": isA11yImprovementEnabled ? 'polite' : ariaLive,
|
|
165
|
+
role: isA11yImprovementEnabled ? 'status' : 'log'
|
|
148
166
|
}, isFocused && !isInitialFocus && ScreenReaderText));
|
|
149
167
|
};
|
|
150
168
|
|
package/dist/es2019/select.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import _extends from "@babel/runtime/helpers/extends";
|
|
2
2
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
3
3
|
import React, { Component } from 'react';
|
|
4
|
-
import {
|
|
4
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
5
|
+
import { isAppleDevice, isSafari } from './accessibility/helpers';
|
|
5
6
|
import { formatGroupLabel as formatGroupLabelBuiltin, getOptionLabel as getOptionLabelBuiltin, getOptionValue as getOptionValueBuiltin, isOptionDisabled as isOptionDisabledBuiltin } from './builtins';
|
|
6
7
|
import { defaultComponents } from './components';
|
|
7
8
|
import { DummyInput, RequiredInput, ScrollManager } from './components/internal';
|
|
@@ -225,7 +226,7 @@ export default class Select extends Component {
|
|
|
225
226
|
_defineProperty(this, "initialTouchY", 0);
|
|
226
227
|
_defineProperty(this, "openAfterFocus", false);
|
|
227
228
|
_defineProperty(this, "scrollToFocusedOptionOnUpdate", false);
|
|
228
|
-
_defineProperty(this, "isAppleDevice", isAppleDevice());
|
|
229
|
+
_defineProperty(this, "isAppleDevice", fg('design_system_select-a11y-improvement') ? isSafari() : isAppleDevice());
|
|
229
230
|
// Refs
|
|
230
231
|
// ------------------------------
|
|
231
232
|
_defineProperty(this, "controlRef", null);
|
|
@@ -1241,7 +1242,7 @@ export default class Select extends Component {
|
|
|
1241
1242
|
labelId,
|
|
1242
1243
|
menuIsOpen,
|
|
1243
1244
|
required,
|
|
1244
|
-
tabIndex
|
|
1245
|
+
tabIndex = 0
|
|
1245
1246
|
} = this.props;
|
|
1246
1247
|
const {
|
|
1247
1248
|
Input
|
|
@@ -1258,7 +1259,7 @@ export default class Select extends Component {
|
|
|
1258
1259
|
|
|
1259
1260
|
// aria attributes makes the JSX "noisy", separated for clarity
|
|
1260
1261
|
const ariaAttributes = {
|
|
1261
|
-
'aria-autocomplete': '
|
|
1262
|
+
'aria-autocomplete': 'both',
|
|
1262
1263
|
'aria-errormessage': this.props['aria-errormessage'],
|
|
1263
1264
|
'aria-expanded': menuIsOpen,
|
|
1264
1265
|
'aria-haspopup': 'listbox',
|
|
@@ -1268,7 +1269,7 @@ export default class Select extends Component {
|
|
|
1268
1269
|
'aria-labelledby': this.props['aria-labelledby'] || labelId,
|
|
1269
1270
|
'aria-required': required || isRequired,
|
|
1270
1271
|
role: 'combobox',
|
|
1271
|
-
'aria-activedescendant': this.
|
|
1272
|
+
'aria-activedescendant': this.state.focusedOptionId || undefined,
|
|
1272
1273
|
...(menuIsOpen && {
|
|
1273
1274
|
'aria-controls': this.getElementId('listbox')
|
|
1274
1275
|
}),
|
|
@@ -1521,14 +1522,16 @@ export default class Select extends Component {
|
|
|
1521
1522
|
menuShouldScrollIntoView,
|
|
1522
1523
|
noOptionsMessage,
|
|
1523
1524
|
onMenuScrollToTop,
|
|
1524
|
-
onMenuScrollToBottom
|
|
1525
|
+
onMenuScrollToBottom,
|
|
1526
|
+
labelId,
|
|
1527
|
+
label
|
|
1525
1528
|
} = this.props;
|
|
1526
1529
|
if (!menuIsOpen) {
|
|
1527
1530
|
return null;
|
|
1528
1531
|
}
|
|
1529
1532
|
|
|
1530
1533
|
// TODO: Internal Option Type here
|
|
1531
|
-
const render = (props, id) => {
|
|
1534
|
+
const render = (props, id, headingId) => {
|
|
1532
1535
|
const {
|
|
1533
1536
|
type,
|
|
1534
1537
|
data,
|
|
@@ -1547,7 +1550,8 @@ export default class Select extends Component {
|
|
|
1547
1550
|
onMouseMove: onHover,
|
|
1548
1551
|
onMouseOver: onHover,
|
|
1549
1552
|
role: 'option',
|
|
1550
|
-
'aria-selected':
|
|
1553
|
+
'aria-selected': !commonProps.isMulti && fg('design_system_select-a11y-improvement') ? isSelected || undefined : isSelected,
|
|
1554
|
+
'aria-describedby': fg('design_system_select-a11y-improvement') ? headingId : undefined
|
|
1551
1555
|
};
|
|
1552
1556
|
return /*#__PURE__*/React.createElement(Option, _extends({}, commonProps, {
|
|
1553
1557
|
innerProps: innerProps,
|
|
@@ -1564,7 +1568,8 @@ export default class Select extends Component {
|
|
|
1564
1568
|
};
|
|
1565
1569
|
let menuUI;
|
|
1566
1570
|
if (this.hasOptions()) {
|
|
1567
|
-
|
|
1571
|
+
const items = this.getCategorizedOptions();
|
|
1572
|
+
menuUI = items.map(item => {
|
|
1568
1573
|
if (item.type === 'group') {
|
|
1569
1574
|
const {
|
|
1570
1575
|
data,
|
|
@@ -1583,7 +1588,7 @@ export default class Select extends Component {
|
|
|
1583
1588
|
data: item.data
|
|
1584
1589
|
},
|
|
1585
1590
|
label: this.formatGroupLabel(item.data)
|
|
1586
|
-
}), item.options.map(option => render(option, `${groupIndex}-${option.index}
|
|
1591
|
+
}), item.options.map(option => render(option, `${groupIndex}-${option.index}`, headingId)));
|
|
1587
1592
|
} else if (item.type === 'option') {
|
|
1588
1593
|
return render(item, `${item.index}`);
|
|
1589
1594
|
}
|
|
@@ -1639,8 +1644,14 @@ export default class Select extends Component {
|
|
|
1639
1644
|
},
|
|
1640
1645
|
innerProps: {
|
|
1641
1646
|
role: 'listbox',
|
|
1642
|
-
'aria-multiselectable
|
|
1643
|
-
|
|
1647
|
+
// don't add aria-multiselectable when ff is on and the value is false
|
|
1648
|
+
'aria-multiselectable': fg('design_system_select-a11y-improvement') ? commonProps.isMulti || undefined : commonProps.isMulti,
|
|
1649
|
+
id: this.getElementId('listbox'),
|
|
1650
|
+
// add aditional label on listbox when ff is on
|
|
1651
|
+
...(fg('design_system_select-a11y-improvement') && {
|
|
1652
|
+
'aria-label': label,
|
|
1653
|
+
'aria-labelledby': labelId
|
|
1654
|
+
})
|
|
1644
1655
|
},
|
|
1645
1656
|
isLoading: isLoading,
|
|
1646
1657
|
maxHeight: maxHeight,
|
|
@@ -7,6 +7,10 @@ function testPlatform(re) {
|
|
|
7
7
|
export function isIPhone() {
|
|
8
8
|
return testPlatform(/^iPhone/i);
|
|
9
9
|
}
|
|
10
|
+
export function isSafari() {
|
|
11
|
+
var ua = navigator.userAgent.toLowerCase();
|
|
12
|
+
return ua.includes('safari') && !ua.includes('chrome');
|
|
13
|
+
}
|
|
10
14
|
export function isMac() {
|
|
11
15
|
return testPlatform(/^Mac/i);
|
|
12
16
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
1
2
|
export var defaultAriaLiveMessages = {
|
|
2
3
|
guidance: function guidance(props) {
|
|
3
4
|
var isSearchable = props.isSearchable,
|
|
@@ -44,19 +45,22 @@ export var defaultAriaLiveMessages = {
|
|
|
44
45
|
_props$label2 = props.label,
|
|
45
46
|
label = _props$label2 === void 0 ? '' : _props$label2,
|
|
46
47
|
selectValue = props.selectValue,
|
|
48
|
+
isMulti = props.isMulti,
|
|
47
49
|
isDisabled = props.isDisabled,
|
|
48
50
|
isSelected = props.isSelected,
|
|
49
51
|
isAppleDevice = props.isAppleDevice;
|
|
50
52
|
var getArrayIndex = function getArrayIndex(arr, item) {
|
|
51
|
-
return arr && arr.length ? "".concat(arr.indexOf(item) + 1, " of ").concat(arr.length) : '';
|
|
53
|
+
return arr && arr.length ? "(".concat(arr.indexOf(item) + 1, " of ").concat(arr.length, ")") : '';
|
|
52
54
|
};
|
|
53
55
|
if (context === 'value' && selectValue) {
|
|
54
56
|
return "value ".concat(label, " focused, ").concat(getArrayIndex(selectValue, focused), ".");
|
|
55
57
|
}
|
|
56
58
|
if (context === 'menu' && isAppleDevice) {
|
|
57
59
|
var disabled = isDisabled ? ' disabled' : '';
|
|
58
|
-
|
|
59
|
-
|
|
60
|
+
// don't announce not selected for single selection
|
|
61
|
+
var notSelectedStatus = !isMulti && fg('design_system_select-a11y-improvement') ? '' : ' not selected';
|
|
62
|
+
var status = "".concat(isSelected ? ' selected' : notSelectedStatus).concat(disabled);
|
|
63
|
+
return "".concat(label).concat(status, ", ").concat(getArrayIndex(options, focused), ", completion selected");
|
|
60
64
|
}
|
|
61
65
|
return '';
|
|
62
66
|
},
|
|
@@ -5,8 +5,9 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
|
|
|
5
5
|
* @jsxRuntime classic
|
|
6
6
|
* @jsx jsx
|
|
7
7
|
*/
|
|
8
|
-
import { Fragment, useMemo } from 'react';
|
|
8
|
+
import { Fragment, useMemo, useRef } from 'react';
|
|
9
9
|
import { jsx } from '@emotion/react';
|
|
10
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
10
11
|
import { defaultAriaLiveMessages } from '../accessibility';
|
|
11
12
|
import A11yText from './internal/a11y-text';
|
|
12
13
|
|
|
@@ -40,6 +41,9 @@ var LiveRegion = function LiveRegion(props) {
|
|
|
40
41
|
var ariaLabel = selectProps['aria-label'] || label;
|
|
41
42
|
var ariaLive = selectProps['aria-live'];
|
|
42
43
|
|
|
44
|
+
// for safari, we will use minimum support from aria-live region
|
|
45
|
+
var isA11yImprovementEnabled = fg('design_system_select-a11y-improvement') && !isAppleDevice;
|
|
46
|
+
|
|
43
47
|
// Update aria live message configuration when prop changes
|
|
44
48
|
var messages = useMemo(function () {
|
|
45
49
|
return _objectSpread(_objectSpread({}, defaultAriaLiveMessages), ariaLiveMessages || {});
|
|
@@ -77,10 +81,17 @@ var LiveRegion = function LiveRegion(props) {
|
|
|
77
81
|
}
|
|
78
82
|
return message;
|
|
79
83
|
}, [ariaSelection, messages, isOptionDisabled, selectValue, getOptionLabel]);
|
|
84
|
+
var prevInputValue = useRef('');
|
|
80
85
|
var ariaFocused = useMemo(function () {
|
|
81
86
|
var focusMsg = '';
|
|
82
87
|
var focused = focusedOption || focusedValue;
|
|
83
88
|
var isSelected = !!(focusedOption && selectValue && selectValue.includes(focusedOption));
|
|
89
|
+
if ((!inputValue || inputValue === prevInputValue.current) && isA11yImprovementEnabled) {
|
|
90
|
+
// only announce focus option when searching when ff is on,
|
|
91
|
+
// for safari, we will announce for all
|
|
92
|
+
return '';
|
|
93
|
+
}
|
|
94
|
+
prevInputValue.current = inputValue;
|
|
84
95
|
if (focused && messages.onFocus) {
|
|
85
96
|
var onFocusProps = {
|
|
86
97
|
focused: focused,
|
|
@@ -90,27 +101,36 @@ var LiveRegion = function LiveRegion(props) {
|
|
|
90
101
|
options: focusableOptions,
|
|
91
102
|
context: focused === focusedOption ? 'menu' : 'value',
|
|
92
103
|
selectValue: selectValue,
|
|
93
|
-
isAppleDevice: isAppleDevice
|
|
104
|
+
isAppleDevice: isAppleDevice,
|
|
105
|
+
isMulti: isMulti
|
|
94
106
|
};
|
|
95
107
|
focusMsg = messages.onFocus(onFocusProps);
|
|
96
108
|
}
|
|
97
109
|
return focusMsg;
|
|
98
|
-
}, [focusedOption, focusedValue, getOptionLabel, isOptionDisabled, messages, focusableOptions, selectValue, isAppleDevice]);
|
|
110
|
+
}, [inputValue, focusedOption, focusedValue, getOptionLabel, isOptionDisabled, messages, focusableOptions, selectValue, isAppleDevice, isA11yImprovementEnabled, isMulti]);
|
|
99
111
|
var ariaResults = useMemo(function () {
|
|
100
112
|
var resultsMsg = '';
|
|
101
113
|
if (menuIsOpen && options.length && !isLoading && messages.onFilter) {
|
|
102
|
-
var
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
114
|
+
var shouldAnnounceAvailableResults = !focusableOptions.length;
|
|
115
|
+
if (shouldAnnounceAvailableResults && fg('design_system_select-a11y-improvement') || !fg('design_system_select-a11y-improvement')) {
|
|
116
|
+
// only announce no option results when ff is on
|
|
117
|
+
var resultsMessage = screenReaderStatus({
|
|
118
|
+
count: focusableOptions.length
|
|
119
|
+
});
|
|
120
|
+
resultsMsg = messages.onFilter({
|
|
121
|
+
inputValue: inputValue,
|
|
122
|
+
resultsMessage: resultsMessage
|
|
123
|
+
});
|
|
124
|
+
}
|
|
109
125
|
}
|
|
110
126
|
return resultsMsg;
|
|
111
127
|
}, [focusableOptions, inputValue, menuIsOpen, messages, options, screenReaderStatus, isLoading]);
|
|
112
128
|
var isInitialFocus = (ariaSelection === null || ariaSelection === void 0 ? void 0 : ariaSelection.action) === 'initial-input-focus';
|
|
113
129
|
var ariaGuidance = useMemo(function () {
|
|
130
|
+
if (fg('design_system_select-a11y-improvement')) {
|
|
131
|
+
// don't announce guidance at all when ff is on
|
|
132
|
+
return '';
|
|
133
|
+
}
|
|
114
134
|
var guidanceMsg = '';
|
|
115
135
|
if (messages.guidance) {
|
|
116
136
|
var context = focusedValue ? 'value' : menuIsOpen ? 'menu' : 'input';
|
|
@@ -132,16 +152,14 @@ var LiveRegion = function LiveRegion(props) {
|
|
|
132
152
|
id: "aria-focused"
|
|
133
153
|
}, ariaFocused), jsx("span", {
|
|
134
154
|
id: "aria-results"
|
|
135
|
-
}, ariaResults), jsx("span", {
|
|
155
|
+
}, ariaResults), !fg('design_system_select-a11y-improvement') && jsx("span", {
|
|
136
156
|
id: "aria-guidance"
|
|
137
157
|
}, ariaGuidance));
|
|
138
158
|
return jsx(Fragment, null, jsx(A11yText, {
|
|
139
159
|
id: id
|
|
140
160
|
}, isInitialFocus && ScreenReaderText), jsx(A11yText, {
|
|
141
|
-
"aria-live": ariaLive,
|
|
142
|
-
|
|
143
|
-
"aria-relevant": "additions text",
|
|
144
|
-
role: "log"
|
|
161
|
+
"aria-live": isA11yImprovementEnabled ? 'polite' : ariaLive,
|
|
162
|
+
role: isA11yImprovementEnabled ? 'status' : 'log'
|
|
145
163
|
}, isFocused && !isInitialFocus && ScreenReaderText));
|
|
146
164
|
};
|
|
147
165
|
|
|
@@ -307,7 +307,9 @@ export var MenuList = function MenuList(props) {
|
|
|
307
307
|
'menu-list--is-multi': isMulti
|
|
308
308
|
}), {
|
|
309
309
|
ref: innerRef
|
|
310
|
-
}, innerProps
|
|
310
|
+
}, innerProps, {
|
|
311
|
+
tabIndex: -1
|
|
312
|
+
}), children);
|
|
311
313
|
};
|
|
312
314
|
|
|
313
315
|
// ==============================
|
package/dist/esm/select.js
CHANGED
|
@@ -12,7 +12,8 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
|
|
|
12
12
|
function _createSuper(t) { var r = _isNativeReflectConstruct(); return function () { var e, o = _getPrototypeOf(t); if (r) { var s = _getPrototypeOf(this).constructor; e = Reflect.construct(o, arguments, s); } else e = o.apply(this, arguments); return _possibleConstructorReturn(this, e); }; }
|
|
13
13
|
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
|
|
14
14
|
import React, { Component } from 'react';
|
|
15
|
-
import {
|
|
15
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
16
|
+
import { isAppleDevice, isSafari } from './accessibility/helpers';
|
|
16
17
|
import { formatGroupLabel as formatGroupLabelBuiltin, getOptionLabel as getOptionLabelBuiltin, getOptionValue as getOptionValueBuiltin, isOptionDisabled as isOptionDisabledBuiltin } from './builtins';
|
|
17
18
|
import { defaultComponents } from './components';
|
|
18
19
|
import { DummyInput, RequiredInput, ScrollManager } from './components/internal';
|
|
@@ -248,7 +249,7 @@ var Select = /*#__PURE__*/function (_Component) {
|
|
|
248
249
|
_defineProperty(_assertThisInitialized(_this), "initialTouchY", 0);
|
|
249
250
|
_defineProperty(_assertThisInitialized(_this), "openAfterFocus", false);
|
|
250
251
|
_defineProperty(_assertThisInitialized(_this), "scrollToFocusedOptionOnUpdate", false);
|
|
251
|
-
_defineProperty(_assertThisInitialized(_this), "isAppleDevice", isAppleDevice());
|
|
252
|
+
_defineProperty(_assertThisInitialized(_this), "isAppleDevice", fg('design_system_select-a11y-improvement') ? isSafari() : isAppleDevice());
|
|
252
253
|
// Refs
|
|
253
254
|
// ------------------------------
|
|
254
255
|
_defineProperty(_assertThisInitialized(_this), "controlRef", null);
|
|
@@ -1227,7 +1228,8 @@ var Select = /*#__PURE__*/function (_Component) {
|
|
|
1227
1228
|
labelId = _this$props8.labelId,
|
|
1228
1229
|
menuIsOpen = _this$props8.menuIsOpen,
|
|
1229
1230
|
required = _this$props8.required,
|
|
1230
|
-
tabIndex = _this$props8.tabIndex
|
|
1231
|
+
_this$props8$tabIndex = _this$props8.tabIndex,
|
|
1232
|
+
tabIndex = _this$props8$tabIndex === void 0 ? 0 : _this$props8$tabIndex;
|
|
1231
1233
|
var _this$getComponents = this.getComponents(),
|
|
1232
1234
|
Input = _this$getComponents.Input;
|
|
1233
1235
|
var _this$state4 = this.state,
|
|
@@ -1239,7 +1241,7 @@ var Select = /*#__PURE__*/function (_Component) {
|
|
|
1239
1241
|
|
|
1240
1242
|
// aria attributes makes the JSX "noisy", separated for clarity
|
|
1241
1243
|
var ariaAttributes = _objectSpread(_objectSpread(_objectSpread({
|
|
1242
|
-
'aria-autocomplete': '
|
|
1244
|
+
'aria-autocomplete': 'both',
|
|
1243
1245
|
'aria-errormessage': this.props['aria-errormessage'],
|
|
1244
1246
|
'aria-expanded': menuIsOpen,
|
|
1245
1247
|
'aria-haspopup': 'listbox',
|
|
@@ -1249,7 +1251,7 @@ var Select = /*#__PURE__*/function (_Component) {
|
|
|
1249
1251
|
'aria-labelledby': this.props['aria-labelledby'] || labelId,
|
|
1250
1252
|
'aria-required': required || isRequired,
|
|
1251
1253
|
role: 'combobox',
|
|
1252
|
-
'aria-activedescendant': this.
|
|
1254
|
+
'aria-activedescendant': this.state.focusedOptionId || undefined
|
|
1253
1255
|
}, menuIsOpen && {
|
|
1254
1256
|
'aria-controls': this.getElementId('listbox')
|
|
1255
1257
|
}), !isSearchable && {
|
|
@@ -1481,13 +1483,15 @@ var Select = /*#__PURE__*/function (_Component) {
|
|
|
1481
1483
|
menuShouldScrollIntoView = _this$props12.menuShouldScrollIntoView,
|
|
1482
1484
|
noOptionsMessage = _this$props12.noOptionsMessage,
|
|
1483
1485
|
onMenuScrollToTop = _this$props12.onMenuScrollToTop,
|
|
1484
|
-
onMenuScrollToBottom = _this$props12.onMenuScrollToBottom
|
|
1486
|
+
onMenuScrollToBottom = _this$props12.onMenuScrollToBottom,
|
|
1487
|
+
labelId = _this$props12.labelId,
|
|
1488
|
+
label = _this$props12.label;
|
|
1485
1489
|
if (!menuIsOpen) {
|
|
1486
1490
|
return null;
|
|
1487
1491
|
}
|
|
1488
1492
|
|
|
1489
1493
|
// TODO: Internal Option Type here
|
|
1490
|
-
var render = function render(props, id) {
|
|
1494
|
+
var render = function render(props, id, headingId) {
|
|
1491
1495
|
var type = props.type,
|
|
1492
1496
|
data = props.data,
|
|
1493
1497
|
isDisabled = props.isDisabled,
|
|
@@ -1508,7 +1512,8 @@ var Select = /*#__PURE__*/function (_Component) {
|
|
|
1508
1512
|
onMouseMove: onHover,
|
|
1509
1513
|
onMouseOver: onHover,
|
|
1510
1514
|
role: 'option',
|
|
1511
|
-
'aria-selected':
|
|
1515
|
+
'aria-selected': !commonProps.isMulti && fg('design_system_select-a11y-improvement') ? isSelected || undefined : isSelected,
|
|
1516
|
+
'aria-describedby': fg('design_system_select-a11y-improvement') ? headingId : undefined
|
|
1512
1517
|
};
|
|
1513
1518
|
return /*#__PURE__*/React.createElement(Option, _extends({}, commonProps, {
|
|
1514
1519
|
innerProps: innerProps,
|
|
@@ -1525,7 +1530,8 @@ var Select = /*#__PURE__*/function (_Component) {
|
|
|
1525
1530
|
};
|
|
1526
1531
|
var menuUI;
|
|
1527
1532
|
if (this.hasOptions()) {
|
|
1528
|
-
|
|
1533
|
+
var items = this.getCategorizedOptions();
|
|
1534
|
+
menuUI = items.map(function (item) {
|
|
1529
1535
|
if (item.type === 'group') {
|
|
1530
1536
|
var data = item.data,
|
|
1531
1537
|
options = item.options,
|
|
@@ -1543,7 +1549,7 @@ var Select = /*#__PURE__*/function (_Component) {
|
|
|
1543
1549
|
},
|
|
1544
1550
|
label: _this4.formatGroupLabel(item.data)
|
|
1545
1551
|
}), item.options.map(function (option) {
|
|
1546
|
-
return render(option, "".concat(groupIndex, "-").concat(option.index));
|
|
1552
|
+
return render(option, "".concat(groupIndex, "-").concat(option.index), headingId);
|
|
1547
1553
|
}));
|
|
1548
1554
|
} else if (item.type === 'option') {
|
|
1549
1555
|
return render(item, "".concat(item.index));
|
|
@@ -1598,11 +1604,15 @@ var Select = /*#__PURE__*/function (_Component) {
|
|
|
1598
1604
|
_this4.getMenuListRef(instance);
|
|
1599
1605
|
scrollTargetRef(instance);
|
|
1600
1606
|
},
|
|
1601
|
-
innerProps: {
|
|
1607
|
+
innerProps: _objectSpread({
|
|
1602
1608
|
role: 'listbox',
|
|
1603
|
-
'aria-multiselectable
|
|
1609
|
+
// don't add aria-multiselectable when ff is on and the value is false
|
|
1610
|
+
'aria-multiselectable': fg('design_system_select-a11y-improvement') ? commonProps.isMulti || undefined : commonProps.isMulti,
|
|
1604
1611
|
id: _this4.getElementId('listbox')
|
|
1605
|
-
},
|
|
1612
|
+
}, fg('design_system_select-a11y-improvement') && {
|
|
1613
|
+
'aria-label': label,
|
|
1614
|
+
'aria-labelledby': labelId
|
|
1615
|
+
}),
|
|
1606
1616
|
isLoading: isLoading,
|
|
1607
1617
|
maxHeight: maxHeight,
|
|
1608
1618
|
focusedOption: focusedOption
|
|
@@ -94,6 +94,10 @@ export interface AriaOnFocusProps<Option, Group extends GroupBase<Option>> {
|
|
|
94
94
|
* Boolean indicating whether user uses Apple device
|
|
95
95
|
*/
|
|
96
96
|
isAppleDevice: boolean;
|
|
97
|
+
/**
|
|
98
|
+
* Boolean value of selectProp isMulti
|
|
99
|
+
*/
|
|
100
|
+
isMulti: boolean;
|
|
97
101
|
}
|
|
98
102
|
export type AriaGuidance = (props: AriaGuidanceProps) => string;
|
|
99
103
|
export type AriaOnChange<Option, IsMulti extends boolean> = (props: AriaOnChangeProps<Option, IsMulti>) => string;
|
|
@@ -94,6 +94,10 @@ export interface AriaOnFocusProps<Option, Group extends GroupBase<Option>> {
|
|
|
94
94
|
* Boolean indicating whether user uses Apple device
|
|
95
95
|
*/
|
|
96
96
|
isAppleDevice: boolean;
|
|
97
|
+
/**
|
|
98
|
+
* Boolean value of selectProp isMulti
|
|
99
|
+
*/
|
|
100
|
+
isMulti: boolean;
|
|
97
101
|
}
|
|
98
102
|
export type AriaGuidance = (props: AriaGuidanceProps) => string;
|
|
99
103
|
export type AriaOnChange<Option, IsMulti extends boolean> = (props: AriaOnChangeProps<Option, IsMulti>) => string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/react-select",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
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",
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@atlaskit/ds-lib": "^3.1.0",
|
|
30
|
+
"@atlaskit/platform-feature-flags": "^0.3.0",
|
|
30
31
|
"@babel/runtime": "^7.0.0",
|
|
31
32
|
"@emotion/react": "^11.7.1",
|
|
32
33
|
"@floating-ui/dom": "^1.0.1",
|
|
@@ -49,6 +50,11 @@
|
|
|
49
50
|
"typescript": "~5.4.2",
|
|
50
51
|
"wait-for-expect": "^1.2.0"
|
|
51
52
|
},
|
|
53
|
+
"platform-feature-flags": {
|
|
54
|
+
"design_system_select-a11y-improvement": {
|
|
55
|
+
"type": "boolean"
|
|
56
|
+
}
|
|
57
|
+
},
|
|
52
58
|
"techstack": {
|
|
53
59
|
"@atlassian/frontend": {
|
|
54
60
|
"import-structure": [
|