@leafygreen-ui/combobox 6.0.5 → 6.0.7

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 (37) hide show
  1. package/.turbo/turbo-build.log +23 -0
  2. package/.turbo/turbo-docs.log +3 -0
  3. package/.turbo/turbo-tsc.log +1 -0
  4. package/CHANGELOG.md +43 -0
  5. package/dist/Chip/Chip.stories.d.ts +7 -0
  6. package/dist/Chip/Chip.stories.d.ts.map +1 -0
  7. package/dist/Combobox.types.d.ts +4 -22
  8. package/dist/Combobox.types.d.ts.map +1 -1
  9. package/dist/ComboboxContext/ComboboxContext.d.ts +11 -2
  10. package/dist/ComboboxContext/ComboboxContext.d.ts.map +1 -1
  11. package/dist/ComboboxGroup/ComboboxGroup.stories.d.ts +7 -0
  12. package/dist/ComboboxGroup/ComboboxGroup.stories.d.ts.map +1 -0
  13. package/dist/ComboboxMenu/ComboboxMenu.d.ts +5 -2
  14. package/dist/ComboboxMenu/ComboboxMenu.d.ts.map +1 -1
  15. package/dist/ComboboxOption/ComboboxOption.stories.d.ts +7 -0
  16. package/dist/ComboboxOption/ComboboxOption.stories.d.ts.map +1 -0
  17. package/dist/esm/index.js +1 -1
  18. package/dist/esm/index.js.map +1 -1
  19. package/dist/index.js +1 -1
  20. package/dist/index.js.map +1 -1
  21. package/dist/test-utils/getTestOptions.testutils.d.ts +3 -0
  22. package/dist/test-utils/getTestOptions.testutils.d.ts.map +1 -0
  23. package/dist/utils/ComboboxTestUtils.d.ts.map +1 -1
  24. package/package.json +18 -16
  25. package/src/Chip/Chip.stories.tsx +35 -0
  26. package/src/Combobox/Combobox.spec.tsx +18 -0
  27. package/src/Combobox.story.tsx +94 -196
  28. package/src/Combobox.types.ts +114 -136
  29. package/src/ComboboxContext/ComboboxContext.tsx +5 -3
  30. package/src/ComboboxGroup/ComboboxGroup.stories.tsx +47 -0
  31. package/src/ComboboxMenu/ComboboxMenu.tsx +9 -12
  32. package/src/ComboboxOption/ComboboxOption.stories.tsx +70 -0
  33. package/src/test-utils/getTestOptions.testutils.tsx +83 -0
  34. package/src/utils/ComboboxTestUtils.tsx +6 -2
  35. package/stories.js +1 -1
  36. package/tsconfig.json +9 -3
  37. package/tsconfig.tsbuildinfo +1 -1
@@ -1,6 +1,7 @@
1
1
  import { ComponentPropsWithoutRef, ReactElement, ReactNode } from 'react';
2
2
 
3
3
  import { Either, HTMLElementProps } from '@leafygreen-ui/lib';
4
+ import { PortalControlProps } from '@leafygreen-ui/popover';
4
5
 
5
6
  /**
6
7
  * Prop Enums & Types
@@ -56,8 +57,8 @@ export const Overflow = {
56
57
  export type Overflow = typeof Overflow[keyof typeof Overflow];
57
58
 
58
59
  export const State = {
59
- error: 'error',
60
60
  none: 'none',
61
+ error: 'error',
61
62
  } as const;
62
63
  export type State = typeof State[keyof typeof State];
63
64
 
@@ -132,141 +133,118 @@ export interface ComboboxMultiselectProps<M extends boolean> {
132
133
  overflow?: M extends true ? Overflow : undefined;
133
134
  }
134
135
 
135
- export interface BaseComboboxProps
136
- extends Omit<HTMLElementProps<'div'>, 'onChange'> {
137
- /**
138
- * Defines the Combobox Options by passing children. Must be `ComboboxOption` or `ComboboxGroup`
139
- */
140
- children?: ReactNode;
141
-
142
- /**
143
- * An accessible label for the input, rendered in a <label> to the DOM
144
- */
145
- label?: string;
146
-
147
- /**
148
- * An accessible label for the input, used only for screen-readers
149
- */
150
- 'aria-label'?: string;
151
-
152
- /**
153
- * A description for the input
154
- */
155
- description?: string;
156
-
157
- /**
158
- * A placeholder for the input element. Uses the native `placeholder` attribute.
159
- */
160
- placeholder?: string;
161
-
162
- /**
163
- * Disables all interaction with the component
164
- */
165
- disabled?: boolean;
166
-
167
- /**
168
- * Defines the visual size of the component
169
- */
170
- size?: ComboboxSize;
171
-
172
- /**
173
- * Toggles Dark Mode
174
- */
175
- darkMode?: boolean;
176
-
177
- /**
178
- * The error state of the component. Defines whether the error message is displayed.
179
- */
180
- state?: State;
181
-
182
- /**
183
- * The message shown below the input when state is `error`
184
- */
185
- errorMessage?: string;
186
-
187
- /**
188
- * The state of search results. Toggles search messages within the menu.
189
- */
190
- searchState?: SearchState;
191
-
192
- /**
193
- * A message shown within the menu when there are no options passed in as children, or `filteredOptions` is an empty array
194
- */
195
- searchEmptyMessage?: string;
196
-
197
- /**
198
- * A message shown within the menu when searchState is `error`
199
- */
200
- searchErrorMessage?: string;
201
-
202
- /**
203
- * A message shown within the menu when searchState is `loading`
204
- */
205
- searchLoadingMessage?: string;
206
-
207
- /**
208
- * A callback called when the search input changes.
209
- * Receives a single argument that is the current input value.
210
- * Use this callback to set `searchState` and/or `filteredOptions` appropriately
211
- */
212
- onFilter?: (value: string) => void;
213
-
214
- /**
215
- * Defines whether the Clear button appears to the right of the input.
216
- */
217
- clearable?: boolean;
218
-
219
- /**
220
- * A callback fired when the Clear button is pressed.
221
- * Fired _after_ `onChange`, and _before_ `onFilter`
222
- */
223
- onClear?: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
224
-
225
- /**
226
- * An array used to define which options are displayed.
227
- * Do not remove options from the JSX children, as this will affect the selected options
228
- */
229
- filteredOptions?: Array<string>;
230
-
231
- /**
232
- * Defines where the ellipses appear in a Chip when the length exceeds the `chipCharacterLimit`
233
- */
234
- chipTruncationLocation?: TruncationLocation;
235
-
236
- /**
237
- * Defined the character limit of a multiselect Chip before they start truncating.
238
- * Note: the three ellipses dots are included in the character limit.
239
- */
240
- chipCharacterLimit?: number;
241
-
242
- /**
243
- * Specifies that the popover content should be rendered at the end of the DOM,
244
- * rather than in the DOM tree.
245
- *
246
- * default: `true`
247
- */
248
- usePortal?: boolean;
249
-
250
- /**
251
- * When usePortal is `true`, specifies a class name to apply to the root element of the portal.
252
- */
253
- portalClassName?: undefined;
254
-
255
- /**
256
- * When usePortal is `true`, specifies an element to portal within. The default behavior is to generate a div at the end of the document to render within.
257
- */
258
- portalContainer?: null;
259
-
260
- /**
261
- * When usePortal is `true`, specifies the scrollable element to position relative to.
262
- */
263
- scrollContainer?: null;
264
-
265
- /**
266
- * Number that controls the z-index of the popover element directly.
267
- */
268
- popoverZIndex?: number;
269
- }
136
+ export type BaseComboboxProps = Omit<HTMLElementProps<'div'>, 'onChange'> &
137
+ PortalControlProps & {
138
+ /**
139
+ * Defines the Combobox Options by passing children. Must be `ComboboxOption` or `ComboboxGroup`
140
+ */
141
+ children?: ReactNode;
142
+
143
+ /**
144
+ * An accessible label for the input, rendered in a <label> to the DOM
145
+ */
146
+ label?: string;
147
+
148
+ /**
149
+ * An accessible label for the input, used only for screen-readers
150
+ */
151
+ 'aria-label'?: string;
152
+
153
+ /**
154
+ * A description for the input
155
+ */
156
+ description?: string;
157
+
158
+ /**
159
+ * A placeholder for the input element. Uses the native `placeholder` attribute.
160
+ */
161
+ placeholder?: string;
162
+
163
+ /**
164
+ * Disables all interaction with the component
165
+ */
166
+ disabled?: boolean;
167
+
168
+ /**
169
+ * Defines the visual size of the component
170
+ */
171
+ size?: ComboboxSize;
172
+
173
+ /**
174
+ * Toggles Dark Mode
175
+ */
176
+ darkMode?: boolean;
177
+
178
+ /**
179
+ * The error state of the component. Defines whether the error message is displayed.
180
+ */
181
+ state?: State;
182
+
183
+ /**
184
+ * The message shown below the input when state is `error`
185
+ */
186
+ errorMessage?: string;
187
+
188
+ /**
189
+ * The state of search results. Toggles search messages within the menu.
190
+ */
191
+ searchState?: SearchState;
192
+
193
+ /**
194
+ * A message shown within the menu when there are no options passed in as children, or `filteredOptions` is an empty array
195
+ */
196
+ searchEmptyMessage?: string;
197
+
198
+ /**
199
+ * A message shown within the menu when searchState is `error`
200
+ */
201
+ searchErrorMessage?: string;
202
+
203
+ /**
204
+ * A message shown within the menu when searchState is `loading`
205
+ */
206
+ searchLoadingMessage?: string;
207
+
208
+ /**
209
+ * A callback called when the search input changes.
210
+ * Receives a single argument that is the current input value.
211
+ * Use this callback to set `searchState` and/or `filteredOptions` appropriately
212
+ */
213
+ onFilter?: (value: string) => void;
214
+
215
+ /**
216
+ * Defines whether the Clear button appears to the right of the input.
217
+ */
218
+ clearable?: boolean;
219
+
220
+ /**
221
+ * A callback fired when the Clear button is pressed.
222
+ * Fired _after_ `onChange`, and _before_ `onFilter`
223
+ */
224
+ onClear?: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
225
+
226
+ /**
227
+ * An array used to define which options are displayed.
228
+ * Do not remove options from the JSX children, as this will affect the selected options
229
+ */
230
+ filteredOptions?: Array<string>;
231
+
232
+ /**
233
+ * Defines where the ellipses appear in a Chip when the length exceeds the `chipCharacterLimit`
234
+ */
235
+ chipTruncationLocation?: TruncationLocation;
236
+
237
+ /**
238
+ * Defined the character limit of a multiselect Chip before they start truncating.
239
+ * Note: the three ellipses dots are included in the character limit.
240
+ */
241
+ chipCharacterLimit?: number;
242
+
243
+ /**
244
+ * Number that controls the z-index of the popover element directly.
245
+ */
246
+ popoverZIndex?: number;
247
+ };
270
248
 
271
249
  export type ComboboxProps<M extends boolean> = Either<
272
250
  BaseComboboxProps & ComboboxMultiselectProps<M>,
@@ -8,7 +8,7 @@ import {
8
8
  TruncationLocation,
9
9
  } from '../Combobox.types';
10
10
 
11
- interface ComboboxData {
11
+ export interface ComboboxData {
12
12
  multiselect: boolean;
13
13
  size: ComboboxSize;
14
14
  withIcons: boolean;
@@ -23,7 +23,7 @@ interface ComboboxData {
23
23
  popoverZIndex?: number;
24
24
  }
25
25
 
26
- export const ComboboxContext = createContext<ComboboxData>({
26
+ export const defaultContext = {
27
27
  multiselect: false,
28
28
  size: ComboboxSize.Default,
29
29
  withIcons: false,
@@ -32,4 +32,6 @@ export const ComboboxContext = createContext<ComboboxData>({
32
32
  state: State.none,
33
33
  searchState: SearchState.unset,
34
34
  overflow: Overflow.expandY,
35
- });
35
+ };
36
+
37
+ export const ComboboxContext = createContext<ComboboxData>(defaultContext);
@@ -0,0 +1,47 @@
1
+ /* eslint-disable react/display-name */
2
+ import React from 'react';
3
+
4
+ import LeafyGreenProvider from '@leafygreen-ui/leafygreen-provider';
5
+ import { StoryMetaType } from '@leafygreen-ui/lib';
6
+
7
+ import { InternalComboboxOption } from '../ComboboxOption';
8
+
9
+ import { InternalComboboxGroup } from './ComboboxGroup';
10
+
11
+ const meta: StoryMetaType<typeof InternalComboboxGroup> = {
12
+ title: 'Components/Combobox/ComboboxGroup',
13
+ component: InternalComboboxGroup,
14
+ parameters: {
15
+ default: null,
16
+ generate: {
17
+ combineArgs: {
18
+ darkMode: [false, true],
19
+ },
20
+ args: {
21
+ label: 'Label',
22
+ children: (
23
+ <>
24
+ <InternalComboboxOption
25
+ value="option"
26
+ displayName="Option"
27
+ isSelected={false}
28
+ isFocused={false}
29
+ setSelected={() => {}}
30
+ index={0}
31
+ />
32
+ </>
33
+ ),
34
+ },
35
+ decorator: (Instance, context) => {
36
+ return (
37
+ <LeafyGreenProvider darkMode={context?.args.darkMode}>
38
+ <Instance />
39
+ </LeafyGreenProvider>
40
+ );
41
+ },
42
+ },
43
+ },
44
+ };
45
+ export default meta;
46
+
47
+ export const Generated = () => <></>;
@@ -6,7 +6,7 @@ import { useAvailableSpace, useForwardedRef } from '@leafygreen-ui/hooks';
6
6
  import Icon from '@leafygreen-ui/icon';
7
7
  import { useDarkMode } from '@leafygreen-ui/leafygreen-provider';
8
8
  import { palette } from '@leafygreen-ui/palette';
9
- import Popover from '@leafygreen-ui/popover';
9
+ import Popover, { PortalControlProps } from '@leafygreen-ui/popover';
10
10
  import { Error } from '@leafygreen-ui/typography';
11
11
 
12
12
  import { ComboboxProps } from '../Combobox.types';
@@ -30,17 +30,14 @@ type ComboboxMenuProps = {
30
30
  id: string;
31
31
  labelId: string;
32
32
  menuWidth: number;
33
- } & Pick<
34
- ComboboxProps<any>,
35
- | 'searchLoadingMessage'
36
- | 'searchErrorMessage'
37
- | 'searchEmptyMessage'
38
- | 'usePortal'
39
- | 'portalClassName'
40
- | 'portalContainer'
41
- | 'scrollContainer'
42
- | 'popoverZIndex'
43
- >;
33
+ } & PortalControlProps &
34
+ Pick<
35
+ ComboboxProps<any>,
36
+ | 'searchLoadingMessage'
37
+ | 'searchErrorMessage'
38
+ | 'searchEmptyMessage'
39
+ | 'popoverZIndex'
40
+ >;
44
41
 
45
42
  export const ComboboxMenu = React.forwardRef<HTMLDivElement, ComboboxMenuProps>(
46
43
  (
@@ -0,0 +1,70 @@
1
+ /* eslint-disable react/display-name, react/jsx-key */
2
+ import React from 'react';
3
+
4
+ import Icon from '@leafygreen-ui/icon';
5
+ import LeafyGreenProvider from '@leafygreen-ui/leafygreen-provider';
6
+ import { StoryMetaType, StoryType } from '@leafygreen-ui/lib';
7
+
8
+ import { ComboboxContext } from '../ComboboxContext';
9
+ import { defaultContext } from '../ComboboxContext/ComboboxContext';
10
+
11
+ import { InternalComboboxOption } from './ComboboxOption';
12
+
13
+ const meta: StoryMetaType<typeof InternalComboboxOption> = {
14
+ title: 'Components/Combobox/ComboboxOption',
15
+ component: InternalComboboxOption,
16
+ parameters: {
17
+ default: null,
18
+ generate: {
19
+ storyNames: ['WithIcons', 'WithoutIcons'],
20
+ combineArgs: {
21
+ darkMode: [false, true],
22
+ description: [undefined, 'This is a description'],
23
+ isSelected: [false, true],
24
+ isFocused: [false, true],
25
+ disabled: [false, true],
26
+ },
27
+ decorator: (Instance, context) => {
28
+ return (
29
+ <LeafyGreenProvider darkMode={context?.args.darkMode}>
30
+ <ComboboxContext.Provider
31
+ value={{ ...defaultContext, withIcons: context?.args.withIcons }}
32
+ >
33
+ <Instance glyph={context?.args.glyph} />
34
+ </ComboboxContext.Provider>
35
+ </LeafyGreenProvider>
36
+ );
37
+ },
38
+ },
39
+ },
40
+ args: {
41
+ displayName: 'Option',
42
+ setSelected: () => {},
43
+ },
44
+ };
45
+
46
+ export default meta;
47
+
48
+ export const WithoutIcons: StoryType<typeof InternalComboboxOption> = () => (
49
+ <></>
50
+ );
51
+ WithoutIcons.parameters = {
52
+ generate: {
53
+ args: {
54
+ /// @ts-expect-error - withIcons is not a component prop
55
+ withIcons: false,
56
+ glyph: undefined,
57
+ },
58
+ },
59
+ };
60
+
61
+ export const WithIcons: StoryType<typeof InternalComboboxOption> = () => <></>;
62
+ WithIcons.parameters = {
63
+ generate: {
64
+ args: {
65
+ /// @ts-expect-error - withIcons is not a component prop
66
+ withIcons: true,
67
+ glyph: <Icon glyph="Cloud" />,
68
+ },
69
+ },
70
+ };
@@ -0,0 +1,83 @@
1
+ import React from 'react';
2
+
3
+ import Icon from '@leafygreen-ui/icon';
4
+
5
+ import { ComboboxGroup, ComboboxOption } from '..';
6
+
7
+ export const getComboboxOptions = (withGlyphs = true) => [
8
+ <ComboboxOption
9
+ key="apple"
10
+ value="apple"
11
+ displayName="Apple"
12
+ data-testid="test-id"
13
+ description="Do I keep the doctor away?"
14
+ // eslint-disable-next-line no-console
15
+ onClick={(event, value) => console.log(event, value)}
16
+ className="className"
17
+ />,
18
+ <ComboboxOption key="banana" value="banana" displayName="Banana" />,
19
+ <ComboboxOption key="carrot" value="carrot" displayName="Carrot" disabled />,
20
+ <ComboboxOption
21
+ key="pomegranate"
22
+ value="pomegranate"
23
+ displayName="Pomegranate"
24
+ glyph={withGlyphs ? <Icon glyph="Warning" /> : undefined}
25
+ description="Watch out, I stain everything I touch LOL"
26
+ disabled
27
+ />,
28
+ <ComboboxOption
29
+ key="plantain"
30
+ value="plantain"
31
+ displayName="Plantain"
32
+ glyph={withGlyphs ? <Icon glyph="Connect" /> : undefined}
33
+ description="Don't confuse me with a banana"
34
+ // eslint-disable-next-line no-console
35
+ onClick={() => console.log('I was clicked')}
36
+ />,
37
+ <ComboboxOption
38
+ key="paragraph"
39
+ value="paragraph"
40
+ displayName="Nullam quis risus eget urna mollis ornare vel eu leo. Vestibulum id ligula porta felis euismod semper."
41
+ />,
42
+ <ComboboxOption
43
+ key="hash"
44
+ value="hash"
45
+ displayName="5f4dcc3b5aa765d61d8327deb882cf995f4dcc3b5aa765d61d8327deb882cf99"
46
+ />,
47
+ <ComboboxOption
48
+ key="dragonfruit"
49
+ value="dragonfruit"
50
+ displayName="Dragonfruit"
51
+ description="Rawr"
52
+ />,
53
+ <ComboboxOption key="eggplant" value="eggplant" displayName="Eggplant" />,
54
+ <ComboboxOption key="fig" value="fig" displayName="Fig" />,
55
+ <ComboboxOption key="grape" value="grape" displayName="Grape" />,
56
+ <ComboboxOption key="honeydew" value="honeydew" displayName="Honeydew" />,
57
+ <ComboboxOption
58
+ key="iceberg-lettuce"
59
+ value="iceberg-lettuce"
60
+ displayName="Iceberg lettuce"
61
+ />,
62
+ <ComboboxGroup key="peppers" label="Peppers">
63
+ <ComboboxOption key="cayenne" value="cayenne" displayName="Cayenne" />
64
+ <ComboboxOption
65
+ key="ghost-pepper"
66
+ value="ghost-pepper"
67
+ displayName="Ghost pepper"
68
+ />
69
+ <ComboboxOption key="habanero" value="habanero" displayName="Habanero" />
70
+ <ComboboxOption key="jalapeno" value="jalapeno" displayName="Jalapeño" />
71
+ <ComboboxOption
72
+ key="red-pepper"
73
+ value="red-pepper"
74
+ displayName="Red pepper"
75
+ />
76
+ <ComboboxOption
77
+ key="scotch-bonnet"
78
+ value="scotch-bonnet"
79
+ displayName="Scotch bonnet"
80
+ description="Don't touch your eyes"
81
+ />
82
+ </ComboboxGroup>,
83
+ ];
@@ -199,8 +199,12 @@ export function renderCombobox<T extends Select>(
199
199
  * @param newProps
200
200
  * @returns
201
201
  */
202
- const rerenderCombobox = (newProps: renderComboboxProps) =>
203
- renderResult.rerender(getComboboxJSX({ ...props, ...newProps }));
202
+ const rerenderCombobox = (newProps: renderComboboxProps) => {
203
+ const rerenderProps = { ...props, ...newProps };
204
+ return renderResult.rerender(
205
+ getComboboxJSX(rerenderProps as renderComboboxProps),
206
+ );
207
+ };
204
208
 
205
209
  /**
206
210
  * @returns all chip elements