Skip to content

Commit 779e411

Browse files
committed
feat(actionmenu): migration to S2
1 parent b3980fc commit 779e411

File tree

10 files changed

+253
-173
lines changed

10 files changed

+253
-173
lines changed

components/actionbutton/stories/template.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export const Template = ({
5555
isActive = false,
5656
isDisabled = false,
5757
hasPopup = "false",
58+
showPopup = false,
5859
popupId,
5960
hideLabel = false,
6061
staticColor,
@@ -105,7 +106,7 @@ export const Template = ({
105106
updateArgs({ isFocused: false });
106107
}}
107108
>
108-
${when(hasPopup && hasPopup !== "false", () =>
109+
${when(showPopup && hasPopup && hasPopup !== "false", () =>
109110
Icon({
110111
size,
111112
iconName: "CornerTriangle" + ({
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"sourceFile": "index.css",
3+
"selectors": [".spectrum-ActionMenu", ".spectrum-ActionMenu-popover"],
4+
"modifiers": [],
5+
"component": ["--spectrum-actionmenu-button-to-menu-gap"],
6+
"global": ["--spectrum-spacing-100"],
7+
"passthroughs": ["--mod-popover-animation-distance"],
8+
"high-contrast": []
9+
}

components/actionmenu/index.css

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*!
2+
* Copyright 2024 Adobe. All rights reserved.
3+
*
4+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License. You may obtain a copy
6+
* of the License at <http://www.apache.org/licenses/LICENSE-2.0>
7+
*
8+
* Unless required by applicable law or agreed to in writing, software distributed under
9+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
10+
* OF ANY KIND, either express or implied. See the License for the specific language
11+
* governing permissions and limitations under the License.
12+
*/
13+
14+
/*
15+
* @spectrum-css/actionmenu
16+
* This component is a combination of a menu, popover, and action button.
17+
* It is used to display a list of actions in a popover menu when the user clicks on an action button.
18+
* The markup has the following structure:
19+
* <div class="spectrum-ActionMenu"> <!-- This is the container -->
20+
* <button class="spectrum-ActionMenu-trigger spectrum-ActionButton"> ... </button> <!-- This is the action button -->
21+
* <div class="spectrum-ActionMenu-popover spectrum-Popover"> <!-- This is the popover that contains the menu -->
22+
* <ul class="spectrum-ActionMenu-menu spectrum-Menu"> <!-- This is the menu -->
23+
*/
24+
25+
.spectrum-ActionMenu {
26+
--spectrum-actionmenu-button-to-menu-gap: var(--spectrum-spacing-100);
27+
/* @todo are these both needed? since popover floats above other UI, I'm not sure if padding really makes sense or matters here */
28+
/* --spectrum-actionmenu-popover-padding: var(--spectrum-spacing-100); */
29+
}
30+
31+
.spectrum-ActionMenu-popover {
32+
/* @passthrough start -- popover */
33+
/* note: right now this is already the same value as the popover animation distance */
34+
--mod-popover-animation-distance: var(--spectrum-actionmenu-button-to-menu-gap);
35+
/* @passthrough end */
36+
}

components/actionmenu/project.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"$schema": "../../node_modules/nx/schemas/project-schema.json",
3+
"name": "actionmenu",
4+
"tags": ["component"],
5+
"targets": {
6+
"build": {},
7+
"clean": {},
8+
"compare": {},
9+
"format": {},
10+
"lint": {},
11+
"report": {},
12+
"test": {
13+
"defaultConfiguration": "scope"
14+
},
15+
"validate": {}
16+
}
17+
}

components/actionmenu/stories/actionmenu.stories.js

Lines changed: 87 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,37 @@
1+
import { ArgGrid } from "@spectrum-css/preview/decorators/utilities.js";
2+
import { disableDefaultModes } from "@spectrum-css/preview/modes";
3+
import { isOpen } from "@spectrum-css/preview/types";
4+
15
import { default as ActionButton } from "@spectrum-css/actionbutton/stories/actionbutton.stories.js";
26
import { default as IconStories } from "@spectrum-css/icon/stories/icon.stories.js";
37
import { default as Menu } from "@spectrum-css/menu/stories/menu.stories.js";
48
import { default as Popover } from "@spectrum-css/popover/stories/popover.stories.js";
5-
import { disableDefaultModes } from "@spectrum-css/preview/modes";
6-
import { isOpen } from "@spectrum-css/preview/types";
7-
import packageJson from "../package.json";
9+
810
import { ActionMenuGroup } from "./actionmenu.test.js";
11+
import { Template } from "./template.js";
12+
13+
import metadata from "../dist/metadata.json";
14+
import packageJson from "../package.json";
915

1016
/**
11-
* The action menu component is an action button with a popover. The `is-selected` class should be applied to the button when the menu is open. Note that the accessibility roles are different for an action menu compared to a normal menu.
17+
* Action menu allows users to access and execute various commands or tasks related to their current workflow. It's typically triggered from an action button by user interaction.
18+
*
19+
* Note that the accessibility roles are different for an action menu compared to a normal menu. The action menu is a combination of a menu, popover, and action button.
1220
*/
1321
export default {
1422
title: "Action menu",
1523
component: "ActionMenu",
1624
argTypes: {
1725
withTip: Popover.argTypes.withTip,
18-
position: Popover.argTypes.position,
26+
position: {
27+
...Popover.argTypes.position,
28+
options: [
29+
"bottom-start",
30+
"bottom-end",
31+
"start-top",
32+
"end-top",
33+
]
34+
},
1935
isOpen,
2036
iconName: {
2137
...(IconStories?.argTypes?.iconName ?? {}),
@@ -35,7 +51,9 @@ export default {
3551
args: {
3652
isOpen: false,
3753
withTip: Popover.args.withTip,
38-
position: Popover.args.position,
54+
position: "bottom-start",
55+
iconName: "AddCircle",
56+
label: "Add",
3957
},
4058
parameters: {
4159
actions: {
@@ -45,37 +63,81 @@ export default {
4563
...(Menu.parameters?.actions?.handles ?? []),
4664
],
4765
},
48-
packageJson,
4966
docs: {
5067
story: {
5168
height: "200px",
5269
}
53-
}
70+
},
71+
design: {
72+
type: "figma",
73+
url: "https://www.figma.com/design/eoZHKJH9a3LJkHYCGt60Vb/S2-token-specs?node-id=19758-3424",
74+
},
75+
packageJson,
76+
metadata,
77+
status: {
78+
type: "migrated",
79+
},
5480
},
81+
tags: ["migrated"],
5582
};
5683

5784
export const Default = ActionMenuGroup.bind({});
5885
Default.args = {
5986
isOpen: true,
60-
position: "bottom",
61-
label: "More actions",
62-
iconName: "More",
63-
items: [
64-
{
65-
label: "Action 1",
66-
},
67-
{
68-
label: "Action 2",
69-
},
70-
{
71-
label: "Action 3",
72-
},
73-
{
74-
label: "Action 4",
75-
},
76-
],
87+
items: [{
88+
heading: "Add new page",
89+
items: [
90+
{
91+
label: "Same size",
92+
iconName: "Copy"
93+
},
94+
{
95+
label: "Custom size",
96+
iconName: "Properties"
97+
},
98+
{
99+
label: "Duplicate",
100+
iconName: "Duplicate"
101+
}
102+
]
103+
}, {
104+
type: "divider"
105+
}, {
106+
heading: "Edit page",
107+
items: [{
108+
label: "Edit timeline",
109+
iconName: "Clock",
110+
description: "Add time to this page"
111+
}],
112+
}],
77113
};
78114

115+
// ********* DOCS ONLY ********* //
116+
117+
/**
118+
* Action menus can be positioned in four locals relative to the trigger but <u>only one menu can be triggered at a single time</u>.
119+
*
120+
* * `top` - The menu appears above the trigger.
121+
* * `bottom` - The menu appears below the trigger.
122+
* * `left` - The menu appears to the left of the trigger.
123+
* * `right` - The menu appears to the right of the trigger.
124+
*
125+
* The `position` prop can be used to set the position of the menu.
126+
*/
127+
export const PlacementOptions = (args, context) => ArgGrid({
128+
Template,
129+
argKey: "position",
130+
...args,
131+
}, context);
132+
PlacementOptions.args = Default.args;
133+
PlacementOptions.tags = ["!dev"];
134+
PlacementOptions.parameters = {
135+
chromatic: {
136+
disableSnapshot: true,
137+
},
138+
};
139+
140+
79141
// ********* VRT ONLY ********* //
80142
export const WithForcedColors = ActionMenuGroup.bind({});
81143
WithForcedColors.args = Default.args;

components/actionmenu/stories/template.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Template as Popover } from "@spectrum-css/popover/stories/template.js";
44
import { getRandomId } from "@spectrum-css/preview/decorators";
55

66
export const Template = ({
7+
rootClass = "spectrum-ActionMenu",
78
id = getRandomId("actionmenu"),
89
testId,
910
triggerId = getRandomId("actionmenu-trigger"),
@@ -33,13 +34,16 @@ export const Template = ({
3334
iconName,
3435
iconSet,
3536
id: triggerId,
36-
customClasses,
37+
customClasses: [`${rootClass}-trigger`],
3738
}, context),
3839
position: "bottom-start",
3940
customStyles,
41+
customClasses: [`${rootClass}-popover`],
42+
customWrapperClasses: [rootClass, ...customClasses],
4043
content: [
4144
(passthroughs) => Menu({
4245
...passthroughs,
46+
customClasses: [`${rootClass}-menu`],
4347
items,
4448
isOpen,
4549
size

components/menu/index.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@
267267

268268
.spectrum-Menu {
269269
display: inline-block;
270-
inline-size: var(--mod-menu-inline-size, auto);
270+
inline-size: var(--mod-menu-inline-size, max-content);
271271
box-sizing: border-box;
272272
margin: 0;
273273
padding: 0;

components/popover/stories/popover.test.js

Lines changed: 8 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -29,104 +29,14 @@ export const PopoverGroup = Variants({
2929
"end",
3030
"end-top",
3131
"end-bottom",
32-
].map((position) => {
33-
const popoverHeight = 158;
34-
const popoverWidth = 105;
35-
36-
let wrapperStyles = {
37-
"justify-content": "center",
38-
// Makes sure that padding does not add to the min-block-size set through the use of the story's parameters.docs.story.height setting.
39-
"box-sizing": "border-box",
40-
};
41-
switch (position) {
42-
case "top":
43-
case "top-left":
44-
case "top-right":
45-
case "top-start":
46-
case "top-end":
47-
wrapperStyles["align-items"] = "end";
48-
wrapperStyles["inline-size"] = `${popoverWidth + 20}px`;
49-
wrapperStyles["padding-block-start"] = `${popoverHeight + 20}px`;
50-
break;
51-
case "bottom":
52-
case "bottom-left":
53-
case "bottom-right":
54-
case "bottom-start":
55-
case "bottom-end":
56-
wrapperStyles["align-items"] = "start";
57-
wrapperStyles["inline-size"] = `${popoverWidth + 20}px`;
58-
wrapperStyles["padding-block-end"] = `${popoverHeight + 20}px`;
59-
break;
60-
case "right":
61-
wrapperStyles["align-items"] = "center";
62-
wrapperStyles["block-size"] = `${popoverHeight + 20}px`;
63-
wrapperStyles["padding-right"] = `${popoverWidth + 20}px`;
64-
break;
65-
case "right-top":
66-
wrapperStyles["align-items"] = "start";
67-
wrapperStyles["block-size"] = `${popoverHeight + 20}px`;
68-
wrapperStyles["padding-right"] = `${popoverWidth + 20}px`;
69-
break;
70-
case "right-bottom":
71-
wrapperStyles["align-items"] = "end";
72-
wrapperStyles["block-size"] = `${popoverHeight + 20}px`;
73-
wrapperStyles["padding-right"] = `${popoverWidth + 20}px`;
74-
break;
75-
case "left":
76-
wrapperStyles["align-items"] = "center";
77-
wrapperStyles["block-size"] = `${popoverHeight + 20}px`;
78-
wrapperStyles["padding-left"] = `${popoverWidth + 20}px`;
79-
break;
80-
case "left-top":
81-
wrapperStyles["align-items"] = "start";
82-
wrapperStyles["block-size"] = `${popoverHeight + 20}px`;
83-
wrapperStyles["padding-left"] = `${popoverWidth + 20}px`;
84-
break;
85-
case "left-bottom":
86-
wrapperStyles["align-items"] = "end";
87-
wrapperStyles["block-size"] = `${popoverHeight + 20}px`;
88-
wrapperStyles["padding-left"] = `${popoverWidth + 20}px`;
89-
break;
90-
case "start":
91-
wrapperStyles["align-items"] = "center";
92-
wrapperStyles["block-size"] = `${popoverHeight + 20}px`;
93-
wrapperStyles["padding-inline-start"] = `${popoverWidth + 20}px`;
94-
break;
95-
case "start-top":
96-
wrapperStyles["align-items"] = "start";
97-
wrapperStyles["block-size"] = `${popoverHeight + 20}px`;
98-
wrapperStyles["padding-inline-start"] = `${popoverWidth + 20}px`;
99-
break;
100-
case "start-bottom":
101-
wrapperStyles["align-items"] = "end";
102-
wrapperStyles["block-size"] = `${popoverHeight + 20}px`;
103-
wrapperStyles["padding-inline-start"] = `${popoverWidth + 20}px`;
104-
break;
105-
case "end":
106-
wrapperStyles["align-items"] = "center";
107-
wrapperStyles["block-size"] = `${popoverHeight + 20}px`;
108-
wrapperStyles["padding-inline-end"] = `${popoverWidth + 20}px`;
109-
break;
110-
case "end-top":
111-
wrapperStyles["align-items"] = "start";
112-
wrapperStyles["block-size"] = `${popoverHeight + 20}px`;
113-
wrapperStyles["padding-inline-end"] = `${popoverWidth + 20}px`;
114-
break;
115-
case "end-bottom":
116-
wrapperStyles["align-items"] = "end";
117-
wrapperStyles["block-size"] = `${popoverHeight + 20}px`;
118-
wrapperStyles["padding-inline-end"] = `${popoverWidth + 20}px`;
119-
break;
120-
}
121-
122-
return {
123-
testHeading: `Position: ${position}`,
124-
wrapperStyles,
125-
position,
126-
popoverHeight,
127-
popoverWidth,
128-
};
129-
}),
32+
].map((position) => ({
33+
testHeading: `Position: ${position}`,
34+
position,
35+
popoverHeight: 178,
36+
popoverWidth: 113,
37+
triggerHeight: 32,
38+
triggerWidth: 66,
39+
})),
13040
{
13141
testHeading: "Dialog style content",
13242
position: "bottom-start",

0 commit comments

Comments
 (0)