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

Desktop: Accessibility: Add missing labels and role information to several controls #10788

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
28 changes: 20 additions & 8 deletions packages/app-desktop/gui/NotePropertiesDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ class NotePropertiesDialog extends React.Component<Props, State> {
textDecoration: 'none',
backgroundColor: theme.backgroundColor,
padding: '.14em',
display: 'flex',
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
marginLeft: '0.5em',
Expand Down Expand Up @@ -281,11 +281,13 @@ class NotePropertiesDialog extends React.Component<Props, State> {
public createNoteField(key: keyof FormNote, value: any) {
const styles = this.styles(this.props.themeId);
const theme = themeStyle(this.props.themeId);
const labelComp = <label style={{ ...theme.textStyle, ...theme.controlBoxLabel }}>{this.formatLabel(key)}</label>;
const labelText = this.formatLabel(key);
const labelComp = <label role='rowheader' style={{ ...theme.textStyle, ...theme.controlBoxLabel }}>{labelText}</label>;
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) => {
Expand Down Expand Up @@ -320,6 +322,7 @@ class NotePropertiesDialog extends React.Component<Props, State> {
void this.saveProperty();
};
editCompIcon = 'fa-save';
editComDescription = _('Save changes');
} else {
controlComp = (
<input
Expand Down Expand Up @@ -374,28 +377,35 @@ class NotePropertiesDialog extends React.Component<Props, State> {
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 = (
<a href="#" onClick={editCompHandler} style={styles.editPropertyButton}>
<a
href="#"
onClick={editCompHandler}
style={styles.editPropertyButton}
aria-label={editComDescription}
title={editComDescription}
>
<i className={`fas ${editCompIcon}`} aria-hidden="true"></i>
</a>
);
}

return (
<div key={key} style={theme.controlBox} className="note-property-box">
<div role='row' key={key} style={theme.controlBox} className="note-property-box">
{labelComp}
{controlComp}
{editComp}
<span role='cell'>{controlComp} {editComp}</span>
</div>
);
}
Expand Down Expand Up @@ -439,8 +449,10 @@ class NotePropertiesDialog extends React.Component<Props, State> {
return (
<div style={theme.dialogModalLayer}>
<div style={theme.dialogBox}>
<div style={theme.dialogTitle}>{_('Note properties')}</div>
<div>{noteComps}</div>
<div style={theme.dialogTitle} id='note-properties-dialog-title'>{_('Note properties')}</div>
<div role='table' aria-labelledby='note-properties-dialog-title'>
{noteComps}
</div>
<DialogButtonRow themeId={this.props.themeId} okButtonShow={!this.isReadOnly()} okButtonRef={this.okButton} onClick={this.buttonRow_click}/>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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 ? <StyledNoteCount className="note-count-label">{noteCount}</StyledNoteCount> : null;
const shareIcon = shareId && !parentId ? <StyledShareIcon className="fas fa-share-alt"></StyledShareIcon> : null;
const shareTitle = _('Shared');
const shareIcon = shareId && !parentId ? <StyledShareIcon aria-label={shareTitle} title={shareTitle} className="fas fa-share-alt"/> : null;
const draggable = ![getTrashFolderId(), Folder.conflictFolderId()].includes(folderId);

const doRenderFolderIcon = () => {
Expand All @@ -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}
Expand All @@ -80,7 +83,7 @@ function FolderItem(props: FolderItemProps) {
onDoubleClick={onFolderToggleClick_}
>
{doRenderFolderIcon()}<StyledSpanFix className="title">{folderTitle}</StyledSpanFix>
{shareIcon} {noteCountComp}
{shareIcon} <NoteCount count={noteCount}/>
</StyledListItemAnchor>
</StyledListItem>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ const HeaderItem: React.FC<Props> = props => {
tabIndex={0}
ref={props.anchorRef}
>
<StyledHeaderIcon className={item.iconName}/>
<StyledHeaderIcon aria-label='' className={item.iconName}/>
<StyledHeaderLabel>{item.label}</StyledHeaderLabel>
</StyledHeader>
{ item.onPlusButtonClick && addButton }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { StyledNoteCount } from '../styles';
import { _n } from '@joplin/lib/locale';


interface Props {
Expand All @@ -8,7 +8,8 @@ interface Props {

const NoteCount: React.FC<Props> = props => {
const count = props.count;
return count ? <StyledNoteCount className="note-count-label">{count}</StyledNoteCount> : null;
const title = _n('Contains %d note', 'Contains %d notes', count, count);
return count ? <div role='note' aria-label={title} title={title} className="note-count-label">{count}</div> : null;
};

export default NoteCount;
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,12 @@ const TagItem = (props: Props) => {
}, [props.onClick, tag]);

return (
<StyledListItem selected={selected}
<StyledListItem
selected={selected}
className={`list-item-container ${selected ? 'selected' : ''}`}
onDrop={props.onTagDrop}
data-tag-id={tag.id}
aria-selected={selected}
>
<EmptyExpandLink/>
<StyledListItemAnchor
Expand Down
1 change: 1 addition & 0 deletions packages/app-desktop/gui/Sidebar/style.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@use 'styles/folder-and-tag-list.scss';
@use 'styles/note-count-label.scss';
@use 'styles/sidebar-expand-icon.scss';
@use 'styles/sidebar-expand-link.scss';
@use 'styles/sidebar-header-container.scss';
Expand Down
6 changes: 0 additions & 6 deletions packages/app-desktop/gui/Sidebar/styles/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,6 @@ export const StyledShareIcon = styled.i`
margin-left: 8px;
`;

export const StyledNoteCount = styled.div`
color: ${(props: StyleProps) => props.theme.colorFaded2};
padding-left: 8px;
user-select: none;
`;

export const StyledSynchronizeButton = styled(Button)`
width: 100%;
`;
Expand Down
6 changes: 6 additions & 0 deletions packages/app-desktop/gui/Sidebar/styles/note-count-label.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

.note-count-label {
color: var(--joplin-color-faded2);
padding-left: 8px;
user-select: none;
}
8 changes: 7 additions & 1 deletion packages/app-desktop/gui/ToolbarButton/ToolbarButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,22 @@ export default function ToolbarButton(props: Props) {
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis' };
const disabled = !isEnabled;
return (
<StyledRoot
className={classes.join(' ')}
disabled={!isEnabled}
title={tooltip}
href="#"
hasTitle={!!title}
onClick={() => {
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}
<span style={style}>{title}</span>
Expand Down
6 changes: 5 additions & 1 deletion packages/app-desktop/gui/lib/SearchInput/SearchInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand All @@ -79,7 +80,10 @@ export default function(props: Props) {
spellCheck={false}
disabled={props.disabled}
/>
<SearchButton onClick={props.onSearchButtonClick}>
<SearchButton
aria-label={iconLabel}
onClick={props.onSearchButtonClick}
>
<SearchButtonIcon className={iconName}/>
</SearchButton>
</Root>
Expand Down
1 change: 1 addition & 0 deletions packages/app-desktop/gui/note-viewer/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<html>
<head id="joplin-container-root-head">
<meta charset="UTF-8">
<title>Note viewer</title>

<style>
body {
Expand Down
Loading