From aa466a6f582f5b583816bfa837dcf078fa7985df Mon Sep 17 00:00:00 2001 From: castastrophe Date: Tue, 29 Jul 2025 16:43:31 -0400 Subject: [PATCH 1/3] feat(actionmenu): migration to S2 --- .storybook/decorators/arg-events.js | 3 +- components/actionbutton/stories/template.js | 3 +- components/actionmenu/dist/metadata.json | 9 + components/actionmenu/index.css | 34 +++ components/actionmenu/project.json | 17 ++ .../actionmenu/stories/actionmenu.stories.js | 154 +++++++++-- .../actionmenu/stories/actionmenu.test.js | 31 +-- components/actionmenu/stories/template.js | 6 +- components/icon/stories/template.js | 12 +- components/menu/index.css | 2 +- components/popover/stories/popover.stories.js | 9 +- components/popover/stories/popover.test.js | 106 +------- components/popover/stories/template.js | 245 +++++++++--------- tasks/component-compare.js | 3 - 14 files changed, 356 insertions(+), 278 deletions(-) create mode 100644 components/actionmenu/dist/metadata.json create mode 100644 components/actionmenu/index.css create mode 100644 components/actionmenu/project.json diff --git a/.storybook/decorators/arg-events.js b/.storybook/decorators/arg-events.js index 5bf3a6f3d8c..3e485c0d04f 100644 --- a/.storybook/decorators/arg-events.js +++ b/.storybook/decorators/arg-events.js @@ -13,10 +13,11 @@ export const withArgEvents = makeDecorator({ parameterName: "argEvents", wrapper: (StoryFn, context) => { /** @type {[Args, (newArgs: Partial) => void, (argNames?: (keyof Args)[]) => void]} */ - const [, updateArgs] = useArgs(context.args); + const [, updateArgs, resetArgs] = useArgs(Object.keys(context.args)); // Bind the updateArgs function for use in nested templates context.updateArgs = updateArgs; + context.resetArgs = resetArgs; return StoryFn(context); }, diff --git a/components/actionbutton/stories/template.js b/components/actionbutton/stories/template.js index 942c3bccefa..2686805ce6a 100644 --- a/components/actionbutton/stories/template.js +++ b/components/actionbutton/stories/template.js @@ -55,6 +55,7 @@ export const Template = ({ isActive = false, isDisabled = false, hasPopup = "false", + showPopup = false, popupId, hideLabel = false, staticColor, @@ -105,7 +106,7 @@ export const Template = ({ updateArgs({ isFocused: false }); }} > - ${when(hasPopup && hasPopup !== "false", () => + ${when(showPopup && hasPopup && hasPopup !== "false", () => Icon({ size, iconName: "CornerTriangle" + ({ diff --git a/components/actionmenu/dist/metadata.json b/components/actionmenu/dist/metadata.json new file mode 100644 index 00000000000..98dfe418a91 --- /dev/null +++ b/components/actionmenu/dist/metadata.json @@ -0,0 +1,9 @@ +{ + "sourceFile": "index.css", + "selectors": [".spectrum-ActionMenu", ".spectrum-ActionMenu-popover"], + "modifiers": ["--mod-actionmenu-button-to-menu-gap"], + "component": ["--spectrum-actionmenu-button-to-menu-gap"], + "global": ["--spectrum-spacing-100"], + "passthroughs": ["--mod-popover-animation-distance"], + "high-contrast": [] +} diff --git a/components/actionmenu/index.css b/components/actionmenu/index.css new file mode 100644 index 00000000000..78d9201282e --- /dev/null +++ b/components/actionmenu/index.css @@ -0,0 +1,34 @@ +/*! + * Copyright 2024 Adobe. All rights reserved. + * + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +/* + * @spectrum-css/actionmenu + * This component is a combination of a menu, popover, and action button. + * It is used to display a list of actions in a popover menu when the user clicks on an action button. + * The markup has the following structure: + *
+ * + *
+ *
    + */ + +.spectrum-ActionMenu { + --spectrum-actionmenu-button-to-menu-gap: var(--mod-actionmenu-button-to-menu-gap, var(--spectrum-spacing-100)); +} + +.spectrum-ActionMenu-popover { + /* @passthrough start -- popover */ + /* note: right now this is already the same value as the popover animation distance */ + --mod-popover-animation-distance: var(--spectrum-actionmenu-button-to-menu-gap); + /* @passthrough end */ +} diff --git a/components/actionmenu/project.json b/components/actionmenu/project.json new file mode 100644 index 00000000000..5b51f022edd --- /dev/null +++ b/components/actionmenu/project.json @@ -0,0 +1,17 @@ +{ + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "name": "actionmenu", + "tags": ["component"], + "targets": { + "build": {}, + "clean": {}, + "compare": {}, + "format": {}, + "lint": {}, + "report": {}, + "test": { + "defaultConfiguration": "scope" + }, + "validate": {} + } +} diff --git a/components/actionmenu/stories/actionmenu.stories.js b/components/actionmenu/stories/actionmenu.stories.js index b3cab764ac2..9139e25fe6b 100644 --- a/components/actionmenu/stories/actionmenu.stories.js +++ b/components/actionmenu/stories/actionmenu.stories.js @@ -1,21 +1,38 @@ +import { ArgGrid, Container } from "@spectrum-css/preview/decorators/utilities.js"; +import { disableDefaultModes } from "@spectrum-css/preview/modes"; +import { isOpen } from "@spectrum-css/preview/types"; + import { default as ActionButton } from "@spectrum-css/actionbutton/stories/actionbutton.stories.js"; import { default as IconStories } from "@spectrum-css/icon/stories/icon.stories.js"; import { default as Menu } from "@spectrum-css/menu/stories/menu.stories.js"; import { default as Popover } from "@spectrum-css/popover/stories/popover.stories.js"; -import { disableDefaultModes } from "@spectrum-css/preview/modes"; -import { isOpen } from "@spectrum-css/preview/types"; -import packageJson from "../package.json"; + +import { Template as IconTemplate } from "@spectrum-css/icon/stories/template.js"; import { ActionMenuGroup } from "./actionmenu.test.js"; +import { Template } from "./template.js"; + +import metadata from "../dist/metadata.json"; +import packageJson from "../package.json"; /** - * 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. + * 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. + * + * 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. */ export default { title: "Action menu", component: "ActionMenu", argTypes: { withTip: Popover.argTypes.withTip, - position: Popover.argTypes.position, + position: { + ...Popover.argTypes.position, + options: [ + "bottom-start", + "bottom-end", + "start-top", + "end-top", + ] + }, isOpen, iconName: { ...(IconStories?.argTypes?.iconName ?? {}), @@ -35,7 +52,9 @@ export default { args: { isOpen: false, withTip: Popover.args.withTip, - position: Popover.args.position, + position: "bottom-start", + iconName: "AddCircle", + label: "Add", }, parameters: { actions: { @@ -45,36 +64,123 @@ export default { ...(Menu.parameters?.actions?.handles ?? []), ], }, - packageJson, docs: { story: { height: "200px", } - } + }, + design: { + type: "figma", + url: "https://www.figma.com/design/eoZHKJH9a3LJkHYCGt60Vb/S2-token-specs?node-id=19758-3424", + }, + packageJson, + metadata, + status: { + type: "migrated", + }, }, + tags: ["migrated"], }; export const Default = ActionMenuGroup.bind({}); Default.args = { isOpen: true, - position: "bottom", - label: "More actions", - iconName: "More", - items: [ - { - label: "Action 1", - }, - { - label: "Action 2", - }, - { - label: "Action 3", - }, - { - label: "Action 4", - }, + iconName: "AddCircle", + label: "Add", + items: [{ + heading: "Add new page", + items: [ + { + label: "Same size", + iconName: "Copy" + }, + { + label: "Custom size", + iconName: "Properties" + }, + { + label: "Duplicate", + iconName: "Duplicate" + } + ] + }, { + type: "divider" + }, { + heading: "Edit page", + items: [{ + label: "Edit timeline", + iconName: "Clock", + description: "Add time to this page" + }], + }], +}; + +// ********* DOCS ONLY ********* // + +/** + * Action menus can be positioned in four locals relative to the trigger but only one menu can be triggered at a single time. + */ +export const PlacementOptions = (args, context) => ArgGrid({ + Template, + argKey: "position", + widthBorder: false, + ...args, +}, context); +PlacementOptions.args = Default.args; +PlacementOptions.tags = ["!dev"]; +PlacementOptions.parameters = { + chromatic: { + disableSnapshot: true, + }, +}; + +/** + * Icon used is a placeholder and can be swapped with any other from icon set along with corresponding label. + */ +export const PlaceholderIcon = (args, context) => Container({ + withBorder: false, + content: [ + Template(args, context), + IconTemplate({ + iconName: "ArrowRight400", + setName: "ui", + fill: "var(--spectrum-gray-400)", + customStyles: { + "margin-block-start": "var(--spectrum-spacing-200)", + }, + }, context), + Template(Default.args, context), ], +}); +PlaceholderIcon.args = { + iconName: "More", + label: "", + isOpen: true, + items: [{ + heading: "Menu section header", + items: [ + { + label: "Menu item", + iconName: "Circle" + }, + { + label: "Menu item", + iconName: "Circle" + }, + { + label: "Menu item", + iconName: "Circle" + } + ] + }], }; +PlaceholderIcon.tags = ["!dev"]; +PlaceholderIcon.parameters = { + chromatic: { + disableSnapshot: true, + }, +}; + // ********* VRT ONLY ********* // export const WithForcedColors = ActionMenuGroup.bind({}); diff --git a/components/actionmenu/stories/actionmenu.test.js b/components/actionmenu/stories/actionmenu.test.js index 159a70c756b..2de430f77ec 100644 --- a/components/actionmenu/stories/actionmenu.test.js +++ b/components/actionmenu/stories/actionmenu.test.js @@ -1,26 +1,17 @@ -import { Variants } from "@spectrum-css/preview/decorators"; +import { ArgGrid, Variants } from "@spectrum-css/preview/decorators"; import { Template } from "./template.js"; export const ActionMenuGroup = Variants({ Template, - testData: [{ - wrapperStyles: { - "min-block-size": "200px", - "align-items": "flex-start", - }, - }, { - testHeading: "Closed menu", - isOpen: false, - wrapperStyles: { - "min-block-size": "50px", - }, - }, { - testHeading: "Custom icon", - isOpen: false, - iconName: "Add", - iconSet: "workflow", - wrapperStyles: { - "min-block-size": "50px", - }, + withSizes: false, + testData: [{}, { + testHeading: "Positioning", + withStates: false, + Template: (args, context) => ArgGrid({ + Template, + argKey: "position", + widthBorder: false, + ...args, + }, context), }], }); diff --git a/components/actionmenu/stories/template.js b/components/actionmenu/stories/template.js index c4bc29768d9..0380dcb12aa 100644 --- a/components/actionmenu/stories/template.js +++ b/components/actionmenu/stories/template.js @@ -4,6 +4,7 @@ import { Template as Popover } from "@spectrum-css/popover/stories/template.js"; import { getRandomId } from "@spectrum-css/preview/decorators"; export const Template = ({ + rootClass = "spectrum-ActionMenu", id = getRandomId("actionmenu"), testId, triggerId = getRandomId("actionmenu-trigger"), @@ -33,13 +34,16 @@ export const Template = ({ iconName, iconSet, id: triggerId, - customClasses, + customClasses: [`${rootClass}-trigger`], }, context), position: "bottom-start", customStyles, + customClasses: [`${rootClass}-popover`], + customWrapperClasses: [rootClass, ...customClasses], content: [ (passthroughs) => Menu({ ...passthroughs, + customClasses: [`${rootClass}-menu`], items, isOpen, size diff --git a/components/icon/stories/template.js b/components/icon/stories/template.js index ae7e10a192f..75e18829819 100644 --- a/components/icon/stories/template.js +++ b/components/icon/stories/template.js @@ -39,6 +39,7 @@ export const Template = ({ fill, id = getRandomId("icon"), customClasses = [], + customStyles = {}, useRef = true, } = {}, context = {}) => { // All icons SVG markup from the global IconLoader are in loaded.icons @@ -113,6 +114,8 @@ export const Template = ({ ...customClasses.reduce((a, c) => ({ ...a, [c]: true }), {}), }; + if (fill) customStyles.color = fill; + /** * Display full SVG file markup from global IconLoader data, when not using a reference to the sprite sheet. */ @@ -129,8 +132,13 @@ export const Template = ({ return acc; }, ""); + const stylesAsString = Object.entries(customStyles).reduce((acc, [key, value]) => { + acc += `${key}: ${value};`; + return acc; + }, ""); + return html`${unsafeSVG( - svgString.replace(/
    position.startsWith(p)) ? "calc(var(--spectrum-popover-height) + var(--spectrum-spacing-100) + var(--spectrum-popover-trigger-height))" : "var(--spectrum-popover-height)", + "min-inline-size": ["top", "bottom"].some(p => position.startsWith(p)) ? "var(--spectrum-popover-width)" : "calc(var(--spectrum-popover-width) + var(--spectrum-spacing-100) + var(--spectrum-popover-trigger-width))", ...popoverWrapperStyles, - })}> + })} class=${classMap(customWrapperClasses.reduce((a, c) => ({ ...a, [c]: true }), {}))} @resize=${() => calculateDimensions({ id, triggerId, popoverHeight, popoverWidth, triggerWidth, triggerHeight })}> ${when(typeof trigger === "function", (passthroughs) => trigger({ ...passthroughs, isSelected: isOpen, @@ -181,7 +237,7 @@ export const Template = ({ id: triggerId, popupId: id, onclick: function() { - updateArgs({ isOpen: !isOpen }); + context.updateArgs({ isOpen: !isOpen }); }, }, context))} @@ -213,67 +269,6 @@ export const Template = ({ `; }; -/** - * Template that displays a Popover with every value of the "position" option. - */ -export const TipPlacementVariants = (args, context) => { - const placementOptions = context?.argTypes?.position?.options ?? []; - return html` -
    - ${placementOptions.map(option => { - let optionDescription; - if (option.startsWith("start") || option.startsWith("end")) - optionDescription = "Changes side with text direction (like a logical property)"; - if (option.startsWith("left") || option.startsWith("right")) - optionDescription = "Text direction does not affect the position"; - - return html` -
    - ${Typography({ - semantics: "detail", - size: "l", - content: [option], - customClasses: ["chromatic-ignore"], - }, context)} -
    -
    - ${Template({ - ...args, - position: option, - isOpen: true, - trigger: () => null, - }, context)} -
    -
    - ${when(optionDescription, () => html` - ${Typography({ - semantics: "body", - size: "s", - content: [html`* ${optionDescription}`], - customClasses: ["chromatic-ignore"], - }, context)} - `)} -
    - `; - })} -
    - `; -}; - /** * Contains a source button with a fixed width, and an always open Popover. */ diff --git a/tasks/component-compare.js b/tasks/component-compare.js index c110233cf99..54d30a64e9b 100644 --- a/tasks/component-compare.js +++ b/tasks/component-compare.js @@ -361,9 +361,6 @@ async function main( components = getAllComponentNames(false); } - // Strip out utilities - components = components.filter(c => !["actionmenu", "commons"].includes(c)); - pathing.output = output; pathing.cache = join(output, "packages"); pathing.base = join(output, "base"); From 904da35e4e74f6d628fc56efe5d017c0cdf76ef9 Mon Sep 17 00:00:00 2001 From: castastrophe Date: Mon, 4 Aug 2025 13:31:31 -0400 Subject: [PATCH 2/3] test(picker): break out tests to resolve failure due to size --- components/actionbutton/dist/metadata.json | 12 +-- components/actionbutton/index.css | 8 +- .../stories/actionbutton.stories.js | 12 ++- components/actionbutton/stories/template.js | 3 +- components/actiongroup/dist/metadata.json | 2 +- components/actiongroup/index.css | 2 +- components/menu/stories/template.js | 32 +++++++- components/picker/stories/picker.stories.js | 13 ++- components/picker/stories/picker.test.js | 80 +++++++++++-------- yarn.lock | 29 +------ 10 files changed, 118 insertions(+), 75 deletions(-) diff --git a/components/actionbutton/dist/metadata.json b/components/actionbutton/dist/metadata.json index f8685ad4f90..736bee6489d 100644 --- a/components/actionbutton/dist/metadata.json +++ b/components/actionbutton/dist/metadata.json @@ -14,17 +14,16 @@ ".spectrum-ActionButton-label", ".spectrum-ActionButton-label:empty", ".spectrum-ActionButton.is-disabled", - ".spectrum-ActionButton.is-selected", - ".spectrum-ActionButton.is-selected.spectrum-ActionButton--emphasized", - ".spectrum-ActionButton.is-selected.spectrum-ActionButton--staticBlack", - ".spectrum-ActionButton.is-selected.spectrum-ActionButton--staticWhite", + ".spectrum-ActionButton.spectrum-ActionButton--emphasized:has([aria-pressed=\"true\"])", ".spectrum-ActionButton.spectrum-ActionButton--quiet", - ".spectrum-ActionButton.spectrum-ActionButton--quiet.is-selected", - ".spectrum-ActionButton.spectrum-ActionButton--quiet:disabled:not(.is-selected)", + ".spectrum-ActionButton.spectrum-ActionButton--quiet:disabled:not(:has([aria-pressed=\"true\"]))", + ".spectrum-ActionButton.spectrum-ActionButton--quiet:has([aria-pressed=\"true\"])", ".spectrum-ActionButton.spectrum-ActionButton--staticBlack", ".spectrum-ActionButton.spectrum-ActionButton--staticBlack.spectrum-ActionButton--quiet", + ".spectrum-ActionButton.spectrum-ActionButton--staticBlack:has([aria-pressed=\"true\"])", ".spectrum-ActionButton.spectrum-ActionButton--staticWhite", ".spectrum-ActionButton.spectrum-ActionButton--staticWhite.spectrum-ActionButton--quiet", + ".spectrum-ActionButton.spectrum-ActionButton--staticWhite:has([aria-pressed=\"true\"])", ".spectrum-ActionButton::-moz-focus-inner", ".spectrum-ActionButton:active", ".spectrum-ActionButton:after", @@ -34,6 +33,7 @@ ".spectrum-ActionButton:focus-visible", ".spectrum-ActionButton:focus-visible:after", ".spectrum-ActionButton:has(.spectrum-ActionButton-icon)", + ".spectrum-ActionButton:has([aria-pressed=\"true\"])", ".spectrum-ActionButton:hover", ".spectrum-ActionButton:not(:has(.spectrum-ActionButton-label))", "a.spectrum-ActionButton" diff --git a/components/actionbutton/index.css b/components/actionbutton/index.css index e08737894eb..b29b598e9ce 100644 --- a/components/actionbutton/index.css +++ b/components/actionbutton/index.css @@ -66,7 +66,7 @@ governing permissions and limitations under the License. --spectrum-actionbutton-background-color-focus: var(--spectrum-gray-200); --spectrum-actionbutton-background-color-disabled: transparent; - &.is-selected { + &:has([aria-pressed="true"]) { --spectrum-actionbutton-background-color-disabled: var(--spectrum-disabled-background-color); } } @@ -119,7 +119,7 @@ governing permissions and limitations under the License. } } - &.is-selected { + &:has([aria-pressed="true"]) { --mod-actionbutton-background-color-default: var(--mod-actionbutton-background-color-default-selected, var(--spectrum-neutral-background-color-selected-default)); --mod-actionbutton-background-color-hover: var(--mod-actionbutton-background-color-hover-selected, var(--spectrum-neutral-background-color-selected-hover)); --mod-actionbutton-background-color-down: var(--mod-actionbutton-background-color-down-selected, var(--spectrum-neutral-background-color-selected-down)); @@ -389,7 +389,7 @@ a.spectrum-ActionButton { --highcontrast-actionbutton-background-color-disabled: Canvas; --highcontrast-actionbutton-content-color-default: CanvasText; - &:disabled:not(.is-selected) { + &:disabled:not(:has([aria-pressed="true"])) { --highcontrast-actionbutton-border-color: Canvas; } } @@ -402,7 +402,7 @@ a.spectrum-ActionButton { } /* Selected always shows as a solid highlighted color. */ - &.is-selected { + &:has([aria-pressed="true"]) { --highcontrast-actionbutton-border-color: Highlight; --highcontrast-actionbutton-background-color-default: Highlight; --highcontrast-actionbutton-content-color-default: HighlightText; diff --git a/components/actionbutton/stories/actionbutton.stories.js b/components/actionbutton/stories/actionbutton.stories.js index d1599920baa..2059417ff06 100644 --- a/components/actionbutton/stories/actionbutton.stories.js +++ b/components/actionbutton/stories/actionbutton.stories.js @@ -1,12 +1,14 @@ import { default as IconStories } from "@spectrum-css/icon/stories/icon.stories.js"; import { Sizes, withDownStateDimensionCapture } from "@spectrum-css/preview/decorators"; import { disableDefaultModes } from "@spectrum-css/preview/modes"; -import { isActive, isDisabled, isEmphasized, isFocused, isHovered, isQuiet, isSelected, size, staticColor } from "@spectrum-css/preview/types"; -import metadata from "../dist/metadata.json"; -import packageJson from "../package.json"; +import { isActive, isDisabled, isEmphasized, isFocused, isHovered, isOpen, isQuiet, isSelected, size, staticColor } from "@spectrum-css/preview/types"; import { ActionButtonGroup } from "./actionbutton.test.js"; import { ActionButtonsWithIconOptions, IconOnlyOption, Template, TreatmentTemplate } from "./template.js"; +// Local assets to render the component styles and structure +import metadata from "../dist/metadata.json"; +import packageJson from "../package.json"; + /** * The action button component represents an action a user can take. * @@ -66,6 +68,10 @@ export default { control: "select", options: ["true", "menu", "listbox", "tree", "grid", "dialog", "false"], }, + isOpen: { + ...isOpen, + if: { arg: "hasPopup", truthy: true }, + }, staticColor: { ...staticColor, if: { arg: "isEmphasized", truthy: false }, diff --git a/components/actionbutton/stories/template.js b/components/actionbutton/stories/template.js index 2686805ce6a..d7be1a31635 100644 --- a/components/actionbutton/stories/template.js +++ b/components/actionbutton/stories/template.js @@ -54,6 +54,7 @@ export const Template = ({ isFocused = false, isActive = false, isDisabled = false, + isOpen = false, hasPopup = "false", showPopup = false, popupId, @@ -74,6 +75,7 @@ export const Template = ({ aria-haspopup=${ifDefined(hasPopup && hasPopup !== "false" ? hasPopup : undefined)} aria-controls=${hasPopup && hasPopup !== "false" ? popupId : undefined} aria-pressed=${isSelected ? "true" : "false"} + aria-expanded=${ifDefined(hasPopup && hasPopup !== "false" ? isOpen ? "true" : "false" : undefined)} class=${classMap({ [rootClass]: true, [`${rootClass}--size${size?.toUpperCase()}`]: @@ -83,7 +85,6 @@ export const Template = ({ [`${rootClass}--static${capitalize(staticColor)}`]: typeof staticColor !== "undefined", ["is-disabled"]: isDisabled, - ["is-selected"]: isSelected, ["is-hover"]: isHovered, ["is-focus-visible"]: isFocused, ["is-active"]: isActive, diff --git a/components/actiongroup/dist/metadata.json b/components/actiongroup/dist/metadata.json index 8d4d2ba86b7..e211b29886d 100644 --- a/components/actiongroup/dist/metadata.json +++ b/components/actiongroup/dist/metadata.json @@ -14,9 +14,9 @@ ".spectrum-ActionGroup--compact:not(.spectrum-ActionGroup--quiet) .spectrum-ActionGroup-item", ".spectrum-ActionGroup--compact:not(.spectrum-ActionGroup--quiet) .spectrum-ActionGroup-item + .spectrum-ActionGroup-item", ".spectrum-ActionGroup--compact:not(.spectrum-ActionGroup--quiet) .spectrum-ActionGroup-item .spectrum-ActionButton-label", - ".spectrum-ActionGroup--compact:not(.spectrum-ActionGroup--quiet) .spectrum-ActionGroup-item.is-selected", ".spectrum-ActionGroup--compact:not(.spectrum-ActionGroup--quiet) .spectrum-ActionGroup-item:first-child", ".spectrum-ActionGroup--compact:not(.spectrum-ActionGroup--quiet) .spectrum-ActionGroup-item:focus-visible", + ".spectrum-ActionGroup--compact:not(.spectrum-ActionGroup--quiet) .spectrum-ActionGroup-item:has([aria-pressed=\"true\"])", ".spectrum-ActionGroup--compact:not(.spectrum-ActionGroup--quiet) .spectrum-ActionGroup-item:hover", ".spectrum-ActionGroup--compact:not(.spectrum-ActionGroup--quiet) .spectrum-ActionGroup-item:last-child", ".spectrum-ActionGroup--justified .spectrum-ActionGroup-item", diff --git a/components/actiongroup/index.css b/components/actiongroup/index.css index ac427bc8636..58f77667ca4 100644 --- a/components/actiongroup/index.css +++ b/components/actiongroup/index.css @@ -102,7 +102,7 @@ margin-inline-end: var(--mod-actiongroup-border-radius-reset, var(--spectrum-actiongroup-border-radius-reset)); } - &.is-selected { + &:has([aria-pressed="true"]) { z-index: 1; } diff --git a/components/menu/stories/template.js b/components/menu/stories/template.js index 2415b816acf..dd861d22fe8 100644 --- a/components/menu/stories/template.js +++ b/components/menu/stories/template.js @@ -246,6 +246,36 @@ const Description = ({ `; +/** + * @param {Object} args + * @param {string} args.rootClass + * @param {string} args.label + * @param {string} args.description + * @param {string} args.thumbnailUrl + * @param {string} args.iconName + * @param {string} args.iconSet + * @param {string} args.exclusiveFeatures + * @param {boolean} args.hasExternalLink + * @param {boolean} args.hasActions + * @param {string} args.id + * @param {number} args.idx + * @param {boolean} args.isActive + * @param {boolean} args.isCollapsible + * @param {boolean} args.isUnavailable + * @param {boolean} args.isDisabled + * @param {boolean} args.isDrillIn + * @param {boolean} args.isFocused + * @param {boolean} args.isHighlighted + * @param {boolean} args.isHovered + * @param {boolean} args.isOpen + * @param {boolean} args.isSelected + * @param {Array} args.items + * @param {ARIAMixin['role']} args.role + * @param {boolean} args.shouldTruncate + * @param {string} args.size + * @param {string} args.selectionMode + * @param {string} args.value +*/ export const MenuItem = ( { rootClass = "spectrum-Menu-item", @@ -304,7 +334,6 @@ export const MenuItem = ( "is-highlighted": isHighlighted, "is-active": isActive, "is-focus-visible": isFocused, - "is-selected": isSelected, "is-disabled": isDisabled, "is-hover": isHovered, [`${rootClass}--drillIn`]: isDrillIn, @@ -317,6 +346,7 @@ export const MenuItem = ( role=${ifDefined(role)} aria-selected=${isSelected ? "true" : "false"} aria-disabled=${isDisabled ? "true" : "false"} + aria-expanded=${isOpen ? "true" : "false"} tabindex=${ifDefined(!isDisabled ? "0" : undefined)} > ${StartAction({ hasActions, idx, isCollapsible, isDisabled, isSelected, isUnavailable, rootClass, selectionMode, size, context })} diff --git a/components/picker/stories/picker.stories.js b/components/picker/stories/picker.stories.js index 306a1490bdf..5388c997895 100644 --- a/components/picker/stories/picker.stories.js +++ b/components/picker/stories/picker.stories.js @@ -5,7 +5,7 @@ import { disableDefaultModes } from "@spectrum-css/preview/modes"; import { isActive, isDisabled, isHovered, isInvalid, isKeyboardFocused, isLoading, isOpen, isQuiet, size } from "@spectrum-css/preview/types"; import metadata from "../dist/metadata.json"; import packageJson from "../package.json"; -import { PickerGroup } from "./picker.test.js"; +import { PickerGroup, PickerGroupVariants } from "./picker.test.js"; import { ClosedAndOpenTemplate, DisabledTemplate, Template } from "./template.js"; /** @@ -182,6 +182,17 @@ Default.parameters = { }; // ********* VRT ONLY ********* // +export const WithVariants = PickerGroupVariants.bind({}); +WithVariants.args = {}; +WithVariants.tags = ["!autodocs"]; +WithVariants.parameters = { + docs: { + story: { + height: "300px", + } + }, +}; + export const WithForcedColors = PickerGroup.bind({}); WithForcedColors.tags = ["!autodocs", "!dev"]; WithForcedColors.parameters = { diff --git a/components/picker/stories/picker.test.js b/components/picker/stories/picker.test.js index 46c8e389348..884b324c80a 100644 --- a/components/picker/stories/picker.test.js +++ b/components/picker/stories/picker.test.js @@ -1,43 +1,21 @@ import { Variants } from "@spectrum-css/preview/decorators"; -import { html } from "lit"; import { Template } from "./template.js"; export const PickerGroup = Variants({ - Template: (args, context) => html`
    ${Template(args, context)}
    `, + Template, wrapperStyles: { "align-items": "flex-start", - "min-block-size": "auto", + "min-block-size": "225px", }, testData: [ { testHeading: "Default, with placeholder", - }, - { - testHeading: "Default, with value and text overflow", - currentValue: "The selected value of the picker, with long text the triggers the overflow behavior with ellipsis", + isOpen: true, }, { testHeading: "Quiet", isQuiet: true, - }, - { - testHeading: "Side label", - labelPosition: "side", - }, - { - testHeading: "Side label, quiet", - labelPosition: "side", - isQuiet: true, - }, - { - testHeading: "Side label, alignment with switch", - labelPosition: "side", - withSwitch: true, - }, - { - testHeading: "With thumbnail icon", - showWorkflowIcon: true, - contentIconName: "Image", + isOpen: true, }, ], stateData: [ @@ -86,19 +64,57 @@ export const PickerGroup = Variants({ isDisabled: true, }, { - testHeading: "Open", - isOpen: true, + testHeading: "Closed", + isOpen: false, wrapperStyles: { - "min-block-size": "225px", + "min-block-size": "auto", }, }, { - testHeading: "Open + hover", - isOpen: true, + testHeading: "Closed + hover", + isOpen: false, isHovered: true, wrapperStyles: { - "min-block-size": "225px", + "min-block-size": "auto", }, }, ], }); + +export const PickerGroupVariants = Variants({ + Template, + wrapperStyles: { + "align-items": "flex-start", + "min-block-size": "225px", + }, + testData: [ + { + testHeading: "Default, with value and text overflow", + currentValue: "The selected value of the picker, with long text the triggers the overflow behavior with ellipsis", + isOpen: true, + }, + { + testHeading: "Side label", + labelPosition: "side", + isOpen: true, + }, + { + testHeading: "Side label, quiet", + labelPosition: "side", + isQuiet: true, + isOpen: true, + }, + { + testHeading: "Side label, alignment with switch", + labelPosition: "side", + withSwitch: true, + isOpen: true, + }, + { + testHeading: "With thumbnail icon", + showWorkflowIcon: true, + contentIconName: "Image", + isOpen: true, + }, + ], +}); diff --git a/yarn.lock b/yarn.lock index 94c095d5e16..6877f8f0956 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8309,31 +8309,10 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001669, caniuse-lite@npm:^1.0.30001688, caniuse-lite@npm:^1.0.30001702": - version: 1.0.30001703 - resolution: "caniuse-lite@npm:1.0.30001703" - checksum: 10c0/ed88e318da28e9e59c4ac3a2e3c42859558b7b713aebf03696a1f916e4ed4b70734dda82be04635e2b62ec355b8639bbed829b7b12ff528d7f9cc31a3a5bea91 - languageName: node - linkType: hard - -"caniuse-lite@npm:^1.0.30001716": - version: 1.0.30001717 - resolution: "caniuse-lite@npm:1.0.30001717" - checksum: 10c0/6c0bb1e5182fd578ebe97ee2203250849754a4e17d985839fab527ad27e125a4c4ffce3ece5505217fedf30ea0bbc17ac9f93e9ac525c0389ccba61c6e8345dc - languageName: node - linkType: hard - -"caniuse-lite@npm:^1.0.30001718": - version: 1.0.30001721 - resolution: "caniuse-lite@npm:1.0.30001721" - checksum: 10c0/fa3a8926899824b385279f1f886fe34c5efb1321c9ece1b9df25c8d567a2706db8450cc5b4d969e769e641593e08ea644909324aba93636a43e4949a75f81c4c - languageName: node - linkType: hard - -"caniuse-lite@npm:^1.0.30001726": - version: 1.0.30001727 - resolution: "caniuse-lite@npm:1.0.30001727" - checksum: 10c0/f0a441c05d8925d728c2d02ce23b001935f52183a3bf669556f302568fe258d1657940c7ac0b998f92bc41383e185b390279a7d779e6d96a2b47881f56400221 +"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001669, caniuse-lite@npm:^1.0.30001688, caniuse-lite@npm:^1.0.30001702, caniuse-lite@npm:^1.0.30001716, caniuse-lite@npm:^1.0.30001718, caniuse-lite@npm:^1.0.30001726": + version: 1.0.30001731 + resolution: "caniuse-lite@npm:1.0.30001731" + checksum: 10c0/d8cddf817d5bec8e7c2106affdbf1bfc3923463ca16697c992b2efeb043e6a5d9dcb70cda913bc6acf9112fd66f9e80279316c08e7800359116925066a63fdfa languageName: node linkType: hard From f3fa4d44275c1af9c436c272d8cc106b22ebb442 Mon Sep 17 00:00:00 2001 From: castastrophe Date: Wed, 6 Aug 2025 19:31:00 -0400 Subject: [PATCH 3/3] fix: updates after feedback from design --- components/actionbutton/dist/metadata.json | 12 +- components/actionbutton/index.css | 73 ++++---- .../stories/actionbutton.stories.js | 12 ++ components/actionbutton/stories/template.js | 17 +- .../actionmenu/stories/actionmenu.stories.js | 162 ++++++++++++++---- components/actionmenu/stories/template.js | 12 +- components/assetcard/index.css | 2 +- components/menu/dist/metadata.json | 28 +-- components/menu/index.css | 22 +-- components/menu/stories/menu.stories.js | 3 - components/menu/stories/menu.test.js | 64 ++++--- components/menu/stories/template.js | 131 +++++--------- components/picker/dist/metadata.json | 4 +- components/picker/index.css | 4 +- components/popover/stories/template.js | 3 +- components/typography/index.css | 14 +- 16 files changed, 306 insertions(+), 257 deletions(-) diff --git a/components/actionbutton/dist/metadata.json b/components/actionbutton/dist/metadata.json index 736bee6489d..4bd3a1f7b91 100644 --- a/components/actionbutton/dist/metadata.json +++ b/components/actionbutton/dist/metadata.json @@ -14,16 +14,16 @@ ".spectrum-ActionButton-label", ".spectrum-ActionButton-label:empty", ".spectrum-ActionButton.is-disabled", - ".spectrum-ActionButton.spectrum-ActionButton--emphasized:has([aria-pressed=\"true\"])", + ".spectrum-ActionButton.spectrum-ActionButton--emphasized:is([aria-pressed=\"true\"])", ".spectrum-ActionButton.spectrum-ActionButton--quiet", - ".spectrum-ActionButton.spectrum-ActionButton--quiet:disabled:not(:has([aria-pressed=\"true\"]))", - ".spectrum-ActionButton.spectrum-ActionButton--quiet:has([aria-pressed=\"true\"])", + ".spectrum-ActionButton.spectrum-ActionButton--quiet:disabled:not(:is([aria-pressed=\"true\"]))", + ".spectrum-ActionButton.spectrum-ActionButton--quiet:is([aria-pressed=\"true\"])", ".spectrum-ActionButton.spectrum-ActionButton--staticBlack", ".spectrum-ActionButton.spectrum-ActionButton--staticBlack.spectrum-ActionButton--quiet", - ".spectrum-ActionButton.spectrum-ActionButton--staticBlack:has([aria-pressed=\"true\"])", + ".spectrum-ActionButton.spectrum-ActionButton--staticBlack:is([aria-pressed=\"true\"])", ".spectrum-ActionButton.spectrum-ActionButton--staticWhite", ".spectrum-ActionButton.spectrum-ActionButton--staticWhite.spectrum-ActionButton--quiet", - ".spectrum-ActionButton.spectrum-ActionButton--staticWhite:has([aria-pressed=\"true\"])", + ".spectrum-ActionButton.spectrum-ActionButton--staticWhite:is([aria-pressed=\"true\"])", ".spectrum-ActionButton::-moz-focus-inner", ".spectrum-ActionButton:active", ".spectrum-ActionButton:after", @@ -33,8 +33,8 @@ ".spectrum-ActionButton:focus-visible", ".spectrum-ActionButton:focus-visible:after", ".spectrum-ActionButton:has(.spectrum-ActionButton-icon)", - ".spectrum-ActionButton:has([aria-pressed=\"true\"])", ".spectrum-ActionButton:hover", + ".spectrum-ActionButton:is([aria-pressed=\"true\"])", ".spectrum-ActionButton:not(:has(.spectrum-ActionButton-label))", "a.spectrum-ActionButton" ], diff --git a/components/actionbutton/index.css b/components/actionbutton/index.css index b29b598e9ce..5fef72dad75 100644 --- a/components/actionbutton/index.css +++ b/components/actionbutton/index.css @@ -47,7 +47,7 @@ governing permissions and limitations under the License. --spectrum-actionbutton-line-height: var(--spectrum-actionbutton-height); --spectrum-actionbutton-text-to-visual: var(--spectrum-text-to-visual-100); - --spectrum-actionbutton-edge-to-hold-icon: var(--spectrum-action-button-edge-to-hold-icon-medium); + --spectrum-actionbutton-edge-to-hold-icon: var(--mod-actionbutton-edge-to-hold-icon, var(--spectrum-action-button-edge-to-hold-icon-medium)); --spectrum-actionbutton-edge-to-visual: var(--spectrum-component-edge-to-visual-100); --spectrum-actionbutton-edge-to-text: var(--spectrum-component-edge-to-text-100); --spectrum-actionbutton-edge-to-visual-only: var(--spectrum-component-edge-to-visual-only-100); @@ -66,7 +66,7 @@ governing permissions and limitations under the License. --spectrum-actionbutton-background-color-focus: var(--spectrum-gray-200); --spectrum-actionbutton-background-color-disabled: transparent; - &:has([aria-pressed="true"]) { + &:is([aria-pressed="true"]) { --spectrum-actionbutton-background-color-disabled: var(--spectrum-disabled-background-color); } } @@ -119,7 +119,7 @@ governing permissions and limitations under the License. } } - &:has([aria-pressed="true"]) { + &:is([aria-pressed="true"]) { --mod-actionbutton-background-color-default: var(--mod-actionbutton-background-color-default-selected, var(--spectrum-neutral-background-color-selected-default)); --mod-actionbutton-background-color-hover: var(--mod-actionbutton-background-color-hover-selected, var(--spectrum-neutral-background-color-selected-hover)); --mod-actionbutton-background-color-down: var(--mod-actionbutton-background-color-down-selected, var(--spectrum-neutral-background-color-selected-down)); @@ -179,7 +179,7 @@ governing permissions and limitations under the License. --spectrum-actionbutton-icon-size: var(--spectrum-workflow-icon-size-50); --spectrum-actionbutton-font-size: var(--spectrum-font-size-50); --spectrum-actionbutton-text-to-visual: var(--spectrum-text-to-visual-50); - --spectrum-actionbutton-edge-to-hold-icon: var(--spectrum-action-button-edge-to-hold-icon-extra-small); + --spectrum-actionbutton-edge-to-hold-icon: var(--mod-actionbutton-edge-to-hold-icon, var(--spectrum-action-button-edge-to-hold-icon-extra-small)); --spectrum-actionbutton-edge-to-visual: var(--spectrum-component-edge-to-visual-50); --spectrum-actionbutton-edge-to-text: var(--spectrum-component-edge-to-text-50); --spectrum-actionbutton-edge-to-visual-only: var(--spectrum-component-edge-to-visual-only-50); @@ -196,7 +196,7 @@ governing permissions and limitations under the License. --spectrum-actionbutton-icon-size: var(--spectrum-workflow-icon-size-75); --spectrum-actionbutton-font-size: var(--spectrum-font-size-75); --spectrum-actionbutton-text-to-visual: var(--spectrum-text-to-visual-75); - --spectrum-actionbutton-edge-to-hold-icon: var(--spectrum-action-button-edge-to-hold-icon-small); + --spectrum-actionbutton-edge-to-hold-icon: var(--mod-actionbutton-edge-to-hold-icon, var(--spectrum-action-button-edge-to-hold-icon-small)); --spectrum-actionbutton-edge-to-visual: var(--spectrum-component-edge-to-visual-75); --spectrum-actionbutton-edge-to-text: var(--spectrum-component-edge-to-text-75); --spectrum-actionbutton-edge-to-visual-only: var(--spectrum-component-edge-to-visual-only-75); @@ -213,7 +213,7 @@ governing permissions and limitations under the License. --spectrum-actionbutton-icon-size: var(--spectrum-workflow-icon-size-200); --spectrum-actionbutton-font-size: var(--spectrum-font-size-200); --spectrum-actionbutton-text-to-visual: var(--spectrum-text-to-visual-200); - --spectrum-actionbutton-edge-to-hold-icon: var(--spectrum-action-button-edge-to-hold-icon-large); + --spectrum-actionbutton-edge-to-hold-icon: var(--mod-actionbutton-edge-to-hold-icon, var(--spectrum-action-button-edge-to-hold-icon-large)); --spectrum-actionbutton-edge-to-visual: var(--spectrum-component-edge-to-visual-200); --spectrum-actionbutton-edge-to-text: var(--spectrum-component-edge-to-text-200); --spectrum-actionbutton-edge-to-visual-only: var(--spectrum-component-edge-to-visual-only-200); @@ -228,7 +228,7 @@ governing permissions and limitations under the License. --spectrum-actionbutton-icon-size: var(--spectrum-workflow-icon-size-300); --spectrum-actionbutton-font-size: var(--spectrum-font-size-300); --spectrum-actionbutton-text-to-visual: var(--spectrum-text-to-visual-300); - --spectrum-actionbutton-edge-to-hold-icon: var(--spectrum-action-button-edge-to-hold-icon-extra-large); + --spectrum-actionbutton-edge-to-hold-icon: var(--mod-actionbutton-edge-to-hold-icon, var(--spectrum-action-button-edge-to-hold-icon-extra-large)); --spectrum-actionbutton-edge-to-visual: var(--spectrum-component-edge-to-visual-300); --spectrum-actionbutton-edge-to-text: var(--spectrum-component-edge-to-text-300); --spectrum-actionbutton-edge-to-visual-only: var(--spectrum-component-edge-to-visual-only-300); @@ -253,6 +253,18 @@ governing permissions and limitations under the License. background-color: var(--highcontrast-actionbutton-background-color-default, var(--mod-actionbutton-background-color-default, var(--spectrum-actionbutton-background-color-default))); color: var(--highcontrast-actionbutton-content-color-default, var(--mod-actionbutton-content-color-default, var(--spectrum-actionbutton-content-color-default))); + transition: border-color var(--highcontrast-actionbutton-animation-duration, var(--mod-actionbutton-animation-duration, var(--spectrum-actionbutton-animation-duration))) ease-in-out; + + &::after { + position: absolute; + inset: 0; + margin: calc((var(--mod-actionbutton-focus-indicator-gap, var(--spectrum-actionbutton-focus-indicator-gap)) + var(--spectrum-actionbutton-border-width)) * -1); + border-radius: var(--mod-actionbutton-focus-indicator-border-radius, var(--spectrum-actionbutton-focus-indicator-border-radius)); + transition: box-shadow var(--highcontrast-actionbutton-animation-duration, var(--mod-actionbutton-animation-duration, var(--spectrum-actionbutton-animation-duration))) ease-in-out; + pointer-events: none; + content: ""; + } + &:hover { background-color: var(--highcontrast-actionbutton-background-color-default, var(--mod-actionbutton-background-color-hover, var(--spectrum-actionbutton-background-color-hover))); color: var(--highcontrast-actionbutton-content-color-default, var(--mod-actionbutton-content-color-hover, var(--spectrum-actionbutton-content-color-hover))); @@ -261,6 +273,13 @@ governing permissions and limitations under the License. &:focus-visible { background-color: var(--highcontrast-actionbutton-background-color-default, var(--mod-actionbutton-background-color-focus, var(--spectrum-actionbutton-background-color-focus))); color: var(--highcontrast-actionbutton-content-color-default, var(--mod-actionbutton-content-color-focus, var(--spectrum-actionbutton-content-color-focus))); + + box-shadow: none; + outline: none; + + &::after { + box-shadow: 0 0 0 var(--mod-actionbutton-focus-indicator-thickness, var(--spectrum-actionbutton-focus-indicator-thickness)) var(--highcontrast-actionbutton-focus-indicator-color, var(--mod-actionbutton-focus-indicator-color, var(--spectrum-actionbutton-focus-indicator-color))); + } } &:active { @@ -314,34 +333,22 @@ a.spectrum-ActionButton { .spectrum-ActionButton-hold { position: absolute; - inset-inline-end: calc(var(--mod-actionbutton-edge-to-hold-icon, var(--spectrum-actionbutton-edge-to-hold-icon)) - var(--spectrum-actionbutton-border-width)); - inset-block-end: calc(var(--mod-actionbutton-edge-to-hold-icon, var(--spectrum-actionbutton-edge-to-hold-icon)) - var(--spectrum-actionbutton-border-width)); + inset-inline-end: calc(var(--spectrum-actionbutton-edge-to-hold-icon) - var(--spectrum-actionbutton-border-width)); + inset-block-end: calc(var(--spectrum-actionbutton-edge-to-hold-icon) - var(--spectrum-actionbutton-border-width)); + display: block; color: inherit; + background-color: currentColor; transform: var(--spectrum-logical-rotation); -} -/* Focus indicator */ -.spectrum-ActionButton { - transition: border-color var(--highcontrast-actionbutton-animation-duration, var(--mod-actionbutton-animation-duration, var(--spectrum-actionbutton-animation-duration))) ease-in-out; - - &::after { - position: absolute; - inset: 0; - margin: calc((var(--mod-actionbutton-focus-indicator-gap, var(--spectrum-actionbutton-focus-indicator-gap)) + var(--spectrum-actionbutton-border-width)) * -1); - border-radius: var(--mod-actionbutton-focus-indicator-border-radius, var(--spectrum-actionbutton-focus-indicator-border-radius)); - transition: box-shadow var(--highcontrast-actionbutton-animation-duration, var(--mod-actionbutton-animation-duration, var(--spectrum-actionbutton-animation-duration))) ease-in-out; - pointer-events: none; - content: ""; - } - - &:focus-visible { - box-shadow: none; - outline: none; + inline-size: 5px; + block-size: 5px; - &::after { - box-shadow: 0 0 0 var(--mod-actionbutton-focus-indicator-thickness, var(--spectrum-actionbutton-focus-indicator-thickness)) var(--highcontrast-actionbutton-focus-indicator-color, var(--mod-actionbutton-focus-indicator-color, var(--spectrum-actionbutton-focus-indicator-color))); - } - } + /** + * A few things to note here: + * - cw = clockwise, ccw = counter-clockwise + */ + aspect-ratio: 1; + clip-path: shape(from 2.96% 82.89%, line to 82.92% 2.91%, arc to 100% 9.98% of 9.89% 9.84% small cw, vline to 84.97%, arc by -15.02% 15.03% of 15.02% 15.03% small cw, hline to 10.03%, arc by -7.09% -17.11% of 10.01% 10.02% small cw); } @media (forced-colors: active) { @@ -389,7 +396,7 @@ a.spectrum-ActionButton { --highcontrast-actionbutton-background-color-disabled: Canvas; --highcontrast-actionbutton-content-color-default: CanvasText; - &:disabled:not(:has([aria-pressed="true"])) { + &:disabled:not(:is([aria-pressed="true"])) { --highcontrast-actionbutton-border-color: Canvas; } } @@ -402,7 +409,7 @@ a.spectrum-ActionButton { } /* Selected always shows as a solid highlighted color. */ - &:has([aria-pressed="true"]) { + &:is([aria-pressed="true"]) { --highcontrast-actionbutton-border-color: Highlight; --highcontrast-actionbutton-background-color-default: Highlight; --highcontrast-actionbutton-content-color-default: HighlightText; diff --git a/components/actionbutton/stories/actionbutton.stories.js b/components/actionbutton/stories/actionbutton.stories.js index 2059417ff06..80070523141 100644 --- a/components/actionbutton/stories/actionbutton.stories.js +++ b/components/actionbutton/stories/actionbutton.stories.js @@ -68,6 +68,17 @@ export default { control: "select", options: ["true", "menu", "listbox", "tree", "grid", "dialog", "false"], }, + showPopup: { + name: "Show popup", + description: "If the button triggers a popup action, this should be set to reflect the type of element that pops-up.", + type: { name: "boolean" }, + table: { + type: { summary: "boolean" }, + category: "Accessibility", + }, + control: "boolean", + if: { arg: "hasPopup", truthy: true }, + }, isOpen: { ...isOpen, if: { arg: "hasPopup", truthy: true }, @@ -83,6 +94,7 @@ export default { isQuiet: false, isEmphasized: false, hasPopup: "false", + showPopup: false, isActive: false, isFocused: false, isHovered: false, diff --git a/components/actionbutton/stories/template.js b/components/actionbutton/stories/template.js index d7be1a31635..909fa292384 100644 --- a/components/actionbutton/stories/template.js +++ b/components/actionbutton/stories/template.js @@ -74,7 +74,7 @@ export const Template = ({ aria-label=${ifDefined(hideLabel ? label : undefined)} aria-haspopup=${ifDefined(hasPopup && hasPopup !== "false" ? hasPopup : undefined)} aria-controls=${hasPopup && hasPopup !== "false" ? popupId : undefined} - aria-pressed=${isSelected ? "true" : "false"} + aria-pressed=${ifDefined(isSelected ? "true" : undefined)} aria-expanded=${ifDefined(hasPopup && hasPopup !== "false" ? isOpen ? "true" : "false" : undefined)} class=${classMap({ [rootClass]: true, @@ -107,20 +107,7 @@ export const Template = ({ updateArgs({ isFocused: false }); }} > - ${when(showPopup && hasPopup && hasPopup !== "false", () => - Icon({ - size, - iconName: "CornerTriangle" + ({ - xs: "75", - s: "75", - m: "100", - l: "200", - xl: "300", - }[size] || "100"), - setName: "ui", - customClasses: [`${rootClass}-hold`], - }, context) - )} + ${when(showPopup && hasPopup && hasPopup !== "false", () => html``)} ${when(iconName, () => Icon({ size, diff --git a/components/actionmenu/stories/actionmenu.stories.js b/components/actionmenu/stories/actionmenu.stories.js index 9139e25fe6b..0fb479167ca 100644 --- a/components/actionmenu/stories/actionmenu.stories.js +++ b/components/actionmenu/stories/actionmenu.stories.js @@ -23,7 +23,6 @@ export default { title: "Action menu", component: "ActionMenu", argTypes: { - withTip: Popover.argTypes.withTip, position: { ...Popover.argTypes.position, options: [ @@ -47,14 +46,23 @@ export default { }, control: { type: "text" }, }, + hasLongPress: { + name: "Long press", + description: "If the trigger supports a long-press action which triggers the menu, this should be set to true.", + type: { name: "boolean" }, + table: { + type: { summary: "boolean" }, + category: "Accessibility", + }, + control: "boolean", + }, items: { table: { disable: true } }, + menuArgs: { table: { disable: true } }, }, args: { - isOpen: false, - withTip: Popover.args.withTip, + isOpen: true, position: "bottom-start", - iconName: "AddCircle", - label: "Add", + hasLongPress: false, }, parameters: { actions: { @@ -82,40 +90,90 @@ export default { tags: ["migrated"], }; +/** + * 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. + * + * 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. + */ export const Default = ActionMenuGroup.bind({}); Default.args = { - isOpen: true, iconName: "AddCircle", label: "Add", + menuArgs: { + hasActions: true, + selectionMode: "multiple", + }, + popoverHeight: 340, + popoverWidth: 242, items: [{ - heading: "Add new page", - items: [ - { - label: "Same size", - iconName: "Copy" - }, - { - label: "Custom size", - iconName: "Properties" - }, - { - label: "Duplicate", - iconName: "Duplicate" - } - ] - }, { - type: "divider" - }, { - heading: "Edit page", + heading: "Menu section header", + description: "Menu section description", items: [{ - label: "Edit timeline", - iconName: "Clock", - description: "Add time to this page" + label: "Menu item", + iconName: "Circle", + }, + { + label: "Menu item", + iconName: "Circle", + }, + { + label: "Menu item", + iconName: "Circle", }], + }, { + heading: "Menu section header", + description: "Menu section description", + selectionMode: "none", + hasActions: false, + items: [{ + label: "Menu item", + iconName: "Circle", + }, + { + label: "Menu item", + iconName: "Circle", + }, + { + label: "Menu item", + iconName: "Circle", + },], }], }; // ********* DOCS ONLY ********* // +/** + * By default, the menu is opened by pressing the trigger element or activating it via the Space or Enter keys. However, there may be cases where the trigger should perform a separate action on press such as selection, and should only display the menu when long pressed. For this use-case, the menu will only be opened when pressing and holding the trigger or by using the Option (Alt on Windows) + Down arrow/Up arrow keys while focusing the trigger. + * + * This example illustrates the expected visuals and states of the action menu for a trigger with both long press and short press behaviors. + */ +export const LongPress = Template.bind({}); +LongPress.args = { + hasLongPress: true, + iconName: "CropRotate", + isOpen: true, + menuArgs: { + selectionMode: "single", + }, + items: [{ + label: "Crop rotate", + iconName: "CropRotate", + isSelected: true, + }, { + label: "Slice", + iconName: "VectorDraw", + }, { + label: "Clone stamp", + iconName: "StampClone", + }], + popoverHeight: 139, + popoverWidth: 167, +}; +LongPress.tags = ["!dev"]; +LongPress.parameters = { + chromatic: { + disableSnapshot: true, + }, +}; /** * Action menus can be positioned in four locals relative to the trigger but only one menu can be triggered at a single time. @@ -123,10 +181,24 @@ Default.args = { export const PlacementOptions = (args, context) => ArgGrid({ Template, argKey: "position", - widthBorder: false, + withBorder: false, ...args, }, context); -PlacementOptions.args = Default.args; +PlacementOptions.args = { + iconName: "More", + items: [{ + label: "Details", + iconName: "FileText" + }, { + label: "Share", + iconName: "Share" + }, { + label: "Remove", + iconName: "Delete" + }], + popoverHeight: 139, + popoverWidth: 123, +}; PlacementOptions.tags = ["!dev"]; PlacementOptions.parameters = { chromatic: { @@ -149,7 +221,35 @@ export const PlaceholderIcon = (args, context) => Container({ "margin-block-start": "var(--spectrum-spacing-200)", }, }, context), - Template(Default.args, context), + Template({ + isOpen: true, + iconName: "AddCircle", + label: "Add", + items: [{ + heading: "Add new page", + items: [ + { + label: "Same size", + iconName: "Copy" + }, + { + label: "Custom size", + iconName: "Properties" + }, + { + label: "Duplicate", + iconName: "Duplicate" + } + ] + }, { + heading: "Edit page", + items: [{ + label: "Edit timeline", + iconName: "Clock", + description: "Add time to this page" + }], + }] + }, context), ], }); PlaceholderIcon.args = { diff --git a/components/actionmenu/stories/template.js b/components/actionmenu/stories/template.js index 0380dcb12aa..2d573f49c4c 100644 --- a/components/actionmenu/stories/template.js +++ b/components/actionmenu/stories/template.js @@ -8,17 +8,20 @@ export const Template = ({ id = getRandomId("actionmenu"), testId, triggerId = getRandomId("actionmenu-trigger"), - customClasses = [], + customClasses =[], customStyles = {}, - items = [], + items =[], isOpen = false, label, iconName = "More", iconSet = "workflow", + hasLongPress = false, + menuArgs = {}, size = "m", ...popoverArgs } = {}, context = {}) => { return Popover({ + ...popoverArgs, size, isOpen, withTip: false, @@ -31,6 +34,7 @@ export const Template = ({ size, label, hasPopup: "menu", + showPopup: hasLongPress, iconName, iconSet, id: triggerId, @@ -40,15 +44,15 @@ export const Template = ({ customStyles, customClasses: [`${rootClass}-popover`], customWrapperClasses: [rootClass, ...customClasses], + contentArgs: menuArgs, content: [ (passthroughs) => Menu({ ...passthroughs, customClasses: [`${rootClass}-menu`], items, isOpen, - size + size, }, context) ], - ...popoverArgs, }, context); }; diff --git a/components/assetcard/index.css b/components/assetcard/index.css index 56c40cc58c1..9fddd6bc555 100644 --- a/components/assetcard/index.css +++ b/components/assetcard/index.css @@ -77,8 +77,8 @@ cursor: pointer; - &:lang(zh), &:lang(ja), + &:lang(zh), &:lang(ko) { --spectrum-assetcard-title-font-family: var(--spectrum-cjk-font-family-stack); --spectrum-assetcard-title-font-style: var(--spectrum-heading-cjk-font-style); diff --git a/components/menu/dist/metadata.json b/components/menu/dist/metadata.json index 3693f34c5dc..ab0bdd193ac 100644 --- a/components/menu/dist/metadata.json +++ b/components/menu/dist/metadata.json @@ -117,6 +117,18 @@ ".spectrum-Menu-item:hover > .spectrum-Menu-itemLabel", ".spectrum-Menu-item:hover > .spectrum-Menu-itemValue", ".spectrum-Menu-item:hover > .spectrum-Menu-sectionHeading", + ".spectrum-Menu-item:is([aria-disabled=\"true\"]) .spectrum-Menu-itemDescription", + ".spectrum-Menu-item:is([aria-disabled=\"true\"]) .spectrum-Menu-itemIcon", + ".spectrum-Menu-item:is([aria-disabled=\"true\"]) .spectrum-Menu-itemLabel", + ".spectrum-Menu-item:is([aria-disabled=\"true\"]) .spectrum-Menu-itemThumbnail", + ".spectrum-Menu-item:is([aria-disabled=\"true\"]) .spectrum-Menu-itemValue", + ".spectrum-Menu-item:is([aria-disabled=\"true\"]) .spectrum-Menu-sectionHeading", + ".spectrum-Menu-item:is([aria-disabled=\"true\"]):hover", + ".spectrum-Menu-item:is([aria-disabled=\"true\"]):hover .spectrum-Menu-itemDescription", + ".spectrum-Menu-item:is([aria-disabled=\"true\"]):hover .spectrum-Menu-itemIcon", + ".spectrum-Menu-item:is([aria-disabled=\"true\"]):hover .spectrum-Menu-itemLabel", + ".spectrum-Menu-item:is([aria-disabled=\"true\"]):hover .spectrum-Menu-itemValue", + ".spectrum-Menu-item:is([aria-disabled=\"true\"]):hover .spectrum-Menu-sectionHeading", ".spectrum-Menu-itemActions", ".spectrum-Menu-itemDescription", ".spectrum-Menu-itemLabel", @@ -124,24 +136,12 @@ ".spectrum-Menu-itemSelection", ".spectrum-Menu-itemThumbnail", ".spectrum-Menu-itemValue", - ".spectrum-Menu-item[aria-disabled=\"true\"] .spectrum-Menu-itemDescription", - ".spectrum-Menu-item[aria-disabled=\"true\"] .spectrum-Menu-itemIcon", - ".spectrum-Menu-item[aria-disabled=\"true\"] .spectrum-Menu-itemLabel", - ".spectrum-Menu-item[aria-disabled=\"true\"] .spectrum-Menu-itemThumbnail", - ".spectrum-Menu-item[aria-disabled=\"true\"] .spectrum-Menu-itemValue", - ".spectrum-Menu-item[aria-disabled=\"true\"] .spectrum-Menu-sectionHeading", - ".spectrum-Menu-item[aria-disabled=\"true\"]:hover", - ".spectrum-Menu-item[aria-disabled=\"true\"]:hover .spectrum-Menu-itemDescription", - ".spectrum-Menu-item[aria-disabled=\"true\"]:hover .spectrum-Menu-itemIcon", - ".spectrum-Menu-item[aria-disabled=\"true\"]:hover .spectrum-Menu-itemLabel", - ".spectrum-Menu-item[aria-disabled=\"true\"]:hover .spectrum-Menu-itemValue", - ".spectrum-Menu-item[aria-disabled=\"true\"]:hover .spectrum-Menu-sectionHeading", ".spectrum-Menu-sectionDescription", ".spectrum-Menu-sectionHeading", ".spectrum-Menu.is-selectable .spectrum-Menu-item", - ".spectrum-Menu.is-selectable .spectrum-Menu-item.is-selected", + ".spectrum-Menu.is-selectable .spectrum-Menu-item:is([aria-selected=\"true\"])", ".spectrum-Menu.is-selectableMultiple .spectrum-Menu-item", - ".spectrum-Menu.is-selectableMultiple .spectrum-Menu-itemCheckbox", + ".spectrum-Menu.is-selectableMultiple:not(:has(.is-selectable)) .spectrum-Menu-itemCheckbox", ".spectrum-Menu.spectrum-Menu--sizeL", ".spectrum-Menu.spectrum-Menu--sizeS", ".spectrum-Menu.spectrum-Menu--sizeXL", diff --git a/components/menu/index.css b/components/menu/index.css index 5701d8dc516..c2dd246ef84 100644 --- a/components/menu/index.css +++ b/components/menu/index.css @@ -266,8 +266,8 @@ } .spectrum-Menu { - display: inline-block; - inline-size: var(--mod-menu-inline-size, max-content); + display: block; + inline-size: var(--mod-menu-inline-size, max(max-content, 100%)); box-sizing: border-box; margin: 0; padding: 0; @@ -292,7 +292,7 @@ padding-inline-start: var(--mod-menu-item-selectable-edge-to-text-not-selected, var(--spectrum-menu-item-selectable-edge-to-text-not-selected)); /* Remove inline-start spacing once an item is selected and a checkmark appears. */ - &.is-selected { + &:is([aria-selected="true"]) { --spectrum-menu-checkmark-display: var(--spectrum-menu-checkmark-display-shown); padding-inline-start: var(--mod-menu-item-label-inline-edge-to-content, var(--spectrum-menu-item-label-inline-edge-to-content)); @@ -300,17 +300,13 @@ } } - &.is-selectable, - &.is-selectableMultiple { - .spectrum-Menu-item { - align-items: start; - } + &.is-selectable .spectrum-Menu-item, + &.is-selectableMultiple .spectrum-Menu-item { + align-items: start; } - &.is-selectableMultiple { - .spectrum-Menu-itemCheckbox { - grid-area: checkmarkArea; - } + &.is-selectableMultiple:not(:has(.is-selectable)) .spectrum-Menu-itemCheckbox { + grid-area: checkmarkArea; } .spectrum-Menu-divider { @@ -612,7 +608,7 @@ /* Disabled menu items */ &.is-disabled, - &[aria-disabled="true"] { + &:is([aria-disabled="true"]) { .spectrum-Menu-itemLabel, .spectrum-Menu-sectionHeading, .spectrum-Menu-itemValue { diff --git a/components/menu/stories/menu.stories.js b/components/menu/stories/menu.stories.js index 75bda11d395..b64ac58a71b 100644 --- a/components/menu/stories/menu.stories.js +++ b/components/menu/stories/menu.stories.js @@ -173,7 +173,6 @@ Default.args = { }, ], }, - { type: "divider" }, { idx: 2, heading: "Menu header - With descriptions and icons", @@ -211,7 +210,6 @@ Default.args = { }, ], }, - { type: "divider" }, { idx: 3, heading: @@ -826,7 +824,6 @@ WithDividersAndHeaders.args = { }, ], }, - { type: "divider" }, { idx: 2, heading: "Actions", diff --git a/components/menu/stories/menu.test.js b/components/menu/stories/menu.test.js index a5dc9eea3af..e8e3af81487 100644 --- a/components/menu/stories/menu.test.js +++ b/components/menu/stories/menu.test.js @@ -1,7 +1,5 @@ import { Variants } from "@spectrum-css/preview/decorators"; -import { html } from "lit"; -import { classMap } from "lit/directives/class-map.js"; -import { MenuItem, Template } from "./template.js"; +import { Template } from "./template.js"; export const MenuWithVariants = Variants({ Template, @@ -101,27 +99,23 @@ export const MenuWithVariants = Variants({ }); export const MenuItemGroup = Variants({ - Template: (args, context) => html` -
      - ${MenuItem( - { - ...args, - rootClass: "spectrum-Menu-item", - thumbnailUrl: (args.hasThumbnail && "thumbnail.png") || args.thumbnailUrl, - }, - context, - )} -
    - `, + Template: ({ + customStyles, + hasActions, + hasThumbnail, + selectionMode, + shouldTruncate, + ...args + }, context) => Template({ + customStyles, + hasActions, + selectionMode, + shouldTruncate, + items: [{ + ...args, + thumbnailUrl: hasThumbnail && !args.thumbnailUrl ? "thumbnail.png" : args.thumbnailUrl, + }], + }, context), wrapperStyles: { "min-block-size": "auto", }, @@ -133,14 +127,14 @@ export const MenuItemGroup = Variants({ { testHeading: "No selection, with thumbnails", description: undefined, - thumbnailUrl: "thumbnail.png" + hasThumbnail: true, }, { testHeading: "No selection, with description", }, { testHeading: "No selection, with thumbnails, description", - thumbnailUrl: "thumbnail.png" + hasThumbnail: true, }, { testHeading: "Single selection: selected", @@ -155,7 +149,7 @@ export const MenuItemGroup = Variants({ value: undefined, selectionMode: "single", isSelected: true, - thumbnailUrl: "thumbnail.png" + hasThumbnail: true, }, { testHeading: "Single selection: unselected", @@ -174,7 +168,7 @@ export const MenuItemGroup = Variants({ label: "Share", iconName: "Share", iconSet: "workflow", - thumbnailUrl: "thumbnail.png" + hasThumbnail: true, }, { testHeading: "Multi-selection: selected", @@ -189,7 +183,7 @@ export const MenuItemGroup = Variants({ value: undefined, selectionMode: "multiple", isSelected: true, - thumbnailUrl: "thumbnail.png" + hasThumbnail: true, }, { testHeading: "Multi-selection: unselected", @@ -206,7 +200,7 @@ export const MenuItemGroup = Variants({ label: "Share", iconName: "Share", iconSet: "workflow", - thumbnailUrl: "thumbnail.png" + hasThumbnail: true, }, { testHeading: "Multi-selection: unselected switches", @@ -221,7 +215,7 @@ export const MenuItemGroup = Variants({ hasActions: true, value: undefined, description: undefined, - thumbnailUrl: "thumbnail.png" + hasThumbnail: true, }, { testHeading: "Multi-selection: selected switches", @@ -238,7 +232,7 @@ export const MenuItemGroup = Variants({ value: undefined, description: undefined, isSelected: true, - thumbnailUrl: "thumbnail.png" + hasThumbnail: true, }, { testHeading: "Multi-selection: switches + description", @@ -251,7 +245,7 @@ export const MenuItemGroup = Variants({ selectionMode: "multiple", hasActions: true, label: "Menu item", - thumbnailUrl: "thumbnail.png" + hasThumbnail: true, }, { testHeading: "Drill-in", @@ -276,7 +270,7 @@ export const MenuItemGroup = Variants({ customStyles: { "inline-size": "175px", }, - thumbnailUrl: "thumbnail.png" + hasThumbnail: true, }, { testHeading: "Text wrapping", @@ -295,7 +289,7 @@ export const MenuItemGroup = Variants({ customStyles: { "inline-size": "175px", }, - thumbnailUrl: "thumbnail.png" + hasThumbnail: true, } ], stateData: [ diff --git a/components/menu/stories/template.js b/components/menu/stories/template.js index dd861d22fe8..d6937e11373 100644 --- a/components/menu/stories/template.js +++ b/components/menu/stories/template.js @@ -7,8 +7,8 @@ import { Template as Switch } from "@spectrum-css/switch/stories/template.js"; import { Template as Thumbnail } from "@spectrum-css/thumbnail/stories/template.js"; import { Template as Tray } from "@spectrum-css/tray/stories/template.js"; -import { Container, getRandomId } from "@spectrum-css/preview/decorators"; -import { html } from "lit"; +import { Container, getRandomId, renderContent } from "@spectrum-css/preview/decorators"; +import { html, nothing } from "lit"; import { classMap } from "lit/directives/class-map.js"; import { ifDefined } from "lit/directives/if-defined.js"; import { styleMap } from "lit/directives/style-map.js"; @@ -97,27 +97,23 @@ const Visual = ({ }; const StartAction = ({ - hasActions, - idx, - isCollapsible, - isDisabled, - isSelected, - isUnavailable, + hasActions = false, + idx = 0, + isCollapsible = false, + isDisabled = false, + isSelected = false, + isUnavailable = false, rootClass, - selectionMode, - size, - context -}) => { - if (isUnavailable) return null; + selectionMode = "none", + size = "m", +} = {}, context = {}) => { + if (isUnavailable) return nothing; if (isCollapsible || (selectionMode == "single" && isSelected)) { return html` ${Icon( { - iconName: iconWithScale( - size, - isCollapsible ? "ChevronRight" : "Checkmark", - ), + iconName: iconWithScale(size, isCollapsible ? "ChevronRight" : "Checkmark"), setName: "ui", useRef: false, size, @@ -141,12 +137,12 @@ const StartAction = ({ }, context)}`; } - return null; + return nothing; }; const EndAction = ({ - hasExternalLink, - hasActions, + hasExternalLink = false, + hasActions = false, idx, isUnavailable, isDisabled, @@ -156,8 +152,7 @@ const EndAction = ({ selectionMode, size, value, - context -}) => html` +} = {}, context = {}) => html` ${when(value, () => html` html` - - ${description} - -`; +const Description = ({ rootClass, content } = {}, context = {}) => html`${renderContent(content, { context })}`; -/** - * @param {Object} args - * @param {string} args.rootClass - * @param {string} args.label - * @param {string} args.description - * @param {string} args.thumbnailUrl - * @param {string} args.iconName - * @param {string} args.iconSet - * @param {string} args.exclusiveFeatures - * @param {boolean} args.hasExternalLink - * @param {boolean} args.hasActions - * @param {string} args.id - * @param {number} args.idx - * @param {boolean} args.isActive - * @param {boolean} args.isCollapsible - * @param {boolean} args.isUnavailable - * @param {boolean} args.isDisabled - * @param {boolean} args.isDrillIn - * @param {boolean} args.isFocused - * @param {boolean} args.isHighlighted - * @param {boolean} args.isHovered - * @param {boolean} args.isOpen - * @param {boolean} args.isSelected - * @param {Array} args.items - * @param {ARIAMixin['role']} args.role - * @param {boolean} args.shouldTruncate - * @param {string} args.size - * @param {string} args.selectionMode - * @param {string} args.value -*/ export const MenuItem = ( { rootClass = "spectrum-Menu-item", @@ -349,11 +303,11 @@ export const MenuItem = ( aria-expanded=${isOpen ? "true" : "false"} tabindex=${ifDefined(!isDisabled ? "0" : undefined)} > - ${StartAction({ hasActions, idx, isCollapsible, isDisabled, isSelected, isUnavailable, rootClass, selectionMode, size, context })} + ${StartAction({ hasActions, idx, isCollapsible, isDisabled, isSelected, isUnavailable, rootClass, selectionMode, size }, context)} ${Visual({ iconName, iconSet, rootClass, size, thumbnailUrl, hasExternalLink, isDrillIn })} ${Label({ hasActions, isCollapsible, label, rootClass, shouldTruncate })} - ${when(description, () => Description({ description, rootClass }))} - ${EndAction({ hasExternalLink, hasActions, idx, isUnavailable, isDisabled, isDrillIn, isSelected, rootClass, selectionMode, size, value, context })} + ${when(description, () => Description({ content: description, rootClass: `${rootClass}Description` }, context))} + ${EndAction({ hasExternalLink, hasActions, idx, isUnavailable, isDisabled, isDrillIn, isSelected, rootClass, selectionMode, size, value }, context)} ${when(isCollapsible && items.length > 0, () => Template( { @@ -527,28 +481,31 @@ export const Template = ( })} > ${items.map((i, idx) => { - if (i.type === "divider") - return html`${hasDividers - ? Divider({ + const content = []; + if ((hasDividers && i.type === "divider") || (items[idx - 1]?.heading && i?.heading)) + content.push(Divider({ tag: "li", size: "s", customClasses: [`${rootClass}-divider`], - }) - : ""}`; - else if (i.heading || i.isTraySubmenu) - return MenuGroup( + })); + + if (i.type === "divider") return content; + + if (i.heading || i.isTraySubmenu) + content.push(MenuGroup( { - ...i, subrole, size, selectionMode, isTraySubmenu, shouldTruncate, + hasActions, + ...i, }, context, - ); + )); else - return MenuItem( + content.push(MenuItem( { ...i, hasActions, @@ -563,7 +520,8 @@ export const Template = ( value: singleItemValue || i.value, }, context, - ); + )); + return content; })} `; @@ -578,7 +536,7 @@ export const Template = ( return menuMarkup; }; -export const DisabledItemGroup = (args, context) => { +export const DisabledItemGroup = (args = {}, context = {}) => { const groupData = [ { heading: "Menu with icons", @@ -647,7 +605,6 @@ export const DisabledItemGroup = (args, context) => { content: html` ${Template({ ...args, - context, shouldTruncate: group.shouldTruncate || false, items: group.items, }, context)} @@ -657,7 +614,7 @@ export const DisabledItemGroup = (args, context) => { }, context); }; -export const OverflowGroup = (args, context) => { +export const OverflowGroup = (args = {}, context = {}) => { const groupData = [ { heading: "Text overflow without descriptions", @@ -756,7 +713,6 @@ export const OverflowGroup = (args, context) => { content: html` ${Template({ ...args, - context, shouldTruncate: group.shouldTruncate || false, items: group.items, }, context)} @@ -769,7 +725,7 @@ export const OverflowGroup = (args, context) => { }, context); }; -export const SelectionGroup = (args, context) => { +export const SelectionGroup = (args = {}, context = {}) => { const groupData = [ { heading: "No selection", @@ -934,7 +890,6 @@ export const SelectionGroup = (args, context) => { heading: group.heading, content: Template({ ...args, - context, selectionMode: group.selectionMode || "none", hasActions: group.hasActions || false, items: group.items, @@ -943,16 +898,16 @@ export const SelectionGroup = (args, context) => { }); }; -export const SubmenuInPopover = (args, context) => Popover({ +export const SubmenuInPopover = (args = {}, context = {}) => Popover({ isOpen: true, position: "end-top", customStyles: { "inline-size": "202px", }, - trigger: (args, context) => ActionButton({ + trigger: (passthroughs) => ActionButton({ label: "Settings", iconName: "Settings", - ...args, + ...passthroughs, }, context), content: [ Template({ diff --git a/components/picker/dist/metadata.json b/components/picker/dist/metadata.json index bde45fd599a..9cae946bea2 100644 --- a/components/picker/dist/metadata.json +++ b/components/picker/dist/metadata.json @@ -68,9 +68,7 @@ ".spectrum-Picker.is-loading .spectrum-Picker-menuIcon", ".spectrum-Picker:after", ".spectrum-Picker:focus-visible", - ".spectrum-Picker:lang(ja) .spectrum-Picker-label", - ".spectrum-Picker:lang(ko) .spectrum-Picker-label", - ".spectrum-Picker:lang(zh) .spectrum-Picker-label" + ".spectrum-Picker:lang(ja, zh, ko) .spectrum-Picker-label" ], "modifiers": [ "--mod-picker-animation-duration", diff --git a/components/picker/index.css b/components/picker/index.css index ea2015af243..04f7290930b 100644 --- a/components/picker/index.css +++ b/components/picker/index.css @@ -375,9 +375,7 @@ } /* CJK (Chinese, Japanese, and Korean) language support */ - .spectrum-Picker:lang(ja) &, - .spectrum-Picker:lang(zh) &, - .spectrum-Picker:lang(ko) & { + .spectrum-Picker:lang(ja, zh, ko) & { line-height: var(--mod-picker-line-height-cjk, var(--spectrum-picker-line-height-cjk)); } } diff --git a/components/popover/stories/template.js b/components/popover/stories/template.js index 69a30ba0699..f7eadc22dc3 100644 --- a/components/popover/stories/template.js +++ b/components/popover/stories/template.js @@ -30,6 +30,7 @@ export const Template = ({ skipAlignment = false, trigger, content = [], + contentArgs = {}, } = {}, context = {}) => { // We need to wait for the popover to render before we can get the actual height and width // of the popover to set the custom properties. This is a temporary solution until we can @@ -258,7 +259,7 @@ export const Template = ({ id=${ifDefined(id)} data-testid=${ifDefined(testId ?? id)} > - ${renderContent(content)} + ${renderContent(content, { context, args: contentArgs })} ${withTip ? position && ["top", "bottom"].some((e) => position.startsWith(e)) ? html`` diff --git a/components/typography/index.css b/components/typography/index.css index 25ffeaa8a2f..8ac518e8157 100644 --- a/components/typography/index.css +++ b/components/typography/index.css @@ -142,8 +142,8 @@ font-weight: var(--mod-heading-sans-serif-strong-emphasized-font-weight, var(--spectrum-heading-sans-serif-strong-emphasized-font-weight)); } - &:lang(zh), &:lang(ja), + &:lang(zh), &:lang(ko) { font-family: var(--mod-heading-cjk-font-family, var(--spectrum-heading-cjk-font-family)); font-style: var(--mod-heading-cjk-font-style, var(--spectrum-heading-cjk-font-style)); @@ -198,8 +198,8 @@ font-weight: var(--mod-heading-sans-serif-heavy-strong-emphasized-font-weight, var(--spectrum-heading-sans-serif-heavy-strong-emphasized-font-weight)); } - &:lang(zh), &:lang(ja), + &:lang(zh), &:lang(ko) { font-style: var(--mod-heading-cjk-heavy-font-style, var(--spectrum-heading-cjk-heavy-font-style)); font-weight: var(--mod-heading-cjk-heavy-font-weight, var(--spectrum-heading-cjk-heavy-font-weight)); @@ -249,8 +249,8 @@ font-weight: var(--mod-heading-sans-serif-light-strong-emphasized-font-weight, var(--spectrum-heading-sans-serif-light-strong-emphasized-font-weight)); } - &:lang(zh), &:lang(ja), + &:lang(zh), &:lang(ko) { font-style: var(--mod-heading-cjk-light-font-style, var(--spectrum-heading-cjk-light-font-style)); font-weight: var(--mod-heading-cjk-light-font-weight, var(--spectrum-heading-cjk-light-font-weight)); @@ -421,8 +421,8 @@ font-weight: var(--mod-body-sans-serif-strong-emphasized-font-weight, var(--spectrum-body-sans-serif-strong-emphasized-font-weight)); } - &:lang(zh), &:lang(ja), + &:lang(zh), &:lang(ko) { font-family: var(--mod-body-cjk-font-family, var(--spectrum-body-cjk-font-family)); font-style: var(--mod-body-cjk-font-style, var(--spectrum-body-cjk-font-style)); @@ -539,8 +539,8 @@ font-weight: var(--mod-detail-sans-serif-strong-emphasized-font-weight, var(--spectrum-detail-sans-serif-strong-emphasized-font-weight)); } - &:lang(zh), &:lang(ja), + &:lang(zh), &:lang(ko) { font-family: var(--mod-detail-cjk-font-family, var(--spectrum-detail-cjk-font-family)); font-style: var(--mod-detail-cjk-font-style, var(--spectrum-detail-cjk-font-style)); @@ -619,8 +619,8 @@ font-weight: var(--mod-detail-sans-serif-light-strong-emphasized-font-weight, var(--spectrum-detail-sans-serif-light-strong-emphasized-font-weight)); } - &:lang(zh), &:lang(ja), + &:lang(zh), &:lang(ko) { font-style: var(--mod-detail-cjk-light-font-style, var(--spectrum-detail-cjk-light-font-style)); font-weight: var(--mod-detail-cjk-light-font-weight, var(--spectrum-detail-cjk-light-font-weight)); @@ -726,8 +726,8 @@ font-weight: var(--mod-code-strong-emphasized-font-weight, var(--spectrum-code-strong-emphasized-font-weight)); } - &:lang(zh), &:lang(ja), + &:lang(zh), &:lang(ko) { font-family: var(--mod-code-cjk-font-family, var(--spectrum-code-cjk-font-family)); font-style: var(--mod-code-cjk-font-style, var(--spectrum-code-cjk-font-style));