From 05d0a896556a157b201964095bdac6a77b2c9c8c Mon Sep 17 00:00:00 2001 From: Karl Burtram Date: Thu, 12 Jul 2018 09:32:43 -0700 Subject: [PATCH] Fix Job History scroll and resize issues (#1912) --- .../views/jobHistory.component.html | 5 +- .../views/jobHistory.component.ts | 140 +++++++++++------- .../parts/jobManagement/views/jobHistory.css | 6 +- .../jobManagement/views/jobManagementView.ts | 2 +- .../views/jobStepsView.component.ts | 41 ++++- 5 files changed, 124 insertions(+), 70 deletions(-) diff --git a/src/sql/parts/jobManagement/views/jobHistory.component.html b/src/sql/parts/jobManagement/views/jobHistory.component.html index 8e51699d2a..173ffd319a 100644 --- a/src/sql/parts/jobManagement/views/jobHistory.component.html +++ b/src/sql/parts/jobManagement/views/jobHistory.component.html @@ -154,8 +154,9 @@ - +
+ +

No Steps Available

- diff --git a/src/sql/parts/jobManagement/views/jobHistory.component.ts b/src/sql/parts/jobManagement/views/jobHistory.component.ts index d578eff46d..c482747e9d 100644 --- a/src/sql/parts/jobManagement/views/jobHistory.component.ts +++ b/src/sql/parts/jobManagement/views/jobHistory.component.ts @@ -7,6 +7,7 @@ import 'vs/css!./jobHistory'; import 'vs/css!sql/media/icons/common-icons'; import * as sqlops from 'sqlops'; +import * as dom from 'vs/base/browser/dom'; import { OnInit, Component, Inject, Input, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, ChangeDetectionStrategy, Injectable } from '@angular/core'; import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar'; import { AgentViewComponent } from 'sql/parts/jobManagement/agent/agentView.component'; @@ -22,30 +23,33 @@ import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/work import { attachListStyler } from 'vs/platform/theme/common/styler'; import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { Disposable } from 'vs/base/common/lifecycle'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { JobManagementView } from 'sql/parts/jobManagement/views/jobManagementView'; +import { TabChild } from 'sql/base/browser/ui/panel/tab.component'; +import { IDashboardService } from 'sql/services/dashboard/common/dashboardService'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; export const DASHBOARD_SELECTOR: string = 'jobhistory-component'; @Component({ selector: DASHBOARD_SELECTOR, templateUrl: decodeURI(require.toUrl('./jobHistory.component.html')), + providers: [{ provide: TabChild, useExisting: forwardRef(() => JobHistoryComponent) }], changeDetection: ChangeDetectionStrategy.OnPush }) @Injectable() -export class JobHistoryComponent extends Disposable implements OnInit { +export class JobHistoryComponent extends JobManagementView implements OnInit { private _tree: Tree; private _treeController: JobHistoryController; private _treeDataSource: JobHistoryDataSource; private _treeRenderer: JobHistoryRenderer; private _treeFilter: JobHistoryFilter; - private _actionBar: Taskbar; @ViewChild('table') private _tableContainer: ElementRef; - @ViewChild('actionbarContainer') private _actionbarContainer: ElementRef; + @ViewChild('jobsteps') private _jobStepsView: ElementRef; @Input() public agentJobInfo: sqlops.AgentJobInfo = undefined; @Input() public agentJobHistories: sqlops.AgentJobHistoryInfo[] = undefined; @@ -67,21 +71,23 @@ export class JobHistoryComponent extends Disposable implements OnInit { constructor( @Inject(forwardRef(() => ElementRef)) el: ElementRef, @Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef, - @Inject(forwardRef(() => CommonServiceInterface)) private _dashboardService: CommonServiceInterface, + @Inject(forwardRef(() => CommonServiceInterface)) commonService: CommonServiceInterface, @Inject(forwardRef(() => AgentViewComponent)) private _agentViewComponent: AgentViewComponent, @Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService, @Inject(INotificationService) private _notificationService: INotificationService, @Inject(IInstantiationService) private instantiationService: IInstantiationService, @Inject(IContextMenuService) private contextMenuService: IContextMenuService, - @Inject(IJobManagementService) private _jobManagementService: IJobManagementService + @Inject(IJobManagementService) private _jobManagementService: IJobManagementService, + @Inject(IKeybindingService) keybindingService: IKeybindingService, + @Inject(IDashboardService) dashboardService: IDashboardService ) { - super(); + super(commonService, dashboardService, contextMenuService, keybindingService, instantiationService); this._treeController = new JobHistoryController(); this._treeDataSource = new JobHistoryDataSource(); this._treeRenderer = new JobHistoryRenderer(); this._treeFilter = new JobHistoryFilter(); let jobCacheObjectMap = this._jobManagementService.jobCacheObjectMap; - this._serverName = _dashboardService.connectionManagementService.connectionInfo.connectionProfile.serverName; + this._serverName = commonService.connectionManagementService.connectionInfo.connectionProfile.serverName; let jobCache = jobCacheObjectMap[this._serverName]; if (jobCache) { this._jobCacheObject = jobCache; @@ -93,7 +99,11 @@ export class JobHistoryComponent extends Disposable implements OnInit { } ngOnInit() { - let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri; + // set base class elements + this._visibilityElement = this._tableContainer; + this._parentComponent = this._agentViewComponent; + + let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri; const self = this; this._treeController.onClick = (tree, element, event, origin = 'mouse') => { const payload = { origin: origin }; @@ -130,56 +140,13 @@ export class JobHistoryComponent extends Disposable implements OnInit { }, {verticalScrollMode: ScrollbarVisibility.Visible}); this._register(attachListStyler(this._tree, this.themeService)); this._tree.layout(JobHistoryComponent.INITIAL_TREE_HEIGHT); - this._initActionBar(); - $(window).resize(() => { - let historyDetails = $('.overview-container').get(0); - let statusBar = $('.part.statusbar').get(0); - if (historyDetails && statusBar) { - let historyBottom = historyDetails.getBoundingClientRect().bottom; - let statusTop = statusBar.getBoundingClientRect().top; - this._tree.layout(statusTop - historyBottom - JobHistoryComponent.HEADING_HEIGHT); - } - }); - } + this.initActionBar(); - ngAfterContentChecked() { - this._agentJobInfo = this._agentViewComponent.agentJobInfo; - if (!this.agentJobInfo) { - this.agentJobInfo = this._agentJobInfo; - this.setActions(); - } - if (this._isVisible === false && this._tableContainer.nativeElement.offsetParent !== null) { - this._isVisible = true; - let jobHistories = this._jobCacheObject.jobHistories[this._agentViewComponent.jobId]; - if (jobHistories && jobHistories.length > 0) { - const self = this; - if (this._jobCacheObject.prevJobID === this._agentViewComponent.jobId || jobHistories[0].jobId === this._agentViewComponent.jobId) { - this._showPreviousRuns = true; - this.buildHistoryTree(self, jobHistories); - $('jobhistory-component .history-details .prev-run-list .monaco-tree').attr('tabIndex', '-1'); - $('jobhistory-component .history-details .prev-run-list .monaco-tree-row').attr('tabIndex', '0'); - this._cd.detectChanges(); - } - } else if (jobHistories && jobHistories.length === 0 ){ - this._showPreviousRuns = false; - this._showSteps = false; - this._noJobsAvailable = true; - this._cd.detectChanges(); - } else { - this.loadHistory(); - } - this._jobCacheObject.prevJobID = this._agentViewComponent.jobId; - } else if (this._isVisible === true && this._agentViewComponent.refresh) { - this.loadHistory(); - this._agentViewComponent.refresh = false; - } else if (this._isVisible === true && this._tableContainer.nativeElement.offsetParent === null) { - this._isVisible = false; - } } private loadHistory() { const self = this; - let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri; + let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri; this._jobManagementService.getJobHistory(ownerUri, this._agentViewComponent.jobId).then((result) => { if (result && result.jobs) { if (result.jobs.length > 0) { @@ -273,12 +240,71 @@ export class JobHistoryComponent extends Disposable implements OnInit { JobManagementUtilities.getActionIconClassName(startIcon, stopIcon, this.agentJobInfo.currentExecutionStatus); } + public onFirstVisible() { + this._agentJobInfo = this._agentViewComponent.agentJobInfo; + if (!this.agentJobInfo) { + this.agentJobInfo = this._agentJobInfo; + this.setActions(); + } - private _initActionBar() { + if (this.isRefreshing ) { + this.loadHistory(); + return; + } + + let jobHistories = this._jobCacheObject.jobHistories[this._agentViewComponent.jobId]; + if (jobHistories && jobHistories.length > 0) { + const self = this; + if (this._jobCacheObject.prevJobID === this._agentViewComponent.jobId || jobHistories[0].jobId === this._agentViewComponent.jobId) { + this._showPreviousRuns = true; + this.buildHistoryTree(self, jobHistories); + $('jobhistory-component .history-details .prev-run-list .monaco-tree').attr('tabIndex', '-1'); + $('jobhistory-component .history-details .prev-run-list .monaco-tree-row').attr('tabIndex', '0'); + this._cd.detectChanges(); + } + } else if (jobHistories && jobHistories.length === 0 ){ + this._showPreviousRuns = false; + this._showSteps = false; + this._noJobsAvailable = true; + this._cd.detectChanges(); + } else { + this.loadHistory(); + } + this._jobCacheObject.prevJobID = this._agentViewComponent.jobId; + } + + public layout() { + let historyDetails = $('.overview-container').get(0); + let statusBar = $('.part.statusbar').get(0); + if (historyDetails && statusBar) { + let historyBottom = historyDetails.getBoundingClientRect().bottom; + let statusTop = statusBar.getBoundingClientRect().top; + let height: number = statusTop - historyBottom - JobHistoryComponent.HEADING_HEIGHT; + + if (this._table) { + this._table.layout(new dom.Dimension( + dom.getContentWidth(this._tableContainer.nativeElement), + height)); + } + + if (this._tree) { + this._tree.layout(height); + } + + if (this._jobStepsView) { + let element = this._jobStepsView.nativeElement as HTMLElement; + if (element) { + element.style.height = height + 'px'; + } + } + } + } + + protected initActionBar() { let runJobAction = this.instantiationService.createInstance(RunJobAction); let stopJobAction = this.instantiationService.createInstance(StopJobAction); let newStepAction = this.instantiationService.createInstance(NewStepAction); - let taskbar = this._actionbarContainer.nativeElement; + let taskbar = this.actionBarContainer.nativeElement; this._actionBar = new Taskbar(taskbar, this.contextMenuService); this._actionBar.context = this; this._actionBar.setContent([ @@ -299,7 +325,7 @@ export class JobHistoryComponent extends Disposable implements OnInit { } public get ownerUri(): string { - return this._dashboardService.connectionManagementService.connectionInfo.ownerUri; + return this._commonService.connectionManagementService.connectionInfo.ownerUri; } public get serverName(): string { diff --git a/src/sql/parts/jobManagement/views/jobHistory.css b/src/sql/parts/jobManagement/views/jobHistory.css index 962d272434..d4c5678dd4 100644 --- a/src/sql/parts/jobManagement/views/jobHistory.css +++ b/src/sql/parts/jobManagement/views/jobHistory.css @@ -167,7 +167,7 @@ table.step-list tr.step-row td { } .vs-dark .history-details > .job-steps { - display: inline-block; + display: block; border-left: 3px solid #444444; padding-left: 10px; height: 100%; @@ -176,10 +176,12 @@ table.step-list tr.step-row td { } .history-details > .job-steps { - display: inline-block; + display: block; border-left: 3px solid #f4f4f4; padding-left: 10px; height: 100%; + width: 90%; + overflow-y: scroll; } .history-details > .job-steps > .step-list { diff --git a/src/sql/parts/jobManagement/views/jobManagementView.ts b/src/sql/parts/jobManagement/views/jobManagementView.ts index f50b09cfcc..fac3d29208 100644 --- a/src/sql/parts/jobManagement/views/jobManagementView.ts +++ b/src/sql/parts/jobManagement/views/jobManagementView.ts @@ -57,8 +57,8 @@ export abstract class JobManagementView extends TabChild implements AfterContent } } else if (this.isVisible === true && this._parentComponent.refresh === true) { this._showProgressWheel = true; - this.onFirstVisible(); this.isRefreshing = true; + this.onFirstVisible(); this._parentComponent.refresh = false; } else if (this.isVisible === true && this._visibilityElement.nativeElement.offsetParent === null) { this.isVisible = false; diff --git a/src/sql/parts/jobManagement/views/jobStepsView.component.ts b/src/sql/parts/jobManagement/views/jobStepsView.component.ts index 6733efb56d..b93074c58b 100644 --- a/src/sql/parts/jobManagement/views/jobStepsView.component.ts +++ b/src/sql/parts/jobManagement/views/jobStepsView.component.ts @@ -8,21 +8,28 @@ import 'vs/css!./jobStepsView'; import { OnInit, Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, Injectable, AfterContentChecked } from '@angular/core'; import { attachListStyler } from 'vs/platform/theme/common/styler'; import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; -import { Disposable } from 'vs/base/common/lifecycle'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service'; import { JobStepsViewController, JobStepsViewDataSource, JobStepsViewFilter, JobStepsViewRenderer, JobStepsViewModel} from 'sql/parts/jobManagement/views/jobStepsViewTree'; import { JobHistoryComponent } from 'sql/parts/jobManagement/views/jobHistory.component'; +import { JobManagementView } from 'sql/parts/jobManagement/views/jobManagementView'; +import { IDashboardService } from 'sql/services/dashboard/common/dashboardService'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { TabChild } from 'sql/base/browser/ui/panel/tab.component'; +import * as dom from 'vs/base/browser/dom'; export const JOBSTEPSVIEW_SELECTOR: string = 'jobstepsview-component'; @Component({ selector: JOBSTEPSVIEW_SELECTOR, - templateUrl: decodeURI(require.toUrl('./jobStepsView.component.html')) + templateUrl: decodeURI(require.toUrl('./jobStepsView.component.html')), + providers: [{ provide: TabChild, useExisting: forwardRef(() => JobStepsViewComponent) }], }) -export class JobStepsViewComponent extends Disposable implements OnInit, AfterContentChecked { +export class JobStepsViewComponent extends JobManagementView implements OnInit, AfterContentChecked { private _tree: Tree; private _treeController = new JobStepsViewController(); @@ -33,15 +40,18 @@ export class JobStepsViewComponent extends Disposable implements OnInit, AfterCo @ViewChild('table') private _tableContainer: ElementRef; - constructor( @Inject(forwardRef(() => ElementRef)) el: ElementRef, @Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef, - @Inject(forwardRef(() => CommonServiceInterface)) private _dashboardService: CommonServiceInterface, + @Inject(forwardRef(() => CommonServiceInterface)) commonService: CommonServiceInterface, @Inject(forwardRef(() => JobHistoryComponent)) private _jobHistoryComponent: JobHistoryComponent, - @Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService + @Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService, + @Inject(IInstantiationService) instantiationService: IInstantiationService, + @Inject(IContextMenuService) contextMenuService: IContextMenuService, + @Inject(IKeybindingService) keybindingService: IKeybindingService, + @Inject(IDashboardService) dashboardService: IDashboardService ) { - super(); + super(commonService, dashboardService, contextMenuService, keybindingService, instantiationService); } ngAfterContentChecked() { @@ -64,7 +74,7 @@ export class JobStepsViewComponent extends Disposable implements OnInit, AfterCo } ngOnInit() { - let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri; + let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri; this._tree = new Tree(this._tableContainer.nativeElement, { controller: this._treeController, dataSource: this._treeDataSource, @@ -73,5 +83,20 @@ export class JobStepsViewComponent extends Disposable implements OnInit, AfterCo }, {verticalScrollMode: ScrollbarVisibility.Visible}); this._register(attachListStyler(this._tree, this.themeService)); } + + public onFirstVisible() { + } + + public layout() { + let jobsViewToolbar = $('jobhistory-component .actionbar-container').get(0); + let statusBar = $('.part.statusbar').get(0); + if (jobsViewToolbar && statusBar) { + let toolbarBottom = jobsViewToolbar.getBoundingClientRect().bottom; + let statusTop = statusBar.getBoundingClientRect().top; + this._table.layout(new dom.Dimension( + dom.getContentWidth(this._tableContainer.nativeElement), + statusTop - toolbarBottom)); + } + } }