Adding on change event in editable dropdowns when value is changes through code. (#14952)

* Adding onChange events in editable dropdowns for values changed through code.

* moved event generation from angular wrapper to core component

* Moving event firing to value setter

* converting back updateSelection to private method

* Removing extra check

* Fixing broken tests by mocking used stuff

* Fixing onTextChange event

* Adding some tests

* Adding necessary tests

* small test name edit

* Fix an assert message
This commit is contained in:
Aasim Khan
2021-04-14 07:48:33 -07:00
committed by GitHub
parent b6bdb68596
commit 5e8ac017a6
4 changed files with 97 additions and 9 deletions

View File

@@ -98,11 +98,13 @@ export class SKURecommendationPage extends MigrationWizardPage {
width: WIZARD_INPUT_COMPONENT_WIDTH width: WIZARD_INPUT_COMPONENT_WIDTH
}).component(); }).component();
this._managedInstanceSubscriptionDropdown = view.modelBuilder.dropDown().withProps({ this._managedInstanceSubscriptionDropdown = view.modelBuilder.dropDown().withProps({
width: WIZARD_INPUT_COMPONENT_WIDTH width: WIZARD_INPUT_COMPONENT_WIDTH,
editable: true
}).component(); }).component();
this._managedInstanceSubscriptionDropdown.onValueChanged((e) => { this._managedInstanceSubscriptionDropdown.onValueChanged((e) => {
if (e.selected) { if (e) {
this.migrationStateModel._targetSubscription = this.migrationStateModel.getSubscription(e.index); const selectedIndex = (<azdata.CategoryValue[]>this._managedInstanceSubscriptionDropdown.values)?.findIndex(v => v.displayName === e);
this.migrationStateModel._targetSubscription = this.migrationStateModel.getSubscription(selectedIndex);
this.migrationStateModel._targetServerInstance = undefined!; this.migrationStateModel._targetServerInstance = undefined!;
this.migrationStateModel._sqlMigrationService = undefined!; this.migrationStateModel._sqlMigrationService = undefined!;
this.populateLocationAndResourceGroupDropdown(); this.populateLocationAndResourceGroupDropdown();
@@ -393,6 +395,7 @@ export class SKURecommendationPage extends MigrationWizardPage {
this._resourceDropdown.loading = true; this._resourceDropdown.loading = true;
try { try {
this._managedInstanceSubscriptionDropdown.values = await this.migrationStateModel.getSubscriptionsDropdownValues(); this._managedInstanceSubscriptionDropdown.values = await this.migrationStateModel.getSubscriptionsDropdownValues();
this._managedInstanceSubscriptionDropdown.value = this._managedInstanceSubscriptionDropdown.values[0];
} catch (e) { } catch (e) {
console.log(e); console.log(e);
} finally { } finally {

View File

@@ -75,6 +75,7 @@ export class Dropdown extends Disposable implements IListVirtualDelegate<string>
private _options: IDropdownOptions; private _options: IDropdownOptions;
private _dataSource = new DropdownDataSource(); private _dataSource = new DropdownDataSource();
public fireOnTextChange?: boolean; public fireOnTextChange?: boolean;
private _previousValue: string;
private _onBlur = this._register(new Emitter<void>()); private _onBlur = this._register(new Emitter<void>());
public onBlur: Event<void> = this._onBlur.event; public onBlur: Event<void> = this._onBlur.event;
@@ -228,7 +229,6 @@ export class Dropdown extends Disposable implements IListVirtualDelegate<string>
} }
if (this.fireOnTextChange) { if (this.fireOnTextChange) {
this.value = e; this.value = e;
this._onValueChange.fire(e);
} }
}); });
@@ -253,7 +253,7 @@ export class Dropdown extends Disposable implements IListVirtualDelegate<string>
return this._selectListContainer.classList.contains('visible'); return this._selectListContainer.classList.contains('visible');
} }
private _setDropdownVisibility(visible: boolean): void { public setDropdownVisibility(visible: boolean): void {
if (visible) { if (visible) {
this._selectListContainer.classList.add('visible'); this._selectListContainer.classList.add('visible');
} else { } else {
@@ -264,7 +264,6 @@ export class Dropdown extends Disposable implements IListVirtualDelegate<string>
private _updateSelection(newValue: string): void { private _updateSelection(newValue: string): void {
this.value = newValue; this.value = newValue;
this._onValueChange.fire(newValue);
this._input.focus(); this._input.focus();
this._hideList(); this._hideList();
} }
@@ -277,12 +276,12 @@ export class Dropdown extends Disposable implements IListVirtualDelegate<string>
this.contextViewService.showContextView({ this.contextViewService.showContextView({
getAnchor: () => this._inputContainer, getAnchor: () => this._inputContainer,
render: container => { render: container => {
this._setDropdownVisibility(true); this.setDropdownVisibility(true);
DOM.append(container, this._selectListContainer); DOM.append(container, this._selectListContainer);
this._updateDropDownList(); this._updateDropDownList();
return { return {
dispose: () => { dispose: () => {
this._setDropdownVisibility(false); this.setDropdownVisibility(false);
} }
}; };
} }
@@ -332,7 +331,11 @@ export class Dropdown extends Disposable implements IListVirtualDelegate<string>
} }
public set value(val: string) { public set value(val: string) {
this._input.value = val; if (this._previousValue !== val) {
this._input.value = val;
this._previousValue = val;
this._onValueChange.fire(val);
}
} }
public get inputElement(): HTMLInputElement { public get inputElement(): HTMLInputElement {
@@ -384,4 +387,12 @@ export class Dropdown extends Disposable implements IListVirtualDelegate<string>
public set ariaLabel(val: string) { public set ariaLabel(val: string) {
this._input.setAriaLabel(val); this._input.setAriaLabel(val);
} }
public get input(): InputBox {
return this._input;
}
public get selectList(): List<IDropdownListItem> {
return this._selectList;
}
} }

View File

@@ -0,0 +1,71 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { Dropdown, IDropdownOptions } from 'sql/base/parts/editableDropdown/browser/dropdown';
const options: IDropdownOptions = {
values: [
'foo1',
'foo2',
'foobar3',
'foobar4'
]
};
suite('Editable dropdown tests', () => {
let container: HTMLElement;
setup(() => {
container = document.createElement('div');
container.style.position = 'absolute';
container.style.width = `${200}px`;
container.style.height = `${200}px`;
});
test('default value for editable dropdown is empty', () => {
const dropdown = new Dropdown(container, undefined, options);
assert(dropdown.value === '');
});
test('changing value through code fires onValueChange event', () => {
const dropdown = new Dropdown(container, undefined, options);
let count = 0;
dropdown.onValueChange((e) => {
count++;
});
dropdown.value = options.values[0];
assert(count === 1, 'onValueChange event was not fired');
dropdown.value = options.values[0];
assert(count === 1, 'onValueChange event should not be fired for setting the same value again');
dropdown.value = options.values[1];
assert(count === 2, 'onValueChange event was not fired for setting a new value of the dropdown');
});
test('changing value through input text fires onValue Change event', () => {
const dropdown = new Dropdown(container, undefined, options);
let count = 0;
dropdown.onValueChange((e) => {
count++;
});
dropdown.fireOnTextChange = true;
dropdown.setDropdownVisibility(true);
dropdown.input.value = options.values[0];
assert(count === 1, 'onValueChange event was not fired for an option from the dropdown list');
dropdown.input.value = 'foo';
assert(count === 2, 'onValueChange event was not fired for a value not in dropdown list');
assert(dropdown.selectList.length === 4, 'list does not have all the values that are matching the input box text');
assert(dropdown.value = 'foo');
dropdown.input.value = 'foobar';
assert(count === 3, 'onValueChange event was not fired for a value not in dropdown list');
assert(dropdown.selectList.length === 2, 'list does not have all the values that are matching the input box text');
assert(dropdown.value = 'foobar');
dropdown.fireOnTextChange = false;
dropdown.input.value = options.values[0];
assert(count === 3, 'onValueChange event was fired with input box value change even after setting the fireOnTextChange to false');
});
});

View File

@@ -466,6 +466,7 @@ suite('SQL QueryAction Tests', () => {
connectionManagementService.setup(x => x.getConnectionProfile(TypeMoq.It.isAny())).returns(() => <IConnectionProfile>{ connectionManagementService.setup(x => x.getConnectionProfile(TypeMoq.It.isAny())).returns(() => <IConnectionProfile>{
databaseName: databaseName databaseName: databaseName
}); });
connectionManagementService.setup(x => x.changeDatabase(TypeMoq.It.isAnyString(), TypeMoq.It.isAnyString())).returns(() => Promise.resolve(true));
// 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, configurationService.object, undefined); listItem = new ListDatabasesActionItem(editor.object, undefined, connectionManagementService.object, undefined, configurationService.object, undefined);
@@ -497,6 +498,7 @@ suite('SQL QueryAction Tests', () => {
let databaseName = 'foobar'; let databaseName = 'foobar';
connectionManagementService.setup(x => x.onConnectionChanged).returns(() => dbChangedEmitter.event); connectionManagementService.setup(x => x.onConnectionChanged).returns(() => dbChangedEmitter.event);
connectionManagementService.setup(x => x.getConnectionProfile(TypeMoq.It.isAny())).returns(() => <IConnectionProfile>{ databaseName: databaseName }); connectionManagementService.setup(x => x.getConnectionProfile(TypeMoq.It.isAny())).returns(() => <IConnectionProfile>{ databaseName: databaseName });
connectionManagementService.setup(x => x.changeDatabase(TypeMoq.It.isAnyString(), TypeMoq.It.isAnyString())).returns(() => Promise.resolve(true));
// ... Create a database dropdown that has been connected // ... Create a database dropdown that has been connected
let listItem = new ListDatabasesActionItem(editor.object, undefined, connectionManagementService.object, undefined, configurationService.object, undefined); let listItem = new ListDatabasesActionItem(editor.object, undefined, connectionManagementService.object, undefined, configurationService.object, undefined);
@@ -519,6 +521,7 @@ suite('SQL QueryAction Tests', () => {
let databaseName = 'foobar'; let databaseName = 'foobar';
connectionManagementService.setup(x => x.onConnectionChanged).returns(() => dbChangedEmitter.event); connectionManagementService.setup(x => x.onConnectionChanged).returns(() => dbChangedEmitter.event);
connectionManagementService.setup(x => x.getConnectionProfile(TypeMoq.It.isAny())).returns(() => <IConnectionProfile>{ databaseName: databaseName }); connectionManagementService.setup(x => x.getConnectionProfile(TypeMoq.It.isAny())).returns(() => <IConnectionProfile>{ databaseName: databaseName });
connectionManagementService.setup(x => x.changeDatabase(TypeMoq.It.isAnyString(), TypeMoq.It.isAnyString())).returns(() => Promise.resolve(true));
// ... Create a database dropdown that has been connected // ... Create a database dropdown that has been connected
let listItem = new ListDatabasesActionItem(editor.object, undefined, connectionManagementService.object, undefined, configurationService.object, undefined); let listItem = new ListDatabasesActionItem(editor.object, undefined, connectionManagementService.object, undefined, configurationService.object, undefined);