Add more options to chart viewer (#1307)

* fixing up chart viewer

* formatting

* everything is working

* removed unnecessary code

* removed unneeded code
This commit is contained in:
Anthony Dresser
2018-05-02 10:15:51 -07:00
committed by GitHub
parent 6f10f7a21a
commit e82b7615b3
11 changed files with 522 additions and 134 deletions

View File

@@ -1,4 +1,3 @@
<div #taskbarContainer></div>
<div style="display: flex; flex-flow: row; overflow: hidden; height: 100%; width: 100%">
<div style="flex:3 3 auto; margin: 5px; overflow: scroll">
@@ -9,49 +8,246 @@
<div class="angular-modal-body-content chart-viewer" style="flex:1 1 auto; border-left: 1px solid; margin: 5px;">
<div style="position: relative; width: 100%">
<div class="dialog-label" id="chartTypeLabel">{{localizedStrings.CHART_TYPE}}</div>
<div class="input-divider" aria-labelledby="chartTypeLabel" #chartTypesContainer></div>
<div [hidden]="chartTypesSelectBox.value === 'count' || chartTypesSelectBox.value === 'image'">
<div [hidden]="!showDataType">
<div class="dialog-label" id="dataTypeLabel">{{localizedStrings.DATA_TYPE}}</div>
<div class="radio-indent" role="radiogroup" aria-labelledby="dataTypeLabel">
<div class="option">
<input type="radio" role="radio" name="data-type" value="number" [(ngModel)]="dataType" aria-labelledby="numberLabel"><span id="numberLabel">{{localizedStrings.NUMBER}}</span>
</div>
<div class="option">
<input type="radio" role="radio" name="data-type" value="point" [(ngModel)]="dataType" aria-labelledby="pointLabel"><span id="pointLabel">{{localizedStrings.POINT}}</span>
</div>
</div>
</div>
<div [hidden]="!showDataDirection">
<div class="dialog-label" id="dataDirectionLabel">{{localizedStrings.DATA_DIRECTION}}</div>
<div class="radio-indent" role="radiogroup" aria-labelledby="dataDirectionLabel">
<div class="option">
<input type="radio" role="radio" name="data-direction" value="vertical" [(ngModel)]="dataDirection" aria-labelledby="verticalLabel"><span id="verticalLabel">{{localizedStrings.VERTICAL}}</span>
</div>
<div class="option">
<input type="radio" role="radio" name="data-direction" value="horizontal" [(ngModel)]="dataDirection" aria-labelledby="horizontalLabel"><span id="horizontalLabel">{{localizedStrings.HORIZONTAL}}</span>
</div>
</div>
</div>
<div [hidden]="!showLabelFirstColumn">
<div class="input-divider" #labelFirstColumnContainer></div>
</div>
<div [hidden]="!showColumnsAsLabels">
<div class="input-divider" #columnsAsLabelsContainer></div>
</div>
<div class="dialog-label" id="legendLabel">{{localizedStrings.LEGEND}}</div>
<div class="input-divider" #legendContainer aria-labelledby="legendLabel"></div>
<div class="footer">
<div class="right-footer">
<div class="footer-button" #createInsightButtonContainer></div>
<div class="footer-button" #saveChartButtonContainer></div>
<div class="footer-button" #copyChartButtonContainer></div>
</div>
</div>
</div>
<select-box #chartTypeSelect class="input-divider"
aria-labelledby="chartTypeLabel"
[options]="insightRegistry.getAllIds()"
[selectedOption]="getDefaultChartType()"
[onlyEmitOnChange]="true"
(onDidSelect)="onChartChanged($event)"></select-box>
<ng-container [ngSwitch]="chartTypesSelectBox.value">
<ng-container *ngSwitchCase="'line'">
<ng-container *ngTemplateOutlet="lineInput"></ng-container>
</ng-container>
<ng-container *ngSwitchCase="'scatter'">
<ng-container *ngTemplateOutlet="scatterInput"></ng-container>
</ng-container>
<ng-container *ngSwitchCase="'timeSeries'">
<ng-container *ngTemplateOutlet="scatterInput"></ng-container>
</ng-container>
<ng-container *ngSwitchCase="'bar'">
<ng-container *ngTemplateOutlet="barInput"></ng-container>
</ng-container>
<ng-container *ngSwitchCase="'horizontalBar'">
<ng-container *ngTemplateOutlet="horizontalBarInput"></ng-container>
</ng-container>
<ng-container *ngSwitchCase="'pie'">
<ng-container *ngTemplateOutlet="pieInput"></ng-container>
</ng-container>
<ng-container *ngSwitchCase="'doughnut'">
<ng-container *ngTemplateOutlet="pieInput"></ng-container>
</ng-container>
<ng-container *ngSwitchCase="'count'">
<ng-container *ngTemplateOutlet="countInput"></ng-container>
</ng-container>
<ng-container *ngSwitchCase="'image'">
<ng-container *ngTemplateOutlet="imageInput"></ng-container>
</ng-container>
<ng-container *ngSwitchCase="'table'">
<ng-container *ngTemplateOutlet="tableInput"></ng-container>
</ng-container>
</ng-container>
</div>
</div>
</div>
</div>
<!--
count control interface
-->
<ng-template #countInput>
</ng-template>
<!--
image control interface
-->
<ng-template #imageInput>
</ng-template>
<!--
table control interface
-->
<ng-template #tableInput>
</ng-template>
<!--
Line graph control interface
-->
<ng-template #lineInput>
<ng-container *ngTemplateOutlet="dataTypeInput"></ng-container>
<ng-template [ngIf]="showDataDirection">
<ng-container *ngTemplateOutlet="dataDirectionInput"></ng-container>
</ng-template>
<ng-template [ngIf]="showColumnsAsLabels">
<ng-container *ngTemplateOutlet="columnsAsLabelsInput"></ng-container>
</ng-template>
<ng-template [ngIf]="showLabelFirstColumn">
<ng-container *ngTemplateOutlet="labelFirstColumnInput"></ng-container>
</ng-template>
<ng-container *ngTemplateOutlet="yAxisLabelInput"></ng-container>
<ng-container *ngTemplateOutlet="xAxisLabelInput"></ng-container>
<ng-container *ngTemplateOutlet="legendInput"></ng-container>
</ng-template>
<!--
scatter graph control interface
-->
<ng-template #scatterInput>
<ng-container *ngTemplateOutlet="legendInput"></ng-container>
<ng-container *ngTemplateOutlet="yAxisLabelInput"></ng-container>
<ng-container *ngTemplateOutlet="xAxisLabelInput"></ng-container>
</ng-template>
<!--
bar graph control interface
-->
<ng-template #barInput>
<ng-container *ngTemplateOutlet="dataDirectionInput"></ng-container>
<ng-template [ngIf]="showColumnsAsLabels">
<ng-container *ngTemplateOutlet="columnsAsLabelsInput"></ng-container>
</ng-template>
<ng-template [ngIf]="showLabelFirstColumn">
<ng-container *ngTemplateOutlet="labelFirstColumnInput"></ng-container>
</ng-template>
<ng-container *ngTemplateOutlet="legendInput"></ng-container>
<ng-container *ngTemplateOutlet="yAxisLabelInput"></ng-container>
<ng-container *ngTemplateOutlet="yAxisMinMaxInput"></ng-container>
<ng-container *ngTemplateOutlet="xAxisLabelInput"></ng-container>
</ng-template>
<!--
Horizontal bar graph control interface
-->
<ng-template #horizontalBarInput>
<ng-container *ngTemplateOutlet="dataDirectionInput"></ng-container>
<ng-template [ngIf]="showColumnsAsLabels">
<ng-container *ngTemplateOutlet="columnsAsLabelsInput"></ng-container>
</ng-template>
<ng-template [ngIf]="showLabelFirstColumn">
<ng-container *ngTemplateOutlet="labelFirstColumnInput"></ng-container>
</ng-template>
<ng-container *ngTemplateOutlet="legendInput"></ng-container>
<ng-container *ngTemplateOutlet="xAxisLabelInput"></ng-container>
<ng-container *ngTemplateOutlet="xAxisMinMaxInput"></ng-container>
<ng-container *ngTemplateOutlet="yAxisLabelInput"></ng-container>
</ng-template>
<!--
Pie graph control interface
-->
<ng-template #pieInput>
<ng-container *ngTemplateOutlet="dataDirectionInput"></ng-container>
<ng-template [ngIf]="showColumnsAsLabels">
<ng-container *ngTemplateOutlet="columnsAsLabelsInput"></ng-container>
</ng-template>
<ng-template [ngIf]="showLabelFirstColumn">
<ng-container *ngTemplateOutlet="labelFirstColumnInput"></ng-container>
</ng-template>
<ng-container *ngTemplateOutlet="legendInput"></ng-container>
</ng-template>
<!--
Legend Input Control interface
Valid for any charting type, i.e not image/count
-->
<ng-template #legendInput>
<div class="dialog-label" id="legendLabel">{{localizedStrings.LEGEND}}</div>
<select-box class="input-divider" aria-labelledby="legendLabel"
[options]="legendOptions"
[selectedOption]="_chartConfig.legendPosition"
[onlyEmitOnChange]="true"
(onDidSelect)="setConfigValue('legendPosition', $event.selected)"></select-box>
</ng-template>
<!--
Data type input control interface
point vs number data type, only valid for the line chart type
-->
<ng-template #dataTypeInput>
<div class="dialog-label" id="dataTypeLabel">{{localizedStrings.DATA_TYPE}}</div>
<div class="radio-indent" role="radiogroup" aria-labelledby="dataTypeLabel">
<div class="option">
<input type="radio" role="radio" name="data-type" value="number" [(ngModel)]="dataType" aria-labelledby="numberLabel"><span id="numberLabel">{{localizedStrings.NUMBER}}</span>
</div>
<div class="option">
<input type="radio" role="radio" name="data-type" value="point" [(ngModel)]="dataType" aria-labelledby="pointLabel"><span id="pointLabel">{{localizedStrings.POINT}}</span>
</div>
</div>
</ng-template>
<!--
Data direction input control
vertical vs horizontal, change which direction is considered a "series" of data
only valid for pie, bar, doughnut charts and line is numbr is the data type
otherwise the direction is assumed for each other type
-->
<ng-template #dataDirectionInput>
<div class="dialog-label" id="dataDirectionLabel">{{localizedStrings.DATA_DIRECTION}}</div>
<div class="radio-indent" role="radiogroup" aria-labelledby="dataDirectionLabel">
<div class="option">
<input type="radio" role="radio" name="data-direction" value="vertical" [(ngModel)]="dataDirection" aria-labelledby="verticalLabel"><span id="verticalLabel">{{localizedStrings.VERTICAL}}</span>
</div>
<div class="option">
<input type="radio" role="radio" name="data-direction" value="horizontal" [(ngModel)]="dataDirection" aria-labelledby="horizontalLabel"><span id="horizontalLabel">{{localizedStrings.HORIZONTAL}}</span>
</div>
</div>
</ng-template>
<!--
Control for specifing if the first column should be used to label the series
Only valid for data direction = horizontal
-->
<ng-template #labelFirstColumnInput>
<checkbox class="input-divider" [label]="localizedStrings.LABEL_FIRST_COLUMN" (onChange)="setConfigValue('labelFirstColumn', $event)"></checkbox>
</ng-template>
<!--
Control whether to use column names as series labels
Only valid for data direction = vertical
-->
<ng-template #columnsAsLabelsInput>
<checkbox class="input-divider" [label]="localizedStrings.COLUMNS_AS_LABELS" (onChange)="setConfigValue('columnsAsLabels', $event)"></checkbox>
</ng-template>
<!--
Y-Axis options controls
valid for any charting value
-->
<ng-template #yAxisLabelInput>
<span>
{{localizedStrings.Y_AXIS_LABEL}}
<input-box (onDidChange)="setConfigValue('yAxisLabel', $event)"></input-box>
</span>
</ng-template>
<ng-template #yAxisMinMaxInput>
<span>
{{localizedStrings.Y_AXIS_MAX_VAL}}
<input-box [type]="'number'" (onDidChange)="setConfigValue('yAxisMax', $event)"></input-box>
</span>
<span>
{{localizedStrings.Y_AXIS_MIN_VAL}}
<input-box [type]="'number'" (onDidChange)="setConfigValue('yAxisMin', $event)"></input-box>
</span>
</ng-template>
<!--
X-Axis options controls
valid for any charting value
-->
<ng-template #xAxisLabelInput>
<span>
{{localizedStrings.X_AXIS_LABEL}}
<input-box (onDidChange)="setConfigValue('xAxisLabel', $event)"></input-box>
</span>
</ng-template>
<ng-template #xAxisMinMaxInput>
<span>
{{localizedStrings.X_AXIS_MAX_VAL}}
<input-box [type]="'number'" (onDidChange)="setConfigValue('xAxisMax', $event)"></input-box>
</span>
<span>
{{localizedStrings.X_AXIS_MIN_VAL}}
<input-box [type]="'number'" (onDidChange)="setConfigValue('xAxisMin', $event)"></input-box>
</span>
</ng-template>

View File

@@ -14,7 +14,6 @@ import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox';
import { ComponentHostDirective } from 'sql/parts/dashboard/common/componentHost.directive';
import { IGridDataSet } from 'sql/parts/grid/common/interfaces';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from 'sql/services/bootstrap/bootstrapService';
import { IInsightData, IInsightsView, IInsightsConfig } from 'sql/parts/dashboard/widgets/insights/interfaces';
import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
@@ -24,6 +23,7 @@ import * as PathUtilities from 'sql/common/pathUtilities';
import { IChartViewActionContext, CopyAction, CreateInsightAction, SaveImageAction } from 'sql/parts/grid/views/query/chartViewerActions';
import * as WorkbenchUtils from 'sql/workbench/common/sqlWorkbenchUtils';
import * as Constants from 'sql/parts/query/common/constants';
import { SelectBox as AngularSelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component';
/* Insights */
import {
@@ -39,6 +39,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
import { mixin } from 'vs/base/common/objects';
import * as paths from 'vs/base/common/paths';
import * as pfs from 'vs/base/node/pfs';
import { ISelectData } from 'vs/base/browser/ui/selectBox/selectBox';
const insightRegistry = Registry.as<IInsightRegistry>(Extensions.InsightContribution);
@@ -53,7 +54,13 @@ const LocalizedStrings = {
LABEL_FIRST_COLUMN: nls.localize('labelFirstColumnLabel', 'Use First Column as row label?'),
COLUMNS_AS_LABELS: nls.localize('columnsAsLabelsLabel', 'Use Column names as labels?'),
LEGEND: nls.localize('legendLabel', 'Legend Position'),
CHART_NOT_FOUND: nls.localize('chartNotFound', 'Could not find chart to save')
CHART_NOT_FOUND: nls.localize('chartNotFound', 'Could not find chart to save'),
X_AXIS_LABEL: nls.localize('xAxisLabel', 'X Axis Label'),
X_AXIS_MIN_VAL: nls.localize('xAxisMinVal', 'X Axis Minimum Value'),
X_AXIS_MAX_VAL: nls.localize('xAxisMaxVal', 'X Axis Maximum Value'),
Y_AXIS_LABEL: nls.localize('yAxisLabel', 'Y Axis Label'),
Y_AXIS_MIN_VAL: nls.localize('yAxisMinVal', 'Y Axis Minimum Value'),
Y_AXIS_MAX_VAL: nls.localize('yAxisMaxVal', 'Y Axis Maximum Value')
};
@Component({
@@ -62,10 +69,7 @@ const LocalizedStrings = {
})
export class ChartViewerComponent implements OnInit, OnDestroy, IChartViewActionContext {
public legendOptions: string[];
private chartTypesSelectBox: SelectBox;
private legendSelectBox: SelectBox;
private labelFirstColumnCheckBox: Checkbox;
private columnsAsLabelsCheckBox: Checkbox;
@ViewChild('chartTypeSelect') private chartTypesSelectBox: AngularSelectBox;
/* UI */
@@ -80,13 +84,12 @@ export class ChartViewerComponent implements OnInit, OnDestroy, IChartViewAction
private _chartComponent: ChartInsight;
private localizedStrings = LocalizedStrings;
private insightRegistry = insightRegistry;
@ViewChild(ComponentHostDirective) private componentHost: ComponentHostDirective;
@ViewChild('taskbarContainer', { read: ElementRef }) private taskbarContainer;
@ViewChild('chartTypesContainer', { read: ElementRef }) private chartTypesElement;
@ViewChild('legendContainer', { read: ElementRef }) private legendElement;
@ViewChild('labelFirstColumnContainer', { read: ElementRef }) private labelFirstColumnElement;
@ViewChild('columnsAsLabelsContainer', { read: ElementRef }) private columnsAsLabelsElement;
constructor(
@Inject(forwardRef(() => ComponentFactoryResolver)) private _componentFactoryResolver: ComponentFactoryResolver,
@@ -114,34 +117,6 @@ export class ChartViewerComponent implements OnInit, OnDestroy, IChartViewAction
private initializeUI() {
// Initialize the taskbar
this._initActionBar();
// Init chart type dropdown
this.chartTypesSelectBox = new SelectBox(insightRegistry.getAllIds(), this.getDefaultChartType(), this._bootstrapService.contextViewService);
this.chartTypesSelectBox.render(this.chartTypesElement.nativeElement);
this.chartTypesSelectBox.onDidSelect(selected => this.onChartChanged());
this._disposables.push(attachSelectBoxStyler(this.chartTypesSelectBox, this._bootstrapService.themeService));
// Init label first column checkbox
// Note: must use 'self' for callback
this.labelFirstColumnCheckBox = new Checkbox(this.labelFirstColumnElement.nativeElement, {
label: LocalizedStrings.LABEL_FIRST_COLUMN,
onChange: () => this.onLabelFirstColumnChanged(),
ariaLabel: LocalizedStrings.LABEL_FIRST_COLUMN
});
// Init label first column checkbox
// Note: must use 'self' for callback
this.columnsAsLabelsCheckBox = new Checkbox(this.columnsAsLabelsElement.nativeElement, {
label: LocalizedStrings.COLUMNS_AS_LABELS,
onChange: () => this.columnsAsLabelsChanged(),
ariaLabel: LocalizedStrings.COLUMNS_AS_LABELS
});
// Init legend dropdown
this.legendSelectBox = new SelectBox(this.legendOptions, this._chartConfig.legendPosition, this._bootstrapService.contextViewService);
this.legendSelectBox.render(this.legendElement.nativeElement);
this.legendSelectBox.onDidSelect(selected => this.onLegendChanged());
this._disposables.push(attachSelectBoxStyler(this.legendSelectBox, this._bootstrapService.themeService));
}
private getDefaultChartType(): string {
@@ -171,29 +146,20 @@ export class ChartViewerComponent implements OnInit, OnDestroy, IChartViewAction
]);
}
public onChartChanged(): void {
public onChartChanged(e: ISelectData): void {
this.setDefaultChartConfig();
if ([Constants.chartTypeScatter, Constants.chartTypeTimeSeries].some(item => item === this.chartTypesSelectBox.value)) {
if ([Constants.chartTypeScatter, Constants.chartTypeTimeSeries].some(item => item === e.selected)) {
this.dataType = DataType.Point;
this.dataDirection = DataDirection.Horizontal;
}
this.initChart();
}
public onLabelFirstColumnChanged(): void {
this._chartConfig.labelFirstColumn = this.labelFirstColumnCheckBox.checked;
this.initChart();
}
public columnsAsLabelsChanged(): void {
this._chartConfig.columnsAsLabels = this.columnsAsLabelsCheckBox.checked;
this.initChart();
}
public onLegendChanged(): void {
this._chartConfig.legendPosition = <LegendPosition>this.legendSelectBox.value;
this.initChart();
setConfigValue(key: string, value: any, refresh = true): void {
this._chartConfig[key] = value;
if (refresh) {
this.initChart();
}
}
public set dataType(type: DataType) {