diff --git a/components/actionbutton/stories/template.js b/components/actionbutton/stories/template.js
index 9c1fd980055..3b3cb8930c2 100644
--- a/components/actionbutton/stories/template.js
+++ b/components/actionbutton/stories/template.js
@@ -109,7 +109,13 @@ export const Template = ({
${when(hasPopup && hasPopup !== "false", () =>
Icon({
size,
- iconName: "CornerTriangle",
+ iconName: "CornerTriangle" + ({
+ xs: "75",
+ s: "75",
+ m: "100",
+ l: "200",
+ xl: "300",
+ }[size] || "100"),
setName: "ui",
customClasses: [`${rootClass}-hold`],
}, context)
diff --git a/components/button/stories/button.test.js b/components/button/stories/button.test.js
index bdc6ee37996..ad83defdf1e 100644
--- a/components/button/stories/button.test.js
+++ b/components/button/stories/button.test.js
@@ -60,8 +60,8 @@ const ButtonIconGroup = (args, context) => Container({
testHeading: "UI icon (larger)",
content: Template({
...args,
- // UI icon that is larger than workflow sizing:
- iconName: "ArrowDown600",
+ // Largest UI icon from UI icon set:
+ iconName: "Cross600",
iconSet: "ui",
}, context),
},
diff --git a/components/icon/stories/icon.mdx b/components/icon/stories/icon.mdx
index de5d3711be7..8aa57f0b338 100644
--- a/components/icon/stories/icon.mdx
+++ b/components/icon/stories/icon.mdx
@@ -67,6 +67,14 @@ with:
+## Missing workflow icon placeholder
+
+In Storybook documentation, if a workflow icon name does not exist in the set, the placeholder "Circle" icon
+will be shown. Missing ui icons will render nothing. The following example purposefully uses an
+icon name that does not exist to demonstrate this behavior.
+
+
+
## Repositories for the icon SVG files
The UI icon SVGs are within the Spectrum CSS repository, which has its own package published to NPM:
diff --git a/components/icon/stories/icon.stories.js b/components/icon/stories/icon.stories.js
index d418250a69d..8fefeb451e1 100644
--- a/components/icon/stories/icon.stories.js
+++ b/components/icon/stories/icon.stories.js
@@ -193,3 +193,20 @@ UIArrows.tags = ["!dev"];
UIArrows.parameters = {
chromatic: { disableSnapshot: true },
};
+
+/**
+ * In Storybook documentation, if a workflow icon name does not exist in the set, the
+ * placeholder "Circle" icon will be shown. Missing ui icons will render
+ * nothing. The following example purposefully uses an icon name that does
+ * not exist to demonstrate this behavior.
+ */
+export const MissingWorkflowIcon = Default.bind({});
+MissingWorkflowIcon.storyName = "Missing workflow icon placeholder";
+MissingWorkflowIcon.tags = ["!dev"];
+MissingWorkflowIcon.args = {
+ setName: "workflow",
+ iconName: "ThisIconNameDoesNotExist",
+};
+MissingWorkflowIcon.parameters = {
+ chromatic: { disableSnapshot: true },
+};
diff --git a/components/icon/stories/template.js b/components/icon/stories/template.js
index b6deef70de5..75826ee6d40 100644
--- a/components/icon/stories/template.js
+++ b/components/icon/stories/template.js
@@ -6,7 +6,7 @@ import { ifDefined } from "lit/directives/if-defined.js";
import { styleMap } from "lit/directives/style-map.js";
import { unsafeSVG } from "lit/directives/unsafe-svg.js";
import { when } from "lit/directives/when.js";
-import { getSpriteSheetName, uiIconsCleaned, uiIconsWithDirections, workflowIconsCleaned } from "./utilities.js";
+import { appendUiIconDefaultSizing, getSpriteSheetName, uiIconsWithDirections, workflowIconsCleaned } from "./utilities.js";
import "../index.css";
@@ -33,7 +33,7 @@ import "../index.css";
export const Template = ({
rootClass = "spectrum-Icon",
size = "m",
- setName,
+ setName = "workflow",
iconName,
uiIconName,
fill,
@@ -50,113 +50,53 @@ export const Template = ({
iconName = uiIconName;
}
- // Make sure icon name is provided.
- if (!iconName) {
+ // Make sure icon set is provided.
+ if (!["ui","workflow"].includes(setName)) {
console.warn(
- "Icon: Could not render a result because no icon name was provided to the icon template."
+ `Icon "${iconName}" is missing its icon set. Make sure you are explicitly setting either the workflow or ui icon set.`
);
return html``;
}
- // Name of icon that corresponds with SVG file. This may differ from the icon name, such as with
- // directional icons that use a single icon.
- let idKey = iconName;
-
- // If a descriptor like "Right", "Left", "Down", or "Up" is present for the UI icons Chevron or
- // Arrow, use that only for the class name and not the icon fetch. This is because these use a
- // single icon file that is rotated in CSS.
- if (
- ["Right", "Left", "Down", "Up"].some((c) => idKey.includes(c)) &&
- setName === "ui"
- ) {
- idKey = idKey.replace(/(Right|Left|Down|Up)/, "");
- }
-
- // Make sure icon set is provided.
- if (!setName) {
- console.warn(
- `Icon "${idKey}" is missing its icon set. Make sure you are explicitly setting either the workflow or ui icon set.`
- );
+ // Make sure icon name is provided.
+ if (!iconName) {
+ console.warn("Icon: Could not render a result because no icon name was provided to the icon template.");
return html``;
}
/**
- * Fallback UI Icon sizing number.
- *
- * If the icon name includes its scale, we want to leave that scale. This is preferred,
- * as UI icons do not use workflow icon sizing.
+ * Append approximate sizing number to UI icons passed in without a sizing number.
*
- * If the UI icon name does not include scale, or the scale does not exist in the current
- * list of UI icons, reformat it to approximate the provided sizing for the component.
+ * Note: It's preferred for components to provide the specific UI sizing numbers in the UI icon
+ * name, rather than relying on this approximation, as UI icons do not use t-shirt sizing.
*/
- if (
- setName === "ui" &&
- (
- // Does not already have size number at the end.
- !idKey.match(/\d{2,3}$/) ||
- // If the provided icon name includes the sizing number, make sure it's a supported sizing number;
- // if not, strip it from the key.
- (
- idKey.match(/\d{2,3}$/) &&
- !uiIconsCleaned.includes(idKey)
- )
- )
- ) {
- let sizeVal;
- switch (size) {
- case "xs":
- if (["CornerTriangle", "Cross"].some(c => idKey.startsWith(c))) {
- sizeVal = "75";
- }
- else if (["Arrow", "Asterisk", "LinkOut"].some(c => idKey.startsWith(c))) {
- sizeVal = "100";
- }
- else {
- sizeVal = "50";
- }
- break;
- case "s":
- if (["Arrow", "Asterisk", "LinkOut"].some(c => idKey.startsWith(c))) {
- sizeVal = "100";
- }
- else {
- sizeVal = "75";
- }
- break;
- case "l":
- if (["Arrow"].some(c => idKey.startsWith(c))) {
- sizeVal = "400";
- }
- else {
- sizeVal = "200";
- }
- break;
- case "xl":
- case "xxl":
- if (["Arrow"].some(c => idKey.startsWith(c))) {
- sizeVal = "400";
- }
- else {
- sizeVal = "300";
- }
- break;
- default:
- sizeVal = "100";
- break;
- }
+ if (setName === "ui") {
+ iconName = appendUiIconDefaultSizing(iconName, size);
+ }
- console.warn(`Using fallback UI Icon sizing number "${sizeVal}" for "${idKey}". UI icon size was not provided or does not exist in the list of available UI icons.`);
+ // Make sure icon exists in the set.
+ if (setName == "ui" && !uiIconsWithDirections.includes(iconName)) {
+ console.warn(`Icon: Could not render an icon with the name "${iconName}" because it does not exist in the "ui" icon set.`);
+ return html``;
+ }
- // Replace sizing number on idKey and iconName with new fallback size.
- idKey = idKey.replace(/\d{2,3}$/, "");
- idKey += sizeVal;
+ if (setName == "workflow" && !workflowIconsCleaned.includes(iconName)) {
+ console.warn(`Icon: Could not render the correct icon with the name "${iconName}" because it does not exist in the "workflow" icon set. Rendering the placeholder icon instead.`);
+ iconName = "Circle";
+ }
- iconName = iconName.replace(/\d{2,3}$/, "");
- iconName += sizeVal;
+ // Name of icon that corresponds with SVG file. This may differ from the icon name, such as with
+ // directional icons that use a single icon.
+ let iconNameToLoad = iconName;
- if (!uiIconsCleaned.includes(idKey)) {
- console.error(`The UI icon "${idKey}" does not exist in the list of available UI icons.`);
- }
+ // If a descriptor like "Right", "Left", "Down", or "Up" is present for the UI icons Chevron or
+ // Arrow, use that only for the class name and not the icon fetch. This is because these use a
+ // single icon file that is rotated in CSS.
+ if (
+ ["Right", "Left", "Down", "Up"].some((c) => iconNameToLoad.includes(c)) &&
+ setName === "ui"
+ ) {
+ iconNameToLoad = iconNameToLoad.replace(/(Right|Left|Down|Up)/, "");
}
/**
@@ -178,8 +118,8 @@ export const Template = ({
*/
if (!useRef) {
let svgString;
- if (loaded?.icons && loaded?.icons[setName]?.[idKey]) {
- svgString = loaded.icons[setName][idKey];
+ if (loaded?.icons && loaded?.icons[setName]?.[iconNameToLoad]) {
+ svgString = loaded.icons[setName][iconNameToLoad];
}
// Return the individual SVG's entire markup.
@@ -194,12 +134,12 @@ export const Template = ({
)}`;
}
else {
- console.warn(`Could not find SVG markup for "${idKey}" in context.loaded.icons. Did you pass through context? Falling back to using the sprite sheet reference instead.`);
+ console.warn(`Could not find SVG markup for "${iconNameToLoad}" in context.loaded.icons. Was context passed through in the template? Falling back to using the sprite sheet reference instead.`);
}
}
// ID of the icon within the sprite sheet for its icon set.
- const iconID = getSpriteSheetName(idKey, setName);
+ const iconID = getSpriteSheetName(iconNameToLoad, setName);
// Return SVG markup with a reference to the icon ID within the sprite sheet.
return html``;
};
@@ -304,7 +244,7 @@ export const WorkflowDefaultTemplate = (args, context) => html`
},
context,
[
- "AlertCircle",
+ "AlertTriangle",
"Bell",
"Camera",
"Color",
@@ -315,7 +255,7 @@ export const WorkflowDefaultTemplate = (args, context) => html`
"Files",
"Hand",
"Lightbulb",
- "Paragraph",
+ "InfoCircle",
]
)}
`;
diff --git a/components/icon/stories/utilities.js b/components/icon/stories/utilities.js
index 2ba609a3416..16faf30eaa3 100644
--- a/components/icon/stories/utilities.js
+++ b/components/icon/stories/utilities.js
@@ -190,3 +190,27 @@ export const getUiIconSizes = (uiIcons) => {
};
export const uiIconSizes = getUiIconSizes(uiIconsWithDirections);
+
+/**
+ * If UI icon name does not have a sizing number appended, add one to approximate the provided
+ * t-shirt sizing for the component, based on the most common mapping.
+ *
+ * @param {string} uiIconName
+ * @param {string} size t-shirt sizing
+ * @returns {string} uiIconName with appended default sizing number, if one is not already present.
+ */
+export const appendUiIconDefaultSizing = (uiIconName, size = "m") => {
+ // If icon name already has a size number on the end, no change is needed.
+ if (uiIconName.match(/\d{2,3}$/)) {
+ return uiIconName;
+ }
+
+ return uiIconName + ({
+ xs: "50",
+ s: "75",
+ m: "100",
+ l: "200",
+ xl: "300",
+ xxl: "400",
+ }[size] || "100");
+};
diff --git a/components/menu/stories/template.js b/components/menu/stories/template.js
index 79c4136fdad..98b765fc520 100644
--- a/components/menu/stories/template.js
+++ b/components/menu/stories/template.js
@@ -14,22 +14,6 @@ import { when } from "lit/directives/when.js";
import "../index.css";
-/**
- * Get the tray submenu back arrow name with scale number (defined in design spec).
- */
-const iconWithScale = (size = "m", iconName = "ArrowLeft") => {
- switch (size) {
- case "s":
- return `${iconName}200`;
- case "l":
- return `${iconName}400`;
- case "xl":
- return `${iconName}500`;
- default:
- return `${iconName}300`;
- }
-};
-
export const MenuItem = (
{
rootClass = "spectrum-Menu-item",
@@ -84,10 +68,7 @@ export const MenuItem = (
${when(isCollapsible || (selectionMode == "single" && isSelected), () =>
Icon(
{
- iconName: iconWithScale(
- size,
- isCollapsible ? "ChevronRight" : "Checkmark",
- ),
+ iconName: isCollapsible ? "ChevronRight" : "Checkmark",
setName: "ui",
useRef: false,
size,
@@ -196,7 +177,7 @@ export const MenuItem = (
${when(isDrillIn, () =>
Icon(
{
- iconName: iconWithScale(size, "ChevronRight"),
+ iconName: "ChevronRight",
setName: "ui",
useRef: false,
size,
@@ -266,7 +247,12 @@ export const MenuGroup = (
>
${Icon(
{
- iconName: iconWithScale(size),
+ iconName: "ArrowRight" + ({
+ s: "100",
+ m: "100",
+ l: "400",
+ xl: "400",
+ }[size] || "100"),
setName: "ui",
size,
customClasses: ["spectrum-Menu-backIcon"],
@@ -473,7 +459,7 @@ export const DisabledItemGroup = (args, context) => {
context,
shouldTruncate: group.shouldTruncate || false,
items: group.items,
- })}
+ }, context)}
`
}, context)}
`)
@@ -582,11 +568,11 @@ export const OverflowGroup = (args, context) => {
context,
shouldTruncate: group.shouldTruncate || false,
items: group.items,
- })}
+ }, context)}
`
- })}
+ }, context)}
`)
- });
+ }, context);
};
export const SelectionGroup = (args, context) => {
@@ -700,12 +686,12 @@ export const SelectionGroup = (args, context) => {
selectionMode: group.selectionMode || "none",
hasActions: group.hasActions || false,
items: group.items,
- })
+ }, context)
}, context))
});
};
-export const SubmenuInPopover = (context) => Popover({
+export const SubmenuInPopover = (args, context) => Popover({
isOpen: true,
position: "end-top",
customStyles: {
@@ -717,7 +703,8 @@ export const SubmenuInPopover = (context) => Popover({
...args,
}, context),
content: [
- (args, context) => Template({
+ Template({
+ ...args,
items: [
{
label: "Language",
@@ -732,9 +719,8 @@ export const SubmenuInPopover = (context) => Popover({
label: "Show grid",
}
],
- ...args
}, context),
- (args, context) => Popover({
+ Popover({
isOpen: true,
position: "end-top",
customStyles: {
@@ -742,7 +728,8 @@ export const SubmenuInPopover = (context) => Popover({
"inline-size": "120px",
},
content: [
- (args, context) => Template({
+ Template({
+ ...args,
selectionMode: "single",
items: [
{
@@ -765,10 +752,8 @@ export const SubmenuInPopover = (context) => Popover({
label: "日本語",
}
],
- ...args,
}, context)
],
- ...args,
}, context)
],
}, context);
diff --git a/components/search/stories/template.js b/components/search/stories/template.js
index 09570d33581..d017e28981f 100644
--- a/components/search/stories/template.js
+++ b/components/search/stories/template.js
@@ -34,6 +34,7 @@ export const Template = ({
size,
customClasses: [`${rootClass}-textfield`],
iconName: "Magnify",
+ setName: "workflow",
type: "search",
placeholder: "Search",
name: "search",
diff --git a/components/swatch/stories/template.js b/components/swatch/stories/template.js
index c1ff884c4c6..bcf81178f6d 100644
--- a/components/swatch/stories/template.js
+++ b/components/swatch/stories/template.js
@@ -106,7 +106,12 @@ export const Template = ({
...(isMixedValue ? [Icon({
customClasses: [`${rootClass}-mixedValueIcon`],
setName: "ui",
- iconName: "Dash",
+ iconName: "Dash" + ({
+ xs: "75",
+ s: "75",
+ m: "100",
+ l: "200",
+ }[size] || "100"),
useRef: false,
}, context)] : []),
]
diff --git a/components/textfield/stories/template.js b/components/textfield/stories/template.js
index 9413a7504e3..e255eeae0ee 100644
--- a/components/textfield/stories/template.js
+++ b/components/textfield/stories/template.js
@@ -75,7 +75,7 @@ export const Template = ({
labelText,
characterCount,
iconName,
- iconSet,
+ iconSet = "workflow",
pattern,
placeholder,
name,
diff --git a/components/treeview/stories/template.js b/components/treeview/stories/template.js
index 6c15ccdacd7..7114a75a1ac 100644
--- a/components/treeview/stories/template.js
+++ b/components/treeview/stories/template.js
@@ -22,7 +22,7 @@ export const TreeViewItem = ({
isOpen,
isDropTarget,
icon,
- iconSet,
+ iconSet = "workflow",
thumbnail,
items,
variant,