@khanacademy/wonder-blocks-dropdown 5.6.3 → 5.8.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # @khanacademy/wonder-blocks-dropdown
2
2
 
3
+ ## 5.8.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 2b8424ca: Allow use of JSX Element as label in SingleSelect and MultiSelect
8
+
9
+ ## 5.7.0
10
+
11
+ ### Minor Changes
12
+
13
+ - c512e76e: Add `startIcon` prop to Combobox
14
+
15
+ ### Patch Changes
16
+
17
+ - Updated dependencies [b5cad0b1]
18
+ - @khanacademy/wonder-blocks-modal@5.1.17
19
+
3
20
  ## 5.6.3
4
21
 
5
22
  ### Patch Changes
@@ -1,5 +1,6 @@
1
- /// <reference types="react" />
1
+ import * as React from "react";
2
2
  import { StyleType } from "@khanacademy/wonder-blocks-core";
3
+ import { PhosphorIcon } from "@khanacademy/wonder-blocks-icon";
3
4
  import { ComboboxLabels, MaybeValueOrValues, OptionItemComponent } from "../util/types";
4
5
  type Props = {
5
6
  /**
@@ -89,6 +90,10 @@ type Props = {
89
90
  * (combobox).
90
91
  */
91
92
  autoComplete?: "none" | "list" | undefined;
93
+ /**
94
+ * An optional decorative icon to display at the start of the combobox.
95
+ */
96
+ startIcon?: React.ReactElement<React.ComponentProps<typeof PhosphorIcon>> | null;
92
97
  };
93
98
  /**
94
99
  * A `Combobox` is an input widget that has an associated `listbox`. This
@@ -102,5 +107,5 @@ type Props = {
102
107
  * Open button (🔽) is pressed.
103
108
  * - It is displayed when the combobox receives focus.
104
109
  */
105
- export default function Combobox({ autoComplete, children, disabled, error, id, labels, onChange, onToggle, opened, placeholder, selectionType, testId, value, }: Props): JSX.Element;
110
+ export default function Combobox({ autoComplete, children, disabled, error, id, labels, onChange, onToggle, opened, placeholder, selectionType, startIcon, testId, value, }: Props): JSX.Element;
106
111
  export {};
@@ -66,6 +66,13 @@ type DefaultProps = Readonly<{
66
66
  * Whether to display shortcuts for Select All and Select None.
67
67
  */
68
68
  shortcuts: boolean;
69
+ /**
70
+ * When false, the SelectOpener can show a Node as a label. When true, the
71
+ * SelectOpener will use a string as a label. If using custom OptionItems, a
72
+ * plain text label can be provided with the `labelAsText` prop.
73
+ * Defaults to true.
74
+ */
75
+ showOpenerLabelAsText: boolean;
69
76
  }>;
70
77
  type Props = AriaProps & DefaultProps & Readonly<{
71
78
  /**
@@ -196,7 +203,7 @@ export default class MultiSelect extends React.Component<Props, State> {
196
203
  handleToggle: (selectedValue: string) => void;
197
204
  handleSelectAll: () => void;
198
205
  handleSelectNone: () => void;
199
- getMenuText(children: OptionItemComponentArray): string;
206
+ getMenuText(children: OptionItemComponentArray): string | JSX.Element;
200
207
  getShortcuts(numOptions: number): DropdownItem[];
201
208
  getMenuItems(children: OptionItemComponentArray): DropdownItem[];
202
209
  mapOptionItemToDropdownItem: (option: OptionItemComponent) => DropdownItem;
@@ -64,6 +64,13 @@ type DefaultProps = Readonly<{
64
64
  * The object containing the custom labels used inside this component.
65
65
  */
66
66
  labels: SingleSelectLabels;
67
+ /**
68
+ * When false, the SelectOpener can show a Node as a label. When true, the
69
+ * SelectOpener will use a string as a label. If using custom OptionItems, a
70
+ * plain text label can be provided with the `labelAsText` prop.
71
+ * Defaults to true.
72
+ */
73
+ showOpenerLabelAsText: boolean;
67
74
  }>;
68
75
  type Props = AriaProps & DefaultProps & Readonly<{
69
76
  /**
package/dist/es/index.js CHANGED
@@ -3,7 +3,7 @@ import * as React from 'react';
3
3
  import { StyleSheet } from 'aphrodite';
4
4
  import { CompactCell, DetailCell } from '@khanacademy/wonder-blocks-cell';
5
5
  import * as tokens from '@khanacademy/wonder-blocks-tokens';
6
- import { spacing, color, mix, fade, font, border } from '@khanacademy/wonder-blocks-tokens';
6
+ import { spacing, color, mix, fade, font, border, semanticColor } from '@khanacademy/wonder-blocks-tokens';
7
7
  import { LabelMedium, LabelSmall, LabelLarge } from '@khanacademy/wonder-blocks-typography';
8
8
  import _objectWithoutPropertiesLoose from '@babel/runtime/helpers/objectWithoutPropertiesLoose';
9
9
  import { View, addStyle, IDProvider, useUniqueIdWithMock } from '@khanacademy/wonder-blocks-core';
@@ -778,6 +778,12 @@ function getLabel(props) {
778
778
  }
779
779
  return "";
780
780
  }
781
+ function getSelectOpenerLabel(showOpenerLabelAsText, props) {
782
+ if (showOpenerLabelAsText) {
783
+ return getLabel(props);
784
+ }
785
+ return props.label;
786
+ }
781
787
 
782
788
  const VIRTUALIZE_THRESHOLD = 125;
783
789
  const StyledSpan$1 = addStyle("span");
@@ -1862,7 +1868,7 @@ const _generateStyles = (light, placeholder, error) => {
1862
1868
  return stateStyles[styleKey];
1863
1869
  };
1864
1870
 
1865
- const _excluded$1 = ["children", "error", "id", "light", "opener", "placeholder", "selectedValue", "testId", "alignment", "autoFocus", "dropdownStyle", "enableTypeAhead", "isFilterable", "labels", "onChange", "onToggle", "opened", "style", "className", "aria-invalid", "aria-required"];
1871
+ const _excluded$1 = ["children", "error", "id", "light", "opener", "placeholder", "selectedValue", "testId", "alignment", "autoFocus", "dropdownStyle", "enableTypeAhead", "isFilterable", "labels", "onChange", "onToggle", "opened", "style", "className", "aria-invalid", "aria-required", "showOpenerLabelAsText"];
1866
1872
  class SingleSelect extends React.Component {
1867
1873
  constructor(props) {
1868
1874
  super(props);
@@ -1969,12 +1975,13 @@ class SingleSelect extends React.Component {
1969
1975
  opener,
1970
1976
  placeholder,
1971
1977
  selectedValue,
1972
- testId
1978
+ testId,
1979
+ showOpenerLabelAsText
1973
1980
  } = _this$props,
1974
1981
  sharedProps = _objectWithoutPropertiesLoose(_this$props, _excluded$1);
1975
1982
  const items = React.Children.toArray(children);
1976
1983
  const selectedItem = items.find(option => option.props.value === selectedValue);
1977
- const menuText = selectedItem ? getLabel(selectedItem.props) : placeholder;
1984
+ const menuText = selectedItem ? getSelectOpenerLabel(showOpenerLabelAsText, selectedItem.props) : placeholder;
1978
1985
  const dropdownOpener = React.createElement(IDProvider, {
1979
1986
  id: id,
1980
1987
  scope: "single-select-opener"
@@ -2069,7 +2076,8 @@ SingleSelect.defaultProps = {
2069
2076
  filter: defaultLabels.filter,
2070
2077
  noResults: defaultLabels.noResults,
2071
2078
  someResults: defaultLabels.someSelected
2072
- }
2079
+ },
2080
+ showOpenerLabelAsText: true
2073
2081
  };
2074
2082
 
2075
2083
  const _excluded = ["id", "light", "opener", "testId", "alignment", "dropdownStyle", "implicitAllEnabled", "isFilterable", "labels", "onChange", "onToggle", "opened", "selectedValues", "shortcuts", "style", "className", "aria-invalid", "aria-required"];
@@ -2170,7 +2178,8 @@ class MultiSelect extends React.Component {
2170
2178
  getMenuText(children) {
2171
2179
  const {
2172
2180
  implicitAllEnabled,
2173
- selectedValues
2181
+ selectedValues,
2182
+ showOpenerLabelAsText
2174
2183
  } = this.props;
2175
2184
  const {
2176
2185
  noneSelected,
@@ -2185,7 +2194,7 @@ class MultiSelect extends React.Component {
2185
2194
  case 1:
2186
2195
  const selectedItem = children.find(option => option.props.value === selectedValues[0]);
2187
2196
  if (selectedItem) {
2188
- const selectedLabel = getLabel(selectedItem == null ? void 0 : selectedItem.props);
2197
+ const selectedLabel = getSelectOpenerLabel(showOpenerLabelAsText, selectedItem == null ? void 0 : selectedItem.props);
2189
2198
  if (selectedLabel) {
2190
2199
  return selectedLabel;
2191
2200
  } else {
@@ -2385,7 +2394,8 @@ MultiSelect.defaultProps = {
2385
2394
  error: false,
2386
2395
  light: false,
2387
2396
  shortcuts: false,
2388
- selectedValues: []
2397
+ selectedValues: [],
2398
+ showOpenerLabelAsText: true
2389
2399
  };
2390
2400
 
2391
2401
  function updateMultipleSelection(previousSelection, value = "") {
@@ -2845,6 +2855,7 @@ function Combobox({
2845
2855
  opened,
2846
2856
  placeholder,
2847
2857
  selectionType = "single",
2858
+ startIcon,
2848
2859
  testId,
2849
2860
  value = ""
2850
2861
  }) {
@@ -2988,6 +2999,20 @@ function Combobox({
2988
2999
  }
2989
3000
  return [labelFromSelected];
2990
3001
  }, [children, labelFromSelected, selected]);
3002
+ const maybeRenderStartIcon = () => {
3003
+ var _startIcon$props$colo;
3004
+ if (!startIcon) {
3005
+ return null;
3006
+ }
3007
+ const startIconElement = React.cloneElement(startIcon, _extends({
3008
+ size: "small"
3009
+ }, startIcon.props, {
3010
+ color: disabled ? color.offBlack32 : (_startIcon$props$colo = startIcon.props.color) != null ? _startIcon$props$colo : semanticColor.icon.primary
3011
+ }));
3012
+ return React.createElement(View, {
3013
+ style: styles.iconWrapper
3014
+ }, startIconElement);
3015
+ };
2991
3016
  const pillIdPrefix = id ? `${id}-pill-` : ids.get("pill");
2992
3017
  const currentActiveDescendant = !openState ? undefined : focusedIndex >= 0 ? (_renderList$focusedIn = renderList[focusedIndex]) == null ? void 0 : (_renderList$focusedIn2 = _renderList$focusedIn.props) == null ? void 0 : _renderList$focusedIn2.id : pillIdPrefix + focusedMultiSelectIndex;
2993
3018
  const controlledWidget = !openState ? undefined : focusedIndex >= 0 ? uniqueId : pillIdPrefix;
@@ -3016,7 +3041,7 @@ function Combobox({
3016
3041
  disabled: disabled,
3017
3042
  testId: testId,
3018
3043
  removeSelectedLabel: labels.removeSelected
3019
- }), React.createElement(TextField, {
3044
+ }), maybeRenderStartIcon(), React.createElement(TextField, {
3020
3045
  id: ids.get("input"),
3021
3046
  testId: testId,
3022
3047
  style: styles.combobox,
@@ -3154,6 +3179,10 @@ const styles = StyleSheet.create({
3154
3179
  },
3155
3180
  clearButton: {
3156
3181
  right: spacing.xLarge_32 + spacing.xSmall_8
3182
+ },
3183
+ iconWrapper: {
3184
+ padding: spacing.xxxSmall_4,
3185
+ minWidth: "auto"
3157
3186
  }
3158
3187
  });
3159
3188
 
package/dist/index.js CHANGED
@@ -814,6 +814,12 @@ function getLabel(props) {
814
814
  }
815
815
  return "";
816
816
  }
817
+ function getSelectOpenerLabel(showOpenerLabelAsText, props) {
818
+ if (showOpenerLabelAsText) {
819
+ return getLabel(props);
820
+ }
821
+ return props.label;
822
+ }
817
823
 
818
824
  const VIRTUALIZE_THRESHOLD = 125;
819
825
  const StyledSpan$1 = wonderBlocksCore.addStyle("span");
@@ -1898,7 +1904,7 @@ const _generateStyles = (light, placeholder, error) => {
1898
1904
  return stateStyles[styleKey];
1899
1905
  };
1900
1906
 
1901
- const _excluded$1 = ["children", "error", "id", "light", "opener", "placeholder", "selectedValue", "testId", "alignment", "autoFocus", "dropdownStyle", "enableTypeAhead", "isFilterable", "labels", "onChange", "onToggle", "opened", "style", "className", "aria-invalid", "aria-required"];
1907
+ const _excluded$1 = ["children", "error", "id", "light", "opener", "placeholder", "selectedValue", "testId", "alignment", "autoFocus", "dropdownStyle", "enableTypeAhead", "isFilterable", "labels", "onChange", "onToggle", "opened", "style", "className", "aria-invalid", "aria-required", "showOpenerLabelAsText"];
1902
1908
  class SingleSelect extends React__namespace.Component {
1903
1909
  constructor(props) {
1904
1910
  super(props);
@@ -2005,12 +2011,13 @@ class SingleSelect extends React__namespace.Component {
2005
2011
  opener,
2006
2012
  placeholder,
2007
2013
  selectedValue,
2008
- testId
2014
+ testId,
2015
+ showOpenerLabelAsText
2009
2016
  } = _this$props,
2010
2017
  sharedProps = _objectWithoutPropertiesLoose__default["default"](_this$props, _excluded$1);
2011
2018
  const items = React__namespace.Children.toArray(children);
2012
2019
  const selectedItem = items.find(option => option.props.value === selectedValue);
2013
- const menuText = selectedItem ? getLabel(selectedItem.props) : placeholder;
2020
+ const menuText = selectedItem ? getSelectOpenerLabel(showOpenerLabelAsText, selectedItem.props) : placeholder;
2014
2021
  const dropdownOpener = React__namespace.createElement(wonderBlocksCore.IDProvider, {
2015
2022
  id: id,
2016
2023
  scope: "single-select-opener"
@@ -2105,7 +2112,8 @@ SingleSelect.defaultProps = {
2105
2112
  filter: defaultLabels.filter,
2106
2113
  noResults: defaultLabels.noResults,
2107
2114
  someResults: defaultLabels.someSelected
2108
- }
2115
+ },
2116
+ showOpenerLabelAsText: true
2109
2117
  };
2110
2118
 
2111
2119
  const _excluded = ["id", "light", "opener", "testId", "alignment", "dropdownStyle", "implicitAllEnabled", "isFilterable", "labels", "onChange", "onToggle", "opened", "selectedValues", "shortcuts", "style", "className", "aria-invalid", "aria-required"];
@@ -2206,7 +2214,8 @@ class MultiSelect extends React__namespace.Component {
2206
2214
  getMenuText(children) {
2207
2215
  const {
2208
2216
  implicitAllEnabled,
2209
- selectedValues
2217
+ selectedValues,
2218
+ showOpenerLabelAsText
2210
2219
  } = this.props;
2211
2220
  const {
2212
2221
  noneSelected,
@@ -2221,7 +2230,7 @@ class MultiSelect extends React__namespace.Component {
2221
2230
  case 1:
2222
2231
  const selectedItem = children.find(option => option.props.value === selectedValues[0]);
2223
2232
  if (selectedItem) {
2224
- const selectedLabel = getLabel(selectedItem == null ? void 0 : selectedItem.props);
2233
+ const selectedLabel = getSelectOpenerLabel(showOpenerLabelAsText, selectedItem == null ? void 0 : selectedItem.props);
2225
2234
  if (selectedLabel) {
2226
2235
  return selectedLabel;
2227
2236
  } else {
@@ -2421,7 +2430,8 @@ MultiSelect.defaultProps = {
2421
2430
  error: false,
2422
2431
  light: false,
2423
2432
  shortcuts: false,
2424
- selectedValues: []
2433
+ selectedValues: [],
2434
+ showOpenerLabelAsText: true
2425
2435
  };
2426
2436
 
2427
2437
  function updateMultipleSelection(previousSelection, value = "") {
@@ -2881,6 +2891,7 @@ function Combobox({
2881
2891
  opened,
2882
2892
  placeholder,
2883
2893
  selectionType = "single",
2894
+ startIcon,
2884
2895
  testId,
2885
2896
  value = ""
2886
2897
  }) {
@@ -3024,6 +3035,20 @@ function Combobox({
3024
3035
  }
3025
3036
  return [labelFromSelected];
3026
3037
  }, [children, labelFromSelected, selected]);
3038
+ const maybeRenderStartIcon = () => {
3039
+ var _startIcon$props$colo;
3040
+ if (!startIcon) {
3041
+ return null;
3042
+ }
3043
+ const startIconElement = React__namespace.cloneElement(startIcon, _extends__default["default"]({
3044
+ size: "small"
3045
+ }, startIcon.props, {
3046
+ color: disabled ? tokens.color.offBlack32 : (_startIcon$props$colo = startIcon.props.color) != null ? _startIcon$props$colo : tokens.semanticColor.icon.primary
3047
+ }));
3048
+ return React__namespace.createElement(wonderBlocksCore.View, {
3049
+ style: styles.iconWrapper
3050
+ }, startIconElement);
3051
+ };
3027
3052
  const pillIdPrefix = id ? `${id}-pill-` : ids.get("pill");
3028
3053
  const currentActiveDescendant = !openState ? undefined : focusedIndex >= 0 ? (_renderList$focusedIn = renderList[focusedIndex]) == null ? void 0 : (_renderList$focusedIn2 = _renderList$focusedIn.props) == null ? void 0 : _renderList$focusedIn2.id : pillIdPrefix + focusedMultiSelectIndex;
3029
3054
  const controlledWidget = !openState ? undefined : focusedIndex >= 0 ? uniqueId : pillIdPrefix;
@@ -3052,7 +3077,7 @@ function Combobox({
3052
3077
  disabled: disabled,
3053
3078
  testId: testId,
3054
3079
  removeSelectedLabel: labels.removeSelected
3055
- }), React__namespace.createElement(wonderBlocksForm.TextField, {
3080
+ }), maybeRenderStartIcon(), React__namespace.createElement(wonderBlocksForm.TextField, {
3056
3081
  id: ids.get("input"),
3057
3082
  testId: testId,
3058
3083
  style: styles.combobox,
@@ -3190,6 +3215,10 @@ const styles = aphrodite.StyleSheet.create({
3190
3215
  },
3191
3216
  clearButton: {
3192
3217
  right: tokens.spacing.xLarge_32 + tokens.spacing.xSmall_8
3218
+ },
3219
+ iconWrapper: {
3220
+ padding: tokens.spacing.xxxSmall_4,
3221
+ minWidth: "auto"
3193
3222
  }
3194
3223
  });
3195
3224
 
@@ -1,3 +1,4 @@
1
+ /// <reference types="react" />
1
2
  import { PropsFor } from "@khanacademy/wonder-blocks-core";
2
3
  import OptionItem from "../components/option-item";
3
4
  /**
@@ -21,4 +22,9 @@ type OptionItemProps = PropsFor<typeof OptionItem>;
21
22
  * Returns a valid label for the given props.
22
23
  */
23
24
  export declare function getLabel(props: OptionItemProps): string;
25
+ /**
26
+ * Returns the label for the SelectOpener in the SingleSelect and MultiSelect.
27
+ * If the label is a Node, and `labelAsText` is undefined, returns the label.
28
+ */
29
+ export declare function getSelectOpenerLabel(showOpenerLabelAsText: boolean, props: OptionItemProps): string | JSX.Element;
24
30
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khanacademy/wonder-blocks-dropdown",
3
- "version": "5.6.3",
3
+ "version": "5.8.0",
4
4
  "design": "v1",
5
5
  "description": "Dropdown variants for Wonder Blocks.",
6
6
  "main": "dist/index.js",
@@ -21,7 +21,7 @@
21
21
  "@khanacademy/wonder-blocks-core": "^7.0.1",
22
22
  "@khanacademy/wonder-blocks-icon": "^4.2.0",
23
23
  "@khanacademy/wonder-blocks-layout": "^2.2.2",
24
- "@khanacademy/wonder-blocks-modal": "^5.1.16",
24
+ "@khanacademy/wonder-blocks-modal": "^5.1.17",
25
25
  "@khanacademy/wonder-blocks-pill": "^2.5.3",
26
26
  "@khanacademy/wonder-blocks-search-field": "^2.3.6",
27
27
  "@khanacademy/wonder-blocks-timing": "^5.0.2",