Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve accessibility of React components #1612

Merged
merged 47 commits into from
Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
1426193
Improve accessibility of existing Material-UI components
ivababukova Jun 30, 2023
8ea79e7
Fix sliders label text not visible
ivababukova Jun 30, 2023
82eea6b
Improve accessibility of TitleInfo
ivababukova Jun 30, 2023
4b3a9c5
Merge main branch in
ivababukova Jul 17, 2023
b4b5ba8
make buttons on poppermenu clickable
ivababukova Jul 17, 2023
f49e35e
same pnpm lock as main
ivababukova Jul 17, 2023
9e93184
update pnpm lock
ivababukova Jul 17, 2023
7ed7d04
add changeset
ivababukova Jul 17, 2023
56168a8
forgot to import css class during merge of main branch
ivababukova Jul 18, 2023
c6d3f11
Merge branch 'main' into ivababukova/accessiility
ivababukova Jul 20, 2023
2fdc513
Merge branch 'main' into ivababukova/accessiility
ivababukova Aug 14, 2023
34a756f
Remove key from TableRow because it doesn't improve accessibility
ivababukova Aug 14, 2023
8c848e3
Generate tooltip id with useId hook
ivababukova Aug 14, 2023
80821e2
Fix bug with react hook useId not always rendering and fix typos in i…
ivababukova Aug 14, 2023
a4e69d5
Use getAriaLabel function for Slider in HeatmapOptions
ivababukova Aug 14, 2023
b0c404d
Fix linting and actually use tooltipId value
ivababukova Aug 14, 2023
0dc2d0e
Change color name to US convention
ivababukova Aug 14, 2023
152a3bb
Better label for Slider in RasterChannelController
ivababukova Aug 14, 2023
802c0c2
Better label for Slider in VectorLayerController
ivababukova Aug 14, 2023
ee2c738
Make the styles definition more specific
ivababukova Aug 14, 2023
c6ee12f
fix typos
ivababukova Aug 14, 2023
ec1f243
one more typo
ivababukova Aug 14, 2023
7919472
Better label for IconButton
ivababukova Aug 14, 2023
4e4f4a5
Remove the use of && in SpatialOptions
ivababukova Aug 14, 2023
a39203d
Use id instead of key
ivababukova Aug 14, 2023
e5371cb
Use disabled
ivababukova Aug 14, 2023
e9a8ad0
Use disabled for ScatterplotOptions
ivababukova Aug 14, 2023
3f1ab5a
Make aria labeling o f Sliders consistent
ivababukova Aug 14, 2023
337f8cc
Address PR comments
ivababukova Aug 15, 2023
7473eb6
Add labels to TableCell and fix eslint settings
ivababukova Aug 16, 2023
4c172a2
Some small fixes that slipped previously
ivababukova Aug 16, 2023
0627aff
Use string template
ivababukova Aug 16, 2023
1054f81
Some small things
ivababukova Aug 16, 2023
a6bfe7c
Match the id of AccordionDetails with aria-controls id of AccordionSu…
ivababukova Aug 17, 2023
c644063
Make naming of aria labels consistent
ivababukova Aug 17, 2023
f1a2f75
Update packages/view-types/feature-list/src/FeatureListOptions.js
ivababukova Aug 21, 2023
d78a38f
forgotten fixes of labels
ivababukova Aug 21, 2023
08336a8
fix comment
ivababukova Aug 21, 2023
db5f915
fix
ivababukova Aug 22, 2023
2aa2009
more small fixes
ivababukova Aug 22, 2023
e645eb6
small fixes
ivababukova Aug 22, 2023
2f32d9c
remove unneccesary keys
ivababukova Aug 22, 2023
d5f61de
another small fix
ivababukova Aug 22, 2023
bf7a34f
Accessibility fixes (#1652)
keller-mark Aug 23, 2023
bfe3ec2
Fix ariaLabel
keller-mark Aug 23, 2023
4986854
Revert extra space
keller-mark Aug 23, 2023
555caa8
Revert extra space
keller-mark Aug 23, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .changeset/wicked-mugs-end.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
"@vitessce/scatterplot-embedding": patch
"@vitessce/scatterplot-gating": patch
"@vitessce/statistical-plots": patch
"@vitessce/layer-controller": patch
"@vitessce/feature-list": patch
"@vitessce/scatterplot": patch
"@vitessce/heatmap": patch
"@vitessce/spatial": patch
"@vitessce/tooltip": patch
"@vitessce/vit-s": patch
---

Improved accessibility of React components
4 changes: 4 additions & 0 deletions .eslintrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ rules:
import/no-duplicates: [2]
import/extensions: [0] # Using require-extensions instead.
import/prefer-default-export: [0]
jsx-a11y/label-has-associated-control: [ 2, {
"depth": 2,
}]
jsx-a11y/label-has-for: off # This rule is deprecated: https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/label-has-for.md
overrides:
- files: 'sites/docs/src/{pages,theme}/**'
rules:
Expand Down
31 changes: 19 additions & 12 deletions packages/view-types/feature-list/src/FeatureListOptions.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useId } from 'react';
import { OptionsContainer, OptionSelect, usePlotOptionsStyles } from '@vitessce/vit-s';
import { TableCell, TableRow, Checkbox } from '@material-ui/core';
import { FEATURELIST_SORT_OPTIONS, ALT_COLNAME } from './constants.js';
Expand All @@ -17,6 +17,8 @@ export default function FeatureListOptions(props) {
primaryColumnName,
} = props;

const featureListId = useId();

function handleFeatureListSortChange(event) {
setFeatureListSort(event.target.value);
}
Expand All @@ -35,16 +37,16 @@ export default function FeatureListOptions(props) {
<OptionsContainer>
{children}
<TableRow>
<TableCell className={classes.labelCell} htmlFor="feature-list-sort-option-select">
Sort Ordering
<TableCell className={classes.labelCell} variant="head" scope="row">
<label htmlFor={`feature-list-sort-option-${featureListId}`}>Sort Ordering</label>
</TableCell>
<TableCell>
<TableCell variant="body">
<OptionSelect
className={classes.select}
value={featureListSort}
onChange={handleFeatureListSortChange}
inputProps={{
id: 'feature-list-sort-option-select',
id: `feature-list-sort-option-${featureListId}`,
}}
>
{FEATURELIST_SORT_OPTIONS.map(option => (
Expand All @@ -56,17 +58,18 @@ export default function FeatureListOptions(props) {
{hasFeatureLabels ? (
<>
<TableRow>
<TableCell className={classes.labelCell} htmlFor="feature-list-sort-key-select">
Sort Key
<TableCell className={classes.labelCell} variant="head" scope="row">
<label htmlFor={`feature-list-sort-key-${featureListId}`}>Sort Key</label>
</TableCell>
<TableCell>
<TableCell variant="body">
<OptionSelect
className={classes.select}
disabled={featureListSort === 'original'}
value={featureListSortKey}
onChange={handleFeatureListSortKeyChange}
inputProps={{
id: 'feature-list-sort-key-select',
'aria-label': 'Select the feature list sort key',
id: `feature-list-sort-key-${featureListId}`,
}}
>
{hasFeatureLabels ? (
Expand All @@ -81,16 +84,20 @@ export default function FeatureListOptions(props) {
</TableCell>
</TableRow>
<TableRow>
<TableCell className={classes.labelCell}>
Show Alternate IDs
<TableCell className={classes.labelCell} variant="head" scope="row">
<label htmlFor={`feature-list-show-alternative-ids-${featureListId}`}>Show Alternate IDs</label>
</TableCell>
<TableCell className={classes.inputCell}>
<TableCell className={classes.inputCell} variant="body">
<Checkbox
className={classes.tableCheckbox}
checked={showFeatureTable}
onChange={handleShowTableChange}
name="feature-list-show-table"
color="default"
inputProps={{
'aria-label': 'Show or hide alternative feature ids',
id: `feature-list-show-alternative-ids-${featureListId}`,
}}
/>
</TableCell>
</TableRow>
Expand Down
51 changes: 33 additions & 18 deletions packages/view-types/heatmap/src/HeatmapOptions.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback } from 'react';
import React, { useCallback, useId } from 'react';
import { debounce } from 'lodash-es';
import { Checkbox, Slider, TableCell, TableRow } from '@material-ui/core';
import { usePlotOptionsStyles, OptionsContainer, OptionSelect } from '@vitessce/vit-s';
Expand All @@ -15,6 +15,7 @@ export default function HeatmapOptions(props) {
} = props;

const classes = usePlotOptionsStyles();
const heatmapOptionsId = useId();

function handleGeneExpressionColormapChange(event) {
setGeneExpressionColormap(event.target.value);
Expand All @@ -35,16 +36,17 @@ export default function HeatmapOptions(props) {
return (
<OptionsContainer>
<TableRow>
<TableCell className={classes.labelCell} htmlFor="gene-expression-colormap-select">
Gene Expression Colormap
<TableCell className={classes.labelCell} variant="head" scope="row">
<label htmlFor={`heatmap-gene-expression-colormap-${heatmapOptionsId}`}>Gene Expression Colormap</label>
</TableCell>
<TableCell className={classes.inputCell}>
<TableCell className={classes.inputCell} variant="body">
<OptionSelect
className={classes.select}
value={geneExpressionColormap}
onChange={handleGeneExpressionColormapChange}
inputProps={{
id: 'gene-expression-colormap-select',
'aria-label': 'Select gene expression colormap',
id: `heatmap-gene-expression-colormap-${heatmapOptionsId}`,
}}
>
{GLSL_COLORMAPS.map(cmap => (
Expand All @@ -54,34 +56,47 @@ export default function HeatmapOptions(props) {
</TableCell>
</TableRow>
<TableRow>
<TableCell className={classes.labelCell}>
Tooltips Visible
<TableCell className={classes.labelCell} variant="head" scope="row">
<label
htmlFor={`heatmap-gene-expression-colormap-tooltip-visibility-${heatmapOptionsId}`}
>
Tooltips Visible
</label>
</TableCell>
<TableCell className={classes.inputCell}>
<TableCell className={classes.inputCell} variant="body">
<Checkbox
className={classes.checkbox}
/**
* We have to use "checked" here, not "value".
* The checkbox state is not persisting with value.
* For reference, https://v4.mui.com/api/checkbox/
*/
/**
* We have to use "checked" here, not "value".
* The checkbox state is not persisting with value.
* For reference, https://v4.mui.com/api/checkbox/
*/
checked={tooltipsVisible}
onChange={handleTooltipsVisibilityChange}
name="gene-expression-colormap-option-toltip-visibility"
name="heatmap-gene-expression-colormap-tooltip-visibility"
color="default"
inputProps={{
'aria-label': 'Show or hide tooltips',
id: `heatmap-gene-expression-colormap-tooltip-visibility-${heatmapOptionsId}`,
}}
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell className={classes.labelCell}>
Gene Expression Colormap Range
<TableCell className={classes.labelCell} variant="head" scope="row">
<label
htmlFor={`heatmap-gene-expression-colormap-range-${heatmapOptionsId}`}
>
Gene Expression Colormap Range
</label>
</TableCell>
<TableCell className={classes.inputCell}>
<TableCell className={classes.inputCell} variant="body">
<Slider
classes={{ root: classes.slider, valueLabel: classes.sliderValueLabel }}
value={geneExpressionColormapRange}
onChange={handleColormapRangeChangeDebounced}
aria-labelledby="gene-expression-colormap-range-slider"
getAriaLabel={index => (index === 0 ? 'Low value colormap range slider' : 'High value colormap range slider')}
id={`heatmap-gene-expression-colormap-range-${heatmapOptionsId}`}
valueLabelDisplay="auto"
step={0.005}
min={0.0}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@ function BitmaskChannelController({
/>
</Grid>
<Grid item xs={1}>
<IconButton onClick={handleChannelRemove} style={{ padding: '6px 6px 6px 0px' }}>
<IconButton
onClick={handleChannelRemove}
style={{ padding: '6px 6px 6px 0px' }}
aria-label="Remove channel"
>
<RemoveCircleIcon />
</IconButton>
</Grid>
Expand Down
17 changes: 14 additions & 3 deletions packages/view-types/layer-controller/src/ChannelOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,25 @@ function ChannelOptions({ handlePropertyChange, handleChannelRemove, handleIQRUp
setOpen={setOpen}
buttonIcon={<MoreVertIcon fontSize="small" />}
buttonClassName={classes.menuButton}
aria-label="Open channel options menu"
>
<MenuItem dense disableGutters onClick={handleRemove}>
<MenuItem dense disableGutters onClick={handleRemove} aria-label="Click to remove channel">
<MuiSpan>Remove</MuiSpan>
</MenuItem>
<MenuItem dense disableGutters onClick={handleIQRUpdate}>
<MenuItem
dense
disableGutters
onClick={handleIQRUpdate}
aria-label="Click to use IQR for channel"
>
<MuiSpan>Use IQR</MuiSpan>
</MenuItem>
<MenuItem dense disableGutters className={classes.colors}>
<MenuItem
dense
disableGutters
className={classes.colors}
aria-label="Click to select color for channel"
>
<ColorPalette handleChange={handleColorSelect} />
</MenuItem>
</PopperMenu>
Expand Down
3 changes: 2 additions & 1 deletion packages/view-types/layer-controller/src/ColorPalette.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ const useStyles = makeStyles(theme => ({
const ColorPalette = ({ handleChange }) => {
const classes = useStyles();
return (
<div className={classes.paletteContainer} aria-label="color-swatch">
<div className={classes.paletteContainer} aria-label="Color swatch">
{VIEWER_PALETTE.map(color => (
<IconButton
className={classes.button}
key={color}
onClick={() => handleChange(color)}
aria-label={`Change color to ${color}`}
>
<LensIcon
fontSize="small"
Expand Down
1 change: 1 addition & 0 deletions packages/view-types/layer-controller/src/ImageAddButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ function ImageAddButton({ imageOptions, handleImageAdd }) {
buttonIcon={<ImageAddIcon />}
buttonClassName={classes.addButton}
placement="bottom-start"
aria-label="Add image menu"
>
{imageOptions.map((imgData, i) => (
<MenuItem dense key={imgData.name} onClick={() => handleAdd(i)}>
Expand Down
17 changes: 13 additions & 4 deletions packages/view-types/layer-controller/src/LayerController.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useRef, useEffect } from 'react';
import React, { useState, useRef, useEffect, useId } from 'react';
import { viv } from '@vitessce/gl';
import {
GLOBAL_LABELS, getSourceFromLoader, isRgb,
Expand Down Expand Up @@ -127,6 +127,7 @@ export default function LayerController(props) {
return undefined;
}, [channels]);

const layerControlsId = useId();
const firstSelection = channels[0]?.selection || {};

const { data, channels: channelOptions } = loader;
Expand Down Expand Up @@ -476,6 +477,7 @@ export default function LayerController(props) {
}
TransitionProps={{ enter: false }}
expanded={!disabled && isExpanded}
id={`layer-controls-accordion-${layerControlsId}`}
>
<AccordionSummary
classes={{
Expand All @@ -486,10 +488,12 @@ export default function LayerController(props) {
}}
expandIcon={<ExpandMoreIcon role="presentation" />}
aria-controls={`layer-${name}-controls`}
aria-expanded={isExpanded}
>
<Grid container direction="column" m={1} justifyContent="center">
<Grid item classes={{ item: overflowEllipsisGridClasses.item }}>
<Button
aria-label="Toggle layer visibility"
onClick={(e) => {
if (!disabled) {
// Needed to prevent affecting the expansion panel from changing
Expand Down Expand Up @@ -530,7 +534,7 @@ export default function LayerController(props) {
value={opacity}
onChange={(e, v) => setOpacity(v)}
valueLabelDisplay="auto"
getAriaLabel={() => 'opacity slider'}
aria-label={`Adjust opacity for layer ${name}`}
min={0}
max={1}
step={0.01}
Expand All @@ -541,17 +545,21 @@ export default function LayerController(props) {
)}
</Grid>
</AccordionSummary>
<AccordionDetails classes={{ root: accordionClasses.accordionDetailsRoot }}>
<AccordionDetails
classes={{ root: accordionClasses.accordionDetailsRoot }}
id={`layer-${name}-controls`}
>
{useVolumeTabs ? (
<>
<Tabs
value={tab}
onChange={handleTabChange}
aria-label="simple tabs example"
aria-label="Change the layer tab type"
style={{ height: '24px', minHeight: '24px' }}
>
<Tab
label="Channels"
aria-label="Channels tab"
style={{
fontSize: '.75rem',
bottom: 12,
Expand All @@ -562,6 +570,7 @@ export default function LayerController(props) {
/>
<Tab
label="Volume"
aria-label="Volume tab"
style={{
fontSize: '.75rem',
bottom: 12,
Expand Down
Loading