accessibility setting based select database dropdown (#2579)

This commit is contained in:
Alan Ren
2018-09-13 15:07:08 -07:00
committed by Karl Burtram
parent 87946996ed
commit b03c0a3e2d
5 changed files with 115 additions and 37 deletions

View File

@@ -0,0 +1,5 @@
.monaco-select-box {
cursor: pointer;
min-width: 150px;
padding: 2px;
}

View File

@@ -43,7 +43,6 @@ import {
import { IQueryModelService } from 'sql/parts/query/execution/queryModel'; import { IQueryModelService } from 'sql/parts/query/execution/queryModel';
import { IEditorDescriptorService } from 'sql/parts/query/editor/editorDescriptorService'; import { IEditorDescriptorService } from 'sql/parts/query/editor/editorDescriptorService';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement'; import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import { attachEditableDropdownStyler } from 'sql/common/theme/styler';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { CancellationToken } from 'vs/base/common/cancellation'; import { CancellationToken } from 'vs/base/common/cancellation';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
@@ -500,7 +499,7 @@ export class QueryEditor extends BaseEditor {
public get listDatabasesActionItem(): ListDatabasesActionItem { public get listDatabasesActionItem(): ListDatabasesActionItem {
if (!this._listDatabasesActionItem) { if (!this._listDatabasesActionItem) {
this._listDatabasesActionItem = this._instantiationService.createInstance(ListDatabasesActionItem, this, this._listDatabasesAction); this._listDatabasesActionItem = this._instantiationService.createInstance(ListDatabasesActionItem, this, this._listDatabasesAction);
this._register(attachEditableDropdownStyler(this._listDatabasesActionItem, this.themeService)); this._register(this._listDatabasesActionItem.attachStyler(this.themeService));
} }
return this._listDatabasesActionItem; return this._listDatabasesActionItem;
} }

View File

@@ -3,6 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information. * Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import 'vs/css!sql/parts/query/editor/media/queryActions';
import * as nls from 'vs/nls'; import * as nls from 'vs/nls';
import { Builder, $ } from 'vs/base/browser/builder'; import { Builder, $ } from 'vs/base/browser/builder';
import { Dropdown } from 'sql/base/browser/ui/editableDropdown/dropdown'; import { Dropdown } from 'sql/base/browser/ui/editableDropdown/dropdown';
@@ -12,6 +13,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base'; import { TPromise } from 'vs/base/common/winjs.base';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IThemeService } from 'vs/platform/theme/common/themeService';
import { attachEditableDropdownStyler, attachSelectBoxStyler } from 'sql/common/theme/styler';
import { ISelectionData } from 'sqlops'; import { ISelectionData } from 'sqlops';
import { import {
@@ -25,6 +27,8 @@ import { QueryEditor } from 'sql/parts/query/editor/queryEditor';
import { IQueryModelService } from 'sql/parts/query/execution/queryModel'; import { IQueryModelService } from 'sql/parts/query/execution/queryModel';
import { INotificationService } from 'vs/platform/notification/common/notification'; import { INotificationService } from 'vs/platform/notification/common/notification';
import Severity from 'vs/base/common/severity'; import Severity from 'vs/base/common/severity';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
/** /**
* Action class that query-based Actions will extend. This base class automatically handles activating and * Action class that query-based Actions will extend. This base class automatically handles activating and
@@ -431,6 +435,9 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
private _isConnected: boolean; private _isConnected: boolean;
private $databaseListDropdown: Builder; private $databaseListDropdown: Builder;
private _dropdown: Dropdown; private _dropdown: Dropdown;
private _databaseSelectBox: SelectBox;
private _isInAccessibilityMode: boolean;
private readonly _selectDatabaseString: string = nls.localize("selectDatabase", "Select Database");
// CONSTRUCTOR ///////////////////////////////////////////////////////// // CONSTRUCTOR /////////////////////////////////////////////////////////
constructor( constructor(
@@ -439,23 +446,33 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService, @IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
@INotificationService private _notificationService: INotificationService, @INotificationService private _notificationService: INotificationService,
@IContextViewService contextViewProvider: IContextViewService, @IContextViewService contextViewProvider: IContextViewService,
@IThemeService themeService: IThemeService @IThemeService themeService: IThemeService,
@IConfigurationService private readonly _configurationService: IConfigurationService
) { ) {
super(); super();
this._toDispose = []; this._toDispose = [];
this.$databaseListDropdown = $('.databaseListDropdown'); this.$databaseListDropdown = $('.databaseListDropdown');
let selectString = nls.localize("selectDatabase", "Select Database"); this._isInAccessibilityMode = this._configurationService.getValue('editor.accessibilitySupport') === 'on';
this._dropdown = new Dropdown(this.$databaseListDropdown.getHTMLElement(), contextViewProvider, themeService, {
strictSelection: true, if (this._isInAccessibilityMode) {
placeholder: selectString, this._databaseSelectBox = new SelectBox([this._selectDatabaseString], this._selectDatabaseString, contextViewProvider, undefined, { ariaLabel: this._selectDatabaseString });
ariaLabel: selectString, this._databaseSelectBox.render(this.$databaseListDropdown.getHTMLElement());
actionLabel: nls.localize('listDatabases.toggleDatabaseNameDropdown', 'Select Database Toggle Dropdown') this._databaseSelectBox.onDidSelect(e => { this.databaseSelected(e.selected); });
}); this._databaseSelectBox.disable();
this._dropdown.onValueChange(s => this.databaseSelected(s));
} else {
this._dropdown = new Dropdown(this.$databaseListDropdown.getHTMLElement(), contextViewProvider, themeService, {
strictSelection: true,
placeholder: this._selectDatabaseString,
ariaLabel: this._selectDatabaseString,
actionLabel: nls.localize('listDatabases.toggleDatabaseNameDropdown', 'Select Database Toggle Dropdown')
});
this._dropdown.onValueChange(s => this.databaseSelected(s));
this._toDispose.push(this._dropdown.onFocus(() => { self.onDropdownFocus(); }));
}
// Register event handlers // Register event handlers
let self = this; let self = this;
this._toDispose.push(this._dropdown.onFocus(() => { self.onDropdownFocus(); }));
this._toDispose.push(this._connectionManagementService.onConnectionChanged(params => { self.onConnectionChanged(params); })); this._toDispose.push(this._connectionManagementService.onConnectionChanged(params => { self.onConnectionChanged(params); }));
} }
@@ -465,7 +482,12 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
} }
public style(styles) { public style(styles) {
this._dropdown.style(styles); if (this._isInAccessibilityMode) {
this._databaseSelectBox.style(styles);
}
else {
this._dropdown.style(styles);
}
} }
public setActionContext(context: any): void { public setActionContext(context: any): void {
@@ -477,11 +499,27 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
} }
public focus(): void { public focus(): void {
this._dropdown.focus(); if (this._isInAccessibilityMode) {
this._databaseSelectBox.focus();
} else {
this._dropdown.focus();
}
} }
public blur(): void { public blur(): void {
this._dropdown.blur(); if (this._isInAccessibilityMode) {
this._databaseSelectBox.blur();
} else {
this._dropdown.blur();
}
}
public attachStyler(themeService: IThemeService): IDisposable {
if (this._isInAccessibilityMode) {
return attachSelectBoxStyler(this, themeService);
} else {
return attachEditableDropdownStyler(this, themeService);
}
} }
public dispose(): void { public dispose(): void {
@@ -496,9 +534,15 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
public onDisconnect(): void { public onDisconnect(): void {
this._isConnected = false; this._isConnected = false;
this._dropdown.enabled = false;
this._currentDatabaseName = undefined; this._currentDatabaseName = undefined;
this._dropdown.value = '';
if (this._isInAccessibilityMode) {
this._databaseSelectBox.disable();
this._databaseSelectBox.setOptions([this._selectDatabaseString]);
} else {
this._dropdown.enabled = false;
this._dropdown.value = '';
}
} }
// PRIVATE HELPERS ///////////////////////////////////////////////////// // PRIVATE HELPERS /////////////////////////////////////////////////////
@@ -515,22 +559,22 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
this._connectionManagementService.changeDatabase(this._editor.uri, dbName) this._connectionManagementService.changeDatabase(this._editor.uri, dbName)
.then( .then(
result => { result => {
if (!result) { if (!result) {
this.resetDatabaseName();
this._notificationService.notify({
severity: Severity.Error,
message: nls.localize('changeDatabase.failed', "Failed to change database")
});
}
},
error => {
this.resetDatabaseName(); this.resetDatabaseName();
this._notificationService.notify({ this._notificationService.notify({
severity: Severity.Error, severity: Severity.Error,
message: nls.localize('changeDatabase.failed', "Failed to change database") message: nls.localize('changeDatabase.failedWithError', "Failed to change database {0}", error)
}); });
}
},
error => {
this.resetDatabaseName();
this._notificationService.notify({
severity: Severity.Error,
message: nls.localize('changeDatabase.failedWithError', "Failed to change database {0}", error)
}); });
});
} }
private getCurrentDatabaseName() { private getCurrentDatabaseName() {
@@ -545,7 +589,11 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
} }
private resetDatabaseName() { private resetDatabaseName() {
this._dropdown.value = this.getCurrentDatabaseName(); if (this._isInAccessibilityMode) {
this._databaseSelectBox.selectWithOptionName(this.getCurrentDatabaseName());
} else {
this._dropdown.value = this.getCurrentDatabaseName();
}
} }
private onConnectionChanged(connParams: IConnectionParams): void { private onConnectionChanged(connParams: IConnectionParams): void {
@@ -579,9 +627,26 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
private updateConnection(databaseName: string) { private updateConnection(databaseName: string) {
this._isConnected = true; this._isConnected = true;
this._dropdown.enabled = true;
this._currentDatabaseName = databaseName; this._currentDatabaseName = databaseName;
this._dropdown.value = databaseName;
if (this._isInAccessibilityMode) {
this._databaseSelectBox.enable();
let self = this;
let uri = self._editor.connectedUri;
if (!uri) {
return;
}
self._connectionManagementService.listDatabases(uri)
.then(result => {
if (result && result.databaseNames) {
this._databaseSelectBox.setOptions(result.databaseNames);
}
this._databaseSelectBox.selectWithOptionName(databaseName);
});
} else {
this._dropdown.enabled = true;
this._dropdown.value = databaseName;
}
} }
// TESTING PROPERTIES ////////////////////////////////////////////////// // TESTING PROPERTIES //////////////////////////////////////////////////

View File

@@ -28,6 +28,7 @@ import { ConnectionManagementService } from 'sql/parts/connection/common/connect
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces'; import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { TestThemeService } from 'sqltest/stubs/themeTestService'; import { TestThemeService } from 'sqltest/stubs/themeTestService';
import { ConfigurationService } from 'vs/platform/configuration/node/configurationService';
import * as TypeMoq from 'typemoq'; import * as TypeMoq from 'typemoq';
import * as assert from 'assert'; import * as assert from 'assert';
@@ -40,6 +41,7 @@ suite('SQL QueryAction Tests', () => {
let editor: TypeMoq.Mock<QueryEditor>; let editor: TypeMoq.Mock<QueryEditor>;
let calledRunQueryOnInput: boolean = undefined; let calledRunQueryOnInput: boolean = undefined;
let testQueryInput: TypeMoq.Mock<QueryInput>; let testQueryInput: TypeMoq.Mock<QueryInput>;
let configurationService: TypeMoq.Mock<ConfigurationService>;
setup(() => { setup(() => {
// Setup a reusable mock QueryInput // Setup a reusable mock QueryInput
@@ -56,6 +58,13 @@ suite('SQL QueryAction Tests', () => {
editor.setup(x => x.getSelection()).returns(() => undefined); editor.setup(x => x.getSelection()).returns(() => undefined);
editor.setup(x => x.getSelection(false)).returns(() => undefined); editor.setup(x => x.getSelection(false)).returns(() => undefined);
editor.setup(x => x.isSelectionEmpty()).returns(() => false); editor.setup(x => x.isSelectionEmpty()).returns(() => false);
configurationService = TypeMoq.Mock.ofInstance({
getValue: () => undefined,
onDidChangeConfiguration: () => undefined
} as any);
configurationService.setup(x => x.getValue(TypeMoq.It.isAny())).returns(() => {
return {};
});
}); });
test('setClass sets child CSS class correctly', (done) => { test('setClass sets child CSS class correctly', (done) => {
@@ -463,7 +472,7 @@ suite('SQL QueryAction Tests', () => {
}); });
// If I query without having initialized anything, state should be clear // If I query without having initialized anything, state should be clear
listItem = new ListDatabasesActionItem(editor.object, undefined, connectionManagementService.object, undefined, undefined, undefined); listItem = new ListDatabasesActionItem(editor.object, undefined, connectionManagementService.object, undefined, undefined, undefined, configurationService.object);
assert.equal(listItem.isEnabled(), false, 'do not expect dropdown enabled unless connected'); assert.equal(listItem.isEnabled(), false, 'do not expect dropdown enabled unless connected');
assert.equal(listItem.currentDatabaseName, undefined, 'do not expect dropdown to have entries unless connected'); assert.equal(listItem.currentDatabaseName, undefined, 'do not expect dropdown to have entries unless connected');
@@ -498,7 +507,7 @@ suite('SQL QueryAction Tests', () => {
cms.setup(x => x.getConnectionProfile(TypeMoq.It.isAny())).returns(() => <IConnectionProfile>{ databaseName: databaseName }); cms.setup(x => x.getConnectionProfile(TypeMoq.It.isAny())).returns(() => <IConnectionProfile>{ databaseName: databaseName });
// ... Create a database dropdown that has been connected // ... Create a database dropdown that has been connected
let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, null, null, null); let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, null, null, null, configurationService.object);
listItem.onConnected(); listItem.onConnected();
// If: I raise a connection changed event // If: I raise a connection changed event
@@ -522,7 +531,7 @@ suite('SQL QueryAction Tests', () => {
cms.setup(x => x.getConnectionProfile(TypeMoq.It.isAny())).returns(() => <IConnectionProfile>{ databaseName: databaseName }); cms.setup(x => x.getConnectionProfile(TypeMoq.It.isAny())).returns(() => <IConnectionProfile>{ databaseName: databaseName });
// ... Create a database dropdown that has been connected // ... Create a database dropdown that has been connected
let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, null, null, null); let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, null, null, null, configurationService.object);
listItem.onConnected(); listItem.onConnected();
// If: I raise a connection changed event for the 'wrong' URI // If: I raise a connection changed event for the 'wrong' URI
@@ -549,7 +558,7 @@ suite('SQL QueryAction Tests', () => {
cms.setup(x => x.onConnectionChanged).returns(() => dbChangedEmitter.event); cms.setup(x => x.onConnectionChanged).returns(() => dbChangedEmitter.event);
// ... Create a database dropdown // ... Create a database dropdown
let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, null, null, null); let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, null, null, null, configurationService.object);
// If: I raise a connection changed event // If: I raise a connection changed event
let eventParams = <IConnectionParams>{ let eventParams = <IConnectionParams>{

View File

@@ -93,7 +93,7 @@ suite('SQL QueryEditor Tests', () => {
instantiationService.setup(x => x.createInstance(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((classDef, editor, action) => { instantiationService.setup(x => x.createInstance(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((classDef, editor, action) => {
if (classDef.ID) { if (classDef.ID) {
if (classDef.ID === 'listDatabaseQueryActionItem') { if (classDef.ID === 'listDatabaseQueryActionItem') {
return new ListDatabasesActionItem(editor, action, connectionManagementService.object, undefined, undefined, undefined); return new ListDatabasesActionItem(editor, action, connectionManagementService.object, undefined, undefined, undefined, configurationService.object);
} }
} }
// Default // Default
@@ -344,7 +344,7 @@ suite('SQL QueryEditor Tests', () => {
queryActionInstantiationService.setup(x => x.createInstance(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())) queryActionInstantiationService.setup(x => x.createInstance(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()))
.returns((definition, editor, action, selectBox) => { .returns((definition, editor, action, selectBox) => {
if (definition.ID === 'listDatabaseQueryActionItem') { if (definition.ID === 'listDatabaseQueryActionItem') {
let item = new ListDatabasesActionItem(editor, action, queryConnectionService.object, undefined, undefined, undefined); let item = new ListDatabasesActionItem(editor, action, queryConnectionService.object, undefined, undefined, undefined,configurationService.object);
return item; return item;
} }
// Default // Default