Analysis task page on v3 UI should show charts
authorrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 10 Feb 2016 21:07:52 +0000 (21:07 +0000)
committerrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 10 Feb 2016 21:07:52 +0000 (21:07 +0000)
https://bugs.webkit.org/show_bug.cgi?id=154057

Reviewed by Chris Dumez.

Extracted ChartPaneBase out of ChartPane and added an instance of its new subclass, AnalysisTaskChartPane,
to the analysis task page. The main difference is that ChartPaneBase doesn't depend on the presence of
this._chartsPage unlike ChartPane. It also doesn't have the header with toolbar (to show breakdown, etc...).

* public/v3/components/base.js:
(ComponentBase.prototype._constructShadowTree): Call htmlTemplate() and cssTemplate() with the right "this".

* public/v3/components/chart-pane-base.js: Added.
(ChartPaneBase): Extracted from ChartPane.
(ChartPaneBase.prototype.configure): Extracted from the constructor. Separating this function allows the
component to be instantiated inside a HTML template.
(ChartPaneBase.prototype._fetchAnalysisTasks): Moved from ChartPane._fetchAnalysisTasks.
(ChartPaneBase.prototype.platformId): Ditto.
(ChartPaneBase.prototype.metricId): Ditto.
(ChartPaneBase.prototype.setOverviewDomain): Ditto.
(ChartPaneBase.prototype.setMainDomain): Ditto.
(ChartPaneBase.prototype._overviewSelectionDidChange): Extracted from the constructor. This is overridden in
ChartPane and unused in AnalysisTaskChartPane.
(ChartPaneBase.prototype._mainSelectionDidChange): Extracted from ChartPane._mainSelectionDidChange.
(ChartPaneBase.prototype._mainSelectionDidZoom): Extracted from ChartPane._mainSelectionDidZoom.
(ChartPaneBase.prototype._indicatorDidChange): Extracted from ChartPane._indicatorDidChange.
(ChartPaneBase.prototype._didFetchData): Moved from ChartPane._fetchAnalysisTasks.
(ChartPaneBase.prototype._openAnalysisTask): Ditto.
(ChartPaneBase.prototype._openCommitViewer): Ditto. Also fixed a bug that we don't show the spinner while
waiting for the data to be fetched by calling this.render() here.
(ChartPaneBase.prototype._keyup): Moved from ChartPane._keyup. Also fixed the bug that the revisions list
doesn't update by calling this.render() here.
(ChartPaneBase.prototype.render): Extracted from ChartPane.render.
(ChartPaneBase.htmlTemplate): Extracted from ChartPane.htmlTemplate.
(ChartPaneBase.paneHeaderTemplate): Added. This is overridden in ChartPane and unused in AnalysisTaskChartPane.
(ChartPaneBase.cssTemplate): Extracted from ChartPane.htmlTemplate.

* public/v3/components/chart-styles.js: Renamed from public/v3/pages/page-with-charts.js.
(PageWithCharts): Renamed from PageWithCharts since it no longer extends PageWithHeading.
(ChartStyles.createChartSourceList):

* public/v3/components/commit-log-viewer.js:
(CommitLogViewer.prototype.view): Set this._repository right away instead of waiting for the fetched data
so that spinner will be shown while the data is being fetched.

* public/v3/index.html:

* public/v3/pages/analysis-task-page.js:
(AnalysisTaskChartPane): Added extends ChartPaneBase.
(AnalysisTaskPage): Added. this._chartPane.
(AnalysisTaskPage.prototype._didFetchTask): Initialize this._chartPane with a domain.
(AnalysisTaskPage.prototype.render): Render this._chartPane.
(AnalysisTaskPage.htmlTemplate):

* public/v3/pages/chart-pane-status-view.js:
(ChartPaneStatusView): Removed the unused router from the argument list.
(ChartPaneStatusView.prototype.pointsRangeForAnalysis): Renamed from analyzeData() since it was ambiguous.
(ChartPaneStatusView.prototype.moveRepositoryWithNotification): Fixed the bug that we don't update the list
of the revisions here.
(ChartPaneStatusView.prototype.computeChartStatusLabels):

* public/v3/pages/chart-pane.js:
(ChartPane): Now extends ChartPaneBase.
(ChartPane.prototype._overviewSelectionDidChange): Extracted from the constructor.
(ChartPane.prototype._mainSelectionDidChange):
(ChartPane.prototype._mainSelectionDidZoom):
(ChartPane.prototype._indicatorDidChange):
(ChartPane.prototype.render):
(ChartPane.prototype._renderActionToolbar):
(ChartPane.paneHeaderTemplate): Extracted from htmlTemplate.
(ChartPane.cssTemplate):
(ChartPane.overviewOptions.selection.onchange): Deleted.
(ChartPane.prototype._fetchAnalysisTasks): Deleted.
(ChartPane.prototype.platformId): Deleted.
(ChartPane.prototype.metricId): Deleted.
(ChartPane.prototype.setOverviewDomain): Deleted.
(ChartPane.prototype.setMainDomain): Deleted.
(ChartPane.prototype._openCommitViewer): Deleted.
(ChartPane.prototype._didFetchData): Deleted.
(ChartPane.prototype._keyup): Deleted.

* public/v3/pages/charts-page.js:
(ChartsPage):
(ChartsPage.createDomainForAnalysisTask): Extracted by createDomainForAnalysisTask; used to set the domain
of the charts in the analysis task page.
(ChartsPage.createStateForAnalysisTask):

* public/v3/pages/dashboard-page.js:
(DashboardPage):
(DashboardPage.prototype._createChartForCell):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@196387 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Websites/perf.webkit.org/ChangeLog
Websites/perf.webkit.org/public/v3/components/base.js
Websites/perf.webkit.org/public/v3/components/chart-pane-base.js [new file with mode: 0644]
Websites/perf.webkit.org/public/v3/components/chart-styles.js [moved from Websites/perf.webkit.org/public/v3/pages/page-with-charts.js with 95% similarity]
Websites/perf.webkit.org/public/v3/components/commit-log-viewer.js
Websites/perf.webkit.org/public/v3/index.html
Websites/perf.webkit.org/public/v3/pages/analysis-task-page.js
Websites/perf.webkit.org/public/v3/pages/chart-pane-status-view.js
Websites/perf.webkit.org/public/v3/pages/chart-pane.js
Websites/perf.webkit.org/public/v3/pages/charts-page.js
Websites/perf.webkit.org/public/v3/pages/dashboard-page.js

index fa3ce68..745d4b1 100644 (file)
@@ -1,3 +1,96 @@
+2016-02-09  Ryosuke Niwa  <rniwa@webkit.org>
+
+        Analysis task page on v3 UI should show charts
+        https://bugs.webkit.org/show_bug.cgi?id=154057
+
+        Reviewed by Chris Dumez.
+
+        Extracted ChartPaneBase out of ChartPane and added an instance of its new subclass, AnalysisTaskChartPane,
+        to the analysis task page. The main difference is that ChartPaneBase doesn't depend on the presence of
+        this._chartsPage unlike ChartPane. It also doesn't have the header with toolbar (to show breakdown, etc...).
+
+        * public/v3/components/base.js:
+        (ComponentBase.prototype._constructShadowTree): Call htmlTemplate() and cssTemplate() with the right "this".
+
+        * public/v3/components/chart-pane-base.js: Added.
+        (ChartPaneBase): Extracted from ChartPane.
+        (ChartPaneBase.prototype.configure): Extracted from the constructor. Separating this function allows the
+        component to be instantiated inside a HTML template.
+        (ChartPaneBase.prototype._fetchAnalysisTasks): Moved from ChartPane._fetchAnalysisTasks.
+        (ChartPaneBase.prototype.platformId): Ditto.
+        (ChartPaneBase.prototype.metricId): Ditto.
+        (ChartPaneBase.prototype.setOverviewDomain): Ditto.
+        (ChartPaneBase.prototype.setMainDomain): Ditto.
+        (ChartPaneBase.prototype._overviewSelectionDidChange): Extracted from the constructor. This is overridden in
+        ChartPane and unused in AnalysisTaskChartPane.
+        (ChartPaneBase.prototype._mainSelectionDidChange): Extracted from ChartPane._mainSelectionDidChange.
+        (ChartPaneBase.prototype._mainSelectionDidZoom): Extracted from ChartPane._mainSelectionDidZoom.
+        (ChartPaneBase.prototype._indicatorDidChange): Extracted from ChartPane._indicatorDidChange.
+        (ChartPaneBase.prototype._didFetchData): Moved from ChartPane._fetchAnalysisTasks.
+        (ChartPaneBase.prototype._openAnalysisTask): Ditto.
+        (ChartPaneBase.prototype._openCommitViewer): Ditto. Also fixed a bug that we don't show the spinner while
+        waiting for the data to be fetched by calling this.render() here.
+        (ChartPaneBase.prototype._keyup): Moved from ChartPane._keyup. Also fixed the bug that the revisions list
+        doesn't update by calling this.render() here.
+        (ChartPaneBase.prototype.render): Extracted from ChartPane.render.
+        (ChartPaneBase.htmlTemplate): Extracted from ChartPane.htmlTemplate.
+        (ChartPaneBase.paneHeaderTemplate): Added. This is overridden in ChartPane and unused in AnalysisTaskChartPane.
+        (ChartPaneBase.cssTemplate): Extracted from ChartPane.htmlTemplate.
+
+        * public/v3/components/chart-styles.js: Renamed from public/v3/pages/page-with-charts.js.
+        (PageWithCharts): Renamed from PageWithCharts since it no longer extends PageWithHeading.
+        (ChartStyles.createChartSourceList):
+
+        * public/v3/components/commit-log-viewer.js:
+        (CommitLogViewer.prototype.view): Set this._repository right away instead of waiting for the fetched data
+        so that spinner will be shown while the data is being fetched.
+
+        * public/v3/index.html:
+
+        * public/v3/pages/analysis-task-page.js:
+        (AnalysisTaskChartPane): Added extends ChartPaneBase.
+        (AnalysisTaskPage): Added. this._chartPane.
+        (AnalysisTaskPage.prototype._didFetchTask): Initialize this._chartPane with a domain.
+        (AnalysisTaskPage.prototype.render): Render this._chartPane.
+        (AnalysisTaskPage.htmlTemplate):
+
+        * public/v3/pages/chart-pane-status-view.js:
+        (ChartPaneStatusView): Removed the unused router from the argument list.
+        (ChartPaneStatusView.prototype.pointsRangeForAnalysis): Renamed from analyzeData() since it was ambiguous.
+        (ChartPaneStatusView.prototype.moveRepositoryWithNotification): Fixed the bug that we don't update the list
+        of the revisions here.
+        (ChartPaneStatusView.prototype.computeChartStatusLabels):
+
+        * public/v3/pages/chart-pane.js:
+        (ChartPane): Now extends ChartPaneBase. 
+        (ChartPane.prototype._overviewSelectionDidChange): Extracted from the constructor.
+        (ChartPane.prototype._mainSelectionDidChange): 
+        (ChartPane.prototype._mainSelectionDidZoom):
+        (ChartPane.prototype._indicatorDidChange):
+        (ChartPane.prototype.render):
+        (ChartPane.prototype._renderActionToolbar):
+        (ChartPane.paneHeaderTemplate): Extracted from htmlTemplate.
+        (ChartPane.cssTemplate):
+        (ChartPane.overviewOptions.selection.onchange): Deleted.
+        (ChartPane.prototype._fetchAnalysisTasks): Deleted.
+        (ChartPane.prototype.platformId): Deleted.
+        (ChartPane.prototype.metricId): Deleted.
+        (ChartPane.prototype.setOverviewDomain): Deleted.
+        (ChartPane.prototype.setMainDomain): Deleted.
+        (ChartPane.prototype._openCommitViewer): Deleted.
+        (ChartPane.prototype._didFetchData): Deleted.
+        (ChartPane.prototype._keyup): Deleted.
+
+        * public/v3/pages/charts-page.js:
+        (ChartsPage):
+        (ChartsPage.createDomainForAnalysisTask): Extracted by createDomainForAnalysisTask; used to set the domain
+        of the charts in the analysis task page.
+        (ChartsPage.createStateForAnalysisTask):
+
+        * public/v3/pages/dashboard-page.js:
+        (DashboardPage):
+        (DashboardPage.prototype._createChartForCell):
+
 2016-02-10  Ryosuke Niwa  <rniwa@webkit.org>
 
         Add the support for maintenance mode
index 1e46ee4..05160ad 100644 (file)
@@ -39,14 +39,14 @@ class ComponentBase {
 
         if (htmlTemplate) {
             var template = document.createElement('template');
-            template.innerHTML = htmlTemplate();
+            template.innerHTML = newTarget.htmlTemplate();
             shadow.appendChild(template.content.cloneNode(true));
             this._recursivelyReplaceUnknownElementsByComponents(shadow);
         }
 
         if (cssTemplate) {
             var style = document.createElement('style');
-            style.textContent = cssTemplate();
+            style.textContent = newTarget.cssTemplate();
             shadow.appendChild(style);
         }
 
diff --git a/Websites/perf.webkit.org/public/v3/components/chart-pane-base.js b/Websites/perf.webkit.org/public/v3/components/chart-pane-base.js
new file mode 100644 (file)
index 0000000..366a294
--- /dev/null
@@ -0,0 +1,612 @@
+
+class ChartPaneBase extends ComponentBase {
+
+    constructor(name)
+    {
+        super(name);
+
+        this._errorMessage = null;
+        this._platformId = null;
+        this._metricId = null;
+        this._platform = null;
+        this._metric = null;
+
+        this._overviewChart = null;
+        this._mainChart = null;
+        this._mainChartStatus = null;
+        this._commitLogViewer = null;
+    }
+
+    configure(platformId, metricId)
+    {
+        var result = ChartStyles.createChartSourceList(platformId, metricId);
+        this._errorMessage = result.error;
+        this._platformId = platformId;
+        this._metricId = metricId;
+        this._platform = result.platform;
+        this._metric = result.metric;
+
+        this._overviewChart = null;
+        this._mainChart = null;
+        this._mainChartStatus = null;
+
+        this._commitLogViewer = this.content().querySelector('commit-log-viewer').component();
+
+        if (result.error)
+            return;
+
+        var formatter = result.metric.makeFormatter(4);
+        var self = this;
+
+        var overviewOptions = ChartStyles.overviewChartOptions(formatter);
+        overviewOptions.selection.onchange = this._overviewSelectionDidChange.bind(this);
+
+        this._overviewChart = new InteractiveTimeSeriesChart(result.sourceList, overviewOptions);
+        this.renderReplace(this.content().querySelector('.chart-pane-overview'), this._overviewChart);
+
+        var mainOptions = ChartStyles.mainChartOptions(formatter);
+        mainOptions.indicator.onchange = this._indicatorDidChange.bind(this);
+        mainOptions.selection.onchange = this._mainSelectionDidChange.bind(this);
+        mainOptions.selection.onzoom = this._mainSelectionDidZoom.bind(this);
+        mainOptions.annotations.onclick = this._openAnalysisTask.bind(this);
+        mainOptions.ondata = this._didFetchData.bind(this);
+        this._mainChart = new InteractiveTimeSeriesChart(result.sourceList, mainOptions);
+        this.renderReplace(this.content().querySelector('.chart-pane-main'), this._mainChart);
+
+        this._mainChartStatus = new ChartPaneStatusView(result.metric, this._mainChart, this._openCommitViewer.bind(this));
+        this.renderReplace(this.content().querySelector('.chart-pane-details'), this._mainChartStatus);
+
+        this.content().querySelector('.chart-pane').addEventListener('keyup', this._keyup.bind(this));
+
+        this._fetchAnalysisTasks(platformId, metricId);
+    }
+
+    _fetchAnalysisTasks(platformId, metricId)
+    {
+        var self = this;
+        AnalysisTask.fetchByPlatformAndMetric(platformId, metricId).then(function (tasks) {
+            self._mainChart.setAnnotations(tasks.map(function (task) {
+                var fillStyle = '#fc6';
+                switch (task.changeType()) {
+                case 'inconclusive':
+                    fillStyle = '#fcc';
+                case 'progression':
+                    fillStyle = '#39f';
+                    break;
+                case 'regression':
+                    fillStyle = '#c60';
+                    break;
+                case 'unchanged':
+                    fillStyle = '#ccc';
+                    break;
+                }
+
+                return {
+                    task: task,
+                    startTime: task.startTime(),
+                    endTime: task.endTime(),
+                    label: task.label(),
+                    fillStyle: fillStyle,
+                };
+            }));
+        });
+    }
+
+    platformId() { return this._platformId; }
+    metricId() { return this._metricId; }
+
+    setOverviewDomain(startTime, endTime)
+    {
+        if (this._overviewChart)
+            this._overviewChart.setDomain(startTime, endTime);
+    }
+
+    setMainDomain(startTime, endTime)
+    {
+        if (this._mainChart)
+            this._mainChart.setDomain(startTime, endTime);
+    }
+
+    _overviewSelectionDidChange(domain, didEndDrag) { }
+
+    _mainSelectionDidChange(selection, didEndDrag)
+    {
+        this.render();
+    }
+
+    _mainSelectionDidZoom(selection)
+    {
+        this._overviewChart.setSelection(selection, this);
+        this._mainChart.setSelection(null);
+        this.render();
+    }
+
+    _indicatorDidChange(indicatorID, isLocked)
+    {
+        this._mainChartStatus.updateRevisionListWithNotification();
+        this.render();
+    }
+
+    _didFetchData()
+    {
+        this._mainChartStatus.updateRevisionListWithNotification();
+        this.render();
+    }
+
+    _openAnalysisTask()
+    { }
+
+    _openCommitViewer(repository, from, to)
+    {
+        this._mainChartStatus.setCurrentRepository(repository);
+        this._commitLogViewer.view(repository, from, to).then(this.render.bind(this));
+        this.render();
+    }
+
+    _keyup(event)
+    {
+        switch (event.keyCode) {
+        case 37: // Left
+            if (!this._mainChart.moveLockedIndicatorWithNotification(false))
+                return;
+            break;
+        case 39: // Right
+            if (!this._mainChart.moveLockedIndicatorWithNotification(true))
+                return;
+            break;
+        case 38: // Up
+            if (!this._mainChartStatus.moveRepositoryWithNotification(false))
+                return;
+        case 40: // Down
+            if (!this._mainChartStatus.moveRepositoryWithNotification(true))
+                return;
+        default:
+            return;
+        }
+
+        this.render();
+
+        event.preventDefault();
+        event.stopPropagation();
+    }
+
+    render()
+    {
+        Instrumentation.startMeasuringTime('ChartPane', 'render');
+
+        super.render();
+
+        if (this._errorMessage) {
+            this.renderReplace(this.content().querySelector('.chart-pane-main'), this._errorMessage);
+            return;
+        }
+
+        if (this._mainChartStatus)
+            this._mainChartStatus.render();
+
+        var body = this.content().querySelector('.chart-pane-body');
+        if (this._commitLogViewer && this._commitLogViewer.currentRepository()) {
+            body.classList.add('has-second-sidebar');
+            this._commitLogViewer.render();
+        } else
+            body.classList.remove('has-second-sidebar');
+
+        Instrumentation.endMeasuringTime('ChartPane', 'render');
+    }
+
+    static htmlTemplate()
+    {
+        return `
+            <section class="chart-pane" tabindex="0">
+                ${this.paneHeaderTemplate()}
+                <div class="chart-pane-body">
+                    <div class="chart-pane-main"></div>
+                    <div class="chart-pane-sidebar">
+                        <div class="chart-pane-overview"></div>
+                        <div class="chart-pane-details"></div>
+                    </div>
+                    <div class="chart-pane-second-sidebar">
+                        <commit-log-viewer></commit-log-viewer>
+                    </div>
+                </div>
+            </section>
+        `;
+    }
+
+    static paneHeaderTemplate() { return ''; }
+
+    static cssTemplate()
+    {
+        return Toolbar.cssTemplate() + `
+            .chart-pane {
+                margin: 1rem;
+                margin-bottom: 2rem;
+                padding: 0rem;
+                height: 18rem;
+                outline: none;
+            }
+
+            .chart-pane:focus .chart-pane-header {
+                background: rgba(204, 153, 51, 0.1);
+            }
+
+            .chart-pane-body {
+                position: relative;
+                width: 100%;
+                height: 100%;
+            }
+
+            .chart-pane-main {
+                padding-right: 20rem;
+                height: 100%;
+                margin: 0;
+                vertical-align: middle;
+                text-align: center;
+            }
+
+            .has-second-sidebar .chart-pane-main {
+                padding-right: 40rem;
+            }
+
+            .chart-pane-main > * {
+                width: 100%;
+                height: 100%;
+            }
+
+            .chart-pane-sidebar,
+            .chart-pane-second-sidebar {
+                position: absolute;
+                right: 0;
+                top: 0;
+                width: 0;
+                border-left: solid 1px #ccc;
+                height: 100%;
+            }
+
+            :not(.has-second-sidebar) > .chart-pane-second-sidebar {
+                border-left: 0;
+            }
+
+            .chart-pane-sidebar {
+                width: 20rem;
+            }
+
+            .has-second-sidebar .chart-pane-sidebar {
+                right: 20rem;
+            }
+
+            .has-second-sidebar .chart-pane-second-sidebar {
+                width: 20rem;
+            }
+
+            .chart-pane-overview {
+                width: 100%;
+                height: 5rem;
+                border-bottom: solid 1px #ccc;
+            }
+
+            .chart-pane-overview > * {
+                display: block;
+                width: 100%;
+                height: 100%;
+            }
+
+            .chart-pane-details {
+                position: relative;
+                display: block;
+                height: calc(100% - 5.5rem - 2px);
+                overflow-y: scroll;
+                padding-top: 0.5rem;
+            }
+        `;
+    }
+
+}
+
+class ChartPaneBase extends ComponentBase {
+
+    constructor(name)
+    {
+        super(name);
+
+        this._errorMessage = null;
+        this._platformId = null;
+        this._metricId = null;
+        this._platform = null;
+        this._metric = null;
+
+        this._overviewChart = null;
+        this._mainChart = null;
+        this._mainChartStatus = null;
+        this._commitLogViewer = null;
+    }
+
+    configure(platformId, metricId)
+    {
+        var result = ChartStyles.createChartSourceList(platformId, metricId);
+        this._errorMessage = result.error;
+        this._platformId = platformId;
+        this._metricId = metricId;
+        this._platform = result.platform;
+        this._metric = result.metric;
+
+        this._overviewChart = null;
+        this._mainChart = null;
+        this._mainChartStatus = null;
+
+        this._commitLogViewer = this.content().querySelector('commit-log-viewer').component();
+
+        if (result.error)
+            return;
+
+        var formatter = result.metric.makeFormatter(4);
+        var self = this;
+
+        var overviewOptions = ChartStyles.overviewChartOptions(formatter);
+        overviewOptions.selection.onchange = this._overviewSelectionDidChange.bind(this);
+
+        this._overviewChart = new InteractiveTimeSeriesChart(result.sourceList, overviewOptions);
+        this.renderReplace(this.content().querySelector('.chart-pane-overview'), this._overviewChart);
+
+        var mainOptions = ChartStyles.mainChartOptions(formatter);
+        mainOptions.indicator.onchange = this._indicatorDidChange.bind(this);
+        mainOptions.selection.onchange = this._mainSelectionDidChange.bind(this);
+        mainOptions.selection.onzoom = this._mainSelectionDidZoom.bind(this);
+        mainOptions.annotations.onclick = this._openAnalysisTask.bind(this);
+        mainOptions.ondata = this._didFetchData.bind(this);
+        this._mainChart = new InteractiveTimeSeriesChart(result.sourceList, mainOptions);
+        this.renderReplace(this.content().querySelector('.chart-pane-main'), this._mainChart);
+
+        this._mainChartStatus = new ChartPaneStatusView(result.metric, this._mainChart, this._openCommitViewer.bind(this));
+        this.renderReplace(this.content().querySelector('.chart-pane-details'), this._mainChartStatus);
+
+        this.content().querySelector('.chart-pane').addEventListener('keyup', this._keyup.bind(this));
+
+        this._fetchAnalysisTasks(platformId, metricId);
+    }
+
+    _fetchAnalysisTasks(platformId, metricId)
+    {
+        var self = this;
+        AnalysisTask.fetchByPlatformAndMetric(platformId, metricId).then(function (tasks) {
+            self._mainChart.setAnnotations(tasks.map(function (task) {
+                var fillStyle = '#fc6';
+                switch (task.changeType()) {
+                case 'inconclusive':
+                    fillStyle = '#fcc';
+                case 'progression':
+                    fillStyle = '#39f';
+                    break;
+                case 'regression':
+                    fillStyle = '#c60';
+                    break;
+                case 'unchanged':
+                    fillStyle = '#ccc';
+                    break;
+                }
+
+                return {
+                    task: task,
+                    startTime: task.startTime(),
+                    endTime: task.endTime(),
+                    label: task.label(),
+                    fillStyle: fillStyle,
+                };
+            }));
+        });
+    }
+
+    platformId() { return this._platformId; }
+    metricId() { return this._metricId; }
+
+    setOverviewDomain(startTime, endTime)
+    {
+        if (this._overviewChart)
+            this._overviewChart.setDomain(startTime, endTime);
+    }
+
+    setMainDomain(startTime, endTime)
+    {
+        if (this._mainChart)
+            this._mainChart.setDomain(startTime, endTime);
+    }
+
+    _overviewSelectionDidChange(domain, didEndDrag) { }
+
+    _mainSelectionDidChange(selection, didEndDrag)
+    {
+        this.render();
+    }
+
+    _mainSelectionDidZoom(selection)
+    {
+        this._overviewChart.setSelection(selection, this);
+        this._mainChart.setSelection(null);
+        this.render();
+    }
+
+    _indicatorDidChange(indicatorID, isLocked)
+    {
+        this._mainChartStatus.updateRevisionListWithNotification();
+        this.render();
+    }
+
+    _didFetchData()
+    {
+        this._mainChartStatus.updateRevisionListWithNotification();
+        this.render();
+    }
+
+    _openAnalysisTask()
+    { }
+
+    _openCommitViewer(repository, from, to)
+    {
+        this._mainChartStatus.setCurrentRepository(repository);
+        this._commitLogViewer.view(repository, from, to).then(this.render.bind(this));
+        this.render();
+    }
+
+    _keyup(event)
+    {
+        switch (event.keyCode) {
+        case 37: // Left
+            if (!this._mainChart.moveLockedIndicatorWithNotification(false))
+                return;
+            break;
+        case 39: // Right
+            if (!this._mainChart.moveLockedIndicatorWithNotification(true))
+                return;
+            break;
+        case 38: // Up
+            if (!this._mainChartStatus.moveRepositoryWithNotification(false))
+                return;
+        case 40: // Down
+            if (!this._mainChartStatus.moveRepositoryWithNotification(true))
+                return;
+        default:
+            return;
+        }
+
+        this.render();
+
+        event.preventDefault();
+        event.stopPropagation();
+    }
+
+    render()
+    {
+        Instrumentation.startMeasuringTime('ChartPane', 'render');
+
+        super.render();
+
+        if (this._errorMessage) {
+            this.renderReplace(this.content().querySelector('.chart-pane-main'), this._errorMessage);
+            return;
+        }
+
+        if (this._mainChartStatus) {
+            this._mainChartStatus.render();
+            this._renderActionToolbar();
+        }
+
+        var body = this.content().querySelector('.chart-pane-body');
+        if (this._commitLogViewer && this._commitLogViewer.currentRepository()) {
+            body.classList.add('has-second-sidebar');
+            this._commitLogViewer.render();
+        } else
+            body.classList.remove('has-second-sidebar');
+
+        Instrumentation.endMeasuringTime('ChartPane', 'render');
+    }
+    
+    _renderActionToolbar() { }
+
+    static htmlTemplate()
+    {
+        return `
+            <section class="chart-pane" tabindex="0">
+                ${this.paneHeaderTemplate()}
+                <div class="chart-pane-body">
+                    <div class="chart-pane-main"></div>
+                    <div class="chart-pane-sidebar">
+                        <div class="chart-pane-overview"></div>
+                        <div class="chart-pane-details"></div>
+                    </div>
+                    <div class="chart-pane-second-sidebar">
+                        <commit-log-viewer></commit-log-viewer>
+                    </div>
+                </div>
+            </section>
+        `;
+    }
+
+    static paneHeaderTemplate() { return ''; }
+
+    static cssTemplate()
+    {
+        return Toolbar.cssTemplate() + `
+            .chart-pane {
+                margin: 1rem;
+                margin-bottom: 2rem;
+                padding: 0rem;
+                height: 18rem;
+                outline: none;
+            }
+
+            .chart-pane:focus .chart-pane-header {
+                background: rgba(204, 153, 51, 0.1);
+            }
+
+            .chart-pane-body {
+                position: relative;
+                width: 100%;
+                height: 100%;
+            }
+
+            .chart-pane-main {
+                padding-right: 20rem;
+                height: 100%;
+                margin: 0;
+                vertical-align: middle;
+                text-align: center;
+            }
+
+            .has-second-sidebar .chart-pane-main {
+                padding-right: 40rem;
+            }
+
+            .chart-pane-main > * {
+                width: 100%;
+                height: 100%;
+            }
+
+            .chart-pane-sidebar,
+            .chart-pane-second-sidebar {
+                position: absolute;
+                right: 0;
+                top: 0;
+                width: 0;
+                border-left: solid 1px #ccc;
+                height: 100%;
+            }
+
+            :not(.has-second-sidebar) > .chart-pane-second-sidebar {
+                border-left: 0;
+            }
+
+            .chart-pane-sidebar {
+                width: 20rem;
+            }
+
+            .has-second-sidebar .chart-pane-sidebar {
+                right: 20rem;
+            }
+
+            .has-second-sidebar .chart-pane-second-sidebar {
+                width: 20rem;
+            }
+
+            .chart-pane-overview {
+                width: 100%;
+                height: 5rem;
+                border-bottom: solid 1px #ccc;
+            }
+
+            .chart-pane-overview > * {
+                display: block;
+                width: 100%;
+                height: 100%;
+            }
+
+            .chart-pane-details {
+                position: relative;
+                display: block;
+                height: calc(100% - 5.5rem - 2px);
+                overflow-y: scroll;
+                padding-top: 0.5rem;
+            }
+        `;
+    }
+
+}
@@ -1,11 +1,5 @@
 
-class PageWithCharts extends PageWithHeading {
-    constructor(name, toolbar)
-    {
-        super(name, toolbar);
-        this._charts = [];
-    }
-
+class ChartStyles {
     static createChartSourceList(platformId, metricId)
     {
         var platform = Platform.findById(platformId);
index d64a99c..71aac74 100644 (file)
@@ -28,6 +28,7 @@ class CommitLogViewer extends ComponentBase {
 
         var promise = CommitLog.fetchBetweenRevisions(repository, from || to, to);
 
+        this._repository = repository;
         this._fetchingPromise = promise;
 
         var self = this;
@@ -39,7 +40,6 @@ class CommitLogViewer extends ComponentBase {
             clearTimeout(spinnerTimer);
             if (self._fetchingPromise != promise)
                 return;
-            self._repository = repository;
             self._fetchingPromise = null;
             self._commits = commits;
         });
index d31f673..df96047 100644 (file)
@@ -74,12 +74,13 @@ Run tools/bundle-v3-scripts to speed up the load time for production.`);
         <script src="components/results-table.js"></script>
         <script src="components/analysis-results-viewer.js"></script>
         <script src="components/test-group-results-table.js"></script>
+        <script src="components/chart-styles.js"></script>
+        <script src="components/chart-pane-base.js"></script>
         <script src="pages/page.js"></script>
         <script src="pages/page-router.js"></script>
         <script src="pages/heading.js"></script>
         <script src="pages/toolbar.js"></script>
         <script src="pages/page-with-heading.js"></script>
-        <script src="pages/page-with-charts.js"></script>
         <script src="pages/domain-control-toolbar.js"></script>
         <script src="pages/dashboard-toolbar.js"></script>
         <script src="pages/dashboard-page.js"></script>
index 8639170..6e9912b 100644 (file)
@@ -1,4 +1,10 @@
 
+class AnalysisTaskChartPane extends ChartPaneBase {
+    constructor() { super('analysis-task-chart-pane'); }
+}
+
+ComponentBase.defineElement('analysis-task-chart-pane', AnalysisTaskChartPane);
+
 class AnalysisTaskPage extends PageWithHeading {
     constructor()
     {
@@ -14,6 +20,7 @@ class AnalysisTaskPage extends PageWithHeading {
         this._endPoint = null;
         this._errorMessage = null;
         this._currentTestGroup = null;
+        this._chartPane = this.content().querySelector('analysis-task-chart-pane').component();
         this._analysisResultsViewer = this.content().querySelector('analysis-results-viewer').component();
         this._analysisResultsViewer.setTestGroupCallback(this._showTestGroup.bind(this));
         this._testGroupResultsTable = this.content().querySelector('test-group-results-table').component();
@@ -62,6 +69,12 @@ class AnalysisTaskPage extends PageWithHeading {
         this._analysisResultsViewer.setSmallerIsBetter(metric.isSmallerBetter());
         this._testGroupResultsTable.setValueFormatter(formatter);
 
+        this._chartPane.configure(platform.id(), metric.id());
+
+        var domain = ChartsPage.createDomainForAnalysisTask(task);
+        this._chartPane.setOverviewDomain(domain[0], domain[1]);
+        this._chartPane.setMainDomain(domain[0], domain[1]);
+
         this.render();
     }
 
@@ -128,6 +141,8 @@ class AnalysisTaskPage extends PageWithHeading {
         this.content().querySelector('.error-message').innerHTML +=
             `<p>This page is read only for now. To schedule a new A/B testing job, use <a href="${v2URL}">v2 page</a>.</p>`;
 
+         this._chartPane.render();
+
         if (this._task) {
             this.renderReplace(this.content().querySelector('.analysis-task-name'), this._task.name());
             var platform = this._task.platform();
@@ -187,7 +202,7 @@ class AnalysisTaskPage extends PageWithHeading {
                 <h2 class="analysis-task-name"></h2>
                 <h3 class="platform-metric-names"><a href=""></a></h3>
                 <p class="error-message"></p>
-                <div class="overview-chart"></div>
+                <div class="overview-chart"><analysis-task-chart-pane></analysis-task-chart-pane></div>
                 <section class="analysis-results-view">
                     <analysis-results-viewer></analysis-results-viewer>
                 </section>
index 02f7e26..73566d5 100644 (file)
@@ -1,19 +1,17 @@
 
 class ChartPaneStatusView extends ChartStatusView {
     
-    constructor(metric, chart, router, revisionCallback)
+    constructor(metric, chart, revisionCallback)
     {
         super(metric, chart);
 
-        this._router = router;
-
         this._buildLabel = null;
         this._buildUrl = null;
 
         this._revisionList = [];
         this._currentRepository = null;
         this._revisionCallback = revisionCallback;
-        this._analyzeData = null;
+        this._pointsRangeForAnalysis = null;
 
         this._renderedRevisionList = null;
         this._renderedRepository = null;
@@ -21,7 +19,7 @@ class ChartPaneStatusView extends ChartStatusView {
         this._usedRevisionRange = null;
     }
 
-    analyzeData() { return this._analyzeData; }
+    pointsRangeForAnalysis() { return this._pointsRangeForAnalysis; }
 
     render()
     {
@@ -101,6 +99,7 @@ class ChartPaneStatusView extends ChartStatusView {
 
         var info = this._revisionList[newIndex];
         this.setCurrentRepository(info.repository);
+        this.updateRevisionListWithNotification();
     }
 
     updateRevisionListWithNotification()
@@ -126,7 +125,7 @@ class ChartPaneStatusView extends ChartStatusView {
 
         this._buildInfo = null;
         this._revisionList = [];
-        this._analyzeData = null;
+        this._pointsRangeForAnalysis = null;
 
         if (!currentPoint)
             return;
@@ -139,7 +138,7 @@ class ChartPaneStatusView extends ChartStatusView {
             this._buildInfo = currentMeasurement;
 
         if (currentPoint && previousPoint && this._chart.currentSelection()) {
-            this._analyzeData = {
+            this._pointsRangeForAnalysis = {
                 startPointId: previousPoint.id,
                 endPointId: currentPoint.id,
             };
index cb800b2..5ecf7b7 100644 (file)
@@ -1,95 +1,18 @@
 
-class ChartPane extends ComponentBase {
+class ChartPane extends ChartPaneBase {
     constructor(chartsPage, platformId, metricId)
     {
         super('chart-pane');
 
-        this._chartsPage = chartsPage;
-        this._platformId = platformId;
-        this._metricId = metricId;
-
-        var result = ChartsPage.createChartSourceList(platformId, metricId);
-        this._errorMessage = result.error;
-        this._platform = result.platform;
-        this._metric = result.metric;
-
-        this._overviewChart = null;
-        this._mainChart = null;
-        this._mainChartStatus = null;
-        this._mainSelection = null;
         this._mainChartIndicatorWasLocked = false;
-        this._status = null;
-        this._revisions = null;
-
+        this._chartsPage = chartsPage;
         this._paneOpenedByClick = null;
 
-        this._commitLogViewer = this.content().querySelector('commit-log-viewer').component();
         this.content().querySelector('close-button').component().setCallback(chartsPage.closePane.bind(chartsPage, this));
 
-        if (result.error)
-            return;
-
-        var formatter = result.metric.makeFormatter(4);
-        var self = this;
-
-        var overviewOptions = ChartsPage.overviewChartOptions(formatter);
-        overviewOptions.selection.onchange = function (domain, didEndDrag) {
-            self._chartsPage.setMainDomainFromOverviewSelection(domain, self, didEndDrag);
-        }
-
-        this._overviewChart = new InteractiveTimeSeriesChart(result.sourceList, overviewOptions);
-        this.renderReplace(this.content().querySelector('.chart-pane-overview'), this._overviewChart);
-
-        var mainOptions = ChartsPage.mainChartOptions(formatter);
-        mainOptions.indicator.onchange = this._indicatorDidChange.bind(this);
-        mainOptions.selection.onchange = this._mainSelectionDidChange.bind(this);
-        mainOptions.selection.onzoom = this._mainSelectionDidZoom.bind(this);
-        mainOptions.annotations.onclick = this._openAnalysisTask.bind(this);
-        mainOptions.ondata = this._didFetchData.bind(this);
-        this._mainChart = new InteractiveTimeSeriesChart(result.sourceList, mainOptions);
-        this.renderReplace(this.content().querySelector('.chart-pane-main'), this._mainChart);
-
-        this._mainChartStatus = new ChartPaneStatusView(result.metric, this._mainChart, chartsPage.router(), this._openCommitViewer.bind(this));
-        this.renderReplace(this.content().querySelector('.chart-pane-details'), this._mainChartStatus);
-
-        this.content().querySelector('.chart-pane').addEventListener('keyup', this._keyup.bind(this));
-        this._fetchAnalysisTasks();
+        this.configure(platformId, metricId);
     }
 
-    _fetchAnalysisTasks()
-    {
-        var self = this;
-        AnalysisTask.fetchByPlatformAndMetric(this._platformId, this._metricId).then(function (tasks) {
-            self._mainChart.setAnnotations(tasks.map(function (task) {
-                var fillStyle = '#fc6';
-                switch (task.changeType()) {
-                case 'inconclusive':
-                    fillStyle = '#fcc';
-                case 'progression':
-                    fillStyle = '#39f';
-                    break;
-                case 'regression':
-                    fillStyle = '#c60';
-                    break;
-                case 'unchanged':
-                    fillStyle = '#ccc';
-                    break;
-                }
-
-                return {
-                    task: task,
-                    startTime: task.startTime(),
-                    endTime: task.endTime(),
-                    label: task.label(),
-                    fillStyle: fillStyle,
-                };
-            }));
-        });
-    }
-
-    platformId() { return this._platformId; }
-    metricId() { return this._metricId; }
-
     serializeState()
     {
         var selection = this._mainChart ? this._mainChart.currentSelection() : null;
@@ -116,36 +39,28 @@ class ChartPane extends ComponentBase {
             this._mainChart.setIndicator(null, false);
     }
 
-    setOverviewDomain(startTime, endTime)
-    {
-        if (this._overviewChart)
-            this._overviewChart.setDomain(startTime, endTime);
-    }
-
     setOverviewSelection(selection)
     {
         if (this._overviewChart)
             this._overviewChart.setSelection(selection);
     }
 
-    setMainDomain(startTime, endTime)
+    _overviewSelectionDidChange(domain, didEndDrag)
     {
-        if (this._mainChart)
-            this._mainChart.setDomain(startTime, endTime);
+        super._overviewSelectionDidChange(domain, didEndDrag);
+        this._chartsPage.setMainDomainFromOverviewSelection(domain, this, didEndDrag);
     }
 
     _mainSelectionDidChange(selection, didEndDrag)
     {
+        super._mainSelectionDidChange(selection, didEndDrag);
         this._chartsPage.mainChartSelectionDidChange(this, didEndDrag);
-        this.render();
     }
 
     _mainSelectionDidZoom(selection)
     {
-        this._overviewChart.setSelection(selection, this);
-        this._mainChart.setSelection(null);
+        super._mainSelectionDidZoom(selection);
         this._chartsPage.setMainDomainFromZoom(selection, this);
-        this.render();
     }
 
     _openAnalysisTask(annotation)
@@ -155,58 +70,13 @@ class ChartPane extends ComponentBase {
 
     _indicatorDidChange(indicatorID, isLocked)
     {
-        this._chartsPage.mainChartIndicatorDidChange(this, isLocked || this._mainChartIndicatorWasLocked);
         this._mainChartIndicatorWasLocked = isLocked;
-        this._mainChartStatus.updateRevisionListWithNotification();
-        this.render();
-    }
-
-    _openCommitViewer(repository, from, to)
-    {
-        var self = this;
-        this._commitLogViewer.view(repository, from, to).then(function () {
-            self._mainChartStatus.setCurrentRepository(self._commitLogViewer.currentRepository());
-            self.render();
-        });
-    }
-    
-    _didFetchData()
-    {
-        this._mainChartStatus.updateRevisionListWithNotification();
-        this.render();
-    }
-
-    _keyup(event)
-    {
-        switch (event.keyCode) {
-        case 37: // Left
-            if (!this._mainChart.moveLockedIndicatorWithNotification(false))
-                return;
-            break;
-        case 39: // Right
-            if (!this._mainChart.moveLockedIndicatorWithNotification(true))
-                return;
-            break;
-        case 38: // Up
-            if (!this._mainChartStatus.moveRepositoryWithNotification(false))
-                return;
-        case 40: // Down
-            if (!this._mainChartStatus.moveRepositoryWithNotification(true))
-                return;
-        default:
-            return;
-        }
-
-        event.preventDefault();
-        event.stopPropagation();
+        this._chartsPage.mainChartIndicatorDidChange(this, isLocked || this._mainChartIndicatorWasLocked);
+        super._indicatorDidChange(indicatorID, isLocked);
     }
 
     render()
     {
-        Instrumentation.startMeasuringTime('ChartPane', 'render');
-
-        super.render();
-
         if (this._platform && this._metric) {
             var metric = this._metric;
             var platform = this._platform;
@@ -215,27 +85,13 @@ class ChartPane extends ComponentBase {
                 metric.fullName() + ' on ' + platform.name());
         }
 
-        if (this._errorMessage) {
-            this.renderReplace(this.content().querySelector('.chart-pane-main'), this._errorMessage);
-            return;
-        }
-
-        if (this._mainChartStatus) {
-            this._mainChartStatus.render();
-            this._renderActionToolbar(this._mainChartStatus.analyzeData());
-        }
-
-        var body = this.content().querySelector('.chart-pane-body');
-        if (this._commitLogViewer.currentRepository()) {
-            body.classList.add('has-second-sidebar');
-            this._commitLogViewer.render();
-        } else
-            body.classList.remove('has-second-sidebar');
+        if (this._mainChartStatus)
+            this._renderActionToolbar();
 
-        Instrumentation.endMeasuringTime('ChartPane', 'render');
+        super.render();
     }
 
-    _renderActionToolbar(analyzeData)
+    _renderActionToolbar()
     {
         var actions = [];
         var platform = this._platform;
@@ -267,7 +123,8 @@ class ChartPane extends ComponentBase {
         }
 
         var analyzePane = this.content().querySelector('.chart-pane-analyze-pane');
-        if (analyzeData) {
+        var pointsRangeForAnalysis = this._mainChartStatus.pointsRangeForAnalysis();
+        if (pointsRangeForAnalysis) {
             actions.push(element('li', {class: this._paneOpenedByClick == analyzePane ? 'selected' : ''},
                 this._makeAnchorToOpenPane(analyzePane, 'Analyze', false)));
 
@@ -277,7 +134,7 @@ class ChartPane extends ComponentBase {
                 var newWindow = window.open(router.url('analysis/task/create'), '_blank');
 
                 var name = analyzePane.querySelector('input').value;
-                AnalysisTask.create(name, analyzeData.startPointId, analyzeData.endPointId).then(function (data) {
+                AnalysisTask.create(name, pointsRangeForAnalysis.startPointId, pointsRangeForAnalysis.endPointId).then(function (data) {
                     newWindow.location.href = router.url('analysis/task/' + data['taskId']);
                     // FIXME: Refetch the list of analysis tasks.
                 }, function (error) {
@@ -359,53 +216,36 @@ class ChartPane extends ComponentBase {
         return anchor;
     }
 
-    static htmlTemplate()
+    static paneHeaderTemplate()
     {
         return `
-            <section class="chart-pane" tabindex="0">
-                <header class="chart-pane-header">
-                    <h2 class="chart-pane-title">-</h2>
-                    <nav class="chart-pane-actions">
-                        <ul>
-                            <li class="close"><close-button></close-button></li>
-                        </ul>
-                        <ul class="chart-pane-action-buttons buttoned-toolbar"></ul>
-                        <ul class="chart-pane-alternative-platforms" style="display:none"></ul>
-                        <form class="chart-pane-analyze-pane" style="display:none">
-                            <input type="text" required>
-                            <button>Create</button>
-                        </form>
-                    </nav>
-                </header>
-                <div class="chart-pane-body">
-                    <div class="chart-pane-main"></div>
-                    <div class="chart-pane-sidebar">
-                        <div class="chart-pane-overview"></div>
-                        <div class="chart-pane-details"></div>
-                    </div>
-                    <div class="chart-pane-second-sidebar">
-                        <commit-log-viewer></commit-log-viewer>
-                    </div>
-                </div>
-            </section>
-`;
+            <header class="chart-pane-header">
+                <h2 class="chart-pane-title">-</h2>
+                <nav class="chart-pane-actions">
+                    <ul>
+                        <li class="close"><close-button></close-button></li>
+                    </ul>
+                    <ul class="chart-pane-action-buttons buttoned-toolbar"></ul>
+                    <ul class="chart-pane-alternative-platforms" style="display:none"></ul>
+                    <form class="chart-pane-analyze-pane" style="display:none">
+                        <input type="text" required>
+                        <button>Create</button>
+                    </form>
+                </nav>
+            </header>
+        `;
     }
 
     static cssTemplate()
     {
-        return Toolbar.cssTemplate() + `
+        return ChartPaneBase.cssTemplate() + `
             .chart-pane {
-                margin: 1rem;
-                margin-bottom: 2rem;
-                padding: 0rem;
-                height: 18rem;
                 border: solid 1px #ccc;
                 border-radius: 0.5rem;
-                outline: none;
             }
 
-            .chart-pane:focus .chart-pane-header {
-                background: rgba(204, 153, 51, 0.1);
+            .chart-pane-body {
+                height: calc(100% - 2rem);
             }
 
             .chart-pane-header {
@@ -494,75 +334,6 @@ class ChartPane extends ComponentBase {
                 outline: none;
                 border: solid 1px #ccc;
             }
-
-            .chart-pane-body {
-                position: relative;
-                width: 100%;
-                height: calc(100% - 2rem);
-            }
-
-            .chart-pane-main {
-                padding-right: 20rem;
-                height: 100%;
-                margin: 0;
-                vertical-align: middle;
-                text-align: center;
-            }
-
-            .has-second-sidebar .chart-pane-main {
-                padding-right: 40rem;
-            }
-
-            .chart-pane-main > * {
-                width: 100%;
-                height: 100%;
-            }
-
-            .chart-pane-sidebar,
-            .chart-pane-second-sidebar {
-                position: absolute;
-                right: 0;
-                top: 0;
-                width: 0;
-                border-left: solid 1px #ccc;
-                height: 100%;
-            }
-
-            :not(.has-second-sidebar) > .chart-pane-second-sidebar {
-                border-left: 0;
-            }
-
-            .chart-pane-sidebar {
-                width: 20rem;
-            }
-
-            .has-second-sidebar .chart-pane-sidebar {
-                right: 20rem;
-            }
-
-            .has-second-sidebar .chart-pane-second-sidebar {
-                width: 20rem;
-            }
-
-            .chart-pane-overview {
-                width: 100%;
-                height: 5rem;
-                border-bottom: solid 1px #ccc;
-            }
-
-            .chart-pane-overview > * {
-                display: block;
-                width: 100%;
-                height: 100%;
-            }
-
-            .chart-pane-details {
-                position: relative;
-                display: block;
-                height: calc(100% - 5.5rem - 2px);
-                overflow-y: scroll;
-                padding-top: 0.5rem;
-            }
 `;
     }
 }
index 89cf48c..c36e3f9 100644 (file)
@@ -1,5 +1,5 @@
 
-class ChartsPage extends PageWithCharts {
+class ChartsPage extends PageWithHeading {
     constructor(toolbar)
     {
         console.assert(toolbar instanceof ChartsToolbar);
@@ -19,13 +19,19 @@ class ChartsPage extends PageWithCharts {
         return state;
     }
 
-    static createStateForAnalysisTask(task)
+    static createDomainForAnalysisTask(task)
     {
         var diff = (task.endTime() - task.startTime()) * 0.1;
+        return [task.startTime() - diff, task.endTime() + diff];
+    }
+
+    static createStateForAnalysisTask(task)
+    {
+        var domain = this.createDomainForAnalysisTask(task);
         var state = {
             paneList: [[task.platform().id(), task.metric().id()]],
             since: Math.round(task.startTime() - (Date.now() - task.startTime()) * 0.1),
-            zoom: [task.startTime() - diff, task.endTime() + diff],
+            zoom: domain,
         };
         return state;
     }
index 27eba95..82a66f8 100644 (file)
@@ -1,10 +1,11 @@
 
-class DashboardPage extends PageWithCharts {
+class DashboardPage extends PageWithHeading {
     constructor(name, table, toolbar)
     {
         console.assert(toolbar instanceof DashboardToolbar);
         super(name, toolbar);
         this._table = table;
+        this._charts = [];
         this._needsTableConstruction = true;
         this._needsStatusUpdate = true;
         this._statusViews = [];
@@ -124,11 +125,11 @@ class DashboardPage extends PageWithCharts {
         if (!platformId || !metricId)
             return '';
 
-        var result = DashboardPage.createChartSourceList(platformId, metricId);
+        var result = ChartStyles.createChartSourceList(platformId, metricId);
         if (result.error)
             return result.error;
 
-        var options = DashboardPage.dashboardOptions(result.metric.makeFormatter(3));
+        var options = ChartStyles.dashboardOptions(result.metric.makeFormatter(3));
         options.ondata = this._fetchedData.bind(this);
         var chart = new TimeSeriesChart(result.sourceList, options);
         this._charts.push(chart);