From 93c53050f7d0c951788e55066b774aeeb94e2329 Mon Sep 17 00:00:00 2001 From: Henry Heino Date: Fri, 26 Jul 2024 14:06:43 -0700 Subject: [PATCH] Desktop: Accessibility: Add missing labels and role information to many controls --- .../app-desktop/gui/NotePropertiesDialog.tsx | 28 +++++++++++++------ .../Sidebar/listItemComponents/FolderItem.tsx | 11 +++++--- .../Sidebar/listItemComponents/HeaderItem.tsx | 2 +- .../Sidebar/listItemComponents/NoteCount.tsx | 5 ++-- .../Sidebar/listItemComponents/TagItem.tsx | 4 ++- packages/app-desktop/gui/Sidebar/style.scss | 1 + .../app-desktop/gui/Sidebar/styles/index.ts | 6 ---- .../gui/Sidebar/styles/note-count-label.scss | 6 ++++ .../gui/ToolbarButton/ToolbarButton.tsx | 8 +++++- .../gui/lib/SearchInput/SearchInput.tsx | 6 +++- .../app-desktop/gui/note-viewer/index.html | 1 + 11 files changed, 54 insertions(+), 24 deletions(-) create mode 100644 packages/app-desktop/gui/Sidebar/styles/note-count-label.scss diff --git a/packages/app-desktop/gui/NotePropertiesDialog.tsx b/packages/app-desktop/gui/NotePropertiesDialog.tsx index 226dcd74f5d..6bd8faa273a 100644 --- a/packages/app-desktop/gui/NotePropertiesDialog.tsx +++ b/packages/app-desktop/gui/NotePropertiesDialog.tsx @@ -174,7 +174,7 @@ class NotePropertiesDialog extends React.Component { textDecoration: 'none', backgroundColor: theme.backgroundColor, padding: '.14em', - display: 'flex', + display: 'inline-flex', alignItems: 'center', justifyContent: 'center', marginLeft: '0.5em', @@ -281,11 +281,13 @@ class NotePropertiesDialog extends React.Component { public createNoteField(key: keyof FormNote, value: any) { const styles = this.styles(this.props.themeId); const theme = themeStyle(this.props.themeId); - const labelComp = ; + const labelText = this.formatLabel(key); + const labelComp = ; let controlComp = null; let editComp = null; let editCompHandler = null; let editCompIcon = null; + let editComDescription = null; // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied const onKeyDown = (event: any) => { @@ -320,6 +322,7 @@ class NotePropertiesDialog extends React.Component { void this.saveProperty(); }; editCompIcon = 'fa-save'; + editComDescription = _('Save changes'); } else { controlComp = ( { this.editPropertyButtonClick(key, value); }; editCompIcon = 'fa-edit'; + editComDescription = _('Edit'); } // Add the copy icon and the 'copy on click' event if (key === 'id') { editCompIcon = 'fa-copy'; editCompHandler = () => clipboard.writeText(value); + editComDescription = _('Copy'); } } if (editCompHandler && !this.isReadOnly()) { editComp = ( - + ); } return ( -
+
{labelComp} - {controlComp} - {editComp} + {controlComp} {editComp}
); } @@ -439,8 +449,10 @@ class NotePropertiesDialog extends React.Component { return (
-
{_('Note properties')}
-
{noteComps}
+
{_('Note properties')}
+
+ {noteComps} +
diff --git a/packages/app-desktop/gui/Sidebar/listItemComponents/FolderItem.tsx b/packages/app-desktop/gui/Sidebar/listItemComponents/FolderItem.tsx index 1c89a5c0cdd..dd7af6140d4 100644 --- a/packages/app-desktop/gui/Sidebar/listItemComponents/FolderItem.tsx +++ b/packages/app-desktop/gui/Sidebar/listItemComponents/FolderItem.tsx @@ -2,12 +2,14 @@ import * as React from 'react'; import { FolderIcon, FolderIconType } from '@joplin/lib/services/database/types'; import ExpandLink from './ExpandLink'; -import { StyledListItem, StyledListItemAnchor, StyledNoteCount, StyledShareIcon, StyledSpanFix } from '../styles'; +import { StyledListItem, StyledListItemAnchor, StyledShareIcon, StyledSpanFix } from '../styles'; import { ItemClickListener, ItemContextMenuListener, ItemDragListener } from '../types'; import FolderIconBox from '../../FolderIconBox'; import { getTrashFolderIcon, getTrashFolderId } from '@joplin/lib/services/trash'; import Folder from '@joplin/lib/models/Folder'; import { ModelType } from '@joplin/lib/BaseModel'; +import { _ } from '@joplin/lib/locale'; +import NoteCount from './NoteCount'; const renderFolderIcon = (folderIcon: FolderIcon) => { if (!folderIcon) { @@ -47,8 +49,8 @@ interface FolderItemProps { function FolderItem(props: FolderItemProps) { const { hasChildren, showFolderIcon, isExpanded, parentId, depth, selected, folderId, folderTitle, folderIcon, noteCount, onFolderDragStart_, onFolderDragOver_, onFolderDrop_, itemContextMenu, folderItem_click, onFolderToggleClick_, shareId } = props; - const noteCountComp = noteCount ? {noteCount} : null; - const shareIcon = shareId && !parentId ? : null; + const shareTitle = _('Shared'); + const shareIcon = shareId && !parentId ? : null; const draggable = ![getTrashFolderId(), Folder.conflictFolderId()].includes(folderId); const doRenderFolderIcon = () => { @@ -69,6 +71,7 @@ function FolderItem(props: FolderItemProps) { isConflictFolder={folderId === Folder.conflictFolderId()} href="#" selected={selected} + aria-selected={selected} shareId={shareId} data-id={folderId} data-type={ModelType.Folder} @@ -80,7 +83,7 @@ function FolderItem(props: FolderItemProps) { onDoubleClick={onFolderToggleClick_} > {doRenderFolderIcon()}{folderTitle} - {shareIcon} {noteCountComp} + {shareIcon} ); diff --git a/packages/app-desktop/gui/Sidebar/listItemComponents/HeaderItem.tsx b/packages/app-desktop/gui/Sidebar/listItemComponents/HeaderItem.tsx index affc0dff692..357a17a7ead 100644 --- a/packages/app-desktop/gui/Sidebar/listItemComponents/HeaderItem.tsx +++ b/packages/app-desktop/gui/Sidebar/listItemComponents/HeaderItem.tsx @@ -61,7 +61,7 @@ const HeaderItem: React.FC = props => { tabIndex={0} ref={props.anchorRef} > - + {item.label} { item.onPlusButtonClick && addButton } diff --git a/packages/app-desktop/gui/Sidebar/listItemComponents/NoteCount.tsx b/packages/app-desktop/gui/Sidebar/listItemComponents/NoteCount.tsx index ec44bcf4555..c88c2fcb164 100644 --- a/packages/app-desktop/gui/Sidebar/listItemComponents/NoteCount.tsx +++ b/packages/app-desktop/gui/Sidebar/listItemComponents/NoteCount.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { StyledNoteCount } from '../styles'; +import { _n } from '@joplin/lib/locale'; interface Props { @@ -8,7 +8,8 @@ interface Props { const NoteCount: React.FC = props => { const count = props.count; - return count ? {count} : null; + const title = _n('Contains %d note', 'Contains %d notes', count, count); + return count ?
{count}
: null; }; export default NoteCount; diff --git a/packages/app-desktop/gui/Sidebar/listItemComponents/TagItem.tsx b/packages/app-desktop/gui/Sidebar/listItemComponents/TagItem.tsx index 911414dc891..6730173f02c 100644 --- a/packages/app-desktop/gui/Sidebar/listItemComponents/TagItem.tsx +++ b/packages/app-desktop/gui/Sidebar/listItemComponents/TagItem.tsx @@ -33,10 +33,12 @@ const TagItem = (props: Props) => { }, [props.onClick, tag]); return ( - props.theme.colorFaded2}; - padding-left: 8px; - user-select: none; -`; - export const StyledSynchronizeButton = styled(Button)` width: 100%; `; diff --git a/packages/app-desktop/gui/Sidebar/styles/note-count-label.scss b/packages/app-desktop/gui/Sidebar/styles/note-count-label.scss new file mode 100644 index 00000000000..790d1d320c5 --- /dev/null +++ b/packages/app-desktop/gui/Sidebar/styles/note-count-label.scss @@ -0,0 +1,6 @@ + +.note-count-label { + color: var(--joplin-color-faded2); + padding-left: 8px; + user-select: none; +} \ No newline at end of file diff --git a/packages/app-desktop/gui/ToolbarButton/ToolbarButton.tsx b/packages/app-desktop/gui/ToolbarButton/ToolbarButton.tsx index f12f5663be2..3c03c34a387 100644 --- a/packages/app-desktop/gui/ToolbarButton/ToolbarButton.tsx +++ b/packages/app-desktop/gui/ToolbarButton/ToolbarButton.tsx @@ -50,16 +50,22 @@ export default function ToolbarButton(props: Props) { whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }; + const disabled = !isEnabled; return ( { if (isEnabled && onClick) onClick(); }} + + // At least on MacOS, the disabled HTML prop isn't sufficient for the screen reader + // to read the element as disable. For this, aria-disabled is necessary. + disabled={disabled} + aria-disabled={!isEnabled} + role='button' > {icon} {title} diff --git a/packages/app-desktop/gui/lib/SearchInput/SearchInput.tsx b/packages/app-desktop/gui/lib/SearchInput/SearchInput.tsx index e8a56647b23..5ef52400c56 100644 --- a/packages/app-desktop/gui/lib/SearchInput/SearchInput.tsx +++ b/packages/app-desktop/gui/lib/SearchInput/SearchInput.tsx @@ -59,6 +59,7 @@ export interface OnChangeEvent { export default function(props: Props) { const iconName = !props.searchStarted ? CommandService.instance().iconName('search') : 'fa fa-times'; + const iconLabel = !props.searchStarted ? _('Search') : _('Clear search'); // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied const onChange = useCallback((event: any) => { @@ -79,7 +80,10 @@ export default function(props: Props) { spellCheck={false} disabled={props.disabled} /> - + diff --git a/packages/app-desktop/gui/note-viewer/index.html b/packages/app-desktop/gui/note-viewer/index.html index f7c63887681..890787bcc40 100644 --- a/packages/app-desktop/gui/note-viewer/index.html +++ b/packages/app-desktop/gui/note-viewer/index.html @@ -2,6 +2,7 @@ + Note viewer