@sproutsocial/seeds-react-menu 1.3.0 → 1.3.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sproutsocial/seeds-react-menu",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "description": "Seeds React Menu",
5
5
  "author": "Sprout Social, Inc.",
6
6
  "license": "MIT",
@@ -45,6 +45,7 @@ const MenuContent = <I extends TypeMenuItemProps = TypeMenuItemProps>({
45
45
  isListbox,
46
46
  hasVisibleItems,
47
47
  autoCloseOnEmpty,
48
+ isInitialized,
48
49
  } = useMenuContentContext();
49
50
  const toggleButtonProps = getToggleButtonProps?.();
50
51
 
@@ -53,7 +54,7 @@ const MenuContent = <I extends TypeMenuItemProps = TypeMenuItemProps>({
53
54
  ? childrenProp
54
55
  : mapMenuItems({ menuItems, MenuItemComponent });
55
56
 
56
- if (!hasVisibleItems && !autoCloseOnEmpty) {
57
+ if (isInitialized && !hasVisibleItems && !autoCloseOnEmpty) {
57
58
  return emptyStateElement ?? <DefaultMenuEmptyState />;
58
59
  }
59
60
 
@@ -122,6 +122,7 @@ export interface TypeMenuContext extends TypeMenuContextProps {
122
122
  props: Pick<TypeTokenInputProps, "inputProps"> &
123
123
  Parameters<Required<TypeMenuContextProps>["getInputProps"]>[0]
124
124
  ) => Omit<TypeDownshiftMergedInputProps<TypeTokenInputProps>, "onClick">;
125
+ isInitialized?: boolean;
125
126
  }
126
127
 
127
128
  export interface TypeMenuProviderProps {
@@ -152,6 +153,7 @@ export const defaultMenuContext: TypeMenuContext = {
152
153
  isItemSelected: () => false,
153
154
  getInputComponentProps: () => ({}),
154
155
  hasVisibleItems: false,
156
+ isInitialized: false,
155
157
  };
156
158
 
157
159
  export const getSelectedItemIds = <
@@ -253,8 +255,6 @@ export const useMenu = ({
253
255
  );
254
256
 
255
257
  const hasVisibleItems = useMemo(() => {
256
- // if (!items || items.length === 0) return false;
257
- // if (!rest.hiddenItemsMap) return true;
258
258
  return (hiddenItemsCount ?? 0) < items.length;
259
259
  }, [items, hiddenItemsCount]);
260
260
 
@@ -272,6 +272,7 @@ export const useMenu = ({
272
272
  contentRef,
273
273
  hasVisibleItems,
274
274
  autoCloseOnEmpty,
275
+ isInitialized: true,
275
276
  ...menuProps,
276
277
  };
277
278
  };
@@ -267,8 +267,11 @@ describe("MenuRoot", () => {
267
267
  const contextProps: TypeMenuContextProps = {
268
268
  toggleMenu: () => setIsOpen(!isOpen),
269
269
  getToggleButtonProps: (props) => ({
270
- onClick: () => setIsOpen(!isOpen),
271
270
  ...props,
271
+ onClick: () => {
272
+ props.onClick?.();
273
+ setIsOpen(!isOpen);
274
+ },
272
275
  }),
273
276
  isOpen,
274
277
  };
@@ -300,6 +303,50 @@ describe("MenuRoot", () => {
300
303
  expect(screen.getByText(randomText)).toBeInTheDocument();
301
304
  });
302
305
 
306
+ it("calls user-supplied onClick when MenuToggleButton is clicked", async () => {
307
+ const userOnClick = jest.fn();
308
+
309
+ const TestCustomMenu = () => {
310
+ const [isOpen, setIsOpen] = useState(false);
311
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
312
+ // @ts-ignore - mock contextProps may be imperfect
313
+ const contextProps: TypeMenuContextProps = {
314
+ toggleMenu: () => setIsOpen(!isOpen),
315
+ getToggleButtonProps: (props) => ({
316
+ ...props,
317
+ onClick: (e) => {
318
+ props.onClick?.(e);
319
+ setIsOpen(!isOpen);
320
+ },
321
+ }),
322
+ isOpen,
323
+ };
324
+
325
+ return (
326
+ <MenuRoot
327
+ menuToggleElement={
328
+ <MenuToggleButton onClick={userOnClick} appearance="secondary">
329
+ Open Menu
330
+ </MenuToggleButton>
331
+ }
332
+ {...testDefaultContext(contextProps)}
333
+ >
334
+ Menu Content
335
+ </MenuRoot>
336
+ );
337
+ };
338
+
339
+ await actRender(<TestCustomMenu />);
340
+
341
+ const button = screen.getByRole("button", { name: /open menu/i });
342
+
343
+ await act(async () => {
344
+ fireEvent.click(button);
345
+ });
346
+
347
+ expect(userOnClick).toHaveBeenCalledTimes(1);
348
+ });
349
+
303
350
  const contextTests: {
304
351
  child: string;
305
352
  TestComponent: React.FC;
@@ -1,5 +1,6 @@
1
1
  import { Button, type TypeButtonProps } from "@sproutsocial/seeds-react-button";
2
2
  import { useMenuToggleContext } from "../MenuContext";
3
+ import type React from "react";
3
4
 
4
5
  export interface TypeMenuToggleButtonProps extends TypeButtonProps {}
5
6
 
@@ -13,17 +14,35 @@ export interface TypeMenuToggleButtonProps extends TypeButtonProps {}
13
14
  export const MenuToggleButton = (props: TypeMenuToggleButtonProps) => {
14
15
  const { getToggleButtonProps, isListbox, getDropdownProps, ref, ariaProps } =
15
16
  useMenuToggleContext();
17
+
18
+ const {
19
+ onClick: userOnClick,
20
+ onKeyDown: userOnKeyDown,
21
+ onBlur: userOnBlur,
22
+ ...restProps
23
+ } = props;
24
+
25
+ const defaultDropdownProps = {
26
+ ref,
27
+ onClick: userOnClick,
28
+ onKeyDown: userOnKeyDown,
29
+ onBlur: userOnBlur,
30
+ };
31
+
32
+ const dropdownProps =
33
+ getDropdownProps?.({
34
+ ...defaultDropdownProps,
35
+ preventKeyAction: true,
36
+ }) ?? defaultDropdownProps;
37
+
38
+ const toggleButtonProps = getToggleButtonProps?.({
39
+ ...dropdownProps,
40
+ refKey: "innerRef",
41
+ ...ariaProps,
42
+ ...(isListbox ? {} : { role: "button", "aria-haspopup": "menu" }),
43
+ });
44
+
16
45
  return (
17
- <Button
18
- appearance="secondary"
19
- {...getToggleButtonProps?.({
20
- // Decide if we want the preventKeyAction stuff. Maybe create a prop to allow for this to be overriden.
21
- ...(getDropdownProps?.({ ref, preventKeyAction: true }) || { ref }),
22
- refKey: "innerRef",
23
- ...ariaProps,
24
- ...(isListbox ? {} : { role: "button", "aria-haspopup": "menu" }),
25
- })}
26
- {...props}
27
- />
46
+ <Button appearance="secondary" {...toggleButtonProps} {...restProps} />
28
47
  );
29
48
  };
@@ -29,9 +29,9 @@ export const testDefaultContext = ({
29
29
  // @ts-expect-error - mocked function does not match real type
30
30
  getToggleButtonProps: jest.fn(({ ref, refKey = "ref", ...props } = {}) => ({
31
31
  onKeyDown: jest.fn(),
32
- ...getToggleButtonProps?.({ ref, refKey, ...props }),
33
32
  ...(ref ? { [refKey]: ref } : {}),
34
33
  ...props,
34
+ ...getToggleButtonProps?.({ ref, refKey, ...props }),
35
35
  })),
36
36
  // @ts-expect-error - mocked function does not match real type
37
37
  getMenuProps: jest.fn((props) => props),