New: Screen reader Accessibility Improvements

This commit is contained in:
matalvernaz 2026-04-20 09:03:34 -07:00 committed by GitHub
parent 33a5ccd794
commit ffaaf5270d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
51 changed files with 199 additions and 37 deletions

View file

@ -137,10 +137,15 @@ function BlocklistRow({
if (name === 'actions') {
return (
<TableRowCell key={name} className={styles.actions}>
<IconButton name={icons.INFO} onPress={handleDetailsPress} />
<IconButton
name={icons.INFO}
aria-label={translate('Details')}
onPress={handleDetailsPress}
/>
<IconButton
title={translate('RemoveFromBlocklist')}
aria-label={translate('RemoveFromBlocklist')}
name={icons.REMOVE}
kind={kinds.DANGER}
isSpinning={isRemoving}

View file

@ -20,6 +20,7 @@ import { useSingleSeries } from 'Series/useSeries';
import CustomFormat from 'typings/CustomFormat';
import { HistoryData, HistoryEventType } from 'typings/History';
import formatCustomFormatScore from 'Utilities/Number/formatCustomFormatScore';
import translate from 'Utilities/String/translate';
import HistoryDetailsModal from './Details/HistoryDetailsModal';
import HistoryEventTypeCell from './HistoryEventTypeCell';
import styles from './HistoryRow.css';
@ -221,7 +222,11 @@ function HistoryRow(props: HistoryRowProps) {
if (name === 'details') {
return (
<TableRowCell key={name} className={styles.details}>
<IconButton name={icons.INFO} onPress={handleDetailsPress} />
<IconButton
name={icons.INFO}
aria-label={translate('Details')}
onPress={handleDetailsPress}
/>
</TableRowCell>
);
}

View file

@ -46,7 +46,7 @@ export function useQueueItemForEpisode(episodeId: number) {
const queue = useContext(QueueDetailsContext);
return useMemo(() => {
return queue?.find((item) => item.episodeIds.includes(episodeId));
return queue?.find((item) => item.episodeIds?.includes(episodeId));
}, [episodeId, queue]);
}

View file

@ -369,6 +369,7 @@ function QueueRow(props: QueueRowProps) {
{showInteractiveImport ? (
<IconButton
name={icons.INTERACTIVE}
aria-label={translate('InteractiveSearch')}
onPress={handleInteractiveImportPress}
/>
) : null}
@ -377,6 +378,7 @@ function QueueRow(props: QueueRowProps) {
<SpinnerIconButton
name={icons.DOWNLOAD}
kind={grabError ? kinds.DANGER : kinds.DEFAULT}
aria-label={translate('Grab')}
isSpinning={isGrabbing}
onPress={handleGrabPress}
/>

View file

@ -65,7 +65,13 @@ function AddNewSeriesSearchResult({ series }: AddNewSeriesSearchResultProps) {
return (
<div className={styles.searchResult}>
<Link className={styles.underlay} {...linkProps} />
<Link
className={styles.underlay}
aria-label={
isExistingSeries ? title : translate('AddSeriesWithTitle', { title })
}
{...linkProps}
/>
<div className={styles.overlay}>
{isSmallScreen ? null : (
@ -113,12 +119,14 @@ function AddNewSeriesSearchResult({ series }: AddNewSeriesSearchResultProps) {
<Link
className={styles.tvdbLink}
to={`https://www.thetvdb.com/?tab=series&id=${tvdbId}`}
aria-label={translate('ViewSeriesOnTvdb', { title })}
onPress={handleTvdbLinkPress}
>
<Icon
className={styles.tvdbLinkIcon}
name={icons.EXTERNAL_LINK}
size={28}
aria-hidden={true}
/>
</Link>
</div>

View file

@ -10,6 +10,7 @@ import {
import { DateFilterValue, FilterType } from 'Helpers/Props/filterTypes';
import { InputChanged } from 'typings/inputs';
import sortByProp from 'Utilities/Array/sortByProp';
import translate from 'Utilities/String/translate';
import BoolFilterBuilderRowValue from './BoolFilterBuilderRowValue';
import DateFilterBuilderRowValue from './DateFilterBuilderRowValue';
import DefaultFilterBuilderRowValue from './DefaultFilterBuilderRowValue';
@ -300,11 +301,16 @@ function FilterBuilderRow<T>({
<div className={styles.actionsContainer}>
<IconButton
name={icons.SUBTRACT}
aria-label={translate('Remove')}
isDisabled={filterCount === 1}
onPress={handleRemovePress}
/>
<IconButton name={icons.ADD} onPress={handleAddPress} />
<IconButton
name={icons.ADD}
aria-label={translate('Add')}
onPress={handleAddPress}
/>
</div>
</div>
);

View file

@ -59,7 +59,11 @@ function CustomFilter({
<div className={styles.label}>{label}</div>
<div className={styles.actions}>
<IconButton name={icons.EDIT} onPress={handleEditPress} />
<IconButton
name={icons.EDIT}
aria-label={translate('Edit')}
onPress={handleEditPress}
/>
<SpinnerIconButton
title={translate('RemoveFilter')}

View file

@ -1,6 +1,7 @@
import React, { useCallback } from 'react';
import IconButton from 'Components/Link/IconButton';
import { icons } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import TextInput from './TextInput';
import styles from './KeyValueListInputItem.css';
@ -77,6 +78,7 @@ function KeyValueListInputItem({
{isNew ? null : (
<IconButton
name={icons.REMOVE}
aria-label={translate('Remove')}
tabIndex={-1}
onPress={handleRemovePress}
/>

View file

@ -4,6 +4,7 @@ import IconButton from 'Components/Link/IconButton';
import Link from 'Components/Link/Link';
import MiddleTruncate from 'Components/MiddleTruncate';
import { icons } from 'Helpers/Props';
import translate from 'Utilities/String/translate';
import { TagBase } from './TagInput';
import styles from './TagInputTag.css';
@ -66,6 +67,7 @@ function TagInputTag<T extends TagBase>({
<IconButton
className={styles.editButton}
name={icons.EDIT}
aria-label={translate('Edit')}
size={9}
onPress={handleEdit}
/>

View file

@ -1,7 +1,6 @@
import classNames from 'classnames';
import React from 'react';
import Icon, { IconProps } from 'Components/Icon';
import translate from 'Utilities/String/translate';
import Link, { LinkProps } from './Link';
import styles from './IconButton.css';
@ -26,7 +25,6 @@ export default function IconButton({
className,
otherProps.isDisabled && styles.isDisabled
)}
aria-label={translate('TableOptionsButton')}
{...otherProps}
>
<Icon
@ -35,6 +33,7 @@ export default function IconButton({
kind={kind}
size={size}
isSpinning={isSpinning}
aria-hidden={true}
/>
</Link>
);

View file

@ -15,6 +15,7 @@ import { Size } from 'Helpers/Props/sizes';
import { isIOS } from 'Utilities/browser';
import * as keyCodes from 'Utilities/Constants/keyCodes';
import { setScrollLock } from 'Utilities/scrollLock';
import { ModalContext } from './ModalContext';
import ModalError from './ModalError';
import styles from './Modal.css';
@ -163,26 +164,36 @@ function Modal({
return null;
}
const headerId = `${modalId}-header`;
return ReactDOM.createPortal(
<FocusLock disabled={false}>
<div className={styles.modalContainer}>
<div
ref={backgroundRef}
className={backdropClassName}
onMouseDown={handleBackdropBeginPress}
onMouseUp={handleBackdropEndPress}
>
<div className={classNames(className, styles[size])} style={style}>
<ErrorBoundary
errorComponent={ModalError}
onModalClose={onModalClose}
<ModalContext.Provider value={{ headerId }}>
<FocusLock disabled={false}>
<div className={styles.modalContainer}>
<div
ref={backgroundRef}
className={backdropClassName}
onMouseDown={handleBackdropBeginPress}
onMouseUp={handleBackdropEndPress}
>
<div
className={classNames(className, styles[size])}
style={style}
role="dialog"
aria-modal="true"
aria-labelledby={headerId}
>
{children}
</ErrorBoundary>
<ErrorBoundary
errorComponent={ModalError}
onModalClose={onModalClose}
>
{children}
</ErrorBoundary>
</div>
</div>
</div>
</div>
</FocusLock>,
</FocusLock>
</ModalContext.Provider>,
node!
);
}

View file

@ -0,0 +1,11 @@
import { createContext, useContext } from 'react';
interface ModalContextValue {
headerId: string;
}
export const ModalContext = createContext<ModalContextValue>({ headerId: '' });
export function useModalContext() {
return useContext(ModalContext);
}

View file

@ -1,4 +1,5 @@
import React, { ForwardedRef, forwardRef, ReactNode } from 'react';
import { useModalContext } from './ModalContext';
import styles from './ModalHeader.css';
interface ModalHeaderProps extends React.HTMLAttributes<HTMLDivElement> {
@ -10,8 +11,15 @@ const ModalHeader = forwardRef(
{ children, ...otherProps }: ModalHeaderProps,
ref: ForwardedRef<HTMLDivElement>
) => {
const { headerId } = useModalContext();
return (
<div ref={ref} className={styles.modalHeader} {...otherProps}>
<div
ref={ref}
id={headerId}
className={styles.modalHeader}
{...otherProps}
>
{children}
</div>
);

View file

@ -54,6 +54,7 @@ function MonitorToggleButton(props: MonitorToggleButtonProps) {
name={iconName}
size={size}
title={title}
aria-label={title}
isDisabled={isDisabled}
isSpinning={isSaving}
{...otherProps}

View file

@ -55,6 +55,7 @@ function PageHeader() {
<IconButton
id="sidebar-toggle-button"
name={icons.NAVBAR_COLLAPSE}
aria-label={translate('Menu')}
onPress={handleSidebarToggle}
/>
</div>

View file

@ -435,10 +435,11 @@ function PageSidebar() {
const ScrollerComponent = isSmallScreen ? Scroller : OverlayScroller;
return (
<div
<nav
ref={sidebarRef}
className={styles.sidebarContainer}
style={containerStyle}
aria-label={translate('MainNavigation')}
>
{isSmallScreen ? (
<div className={styles.sidebarHeader}>
@ -521,7 +522,7 @@ function PageSidebar() {
<Messages />
</ScrollerComponent>
</div>
</nav>
);
}

View file

@ -46,11 +46,12 @@ function PageSidebarItem({
isActive && styles.isActiveLink
)}
to={to}
aria-current={isActive ? 'page' : undefined}
onPress={handlePress}
>
{!!iconName && (
<span className={styles.iconContainer}>
<Icon name={iconName} />
<Icon name={iconName} aria-hidden={true} />
</span>
)}

View file

@ -7,6 +7,7 @@ import { icons, scrollDirections } from 'Helpers/Props';
import { SortDirection } from 'Helpers/Props/sortDirections';
import { CheckInputChanged } from 'typings/inputs';
import { TableOptionsChangePayload } from 'typings/Table';
import translate from 'Utilities/String/translate';
import Column from './Column';
import TableHeader from './TableHeader';
import TableHeaderCell from './TableHeaderCell';
@ -94,7 +95,10 @@ function Table({
canModifyColumns={canModifyColumns}
onTableOptionChange={onTableOptionChange}
>
<IconButton name={icons.ADVANCED_SETTINGS} />
<IconButton
name={icons.ADVANCED_SETTINGS}
aria-label={translate('AdvancedSettings')}
/>
</TableOptionsModalWrapper>
</TableHeaderCell>
);

View file

@ -1,4 +1,4 @@
import React, { useCallback } from 'react';
import React, { useCallback, useMemo } from 'react';
import Icon from 'Components/Icon';
import Link from 'Components/Link/Link';
import { icons, sortDirections } from 'Helpers/Props';
@ -41,6 +41,20 @@ function TableHeaderCell({
? icons.SORT_ASCENDING
: icons.SORT_DESCENDING;
const ariaSortValue = useMemo(() => {
if (!isSortable) {
return undefined;
}
if (!isSorting) {
return 'none';
}
return sortDirection === sortDirections.ASCENDING
? 'ascending'
: 'descending';
}, [isSorting, sortDirection, isSortable]);
const handlePress = useCallback(() => {
if (fixedSortDirection) {
onSortPress?.(name, fixedSortDirection);
@ -56,14 +70,20 @@ function TableHeaderCell({
className={className}
// label={typeof label === 'function' ? label() : label}
title={typeof columnLabel === 'function' ? columnLabel() : columnLabel}
scope="col"
aria-sort={ariaSortValue}
onPress={handlePress}
>
{children}
{isSorting && <Icon name={sortIcon} className={styles.sortIcon} />}
{isSorting ? (
<Icon name={sortIcon} className={styles.sortIcon} aria-hidden={true} />
) : null}
</Link>
) : (
<th className={className}>{children}</th>
<th className={className} scope="col">
{children}
</th>
);
}

View file

@ -108,9 +108,10 @@ function TablePager({
isFirstPage && styles.disabledPageButton
)}
isDisabled={isFirstPage}
aria-label={translate('PagerGoToFirstPage')}
onPress={handleFirstPagePress}
>
<Icon name={icons.PAGE_FIRST} />
<Icon name={icons.PAGE_FIRST} aria-hidden={true} />
</Link>
<Link
@ -119,15 +120,20 @@ function TablePager({
isFirstPage && styles.disabledPageButton
)}
isDisabled={isFirstPage}
aria-label={translate('PagerGoToPreviousPage')}
onPress={onPreviousPagePress}
>
<Icon name={icons.PAGE_PREVIOUS} />
<Icon name={icons.PAGE_PREVIOUS} aria-hidden={true} />
</Link>
<div className={styles.pageNumber}>
{isShowingPageSelect ? null : (
<Link
isDisabled={totalPages === 1}
aria-label={translate('PagerGoToPage', {
page,
totalPages: totalPages ?? 0,
})}
onPress={handleOpenPageSelectClick}
>
{page} / {totalPages}
@ -153,9 +159,10 @@ function TablePager({
isLastPage && styles.disabledPageButton
)}
isDisabled={isLastPage}
aria-label={translate('PagerGoToNextPage')}
onPress={onNextPagePress}
>
<Icon name={icons.PAGE_NEXT} />
<Icon name={icons.PAGE_NEXT} aria-hidden={true} />
</Link>
<Link
@ -164,9 +171,10 @@ function TablePager({
isLastPage && styles.disabledPageButton
)}
isDisabled={isLastPage}
aria-label={translate('PagerGoToLastPage')}
onPress={onLastPagePress}
>
<Icon name={icons.PAGE_LAST} />
<Icon name={icons.PAGE_LAST} aria-hidden={true} />
</Link>
</div>
</div>

View file

@ -54,6 +54,7 @@ function EpisodeSearchCell({
<IconButton
name={icons.INTERACTIVE}
title={translate('InteractiveSearch')}
aria-label={translate('InteractiveSearch')}
onPress={setDetailsModalOpen}
/>

View file

@ -128,6 +128,7 @@ function EpisodeHistoryRow({
{eventType === 'grabbed' && (
<IconButton
title={translate('MarkAsFailed')}
aria-label={translate('MarkAsFailed')}
name={icons.REMOVE}
size={14}
onPress={handleMarkAsFailedPress}

View file

@ -124,6 +124,7 @@ function EpisodeFileRow(props: EpisodeFileRowProps) {
<IconButton
title={translate('DeleteEpisodeFromDisk')}
aria-label={translate('DeleteEpisodeFromDisk')}
name={icons.REMOVE}
onPress={setRemoveEpisodeFileModalOpen}
/>

View file

@ -33,6 +33,7 @@ function FavoriteFolderRow({ folder, onPress }: FavoriteFolderRowProps) {
<TableRowCell className={styles.actions}>
<IconButton
title={translate('FavoriteFolderRemove')}
aria-label={translate('FavoriteFolderRemove')}
kind="danger"
name={icons.HEART}
onPress={handleRemoveFavoritePress}

View file

@ -64,6 +64,11 @@ function RecentFolderRow({
? translate('FavoriteFolderRemove')
: translate('FavoriteFolderAdd')
}
aria-label={
isFavorite
? translate('FavoriteFolderRemove')
: translate('FavoriteFolderAdd')
}
kind={isFavorite ? 'danger' : 'default'}
name={isFavorite ? icons.HEART : icons.HEART_OUTLINE}
onPress={handleFavoritePress}
@ -71,6 +76,7 @@ function RecentFolderRow({
<IconButton
title={translate('Remove')}
aria-label={translate('Remove')}
name={icons.REMOVE}
onPress={handleRemovePress}
/>

View file

@ -83,6 +83,7 @@ function RootFolderRow(props: RootFolderRowProps) {
<TableRowCell className={styles.actions}>
<IconButton
title={translate('RemoveRootFolder')}
aria-label={translate('RemoveRootFolder')}
name={icons.REMOVE}
onPress={onDeletePress}
/>

View file

@ -574,6 +574,9 @@ function SeriesDetails({ seriesId }: SeriesDetailsProps) {
title={translate('SeriesDetailsGoTo', {
title: previousSeries.title,
})}
aria-label={translate('SeriesDetailsGoTo', {
title: previousSeries.title,
})}
to={`/series/${previousSeries.titleSlug}`}
/>
) : null}
@ -586,6 +589,9 @@ function SeriesDetails({ seriesId }: SeriesDetailsProps) {
title={translate('SeriesDetailsGoTo', {
title: nextSeries.title,
})}
aria-label={translate('SeriesDetailsGoTo', {
title: nextSeries.title,
})}
to={`/series/${nextSeries.titleSlug}`}
/>
) : null}

View file

@ -432,6 +432,7 @@ function SeriesDetailsSeason({
className={styles.actionButton}
name={icons.INTERACTIVE}
title={translate('InteractiveSearchSeason')}
aria-label={translate('InteractiveSearchSeason')}
size={24}
isDisabled={!totalEpisodeCount}
onPress={handleInteractiveSearchPress}
@ -441,6 +442,7 @@ function SeriesDetailsSeason({
className={styles.actionButton}
name={icons.ORGANIZE}
title={translate('PreviewRenameSeason')}
aria-label={translate('PreviewRenameSeason')}
size={24}
isDisabled={!episodeFileCount}
onPress={handleOrganizePress}
@ -450,6 +452,7 @@ function SeriesDetailsSeason({
className={styles.actionButton}
name={icons.EPISODE_FILE}
title={translate('ManageEpisodesSeason')}
aria-label={translate('ManageEpisodesSeason')}
size={24}
isDisabled={!episodeFileCount}
onPress={handleManageEpisodesPress}
@ -459,6 +462,7 @@ function SeriesDetailsSeason({
className={styles.actionButton}
name={icons.HISTORY}
title={translate('HistorySeason')}
aria-label={translate('HistorySeason')}
size={24}
isDisabled={!totalEpisodeCount}
onPress={handleHistoryPress}
@ -506,6 +510,7 @@ function SeriesDetailsSeason({
name={icons.COLLAPSE}
size={20}
title={translate('HideEpisodes')}
aria-label={translate('HideEpisodes')}
onPress={handleExpandPress}
/>
</div>

View file

@ -158,6 +158,7 @@ function SeriesHistoryRow({
{eventType === 'grabbed' ? (
<IconButton
title={translate('MarkAsFailed')}
aria-label={translate('MarkAsFailed')}
name={icons.REMOVE}
size={14}
onPress={handleMarkAsFailedPress}

View file

@ -215,6 +215,7 @@ function SeriesIndexOverview(props: SeriesIndexOverviewProps) {
<IconButton
name={icons.EDIT}
title={translate('EditSeries')}
aria-label={translate('EditSeries')}
onPress={onEditSeriesPress}
/>
</div>

View file

@ -162,6 +162,7 @@ function SeriesIndexPoster(props: SeriesIndexPosterProps) {
className={styles.action}
name={icons.EDIT}
title={translate('EditSeries')}
aria-label={translate('EditSeries')}
tabIndex={-1}
onPress={onEditSeriesPress}
/>

View file

@ -490,6 +490,7 @@ function SeriesIndexRow(props: SeriesIndexRowProps) {
<IconButton
name={icons.EDIT}
title={translate('EditSeries')}
aria-label={translate('EditSeries')}
onPress={onEditSeriesPress}
/>
</VirtualTableRowCell>

View file

@ -16,6 +16,7 @@ import {
} from 'Series/seriesOptionsStore';
import { CheckInputChanged } from 'typings/inputs';
import { TableOptionsChangePayload } from 'typings/Table';
import translate from 'Utilities/String/translate';
import hasGrowableColumns from './hasGrowableColumns';
import SeriesIndexTableOptions from './SeriesIndexTableOptions';
import styles from './SeriesIndexTableHeader.css';
@ -95,7 +96,10 @@ function SeriesIndexTableHeader(props: SeriesIndexTableHeaderProps) {
optionsComponent={SeriesIndexTableOptions}
onTableOptionChange={onTableOptionChange}
>
<IconButton name={icons.ADVANCED_SETTINGS} />
<IconButton
name={icons.ADVANCED_SETTINGS}
aria-label={translate('AdvancedSettings')}
/>
</TableOptionsModalWrapper>
</VirtualTableHeaderCell>
);

View file

@ -83,6 +83,7 @@ function CustomFormat({
<IconButton
className={styles.cloneButton}
title={translate('CloneCustomFormat')}
aria-label={translate('CloneCustomFormat')}
name={icons.CLONE}
onPress={handleCloneCustomFormatPressHandler}
/>
@ -90,6 +91,7 @@ function CustomFormat({
<IconButton
className={styles.cloneButton}
title={translate('ExportCustomFormat')}
aria-label={translate('ExportCustomFormat')}
name={icons.EXPORT}
onPress={handleExportCustomFormatPress}
/>

View file

@ -94,6 +94,7 @@ function ManageCustomFormatsModalRow({
<TableRowCell className={styles.actions}>
<IconButton
name={icons.EDIT}
aria-label={translate('Edit')}
onPress={handleEditCustomFormatModalOpen}
/>
</TableRowCell>

View file

@ -73,6 +73,7 @@ function Specification({
<IconButton
className={styles.cloneButton}
title={translate('CloneCondition')}
aria-label={translate('CloneCondition')}
name={icons.CLONE}
onPress={handleCloneSpecificationPress}
/>

View file

@ -77,6 +77,7 @@ function ImportListExclusionRow({
<TableRowCell className={styles.actions}>
<IconButton
name={icons.EDIT}
aria-label={translate('Edit')}
onPress={setEditImportListExclusionModalOpen}
/>
</TableRowCell>

View file

@ -199,6 +199,7 @@ function ImportListExclusionsContent() {
<TableRowCell>
<IconButton
name={icons.ADD}
aria-label={translate('Add')}
onPress={setAddImportListExclusionModalOpen}
/>
</TableRowCell>

View file

@ -76,6 +76,7 @@ function ImportList({
<IconButton
className={styles.cloneButton}
title={translate('CloneImportList')}
aria-label={translate('CloneImportList')}
name={icons.CLONE}
onPress={handleCloneImportListPress}
/>

View file

@ -75,6 +75,7 @@ function Indexer({
<IconButton
className={styles.cloneButton}
title={translate('CloneIndexer')}
aria-label={translate('CloneIndexer')}
name={icons.CLONE}
onPress={handleCloneIndexerPress}
/>

View file

@ -76,6 +76,7 @@ function QualityProfile({
<IconButton
className={styles.cloneButton}
title={translate('CloneProfile')}
aria-label={translate('CloneProfile')}
name={icons.CLONE}
onPress={handleCloneQualityProfilePress}
/>

View file

@ -75,6 +75,7 @@ function QualityProfileItem({
className={styles.createGroupButton}
name={icons.GROUP}
title={translate('Group')}
aria-label={translate('Group')}
onPress={handleCreateGroupPress}
/>
)}

View file

@ -89,6 +89,7 @@ function QualityProfileItemGroup({
className={styles.deleteGroupButton}
name={icons.UNGROUP}
title={translate('Ungroup')}
aria-label={translate('Ungroup')}
onPress={handleDeleteGroupPress}
/>

View file

@ -74,6 +74,7 @@ export default function AutoTagging({
<IconButton
className={styles.cloneButton}
title={translate('CloneAutoTag')}
aria-label={translate('CloneAutoTag')}
name={icons.CLONE}
onPress={onClonePress}
/>

View file

@ -70,6 +70,7 @@ export default function Specification({
<IconButton
className={styles.cloneButton}
title={translate('Clone')}
aria-label={translate('Clone')}
name={icons.CLONE}
onPress={onClonePress}
/>

View file

@ -96,12 +96,14 @@ function BackupRow({ id, type, name, path, size, time }: BackupRowProps) {
<TableRowCell className={styles.actions}>
<IconButton
title={translate('RestoreBackup')}
aria-label={translate('RestoreBackup')}
name={icons.RESTORE}
onPress={handleRestorePress}
/>
<IconButton
title={translate('DeleteBackup')}
aria-label={translate('DeleteBackup')}
name={icons.DELETE}
onPress={handleDeletePress}
/>

View file

@ -118,6 +118,7 @@ function Health() {
name={icons.WIKI}
to={item.wikiUrl}
title={translate('ReadTheWikiForMoreInformation')}
aria-label={translate('ReadTheWikiForMoreInformation')}
/>
<HealthItemLink source={source} />

View file

@ -20,6 +20,7 @@ function HealthItemLink(props: HealthItemLinkProps) {
<IconButton
name={icons.SETTINGS}
title={translate('Settings')}
aria-label={translate('Settings')}
to="/settings/indexers"
/>
);
@ -30,6 +31,7 @@ function HealthItemLink(props: HealthItemLinkProps) {
<IconButton
name={icons.SETTINGS}
title={translate('Settings')}
aria-label={translate('Settings')}
to="/settings/downloadclients"
/>
);
@ -38,6 +40,7 @@ function HealthItemLink(props: HealthItemLinkProps) {
<IconButton
name={icons.SETTINGS}
title={translate('Settings')}
aria-label={translate('Settings')}
to="/settings/connect"
/>
);
@ -46,6 +49,7 @@ function HealthItemLink(props: HealthItemLinkProps) {
<IconButton
name={icons.SERIES_CONTINUING}
title={translate('SeriesEditor')}
aria-label={translate('SeriesEditor')}
to="/serieseditor"
/>
);
@ -54,6 +58,7 @@ function HealthItemLink(props: HealthItemLinkProps) {
<IconButton
name={icons.UPDATE}
title={translate('Updates')}
aria-label={translate('Updates')}
to="/system/updates"
/>
);

View file

@ -219,6 +219,7 @@ export default function QueuedTaskRow(props: QueuedTaskRowProps) {
{status === 'queued' && (
<IconButton
title={translate('RemovedFromTaskQueue')}
aria-label={translate('RemovedFromTaskQueue')}
name={icons.REMOVE}
onPress={openCancelConfirmModal}
/>

View file

@ -10,6 +10,7 @@ import { isCommandExecuting } from 'Utilities/Command';
import formatDate from 'Utilities/Date/formatDate';
import formatDateTime from 'Utilities/Date/formatDateTime';
import formatTimeSpan from 'Utilities/Date/formatTimeSpan';
import translate from 'Utilities/String/translate';
import styles from './ScheduledTaskRow.css';
interface ScheduledTaskRowProps {
@ -138,6 +139,7 @@ function ScheduledTaskRow({
<SpinnerIconButton
name={icons.REFRESH}
spinningName={icons.REFRESH}
aria-label={translate('Run')}
isSpinning={isExecuting}
onPress={handleExecutePress}
/>

View file

@ -57,6 +57,7 @@
"AddedDate": "Added: {date}",
"AddedToDownloadQueue": "Added to download queue",
"AddingTag": "Adding tag",
"AdvancedSettings": "Advanced Settings",
"AfterManualRefresh": "After Manual Refresh",
"Age": "Age",
"AgeWhenGrabbed": "Age (when grabbed)",
@ -1182,6 +1183,7 @@
"Logs": "Logs",
"LongDateFormat": "Long Date Format",
"Lowercase": "Lowercase",
"MainNavigation": "Main Navigation",
"MaintenanceRelease": "Maintenance Release: bug fixes and other improvements. See Github Commit History for more details",
"ManageClients": "Manage Clients",
"ManageCustomFormats": "Manage Custom Formats",
@ -1635,6 +1637,11 @@
"OverviewOptions": "Overview Options",
"PackageVersion": "Package Version",
"PackageVersionInfo": "{packageVersion} by {packageAuthor}",
"PagerGoToFirstPage": "Go to first page",
"PagerGoToLastPage": "Go to last page",
"PagerGoToNextPage": "Go to next page",
"PagerGoToPage": "Go to page {page} of {totalPages}",
"PagerGoToPreviousPage": "Go to previous page",
"Parse": "Parse",
"ParseModalErrorParsing": "Error parsing, please try again.",
"ParseModalHelpText": "Enter a release title in the input above",
@ -1875,6 +1882,7 @@
"RssSyncInterval": "RSS Sync Interval",
"RssSyncIntervalHelpText": "Interval in minutes. Set to zero to disable (this will stop all automatic release grabbing)",
"RssSyncIntervalHelpTextWarning": "This will apply to all indexers, please follow the rules set forth by them",
"Run": "Run",
"Runtime": "Runtime",
"Saturday": "Saturday",
"Save": "Save",
@ -2232,6 +2240,7 @@
"VideoCodec": "Video Codec",
"VideoDynamicRange": "Video Dynamic Range",
"View": "View",
"ViewSeriesOnTvdb": "View {title} on TVDB",
"VisitTheWikiForMoreDetails": "Visit the wiki for more details: ",
"WaitingToImport": "Waiting to Import",
"WaitingToProcess": "Waiting to Process",