@sproutsocial/seeds-react-menu 1.6.0 → 1.6.1

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.6.0",
3
+ "version": "1.6.1",
4
4
  "description": "Seeds React Menu",
5
5
  "author": "Sprout Social, Inc.",
6
6
  "license": "MIT",
@@ -65,11 +65,22 @@ const handleOnClick = (e: React.MouseEvent<HTMLElement>, itemName: string) => {
65
65
  action(`${itemName}`)(e);
66
66
  };
67
67
 
68
- export const ActionMenuSimple: Story = {
68
+ type ActionMenuSimpleStoryArgs = ActionMenuStoryArgs & {
69
+ "Show Conditional Item"?: boolean;
70
+ };
71
+
72
+ export const ActionMenuSimple: StoryObj<ActionMenuSimpleStoryArgs> = {
69
73
  name: "Simple Action Menu",
70
74
  args: {
71
75
  "Trigger Text": "ActionMenu",
72
76
  "Trigger Appearance": "secondary",
77
+ "Show Conditional Item": false,
78
+ },
79
+ argTypes: {
80
+ "Show Conditional Item": {
81
+ control: { type: "boolean" },
82
+ description: "Toggle to show/hide the conditional menu item",
83
+ },
73
84
  },
74
85
  render: (args) => {
75
86
  return (
@@ -106,6 +117,14 @@ export const ActionMenuSimple: Story = {
106
117
  >
107
118
  This Opens a Link
108
119
  </MenuItem>
120
+ {args["Show Conditional Item"] && (
121
+ <MenuItem
122
+ onClick={(e) => handleOnClick(e, "Conditional Menu Item")}
123
+ id="conditional"
124
+ >
125
+ Conditional Menu Item
126
+ </MenuItem>
127
+ )}
109
128
  </MenuGroup>
110
129
  </MenuContent>
111
130
  </ActionMenu>
@@ -8,6 +8,7 @@ import {
8
8
  import {
9
9
  SingleSelectMenu,
10
10
  MultiSelectMenu,
11
+ ActionMenu,
11
12
  MenuContent,
12
13
  MenuGroup,
13
14
  MenuToggleButton,
@@ -284,4 +285,36 @@ describe("MenuGroup", () => {
284
285
  xit("ignores the customMenuItem prop", () => {});
285
286
  });
286
287
  });
288
+
289
+ it("supports conditionally rendered items when false", () => {
290
+ // Spy on console.error to catch and verify console errors
291
+ const consoleErrorSpy = jest
292
+ .spyOn(console, "error")
293
+ .mockImplementation(() => {});
294
+
295
+ render(
296
+ <ActionMenu
297
+ defaultIsOpen
298
+ menuToggleElement={<MenuToggleButton>Test Menu</MenuToggleButton>}
299
+ >
300
+ <MenuContent>
301
+ <MenuGroup id="group">
302
+ <MenuItem id="item-1">Item 1</MenuItem>
303
+ {false && (
304
+ <MenuItem id="conditional" onClick={() => {}}>
305
+ Conditional Menu Item
306
+ </MenuItem>
307
+ )}
308
+ <MenuItem id="item-2">Item 2</MenuItem>
309
+ </MenuGroup>
310
+ </MenuContent>
311
+ </ActionMenu>
312
+ );
313
+
314
+ // Verify no console errors occurred during render
315
+ expect(consoleErrorSpy).not.toHaveBeenCalled();
316
+
317
+ // Restore console.error
318
+ consoleErrorSpy.mockRestore();
319
+ });
287
320
  });
@@ -10,6 +10,11 @@ export const MenuGroupTypeTest = () => (
10
10
  <MenuItem id="1">Test 1</MenuItem>
11
11
  <MenuItem id="2">Test 2</MenuItem>
12
12
  </MenuGroup>
13
+ {/* MenuGroup allows children with conditional MenuItems */}
14
+ <MenuGroup id="group">
15
+ <MenuItem id="1">Test 1</MenuItem>
16
+ {false && <MenuItem id="2">Test 2</MenuItem>}
17
+ </MenuGroup>
13
18
  {/* MenuGroup allows menuItems prop */}
14
19
  <MenuGroup id="group" menuItems={[{ id: "1", children: "1" }]} />
15
20
  {/* MenuGroup allows menuItems prop with MenuItemComponent */}
@@ -5,7 +5,7 @@ import type {
5
5
  TypeSubmenuValidMenuComponentProps,
6
6
  } from "../SubmenuItem";
7
7
  import type { TypeMenuContext } from "../MenuContext";
8
- import type { TypeMenuGroupProps, TypeMenuGroupBaseProps } from "../MenuGroup";
8
+ import type { TypeMenuGroupProps } from "../MenuGroup";
9
9
  import type { TypeMenuRootProps } from "../MenuRoot";
10
10
  import type {
11
11
  TypeNotEmptyChildren,
@@ -46,12 +46,13 @@ const walkAllChildren = (
46
46
  if (element === null || element === undefined) return;
47
47
  callback(element, parents);
48
48
  const newParents = [...parents, element];
49
- const children = (element as any).props?.children;
50
- React.Children.toArray(children).forEach((child) => {
51
- walk(child, newParents);
52
- });
49
+ if (React.isValidElement(element) && element.props?.children) {
50
+ React.Children.toArray(element.props.children).forEach((child) => {
51
+ walk(child, newParents);
52
+ });
53
+ }
53
54
  };
54
- const parents = [];
55
+ const parents: ReactNode[] = [];
55
56
  React.Children.toArray(children).forEach((child) => {
56
57
  walk(child, parents);
57
58
  });
@@ -114,22 +115,22 @@ const addItemsFromGroup = <I extends TypeMenuItemProps = TypeMenuItemProps>({
114
115
  });
115
116
  });
116
117
  } else if (menuGroup.props.children) {
117
- let children = menuGroup.props.children as TypeItemElement[];
118
- // If there is only one child, it will be an object. Make it an array.
119
- if (!Array.isArray(children)) {
120
- children = [children];
121
- }
118
+ // Convert children to array to handle both arrays and iterables
119
+ const children = React.Children.toArray(menuGroup.props.children);
122
120
  children.forEach((menuItem) => {
123
- addMenuItem({
124
- itemProps: {
125
- id: menuItem.props.id,
126
- disabled: menuItem.props.disabled,
127
- menuItemProps: menuItem.props,
128
- menuGroupProps: menuGroup.props,
129
- },
130
- allMenuItems,
131
- menuItemsMap,
132
- });
121
+ // Only process React elements that have props
122
+ if (React.isValidElement(menuItem)) {
123
+ addMenuItem({
124
+ itemProps: {
125
+ id: menuItem.props.id,
126
+ disabled: menuItem.props.disabled,
127
+ menuItemProps: menuItem.props,
128
+ menuGroupProps: menuGroup.props,
129
+ },
130
+ allMenuItems,
131
+ menuItemsMap,
132
+ });
133
+ }
133
134
  });
134
135
  }
135
136
  };
@@ -269,7 +270,7 @@ export const useMenuChildren = <
269
270
  });
270
271
  children = getChildrenFromMenuItems({ menuItems, MenuItemComponent });
271
272
  } else if (childrenProp) {
272
- children = childrenProp as TypeNotEmptyChildren;
273
+ children = childrenProp;
273
274
  setMenuItemsFromChildren({
274
275
  children: childrenProp,
275
276
  allMenuItems,
package/src/types.ts CHANGED
@@ -46,10 +46,8 @@ export type TypeChildrenOrItems<
46
46
  }
47
47
  );
48
48
 
49
- export type TypeNotEmptyChildren =
50
- | React.ReactChild
51
- | React.ReactFragment
52
- | React.ReactPortal;
49
+ // Type that excludes null and undefined from React.ReactNode
50
+ export type TypeNotEmptyChildren = Exclude<React.ReactNode, null | undefined>;
53
51
 
54
52
  type TypeInputEventListeners =
55
53
  | "onKeyDown"