diff --git a/src/sql/base/browser/ui/inputBox/inputBox.ts b/src/sql/base/browser/ui/inputBox/inputBox.ts index 69b61f346c..302f53a4a6 100644 --- a/src/sql/base/browser/ui/inputBox/inputBox.ts +++ b/src/sql/base/browser/ui/inputBox/inputBox.ts @@ -24,7 +24,8 @@ export interface IInputOptions extends vsIInputBoxOptions { * to run the base VS Input Box validation logic. See validate() override * for more info. */ - requireForceValidations?: boolean + requireForceValidations?: boolean; + required?: boolean; } export class InputBox extends vsInputBox { @@ -60,6 +61,7 @@ export class InputBox extends vsInputBox { if (_sqlOptions && _sqlOptions.type === 'textarea') { this._isTextAreaInput = true; } + this.required = !!this._sqlOptions.required; } public override style(styles: IInputBoxStyles): void { @@ -123,6 +125,14 @@ export class InputBox extends vsInputBox { return !this.inputElement.hasAttribute('disabled'); } + public get required(): boolean { + return this.inputElement.required; + } + + public set required(v: boolean) { + this.inputElement.required = v; + } + public get hideErrors(): boolean { return this._hideErrors; } diff --git a/src/sql/workbench/browser/modal/dialogHelper.ts b/src/sql/workbench/browser/modal/dialogHelper.ts index c381f9f512..2bed853c76 100644 --- a/src/sql/workbench/browser/modal/dialogHelper.ts +++ b/src/sql/workbench/browser/modal/dialogHelper.ts @@ -11,7 +11,7 @@ import * as types from 'vs/base/common/types'; import * as azdata from 'azdata'; -export function appendRow(container: HTMLElement, label: string, labelClass: string, cellContainerClass: string, rowContainerClass?: string | Array): HTMLElement { +export function appendRow(container: HTMLElement, label: string, labelClass: string, cellContainerClass: string, rowContainerClass?: string | Array, showRequiredIndicator: boolean = false): HTMLElement { let rowContainer = append(container, $('tr')); if (rowContainerClass) { if (types.isString(rowContainerClass)) { @@ -20,7 +20,15 @@ export function appendRow(container: HTMLElement, label: string, labelClass: str addClasses(rowContainer, ...rowContainerClass); } } - append(append(rowContainer, $(`td.${labelClass}`)), $('div')).innerText = label; + const labelContainer = append(append(rowContainer, $(`td.${labelClass}`)), $('div.dialog-label-container')); + labelContainer.style.display = 'flex'; + append(labelContainer, $('div')).innerText = label; + if (showRequiredIndicator) { + const indicator = append(labelContainer, $('span.required-indicator')); + indicator.innerText = '*'; + indicator.style.color = 'red'; + indicator.style.marginLeft = '5px'; + } let inputCellContainer = append(rowContainer, $(`td.${cellContainerClass}`)); return inputCellContainer; diff --git a/src/sql/workbench/services/connection/browser/connectionWidget.ts b/src/sql/workbench/services/connection/browser/connectionWidget.ts index 3444321037..c996dfa73a 100644 --- a/src/sql/workbench/services/connection/browser/connectionWidget.ts +++ b/src/sql/workbench/services/connection/browser/connectionWidget.ts @@ -13,7 +13,6 @@ import * as DialogHelper from 'sql/workbench/browser/modal/dialogHelper'; import { IConnectionComponentCallbacks } from 'sql/workbench/services/connection/browser/connectionDialogService'; import { IConnectionProfile } from 'sql/platform/connection/common/interfaces'; import { ConnectionOptionSpecialType } from 'sql/workbench/api/common/sqlExtHostTypes'; -import * as Constants from 'sql/platform/connection/common/constants'; import { ConnectionProfileGroup, IConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup'; import { Dropdown } from 'sql/base/parts/editableDropdown/browser/dropdown'; import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement'; @@ -52,7 +51,6 @@ export class ConnectionWidget extends lifecycle.Disposable { private _serverNameInputBox: InputBox; private _userNameInputBox: InputBox; private _passwordInputBox: InputBox; - private _password: string; private _rememberPasswordCheckBox: Checkbox; private _azureAccountDropdown: SelectBox; private _azureTenantDropdown: SelectBox; @@ -225,7 +223,7 @@ export class ConnectionWidget extends lifecycle.Disposable { protected addServerNameOption(): void { // Server name let serverNameOption = this._optionsMaps[ConnectionOptionSpecialType.serverName]; - let serverName = DialogHelper.appendRow(this._tableContainer, serverNameOption.displayName, 'connection-label', 'connection-input'); + let serverName = DialogHelper.appendRow(this._tableContainer, serverNameOption.displayName, 'connection-label', 'connection-input', undefined, true); this._serverNameInputBox = new InputBox(serverName, this._contextViewService, { validationOptions: { validation: (value: string) => { @@ -237,7 +235,8 @@ export class ConnectionWidget extends lifecycle.Disposable { return undefined; } }, - ariaLabel: serverNameOption.displayName + ariaLabel: serverNameOption.displayName, + required: true }); } @@ -245,19 +244,19 @@ export class ConnectionWidget extends lifecycle.Disposable { // Username let self = this; let userNameOption = this._optionsMaps[ConnectionOptionSpecialType.userName]; - let userName = DialogHelper.appendRow(this._tableContainer, userNameOption.displayName, 'connection-label', 'connection-input', 'username-row'); + let userName = DialogHelper.appendRow(this._tableContainer, userNameOption.displayName, 'connection-label', 'connection-input', 'username-row', userNameOption.isRequired); this._userNameInputBox = new InputBox(userName, this._contextViewService, { validationOptions: { validation: (value: string) => self.validateUsername(value, userNameOption.isRequired) ? ({ type: MessageType.ERROR, content: localize('connectionWidget.missingRequireField', "{0} is required.", userNameOption.displayName) }) : null }, - ariaLabel: userNameOption.displayName + ariaLabel: userNameOption.displayName, + required: userNameOption.isRequired }); // Password let passwordOption = this._optionsMaps[ConnectionOptionSpecialType.password]; let password = DialogHelper.appendRow(this._tableContainer, passwordOption.displayName, 'connection-label', 'connection-input', 'password-row'); this._passwordInputBox = new InputBox(password, this._contextViewService, { ariaLabel: passwordOption.displayName }); this._passwordInputBox.inputElement.type = 'password'; - this._password = ''; // Remember password let rememberPasswordLabel = localize('rememberPassword', "Remember password"); @@ -368,7 +367,7 @@ export class ConnectionWidget extends lifecycle.Disposable { this._databaseDropdownExpanded = true; if (this.serverName) { this._databaseNameInputBox.values = [this._loadingDatabaseName]; - this._callbacks.onFetchDatabases(this.serverName, this.authenticationType, this.userName, this._password, this.authToken).then(databases => { + this._callbacks.onFetchDatabases(this.serverName, this.authenticationType, this.userName, this.password, this.authToken).then(databases => { if (databases) { this._databaseNameInputBox.values = databases.sort((a, b) => a.localeCompare(b)); } else { @@ -431,10 +430,6 @@ export class ConnectionWidget extends lifecycle.Disposable { this._register(this._userNameInputBox.onDidChange(userName => { this.setConnectButton(); })); - - this._register(this._passwordInputBox.onDidChange(passwordInput => { - this._password = passwordInput; - })); } private onGroupSelected(selectedGroup: string) { @@ -458,31 +453,14 @@ export class ConnectionWidget extends lifecycle.Disposable { protected onAuthTypeSelected(selectedAuthType: string) { let currentAuthType = this.getMatchingAuthType(selectedAuthType); - if (currentAuthType === AuthenticationType.AzureMFAAndUser) { - this._userNameInputBox.enable(); - this._passwordInputBox.disable(); - this._passwordInputBox.hideMessage(); - this._passwordInputBox.value = ''; - this._password = ''; - - this._rememberPasswordCheckBox.checked = false; - this._rememberPasswordCheckBox.enabled = false; - } else if (currentAuthType !== AuthenticationType.SqlLogin) { - this._userNameInputBox.disable(); - this._passwordInputBox.disable(); - this._userNameInputBox.hideMessage(); - this._passwordInputBox.hideMessage(); - this._userNameInputBox.value = ''; - this._passwordInputBox.value = ''; - this._password = ''; - - this._rememberPasswordCheckBox.checked = false; - this._rememberPasswordCheckBox.enabled = false; - } else { - this._userNameInputBox.enable(); - this._passwordInputBox.enable(); - this._rememberPasswordCheckBox.enabled = true; - } + this._userNameInputBox.hideMessage(); + this._passwordInputBox.hideMessage(); + this._rememberPasswordCheckBox.checked = false; + this._azureAccountDropdown.hideMessage(); + this._azureTenantDropdown.hideMessage(); + this._tableContainer.classList.add('hide-username'); + this._tableContainer.classList.add('hide-password'); + this._tableContainer.classList.add('hide-azure-accounts'); if (currentAuthType === AuthenticationType.AzureMFA) { this.fillInAzureAccountOptions().then(async () => { @@ -493,8 +471,6 @@ export class ConnectionWidget extends lifecycle.Disposable { this._azureTenantDropdown.enable(); }).catch(err => this._logService.error(`Unexpected error populating Azure Account dropdown : ${err}`)); // Immediately show/hide appropriate elements though so user gets immediate feedback while we load accounts - this._tableContainer.classList.add('hide-username'); - this._tableContainer.classList.add('hide-password'); this._tableContainer.classList.remove('hide-azure-accounts'); } else if (currentAuthType === AuthenticationType.AzureMFAAndUser) { this.fillInAzureAccountOptions().then(async () => { @@ -506,7 +482,6 @@ export class ConnectionWidget extends lifecycle.Disposable { }).catch(err => this._logService.error(`Unexpected error populating Azure Account dropdown : ${err}`)); // Immediately show/hide appropriate elements though so user gets immediate feedback while we load accounts this._tableContainer.classList.remove('hide-username'); - this._tableContainer.classList.add('hide-password'); this._tableContainer.classList.remove('hide-azure-accounts'); } else if (currentAuthType === AuthenticationType.dSTSAuth) { this._accountManagementService.getAccountsForProvider('dstsAuth').then(accounts => { @@ -521,25 +496,9 @@ export class ConnectionWidget extends lifecycle.Disposable { }); } }); - this._tableContainer.classList.add('hide-username'); - this._tableContainer.classList.add('hide-password'); - this._tableContainer.classList.add('hide-azure-accounts'); - } else if (currentAuthType === AuthenticationType.None) { - this._azureAccountDropdown.disable(); - this._azureAccountDropdown.hideMessage(); - this._azureTenantDropdown.disable(); - this._azureTenantDropdown.hideMessage(); - this._tableContainer.classList.add('hide-username'); - this._tableContainer.classList.add('hide-password'); - this._tableContainer.classList.add('hide-azure-accounts'); - } else { - this._azureAccountDropdown.disable(); - this._azureAccountDropdown.hideMessage(); - this._azureTenantDropdown.disable(); - this._azureTenantDropdown.hideMessage(); + } else if (currentAuthType === AuthenticationType.SqlLogin) { this._tableContainer.classList.remove('hide-username'); this._tableContainer.classList.remove('hide-password'); - this._tableContainer.classList.add('hide-azure-accounts'); } } @@ -686,8 +645,7 @@ export class ConnectionWidget extends lifecycle.Disposable { this._serverNameInputBox.value = this.getModelValue(connectionInfo.serverName); this._connectionNameInputBox.value = this.getModelValue(connectionInfo.connectionName); this._userNameInputBox.value = this.getModelValue(connectionInfo.userName); - this._passwordInputBox.value = connectionInfo.password ? Constants.passwordChars : ''; - this._password = this.getModelValue(connectionInfo.password); + this._passwordInputBox.value = this.getModelValue(connectionInfo.password); this._saveProfile = connectionInfo.saveProfile; this._azureTenantId = connectionInfo.azureTenantId; if (this._databaseNameInputBox) { @@ -847,7 +805,7 @@ export class ConnectionWidget extends lifecycle.Disposable { } public get password(): string { - return this._password; + return this._passwordInputBox.value; } public get authenticationType(): string {