From c5132045016e272b81cba8f3be1bfd7fe1e13792 Mon Sep 17 00:00:00 2001 From: Udeesha Gautam <46980425+udeeshagautam@users.noreply.github.com> Date: Tue, 20 Aug 2019 10:11:34 -0700 Subject: [PATCH] Bug/accessibility 1 (#6774) * fixing simple name changes * Fixing button color and tabbing on tabs * removing some extra lines of code * Adding some null checks * Updating as per PR comments --- .../theme-carbon/themes/dark_carbon.json | 4 +- .../theme-carbon/themes/light_carbon.json | 4 +- src/sql/base/browser/ui/panel/panel.ts | 54 +++++++++++++++++-- .../parts/backup/browser/backup.component.ts | 1 + .../parts/restore/browser/restoreDialog.ts | 9 ++-- .../fileBrowser/browser/fileBrowserDialog.ts | 3 +- 6 files changed, 61 insertions(+), 14 deletions(-) diff --git a/extensions/theme-carbon/themes/dark_carbon.json b/extensions/theme-carbon/themes/dark_carbon.json index a7d421b0d7..719d4cd9b4 100644 --- a/extensions/theme-carbon/themes/dark_carbon.json +++ b/extensions/theme-carbon/themes/dark_carbon.json @@ -14,7 +14,7 @@ "textLinkActiveForeground": "#30b4ff", //Button control - "button.background": "#0078d7cc", + "button.background": "#0E639C", "button.foreground": "#ffffff", "button.hoverBackground": "#0078d7", @@ -530,4 +530,4 @@ } } ] -} \ No newline at end of file +} diff --git a/extensions/theme-carbon/themes/light_carbon.json b/extensions/theme-carbon/themes/light_carbon.json index 86c838cbdc..d32d54a36a 100644 --- a/extensions/theme-carbon/themes/light_carbon.json +++ b/extensions/theme-carbon/themes/light_carbon.json @@ -14,7 +14,7 @@ "textLinkActiveForeground": "#3062d6", //Button control - "button.background": "#0078d7cc", + "button.background": "#007ACC", "button.foreground": "#ffffff", "button.hoverBackground": "#0078d7", @@ -562,4 +562,4 @@ } } ] -} \ No newline at end of file +} diff --git a/src/sql/base/browser/ui/panel/panel.ts b/src/sql/base/browser/ui/panel/panel.ts index aa7eaf865d..f55383e9d6 100644 --- a/src/sql/base/browser/ui/panel/panel.ts +++ b/src/sql/base/browser/ui/panel/panel.ts @@ -10,7 +10,7 @@ import * as DOM from 'vs/base/browser/dom'; import { IAction } from 'vs/base/common/actions'; import { IActionOptions, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { KeyCode } from 'vs/base/common/keyCodes'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Color } from 'vs/base/common/color'; import { isUndefinedOrNull } from 'vs/base/common/types'; @@ -77,6 +77,7 @@ export class TabbedPanel extends Disposable { public onTabChange: Event = this._onTabChange.event; private tabHistory: string[] = []; + private _tabOrder: PanelTabIdentifier[] = []; constructor(container: HTMLElement, private options: IPanelOptions = defaultOptions) { super(); @@ -84,9 +85,9 @@ export class TabbedPanel extends Disposable { this._styleElement = DOM.createStyleSheet(this.parent); container.appendChild(this.parent); this.header = DOM.$('.composite.title'); + this.header.setAttribute('tabindex', '0'); this.tabList = DOM.$('.tabList'); this.tabList.setAttribute('role', 'tablist'); - this.tabList.setAttribute('tabindex', '0'); this.tabList.style.height = this.headersize + 'px'; this.header.appendChild(this.tabList); let actionbarcontainer = DOM.$('.title-actions'); @@ -101,6 +102,7 @@ export class TabbedPanel extends Disposable { this.body = DOM.$('.tabBody'); this.body.setAttribute('role', 'tabpanel'); this.parent.appendChild(this.body); + this._register(DOM.addDisposableListener(this.header, DOM.EventType.FOCUS, e => this.focusCurrentTab())); } public dispose() { @@ -142,7 +144,7 @@ export class TabbedPanel extends Disposable { private _createTab(tab: IInternalPanelTab, index?: number): void { let tabHeaderElement = DOM.$('.tab-header'); - tabHeaderElement.setAttribute('tabindex', '0'); + tabHeaderElement.setAttribute('tabindex', '-1'); tabHeaderElement.setAttribute('role', 'tab'); tabHeaderElement.setAttribute('aria-selected', 'false'); tabHeaderElement.setAttribute('aria-controls', tab.tab.identifier); @@ -161,19 +163,40 @@ export class TabbedPanel extends Disposable { invokeTabSelectedHandler(); })); - tab.disposables.push(DOM.addDisposableListener(tabHeaderElement, DOM.EventType.KEY_UP, (e: KeyboardEvent) => { + tab.disposables.push(DOM.addDisposableListener(tabHeaderElement, DOM.EventType.KEY_DOWN, (e: KeyboardEvent) => { let event = new StandardKeyboardEvent(e); if (event.equals(KeyCode.Enter)) { this.showTab(tab.tab.identifier); invokeTabSelectedHandler(); e.stopImmediatePropagation(); } + if (event.equals(KeyCode.RightArrow)) { + let currentIndex = this._tabOrder.findIndex(x => x === tab.tab.identifier); + this.focusNextTab(currentIndex + 1); + } + if (event.equals(KeyCode.LeftArrow)) { + let currentIndex = this._tabOrder.findIndex(x => x === tab.tab.identifier); + this.focusNextTab(currentIndex - 1); + } + if (event.equals(KeyCode.Tab)) { + e.preventDefault(); + if (this._shownTabId) { + const shownTab = this._tabMap.get(this._shownTabId); + if (shownTab) { + shownTab.tab.view.focus(); + } + } + } })); + const insertBefore = !isUndefinedOrNull(index) ? this.tabList.children.item(index) : undefined; - if (insertBefore) { + if (insertBefore && index) { + this._tabOrder.copyWithin(index + 1, index); + this._tabOrder[index] = tab.tab.identifier; this.tabList.insertBefore(tabHeaderElement, insertBefore); } else { this.tabList.append(tabHeaderElement); + this._tabOrder.push(tab.tab.identifier); } tab.header = tabHeaderElement; tab.label = tabLabel; @@ -244,6 +267,8 @@ export class TabbedPanel extends Disposable { } dispose(actualTab.disposables); this._tabMap.delete(tab); + let index = this._tabOrder.findIndex(t => t === tab); + this._tabOrder.splice(index, 1); if (this._shownTabId === tab) { this._shownTabId = undefined; while (this._shownTabId === undefined && this.tabHistory.length > 0) { @@ -266,6 +291,25 @@ export class TabbedPanel extends Disposable { } } + private focusNextTab(index: number): void { + if (index < 0 || index > this.tabList.children.length) { + return; + } + let tab = (this.tabList.children[index]); + if (tab) { + tab.focus(); + } + } + + private focusCurrentTab(): void { + if (this._shownTabId) { + const tab = this._tabMap.get(this._shownTabId); + if (tab) { + tab.header.focus(); + } + } + } + public style(styles: ITabbedPanelStyles): void { const content: string[] = []; diff --git a/src/sql/workbench/parts/backup/browser/backup.component.ts b/src/sql/workbench/parts/backup/browser/backup.component.ts index f5b171fe34..0195bd93c1 100644 --- a/src/sql/workbench/parts/backup/browser/backup.component.ts +++ b/src/sql/workbench/parts/backup/browser/backup.component.ts @@ -271,6 +271,7 @@ export class BackupComponent { // Set backup path list this.pathListBox = new ListBox([], this.contextViewService); + this.pathListBox.setAriaLabel(LocalizedStrings.BACKUP_DEVICE); this.pathListBox.onKeyDown(e => { if (this.pathListBox.selectedOptions.length > 0) { const key = e.keyCode; diff --git a/src/sql/workbench/parts/restore/browser/restoreDialog.ts b/src/sql/workbench/parts/restore/browser/restoreDialog.ts index 9fa45c9af8..cd0d4fcf37 100644 --- a/src/sql/workbench/parts/restore/browser/restoreDialog.ts +++ b/src/sql/workbench/parts/restore/browser/restoreDialog.ts @@ -63,7 +63,7 @@ export class RestoreDialog extends Modal { private _scriptButton: Button; private _restoreButton: Button; private _closeButton: Button; - private _optionsMap: { [name: string]: Widget } = {}; + private _optionsMap: { [name: string]: SelectBox | InputBox | Checkbox } = {}; private _restoreLabel: string; private _restoreTitle: string; private _databaseTitle: string; @@ -250,6 +250,7 @@ export class RestoreDialog extends Modal { this._restorePlanData = new TableDataView(); this._restorePlanTable = new Table(this._restorePlanTableContainer, { dataProvider: this._restorePlanData, columns: this._restorePlanColumn }, { enableColumnReorder: false }); + this._restorePlanTable.setTableTitle(localize('restorePlan', "Restore plan")); this._restorePlanTable.setSelectionModel(new RowSelectionModel({ selectActiveRow: false })); this._restorePlanTable.onSelectedRowsChanged((e, data) => this.backupFileCheckboxChanged(e, data)); @@ -339,7 +340,7 @@ export class RestoreDialog extends Modal { DOM.append(c, generalTab); }, layout: () => { }, - focus: () => generalTab.focus() + focus: () => this._restoreFromSelectBox ? this._restoreFromSelectBox.focus() : generalTab.focus() } }); @@ -351,7 +352,7 @@ export class RestoreDialog extends Modal { render: c => { c.appendChild(fileContentElement); }, - focus: () => fileContentElement.focus() + focus: () => this._optionsMap[this._relocateDatabaseFilesOption] ? this._optionsMap[this._relocateDatabaseFilesOption].focus() : fileContentElement.focus() } }); @@ -363,7 +364,7 @@ export class RestoreDialog extends Modal { render: c => { c.appendChild(optionsContentElement); }, - focus: () => optionsContentElement.focus() + focus: () => this._optionsMap[this._withReplaceDatabaseOption] ? this._optionsMap[this._withReplaceDatabaseOption].focus() : optionsContentElement.focus() } }); diff --git a/src/sql/workbench/services/fileBrowser/browser/fileBrowserDialog.ts b/src/sql/workbench/services/fileBrowser/browser/fileBrowserDialog.ts index af6caa82b3..1cda6b5353 100644 --- a/src/sql/workbench/services/fileBrowser/browser/fileBrowserDialog.ts +++ b/src/sql/workbench/services/fileBrowser/browser/fileBrowserDialog.ts @@ -96,8 +96,9 @@ export class FileBrowserDialog extends Modal { ariaLabel: pathLabel }); - this._fileFilterSelectBox = new SelectBox(['*'], '*', this._contextViewService); let filterLabel = localize('fileFilter', "Files of type"); + this._fileFilterSelectBox = new SelectBox(['*'], '*', this._contextViewService); + this._fileFilterSelectBox.setAriaLabel(filterLabel); let filterBuilder = DialogHelper.appendRow(tableContainer, filterLabel, 'file-input-label', 'file-input-box'); DialogHelper.appendInputSelectBox(filterBuilder, this._fileFilterSelectBox);