@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 +6 -0
- package/dist/es/index.js +14 -3
- package/dist/index.js +35 -3
- package/package.json +1 -1
- package/src/components/__tests__/dropdown-core-virtualized.test.js +0 -9
- package/src/components/dropdown-core.js +12 -0
- 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
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
|
@@ -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
|
|
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:
|
|
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);
|
package/src/util/constants.js
CHANGED
|
@@ -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
|