a11y: several improvements
Massimo Melina committed
Jan 11, 2024 at 10:53 UTC
0dab6193e68569635dadaeb215126b1f4fccc2b3
8 files changed
+17
-9
admin/src/App.ts
+1
@@ -54,6 +54,7 @@ function Routed() {
54
navigate(path || '/')
55
})
56
return h(Fragment, {},
57
+ h('h1', { hidden: true }, "Admin-panel"),
58
!large && h(StickyBar, { title, openMenu: () => setOpen(true) }),
59
!large && h(Drawer, { anchor:'left', open, onClose(){ setOpen(false) } },
60
h(MainMenu, {
frontend/src/Breadcrumbs.ts
+4
-3
@@ -16,8 +16,8 @@ export function Breadcrumbs() {
16
const breadcrumbs = currentPath ? currentPath.split('/').map(x => [prev += x + '/', decodeURIComponent(x)]) : []
17
const {t} = useI18N()
18
return h(Fragment, {},
19
- h(Breadcrumb, { label: hIcon('parent', { title: t`parent folder` }), path: parent }),
20
- h(Breadcrumb, { label: hIcon('home', { title: t`home` }), path: base, current: !currentPath }),
19
+ h(Breadcrumb, { label: hIcon('parent'), title: t`parent folder`, path: parent }),
20
+ h(Breadcrumb, { label: hIcon('home'), title: t`home`, path: base, current: !currentPath }),
21
breadcrumbs.map(([path,label], i) =>
22
h(Breadcrumb, {
23
key: path,
@@ -28,7 +28,7 @@ export function Breadcrumbs() {
28
)
29
}
30
31
-function Breadcrumb({ path, label, current }:{ current?: boolean, path: string, label?: string | ReactElement }) {
31
+function Breadcrumb({ path, label, current, title }:{ current?: boolean, path: string, label?: string | ReactElement, title?: string }) {
32
const PAD = '\u00A0\u00A0' // make small elements easier to tap. Don't use min-width 'cause it requires display-inline that breaks word-wrapping
33
if (typeof label === 'string' && label.length < 3)
34
label = PAD + label + PAD
@@ -38,6 +38,7 @@ function Breadcrumb({ path, label, current }:{ current?: boolean, path: string,
38
return h(Link, {
39
className: 'breadcrumb',
40
to: path || '/',
41
+ title,
42
async onClick(ev) {
43
if (!current) return
44
if (typeof label !== 'string')
frontend/src/BrowseFiles.ts
+1
@@ -205,6 +205,7 @@ const Entry = memo(({ entry, midnight, separator }: EntryProps) => {
205
h(CustomCode, { name: 'entry', props: { entry }, ifEmpty: () => h(Fragment, {},
206
showFilter && h(Checkbox, {
207
disabled: isLink,
208
+ 'aria-label': entry.name,
209
value: selected[uri],
210
onChange(v){
211
if (v)
frontend/src/FilterBar.ts
+1
@@ -22,6 +22,7 @@ export function FilterBar() {
22
h(Checkbox, {
23
value: all,
24
tabIndex,
25
+ 'aria-label': "Select all",
26
onContextMenu(ev) {
27
ev.preventDefault()
28
select(undefined)
frontend/src/dialog.ts
+1
@@ -82,6 +82,7 @@ export function alertDialog(msg: ReactElement | string | Error, type:AlertType='
82
title: t(_.capitalize(type)),
83
icon: '!',
84
onClose: ret.resolve,
85
+ dialogProps: { role: 'alertdialog' },
86
Content
87
}))
88
frontend/src/icons.ts
+1
-1
@@ -50,7 +50,7 @@ document.fonts.ready.then(async ()=> {
50
})
51
52
interface IconProps { name:string, className?:string, alt?:string, [rest:string]: any }
53
-export const Icon = memo(({ name, alt, className='', ...props }: IconProps) => {
53
+export const Icon = memo(({ name, alt='', className='', ...props }: IconProps) => {
54
if (!name) return null
55
const [emoji, clazz=name] = SYS_ICONS[name] || []
56
const { iconsReady } = useSnapState()
frontend/src/menu.ts
+4
-3
@@ -40,7 +40,7 @@ export function MenuPanel() {
40
const list = useMemo(() => Object.keys(selected).map(s => s.slice(ofs, s.endsWith('/') ? -1 : Infinity)).join('*'), [selected])
41
42
// avoid useless dom changes while we are still waiting for necessary data
43
- const [changingButton, setChangingButton] = useState('')
43
+ const [changingButton, setChangingButton] = useState<'' | 'upload' | 'delete'>('')
44
useEffect(() => {
45
if (can_upload !== undefined)
46
setChangingButton(showFilter && can_delete ? 'delete' : (can_upload || qs.length > 0) ? 'upload' : '')
@@ -68,8 +68,9 @@ export function MenuPanel() {
68
id: 'upload-button',
69
icon: 'upload',
70
label: t`Upload`,
71
- tabIndex: changingButton === 'upload' ? undefined : -1,
72
- className: changingButton === 'upload' ? 'show-sliding ' + (uploading ? 'ani-working' : '') : 'before-sliding',
71
+ disabled: !changingButton,
72
+ tabIndex: changingButton ? undefined : -1,
73
+ className: changingButton ? 'show-sliding ' + (uploading ? 'ani-working' : '') : 'before-sliding',
74
onClick: showUpload,
75
}),
76
h(MenuButton, showFilter && can_delete ? {
shared/dialogs.ts
+4
-2
@@ -101,8 +101,10 @@ function Dialog(d:DialogOptions) {
101
className: 'dialog-icon dialog-closer',
102
onClick() { closeDialog() }
103
}, d.closableContent),
104
- d.icon && h('div', { className: 'dialog-icon dialog-type' + (typeof d.icon === 'string' ? ' dialog-icon-text' : '') },
105
- componentOrNode(d.icon)),
104
+ d.icon && h('div', {
105
+ className: 'dialog-icon dialog-type' + (typeof d.icon === 'string' ? ' dialog-icon-text' : ''),
106
+ 'aria-hidden': true,
107
+ }, componentOrNode(d.icon)),
108
h('div', { className: 'dialog-title' }, componentOrNode(d.title)),
109
h('div', { className: 'dialog-content' }, h(d.Content || 'div'))
110
)