@khanacademy/wonder-blocks-dropdown 4.0.0 → 5.0.0

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 (34) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/components/check.d.ts +0 -6
  3. package/dist/components/checkbox.d.ts +0 -6
  4. package/dist/components/dropdown-opener.d.ts +2 -2
  5. package/dist/components/option-item.d.ts +34 -2
  6. package/dist/components/select-opener.d.ts +2 -1
  7. package/dist/es/index.js +191 -116
  8. package/dist/index.js +189 -114
  9. package/dist/util/dropdown-menu-styles.d.ts +0 -10
  10. package/dist/util/helpers.d.ts +8 -0
  11. package/dist/util/popper-max-height-modifier.d.ts +17 -0
  12. package/dist/util/types.d.ts +11 -1
  13. package/package.json +10 -10
  14. package/src/components/__tests__/dropdown-core.test.tsx +1 -1
  15. package/src/components/__tests__/dropdown-popper.test.tsx +33 -3
  16. package/src/components/__tests__/multi-select.test.tsx +42 -1
  17. package/src/components/__tests__/option-item.test.tsx +86 -0
  18. package/src/components/__tests__/single-select.test.tsx +15 -9
  19. package/src/components/check.tsx +6 -19
  20. package/src/components/checkbox.tsx +10 -30
  21. package/src/components/dropdown-core.tsx +21 -17
  22. package/src/components/dropdown-opener.tsx +2 -2
  23. package/src/components/dropdown-popper.tsx +2 -0
  24. package/src/components/multi-select.tsx +17 -4
  25. package/src/components/option-item.tsx +182 -69
  26. package/src/components/select-opener.tsx +2 -1
  27. package/src/components/single-select.tsx +5 -2
  28. package/src/util/__tests__/{helpers.test.ts → helpers.test.tsx} +50 -1
  29. package/src/util/constants.ts +1 -1
  30. package/src/util/dropdown-menu-styles.ts +0 -26
  31. package/src/util/helpers.ts +27 -0
  32. package/src/util/popper-max-height-modifier.ts +57 -0
  33. package/src/util/types.ts +13 -1
  34. package/tsconfig-build.tsbuildinfo +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # @khanacademy/wonder-blocks-dropdown
2
2
 
3
+ ## 5.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - 56a896c6: Allow custom OptionItem elements (internally using cell components)
8
+
9
+ ### Patch Changes
10
+
11
+ - 70e846cb: Allow changing the popper's height to accomodate for the viewport size
12
+ - Updated dependencies [56a896c6]
13
+ - Updated dependencies [23ab9f8c]
14
+ - Updated dependencies [6df21f71]
15
+ - @khanacademy/wonder-blocks-cell@3.2.0
16
+ - @khanacademy/wonder-blocks-icon@4.0.1
17
+ - @khanacademy/wonder-blocks-core@6.3.1
18
+ - @khanacademy/wonder-blocks-search-field@2.1.26
19
+ - @khanacademy/wonder-blocks-clickable@4.0.12
20
+ - @khanacademy/wonder-blocks-layout@2.0.25
21
+ - @khanacademy/wonder-blocks-modal@4.0.39
22
+ - @khanacademy/wonder-blocks-typography@2.1.10
23
+
3
24
  ## 4.0.0
4
25
 
5
26
  ### Major Changes
@@ -8,12 +8,6 @@ type CheckProps = {
8
8
  disabled: boolean;
9
9
  /** Whether option item is selected. */
10
10
  selected: boolean;
11
- /** Whether option item is pressed. */
12
- pressed: boolean;
13
- /** Whether option item is hovered. */
14
- hovered: boolean;
15
- /** Whether option item is focused. */
16
- focused: boolean;
17
11
  };
18
12
  /**
19
13
  * The check component used by OptionItem.
@@ -8,12 +8,6 @@ type CheckProps = {
8
8
  disabled: boolean;
9
9
  /** Whether option item is selected. */
10
10
  selected: boolean;
11
- /** Whether option item is pressed. */
12
- pressed: boolean;
13
- /** Whether option item is hovered. */
14
- hovered: boolean;
15
- /** Whether option item is focused. */
16
- focused: boolean;
17
11
  };
18
12
  /**
19
13
  * The checkbox component used by OptionItem.
@@ -1,7 +1,7 @@
1
1
  import * as React from "react";
2
2
  import type { AriaProps } from "@khanacademy/wonder-blocks-core";
3
3
  import type { ChildrenProps, ClickableState } from "@khanacademy/wonder-blocks-clickable";
4
- import type { OpenerProps } from "../util/types";
4
+ import type { OpenerProps, OptionLabel } from "../util/types";
5
5
  type Props = Partial<Omit<AriaProps, "aria-disabled">> & {
6
6
  /**
7
7
  * The child function that returns the anchor the Dropdown will be activated
@@ -26,7 +26,7 @@ type Props = Partial<Omit<AriaProps, "aria-disabled">> & {
26
26
  /**
27
27
  * Text for the opener that can be passed to the child as an argument.
28
28
  */
29
- text: string;
29
+ text: OptionLabel;
30
30
  };
31
31
  type DefaultProps = {
32
32
  disabled: Props["disabled"];
@@ -1,12 +1,19 @@
1
1
  import * as React from "react";
2
- import type { AriaProps, StyleType } from "@khanacademy/wonder-blocks-core";
2
+ import { AriaProps, StyleType } from "@khanacademy/wonder-blocks-core";
3
3
  import Check from "./check";
4
4
  import Checkbox from "./checkbox";
5
+ import { CellProps, OptionLabel } from "../util/types";
5
6
  type OptionProps = AriaProps & {
6
7
  /**
7
8
  * Display text of the option item.
8
9
  */
9
- label: string;
10
+ label: OptionLabel;
11
+ /**
12
+ * Optional text to use as the label. If not provided, label will be used.
13
+ * This is useful for cases where the label is a complex component and you
14
+ * want to display a simpler string in the menu.
15
+ */
16
+ labelAsText?: string;
10
17
  /**
11
18
  * Value of the item, used as a key of sorts for the parent to manage its
12
19
  * items, because label/display text may be identical for some selects. This
@@ -52,9 +59,34 @@ type OptionProps = AriaProps & {
52
59
  * @ignore
53
60
  */
54
61
  style?: StyleType;
62
+ /**
63
+ * Inherited from WB Cell.
64
+ */
65
+ /**
66
+ * Adds a horizontal rule at the bottom of the cell that can be used to
67
+ * separate items within ActionMenu instances. Defaults to `none`.
68
+ */
69
+ horizontalRule: CellProps["horizontalRule"];
70
+ /**
71
+ * Optional left accessory to display in the `OptionItem` element.
72
+ */
73
+ leftAccessory?: CellProps["leftAccessory"];
74
+ /**
75
+ * Optional right accessory to display in the `OptionItem` element.
76
+ */
77
+ rightAccessory?: CellProps["rightAccessory"];
78
+ /**
79
+ * Optional subtitle to display before the label.
80
+ */
81
+ subtitle1?: CellProps["subtitle1"];
82
+ /**
83
+ * Optional subtitle to display after the label.
84
+ */
85
+ subtitle2?: CellProps["subtitle2"];
55
86
  };
56
87
  type DefaultProps = {
57
88
  disabled: OptionProps["disabled"];
89
+ horizontalRule: OptionProps["horizontalRule"];
58
90
  onToggle: OptionProps["onToggle"];
59
91
  role: OptionProps["role"];
60
92
  selected: OptionProps["selected"];
@@ -1,10 +1,11 @@
1
1
  import * as React from "react";
2
2
  import type { AriaProps } from "@khanacademy/wonder-blocks-core";
3
+ import { OptionLabel } from "../util/types";
3
4
  type SelectOpenerProps = AriaProps & {
4
5
  /**
5
6
  * Display text in the SelectOpener.
6
7
  */
7
- children: string;
8
+ children: OptionLabel;
8
9
  /**
9
10
  * Whether the SelectOpener is disabled. If disabled, disallows interaction.
10
11
  * Default false.
package/dist/es/index.js CHANGED
@@ -1,21 +1,22 @@
1
1
  import * as React from 'react';
2
2
  import { StyleSheet } from 'aphrodite';
3
- import { CompactCell } from '@khanacademy/wonder-blocks-cell';
3
+ import { CompactCell, DetailCell } from '@khanacademy/wonder-blocks-cell';
4
4
  import Color, { mix, fade, SemanticColor } from '@khanacademy/wonder-blocks-color';
5
5
  import Spacing from '@khanacademy/wonder-blocks-spacing';
6
- import { LabelMedium, LabelLarge } from '@khanacademy/wonder-blocks-typography';
6
+ import { LabelMedium, LabelSmall, LabelLarge } from '@khanacademy/wonder-blocks-typography';
7
7
  import { tokens } from '@khanacademy/wonder-blocks-theming';
8
8
  import { View, addStyle } from '@khanacademy/wonder-blocks-core';
9
- import { getClickableBehavior, ClickableBehavior } from '@khanacademy/wonder-blocks-clickable';
9
+ import { Strut } from '@khanacademy/wonder-blocks-layout';
10
10
  import { PhosphorIcon } from '@khanacademy/wonder-blocks-icon';
11
11
  import checkIcon from '@phosphor-icons/core/bold/check-bold.svg';
12
12
  import * as ReactDOM from 'react-dom';
13
+ import { ClickableBehavior, getClickableBehavior } from '@khanacademy/wonder-blocks-clickable';
13
14
  import SearchField from '@khanacademy/wonder-blocks-search-field';
14
15
  import { withActionScheduler } from '@khanacademy/wonder-blocks-timing';
15
16
  import { VariableSizeList } from 'react-window';
16
17
  import { Popper } from 'react-popper';
17
18
  import { maybeGetPortalMountedModalHostElement } from '@khanacademy/wonder-blocks-modal';
18
- import { Strut } from '@khanacademy/wonder-blocks-layout';
19
+ import { detectOverflow } from '@popperjs/core';
19
20
  import caretDownIcon from '@phosphor-icons/core/bold/caret-down-bold.svg';
20
21
  import { __RouterContext } from 'react-router';
21
22
 
@@ -59,15 +60,15 @@ const defaultLabels = {
59
60
  selectNoneLabel: "Select none",
60
61
  selectAllLabel: numOptions => `Select all (${numOptions})`,
61
62
  noneSelected: "0 items",
62
- someSelected: numSelectedValues => `${numSelectedValues} items`,
63
+ someSelected: numSelectedValues => numSelectedValues === 1 ? "1 item" : `${numSelectedValues} items`,
63
64
  allSelected: "All items"
64
65
  };
65
66
 
66
67
  const {
67
- blue: blue$2,
68
- white: white$3,
69
- offBlack: offBlack$2,
70
- offBlack32: offBlack32$3
68
+ blue: blue$1,
69
+ white: white$1,
70
+ offBlack: offBlack$1,
71
+ offBlack32: offBlack32$1
71
72
  } = Color;
72
73
  class ActionItem extends React.Component {
73
74
  static isClassOf(instance) {
@@ -130,18 +131,18 @@ const styles$8 = StyleSheet.create({
130
131
  outlineOffset: -Spacing.xxxxSmall_2
131
132
  },
132
133
  [":hover[aria-disabled=false]"]: {
133
- color: white$3,
134
- background: blue$2
134
+ color: white$1,
135
+ background: blue$1
135
136
  },
136
137
  ["@media not (hover: hover)"]: {
137
138
  [":hover[aria-disabled=false]"]: {
138
- color: white$3,
139
- background: offBlack$2
139
+ color: white$1,
140
+ background: offBlack$1
140
141
  }
141
142
  },
142
143
  [":active[aria-disabled=false]"]: {
143
- color: mix(fade(blue$2, 0.32), white$3),
144
- background: mix(offBlack32$3, blue$2)
144
+ color: mix(fade(blue$1, 0.32), white$1),
145
+ background: mix(offBlack32$1, blue$1)
145
146
  }
146
147
  },
147
148
  shared: {
@@ -170,30 +171,22 @@ function _objectWithoutPropertiesLoose(source, excluded) {
170
171
  return target;
171
172
  }
172
173
 
173
- const {
174
- offBlack: offBlack$1,
175
- offBlack32: offBlack32$2,
176
- white: white$2
177
- } = Color;
178
174
  const Check = function Check(props) {
179
175
  const {
180
- disabled,
181
- selected,
182
- pressed,
183
- hovered,
184
- focused
176
+ selected
185
177
  } = props;
186
178
  return React.createElement(PhosphorIcon, {
187
179
  icon: checkIcon,
188
180
  size: "small",
189
- color: disabled ? offBlack32$2 : pressed || hovered || focused ? white$2 : offBlack$1,
190
181
  style: [styles$7.bounds, !selected && styles$7.hide]
191
182
  });
192
183
  };
193
184
  const styles$7 = StyleSheet.create({
194
185
  bounds: {
195
- minHeight: 16,
196
- minWidth: 16
186
+ alignSelf: "center",
187
+ height: Spacing.medium_16,
188
+ minHeight: Spacing.medium_16,
189
+ minWidth: Spacing.medium_16
197
190
  },
198
191
  hide: {
199
192
  visibility: "hidden"
@@ -201,33 +194,22 @@ const styles$7 = StyleSheet.create({
201
194
  });
202
195
 
203
196
  const {
204
- blue: blue$1,
205
- white: white$1,
206
197
  offBlack16,
207
- offBlack32: offBlack32$1,
208
198
  offBlack50,
209
199
  offWhite
210
200
  } = Color;
211
201
  const Checkbox = function Checkbox(props) {
212
202
  const {
213
203
  disabled,
214
- selected,
215
- pressed,
216
- hovered,
217
- focused
204
+ selected
218
205
  } = props;
219
- const activeBlue = mix(offBlack32$1, blue$1);
220
- const clickInteraction = pressed || hovered || focused;
221
- const bgColor = disabled ? offWhite : selected && !clickInteraction ? blue$1 : white$1;
222
- const checkColor = disabled ? offBlack32$1 : clickInteraction ? pressed ? activeBlue : blue$1 : white$1;
223
206
  return React.createElement(View, {
224
- style: [styles$6.checkbox, (clickInteraction || selected && !disabled) && styles$6.noBorder, disabled && styles$6.disabledCheckbox, {
225
- backgroundColor: bgColor
226
- }]
207
+ className: "checkbox",
208
+ style: [styles$6.checkbox, selected && !disabled && styles$6.noBorder, disabled && styles$6.disabledCheckbox]
227
209
  }, selected && React.createElement(PhosphorIcon, {
228
210
  icon: checkIcon,
229
211
  size: "small",
230
- color: checkColor,
212
+ className: "check",
231
213
  style: [{
232
214
  width: Spacing.small_12,
233
215
  height: Spacing.small_12,
@@ -237,8 +219,10 @@ const Checkbox = function Checkbox(props) {
237
219
  };
238
220
  const styles$6 = StyleSheet.create({
239
221
  checkbox: {
240
- minHeight: 16,
241
- minWidth: 16,
222
+ alignSelf: "center",
223
+ minHeight: Spacing.medium_16,
224
+ minWidth: Spacing.medium_16,
225
+ height: Spacing.medium_16,
242
226
  borderRadius: 3,
243
227
  borderWidth: 1,
244
228
  borderStyle: "solid",
@@ -258,7 +242,7 @@ const styles$6 = StyleSheet.create({
258
242
  }
259
243
  });
260
244
 
261
- const _excluded$5 = ["disabled", "label", "role", "selected", "testId", "style", "value", "onClick", "onToggle", "variant"];
245
+ const _excluded$5 = ["disabled", "label", "role", "selected", "testId", "style", "leftAccessory", "horizontalRule", "rightAccessory", "subtitle1", "subtitle2", "value", "onClick", "onToggle", "variant"];
262
246
  class OptionItem extends React.Component {
263
247
  constructor(...args) {
264
248
  super(...args);
@@ -292,42 +276,54 @@ class OptionItem extends React.Component {
292
276
  role,
293
277
  selected,
294
278
  testId,
295
- style
279
+ style,
280
+ leftAccessory,
281
+ horizontalRule,
282
+ rightAccessory,
283
+ subtitle1,
284
+ subtitle2
296
285
  } = _this$props,
297
286
  sharedProps = _objectWithoutPropertiesLoose(_this$props, _excluded$5);
298
- const ClickableBehavior = getClickableBehavior();
299
287
  const CheckComponent = this.getCheckComponent();
300
- return React.createElement(ClickableBehavior, {
288
+ const defaultStyle = [styles$5.item, style];
289
+ return React.createElement(DetailCell, _extends({
301
290
  disabled: disabled,
302
- onClick: this.handleClick,
291
+ horizontalRule: horizontalRule,
292
+ rootStyle: defaultStyle,
293
+ style: styles$5.itemContainer,
294
+ "aria-selected": selected ? "true" : "false",
303
295
  role: role,
304
- tabIndex: 0
305
- }, (state, childrenProps) => {
306
- const {
307
- pressed,
308
- hovered,
309
- focused
310
- } = state;
311
- const defaultStyle = [styles$5.itemContainer, pressed ? styles$5.active : (hovered || focused) && styles$5.focus, disabled && styles$5.disabled, style];
312
- return React.createElement(View, _extends({}, sharedProps, {
313
- testId: testId,
314
- style: defaultStyle,
315
- "aria-selected": selected ? "true" : "false",
316
- role: role
317
- }, childrenProps), React.createElement(CheckComponent, {
296
+ testId: testId,
297
+ leftAccessory: React.createElement(React.Fragment, null, leftAccessory ? React.createElement(View, {
298
+ style: {
299
+ flexDirection: "row"
300
+ }
301
+ }, React.createElement(CheckComponent, {
302
+ disabled: disabled,
303
+ selected: selected
304
+ }), React.createElement(Strut, {
305
+ size: Spacing.xSmall_8
306
+ }), leftAccessory) : React.createElement(CheckComponent, {
318
307
  disabled: disabled,
319
- selected: selected,
320
- pressed: pressed,
321
- hovered: hovered,
322
- focused: focused
323
- }), React.createElement(LabelMedium, {
308
+ selected: selected
309
+ })),
310
+ rightAccessory: rightAccessory,
311
+ subtitle1: subtitle1 ? React.createElement(LabelSmall, {
312
+ className: "subtitle"
313
+ }, subtitle1) : undefined,
314
+ title: React.createElement(LabelMedium, {
324
315
  style: styles$5.label
325
- }, label));
326
- });
316
+ }, label),
317
+ subtitle2: subtitle2 ? React.createElement(LabelSmall, {
318
+ className: "subtitle"
319
+ }, subtitle2) : undefined,
320
+ onClick: this.handleClick
321
+ }, sharedProps));
327
322
  }
328
323
  }
329
324
  OptionItem.defaultProps = {
330
325
  disabled: false,
326
+ horizontalRule: "none",
331
327
  onToggle: () => void 0,
332
328
  role: "option",
333
329
  selected: false
@@ -339,37 +335,67 @@ const {
339
335
  offBlack,
340
336
  offBlack32
341
337
  } = Color;
338
+ const activeBlue = mix(offBlack32, blue);
342
339
  const styles$5 = StyleSheet.create({
340
+ item: {
341
+ minHeight: "unset",
342
+ paddingBlock: Spacing.xxxxSmall_2,
343
+ ":focus": {
344
+ borderRadius: Spacing.xxxSmall_4,
345
+ outline: `${Spacing.xxxxSmall_2}px solid ${Color.blue}`,
346
+ outlineOffset: -Spacing.xxxxSmall_2
347
+ },
348
+ ":focus-visible": {
349
+ overflow: "visible"
350
+ },
351
+ [":hover[aria-disabled=false]"]: {
352
+ color: white,
353
+ background: blue
354
+ },
355
+ ["@media not (hover: hover)"]: {
356
+ [":hover[aria-disabled=false]"]: {
357
+ color: white,
358
+ background: offBlack
359
+ }
360
+ },
361
+ [":active[aria-disabled=false]"]: {
362
+ color: mix(fade(blue, 0.32), white),
363
+ background: activeBlue
364
+ },
365
+ [":hover[aria-disabled=false] .checkbox"]: {
366
+ background: white
367
+ },
368
+ [":hover[aria-disabled=false] .check"]: {
369
+ color: blue
370
+ },
371
+ [":active[aria-disabled=false] .check"]: {
372
+ color: activeBlue
373
+ },
374
+ [":is([aria-selected=true]) .checkbox"]: {
375
+ background: blue
376
+ },
377
+ [":is([aria-selected=true]) .check"]: {
378
+ color: white
379
+ },
380
+ [":is([aria-disabled=false]) .subtitle"]: {
381
+ color: Color.offBlack64
382
+ },
383
+ [":hover[aria-disabled=false] .subtitle"]: {
384
+ color: Color.offWhite
385
+ },
386
+ [":active[aria-disabled=false] .subtitle"]: {
387
+ color: mix(fade(blue, 0.16), white)
388
+ }
389
+ },
343
390
  itemContainer: {
344
- flexDirection: "row",
345
- background: white,
346
- color: offBlack,
347
- alignItems: "center",
348
- height: DROPDOWN_ITEM_HEIGHT,
349
- minHeight: DROPDOWN_ITEM_HEIGHT,
350
- border: 0,
351
- outline: 0,
352
- paddingLeft: Spacing.xSmall_8,
391
+ minHeight: "unset",
392
+ padding: Spacing.xSmall_8,
353
393
  paddingRight: Spacing.medium_16,
354
- whiteSpace: "nowrap",
355
- cursor: "default"
356
- },
357
- focus: {
358
- color: white,
359
- background: blue
360
- },
361
- active: {
362
- color: mix(fade(blue, 0.32), white),
363
- background: mix(offBlack32, blue)
364
- },
365
- disabled: {
366
- color: offBlack32,
367
- background: white
394
+ whiteSpace: "nowrap"
368
395
  },
369
396
  label: {
370
397
  whiteSpace: "nowrap",
371
398
  userSelect: "none",
372
- marginLeft: Spacing.xSmall_8,
373
399
  overflow: "hidden",
374
400
  textOverflow: "ellipsis"
375
401
  },
@@ -479,15 +505,6 @@ function getDropdownMenuHeight(items, initialHeight = 0) {
479
505
  }
480
506
  }, initialHeight);
481
507
  }
482
- function generateDropdownMenuStyles(minWidth, maxHeight) {
483
- const styles = StyleSheet.create({
484
- dropdownMenu: {
485
- minWidth,
486
- maxHeight
487
- }
488
- });
489
- return styles.dropdownMenu;
490
- }
491
508
 
492
509
  class DropdownCoreVirtualized extends React.Component {
493
510
  constructor(props) {
@@ -590,6 +607,39 @@ class DropdownCoreVirtualized extends React.Component {
590
607
  }
591
608
  var DropdownCoreVirtualized$1 = withActionScheduler(DropdownCoreVirtualized);
592
609
 
610
+ function modifyMaxHeight({
611
+ state,
612
+ options
613
+ }) {
614
+ const overflow = detectOverflow(state, options);
615
+ const {
616
+ y
617
+ } = state.modifiersData.preventOverflow || {
618
+ x: 0,
619
+ y: 0
620
+ };
621
+ const {
622
+ height
623
+ } = state.rects.popper;
624
+ const [basePlacement] = state.placement.split("-");
625
+ const heightProp = basePlacement === "top" ? "top" : "bottom";
626
+ const maxHeight = height - overflow[heightProp] - y;
627
+ state.styles.popper = _extends({}, state.styles.popper, {
628
+ maxHeight: `${maxHeight}px`,
629
+ ["--popper-max-height"]: `${maxHeight}px`
630
+ });
631
+ }
632
+ const maxHeightModifier = {
633
+ name: "maxHeight",
634
+ enabled: true,
635
+ phase: "main",
636
+ options: {
637
+ padding: DROPDOWN_ITEM_HEIGHT
638
+ },
639
+ requiresIfExists: ["offset", "preventOverflow", "flip"],
640
+ fn: modifyMaxHeight
641
+ };
642
+
593
643
  const modifiers = [{
594
644
  name: "preventOverflow",
595
645
  options: {
@@ -597,7 +647,7 @@ const modifiers = [{
597
647
  altAxis: true,
598
648
  tether: false
599
649
  }
600
- }];
650
+ }, maxHeightModifier];
601
651
  const DropdownPopper = function DropdownPopper({
602
652
  children,
603
653
  alignment = "left",
@@ -652,6 +702,18 @@ function debounce(callback, wait) {
652
702
  timeout = setTimeout(later, wait);
653
703
  };
654
704
  }
705
+ function isString(x) {
706
+ return typeof x === "string";
707
+ }
708
+ function getLabel(props) {
709
+ if (isString(props.label)) {
710
+ return props.label;
711
+ }
712
+ if (isString(props.labelAsText)) {
713
+ return props.labelAsText;
714
+ }
715
+ return "";
716
+ }
655
717
 
656
718
  const VIRTUALIZE_THRESHOLD = 125;
657
719
  const StyledSpan = addStyle("span");
@@ -783,12 +845,14 @@ class DropdownCore extends React.Component {
783
845
  const foundIndex = this.props.items.filter(item => item.focusable).findIndex(({
784
846
  component
785
847
  }) => {
786
- var _component$props;
787
848
  if (SeparatorItem.isClassOf(component)) {
788
849
  return false;
789
850
  }
790
- const label = (_component$props = component.props) == null ? void 0 : _component$props.label.toLowerCase();
791
- return label.startsWith(key.toLowerCase());
851
+ if (OptionItem.isClassOf(component)) {
852
+ const optionItemProps = component.props;
853
+ return getLabel(optionItemProps).toLowerCase().startsWith(key.toLowerCase());
854
+ }
855
+ return false;
792
856
  });
793
857
  if (foundIndex >= 0) {
794
858
  const isClosed = !this.props.open;
@@ -1103,14 +1167,15 @@ class DropdownCore extends React.Component {
1103
1167
  } = this.props;
1104
1168
  const openerStyle = openerElement && window.getComputedStyle(openerElement);
1105
1169
  const minDropdownWidth = openerStyle ? openerStyle.getPropertyValue("width") : 0;
1106
- const maxDropdownHeight = getDropdownMenuHeight(this.props.items);
1107
1170
  return React.createElement(View, {
1108
1171
  onMouseUp: this.handleDropdownMouseUp,
1109
1172
  style: [styles$3.dropdown, light && styles$3.light, isReferenceHidden && styles$3.hidden, dropdownStyle],
1110
1173
  testId: "dropdown-core-container"
1111
1174
  }, isFilterable && this.renderSearchField(), React.createElement(View, {
1112
1175
  role: role,
1113
- style: [styles$3.listboxOrMenu, generateDropdownMenuStyles(minDropdownWidth, maxDropdownHeight)],
1176
+ style: [styles$3.listboxOrMenu, {
1177
+ minWidth: minDropdownWidth
1178
+ }],
1114
1179
  "aria-invalid": role === "listbox" ? ariaInvalid : undefined,
1115
1180
  "aria-required": role === "listbox" ? ariaRequired : undefined
1116
1181
  }, listRenderer), this.maybeRenderNoResults());
@@ -1184,7 +1249,8 @@ const styles$3 = StyleSheet.create({
1184
1249
  paddingTop: Spacing.xxxSmall_4,
1185
1250
  paddingBottom: Spacing.xxxSmall_4,
1186
1251
  border: `solid 1px ${Color.offBlack16}`,
1187
- boxShadow: `0px 8px 8px 0px ${fade(Color.offBlack, 0.1)}`
1252
+ boxShadow: `0px 8px 8px 0px ${fade(Color.offBlack, 0.1)}`,
1253
+ maxHeight: "var(--popper-max-height)"
1188
1254
  },
1189
1255
  light: {
1190
1256
  border: "none"
@@ -1204,7 +1270,8 @@ const styles$3 = StyleSheet.create({
1204
1270
  searchInputStyle: {
1205
1271
  margin: Spacing.xSmall_8,
1206
1272
  marginTop: Spacing.xxxSmall_4,
1207
- minHeight: "auto"
1273
+ minHeight: "auto",
1274
+ position: "sticky"
1208
1275
  },
1209
1276
  srOnly: {
1210
1277
  border: 0,
@@ -1757,7 +1824,7 @@ class SingleSelect extends React.Component {
1757
1824
  const lowercasedSearchText = searchText.toLowerCase();
1758
1825
  return children.filter(({
1759
1826
  props
1760
- }) => !searchText || props.label.toLowerCase().indexOf(lowercasedSearchText) > -1);
1827
+ }) => !searchText || getLabel(props).indexOf(lowercasedSearchText) > -1);
1761
1828
  }
1762
1829
  getMenuItems(children) {
1763
1830
  const {
@@ -1781,7 +1848,7 @@ class SingleSelect extends React.Component {
1781
1848
  sharedProps = _objectWithoutPropertiesLoose(_this$props, _excluded$1);
1782
1849
  const items = React.Children.toArray(children);
1783
1850
  const selectedItem = items.find(option => option.props.value === selectedValue);
1784
- const menuText = selectedItem ? selectedItem.props.label : placeholder;
1851
+ const menuText = selectedItem ? getLabel(selectedItem.props) || defaultLabels.someSelected(1) : placeholder;
1785
1852
  const dropdownOpener = opener ? React.createElement(DropdownOpener, {
1786
1853
  onClick: this.handleClick,
1787
1854
  disabled: numItems === 0 || disabled,
@@ -1971,7 +2038,15 @@ class MultiSelect extends React.Component {
1971
2038
  return noSelectionText;
1972
2039
  case 1:
1973
2040
  const selectedItem = children.find(option => option.props.value === selectedValues[0]);
1974
- return selectedItem ? selectedItem.props.label : noSelectionText;
2041
+ if (selectedItem) {
2042
+ const selectedLabel = getLabel(selectedItem == null ? void 0 : selectedItem.props);
2043
+ if (selectedLabel) {
2044
+ return selectedLabel;
2045
+ } else {
2046
+ return someSelected(1);
2047
+ }
2048
+ }
2049
+ return noSelectionText;
1975
2050
  case children.length:
1976
2051
  return allSelected;
1977
2052
  default:
@@ -2036,7 +2111,7 @@ class MultiSelect extends React.Component {
2036
2111
  const lowercasedSearchText = searchText.toLowerCase();
2037
2112
  const filteredChildren = children.filter(({
2038
2113
  props
2039
- }) => !searchText || props.label.toLowerCase().indexOf(lowercasedSearchText) > -1);
2114
+ }) => !searchText || getLabel(props).toLowerCase().indexOf(lowercasedSearchText) > -1);
2040
2115
  const lastSelectedChildren = [];
2041
2116
  const restOfTheChildren = [];
2042
2117
  for (const child of filteredChildren) {