diff --git a/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcDashboard.ts b/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcDashboard.ts index ea4a94c5c1..b33d361bfc 100644 --- a/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcDashboard.ts +++ b/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcDashboard.ts @@ -38,7 +38,7 @@ export class BdcDashboard extends InitializingComponent { this.dashboard.registerTabs(async (modelView: azdata.ModelView) => { this.modelView = modelView; - const overviewPage = new BdcDashboardOverviewPage(this.model, modelView); + const overviewPage = new BdcDashboardOverviewPage(this.model, modelView, this.dashboard); this.overviewTab = { title: loc.bdcOverview, id: 'overview-tab', diff --git a/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcDashboardOverviewPage.ts b/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcDashboardOverviewPage.ts index df50b587e7..cedfadbc0f 100644 --- a/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcDashboardOverviewPage.ts +++ b/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcDashboardOverviewPage.ts @@ -31,7 +31,7 @@ export class BdcDashboardOverviewPage extends BdcDashboardPage { private endpointsErrorMessage: azdata.TextComponent; private serviceStatusErrorMessage: azdata.TextComponent; - constructor(model: BdcDashboardModel, modelView: azdata.ModelView) { + constructor(model: BdcDashboardModel, modelView: azdata.ModelView, private dashboard: azdata.window.ModelViewDashboard) { super(model, modelView); this.model.onDidUpdateEndpoints(endpoints => this.eventuallyRunOnInitialized(() => this.handleEndpointsUpdate(endpoints))); this.model.onDidUpdateBdcStatus(bdcStatus => this.eventuallyRunOnInitialized(() => this.handleBdcStatusUpdate(bdcStatus))); @@ -339,7 +339,7 @@ export class BdcDashboardOverviewPage extends BdcDashboardPage { CSSStyles: { ...cssStyles.text } }).component(); nameCell.onDidClick(() => { - //this.dashboard.switchToServiceTab(serviceStatus.serviceName); TODO: Enable direct link to tab page + this.dashboard.selectTab(serviceStatus.serviceName); }); const viewDetailsButton = serviceStatus.healthStatus !== 'healthy' && serviceStatus.details && serviceStatus.details.length > 0 ? createViewDetailsButton(this.modelView.modelBuilder, serviceStatus.details) : undefined; diff --git a/src/sql/azdata.proposed.d.ts b/src/sql/azdata.proposed.d.ts index 067cfa3b04..9781c2ba27 100644 --- a/src/sql/azdata.proposed.d.ts +++ b/src/sql/azdata.proposed.d.ts @@ -217,6 +217,12 @@ declare module 'azdata' { * @param tabs new tabs */ updateTabs(tabs: (Tab | TabGroup)[]): void; + + /** + * Selects the tab with the specified id + * @param id The id of the tab to select + */ + selectTab(id: string): void; } /** @@ -349,6 +355,7 @@ declare module 'azdata' { registerTabs(handler: (view: ModelView) => Thenable<(DashboardTab | DashboardTabGroup)[]>): void; open(): Thenable; updateTabs(tabs: (DashboardTab | DashboardTabGroup)[]): void; + selectTab(id: string): void; } export function createModelViewDashboard(title: string, options?: ModelViewDashboardOptions): ModelViewDashboard; diff --git a/src/sql/platform/dashboard/browser/interfaces.ts b/src/sql/platform/dashboard/browser/interfaces.ts index a2924ca95a..5e7e8323c2 100644 --- a/src/sql/platform/dashboard/browser/interfaces.ts +++ b/src/sql/platform/dashboard/browser/interfaces.ts @@ -23,6 +23,13 @@ export enum ComponentEventType { onEnterKeyPressed } +/** + * Actions that can be handled by ModelView components + */ +export enum ModelViewAction { + SelectTab = 'selectTab' +} + /** * Defines a component and can be used to map from the model-backed version of the * world to the frontend UI; @@ -95,6 +102,7 @@ export interface IComponent extends IDisposable { setDataProvider(handle: number, componentId: string, context: any): void; refreshDataProvider(item: any): void; focus(): void; + doAction(action: string, ...args: any[]): void; } export enum ModelComponentTypes { diff --git a/src/sql/platform/model/browser/modelViewService.ts b/src/sql/platform/model/browser/modelViewService.ts index d88e7ca910..73b3d95c42 100644 --- a/src/sql/platform/model/browser/modelViewService.ts +++ b/src/sql/platform/model/browser/modelViewService.ts @@ -45,4 +45,5 @@ export interface IModelView extends IView { validate(componentId: string): Thenable; readonly onDestroy: Event; focus(componentId: string): void; + doAction(componentId: string, action: string, ...args: any[]): void; } diff --git a/src/sql/workbench/api/browser/mainThreadModelView.ts b/src/sql/workbench/api/browser/mainThreadModelView.ts index d28288ef8d..22837542ce 100644 --- a/src/sql/workbench/api/browser/mainThreadModelView.ts +++ b/src/sql/workbench/api/browser/mainThreadModelView.ts @@ -104,6 +104,10 @@ export class MainThreadModelView extends Disposable implements MainThreadModelVi return new Promise(resolve => this.execModelViewAction(handle, (modelView) => resolve(modelView.focus(componentId)))); } + $doAction(handle: number, componentId: string, action: string, ...args: any[]): Thenable { + return new Promise(resolve => this.execModelViewAction(handle, (modelView) => resolve(modelView.doAction(componentId, action, ...args)))); + } + private runCustomValidations(handle: number, componentId: string): Thenable { return this._proxy.$runCustomValidations(handle, componentId); } diff --git a/src/sql/workbench/api/common/extHostModelView.ts b/src/sql/workbench/api/common/extHostModelView.ts index 35e6f2bf0a..6dd4f6a106 100644 --- a/src/sql/workbench/api/common/extHostModelView.ts +++ b/src/sql/workbench/api/common/extHostModelView.ts @@ -15,7 +15,7 @@ import * as vscode from 'vscode'; import * as azdata from 'azdata'; import { SqlMainContext, ExtHostModelViewShape, MainThreadModelViewShape, ExtHostModelViewTreeViewsShape } from 'sql/workbench/api/common/sqlExtHost.protocol'; -import { IItemConfig, ModelComponentTypes, IComponentShape, IComponentEventArgs, ComponentEventType, ColumnSizingMode } from 'sql/workbench/api/common/sqlExtHostTypes'; +import { IItemConfig, ModelComponentTypes, IComponentShape, IComponentEventArgs, ComponentEventType, ColumnSizingMode, ModelViewAction } from 'sql/workbench/api/common/sqlExtHostTypes'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { firstIndex } from 'vs/base/common/arrays'; import { ILogService } from 'vs/platform/log/common/log'; @@ -818,6 +818,10 @@ class ComponentWrapper implements azdata.Component { public focus() { return this._proxy.$focus(this._handle, this._id); } + + public doAction(action: ModelViewAction, ...args: any[]): Thenable { + return this._proxy.$doAction(this._handle, this._id, action, ...args); + } } class ComponentWithIconWrapper extends ComponentWrapper { @@ -1755,7 +1759,7 @@ class TabbedPanelComponentWrapper extends ComponentWrapper implements azdata.Tab const itemConfigs = createFromTabs(tabs); // Go through all of the tabs and either update their layout if they already exist // or add them if they don't. - // We do not currently support reordering or removing tabs. + // We do not currently support reordering or removing tabs. itemConfigs.forEach(newItemConfig => { const existingTab = this.itemConfigs.find(itemConfig => newItemConfig.config.id === itemConfig.config.id); if (existingTab) { @@ -1766,6 +1770,10 @@ class TabbedPanelComponentWrapper extends ComponentWrapper implements azdata.Tab }); } + public selectTab(id: string): void { + this.doAction(ModelViewAction.SelectTab, id); + } + public get onTabChanged(): vscode.Event { let emitter = this._emitterMap.get(ComponentEventType.onDidChange); return emitter && emitter.event; diff --git a/src/sql/workbench/api/common/extHostModelViewDialog.ts b/src/sql/workbench/api/common/extHostModelViewDialog.ts index f59115acb3..a0c93bee1d 100644 --- a/src/sql/workbench/api/common/extHostModelViewDialog.ts +++ b/src/sql/workbench/api/common/extHostModelViewDialog.ts @@ -526,6 +526,10 @@ class ModelViewDashboardImpl implements azdata.window.ModelViewDashboard { }); return tabs; } + + selectTab(id: string): void { + this._tabbedPanel.selectTab(id); + } } export class ExtHostModelViewDialog implements ExtHostModelViewDialogShape { diff --git a/src/sql/workbench/api/common/sqlExtHost.protocol.ts b/src/sql/workbench/api/common/sqlExtHost.protocol.ts index 2253753760..98a75306b8 100644 --- a/src/sql/workbench/api/common/sqlExtHost.protocol.ts +++ b/src/sql/workbench/api/common/sqlExtHost.protocol.ts @@ -739,6 +739,7 @@ export interface MainThreadModelViewShape extends IDisposable { $setDataProvider(handle: number, componentId: string): Thenable; $refreshDataProvider(handle: number, componentId: string, item?: any): Thenable; $focus(handle: number, componentId: string): Thenable; + $doAction(handle: number, componentId: string, action: string, ...args: any[]): Thenable; } export interface ExtHostObjectExplorerShape { diff --git a/src/sql/workbench/api/common/sqlExtHostTypes.ts b/src/sql/workbench/api/common/sqlExtHostTypes.ts index de53026283..2a56dc4a62 100644 --- a/src/sql/workbench/api/common/sqlExtHostTypes.ts +++ b/src/sql/workbench/api/common/sqlExtHostTypes.ts @@ -179,6 +179,10 @@ export enum ModelComponentTypes { PropertiesContainer } +export enum ModelViewAction { + SelectTab = 'selectTab' +} + export enum ColumnSizingMode { ForceFit = 0, // all columns will be sized to fit in viewable space, no horiz scroll bar AutoFit = 1, // columns will be ForceFit up to a certain number; currently 3. At 4 or more the behavior will switch to NO force fit diff --git a/src/sql/workbench/browser/modelComponents/componentBase.ts b/src/sql/workbench/browser/modelComponents/componentBase.ts index 693a8df9e7..33a02e3c1d 100644 --- a/src/sql/workbench/browser/modelComponents/componentBase.ts +++ b/src/sql/workbench/browser/modelComponents/componentBase.ts @@ -266,6 +266,10 @@ export abstract class ComponentBase extends Disposable implements IComponent, On (this._el.nativeElement).focus(); } + public doAction(action: string, ...args: any[]): void { + // no-op, components should override this if they want to handle actions + } + protected onkeydown(domNode: HTMLElement, listener: (e: StandardKeyboardEvent) => void): void { this._register(addDisposableListener(domNode, EventType.KEY_DOWN, (e: KeyboardEvent) => listener(new StandardKeyboardEvent(e)))); } diff --git a/src/sql/workbench/browser/modelComponents/tabbedPanel.component.ts b/src/sql/workbench/browser/modelComponents/tabbedPanel.component.ts index eacbecdbfe..0e4522d875 100644 --- a/src/sql/workbench/browser/modelComponents/tabbedPanel.component.ts +++ b/src/sql/workbench/browser/modelComponents/tabbedPanel.component.ts @@ -2,16 +2,17 @@ * 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!./media/tabbedPanel'; import { AfterViewInit, ChangeDetectorRef, Component, ElementRef, forwardRef, Inject, Input, OnDestroy, ViewChild } from '@angular/core'; import { NavigationBarLayout, PanelComponent } from 'sql/base/browser/ui/panel/panel.component'; import { TabType } from 'sql/base/browser/ui/panel/tab.component'; import { ContainerBase, ItemDescriptor } from 'sql/workbench/browser/modelComponents/componentBase'; -import { ComponentEventType, IComponent, IComponentDescriptor, IModelStore } from 'sql/platform/dashboard/browser/interfaces'; -import 'vs/css!./media/tabbedPanel'; +import { ComponentEventType, IComponent, IComponentDescriptor, IModelStore, ModelViewAction } from 'sql/platform/dashboard/browser/interfaces'; import { IUserFriendlyIcon, createIconCssClass } from 'sql/workbench/browser/modelComponents/iconUtils'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { attachTabbedPanelStyler } from 'sql/workbench/common/styler'; import { TabbedPanelLayout } from 'azdata'; +import { ILogService } from 'vs/platform/log/common/log'; export interface TabConfig { title: string; @@ -50,7 +51,8 @@ export default class TabbedPanelComponent extends ContainerBase imple constructor( @Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef, @Inject(forwardRef(() => ElementRef)) el: ElementRef, - @Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService + @Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService, + @Inject(ILogService) private logService: ILogService ) { super(changeRef, el); } @@ -125,4 +127,19 @@ export default class TabbedPanelComponent extends ContainerBase imple onItemLayoutUpdated(item: ItemDescriptor): void { this._panel.updateTab(item.config.id, { title: item.config.title, iconClass: item.config.icon ? createIconCssClass(item.config.icon) : undefined }); } + + public doAction(action: string, ...args: any[]): void { + switch (action) { + case ModelViewAction.SelectTab: + if (typeof args?.[0] !== 'string') { + this.logService.warn(`Got unknown arg type for SelectTab action ${args?.[0]}`); + return; + } + this.selectTab(args[0]); + } + } + + public selectTab(id: string): void { + this._panel.selectTab(id); + } } diff --git a/src/sql/workbench/browser/modelComponents/viewBase.ts b/src/sql/workbench/browser/modelComponents/viewBase.ts index 33f770f187..085e357348 100644 --- a/src/sql/workbench/browser/modelComponents/viewBase.ts +++ b/src/sql/workbench/browser/modelComponents/viewBase.ts @@ -156,4 +156,8 @@ export abstract class ViewBase extends AngularDisposable implements IModelView { public focus(componentId: string): void { return this.queueAction(componentId, (component) => component.focus()); } + + public doAction(componentId: string, action: string, ...args: any[]): void { + return this.queueAction(componentId, (component) => component.doAction(action, ...args)); + } }