diff --git a/src/sql/base/browser/ui/modal/media/modal.css b/src/sql/base/browser/ui/modal/media/modal.css index 90b1a2d131..6e120f15be 100644 --- a/src/sql/base/browser/ui/modal/media/modal.css +++ b/src/sql/base/browser/ui/modal/media/modal.css @@ -172,4 +172,22 @@ margin-right: 10px; width: 20px; height: 20px; +} + +.modal .modal-footer .dialogErrorMessage { + align-items: center; + max-height: 30px; + margin-right: 5px; +} + +.modal .dialogErrorMessage .icon { + float: left; + margin-right: 10px; + width: auto; + height: 20px; +} + +.modal .modal-footer .dialogErrorMessage .errorMessage { + max-height: 100%; + overflow-y: scroll; } \ No newline at end of file diff --git a/src/sql/base/browser/ui/modal/modal.ts b/src/sql/base/browser/ui/modal/modal.ts index c5bc51a7fa..489b839eeb 100644 --- a/src/sql/base/browser/ui/modal/modal.ts +++ b/src/sql/base/browser/ui/modal/modal.ts @@ -21,9 +21,13 @@ import { Button } from 'sql/base/browser/ui/button/button'; import * as TelemetryUtils from 'sql/common/telemetryUtilities'; import * as TelemetryKeys from 'sql/common/telemetryKeys'; import { localize } from 'vs/nls'; +import { MessageLevel } from 'sql/workbench/api/common/sqlExtHostTypes'; export const MODAL_SHOWING_KEY = 'modalShowing'; export const MODAL_SHOWING_CONTEXT = new RawContextKey>(MODAL_SHOWING_KEY, []); +const INFO_ALT_TEXT = localize('infoAltText', 'Info'); +const WARNING_ALT_TEXT = localize('warningAltText', 'Warning'); +const ERROR_ALT_TEXT = localize('errorAltText', 'Error'); export interface IModalDialogStyles { dialogForeground?: Color; @@ -145,7 +149,7 @@ export abstract class Modal extends Disposable implements IThemable { /** * Build and render the modal, will call {@link Modal#renderBody} */ - public render() { + public render(errorMessagesInFooter: boolean = false) { let modalBodyClass = (this._modalOptions.isAngular === false ? 'modal-body' : 'modal-body-and-footer'); let parts: Array = []; // This modal header section refers to the header of of the dialog @@ -182,17 +186,6 @@ export abstract class Modal extends Disposable implements IThemable { this.renderBody(body.getHTMLElement()); - if (this._modalOptions.isAngular === false && this._modalOptions.hasErrors) { - body.div({ class: 'dialogErrorMessage', id: 'dialogErrorMessage' }, (errorMessageContainer) => { - errorMessageContainer.div({ class: 'icon error' }, (iconContainer) => { - this._errorIconElement = iconContainer.getHTMLElement(); - this._errorIconElement.style.visibility = 'hidden'; - }); - errorMessageContainer.div({ class: 'errorMessage' }, (messageContainer) => { - this._errorMessage = messageContainer; - }); - }); - } // This modal footer section refers to the footer of of the dialog if (this._modalOptions.isAngular === false) { this._modalFooterSection = $().div({ class: 'modal-footer' }, (modelFooter) => { @@ -221,6 +214,19 @@ export abstract class Modal extends Disposable implements IThemable { builderClass += ' wide'; } + if (this._modalOptions.isAngular === false && this._modalOptions.hasErrors) { + let builder = errorMessagesInFooter ? this._leftFooter : body; + builder.div({ class: 'dialogErrorMessage', id: 'dialogErrorMessage' }, (errorMessageContainer) => { + errorMessageContainer.div({ class: 'icon error' }, (iconContainer) => { + this._errorIconElement = iconContainer.getHTMLElement(); + this._errorIconElement.style.visibility = 'hidden'; + }); + errorMessageContainer.div({ class: 'errorMessage' }, (messageContainer) => { + this._errorMessage = messageContainer; + }); + }); + } + // The builder builds the dialog. It append header, body and footer sections. this._builder = $().div({ class: builderClass, 'role': 'dialog' }, (dialogContainer) => { this._modalDialog = dialogContainer.div({ class: 'modal-dialog ', role: 'document' }, (modalDialog) => { @@ -355,14 +361,33 @@ export abstract class Modal extends Disposable implements IThemable { * Show an error in the error message element * @param err Text to show in the error message */ - protected setError(err: string) { + protected setError(err: string, level: MessageLevel = MessageLevel.Error) { if (this._modalOptions.hasErrors) { if (err === '') { this._errorIconElement.style.visibility = 'hidden'; } else { + const levelClasses = ['info', 'warning', 'error']; + let selectedLevel = levelClasses[2]; + let altText = ERROR_ALT_TEXT; + if (level === MessageLevel.Information) { + selectedLevel = levelClasses[0]; + altText = INFO_ALT_TEXT; + } else if (level === MessageLevel.Warning) { + selectedLevel = levelClasses[1]; + altText = WARNING_ALT_TEXT; + } + levelClasses.forEach(level => { + if (selectedLevel === level) { + this._errorIconElement.classList.add(level); + } else { + this._errorIconElement.classList.remove(level); + } + }); + + this._errorIconElement.title = altText; this._errorIconElement.style.visibility = 'visible'; } - this._errorMessage.innerHtml(err); + this._errorMessage.text(err); } } diff --git a/src/sql/media/icons/status_info.svg b/src/sql/media/icons/status_info.svg index aabb0d4051..109c79c768 100644 --- a/src/sql/media/icons/status_info.svg +++ b/src/sql/media/icons/status_info.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/sql/platform/dialog/customDialogService.ts b/src/sql/platform/dialog/customDialogService.ts index ccf11c19a9..76c9bd8371 100644 --- a/src/sql/platform/dialog/customDialogService.ts +++ b/src/sql/platform/dialog/customDialogService.ts @@ -13,8 +13,8 @@ import { Dialog, Wizard, DialogTab } from 'sql/platform/dialog/dialogTypes'; import { IModalOptions } from 'sql/base/browser/ui/modal/modal'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -const defaultOptions: IModalOptions = { hasBackButton: false, isWide: false }; -const defaultWizardOptions: IModalOptions = { hasBackButton: false, isWide: true }; +const defaultOptions: IModalOptions = { hasBackButton: false, isWide: false, hasErrors: true }; +const defaultWizardOptions: IModalOptions = { hasBackButton: false, isWide: true, hasErrors: true }; export class CustomDialogService { private _dialogModals = new Map(); diff --git a/src/sql/platform/dialog/dialogModal.ts b/src/sql/platform/dialog/dialogModal.ts index 3633dabf97..5d0ef52281 100644 --- a/src/sql/platform/dialog/dialogModal.ts +++ b/src/sql/platform/dialog/dialogModal.ts @@ -24,6 +24,7 @@ import { localize } from 'vs/nls'; import { Emitter } from 'vs/base/common/event'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { DialogMessage, MessageLevel } from '../../workbench/api/common/sqlExtHostTypes'; export class DialogModal extends Modal { private _dialogPane: DialogPane; @@ -52,7 +53,7 @@ export class DialogModal extends Modal { } public render() { - super.render(); + super.render(true); attachModalDialogStyler(this, this._themeService); if (this.backButton) { @@ -71,6 +72,17 @@ export class DialogModal extends Modal { this._dialog.okButton.registerClickEvent(this._onDone.event); this._cancelButton = this.addDialogButton(this._dialog.cancelButton, () => this.cancel(), false); this._dialog.cancelButton.registerClickEvent(this._onCancel.event); + + let messageChangeHandler = (message: DialogMessage) => { + if (message && message.text) { + this.setError(message.text, message.level); + } else { + this.setError(''); + } + }; + + messageChangeHandler(this._dialog.message); + this._dialog.onMessageChange(message => messageChangeHandler(message)); } private addDialogButton(button: DialogButton, onSelect: () => void = () => undefined, registerClickEvent: boolean = true): Button { diff --git a/src/sql/platform/dialog/dialogPane.ts b/src/sql/platform/dialog/dialogPane.ts index 7584eb2af8..cf1cb11efb 100644 --- a/src/sql/platform/dialog/dialogPane.ts +++ b/src/sql/platform/dialog/dialogPane.ts @@ -15,6 +15,7 @@ import { TabbedPanel, IPanelTab, IPanelView } from 'sql/base/browser/ui/panel/pa import { bootstrapAngular } from 'sql/services/bootstrap/bootstrapService'; import { DialogModule } from 'sql/platform/dialog/dialog.module'; import { DialogComponentParams } from 'sql/platform/dialog/dialogContainer.component'; +import { DialogMessage } from 'sql/workbench/api/common/sqlExtHostTypes'; import * as DOM from 'vs/base/browser/dom'; import { Builder } from 'vs/base/browser/builder'; diff --git a/src/sql/platform/dialog/dialogTypes.ts b/src/sql/platform/dialog/dialogTypes.ts index ffd3583dd6..4a98c6377c 100644 --- a/src/sql/platform/dialog/dialogTypes.ts +++ b/src/sql/platform/dialog/dialogTypes.ts @@ -8,6 +8,7 @@ import * as sqlops from 'sqlops'; import { localize } from 'vs/nls'; import { Event, Emitter } from 'vs/base/common/event'; +import { DialogMessage } from 'sql/workbench/api/common/sqlExtHostTypes'; export class ModelViewPane { private _valid: boolean = true; @@ -45,6 +46,9 @@ export class Dialog extends ModelViewPane { public okButton: DialogButton = new DialogButton(Dialog.DONE_BUTTON_LABEL, true); public cancelButton: DialogButton = new DialogButton(Dialog.CANCEL_BUTTON_LABEL, true); public customButtons: DialogButton[]; + private _onMessageChange = new Emitter(); + public readonly onMessageChange = this._onMessageChange.event; + private _message: DialogMessage; constructor(public title: string, content?: string | DialogTab[]) { super(); @@ -52,6 +56,17 @@ export class Dialog extends ModelViewPane { this.content = content; } } + + public get message(): DialogMessage { + return this._message; + } + + public set message(value: DialogMessage) { + if (this._message && !value || !this._message && value || this._message && value && (this._message.level !== value.level || this._message.text !== value.text)) { + this._message = value; + this._onMessageChange.fire(this._message); + } + } } export class DialogButton implements sqlops.window.modelviewdialog.Button { @@ -140,6 +155,9 @@ export class Wizard { private _pageRemovedEmitter = new Emitter(); public readonly onPageRemoved = this._pageRemovedEmitter.event; private _navigationValidator: (pageChangeInfo: sqlops.window.modelviewdialog.WizardPageChangeInfo) => boolean | Thenable; + private _onMessageChange = new Emitter(); + public readonly onMessageChange = this._onMessageChange.event; + private _message: DialogMessage; constructor(public title: string) { } @@ -207,4 +225,15 @@ export class Wizard { return Promise.resolve(true); } } + + public get message(): DialogMessage { + return this._message; + } + + public set message(value: DialogMessage) { + if (this._message && !value || !this._message && value || this._message && value && (this._message.level !== value.level || this._message.text !== value.text)) { + this._message = value; + this._onMessageChange.fire(this._message); + } + } } \ No newline at end of file diff --git a/src/sql/platform/dialog/media/dialogModal.css b/src/sql/platform/dialog/media/dialogModal.css index 54c1578089..a3b9d91c14 100644 --- a/src/sql/platform/dialog/media/dialogModal.css +++ b/src/sql/platform/dialog/media/dialogModal.css @@ -5,7 +5,7 @@ .dialogModal-body { display: flex; - flex-direction: row; + flex-direction: column; height: 100%; width: 100%; min-width: 500px; @@ -29,4 +29,4 @@ .footer-button.dialogModal-hidden { margin: 0; -} \ No newline at end of file +} diff --git a/src/sql/platform/dialog/wizardModal.ts b/src/sql/platform/dialog/wizardModal.ts index 6960991dd7..e9060611e4 100644 --- a/src/sql/platform/dialog/wizardModal.ts +++ b/src/sql/platform/dialog/wizardModal.ts @@ -24,6 +24,7 @@ import { localize } from 'vs/nls'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { Emitter } from 'vs/base/common/event'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { DialogMessage, MessageLevel } from '../../workbench/api/common/sqlExtHostTypes'; export class WizardModal extends Modal { private _dialogPanes = new Map(); @@ -58,7 +59,7 @@ export class WizardModal extends Modal { } public render() { - super.render(); + super.render(true); attachModalDialogStyler(this, this._themeService); if (this.backButton) { @@ -66,6 +67,13 @@ export class WizardModal extends Modal { attachButtonStyler(this.backButton, this._themeService, { buttonBackground: SIDE_BAR_BACKGROUND, buttonHoverBackground: SIDE_BAR_BACKGROUND }); } + if (this._wizard.customButtons) { + this._wizard.customButtons.forEach(button => { + let buttonElement = this.addDialogButton(button); + this.updateButtonElement(buttonElement, button); + }); + } + this._previousButton = this.addDialogButton(this._wizard.backButton, () => this.showPage(this.getCurrentPage() - 1)); this._nextButton = this.addDialogButton(this._wizard.nextButton, () => this.showPage(this.getCurrentPage() + 1)); this._generateScriptButton = this.addDialogButton(this._wizard.generateScriptButton, () => undefined); @@ -73,6 +81,17 @@ export class WizardModal extends Modal { this._wizard.doneButton.registerClickEvent(this._onDone.event); this._cancelButton = this.addDialogButton(this._wizard.cancelButton, () => this.cancel(), false); this._wizard.cancelButton.registerClickEvent(this._onCancel.event); + + let messageChangeHandler = (message: DialogMessage) => { + if (message && message.text) { + this.setError(message.text, message.level); + } else { + this.setError(''); + } + }; + + messageChangeHandler(this._wizard.message); + this._wizard.onMessageChange(message => messageChangeHandler(message)); } private addDialogButton(button: DialogButton, onSelect: () => void = () => undefined, registerClickEvent: boolean = true): Button { @@ -100,7 +119,6 @@ export class WizardModal extends Modal { this._body = bodyBuilder.getHTMLElement(); }); - let builder = new Builder(this._body); this._wizard.pages.forEach(page => { this.registerPage(page); }); diff --git a/src/sql/sqlops.proposed.d.ts b/src/sql/sqlops.proposed.d.ts index 69b5ef50bf..3955e15727 100644 --- a/src/sql/sqlops.proposed.d.ts +++ b/src/sql/sqlops.proposed.d.ts @@ -582,6 +582,24 @@ declare module 'sqlops' { */ export function createWizard(title: string): Wizard; + /** + * Used to control whether a message in a dialog/wizard is displayed as an error, + * warning, or informational message. Default is error. + */ + export enum MessageLevel { + Error = 0, + Warning = 1, + Information = 2 + } + + /** + * A message shown in a dialog. If the level is not set it defaults to error. + */ + export type DialogMessage = { + readonly text: string, + readonly level?: MessageLevel + }; + export interface ModelViewPanel { /** * Register model view content for the dialog. @@ -632,6 +650,12 @@ declare module 'sqlops' { * Any additional buttons that should be displayed */ customButtons: Button[]; + + /** + * Set the informational message shown in the dialog. Hidden when the message is + * undefined or the text is empty or undefined. The default level is error. + */ + message: DialogMessage; } export interface DialogTab extends ModelViewPanel { @@ -798,6 +822,12 @@ declare module 'sqlops' { * cancel it. */ registerNavigationValidator(validator: (pageChangeInfo: WizardPageChangeInfo) => boolean | Thenable): void; + + /** + * Set the informational message shown in the wizard. Hidden when the message is + * undefined or the text is empty or undefined. The default level is error. + */ + message: DialogMessage } } } diff --git a/src/sql/workbench/api/common/sqlExtHostTypes.ts b/src/sql/workbench/api/common/sqlExtHostTypes.ts index e629d3a572..f8249168b7 100644 --- a/src/sql/workbench/api/common/sqlExtHostTypes.ts +++ b/src/sql/workbench/api/common/sqlExtHostTypes.ts @@ -149,6 +149,7 @@ export interface IModelViewDialogDetails { okButton: number; cancelButton: number; customButtons: number[]; + message: DialogMessage; } export interface IModelViewTabDetails { @@ -179,6 +180,18 @@ export interface IModelViewWizardDetails { nextButton: number; backButton: number; customButtons: number[]; + message: DialogMessage; +} + +export enum MessageLevel { + Error = 0, + Warning = 1, + Information = 2 +} + +export interface DialogMessage { + text: string; + level?: MessageLevel; } /// Card-related APIs that need to be here to avoid early load issues diff --git a/src/sql/workbench/api/node/extHostModelViewDialog.ts b/src/sql/workbench/api/node/extHostModelViewDialog.ts index 701f68d6a7..7e0d7bd622 100644 --- a/src/sql/workbench/api/node/extHostModelViewDialog.ts +++ b/src/sql/workbench/api/node/extHostModelViewDialog.ts @@ -93,6 +93,7 @@ class DialogImpl extends ModelViewPanelImpl implements sqlops.window.modelviewdi public okButton: sqlops.window.modelviewdialog.Button; public cancelButton: sqlops.window.modelviewdialog.Button; public customButtons: sqlops.window.modelviewdialog.Button[]; + private _message: sqlops.window.modelviewdialog.DialogMessage; constructor(extHostModelViewDialog: ExtHostModelViewDialog, extHostModelView: ExtHostModelViewShape) { @@ -105,6 +106,15 @@ class DialogImpl extends ModelViewPanelImpl implements sqlops.window.modelviewdi super.setModelViewId(value); this.content = value; } + + public get message(): sqlops.window.modelviewdialog.DialogMessage { + return this._message; + } + + public set message(value: sqlops.window.modelviewdialog.DialogMessage) { + this._message = value; + this._extHostModelViewDialog.updateDialogContent(this); + } } class TabImpl extends ModelViewPanelImpl implements sqlops.window.modelviewdialog.DialogTab { @@ -218,6 +228,7 @@ class WizardImpl implements sqlops.window.modelviewdialog.Wizard { private _pageChangedEmitter = new Emitter(); public readonly onPageChanged = this._pageChangedEmitter.event; private _navigationValidator: (info: sqlops.window.modelviewdialog.WizardPageChangeInfo) => boolean | Thenable; + private _message: sqlops.window.modelviewdialog.DialogMessage; constructor(public title: string, private _extHostModelViewDialog: ExtHostModelViewDialog) { this.doneButton = this._extHostModelViewDialog.createButton(DONE_LABEL); @@ -234,6 +245,15 @@ class WizardImpl implements sqlops.window.modelviewdialog.Wizard { return this._currentPage; } + public get message(): sqlops.window.modelviewdialog.DialogMessage { + return this._message; + } + + public set message(value: sqlops.window.modelviewdialog.DialogMessage) { + this._message = value; + this._extHostModelViewDialog.updateWizard(this); + } + public addPage(page: sqlops.window.modelviewdialog.WizardPage, index?: number): Thenable { return this._extHostModelViewDialog.updateWizardPage(page).then(() => { this._extHostModelViewDialog.addPage(this, page, index); @@ -387,7 +407,8 @@ export class ExtHostModelViewDialog implements ExtHostModelViewDialogShape { okButton: this.getHandle(dialog.okButton), cancelButton: this.getHandle(dialog.cancelButton), content: dialog.content && typeof dialog.content !== 'string' ? dialog.content.map(tab => this.getHandle(tab)) : dialog.content as string, - customButtons: dialog.customButtons ? dialog.customButtons.map(button => this.getHandle(button)) : undefined + customButtons: dialog.customButtons ? dialog.customButtons.map(button => this.getHandle(button)) : undefined, + message: dialog.message }); } @@ -495,7 +516,8 @@ export class ExtHostModelViewDialog implements ExtHostModelViewDialogShape { generateScriptButton: this.getHandle(wizard.generateScriptButton), doneButton: this.getHandle(wizard.doneButton), nextButton: this.getHandle(wizard.nextButton), - customButtons: wizard.customButtons ? wizard.customButtons.map(button => this.getHandle(button)) : undefined + customButtons: wizard.customButtons ? wizard.customButtons.map(button => this.getHandle(button)) : undefined, + message: wizard.message }); } diff --git a/src/sql/workbench/api/node/mainThreadModelViewDialog.ts b/src/sql/workbench/api/node/mainThreadModelViewDialog.ts index 6ea0fd28a1..ff5748b298 100644 --- a/src/sql/workbench/api/node/mainThreadModelViewDialog.ts +++ b/src/sql/workbench/api/node/mainThreadModelViewDialog.ts @@ -94,6 +94,8 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape dialog.customButtons = details.customButtons.map(buttonHandle => this.getButton(buttonHandle)); } + dialog.message = details.message; + return Promise.resolve(); } @@ -169,6 +171,7 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape if (details.customButtons !== undefined) { wizard.customButtons = details.customButtons.map(buttonHandle => this.getButton(buttonHandle)); } + wizard.message = details.message; return Promise.resolve(); } diff --git a/src/sql/workbench/api/node/sqlExtHost.api.impl.ts b/src/sql/workbench/api/node/sqlExtHost.api.impl.ts index 444c15807c..c0153d8cfc 100644 --- a/src/sql/workbench/api/node/sqlExtHost.api.impl.ts +++ b/src/sql/workbench/api/node/sqlExtHost.api.impl.ts @@ -315,7 +315,8 @@ export function createApiFactory( }, createWizard(title: string): sqlops.window.modelviewdialog.Wizard { return extHostModelViewDialog.createWizard(title); - } + }, + MessageLevel: sqlExtHostTypes.MessageLevel }; const window: typeof sqlops.window = { diff --git a/src/sqltest/workbench/api/extHostModelViewDialog.test.ts b/src/sqltest/workbench/api/extHostModelViewDialog.test.ts index b267f0c44b..b109232a65 100644 --- a/src/sqltest/workbench/api/extHostModelViewDialog.test.ts +++ b/src/sqltest/workbench/api/extHostModelViewDialog.test.ts @@ -9,6 +9,7 @@ import { Mock, It, Times } from 'typemoq'; import { ExtHostModelViewDialog } from 'sql/workbench/api/node/extHostModelViewDialog'; import { MainThreadModelViewDialogShape, ExtHostModelViewShape } from 'sql/workbench/api/node/sqlExtHost.protocol'; import { IMainContext } from 'vs/workbench/api/node/extHost.protocol'; +import { MessageLevel } from 'sql/workbench/api/common/sqlExtHostTypes'; 'use strict'; @@ -290,4 +291,20 @@ suite('ExtHostModelViewDialog Tests', () => { assert.equal(validationInfo.lastPage, lastPage); assert.equal(validationInfo.newPage, newPage); }); + + test('Changing the wizard message sends the new message to the main thread', () => { + // Set up the main thread mock to record the call + mockProxy.setup(x => x.$setWizardDetails(It.isAny(), It.isAny())); + let wizard = extHostModelViewDialog.createWizard('wizard_1'); + + // If I update the wizard's message + let newMessage = { + level: MessageLevel.Error, + text: 'test message' + }; + wizard.message = newMessage; + + // Then the main thread gets notified of the new details + mockProxy.verify(x => x.$setWizardDetails(It.isAny(), It.is(x => x.message === newMessage)), Times.once()); + }); }); \ No newline at end of file diff --git a/src/sqltest/workbench/api/mainThreadModelViewDialog.test.ts b/src/sqltest/workbench/api/mainThreadModelViewDialog.test.ts index d970613b42..e4b00138b8 100644 --- a/src/sqltest/workbench/api/mainThreadModelViewDialog.test.ts +++ b/src/sqltest/workbench/api/mainThreadModelViewDialog.test.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import { Mock, It, Times } from 'typemoq'; import { MainThreadModelViewDialog } from 'sql/workbench/api/node/mainThreadModelViewDialog'; import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol'; -import { IModelViewButtonDetails, IModelViewTabDetails, IModelViewDialogDetails, IModelViewWizardPageDetails, IModelViewWizardDetails } from 'sql/workbench/api/common/sqlExtHostTypes'; +import { IModelViewButtonDetails, IModelViewTabDetails, IModelViewDialogDetails, IModelViewWizardPageDetails, IModelViewWizardDetails, DialogMessage, MessageLevel } from 'sql/workbench/api/common/sqlExtHostTypes'; import { CustomDialogService } from 'sql/platform/dialog/customDialogService'; import { Dialog, DialogTab, Wizard } from 'sql/platform/dialog/dialogTypes'; import { ExtHostModelViewDialogShape } from 'sql/workbench/api/node/sqlExtHost.protocol'; @@ -112,7 +112,8 @@ suite('MainThreadModelViewDialog Tests', () => { content: [tab1Handle, tab2Handle], okButton: okButtonHandle, cancelButton: cancelButtonHandle, - customButtons: [button1Handle, button2Handle] + customButtons: [button1Handle, button2Handle], + message: undefined }; // Set up the wizard details @@ -152,7 +153,8 @@ suite('MainThreadModelViewDialog Tests', () => { currentPage: undefined, title: 'wizard_title', customButtons: [], - pages: [page1Handle, page2Handle] + pages: [page1Handle, page2Handle], + message: undefined }; // Register the buttons, tabs, and dialog @@ -322,10 +324,27 @@ suite('MainThreadModelViewDialog Tests', () => { mockExtHostModelViewDialog.setup(x => x.$validateNavigation(It.isAny(), It.isAny())); // If I call validateNavigation on the wizard that gets created - let wizard: Wizard = (mainThreadModelViewDialog as any).getWizard(wizardHandle); - wizard.validateNavigation(1); + mainThreadModelViewDialog.$openWizard(wizardHandle); + openedWizard.validateNavigation(1); // Then the call gets forwarded to the extension host mockExtHostModelViewDialog.verify(x => x.$validateNavigation(It.is(handle => handle === wizardHandle), It.is(info => info.newPage === 1)), Times.once()); }); + + test('Adding a message to a wizard fires events on the created wizard', () => { + mainThreadModelViewDialog.$openWizard(wizardHandle); + let newMessage: DialogMessage; + openedWizard.onMessageChange(message => newMessage = message); + + // If I change the wizard's message + wizardDetails.message = { + level: MessageLevel.Error, + text: 'test message' + }; + mainThreadModelViewDialog.$setWizardDetails(wizardHandle, wizardDetails); + + // Then the message gets changed on the wizard + assert.equal(newMessage, wizardDetails.message, 'New message was not included in the fired event'); + assert.equal(openedWizard.message, wizardDetails.message, 'New message was not set on the wizard'); + }); }); \ No newline at end of file