@khanacademy/wonder-blocks-dropdown 2.7.0 → 2.7.3
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 +28 -0
- package/dist/es/index.js +50 -7
- package/dist/index.js +71 -7
- package/package.json +10 -10
- package/src/__tests__/__snapshots__/generated-snapshot.test.js.snap +494 -0
- package/src/components/__tests__/dropdown-core-virtualized.test.js +0 -9
- package/src/components/__tests__/dropdown-core.test.js +65 -42
- package/src/components/__tests__/multi-select.test.js +57 -0
- package/src/components/__tests__/single-select.test.js +30 -0
- package/src/components/dropdown-core.js +55 -1
- package/src/components/multi-select.js +2 -1
- package/src/components/search-text-input.js +22 -0
- package/src/components/single-select.stories.js +9 -2
- package/src/util/constants.js +3 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# @khanacademy/wonder-blocks-dropdown
|
|
2
2
|
|
|
3
|
+
## 2.7.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- d0a76cf5: Add live region to announce the number of options (fixing an iOS issue)
|
|
8
|
+
|
|
9
|
+
## 2.7.2
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Updated dependencies [5f4a4297]
|
|
14
|
+
- Updated dependencies [2b96fd59]
|
|
15
|
+
- @khanacademy/wonder-blocks-core@4.3.2
|
|
16
|
+
- @khanacademy/wonder-blocks-button@2.11.6
|
|
17
|
+
- @khanacademy/wonder-blocks-clickable@2.2.7
|
|
18
|
+
- @khanacademy/wonder-blocks-icon@1.2.28
|
|
19
|
+
- @khanacademy/wonder-blocks-icon-button@3.4.7
|
|
20
|
+
- @khanacademy/wonder-blocks-layout@1.4.10
|
|
21
|
+
- @khanacademy/wonder-blocks-modal@2.3.2
|
|
22
|
+
- @khanacademy/wonder-blocks-search-field@1.0.5
|
|
23
|
+
- @khanacademy/wonder-blocks-typography@1.1.32
|
|
24
|
+
|
|
25
|
+
## 2.7.1
|
|
26
|
+
|
|
27
|
+
### Patch Changes
|
|
28
|
+
|
|
29
|
+
- 9c2580e6: Fixed the search field focus bug in dropdowns
|
|
30
|
+
|
|
3
31
|
## 2.7.0
|
|
4
32
|
|
|
5
33
|
### Minor Changes
|
package/dist/es/index.js
CHANGED
|
@@ -35,7 +35,8 @@ const filterableDropdownStyle = {
|
|
|
35
35
|
};
|
|
36
36
|
const searchInputStyle = {
|
|
37
37
|
margin: Spacing.xSmall_8,
|
|
38
|
-
marginTop: Spacing.xxxSmall_4
|
|
38
|
+
marginTop: Spacing.xxxSmall_4,
|
|
39
|
+
minHeight: "auto"
|
|
39
40
|
};
|
|
40
41
|
const DROPDOWN_ITEM_HEIGHT = 40;
|
|
41
42
|
const MAX_VISIBLE_ITEMS = 9;
|
|
@@ -491,6 +492,14 @@ class SearchTextInput extends React.Component {
|
|
|
491
492
|
return instance && instance.type && instance.type.__IS_SEARCH_TEXT_INPUT__;
|
|
492
493
|
}
|
|
493
494
|
|
|
495
|
+
componentDidMount() {
|
|
496
|
+
if (this.props.autofocus) {
|
|
497
|
+
var _this$props$itemRef;
|
|
498
|
+
|
|
499
|
+
(_this$props$itemRef = this.props.itemRef) == null ? void 0 : _this$props$itemRef.current.focus();
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
494
503
|
render() {
|
|
495
504
|
const {
|
|
496
505
|
labels,
|
|
@@ -710,6 +719,7 @@ function DropdownPopper({
|
|
|
710
719
|
}
|
|
711
720
|
|
|
712
721
|
const VIRTUALIZE_THRESHOLD = 125;
|
|
722
|
+
const StyledSpan = addStyle("span");
|
|
713
723
|
|
|
714
724
|
class DropdownCore extends React.Component {
|
|
715
725
|
static sameItemsFocusable(prevItems, currentItems) {
|
|
@@ -1102,7 +1112,8 @@ class DropdownCore extends React.Component {
|
|
|
1102
1112
|
this.handleClickFocus(0);
|
|
1103
1113
|
this.focusCurrentItem();
|
|
1104
1114
|
},
|
|
1105
|
-
style: searchInputStyle
|
|
1115
|
+
style: searchInputStyle,
|
|
1116
|
+
autofocus: this.focusedIndex === 0
|
|
1106
1117
|
}));
|
|
1107
1118
|
}
|
|
1108
1119
|
|
|
@@ -1135,7 +1146,8 @@ class DropdownCore extends React.Component {
|
|
|
1135
1146
|
},
|
|
1136
1147
|
populatedProps: {
|
|
1137
1148
|
style: searchInputStyle,
|
|
1138
|
-
itemRef: this.state.itemRefs[focusIndex] ? this.state.itemRefs[focusIndex].ref : null
|
|
1149
|
+
itemRef: this.state.itemRefs[focusIndex] ? this.state.itemRefs[focusIndex].ref : null,
|
|
1150
|
+
autofocus: this.focusedIndex === 0
|
|
1139
1151
|
}
|
|
1140
1152
|
});
|
|
1141
1153
|
}
|
|
@@ -1190,6 +1202,24 @@ class DropdownCore extends React.Component {
|
|
|
1190
1202
|
}, isReferenceHidden => this.renderDropdownMenu(listRenderer, isReferenceHidden));
|
|
1191
1203
|
}
|
|
1192
1204
|
|
|
1205
|
+
renderLiveRegion() {
|
|
1206
|
+
const {
|
|
1207
|
+
items,
|
|
1208
|
+
open
|
|
1209
|
+
} = this.props;
|
|
1210
|
+
const {
|
|
1211
|
+
labels
|
|
1212
|
+
} = this.state;
|
|
1213
|
+
const totalItems = this.hasSearchBox() ? items.length - 1 : items.length;
|
|
1214
|
+
return React.createElement(StyledSpan, {
|
|
1215
|
+
"aria-live": "polite",
|
|
1216
|
+
"aria-atomic": "true",
|
|
1217
|
+
"aria-relevant": "additions text",
|
|
1218
|
+
style: styles$3.srOnly,
|
|
1219
|
+
"data-test-id": "dropdown-live-region"
|
|
1220
|
+
}, open && labels.someSelected(totalItems));
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1193
1223
|
render() {
|
|
1194
1224
|
const {
|
|
1195
1225
|
open,
|
|
@@ -1202,7 +1232,7 @@ class DropdownCore extends React.Component {
|
|
|
1202
1232
|
onKeyUp: this.handleKeyUp,
|
|
1203
1233
|
style: [styles$3.menuWrapper, style],
|
|
1204
1234
|
className: className
|
|
1205
|
-
}, opener, open && this.renderDropdown());
|
|
1235
|
+
}, this.renderLiveRegion(), opener, open && this.renderDropdown());
|
|
1206
1236
|
}
|
|
1207
1237
|
|
|
1208
1238
|
}
|
|
@@ -1210,7 +1240,8 @@ class DropdownCore extends React.Component {
|
|
|
1210
1240
|
DropdownCore.defaultProps = {
|
|
1211
1241
|
alignment: "left",
|
|
1212
1242
|
labels: {
|
|
1213
|
-
noResults: defaultLabels.noResults
|
|
1243
|
+
noResults: defaultLabels.noResults,
|
|
1244
|
+
someSelected: defaultLabels.someSelected
|
|
1214
1245
|
},
|
|
1215
1246
|
light: false
|
|
1216
1247
|
};
|
|
@@ -1238,6 +1269,16 @@ const styles$3 = StyleSheet.create({
|
|
|
1238
1269
|
color: Color.offBlack64,
|
|
1239
1270
|
alignSelf: "center",
|
|
1240
1271
|
marginTop: Spacing.xxSmall_6
|
|
1272
|
+
},
|
|
1273
|
+
srOnly: {
|
|
1274
|
+
border: 0,
|
|
1275
|
+
clip: "rect(0,0,0,0)",
|
|
1276
|
+
height: 1,
|
|
1277
|
+
margin: -1,
|
|
1278
|
+
overflow: "hidden",
|
|
1279
|
+
padding: 0,
|
|
1280
|
+
position: "absolute",
|
|
1281
|
+
width: 1
|
|
1241
1282
|
}
|
|
1242
1283
|
});
|
|
1243
1284
|
var DropdownCore$1 = withActionScheduler(DropdownCore);
|
|
@@ -2241,7 +2282,8 @@ class MultiSelect extends React.Component {
|
|
|
2241
2282
|
searchText
|
|
2242
2283
|
} = this.state;
|
|
2243
2284
|
const {
|
|
2244
|
-
noResults
|
|
2285
|
+
noResults,
|
|
2286
|
+
someSelected
|
|
2245
2287
|
} = this.state.labels;
|
|
2246
2288
|
const allChildren = React.Children.toArray(children).filter(Boolean);
|
|
2247
2289
|
const numOptions = allChildren.length;
|
|
@@ -2262,7 +2304,8 @@ class MultiSelect extends React.Component {
|
|
|
2262
2304
|
onSearchTextChanged: isFilterable ? this.handleSearchTextChanged : null,
|
|
2263
2305
|
searchText: isFilterable ? searchText : "",
|
|
2264
2306
|
labels: {
|
|
2265
|
-
noResults
|
|
2307
|
+
noResults,
|
|
2308
|
+
someSelected
|
|
2266
2309
|
}
|
|
2267
2310
|
});
|
|
2268
2311
|
}
|
package/dist/index.js
CHANGED
|
@@ -127,7 +127,10 @@ const filterableDropdownStyle = {
|
|
|
127
127
|
};
|
|
128
128
|
const searchInputStyle = {
|
|
129
129
|
margin: _khanacademy_wonder_blocks_spacing__WEBPACK_IMPORTED_MODULE_0___default.a.xSmall_8,
|
|
130
|
-
marginTop: _khanacademy_wonder_blocks_spacing__WEBPACK_IMPORTED_MODULE_0___default.a.xxxSmall_4
|
|
130
|
+
marginTop: _khanacademy_wonder_blocks_spacing__WEBPACK_IMPORTED_MODULE_0___default.a.xxxSmall_4,
|
|
131
|
+
// Set `minHeight` to "auto" to stop the search field from having
|
|
132
|
+
// a height of 0 and being cut off.
|
|
133
|
+
minHeight: "auto"
|
|
131
134
|
}; // The default item height
|
|
132
135
|
|
|
133
136
|
const DROPDOWN_ITEM_HEIGHT = 40;
|
|
@@ -257,6 +260,23 @@ class SearchTextInput extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
|
|
|
257
260
|
return instance && instance.type && instance.type.__IS_SEARCH_TEXT_INPUT__;
|
|
258
261
|
}
|
|
259
262
|
|
|
263
|
+
// TODO(WB-1310): Remove `componentDidMount` autofocus on the search field
|
|
264
|
+
// after making the search field sticky.
|
|
265
|
+
componentDidMount() {
|
|
266
|
+
// We need to re-focus on the text input after it mounts because of
|
|
267
|
+
// the case in which the dropdown switches between virtualized and
|
|
268
|
+
// non-virtualized. It can rerender the search field as the user is
|
|
269
|
+
// typing based on the number of search results, which results
|
|
270
|
+
// in losing focus on the field so the user can't type anymore.
|
|
271
|
+
// To work around this issue, this temporary fix auto-focuses on the
|
|
272
|
+
// search field on mount.
|
|
273
|
+
if (this.props.autofocus) {
|
|
274
|
+
var _this$props$itemRef;
|
|
275
|
+
|
|
276
|
+
(_this$props$itemRef = this.props.itemRef) == null ? void 0 : _this$props$itemRef.current.focus();
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
260
280
|
render() {
|
|
261
281
|
const {
|
|
262
282
|
labels,
|
|
@@ -767,6 +787,7 @@ DropdownOpener.defaultProps = {
|
|
|
767
787
|
*/
|
|
768
788
|
|
|
769
789
|
const VIRTUALIZE_THRESHOLD = 125;
|
|
790
|
+
const StyledSpan = Object(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_6__["addStyle"])("span");
|
|
770
791
|
|
|
771
792
|
/**
|
|
772
793
|
* A core dropdown component that takes an opener and children to display as
|
|
@@ -1253,7 +1274,13 @@ class DropdownCore extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
|
|
|
1253
1274
|
this.focusCurrentItem();
|
|
1254
1275
|
},
|
|
1255
1276
|
// apply custom styles
|
|
1256
|
-
style: _util_constants_js__WEBPACK_IMPORTED_MODULE_12__[/* searchInputStyle */ "h"]
|
|
1277
|
+
style: _util_constants_js__WEBPACK_IMPORTED_MODULE_12__[/* searchInputStyle */ "h"],
|
|
1278
|
+
// TODO(WB-1310): Remove the autofocus prop after making
|
|
1279
|
+
// the search field sticky.
|
|
1280
|
+
// Currently autofocusing on the search field to work
|
|
1281
|
+
// around it losing focus on mount when switching between
|
|
1282
|
+
// virtualized and non-virtualized dropdown filter results.
|
|
1283
|
+
autofocus: this.focusedIndex === 0
|
|
1257
1284
|
});
|
|
1258
1285
|
} // Render OptionItem and/or ActionItem elements.
|
|
1259
1286
|
|
|
@@ -1299,7 +1326,13 @@ class DropdownCore extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
|
|
|
1299
1326
|
populatedProps: {
|
|
1300
1327
|
style: _util_constants_js__WEBPACK_IMPORTED_MODULE_12__[/* searchInputStyle */ "h"],
|
|
1301
1328
|
// pass the current ref down to the input element
|
|
1302
|
-
itemRef: this.state.itemRefs[focusIndex] ? this.state.itemRefs[focusIndex].ref : null
|
|
1329
|
+
itemRef: this.state.itemRefs[focusIndex] ? this.state.itemRefs[focusIndex].ref : null,
|
|
1330
|
+
// TODO(WB-1310): Remove the autofocus prop after making
|
|
1331
|
+
// the search field sticky.
|
|
1332
|
+
// Currently autofocusing on the search field to work
|
|
1333
|
+
// around it losing focus on mount when switching between
|
|
1334
|
+
// virtualized and non-virtualized dropdown filter results.
|
|
1335
|
+
autofocus: this.focusedIndex === 0
|
|
1303
1336
|
}
|
|
1304
1337
|
};
|
|
1305
1338
|
}
|
|
@@ -1369,6 +1402,24 @@ class DropdownCore extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
|
|
|
1369
1402
|
}, isReferenceHidden => this.renderDropdownMenu(listRenderer, isReferenceHidden));
|
|
1370
1403
|
}
|
|
1371
1404
|
|
|
1405
|
+
renderLiveRegion() {
|
|
1406
|
+
const {
|
|
1407
|
+
items,
|
|
1408
|
+
open
|
|
1409
|
+
} = this.props;
|
|
1410
|
+
const {
|
|
1411
|
+
labels
|
|
1412
|
+
} = this.state;
|
|
1413
|
+
const totalItems = this.hasSearchBox() ? items.length - 1 : items.length;
|
|
1414
|
+
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](StyledSpan, {
|
|
1415
|
+
"aria-live": "polite",
|
|
1416
|
+
"aria-atomic": "true",
|
|
1417
|
+
"aria-relevant": "additions text",
|
|
1418
|
+
style: styles.srOnly,
|
|
1419
|
+
"data-test-id": "dropdown-live-region"
|
|
1420
|
+
}, open && labels.someSelected(totalItems));
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1372
1423
|
render() {
|
|
1373
1424
|
const {
|
|
1374
1425
|
open,
|
|
@@ -1381,7 +1432,7 @@ class DropdownCore extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
|
|
|
1381
1432
|
onKeyUp: this.handleKeyUp,
|
|
1382
1433
|
style: [styles.menuWrapper, style],
|
|
1383
1434
|
className: className
|
|
1384
|
-
}, opener, open && this.renderDropdown());
|
|
1435
|
+
}, this.renderLiveRegion(), opener, open && this.renderDropdown());
|
|
1385
1436
|
}
|
|
1386
1437
|
|
|
1387
1438
|
}
|
|
@@ -1389,7 +1440,8 @@ class DropdownCore extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
|
|
|
1389
1440
|
DropdownCore.defaultProps = {
|
|
1390
1441
|
alignment: "left",
|
|
1391
1442
|
labels: {
|
|
1392
|
-
noResults: _util_constants_js__WEBPACK_IMPORTED_MODULE_12__[/* defaultLabels */ "e"].noResults
|
|
1443
|
+
noResults: _util_constants_js__WEBPACK_IMPORTED_MODULE_12__[/* defaultLabels */ "e"].noResults,
|
|
1444
|
+
someSelected: _util_constants_js__WEBPACK_IMPORTED_MODULE_12__[/* defaultLabels */ "e"].someSelected
|
|
1393
1445
|
},
|
|
1394
1446
|
light: false
|
|
1395
1447
|
};
|
|
@@ -1418,6 +1470,16 @@ const styles = aphrodite__WEBPACK_IMPORTED_MODULE_2__["StyleSheet"].create({
|
|
|
1418
1470
|
color: _khanacademy_wonder_blocks_color__WEBPACK_IMPORTED_MODULE_4___default.a.offBlack64,
|
|
1419
1471
|
alignSelf: "center",
|
|
1420
1472
|
marginTop: _khanacademy_wonder_blocks_spacing__WEBPACK_IMPORTED_MODULE_5___default.a.xxSmall_6
|
|
1473
|
+
},
|
|
1474
|
+
srOnly: {
|
|
1475
|
+
border: 0,
|
|
1476
|
+
clip: "rect(0,0,0,0)",
|
|
1477
|
+
height: 1,
|
|
1478
|
+
margin: -1,
|
|
1479
|
+
overflow: "hidden",
|
|
1480
|
+
padding: 0,
|
|
1481
|
+
position: "absolute",
|
|
1482
|
+
width: 1
|
|
1421
1483
|
}
|
|
1422
1484
|
});
|
|
1423
1485
|
/* harmony default export */ __webpack_exports__["a"] = (Object(_khanacademy_wonder_blocks_timing__WEBPACK_IMPORTED_MODULE_8__["withActionScheduler"])(DropdownCore));
|
|
@@ -2600,7 +2662,8 @@ class MultiSelect extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
|
|
|
2600
2662
|
searchText
|
|
2601
2663
|
} = this.state;
|
|
2602
2664
|
const {
|
|
2603
|
-
noResults
|
|
2665
|
+
noResults,
|
|
2666
|
+
someSelected
|
|
2604
2667
|
} = this.state.labels;
|
|
2605
2668
|
const allChildren = react__WEBPACK_IMPORTED_MODULE_0__["Children"].toArray(children).filter(Boolean);
|
|
2606
2669
|
const numOptions = allChildren.length;
|
|
@@ -2621,7 +2684,8 @@ class MultiSelect extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
|
|
|
2621
2684
|
onSearchTextChanged: isFilterable ? this.handleSearchTextChanged : null,
|
|
2622
2685
|
searchText: isFilterable ? searchText : "",
|
|
2623
2686
|
labels: {
|
|
2624
|
-
noResults
|
|
2687
|
+
noResults,
|
|
2688
|
+
someSelected
|
|
2625
2689
|
}
|
|
2626
2690
|
});
|
|
2627
2691
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@khanacademy/wonder-blocks-dropdown",
|
|
3
|
-
"version": "2.7.
|
|
3
|
+
"version": "2.7.3",
|
|
4
4
|
"design": "v1",
|
|
5
5
|
"description": "Dropdown variants for Wonder Blocks.",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -16,18 +16,18 @@
|
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@babel/runtime": "^7.16.3",
|
|
19
|
-
"@khanacademy/wonder-blocks-button": "^2.11.
|
|
20
|
-
"@khanacademy/wonder-blocks-clickable": "^2.2.
|
|
19
|
+
"@khanacademy/wonder-blocks-button": "^2.11.6",
|
|
20
|
+
"@khanacademy/wonder-blocks-clickable": "^2.2.7",
|
|
21
21
|
"@khanacademy/wonder-blocks-color": "^1.1.20",
|
|
22
|
-
"@khanacademy/wonder-blocks-core": "^4.3.
|
|
23
|
-
"@khanacademy/wonder-blocks-icon": "^1.2.
|
|
24
|
-
"@khanacademy/wonder-blocks-icon-button": "^3.4.
|
|
25
|
-
"@khanacademy/wonder-blocks-layout": "^1.4.
|
|
26
|
-
"@khanacademy/wonder-blocks-modal": "^2.3.
|
|
27
|
-
"@khanacademy/wonder-blocks-search-field": "^1.0.
|
|
22
|
+
"@khanacademy/wonder-blocks-core": "^4.3.2",
|
|
23
|
+
"@khanacademy/wonder-blocks-icon": "^1.2.28",
|
|
24
|
+
"@khanacademy/wonder-blocks-icon-button": "^3.4.7",
|
|
25
|
+
"@khanacademy/wonder-blocks-layout": "^1.4.10",
|
|
26
|
+
"@khanacademy/wonder-blocks-modal": "^2.3.2",
|
|
27
|
+
"@khanacademy/wonder-blocks-search-field": "^1.0.5",
|
|
28
28
|
"@khanacademy/wonder-blocks-spacing": "^3.0.5",
|
|
29
29
|
"@khanacademy/wonder-blocks-timing": "^2.1.0",
|
|
30
|
-
"@khanacademy/wonder-blocks-typography": "^1.1.
|
|
30
|
+
"@khanacademy/wonder-blocks-typography": "^1.1.32"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"@popperjs/core": "^2.10.1",
|