diff --git a/src/vs/base/browser/ui/aria/aria.css b/src/vs/base/browser/ui/aria/aria.css new file mode 100644 index 00000000000..dfd372bb2b5 --- /dev/null +++ b/src/vs/base/browser/ui/aria/aria.css @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.aria-alert-container { + position: absolute; /* try to hide from workbench but not from screen readers */ +} \ No newline at end of file diff --git a/src/vs/base/browser/ui/aria/aria.ts b/src/vs/base/browser/ui/aria/aria.ts new file mode 100644 index 00000000000..7c6294c3429 --- /dev/null +++ b/src/vs/base/browser/ui/aria/aria.ts @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./aria'; +import {Builder, $} from 'vs/base/browser/builder'; + +let ariaAlertContainer: Builder; +export function setAlertContainer(parent: HTMLElement) { + ariaAlertContainer = $('.aria-alert-container').attr({ 'role': 'alert' }).appendTo(parent); +} + +/** + * Given the provided message, will make sure that it is read as alert to screen readers. + */ +export function alert(msg: string): void { + if (!ariaAlertContainer) { + console.warn('ARIA alert support needs a container. Call setAlertContainer() first.'); + return; + } + + $(ariaAlertContainer).empty(); + $('span').text(msg).appendTo(ariaAlertContainer); +} \ No newline at end of file diff --git a/src/vs/base/browser/ui/inputbox/inputBox.ts b/src/vs/base/browser/ui/inputbox/inputBox.ts index 10132d3eb53..a153374e115 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.ts +++ b/src/vs/base/browser/ui/inputbox/inputBox.ts @@ -6,11 +6,13 @@ import 'vs/css!./inputBox'; +import nls = require('vs/nls'); import * as Bal from 'vs/base/browser/browser'; import * as dom from 'vs/base/browser/dom'; import * as browser from 'vs/base/browser/browserService'; import {IHTMLContentElement} from 'vs/base/common/htmlContent'; import {renderHtml} from 'vs/base/browser/htmlContentRenderer'; +import aria = require('vs/base/browser/ui/aria/aria'); import {IAction} from 'vs/base/common/actions'; import {ActionBar} from 'vs/base/browser/ui/actionbar/actionbar'; import {IContextViewProvider, AnchorAlignment} from 'vs/base/browser/ui/contextview/contextview'; @@ -234,6 +236,18 @@ export class InputBox extends Widget { dom.removeClass(this.element, 'error'); dom.addClass(this.element, this.classForType(message.type)); + // ARIA Support + let alertText: string; + if (message.type === MessageType.ERROR) { + alertText = nls.localize('alertErrorMessage', "Error: {0}", message.content); + } else if (message.type === MessageType.WARNING) { + alertText = nls.localize('alertWarningMessage', "Warning: {0}", message.content); + } else { + alertText = nls.localize('alertInfoMessage', "Info: {0}", message.content); + } + + aria.alert(alertText); + if (this.hasFocus() || force) { this._showMessage(); } diff --git a/src/vs/base/browser/ui/messagelist/messageList.css b/src/vs/base/browser/ui/messagelist/messageList.css index 5b6827a2d22..2567d03bd71 100644 --- a/src/vs/base/browser/ui/messagelist/messageList.css +++ b/src/vs/base/browser/ui/messagelist/messageList.css @@ -16,10 +16,6 @@ left: 15%; } -.aria-alert-container { - position: absolute; /* try to hide from workbench */ -} - .vs .global-message-list { box-shadow: 0 2px 8px #A8A8A8; } diff --git a/src/vs/base/browser/ui/messagelist/messageList.ts b/src/vs/base/browser/ui/messagelist/messageList.ts index cce9a4b6633..5c0dd01642a 100644 --- a/src/vs/base/browser/ui/messagelist/messageList.ts +++ b/src/vs/base/browser/ui/messagelist/messageList.ts @@ -11,6 +11,7 @@ import {Promise} from 'vs/base/common/winjs.base'; import {Builder, withElementById, $} from 'vs/base/browser/builder'; import DOM = require('vs/base/browser/dom'); import errors = require('vs/base/common/errors'); +import aria = require('vs/base/browser/ui/aria/aria'); import types = require('vs/base/common/types'); import Event, {Emitter} from 'vs/base/common/event'; import {Action} from 'vs/base/common/actions'; @@ -57,7 +58,6 @@ export class MessageList { private messages: IMessageEntry[]; private messageListPurger: Promise; private messageListContainer: Builder; - private ariaAlertContainer: Builder; private containerElementId: string; private options: IMessageListOptions; @@ -75,8 +75,6 @@ export class MessageList { this._onMessagesShowing = new Emitter(); this._onMessagesCleared = new Emitter(); - - this.ariaAlertContainer = $('.aria-alert-container').attr({ 'role': 'alert' }).appendTo(document.body); } public get onMessagesShowing(): Event { @@ -147,7 +145,6 @@ export class MessageList { this.renderMessages(true, 1); // Support in Screen Readers too - $(this.ariaAlertContainer).empty(); let alertText: string; if (severity === Severity.Error) { alertText = nls.localize('alertErrorMessage', "Error: {0}", message); @@ -157,7 +154,7 @@ export class MessageList { alertText = nls.localize('alertInfoMessage', "Info: {0}", message); } - $('span').text(alertText).appendTo(this.ariaAlertContainer); + aria.alert(alertText); return () => { this.hideMessage(id); diff --git a/src/vs/workbench/electron-browser/shell.ts b/src/vs/workbench/electron-browser/shell.ts index 78de96794cf..f1c8c715753 100644 --- a/src/vs/workbench/electron-browser/shell.ts +++ b/src/vs/workbench/electron-browser/shell.ts @@ -18,6 +18,7 @@ import {Promise, TPromise} from 'vs/base/common/winjs.base'; import {Dimension, Builder, $} from 'vs/base/browser/builder'; import objects = require('vs/base/common/objects'); import dom = require('vs/base/browser/dom'); +import aria = require('vs/base/browser/ui/aria/aria'); import {Emitter} from 'vs/base/common/event'; import {IDisposable} from 'vs/base/common/lifecycle'; import errors = require('vs/base/common/errors'); @@ -192,6 +193,9 @@ export class WorkbenchShell { private createContents(parent: Builder): Builder { + // ARIA + aria.setAlertContainer(document.body); + // Workbench Container let workbenchContainer = $(parent).div();