Browse Source

Make various buttons across the UI accessible for screen reader users

There are 2 types of buttons:

1. Buttons with purely iconic labels
2. Buttons that have a label that displays on hover

The tooltip component from rsuite doesn't implement the aria tooltip
pattern,
hence I added labels to the triggering control instead as a workaround
master
Dickson 3 years ago
committed by Jeff
parent
commit
3a35d29c25
  1. 6
      src/components/card/Card.tsx
  2. 16
      src/components/layout/Layout.tsx
  3. 2
      src/components/library/ArtistView.tsx
  4. 32
      src/components/player/PlayerBar.tsx
  5. 2
      src/components/scrollingmenu/ScrollingMenu.tsx
  6. 1
      src/components/search/SearchBar.tsx
  7. 1
      src/components/selectionbar/SelectionButtons.tsx
  8. 48
      src/components/shared/ToolbarButtons.tsx

6
src/components/card/Card.tsx

@ -306,6 +306,7 @@ const Card = ({
<CustomTooltip text={t('Add to queue (later)')} delay={1000}> <CustomTooltip text={t('Add to queue (later)')} delay={1000}>
<AppendOverlayButton <AppendOverlayButton
aria-label={t('Add to queue (later)')}
onClick={() => handlePlayAppend('later')} onClick={() => handlePlayAppend('later')}
size={size <= 160 ? 'xs' : 'sm'} size={size <= 160 ? 'xs' : 'sm'}
icon={<Icon icon="plus" />} icon={<Icon icon="plus" />}
@ -314,6 +315,7 @@ const Card = ({
<CustomTooltip text={t('Add to queue (next)')} delay={1000}> <CustomTooltip text={t('Add to queue (next)')} delay={1000}>
<AppendNextOverlayButton <AppendNextOverlayButton
aria-label={t('Add to queue (next)')}
onClick={() => handlePlayAppend('next')} onClick={() => handlePlayAppend('next')}
size={size <= 160 ? 'xs' : 'sm'} size={size <= 160 ? 'xs' : 'sm'}
icon={<Icon icon="plus-circle" />} icon={<Icon icon="plus-circle" />}
@ -323,6 +325,9 @@ const Card = ({
{playClick.type !== 'playlist' && ( {playClick.type !== 'playlist' && (
<CustomTooltip text={t('Toggle favorite')} delay={1000}> <CustomTooltip text={t('Toggle favorite')} delay={1000}>
<FavoriteOverlayButton <FavoriteOverlayButton
aria-label={
rest.details.starred ? t('Remove from favorites') : t('Add to favorites')
}
onClick={() => handleFavorite(rest.details)} onClick={() => handleFavorite(rest.details)}
size={size <= 160 ? 'xs' : 'sm'} size={size <= 160 ? 'xs' : 'sm'}
icon={<Icon icon={rest.details.starred ? 'heart' : 'heart-o'} />} icon={<Icon icon={rest.details.starred ? 'heart' : 'heart-o'} />}
@ -332,6 +337,7 @@ const Card = ({
{!rest.isModal && !noModalButton && ( {!rest.isModal && !noModalButton && (
<CustomTooltip text={t('View in modal')} delay={1000}> <CustomTooltip text={t('View in modal')} delay={1000}>
<ModalViewOverlayButton <ModalViewOverlayButton
aria-label={t('View in modal')}
size={size <= 160 ? 'xs' : 'sm'} size={size <= 160 ? 'xs' : 'sm'}
icon={<Icon icon="external-link" />} icon={<Icon icon="external-link" />}
onClick={handleOpenModal} onClick={handleOpenModal}

16
src/components/layout/Layout.tsx

@ -145,10 +145,20 @@ const Layout = ({ footer, children, disableSidebar, font }: any) => {
> >
<FlexboxGrid.Item> <FlexboxGrid.Item>
<ButtonToolbar> <ButtonToolbar>
<StyledButton appearance="subtle" size="sm" onClick={() => history.goBack()}> <StyledButton
aria-label="back"
appearance="subtle"
size="sm"
onClick={() => history.goBack()}
>
<Icon icon="arrow-left-line" /> <Icon icon="arrow-left-line" />
</StyledButton> </StyledButton>
<StyledButton appearance="subtle" size="sm" onClick={() => history.goForward()}> <StyledButton
aria-label="next"
appearance="subtle"
size="sm"
onClick={() => history.goForward()}
>
<Icon icon="arrow-right-line" /> <Icon icon="arrow-right-line" />
</StyledButton> </StyledButton>
</ButtonToolbar> </ButtonToolbar>
@ -200,7 +210,7 @@ const Layout = ({ footer, children, disableSidebar, font }: any) => {
placement="bottomEnd" placement="bottomEnd"
preventOverflow preventOverflow
> >
<StyledButton appearance="subtle"> <StyledButton aria-label="settings" appearance="subtle">
<Icon icon="cog" /> <Icon icon="cog" />
</StyledButton> </StyledButton>
</Whisper> </Whisper>

2
src/components/library/ArtistView.tsx

@ -738,7 +738,7 @@ const ArtistView = ({ ...rest }: any) => {
} }
> >
<CustomTooltip text={t('Info')}> <CustomTooltip text={t('Info')}>
<StyledButton appearance="subtle" size="lg"> <StyledButton aria-label={t('Info')} appearance="subtle" size="lg">
<Icon icon="info-circle" /> <Icon icon="info-circle" />
</StyledButton> </StyledButton>
</CustomTooltip> </CustomTooltip>

32
src/components/player/PlayerBar.tsx

@ -381,6 +381,7 @@ const PlayerBar = () => {
style={{ cursor: 'pointer' }} style={{ cursor: 'pointer' }}
/> />
<StyledButton <StyledButton
aria-label="show cover art"
size="xs" size="xs"
onClick={() => { onClick={() => {
dispatch(setSidebar({ coverArt: true })); dispatch(setSidebar({ coverArt: true }));
@ -531,6 +532,8 @@ const PlayerBar = () => {
{/* Seek Backward Button */} {/* Seek Backward Button */}
<CustomTooltip text={t('Seek backward')} delay={1000}> <CustomTooltip text={t('Seek backward')} delay={1000}>
<PlayerControlIcon <PlayerControlIcon
aria-label={t('Seek backward')}
role="button"
tabIndex={0} tabIndex={0}
icon="backward" icon="backward"
size="lg" size="lg"
@ -546,6 +549,8 @@ const PlayerBar = () => {
{/* Previous Song Button */} {/* Previous Song Button */}
<CustomTooltip text={t('Previous Track')} delay={1000}> <CustomTooltip text={t('Previous Track')} delay={1000}>
<PlayerControlIcon <PlayerControlIcon
aria-label={t('Previous Track')}
role="button"
tabIndex={0} tabIndex={0}
icon="step-backward" icon="step-backward"
size="lg" size="lg"
@ -561,6 +566,9 @@ const PlayerBar = () => {
{/* Play/Pause Button */} {/* Play/Pause Button */}
<CustomTooltip text={t('Play/Pause')} delay={1000}> <CustomTooltip text={t('Play/Pause')} delay={1000}>
<PlayerControlIcon <PlayerControlIcon
aria-label={t('Play')}
aria-pressed={player.status === 'PLAYING'}
role="button"
tabIndex={0} tabIndex={0}
icon={player.status === 'PLAYING' ? 'pause-circle' : 'play-circle'} icon={player.status === 'PLAYING' ? 'pause-circle' : 'play-circle'}
size="3x" size="3x"
@ -575,6 +583,8 @@ const PlayerBar = () => {
{/* Next Song Button */} {/* Next Song Button */}
<CustomTooltip text={t('Next Track')} delay={1000}> <CustomTooltip text={t('Next Track')} delay={1000}>
<PlayerControlIcon <PlayerControlIcon
aria-label={t('Next Track')}
role="button"
tabIndex={0} tabIndex={0}
icon="step-forward" icon="step-forward"
size="lg" size="lg"
@ -590,6 +600,8 @@ const PlayerBar = () => {
{/* Seek Forward Button */} {/* Seek Forward Button */}
<CustomTooltip text={t('Seek forward')} delay={1000}> <CustomTooltip text={t('Seek forward')} delay={1000}>
<PlayerControlIcon <PlayerControlIcon
aria-label={t('Seek forward')}
role="button"
tabIndex={0} tabIndex={0}
icon="forward" icon="forward"
size="lg" size="lg"
@ -700,6 +712,9 @@ const PlayerBar = () => {
{/* Favorite Button */} {/* Favorite Button */}
<CustomTooltip text={t('Favorite')}> <CustomTooltip text={t('Favorite')}>
<PlayerControlIcon <PlayerControlIcon
aria-label={t('Favorite')}
aria-pressed={!!playQueue[currentEntryList][playQueue.currentIndex]?.starred}
role="button"
tabIndex={0} tabIndex={0}
icon={ icon={
playQueue[currentEntryList][playQueue.currentIndex]?.starred playQueue[currentEntryList][playQueue.currentIndex]?.starred
@ -733,6 +748,17 @@ const PlayerBar = () => {
} }
> >
<PlayerControlIcon <PlayerControlIcon
aria-label={
playQueue.repeat === 'all'
? t('Repeat all')
: playQueue.repeat === 'one'
? t('Repeat one')
: t('Repeat')
}
aria-pressed={
playQueue.repeat === 'all' || playQueue.repeat === 'one' ? 'true' : 'false'
}
role="button"
tabIndex={0} tabIndex={0}
icon="refresh" icon="refresh"
size="lg" size="lg"
@ -752,6 +778,9 @@ const PlayerBar = () => {
{/* Shuffle Button */} {/* Shuffle Button */}
<CustomTooltip text={t('Shuffle')}> <CustomTooltip text={t('Shuffle')}>
<PlayerControlIcon <PlayerControlIcon
aria-label={t('Shuffle')}
aria-pressed={playQueue.shuffle ? 'true' : 'false'}
role="button"
tabIndex={0} tabIndex={0}
icon="random" icon="random"
size="lg" size="lg"
@ -769,6 +798,9 @@ const PlayerBar = () => {
{/* Display Queue Button */} {/* Display Queue Button */}
<CustomTooltip text={t('Mini')}> <CustomTooltip text={t('Mini')}>
<PlayerControlIcon <PlayerControlIcon
aria-label="show play queue"
aria-pressed={playQueue.displayQueue ? 'true' : 'false'}
role="button"
tabIndex={0} tabIndex={0}
icon="tasks" icon="tasks"
size="lg" size="lg"

2
src/components/scrollingmenu/ScrollingMenu.tsx

@ -60,6 +60,7 @@ const ScrollingMenu = ({
<ButtonToolbar> <ButtonToolbar>
<ButtonGroup> <ButtonGroup>
<StyledButton <StyledButton
aria-label="scroll left"
size="sm" size="sm"
appearance="subtle" appearance="subtle"
onClick={() => { onClick={() => {
@ -75,6 +76,7 @@ const ScrollingMenu = ({
<Icon icon="arrow-left" /> <Icon icon="arrow-left" />
</StyledButton> </StyledButton>
<StyledButton <StyledButton
aria-label="scroll right"
size="sm" size="sm"
appearance="subtle" appearance="subtle"
onClick={() => { onClick={() => {

1
src/components/search/SearchBar.tsx

@ -709,6 +709,7 @@ const SearchBar = () => {
> >
<span style={{ display: 'inline-block' }}> <span style={{ display: 'inline-block' }}>
<StyledButton <StyledButton
aria-label="search"
onClick={() => { onClick={() => {
setOpenSearch(true); setOpenSearch(true);
setTimeout(() => { setTimeout(() => {

1
src/components/selectionbar/SelectionButtons.tsx

@ -11,6 +11,7 @@ const CustomIconButton = ({ tooltipText, icon, handleClick, ...rest }: any) => {
{tooltipText ? ( {tooltipText ? (
<CustomTooltip text={tooltipText}> <CustomTooltip text={tooltipText}>
<IconButton <IconButton
aria-label={tooltipText}
size="xs" size="xs"
{...rest} {...rest}
icon={<Icon icon={icon} {...rest} />} icon={<Icon icon={icon} {...rest} />}

48
src/components/shared/ToolbarButtons.tsx

@ -7,7 +7,7 @@ import { StyledButton } from './styled';
export const PlayButton = ({ text, ...rest }: any) => { export const PlayButton = ({ text, ...rest }: any) => {
return ( return (
<CustomTooltip text={text || i18n.t('Play')}> <CustomTooltip text={text || i18n.t('Play')}>
<StyledButton {...rest} tabIndex={0}> <StyledButton aria-label={text || i18n.t('Play')} {...rest} tabIndex={0}>
<Icon icon="play" /> <Icon icon="play" />
</StyledButton> </StyledButton>
</CustomTooltip> </CustomTooltip>
@ -17,7 +17,7 @@ export const PlayButton = ({ text, ...rest }: any) => {
export const PlayAppendButton = ({ text, ...rest }: any) => { export const PlayAppendButton = ({ text, ...rest }: any) => {
return ( return (
<CustomTooltip text={text || i18n.t('Add to queue (later)')}> <CustomTooltip text={text || i18n.t('Add to queue (later)')}>
<StyledButton {...rest} tabIndex={0}> <StyledButton aria-label={text || i18n.t('Add to queue (later)')} {...rest} tabIndex={0}>
<Icon icon="plus" /> <Icon icon="plus" />
</StyledButton> </StyledButton>
</CustomTooltip> </CustomTooltip>
@ -27,7 +27,7 @@ export const PlayAppendButton = ({ text, ...rest }: any) => {
export const PlayAppendNextButton = ({ text, ...rest }: any) => { export const PlayAppendNextButton = ({ text, ...rest }: any) => {
return ( return (
<CustomTooltip text={text || i18n.t('Add to queue (next)')}> <CustomTooltip text={text || i18n.t('Add to queue (next)')}>
<StyledButton {...rest} tabIndex={0}> <StyledButton aria-label={text || i18n.t('Add to queue (next)')} {...rest} tabIndex={0}>
<Icon icon="plus-circle" /> <Icon icon="plus-circle" />
</StyledButton> </StyledButton>
</CustomTooltip> </CustomTooltip>
@ -37,7 +37,7 @@ export const PlayAppendNextButton = ({ text, ...rest }: any) => {
export const PlayShuffleAppendButton = ({ ...rest }) => { export const PlayShuffleAppendButton = ({ ...rest }) => {
return ( return (
<CustomTooltip text={i18n.t('Add shuffled to queue')} onClick={rest.onClick}> <CustomTooltip text={i18n.t('Add shuffled to queue')} onClick={rest.onClick}>
<StyledButton {...rest} tabIndex={0}> <StyledButton aria-label={i18n.t('Add shuffled to queue')} {...rest} tabIndex={0}>
<Icon icon="plus-square" /> <Icon icon="plus-square" />
</StyledButton> </StyledButton>
</CustomTooltip> </CustomTooltip>
@ -47,7 +47,7 @@ export const PlayShuffleAppendButton = ({ ...rest }) => {
export const SaveButton = ({ text, ...rest }: any) => { export const SaveButton = ({ text, ...rest }: any) => {
return ( return (
<CustomTooltip text={text || i18n.t('Save')}> <CustomTooltip text={text || i18n.t('Save')}>
<StyledButton {...rest} tabIndex={0}> <StyledButton aria-label={text || i18n.t('Save')} {...rest} tabIndex={0}>
<Icon icon="save" /> <Icon icon="save" />
</StyledButton> </StyledButton>
</CustomTooltip> </CustomTooltip>
@ -57,7 +57,7 @@ export const SaveButton = ({ text, ...rest }: any) => {
export const EditButton = ({ ...rest }) => { export const EditButton = ({ ...rest }) => {
return ( return (
<CustomTooltip text={i18n.t('Edit')}> <CustomTooltip text={i18n.t('Edit')}>
<StyledButton {...rest} tabIndex={0}> <StyledButton aria-label={i18n.t('Edit')} {...rest} tabIndex={0}>
<Icon icon="edit2" /> <Icon icon="edit2" />
</StyledButton> </StyledButton>
</CustomTooltip> </CustomTooltip>
@ -67,7 +67,7 @@ export const EditButton = ({ ...rest }) => {
export const UndoButton = ({ ...rest }) => { export const UndoButton = ({ ...rest }) => {
return ( return (
<CustomTooltip text={i18n.t('Reset')}> <CustomTooltip text={i18n.t('Reset')}>
<StyledButton {...rest} tabIndex={0}> <StyledButton aria-label={i18n.t('Reset')} {...rest} tabIndex={0}>
<Icon icon="undo" /> <Icon icon="undo" />
</StyledButton> </StyledButton>
</CustomTooltip> </CustomTooltip>
@ -77,7 +77,7 @@ export const UndoButton = ({ ...rest }) => {
export const DeleteButton = ({ ...rest }) => { export const DeleteButton = ({ ...rest }) => {
return ( return (
<CustomTooltip text={i18n.t('Delete')}> <CustomTooltip text={i18n.t('Delete')}>
<StyledButton {...rest} tabIndex={0}> <StyledButton aria-label={i18n.t('Delete')} {...rest} tabIndex={0}>
<Icon icon="trash" /> <Icon icon="trash" />
</StyledButton> </StyledButton>
</CustomTooltip> </CustomTooltip>
@ -87,7 +87,11 @@ export const DeleteButton = ({ ...rest }) => {
export const FavoriteButton = ({ isFavorite, ...rest }: any) => { export const FavoriteButton = ({ isFavorite, ...rest }: any) => {
return ( return (
<CustomTooltip text={i18n.t('Toggle favorite')}> <CustomTooltip text={i18n.t('Toggle favorite')}>
<StyledButton tabIndex={0} {...rest}> <StyledButton
aria-label={isFavorite ? i18n.t('Remove from favorites') : i18n.t('Add to favorites')}
tabIndex={0}
{...rest}
>
<Icon icon={isFavorite ? 'heart' : 'heart-o'} /> <Icon icon={isFavorite ? 'heart' : 'heart-o'} />
</StyledButton> </StyledButton>
</CustomTooltip> </CustomTooltip>
@ -101,7 +105,15 @@ export const DownloadButton = ({ downloadSize, ...rest }: any) => {
downloadSize ? i18n.t('Download ({{downloadSize}})', { downloadSize }) : i18n.t('Download') downloadSize ? i18n.t('Download ({{downloadSize}})', { downloadSize }) : i18n.t('Download')
} }
> >
<StyledButton {...rest} tabIndex={0}> <StyledButton
aria-label={
downloadSize
? i18n.t('Download ({{downloadSize}})', { downloadSize })
: i18n.t('Download')
}
{...rest}
tabIndex={0}
>
<Icon icon="download" /> <Icon icon="download" />
</StyledButton> </StyledButton>
</CustomTooltip> </CustomTooltip>
@ -111,7 +123,7 @@ export const DownloadButton = ({ downloadSize, ...rest }: any) => {
export const ShuffleButton = ({ ...rest }) => { export const ShuffleButton = ({ ...rest }) => {
return ( return (
<CustomTooltip text={i18n.t('Shuffle queue')}> <CustomTooltip text={i18n.t('Shuffle queue')}>
<StyledButton tabIndex={0} {...rest}> <StyledButton aria-label={i18n.t('Shuffle queue')} tabIndex={0} {...rest}>
<Icon icon="random" /> <Icon icon="random" />
</StyledButton> </StyledButton>
</CustomTooltip> </CustomTooltip>
@ -121,7 +133,7 @@ export const ShuffleButton = ({ ...rest }) => {
export const ClearQueueButton = ({ ...rest }) => { export const ClearQueueButton = ({ ...rest }) => {
return ( return (
<CustomTooltip text={i18n.t('Clear queue')}> <CustomTooltip text={i18n.t('Clear queue')}>
<StyledButton tabIndex={0} {...rest}> <StyledButton aria-label={i18n.t('Clear queue')} tabIndex={0} {...rest}>
<Icon icon="trash2" /> <Icon icon="trash2" />
</StyledButton> </StyledButton>
</CustomTooltip> </CustomTooltip>
@ -158,7 +170,7 @@ export const FilterButton = ({ ...rest }) => {
export const AutoPlaylistButton = ({ noText, ...rest }: any) => { export const AutoPlaylistButton = ({ noText, ...rest }: any) => {
return ( return (
<CustomTooltip text={i18n.t('Auto playlist')}> <CustomTooltip text={i18n.t('Auto playlist')}>
<StyledButton tabIndex={0} {...rest}> <StyledButton aria-label={i18n.t('Auto playlist')} tabIndex={0} {...rest}>
<Icon icon="plus-square" style={{ marginRight: noText ? '0px' : '10px' }} /> <Icon icon="plus-square" style={{ marginRight: noText ? '0px' : '10px' }} />
{!noText && i18n.t('Auto playlist')} {!noText && i18n.t('Auto playlist')}
</StyledButton> </StyledButton>
@ -169,7 +181,7 @@ export const AutoPlaylistButton = ({ noText, ...rest }: any) => {
export const MoveUpButton = ({ ...rest }) => { export const MoveUpButton = ({ ...rest }) => {
return ( return (
<CustomTooltip text={i18n.t('Move selected up')}> <CustomTooltip text={i18n.t('Move selected up')}>
<StyledButton {...rest} tabIndex={0}> <StyledButton aria-label={i18n.t('Move selected up')} {...rest} tabIndex={0}>
<Icon icon="angle-up" /> <Icon icon="angle-up" />
</StyledButton> </StyledButton>
</CustomTooltip> </CustomTooltip>
@ -179,7 +191,7 @@ export const MoveUpButton = ({ ...rest }) => {
export const MoveDownButton = ({ ...rest }) => { export const MoveDownButton = ({ ...rest }) => {
return ( return (
<CustomTooltip text={i18n.t('Move selected down')}> <CustomTooltip text={i18n.t('Move selected down')}>
<StyledButton {...rest} tabIndex={0}> <StyledButton aria-label={i18n.t('Move selected down')} {...rest} tabIndex={0}>
<Icon icon="angle-down" /> <Icon icon="angle-down" />
</StyledButton> </StyledButton>
</CustomTooltip> </CustomTooltip>
@ -189,7 +201,7 @@ export const MoveDownButton = ({ ...rest }) => {
export const MoveTopButton = ({ ...rest }) => { export const MoveTopButton = ({ ...rest }) => {
return ( return (
<CustomTooltip text={i18n.t('Move selected to top')}> <CustomTooltip text={i18n.t('Move selected to top')}>
<StyledButton {...rest} tabIndex={0}> <StyledButton aria-label={i18n.t('Move selected to top')} {...rest} tabIndex={0}>
<Icon icon="arrow-up2" /> <Icon icon="arrow-up2" />
</StyledButton> </StyledButton>
</CustomTooltip> </CustomTooltip>
@ -199,7 +211,7 @@ export const MoveTopButton = ({ ...rest }) => {
export const MoveBottomButton = ({ ...rest }) => { export const MoveBottomButton = ({ ...rest }) => {
return ( return (
<CustomTooltip text={i18n.t('Move selected to bottom')}> <CustomTooltip text={i18n.t('Move selected to bottom')}>
<StyledButton {...rest} tabIndex={0}> <StyledButton aria-label={i18n.t('Move selected to bottom')} {...rest} tabIndex={0}>
<Icon icon="arrow-down2" /> <Icon icon="arrow-down2" />
</StyledButton> </StyledButton>
</CustomTooltip> </CustomTooltip>
@ -209,7 +221,7 @@ export const MoveBottomButton = ({ ...rest }) => {
export const RemoveSelectedButton = ({ ...rest }) => { export const RemoveSelectedButton = ({ ...rest }) => {
return ( return (
<CustomTooltip text={i18n.t('Remove selected')}> <CustomTooltip text={i18n.t('Remove selected')}>
<StyledButton {...rest} tabIndex={0}> <StyledButton aria-label={i18n.t('Remove selected')} {...rest} tabIndex={0}>
<Icon icon="close" /> <Icon icon="close" />
</StyledButton> </StyledButton>
</CustomTooltip> </CustomTooltip>

Loading…
Cancel
Save