@khanacademy/wonder-blocks-dropdown 2.3.19

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.
Files changed (41) hide show
  1. package/LICENSE +21 -0
  2. package/dist/es/index.js +3403 -0
  3. package/dist/index.js +3966 -0
  4. package/dist/index.js.flow +2 -0
  5. package/docs.md +12 -0
  6. package/package.json +44 -0
  7. package/src/__tests__/__snapshots__/generated-snapshot.test.js.snap +4054 -0
  8. package/src/__tests__/generated-snapshot.test.js +1612 -0
  9. package/src/__tests__/index.test.js +23 -0
  10. package/src/components/__mocks__/dropdown-core-virtualized.js +40 -0
  11. package/src/components/__tests__/__snapshots__/action-item.test.js.snap +63 -0
  12. package/src/components/__tests__/action-item.test.js +43 -0
  13. package/src/components/__tests__/action-menu.test.js +544 -0
  14. package/src/components/__tests__/dropdown-core-virtualized.test.js +119 -0
  15. package/src/components/__tests__/dropdown-core.test.js +659 -0
  16. package/src/components/__tests__/multi-select.test.js +982 -0
  17. package/src/components/__tests__/search-text-input.test.js +144 -0
  18. package/src/components/__tests__/single-select.test.js +588 -0
  19. package/src/components/action-item.js +270 -0
  20. package/src/components/action-menu-opener-core.js +203 -0
  21. package/src/components/action-menu.js +300 -0
  22. package/src/components/action-menu.md +338 -0
  23. package/src/components/check.js +59 -0
  24. package/src/components/checkbox.js +111 -0
  25. package/src/components/dropdown-core-virtualized-item.js +62 -0
  26. package/src/components/dropdown-core-virtualized.js +246 -0
  27. package/src/components/dropdown-core.js +770 -0
  28. package/src/components/dropdown-opener.js +101 -0
  29. package/src/components/multi-select.js +597 -0
  30. package/src/components/multi-select.md +718 -0
  31. package/src/components/multi-select.stories.js +111 -0
  32. package/src/components/option-item.js +239 -0
  33. package/src/components/search-text-input.js +227 -0
  34. package/src/components/select-opener.js +297 -0
  35. package/src/components/separator-item.js +50 -0
  36. package/src/components/single-select.js +418 -0
  37. package/src/components/single-select.md +520 -0
  38. package/src/components/single-select.stories.js +107 -0
  39. package/src/index.js +20 -0
  40. package/src/util/constants.js +50 -0
  41. package/src/util/types.js +32 -0
@@ -0,0 +1,101 @@
1
+ // @flow
2
+ import * as React from "react";
3
+
4
+ import {ClickableBehavior} from "@khanacademy/wonder-blocks-clickable";
5
+
6
+ import type {AriaProps} from "@khanacademy/wonder-blocks-core";
7
+ import type {
8
+ ChildrenProps,
9
+ ClickableState,
10
+ } from "@khanacademy/wonder-blocks-clickable";
11
+
12
+ import type {OpenerProps} from "../util/types.js";
13
+
14
+ type Props = {|
15
+ ...$Rest<AriaProps, {|"aria-disabled": "true" | "false" | void|}>,
16
+
17
+ /**
18
+ * The child function that returns the anchor the Dropdown will be activated
19
+ * by. This function takes two arguments:
20
+ *
21
+ * - `eventState`: allows the opener element to access pointer event state.
22
+ * - `text`: Passes the menu's text/label defined in the parent component.
23
+ */
24
+ children: (openerProps: OpenerProps) => React.Element<any>,
25
+
26
+ /**
27
+ * Whether the opener is disabled. If disabled, disallows interaction.
28
+ */
29
+ disabled: boolean,
30
+
31
+ /**
32
+ * Callback for when the opener is pressed.
33
+ */
34
+ onClick: (e: SyntheticEvent<>) => mixed,
35
+
36
+ /**
37
+ * Test ID used for e2e testing.
38
+ */
39
+ testId?: string,
40
+
41
+ /**
42
+ * Text for the opener that can be passed to the child as an argument.
43
+ */
44
+ text: string,
45
+ |};
46
+
47
+ type DefaultProps = {|
48
+ disabled: $PropertyType<Props, "disabled">,
49
+ |};
50
+
51
+ class DropdownOpener extends React.Component<Props> {
52
+ static defaultProps: DefaultProps = {
53
+ disabled: false,
54
+ };
55
+
56
+ getTestIdFromProps: (childrenProps: any) => string = (childrenProps) => {
57
+ return childrenProps.testId || childrenProps["data-test-id"];
58
+ };
59
+
60
+ renderAnchorChildren(
61
+ eventState: ClickableState,
62
+ clickableChildrenProps: ChildrenProps,
63
+ ): React.Node {
64
+ const {disabled, testId, text} = this.props;
65
+ const renderedChildren = this.props.children({...eventState, text});
66
+ const childrenProps = renderedChildren.props;
67
+ const childrenTestId = this.getTestIdFromProps(childrenProps);
68
+
69
+ return React.cloneElement(renderedChildren, {
70
+ ...clickableChildrenProps,
71
+ disabled,
72
+ onClick: childrenProps.onClick
73
+ ? (e: SyntheticMouseEvent<>) => {
74
+ // This is done to avoid overriding a
75
+ // custom onClick handler inside the
76
+ // children node
77
+ childrenProps.onClick(e);
78
+ clickableChildrenProps.onClick(e);
79
+ }
80
+ : clickableChildrenProps.onClick,
81
+ // try to get the testId from the child element
82
+ // If it's not set, try to fallback to the parent's testId
83
+ "data-test-id": childrenTestId || testId,
84
+ });
85
+ }
86
+
87
+ render(): React.Node {
88
+ return (
89
+ <ClickableBehavior
90
+ onClick={this.props.onClick}
91
+ disabled={this.props.disabled}
92
+ >
93
+ {(eventState, handlers) =>
94
+ this.renderAnchorChildren(eventState, handlers)
95
+ }
96
+ </ClickableBehavior>
97
+ );
98
+ }
99
+ }
100
+
101
+ export default DropdownOpener;
@@ -0,0 +1,597 @@
1
+ // @flow
2
+
3
+ import * as React from "react";
4
+ import ReactDOM from "react-dom";
5
+
6
+ import type {AriaProps, StyleType} from "@khanacademy/wonder-blocks-core";
7
+
8
+ import ActionItem from "./action-item.js";
9
+ import DropdownCore from "./dropdown-core.js";
10
+ import DropdownOpener from "./dropdown-opener.js";
11
+ import SearchTextInput from "./search-text-input.js";
12
+ import SelectOpener from "./select-opener.js";
13
+ import SeparatorItem from "./separator-item.js";
14
+ import {
15
+ defaultLabels,
16
+ selectDropdownStyle,
17
+ filterableDropdownStyle,
18
+ } from "../util/constants.js";
19
+
20
+ import typeof OptionItem from "./option-item.js";
21
+ import type {DropdownItem, OpenerProps} from "../util/types.js";
22
+
23
+ export type Labels = {|
24
+ /**
25
+ * Label for describing the dismiss icon on the search filter.
26
+ */
27
+ clearSearch: string,
28
+
29
+ /**
30
+ * Label for the search placeholder.
31
+ */
32
+ filter: string,
33
+
34
+ /**
35
+ * Label for when the filter returns no results.
36
+ */
37
+ noResults: string,
38
+
39
+ /**
40
+ * Label for the "select all" shortcut option.
41
+ */
42
+ selectAllLabel: (numOptions: number) => string,
43
+
44
+ /**
45
+ * Label for the "select none" shortcut option
46
+ */
47
+
48
+ selectNoneLabel: string,
49
+
50
+ /**
51
+ * Label for the opening component when there are no items selected.
52
+ */
53
+ noneSelected: string,
54
+
55
+ /**
56
+ * Label for the opening component when there are some items selected.
57
+ */
58
+ someSelected: (numOptions: number) => string,
59
+
60
+ /**
61
+ * Label for the opening component when all the items have been selected.
62
+ */
63
+ allSelected: string,
64
+ |};
65
+
66
+ type DefaultProps = {|
67
+ /**
68
+ * Whether this dropdown should be left-aligned or right-aligned with the
69
+ * opener component. Defaults to left-aligned.
70
+ */
71
+ alignment: "left" | "right",
72
+
73
+ /**
74
+ * Whether this component is disabled. A disabled dropdown may not be opened
75
+ * and does not support interaction. Defaults to false.
76
+ */
77
+ disabled: boolean,
78
+
79
+ /**
80
+ * Whether to display the "light" version of this component instead, for
81
+ * use when the component is used on a dark background.
82
+ */
83
+ light: boolean,
84
+
85
+ /**
86
+ * The values of the items that are currently selected.
87
+ */
88
+ selectedValues: Array<string>,
89
+
90
+ /**
91
+ * Whether to display shortcuts for Select All and Select None.
92
+ */
93
+ shortcuts: boolean,
94
+ |};
95
+
96
+ type Props = {|
97
+ ...AriaProps,
98
+
99
+ ...DefaultProps,
100
+
101
+ /**
102
+ * The items in this select.
103
+ */
104
+ children?: Array<?(React.Element<OptionItem> | false)>,
105
+
106
+ /**
107
+ * Optional styling to add to the dropdown wrapper.
108
+ */
109
+ dropdownStyle?: StyleType,
110
+
111
+ /**
112
+ * Unique identifier attached to the field control. If used, we need to
113
+ * guarantee that the ID is unique within everything rendered on a page.
114
+ * Used to match `<label>` with `<button>` elements for screenreaders.
115
+ */
116
+ id?: string,
117
+
118
+ /**
119
+ * When this is true, the menu text shows either "All items" or the value
120
+ * set in `props.labels.allSelected` when no item is selected.
121
+ */
122
+ implicitAllEnabled?: boolean,
123
+
124
+ /**
125
+ * When this is true, the dropdown body shows a search text input at the
126
+ * top. The items will be filtered by the input.
127
+ * Selected items will be moved to the top when the dropdown is re-opened.
128
+ */
129
+ isFilterable?: boolean,
130
+
131
+ /**
132
+ * The object containing the custom labels used inside this component.
133
+ */
134
+ labels?: Labels,
135
+
136
+ /**
137
+ * Callback for when the selection changes. Parameter is an updated array of
138
+ * the values that are now selected.
139
+ */
140
+ onChange: (selectedValues: Array<string>) => mixed,
141
+
142
+ /**
143
+ * In controlled mode, use this prop in case the parent needs to be notified
144
+ * when the menu opens/closes.
145
+ */
146
+ onToggle?: (opened: boolean) => mixed,
147
+
148
+ /**
149
+ * Can be used to override the state of the ActionMenu by parent elements
150
+ */
151
+ opened?: boolean,
152
+
153
+ /**
154
+ * The child function that returns the anchor the MultiSelect will be
155
+ * activated by. This function takes eventState, which allows the opener
156
+ * element to access pointer event state.
157
+ */
158
+ opener?: (openerProps: OpenerProps) => React.Element<any>,
159
+
160
+ /**
161
+ * Optional styling to add to the opener component wrapper.
162
+ */
163
+ style?: StyleType,
164
+
165
+ /**
166
+ * Adds CSS classes to the opener component wrapper.
167
+ */
168
+ className?: string,
169
+
170
+ /**
171
+ * Test ID used for e2e testing.
172
+ */
173
+ testId?: string,
174
+ |};
175
+
176
+ type State = {|
177
+ /**
178
+ * Whether or not the dropdown is open.
179
+ */
180
+ open: boolean,
181
+
182
+ /**
183
+ * The text input to filter the items by their label. Defaults to an empty
184
+ * string.
185
+ */
186
+ searchText: string,
187
+
188
+ /**
189
+ * The selected values that are set when the dropdown is opened. We use
190
+ * this to move the selected items to the top when the dropdown is
191
+ * re-opened.
192
+ */
193
+ lastSelectedValues: Array<string>,
194
+
195
+ /**
196
+ * The object containing the custom labels used inside this component.
197
+ */
198
+ labels: Labels,
199
+
200
+ /**
201
+ * The DOM reference to the opener element. This is mainly used to set focus
202
+ * to this element, and also to pass the reference to Popper.js.
203
+ */
204
+ openerElement: ?HTMLElement,
205
+ |};
206
+
207
+ /**
208
+ * A dropdown that consists of multiple selection items. This select allows
209
+ * multiple options to be selected. Clients are responsible for keeping track
210
+ * of the selected items.
211
+ *
212
+ * The multi select stays open until closed by the user. The onChange callback
213
+ * happens every time there is a change in the selection of the items.
214
+ */
215
+ export default class MultiSelect extends React.Component<Props, State> {
216
+ labels: Labels;
217
+
218
+ static defaultProps: DefaultProps = {
219
+ alignment: "left",
220
+ disabled: false,
221
+ light: false,
222
+ shortcuts: false,
223
+ selectedValues: [],
224
+ };
225
+
226
+ constructor(props: Props) {
227
+ super(props);
228
+
229
+ this.state = {
230
+ open: false,
231
+ searchText: "",
232
+ lastSelectedValues: [],
233
+ // merge custom labels with the default ones
234
+ labels: {...defaultLabels, ...props.labels},
235
+ openerElement: null,
236
+ };
237
+ // merge custom labels with the default ones
238
+ this.labels = {...defaultLabels, ...props.labels};
239
+ }
240
+
241
+ /**
242
+ * Used to sync the `opened` state when this component acts as a controlled
243
+ * component
244
+ */
245
+ static getDerivedStateFromProps(
246
+ props: Props,
247
+ state: State,
248
+ ): ?Partial<State> {
249
+ return {
250
+ open: typeof props.opened === "boolean" ? props.opened : state.open,
251
+ };
252
+ }
253
+
254
+ componentDidUpdate(prevProps: Props) {
255
+ if (this.props.labels !== prevProps.labels) {
256
+ // eslint-disable-next-line react/no-did-update-set-state
257
+ this.setState({
258
+ labels: {...this.state.labels, ...this.props.labels},
259
+ });
260
+ }
261
+ }
262
+
263
+ handleOpenChanged: (opened: boolean) => void = (opened) => {
264
+ this.setState({
265
+ open: opened,
266
+ searchText: "",
267
+ lastSelectedValues: this.props.selectedValues,
268
+ });
269
+
270
+ if (this.props.onToggle) {
271
+ this.props.onToggle(opened);
272
+ }
273
+ };
274
+
275
+ handleToggle: (selectedValue: string) => void = (selectedValue) => {
276
+ const {onChange, selectedValues} = this.props;
277
+
278
+ if (selectedValues.includes(selectedValue)) {
279
+ const index = selectedValues.indexOf(selectedValue);
280
+ const updatedSelection = [
281
+ ...selectedValues.slice(0, index),
282
+ ...selectedValues.slice(index + 1),
283
+ ];
284
+ onChange(updatedSelection);
285
+ } else {
286
+ // Item was newly selected
287
+ onChange([...selectedValues, selectedValue]);
288
+ }
289
+ };
290
+
291
+ handleSelectAll: () => void = () => {
292
+ const {children, onChange} = this.props;
293
+ const selected = React.Children.toArray(children)
294
+ .filter(Boolean)
295
+ .map((option) => option.props.value);
296
+ onChange(selected);
297
+ };
298
+
299
+ handleSelectNone: () => void = () => {
300
+ const {onChange} = this.props;
301
+ onChange([]);
302
+ };
303
+
304
+ getMenuText(children: Array<React.Element<OptionItem>>): string {
305
+ const {implicitAllEnabled, selectedValues} = this.props;
306
+ const {noneSelected, someSelected, allSelected} = this.state.labels;
307
+
308
+ // When implicit all enabled, use `labels.allSelected` when no selection
309
+ // otherwise, use the `labels.noneSelected` value
310
+ const noSelectionText = implicitAllEnabled ? allSelected : noneSelected;
311
+
312
+ switch (selectedValues.length) {
313
+ case 0:
314
+ return noSelectionText;
315
+ case 1:
316
+ // If there is one item selected, we display its label. If for
317
+ // some reason we can't find the selected item, we use the
318
+ // display text for the case where nothing is selected.
319
+ const selectedItem = children.find(
320
+ (option) => option.props.value === selectedValues[0],
321
+ );
322
+ return selectedItem
323
+ ? selectedItem.props.label
324
+ : noSelectionText;
325
+ case children.length:
326
+ return allSelected;
327
+ default:
328
+ return someSelected(selectedValues.length);
329
+ }
330
+ }
331
+
332
+ getSearchField(): Array<DropdownItem> {
333
+ if (!this.props.isFilterable) {
334
+ return [];
335
+ }
336
+
337
+ const {clearSearch, filter} = this.state.labels;
338
+
339
+ return [
340
+ {
341
+ component: (
342
+ <SearchTextInput
343
+ key="search-text-input"
344
+ onChange={this.handleSearchTextChanged}
345
+ searchText={this.state.searchText}
346
+ labels={{
347
+ clearSearch,
348
+ filter,
349
+ }}
350
+ />
351
+ ),
352
+ focusable: true,
353
+ populatedProps: {},
354
+ },
355
+ ];
356
+ }
357
+
358
+ getShortcuts(numOptions: number): Array<DropdownItem> {
359
+ const {selectedValues, shortcuts} = this.props;
360
+ const {selectAllLabel, selectNoneLabel} = this.state.labels;
361
+
362
+ // When there's search text input to filter, shortcuts should be hidden
363
+ if (shortcuts && !this.state.searchText) {
364
+ const selectAllDisabled = numOptions === selectedValues.length;
365
+ const selectAll = {
366
+ component: (
367
+ <ActionItem
368
+ disabled={selectAllDisabled}
369
+ label={selectAllLabel(numOptions)}
370
+ indent={true}
371
+ onClick={this.handleSelectAll}
372
+ />
373
+ ),
374
+ focusable: !selectAllDisabled,
375
+ populatedProps: {},
376
+ };
377
+
378
+ const selectNoneDisabled = selectedValues.length === 0;
379
+ const selectNone = {
380
+ component: (
381
+ <ActionItem
382
+ disabled={selectNoneDisabled}
383
+ label={selectNoneLabel}
384
+ indent={true}
385
+ onClick={this.handleSelectNone}
386
+ />
387
+ ),
388
+ focusable: !selectNoneDisabled,
389
+ populatedProps: {},
390
+ };
391
+
392
+ const separator = {
393
+ component: <SeparatorItem key="shortcuts-separator" />,
394
+ focusable: false,
395
+ populatedProps: {},
396
+ };
397
+
398
+ return [selectAll, selectNone, separator];
399
+ } else {
400
+ return [];
401
+ }
402
+ }
403
+
404
+ getMenuItems(
405
+ children: Array<React.Element<OptionItem>>,
406
+ ): Array<DropdownItem> {
407
+ const {isFilterable} = this.props;
408
+ // If it's not filterable, no need to do any extra besides mapping the
409
+ // option items to dropdown items.
410
+ if (!isFilterable) {
411
+ return children.map(this.mapOptionItemToDropdownItem);
412
+ }
413
+
414
+ const {searchText, lastSelectedValues} = this.state;
415
+
416
+ const lowercasedSearchText = searchText.toLowerCase();
417
+
418
+ // Filter the children with the searchText if any.
419
+ const filteredChildren = children.filter(
420
+ ({props}) =>
421
+ !searchText ||
422
+ props.label.toLowerCase().indexOf(lowercasedSearchText) > -1,
423
+ );
424
+
425
+ const lastSelectedChildren = [];
426
+ const restOfTheChildren = [];
427
+ for (const child of filteredChildren) {
428
+ if (lastSelectedValues.includes(child.props.value)) {
429
+ lastSelectedChildren.push(child);
430
+ } else {
431
+ restOfTheChildren.push(child);
432
+ }
433
+ }
434
+
435
+ const lastSelectedItems = lastSelectedChildren.map(
436
+ this.mapOptionItemToDropdownItem,
437
+ );
438
+
439
+ // We want to add SeparatorItem in between last selected items and the
440
+ // rest of the items only when both of them exists.
441
+ if (lastSelectedChildren.length && restOfTheChildren.length) {
442
+ lastSelectedItems.push({
443
+ component: <SeparatorItem key="selected-separator" />,
444
+ focusable: false,
445
+ populatedProps: {},
446
+ });
447
+ }
448
+
449
+ return [
450
+ ...lastSelectedItems,
451
+ ...restOfTheChildren.map(this.mapOptionItemToDropdownItem),
452
+ ];
453
+ }
454
+
455
+ mapOptionItemToDropdownItem: (
456
+ option: React.Element<OptionItem>,
457
+ ) => DropdownItem = (option: React.Element<OptionItem>): DropdownItem => {
458
+ const {selectedValues} = this.props;
459
+ const {disabled, value} = option.props;
460
+ return {
461
+ component: option,
462
+ focusable: !disabled,
463
+ populatedProps: {
464
+ onToggle: this.handleToggle,
465
+ selected: selectedValues.includes(value),
466
+ variant: "checkbox",
467
+ },
468
+ };
469
+ };
470
+
471
+ handleOpenerRef: (node: any) => void = (node: any) => {
472
+ const openerElement = ((ReactDOM.findDOMNode(node): any): HTMLElement);
473
+ this.setState({openerElement});
474
+ };
475
+
476
+ handleSearchTextChanged: (searchText: string) => void = (
477
+ searchText: string,
478
+ ) => {
479
+ this.setState({searchText});
480
+ };
481
+
482
+ handleClick: (e: SyntheticEvent<>) => void = (e: SyntheticEvent<>) => {
483
+ this.handleOpenChanged(!this.state.open);
484
+ };
485
+
486
+ renderOpener(
487
+ allChildren: Array<React.Element<OptionItem>>,
488
+ ):
489
+ | React.Element<typeof DropdownOpener>
490
+ | React.Element<typeof SelectOpener> {
491
+ const {
492
+ disabled,
493
+ id,
494
+ light,
495
+ opener,
496
+ testId,
497
+ // the following props are being included here to avoid
498
+ // passing them down to the opener as part of sharedProps
499
+ /* eslint-disable no-unused-vars */
500
+ alignment,
501
+ dropdownStyle,
502
+ implicitAllEnabled,
503
+ isFilterable,
504
+ labels,
505
+ onChange,
506
+ onToggle,
507
+ opened,
508
+ selectedValues,
509
+ shortcuts,
510
+ style,
511
+ className,
512
+ /* eslint-enable no-unused-vars */
513
+ ...sharedProps
514
+ } = this.props;
515
+ const {noneSelected} = this.state.labels;
516
+
517
+ const menuText = this.getMenuText(allChildren);
518
+ const numOptions = allChildren.length;
519
+
520
+ const dropdownOpener = opener ? (
521
+ <DropdownOpener
522
+ onClick={this.handleClick}
523
+ disabled={numOptions === 0 || disabled}
524
+ ref={this.handleOpenerRef}
525
+ text={menuText}
526
+ >
527
+ {opener}
528
+ </DropdownOpener>
529
+ ) : (
530
+ <SelectOpener
531
+ {...sharedProps}
532
+ disabled={numOptions === 0 || disabled}
533
+ id={id}
534
+ isPlaceholder={menuText === noneSelected}
535
+ light={light}
536
+ onOpenChanged={this.handleOpenChanged}
537
+ open={this.state.open}
538
+ ref={this.handleOpenerRef}
539
+ testId={testId}
540
+ >
541
+ {menuText}
542
+ </SelectOpener>
543
+ );
544
+
545
+ return dropdownOpener;
546
+ }
547
+
548
+ render(): React.Node {
549
+ const {
550
+ alignment,
551
+ light,
552
+ style,
553
+ className,
554
+ dropdownStyle,
555
+ children,
556
+ isFilterable,
557
+ } = this.props;
558
+ const {open, searchText} = this.state;
559
+ const {noResults} = this.state.labels;
560
+
561
+ const allChildren = React.Children.toArray(children).filter(Boolean);
562
+ const numOptions = allChildren.length;
563
+ const filteredItems = this.getMenuItems(allChildren);
564
+ const opener = this.renderOpener(allChildren);
565
+
566
+ return (
567
+ <DropdownCore
568
+ role="listbox"
569
+ alignment={alignment}
570
+ dropdownStyle={[
571
+ isFilterable && filterableDropdownStyle,
572
+ selectDropdownStyle,
573
+ dropdownStyle,
574
+ ]}
575
+ items={[
576
+ ...this.getSearchField(),
577
+ ...this.getShortcuts(numOptions),
578
+ ...filteredItems,
579
+ ]}
580
+ light={light}
581
+ onOpenChanged={this.handleOpenChanged}
582
+ open={open}
583
+ opener={opener}
584
+ openerElement={this.state.openerElement}
585
+ style={style}
586
+ className={className}
587
+ onSearchTextChanged={
588
+ isFilterable ? this.handleSearchTextChanged : null
589
+ }
590
+ searchText={isFilterable ? searchText : ""}
591
+ labels={{
592
+ noResults,
593
+ }}
594
+ />
595
+ );
596
+ }
597
+ }