Skip to content

Commit 2b5f38d

Browse files
committed
Add success and error toasts
1 parent 7510a33 commit 2b5f38d

File tree

1 file changed

+139
-106
lines changed

1 file changed

+139
-106
lines changed

.storybook/components/Icons.tsx

+139-106
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,13 @@ import { SearchInput } from '../../packages/circuit-ui/components/SearchInput/Se
3131
import { Select } from '../../packages/circuit-ui/components/Select/Select.js';
3232
import { SelectorGroup } from '../../packages/circuit-ui/components/SelectorGroup/SelectorGroup.js';
3333
import { Tooltip } from '../../packages/circuit-ui/components/Tooltip/Tooltip.js';
34+
import { IconButton } from '../../packages/circuit-ui/components/Button/IconButton.js';
35+
import { ToastProvider } from '../../packages/circuit-ui/components/ToastContext/ToastContext.js';
36+
import { useNotificationToast } from '../../packages/circuit-ui/components/NotificationToast/NotificationToast.js';
3437
import { clsx } from '../../packages/circuit-ui/styles/clsx.js';
3538
import { utilClasses } from '../../packages/circuit-ui/styles/utility.js';
3639
import { slugify } from '../slugify.js';
3740
import classes from './Icons.module.css';
38-
import { IconButton } from '@sumup-oss/circuit-ui';
3941

4042
function groupBy(
4143
icons: IconsManifest['icons'],
@@ -114,113 +116,144 @@ export function Icons() {
114116

115117
return (
116118
<Unstyled>
117-
<fieldset className={classes.filters}>
118-
<legend className={utilClasses.hideVisually}>Icon filters</legend>
119-
<SearchInput
120-
label="Search by name or keyword"
121-
placeholder="Search..."
122-
value={search}
123-
onChange={handleChange(setSearch)}
124-
onClear={() => setSearch('')}
125-
clearLabel="Clear"
126-
/>
127-
<Select
128-
label="Size"
129-
options={sizeOptions}
130-
value={size}
131-
onChange={handleChange(setSize)}
132-
/>
133-
<Select
134-
label="Color"
135-
options={colorOptions}
136-
value={color}
137-
onChange={handleChange(setColor)}
119+
<ToastProvider>
120+
<fieldset className={classes.filters}>
121+
<legend className={utilClasses.hideVisually}>Icon filters</legend>
122+
<SearchInput
123+
label="Search by name or keyword"
124+
placeholder="Search..."
125+
value={search}
126+
onChange={handleChange(setSearch)}
127+
onClear={() => setSearch('')}
128+
clearLabel="Clear"
129+
/>
130+
<Select
131+
label="Size"
132+
options={sizeOptions}
133+
value={size}
134+
onChange={handleChange(setSize)}
135+
/>
136+
<Select
137+
label="Color"
138+
options={colorOptions}
139+
value={color}
140+
onChange={handleChange(setColor)}
141+
/>
142+
<SelectorGroup
143+
label="Scale"
144+
options={scaleOptions}
145+
value={scale}
146+
onChange={handleChange(setScale)}
147+
/>
148+
</fieldset>
149+
150+
{activeIcons.length <= 0 ? (
151+
<Body>No icons found</Body>
152+
) : (
153+
Object.entries<IconsManifest['icons']>(
154+
groupBy(activeIcons, 'category'),
155+
).map(([category, items]) => (
156+
<section key={category} className={classes.category}>
157+
<Headline as="h2" size="m" id={slugify(category)}>
158+
{category}
159+
</Headline>
160+
<div className={classes.list}>
161+
{sortBy(items, 'name').map((icon) => (
162+
<Icon
163+
key={`${icon.name}-${icon.size}`}
164+
icon={icon}
165+
scale={scale}
166+
color={color}
167+
/>
168+
))}
169+
</div>
170+
</section>
171+
))
172+
)}
173+
</ToastProvider>
174+
</Unstyled>
175+
);
176+
}
177+
178+
function Icon({
179+
icon,
180+
scale,
181+
color,
182+
}: { icon: IconsManifest['icons'][number]; scale: string; color: string }) {
183+
const { setToast } = useNotificationToast();
184+
185+
const id = `${icon.name}-${icon.size}`;
186+
const componentName = getComponentName(
187+
icon.name,
188+
) as keyof typeof iconComponents;
189+
const Icon = iconComponents[componentName] as IconComponentType;
190+
191+
const copyIconURL = () => {
192+
const iconURL = `https://circuit.sumup.com/icons/v2/${icon.name}_${icon.size}.svg`;
193+
navigator.clipboard
194+
.writeText(iconURL)
195+
.then(() => {
196+
setToast({
197+
variant: 'success',
198+
body: `Copied the ${componentName} (${icon.size}) icon URL to the clipboard.`,
199+
});
200+
})
201+
.catch((error) => {
202+
console.error(error);
203+
setToast({
204+
variant: 'danger',
205+
body: `Failed to copy the ${componentName} (${icon.size}) icon URL to the clipboard.`,
206+
});
207+
});
208+
};
209+
210+
return (
211+
<div className={classes.wrapper}>
212+
<div className={clsx(classes['icon-wrapper'], classes[scale])}>
213+
<Icon
214+
aria-labelledby={id}
215+
size={icon.size}
216+
className={classes.icon}
217+
style={{
218+
color,
219+
backgroundColor:
220+
color === 'var(--cui-fg-on-strong)'
221+
? 'var(--cui-bg-strong)'
222+
: 'var(--cui-bg-normal)',
223+
}}
138224
/>
139-
<SelectorGroup
140-
label="Scale"
141-
options={scaleOptions}
142-
value={scale}
143-
onChange={handleChange(setScale)}
225+
</div>
226+
<span id={id} className={classes.label}>
227+
{componentName}
228+
<span className={classes.size}>{icon.size}</span>
229+
</span>
230+
{icon.deprecation && (
231+
<Tooltip
232+
type="description"
233+
label={icon.deprecation}
234+
component={(props) => (
235+
<Badge
236+
{...props}
237+
tabIndex={0}
238+
variant="warning"
239+
className={classes.badge}
240+
>
241+
Deprecated
242+
</Badge>
243+
)}
144244
/>
145-
</fieldset>
146-
147-
{activeIcons.length <= 0 ? (
148-
<Body>No icons found</Body>
149-
) : (
150-
Object.entries<IconsManifest['icons']>(
151-
groupBy(activeIcons, 'category'),
152-
).map(([category, items]) => (
153-
<section key={category} className={classes.category}>
154-
<Headline as="h2" size="m" id={slugify(category)}>
155-
{category}
156-
</Headline>
157-
<div className={classes.list}>
158-
{sortBy(items, 'name').map((icon) => {
159-
const id = `${icon.name}-${icon.size}`;
160-
const componentName = getComponentName(
161-
icon.name,
162-
) as keyof typeof iconComponents;
163-
const Icon = iconComponents[componentName] as IconComponentType;
164-
const copyIconURL = () => {
165-
const iconURL = `https://circuit.sumup.com/icons/v2/${icon.name}_${icon.size}.svg`;
166-
navigator.clipboard.writeText(iconURL);
167-
};
168-
return (
169-
<div key={id} className={classes.wrapper}>
170-
<div
171-
className={clsx(classes['icon-wrapper'], classes[scale])}
172-
>
173-
<Icon
174-
aria-labelledby={id}
175-
size={icon.size}
176-
className={classes.icon}
177-
style={{
178-
color,
179-
backgroundColor:
180-
color === 'var(--cui-fg-on-strong)'
181-
? 'var(--cui-bg-strong)'
182-
: 'var(--cui-bg-normal)',
183-
}}
184-
/>
185-
</div>
186-
<span id={id} className={classes.label}>
187-
{componentName}
188-
<span className={classes.size}>{icon.size}</span>
189-
</span>
190-
{icon.deprecation && (
191-
<Tooltip
192-
type="description"
193-
label={icon.deprecation}
194-
component={(props) => (
195-
<Badge
196-
{...props}
197-
tabIndex={0}
198-
variant="warning"
199-
className={classes.badge}
200-
>
201-
Deprecated
202-
</Badge>
203-
)}
204-
/>
205-
)}
206-
{navigator.clipboard && (
207-
<IconButton
208-
variant="tertiary"
209-
size="s"
210-
icon={iconComponents.Link}
211-
className={classes.copy}
212-
onClick={copyIconURL}
213-
>
214-
Copy URL
215-
</IconButton>
216-
)}
217-
</div>
218-
);
219-
})}
220-
</div>
221-
</section>
222-
))
223245
)}
224-
</Unstyled>
246+
{navigator.clipboard && (
247+
<IconButton
248+
variant="tertiary"
249+
size="s"
250+
icon={iconComponents.Link}
251+
className={classes.copy}
252+
onClick={copyIconURL}
253+
>
254+
Copy URL
255+
</IconButton>
256+
)}
257+
</div>
225258
);
226259
}

0 commit comments

Comments
 (0)