@sproutsocial/seeds-react-menu 0.2.1 → 0.2.3

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 (44) hide show
  1. package/.eslintrc.js +2 -6
  2. package/.turbo/turbo-build.log +10 -10
  3. package/CHANGELOG.md +13 -0
  4. package/dist/esm/index.js +12 -12
  5. package/dist/esm/index.js.map +1 -1
  6. package/dist/index.d.mts +34 -12
  7. package/dist/index.d.ts +34 -12
  8. package/dist/index.js +7 -11
  9. package/dist/index.js.map +1 -1
  10. package/package.json +3 -5
  11. package/src/ActionMenu/ActionMenu.tsx +4 -19
  12. package/src/ActionMenu/ActionMenuTypes.ts +12 -0
  13. package/src/ActionMenu/index.ts +1 -0
  14. package/src/{ComboBox/ComboBox.feature → Autocomplete/Autocomplete.feature} +16 -16
  15. package/src/Autocomplete/Autocomplete.tsx +65 -0
  16. package/src/Autocomplete/AutocompleteTypes.ts +8 -0
  17. package/src/Autocomplete/__tests__/Autocomplete.test.tsx +113 -0
  18. package/src/Autocomplete/__tests__/Autocomplete.typetest.tsx +78 -0
  19. package/src/Autocomplete/index.ts +2 -0
  20. package/src/MenuContent/MenuContent.tsx +1 -1
  21. package/src/MenuContent/MenuContentTypes.ts +2 -1
  22. package/src/MenuContext.tsx +71 -10
  23. package/src/MenuGroup/MenuGroupTypes.ts +0 -1
  24. package/src/MenuGroup/__tests__/MenuGroup.test.tsx +0 -1
  25. package/src/MenuHeader/__tests__/MenuHeader.typetest.tsx +0 -1
  26. package/src/MenuItem/MenuItem.tsx +0 -4
  27. package/src/MenuItem/__tests__/MenuItem.test.tsx +4 -1
  28. package/src/MenuItem/styles.tsx +1 -1
  29. package/src/MenuItem/useOptionProps.tsx +0 -1
  30. package/src/MenuRoot/MenuRoot.tsx +2 -2
  31. package/src/MenuRoot/__tests__/MenuRoot.test.tsx +1 -2
  32. package/src/MultiSelectMenu/MultiSelectMenu.tsx +0 -1
  33. package/src/MultiSelectMenu/MultiSelectMenuTypes.ts +5 -1
  34. package/src/MultiSelectMenu/__tests__/MultiSelectMenu.test.tsx +5 -2
  35. package/src/MultiSelectMenu/index.ts +1 -0
  36. package/src/SingleSelectMenu/SingleSelectMenu.tsx +0 -1
  37. package/src/SingleSelectMenu/SingleSelectMenuTypes.ts +2 -3
  38. package/src/hooks/useItemsFromChildren.tsx +7 -5
  39. package/src/index.ts +1 -0
  40. package/src/reducers/useComboboxStateReducer.tsx +26 -0
  41. package/src/types.ts +2 -5
  42. package/tsconfig.json +1 -1
  43. package/.eslintignore +0 -6
  44. package/src/ComboBox/TokenInput.feature +0 -84
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sproutsocial/seeds-react-menu",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Seeds React Menu",
5
5
  "author": "Sprout Social, Inc.",
6
6
  "license": "MIT",
@@ -14,9 +14,7 @@
14
14
  "clean:modules": "rm -rf node_modules",
15
15
  "typecheck": "tsc --noEmit",
16
16
  "test": "jest",
17
- "test:watch": "jest --watch --coverage=false",
18
- "lint": "eslint . --ext .js,.jsx,.ts,.tsx",
19
- "lint:fix": "eslint . --ext .js,.jsx,.ts,.tsx --fix"
17
+ "test:watch": "jest --watch --coverage=false"
20
18
  },
21
19
  "dependencies": {
22
20
  "@sproutsocial/seeds-react-box": "*",
@@ -32,7 +30,7 @@
32
30
  "motion": "^11.11.17"
33
31
  },
34
32
  "devDependencies": {
35
- "eslint-config-seeds": "*",
33
+ "@sproutsocial/eslint-config-seeds": "*",
36
34
  "@sproutsocial/seeds-react-testing-library": "*",
37
35
  "@sproutsocial/seeds-testing": "*",
38
36
  "@types/react": "^18.0.0",
@@ -1,7 +1,6 @@
1
1
  import React, { useEffect } from "react";
2
2
  import {
3
3
  useSelect,
4
- type UseSelectProps,
5
4
  type UseSelectState,
6
5
  type UseSelectStateChange,
7
6
  } from "downshift";
@@ -11,22 +10,9 @@ import {
11
10
  defaultUseSelectProps,
12
11
  useSelectStateReducer,
13
12
  } from "../reducers/useSelectStateReducer";
14
- import {
15
- MenuRoot,
16
- type TypeMenuRootProps,
17
- type TypeMenuRootOwnProps,
18
- } from "../MenuRoot";
13
+ import { MenuRoot } from "../MenuRoot";
19
14
  import { itemToString } from "../utils/itemToString";
20
-
21
- type TypeInvalidMenuProps =
22
- | "initialSelectedItem"
23
- | "defaultSelectedItem"
24
- | "selectedItem";
25
- export interface TypeActionMenuProps
26
- extends TypeMenuRootOwnProps,
27
- Partial<Omit<UseSelectProps<TypeDefaultItemProps>, TypeInvalidMenuProps>> {
28
- children: TypeMenuRootProps["children"];
29
- }
15
+ import type { TypeActionMenuProps } from "./ActionMenuTypes";
30
16
 
31
17
  // Manually fire the onClick handler when Enter or Space is pressed
32
18
  const handleStateChange = (
@@ -49,7 +35,7 @@ const handleStateChange = (
49
35
  if (!selectedItem) return;
50
36
 
51
37
  // if selectedItem is disabled, do nothing
52
- if (selectedItem.element?.props?.disabled) {
38
+ if (selectedItem.disabled) {
53
39
  return;
54
40
  }
55
41
 
@@ -58,7 +44,7 @@ const handleStateChange = (
58
44
  changes.type === useSelect.stateChangeTypes.ToggleButtonKeyDownEnter;
59
45
 
60
46
  // selectedItem props
61
- const selectedItemProps = selectedItem.element?.props;
47
+ const selectedItemProps = selectedItem.menuItemProps;
62
48
 
63
49
  if (selectedItemProps?.onClick) {
64
50
  // Create a synthetic event to pass into the onClick
@@ -111,7 +97,6 @@ export const ActionMenu = ({
111
97
  onStateChange: (changes) => handleStateChange(changes, currentSelectedItem),
112
98
  stateReducer: (state, actionAndChanges) => {
113
99
  let newState: Partial<UseSelectState<TypeDefaultItemProps>>;
114
- // eslint-disable-next-line prefer-const, react-hooks/rules-of-hooks
115
100
  newState = useSelectStateReducer(state, actionAndChanges);
116
101
  return externalStateReducer
117
102
  ? externalStateReducer(state, {
@@ -0,0 +1,12 @@
1
+ import { type TypeMenuRootProps, type TypeMenuRootOwnProps } from "../MenuRoot";
2
+ import type { TypeDownshiftSelectProps } from "../MenuContext";
3
+
4
+ type TypeInvalidMenuProps =
5
+ | "initialSelectedItem"
6
+ | "defaultSelectedItem"
7
+ | "selectedItem";
8
+ export interface TypeActionMenuProps
9
+ extends TypeMenuRootOwnProps,
10
+ Omit<TypeDownshiftSelectProps, TypeInvalidMenuProps> {
11
+ children: TypeMenuRootProps["children"];
12
+ }
@@ -1 +1,2 @@
1
1
  export * from "./ActionMenu";
2
+ export * from "./ActionMenuTypes";
@@ -1,4 +1,4 @@
1
- Feature: ComboBox
1
+ Feature: Autocomplete
2
2
 
3
3
  A component that allows users to search, select, and manage items with autocomplete functionality.
4
4
 
@@ -8,31 +8,31 @@ Feature: ComboBox
8
8
  Given an end user is using the component
9
9
 
10
10
  Scenario: User can autocomplete text inputs
11
- Given the ComboBox Input is focused
11
+ Given the Autocomplete Input is focused
12
12
  When text is typed into the input
13
13
  Then a list of matching items is displayed
14
14
  And an item can be selected from the list
15
15
 
16
16
  Scenario: User can add a new value
17
- Given the Combobox Input has a value
17
+ Given the Autocomplete Input has a value
18
18
  When new values are allowed
19
19
  And the value has no match
20
20
  Then a new value can be created
21
21
 
22
22
  Scenario: User gets a warning on error
23
- Given the Combobox Input has a value
23
+ Given the Autocomplete Input has a value
24
24
  When the value is invalid
25
25
  Then a warning message appears
26
26
 
27
27
  Scenario: User can select an item from the list
28
- Given the ComboBox Input has text
28
+ Given the Autocomplete Input has text
29
29
  When an item is selected
30
30
  Then the item is marked as selected
31
31
  And the input reflects the selected item
32
32
 
33
33
  Scenario: Screen reader announces combobox
34
34
  Given a screen reader is being used
35
- When the Combobox input is focused
35
+ When the Autocomplete input is focused
36
36
  Then the screen reader announces it as a "combobox"
37
37
 
38
38
  Scenario: Component handles input change
@@ -40,7 +40,7 @@ Feature: ComboBox
40
40
  Then the onChange callback fires
41
41
 
42
42
  Scenario Outline: Custom open/close behavior
43
- Given the ComboBox is rendered with custom behavior
43
+ Given the Autocomplete is rendered with custom behavior
44
44
  When the `<action>` is performed
45
45
  Then the `<handler>` is executed
46
46
 
@@ -51,7 +51,7 @@ Feature: ComboBox
51
51
  | input blurred | onBlur |
52
52
 
53
53
  Scenario Outline: Autocomplete suggestions are displayed
54
- Given the ComboBox is rendered with items
54
+ Given the Autocomplete is rendered with items
55
55
  When <text> is typed into the input
56
56
  Then matching <suggestion> items are displayed
57
57
 
@@ -65,21 +65,21 @@ Feature: ComboBox
65
65
  Background:
66
66
  Given an engineer is developing using the component
67
67
 
68
- Scenario: ComboBox extends Menu
69
- When Menu features are used in ComboBox
70
- Then the ComboBox responds as a Menu would
68
+ Scenario: Autocomplete extends Menu
69
+ When Menu features are used in Autocomplete
70
+ Then the Autocomplete responds as a Menu would
71
71
 
72
- Scenario: ComboBox accepts selected item
73
- When a selected item is passed to the ComboBox
72
+ Scenario: Autocomplete accepts selected item
73
+ When a selected item is passed to the Autocomplete
74
74
  Then the input reflects the selected item
75
75
 
76
- Scenario: ComboBox hasWarning
76
+ Scenario: Autocomplete hasWarning
77
77
  When the input's validation has an error
78
78
  Then 'hasWarning' becomes true
79
79
 
80
- Scenario Outline: ComboBox checks its types
80
+ Scenario Outline: Autocomplete checks its types
81
81
  When <propName> is passed a value
82
- Then ComboBox expects it to be of type <propType>
82
+ Then Autocomplete expects it to be of type <propType>
83
83
 
84
84
  Examples: Prop Types
85
85
  | propName | propType |
@@ -0,0 +1,65 @@
1
+ import React from "react";
2
+ import { useCombobox, type UseComboboxState } from "downshift";
3
+ import { useItemsFromChildren } from "../hooks/useItemsFromChildren";
4
+ import {
5
+ defaultUseComboboxProps,
6
+ useComboboxStateReducer,
7
+ } from "../reducers/useComboboxStateReducer";
8
+ import { itemToString } from "../utils/itemToString";
9
+ import { MenuRoot } from "../MenuRoot";
10
+ import type { TypeAutocompleteProps } from "./AutocompleteTypes";
11
+ import type { TypeDefaultItemProps } from "../types";
12
+
13
+ /**
14
+ * @link https://seeds.sproutsocial.com/components/multiselect-menu/
15
+ *
16
+ * @description A Autocomplete allows multiple {@link https://github.com/sproutsocial/seeds/tree/dev/seeds-react/seeds-react-menu/src/MenuItem | MenuItems} to be selected from a list or groups of lists.
17
+ *
18
+ * @see {@link https://seeds.sproutsocial.com/components/menu-group/ | MenuGroup}
19
+ */
20
+
21
+ export const Autocomplete = ({
22
+ children,
23
+ stateReducer: externalStateReducer,
24
+ ...useComboboxProps
25
+ }: TypeAutocompleteProps) => {
26
+ const { allMenuItems, menuItemsMap } = useItemsFromChildren({ children });
27
+
28
+ const { isOpen, ...useComboboxReturnProps } = useCombobox({
29
+ ...defaultUseComboboxProps,
30
+ // onInputValueChange({ inputValue }) {
31
+ // setItems(books.filter(getBooksFilter(inputValue)));
32
+ // },
33
+ items: allMenuItems,
34
+ itemToString,
35
+ selectedItem: null,
36
+ defaultHighlightedIndex: 0,
37
+ stateReducer: (state, actionAndChanges) => {
38
+ let newState: Partial<UseComboboxState<TypeDefaultItemProps>>;
39
+ newState = useComboboxStateReducer(state, actionAndChanges);
40
+ return externalStateReducer
41
+ ? externalStateReducer(state, {
42
+ ...actionAndChanges,
43
+ changes: newState,
44
+ })
45
+ : newState;
46
+ },
47
+ ...useCombobox,
48
+ });
49
+
50
+ return (
51
+ <MenuRoot
52
+ {...{
53
+ isListbox: true,
54
+ items: allMenuItems,
55
+ itemsMap: menuItemsMap,
56
+ itemToString,
57
+ isOpen,
58
+ ...useComboboxProps,
59
+ ...useComboboxReturnProps,
60
+ }}
61
+ >
62
+ {children}
63
+ </MenuRoot>
64
+ );
65
+ };
@@ -0,0 +1,8 @@
1
+ import type { TypeMenuRootProps, TypeMenuRootOwnProps } from "../MenuRoot";
2
+ import type { TypeDownshiftComboboxProps } from "../MenuContext";
3
+
4
+ export interface TypeAutocompleteProps
5
+ extends TypeMenuRootOwnProps,
6
+ TypeDownshiftComboboxProps {
7
+ children: TypeMenuRootProps["children"];
8
+ }
@@ -0,0 +1,113 @@
1
+ // import React from "react";
2
+ // import {
3
+ // render,
4
+ // screen,
5
+ // fireEvent,
6
+ // waitFor,
7
+ // } from "@sproutsocial/seeds-react-testing-library";
8
+ // import { Autocomplete } from "../Autocomplete";
9
+
10
+ xdescribe("Autocomplete Component", () => {
11
+ it("should render the Autocomplete component", () => {
12
+ // render(<Autocomplete />);
13
+ // const autocompleteElement = screen.getByRole("combobox");
14
+ // expect(autocompleteElement).toBeInTheDocument();
15
+ });
16
+
17
+ it("should display suggestions when typing", async () => {
18
+ // render(<Autocomplete />);
19
+ // const inputElement = screen.getByRole("combobox");
20
+ // fireEvent.change(inputElement, { target: { value: "test" } });
21
+ // await waitFor(() => {
22
+ // const suggestions = screen.getAllByRole("option");
23
+ // expect(suggestions.length).toBeGreaterThan(0);
24
+ // });
25
+ });
26
+
27
+ it("should select a suggestion when clicked", async () => {
28
+ // render(<Autocomplete />);
29
+ // const inputElement = screen.getByRole("combobox");
30
+ // fireEvent.change(inputElement, { target: { value: "test" } });
31
+ // await waitFor(() => {
32
+ // const suggestion = screen.getByText("test suggestion");
33
+ // fireEvent.click(suggestion);
34
+ // expect(inputElement.value).toBe("test suggestion");
35
+ // });
36
+ });
37
+
38
+ it("should allow adding a new value when no match is found", async () => {
39
+ // render(<Autocomplete allowNewValues />);
40
+ // const inputElement = screen.getByRole("combobox");
41
+ // fireEvent.change(inputElement, { target: { value: "new value" } });
42
+ // await waitFor(() => {
43
+ // const newValueOption = screen.getByText('Add "new value"');
44
+ // fireEvent.click(newValueOption);
45
+ // expect(inputElement.value).toBe("new value");
46
+ // });
47
+ });
48
+
49
+ it("should display a warning message when the value is invalid", async () => {
50
+ // render(<Autocomplete />);
51
+ // const inputElement = screen.getByRole("combobox");
52
+ // fireEvent.change(inputElement, { target: { value: "invalid value" } });
53
+ // await waitFor(() => {
54
+ // const warningMessage = screen.getByText("Invalid value");
55
+ // expect(warningMessage).toBeInTheDocument();
56
+ // });
57
+ });
58
+
59
+ it("should mark an item as selected and reflect it in the input", async () => {
60
+ // render(<Autocomplete />);
61
+ // const inputElement = screen.getByRole("combobox");
62
+ // fireEvent.change(inputElement, { target: { value: "test" } });
63
+ // await waitFor(() => {
64
+ // const suggestion = screen.getByText("test suggestion");
65
+ // fireEvent.click(suggestion);
66
+ // expect(inputElement.value).toBe("test suggestion");
67
+ // expect(suggestion).toHaveAttribute("aria-selected", "true");
68
+ // });
69
+ });
70
+
71
+ it('should announce the input as a "combobox" for screen readers', () => {
72
+ // render(<Autocomplete />);
73
+ // const inputElement = screen.getByRole("combobox");
74
+ // expect(inputElement).toHaveAttribute("aria-haspopup", "listbox");
75
+ });
76
+
77
+ it("should fire the onChange callback when the input value changes", () => {
78
+ // const handleChange = jest.fn();
79
+ // render(<Autocomplete onChange={handleChange} />);
80
+ // const inputElement = screen.getByRole("combobox");
81
+ // fireEvent.change(inputElement, { target: { value: "test" } });
82
+ // expect(handleChange).toHaveBeenCalledWith("test");
83
+ });
84
+
85
+ it.each`
86
+ action | handler
87
+ ${"item selected"} | ${"onSelect"}
88
+ ${"input focused"} | ${"onFocus"}
89
+ ${"input blurred"} | ${"onBlur"}
90
+ `(
91
+ "should execute $handler when $action is performed",
92
+ async ({ action, handler }) => {
93
+ // const handlerMock = jest.fn();
94
+ // const props = { [handler]: handlerMock };
95
+ // render(<Autocomplete {...props} />);
96
+ // const inputElement = screen.getByRole("combobox");
97
+ // if (action === "item selected") {
98
+ // fireEvent.change(inputElement, { target: { value: "test" } });
99
+ // await waitFor(() => {
100
+ // const suggestion = screen.getByText("test suggestion");
101
+ // fireEvent.click(suggestion);
102
+ // expect(handlerMock).toHaveBeenCalled();
103
+ // });
104
+ // } else if (action === "input focused") {
105
+ // fireEvent.focus(inputElement);
106
+ // expect(handlerMock).toHaveBeenCalled();
107
+ // } else if (action === "input blurred") {
108
+ // fireEvent.blur(inputElement);
109
+ // expect(handlerMock).toHaveBeenCalled();
110
+ // }
111
+ }
112
+ );
113
+ });
@@ -0,0 +1,78 @@
1
+ import * as React from "react";
2
+ import {
3
+ MultiSelectMenu,
4
+ MenuContent,
5
+ MenuHeader,
6
+ MenuFooter,
7
+ } from "../../index";
8
+
9
+ export const MultiSelectMenuTypeTest = () => (
10
+ <>
11
+ {/* Valid MultiSelectMenu with minimal required props */}
12
+ <MultiSelectMenu menuToggleElement={<>test</>}>Test</MultiSelectMenu>
13
+ {/* Valid MultiSelectMenu allows MenuContent by itself as a child */}
14
+ <MultiSelectMenu menuToggleElement={<>test</>}>
15
+ <MenuContent>Test</MenuContent>
16
+ </MultiSelectMenu>
17
+ {/* Valid MultiSelectMenu allows multiple children without fragment */}
18
+ <MultiSelectMenu menuToggleElement={<>test</>}>
19
+ <MenuHeader>Test</MenuHeader>
20
+ <MenuContent>Test</MenuContent>
21
+ <MenuFooter>Test</MenuFooter>
22
+ </MultiSelectMenu>
23
+ {/* Valid MultiSelectMenu accepts Downshift props and functions for useSelect */}
24
+ <MultiSelectMenu
25
+ menuToggleElement={<>test</>}
26
+ onSelectedItemChange={() => {}}
27
+ selectedItem={null}
28
+ stateReducer={(c) => c}
29
+ onStateChange={() => {}}
30
+ getA11yStatusMessage={() => ""}
31
+ isOpen
32
+ >
33
+ Test
34
+ </MultiSelectMenu>
35
+ {/* Valid MultiSelectMenu accepts Downshift props and functions for useMultipleSelection */}
36
+ <MultiSelectMenu
37
+ menuToggleElement={<>test</>}
38
+ onSelectedItemsChange={() => {}}
39
+ selectedItems={[]}
40
+ multiSelectStateReducer={(c) => c}
41
+ onMultiSelectStateChange={() => {}}
42
+ getA11yMultiStatusMessage={() => ""}
43
+ >
44
+ Test
45
+ </MultiSelectMenu>
46
+
47
+ {/* @ts-expect-error - menuToggleElement is a required prop */}
48
+ <MultiSelectMenu>Test</MultiSelectMenu>
49
+ <MultiSelectMenu
50
+ /* @ts-expect-error - menuToggleElement must be a valid react element */
51
+ menuToggleElement="test"
52
+ >
53
+ Test
54
+ </MultiSelectMenu>
55
+ {/* @ts-expect-error - when no children provided */}
56
+ <MultiSelectMenu menuToggleElement="test" />
57
+
58
+ <MultiSelectMenu
59
+ menuToggleElement={<>test</>}
60
+ /* @ts-expect-error - isOpen can't be a string */
61
+ isOpen="false"
62
+ /* @ts-expect-error - onSelectedItemChange can't be a string */
63
+ onSelectedItemChange="test"
64
+ /* @ts-expect-error - selectedItem can't be a string */
65
+ selectedItem="test"
66
+ /* @ts-expect-error - stateReducer must have a return value */
67
+ stateReducer={() => {}}
68
+ /* @ts-expect-error - onMultiSelectStateChange can't be a string */
69
+ onMultiSelectStateChange="test"
70
+ /* @ts-expect-error - selectedItems can't be a string */
71
+ selectedItems="test"
72
+ /* @ts-expect-error - multiSelectStateReducer must have a return value */
73
+ multiSelectStateReducer="test"
74
+ >
75
+ Test
76
+ </MultiSelectMenu>
77
+ </>
78
+ );
@@ -0,0 +1,2 @@
1
+ export * from "./Autocomplete";
2
+ export * from "./AutocompleteTypes";
@@ -38,7 +38,7 @@ const MenuContent = ({
38
38
  id={id}
39
39
  tabIndex={0}
40
40
  // apply onKeyDown logic for downshift keyboard accessibility support
41
- onKeyDown={getToggleButtonProps?.().onKeyDown}
41
+ onKeyDown={getToggleButtonProps?.()["onKeyDown"] || undefined}
42
42
  // apply active descendent updates to container for proper accessibility
43
43
  aria-activedescendant={getToggleButtonProps?.()["aria-activedescendant"]}
44
44
  {...getMenuProps?.(
@@ -1,5 +1,6 @@
1
+ import React from "react";
1
2
  import { type HTMLMotionProps } from "motion/react";
2
- import type { ReactNode } from "react";
3
+ import type { ReactNode, RefObject } from "react";
3
4
 
4
5
  export interface TypeMenuContentProps extends HTMLMotionProps<"ul"> {
5
6
  children: ReactNode;
@@ -1,5 +1,12 @@
1
- import React, { createContext, useMemo, useContext } from "react";
1
+ import React, {
2
+ createContext,
3
+ useMemo,
4
+ useContext,
5
+ type RefObject,
6
+ } from "react";
2
7
  import type {
8
+ UseComboboxReturnValue,
9
+ UseComboboxProps,
3
10
  UseSelectReturnValue,
4
11
  UseSelectProps,
5
12
  UseMultipleSelectionReturnValue,
@@ -7,26 +14,80 @@ import type {
7
14
  } from "downshift";
8
15
  import type { TypeDefaultItemProps, TypeNotEmptyChildren } from "./types";
9
16
 
17
+ export interface TypeDownshiftComboboxProps
18
+ extends Partial<UseComboboxProps<TypeDefaultItemProps>> {}
19
+
20
+ export interface TypeDownshiftSelectProps
21
+ extends Partial<UseSelectProps<TypeDefaultItemProps>> {}
22
+
10
23
  export interface TypeDownshiftMultiSelectProps
11
- extends Partial<UseSelectProps<TypeDefaultItemProps>>,
12
- Partial<
13
- Omit<
14
- UseMultipleSelectionProps<TypeDefaultItemProps>,
15
- "stateReducer" | "onStateChange" | "getA11yStatusMessage"
16
- >
17
- > {
24
+ extends Partial<
25
+ Omit<
26
+ UseMultipleSelectionProps<TypeDefaultItemProps>,
27
+ "stateReducer" | "onStateChange" | "getA11yStatusMessage"
28
+ >
29
+ > {
18
30
  // Multi Select Props with conflicting useSelect prop names
19
31
  getA11yMultiStatusMessage?: UseMultipleSelectionProps<TypeDefaultItemProps>["getA11yStatusMessage"];
20
32
  onMultiSelectStateChange?: UseMultipleSelectionProps<TypeDefaultItemProps>["onStateChange"];
21
33
  multiSelectStateReducer?: UseMultipleSelectionProps<TypeDefaultItemProps>["stateReducer"];
22
34
  }
23
35
 
36
+ export interface TypeDownshiftComboboxReturnValue
37
+ extends Partial<UseComboboxReturnValue<TypeDefaultItemProps>> {}
38
+
39
+ export interface TypeDownshiftSelectReturnValue
40
+ extends Partial<UseSelectReturnValue<TypeDefaultItemProps>> {}
41
+
42
+ export interface TypeDownshiftMultiSelectReturnValue
43
+ extends Partial<UseMultipleSelectionReturnValue<TypeDefaultItemProps>> {}
44
+
45
+ type TypeDonwshiftSharedReturnValue =
46
+ | "getLabelProps"
47
+ | "getToggleButtonProps"
48
+ | "getMenuProps";
49
+ type TypeDonwshiftSharedProps =
50
+ | "onHighlightedIndexChange"
51
+ | "onIsOpenChange"
52
+ | "onSelectedItemChange"
53
+ | "onStateChange"
54
+ | "stateReducer";
24
55
  export interface TypeMenuContextProps
25
- extends Partial<UseSelectReturnValue<TypeDefaultItemProps>>,
26
- Partial<UseMultipleSelectionReturnValue<TypeDefaultItemProps>>,
56
+ extends Omit<
57
+ TypeDownshiftComboboxReturnValue,
58
+ TypeDonwshiftSharedReturnValue
59
+ >,
60
+ Omit<TypeDownshiftComboboxProps, TypeDonwshiftSharedProps>,
61
+ Omit<TypeDownshiftSelectReturnValue, TypeDonwshiftSharedReturnValue>,
62
+ Omit<TypeDownshiftSelectProps, TypeDonwshiftSharedProps>,
63
+ TypeDownshiftMultiSelectReturnValue,
27
64
  TypeDownshiftMultiSelectProps {
28
65
  items: TypeDefaultItemProps[];
29
66
  isListbox?: boolean;
67
+ onHighlightedIndexChange?:
68
+ | TypeDownshiftComboboxProps["onHighlightedIndexChange"]
69
+ | TypeDownshiftSelectProps["onHighlightedIndexChange"];
70
+ onIsOpenChange?:
71
+ | TypeDownshiftComboboxProps["onIsOpenChange"]
72
+ | TypeDownshiftSelectProps["onIsOpenChange"];
73
+ onSelectedItemChange?:
74
+ | TypeDownshiftComboboxProps["onSelectedItemChange"]
75
+ | TypeDownshiftSelectProps["onSelectedItemChange"];
76
+ onStateChange?:
77
+ | TypeDownshiftComboboxProps["onStateChange"]
78
+ | TypeDownshiftSelectProps["onStateChange"];
79
+ stateReducer?:
80
+ | TypeDownshiftComboboxProps["stateReducer"]
81
+ | TypeDownshiftSelectProps["stateReducer"];
82
+ getLabelProps?:
83
+ | TypeDownshiftComboboxReturnValue["getLabelProps"]
84
+ | TypeDownshiftSelectReturnValue["getLabelProps"];
85
+ getToggleButtonProps?:
86
+ | TypeDownshiftComboboxReturnValue["getToggleButtonProps"]
87
+ | TypeDownshiftSelectReturnValue["getToggleButtonProps"];
88
+ getMenuProps?:
89
+ | TypeDownshiftComboboxReturnValue["getMenuProps"]
90
+ | TypeDownshiftSelectReturnValue["getMenuProps"];
30
91
  }
31
92
 
32
93
  export interface TypeMenuContext extends TypeMenuContextProps {
@@ -25,6 +25,5 @@ export interface TypeMenuGroupProps
25
25
  Omit<React.ComponentPropsWithoutRef<"ul">, "color"> {
26
26
  title?: string;
27
27
  children: ReactNode;
28
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
29
28
  titleAs?: string | React.ComponentType<any>;
30
29
  }
@@ -44,7 +44,6 @@ describe("MenuGroup", () => {
44
44
  const menuGroup = screen.getByRole("group", { name: menuTitle });
45
45
  expect(menuGroup).toBeInTheDocument();
46
46
  expect(
47
- // eslint-disable-next-line testing-library/prefer-presence-queries
48
47
  screen.queryByRole("heading", { name: menuTitle })
49
48
  ).toBeInTheDocument();
50
49
  });
@@ -6,7 +6,6 @@ export const MenuHeaderTypeTest = () => (
6
6
  title="Menu Header"
7
7
  leftAction={<div>left action</div>}
8
8
  rightAction={<div>right action</div>}
9
- // eslint-disable-next-line react/no-children-prop
10
9
  children={<div>children</div>}
11
10
  />
12
11
  {/* left and right action accept strings */}
@@ -17,12 +17,9 @@ export const MenuItem = ({
17
17
  children,
18
18
  onClick,
19
19
  onKeyDown,
20
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
21
20
  disabled = false,
22
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
23
21
  ref,
24
22
  href,
25
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
26
23
  color,
27
24
  ...props
28
25
  }: TypeMenuItemProps) => {
@@ -53,7 +50,6 @@ export const MenuItem = ({
53
50
  // use anchor tag if href is provided and button for menuitem
54
51
  as={isListbox ? undefined : href ? "a" : "button"}
55
52
  href={href}
56
- // eslint-disable-next-line react/no-children-prop
57
53
  children={children}
58
54
  {...optionProps}
59
55
  {...props}
@@ -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", {