Merge pull request #11342 from ericvoshall/catalog-a11y-fixes

Accessibility updates for the Catalog page and example App
This commit is contained in:
Patrik Oldsberg
2022-05-19 11:48:17 +02:00
committed by GitHub
15 changed files with 193 additions and 96 deletions
@@ -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>
);
};
@@ -167,7 +167,7 @@ describe('<UserListPicker />', () => {
expect(
getAllByRole('menuitem').map(({ textContent }) => textContent),
).toEqual(['Owned', 'Starred', 'All']);
).toEqual(['Owned 1', 'Starred 1', 'All 4']);
});
it('includes counts alongside each filter', async () => {
@@ -183,10 +183,8 @@ describe('<UserListPicker />', () => {
// menuitem itself, so we pick off the next sibling.
await waitFor(() => {
expect(
getAllByRole('menuitem').map(
({ nextSibling }) => nextSibling?.textContent,
),
).toEqual(['1', '1', '4']);
getAllByRole('menuitem').map(({ textContent }) => textContent),
).toEqual(['Owned 1', 'Starred 1', 'All 4']);
});
});
@@ -206,10 +204,8 @@ describe('<UserListPicker />', () => {
await waitFor(() => {
expect(
getAllByRole('menuitem').map(
({ nextSibling }) => nextSibling?.textContent,
),
).toEqual(['1', '0', '2']);
getAllByRole('menuitem').map(({ textContent }) => textContent),
).toEqual(['Owned 1', 'Starred 0', 'All 2']);
});
});
@@ -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}>
@@ -261,7 +267,7 @@ export const UserListPicker = (props: UserListPickerProps) => {
</ListItemIcon>
)}
<ListItemText>
<Typography variant="body1">{item.label}</Typography>
<Typography variant="body1">{item.label} </Typography>
</ListItemText>
<ListItemSecondaryAction>
{filterCounts[item.id] ?? '-'}
@@ -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}</Typography>
<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}</Typography>
<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}</Typography>
{isStarred ? <YellowStar /> : <StarBorder />}
</>
),
tooltip: title,
onClick: () => toggleStarredEntity(entity),
};
},
@@ -88,8 +88,8 @@ describe('TemplateListPage', () => {
{ mountedRoutes: { '/': rootRouteRef } },
);
expect(getByRole('menuitem', { name: 'All' })).toBeInTheDocument();
expect(getByRole('menuitem', { name: 'Starred' })).toBeInTheDocument();
expect(getByRole('menuitem', { name: /All/ })).toBeInTheDocument();
expect(getByRole('menuitem', { name: /Starred/ })).toBeInTheDocument();
});
it('should render the category picker', async () => {