@sproutsocial/seeds-react-menu 0.2.1 → 0.2.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": "0.2.1",
3
+ "version": "0.2.2",
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",
@@ -49,7 +49,7 @@ const handleStateChange = (
49
49
  if (!selectedItem) return;
50
50
 
51
51
  // if selectedItem is disabled, do nothing
52
- if (selectedItem.element?.props?.disabled) {
52
+ if (selectedItem.disabled) {
53
53
  return;
54
54
  }
55
55
 
@@ -58,7 +58,7 @@ const handleStateChange = (
58
58
  changes.type === useSelect.stateChangeTypes.ToggleButtonKeyDownEnter;
59
59
 
60
60
  // selectedItem props
61
- const selectedItemProps = selectedItem.element?.props;
61
+ const selectedItemProps = selectedItem.menuItemProps;
62
62
 
63
63
  if (selectedItemProps?.onClick) {
64
64
  // Create a synthetic event to pass into the onClick
@@ -111,7 +111,6 @@ export const ActionMenu = ({
111
111
  onStateChange: (changes) => handleStateChange(changes, currentSelectedItem),
112
112
  stateReducer: (state, actionAndChanges) => {
113
113
  let newState: Partial<UseSelectState<TypeDefaultItemProps>>;
114
- // eslint-disable-next-line prefer-const, react-hooks/rules-of-hooks
115
114
  newState = useSelectStateReducer(state, actionAndChanges);
116
115
  return externalStateReducer
117
116
  ? externalStateReducer(state, {
@@ -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,4 +1,9 @@
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 {
3
8
  UseSelectReturnValue,
4
9
  UseSelectProps,
@@ -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}
@@ -23,7 +23,6 @@ export const MenuItemContainer = styled.li`
23
23
  list-style-type: none;
24
24
  margin-left: ${({ theme }) => theme.space[300]};
25
25
  margin-right: ${({ theme }) => theme.space[300]};
26
- transition: all ${({ theme }) => theme.duration["fast"]};
27
26
 
28
27
  &:first-child {
29
28
  margin-top: ${({ theme }) => theme.space[300]};
@@ -49,6 +48,7 @@ export const StyledMenuItem = styled.div<{ $highlighted: boolean }>`
49
48
  padding: ${({ theme }) => theme.space[300]};
50
49
  outline: 0;
51
50
  ${({ theme }) => theme.typography[200]};
51
+ transition: all ${({ theme }) => theme.duration["fast"]};
52
52
 
53
53
  /* Highlighted */
54
54
  ${({ $highlighted }) => $highlighted && highlightedStyles}
@@ -85,7 +85,6 @@ export const useOptionProps = ({
85
85
  () =>
86
86
  selectedItem?.id === id ||
87
87
  !!selectedItems?.find((item) => item?.id === id),
88
- // eslint-disable-next-line react-hooks/exhaustive-deps
89
88
  [selectedItem, selectedItems]
90
89
  );
91
90
 
@@ -1,11 +1,10 @@
1
- import React, { useCallback } from "react";
1
+ import React, { useCallback, useRef } from "react";
2
2
  import styled from "styled-components";
3
3
  import { Popout, type TypePopoutProps } from "@sproutsocial/seeds-react-popout";
4
4
 
5
5
  import { MenuProvider, type TypeMenuProviderProps } from "../MenuContext";
6
6
 
7
7
  export interface TypeMenuRootOwnProps {
8
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
9
8
  menuToggleElement: React.ReactElement<any>;
10
9
  popoutProps?: Partial<
11
10
  Omit<TypePopoutProps, "isOpen" | "setIsOpen" | "content" | "children">
@@ -46,6 +45,7 @@ export const MenuRoot = ({
46
45
  return (
47
46
  <MenuProvider {...contextProps}>
48
47
  <Popout
48
+ menuPopout
49
49
  isOpen={!!isOpen}
50
50
  setIsOpen={setIsOpen}
51
51
  content={
@@ -5,6 +5,7 @@ import {
5
5
  screen,
6
6
  waitFor,
7
7
  } from "@sproutsocial/seeds-react-testing-library";
8
+ import { Box } from "@sproutsocial/seeds-react-box";
8
9
  import { Button } from "@sproutsocial/seeds-react-button";
9
10
  import {
10
11
  MenuRoot,
@@ -17,7 +18,6 @@ import {
17
18
  type TypeMenuContextProps,
18
19
  } from "../../index";
19
20
 
20
- // eslint-disable-next-line react/display-name
21
21
  jest.mock("@sproutsocial/seeds-react-portal", () => ({ children }) => (
22
22
  <div>{children}</div>
23
23
  ));
@@ -197,7 +197,6 @@ describe("MenuRoot", () => {
197
197
  const { user } = render(
198
198
  <MenuRoot
199
199
  menuToggleElement={<CustomToggleElement />}
200
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
201
200
  // @ts-ignore - mock contextProps may be imperfect
202
201
  getToggleButtonProps={() => ({ onClick: mockToggleClick })}
203
202
  >
@@ -82,7 +82,6 @@ export const MultiSelectMenu = ({
82
82
  defaultHighlightedIndex: 0,
83
83
  stateReducer: (state, actionAndChanges) => {
84
84
  let newState: Partial<UseSelectState<TypeDefaultItemProps>>;
85
- // eslint-disable-next-line react-hooks/rules-of-hooks
86
85
  newState = useSelectStateReducer(state, actionAndChanges);
87
86
  newState = selectReducerForMultiSelect(state, {
88
87
  ...actionAndChanges,
@@ -1,5 +1,3 @@
1
- /* eslint-disable testing-library/prefer-find-by */
2
- /* eslint-disable testing-library/no-wait-for-multiple-assertions */
3
1
  /* eslint-disable testing-library/no-unnecessary-act */
4
2
  import {
5
3
  MultiSelectMenu,
@@ -11,6 +9,9 @@ import {
11
9
  import {
12
10
  render,
13
11
  screen,
12
+ fireEvent,
13
+ act,
14
+ cleanup,
14
15
  waitFor,
15
16
  } from "@sproutsocial/seeds-react-testing-library";
16
17
 
@@ -22,6 +23,10 @@ const menuItems = [
22
23
  ];
23
24
 
24
25
  describe("MultiSelectMenu", () => {
26
+ afterEach(() => {
27
+ cleanup();
28
+ });
29
+
25
30
  it("should render the MenuItems when provided as children", async () => {
26
31
  const { user } = render(
27
32
  <MultiSelectMenu
@@ -36,7 +36,6 @@ export const SingleSelectMenu = ({
36
36
  itemToString,
37
37
  stateReducer: (state, actionAndChanges) => {
38
38
  let newState: Partial<UseSelectState<TypeDefaultItemProps>>;
39
- // eslint-disable-next-line react-hooks/rules-of-hooks, prefer-const
40
39
  newState = useSelectStateReducer(state, actionAndChanges);
41
40
  return externalStateReducer
42
41
  ? externalStateReducer(state, {
@@ -1,10 +1,14 @@
1
1
  import React, { type ReactNode, useMemo } from "react";
2
- import type { TypeItemElements } from "../types";
2
+ import type { TypeMenuItemProps } from "../MenuItem";
3
3
  import type { TypeMenuContext } from "../MenuContext";
4
4
 
5
5
  export interface TypeUseItemsFromChildrenProps {
6
6
  children: ReactNode;
7
7
  }
8
+ export interface TypeItemElements {
9
+ element: React.ReactElement<TypeMenuItemProps>;
10
+ parents: readonly React.ReactNode[];
11
+ }
8
12
 
9
13
  // https://stackoverflow.com/questions/69119016/find-specific-children-components-regardless-of-depth-in-react
10
14
  const walkAllChildren = (
@@ -16,7 +20,6 @@ const walkAllChildren = (
16
20
  // console.log("element", element);
17
21
  callback(element, parents);
18
22
  const newParents = [...parents, element];
19
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
23
  const children = (element as any).props?.children;
21
24
  React.Children.toArray(children).forEach((child) => {
22
25
  walk(child, newParents);
@@ -60,13 +63,12 @@ export const useItemsFromChildren = ({
60
63
  const allMenuItemElements = getAllMenuItems(children);
61
64
  const menuItemsMap: TypeMenuContext["itemsMap"] = {};
62
65
  const allMenuItems: TypeMenuContext["items"] = allMenuItemElements.map(
63
- ({ element, parents }, index) => {
66
+ ({ element }, index) => {
64
67
  const item = {
65
68
  id: element.props.id,
66
69
  disabled: element.props.disabled,
67
70
  index,
68
- element,
69
- parents,
71
+ menuItemProps: element.props,
70
72
  };
71
73
  menuItemsMap[`${item.id}`] = item;
72
74
  return item;
package/src/index.ts CHANGED
@@ -17,3 +17,4 @@ export * from "./MultiSelectMenu";
17
17
  export * from "./MenuContext";
18
18
  export * from "./hooks/useItemsFromChildren";
19
19
  export * from "./reducers/useSelectStateReducer";
20
+ export * from "./types";
package/src/types.ts CHANGED
@@ -1,13 +1,10 @@
1
1
  import type { TypeMenuItemProps } from "./MenuItem";
2
2
 
3
- export interface TypeItemElements {
4
- element: React.ReactElement<TypeMenuItemProps>;
5
- parents: readonly React.ReactNode[];
6
- }
7
- export interface TypeDefaultItemProps extends Partial<TypeItemElements> {
3
+ export interface TypeDefaultItemProps {
8
4
  id: string;
9
5
  index: number;
10
6
  disabled?: boolean;
7
+ menuItemProps?: TypeMenuItemProps;
11
8
  }
12
9
 
13
10
  export type TypeNotEmptyChildren =
package/tsconfig.json CHANGED
@@ -8,5 +8,5 @@
8
8
  "verbatimModuleSyntax": true
9
9
  },
10
10
  "include": ["src/**/*"],
11
- "exclude": ["node_modules", "dist", "coverage"]
11
+ "exclude": ["node_modules", "dist"]
12
12
  }
package/.eslintignore DELETED
@@ -1,6 +0,0 @@
1
- # Node modules
2
- node_modules/
3
-
4
- # Build output
5
- dist/
6
- coverage/