Skip to content

Commit 5dfd67a

Browse files
feat(HorizontalScroll): add focusable state to scrollable container (#8785)
* feat(HorizontalScroll): add focusable state to scrollable container * test: update tabs screens * test: fix test * Revert "test: update tabs screens" This reverts commit aa6af1b. * fix(HorizontalScroll): revert overflow: hidden delete
1 parent ce4dca0 commit 5dfd67a

File tree

6 files changed

+62
-3
lines changed

6 files changed

+62
-3
lines changed

packages/vkui/src/components/HorizontalScroll/HorizontalScroll.e2e-playground.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,9 @@ export const HorizontalScrollWithoutHasMousePlayground = ({
113113
</ConfigProvider>
114114
);
115115
};
116+
117+
export const HorizontalScrollWithFocusVisible = (props: ComponentPlaygroundProps) => (
118+
<ComponentPlayground {...props}>
119+
{(props: HorizontalScrollProps) => <HorizontalScroll {...props}>{items}</HorizontalScroll>}
120+
</ComponentPlayground>
121+
);

packages/vkui/src/components/HorizontalScroll/HorizontalScroll.e2e.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { ViewWidth } from '../../lib/adaptivity';
33
import {
44
HorizontalScrollMobilePlayground,
55
HorizontalScrollSmallTabletPlayground,
6+
HorizontalScrollWithFocusVisible,
67
HorizontalScrollWithHasMousePlayground,
78
HorizontalScrollWithoutHasMousePlayground,
89
} from './HorizontalScroll.e2e-playground';
@@ -87,3 +88,25 @@ test.describe('HorizontalScroll', () => {
8788
});
8889
});
8990
});
91+
92+
test.describe('HorizontalScroll', () => {
93+
test.use({
94+
adaptivityProviderProps: {
95+
viewWidth: ViewWidth.SMALL_TABLET,
96+
hasPointer: true,
97+
},
98+
onlyForPlatforms: ['android'],
99+
});
100+
101+
test('State: Focus Visible', async ({
102+
mount,
103+
page,
104+
expectScreenshotClippedToContent,
105+
componentPlaygroundProps,
106+
}) => {
107+
await mount(<HorizontalScrollWithFocusVisible {...componentPlaygroundProps} />);
108+
await page.emulateMedia({ reducedMotion: 'reduce' });
109+
await page.keyboard.press('Tab');
110+
await expectScreenshotClippedToContent();
111+
});
112+
});

packages/vkui/src/components/HorizontalScroll/HorizontalScroll.test.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,18 @@ describe('HorizontalScroll', () => {
7171
it('disables navigation to arrows by keyboard', async () => {
7272
jest.useFakeTimers();
7373

74+
let scrollContainer: HTMLDivElement | null = null;
75+
7476
const result = render(
7577
<AdaptivityProvider hasPointer>
76-
<HorizontalScroll getRef={mockRef} data-testid="horizontal-scroll" showArrows="always">
78+
<HorizontalScroll
79+
getRef={(e) => {
80+
scrollContainer = e;
81+
mockRef(e);
82+
}}
83+
data-testid="horizontal-scroll"
84+
showArrows="always"
85+
>
7786
<button
7887
type="button"
7988
data-testid="focusable-element"
@@ -88,6 +97,9 @@ describe('HorizontalScroll', () => {
8897

8998
expect(document.activeElement).toBe(document.body);
9099

100+
await userEvent.tab();
101+
expect(document.activeElement).toBe(scrollContainer);
102+
91103
await userEvent.tab();
92104
expect(document.activeElement).toBe(result.getByTestId('focusable-element'));
93105

@@ -236,7 +248,7 @@ describe('HorizontalScroll', () => {
236248
});
237249
});
238250

239-
function mockRef(element: HTMLDivElement) {
251+
function mockRef(element: HTMLDivElement | null) {
240252
if (!element) {
241253
return;
242254
}

packages/vkui/src/components/HorizontalScroll/HorizontalScroll.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import { classNames, noop } from '@vkontakte/vkjs';
55
import { useAdaptivityHasPointer } from '../../hooks/useAdaptivityHasPointer';
66
import { useConfigDirection } from '../../hooks/useConfigDirection';
77
import { useExternRef } from '../../hooks/useExternRef';
8+
import { useFocusVisible } from '../../hooks/useFocusVisible';
9+
import { useFocusVisibleClassName } from '../../hooks/useFocusVisibleClassName';
810
import { easeInOutSine } from '../../lib/fx';
911
import { useIsomorphicLayoutEffect } from '../../lib/useIsomorphicLayoutEffect';
1012
import type { HasRef, HTMLAttributesWithRootRef } from '../../types';
@@ -216,6 +218,11 @@ export const HorizontalScroll = ({
216218
}: HorizontalScrollProps): React.ReactNode => {
217219
const [canScrollStart, setCanScrollStart] = React.useState(false);
218220
const [canScrollEnd, setCanScrollEnd] = React.useState(false);
221+
const { focusVisible, ...focusEvents } = useFocusVisible();
222+
const focusVisibleClassNames = useFocusVisibleClassName({
223+
focusVisible,
224+
});
225+
219226
const direction = useConfigDirection();
220227
const isRtl = direction === 'rtl';
221228

@@ -346,7 +353,12 @@ export const HorizontalScroll = ({
346353
onClick={scrollToEnd}
347354
/>
348355
)}
349-
<div className={styles.in} ref={scrollerRef}>
356+
<div
357+
className={classNames(styles.in, focusVisibleClassNames)}
358+
ref={scrollerRef}
359+
tabIndex={0}
360+
{...focusEvents}
361+
>
350362
<ContentWrapperComponent
351363
className={classNames(styles.inWrapper, contentWrapperClassName)}
352364
ref={contentWrapperRef}
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)