Skip to content

Commit 4252d57

Browse files
committed
Refactor how icons are created and the link is opened
1 parent 4b17eac commit 4252d57

File tree

6 files changed

+91
-90
lines changed

6 files changed

+91
-90
lines changed

code/addons/a11y/src/components/A11yContext.tsx

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
type StoryFinishedPayload,
1010
} from 'storybook/internal/core-events';
1111

12-
import type { ClickEventDetails } from 'storybook/highlight';
12+
import type { ClickEventDetails, HighlightMenuItem } from 'storybook/highlight';
1313
import { HIGHLIGHT, REMOVE_HIGHLIGHT, SCROLL_INTO_VIEW } from 'storybook/highlight';
1414
import {
1515
experimental_getStatusStore,
@@ -215,15 +215,18 @@ export const A11yContextProvider: FC<PropsWithChildren> = (props) => {
215215
const handleSelect = useCallback(
216216
(itemId: string, details: ClickEventDetails) => {
217217
const [type, id] = itemId.split('.');
218-
const result = results?.[type as RuleType]?.find((r) => r.id === id);
219-
const index =
220-
result?.nodes.findIndex((n) => details.selectors.some((s) => s === String(n.target))) ?? -1;
221-
if (index !== -1) {
222-
const key = `${type}.${id}.${index + 1}`;
223-
setSelectedItems(new Map([[`${type}.${id}`, key]]));
224-
setTimeout(() => {
225-
document.getElementById(key)?.scrollIntoView({ behavior: 'smooth', block: 'center' });
226-
}, 100);
218+
const { helpUrl, nodes } = results?.[type as RuleType]?.find((r) => r.id === id) || {};
219+
const openedWindow = helpUrl && window.open(helpUrl, '_blank', 'noopener,noreferrer');
220+
if (nodes && !openedWindow) {
221+
const index =
222+
nodes.findIndex((n) => details.selectors.some((s) => s === String(n.target))) ?? -1;
223+
if (index !== -1) {
224+
const key = `${type}.${id}.${index + 1}`;
225+
setSelectedItems(new Map([[`${type}.${id}`, key]]));
226+
setTimeout(() => {
227+
document.getElementById(key)?.scrollIntoView({ behavior: 'smooth', block: 'center' });
228+
}, 100);
229+
}
227230
}
228231
},
229232
[results]
@@ -323,7 +326,7 @@ export const A11yContextProvider: FC<PropsWithChildren> = (props) => {
323326
focusStyles: {
324327
backgroundColor: 'transparent',
325328
},
326-
menu: results?.[tab as RuleType].map((result) => {
329+
menu: results?.[tab as RuleType].map<HighlightMenuItem[]>((result) => {
327330
const selectors = result.nodes
328331
.flatMap((n) => n.target)
329332
.map(String)
@@ -337,8 +340,9 @@ export const A11yContextProvider: FC<PropsWithChildren> = (props) => {
337340
},
338341
{
339342
id: `${tab}.${result.id}`,
340-
icon: 'info',
341-
title: 'View more details',
343+
iconLeft: 'info',
344+
iconRight: 'shareAlt',
345+
title: 'Learn how to resolve this violation',
342346
clickEvent: EVENTS.SELECT,
343347
selectors,
344348
},
@@ -362,7 +366,7 @@ export const A11yContextProvider: FC<PropsWithChildren> = (props) => {
362366
focusStyles: {
363367
backgroundColor: 'transparent',
364368
},
365-
menu: results?.[tab as RuleType].map((result) => {
369+
menu: results?.[tab as RuleType].map<HighlightMenuItem[]>((result) => {
366370
const selectors = result.nodes
367371
.flatMap((n) => n.target)
368372
.map(String)
@@ -376,8 +380,9 @@ export const A11yContextProvider: FC<PropsWithChildren> = (props) => {
376380
},
377381
{
378382
id: `${tab}.${result.id}`,
379-
icon: 'info',
380-
title: 'View more details',
383+
iconLeft: 'info',
384+
iconRight: 'shareAlt',
385+
title: 'Learn how to resolve this violation',
381386
clickEvent: EVENTS.SELECT,
382387
selectors,
383388
},

code/core/src/highlight/icons.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
export const iconPaths = {
2+
chevronLeft: [
3+
'M9.10355 10.1464C9.29882 10.3417 9.29882 10.6583 9.10355 10.8536C8.90829 11.0488 8.59171 11.0488 8.39645 10.8536L4.89645 7.35355C4.70118 7.15829 4.70118 6.84171 4.89645 6.64645L8.39645 3.14645C8.59171 2.95118 8.90829 2.95118 9.10355 3.14645C9.29882 3.34171 9.29882 3.65829 9.10355 3.85355L5.95711 7L9.10355 10.1464Z',
4+
],
5+
chevronRight: [
6+
'M4.89645 10.1464C4.70118 10.3417 4.70118 10.6583 4.89645 10.8536C5.09171 11.0488 5.40829 11.0488 5.60355 10.8536L9.10355 7.35355C9.29882 7.15829 9.29882 6.84171 9.10355 6.64645L5.60355 3.14645C5.40829 2.95118 5.09171 2.95118 4.89645 3.14645C4.70118 3.34171 4.70118 3.65829 4.89645 3.85355L8.04289 7L4.89645 10.1464Z',
7+
],
8+
info: [
9+
'M7 5.5a.5.5 0 01.5.5v4a.5.5 0 01-1 0V6a.5.5 0 01.5-.5zM7 4.5A.75.75 0 107 3a.75.75 0 000 1.5z',
10+
'M7 14A7 7 0 107 0a7 7 0 000 14zm0-1A6 6 0 107 1a6 6 0 000 12z',
11+
],
12+
shareAlt: [
13+
'M2 1.004a1 1 0 00-1 1v10a1 1 0 001 1h10a1 1 0 001-1v-4.5a.5.5 0 00-1 0v4.5H2v-10h4.5a.5.5 0 000-1H2z',
14+
'M7.354 7.357L12 2.711v1.793a.5.5 0 001 0v-3a.5.5 0 00-.5-.5h-3a.5.5 0 100 1h1.793L6.646 6.65a.5.5 0 10.708.707z',
15+
],
16+
};
17+
18+
export type IconName = keyof typeof iconPaths;

code/core/src/highlight/types.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { IconName } from './icons';
2+
13
export interface HighlightParameters {
24
/**
35
* Highlight configuration
@@ -17,8 +19,10 @@ export interface HighlightMenuItem {
1719
title: string;
1820
/** Description of the menu item */
1921
description?: string;
20-
/** Icon for the menu item */
21-
icon?: 'info';
22+
/** Icon for the menu item, left side */
23+
iconLeft?: IconName;
24+
/** Icon for the menu item, right side */
25+
iconRight?: IconName;
2226
/** Name for a channel event to trigger when the menu item is clicked */
2327
clickEvent?: string;
2428
/** HTML selectors for which this menu item should show (subset of `selectors`) */

code/core/src/highlight/useHighlights.stories.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,8 @@ export const Menu = meta.story({
173173
id: 'links-need-discernible-text-details',
174174
title: 'Important stuff',
175175
description: 'Click here to view more details.',
176-
icon: 'info',
176+
iconLeft: 'info',
177+
iconRight: 'shareAlt',
177178
clickEvent: 'my-click-event',
178179
},
179180
],

code/core/src/highlight/useHighlights.ts

Lines changed: 28 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
import type { Box, Highlight, HighlightOptions, RawHighlightOptions } from './types';
1313
import {
1414
createElement,
15+
createIcon,
1516
getEventDetails,
1617
hidePopover,
1718
isOverMenu,
@@ -28,54 +29,6 @@ const menuId = 'storybook-highlights-menu';
2829
const rootId = 'storybook-highlights-root';
2930
const storybookRootId = 'storybook-root';
3031

31-
const chevronLeft = () =>
32-
createElement(
33-
'svg',
34-
{ width: '14', height: '14', viewBox: '0 0 14 14', xmlns: 'http://www.w3.org/2000/svg' },
35-
[
36-
createElement('path', {
37-
'fill-rule': 'evenodd',
38-
'clip-rule': 'evenodd',
39-
d: 'M9.10355 10.1464C9.29882 10.3417 9.29882 10.6583 9.10355 10.8536C8.90829 11.0488 8.59171 11.0488 8.39645 10.8536L4.89645 7.35355C4.70118 7.15829 4.70118 6.84171 4.89645 6.64645L8.39645 3.14645C8.59171 2.95118 8.90829 2.95118 9.10355 3.14645C9.29882 3.34171 9.29882 3.65829 9.10355 3.85355L5.95711 7L9.10355 10.1464Z',
40-
fill: 'currentColor',
41-
}),
42-
]
43-
);
44-
45-
const chevronRight = () =>
46-
createElement(
47-
'svg',
48-
{ width: '14', height: '14', viewBox: '0 0 14 14', xmlns: 'http://www.w3.org/2000/svg' },
49-
[
50-
createElement('path', {
51-
'fill-rule': 'evenodd',
52-
'clip-rule': 'evenodd',
53-
d: 'M4.89645 10.1464C4.70118 10.3417 4.70118 10.6583 4.89645 10.8536C5.09171 11.0488 5.40829 11.0488 5.60355 10.8536L9.10355 7.35355C9.29882 7.15829 9.29882 6.84171 9.10355 6.64645L5.60355 3.14645C5.40829 2.95118 5.09171 2.95118 4.89645 3.14645C4.70118 3.34171 4.70118 3.65829 4.89645 3.85355L8.04289 7L4.89645 10.1464Z',
54-
fill: 'currentColor',
55-
}),
56-
]
57-
);
58-
59-
const info = () =>
60-
createElement(
61-
'svg',
62-
{ width: '14', height: '14', viewBox: '0 0 14 14', xmlns: 'http://www.w3.org/2000/svg' },
63-
[
64-
createElement('path', {
65-
'fill-rule': 'evenodd',
66-
'clip-rule': 'evenodd',
67-
d: 'M7 5.5a.5.5 0 01.5.5v4a.5.5 0 01-1 0V6a.5.5 0 01.5-.5zM7 4.5A.75.75 0 107 3a.75.75 0 000 1.5z',
68-
fill: 'currentColor',
69-
}),
70-
createElement('path', {
71-
'fill-rule': 'evenodd',
72-
'clip-rule': 'evenodd',
73-
d: 'M7 14A7 7 0 107 0a7 7 0 000 14zm0-1A6 6 0 107 1a6 6 0 000 12z',
74-
fill: 'currentColor',
75-
}),
76-
]
77-
);
78-
7932
export const useHighlights = (channel: Channel) => {
8033
if (globalThis.__STORYBOOK_HIGHLIGHT_INITIALIZED) {
8134
return;
@@ -395,7 +348,7 @@ export const useHighlights = (channel: Channel) => {
395348
font-family: inherit;
396349
font-size: inherit;
397350
}
398-
#${menuId} button:focus {
351+
#${menuId} button:focus-visible {
399352
outline-color: #029CFD;
400353
}
401354
#${menuId} button:hover {
@@ -415,7 +368,7 @@ export const useHighlights = (channel: Channel) => {
415368
margin: 1px;
416369
color: #73828C;
417370
}
418-
#${menuId} li > button:hover svg, #${menuId} li > button:focus svg {
371+
#${menuId} li > button:hover svg, #${menuId} li > button:focus-visible svg {
419372
color: #029CFD;
420373
}
421374
#${menuId} .element-list li svg {
@@ -441,6 +394,7 @@ export const useHighlights = (channel: Channel) => {
441394
#${menuId} .menu-item-content {
442395
display: flex;
443396
flex-direction: column;
397+
flex-grow: 1;
444398
}
445399
`,
446400
])
@@ -480,9 +434,9 @@ export const useHighlights = (channel: Channel) => {
480434
const asButton = selectable || selectedElement;
481435
return createElement('li', props, [
482436
createElement(asButton ? 'button' : 'div', asButton ? { type: 'button' } : {}, [
483-
selectedElement ? chevronLeft() : null,
437+
selectedElement ? createIcon('chevronLeft') : null,
484438
createElement('code', {}, [target.element.outerHTML]),
485-
selectable ? chevronRight() : null,
439+
selectable ? createIcon('chevronRight') : null,
486440
]),
487441
]);
488442
})
@@ -507,25 +461,28 @@ export const useHighlights = (channel: Channel) => {
507461
createElement(
508462
'ul',
509463
{ class: 'menu-items' },
510-
menuItems.map(({ id, title, description, icon, clickEvent: event }) => {
511-
const onClick =
512-
event && (() => channel.emit(event, id, getEventDetails(target)));
513-
return createElement('li', {}, [
514-
createElement(
515-
onClick ? 'button' : 'div',
516-
onClick
517-
? { class: 'menu-item', type: 'button', onClick }
518-
: { class: 'menu-item' },
519-
[
520-
icon === 'info' ? info() : null,
521-
createElement('div', { class: 'menu-item-content' }, [
522-
createElement(description ? 'strong' : 'span', {}, [title]),
523-
description && createElement('span', {}, [description]),
524-
]),
525-
]
526-
),
527-
]);
528-
})
464+
menuItems.map(
465+
({ id, title, description, iconLeft, iconRight, clickEvent: event }) => {
466+
const onClick =
467+
event && (() => channel.emit(event, id, getEventDetails(target)));
468+
return createElement('li', {}, [
469+
createElement(
470+
onClick ? 'button' : 'div',
471+
onClick
472+
? { class: 'menu-item', type: 'button', onClick }
473+
: { class: 'menu-item' },
474+
[
475+
iconLeft ? createIcon(iconLeft) : null,
476+
createElement('div', { class: 'menu-item-content' }, [
477+
createElement(description ? 'strong' : 'span', {}, [title]),
478+
description && createElement('span', {}, [description]),
479+
]),
480+
iconRight ? createIcon(iconRight) : null,
481+
]
482+
),
483+
]);
484+
}
485+
)
529486
),
530487
])
531488
)

code/core/src/highlight/utils.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/* eslint-env browser */
2+
import { type IconName, iconPaths } from './icons';
23
import type {
34
Box,
45
ClickEventDetails,
@@ -50,6 +51,21 @@ export const createElement = (type: string, props: Record<string, any>, children
5051
return element;
5152
};
5253

54+
export const createIcon = (name: IconName) =>
55+
iconPaths[name] &&
56+
createElement(
57+
'svg',
58+
{ width: '14', height: '14', viewBox: '0 0 14 14', xmlns: 'http://www.w3.org/2000/svg' },
59+
iconPaths[name].map((d) =>
60+
createElement('path', {
61+
fill: 'currentColor',
62+
'fill-rule': 'evenodd',
63+
'clip-rule': 'evenodd',
64+
d,
65+
})
66+
)
67+
);
68+
5369
export const normalizeOptions = (options: RawHighlightOptions): Highlight => {
5470
if ('elements' in options) {
5571
// Legacy format

0 commit comments

Comments
 (0)