Skip to content

Commit 2946071

Browse files
authored
Allow VideoPlayer tooltips to be hovered (#1051)
* use JS to control VideoTooltip hover visibility * add changeset * fix failing test * apply PR feedback * reword changeset * clear timeout on unmount * add test asserting new functionality
1 parent 9e860a1 commit 2946071

File tree

4 files changed

+70
-15
lines changed

4 files changed

+70
-15
lines changed

.changeset/heavy-turtles-accept.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@primer/react-brand': patch
3+
---
4+
5+
Tooltips in the `VideoPlayer` component now remain briefly visible after the pointer is moved away from the toggle. This small delay improves general usability and helps meet the [WCAG 1.4.13 criterion](https://www.w3.org/TR/WCAG22/#content-on-hover-or-focus) for accessible hover interactions with tooltips.

packages/react/src/VideoPlayer/VideoPlayer.module.css

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -183,10 +183,6 @@
183183
color: var(--brand-videoPlayer-controls-fgColor);
184184
}
185185

186-
.VideoPlayer__iconControl:hover .VideoPlayer__tooltip {
187-
opacity: 1;
188-
}
189-
190186
.VideoPlayer__seek {
191187
flex: 1;
192188
display: flex;
@@ -240,10 +236,6 @@
240236
border-radius: var(--brand-VideoPlayer-range-borderRadius);
241237
}
242238

243-
.VideoPlayer__range:hover .VideoPlayer__tooltip {
244-
opacity: 1;
245-
}
246-
247239
.VideoPlayer__rangeInput {
248240
-webkit-appearance: none;
249241
-moz-appearance: none;
@@ -323,10 +315,8 @@
323315
width: var(--base-size-8);
324316
height: var(--base-size-8);
325317
margin-bottom: var(--base-size-16);
326-
pointer-events: none;
327318
opacity: 0;
328319
transform: translate(-50%, 50%);
329-
transition: transform var(--brand-animation-duration-default) var(--brand-animation-easing-default);
330320
}
331321

332322
.VideoPlayer__tooltip::before {

packages/react/src/VideoPlayer/VideoPlayer.test.tsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,4 +210,33 @@ describe('VideoPlayer', () => {
210210

211211
expect(queryByLabelText('GitHub logo')).not.toBeInTheDocument()
212212
})
213+
214+
it('keeps tooltips visible for 100ms after the user stops hovering over the associated control', async () => {
215+
const user = userEvent.setup()
216+
217+
const {getByRole, getByText} = render(
218+
<VideoPlayer poster="/example-poster.jpg" title="test video">
219+
<VideoPlayer.Source src="/example.mp4" />
220+
<VideoPlayer.Track src="/example.vtt" default kind="subtitles" srcLang="en" label="English" />
221+
</VideoPlayer>,
222+
)
223+
224+
const captionsButton = getByRole('button', {name: 'Enable captions'})
225+
226+
await user.hover(captionsButton)
227+
228+
const tooltip = getByText('Enable captions')
229+
230+
expect(tooltip).toBeVisible()
231+
232+
await user.unhover(captionsButton)
233+
expect(tooltip).toBeVisible()
234+
235+
await waitFor(
236+
() => {
237+
expect(tooltip).not.toBeVisible()
238+
},
239+
{timeout: 100},
240+
)
241+
})
213242
})

packages/react/src/VideoPlayer/components/VideoTooltip/VideoTooltip.tsx

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ type VideoTooltipProps = HTMLAttributes<HTMLDivElement>
99
export const VideoTooltip = ({children, className, ...rest}: VideoTooltipProps) => {
1010
const tooltipRef = useRef<HTMLDivElement>(null)
1111
const [hasFocus, setHasFocus] = useState(false)
12+
const mouseenterTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
1213

1314
useEffect(() => {
1415
const tooltip = tooltipRef.current
@@ -18,6 +19,13 @@ export const VideoTooltip = ({children, className, ...rest}: VideoTooltipProps)
1819
return
1920
}
2021

22+
const clearTimeoutAndRef = () => {
23+
if (mouseenterTimeoutRef.current) {
24+
clearTimeout(mouseenterTimeoutRef.current)
25+
mouseenterTimeoutRef.current = null
26+
}
27+
}
28+
2129
const handleKeyDown = (e: KeyboardEvent) => {
2230
if (e.key === 'Escape') {
2331
setHasFocus(false)
@@ -34,19 +42,38 @@ export const VideoTooltip = ({children, className, ...rest}: VideoTooltipProps)
3442
setHasFocus(false)
3543
}
3644

45+
const onMouseEnterTooltip = () => {
46+
clearTimeoutAndRef()
47+
setHasFocus(true)
48+
}
49+
50+
const onMouseLeaveTooltip = () => {
51+
clearTimeoutAndRef()
52+
53+
mouseenterTimeoutRef.current = setTimeout(() => {
54+
setHasFocus(false)
55+
}, 100)
56+
}
57+
3758
parent.addEventListener('focus', checkFocus)
3859
parent.addEventListener('blur', checkFocus)
3960
parent.addEventListener('focusin', checkFocus)
4061
parent.addEventListener('focusout', checkFocus)
4162
parent.addEventListener('click', onClick)
63+
parent.addEventListener('mouseenter', onMouseEnterTooltip)
64+
parent.addEventListener('mouseleave', onMouseLeaveTooltip)
4265
window.addEventListener('keydown', handleKeyDown)
4366

4467
return () => {
68+
clearTimeoutAndRef()
69+
4570
parent.removeEventListener('focus', checkFocus)
4671
parent.removeEventListener('blur', checkFocus)
4772
parent.removeEventListener('focusin', checkFocus)
4873
parent.removeEventListener('focusout', checkFocus)
4974
parent.removeEventListener('click', onClick)
75+
parent.removeEventListener('mouseenter', onMouseEnterTooltip)
76+
parent.removeEventListener('mouseleave', onMouseLeaveTooltip)
5077
window.removeEventListener('keydown', handleKeyDown)
5178
}
5279
}, [tooltipRef])
@@ -57,11 +84,15 @@ export const VideoTooltip = ({children, className, ...rest}: VideoTooltipProps)
5784
ref={tooltipRef}
5885
{...rest}
5986
>
60-
<span className={styles.VideoPlayer__tooltipContent}>
61-
<Text className={styles.VideoPlayer__tooltipText} weight="medium">
62-
{children}
63-
</Text>
64-
</span>
87+
{hasFocus ? (
88+
<span className={styles.VideoPlayer__tooltipContent}>
89+
<Text className={styles.VideoPlayer__tooltipText} weight="medium">
90+
{children}
91+
</Text>
92+
</span>
93+
) : (
94+
<span className="visually-hidden">{children}</span>
95+
)}
6596
</div>
6697
)
6798
}

0 commit comments

Comments
 (0)