Skip to content

Commit d569d5f

Browse files
correi-ffrancoisno
authored andcommitted
fixes #102 Browsing with tabulation in Carousel can switch on hidden cards with buttons
1 parent a5937bd commit d569d5f

File tree

4 files changed

+82
-31
lines changed

4 files changed

+82
-31
lines changed

src/components/Card/Card.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,11 +124,21 @@ export interface CardProps {
124124
imageAlternative?: string;
125125
buttons?: Button[];
126126
roleDescription?: string;
127+
isHidden?: boolean;
127128
onAction: (button: Button) => void;
128129
}
129130

130131
const Card = React.forwardRef<HTMLLIElement, CardProps>(function cardRender(
131-
{ title, subTitle, imageUrl, imageAlternative, buttons, roleDescription, onAction }: CardProps,
132+
{
133+
title,
134+
subTitle,
135+
imageUrl,
136+
imageAlternative,
137+
buttons,
138+
roleDescription,
139+
isHidden = false,
140+
onAction,
141+
}: CardProps,
132142
ref,
133143
) {
134144
return (
@@ -143,7 +153,7 @@ const Card = React.forwardRef<HTMLLIElement, CardProps>(function cardRender(
143153
: 'Slide'
144154
}
145155
>
146-
<CardContainer>
156+
<CardContainer aria-hidden={isHidden}>
147157
{imageUrl && <CardImage src={imageUrl} alt={imageAlternative} />}
148158
<CardTitle>{title}</CardTitle>
149159
{subTitle && (
@@ -158,6 +168,7 @@ const Card = React.forwardRef<HTMLLIElement, CardProps>(function cardRender(
158168
<Button
159169
onClick={onAction.bind(null, button)}
160170
onKeyPress={onAction.bind(null, button)}
171+
{...(isHidden && { tabIndex: -1 })}
161172
>
162173
{button.label}
163174
</Button>
Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react';
22
import { storiesOf } from '@storybook/react';
33
import { action } from '@storybook/addon-actions';
4-
4+
import { UrlButton } from '../../TockContext';
55
import Carousel from './Carousel';
66
import Card, { CardProps } from '../Card';
77

@@ -14,10 +14,30 @@ const cards: CardProps[] = Array.from(Array(CARD_COUNT)).map((_, i) => ({
1414
onAction: onButtonClick,
1515
}));
1616

17-
storiesOf('Carousel', module).add('Default', () => (
18-
<Carousel>
19-
{cards.map((props: CardProps) => (
20-
<Card key={props.title} {...props} />
21-
))}
22-
</Carousel>
23-
));
17+
const cardsWithButtons: CardProps[] = Array.from(Array(CARD_COUNT)).map(
18+
(_, i) => ({
19+
title: `Card #${i}`,
20+
imageUrl: 'https://avatars0.githubusercontent.com/u/48585267?s=200&v=4',
21+
onAction: onButtonClick,
22+
buttons: [
23+
new UrlButton('Website 1', 'https://sncf.com'),
24+
new UrlButton('Website 2', 'https://sncf.com'),
25+
],
26+
}),
27+
);
28+
29+
storiesOf('Carousel', module)
30+
.add('Default', () => (
31+
<Carousel>
32+
{cards.map((props: CardProps) => (
33+
<Card key={props.title} {...props} />
34+
))}
35+
</Carousel>
36+
))
37+
.add('With buttons', () => (
38+
<Carousel>
39+
{cardsWithButtons.map((props: CardProps) => (
40+
<Card key={props.title} {...props} />
41+
))}
42+
</Carousel>
43+
));

src/components/Carousel/Carousel.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ const Carousel: (props: {
128128
const [ref, previous, next] = useCarousel<HTMLUListElement>(children?.length);
129129
const [leftVisible, rightVisible] = useArrowVisibility(
130130
ref.container,
131-
ref.items,
131+
ref.items.map((item) => item.refObject),
132132
);
133133

134134
return (
@@ -151,13 +151,15 @@ const Carousel: (props: {
151151
aria-roledescription={
152152
accessibility?.carousel?.roleDescription || 'Carousel'
153153
}
154+
tabIndex={-1}
154155
>
155156
{children?.map((child, i) =>
156157
React.cloneElement(
157158
child,
158159
{
159-
ref: ref.items[i],
160+
ref: ref.items[i].refObject,
160161
roleDescription: accessibility?.carousel?.slideRoleDescription,
162+
isHidden: ref.items[i].isHidden,
161163
},
162164
undefined,
163165
),

src/components/Carousel/hooks/useCarousel.ts

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,25 @@
1-
import { RefObject, useRef, useCallback, useEffect } from 'react';
1+
import {
2+
RefObject,
3+
useRef,
4+
useCallback,
5+
useEffect,
6+
useState,
7+
Dispatch,
8+
SetStateAction,
9+
} from 'react';
210
import useRefs from './useRefs';
311
import useMeasures, { Measure } from './useMeasures';
412

13+
type CarouselItem = {
14+
refObject: RefObject<HTMLElement>;
15+
isHidden: boolean;
16+
setIsHidden: Dispatch<SetStateAction<boolean>>;
17+
};
18+
519
type CarouselReturn<T> = [
620
{
721
container: RefObject<T>;
8-
items: RefObject<HTMLElement>[];
22+
items: CarouselItem[];
923
},
1024
() => void,
1125
() => void,
@@ -19,26 +33,24 @@ function getMeanX(
1933
return Math.round((previous.x + previous.width + target.x) / 2);
2034
}
2135

22-
function setAriaAttributes(
36+
function setHiddenItems(
2337
measures: Measure[],
24-
itemRefs: RefObject<HTMLElement>[],
38+
carouselItems: CarouselItem[],
2539
targetIndex: number,
2640
width: number,
2741
) {
2842
if (width !== 0) {
29-
itemRefs.forEach((item) => {
30-
const offsetLeftItem = item.current?.offsetLeft || 0;
43+
carouselItems.forEach((item) => {
44+
const offsetLeftItem = item.refObject.current?.offsetLeft || 0;
3145

3246
if (
3347
offsetLeftItem < measures[targetIndex].x ||
34-
offsetLeftItem + (item.current?.offsetWidth || 0) >
48+
offsetLeftItem + (item.refObject.current?.offsetWidth || 0) >
3549
measures[targetIndex].x + width
3650
) {
37-
item.current?.setAttribute('aria-hidden', 'true');
38-
item.current?.setAttribute('tabIndex', '-1');
51+
item.setIsHidden(true);
3952
} else {
40-
item.current?.removeAttribute('aria-hidden');
41-
item.current?.removeAttribute('tabIndex');
53+
item.setIsHidden(false);
4254
}
4355
});
4456
}
@@ -48,7 +60,7 @@ function scrollStep(
4860
direction: 'NEXT' | 'PREVIOUS',
4961
container: HTMLElement | null,
5062
measures: Measure[],
51-
itemRefs: RefObject<HTMLElement>[],
63+
carouselItems: CarouselItem[],
5264
) {
5365
if (!container) return;
5466
const x = container.scrollLeft;
@@ -62,7 +74,7 @@ function scrollStep(
6274
measures[targetIndex],
6375
measures[targetIndex - 1],
6476
);
65-
setAriaAttributes(measures, itemRefs, targetIndex, width);
77+
setHiddenItems(measures, carouselItems, targetIndex, width);
6678
} else {
6779
const firstLeftHidden = measures
6880
.slice()
@@ -78,30 +90,36 @@ function scrollStep(
7890
measures[targetIndex - 1],
7991
measures[targetIndex],
8092
);
81-
setAriaAttributes(measures, itemRefs, targetIndex, width);
93+
setHiddenItems(measures, carouselItems, targetIndex, width);
8294
}
8395
}
8496

8597
export default function useCarousel<T>(itemCount = 0): CarouselReturn<T> {
8698
const containerRef = useRef(null);
8799
const itemRefs = useRefs(itemCount);
88100
const measures = useMeasures(itemRefs);
101+
const carouselItems: CarouselItem[] = Array.from(Array(itemCount)).map<
102+
CarouselItem
103+
>((_, i) => {
104+
const [isHidden, setIsHidden] = useState(false);
105+
return Object.create({ refObject: itemRefs[i++], isHidden, setIsHidden });
106+
});
89107

90108
const previous = useCallback(
91-
() => scrollStep('PREVIOUS', containerRef.current, measures, itemRefs),
109+
() => scrollStep('PREVIOUS', containerRef.current, measures, carouselItems),
92110
[containerRef, measures],
93111
);
94112

95113
const next = useCallback(
96-
() => scrollStep('NEXT', containerRef.current, measures, itemRefs),
114+
() => scrollStep('NEXT', containerRef.current, measures, carouselItems),
97115
[containerRef, measures],
98116
);
99117

100118
useEffect(() => {
101119
if (measures !== undefined && measures.length !== 0) {
102-
setAriaAttributes(
120+
setHiddenItems(
103121
measures,
104-
itemRefs,
122+
carouselItems,
105123
0,
106124
(containerRef.current as HTMLElement | null)?.clientWidth || 0,
107125
);
@@ -111,7 +129,7 @@ export default function useCarousel<T>(itemCount = 0): CarouselReturn<T> {
111129
return [
112130
{
113131
container: containerRef,
114-
items: itemRefs,
132+
items: carouselItems,
115133
},
116134
previous,
117135
next,

0 commit comments

Comments
 (0)