Skip to content

Commit 79b92e1

Browse files
docs(badge): enable icon control (#6240)
Co-authored-by: Cory Dransfeldt <445732+cdransf@users.noreply.github.com>
1 parent d303db5 commit 79b92e1

3 files changed

Lines changed: 95 additions & 30 deletions

File tree

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/**
2+
* Copyright 2026 Adobe. All rights reserved.
3+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License. You may obtain a copy
5+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software distributed under
8+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9+
* OF ANY KIND, either express or implied. See the License for the specific language
10+
* governing permissions and limitations under the License.
11+
*/
12+
13+
import { nothing, TemplateResult } from 'lit';
14+
15+
// Matches the Spectrum icon numeric suffix convention used in icon element file names.
16+
// Aligns with spectrum-css's appendUiIconDefaultSizing mapping.
17+
const sizeToSuffix: Record<string, string> = {
18+
xs: '50',
19+
s: '75',
20+
m: '100',
21+
l: '200',
22+
xl: '300',
23+
xxl: '400',
24+
};
25+
26+
/**
27+
* Resolves a Spectrum icon TemplateResult from a namespace import of icon elements,
28+
* a PascalCase base name, and a t-shirt size string.
29+
*
30+
* Tries the size-variant key first (e.g. `Checkmark100Icon`), then falls back to
31+
* the unsuffixed key (e.g. `AlertIcon`) for icons with no size variants.
32+
*
33+
* @example
34+
* import * as Icons from '../icon/elements/index.js';
35+
* iconForSize(Icons, 'Checkmark', 'l') // → Icons.Checkmark200Icon()
36+
* iconForSize(Icons, 'Alert', 'm') // → Icons.AlertIcon()
37+
*/
38+
export const iconForSize = (
39+
icons: Record<string, () => TemplateResult>,
40+
baseName: string,
41+
size: string
42+
): TemplateResult | typeof nothing => {
43+
const suffix = sizeToSuffix[size] ?? '100';
44+
const sizedFn = icons[`${baseName}${suffix}Icon`];
45+
if (sizedFn) {
46+
return sizedFn();
47+
}
48+
const fixedFn = icons[`${baseName}Icon`];
49+
if (fixedFn) {
50+
return fixedFn();
51+
}
52+
return nothing;
53+
};

2nd-gen/packages/swc/.storybook/helpers/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@
1111
*/
1212

1313
export { formatTitle } from './format-title.js';
14+
export { iconForSize } from './icon-for-size.js';

2nd-gen/packages/swc/components/badge/stories/badge.stories.ts

Lines changed: 41 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
* governing permissions and limitations under the License.
1111
*/
1212

13-
import { html, TemplateResult } from 'lit';
13+
import { html, nothing } from 'lit';
1414
import type { Meta, StoryObj as Story } from '@storybook/web-components';
1515
import { getStorybookHelpers } from '@wc-toolkit/storybook-helpers';
1616

@@ -30,12 +30,8 @@ import {
3030
FIXED_VALUES,
3131
type FixedValues,
3232
} from '../../../../core/components/badge/Badge.types.js';
33-
import {
34-
Checkmark75Icon,
35-
Checkmark100Icon,
36-
Checkmark200Icon,
37-
Checkmark300Icon,
38-
} from '../../icon/elements/index.js';
33+
import { iconForSize } from '../../../.storybook/helpers/index.js';
34+
import * as Icons from '../../icon/elements/index.js';
3935

4036
// ────────────────
4137
// METADATA
@@ -73,13 +69,15 @@ argTypes.size = {
7369
},
7470
};
7571

76-
// @todo: create a select dropdown with all available/acceptable icons for a component.
77-
// For now, this arg is turned off in the control table since the string doesn't get parsed as HTML: SWC-1853
7872
argTypes['icon-slot'] = {
7973
...argTypes['icon-slot'],
80-
control: false,
74+
control: { type: 'select' },
75+
options: [undefined, 'Checkmark', 'Cross', 'Alert'],
8176
description:
82-
'Accepts an icon element. The control is disabled. Use the Anatomy story to see icon usage. Enhancements to this control will be added in a future release.',
77+
'Select a named icon to display in the icon slot. The control maps each name to ' +
78+
'the correct size-paired icon element via the shared `iconForSize` helper. Only ' +
79+
'UI icons currently available in 2nd-gen are offered. The full workflow icon set ' +
80+
'is not yet ported.',
8381
};
8482

8583
argTypes.outline = {
@@ -173,18 +171,6 @@ const nonSemanticLabels = {
173171

174172
const allVariantsLabels = { ...semanticLabels, ...nonSemanticLabels };
175173

176-
const checkmarkIconForSize = (size: string): TemplateResult => {
177-
const validSize: BadgeSize = BADGE_VALID_SIZES.includes(size as BadgeSize)
178-
? (size as BadgeSize)
179-
: 'm';
180-
return {
181-
s: Checkmark75Icon,
182-
m: Checkmark100Icon,
183-
l: Checkmark200Icon,
184-
xl: Checkmark300Icon,
185-
}[validSize]();
186-
};
187-
188174
const fixedLabels = {
189175
'block-start': 'Block start',
190176
'block-end': 'Block end',
@@ -197,9 +183,34 @@ const fixedLabels = {
197183
// ────────────────────
198184

199185
export const Playground: Story = {
200-
render: (args) => template(args),
186+
render: (args) => {
187+
const iconKey = (args['icon-slot'] as string) || '';
188+
const size = (
189+
BADGE_VALID_SIZES.includes(args.size as BadgeSize) ? args.size : 'm'
190+
) as BadgeSize;
191+
192+
return html`
193+
<swc-badge
194+
variant=${args.variant ?? 'neutral'}
195+
size=${size}
196+
?subtle=${args.subtle}
197+
?outline=${args.outline}
198+
fixed=${args.fixed ?? nothing}
199+
>
200+
${iconKey
201+
? html`
202+
<swc-icon size=${size} slot="icon" aria-hidden="true">
203+
${iconForSize(Icons, iconKey, size)}
204+
</swc-icon>
205+
`
206+
: nothing}
207+
${args['default-slot'] ?? ''}
208+
</swc-badge>
209+
`;
210+
},
201211
args: {
202212
'default-slot': 'Active',
213+
'icon-slot': undefined,
203214
},
204215
tags: ['autodocs', 'dev'],
205216
};
@@ -246,12 +257,12 @@ export const Anatomy: Story = {
246257
aria-label="Checkmark"
247258
>
248259
<swc-icon size=${size} slot="icon">
249-
${checkmarkIconForSize(size)}
260+
${iconForSize(Icons, 'Checkmark', size)}
250261
</swc-icon>
251262
</swc-badge>
252263
<swc-badge variant=${args.variant} size=${size}>
253264
<swc-icon size=${size} slot="icon">
254-
${checkmarkIconForSize(size)}
265+
${iconForSize(Icons, 'Checkmark', size)}
255266
</swc-icon>
256267
Icon and label
257268
</swc-badge>
@@ -286,7 +297,7 @@ export const Sizes: Story = {
286297
(size) => html`
287298
<swc-badge variant=${args.variant} size=${size}>
288299
<swc-icon size=${size} slot="icon">
289-
${checkmarkIconForSize(size)}
300+
${iconForSize(Icons, 'Checkmark', size)}
290301
</swc-icon>
291302
${sizeLabels[size]}
292303
</swc-badge>
@@ -316,7 +327,7 @@ export const Sizes: Story = {
316327
aria-label=${sizeLabels[size]}
317328
>
318329
<swc-icon size=${size} slot="icon">
319-
${checkmarkIconForSize(size)}
330+
${iconForSize(Icons, 'Checkmark', size)}
320331
</swc-icon>
321332
</swc-badge>
322333
`
@@ -637,7 +648,7 @@ export const Accessibility: Story = {
637648
<!-- Icon + text: icon is decorative, aria-hidden="true" hides it from assistive technology -->
638649
<swc-badge variant="positive" size=${args.size}>
639650
<swc-icon size=${args.size} slot="icon" aria-hidden="true">
640-
${checkmarkIconForSize(args.size)}
651+
${iconForSize(Icons, 'Checkmark', args.size)}
641652
</swc-icon>
642653
Approved
643654
</swc-badge>
@@ -650,7 +661,7 @@ export const Accessibility: Story = {
650661
aria-label="Approved"
651662
>
652663
<swc-icon size=${args.size} slot="icon">
653-
${checkmarkIconForSize(args.size)}
664+
${iconForSize(Icons, 'Checkmark', args.size)}
654665
</swc-icon>
655666
</swc-badge>
656667
`,

0 commit comments

Comments
 (0)