@khanacademy/wonder-blocks-dropdown 5.8.1 → 6.1.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,51 @@
1
1
  # @khanacademy/wonder-blocks-dropdown
2
2
 
3
+ ## 6.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 71e70869: # SingleSelect
8
+
9
+ - Add `required`, `validate`, and `onValidate` props to support validation.
10
+ - DropdownOpener and SelectOpener:
11
+ - Add `onBlur` prop
12
+ - Set `aria-invalid` if it is in an error state.
13
+
14
+ - bc4da9ec: # MultiSelect
15
+
16
+ - Add `required`, `validate`, and `onValidate` props to support validation.
17
+ - Set `aria-invalid` on the opener if it is in an error state
18
+ - Share validation logic with SingleSelect
19
+
20
+ ### Patch Changes
21
+
22
+ - c7178e13: Update SingleSelect and MultiSelect to functional components
23
+ - 71e70869: Update `DropdownCore` to check for key presses using `event.key` instead of `event.which` or `event.keyCode` (which are both deprecated now)
24
+ - Updated dependencies [e9a119a8]
25
+ - Updated dependencies [257b6bc3]
26
+ - @khanacademy/wonder-blocks-search-field@3.1.0
27
+
28
+ ## 6.0.0
29
+
30
+ ### Major Changes
31
+
32
+ - e6abdd17: Upgrade to React 18
33
+
34
+ ### Patch Changes
35
+
36
+ - Updated dependencies [e6abdd17]
37
+ - @khanacademy/wonder-blocks-timing@6.0.0
38
+ - @khanacademy/wonder-blocks-core@8.0.0
39
+ - @khanacademy/wonder-blocks-cell@4.0.0
40
+ - @khanacademy/wonder-blocks-clickable@5.0.0
41
+ - @khanacademy/wonder-blocks-icon@5.0.0
42
+ - @khanacademy/wonder-blocks-layout@3.0.0
43
+ - @khanacademy/wonder-blocks-modal@6.0.0
44
+ - @khanacademy/wonder-blocks-pill@3.0.0
45
+ - @khanacademy/wonder-blocks-search-field@3.0.0
46
+ - @khanacademy/wonder-blocks-tokens@3.0.0
47
+ - @khanacademy/wonder-blocks-typography@3.0.0
48
+
3
49
  ## 5.8.1
4
50
 
5
51
  ### Patch Changes
@@ -1,4 +1,4 @@
1
- /// <reference types="react" />
1
+ import * as React from "react";
2
2
  import { ComboboxLabels, MaybeValueOrValues, OptionItemComponent } from "../util/types";
3
3
  type Props = {
4
4
  /**
@@ -48,5 +48,5 @@ type Props = {
48
48
  *
49
49
  * @see https://bugs.webkit.org/show_bug.cgi?id=167671
50
50
  */
51
- export declare function ComboboxLiveRegion({ focusedIndex, focusedMultiSelectIndex, labels, selectedLabels, opened, options, selected, selectionType, testId, }: Props): JSX.Element;
51
+ export declare function ComboboxLiveRegion({ focusedIndex, focusedMultiSelectIndex, labels, selectedLabels, opened, options, selected, selectionType, testId, }: Props): React.JSX.Element;
52
52
  export {};
@@ -107,5 +107,5 @@ type Props = {
107
107
  * Open button (🔽) is pressed.
108
108
  * - It is displayed when the combobox receives focus.
109
109
  */
110
- export default function Combobox({ autoComplete, children, disabled, error, id, labels, onChange, onToggle, opened, placeholder, selectionType, startIcon, 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): React.JSX.Element;
111
111
  export {};
@@ -6,7 +6,7 @@ declare const _default: {
6
6
  data: DropdownItem[];
7
7
  width?: number | null | undefined;
8
8
  listRef?: React.RefObject<List<any>> | undefined;
9
- }): JSX.Element;
9
+ }): React.JSX.Element;
10
10
  displayName: string;
11
11
  };
12
12
  export default _default;
@@ -19,6 +19,10 @@ type Props = Partial<Omit<AriaProps, "aria-disabled">> & {
19
19
  * Callback for when the opener is pressed.
20
20
  */
21
21
  onClick: (e: React.SyntheticEvent) => unknown;
22
+ /**
23
+ * Callback for when the opener is blurred.
24
+ */
25
+ onBlur?: (e: React.SyntheticEvent) => unknown;
22
26
  /**
23
27
  * Test ID used for e2e testing.
24
28
  */
@@ -35,6 +39,10 @@ type Props = Partial<Omit<AriaProps, "aria-disabled">> & {
35
39
  * The unique identifier for the opener.
36
40
  */
37
41
  id?: string;
42
+ /**
43
+ * If the dropdown has an error.
44
+ */
45
+ error?: boolean;
38
46
  };
39
47
  type DefaultProps = {
40
48
  disabled: Props["disabled"];
@@ -1,4 +1,4 @@
1
- /// <reference types="react" />
1
+ import * as React from "react";
2
2
  import { StyleType } from "@khanacademy/wonder-blocks-core";
3
3
  import { MaybeValueOrValues, OptionItemComponent } from "../util/types";
4
4
  type Props = {
@@ -81,5 +81,5 @@ type Props = {
81
81
  * </Listbox>
82
82
  * ```
83
83
  */
84
- export default function Listbox(props: Props): JSX.Element;
84
+ export default function Listbox(props: Props): React.JSX.Element;
85
85
  export {};
@@ -1,9 +1,7 @@
1
1
  import * as React from "react";
2
2
  import { type AriaProps, type StyleType } from "@khanacademy/wonder-blocks-core";
3
- import DropdownOpener from "./dropdown-opener";
4
- import SelectOpener from "./select-opener";
5
3
  import OptionItem from "./option-item";
6
- import type { DropdownItem, OpenerProps, OptionItemComponent, OptionItemComponentArray } from "../util/types";
4
+ import type { OpenerProps } from "../util/types";
7
5
  export type Labels = {
8
6
  /**
9
7
  * Label for describing the dismiss icon on the search filter.
@@ -43,36 +41,36 @@ type DefaultProps = Readonly<{
43
41
  * Whether this dropdown should be left-aligned or right-aligned with the
44
42
  * opener component. Defaults to left-aligned.
45
43
  */
46
- alignment: "left" | "right";
44
+ alignment?: "left" | "right";
47
45
  /**
48
46
  * Whether this component is disabled. A disabled dropdown may not be opened
49
47
  * and does not support interaction. Defaults to false.
50
48
  */
51
- disabled: boolean;
49
+ disabled?: boolean;
52
50
  /**
53
51
  * Whether this component is in an error state. Defaults to false.
54
52
  */
55
- error: boolean;
53
+ error?: boolean;
56
54
  /**
57
55
  * Whether to display the "light" version of this component instead, for
58
56
  * use when the component is used on a dark background.
59
57
  */
60
- light: boolean;
58
+ light?: boolean;
61
59
  /**
62
60
  * The values of the items that are currently selected.
63
61
  */
64
- selectedValues: Array<string>;
62
+ selectedValues?: Array<string>;
65
63
  /**
66
64
  * Whether to display shortcuts for Select All and Select None.
67
65
  */
68
- shortcuts: boolean;
66
+ shortcuts?: boolean;
69
67
  /**
70
68
  * When false, the SelectOpener can show a Node as a label. When true, the
71
69
  * SelectOpener will use a string as a label. If using custom OptionItems, a
72
70
  * plain text label can be provided with the `labelAsText` prop.
73
71
  * Defaults to true.
74
72
  */
75
- showOpenerLabelAsText: boolean;
73
+ showOpenerLabelAsText?: boolean;
76
74
  }>;
77
75
  type Props = AriaProps & DefaultProps & Readonly<{
78
76
  /**
@@ -143,32 +141,41 @@ type Props = AriaProps & DefaultProps & Readonly<{
143
141
  * opener's `aria-controls` attribute for screenreaders.
144
142
  */
145
143
  dropdownId?: string;
146
- }>;
147
- type State = Readonly<{
148
- /**
149
- * Whether or not the dropdown is open.
150
- */
151
- open: boolean;
152
144
  /**
153
- * The text input to filter the items by their label. Defaults to an empty
154
- * string.
155
- */
156
- searchText: string;
157
- /**
158
- * The selected values that are set when the dropdown is opened. We use
159
- * this to move the selected items to the top when the dropdown is
160
- * re-opened.
161
- */
162
- lastSelectedValues: Array<string>;
163
- /**
164
- * The object containing the custom labels used inside this component.
165
- */
166
- labels: Labels;
167
- /**
168
- * The DOM reference to the opener element. This is mainly used to set focus
169
- * to this element, and also to pass the reference to Popper.js.
170
- */
171
- openerElement?: HTMLElement;
145
+ * Whether this field is required to continue, or the error message to
146
+ * render if this field is left blank.
147
+ *
148
+ * This can be a boolean or a string.
149
+ *
150
+ * String:
151
+ * Please pass in a translated string to use as the error message that will
152
+ * render if the user leaves this field blank. If this field is required,
153
+ * and a string is not passed in, a default untranslated string will render
154
+ * upon error.
155
+ * Note: The string will not be used if a `validate` prop is passed in.
156
+ *
157
+ * Example message: i18n._("A password is required to log in.")
158
+ *
159
+ * Boolean:
160
+ * True/false indicating whether this field is required. Please do not pass
161
+ * in `true` if possible - pass in the error string instead.
162
+ * If `true` is passed, and a `validate` prop is not passed, that means
163
+ * there is no corresponding message and the default untranlsated message
164
+ * will be used.
165
+ */
166
+ required?: boolean | string;
167
+ /**
168
+ * Provide a validation for the field value.
169
+ * Return a string error message or null | void for a valid input.
170
+ *
171
+ * Use this for errors that are shown to the user while they are filling out
172
+ * a form.
173
+ */
174
+ validate?: (value: string[]) => string | null | void;
175
+ /**
176
+ * Called right after the field is validated.
177
+ */
178
+ onValidate?: (errorMessage?: string | null | undefined) => unknown;
172
179
  }>;
173
180
  /**
174
181
  * A dropdown that consists of multiple selection items. This select allows
@@ -189,28 +196,5 @@ type State = Readonly<{
189
196
  * </MultiSelect>
190
197
  * ```
191
198
  */
192
- export default class MultiSelect extends React.Component<Props, State> {
193
- labels: Labels;
194
- static defaultProps: DefaultProps;
195
- constructor(props: Props);
196
- /**
197
- * Used to sync the `opened` state when this component acts as a controlled
198
- * component
199
- */
200
- static getDerivedStateFromProps(props: Props, state: State): Partial<State> | null;
201
- componentDidUpdate(prevProps: Props): void;
202
- handleOpenChanged: (opened: boolean) => void;
203
- handleToggle: (selectedValue: string) => void;
204
- handleSelectAll: () => void;
205
- handleSelectNone: () => void;
206
- getMenuText(children: OptionItemComponentArray): string | JSX.Element;
207
- getShortcuts(numOptions: number): DropdownItem[];
208
- getMenuItems(children: OptionItemComponentArray): DropdownItem[];
209
- mapOptionItemToDropdownItem: (option: OptionItemComponent) => DropdownItem;
210
- handleOpenerRef: (node?: any) => void;
211
- handleSearchTextChanged: (searchText: string) => void;
212
- handleClick: (e: React.SyntheticEvent) => void;
213
- renderOpener(allChildren: React.ReactElement<React.ComponentProps<typeof OptionItem>>[], isDisabled: boolean, dropdownId: string): React.ReactElement<React.ComponentProps<typeof DropdownOpener>> | React.ReactElement<React.ComponentProps<typeof SelectOpener>>;
214
- render(): React.ReactNode;
215
- }
216
- export {};
199
+ declare const MultiSelect: (props: Props) => React.JSX.Element;
200
+ export default MultiSelect;
@@ -42,6 +42,10 @@ type SelectOpenerProps = AriaProps & {
42
42
  * Test ID used for e2e testing.
43
43
  */
44
44
  testId?: string;
45
+ /**
46
+ * Called when it is blurred
47
+ */
48
+ onBlur?: (e: React.SyntheticEvent) => unknown;
45
49
  };
46
50
  type DefaultProps = {
47
51
  disabled: SelectOpenerProps["disabled"];
@@ -1,9 +1,7 @@
1
1
  import * as React from "react";
2
2
  import { type AriaProps, type StyleType } from "@khanacademy/wonder-blocks-core";
3
- import DropdownOpener from "./dropdown-opener";
4
- import SelectOpener from "./select-opener";
5
3
  import OptionItem from "./option-item";
6
- import type { DropdownItem, OpenerProps, OptionItemComponentArray } from "../util/types";
4
+ import type { OpenerProps } from "../util/types";
7
5
  export type SingleSelectLabels = {
8
6
  /**
9
7
  * Label for describing the dismiss icon on the search filter.
@@ -27,16 +25,16 @@ type DefaultProps = Readonly<{
27
25
  * Whether this dropdown should be left-aligned or right-aligned with the
28
26
  * opener component. Defaults to left-aligned.
29
27
  */
30
- alignment: "left" | "right";
28
+ alignment?: "left" | "right";
31
29
  /**
32
30
  * Whether to auto focus an option. Defaults to true.
33
31
  */
34
- autoFocus: boolean;
32
+ autoFocus?: boolean;
35
33
  /**
36
34
  * Whether this component is disabled. A disabled dropdown may not be opened
37
35
  * and does not support interaction. Defaults to false.
38
36
  */
39
- disabled: boolean;
37
+ disabled?: boolean;
40
38
  /**
41
39
  * Whether to enable the type-ahead suggestions feature. Defaults to true.
42
40
  *
@@ -50,27 +48,27 @@ type DefaultProps = Readonly<{
50
48
  * some cases where it's not desirable (for example when using a `TextField`
51
49
  * as the opener element).
52
50
  */
53
- enableTypeAhead: boolean;
51
+ enableTypeAhead?: boolean;
54
52
  /**
55
53
  * Whether or not the input in is an error state. Defaults to false.
56
54
  */
57
- error: boolean;
55
+ error?: boolean;
58
56
  /**
59
57
  * Whether to display the "light" version of this component instead, for
60
58
  * use when the component is used on a dark background.
61
59
  */
62
- light: boolean;
60
+ light?: boolean;
63
61
  /**
64
62
  * The object containing the custom labels used inside this component.
65
63
  */
66
- labels: SingleSelectLabels;
64
+ labels?: SingleSelectLabels;
67
65
  /**
68
66
  * When false, the SelectOpener can show a Node as a label. When true, the
69
67
  * SelectOpener will use a string as a label. If using custom OptionItems, a
70
68
  * plain text label can be provided with the `labelAsText` prop.
71
69
  * Defaults to true.
72
70
  */
73
- showOpenerLabelAsText: boolean;
71
+ showOpenerLabelAsText?: boolean;
74
72
  }>;
75
73
  type Props = AriaProps & DefaultProps & Readonly<{
76
74
  /**
@@ -139,22 +137,41 @@ type Props = AriaProps & DefaultProps & Readonly<{
139
137
  * opener's `aria-controls` attribute for screenreaders.
140
138
  */
141
139
  dropdownId?: string;
142
- }>;
143
- type State = Readonly<{
144
140
  /**
145
- * Whether or not the dropdown is open.
141
+ * Whether this field is required to continue, or the error message to
142
+ * render if this field is left blank.
143
+ *
144
+ * This can be a boolean or a string.
145
+ *
146
+ * String:
147
+ * Please pass in a translated string to use as the error message that will
148
+ * render if the user leaves this field blank. If this field is required,
149
+ * and a string is not passed in, a default untranslated string will render
150
+ * upon error.
151
+ * Note: The string will not be used if a `validate` prop is passed in.
152
+ *
153
+ * Example message: i18n._("A password is required to log in.")
154
+ *
155
+ * Boolean:
156
+ * True/false indicating whether this field is required. Please do not pass
157
+ * in `true` if possible - pass in the error string instead.
158
+ * If `true` is passed, and a `validate` prop is not passed, that means
159
+ * there is no corresponding message and the default untranlsated message
160
+ * will be used.
146
161
  */
147
- open: boolean;
162
+ required?: boolean | string;
148
163
  /**
149
- * The text input to filter the items by their label. Defaults to an empty
150
- * string.
164
+ * Provide a validation for the field value.
165
+ * Return a string error message or null | void for a valid input.
166
+ *
167
+ * Use this for errors that are shown to the user while they are filling out
168
+ * a form.
151
169
  */
152
- searchText: string;
170
+ validate?: (value?: string | null) => string | null | void;
153
171
  /**
154
- * The DOM reference to the opener element. This is mainly used to set focus
155
- * to this element, and also to pass the reference to Popper.js.
172
+ * Called right after the field is validated.
156
173
  */
157
- openerElement?: HTMLElement;
174
+ onValidate?: (errorMessage?: string | null | undefined) => unknown;
158
175
  }>;
159
176
  /**
160
177
  * The single select allows the selection of one item. Clients are responsible
@@ -201,24 +218,5 @@ type State = Readonly<{
201
218
  * </SingleSelect>
202
219
  * ```
203
220
  */
204
- export default class SingleSelect extends React.Component<Props, State> {
205
- selectedIndex: number;
206
- static defaultProps: DefaultProps;
207
- constructor(props: Props);
208
- /**
209
- * Used to sync the `opened` state when this component acts as a controlled
210
- * component
211
- */
212
- static getDerivedStateFromProps(props: Props, state: State): Partial<State> | null;
213
- handleOpenChanged: (opened: boolean) => void;
214
- handleToggle: (selectedValue: string) => void;
215
- mapOptionItemsToDropdownItems: (children: OptionItemComponentArray) => DropdownItem[];
216
- filterChildren(children: OptionItemComponentArray): OptionItemComponentArray;
217
- getMenuItems(children: OptionItemComponentArray): DropdownItem[];
218
- handleSearchTextChanged: (searchText: string) => void;
219
- handleOpenerRef: (node?: any) => void;
220
- handleClick: (e: React.SyntheticEvent) => void;
221
- renderOpener(isDisabled: boolean, dropdownId: string): React.ReactElement<React.ComponentProps<typeof DropdownOpener>> | React.ReactElement<React.ComponentProps<typeof SelectOpener>>;
222
- render(): React.ReactNode;
223
- }
224
- export {};
221
+ declare const SingleSelect: (props: Props) => React.JSX.Element;
222
+ export default SingleSelect;