diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index a7e032c8f28..76a988cb508 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -199,7 +199,12 @@ "alignment": "right", "priority": 17, "name": "My Static Item", - "text": "Hello $(globe)" + "text": "Hello $(globe)", + "tooltip": "Hover World", + "accessibilityInformation": { + "label": "Hello World", + "role": "button" + } } }, "scripts": { diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts index f5111386da4..2a9fe97c144 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/window.test.ts @@ -1045,6 +1045,8 @@ suite('vscode API - window', () => { assert.strictEqual(item.priority, 17); assert.strictEqual(item.name, 'My Static Item'); assert.strictEqual(item.text, 'Hello $(globe)'); + assert.strictEqual(item.tooltip, 'Hover World'); + assert.deepStrictEqual(item.accessibilityInformation, { label: 'Hello World', role: 'button' }); item.dispose(); }); diff --git a/src/vs/platform/accessibility/common/accessibility.ts b/src/vs/platform/accessibility/common/accessibility.ts index e156660fece..f7325c2caa9 100644 --- a/src/vs/platform/accessibility/common/accessibility.ts +++ b/src/vs/platform/accessibility/common/accessibility.ts @@ -40,3 +40,9 @@ export interface IAccessibilityInformation { label: string; role?: string; } + +export function isAccessibilityInformation(obj: any): obj is IAccessibilityInformation { + return obj && typeof obj === 'object' + && typeof obj.label === 'string' + && (typeof obj.role === 'undefined' || typeof obj.role === 'string'); +} diff --git a/src/vs/workbench/api/browser/mainThreadStatusBar.ts b/src/vs/workbench/api/browser/mainThreadStatusBar.ts index bb851587960..289bde01e57 100644 --- a/src/vs/workbench/api/browser/mainThreadStatusBar.ts +++ b/src/vs/workbench/api/browser/mainThreadStatusBar.ts @@ -43,9 +43,11 @@ export class MainThreadStatusBar implements MainThreadStatusBarShape { entryId, name: item.entry.name, text: item.entry.text, + tooltip: item.entry.tooltip as string | undefined, command: typeof item.entry.command === 'string' ? item.entry.command : typeof item.entry.command === 'object' ? item.entry.command.id : undefined, priority: item.priority, - alignLeft: item.alignment === StatusbarAlignment.LEFT + alignLeft: item.alignment === StatusbarAlignment.LEFT, + accessibilityInformation: item.entry.ariaLabel ? { label: item.entry.ariaLabel, role: item.entry.role } : undefined }; } } diff --git a/src/vs/workbench/api/browser/statusBarExtensionPoint.ts b/src/vs/workbench/api/browser/statusBarExtensionPoint.ts index 2b7c3c90d07..2ba47f8425e 100644 --- a/src/vs/workbench/api/browser/statusBarExtensionPoint.ts +++ b/src/vs/workbench/api/browser/statusBarExtensionPoint.ts @@ -12,7 +12,7 @@ import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/exte import { IStatusbarService, StatusbarAlignment as MainThreadStatusBarAlignment, IStatusbarEntryAccessor, IStatusbarEntry, StatusbarAlignment, IStatusbarEntryPriority } from 'vs/workbench/services/statusbar/browser/statusbar'; import { ThemeColor } from 'vs/base/common/themables'; import { Command } from 'vs/editor/common/languages'; -import { IAccessibilityInformation } from 'vs/platform/accessibility/common/accessibility'; +import { IAccessibilityInformation, isAccessibilityInformation } from 'vs/platform/accessibility/common/accessibility'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { getCodiconAriaLabel } from 'vs/base/common/iconLabels'; import { hash } from 'vs/base/common/hash'; @@ -157,15 +157,21 @@ interface IUserFriendlyStatusItemEntry { alignment: 'left' | 'right'; command?: string; priority?: number; + tooltip?: string; + accessibilityInformation?: IAccessibilityInformation; } -function isUserFriendlyStatusItemEntry(obj: any): obj is IUserFriendlyStatusItemEntry { +function isUserFriendlyStatusItemEntry(candidate: any): candidate is IUserFriendlyStatusItemEntry { + const obj = candidate as IUserFriendlyStatusItemEntry; return (typeof obj.id === 'string' && obj.id.length > 0) && typeof obj.name === 'string' && typeof obj.text === 'string' && (obj.alignment === 'left' || obj.alignment === 'right') && (obj.command === undefined || typeof obj.command === 'string') - && (obj.priority === undefined || typeof obj.priority === 'number'); + && (obj.tooltip === undefined || typeof obj.tooltip === 'string') + && (obj.priority === undefined || typeof obj.priority === 'number') + && (obj.accessibilityInformation === undefined || isAccessibilityInformation(obj.accessibilityInformation)) + ; } const statusBarItemSchema: IJSONSchema = { @@ -184,6 +190,10 @@ const statusBarItemSchema: IJSONSchema = { type: 'string', description: localize('text', 'The text to show for the entry. You can embed icons in the text by leveraging the `$()`-syntax, like \'Hello $(globe)!\'') }, + tooltip: { + type: 'string', + description: localize('tooltip', 'The tooltip text for the entry.') + }, command: { type: 'string', description: localize('command', 'The command to execute when the status bar entry is clicked.') @@ -196,6 +206,20 @@ const statusBarItemSchema: IJSONSchema = { priority: { type: 'number', description: localize('priority', 'The priority of the status bar entry. Higher value means the item should be shown more to the left.') + }, + accessibilityInformation: { + type: 'object', + description: localize('accessibilityInformation', 'Defines the role and aria label to be used when the status bar entry is focused.'), + properties: { + role: { + type: 'string', + description: localize('accessibilityInformation.role', 'The role of the status bar entry which defines how a screen reader interacts with it. More about aria roles can be found here https://w3c.github.io/aria/#widget_roles') + }, + label: { + type: 'string', + description: localize('accessibilityInformation.label', 'The aria label of the status bar entry. Defaults to the entry\'s text.') + } + } } } }; @@ -249,12 +273,12 @@ export class StatusBarItemsExtensionPoint { ExtensionIdentifier.toKey(entry.description.identifier), candidate.name ?? entry.description.displayName ?? entry.description.name, candidate.text, - undefined, + candidate.tooltip, candidate.command ? { id: candidate.command, title: candidate.name } : undefined, undefined, undefined, candidate.alignment === 'left', candidate.priority, - undefined + candidate.accessibilityInformation ); contributions.add(toDisposable(() => statusBarItemsService.unsetEntry(fullItemId))); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index aa6631d5e9e..601f5ca4eb9 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -624,7 +624,9 @@ export type StatusBarItemDto = { priority?: number; name: string; text: string; + tooltip?: string; command?: string; + accessibilityInformation?: IAccessibilityInformation; }; export interface ExtHostStatusBarShape { diff --git a/src/vs/workbench/api/common/extHostStatusBar.ts b/src/vs/workbench/api/common/extHostStatusBar.ts index 4325a9da347..a4864e16eae 100644 --- a/src/vs/workbench/api/common/extHostStatusBar.ts +++ b/src/vs/workbench/api/common/extHostStatusBar.ts @@ -71,7 +71,9 @@ export class ExtHostStatusBarEntry implements vscode.StatusBarItem { this._visible = true; this.name = item.name; this.text = item.text; + this.tooltip = item.tooltip; this.command = item.command; + this.accessibilityInformation = item.accessibilityInformation; } } else { this._entryId = String(ExtHostStatusBarEntry.ID_GEN++);