Skip to content

Commit 61ee2ad

Browse files
fix: keyboard accessibility for example site (#5946)
* fix: keyboard accessibility for example site * chore: remove redundant assignment of DOM attribute Co-authored-by: Joe Anderson <joe@anderbell.studio> * refactor: replace MouseEvent with PointerEvent for improved button interactions --------- Co-authored-by: Joe Anderson <joe@anderbell.studio>
1 parent a8fc9a4 commit 61ee2ad

File tree

8 files changed

+79
-29
lines changed

8 files changed

+79
-29
lines changed

site/examples/ts/code-highlighting.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import 'prismjs/components/prism-python'
1010
import 'prismjs/components/prism-sql'
1111
import 'prismjs/components/prism-tsx'
1212
import 'prismjs/components/prism-typescript'
13-
import React, { ChangeEvent, MouseEvent, useCallback, useState } from 'react'
13+
import React, { ChangeEvent, PointerEvent, useCallback, useState } from 'react'
1414
import {
1515
Editor,
1616
Element,
@@ -143,10 +143,10 @@ const CodeBlockButton = () => {
143143
<Button
144144
data-test-id="code-block-button"
145145
active
146-
onMouseDown={(event: MouseEvent<HTMLButtonElement>) => {
146+
onPointerDown={(event: PointerEvent<HTMLButtonElement>) => {
147147
event.preventDefault()
148-
handleClick()
149148
}}
149+
onClick={handleClick}
150150
>
151151
<Icon>code</Icon>
152152
</Button>

site/examples/ts/components/index.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,16 @@ export const Button = React.forwardRef(
2020
reversed: boolean
2121
} & BaseProps
2222
>,
23-
ref: Ref<HTMLSpanElement>
23+
ref: Ref<HTMLButtonElement>
2424
) => (
25-
<span
25+
<button
2626
{...props}
2727
ref={ref}
2828
className={cx(
29-
className,
3029
css`
30+
border: none;
31+
background: none;
32+
padding: 0;
3133
cursor: pointer;
3234
color: ${reversed
3335
? active
@@ -36,7 +38,8 @@ export const Button = React.forwardRef(
3638
: active
3739
? 'black'
3840
: '#ccc'};
39-
`
41+
`,
42+
className
4043
)}
4144
/>
4245
)

site/examples/ts/editable-voids.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { css } from '@emotion/css'
2-
import React, { MouseEvent, useMemo, useState } from 'react'
2+
import React, { PointerEvent, useMemo, useState } from 'react'
33
import { createEditor, Descendant, Transforms } from 'slate'
44
import { withHistory } from 'slate-history'
55
import {
@@ -130,10 +130,10 @@ const InsertEditableVoidButton = () => {
130130
const editor = useSlateStatic()
131131
return (
132132
<Button
133-
onMouseDown={(event: MouseEvent<HTMLSpanElement>) => {
133+
onPointerDown={(event: PointerEvent<HTMLButtonElement>) => {
134134
event.preventDefault()
135-
insertEditableVoid(editor)
136135
}}
136+
onClick={() => insertEditableVoid(editor)}
137137
>
138138
<Icon>add</Icon>
139139
</Button>

site/examples/ts/iframe.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import isHotkey from 'is-hotkey'
2-
import React, { MouseEvent, useCallback, useMemo, useState } from 'react'
2+
import React, { PointerEvent, useCallback, useMemo, useState } from 'react'
33
import { createPortal } from 'react-dom'
44
import { Editor, createEditor, Descendant } from 'slate'
55
import { withHistory } from 'slate-history'
@@ -115,10 +115,10 @@ const MarkButton = ({ format, icon }: MarkButtonProps) => {
115115
return (
116116
<Button
117117
active={isMarkActive(editor, format)}
118-
onMouseDown={(event: MouseEvent) => {
118+
onPointerDown={(event: PointerEvent<HTMLButtonElement>) => {
119119
event.preventDefault()
120-
toggleMark(editor, format)
121120
}}
121+
onClick={() => toggleMark(editor, format)}
122122
>
123123
<Icon>{icon}</Icon>
124124
</Button>

site/examples/ts/images.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { css } from '@emotion/css'
22
import imageExtensions from 'image-extensions'
33
import isHotkey from 'is-hotkey'
44
import isUrl from 'is-url'
5-
import React, { MouseEvent, useMemo } from 'react'
5+
import React, { PointerEvent, useMemo } from 'react'
66
import { Descendant, Transforms, createEditor } from 'slate'
77
import { withHistory } from 'slate-history'
88
import {
@@ -135,6 +135,9 @@ const Image = ({
135135
/>
136136
<Button
137137
active
138+
onPointerDown={(event: PointerEvent<HTMLButtonElement>) => {
139+
event.preventDefault()
140+
}}
138141
onClick={() => Transforms.removeNodes(editor, { at: path })}
139142
className={css`
140143
display: ${selected && focused ? 'inline' : 'none'};
@@ -155,8 +158,10 @@ const InsertImageButton = () => {
155158
const editor = useSlateStatic()
156159
return (
157160
<Button
158-
onMouseDown={(event: MouseEvent) => {
161+
onPointerDown={(event: PointerEvent<HTMLButtonElement>) =>
159162
event.preventDefault()
163+
}
164+
onClick={() => {
160165
const url = window.prompt('Enter the URL of the image:')
161166
if (url && !isImageUrl(url)) {
162167
alert('URL is not an image')

site/examples/ts/inlines.tsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { css } from '@emotion/css'
22
import { isKeyHotkey } from 'is-hotkey'
33
import isUrl from 'is-url'
4-
import React, { MouseEvent, useMemo } from 'react'
4+
import React, { PointerEvent, useMemo } from 'react'
55
import {
66
createEditor,
77
Descendant,
@@ -400,8 +400,10 @@ const AddLinkButton = () => {
400400
return (
401401
<Button
402402
active={isLinkActive(editor)}
403-
onMouseDown={(event: MouseEvent) => {
403+
onPointerDown={(event: PointerEvent<HTMLButtonElement>) =>
404404
event.preventDefault()
405+
}
406+
onClick={() => {
405407
const url = window.prompt('Enter the URL of the link:')
406408
if (!url) return
407409
insertLink(editor, url)
@@ -418,7 +420,10 @@ const RemoveLinkButton = () => {
418420
return (
419421
<Button
420422
active={isLinkActive(editor)}
421-
onMouseDown={(event: MouseEvent) => {
423+
onPointerDown={(event: PointerEvent<HTMLButtonElement>) =>
424+
event.preventDefault()
425+
}
426+
onClick={() => {
422427
if (isLinkActive(editor)) {
423428
unwrapLink(editor)
424429
}
@@ -434,8 +439,10 @@ const ToggleEditableButtonButton = () => {
434439
return (
435440
<Button
436441
active
437-
onMouseDown={(event: MouseEvent) => {
442+
onPointerDown={(event: PointerEvent<HTMLButtonElement>) =>
438443
event.preventDefault()
444+
}
445+
onClick={() => {
439446
if (isButtonActive(editor)) {
440447
unwrapButton(editor)
441448
} else {

site/examples/ts/richtext.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import isHotkey from 'is-hotkey'
2-
import React, { KeyboardEvent, MouseEvent, useCallback, useMemo } from 'react'
2+
import React, { KeyboardEvent, PointerEvent, useCallback, useMemo } from 'react'
33
import {
44
Descendant,
55
Editor,
@@ -247,10 +247,10 @@ const BlockButton = ({ format, icon }: BlockButtonProps) => {
247247
format,
248248
isAlignType(format) ? 'align' : 'type'
249249
)}
250-
onMouseDown={(event: MouseEvent<HTMLSpanElement>) => {
250+
onPointerDown={(event: PointerEvent<HTMLButtonElement>) =>
251251
event.preventDefault()
252-
toggleBlock(editor, format)
253-
}}
252+
}
253+
onClick={() => toggleBlock(editor, format)}
254254
>
255255
<Icon>{icon}</Icon>
256256
</Button>
@@ -267,10 +267,10 @@ const MarkButton = ({ format, icon }: MarkButtonProps) => {
267267
return (
268268
<Button
269269
active={isMarkActive(editor, format)}
270-
onMouseDown={(event: MouseEvent<HTMLSpanElement>) => {
270+
onPointerDown={(event: PointerEvent<HTMLButtonElement>) =>
271271
event.preventDefault()
272-
toggleMark(editor, format)
273-
}}
272+
}
273+
onClick={() => toggleMark(editor, format)}
274274
>
275275
<Icon>{icon}</Icon>
276276
</Button>

site/pages/examples/[example].tsx

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,9 @@ const TabList = ({
135135
...props
136136
}: React.HTMLAttributes<HTMLDivElement> & { isVisible?: boolean }) => (
137137
<div
138+
role="menu"
139+
aria-label="Examples navigation"
140+
aria-hidden={!isVisible}
138141
{...props}
139142
className={css`
140143
background-color: #222;
@@ -143,11 +146,14 @@ const TabList = ({
143146
overflow: auto;
144147
padding-top: 0.2em;
145148
position: absolute;
146-
transition: width 0.2s;
149+
transition:
150+
width 0.2s,
151+
visibility 0.2s;
147152
width: ${isVisible ? '200px' : '0'};
148153
white-space: nowrap;
149154
max-height: 70vh;
150155
z-index: 3; /* To appear above the underlay */
156+
visibility: ${isVisible ? 'visible' : 'hidden'};
151157
`}
152158
/>
153159
)
@@ -171,10 +177,16 @@ const TabListUnderlay = ({
171177
)
172178

173179
const TabButton = (props: React.HTMLAttributes<HTMLSpanElement>) => (
174-
<span
180+
<button
175181
{...props}
182+
aria-label="Toggle examples menu"
183+
aria-haspopup="menu"
176184
className={css`
177185
margin-left: 0.8em;
186+
background: none;
187+
border: none;
188+
cursor: pointer;
189+
padding: 0;
178190
179191
&:hover {
180192
cursor: pointer;
@@ -204,6 +216,8 @@ const Tab = React.forwardRef(
204216
<a
205217
ref={ref}
206218
href={href}
219+
role="menuitem"
220+
aria-current={active ? 'page' : undefined}
207221
{...props}
208222
className={css`
209223
display: inline-block;
@@ -342,6 +356,12 @@ const ExamplePage = ({ example }: { example: string }) => {
342356
e.stopPropagation()
343357
setShowTabs(!showTabs)
344358
}}
359+
onKeyDown={(e: React.KeyboardEvent) => {
360+
if (e.key === 'Escape') {
361+
setShowTabs(false)
362+
}
363+
}}
364+
aria-expanded={showTabs}
345365
>
346366
<Icon>menu</Icon>
347367
</TabButton>
@@ -368,7 +388,17 @@ const ExamplePage = ({ example }: { example: string }) => {
368388
legacyBehavior
369389
passHref
370390
>
371-
<Tab onClick={() => setShowTabs(false)}>{n}</Tab>
391+
<Tab
392+
onClick={() => setShowTabs(false)}
393+
active={p === path}
394+
onKeyDown={(e: React.KeyboardEvent) => {
395+
if (e.key === 'Escape') {
396+
setShowTabs(false)
397+
}
398+
}}
399+
>
400+
{n}
401+
</Tab>
372402
</Link>
373403
))}
374404
</TabList>
@@ -393,6 +423,11 @@ const ExamplePage = ({ example }: { example: string }) => {
393423
<TabListUnderlay
394424
isVisible={showTabs}
395425
onClick={() => setShowTabs(false)}
426+
onKeyDown={(e: React.KeyboardEvent) => {
427+
if (e.key === 'Escape') {
428+
setShowTabs(false)
429+
}
430+
}}
396431
/>
397432
</div>
398433
</ErrorBoundary>

0 commit comments

Comments
 (0)