Skip to content

Commit 64d193d

Browse files
authored
Add Accessibility API (#10961)
Add AccessibilityInformation interface. Add optional accessibilityInformation to StatusBarItem and TreeItem. Proposed API: Add StatusBarItemOptions (containing accessibilityInformation). Provide window.createStatusBarItem(StatusBarItemOptions). Update createStatusBarItem handler to handle StatusBarItems. Contributed on behalf of STMicroelectronics.
1 parent a9c970d commit 64d193d

File tree

13 files changed

+130
-17
lines changed

13 files changed

+130
-17
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@
1010

1111
- [core] Move code for untitled resources into `core` from `plugin-ext` and allow users to open untitled editors with `New File` command. [#10868](https://github.com/eclipse-theia/theia/pull/10868)
1212
- [plugin] added support for `SnippetString.appendChoice` [#10969](https://github.com/eclipse-theia/theia/pull/10969) - Contributed on behalf of STMicroelectronics
13+
- [plugin] added support for `AccessibilityInformation` [#10961](https://github.com/eclipse-theia/theia/pull/10961) - Contributed on behalf of STMicroelectronics
1314

1415
<a name="breaking_changes_1.25.0">[Breaking Changes:](#breaking_changes_1.25.0)</a>
1516

16-
1717
## v1.24.0 - 3/31/2022
1818

1919
[1.24.0 Milestone](https://github.com/eclipse-theia/theia/milestone/32)

packages/core/src/browser/shell/application-shell.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1422,6 +1422,10 @@ export class ApplicationShell extends Widget {
14221422
alignment: StatusBarAlignment.RIGHT,
14231423
tooltip: 'Toggle Bottom Panel',
14241424
command: 'core.toggle.bottom.panel',
1425+
accessibilityInformation: {
1426+
label: 'Toggle Bottom Panel',
1427+
role: 'button'
1428+
},
14251429
priority: -1000
14261430
};
14271431
this.statusBar.setElement(BOTTOM_PANEL_TOGGLE_ID, element);

packages/core/src/browser/status-bar/status-bar.tsx

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { ReactWidget } from '../widgets/react-widget';
2222
import { FrontendApplicationStateService } from '../frontend-application-state';
2323
import { LabelParser, LabelIcon } from '../label-parser';
2424
import { PreferenceService } from '../preferences';
25+
import { AccessibilityInformation } from '../../common/accessibility';
2526

2627
export interface StatusBarEntry {
2728
/**
@@ -44,20 +45,14 @@ export interface StatusBarEntry {
4445
// eslint-disable-next-line @typescript-eslint/no-explicit-any
4546
arguments?: any[];
4647
priority?: number;
48+
accessibilityInformation?: AccessibilityInformation;
4749
onclick?: (e: MouseEvent) => void;
4850
}
4951

5052
export enum StatusBarAlignment {
5153
LEFT, RIGHT
5254
}
5355

54-
export interface StatusBarEntryAttributes {
55-
className?: string;
56-
title?: string;
57-
style?: object;
58-
onClick?: (e: MouseEvent) => void;
59-
}
60-
6156
export const STATUSBAR_WIDGET_FACTORY_ID = 'statusBar';
6257

6358
export const StatusBar = Symbol('StatusBar');
@@ -169,15 +164,15 @@ export class StatusBarImpl extends ReactWidget implements StatusBar {
169164
};
170165
}
171166

172-
protected createAttributes(entry: StatusBarEntry): StatusBarEntryAttributes {
173-
const attrs: StatusBarEntryAttributes = {};
167+
protected createAttributes(entry: StatusBarEntry): React.Attributes & React.HTMLAttributes<HTMLElement> {
168+
const attrs: React.Attributes & React.HTMLAttributes<HTMLElement> = {};
174169

175170
if (entry.command) {
176171
attrs.onClick = this.onclick(entry);
177172
attrs.className = 'element hasCommand';
178173
} else if (entry.onclick) {
179174
attrs.onClick = e => {
180-
if (entry.onclick) {
175+
if (entry.onclick && e instanceof MouseEvent) {
181176
entry.onclick(e);
182177
}
183178
};
@@ -189,15 +184,20 @@ export class StatusBarImpl extends ReactWidget implements StatusBar {
189184
if (entry.tooltip) {
190185
attrs.title = entry.tooltip;
191186
}
187+
if (entry.className) {
188+
attrs.className += ' ' + entry.className;
189+
}
190+
if (entry.accessibilityInformation) {
191+
attrs['aria-label'] = entry.accessibilityInformation.label;
192+
attrs.role = entry.accessibilityInformation.role;
193+
} else {
194+
attrs['aria-label'] = [entry.text, entry.tooltip].join(', ');
195+
}
192196

193197
attrs.style = {
194198
color: entry.color || this.color
195199
};
196200

197-
if (entry.className) {
198-
attrs.className += ' ' + entry.className;
199-
}
200-
201201
return attrs;
202202
}
203203

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// *****************************************************************************
2+
// Copyright (C) 2022 STMicroelectronics and others.
3+
//
4+
// This program and the accompanying materials are made available under the
5+
// terms of the Eclipse Public License v. 2.0 which is available at
6+
// http://www.eclipse.org/legal/epl-2.0.
7+
//
8+
// This Source Code may also be made available under the following Secondary
9+
// Licenses when the conditions for such availability set forth in the Eclipse
10+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
11+
// with the GNU Classpath Exception which is available at
12+
// https://www.gnu.org/software/classpath/license.html.
13+
//
14+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15+
// *****************************************************************************
16+
17+
/**
18+
* Accessibility information which controls screen reader behavior.
19+
*/
20+
export interface AccessibilityInformation {
21+
/**
22+
* Label to be read out by a screen reader once the item has focus.
23+
*/
24+
readonly label: string;
25+
26+
/**
27+
* Role of the widget which defines how a screen reader interacts with it.
28+
* The role should be set in special cases when for example a tree-like element behaves like a checkbox.
29+
* If role is not specified the editor will pick the appropriate role automatically.
30+
* More about aria roles can be found here https://w3c.github.io/aria/#widget_roles
31+
*/
32+
readonly role?: string;
33+
}

packages/messages/src/browser/notifications-contribution.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,10 @@ export class NotificationsContribution implements FrontendApplicationContributio
5555
alignment: StatusBarAlignment.RIGHT,
5656
priority: -900,
5757
command: NotificationsCommands.TOGGLE.id,
58-
tooltip: this.getStatusBarItemTooltip(count)
58+
tooltip: this.getStatusBarItemTooltip(count),
59+
accessibilityInformation: {
60+
label: this.getStatusBarItemTooltip(count)
61+
}
5962
});
6063
}
6164
protected getStatusBarItemText(count: number): string {

packages/plugin-ext/src/common/plugin-api-rpc.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,7 @@ export interface StatusBarMessageRegistryMain {
407407
color: string | undefined,
408408
tooltip: string | undefined,
409409
command: string | undefined,
410+
accessibilityInformation: theia.AccessibilityInformation,
410411
args: any[] | undefined): PromiseLike<void>;
411412
$dispose(id: string): void;
412413
}
@@ -713,6 +714,8 @@ export interface TreeViewItem {
713714

714715
command?: Command;
715716

717+
accessibilityInformation?: theia.AccessibilityInformation;
718+
716719
}
717720

718721
export interface TreeViewSelection {

packages/plugin-ext/src/main/browser/status-bar-message-registry-main.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,22 @@ export class StatusBarMessageRegistryMainImpl implements StatusBarMessageRegistr
4545
color: string | undefined,
4646
tooltip: string | undefined,
4747
command: string | undefined,
48+
accessibilityInformation: types.AccessibilityInformation,
4849
// eslint-disable-next-line @typescript-eslint/no-explicit-any
4950
args: any[] | undefined): Promise<void> {
51+
const ariaLabel = accessibilityInformation?.label;
52+
const role = accessibilityInformation?.role;
53+
5054
const entry = {
5155
text: text || '',
56+
ariaLabel,
57+
role,
5258
priority,
5359
alignment: alignment === types.StatusBarAlignment.Left ? StatusBarAlignment.LEFT : StatusBarAlignment.RIGHT,
5460
color: color && (this.colorRegistry.getCurrentColor(color) || color),
5561
tooltip,
5662
command,
63+
accessibilityInformation,
5764
args
5865
};
5966

packages/plugin-ext/src/main/browser/view/tree-view-widget.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import { ContextKeyService } from '@theia/core/lib/browser/context-key-service';
4545
import * as markdownit from '@theia/core/shared/markdown-it';
4646
import { isMarkdownString } from '../../../plugin/markdown-string';
4747
import { LabelParser } from '@theia/core/lib/browser/label-parser';
48+
import { AccessibilityInformation } from '@theia/plugin';
4849

4950
export const TREE_NODE_HYPERLINK = 'theia-TreeNodeHyperlink';
5051
export const VIEW_ITEM_CONTEXT_MENU: MenuPath = ['view-item-context-menu'];
@@ -63,6 +64,7 @@ export interface TreeViewNode extends SelectableTreeNode {
6364
tooltip?: string;
6465
// eslint-disable-next-line @typescript-eslint/no-explicit-any
6566
description?: string | boolean | any;
67+
accessibilityInformation?: AccessibilityInformation;
6668
}
6769
export namespace TreeViewNode {
6870
export function is(arg: TreeNode | undefined): arg is TreeViewNode {
@@ -159,6 +161,7 @@ export class PluginTree extends TreeImpl {
159161
tooltip: item.tooltip,
160162
contextValue: item.contextValue,
161163
command: item.command,
164+
accessibilityInformation: item.accessibilityInformation,
162165
};
163166
const node = this.getNode(item.id);
164167
if (item.collapsibleState !== undefined && item.collapsibleState !== TreeViewItemCollapsibleState.None) {
@@ -301,6 +304,14 @@ export class TreeViewWidget extends TreeViewWelcomeWidget {
301304
id: node.id
302305
};
303306

307+
if (node.accessibilityInformation) {
308+
attrs = {
309+
...attrs,
310+
'aria-label': node.accessibilityInformation.label,
311+
'role': node.accessibilityInformation.role
312+
};
313+
}
314+
304315
if (node.tooltip && isMarkdownString(node.tooltip)) {
305316
// Render markdown in custom tooltip
306317
const tooltip = this.markdownIt.render(node.tooltip.value);

packages/plugin-ext/src/plugin/status-bar/status-bar-item.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export class StatusBarItemImpl implements theia.StatusBarItem {
2929
private _tooltip: string;
3030
private _color: string | ThemeColor;
3131
private _command: string | theia.Command;
32+
private _accessibilityInformation: theia.AccessibilityInformation;
3233

3334
private _isVisible: boolean;
3435
private _timeoutHandle: NodeJS.Timer | undefined;
@@ -73,6 +74,10 @@ export class StatusBarItemImpl implements theia.StatusBarItem {
7374
return this._command;
7475
}
7576

77+
public get accessibilityInformation(): theia.AccessibilityInformation {
78+
return this._accessibilityInformation;
79+
}
80+
7681
public set text(text: string) {
7782
this._text = text;
7883
this.update();
@@ -93,6 +98,11 @@ export class StatusBarItemImpl implements theia.StatusBarItem {
9398
this.update();
9499
}
95100

101+
public set accessibilityInformation(information: theia.AccessibilityInformation) {
102+
this._accessibilityInformation = information;
103+
this.update();
104+
}
105+
96106
public show(): void {
97107
this._isVisible = true;
98108
this.update();
@@ -126,6 +136,7 @@ export class StatusBarItemImpl implements theia.StatusBarItem {
126136
typeof this.color === 'string' ? this.color : this.color && this.color.id,
127137
this.tooltip,
128138
commandId,
139+
this.accessibilityInformation,
129140
args);
130141
}, 0);
131142
}

packages/plugin-ext/src/plugin/tree/tree-views.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,8 @@ class TreeViewExtImpl<T> implements Disposable {
386386
tooltip: treeItem.tooltip,
387387
collapsibleState: treeItem.collapsibleState,
388388
contextValue: treeItem.contextValue,
389-
command: this.commandsConverter.toSafeCommand(treeItem.command, toDisposeElement)
389+
command: this.commandsConverter.toSafeCommand(treeItem.command, toDisposeElement),
390+
accessibilityInformation: treeItem.accessibilityInformation
390391
} as TreeViewItem;
391392

392393
return treeViewItem;

0 commit comments

Comments
 (0)