Editable dropdown component improvement (#13389)

* replace Tree with List

* comments
This commit is contained in:
Alan Ren
2020-11-13 13:36:54 -08:00
committed by GitHub
parent 6b657259a5
commit 99e3da5b48
12 changed files with 203 additions and 323 deletions

View File

@@ -1,20 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Action } from 'vs/base/common/actions';
export class ToggleDropdownAction extends Action {
private static readonly ID = 'dropdownAction.toggle';
private static readonly ICON = 'dropdown-arrow';
constructor(private _fn: () => any, label: string) {
super(ToggleDropdownAction.ID, label, ToggleDropdownAction.ICON);
}
public run(): Promise<boolean> {
this._fn();
return Promise.resolve(true);
}
}

View File

@@ -4,25 +4,24 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/dropdownList'; import 'vs/css!./media/dropdownList';
import { IInputBoxStyles, InputBox } from 'sql/base/browser/ui/inputBox/inputBox';
import { DropdownDataSource, DropdownFilter, DropdownModel, DropdownRenderer, DropdownController } from './dropdownTree'; import { DropdownDataSource, IDropdownListItem, DropdownListRenderer, SELECT_OPTION_ENTRY_TEMPLATE_ID } from 'sql/base/parts/editableDropdown/browser/dropdownList';
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
import { mixin } from 'vs/base/common/objects';
import { InputBox, IInputBoxStyles } from 'sql/base/browser/ui/inputBox/inputBox';
import { IMessage, MessageType } from 'vs/base/browser/ui/inputbox/inputBox';
import { IListStyles } from 'vs/base/browser/ui/list/listWidget';
import * as DOM from 'vs/base/browser/dom'; import * as DOM from 'vs/base/browser/dom';
import { Disposable } from 'vs/base/common/lifecycle';
import { Color } from 'vs/base/common/color';
import * as nls from 'vs/nls';
import { Event, Emitter } from 'vs/base/common/event';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes'; import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; import { IMessage, MessageType } from 'vs/base/browser/ui/inputbox/inputBox';
import { ITree } from 'vs/base/parts/tree/browser/tree'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
import { IListStyles, List } from 'vs/base/browser/ui/list/listWidget';
import { Color } from 'vs/base/common/color';
import { onUnexpectedError } from 'vs/base/common/errors'; import { onUnexpectedError } from 'vs/base/common/errors';
import { Emitter, Event } from 'vs/base/common/event';
import { KeyCode } from 'vs/base/common/keyCodes';
import { Disposable } from 'vs/base/common/lifecycle';
import { clamp } from 'vs/base/common/numbers'; import { clamp } from 'vs/base/common/numbers';
import { mixin } from 'vs/base/common/objects';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import * as nls from 'vs/nls';
export interface IDropdownOptions extends IDropdownStyles { export interface IDropdownOptions extends IDropdownStyles {
/** /**
@@ -53,10 +52,6 @@ export interface IDropdownOptions extends IDropdownStyles {
* Value to use as aria-label for the input box * Value to use as aria-label for the input box
*/ */
ariaLabel?: string; ariaLabel?: string;
/**
* Label for the dropdown action
*/
actionLabel: string;
} }
export interface IDropdownStyles { export interface IDropdownStyles {
@@ -69,22 +64,17 @@ const errorMessage = nls.localize('editableDropdown.errorValidate', "Must be an
const defaults: IDropdownOptions = { const defaults: IDropdownOptions = {
strictSelection: true, strictSelection: true,
maxHeight: 300, maxHeight: 300,
errorMessage: errorMessage, errorMessage: errorMessage
contextBorder: Color.fromHex('#696969'),
actionLabel: nls.localize('dropdownAction.toggle', "Toggle dropdown")
}; };
export class Dropdown extends Disposable { export class Dropdown extends Disposable implements IListVirtualDelegate<string> {
private _el: HTMLElement; private _el: HTMLElement;
private _inputContainer: HTMLElement; private _inputContainer: HTMLElement;
private _treeContainer: HTMLElement; private _selectListContainer: HTMLElement;
private _input: InputBox; private _input: InputBox;
private _tree: ITree; private _selectList: List<IDropdownListItem>;
private _options: IDropdownOptions; private _options: IDropdownOptions;
private _dataSource = new DropdownDataSource(); private _dataSource = new DropdownDataSource();
private _filter = new DropdownFilter();
private _renderer = new DropdownRenderer();
private _controller = new DropdownController();
public fireOnTextChange?: boolean; public fireOnTextChange?: boolean;
private _onBlur = this._register(new Emitter<void>()); private _onBlur = this._register(new Emitter<void>());
@@ -114,7 +104,7 @@ export class Dropdown extends Disposable {
this._inputContainer = DOM.append(this._el, DOM.$('.dropdown-input.select-container')); this._inputContainer = DOM.append(this._el, DOM.$('.dropdown-input.select-container'));
this._inputContainer.style.width = '100%'; this._inputContainer.style.width = '100%';
this._treeContainer = DOM.$('.dropdown-tree'); this._selectListContainer = DOM.$('div');
this._input = new InputBox(this._inputContainer, contextViewService, { this._input = new InputBox(this._inputContainer, contextViewService, {
validationOptions: { validationOptions: {
@@ -138,7 +128,7 @@ export class Dropdown extends Disposable {
const inputTracker = this._register(DOM.trackFocus(this._input.inputElement)); const inputTracker = this._register(DOM.trackFocus(this._input.inputElement));
inputTracker.onDidBlur(() => { inputTracker.onDidBlur(() => {
if (!this._tree.isDOMFocused()) { if (!this._selectList.isDOMFocused()) {
this._onBlur.fire(); this._onBlur.fire();
} }
}); });
@@ -152,7 +142,7 @@ export class Dropdown extends Disposable {
e.stopPropagation(); e.stopPropagation();
break; break;
case KeyCode.Escape: case KeyCode.Escape:
if (this._treeContainer.parentElement) { if (this._isDropDownVisible) {
this._input.validate(); this._input.validate();
this._onBlur.fire(); this._onBlur.fire();
this._hideList(); this._hideList();
@@ -166,53 +156,76 @@ export class Dropdown extends Disposable {
e.stopPropagation(); e.stopPropagation();
break; break;
case KeyCode.DownArrow: case KeyCode.DownArrow:
if (!this._treeContainer.parentElement) { if (!this._isDropDownVisible) {
this._showList(); this._showList();
} }
this._tree.domFocus(); setTimeout(() => {
this._tree.focusFirst(); this._selectList.domFocus();
this._selectList.focusFirst();
}, 0);
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
break; break;
} }
})); }));
this._tree = new Tree(this._treeContainer, { this._selectList = new List('EditableDropdown', this._selectListContainer, this, [new DropdownListRenderer()], {
dataSource: this._dataSource, useShadows: false,
filter: this._filter, verticalScrollMode: ScrollbarVisibility.Visible,
renderer: this._renderer, keyboardSupport: true,
controller: this._controller mouseSupport: true,
}, { paddingOnRow: false, indentPixels: 0, twistiePixels: 0 }); accessibilityProvider: {
getAriaLabel: (element) => element.text,
const treeTracker = this._register(DOM.trackFocus(this._tree.getHTMLElement())); getWidgetAriaLabel: () => nls.localize('selectBox', "Select Box"),
getRole: () => 'option',
treeTracker.onDidBlur(() => { getWidgetRole: () => 'listbox'
if (!this._input.hasFocus()) {
this._onBlur.fire();
} }
}); });
this.values = this._options.values; this.values = this._options.values;
this._register(this._selectList.onDidBlur(() => {
this._controller.onSelectionChange(e => {
this.value = e.value;
this._onValueChange.fire(e.value);
this._input.focus();
this._hideList(); this._hideList();
}); }));
this._controller.onDropdownEscape(() => { this._register(this._selectList.onKeyDown((e) => {
this._hideList(); const event = new StandardKeyboardEvent(e);
// have to put this in the setTimeout to make sure the focus can be set properly when the context menu is opened by pressing the DownArrow key let handled: boolean = false;
setTimeout(() => { switch (event.keyCode) {
this._input.focus(); case KeyCode.Escape:
}, 0); this._hideList();
}); setTimeout(() => {
this._input.focus();
}, 0);
handled = true;
break;
case KeyCode.Enter:
case KeyCode.Space:
const focusedElements = this._selectList.getFocusedElements();
if (focusedElements.length !== 0) {
this._updateSelection(focusedElements[0].text);
handled = true;
}
break;
default:
return;
}
if (handled) {
e.preventDefault();
e.stopPropagation();
}
}));
this._register(this._selectList.onMouseClick((e) => {
if (e.element) {
this._updateSelection(e.element.text);
}
}));
this._input.onDidChange(e => { this._input.onDidChange(e => {
if (this._dataSource.options) { if (this._dataSource.values?.length > 0) {
this._filter.filterString = e; this._dataSource.filter = e;
this._layoutTree(); if (this._isDropDownVisible) {
this._updateDropDownList();
}
} }
if (this.fireOnTextChange) { if (this.fireOnTextChange) {
this.value = e; this.value = e;
@@ -225,25 +238,52 @@ export class Dropdown extends Disposable {
this._input.validate(); this._input.validate();
}); });
this._register(this._tree); this._register(this._selectList);
this._register(this._input); this._register(this._input);
} }
getHeight(): number {
return 22;
}
getTemplateId(): string {
return SELECT_OPTION_ENTRY_TEMPLATE_ID;
}
private get _isDropDownVisible(): boolean {
return this._selectListContainer.classList.contains('visible');
}
private _setDropdownVisibility(visible: boolean): void {
if (visible) {
this._selectListContainer.classList.add('visible');
} else {
this._selectListContainer.classList.remove('visible');
}
this._selectListContainer.setAttribute('aria-hidden', `${!visible}`);
}
private _updateSelection(newValue: string): void {
this.value = newValue;
this._onValueChange.fire(newValue);
this._input.focus();
this._hideList();
}
private _showList(): void { private _showList(): void {
if (this._input.isEnabled()) { if (this._input.isEnabled()) {
this._inputContainer.setAttribute('aria-expanded', 'true'); this._inputContainer.setAttribute('aria-expanded', 'true');
this._onFocus.fire(); this._onFocus.fire();
this._filter.filterString = ''; this._dataSource.filter = undefined;
this.contextViewService.showContextView({ this.contextViewService.showContextView({
getAnchor: () => this._inputContainer, getAnchor: () => this._inputContainer,
render: container => { render: container => {
DOM.append(container, this._treeContainer); this._setDropdownVisibility(true);
this._layoutTree(); DOM.append(container, this._selectListContainer);
this._updateDropDownList();
return { return {
dispose: () => { dispose: () => {
// when we dispose we want to remove treecontainer so that it doesn't have a parent this._setDropdownVisibility(false);
// we often use the presense of a parent to detect if the tree is being shown
this._treeContainer.remove();
} }
}; };
} }
@@ -252,55 +292,43 @@ export class Dropdown extends Disposable {
} }
private _hideList(): void { private _hideList(): void {
this.contextViewService.hideContextView(); //this.contextViewService.hideContextView();
this._inputContainer.setAttribute('aria-expanded', 'false'); this._inputContainer.setAttribute('aria-expanded', 'false');
} }
private _layoutTree(): void { private _updateDropDownList(): void {
if (this._dataSource && this._dataSource.options && this._dataSource.options.length > 0) { try {
let filteredLength = this._dataSource.options.reduce((p, i) => { this._selectList.splice(0, this._selectList.length, this._dataSource.filteredValues.map(v => { return { text: v }; }));
if (this._filter.isVisible(undefined, i)) { } catch (e) {
return p + 1; onUnexpectedError(e);
} else {
return p;
}
}, 0);
let height = filteredLength * this._renderer.getHeight() > this._options.maxHeight! ? this._options.maxHeight! : filteredLength * this._renderer.getHeight();
this._treeContainer.style.height = height + 'px';
this.updateTreeWidth();
this._tree.layout(parseInt(this._treeContainer.style.height));
this._tree.refresh().catch(e => onUnexpectedError(e));
} }
}
/** let width = this._inputContainer.clientWidth;
* Update the width of the context tree to better fit the contents. if (this._dataSource && this._dataSource.filteredValues) {
*/ const longestOption = this._dataSource.filteredValues.reduce((previous, current) => {
private updateTreeWidth(): void { return previous.length > current.length ? previous : current;
if (this._dataSource && this._dataSource.options) { }, '');
const longestOption = this._dataSource.options.reduce((previous, current) => { this._widthControlElement.innerText = longestOption;
return previous.value.length > current.value.length ? previous : current;
}, { value: '' });
this._widthControlElement.innerText = longestOption.value;
const inputContainerWidth = DOM.getContentWidth(this._inputContainer); const inputContainerWidth = DOM.getContentWidth(this._inputContainer);
const longestOptionWidth = DOM.getTotalWidth(this._widthControlElement); const longestOptionWidth = DOM.getTotalWidth(this._widthControlElement);
this._treeContainer.style.width = `${clamp(longestOptionWidth, inputContainerWidth, 500)}px`; width = clamp(longestOptionWidth, inputContainerWidth, 500);
} }
const height = Math.min((this._dataSource.filteredValues?.length ?? 0) * this.getHeight(), this._options.maxHeight ?? 500);
this._selectListContainer.style.width = `${width}px`;
this._selectListContainer.style.height = `${height}px`;
this._selectList.layout(height, width);
} }
public set values(vals: string[] | undefined) { public set values(vals: string[] | undefined) {
if (vals) { if (vals) {
this._filter.filterString = ''; this._dataSource.filter = undefined;
this._dataSource.options = vals.map(i => { return { value: i }; }); this._dataSource.values = vals;
this.updateTreeWidth(); if (this._isDropDownVisible) {
let height = this._dataSource.options.length * 22 > this._options.maxHeight! ? this._options.maxHeight! : this._dataSource.options.length * 22; this._updateDropDownList();
this._treeContainer.style.height = height + 'px'; }
this._tree.layout(parseInt(this._treeContainer.style.height));
this._tree.setInput(new DropdownModel()).catch(e => onUnexpectedError(e));
this._input.validate(); this._input.validate();
} }
} }
@@ -326,14 +354,14 @@ export class Dropdown extends Disposable {
} }
style(style: IListStyles & IInputBoxStyles & IDropdownStyles) { style(style: IListStyles & IInputBoxStyles & IDropdownStyles) {
this._tree.style(style); this._selectList.style(style);
this._input.style(style); this._input.style(style);
this._treeContainer.style.backgroundColor = style.contextBackground ? style.contextBackground.toString() : ''; this._selectListContainer.style.backgroundColor = style.contextBackground ? style.contextBackground.toString() : '';
this._treeContainer.style.outline = `1px solid ${style.contextBorder || this._options.contextBorder}`; this._selectListContainer.style.outline = `1px solid ${style.contextBorder}`;
} }
private _inputValidator(value: string): IMessage | null { private _inputValidator(value: string): IMessage | null {
if (!this._input.hasFocus() && !this._tree.isDOMFocused() && this._dataSource.options && !this._dataSource.options.some(i => i.value === value)) { if (!this._input.hasFocus() && !this._selectList.isDOMFocused() && this._dataSource.values && !this._dataSource.values.some(i => i === value)) {
if (this._options.strictSelection && this._options.errorMessage) { if (this._options.strictSelection && this._options.errorMessage) {
return { return {
content: this._options.errorMessage, content: this._options.errorMessage,

View File

@@ -0,0 +1,57 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/dropdownList';
import * as DOM from 'vs/base/browser/dom';
import { IListRenderer } from 'vs/base/browser/ui/list/list';
const $ = DOM.$;
export const SELECT_OPTION_ENTRY_TEMPLATE_ID = 'editableDropDownOption.entry.template';
export interface IDropdownListTemplateData {
root: HTMLElement;
text: HTMLElement;
}
export interface IDropdownListItem {
text: string;
}
export class DropdownListRenderer implements IListRenderer<IDropdownListItem, IDropdownListTemplateData> {
get templateId(): string { return SELECT_OPTION_ENTRY_TEMPLATE_ID; }
renderTemplate(container: HTMLElement): IDropdownListTemplateData {
const data: IDropdownListTemplateData = Object.create(null);
data.root = container;
data.text = DOM.append(container, $('span.editable-drop-option-text'));
return data;
}
renderElement(element: IDropdownListItem, index: number, templateData: IDropdownListTemplateData): void {
const data: IDropdownListTemplateData = templateData;
const text = element.text;
data.text.textContent = text;
data.text.title = text;
}
disposeTemplate(templateData: IDropdownListTemplateData): void {
}
}
export class DropdownDataSource {
values: string[];
filter: string | undefined;
public get filteredValues(): string[] {
if (this.filter) {
return this.values.filter(v => {
return v.toLocaleLowerCase().indexOf(this.filter.toLocaleLowerCase()) !== -1;
});
}
return this.values;
}
}

View File

@@ -1,153 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as tree from 'vs/base/parts/tree/browser/tree';
import * as TreeDefaults from 'vs/base/parts/tree/browser/treeDefaults';
import { generateUuid } from 'vs/base/common/uuid';
import * as DOM from 'vs/base/browser/dom';
import { Event, Emitter } from 'vs/base/common/event';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
export interface Template {
label: HTMLElement;
row: HTMLElement;
}
export interface Resource {
value: string;
}
export class DropdownModel {
public static ID = generateUuid();
}
export class DropdownRenderer implements tree.IRenderer {
public getHeight(): number {
return 22;
}
public getTemplateId(): string {
return '';
}
public renderTemplate(tree: tree.ITree, templateId: string, container: HTMLElement): Template {
const row = DOM.$('div.list-row');
row.style.height = '22px';
row.style.paddingLeft = '5px';
DOM.append(container, row);
const label = DOM.$('span.label');
label.style.margin = 'auto';
label.style.verticalAlign = 'middle';
DOM.append(row, label);
return { label, row };
}
public renderElement(tree: tree.ITree, element: Resource, templateId: string, templateData: Template): void {
templateData.label.innerText = element.value;
templateData.row.title = element.value;
}
public disposeTemplate(tree: tree.ITree, templateId: string, templateData: Template): void {
// no op
}
}
export class DropdownDataSource implements tree.IDataSource {
public options?: Array<Resource>;
public getId(tree: tree.ITree, element: Resource | DropdownModel): string {
if (element instanceof DropdownModel) {
return DropdownModel.ID;
} else {
return (element as Resource).value;
}
}
public hasChildren(tree: tree.ITree, element: Resource | DropdownModel): boolean {
if (element instanceof DropdownModel) {
return true;
} else {
return false;
}
}
public getChildren(tree: tree.ITree, element: Resource | DropdownModel): Promise<any> {
if (element instanceof DropdownModel) {
return Promise.resolve(this.options);
} else {
return Promise.resolve(undefined);
}
}
public getParent(tree: tree.ITree, element: Resource | DropdownModel): Promise<any> {
if (element instanceof DropdownModel) {
return Promise.resolve(undefined);
} else {
return Promise.resolve(new DropdownModel());
}
}
}
export class DropdownFilter extends TreeDefaults.DefaultFilter {
public filterString?: string;
public isVisible(tree: tree.ITree | undefined, element: Resource): boolean {
if (this.filterString) {
return element.value.toLowerCase().indexOf(this.filterString.toLowerCase()) !== -1;
} else {
return true;
}
}
}
export class DropdownController extends TreeDefaults.DefaultController {
private _onSelectionChange = new Emitter<Resource>();
public readonly onSelectionChange: Event<Resource> = this._onSelectionChange.event;
private _onDropdownEscape = new Emitter<void>();
public readonly onDropdownEscape: Event<void> = this._onDropdownEscape.event;
constructor() {
super();
}
protected onEscape(tree: tree.ITree, event: IKeyboardEvent): boolean {
let response = super.onEscape(tree, event);
this._onDropdownEscape.fire();
return response;
}
protected onLeftClick(tree: tree.ITree, element: any, eventish: TreeDefaults.ICancelableEvent, origin: string): boolean {
let response = super.onLeftClick(tree, element, eventish, origin);
if (response) {
this._onSelectionChange.fire(tree.getSelection()[0]);
}
return response;
}
public onKeyDown(tree: tree.ITree, event: IKeyboardEvent): boolean {
// The enter key press is handled on key up by our base class (DefaultController) but
// we want to stop it here because we know we're going to handle it (by selecting the item)
// and letting it propagate up means that other controls may incorrectly handle it first
// if they're listening to onKeyDown
const response = super.onKeyDown(tree, event);
if (event.keyCode === KeyCode.Enter) {
DOM.EventHelper.stop(event, true);
return true;
}
return response;
}
protected onEnter(tree: tree.ITree, event: IKeyboardEvent): boolean {
let response = super.onEnter(tree, event);
if (response) {
this._onSelectionChange.fire(tree.getSelection()[0]);
DOM.EventHelper.stop(event, true);
}
return response;
}
}

View File

@@ -3,43 +3,17 @@
* 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.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
.vs .dropdown-arrow.codicon {
background-image: url("dropdownarrow.svg");
}
.vs-dark .dropdown-arrow.codicon,
.hc-black .dropdown-arrow.codicon {
background-image: url("dropdownarrow_inverse.svg");
}
.monaco-dropdown-width-control-element { .monaco-dropdown-width-control-element {
position: absolute; position: absolute;
left: -10000px; left: -10000px;
visibility: hidden; visibility: hidden;
} }
.monaco-dropdown .monaco-action-bar .action-label.codicon.dropdown-arrow { .editable-drop-option-text {
padding: 0; padding-left: 6px;
background-size: 10px; padding-right: 4px;
background-position: 50%;
}
.monaco-dropdown .monaco-action-bar .action-item {
margin: 0;
}
.dropdown-tree .list-row {
width: 100%;
box-sizing: border-box;
}
.dropdown-tree .content {
width: 100%;
}
.dropdown-tree .list-row .label {
width: 100%;
display: inline-block;
text-overflow: ellipsis; text-overflow: ellipsis;
display: inline-block;
width: 100%;
overflow: hidden; overflow: hidden;
} }

View File

@@ -1 +0,0 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12"><defs><style>.cls-1{fill:#333;}</style></defs><title>dropdownarrow</title><path class="cls-1" d="M0,3H12L6,9Z"/></svg>

Before

Width:  |  Height:  |  Size: 211 B

View File

@@ -1 +0,0 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12"><defs><style>.cls-1{fill:#fff;}</style></defs><title>dropdownarrow_inverse</title><path class="cls-1" d="M0,3H12L6,9Z"/></svg>

Before

Width:  |  Height:  |  Size: 219 B

View File

@@ -44,8 +44,7 @@ export class EditableDropDown extends AngularDisposable implements OnInit, OnCha
strictSelection: false, strictSelection: false,
placeholder: '', placeholder: '',
maxHeight: 125, maxHeight: 125,
ariaLabel: '', ariaLabel: ''
actionLabel: ''
}; };
this._selectbox = new Dropdown(this._el.nativeElement, this.contextViewService, dropdownOptions); this._selectbox = new Dropdown(this._el.nativeElement, this.contextViewService, dropdownOptions);
this._selectbox.values = this.options; this._selectbox.values = this.options;

View File

@@ -70,8 +70,7 @@ export default class DropDownComponent extends ComponentBase<azdata.DropDownProp
strictSelection: false, strictSelection: false,
placeholder: '', placeholder: '',
maxHeight: 125, maxHeight: 125,
ariaLabel: '', ariaLabel: ''
actionLabel: ''
}; };
this._editableDropdown = new Dropdown(this._editableDropDownContainer.nativeElement, this.contextViewService, this._editableDropdown = new Dropdown(this._editableDropDownContainer.nativeElement, this.contextViewService,
dropdownOptions); dropdownOptions);

View File

@@ -607,8 +607,7 @@ export class ListDatabasesActionItem extends Disposable implements IActionViewIt
this._dropdown = new Dropdown(this._databaseListDropdown, contextViewProvider, { this._dropdown = new Dropdown(this._databaseListDropdown, contextViewProvider, {
strictSelection: true, strictSelection: true,
placeholder: this._selectDatabaseString, placeholder: this._selectDatabaseString,
ariaLabel: this._selectDatabaseString, ariaLabel: this._selectDatabaseString
actionLabel: nls.localize('listDatabases.toggleDatabaseNameDropdown', "Select Database Toggle Dropdown")
}); });
this._register(this._dropdown.onValueChange(s => this.databaseSelected(s))); this._register(this._dropdown.onValueChange(s => this.databaseSelected(s)));
this._register(this._dropdown.onFocus(() => this.onDropdownFocus())); this._register(this._dropdown.onFocus(() => this.onDropdownFocus()));

View File

@@ -161,6 +161,7 @@ export class ConnectionWidget extends lifecycle.Disposable {
this._previousGroupOption = this._serverGroupSelectBox.value; this._previousGroupOption = this._serverGroupSelectBox.value;
this._container = DOM.append(container, DOM.$('div.connection-table')); this._container = DOM.append(container, DOM.$('div.connection-table'));
this._tableContainer = DOM.append(this._container, DOM.$('table.connection-table-content')); this._tableContainer = DOM.append(this._container, DOM.$('table.connection-table-content'));
this._tableContainer.setAttribute('role', 'presentation');
this.fillInConnectionForm(authTypeChanged); this.fillInConnectionForm(authTypeChanged);
this.registerListeners(); this.registerListeners();
if (this._authTypeSelectBox) { if (this._authTypeSelectBox) {
@@ -284,8 +285,7 @@ export class ConnectionWidget extends lifecycle.Disposable {
strictSelection: false, strictSelection: false,
placeholder: this._defaultDatabaseName, placeholder: this._defaultDatabaseName,
maxHeight: 125, maxHeight: 125,
ariaLabel: databaseOption.displayName, ariaLabel: databaseOption.displayName
actionLabel: localize('connectionWidget.toggleDatabaseNameDropdown', "Select Database Toggle Dropdown")
}); });
} }
} }

View File

@@ -213,8 +213,7 @@ export class RestoreDialog extends Modal {
this._databaseDropdown = new Dropdown(dropdownContainer, this._contextViewService, this._databaseDropdown = new Dropdown(dropdownContainer, this._contextViewService,
{ {
strictSelection: false, strictSelection: false,
ariaLabel: LocalizedStrings.TARGETDATABASE, ariaLabel: LocalizedStrings.TARGETDATABASE
actionLabel: localize('restoreDialog.toggleDatabaseNameDropdown', "Select Database Toggle Dropdown")
} }
); );
this._databaseDropdown.onValueChange(s => { this._databaseDropdown.onValueChange(s => {