@sproutsocial/seeds-react-menu 1.4.0 → 1.5.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sproutsocial/seeds-react-menu",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "Seeds React Menu",
5
5
  "author": "Sprout Social, Inc.",
6
6
  "license": "MIT",
@@ -23,7 +23,7 @@
23
23
  "@sproutsocial/seeds-react-icon": "^1.1.2",
24
24
  "@sproutsocial/seeds-react-input": "^1.4.2",
25
25
  "@sproutsocial/seeds-react-label": "^1.0.1",
26
- "@sproutsocial/seeds-react-popout": "^2.2.0",
26
+ "@sproutsocial/seeds-react-popout": "^2.4.0",
27
27
  "@sproutsocial/seeds-react-radio": "^1.3.0",
28
28
  "@sproutsocial/seeds-react-switch": "^1.2.1",
29
29
  "@sproutsocial/seeds-react-system-props": "^3.0.2",
package/src/Menu.tsx CHANGED
@@ -12,9 +12,13 @@ import { MenuRoot } from "./MenuRoot";
12
12
  import { MenuLabel } from "./MenuLabel";
13
13
  import { MenuToggleButton } from "./MenuToggleButton";
14
14
  import { MenuSearchInput } from "./MenuSearchInput";
15
+ /** Not intended for individual use; use AutocompleteTokenInput or MenuSearchTokenInput instead */
15
16
  import { MenuTokenInput } from "./MenuTokenInput";
16
17
  import { MenuSearchTokenInput } from "./MenuSearchTokenInput";
17
18
  import { SubmenuItem } from "./SubmenuItem";
19
+ import { ListboxToggleButton } from "./ListboxToggleButton";
20
+ import { AutocompleteInput } from "./Autocomplete/AutocompleteInput";
21
+ import { AutocompleteTokenInput } from "./Autocomplete/AutocompleteTokenInput";
18
22
 
19
23
  // Menus
20
24
  import { ActionMenu } from "./ActionMenu";
@@ -40,10 +44,12 @@ export {
40
44
  MenuTokenInput,
41
45
  MenuSearchTokenInput,
42
46
  SubmenuItem,
47
+ ListboxToggleButton,
48
+ AutocompleteInput,
49
+ AutocompleteTokenInput,
43
50
  ActionMenu,
44
51
  SingleSelectMenu,
45
52
  MultiSelectMenu,
46
- Autocomplete,
47
53
  SingleAutocomplete,
48
54
  MultiAutocomplete,
49
55
  NestedMenu,
@@ -8,7 +8,6 @@ import type {
8
8
  TypeSpaceSystemProps,
9
9
  TypeTypographySystemProps,
10
10
  } from "@sproutsocial/seeds-react-system-props";
11
- import type { TypeMenuRootProps } from "../MenuRoot";
12
11
  import type { TypeMenuItemProps } from "../MenuItem";
13
12
  import type { TypeChildrenOrItems } from "../types";
14
13
 
@@ -5,7 +5,11 @@ import React, {
5
5
  type RefObject,
6
6
  } from "react";
7
7
  import styled from "styled-components";
8
- import { Popout, type TypePopoutProps } from "@sproutsocial/seeds-react-popout";
8
+ import {
9
+ Popout,
10
+ type TypePopoutProps,
11
+ type EnumPlacements,
12
+ } from "@sproutsocial/seeds-react-popout";
9
13
  import { mergeRefs } from "@sproutsocial/seeds-react-utilities";
10
14
 
11
15
  import {
@@ -42,35 +46,59 @@ export const CustomPopoutContent = styled(Popout.Content)`
42
46
  overflow: hidden;
43
47
  `;
44
48
 
45
- const menuAnimationConfig = {
49
+ export const menuPlacementOrigin: {
50
+ [key in EnumPlacements]?: { originX: number; originY: number };
51
+ } = {
52
+ top: { originX: 0.5, originY: 1 },
53
+ ["top-start"]: { originX: 0, originY: 1 },
54
+ ["top-end"]: { originX: 1, originY: 1 },
55
+ bottom: { originX: 0.5, originY: 0 },
56
+ ["bottom-start"]: { originX: 0, originY: 0 },
57
+ ["bottom-end"]: { originX: 1, originY: 0 },
58
+ left: { originX: 1, originY: 0.5 },
59
+ ["left-start"]: { originX: 1, originY: 0 },
60
+ ["left-end"]: { originX: 1, originY: 1 },
61
+ right: { originX: 0, originY: 0.5 },
62
+ ["right-start"]: { originX: 0, originY: 0 },
63
+ ["right-end"]: { originX: 0, originY: 1 },
64
+ };
65
+
66
+ export const menuAnimationConfig = {
46
67
  layout: true,
47
- initial: {
48
- scale: 0,
49
- opacity: 0,
50
- originX: 0,
51
- originY: 0,
52
- },
53
- animate: {
54
- opacity: 1,
55
- scale: 1,
56
- originX: 0,
57
- originY: 0,
58
-
59
- transition: {
60
- type: "tween",
61
- duration: MOTION_DURATION_FAST,
62
- ease: "circOut",
63
- },
64
- },
65
- exit: {
66
- opacity: 0,
67
- scale: 1,
68
- transition: {
69
- type: "tween",
70
- duration: MOTION_DURATION_FAST,
71
- ease: "circIn",
68
+ variants: {
69
+ initial: (
70
+ custom: { placement: EnumPlacements } = { placement: "bottom" }
71
+ ) => ({
72
+ ...menuPlacementOrigin[custom.placement],
73
+ scale: 0,
74
+ opacity: 0,
75
+ }),
76
+ animate: (
77
+ custom: { placement: EnumPlacements } = { placement: "bottom" }
78
+ ) => ({
79
+ ...menuPlacementOrigin[custom.placement],
80
+ opacity: 1,
81
+ scale: 1,
82
+
83
+ transition: {
84
+ type: "tween",
85
+ duration: MOTION_DURATION_FAST,
86
+ ease: "circOut",
87
+ },
88
+ }),
89
+ exit: {
90
+ opacity: 0,
91
+ scale: 1,
92
+ transition: {
93
+ type: "tween",
94
+ duration: MOTION_DURATION_FAST,
95
+ ease: "circIn",
96
+ },
72
97
  },
73
98
  },
99
+ initial: "initial",
100
+ animate: "animate",
101
+ exit: "exit",
74
102
  };
75
103
 
76
104
  export interface TypeMenuPopoutProps
@@ -19,6 +19,7 @@ import {
19
19
  MenuGroup,
20
20
  type TypeMenuContextProps,
21
21
  } from "../../index";
22
+ import { menuPlacementOrigin } from "../MenuRoot";
22
23
  import { testDefaultContext } from "../../menuTestingUtils";
23
24
 
24
25
  jest.mock("@sproutsocial/seeds-react-portal", () => ({ children }) => (
@@ -402,3 +403,39 @@ describe("MenuRoot", () => {
402
403
  }
403
404
  );
404
405
  });
406
+
407
+ describe("MenuPopout", () => {
408
+ describe("originX and originY mapping", () => {
409
+ const placements = Object.keys(menuPlacementOrigin);
410
+
411
+ placements.forEach((placement) => {
412
+ it(`maps placement "${placement}" to correct originX and originY`, () => {
413
+ const expected = menuPlacementOrigin[placement];
414
+ expect(expected).toBeDefined();
415
+ // Check the expected values directly
416
+ switch (placement) {
417
+ case "top":
418
+ expect(expected.originX).toBe(0.5);
419
+ expect(expected.originY).toBe(1);
420
+ break;
421
+ case "bottom":
422
+ expect(expected.originX).toBe(0.5);
423
+ expect(expected.originY).toBe(0);
424
+ break;
425
+ case "left":
426
+ expect(expected.originX).toBe(1);
427
+ expect(expected.originY).toBe(0.5);
428
+ break;
429
+ case "right":
430
+ expect(expected.originX).toBe(0);
431
+ expect(expected.originY).toBe(0.5);
432
+ break;
433
+ // Add more cases as needed
434
+ default:
435
+ // If you add more placements, specify their expected values here
436
+ break;
437
+ }
438
+ });
439
+ });
440
+ });
441
+ });
@@ -185,9 +185,6 @@ export const NestedMenuMultiLevelStory: Story = {
185
185
  menuToggleElement={
186
186
  <MenuToggleButton width="200px">Open Menu</MenuToggleButton>
187
187
  }
188
- popoutProps={{
189
- placement: "top",
190
- }}
191
188
  />
192
189
  </Box>
193
190
  </MotionConfig>
@@ -26,8 +26,7 @@ export const NestedMenuPopout = ({
26
26
 
27
27
  return (
28
28
  <MenuPopout
29
- // TODO: add after prop is added to Popout
30
- // lockPlacement
29
+ lockPlacement
31
30
  // Reset the selected menu path after the close animation completes
32
31
  onClose={() => {
33
32
  onClose?.();