@khanacademy/wonder-blocks-dropdown 2.7.0 → 2.7.1

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,11 @@
1
1
  # @khanacademy/wonder-blocks-dropdown
2
2
 
3
+ ## 2.7.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 9c2580e6: Fixed the search field focus bug in dropdowns
8
+
3
9
  ## 2.7.0
4
10
 
5
11
  ### 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,
@@ -1102,7 +1111,8 @@ class DropdownCore extends React.Component {
1102
1111
  this.handleClickFocus(0);
1103
1112
  this.focusCurrentItem();
1104
1113
  },
1105
- style: searchInputStyle
1114
+ style: searchInputStyle,
1115
+ autofocus: this.focusedIndex === 0
1106
1116
  }));
1107
1117
  }
1108
1118
 
@@ -1135,7 +1145,8 @@ class DropdownCore extends React.Component {
1135
1145
  },
1136
1146
  populatedProps: {
1137
1147
  style: searchInputStyle,
1138
- itemRef: this.state.itemRefs[focusIndex] ? this.state.itemRefs[focusIndex].ref : null
1148
+ itemRef: this.state.itemRefs[focusIndex] ? this.state.itemRefs[focusIndex].ref : null,
1149
+ autofocus: this.focusedIndex === 0
1139
1150
  }
1140
1151
  });
1141
1152
  }
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,
@@ -1253,7 +1273,13 @@ class DropdownCore extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
1253
1273
  this.focusCurrentItem();
1254
1274
  },
1255
1275
  // apply custom styles
1256
- style: _util_constants_js__WEBPACK_IMPORTED_MODULE_12__[/* searchInputStyle */ "h"]
1276
+ style: _util_constants_js__WEBPACK_IMPORTED_MODULE_12__[/* searchInputStyle */ "h"],
1277
+ // TODO(WB-1310): Remove the autofocus prop after making
1278
+ // the search field sticky.
1279
+ // Currently autofocusing on the search field to work
1280
+ // around it losing focus on mount when switching between
1281
+ // virtualized and non-virtualized dropdown filter results.
1282
+ autofocus: this.focusedIndex === 0
1257
1283
  });
1258
1284
  } // Render OptionItem and/or ActionItem elements.
1259
1285
 
@@ -1299,7 +1325,13 @@ class DropdownCore extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
1299
1325
  populatedProps: {
1300
1326
  style: _util_constants_js__WEBPACK_IMPORTED_MODULE_12__[/* searchInputStyle */ "h"],
1301
1327
  // pass the current ref down to the input element
1302
- itemRef: this.state.itemRefs[focusIndex] ? this.state.itemRefs[focusIndex].ref : null
1328
+ itemRef: this.state.itemRefs[focusIndex] ? this.state.itemRefs[focusIndex].ref : null,
1329
+ // TODO(WB-1310): Remove the autofocus prop after making
1330
+ // the search field sticky.
1331
+ // Currently autofocusing on the search field to work
1332
+ // around it losing focus on mount when switching between
1333
+ // virtualized and non-virtualized dropdown filter results.
1334
+ autofocus: this.focusedIndex === 0
1303
1335
  }
1304
1336
  };
1305
1337
  }
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.1",
4
4
  "design": "v1",
5
5
  "description": "Dropdown variants for Wonder Blocks.",
6
6
  "main": "dist/index.js",
@@ -8,15 +8,6 @@ import DropdownCoreVirtualized from "../dropdown-core-virtualized.js";
8
8
  import SearchTextInput from "../search-text-input.js";
9
9
 
10
10
  describe("DropdownCoreVirtualized", () => {
11
- beforeEach(() => {
12
- jest.useFakeTimers();
13
- });
14
-
15
- afterEach(() => {
16
- jest.runOnlyPendingTimers();
17
- jest.useRealTimers();
18
- });
19
-
20
11
  it("should sort the items on first load", () => {
21
12
  // Arrange
22
13
  const optionItems = ["a", "bb", "ccc"].map((item, i) => ({
@@ -669,6 +669,12 @@ class DropdownCore extends React.Component<Props, State> {
669
669
  },
670
670
  // apply custom styles
671
671
  style: searchInputStyle,
672
+ // TODO(WB-1310): Remove the autofocus prop after making
673
+ // the search field sticky.
674
+ // Currently autofocusing on the search field to work
675
+ // around it losing focus on mount when switching between
676
+ // virtualized and non-virtualized dropdown filter results.
677
+ autofocus: this.focusedIndex === 0,
672
678
  });
673
679
  }
674
680
 
@@ -719,6 +725,12 @@ class DropdownCore extends React.Component<Props, State> {
719
725
  itemRef: this.state.itemRefs[focusIndex]
720
726
  ? this.state.itemRefs[focusIndex].ref
721
727
  : null,
728
+ // TODO(WB-1310): Remove the autofocus prop after making
729
+ // the search field sticky.
730
+ // Currently autofocusing on the search field to work
731
+ // around it losing focus on mount when switching between
732
+ // virtualized and non-virtualized dropdown filter results.
733
+ autofocus: this.focusedIndex === 0,
722
734
  },
723
735
  };
724
736
  }
@@ -51,6 +51,13 @@ type Props = {|
51
51
  * Test ID used for e2e testing.
52
52
  */
53
53
  testId?: string,
54
+
55
+ /**
56
+ * Automatically focus on this search field on mount.
57
+ * TODO(WB-1310): Remove the autofocus prop after making
58
+ * the search field sticky in dropdowns.
59
+ */
60
+ autofocus?: boolean,
54
61
  |};
55
62
 
56
63
  type DefaultProps = {|
@@ -71,6 +78,21 @@ export default class SearchTextInput extends React.Component<Props> {
71
78
  },
72
79
  };
73
80
 
81
+ // TODO(WB-1310): Remove `componentDidMount` autofocus on the search field
82
+ // after making the search field sticky.
83
+ componentDidMount() {
84
+ // We need to re-focus on the text input after it mounts because of
85
+ // the case in which the dropdown switches between virtualized and
86
+ // non-virtualized. It can rerender the search field as the user is
87
+ // typing based on the number of search results, which results
88
+ // in losing focus on the field so the user can't type anymore.
89
+ // To work around this issue, this temporary fix auto-focuses on the
90
+ // search field on mount.
91
+ if (this.props.autofocus) {
92
+ this.props.itemRef?.current.focus();
93
+ }
94
+ }
95
+
74
96
  static __IS_SEARCH_TEXT_INPUT__: boolean = true;
75
97
 
76
98
  render(): React.Node {
@@ -79,25 +79,28 @@ const optionItems = new Array(1000)
79
79
  ));
80
80
 
81
81
  type Props = {|
82
+ selectedValue?: ?string,
82
83
  opened: boolean,
83
84
  |};
84
85
 
85
86
  type State = {|
86
- selectedValue: string,
87
+ selectedValue?: ?string,
87
88
  opened: boolean,
88
89
  |};
89
90
 
90
91
  type DefaultProps = {|
92
+ selectedValue: $PropertyType<Props, "selectedValue">,
91
93
  opened: $PropertyType<Props, "opened">,
92
94
  |};
93
95
 
94
96
  class SingleSelectWithFilter extends React.Component<Props, State> {
95
97
  static defaultProps: DefaultProps = {
98
+ selectedValue: "2",
96
99
  opened: false,
97
100
  };
98
101
 
99
102
  state: State = {
100
- selectedValue: "2",
103
+ selectedValue: this.props.selectedValue,
101
104
  opened: this.props.opened,
102
105
  };
103
106
 
@@ -167,6 +170,10 @@ export const WithFilterOpened: StoryComponentType = () => (
167
170
  <SingleSelectWithFilter opened={true} />
168
171
  );
169
172
 
173
+ export const WithFilterOpenedNoValueSelected: StoryComponentType = () => (
174
+ <SingleSelectWithFilter opened={true} selectedValue={null} />
175
+ );
176
+
170
177
  export const DropdownInModal: StoryComponentType = () => {
171
178
  const [value, setValue] = React.useState(null);
172
179
  const [opened, setOpened] = React.useState(true);
@@ -24,6 +24,9 @@ export const filterableDropdownStyle = {
24
24
  export const searchInputStyle = {
25
25
  margin: Spacing.xSmall_8,
26
26
  marginTop: Spacing.xxxSmall_4,
27
+ // Set `minHeight` to "auto" to stop the search field from having
28
+ // a height of 0 and being cut off.
29
+ minHeight: "auto",
27
30
  };
28
31
 
29
32
  // The default item height