From d8b693341e814e792dbb644ded52ec1632b60c2c Mon Sep 17 00:00:00 2001 From: brian-harris <61598682+brian-harris@users.noreply.github.com> Date: Mon, 7 Jun 2021 17:55:08 -0700 Subject: [PATCH] Dev/brih/task/make dropdowns searchable (#15615) * make dropdown controls editable, searchable * updte method name and return type * update error message, and dropdown index selection logic * address review feedback --- extensions/sql-migration/src/api/utils.ts | 14 +++ .../createSqlMigrationServiceDialog.ts | 8 +- .../sql-migration/src/models/stateMachine.ts | 3 +- .../src/wizard/accountsSelectionPage.ts | 25 ++++-- .../src/wizard/databaseBackupPage.ts | 89 ++++++++++++------- .../src/wizard/integrationRuntimePage.ts | 30 ++++--- .../src/wizard/skuRecommendationPage.ts | 64 ++++++++----- .../src/wizard/subscriptionSelectionPage.ts | 22 +++-- 8 files changed, 171 insertions(+), 84 deletions(-) diff --git a/extensions/sql-migration/src/api/utils.ts b/extensions/sql-migration/src/api/utils.ts index 0cd2a2b378..cd201a1c57 100644 --- a/extensions/sql-migration/src/api/utils.ts +++ b/extensions/sql-migration/src/api/utils.ts @@ -3,6 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { CategoryValue, DropDownComponent } from 'azdata'; import { DAYS, HRS, MINUTE, SEC } from '../constants/strings'; import { AdsMigrationStatus } from '../dialog/migrationStatus/migrationStatusDialogModel'; import { MigrationContext } from '../models/migrationLocalStorage'; @@ -120,3 +121,16 @@ export function filterMigrations(databaseMigrations: MigrationContext[], statusF } return filteredMigration; } + +export function selectDropDownIndex(dropDown: DropDownComponent, index: number): void { + if (index >= 0 && dropDown.values && index <= dropDown.values.length - 1) { + const value = dropDown.values[index]; + dropDown.value = value as CategoryValue; + } +} + +export function findDropDownItemIndex(dropDown: DropDownComponent, value: string): number { + return dropDown.values && + dropDown.values.findIndex((v: any) => ((v as CategoryValue)?.displayName?.toLowerCase() === value?.toLowerCase())) || + -1; +} diff --git a/extensions/sql-migration/src/dialog/createSqlMigrationService/createSqlMigrationServiceDialog.ts b/extensions/sql-migration/src/dialog/createSqlMigrationService/createSqlMigrationServiceDialog.ts index 91ab61401f..494e87824f 100644 --- a/extensions/sql-migration/src/dialog/createSqlMigrationService/createSqlMigrationServiceDialog.ts +++ b/extensions/sql-migration/src/dialog/createSqlMigrationService/createSqlMigrationServiceDialog.ts @@ -12,6 +12,7 @@ import * as os from 'os'; import { azureResource } from 'azureResource'; import { IntergrationRuntimePage } from '../../wizard/integrationRuntimePage'; import { IconPathHelper } from '../../constants/iconPathHelper'; +import { selectDropDownIndex } from '../../api/utils'; export class CreateSqlMigrationServiceDialog { @@ -174,7 +175,9 @@ export class CreateSqlMigrationServiceDialog { }).component(); this.migrationServiceResourceGroupDropdown = this._view.modelBuilder.dropDown().withProps({ - required: true + required: true, + editable: true, + fireOnTextChange: true, }).component(); const migrationServiceNameLabel = this._view.modelBuilder.text().withProps({ @@ -252,7 +255,7 @@ export class CreateSqlMigrationServiceDialog { private async populateSubscriptions(): Promise { this.migrationServiceResourceGroupDropdown.loading = true; this.migrationServiceSubscription.value = this.migrationStateModel._targetSubscription.name; - this.populateResourceGroups(); + await this.populateResourceGroups(); } private async populateResourceGroups(): Promise { @@ -276,6 +279,7 @@ export class CreateSqlMigrationServiceDialog { ]; } this.migrationServiceResourceGroupDropdown.values = resourceGroupDropdownValues; + selectDropDownIndex(this.migrationServiceResourceGroupDropdown, 0); this.migrationServiceResourceGroupDropdown.loading = false; } diff --git a/extensions/sql-migration/src/models/stateMachine.ts b/extensions/sql-migration/src/models/stateMachine.ts index 29542ed20a..250422a99c 100644 --- a/extensions/sql-migration/src/models/stateMachine.ts +++ b/extensions/sql-migration/src/models/stateMachine.ts @@ -724,8 +724,9 @@ export class MigrationStateModel implements Model, vscode.Disposable { vscode.window.showInformationMessage(localize("sql.migration.starting.migration.message", 'Starting migration for database {0} to {1} - {2}', this._migrationDbs[i], this._targetServerInstance.name, this._targetDatabaseNames[i])); } } catch (e) { + vscode.window.showErrorMessage( + localize('sql.migration.starting.migration.error', "An error occurred while starting the migration: '{0}'", e.message)); console.log(e); - vscode.window.showInformationMessage(e); } vscode.commands.executeCommand('sqlmigration.refreshMigrationTiles'); diff --git a/extensions/sql-migration/src/wizard/accountsSelectionPage.ts b/extensions/sql-migration/src/wizard/accountsSelectionPage.ts index f05cd423f4..ed5ed9155a 100644 --- a/extensions/sql-migration/src/wizard/accountsSelectionPage.ts +++ b/extensions/sql-migration/src/wizard/accountsSelectionPage.ts @@ -9,7 +9,7 @@ import { MigrationWizardPage } from '../models/migrationWizardPage'; import { MigrationStateModel, StateChangeEvent } from '../models/stateMachine'; import * as constants from '../constants/strings'; import { WIZARD_INPUT_COMPONENT_WIDTH } from './wizardController'; -import { deepClone } from '../api/utils'; +import { deepClone, findDropDownItemIndex, selectDropDownIndex } from '../api/utils'; export class AccountsSelectionPage extends MigrationWizardPage { private _azureAccountsDropdown!: azdata.DropDownComponent; @@ -45,7 +45,9 @@ export class AccountsSelectionPage extends MigrationWizardPage { this._azureAccountsDropdown = view.modelBuilder.dropDown() .withProps({ - width: WIZARD_INPUT_COMPONENT_WIDTH + width: WIZARD_INPUT_COMPONENT_WIDTH, + editable: true, + fireOnTextChange: true, }) .withValidation((c) => { if (c.value) { @@ -71,13 +73,15 @@ export class AccountsSelectionPage extends MigrationWizardPage { }).component(); this._azureAccountsDropdown.onValueChanged(async (value) => { - if (value.selected) { - const selectedAzureAccount = this.migrationStateModel.getAccount(value.index); + const selectedIndex = findDropDownItemIndex(this._azureAccountsDropdown, value); + if (selectedIndex > -1) { + const selectedAzureAccount = this.migrationStateModel.getAccount(selectedIndex); // Making a clone of the account object to preserve the original tenants this.migrationStateModel._azureAccount = deepClone(selectedAzureAccount); if (this.migrationStateModel._azureAccount.properties.tenants.length > 1) { this.migrationStateModel._accountTenants = selectedAzureAccount.properties.tenants; this._accountTenantDropdown.values = await this.migrationStateModel.getTenantValues(); + selectDropDownIndex(this._accountTenantDropdown, 0); this._accountTenantFlexContainer.updateCssStyles({ 'display': 'inline' }); @@ -89,7 +93,7 @@ export class AccountsSelectionPage extends MigrationWizardPage { this.migrationStateModel._subscriptions = undefined!; this.migrationStateModel._targetSubscription = undefined!; this.migrationStateModel._databaseBackup.subscription = undefined!; - this._azureAccountsDropdown.validate(); + await this._azureAccountsDropdown.validate(); } }); @@ -140,7 +144,9 @@ export class AccountsSelectionPage extends MigrationWizardPage { }).component(); this._accountTenantDropdown = view.modelBuilder.dropDown().withProps({ - width: WIZARD_INPUT_COMPONENT_WIDTH + width: WIZARD_INPUT_COMPONENT_WIDTH, + editable: true, + fireOnTextChange: true, }).component(); this._accountTenantDropdown.onValueChanged(value => { @@ -148,8 +154,9 @@ export class AccountsSelectionPage extends MigrationWizardPage { * Replacing all the tenants in azure account with the tenant user has selected. * All azure requests will only run on this tenant from now on */ - if (value.selected) { - this.migrationStateModel._azureAccount.properties.tenants = [this.migrationStateModel.getTenant(value.index)]; + const selectedIndex = findDropDownItemIndex(this._accountTenantDropdown, value); + if (selectedIndex > -1) { + this.migrationStateModel._azureAccount.properties.tenants = [this.migrationStateModel.getTenant(selectedIndex)]; this.migrationStateModel._subscriptions = undefined!; this.migrationStateModel._targetSubscription = undefined!; this.migrationStateModel._databaseBackup.subscription = undefined!; @@ -184,6 +191,8 @@ export class AccountsSelectionPage extends MigrationWizardPage { } finally { this._azureAccountsDropdown.loading = false; } + + selectDropDownIndex(this._azureAccountsDropdown, 0); } public async onPageEnter(): Promise { diff --git a/extensions/sql-migration/src/wizard/databaseBackupPage.ts b/extensions/sql-migration/src/wizard/databaseBackupPage.ts index 45a7b43b9e..97002b7525 100644 --- a/extensions/sql-migration/src/wizard/databaseBackupPage.ts +++ b/extensions/sql-migration/src/wizard/databaseBackupPage.ts @@ -11,6 +11,8 @@ import { Blob, MigrationSourceAuthenticationType, MigrationStateModel, Migration import * as constants from '../constants/strings'; import { IconPathHelper } from '../constants/iconPathHelper'; import { WIZARD_INPUT_COMPONENT_WIDTH } from './wizardController'; +import { findDropDownItemIndex, selectDropDownIndex } from '../api/utils'; + export class DatabaseBackupPage extends MigrationWizardPage { private _view!: azdata.ModelView; @@ -392,6 +394,7 @@ export class DatabaseBackupPage extends MigrationWizardPage { private createTargetDatabaseContainer(): azdata.FlexContainer { + const WIZARD_INPUT_COMPONENT_WIDTH = '200px'; const headerCssStyles: azdata.CssStyles = { 'border': 'none', 'font-size': '13px', @@ -433,7 +436,7 @@ export class DatabaseBackupPage extends MigrationWizardPage { rowCssStyles: rowCssStyle, headerCssStyles: headerCssStyles, isReadOnly: true, - width: '200px' + width: WIZARD_INPUT_COMPONENT_WIDTH }, { displayName: constants.TARGET_DATABASE_NAME, @@ -441,7 +444,7 @@ export class DatabaseBackupPage extends MigrationWizardPage { rowCssStyles: rowCssStyle, headerCssStyles: headerCssStyles, isReadOnly: true, - width: '200px' + width: WIZARD_INPUT_COMPONENT_WIDTH }, { displayName: constants.RESOURCE_GROUP, @@ -449,7 +452,7 @@ export class DatabaseBackupPage extends MigrationWizardPage { rowCssStyles: rowCssStyle, headerCssStyles: headerCssStyles, isReadOnly: true, - width: '200px' + width: WIZARD_INPUT_COMPONENT_WIDTH }, { displayName: constants.STORAGE_ACCOUNT, @@ -457,7 +460,7 @@ export class DatabaseBackupPage extends MigrationWizardPage { rowCssStyles: rowCssStyle, headerCssStyles: headerCssStyles, isReadOnly: true, - width: '200px' + width: WIZARD_INPUT_COMPONENT_WIDTH }, { displayName: constants.BLOB_CONTAINER, @@ -465,7 +468,7 @@ export class DatabaseBackupPage extends MigrationWizardPage { rowCssStyles: rowCssStyle, headerCssStyles: headerCssStyles, isReadOnly: true, - width: '200px' + width: WIZARD_INPUT_COMPONENT_WIDTH } ] }).component(); @@ -551,12 +554,15 @@ export class DatabaseBackupPage extends MigrationWizardPage { } }).component(); this._networkShareStorageAccountResourceGroupDropdown = this._view.modelBuilder.dropDown().withProps({ - width: WIZARD_INPUT_COMPONENT_WIDTH + width: WIZARD_INPUT_COMPONENT_WIDTH, + editable: true, + fireOnTextChange: true, }).component(); - this._networkShareStorageAccountResourceGroupDropdown.onValueChanged(e => { - if (e.selected) { - this.migrationStateModel._databaseBackup.networkShare.resourceGroup = this.migrationStateModel.getAzureResourceGroup(e.index); - this.loadNetworkShareStorageDropdown(); + this._networkShareStorageAccountResourceGroupDropdown.onValueChanged(async (value) => { + const selectedIndex = findDropDownItemIndex(this._networkShareStorageAccountResourceGroupDropdown, value); + if (selectedIndex > -1) { + this.migrationStateModel._databaseBackup.networkShare.resourceGroup = this.migrationStateModel.getAzureResourceGroup(selectedIndex); + await this.loadNetworkShareStorageDropdown(); } }); @@ -572,11 +578,14 @@ export class DatabaseBackupPage extends MigrationWizardPage { this._networkShareContainerStorageAccountDropdown = this._view.modelBuilder.dropDown() .withProps({ required: true, - width: WIZARD_INPUT_COMPONENT_WIDTH + width: WIZARD_INPUT_COMPONENT_WIDTH, + editable: true, + fireOnTextChange: true, }).component(); this._networkShareContainerStorageAccountDropdown.onValueChanged((value) => { - if (value.selected) { - this.migrationStateModel._databaseBackup.networkShare.storageAccount = this.migrationStateModel.getStorageAccount(value.index); + const selectedIndex = findDropDownItemIndex(this._networkShareContainerStorageAccountDropdown, value); + if (selectedIndex > -1) { + this.migrationStateModel._databaseBackup.networkShare.storageAccount = this.migrationStateModel.getStorageAccount(selectedIndex); } }); @@ -587,8 +596,8 @@ export class DatabaseBackupPage extends MigrationWizardPage { height: 25 }).component(); - this._networkShareContainerStorageAccountRefreshButton.onDidClick((e) => { - this.loadNetworkShareStorageDropdown(); + this._networkShareContainerStorageAccountRefreshButton.onDidClick(async (value) => { + await this.loadNetworkShareStorageDropdown(); }); const storageAccountContainer = this._view.modelBuilder.flexContainer().component(); @@ -628,6 +637,7 @@ export class DatabaseBackupPage extends MigrationWizardPage { public async onPageEnter(): Promise { if (this.migrationStateModel.refreshDatabaseBackupPage) { + const WIZARD_INPUT_COMPONENT_WIDTH = '200px'; const connectionProfile = await this.migrationStateModel.getSourceConnectionProfile(); const queryProvider = azdata.dataprotocol.getProvider((await this.migrationStateModel.getSourceConnectionProfile()).providerId, azdata.DataProviderType.QueryProvider); const query = 'select SUSER_NAME()'; @@ -656,7 +666,7 @@ export class DatabaseBackupPage extends MigrationWizardPage { const targetDatabaseInput = this._view.modelBuilder.inputBox().withProps({ required: true, value: db, - width: '200px' + width: WIZARD_INPUT_COMPONENT_WIDTH }).withValidation(c => { if (this._networkShareTargetDatabaseNames.filter(t => t.value === c.value).length > 1) { //Making sure no databases have duplicate values. c.validationErrorMessage = constants.DUPLICATE_NAME_ERROR; @@ -680,7 +690,7 @@ export class DatabaseBackupPage extends MigrationWizardPage { const blobtargetDatabaseInput = this._view.modelBuilder.inputBox().withProps({ required: true, value: db, - width: '200px' + width: WIZARD_INPUT_COMPONENT_WIDTH }).withValidation(c => { if (this._blobContainerTargetDatabaseNames.filter(t => t.value === c.value).length > 1) { //Making sure no databases have duplicate values. c.validationErrorMessage = constants.DUPLICATE_NAME_ERROR; @@ -702,24 +712,31 @@ export class DatabaseBackupPage extends MigrationWizardPage { this._blobContainerTargetDatabaseNames.push(blobtargetDatabaseInput); const blobContainerResourceDropdown = this._view.modelBuilder.dropDown().withProps({ - width: '200px' + width: WIZARD_INPUT_COMPONENT_WIDTH, + editable: true, + fireOnTextChange: true, }).component(); - blobContainerResourceDropdown.onValueChanged(e => { - if (e.selected && e.selected !== constants.RESOURCE_GROUP_NOT_FOUND) { - this.migrationStateModel._databaseBackup.blobs[index].resourceGroup = this.migrationStateModel.getAzureResourceGroup(e.index); + blobContainerResourceDropdown.onValueChanged(async (value) => { + const selectedIndex = findDropDownItemIndex(blobContainerResourceDropdown, value); + if (selectedIndex > -1 && value !== constants.RESOURCE_GROUP_NOT_FOUND) { + this.migrationStateModel._databaseBackup.blobs[index].resourceGroup = this.migrationStateModel.getAzureResourceGroup(selectedIndex); } - this.loadblobStorageDropdown(index); + + await this.loadblobStorageDropdown(index); }); this._blobContainerResourceGroupDropdowns.push(blobContainerResourceDropdown); const blobContainerStorageAccountDropdown = this._view.modelBuilder.dropDown() .withProps({ - width: '200px' + width: WIZARD_INPUT_COMPONENT_WIDTH, + editable: true, + fireOnTextChange: true, }).component(); blobContainerStorageAccountDropdown.onValueChanged(async (value) => { - if (value.selected && value.selected !== constants.NO_STORAGE_ACCOUNT_FOUND) { - this.migrationStateModel._databaseBackup.blobs[index].storageAccount = this.migrationStateModel.getStorageAccount(value.index); + const selectedIndex = findDropDownItemIndex(blobContainerStorageAccountDropdown, value); + if (selectedIndex > -1 && value !== constants.NO_STORAGE_ACCOUNT_FOUND) { + this.migrationStateModel._databaseBackup.blobs[index].storageAccount = this.migrationStateModel.getStorageAccount(selectedIndex); } await this.loadBlobContainerDropdown(index); }); @@ -727,11 +744,14 @@ export class DatabaseBackupPage extends MigrationWizardPage { const blobContainerDropdown = this._view.modelBuilder.dropDown() .withProps({ - width: '200px' + width: WIZARD_INPUT_COMPONENT_WIDTH, + editable: true, + fireOnTextChange: true, }).component(); - blobContainerDropdown.onValueChanged(async (value) => { - if (value.selected && value.selected !== constants.NO_BLOBCONTAINERS_FOUND) { - this.migrationStateModel._databaseBackup.blobs[index].blobContainer = this.migrationStateModel.getBlobContainer(value.index); + blobContainerDropdown.onValueChanged(value => { + const selectedIndex = findDropDownItemIndex(blobContainerStorageAccountDropdown, value); + if (selectedIndex > -1 && value !== constants.NO_BLOBCONTAINERS_FOUND) { + this.migrationStateModel._databaseBackup.blobs[index].blobContainer = this.migrationStateModel.getBlobContainer(selectedIndex); } }); this._blobContainerDropdowns.push(blobContainerDropdown); @@ -953,11 +973,12 @@ export class DatabaseBackupPage extends MigrationWizardPage { this._networkShareStorageAccountResourceGroupDropdown.loading = true; try { this._networkShareStorageAccountResourceGroupDropdown.values = await this.migrationStateModel.getAzureResourceGroupDropdownValues(this.migrationStateModel._databaseBackup.subscription); + selectDropDownIndex(this._networkShareStorageAccountResourceGroupDropdown, 0); } catch (error) { console.log(error); } finally { this._networkShareStorageAccountResourceGroupDropdown.loading = false; - this.loadNetworkShareStorageDropdown(); + await this.loadNetworkShareStorageDropdown(); } } @@ -965,6 +986,7 @@ export class DatabaseBackupPage extends MigrationWizardPage { this._networkShareContainerStorageAccountDropdown.loading = true; try { this._networkShareContainerStorageAccountDropdown.values = await this.migrationStateModel.getStorageAccountValues(this.migrationStateModel._databaseBackup.subscription, this.migrationStateModel._databaseBackup.networkShare.resourceGroup); + selectDropDownIndex(this._networkShareContainerStorageAccountDropdown, 0); } catch (error) { console.log(error); } finally { @@ -976,7 +998,10 @@ export class DatabaseBackupPage extends MigrationWizardPage { this._blobContainerResourceGroupDropdowns.forEach(v => v.loading = true); try { const resourceGroupValues = await this.migrationStateModel.getAzureResourceGroupDropdownValues(this.migrationStateModel._databaseBackup.subscription); - this._blobContainerResourceGroupDropdowns.forEach(v => v.values = resourceGroupValues); + this._blobContainerResourceGroupDropdowns.forEach(dropDown => { + dropDown.values = resourceGroupValues; + selectDropDownIndex(dropDown, 0); + }); } catch (error) { console.log(error); } finally { @@ -988,6 +1013,7 @@ export class DatabaseBackupPage extends MigrationWizardPage { this._blobContainerStorageAccountDropdowns[index].loading = true; try { this._blobContainerStorageAccountDropdowns[index].values = await this.migrationStateModel.getStorageAccountValues(this.migrationStateModel._databaseBackup.subscription, this.migrationStateModel._databaseBackup.blobs[index].resourceGroup); + selectDropDownIndex(this._blobContainerStorageAccountDropdowns[index], 0); } catch (error) { console.log(error); } finally { @@ -1000,6 +1026,7 @@ export class DatabaseBackupPage extends MigrationWizardPage { try { const blobContainerValues = await this.migrationStateModel.getBlobContainerValues(this.migrationStateModel._databaseBackup.subscription, this.migrationStateModel._databaseBackup.blobs[index].storageAccount); this._blobContainerDropdowns[index].values = blobContainerValues; + selectDropDownIndex(this._blobContainerDropdowns[index], 0); } catch (error) { console.log(error); } finally { diff --git a/extensions/sql-migration/src/wizard/integrationRuntimePage.ts b/extensions/sql-migration/src/wizard/integrationRuntimePage.ts index da517d64f7..358fe3214f 100644 --- a/extensions/sql-migration/src/wizard/integrationRuntimePage.ts +++ b/extensions/sql-migration/src/wizard/integrationRuntimePage.ts @@ -12,6 +12,7 @@ import * as constants from '../constants/strings'; import { WIZARD_INPUT_COMPONENT_WIDTH } from './wizardController'; import { getLocationDisplayName, getSqlMigrationService, getSqlMigrationServiceAuthKeys, getSqlMigrationServiceMonitoringData, SqlManagedInstance, SqlMigrationService } from '../api/azure'; import { IconPathHelper } from '../constants/iconPathHelper'; +import { findDropDownItemIndex } from '../api/utils'; export class IntergrationRuntimePage extends MigrationWizardPage { @@ -174,12 +175,14 @@ export class IntergrationRuntimePage extends MigrationWizardPage { }).component(); this._resourceGroupDropdown = this._view.modelBuilder.dropDown().withProps({ width: WIZARD_INPUT_COMPONENT_WIDTH, - editable: true + editable: true, + fireOnTextChange: true, }).component(); this._resourceGroupDropdown.onValueChanged(async (value) => { - if (value) { - this.populateDms(value); + const selectedIndex = findDropDownItemIndex(this._resourceGroupDropdown, value); + if (selectedIndex > -1) { + await this.populateDms(value); } }); @@ -193,7 +196,8 @@ export class IntergrationRuntimePage extends MigrationWizardPage { this._dmsDropdown = this._view.modelBuilder.dropDown().withProps({ width: WIZARD_INPUT_COMPONENT_WIDTH, - editable: true + editable: true, + fireOnTextChange: true, }).component(); this._dmsDropdown.onValueChanged(async (value) => { @@ -201,9 +205,11 @@ export class IntergrationRuntimePage extends MigrationWizardPage { this.wizard.message = { text: '' }; - const selectedIndex = (this._dmsDropdown.values)?.findIndex((v) => v.displayName === value); - this.migrationStateModel._sqlMigrationService = this.migrationStateModel.getMigrationService(selectedIndex); - this.loadMigrationServiceStatus(); + const selectedIndex = findDropDownItemIndex(this._dmsDropdown, value); + if (selectedIndex > -1) { + this.migrationStateModel._sqlMigrationService = this.migrationStateModel.getMigrationService(selectedIndex); + await this.loadMigrationServiceStatus(); + } } }); @@ -395,10 +401,11 @@ export class IntergrationRuntimePage extends MigrationWizardPage { let index = 0; if (resourceGroupName) { - index = (this._resourceGroupDropdown.values).findIndex(v => v.displayName.toLowerCase() === resourceGroupName.toLowerCase()); + index = findDropDownItemIndex(this._resourceGroupDropdown, resourceGroupName); } + if ((this._resourceGroupDropdown.value)?.displayName.toLowerCase() === (this._resourceGroupDropdown.values[index])?.displayName.toLowerCase()) { - this.populateDms((this._resourceGroupDropdown.value)?.displayName); + await this.populateDms((this._resourceGroupDropdown.value)?.displayName); } else { this._resourceGroupDropdown.value = this._resourceGroupDropdown.values[index]; } @@ -407,7 +414,6 @@ export class IntergrationRuntimePage extends MigrationWizardPage { } finally { this._resourceGroupDropdown.loading = false; } - } public async populateDms(resourceGroupName: string): Promise { @@ -419,9 +425,9 @@ export class IntergrationRuntimePage extends MigrationWizardPage { this._dmsDropdown.values = await this.migrationStateModel.getSqlMigrationServiceValues(this.migrationStateModel._targetSubscription, this.migrationStateModel._targetServerInstance, resourceGroupName); let index = -1; if (this.migrationStateModel._sqlMigrationService) { - index = (this._dmsDropdown.values).findIndex(v => v.displayName.toLowerCase() === this.migrationStateModel._sqlMigrationService.name.toLowerCase()); + index = findDropDownItemIndex(this._dmsDropdown, this.migrationStateModel._sqlMigrationService.name); } - if (index !== -1) { + if (index > -1) { this._dmsDropdown.value = this._dmsDropdown.values[index]; } else { this._dmsDropdown.value = this._dmsDropdown.values[0]; diff --git a/extensions/sql-migration/src/wizard/skuRecommendationPage.ts b/extensions/sql-migration/src/wizard/skuRecommendationPage.ts index 56f45bd0cd..d726a29abe 100644 --- a/extensions/sql-migration/src/wizard/skuRecommendationPage.ts +++ b/extensions/sql-migration/src/wizard/skuRecommendationPage.ts @@ -12,6 +12,7 @@ import * as vscode from 'vscode'; import { EOL } from 'os'; import { IconPath, IconPathHelper } from '../constants/iconPathHelper'; import { WIZARD_INPUT_COMPONENT_WIDTH } from './wizardController'; +import { findDropDownItemIndex, selectDropDownIndex } from '../api/utils'; export interface Product { type: MigrationTargetType; @@ -291,15 +292,16 @@ export class SKURecommendationPage extends MigrationWizardPage { }).component(); this._managedInstanceSubscriptionDropdown = this._view.modelBuilder.dropDown().withProps({ width: WIZARD_INPUT_COMPONENT_WIDTH, - editable: true + editable: true, + fireOnTextChange: true, }).component(); - this._managedInstanceSubscriptionDropdown.onValueChanged((e) => { - if (e) { - const selectedIndex = (this._managedInstanceSubscriptionDropdown.values)?.findIndex(v => v.displayName === e); + this._managedInstanceSubscriptionDropdown.onValueChanged(async (value) => { + const selectedIndex = findDropDownItemIndex(this._managedInstanceSubscriptionDropdown, value); + if (selectedIndex > -1) { this.migrationStateModel._targetSubscription = this.migrationStateModel.getSubscription(selectedIndex); this.migrationStateModel._targetServerInstance = undefined!; this.migrationStateModel._sqlMigrationService = undefined!; - this.populateLocationAndResourceGroupDropdown(); + await this.populateLocationAndResourceGroupDropdown(); } }); @@ -312,12 +314,15 @@ export class SKURecommendationPage extends MigrationWizardPage { } }).component(); this._azureLocationDropdown = this._view.modelBuilder.dropDown().withProps({ - width: WIZARD_INPUT_COMPONENT_WIDTH + width: WIZARD_INPUT_COMPONENT_WIDTH, + editable: true, + fireOnTextChange: true, }).component(); - this._azureLocationDropdown.onValueChanged((e) => { - if (e.selected) { - this.migrationStateModel._location = this.migrationStateModel.getLocation(e.index); - this.populateResourceInstanceDropdown(); + this._azureLocationDropdown.onValueChanged(async (value) => { + const selectedIndex = findDropDownItemIndex(this._azureLocationDropdown, value); + if (selectedIndex > -1) { + this.migrationStateModel._location = this.migrationStateModel.getLocation(selectedIndex); + await this.populateResourceInstanceDropdown(); } }); @@ -330,12 +335,15 @@ export class SKURecommendationPage extends MigrationWizardPage { } }).component(); this._azureResourceGroupDropdown = this._view.modelBuilder.dropDown().withProps({ - width: WIZARD_INPUT_COMPONENT_WIDTH + width: WIZARD_INPUT_COMPONENT_WIDTH, + editable: true, + fireOnTextChange: true, }).component(); - this._azureResourceGroupDropdown.onValueChanged((e) => { - if (e.selected) { - this.migrationStateModel._resourceGroup = this.migrationStateModel.getAzureResourceGroup(e.index); - this.populateResourceInstanceDropdown(); + this._azureResourceGroupDropdown.onValueChanged(async (value) => { + const selectedIndex = findDropDownItemIndex(this._azureResourceGroupDropdown, value); + if (selectedIndex > -1) { + this.migrationStateModel._resourceGroup = this.migrationStateModel.getAzureResourceGroup(selectedIndex); + await this.populateResourceInstanceDropdown(); } }); this._resourceDropdownLabel = this._view.modelBuilder.text().withProps({ @@ -348,17 +356,20 @@ export class SKURecommendationPage extends MigrationWizardPage { }).component(); this._resourceDropdown = this._view.modelBuilder.dropDown().withProps({ - width: WIZARD_INPUT_COMPONENT_WIDTH + width: WIZARD_INPUT_COMPONENT_WIDTH, + editable: true, + fireOnTextChange: true, }).component(); - this._resourceDropdown.onValueChanged((e) => { - if (e?.selected && - e.selected !== constants.NO_MANAGED_INSTANCE_FOUND && - e.selected !== constants.NO_VIRTUAL_MACHINE_FOUND) { + this._resourceDropdown.onValueChanged(value => { + const selectedIndex = findDropDownItemIndex(this._resourceDropdown, value); + if (selectedIndex > -1 && + value !== constants.NO_MANAGED_INSTANCE_FOUND && + value !== constants.NO_VIRTUAL_MACHINE_FOUND) { this.migrationStateModel._sqlMigrationServices = undefined!; if (this._rbg.selectedCardId === MigrationTargetType.SQLVM) { - this.migrationStateModel._targetServerInstance = this.migrationStateModel.getVirtualMachine(e.index); + this.migrationStateModel._targetServerInstance = this.migrationStateModel.getVirtualMachine(selectedIndex); } else { - this.migrationStateModel._targetServerInstance = this.migrationStateModel.getManagedInstance(e.index); + this.migrationStateModel._targetServerInstance = this.migrationStateModel.getManagedInstance(selectedIndex); } } }); @@ -425,11 +436,12 @@ export class SKURecommendationPage extends MigrationWizardPage { this._resourceDropdown.loading = true; try { this._managedInstanceSubscriptionDropdown.values = await this.migrationStateModel.getSubscriptionsDropdownValues(); - this._managedInstanceSubscriptionDropdown.value = this._managedInstanceSubscriptionDropdown.values[0]; + selectDropDownIndex(this._managedInstanceSubscriptionDropdown, 0); } catch (e) { console.log(e); } finally { this._managedInstanceSubscriptionDropdown.loading = false; + this._resourceDropdown.loading = false; } } } @@ -439,7 +451,9 @@ export class SKURecommendationPage extends MigrationWizardPage { this._azureLocationDropdown.loading = true; try { this._azureResourceGroupDropdown.values = await this.migrationStateModel.getAzureResourceGroupDropdownValues(this.migrationStateModel._targetSubscription); + selectDropDownIndex(this._azureResourceGroupDropdown, 0); this._azureLocationDropdown.values = await this.migrationStateModel.getAzureLocationDropdownValues(this.migrationStateModel._targetSubscription); + selectDropDownIndex(this._azureLocationDropdown, 0); } catch (e) { console.log(e); } finally { @@ -449,8 +463,8 @@ export class SKURecommendationPage extends MigrationWizardPage { } private async populateResourceInstanceDropdown(): Promise { - this._resourceDropdown.loading = true; try { + this._resourceDropdown.loading = true; if (this._rbg.selectedCardId === MigrationTargetType.SQLVM) { this._resourceDropdownLabel.value = constants.AZURE_SQL_DATABASE_VIRTUAL_MACHINE; this._resourceDropdown.values = await this.migrationStateModel.getSqlVirtualMachineValues(this.migrationStateModel._targetSubscription, this.migrationStateModel._location, this.migrationStateModel._resourceGroup); @@ -459,6 +473,8 @@ export class SKURecommendationPage extends MigrationWizardPage { this._resourceDropdownLabel.value = constants.AZURE_SQL_DATABASE_MANAGED_INSTANCE; this._resourceDropdown.values = await this.migrationStateModel.getManagedInstanceValues(this.migrationStateModel._targetSubscription, this.migrationStateModel._location, this.migrationStateModel._resourceGroup); } + + selectDropDownIndex(this._resourceDropdown, 0); } catch (e) { console.log(e); } finally { diff --git a/extensions/sql-migration/src/wizard/subscriptionSelectionPage.ts b/extensions/sql-migration/src/wizard/subscriptionSelectionPage.ts index d5621398f0..b3026699fa 100644 --- a/extensions/sql-migration/src/wizard/subscriptionSelectionPage.ts +++ b/extensions/sql-migration/src/wizard/subscriptionSelectionPage.ts @@ -9,6 +9,7 @@ import { MigrationStateModel, StateChangeEvent } from '../models/stateMachine'; import { SUBSCRIPTION_SELECTION_PAGE_TITLE, SUBSCRIPTION_SELECTION_AZURE_ACCOUNT_TITLE, SUBSCRIPTION_SELECTION_AZURE_PRODUCT_TITLE, SUBSCRIPTION_SELECTION_AZURE_SUBSCRIPTION_TITLE } from '../constants/strings'; import { Disposable } from 'vscode'; import { getSubscriptions, Subscription, getAvailableManagedInstanceProducts, AzureProduct, getAvailableSqlServers } from '../api/azure'; +import { selectDropDownIndex } from '../api/utils'; interface GenericValue extends azdata.CategoryValue { value: T; @@ -37,7 +38,7 @@ export class SubscriptionSelectionPage extends MigrationWizardPage { private async initialState(view: azdata.ModelView) { this.accountDropDown = this.createAccountDropDown(view); this.subscriptionDropDown = this.createSubscriptionDropDown(view); - this.productDropDown = this.createProdcutDropDown(view); + this.productDropDown = this.createProductDropDown(view); const form = view.modelBuilder.formContainer().withFormItems( [ @@ -53,10 +54,12 @@ export class SubscriptionSelectionPage extends MigrationWizardPage { private createAccountDropDown(view: azdata.ModelView): azdata.FormComponent { const dropDown = view.modelBuilder.dropDown().withProperties({ values: [], + editable: true, + fireOnTextChange: true, }); - this.disposables.push(dropDown.component().onValueChanged(() => { - this.accountValueChanged().catch(console.error); + this.disposables.push(dropDown.component().onValueChanged(async () => { + await this.accountValueChanged().catch(console.error); })); return { @@ -68,10 +71,12 @@ export class SubscriptionSelectionPage extends MigrationWizardPage { private createSubscriptionDropDown(view: azdata.ModelView): azdata.FormComponent { const dropDown = view.modelBuilder.dropDown().withProperties({ values: [], + editable: true, + fireOnTextChange: true, }); - this.disposables.push(dropDown.component().onValueChanged(() => { - this.subscriptionValueChanged().catch(console.error); + this.disposables.push(dropDown.component().onValueChanged(async () => { + await this.subscriptionValueChanged().catch(console.error); })); return { @@ -80,9 +85,11 @@ export class SubscriptionSelectionPage extends MigrationWizardPage { }; } - private createProdcutDropDown(view: azdata.ModelView): azdata.FormComponent { + private createProductDropDown(view: azdata.ModelView): azdata.FormComponent { const dropDown = view.modelBuilder.dropDown().withProperties({ values: [], + editable: true, + fireOnTextChange: true, }); return { @@ -132,6 +139,7 @@ export class SubscriptionSelectionPage extends MigrationWizardPage { }); this.accountDropDown!.component.values = values; + selectDropDownIndex(this.accountDropDown!.component, 0); await this.accountValueChanged(); } @@ -145,6 +153,7 @@ export class SubscriptionSelectionPage extends MigrationWizardPage { }); this.subscriptionDropDown!.component.values = values; + selectDropDownIndex(this.subscriptionDropDown!.component, 0); await this.subscriptionValueChanged(); } @@ -158,6 +167,7 @@ export class SubscriptionSelectionPage extends MigrationWizardPage { }); this.productDropDown!.component.values = values; + selectDropDownIndex(this.productDropDown!.component, 0); } public async onPageEnter(): Promise {