Catalog page accessibility fixes

- Fix user picker heading level / menu item role
- Add label to Action buttons
- Fix filter control label issues
- Change table header level
- Add aria-label to select component
- Add fallback presentation role
- Add screenreader text for logo link

Signed-off-by: Eric Voshall <6836155+ericvoshall@users.noreply.github.com>
This commit is contained in:
Eric Voshall
2022-05-05 12:27:15 -04:00
parent e0752880b5
commit 3c7be6b405
8 changed files with 131 additions and 84 deletions
@@ -77,6 +77,7 @@ const SidebarLogo = () => {
to="/"
underline="none"
className={classes.link}
aria-label="Home"
>
{isOpen ? <LogoFull /> : <LogoIcon />}
</Link>
@@ -199,6 +199,7 @@ export function SelectComponent(props: SelectProps) {
<FormControl className={classes.formControl}>
<InputLabel className={classes.formLabel}>{label}</InputLabel>
<Select
aria-label={label}
value={value}
native={native}
disabled={disabled}
@@ -499,7 +499,7 @@ export function Table<T extends object = {}>(props: TableProps<T>) {
icons={tableIcons}
title={
<>
<Typography variant="h5" component="h3">
<Typography variant="h5" component="h2">
{title}
</Typography>
{subtitle && (
@@ -99,31 +99,38 @@ export const EntityLifecyclePicker = () => {
return (
<Box pb={1} pt={1}>
<Typography variant="button">Lifecycle</Typography>
<Autocomplete
aria-label="Lifecycle"
multiple
options={availableLifecycles}
value={selectedLifecycles}
onChange={(_: object, value: string[]) => setSelectedLifecycles(value)}
renderOption={(option, { selected }) => (
<FormControlLabel
control={
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
checked={selected}
/>
}
label={option}
/>
)}
size="small"
popupIcon={<ExpandMoreIcon data-testid="lifecycle-picker-expand" />}
renderInput={params => (
<TextField {...params} className={classes.input} variant="outlined" />
)}
/>
<Typography variant="button" component="label">
Lifecycle
<Autocomplete
multiple
options={availableLifecycles}
value={selectedLifecycles}
onChange={(_: object, value: string[]) =>
setSelectedLifecycles(value)
}
renderOption={(option, { selected }) => (
<FormControlLabel
control={
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
checked={selected}
/>
}
label={option}
/>
)}
size="small"
popupIcon={<ExpandMoreIcon data-testid="lifecycle-picker-expand" />}
renderInput={params => (
<TextField
{...params}
className={classes.input}
variant="outlined"
/>
)}
/>
</Typography>
</Box>
);
};
@@ -103,31 +103,36 @@ export const EntityOwnerPicker = () => {
return (
<Box pb={1} pt={1}>
<Typography variant="button">Owner</Typography>
<Autocomplete
multiple
aria-label="Owner"
options={availableOwners}
value={selectedOwners}
onChange={(_: object, value: string[]) => setSelectedOwners(value)}
renderOption={(option, { selected }) => (
<FormControlLabel
control={
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
checked={selected}
/>
}
label={option}
/>
)}
size="small"
popupIcon={<ExpandMoreIcon data-testid="owner-picker-expand" />}
renderInput={params => (
<TextField {...params} className={classes.input} variant="outlined" />
)}
/>
<Typography variant="button" component="label">
Owner
<Autocomplete
multiple
options={availableOwners}
value={selectedOwners}
onChange={(_: object, value: string[]) => setSelectedOwners(value)}
renderOption={(option, { selected }) => (
<FormControlLabel
control={
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
checked={selected}
/>
}
label={option}
/>
)}
size="small"
popupIcon={<ExpandMoreIcon data-testid="owner-picker-expand" />}
renderInput={params => (
<TextField
{...params}
className={classes.input}
variant="outlined"
/>
)}
/>
</Typography>
</Box>
);
};
@@ -95,31 +95,36 @@ export const EntityTagPicker = () => {
return (
<Box pb={1} pt={1}>
<Typography variant="button">Tags</Typography>
<Autocomplete
multiple
aria-label="Tags"
options={availableTags}
value={selectedTags}
onChange={(_: object, value: string[]) => setSelectedTags(value)}
renderOption={(option, { selected }) => (
<FormControlLabel
control={
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
checked={selected}
/>
}
label={option}
/>
)}
size="small"
popupIcon={<ExpandMoreIcon data-testid="tag-picker-expand" />}
renderInput={params => (
<TextField {...params} className={classes.input} variant="outlined" />
)}
/>
<Typography variant="button" component="label">
Tags
<Autocomplete
multiple
options={availableTags}
value={selectedTags}
onChange={(_: object, value: string[]) => setSelectedTags(value)}
renderOption={(option, { selected }) => (
<FormControlLabel
control={
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
checked={selected}
/>
}
label={option}
/>
)}
size="small"
popupIcon={<ExpandMoreIcon data-testid="tag-picker-expand" />}
renderInput={params => (
<TextField
{...params}
className={classes.input}
variant="outlined"
/>
)}
/>
</Typography>
</Box>
);
};
@@ -238,13 +238,18 @@ export const UserListPicker = (props: UserListPickerProps) => {
<Card className={classes.root}>
{filterGroups.map(group => (
<Fragment key={group.name}>
<Typography variant="subtitle2" className={classes.title}>
<Typography
variant="subtitle2"
component="span"
className={classes.title}
>
{group.name}
</Typography>
<Card className={classes.groupWrapper}>
<List disablePadding dense role="menu">
<List disablePadding dense role="menu" aria-label={group.name}>
{group.items.map(item => (
<MenuItem
role="none presentation"
key={item.id}
button
divider
@@ -254,6 +259,7 @@ export const UserListPicker = (props: UserListPickerProps) => {
disabled={filterCounts[item.id] === 0}
data-testid={`user-picker-${item.id}`}
tabIndex={0}
ContainerProps={{ role: 'menuitem' }}
>
{item.icon && (
<ListItemIcon className={classes.listIcon}>
@@ -41,6 +41,7 @@ import {
import StarBorder from '@material-ui/icons/StarBorder';
import { withStyles } from '@material-ui/core/styles';
import Star from '@material-ui/icons/Star';
import { Typography } from '@material-ui/core';
/**
* Props for {@link CatalogTable}.
@@ -115,9 +116,16 @@ export const CatalogTable = (props: CatalogTableProps) => {
const defaultActions: TableProps<CatalogTableRow>['actions'] = [
({ entity }) => {
const url = entity.metadata.annotations?.[ANNOTATION_VIEW_URL];
const title = 'View';
return {
icon: () => <OpenInNew aria-label="View" fontSize="small" />,
tooltip: 'View',
icon: () => (
<>
<Typography variant="srOnly" title={title} />
<OpenInNew fontSize="small" />
</>
),
tooltip: title,
disabled: !url,
onClick: () => {
if (!url) return;
@@ -127,9 +135,16 @@ export const CatalogTable = (props: CatalogTableProps) => {
},
({ entity }) => {
const url = entity.metadata.annotations?.[ANNOTATION_EDIT_URL];
const title = 'Edit';
return {
icon: () => <Edit aria-label="Edit" fontSize="small" />,
tooltip: 'Edit',
icon: () => (
<>
<Typography variant="srOnly" title={title} />
<Edit fontSize="small" />
</>
),
tooltip: title,
disabled: !url,
onClick: () => {
if (!url) return;
@@ -139,10 +154,17 @@ export const CatalogTable = (props: CatalogTableProps) => {
},
({ entity }) => {
const isStarred = isStarredEntity(entity);
const title = isStarred ? 'Remove from favorites' : 'Add to favorites';
return {
cellStyle: { paddingLeft: '1em' },
icon: () => (isStarred ? <YellowStar /> : <StarBorder />),
tooltip: isStarred ? 'Remove from favorites' : 'Add to favorites',
icon: () => (
<>
<Typography variant="srOnly" title={title} />
{isStarred ? <YellowStar /> : <StarBorder />}
</>
),
tooltip: title,
onClick: () => toggleStarredEntity(entity),
};
},