mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Add info/warning/error messages for wizards and dialogs (#1696)
This commit is contained in:
@@ -172,4 +172,22 @@
|
|||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 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;
|
||||||
}
|
}
|
||||||
@@ -21,9 +21,13 @@ import { Button } from 'sql/base/browser/ui/button/button';
|
|||||||
import * as TelemetryUtils from 'sql/common/telemetryUtilities';
|
import * as TelemetryUtils from 'sql/common/telemetryUtilities';
|
||||||
import * as TelemetryKeys from 'sql/common/telemetryKeys';
|
import * as TelemetryKeys from 'sql/common/telemetryKeys';
|
||||||
import { localize } from 'vs/nls';
|
import { localize } from 'vs/nls';
|
||||||
|
import { MessageLevel } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||||
|
|
||||||
export const MODAL_SHOWING_KEY = 'modalShowing';
|
export const MODAL_SHOWING_KEY = 'modalShowing';
|
||||||
export const MODAL_SHOWING_CONTEXT = new RawContextKey<Array<string>>(MODAL_SHOWING_KEY, []);
|
export const MODAL_SHOWING_CONTEXT = new RawContextKey<Array<string>>(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 {
|
export interface IModalDialogStyles {
|
||||||
dialogForeground?: Color;
|
dialogForeground?: Color;
|
||||||
@@ -145,7 +149,7 @@ export abstract class Modal extends Disposable implements IThemable {
|
|||||||
/**
|
/**
|
||||||
* Build and render the modal, will call {@link Modal#renderBody}
|
* 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 modalBodyClass = (this._modalOptions.isAngular === false ? 'modal-body' : 'modal-body-and-footer');
|
||||||
let parts: Array<HTMLElement> = [];
|
let parts: Array<HTMLElement> = [];
|
||||||
// This modal header section refers to the header of of the dialog
|
// 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());
|
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
|
// This modal footer section refers to the footer of of the dialog
|
||||||
if (this._modalOptions.isAngular === false) {
|
if (this._modalOptions.isAngular === false) {
|
||||||
this._modalFooterSection = $().div({ class: 'modal-footer' }, (modelFooter) => {
|
this._modalFooterSection = $().div({ class: 'modal-footer' }, (modelFooter) => {
|
||||||
@@ -221,6 +214,19 @@ export abstract class Modal extends Disposable implements IThemable {
|
|||||||
builderClass += ' wide';
|
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.
|
// The builder builds the dialog. It append header, body and footer sections.
|
||||||
this._builder = $().div({ class: builderClass, 'role': 'dialog' }, (dialogContainer) => {
|
this._builder = $().div({ class: builderClass, 'role': 'dialog' }, (dialogContainer) => {
|
||||||
this._modalDialog = dialogContainer.div({ class: 'modal-dialog ', role: 'document' }, (modalDialog) => {
|
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
|
* Show an error in the error message element
|
||||||
* @param err Text to show in the error message
|
* @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 (this._modalOptions.hasErrors) {
|
||||||
if (err === '') {
|
if (err === '') {
|
||||||
this._errorIconElement.style.visibility = 'hidden';
|
this._errorIconElement.style.visibility = 'hidden';
|
||||||
} else {
|
} 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._errorIconElement.style.visibility = 'visible';
|
||||||
}
|
}
|
||||||
this._errorMessage.innerHtml(err);
|
this._errorMessage.text(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><style type="text/css">.icon-canvas-transparent{opacity:0;fill:#F6F6F6;} .icon-vs-out{fill:#F6F6F6;} .icon-vs-blue{fill:#1BA1E2;} .icon-white{fill:#FFFFFF;}</style><path class="icon-canvas-transparent" d="M16 16h-16v-16h16v16z" id="canvas"/><path class="icon-vs-out" d="M0 8c0-4.418 3.582-8 8-8s8 3.582 8 8-3.582 8-8 8-8-3.582-8-8z" id="outline"/><path class="icon-vs-blue" d="M8 1c-3.865 0-7 3.135-7 7s3.135 7 7 7 7-3.135 7-7-3.135-7-7-7zm1 12h-2v-7h2v7zm0-8h-2v-2h2v2z" id="iconBg"/><path class="icon-white" d="M7 6h2v7h-2v-7zm0-1h2v-2h-2v2z" id="iconFg"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style type="text/css">.icon-canvas-transparent{opacity:0;fill:#F6F6F6;} .icon-vs-out{fill:#F6F6F6;} .icon-vs-blue{fill:#1BA1E2;} .icon-white{fill:#FFFFFF;}</style><path class="icon-canvas-transparent" d="M16 16h-16v-16h16v16z" id="canvas"/><path class="icon-vs-out" d="M0 8c0-4.418 3.582-8 8-8s8 3.582 8 8-3.582 8-8 8-8-3.582-8-8z" id="outline"/><path class="icon-vs-blue" d="M8 1c-3.865 0-7 3.135-7 7s3.135 7 7 7 7-3.135 7-7-3.135-7-7-7zm1 12h-2v-7h2v7zm0-8h-2v-2h2v2z" id="iconBg"/><path class="icon-white" d="M7 6h2v7h-2v-7zm0-1h2v-2h-2v2z" id="iconFg"/></svg>
|
||||||
|
Before Width: | Height: | Size: 627 B After Width: | Height: | Size: 624 B |
@@ -13,8 +13,8 @@ import { Dialog, Wizard, DialogTab } from 'sql/platform/dialog/dialogTypes';
|
|||||||
import { IModalOptions } from 'sql/base/browser/ui/modal/modal';
|
import { IModalOptions } from 'sql/base/browser/ui/modal/modal';
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
|
|
||||||
const defaultOptions: IModalOptions = { hasBackButton: false, isWide: false };
|
const defaultOptions: IModalOptions = { hasBackButton: false, isWide: false, hasErrors: true };
|
||||||
const defaultWizardOptions: IModalOptions = { hasBackButton: false, isWide: true };
|
const defaultWizardOptions: IModalOptions = { hasBackButton: false, isWide: true, hasErrors: true };
|
||||||
|
|
||||||
export class CustomDialogService {
|
export class CustomDialogService {
|
||||||
private _dialogModals = new Map<Dialog, DialogModal>();
|
private _dialogModals = new Map<Dialog, DialogModal>();
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import { localize } from 'vs/nls';
|
|||||||
import { Emitter } from 'vs/base/common/event';
|
import { Emitter } from 'vs/base/common/event';
|
||||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
|
import { DialogMessage, MessageLevel } from '../../workbench/api/common/sqlExtHostTypes';
|
||||||
|
|
||||||
export class DialogModal extends Modal {
|
export class DialogModal extends Modal {
|
||||||
private _dialogPane: DialogPane;
|
private _dialogPane: DialogPane;
|
||||||
@@ -52,7 +53,7 @@ export class DialogModal extends Modal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
super.render();
|
super.render(true);
|
||||||
attachModalDialogStyler(this, this._themeService);
|
attachModalDialogStyler(this, this._themeService);
|
||||||
|
|
||||||
if (this.backButton) {
|
if (this.backButton) {
|
||||||
@@ -71,6 +72,17 @@ export class DialogModal extends Modal {
|
|||||||
this._dialog.okButton.registerClickEvent(this._onDone.event);
|
this._dialog.okButton.registerClickEvent(this._onDone.event);
|
||||||
this._cancelButton = this.addDialogButton(this._dialog.cancelButton, () => this.cancel(), false);
|
this._cancelButton = this.addDialogButton(this._dialog.cancelButton, () => this.cancel(), false);
|
||||||
this._dialog.cancelButton.registerClickEvent(this._onCancel.event);
|
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 {
|
private addDialogButton(button: DialogButton, onSelect: () => void = () => undefined, registerClickEvent: boolean = true): Button {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { TabbedPanel, IPanelTab, IPanelView } from 'sql/base/browser/ui/panel/pa
|
|||||||
import { bootstrapAngular } from 'sql/services/bootstrap/bootstrapService';
|
import { bootstrapAngular } from 'sql/services/bootstrap/bootstrapService';
|
||||||
import { DialogModule } from 'sql/platform/dialog/dialog.module';
|
import { DialogModule } from 'sql/platform/dialog/dialog.module';
|
||||||
import { DialogComponentParams } from 'sql/platform/dialog/dialogContainer.component';
|
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 * as DOM from 'vs/base/browser/dom';
|
||||||
import { Builder } from 'vs/base/browser/builder';
|
import { Builder } from 'vs/base/browser/builder';
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
import * as sqlops from 'sqlops';
|
import * as sqlops from 'sqlops';
|
||||||
import { localize } from 'vs/nls';
|
import { localize } from 'vs/nls';
|
||||||
import { Event, Emitter } from 'vs/base/common/event';
|
import { Event, Emitter } from 'vs/base/common/event';
|
||||||
|
import { DialogMessage } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||||
|
|
||||||
export class ModelViewPane {
|
export class ModelViewPane {
|
||||||
private _valid: boolean = true;
|
private _valid: boolean = true;
|
||||||
@@ -45,6 +46,9 @@ export class Dialog extends ModelViewPane {
|
|||||||
public okButton: DialogButton = new DialogButton(Dialog.DONE_BUTTON_LABEL, true);
|
public okButton: DialogButton = new DialogButton(Dialog.DONE_BUTTON_LABEL, true);
|
||||||
public cancelButton: DialogButton = new DialogButton(Dialog.CANCEL_BUTTON_LABEL, true);
|
public cancelButton: DialogButton = new DialogButton(Dialog.CANCEL_BUTTON_LABEL, true);
|
||||||
public customButtons: DialogButton[];
|
public customButtons: DialogButton[];
|
||||||
|
private _onMessageChange = new Emitter<DialogMessage>();
|
||||||
|
public readonly onMessageChange = this._onMessageChange.event;
|
||||||
|
private _message: DialogMessage;
|
||||||
|
|
||||||
constructor(public title: string, content?: string | DialogTab[]) {
|
constructor(public title: string, content?: string | DialogTab[]) {
|
||||||
super();
|
super();
|
||||||
@@ -52,6 +56,17 @@ export class Dialog extends ModelViewPane {
|
|||||||
this.content = content;
|
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 {
|
export class DialogButton implements sqlops.window.modelviewdialog.Button {
|
||||||
@@ -140,6 +155,9 @@ export class Wizard {
|
|||||||
private _pageRemovedEmitter = new Emitter<WizardPage>();
|
private _pageRemovedEmitter = new Emitter<WizardPage>();
|
||||||
public readonly onPageRemoved = this._pageRemovedEmitter.event;
|
public readonly onPageRemoved = this._pageRemovedEmitter.event;
|
||||||
private _navigationValidator: (pageChangeInfo: sqlops.window.modelviewdialog.WizardPageChangeInfo) => boolean | Thenable<boolean>;
|
private _navigationValidator: (pageChangeInfo: sqlops.window.modelviewdialog.WizardPageChangeInfo) => boolean | Thenable<boolean>;
|
||||||
|
private _onMessageChange = new Emitter<DialogMessage>();
|
||||||
|
public readonly onMessageChange = this._onMessageChange.event;
|
||||||
|
private _message: DialogMessage;
|
||||||
|
|
||||||
constructor(public title: string) { }
|
constructor(public title: string) { }
|
||||||
|
|
||||||
@@ -207,4 +225,15 @@ export class Wizard {
|
|||||||
return Promise.resolve(true);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
.dialogModal-body {
|
.dialogModal-body {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: column;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-width: 500px;
|
min-width: 500px;
|
||||||
@@ -29,4 +29,4 @@
|
|||||||
|
|
||||||
.footer-button.dialogModal-hidden {
|
.footer-button.dialogModal-hidden {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import { localize } from 'vs/nls';
|
|||||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||||
import { Emitter } from 'vs/base/common/event';
|
import { Emitter } from 'vs/base/common/event';
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
|
import { DialogMessage, MessageLevel } from '../../workbench/api/common/sqlExtHostTypes';
|
||||||
|
|
||||||
export class WizardModal extends Modal {
|
export class WizardModal extends Modal {
|
||||||
private _dialogPanes = new Map<WizardPage, DialogPane>();
|
private _dialogPanes = new Map<WizardPage, DialogPane>();
|
||||||
@@ -58,7 +59,7 @@ export class WizardModal extends Modal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
super.render();
|
super.render(true);
|
||||||
attachModalDialogStyler(this, this._themeService);
|
attachModalDialogStyler(this, this._themeService);
|
||||||
|
|
||||||
if (this.backButton) {
|
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 });
|
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._previousButton = this.addDialogButton(this._wizard.backButton, () => this.showPage(this.getCurrentPage() - 1));
|
||||||
this._nextButton = this.addDialogButton(this._wizard.nextButton, () => 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);
|
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._wizard.doneButton.registerClickEvent(this._onDone.event);
|
||||||
this._cancelButton = this.addDialogButton(this._wizard.cancelButton, () => this.cancel(), false);
|
this._cancelButton = this.addDialogButton(this._wizard.cancelButton, () => this.cancel(), false);
|
||||||
this._wizard.cancelButton.registerClickEvent(this._onCancel.event);
|
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 {
|
private addDialogButton(button: DialogButton, onSelect: () => void = () => undefined, registerClickEvent: boolean = true): Button {
|
||||||
@@ -100,7 +119,6 @@ export class WizardModal extends Modal {
|
|||||||
this._body = bodyBuilder.getHTMLElement();
|
this._body = bodyBuilder.getHTMLElement();
|
||||||
});
|
});
|
||||||
|
|
||||||
let builder = new Builder(this._body);
|
|
||||||
this._wizard.pages.forEach(page => {
|
this._wizard.pages.forEach(page => {
|
||||||
this.registerPage(page);
|
this.registerPage(page);
|
||||||
});
|
});
|
||||||
|
|||||||
30
src/sql/sqlops.proposed.d.ts
vendored
30
src/sql/sqlops.proposed.d.ts
vendored
@@ -582,6 +582,24 @@ declare module 'sqlops' {
|
|||||||
*/
|
*/
|
||||||
export function createWizard(title: string): Wizard;
|
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 {
|
export interface ModelViewPanel {
|
||||||
/**
|
/**
|
||||||
* Register model view content for the dialog.
|
* Register model view content for the dialog.
|
||||||
@@ -632,6 +650,12 @@ declare module 'sqlops' {
|
|||||||
* Any additional buttons that should be displayed
|
* Any additional buttons that should be displayed
|
||||||
*/
|
*/
|
||||||
customButtons: Button[];
|
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 {
|
export interface DialogTab extends ModelViewPanel {
|
||||||
@@ -798,6 +822,12 @@ declare module 'sqlops' {
|
|||||||
* cancel it.
|
* cancel it.
|
||||||
*/
|
*/
|
||||||
registerNavigationValidator(validator: (pageChangeInfo: WizardPageChangeInfo) => boolean | Thenable<boolean>): void;
|
registerNavigationValidator(validator: (pageChangeInfo: WizardPageChangeInfo) => boolean | Thenable<boolean>): 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -149,6 +149,7 @@ export interface IModelViewDialogDetails {
|
|||||||
okButton: number;
|
okButton: number;
|
||||||
cancelButton: number;
|
cancelButton: number;
|
||||||
customButtons: number[];
|
customButtons: number[];
|
||||||
|
message: DialogMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IModelViewTabDetails {
|
export interface IModelViewTabDetails {
|
||||||
@@ -179,6 +180,18 @@ export interface IModelViewWizardDetails {
|
|||||||
nextButton: number;
|
nextButton: number;
|
||||||
backButton: number;
|
backButton: number;
|
||||||
customButtons: 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
|
/// Card-related APIs that need to be here to avoid early load issues
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ class DialogImpl extends ModelViewPanelImpl implements sqlops.window.modelviewdi
|
|||||||
public okButton: sqlops.window.modelviewdialog.Button;
|
public okButton: sqlops.window.modelviewdialog.Button;
|
||||||
public cancelButton: sqlops.window.modelviewdialog.Button;
|
public cancelButton: sqlops.window.modelviewdialog.Button;
|
||||||
public customButtons: sqlops.window.modelviewdialog.Button[];
|
public customButtons: sqlops.window.modelviewdialog.Button[];
|
||||||
|
private _message: sqlops.window.modelviewdialog.DialogMessage;
|
||||||
|
|
||||||
constructor(extHostModelViewDialog: ExtHostModelViewDialog,
|
constructor(extHostModelViewDialog: ExtHostModelViewDialog,
|
||||||
extHostModelView: ExtHostModelViewShape) {
|
extHostModelView: ExtHostModelViewShape) {
|
||||||
@@ -105,6 +106,15 @@ class DialogImpl extends ModelViewPanelImpl implements sqlops.window.modelviewdi
|
|||||||
super.setModelViewId(value);
|
super.setModelViewId(value);
|
||||||
this.content = 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 {
|
class TabImpl extends ModelViewPanelImpl implements sqlops.window.modelviewdialog.DialogTab {
|
||||||
@@ -218,6 +228,7 @@ class WizardImpl implements sqlops.window.modelviewdialog.Wizard {
|
|||||||
private _pageChangedEmitter = new Emitter<sqlops.window.modelviewdialog.WizardPageChangeInfo>();
|
private _pageChangedEmitter = new Emitter<sqlops.window.modelviewdialog.WizardPageChangeInfo>();
|
||||||
public readonly onPageChanged = this._pageChangedEmitter.event;
|
public readonly onPageChanged = this._pageChangedEmitter.event;
|
||||||
private _navigationValidator: (info: sqlops.window.modelviewdialog.WizardPageChangeInfo) => boolean | Thenable<boolean>;
|
private _navigationValidator: (info: sqlops.window.modelviewdialog.WizardPageChangeInfo) => boolean | Thenable<boolean>;
|
||||||
|
private _message: sqlops.window.modelviewdialog.DialogMessage;
|
||||||
|
|
||||||
constructor(public title: string, private _extHostModelViewDialog: ExtHostModelViewDialog) {
|
constructor(public title: string, private _extHostModelViewDialog: ExtHostModelViewDialog) {
|
||||||
this.doneButton = this._extHostModelViewDialog.createButton(DONE_LABEL);
|
this.doneButton = this._extHostModelViewDialog.createButton(DONE_LABEL);
|
||||||
@@ -234,6 +245,15 @@ class WizardImpl implements sqlops.window.modelviewdialog.Wizard {
|
|||||||
return this._currentPage;
|
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<void> {
|
public addPage(page: sqlops.window.modelviewdialog.WizardPage, index?: number): Thenable<void> {
|
||||||
return this._extHostModelViewDialog.updateWizardPage(page).then(() => {
|
return this._extHostModelViewDialog.updateWizardPage(page).then(() => {
|
||||||
this._extHostModelViewDialog.addPage(this, page, index);
|
this._extHostModelViewDialog.addPage(this, page, index);
|
||||||
@@ -387,7 +407,8 @@ export class ExtHostModelViewDialog implements ExtHostModelViewDialogShape {
|
|||||||
okButton: this.getHandle(dialog.okButton),
|
okButton: this.getHandle(dialog.okButton),
|
||||||
cancelButton: this.getHandle(dialog.cancelButton),
|
cancelButton: this.getHandle(dialog.cancelButton),
|
||||||
content: dialog.content && typeof dialog.content !== 'string' ? dialog.content.map(tab => this.getHandle(tab)) : dialog.content as string,
|
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),
|
generateScriptButton: this.getHandle(wizard.generateScriptButton),
|
||||||
doneButton: this.getHandle(wizard.doneButton),
|
doneButton: this.getHandle(wizard.doneButton),
|
||||||
nextButton: this.getHandle(wizard.nextButton),
|
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
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -94,6 +94,8 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape
|
|||||||
dialog.customButtons = details.customButtons.map(buttonHandle => this.getButton(buttonHandle));
|
dialog.customButtons = details.customButtons.map(buttonHandle => this.getButton(buttonHandle));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dialog.message = details.message;
|
||||||
|
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,6 +171,7 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape
|
|||||||
if (details.customButtons !== undefined) {
|
if (details.customButtons !== undefined) {
|
||||||
wizard.customButtons = details.customButtons.map(buttonHandle => this.getButton(buttonHandle));
|
wizard.customButtons = details.customButtons.map(buttonHandle => this.getButton(buttonHandle));
|
||||||
}
|
}
|
||||||
|
wizard.message = details.message;
|
||||||
|
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -315,7 +315,8 @@ export function createApiFactory(
|
|||||||
},
|
},
|
||||||
createWizard(title: string): sqlops.window.modelviewdialog.Wizard {
|
createWizard(title: string): sqlops.window.modelviewdialog.Wizard {
|
||||||
return extHostModelViewDialog.createWizard(title);
|
return extHostModelViewDialog.createWizard(title);
|
||||||
}
|
},
|
||||||
|
MessageLevel: sqlExtHostTypes.MessageLevel
|
||||||
};
|
};
|
||||||
|
|
||||||
const window: typeof sqlops.window = {
|
const window: typeof sqlops.window = {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { Mock, It, Times } from 'typemoq';
|
|||||||
import { ExtHostModelViewDialog } from 'sql/workbench/api/node/extHostModelViewDialog';
|
import { ExtHostModelViewDialog } from 'sql/workbench/api/node/extHostModelViewDialog';
|
||||||
import { MainThreadModelViewDialogShape, ExtHostModelViewShape } from 'sql/workbench/api/node/sqlExtHost.protocol';
|
import { MainThreadModelViewDialogShape, ExtHostModelViewShape } from 'sql/workbench/api/node/sqlExtHost.protocol';
|
||||||
import { IMainContext } from 'vs/workbench/api/node/extHost.protocol';
|
import { IMainContext } from 'vs/workbench/api/node/extHost.protocol';
|
||||||
|
import { MessageLevel } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
@@ -290,4 +291,20 @@ suite('ExtHostModelViewDialog Tests', () => {
|
|||||||
assert.equal(validationInfo.lastPage, lastPage);
|
assert.equal(validationInfo.lastPage, lastPage);
|
||||||
assert.equal(validationInfo.newPage, newPage);
|
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());
|
||||||
|
});
|
||||||
});
|
});
|
||||||
@@ -7,7 +7,7 @@ import * as assert from 'assert';
|
|||||||
import { Mock, It, Times } from 'typemoq';
|
import { Mock, It, Times } from 'typemoq';
|
||||||
import { MainThreadModelViewDialog } from 'sql/workbench/api/node/mainThreadModelViewDialog';
|
import { MainThreadModelViewDialog } from 'sql/workbench/api/node/mainThreadModelViewDialog';
|
||||||
import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol';
|
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 { CustomDialogService } from 'sql/platform/dialog/customDialogService';
|
||||||
import { Dialog, DialogTab, Wizard } from 'sql/platform/dialog/dialogTypes';
|
import { Dialog, DialogTab, Wizard } from 'sql/platform/dialog/dialogTypes';
|
||||||
import { ExtHostModelViewDialogShape } from 'sql/workbench/api/node/sqlExtHost.protocol';
|
import { ExtHostModelViewDialogShape } from 'sql/workbench/api/node/sqlExtHost.protocol';
|
||||||
@@ -112,7 +112,8 @@ suite('MainThreadModelViewDialog Tests', () => {
|
|||||||
content: [tab1Handle, tab2Handle],
|
content: [tab1Handle, tab2Handle],
|
||||||
okButton: okButtonHandle,
|
okButton: okButtonHandle,
|
||||||
cancelButton: cancelButtonHandle,
|
cancelButton: cancelButtonHandle,
|
||||||
customButtons: [button1Handle, button2Handle]
|
customButtons: [button1Handle, button2Handle],
|
||||||
|
message: undefined
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set up the wizard details
|
// Set up the wizard details
|
||||||
@@ -152,7 +153,8 @@ suite('MainThreadModelViewDialog Tests', () => {
|
|||||||
currentPage: undefined,
|
currentPage: undefined,
|
||||||
title: 'wizard_title',
|
title: 'wizard_title',
|
||||||
customButtons: [],
|
customButtons: [],
|
||||||
pages: [page1Handle, page2Handle]
|
pages: [page1Handle, page2Handle],
|
||||||
|
message: undefined
|
||||||
};
|
};
|
||||||
|
|
||||||
// Register the buttons, tabs, and dialog
|
// Register the buttons, tabs, and dialog
|
||||||
@@ -322,10 +324,27 @@ suite('MainThreadModelViewDialog Tests', () => {
|
|||||||
mockExtHostModelViewDialog.setup(x => x.$validateNavigation(It.isAny(), It.isAny()));
|
mockExtHostModelViewDialog.setup(x => x.$validateNavigation(It.isAny(), It.isAny()));
|
||||||
|
|
||||||
// If I call validateNavigation on the wizard that gets created
|
// If I call validateNavigation on the wizard that gets created
|
||||||
let wizard: Wizard = (mainThreadModelViewDialog as any).getWizard(wizardHandle);
|
mainThreadModelViewDialog.$openWizard(wizardHandle);
|
||||||
wizard.validateNavigation(1);
|
openedWizard.validateNavigation(1);
|
||||||
|
|
||||||
// Then the call gets forwarded to the extension host
|
// 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());
|
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');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
Reference in New Issue
Block a user