@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 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.0",
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.5",
20
- "@khanacademy/wonder-blocks-clickable": "^2.2.6",
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.1",
23
- "@khanacademy/wonder-blocks-icon": "^1.2.27",
24
- "@khanacademy/wonder-blocks-icon-button": "^3.4.6",
25
- "@khanacademy/wonder-blocks-layout": "^1.4.9",
26
- "@khanacademy/wonder-blocks-modal": "^2.3.1",
27
- "@khanacademy/wonder-blocks-search-field": "^1.0.4",
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.31"
30
+ "@khanacademy/wonder-blocks-typography": "^1.1.32"
31
31
  },
32
32
  "peerDependencies": {
33
33
  "@popperjs/core": "^2.10.1",