diff --git a/src/sql/media/icons/common-icons.css b/src/sql/media/icons/common-icons.css index 6d2b78484a..1a93df7c1a 100644 --- a/src/sql/media/icons/common-icons.css +++ b/src/sql/media/icons/common-icons.css @@ -57,6 +57,12 @@ background: url("server_page_inverse.svg") center center no-repeat; } +.vs .icon.globalError, +.vs-dark .icon.globalError, +.hc-black .icon.globalError { + background: url("globalerror.svg") center center no-repeat; +} + .vs .icon.error, .vs-dark .icon.error, .hc-black .icon.error { diff --git a/src/sql/media/icons/globalerror.svg b/src/sql/media/icons/globalerror.svg new file mode 100644 index 0000000000..271117e28b --- /dev/null +++ b/src/sql/media/icons/globalerror.svg @@ -0,0 +1 @@ +globalerror \ No newline at end of file diff --git a/src/sql/parts/dashboard/common/dashboardHelper.ts b/src/sql/parts/dashboard/common/dashboardHelper.ts index f6bb49f585..6c61bd1657 100644 --- a/src/sql/parts/dashboard/common/dashboardHelper.ts +++ b/src/sql/parts/dashboard/common/dashboardHelper.ts @@ -178,8 +178,9 @@ export function getDashboardContainer(container: object): { result: boolean, mes if (!containerTypeFound) { let dashboardContainer = dashboardcontainerRegistry.getRegisteredContainer(key); if (!dashboardContainer) { - let error = nls.localize('unknownDashboardContainerError', '{0} is an unknown container.', key); - return { result: false, message: error, container: null }; + let errorMessage = nls.localize('unknownDashboardContainerError', '{0} is an unknown container.', key); + error(errorMessage); + return { result: false, message: errorMessage, container: null }; } else { container = dashboardContainer.container; } diff --git a/src/sql/parts/dashboard/common/dashboardPage.component.html b/src/sql/parts/dashboard/common/dashboardPage.component.html index 5ec536b133..b883819988 100644 --- a/src/sql/parts/dashboard/common/dashboardPage.component.html +++ b/src/sql/parts/dashboard/common/dashboardPage.component.html @@ -17,6 +17,8 @@ + + diff --git a/src/sql/parts/dashboard/common/dashboardPage.component.ts b/src/sql/parts/dashboard/common/dashboardPage.component.ts index 4838824ab5..7c70e70586 100644 --- a/src/sql/parts/dashboard/common/dashboardPage.component.ts +++ b/src/sql/parts/dashboard/common/dashboardPage.component.ts @@ -20,7 +20,6 @@ import { TabComponent } from 'sql/base/browser/ui/panel/tab.component'; import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from 'sql/services/bootstrap/bootstrapService'; import { AngularEventType } from 'sql/services/angularEventing/angularEventingService'; import { DashboardTab } from 'sql/parts/dashboard/common/interfaces'; -import { error } from 'sql/base/common/log'; import * as dashboardHelper from 'sql/parts/dashboard/common/dashboardHelper'; import { WIDGETS_CONTAINER } from 'sql/parts/dashboard/containers/dashboardWidgetContainer.contribution'; import { GRID_CONTAINER } from 'sql/parts/dashboard/containers/dashboardGridContainer.contribution'; @@ -191,12 +190,10 @@ export abstract class DashboardPage extends Disposable implements OnDestroy { let selectedTabs = dashboardTabs.map(v => { let containerResult = dashboardHelper.getDashboardContainer(v.container); if (!containerResult.result) { - let errorTitle = nls.localize('dashboardPage_loadTabError', 'Cannot open {0}. ', v.title); - this.dashboardService.messageService.show(Severity.Error, errorTitle + containerResult.message); - return null; + return { id: v.id, title: v.title, container: { 'error-container': undefined }, alwaysShow: v.alwaysShow }; } - let key = Object.keys(containerResult.container)[0]; + let key = Object.keys(containerResult.container)[0]; if (key === WIDGETS_CONTAINER || key === GRID_CONTAINER) { let configs = Object.values(containerResult.container)[0]; this._configModifiers.forEach(cb => { @@ -214,31 +211,26 @@ export abstract class DashboardPage extends Disposable implements OnDestroy { } return { id: v.id, title: v.title, container: containerResult.container, alwaysShow: v.alwaysShow }; }).map(v => { - if (v) { - let actions = []; - if (!v.alwaysShow) { - let pinnedTab = this._pinnedTabs.find(i => i.tabId === v.id); - actions.push(this.dashboardService.instantiationService.createInstance(PinUnpinTabAction, v.id, this.dashboardService.getUnderlyingUri(), !!pinnedTab)); - } - - let config = v as TabConfig; - config.context = this.context; - config.editable = false; - config.canClose = true; - config.actions = actions; - this.addNewTab(config); - return config; + let actions = []; + if (!v.alwaysShow) { + let pinnedTab = this._pinnedTabs.find(i => i.tabId === v.id); + actions.push(this.dashboardService.instantiationService.createInstance(PinUnpinTabAction, v.id, this.dashboardService.getUnderlyingUri(), !!pinnedTab)); } - return null; + + let config = v as TabConfig; + config.context = this.context; + config.editable = false; + config.canClose = true; + config.actions = actions; + this.addNewTab(config); + return config; }); if (openLastTab) { // put this immediately on the stack so that is ran *after* the tab is rendered setTimeout(() => { let selectedLastTab = selectedTabs.pop(); - if (selectedLastTab) { - this._panel.selectTab(selectedLastTab.id); - } + this._panel.selectTab(selectedLastTab.id); }); } } diff --git a/src/sql/parts/dashboard/containers/dashboardErrorContainer.component.ts b/src/sql/parts/dashboard/containers/dashboardErrorContainer.component.ts new file mode 100644 index 0000000000..ecc49c4fe4 --- /dev/null +++ b/src/sql/parts/dashboard/containers/dashboardErrorContainer.component.ts @@ -0,0 +1,58 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./dashboardErrorContainer'; + +import { Component, Inject, Input, forwardRef, ViewChild, ElementRef, ChangeDetectorRef, AfterViewInit } from '@angular/core'; + +import { TabConfig } from 'sql/parts/dashboard/common/dashboardWidget'; +import { DashboardTab } from 'sql/parts/dashboard/common/interfaces'; + +import Event, { Emitter } from 'vs/base/common/event'; +import * as nls from 'vs/nls'; + +@Component({ + selector: 'dashboard-error-container', + providers: [{ provide: DashboardTab, useExisting: forwardRef(() => DashboardErrorContainer) }], + template: ` +
+
+
+
+
+
+ ` +}) +export class DashboardErrorContainer extends DashboardTab implements AfterViewInit { + @Input() private tab: TabConfig; + private _onResize = new Emitter(); + public readonly onResize: Event = this._onResize.event; + + @ViewChild('errorMessage', { read: ElementRef }) private _errorMessageContainer: ElementRef; + constructor( + @Inject(forwardRef(() => ChangeDetectorRef)) protected _cd: ChangeDetectorRef + ) { + super(); + } + + ngAfterViewInit() { + let errorMessage = this._errorMessageContainer.nativeElement as HTMLElement; + errorMessage.innerHTML = nls.localize('dashboardNavSection_loadTabError', 'The {0} has an invalid content. Please contact extension owner.', this.tab.title); + } + + public get id(): string { + return this.tab.id; + } + + public get editable(): boolean { + return false; + } + + public layout() { + } + + public refresh(): void { + } +} diff --git a/src/sql/parts/dashboard/containers/dashboardErrorContainer.css b/src/sql/parts/dashboard/containers/dashboardErrorContainer.css new file mode 100644 index 0000000000..57b27a0534 --- /dev/null +++ b/src/sql/parts/dashboard/containers/dashboardErrorContainer.css @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +dashboard-error-container { + height: 100%; + width: 100%; +} + +dashboard-error-container .error-container { + padding: 6px; + background: #D02E00; + color: white; +} + +dashboard-error-container .error-container .icon.globalError { + height: 16px; + width: 16px; + float: left; + padding-right: 15px; +} + diff --git a/src/sql/parts/dashboard/containers/dashboardNavSection.component.html b/src/sql/parts/dashboard/containers/dashboardNavSection.component.html index 0f94fbd803..3a887ba105 100644 --- a/src/sql/parts/dashboard/containers/dashboardNavSection.component.html +++ b/src/sql/parts/dashboard/containers/dashboardNavSection.component.html @@ -12,5 +12,7 @@ + + \ No newline at end of file diff --git a/src/sql/parts/dashboard/containers/dashboardNavSection.component.ts b/src/sql/parts/dashboard/containers/dashboardNavSection.component.ts index e5534364ab..1a8478957c 100644 --- a/src/sql/parts/dashboard/containers/dashboardNavSection.component.ts +++ b/src/sql/parts/dashboard/containers/dashboardNavSection.component.ts @@ -12,14 +12,12 @@ import { WidgetConfig, TabConfig, NavSectionConfig } from 'sql/parts/dashboard/c import { PanelComponent, IPanelOptions, NavigationBarLayout } from 'sql/base/browser/ui/panel/panel.component'; import { TabComponent } from 'sql/base/browser/ui/panel/tab.component'; import { DashboardTab } from 'sql/parts/dashboard/common/interfaces'; -import { error } from 'sql/base/common/log'; import { WIDGETS_CONTAINER } from 'sql/parts/dashboard/containers/dashboardWidgetContainer.contribution'; import { GRID_CONTAINER } from 'sql/parts/dashboard/containers/dashboardGridContainer.contribution'; import * as dashboardHelper from 'sql/parts/dashboard/common/dashboardHelper'; import { Registry } from 'vs/platform/registry/common/platform'; import Event, { Emitter } from 'vs/base/common/event'; -import Severity from 'vs/base/common/severity'; import * as nls from 'vs/nls'; @Component({ @@ -90,9 +88,7 @@ export class DashboardNavSection extends DashboardTab implements OnDestroy, OnCh let containerResult = dashboardHelper.getDashboardContainer(v.container); if (!containerResult.result) { - let errorTitle = nls.localize('dashboardNavSection_loadTabError', 'There is an error while loading {0} section. ', v.title); - this.dashboardService.messageService.show(Severity.Error, errorTitle + containerResult.message); - return null; + return { id: v.id, title: v.title, container: { 'error-container': undefined } }; } let key = Object.keys(containerResult.container)[0]; @@ -113,23 +109,18 @@ export class DashboardNavSection extends DashboardTab implements OnDestroy, OnCh } return { id: v.id, title: v.title, container: containerResult.container }; }).map(v => { - if (v) { - let config = v as TabConfig; - config.context = this.tab.context; - config.editable = false; - config.canClose = false; - this.addNewTab(config); - return config; - } - return null; + let config = v as TabConfig; + config.context = this.tab.context; + config.editable = false; + config.canClose = false; + this.addNewTab(config); + return config; }); - if (selectedTabs && selectedTabs[0]) { - // put this immediately on the stack so that is ran *after* the tab is rendered - setTimeout(() => { - this._panel.selectTab(selectedTabs[0].id); - }); - } + // put this immediately on the stack so that is ran *after* the tab is rendered + setTimeout(() => { + this._panel.selectTab(selectedTabs[0].id); + }); } } diff --git a/src/sql/parts/dashboard/dashboard.module.ts b/src/sql/parts/dashboard/dashboard.module.ts index 06ec40d0f4..dc42c7f84e 100644 --- a/src/sql/parts/dashboard/dashboard.module.ts +++ b/src/sql/parts/dashboard/dashboard.module.ts @@ -30,6 +30,7 @@ import { DashboardWidgetWrapper } from 'sql/parts/dashboard/contents/dashboardWi import { DashboardWidgetContainer } from 'sql/parts/dashboard/containers/dashboardWidgetContainer.component'; import { DashboardGridContainer } from 'sql/parts/dashboard/containers/dashboardGridContainer.component'; import { DashboardWebviewContainer } from 'sql/parts/dashboard/containers/dashboardWebviewContainer.component'; +import { DashboardErrorContainer } from 'sql/parts/dashboard/containers/dashboardErrorContainer.component'; import { DashboardNavSection } from 'sql/parts/dashboard/containers/dashboardNavSection.component'; import { WidgetContent } from 'sql/parts/dashboard/contents/widgetContent.component'; import { WebviewContent } from 'sql/parts/dashboard/contents/webviewContent.component'; @@ -37,7 +38,7 @@ import { BreadcrumbComponent } from 'sql/base/browser/ui/breadcrumb/breadcrumb.c import { IBreadcrumbService } from 'sql/base/browser/ui/breadcrumb/interfaces'; import { DashboardHomeContainer } from 'sql/parts/dashboard/containers/dashboardHomeContainer.component'; -let baseComponents = [DashboardHomeContainer, DashboardComponent, DashboardWidgetWrapper, DashboardWebviewContainer, DashboardWidgetContainer, DashboardGridContainer, DashboardNavSection, WidgetContent, WebviewContent, ComponentHostDirective, BreadcrumbComponent]; +let baseComponents = [DashboardHomeContainer, DashboardComponent, DashboardWidgetWrapper, DashboardWebviewContainer, DashboardWidgetContainer, DashboardGridContainer, DashboardErrorContainer, DashboardNavSection, WidgetContent, WebviewContent, ComponentHostDirective, BreadcrumbComponent]; /* Panel */ import { PanelModule } from 'sql/base/browser/ui/panel/panel.module';