Make scrollable code boxes accessible by keyboards (#296)
* Make scrollable code boxes accessible by keyboards * Fixed the issue on MacOS+Chrome (triple announce) * Resize the scroll handle * Add aria-label to the scroll handle
Nurbol Alpysbayev committed
Nov 18, 2022 at 18:37 UTC
c2cfe938df7cb5bd3d7ab13a92194f374ddfcacb
1 file changed
+26
-1
theme/src/components/code.js
+26
-1
@@ -2,12 +2,19 @@ import {Absolute, BorderBox, Relative, Text} from '@primer/components'
2
import Highlight, {defaultProps} from 'prism-react-renderer'
3
import githubTheme from 'prism-react-renderer/themes/github'
4
import React from 'react'
5
+import {useState, useEffect} from 'react'
6
import ClipboardCopy from './clipboard-copy'
7
import LiveCode from './live-code'
8
9
function Code({className, children, live, noinline}) {
10
+ const [scrollHandleStyle, setScrollHandleStyle] = useState({position: 'absolute'})
11
const language = className ? className.replace(/language-/, '') : ''
12
const code = children.trim()
13
+ const scrollHandleRef = React.createRef()
14
+
15
+ useEffect(() => {
16
+ resizeScrollHandle()
17
+ })
18
19
if (live) {
20
return <LiveCode code={code} language={language} noinline={noinline} />
@@ -15,7 +22,7 @@ function Code({className, children, live, noinline}) {
22
23
return (
24
<Relative>
18
- <Absolute top={0} right={0} p={2}>
25
+ <Absolute top={0} right={0} p={2} zIndex={1}>
26
<ClipboardCopy value={code} />
27
</Absolute>
28
<Highlight
@@ -34,6 +41,8 @@ function Code({className, children, live, noinline}) {
41
border={0}
42
style={{...style, overflow: 'auto'}}
43
>
44
+ {/* This is the scroll handle, it is supposed to be focused with keyboard and scroll a wide codebox horizontally */}
45
+ <div aria-hidden="true" tabIndex={0} style={scrollHandleStyle} ref={scrollHandleRef} aria-label={code}> </div>
46
{tokens.map((line, i) => (
47
<div key={i} {...getLineProps({line, key: i})}>
48
{line.map((token, key) => (
@@ -51,6 +60,22 @@ function Code({className, children, live, noinline}) {
60
</Highlight>
61
</Relative>
62
)
63
+
64
+ /**
65
+ * Resize the scroll handle to the size of the code contents, since the former has to be positioned absolutely.
66
+ */
67
+ function resizeScrollHandle() {
68
+ // Skip if already resized.
69
+ if (typeof scrollHandleStyle.width !== 'undefined')
70
+ return
71
+
72
+ const node = scrollHandleRef.current
73
+ node.parentElement.style.position = 'relative'
74
+ const computedStyle = getComputedStyle(node.parentElement)
75
+ const height = node.parentElement.clientHeight - parseInt(computedStyle.paddingTop, 10) - parseInt(computedStyle.paddingBottom, 10)
76
+ const width = node.parentElement.scrollWidth - parseInt(computedStyle.paddingLeft, 10) - parseInt(computedStyle.paddingRight, 10)
77
+ setScrollHandleStyle({...scrollHandleStyle, height, width})
78
+ }
79
}
80
81
export default Code