@sproutsocial/seeds-react-menu 0.2.2 → 0.3.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 (38) hide show
  1. package/.turbo/turbo-build.log +10 -10
  2. package/CHANGELOG.md +18 -0
  3. package/dist/esm/index.js +18 -4
  4. package/dist/esm/index.js.map +1 -1
  5. package/dist/index.d.mts +70 -43
  6. package/dist/index.d.ts +70 -43
  7. package/dist/index.js +18 -4
  8. package/dist/index.js.map +1 -1
  9. package/package.json +1 -1
  10. package/src/ActionMenu/ActionMenu.tsx +2 -16
  11. package/src/ActionMenu/ActionMenuTypes.ts +12 -0
  12. package/src/ActionMenu/index.ts +1 -0
  13. package/src/{ComboBox/ComboBox.feature → Autocomplete/Autocomplete.feature} +16 -16
  14. package/src/Autocomplete/Autocomplete.tsx +65 -0
  15. package/src/Autocomplete/AutocompleteTypes.ts +8 -0
  16. package/src/Autocomplete/__tests__/Autocomplete.test.tsx +113 -0
  17. package/src/Autocomplete/__tests__/Autocomplete.typetest.tsx +78 -0
  18. package/src/Autocomplete/index.ts +2 -0
  19. package/src/MenuContent/MenuContent.tsx +3 -3
  20. package/src/MenuContext.tsx +65 -9
  21. package/src/MenuGroup/MenuGroup.feature +15 -15
  22. package/src/MenuGroup/MenuGroup.tsx +6 -2
  23. package/src/MenuGroup/MenuGroupTypes.ts +2 -0
  24. package/src/MenuGroup/__tests__/MenuGroup.test.tsx +144 -9
  25. package/src/MenuGroup/__tests__/MenuGroup.typetest.tsx +24 -12
  26. package/src/MenuItem/MenuItem.tsx +1 -1
  27. package/src/MenuItem/__tests__/MenuItem.test.tsx +4 -1
  28. package/src/MenuRoot/__tests__/MenuRoot.test.tsx +1 -1
  29. package/src/MultiSelectMenu/MultiSelectMenu.tsx +8 -0
  30. package/src/MultiSelectMenu/MultiSelectMenuTypes.ts +5 -1
  31. package/src/MultiSelectMenu/__tests__/MultiSelectMenu.test.tsx +6 -8
  32. package/src/MultiSelectMenu/index.ts +1 -0
  33. package/src/SingleSelectMenu/SingleSelectMenuTypes.ts +2 -3
  34. package/src/SingleSelectMenu/__tests__/SingleSelectMenu.test.tsx +3 -3
  35. package/src/hooks/useItemsFromChildren.tsx +12 -5
  36. package/src/reducers/useComboboxStateReducer.tsx +26 -0
  37. package/src/types.ts +2 -0
  38. package/src/ComboBox/TokenInput.feature +0 -84
@@ -1,6 +1,13 @@
1
- import { render, screen } from "@sproutsocial/seeds-react-testing-library";
1
+ import {
2
+ render,
3
+ screen,
4
+ waitFor,
5
+ fireEvent,
6
+ act,
7
+ } from "@sproutsocial/seeds-react-testing-library";
2
8
  import {
3
9
  SingleSelectMenu,
10
+ MultiSelectMenu,
4
11
  MenuContent,
5
12
  MenuGroup,
6
13
  MenuToggleButton,
@@ -14,11 +21,139 @@ const menuItems = [
14
21
  ];
15
22
 
16
23
  describe("MenuGroup", () => {
17
- xdescribe("User Behavior", () => {
18
- // To be enabled when isSingleSelect prop added
19
- xit("behaves as it normally would when isSingleSelect is undefined", () => {});
20
- xit("behaves as it normally would when isSingleSelect is false", () => {});
21
- xit("behaves like a single select when isSingleSelect is true", () => {});
24
+ describe("User Behavior", () => {
25
+ it("allows multiple selections per group when isSingleSelect is undefined", () => {
26
+ render(
27
+ <MultiSelectMenu
28
+ defaultIsOpen
29
+ menuToggleElement={<MenuToggleButton>Test Menu</MenuToggleButton>}
30
+ >
31
+ <MenuContent>
32
+ <MenuGroup id="group">
33
+ {menuItems.map(({ id, label }) => (
34
+ <MenuItem key={id} id={id}>
35
+ {label}
36
+ </MenuItem>
37
+ ))}
38
+ </MenuGroup>
39
+ </MenuContent>
40
+ </MultiSelectMenu>
41
+ );
42
+
43
+ const options = screen.getAllByRole("option");
44
+
45
+ // Click on each option
46
+ for (const option of screen.getAllByRole("option")) {
47
+ fireEvent.click(option);
48
+ }
49
+
50
+ // Expect all options to be selected.
51
+ for (const option of options) {
52
+ expect(option).toHaveAttribute("aria-selected", "true");
53
+ }
54
+ });
55
+ it("allows multiple selections per group when isSingleSelect is false", () => {
56
+ render(
57
+ <MultiSelectMenu
58
+ defaultIsOpen
59
+ menuToggleElement={<MenuToggleButton>Test Menu</MenuToggleButton>}
60
+ >
61
+ <MenuContent>
62
+ <MenuGroup id="group" isSingleSelect={false}>
63
+ {menuItems.map(({ id, label }) => (
64
+ <MenuItem key={id} id={id}>
65
+ {label}
66
+ </MenuItem>
67
+ ))}
68
+ </MenuGroup>
69
+ </MenuContent>
70
+ </MultiSelectMenu>
71
+ );
72
+
73
+ const options = screen.getAllByRole("option");
74
+
75
+ // Click on each option
76
+ for (const option of screen.getAllByRole("option")) {
77
+ fireEvent.click(option);
78
+ }
79
+
80
+ // Expect all options to be selected.
81
+ for (const option of options) {
82
+ expect(option).toHaveAttribute("aria-selected", "true");
83
+ }
84
+ });
85
+ it("only allows one selection per group when isSingleSelect is true", async () => {
86
+ render(
87
+ <MultiSelectMenu
88
+ defaultIsOpen
89
+ menuToggleElement={<MenuToggleButton>Test Menu</MenuToggleButton>}
90
+ >
91
+ <MenuContent>
92
+ <MenuGroup id="group" isSingleSelect={true}>
93
+ {menuItems.map(({ id, label }) => (
94
+ <MenuItem key={id} id={id}>
95
+ {label}
96
+ </MenuItem>
97
+ ))}
98
+ </MenuGroup>
99
+ </MenuContent>
100
+ </MultiSelectMenu>
101
+ );
102
+
103
+ const options = screen.getAllByRole("option");
104
+
105
+ // Click on each option
106
+ for (const option of screen.getAllByRole("option")) {
107
+ fireEvent.click(option);
108
+ }
109
+
110
+ // Expect only the last option to be selected.
111
+ await waitFor(() =>
112
+ expect(options[0]).toHaveAttribute("aria-selected", "false")
113
+ );
114
+ await waitFor(() =>
115
+ expect(options[1]).toHaveAttribute("aria-selected", "false")
116
+ );
117
+ await waitFor(() =>
118
+ expect(options[2]).toHaveAttribute("aria-selected", "true")
119
+ );
120
+ });
121
+ it("only allows one selection per group in SingleSelectMenu, even if isSingleSelect is false", async () => {
122
+ render(
123
+ <SingleSelectMenu
124
+ defaultIsOpen
125
+ menuToggleElement={<MenuToggleButton>Test Menu</MenuToggleButton>}
126
+ >
127
+ <MenuContent>
128
+ <MenuGroup id="group" isSingleSelect={false}>
129
+ {menuItems.map(({ id, label }) => (
130
+ <MenuItem key={id} id={id}>
131
+ {label}
132
+ </MenuItem>
133
+ ))}
134
+ </MenuGroup>
135
+ </MenuContent>
136
+ </SingleSelectMenu>
137
+ );
138
+
139
+ const options = screen.getAllByRole("option");
140
+
141
+ // Click on each option
142
+ for (const option of screen.getAllByRole("option")) {
143
+ fireEvent.click(option);
144
+ }
145
+
146
+ // Expect only the last option to be selected.
147
+ await waitFor(() =>
148
+ expect(options[0]).toHaveAttribute("aria-selected", "false")
149
+ );
150
+ await waitFor(() =>
151
+ expect(options[1]).toHaveAttribute("aria-selected", "false")
152
+ );
153
+ await waitFor(() =>
154
+ expect(options[2]).toHaveAttribute("aria-selected", "true")
155
+ );
156
+ });
22
157
  });
23
158
  describe("API", () => {
24
159
  it("renders a group with a title", () => {
@@ -29,7 +164,7 @@ describe("MenuGroup", () => {
29
164
  menuToggleElement={<MenuToggleButton>Test Menu</MenuToggleButton>}
30
165
  >
31
166
  <MenuContent>
32
- <MenuGroup title={menuTitle}>
167
+ <MenuGroup title={menuTitle} id={menuTitle}>
33
168
  {menuItems.map(({ id, label }) => (
34
169
  <MenuItem key={id} id={id}>
35
170
  {label}
@@ -55,7 +190,7 @@ describe("MenuGroup", () => {
55
190
  menuToggleElement={<MenuToggleButton>Test Menu</MenuToggleButton>}
56
191
  >
57
192
  <MenuContent>
58
- <MenuGroup>
193
+ <MenuGroup id="group">
59
194
  {menuItems.map(({ id, label }) => (
60
195
  <MenuItem key={id} id={id}>
61
196
  {label}
@@ -78,7 +213,7 @@ describe("MenuGroup", () => {
78
213
  menuToggleElement={<MenuToggleButton>Test Menu</MenuToggleButton>}
79
214
  >
80
215
  <MenuContent>
81
- <MenuGroup>
216
+ <MenuGroup id="group">
82
217
  {menuItems.map(({ id, label }) => (
83
218
  <MenuItem key={id} id={id}>
84
219
  {label}
@@ -4,41 +4,53 @@ import { Text } from "@sproutsocial/seeds-react-text";
4
4
  export const MenuGroupTypeTest = () => (
5
5
  <>
6
6
  {/* MenuGroup allows children */}
7
- <MenuGroup>Test</MenuGroup>
7
+ <MenuGroup id="group">Test</MenuGroup>
8
8
  {/* MenuGroup allows children with MenuItems */}
9
- <MenuGroup>
9
+ <MenuGroup id="group">
10
10
  <MenuItem id="1">Test 1</MenuItem>
11
11
  <MenuItem id="2">Test 2</MenuItem>
12
12
  </MenuGroup>
13
+ {/* MenuGroup allows isSingleSelect prop */}
14
+ <MenuGroup id="group-1" isSingleSelect={true}>
15
+ <MenuItem id="1">Test 1</MenuItem>
16
+ </MenuGroup>
13
17
  {/* MenuGroup allows title as a string */}
14
- <MenuGroup title="title">Test</MenuGroup>
18
+ <MenuGroup id="group" title="title">
19
+ Test
20
+ </MenuGroup>
15
21
  {/* MenuGroup allows titleAs as a string */}
16
- <MenuGroup title="title" titleAs="h3">
22
+ <MenuGroup id="group" title="title" titleAs="h3">
17
23
  Test
18
24
  </MenuGroup>
19
25
  {/* MenuGroup allows titleAs as a component */}
20
- <MenuGroup title="title" titleAs={Text}>
26
+ <MenuGroup id="group" title="title" titleAs={Text}>
21
27
  Test
22
28
  </MenuGroup>
23
29
  {/* MenuGroup accepts ul props */}
24
- <MenuGroup aria-label="test label">Test</MenuGroup>
30
+ <MenuGroup id="group" aria-label="test label">
31
+ Test
32
+ </MenuGroup>
25
33
  {/* MenuGroup accepts style props */}
26
- <MenuGroup border="1px solid black" bg="white" width="150px">
34
+ <MenuGroup id="group" border="1px solid black" bg="white" width="150px">
27
35
  Test
28
36
  </MenuGroup>
29
37
 
30
38
  {/* @ts-expect-error - MenuGroup requires children */}
31
- <MenuGroup />
39
+ <MenuGroup id="group" />
32
40
  {/* @ts-expect-error - MenuGroup title is invalid as number */}
33
- <MenuGroup title={123}>Test</MenuGroup>
41
+ <MenuGroup id="group" title={123}>
42
+ Test
43
+ </MenuGroup>
34
44
  {/* @ts-expect-error - MenuGroup title is invalid as boolean */}
35
- <MenuGroup title={false}>Test</MenuGroup>
45
+ <MenuGroup id="group" title={false}>
46
+ Test
47
+ </MenuGroup>
36
48
  {/* @ts-expect-error - MenuGroup titleAs is invalid as number */}
37
- <MenuGroup title="title" titleAs={123}>
49
+ <MenuGroup id="group" title="title" titleAs={123}>
38
50
  Test
39
51
  </MenuGroup>
40
52
  {/* @ts-expect-error - MenuGroup titleAs is invalid as boolean */}
41
- <MenuGroup title="title" titleAs={false}>
53
+ <MenuGroup id="group" title="title" titleAs={false}>
42
54
  Test
43
55
  </MenuGroup>
44
56
  </>
@@ -25,12 +25,12 @@ export const MenuItem = ({
25
25
  }: TypeMenuItemProps) => {
26
26
  const { getItemProps, itemsMap, highlightedIndex, isListbox } =
27
27
  useContext(MenuContext);
28
+ const item = (itemsMap[id] || { index: 0, id }) as TypeDefaultItemProps;
28
29
  const { ...optionProps } = useOptionProps({
29
30
  id,
30
31
  children,
31
32
  ...props,
32
33
  });
33
- const item = (itemsMap[id] || { index: 0, id }) as TypeDefaultItemProps;
34
34
  const highlighted = highlightedIndex === item?.index;
35
35
 
36
36
  return (
@@ -4,6 +4,7 @@ import {
4
4
  screen,
5
5
  render,
6
6
  waitFor,
7
+ act,
7
8
  } from "@sproutsocial/seeds-react-testing-library";
8
9
  import {
9
10
  PointerEventsCheckLevel,
@@ -394,6 +395,8 @@ describe("MenuItem when isListbox", () => {
394
395
  });
395
396
 
396
397
  describe("API", () => {
398
+ // This test has many tests inside it due to the `each` usage, so extend the timeout.
399
+ jest.setTimeout(20 * 1000);
397
400
  test.each([INPUT_TYPES.CHECKBOX, INPUT_TYPES.RADIO, INPUT_TYPES.SWITCH])(
398
401
  "supports %s input types",
399
402
  (inputType) => {
@@ -491,7 +494,7 @@ describe("MenuItem when isListbox", () => {
491
494
  const menuItem = screen.getByRole("option", { selected: true });
492
495
  testByType[inputType].isSelected();
493
496
 
494
- await user.click(menuItem);
497
+ await act(() => user.click(menuItem));
495
498
 
496
499
  expect(
497
500
  await screen.findByRole("option", {
@@ -219,7 +219,7 @@ describe("MenuRoot", () => {
219
219
  >
220
220
  <MenuHeader title="Menu Header" />
221
221
  <div>Custom Banner</div>
222
- <MenuGroup title="Menu Group">
222
+ <MenuGroup title="Menu Group" id="group">
223
223
  <MenuItem id="1">Item 1</MenuItem>
224
224
  </MenuGroup>
225
225
  <MenuFooter>Menu Footer</MenuFooter>
@@ -105,6 +105,14 @@ export const MultiSelectMenu = ({
105
105
  } else {
106
106
  addSelectedItem(newSelectedItem);
107
107
  }
108
+ // If the newly selected item's group is single select, remove any other selected items in that group.
109
+ if (newSelectedItem.menuGroupProps?.isSingleSelect) {
110
+ const groupItems = selectedItems.filter(
111
+ (item) =>
112
+ item.menuGroupProps?.id === newSelectedItem.menuGroupProps?.id
113
+ );
114
+ groupItems.forEach((item) => removeSelectedItem(item));
115
+ }
108
116
  break;
109
117
  default:
110
118
  break;
@@ -1,8 +1,12 @@
1
1
  import type { TypeMenuRootProps, TypeMenuRootOwnProps } from "../MenuRoot";
2
- import type { TypeDownshiftMultiSelectProps } from "../MenuContext";
2
+ import type {
3
+ TypeDownshiftSelectProps,
4
+ TypeDownshiftMultiSelectProps,
5
+ } from "../MenuContext";
3
6
 
4
7
  export interface TypeMultiSelectMenuProps
5
8
  extends TypeMenuRootOwnProps,
9
+ TypeDownshiftSelectProps,
6
10
  TypeDownshiftMultiSelectProps {
7
11
  children: TypeMenuRootProps["children"];
8
12
  }
@@ -9,8 +9,6 @@ import {
9
9
  import {
10
10
  render,
11
11
  screen,
12
- fireEvent,
13
- act,
14
12
  cleanup,
15
13
  waitFor,
16
14
  } from "@sproutsocial/seeds-react-testing-library";
@@ -37,7 +35,7 @@ describe("MultiSelectMenu", () => {
37
35
  }
38
36
  >
39
37
  <MenuContent>
40
- <MenuGroup>
38
+ <MenuGroup id="group">
41
39
  {menuItems.map((item) => (
42
40
  <MenuItem key={item.id} id={item.id} label={item.label}>
43
41
  {item.label}
@@ -80,7 +78,7 @@ describe("MultiSelectMenu", () => {
80
78
  }
81
79
  >
82
80
  <MenuContent>
83
- <MenuGroup>
81
+ <MenuGroup id="group">
84
82
  {menuItems.map((item) => (
85
83
  <MenuItem
86
84
  key={item.id}
@@ -138,7 +136,7 @@ describe("MultiSelectMenu", () => {
138
136
  }
139
137
  >
140
138
  <MenuContent>
141
- <MenuGroup>
139
+ <MenuGroup id="group">
142
140
  {menuItems.map((item) => (
143
141
  <MenuItem
144
142
  key={item.id}
@@ -201,7 +199,7 @@ describe("MultiSelectMenu", () => {
201
199
  }
202
200
  >
203
201
  <MenuContent>
204
- <MenuGroup>
202
+ <MenuGroup id="group">
205
203
  {menuItems.map((item) => (
206
204
  <MenuItem
207
205
  key={item.id}
@@ -259,7 +257,7 @@ describe("MultiSelectMenu", () => {
259
257
  selectedItems={[menuItems[1], menuItems[2]]}
260
258
  >
261
259
  <MenuContent>
262
- <MenuGroup>
260
+ <MenuGroup id="group">
263
261
  {menuItems.map((item) => (
264
262
  <MenuItem
265
263
  key={item.id}
@@ -312,7 +310,7 @@ describe("MultiSelectMenu", () => {
312
310
  onSelectedItemsChange={mockSelectedItemsChange}
313
311
  >
314
312
  <MenuContent>
315
- <MenuGroup>
313
+ <MenuGroup id="group">
316
314
  {menuItems.map((item) => (
317
315
  <MenuItem
318
316
  key={item.id}
@@ -1 +1,2 @@
1
1
  export * from "./MultiSelectMenu";
2
+ export * from "./MultiSelectMenuTypes";
@@ -1,9 +1,8 @@
1
- import type { UseSelectProps } from "downshift";
2
1
  import type { TypeMenuRootProps, TypeMenuRootOwnProps } from "../MenuRoot";
3
- import type { TypeDefaultItemProps } from "../types";
2
+ import type { TypeDownshiftSelectProps } from "../MenuContext";
4
3
 
5
4
  export interface TypeSingleSelectMenuProps
6
5
  extends TypeMenuRootOwnProps,
7
- Partial<UseSelectProps<TypeDefaultItemProps>> {
6
+ TypeDownshiftSelectProps {
8
7
  children: TypeMenuRootProps["children"];
9
8
  }
@@ -34,7 +34,7 @@ describe("User Behavior", () => {
34
34
  }
35
35
  >
36
36
  <MenuContent>
37
- <MenuGroup title="Menu Title">
37
+ <MenuGroup title="Menu Title" id="menu-title">
38
38
  {menuItems.map(({ id, label }) => (
39
39
  <MenuItem key={id} id={id}>
40
40
  {label}
@@ -100,7 +100,7 @@ describe("User Behavior", () => {
100
100
  }
101
101
  >
102
102
  <MenuContent>
103
- <MenuGroup title="Menu Title">
103
+ <MenuGroup title="Menu Title" id="menu-title">
104
104
  {menuItems.map(({ id, label }) => (
105
105
  <MenuItem key={id} id={id}>
106
106
  {label}
@@ -135,7 +135,7 @@ describe("User Behavior", () => {
135
135
  menuToggleElement={<MenuToggleButton>Click me</MenuToggleButton>}
136
136
  >
137
137
  <MenuContent>
138
- <MenuGroup title="Menu Title">
138
+ <MenuGroup title="Menu Title" id="menu-title">
139
139
  {menuItems.map(({ id, label }) => (
140
140
  <MenuItem key={id} id={id}>
141
141
  {label}
@@ -1,14 +1,18 @@
1
1
  import React, { type ReactNode, useMemo } from "react";
2
2
  import type { TypeMenuItemProps } from "../MenuItem";
3
3
  import type { TypeMenuContext } from "../MenuContext";
4
+ import type { TypeMenuGroupProps } from "../MenuGroup/index";
4
5
 
5
6
  export interface TypeUseItemsFromChildrenProps {
6
7
  children: ReactNode;
7
8
  }
8
- export interface TypeItemElements {
9
+ export interface TypeItemElement {
9
10
  element: React.ReactElement<TypeMenuItemProps>;
10
11
  parents: readonly React.ReactNode[];
11
12
  }
13
+ export interface TypeMenuGroupElement {
14
+ element: React.ReactElement<TypeMenuGroupProps>;
15
+ }
12
16
 
13
17
  // https://stackoverflow.com/questions/69119016/find-specific-children-components-regardless-of-depth-in-react
14
18
  const walkAllChildren = (
@@ -43,12 +47,11 @@ const checkIfIsElement = (element: ReactNode, name: string): boolean => {
43
47
  };
44
48
 
45
49
  const getAllMenuItems = (children: ReactNode) => {
46
- const allMenuItems: TypeItemElements[] = [];
47
- // console.log("children", children);
50
+ const allMenuItems: TypeItemElement[] = [];
48
51
  walkAllChildren(children, (element, parents) => {
49
52
  if (checkIfIsElement(element, "MenuItem")) {
50
53
  allMenuItems.push({
51
- element: element as TypeItemElements["element"],
54
+ element: element as TypeItemElement["element"],
52
55
  parents,
53
56
  });
54
57
  }
@@ -63,12 +66,16 @@ export const useItemsFromChildren = ({
63
66
  const allMenuItemElements = getAllMenuItems(children);
64
67
  const menuItemsMap: TypeMenuContext["itemsMap"] = {};
65
68
  const allMenuItems: TypeMenuContext["items"] = allMenuItemElements.map(
66
- ({ element }, index) => {
69
+ ({ element, parents }, index) => {
70
+ const parentMenuGroup = parents.find((parent) =>
71
+ checkIfIsElement(parent, "MenuGroup")
72
+ ) as TypeMenuGroupElement["element"];
67
73
  const item = {
68
74
  id: element.props.id,
69
75
  disabled: element.props.disabled,
70
76
  index,
71
77
  menuItemProps: element.props,
78
+ menuGroupProps: parentMenuGroup ? parentMenuGroup.props : undefined,
72
79
  };
73
80
  menuItemsMap[`${item.id}`] = item;
74
81
  return item;
@@ -0,0 +1,26 @@
1
+ import { useCombobox } from "downshift";
2
+ import type { UseComboboxProps } from "downshift";
3
+ import type { TypeDefaultItemProps } from "../types";
4
+
5
+ export const useComboboxStateReducer: Required<
6
+ UseComboboxProps<TypeDefaultItemProps>
7
+ >["stateReducer"] = (state, actionAndChanges) => {
8
+ const { type, changes } = actionAndChanges;
9
+ switch (type) {
10
+ /** If downshift is blurred by tabbing within the menu, stay open */
11
+ // case useCombobox.stateChangeTypes.InputBlur:
12
+ // return {
13
+ // ...changes,
14
+ // // keep menu open on blur
15
+ // isOpen: true,
16
+ // };
17
+
18
+ /** Return standard change payload */
19
+ default:
20
+ return changes;
21
+ }
22
+ };
23
+
24
+ export const defaultUseComboboxProps = {
25
+ isItemDisabled: (item) => !!item.disabled,
26
+ };
package/src/types.ts CHANGED
@@ -1,10 +1,12 @@
1
1
  import type { TypeMenuItemProps } from "./MenuItem";
2
+ import type { TypeMenuGroupProps } from "./MenuGroup";
2
3
 
3
4
  export interface TypeDefaultItemProps {
4
5
  id: string;
5
6
  index: number;
6
7
  disabled?: boolean;
7
8
  menuItemProps?: TypeMenuItemProps;
9
+ menuGroupProps?: TypeMenuGroupProps;
8
10
  }
9
11
 
10
12
  export type TypeNotEmptyChildren =
@@ -1,84 +0,0 @@
1
- Feature: TokenInput
2
-
3
- A component that allows users to search, select, and manage tokens with autocomplete functionality.
4
-
5
- Rule: User behavior
6
-
7
- Background:
8
- Given an end user is using the component
9
-
10
- Scenario: User can add tokens
11
- Given the ComboBox accepts new values
12
- When text is typed
13
- And a delimiter key is pressed
14
- Then a new token is created from the input text
15
-
16
- Scenario: User can not add tokens
17
- Given the ComboBox does not accept new values
18
- When text is typed
19
- And a delimiter key is pressed
20
- Then no new token is added
21
-
22
- Scenario: User can remove tokens manually
23
- Given the ComboBox has tokens
24
- When token's remove button is clicked
25
- Then the token is removed
26
-
27
- Scenario: User can remove tokens with backspace
28
- Given the ComboBox has tokens
29
- When backspace is hit
30
- Then the token is removed
31
-
32
- Scenario: User can navigate tokens via keyboard
33
- Given the ComboBox has tokens
34
- And the Combobox Input has focus
35
- When the tokens are navigated using arrow keys
36
- Then the activeToken callback fires
37
-
38
- Scenario: Component handles token addition
39
- When a new token is added
40
- Then the onAddToken callback fires
41
-
42
- Scenario: Component handles token removal
43
- Given the ComboBox is rendered with tokens
44
- When a token is removed
45
- Then the onRemoveToken callback fires
46
-
47
- Scenario: Component handles token click
48
- When a token is clicked
49
- Then the onClickToken callback fires
50
-
51
- Scenario: Component handles input change
52
- When the input value changes
53
- Then the onChange callback fires
54
-
55
- Rule: API
56
- Background:
57
- Given an engineer is developing using the component
58
-
59
- Scenario: TokenInput extends Combobox
60
- When Combobox features are used in TokenInput
61
- Then the TokenInput responds as a Combobox would
62
-
63
- Scenario: ComboBox accepts tokens
64
- When a token component is passed to the Combobox
65
- And an item is selected
66
- Then the token is rendered in the input
67
-
68
- Scenario: ComboBox has a maximum token length
69
- When 'tokenMaxLength' is set to <value>
70
- Then the token string size is limited to <value>
71
-
72
- Scenario Outline: ComboBox checks its types
73
- When <propName> is passed a value
74
- Then ComboBox expects it to be of type <propType>
75
-
76
- Examples: Prop Types
77
- | activeToken | string |
78
- | tokens | array of { id: string, iconName?: EnumIconNames, value: string, valid?: boolean } |
79
- | onChangeTokens | function |
80
- | onAddToken | function |
81
- | onRemoveToken | function |
82
- | onClickToken | function |
83
- | maxLength | number |
84
- | tokenMaxLength | number |