Skip to content

Commit ad66209

Browse files
authored
Merge pull request storybookjs#33876 from storybookjs/sidnioulz/fix-focusable-scroll-area
A11y: Add ScrollArea prop focusable for when it has static children
2 parents c046a5f + 0cb0d09 commit ad66209

3 files changed

Lines changed: 59 additions & 6 deletions

File tree

code/core/src/components/components/ScrollArea/ScrollArea.stories.tsx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,37 @@ export const CustomSize = () => (
123123
))}
124124
</ScrollArea>
125125
);
126+
127+
export const FocusableVertical = () => (
128+
<ScrollArea vertical focusable>
129+
{list((i) => (
130+
<Fragment key={i}>
131+
<Block>{i}</Block>
132+
<br />
133+
</Fragment>
134+
))}
135+
</ScrollArea>
136+
);
137+
138+
export const FocusableHorizontal = () => (
139+
<ScrollArea horizontal focusable>
140+
<div style={{ padding: 5 }}>
141+
{list((i) => (
142+
<Block key={i}>{i}</Block>
143+
))}
144+
</div>
145+
</ScrollArea>
146+
);
147+
148+
export const FocusableBoth = () => (
149+
<ScrollArea horizontal vertical focusable>
150+
{list((i) => (
151+
<Fragment key={i}>
152+
{list((ii) => (
153+
<Block key={ii}>{ii * i}</Block>
154+
))}
155+
<br />
156+
</Fragment>
157+
))}
158+
</ScrollArea>
159+
);

code/core/src/components/components/ScrollArea/ScrollArea.tsx

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ export interface ScrollAreaProps {
1111
offset?: number;
1212
scrollbarSize?: number;
1313
scrollPadding?: number | string;
14+
/**
15+
* Set this to define a tabIndex on the scrollable content; only needed when content has no
16+
* interactive elements.
17+
*/
18+
focusable?: boolean;
1419
}
1520

1621
const ScrollAreaRoot = styled(ScrollAreaPrimitive.Root)<{ scrollbarsize: number; offset: number }>(
@@ -23,10 +28,18 @@ const ScrollAreaRoot = styled(ScrollAreaPrimitive.Root)<{ scrollbarsize: number;
2328
})
2429
);
2530

26-
const ScrollAreaViewport = styled(ScrollAreaPrimitive.Viewport)({
27-
width: '100%',
28-
height: '100%',
29-
});
31+
const ScrollAreaViewport = styled(ScrollAreaPrimitive.Viewport)<{ focusable: boolean }>(
32+
({ focusable, theme }) => ({
33+
width: '100%',
34+
height: '100%',
35+
'&:focus': focusable
36+
? {
37+
outline: `2px solid ${theme.color.secondary}`,
38+
outlineOffset: -2,
39+
}
40+
: {},
41+
})
42+
);
3043

3144
const ScrollAreaScrollbar = styled(ScrollAreaPrimitive.Scrollbar)<{
3245
offset: number;
@@ -89,11 +102,17 @@ export const ScrollArea = forwardRef<HTMLDivElement, ScrollAreaProps>(
89102
scrollbarSize = 6,
90103
scrollPadding = 0,
91104
className,
105+
focusable = false,
92106
},
93107
ref
94108
) => (
95109
<ScrollAreaRoot scrollbarsize={scrollbarSize} offset={offset} className={className}>
96-
<ScrollAreaViewport ref={ref} style={{ scrollPadding }}>
110+
<ScrollAreaViewport
111+
ref={ref}
112+
style={{ scrollPadding }}
113+
tabIndex={focusable ? 0 : undefined}
114+
focusable={focusable}
115+
>
97116
{children}
98117
</ScrollAreaViewport>
99118
{horizontal && (

code/core/src/components/components/syntaxhighlighter/syntaxhighlighter.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ const Wrapper = styled.div<WrapperProps>(
9191
);
9292

9393
const UnstyledScroller = ({ children, className }: ScrollAreaProps) => (
94-
<ScrollArea horizontal vertical className={className}>
94+
<ScrollArea horizontal vertical focusable className={className}>
9595
{children}
9696
</ScrollArea>
9797
);

0 commit comments

Comments
 (0)