diff --git a/.changeset/small-berries-dream.md b/.changeset/small-berries-dream.md new file mode 100644 index 00000000000..c255c3941c5 --- /dev/null +++ b/.changeset/small-berries-dream.md @@ -0,0 +1,16 @@ +--- +"@spectrum-css/menu": minor +--- + +This handles a few remaining items from the initial S2 migration: + +- update to use the correct "LinkOut" icon (previously unavailable) +- add "Unavailable" icon + - the functionality in WC will be to open an explanatory popover + +Additionally, per design review, updates were made regarding valid feature combos: + +- **Not allowed:** + - external links with: thumbnails, drill-in, unavailable, or selection modes + - thumbnails with: drill-in, external links + - new "unavailable" with: selection modes, external links diff --git a/components/accordion/index.css b/components/accordion/index.css index e628d3e93ac..d250feb2f21 100644 --- a/components/accordion/index.css +++ b/components/accordion/index.css @@ -76,10 +76,7 @@ ); /* Calculated vertical spacing for action button and switch to center them within the accordion item */ - --spectrum-accordion-item-direct-actions-vertical-spacing: calc( - (var(--mod-accordion-item-min-block-size, var(--spectrum-accordion-item-min-block-size)) - - var(--mod-accordion-item-direct-actions-height, var(--spectrum-accordion-item-direct-actions-height))) / 2 - ); + --spectrum-accordion-item-direct-actions-vertical-spacing: calc((var(--mod-accordion-item-min-block-size, var(--spectrum-accordion-item-min-block-size)) - var(--mod-accordion-item-direct-actions-height, var(--spectrum-accordion-item-direct-actions-height))) / 2); /* Right-to-left adjustments for transforms */ &:dir(rtl) { diff --git a/components/menu/dist/metadata.json b/components/menu/dist/metadata.json index 50dedb06b77..3693f34c5dc 100644 --- a/components/menu/dist/metadata.json +++ b/components/menu/dist/metadata.json @@ -16,7 +16,6 @@ ".spectrum-Menu .spectrum-Menu-item--drillIn.is-open", ".spectrum-Menu .spectrum-Menu-item:focus .spectrum-Menu-itemCheckbox", ".spectrum-Menu .spectrum-Menu-item:focus .spectrum-Menu-itemSwitch", - ".spectrum-Menu .spectrum-Menu-item:has(> .spectrum-Menu-itemIcon--workflowIcon) .spectrum-Menu-linkout", ".spectrum-Menu .spectrum-Menu-item:hover .spectrum-Menu-itemCheckbox", ".spectrum-Menu .spectrum-Menu-item:hover .spectrum-Menu-itemSwitch", ".spectrum-Menu .spectrum-Menu-itemIcon", @@ -100,15 +99,15 @@ ".spectrum-Menu-item:focus-visible > .spectrum-Menu-itemLabel", ".spectrum-Menu-item:focus-visible > .spectrum-Menu-itemValue", ".spectrum-Menu-item:focus-visible > .spectrum-Menu-sectionHeading", - ".spectrum-Menu-item:has(> .spectrum-Menu-itemThumbnail) > .spectrum-Menu-checkmark", - ".spectrum-Menu-item:has(> .spectrum-Menu-itemThumbnail) > .spectrum-Menu-itemActions", - ".spectrum-Menu-item:has(> .spectrum-Menu-itemThumbnail) > .spectrum-Menu-itemCheckbox", - ".spectrum-Menu-item:has(> .spectrum-Menu-itemThumbnail) > .spectrum-Menu-itemLabel", - ".spectrum-Menu-item:has(> .spectrum-Menu-itemThumbnail) > .spectrum-Menu-itemValue", ".spectrum-Menu-item:has(> .spectrum-Menu-itemThumbnail):has(> .spectrum-Menu-itemDescription) .spectrum-Menu-itemThumbnail", - ".spectrum-Menu-item:has(> .spectrum-Menu-itemThumbnail):has(> .spectrum-Menu-itemDescription) > .spectrum-Menu-checkmark", - ".spectrum-Menu-item:has(> .spectrum-Menu-itemThumbnail):has(> .spectrum-Menu-itemDescription) > .spectrum-Menu-itemCheckbox", - ".spectrum-Menu-item:has(> .spectrum-Menu-itemThumbnail):has(> .spectrum-Menu-itemDescription) > .spectrum-Menu-itemDescription", + ".spectrum-Menu-item:has(> .spectrum-Menu-itemThumbnail):not(:has(.spectrum-Menu-itemSwitch)) > .spectrum-Menu-checkmark", + ".spectrum-Menu-item:has(> .spectrum-Menu-itemThumbnail):not(:has(.spectrum-Menu-itemSwitch)) > .spectrum-Menu-itemActions", + ".spectrum-Menu-item:has(> .spectrum-Menu-itemThumbnail):not(:has(.spectrum-Menu-itemSwitch)) > .spectrum-Menu-itemCheckbox", + ".spectrum-Menu-item:has(> .spectrum-Menu-itemThumbnail):not(:has(.spectrum-Menu-itemSwitch)) > .spectrum-Menu-itemLabel", + ".spectrum-Menu-item:has(> .spectrum-Menu-itemThumbnail):not(:has(.spectrum-Menu-itemSwitch)) > .spectrum-Menu-itemValue", + ".spectrum-Menu-item:has(> .spectrum-Menu-itemThumbnail):not(:has(.spectrum-Menu-itemSwitch)):has(> .spectrum-Menu-itemDescription) > .spectrum-Menu-checkmark", + ".spectrum-Menu-item:has(> .spectrum-Menu-itemThumbnail):not(:has(.spectrum-Menu-itemSwitch)):has(> .spectrum-Menu-itemDescription) > .spectrum-Menu-itemCheckbox", + ".spectrum-Menu-item:has(> .spectrum-Menu-itemThumbnail):not(:has(.spectrum-Menu-itemSwitch)):has(> .spectrum-Menu-itemDescription) > .spectrum-Menu-itemDescription", ".spectrum-Menu-item:hover", ".spectrum-Menu-item:hover .spectrum-Menu-linkout", ".spectrum-Menu-item:hover > .spectrum-Menu-checkmark", diff --git a/components/menu/index.css b/components/menu/index.css index 7f36a14298f..2baacb9b7ce 100644 --- a/components/menu/index.css +++ b/components/menu/index.css @@ -300,11 +300,14 @@ } } + &.is-selectable, &.is-selectableMultiple { .spectrum-Menu-item { align-items: start; } + } + &.is-selectableMultiple { .spectrum-Menu-itemCheckbox { grid-area: checkmarkArea; } @@ -329,7 +332,6 @@ color: var(--highcontrast-menu-item-color-default, var(--mod-menu-item-label-icon-color-default, var(--spectrum-menu-item-label-icon-color-default))); grid-area: iconArea; - align-self: start; } .spectrum-Menu-checkmark { @@ -347,7 +349,6 @@ margin-inline-end: var(--mod-menu-item-text-to-control, var(--spectrum-menu-item-text-to-control)); grid-area: checkmarkArea; - align-self: start; } .spectrum-Menu-chevron { @@ -358,7 +359,6 @@ margin-inline-end: var(--mod-menu-item-text-to-control, var(--spectrum-menu-item-text-to-control)); grid-area: chevronArea; - align-self: center; &:dir(rtl) { transform: rotate(-180deg); @@ -386,17 +386,6 @@ .spectrum-Menu-linkout { block-size: var(--mod-menu-item-linkout-icon-height, var(--spectrum-menu-item-linkout-icon-height)); inline-size: var(--mod-menu-item-linkout-icon-width, var(--spectrum-menu-item-linkout-icon-width)); - - /* Improve vertical alignment of linkout icon */ - /* TODO: revisit with #1194 when final icon available */ - display: block; - margin-block-start: calc(var(--mod-menu-item-label-font-size, var(--spectrum-menu-item-label-font-size)) - var(--spectrum-menu-item-linkout-icon-height)); - } - - /* Improve vertical alignment of linkout icon */ - /* TODO: revisit with #1194 when final icon available */ - .spectrum-Menu-item:has(> .spectrum-Menu-itemIcon--workflowIcon) .spectrum-Menu-linkout { - margin-block-start: calc(var(--mod-menu-item-label-font-size, var(--spectrum-menu-item-label-font-size)) - var(--mod-menu-item-icon-height, var(--spectrum-menu-item-icon-height) / 2)); } /* Presentational list items for sections and dividers. */ @@ -443,7 +432,7 @@ grid-template-rows: 1fr auto; /* Alignment adjustments if there's a thumbnail */ - &:has(> .spectrum-Menu-itemThumbnail) { + &:has(> .spectrum-Menu-itemThumbnail):not(:has(.spectrum-Menu-itemSwitch)) { > .spectrum-Menu-itemCheckbox, > .spectrum-Menu-checkmark, > .spectrum-Menu-itemActions, @@ -694,13 +683,12 @@ color: var(--highcontrast-menu-item-color-default, var(--mod-menu-item-value-color-default, var(--spectrum-menu-item-value-color-default))); font-size: var(--mod-menu-item-label-font-size, var(--spectrum-menu-item-label-font-size)); justify-self: end; - align-self: start; margin-inline-start: var(--mod-menu-item-label-to-value-area-min-spacing, var(--spectrum-menu-item-label-to-value-area-min-spacing)); } .spectrum-Menu-itemActions { + display: grid; grid-area: actionsArea; - align-self: start; margin-inline-start: var(--mod-menu-item-label-to-value-area-min-spacing, var(--spectrum-menu-item-label-to-value-area-min-spacing)); } @@ -751,7 +739,6 @@ /* Span two rows to properly align to description when present */ .spectrum-Menu-item:has(> &):has(> .spectrum-Menu-itemDescription) & { grid-row-end: visualOverflow; - align-self: center; } } diff --git a/components/menu/stories/menu.stories.js b/components/menu/stories/menu.stories.js index ccd6d346ee6..75bda11d395 100644 --- a/components/menu/stories/menu.stories.js +++ b/components/menu/stories/menu.stories.js @@ -1,23 +1,44 @@ import { default as IconStories } from "@spectrum-css/icon/stories/icon.stories.js"; -import { Sizes, withDownStateDimensionCapture } from "@spectrum-css/preview/decorators"; +import { + Sizes, + withDownStateDimensionCapture, +} from "@spectrum-css/preview/decorators"; import { disableDefaultModes } from "@spectrum-css/preview/modes"; -import { isActive, isDisabled, isFocused, isHovered, isSelected, size } from "@spectrum-css/preview/types"; +import { + isActive, + isDisabled, + isFocused, + isHovered, + isSelected, + size, +} from "@spectrum-css/preview/types"; import metadata from "../dist/metadata.json"; import packageJson from "../package.json"; -import { MenuItemGroup, MenuTraySubmenu, MenuWithVariants } from "./menu.test.js"; -import { DisabledItemGroup, OverflowGroup, SelectionGroup, SubmenuInPopover, Template } from "./template.js"; +import { + MenuItemGroup, + MenuTraySubmenu, + MenuWithVariants, +} from "./menu.test.js"; +import { + DisabledItemGroup, + OverflowGroup, + SelectionGroup, + SubmenuInPopover, + Template, +} from "./template.js"; /** * A menu is used for creating a menu list. The various elements inside a menu can be: a menu group, a menu item, or a * menu divider. Often a menu will appear in a popover so that it displays as a toggling menu. -*/ + */ export default { title: "Menu", component: "Menu", argTypes: { selectionMode: { name: "Selection mode", - description: "Determines whether items in the menu can be selected, and how many", + description: + "Determines whether items in the menu can be selected, and how many", type: { name: "string", required: true }, table: { type: { summary: "string" }, @@ -79,8 +100,8 @@ export default { }, docs: { story: { - height: "300px" - } + height: "300px", + }, }, design: { type: "figma", @@ -93,11 +114,9 @@ export default { metadata, status: { type: "migrated", - } + }, }, - decorators: [ - withDownStateDimensionCapture, - ], + decorators: [withDownStateDimensionCapture], tags: ["migrated"], }; @@ -115,7 +134,7 @@ Default.args = { items: [ { label: "Default menu item", - iconName: "Comment" + iconName: "Comment", }, { label: "Focused menu item", @@ -124,11 +143,16 @@ Default.args = { isActive: true, }, { - label: "A menu item with a longer label that causes the text to wrap to the next line" + label: + "A menu item with a longer label that causes the text to wrap to the next line", }, { label: "Menu item with no icon", }, + { + label: "Menu item as unavailable", + isUnavailable: true, + }, { label: "Menu item as external link", hasExternalLink: true, @@ -136,11 +160,11 @@ Default.args = { { label: "Menu item as external link with icon", hasExternalLink: true, - iconName: "Data" + iconName: "Data", }, { label: "Menu item with a thumbnail", - thumbnailUrl: "thumbnail.png" + thumbnailUrl: "thumbnail.png", }, { label: "Disabled menu item", @@ -162,24 +186,27 @@ Default.args = { }, { label: "Selected item", - description: "This item is checked if single-select or multi-select mode is turned on", + description: + "This item is checked if single-select or multi-select mode is turned on", isSelected: true, }, { label: "Selected item with thumbnail", isSelected: true, - thumbnailUrl: "thumbnail.png" + thumbnailUrl: "thumbnail.png", }, { label: "Selected item with thumbnail", - description: "This item is checked if single-select or multi-select mode is turned on", + description: + "This item is checked if single-select or multi-select mode is turned on", isSelected: true, - thumbnailUrl: "thumbnail.png" + thumbnailUrl: "thumbnail.png", }, { label: "Selected item with icon", iconName: "Cloud", - description: "This item is checked if single-select or multi-select mode is turned on", + description: + "This item is checked if single-select or multi-select mode is turned on", isSelected: true, }, ], @@ -187,26 +214,32 @@ Default.args = { { type: "divider" }, { idx: 3, - heading: "Menu header - With actions, icons, thumbnails, short descriptions, and values and longer header text that wraps", - sectionDescription: "This menu header also has a description that is long enough to hopefully just maybe wrap if it's long enough", + heading: + "Menu header - With actions, icons, thumbnails, short descriptions, and values and longer header text that wraps", + sectionDescription: + "This menu header also has a description that is long enough to hopefully just maybe wrap if it's long enough", id: "menu-heading-desc-icon-value", hasActions: true, items: [ { - label: "Menu item with action and a longer label that truncates if it is long enough to truncate", + label: + "Menu item with action and a longer label that truncates if it is long enough to truncate", iconName: "Cut", - description: "This item has a switch if multi-select mode is turned on.", + description: + "This item has a switch if multi-select mode is turned on.", }, { label: "Menu item with action", iconName: "Copy", - description: "In multi-select mode, this item will be switched on. In single-select mode, this item will be checked.", + description: + "In multi-select mode, this item will be switched on. In single-select mode, this item will be checked.", isSelected: true, }, { label: "Menu item with action and value", iconName: "Paste", - description: "This item has a value. If multi-select mode is turned on, it also has a switch and the value can be used to label the switch.", + description: + "This item has a value. If multi-select mode is turned on, it also has a switch and the value can be used to label the switch.", value: "⌘ C", }, { @@ -226,17 +259,24 @@ Default.args = { value: "⌘ C", thumbnailUrl: "thumbnail.png", }, + { + label: "Menu item as unavailable", + description: "And a description, too", + isUnavailable: true, + }, { label: "Menu item with external link action", - description: "Menu item with external link action (does not work in multi-select mode)", + description: + "Menu item with external link action (does not work in multi-select mode)", hasExternalLink: true, }, { label: "Disabled menu item with external link action", - description: "Menu item with external link action (does not work in multi-select mode)", + description: + "Menu item with external link action (does not work in multi-select mode)", hasExternalLink: true, isDisabled: true, - } + }, ], }, { @@ -262,7 +302,7 @@ Default.args = { label: "Disabled menu item with drill-in", isDrillIn: true, isDisabled: true, - } + }, ], }, ], @@ -297,8 +337,8 @@ TraySubmenu.args = { { label: "Rulers", }, - ] - } + ], + }, ], }; TraySubmenu.parameters = { @@ -307,10 +347,10 @@ TraySubmenu.parameters = { story: { inline: false, height: "300px", - } + }, }, viewport: { - defaultViewport: "mobile2" + defaultViewport: "mobile2", }, }; @@ -358,11 +398,12 @@ MenuItem.argTypes = { }, iconName: { ...(IconStories?.argTypes?.iconName ?? {}), - if: { arg: "hasThumbnail", truthy: false }, + if: { arg: "exclusiveFeatures", neq: "hasThumbnail" }, }, hasActions: { name: "Has switches", - description: "If multiple selection is enabled, show switches instead of checkboxes to show which items have been selected", + description: + "If multiple selection is enabled, show switches instead of checkboxes to show which items have been selected", type: { name: "boolean" }, table: { type: { summary: "boolean" }, @@ -371,35 +412,27 @@ MenuItem.argTypes = { control: "boolean", if: { arg: "selectionMode", eq: "multiple" }, }, - hasExternalLink: { - name: "Has external link", - description: "Has external link action", - type: { name: "boolean" }, + exclusiveFeatures: { + name: "Mutually exclusive features", + description: "These options are intended to never be used in combination with each other:\n- `hasExternalLink`: Displays external link icon\n- `hasThumbnail`: Displays a thumbnail, taking the place of an icon\n- `isDrillIn`: Displays submenu indicator", + options: ["none", "hasExternalLink", "hasThumbnail", "isDrillIn"], + control: "select", table: { - type: { summary: "boolean" }, - category: "Content", - }, - control: "boolean", - }, - hasThumbnail: { - name: "Has thumbnail", - description: "Displays a thumbnail in the label", - type: { name: "boolean" }, - table: { - type: { summary: "boolean" }, + type: { summary: "string" }, category: "Content", }, - control: "boolean" }, - isDrillIn: { - name: "Is drill-in", - description: "Displays drill-in menu indicator", + isUnavailable: { + name: "Is unavailable", + description: + "Displays unavailable icon intended to toggle explanatory popover.\n\nShould not be used with external links, drill-ins, or selectable items", type: { name: "boolean" }, table: { type: { summary: "boolean" }, category: "Content", }, - control: "boolean" + control: "boolean", + if: { arg: "exclusiveFeatures", neq: "hasExternalLink" }, }, // These settings are not used in the MenuItem story hasDividers: { table: { disable: true } }, @@ -407,18 +440,17 @@ MenuItem.argTypes = { }; MenuItem.args = { label: "Start a chat", - iconName: "Comment", description: "Menu item description", value: "⌘ N", + iconName: "Comment", + exclusiveFeatures: "none", + isUnavailable: false, isDisabled: false, isActive: false, isFocused: false, isHovered: false, isSelected: false, - hasActions: false, - hasExternalLink: false, - hasThumbnail: false, - isDrillIn: false, + hasActions: false }; MenuItem.parameters = { design: { @@ -489,25 +521,19 @@ Collapsible.args = { label: "Tablet", iconName: "DeviceTablet", isCollapsible: true, - items: [ - { label: "Defaults to not visible within closed item" }, - ], + items: [{ label: "Defaults to not visible within closed item" }], }, { label: "Social Media", iconName: "ShareAndroid", isCollapsible: true, - items: [ - { label: "Defaults to not visible within closed item" }, - ], + items: [{ label: "Defaults to not visible within closed item" }], }, { label: "Watches", iconName: "Clock", isCollapsible: true, - items: [ - { label: "Defaults to not visible within closed item" }, - ], + items: [{ label: "Defaults to not visible within closed item" }], }, ], }; @@ -523,12 +549,16 @@ Collapsible.args = { * [action button](?path=/docs/components-action-button--docs)). Similarly, any components displayed inside a menu item * (such as a [switch](?path=/docs/components-switch--docs)) must also be of the same size. */ -export const Sizing = (args, context) => Sizes({ - Template, - withHeading: false, - withBorder: false, - ...args, -}, context); +export const Sizing = (args, context) => + Sizes( + { + Template, + withHeading: false, + withBorder: false, + ...args, + }, + context, + ); Sizing.storyName = "Default"; Sizing.tags = ["!dev"]; Sizing.args = { @@ -539,19 +569,19 @@ Sizing.args = { }, { idx: 2, - label: "Menu item with icon", - iconName: "Cloud", - }, - { - idx: 3, label: "Menu item with optional description", description: "Short description of menu item", }, { - idx: 4, + idx: 3, label: "Menu item with value", value: "Value", }, + { + idx: 4, + label: "Menu item with icon", + iconName: "Cloud", + }, { idx: 5, label: "Menu item with icon and description", @@ -561,21 +591,26 @@ Sizing.args = { { idx: 6, label: "Menu item as external link", - hasExternalLink: true + hasExternalLink: true, }, { idx: 7, + label: "Menu item as unavailable", + isUnavailable: true, + }, + { + idx: 8, label: "Menu item with thumbnail", value: "Value", - thumbnailUrl: "thumbnail.png" + thumbnailUrl: "thumbnail.png", }, { - idx: 8, + idx: 9, label: "Menu item with thumbnail", description: "and description", - thumbnailUrl: "thumbnail.png" - }, - ] + thumbnailUrl: "thumbnail.png", + } + ], }; Sizing.parameters = { chromatic: { disableSnapshot: true }, @@ -602,7 +637,7 @@ DrillInChevron.args = { }, { label: "Menu item", - } + }, ], }; @@ -632,6 +667,8 @@ MenuItemSelection.parameters = { * The last item in each of these menus is disabled. A menu item in a disabled state shows that an option exists, but * is not available in that circumstance. This state can be used to maintain layout continuity and to communicate that * an action may become available later. + * + * To explain why an option is not actionable and keep it navigable, instead use the [_unavailable_ menu item type](#unavailable%20items). */ export const DisabledItems = DisabledItemGroup.bind({}); DisabledItems.storyName = "Disabled items"; @@ -640,6 +677,72 @@ DisabledItems.parameters = { chromatic: { disableSnapshot: true }, }; +/** + * The "Unavailable" menu item type may be used when the item is inactive and requires further explanation. + * The menu item should toggle a popover containing the explanation of why the item is unavailable. + * In the example, the first item is marked as "unavailable" as noted by the icon. + * + * This is not the same as disabled, since the item can still be navigated to, but just has unavailable functionality. + * + * Items with drill-in submenus, external links, or that are selectable cannot be made unavailable. + */ +export const UnavailableItems = Template.bind({}); +UnavailableItems.storyName = "Unavailable items"; +UnavailableItems.tags = ["!dev"]; +UnavailableItems.parameters = { + chromatic: { disableSnapshot: true }, +}; +UnavailableItems.args = { + items: [ + { + label: "Marquee", + isSelected: true, + iconName: "SelectRectangle", + isUnavailable: true + }, + { + label: "Add", + iconName: "SelectMulti", + }, + { + label: "Subtract", + iconName: "SelectNone", + }, + ], +}; + +/** + * If a menu item will navigate away from the application, it may use the external link icon. + * + * Menu item types not valid as external links include those with thumbnails, selection modes, + * drill-in submenus, or marked as "unavailable". + */ +export const ExternalLinks = Template.bind({}); +ExternalLinks.storyName = "External links"; +ExternalLinks.tags = ["!dev"]; +ExternalLinks.parameters = { + chromatic: { disableSnapshot: true }, +}; +ExternalLinks.args = { + items: [ + { + label: "An offsite link item", + hasExternalLink: true + }, + { + label: "Link with item icon", + iconName: "Chat", + hasExternalLink: true + }, + { + label: "An external link", + iconName: "Folder", + description: "And an additional description", + hasExternalLink: true + }, + ], +}; + /** * When a menu item’s label or description exceed the available horizontal space, the default behavior is to wrap the * text to a new line. @@ -661,7 +764,7 @@ TextOverflow.parameters = { TextOverflow.args = { customStyles: { "max-inline-size": "175px", - } + }, }; // story used in Picker component as well as docs page @@ -721,13 +824,13 @@ WithDividersAndHeaders.args = { label: "Subtract", iconName: "SelectNone", }, - ] + ], }, { type: "divider" }, { idx: 2, heading: "Actions", - sectionDescription:"With an optional description", + sectionDescription: "With an optional description", id: "menu-actions", selectionMode: "single", items: [ @@ -735,9 +838,9 @@ WithDividersAndHeaders.args = { label: "Deselect", iconName: "SelectNo", isDisabled: true, - } - ] - } + }, + ], + }, ], }; @@ -747,6 +850,6 @@ WithForcedColors.tags = ["!autodocs", "!dev"]; WithForcedColors.parameters = { chromatic: { forcedColors: "active", - modes: disableDefaultModes + modes: disableDefaultModes, }, }; diff --git a/components/menu/stories/menu.test.js b/components/menu/stories/menu.test.js index 71165c3c568..a5dc9eea3af 100644 --- a/components/menu/stories/menu.test.js +++ b/components/menu/stories/menu.test.js @@ -193,6 +193,7 @@ export const MenuItemGroup = Variants({ }, { testHeading: "Multi-selection: unselected", + description: undefined, selectionMode: "multiple", label: "Share", iconName: "Share", @@ -200,6 +201,7 @@ export const MenuItemGroup = Variants({ }, { testHeading: "Multi-selection with thumbnails: unselected", + description: undefined, selectionMode: "multiple", label: "Share", iconName: "Share", @@ -239,13 +241,13 @@ export const MenuItemGroup = Variants({ thumbnailUrl: "thumbnail.png" }, { - testHeading: "Multi-selection: switches + labels", + testHeading: "Multi-selection: switches + description", selectionMode: "multiple", hasActions: true, label: "Menu item", }, { - testHeading: "Multi-selection with thumbnails: switches + labels", + testHeading: "Multi-selection with thumbnails: switches + description", selectionMode: "multiple", hasActions: true, label: "Menu item", @@ -255,11 +257,6 @@ export const MenuItemGroup = Variants({ testHeading: "Drill-in", isDrillIn: true, }, - { - testHeading: "Drill-in with thumbnails", - isDrillIn: true, - thumbnailUrl: "thumbnail.png" - }, { testHeading: "Truncation", description: "Description will wrap", @@ -326,23 +323,23 @@ export const MenuItemGroup = Variants({ { testHeading: "Without icon", iconName: undefined, - include: ["No selection", "No selection, with description", "Single selection: selected", "Single selection: unselected", "Multi-selection: selected", "Multi-selection: unselected", "Multi-selection: unselected switches", "Multi-selection: selected switches", "Multi-selection: switches + labels", "Drill-in", "Truncation", "Text wrapping"], + include: ["No selection", "No selection, with description", "Single selection: selected", "Single selection: unselected", "Multi-selection: selected", "Multi-selection: unselected", "Multi-selection: unselected switches", "Multi-selection: selected switches", "Multi-selection: switches + description", "Drill-in", "Truncation", "Text wrapping"], }, { testHeading: "Without value", value: undefined, - include: ["No selection", "No selection, with thumbnails", "No selection, with description", "No selection, with thumbnails, description", "Multi-selection: unselected", "Multi-selection with thumbnails: unselected", "Multi-selection: switches + labels", "Multi-selection with thumbnails: switches + labels", "Drill-in", "Drill-in with thumbnails"], + include: ["No selection", "No selection, with thumbnails", "No selection, with description", "No selection, with thumbnails, description", "Multi-selection: unselected", "Multi-selection with thumbnails: unselected", "Multi-selection: switches + description", "Multi-selection with thumbnails: switches + description", "Drill-in", "Drill-in with thumbnails"], }, { testHeading: "Without value or icon", iconName: undefined, value: undefined, - include: ["No selection", "No selection, with description", "Multi-selection: unselected", "Multi-selection: switches + labels", "Drill-in"], + include: ["No selection", "No selection, with description", "Multi-selection: unselected", "Multi-selection: switches + description", "Drill-in"], }, { testHeading: "With value", value: "⌘ N", - include: ["Truncation", "Truncation with thumbnails", "Text wrapping", "Text wrapping with thumbnails"], + include: ["Single selection: selected", "Single selection: unselected", "Multi-selection: selected", "Multi-selection: unselected", "Multi-selection: unselected switches", "Multi-selection: selected switches", "Multi-selection: switches + description", "Truncation", "Truncation with thumbnails", "Text wrapping", "Text wrapping with thumbnails"], }, { testHeading: "With multi-select switch", @@ -363,15 +360,34 @@ export const MenuItemGroup = Variants({ include: ["Drill-in", "Drill-in with thumbnails", "Truncation", "Truncation with thumbnails", "Text wrapping", "Text wrapping with thumbnails"], }, { + testHeading: "With description", + description: "Menu item description", + include: ["Single selection: selected", "Single selection with thumbnails: selected", "Single selection: unselected", "Single selection with thumbnails: unselected", "Multi-selection: selected","Multi-selection with thumbnails: selected","Multi-selection: unselected","Multi-selection with thumbnails: unselected","Multi-selection: unselected switches","Multi-selection with thumbnails: unselected switches","Multi-selection: selected switches","Multi-selection with thumbnails: selected switches",] + }, + { + testHeading: "Unavailable", + isUnavailable: true, + value: undefined, + include: ["No selection", "No selection, with thumbnails", "No selection, with description", "No selection, with thumbnails, description", "Truncation","Text wrapping"] + }, + { + testHeading: "Unavailable without icon", + isUnavailable: true, + value: undefined, + iconName: undefined, + include: ["No selection", "No selection, with description","Truncation","Text wrapping"] + }, + { + // Not allowed for use with thumbnails testHeading: "External link", hasExternalLink: true, - include: ["No selection", "No selection, with thumbnails", "No selection, with description", "No selection, with thumbnails, description", "Truncation", "Truncation with thumbnails", "Text wrapping", "Text wrapping with thumbnails"] + include: ["No selection", "No selection, with description", "Truncation", "Text wrapping"] }, { testHeading: "External link without value", hasExternalLink: true, value: undefined, - include: ["No selection", "No selection, with thumbnails", "No selection, with description", "No selection, with thumbnails, description"] + include: ["No selection", "No selection, with description"] }, { testHeading: "External link without value, icon", diff --git a/components/menu/stories/template.js b/components/menu/stories/template.js index d77847e629f..2415b816acf 100644 --- a/components/menu/stories/template.js +++ b/components/menu/stories/template.js @@ -66,9 +66,11 @@ const Visual = ({ iconSet, rootClass, size, - thumbnailUrl + thumbnailUrl, + hasExternalLink, + isDrillIn }) => { - if (thumbnailUrl) { + if (thumbnailUrl && !(hasExternalLink || isDrillIn)) { return html` ${Thumbnail({ imageURL: thumbnailUrl, @@ -100,11 +102,14 @@ const StartAction = ({ isCollapsible, isDisabled, isSelected, + isUnavailable, rootClass, selectionMode, size, context }) => { + if (isUnavailable) return null; + if (isCollapsible || (selectionMode == "single" && isSelected)) { return html` ${Icon( @@ -143,6 +148,7 @@ const EndAction = ({ hasExternalLink, hasActions, idx, + isUnavailable, isDisabled, isDrillIn, isSelected, @@ -182,22 +188,38 @@ const EndAction = ({ )} ${when( - hasExternalLink && !(hasActions && selectionMode === "multiple"), + hasExternalLink && !(isUnavailable || isDrillIn) && !(hasActions && selectionMode === "multiple"), () => html`