Chore: Fix noImplicitAny issues (#17636)

* noImplicitAny: TableRenderer

* noImplicitAny and add types

* HeatmapData interface

* Heatmap rendering

* Fix optional type

* Remove use of lodash
This commit is contained in:
Tobias Skarhed
2019-06-19 13:40:42 +02:00
committed by GitHub
parent 7eb820081d
commit 17826796da
12 changed files with 164 additions and 111 deletions

View File

@@ -189,6 +189,7 @@
"dependencies": { "dependencies": {
"@babel/polyfill": "7.2.5", "@babel/polyfill": "7.2.5",
"@torkelo/react-select": "2.4.1", "@torkelo/react-select": "2.4.1",
"@types/angular-route": "1.7.0",
"@types/react-redux": "^7.0.8", "@types/react-redux": "^7.0.8",
"@types/reselect": "2.2.0", "@types/reselect": "2.2.0",
"angular": "1.6.6", "angular": "1.6.6",

View File

@@ -1,24 +1,16 @@
import _ from 'lodash'; import _ from 'lodash';
import { TimeSeries } from 'app/core/core';
import { Bucket, HeatmapCard, HeatmapCardStats, YBucket, XBucket } from './types';
const VALUE_INDEX = 0; const VALUE_INDEX = 0;
const TIME_INDEX = 1; const TIME_INDEX = 1;
interface XBucket {
x: number;
buckets: any;
}
interface YBucket {
y: number;
values: number[];
}
/** /**
* Convert histogram represented by the list of series to heatmap object. * Convert histogram represented by the list of series to heatmap object.
* @param seriesList List of time series * @param seriesList List of time series
*/ */
function histogramToHeatmap(seriesList) { function histogramToHeatmap(seriesList: TimeSeries[]) {
const heatmap = {}; const heatmap: any = {};
for (let i = 0; i < seriesList.length; i++) { for (let i = 0; i < seriesList.length; i++) {
const series = seriesList[i]; const series = seriesList[i];
@@ -59,7 +51,7 @@ function histogramToHeatmap(seriesList) {
/** /**
* Sort series representing histogram by label value. * Sort series representing histogram by label value.
*/ */
function sortSeriesByLabel(s1, s2) { function sortSeriesByLabel(s1: { label: string }, s2: { label: string }) {
let label1, label2; let label1, label2;
try { try {
@@ -93,22 +85,6 @@ function parseHistogramLabel(label: string): number {
return value; return value;
} }
interface HeatmapCard {
x: number;
y: number;
yBounds: {
top: number | null;
bottom: number | null;
};
values: number[];
count: number;
}
interface HeatmapCardStats {
min: number;
max: number;
}
/** /**
* Convert buckets into linear array of "cards" - objects, represented heatmap elements. * Convert buckets into linear array of "cards" - objects, represented heatmap elements.
* @param {Object} buckets * @param {Object} buckets
@@ -162,11 +138,11 @@ function convertToCards(buckets: any, hideZero = false): { cards: HeatmapCard[];
* @param {Number} minValue Minimum series value * @param {Number} minValue Minimum series value
* @return {Object} Transformed buckets * @return {Object} Transformed buckets
*/ */
function mergeZeroBuckets(buckets, minValue) { function mergeZeroBuckets(buckets: any, minValue: number) {
_.forEach(buckets, xBucket => { _.forEach(buckets, xBucket => {
const yBuckets = xBucket.buckets; const yBuckets = xBucket.buckets;
const emptyBucket = { const emptyBucket: any = {
bounds: { bottom: 0, top: 0 }, bounds: { bottom: 0, top: 0 },
values: [], values: [],
points: [], points: [],
@@ -176,7 +152,7 @@ function mergeZeroBuckets(buckets, minValue) {
const nullBucket = yBuckets[0] || emptyBucket; const nullBucket = yBuckets[0] || emptyBucket;
const minBucket = yBuckets[minValue] || emptyBucket; const minBucket = yBuckets[minValue] || emptyBucket;
const newBucket = { const newBucket: any = {
y: 0, y: 0,
bounds: { bottom: minValue, top: minBucket.bounds.top || minValue }, bounds: { bottom: minValue, top: minBucket.bounds.top || minValue },
values: [], values: [],
@@ -228,7 +204,7 @@ function mergeZeroBuckets(buckets, minValue) {
* xBucketBound_N: {} * xBucketBound_N: {}
* } * }
*/ */
function convertToHeatMap(seriesList, yBucketSize, xBucketSize, logBase = 1) { function convertToHeatMap(seriesList: TimeSeries[], yBucketSize: number, xBucketSize: number, logBase = 1) {
const heatmap = {}; const heatmap = {};
for (const series of seriesList) { for (const series of seriesList) {
@@ -264,7 +240,7 @@ function convertToHeatMap(seriesList, yBucketSize, xBucketSize, logBase = 1) {
return heatmap; return heatmap;
} }
function pushToXBuckets(buckets, point, bucketNum, seriesName) { function pushToXBuckets(buckets: any, point: any[], bucketNum: number, seriesName: string) {
const value = point[VALUE_INDEX]; const value = point[VALUE_INDEX];
if (value === null || value === undefined || isNaN(value)) { if (value === null || value === undefined || isNaN(value)) {
return; return;
@@ -285,7 +261,13 @@ function pushToXBuckets(buckets, point, bucketNum, seriesName) {
} }
} }
function pushToYBuckets(buckets, bucketNum, value, point, bounds) { function pushToYBuckets(
buckets: Bucket,
bucketNum: number,
value: any,
point: string[],
bounds: { bottom: number; top: number }
) {
let count = 1; let count = 1;
// Use the 3rd argument as scale/count // Use the 3rd argument as scale/count
if (point.length > 3) { if (point.length > 3) {
@@ -306,7 +288,7 @@ function pushToYBuckets(buckets, bucketNum, value, point, bounds) {
} }
} }
function getValueBucketBound(value, yBucketSize, logBase) { function getValueBucketBound(value: any, yBucketSize: number, logBase: number) {
if (logBase === 1) { if (logBase === 1) {
return getBucketBound(value, yBucketSize); return getBucketBound(value, yBucketSize);
} else { } else {
@@ -317,7 +299,7 @@ function getValueBucketBound(value, yBucketSize, logBase) {
/** /**
* Find bucket for given value (for linear scale) * Find bucket for given value (for linear scale)
*/ */
function getBucketBounds(value, bucketSize) { function getBucketBounds(value: number, bucketSize: number) {
let bottom, top; let bottom, top;
bottom = Math.floor(value / bucketSize) * bucketSize; bottom = Math.floor(value / bucketSize) * bucketSize;
top = (Math.floor(value / bucketSize) + 1) * bucketSize; top = (Math.floor(value / bucketSize) + 1) * bucketSize;
@@ -325,12 +307,12 @@ function getBucketBounds(value, bucketSize) {
return { bottom, top }; return { bottom, top };
} }
function getBucketBound(value, bucketSize) { function getBucketBound(value: number, bucketSize: number) {
const bounds = getBucketBounds(value, bucketSize); const bounds = getBucketBounds(value, bucketSize);
return bounds.bottom; return bounds.bottom;
} }
function convertToValueBuckets(xBucket, bucketSize) { function convertToValueBuckets(xBucket: { values: any; points: any }, bucketSize: number) {
const values = xBucket.values; const values = xBucket.values;
const points = xBucket.points; const points = xBucket.points;
const buckets = {}; const buckets = {};
@@ -347,7 +329,7 @@ function convertToValueBuckets(xBucket, bucketSize) {
/** /**
* Find bucket for given value (for log scales) * Find bucket for given value (for log scales)
*/ */
function getLogScaleBucketBounds(value, yBucketSplitFactor, logBase) { function getLogScaleBucketBounds(value: number, yBucketSplitFactor: number, logBase: number) {
let top, bottom; let top, bottom;
if (value === 0) { if (value === 0) {
return { bottom: 0, top: 0 }; return { bottom: 0, top: 0 };
@@ -371,12 +353,16 @@ function getLogScaleBucketBounds(value, yBucketSplitFactor, logBase) {
return { bottom, top }; return { bottom, top };
} }
function getLogScaleBucketBound(value, yBucketSplitFactor, logBase) { function getLogScaleBucketBound(value: number, yBucketSplitFactor: number, logBase: number) {
const bounds = getLogScaleBucketBounds(value, yBucketSplitFactor, logBase); const bounds = getLogScaleBucketBounds(value, yBucketSplitFactor, logBase);
return bounds.bottom; return bounds.bottom;
} }
function convertToLogScaleValueBuckets(xBucket, yBucketSplitFactor, logBase) { function convertToLogScaleValueBuckets(
xBucket: { values: any; points: any },
yBucketSplitFactor: number,
logBase: number
) {
const values = xBucket.values; const values = xBucket.values;
const points = xBucket.points; const points = xBucket.points;
@@ -395,7 +381,7 @@ function convertToLogScaleValueBuckets(xBucket, yBucketSplitFactor, logBase) {
* @param value * @param value
* @param base logarithm base * @param base logarithm base
*/ */
function logp(value, base) { function logp(value: number, base: number) {
return Math.log(value) / Math.log(base); return Math.log(value) / Math.log(base);
} }

View File

@@ -19,7 +19,7 @@ const MIN_CARD_SIZE = 1,
Y_AXIS_TICK_PADDING = 5, Y_AXIS_TICK_PADDING = 5,
MIN_SELECTION_WIDTH = 2; MIN_SELECTION_WIDTH = 2;
export default function rendering(scope, elem, attrs, ctrl) { export default function rendering(scope: any, elem: any, attrs: any, ctrl: any) {
return new HeatmapRenderer(scope, elem, attrs, ctrl); return new HeatmapRenderer(scope, elem, attrs, ctrl);
} }
export class HeatmapRenderer { export class HeatmapRenderer {
@@ -51,7 +51,7 @@ export class HeatmapRenderer {
padding: any; padding: any;
margin: any; margin: any;
dataRangeWidingFactor: number; dataRangeWidingFactor: number;
constructor(private scope, private elem, attrs, private ctrl) { constructor(private scope: any, private elem: any, attrs: any, private ctrl: any) {
// $heatmap is JQuery object, but heatmap is D3 // $heatmap is JQuery object, but heatmap is D3
this.$heatmap = this.elem.find('.heatmap-panel'); this.$heatmap = this.elem.find('.heatmap-panel');
this.tooltip = new HeatmapTooltip(this.$heatmap, this.scope); this.tooltip = new HeatmapTooltip(this.$heatmap, this.scope);
@@ -89,7 +89,7 @@ export class HeatmapRenderer {
this.clearCrosshair(); this.clearCrosshair();
} }
onGraphHover(event) { onGraphHover(event: { pos: any }) {
this.drawSharedCrosshair(event.pos); this.drawSharedCrosshair(event.pos);
} }
@@ -116,7 +116,7 @@ export class HeatmapRenderer {
} }
} }
getYAxisWidth(elem) { getYAxisWidth(elem: any) {
const axisText = elem.selectAll('.axis-y text').nodes(); const axisText = elem.selectAll('.axis-y text').nodes();
const maxTextWidth = _.max( const maxTextWidth = _.max(
_.map(axisText, text => { _.map(axisText, text => {
@@ -128,7 +128,7 @@ export class HeatmapRenderer {
return maxTextWidth; return maxTextWidth;
} }
getXAxisHeight(elem) { getXAxisHeight(elem: any) {
const axisLine = elem.select('.axis-x line'); const axisLine = elem.select('.axis-x line');
if (!axisLine.empty()) { if (!axisLine.empty()) {
const axisLinePosition = parseFloat(elem.select('.axis-x line').attr('y2')); const axisLinePosition = parseFloat(elem.select('.axis-x line').attr('y2'));
@@ -244,7 +244,7 @@ export class HeatmapRenderer {
} }
// Wide Y values range and anjust to bucket size // Wide Y values range and anjust to bucket size
wideYAxisRange(min, max, tickInterval) { wideYAxisRange(min: number, max: number, tickInterval: number) {
const yWiding = (max * (this.dataRangeWidingFactor - 1) - min * (this.dataRangeWidingFactor - 1)) / 2; const yWiding = (max * (this.dataRangeWidingFactor - 1) - min * (this.dataRangeWidingFactor - 1)) / 2;
let yMin, yMax; let yMin, yMax;
@@ -349,7 +349,7 @@ export class HeatmapRenderer {
this.ctrl.decimals = decimals; this.ctrl.decimals = decimals;
const tickValueFormatter = this.tickValueFormatter.bind(this); const tickValueFormatter = this.tickValueFormatter.bind(this);
function tickFormatter(valIndex) { function tickFormatter(valIndex: string) {
let valueFormatted = tsBuckets[valIndex]; let valueFormatted = tsBuckets[valIndex];
if (!_.isNaN(_.toNumber(valueFormatted)) && valueFormatted !== '') { if (!_.isNaN(_.toNumber(valueFormatted)) && valueFormatted !== '') {
// Try to format numeric tick labels // Try to format numeric tick labels
@@ -393,7 +393,7 @@ export class HeatmapRenderer {
} }
// Adjust data range to log base // Adjust data range to log base
adjustLogRange(min, max, logBase) { adjustLogRange(min: number, max: number, logBase: number) {
let yMin = this.data.heatmapStats.minLog; let yMin = this.data.heatmapStats.minLog;
if (this.data.heatmapStats.minLog > 1 || !this.data.heatmapStats.minLog) { if (this.data.heatmapStats.minLog > 1 || !this.data.heatmapStats.minLog) {
yMin = 1; yMin = 1;
@@ -407,15 +407,15 @@ export class HeatmapRenderer {
return { yMin, yMax }; return { yMin, yMax };
} }
adjustLogMax(max, base) { adjustLogMax(max: number, base: number) {
return Math.pow(base, Math.ceil(ticksUtils.logp(max, base))); return Math.pow(base, Math.ceil(ticksUtils.logp(max, base)));
} }
adjustLogMin(min, base) { adjustLogMin(min: number, base: number) {
return Math.pow(base, Math.floor(ticksUtils.logp(min, base))); return Math.pow(base, Math.floor(ticksUtils.logp(min, base)));
} }
logScaleTickValues(domain, base) { logScaleTickValues(domain: any[], base: number) {
const domainMin = domain[0]; const domainMin = domain[0];
const domainMax = domain[1]; const domainMax = domain[1];
const tickValues = []; const tickValues = [];
@@ -437,9 +437,9 @@ export class HeatmapRenderer {
return tickValues; return tickValues;
} }
tickValueFormatter(decimals, scaledDecimals = null) { tickValueFormatter(decimals: number, scaledDecimals: any = null) {
const format = this.panel.yAxis.format; const format = this.panel.yAxis.format;
return value => { return (value: any) => {
try { try {
return format !== 'none' ? getValueFormat(format)(value, decimals, scaledDecimals) : value; return format !== 'none' ? getValueFormat(format)(value, decimals, scaledDecimals) : value;
} catch (err) { } catch (err) {
@@ -555,17 +555,17 @@ export class HeatmapRenderer {
const $cards = this.$heatmap.find('.heatmap-card'); const $cards = this.$heatmap.find('.heatmap-card');
$cards $cards
.on('mouseenter', event => { .on('mouseenter', (event: any) => {
this.tooltip.mouseOverBucket = true; this.tooltip.mouseOverBucket = true;
this.highlightCard(event); this.highlightCard(event);
}) })
.on('mouseleave', event => { .on('mouseleave', (event: any) => {
this.tooltip.mouseOverBucket = false; this.tooltip.mouseOverBucket = false;
this.resetCardHighLight(event); this.resetCardHighLight(event);
}); });
} }
highlightCard(event) { highlightCard(event: any) {
const color = d3.select(event.target).style('fill'); const color = d3.select(event.target).style('fill');
const highlightColor = d3.color(color).darker(2); const highlightColor = d3.color(color).darker(2);
const strokeColor = d3.color(color).brighter(4); const strokeColor = d3.color(color).brighter(4);
@@ -577,7 +577,7 @@ export class HeatmapRenderer {
.style('stroke-width', 1); .style('stroke-width', 1);
} }
resetCardHighLight(event) { resetCardHighLight(event: any) {
d3.select(event.target) d3.select(event.target)
.style('fill', this.tooltip.originalFillColor) .style('fill', this.tooltip.originalFillColor)
.style('stroke', this.tooltip.originalFillColor) .style('stroke', this.tooltip.originalFillColor)
@@ -599,7 +599,7 @@ export class HeatmapRenderer {
this.cardHeight = yGridSize ? yGridSize - this.cardPadding * 2 : 0; this.cardHeight = yGridSize ? yGridSize - this.cardPadding * 2 : 0;
} }
getCardX(d) { getCardX(d: { x: any }) {
let x; let x;
if (this.xScale(d.x) < 0) { if (this.xScale(d.x) < 0) {
// Cut card left to prevent overlay // Cut card left to prevent overlay
@@ -611,7 +611,7 @@ export class HeatmapRenderer {
return x; return x;
} }
getCardWidth(d) { getCardWidth(d: { x: any }) {
let w = this.cardWidth; let w = this.cardWidth;
if (this.xScale(d.x) < 0) { if (this.xScale(d.x) < 0) {
// Cut card left to prevent overlay // Cut card left to prevent overlay
@@ -626,7 +626,7 @@ export class HeatmapRenderer {
return w; return w;
} }
getCardY(d) { getCardY(d: { y: number }) {
let y = this.yScale(d.y) + this.chartTop - this.cardHeight - this.cardPadding; let y = this.yScale(d.y) + this.chartTop - this.cardHeight - this.cardPadding;
if (this.panel.yAxis.logBase !== 1 && d.y === 0) { if (this.panel.yAxis.logBase !== 1 && d.y === 0) {
y = this.chartBottom - this.cardHeight - this.cardPadding; y = this.chartBottom - this.cardHeight - this.cardPadding;
@@ -639,7 +639,7 @@ export class HeatmapRenderer {
return y; return y;
} }
getCardHeight(d) { getCardHeight(d: { y: number }) {
const y = this.yScale(d.y) + this.chartTop - this.cardHeight - this.cardPadding; const y = this.yScale(d.y) + this.chartTop - this.cardHeight - this.cardPadding;
let h = this.cardHeight; let h = this.cardHeight;
@@ -664,7 +664,7 @@ export class HeatmapRenderer {
return h; return h;
} }
getCardColor(d) { getCardColor(d: { count: any }) {
if (this.panel.color.mode === 'opacity') { if (this.panel.color.mode === 'opacity') {
return getColorFromHexRgbOrName( return getColorFromHexRgbOrName(
this.panel.color.cardColor, this.panel.color.cardColor,
@@ -675,7 +675,7 @@ export class HeatmapRenderer {
} }
} }
getCardOpacity(d) { getCardOpacity(d: { count: any }) {
if (this.panel.color.mode === 'opacity') { if (this.panel.color.mode === 'opacity') {
return this.opacityScale(d.count); return this.opacityScale(d.count);
} else { } else {
@@ -683,14 +683,14 @@ export class HeatmapRenderer {
} }
} }
getEventOffset(event) { getEventOffset(event: any) {
const elemOffset = this.$heatmap.offset(); const elemOffset = this.$heatmap.offset();
const x = Math.floor(event.clientX - elemOffset.left); const x = Math.floor(event.clientX - elemOffset.left);
const y = Math.floor(event.clientY - elemOffset.top); const y = Math.floor(event.clientY - elemOffset.top);
return { x, y }; return { x, y };
} }
onMouseDown(event) { onMouseDown(event: any) {
const offset = this.getEventOffset(event); const offset = this.getEventOffset(event);
this.selection.active = true; this.selection.active = true;
this.selection.x1 = offset.x; this.selection.x1 = offset.x;
@@ -726,7 +726,7 @@ export class HeatmapRenderer {
this.clearCrosshair(); this.clearCrosshair();
} }
onMouseMove(event) { onMouseMove(event: any) {
if (!this.heatmap) { if (!this.heatmap) {
return; return;
} }
@@ -747,10 +747,10 @@ export class HeatmapRenderer {
} }
} }
getEventPos(event, offset) { getEventPos(event: { pageX: any; pageY: any }, offset: { x: any; y: any }) {
const x = this.xScale.invert(offset.x - this.yAxisWidth).valueOf(); const x = this.xScale.invert(offset.x - this.yAxisWidth).valueOf();
const y = this.yScale.invert(offset.y - this.chartTop); const y = this.yScale.invert(offset.y - this.chartTop);
const pos = { const pos: any = {
pageX: event.pageX, pageX: event.pageX,
pageY: event.pageY, pageY: event.pageY,
x: x, x: x,
@@ -764,20 +764,20 @@ export class HeatmapRenderer {
return pos; return pos;
} }
emitGraphHoverEvent(pos) { emitGraphHoverEvent(pos: { panelRelY: number; offset: { y: number } }) {
// Set minimum offset to prevent showing legend from another panel // Set minimum offset to prevent showing legend from another panel
pos.panelRelY = Math.max(pos.offset.y / this.height, 0.001); pos.panelRelY = Math.max(pos.offset.y / this.height, 0.001);
// broadcast to other graph panels that we are hovering // broadcast to other graph panels that we are hovering
appEvents.emit('graph-hover', { pos: pos, panel: this.panel }); appEvents.emit('graph-hover', { pos: pos, panel: this.panel });
} }
limitSelection(x2) { limitSelection(x2: number) {
x2 = Math.max(x2, this.yAxisWidth); x2 = Math.max(x2, this.yAxisWidth);
x2 = Math.min(x2, this.chartWidth + this.yAxisWidth); x2 = Math.min(x2, this.chartWidth + this.yAxisWidth);
return x2; return x2;
} }
drawSelection(posX1, posX2) { drawSelection(posX1: number, posX2: number) {
if (this.heatmap) { if (this.heatmap) {
this.heatmap.selectAll('.heatmap-selection').remove(); this.heatmap.selectAll('.heatmap-selection').remove();
const selectionX = Math.min(posX1, posX2); const selectionX = Math.min(posX1, posX2);
@@ -804,7 +804,7 @@ export class HeatmapRenderer {
} }
} }
drawCrosshair(position) { drawCrosshair(position: number) {
if (this.heatmap) { if (this.heatmap) {
this.heatmap.selectAll('.heatmap-crosshair').remove(); this.heatmap.selectAll('.heatmap-crosshair').remove();
@@ -825,7 +825,7 @@ export class HeatmapRenderer {
} }
} }
drawSharedCrosshair(pos) { drawSharedCrosshair(pos: { x: any }) {
if (this.heatmap && this.ctrl.dashboard.graphTooltip !== 0) { if (this.heatmap && this.ctrl.dashboard.graphTooltip !== 0) {
const posX = this.xScale(pos.x) + this.yAxisWidth; const posX = this.xScale(pos.x) + this.yAxisWidth;
this.drawCrosshair(posX); this.drawCrosshair(posX);

View File

@@ -8,6 +8,7 @@ import {
calculateBucketSize, calculateBucketSize,
isHeatmapDataEqual, isHeatmapDataEqual,
} from '../heatmap_data_converter'; } from '../heatmap_data_converter';
import { HeatmapData } from '../types';
describe('isHeatmapDataEqual', () => { describe('isHeatmapDataEqual', () => {
const ctx: any = {}; const ctx: any = {};
@@ -248,7 +249,7 @@ describe('Histogram converter', () => {
beforeEach(() => {}); beforeEach(() => {});
it('should build proper heatmap data', () => { it('should build proper heatmap data', () => {
const expectedHeatmap = { const expectedHeatmap: HeatmapData = {
'1422774000000': { '1422774000000': {
x: 1422774000000, x: 1422774000000,
buckets: { buckets: {
@@ -322,7 +323,7 @@ describe('Histogram converter', () => {
}); });
describe('convertToCards', () => { describe('convertToCards', () => {
let buckets = {}; let buckets: HeatmapData = {};
beforeEach(() => { beforeEach(() => {
buckets = { buckets = {

View File

@@ -0,0 +1,42 @@
export interface Bucket {
[x: string]: {
y: any;
bounds: any;
values: any[];
points?: any[];
count: number;
};
}
export interface XBucket {
x: number;
buckets: any;
}
export interface YBucket {
y: number;
values: number[];
}
export interface HeatmapCard {
x: number;
y: number;
yBounds: {
top: number | null;
bottom: number | null;
};
values: number[];
count: number;
}
export interface HeatmapCardStats {
min: number;
max: number;
}
export interface HeatmapData {
[key: string]: {
x: number;
buckets: Bucket;
};
}

View File

@@ -1,25 +1,27 @@
import _ from 'lodash'; import _ from 'lodash';
import { getValueFormat, getColorFromHexRgbOrName, GrafanaThemeType } from '@grafana/ui'; import { getValueFormat, getColorFromHexRgbOrName, GrafanaThemeType, ScopedVars } from '@grafana/ui';
import { stringToJsRegex } from '@grafana/data'; import { stringToJsRegex } from '@grafana/data';
import { ColumnStyle } from '@grafana/ui/src/components/Table/TableCellBuilder'; import { ColumnStyle } from '@grafana/ui/src/components/Table/TableCellBuilder';
import { dateTime } from '@grafana/ui/src/utils/moment_wrapper'; import { dateTime } from '@grafana/ui/src/utils/moment_wrapper';
import { TemplateSrv } from 'app/features/templating/template_srv';
import { TableRenderModel, ColumnRender } from './types';
export class TableRenderer { export class TableRenderer {
formatters: any[]; formatters: any[];
colorState: any; colorState: any;
constructor( constructor(
private panel, private panel: { styles: ColumnStyle[]; pageSize: number },
private table, private table: TableRenderModel,
private isUtc, private isUtc: boolean,
private sanitize, private sanitize: (v: any) => any,
private templateSrv, private templateSrv: TemplateSrv,
private theme?: GrafanaThemeType private theme?: GrafanaThemeType
) { ) {
this.initColumns(); this.initColumns();
} }
setTable(table) { setTable(table: TableRenderModel) {
this.table = table; this.table = table;
this.initColumns(); this.initColumns();
@@ -52,7 +54,7 @@ export class TableRenderer {
} }
} }
getColorForValue(value, style: ColumnStyle) { getColorForValue(value: number, style: ColumnStyle) {
if (!style.thresholds) { if (!style.thresholds) {
return null; return null;
} }
@@ -64,7 +66,7 @@ export class TableRenderer {
return getColorFromHexRgbOrName(_.first(style.colors), this.theme); return getColorFromHexRgbOrName(_.first(style.colors), this.theme);
} }
defaultCellFormatter(v, style: ColumnStyle) { defaultCellFormatter(v: any, style: ColumnStyle) {
if (v === null || v === void 0 || v === undefined) { if (v === null || v === void 0 || v === undefined) {
return ''; return '';
} }
@@ -80,19 +82,17 @@ export class TableRenderer {
} }
} }
createColumnFormatter(column) { createColumnFormatter(column: ColumnRender) {
if (!column.style) { if (!column.style) {
return this.defaultCellFormatter; return this.defaultCellFormatter;
} }
if (column.style.type === 'hidden') { if (column.style.type === 'hidden') {
return v => { return (v: any): undefined => undefined;
return undefined;
};
} }
if (column.style.type === 'date') { if (column.style.type === 'date') {
return v => { return (v: any) => {
if (v === undefined || v === null) { if (v === undefined || v === null) {
return '-'; return '-';
} }
@@ -117,7 +117,7 @@ export class TableRenderer {
} }
if (column.style.type === 'string') { if (column.style.type === 'string') {
return v => { return (v: any): any => {
if (_.isArray(v)) { if (_.isArray(v)) {
v = v.join(', '); v = v.join(', ');
} }
@@ -173,7 +173,7 @@ export class TableRenderer {
if (column.style.type === 'number') { if (column.style.type === 'number') {
const valueFormatter = getValueFormat(column.unit || column.style.unit); const valueFormatter = getValueFormat(column.unit || column.style.unit);
return v => { return (v: any): any => {
if (v === null || v === void 0) { if (v === null || v === void 0) {
return '-'; return '-';
} }
@@ -187,12 +187,12 @@ export class TableRenderer {
}; };
} }
return value => { return (value: any) => {
return this.defaultCellFormatter(value, column.style); return this.defaultCellFormatter(value, column.style);
}; };
} }
setColorState(value, style: ColumnStyle) { setColorState(value: any, style: ColumnStyle) {
if (!style.colorMode) { if (!style.colorMode) {
return; return;
} }
@@ -209,22 +209,22 @@ export class TableRenderer {
this.colorState[style.colorMode] = this.getColorForValue(numericValue, style); this.colorState[style.colorMode] = this.getColorForValue(numericValue, style);
} }
renderRowVariables(rowIndex) { renderRowVariables(rowIndex: number) {
const scopedVars = {}; const scopedVars: ScopedVars = {};
let cellVariable; let cellVariable;
const row = this.table.rows[rowIndex]; const row = this.table.rows[rowIndex];
for (let i = 0; i < row.length; i++) { for (let i = 0; i < row.length; i++) {
cellVariable = `__cell_${i}`; cellVariable = `__cell_${i}`;
scopedVars[cellVariable] = { value: row[i] }; scopedVars[cellVariable] = { value: row[i], text: row[i] ? row[i].toString() : '' };
} }
return scopedVars; return scopedVars;
} }
formatColumnValue(colIndex, value) { formatColumnValue(colIndex: number, value: any) {
return this.formatters[colIndex] ? this.formatters[colIndex](value) : value; return this.formatters[colIndex] ? this.formatters[colIndex](value) : value;
} }
renderCell(columnIndex, rowIndex, value, addWidthHack = false) { renderCell(columnIndex: number, rowIndex: number, value: any, addWidthHack = false) {
value = this.formatColumnValue(columnIndex, value); value = this.formatColumnValue(columnIndex, value);
const column = this.table.columns[columnIndex]; const column = this.table.columns[columnIndex];
@@ -267,7 +267,7 @@ export class TableRenderer {
if (column.style && column.style.link) { if (column.style && column.style.link) {
// Render cell as link // Render cell as link
const scopedVars = this.renderRowVariables(rowIndex); const scopedVars = this.renderRowVariables(rowIndex);
scopedVars['__cell'] = { value: value }; scopedVars['__cell'] = { value: value, text: value ? value.toString() : '' };
const cellLink = this.templateSrv.replace(column.style.linkUrl, scopedVars, encodeURIComponent); const cellLink = this.templateSrv.replace(column.style.linkUrl, scopedVars, encodeURIComponent);
const cellLinkTooltip = this.templateSrv.replace(column.style.linkTooltip, scopedVars); const cellLinkTooltip = this.templateSrv.replace(column.style.linkTooltip, scopedVars);
@@ -305,7 +305,7 @@ export class TableRenderer {
return columnHtml; return columnHtml;
} }
render(page) { render(page: number) {
const pageSize = this.panel.pageSize || 100; const pageSize = this.panel.pageSize || 100;
const startPos = page * pageSize; const startPos = page * pageSize;
const endPos = Math.min(startPos + pageSize, this.table.rows.length); const endPos = Math.min(startPos + pageSize, this.table.rows.length);

View File

@@ -1,7 +1,7 @@
import _ from 'lodash'; import _ from 'lodash';
import TableModel from 'app/core/table_model'; import TableModel from 'app/core/table_model';
import { TableRenderer } from '../renderer'; import { TableRenderer } from '../renderer';
import { getColorDefinitionByName } from '@grafana/ui'; import { getColorDefinitionByName, ScopedVars } from '@grafana/ui';
describe('when rendering table', () => { describe('when rendering table', () => {
const SemiDarkOrange = getColorDefinitionByName('semi-dark-orange'); const SemiDarkOrange = getColorDefinitionByName('semi-dark-orange');
@@ -166,12 +166,12 @@ describe('when rendering table', () => {
], ],
}; };
const sanitize = value => { const sanitize = (value: any): string => {
return 'sanitized'; return 'sanitized';
}; };
const templateSrv = { const templateSrv = {
replace: (value, scopedVars) => { replace: (value: any, scopedVars: ScopedVars) => {
if (scopedVars) { if (scopedVars) {
// For testing variables replacement in link // For testing variables replacement in link
_.each(scopedVars, (val, key) => { _.each(scopedVars, (val, key) => {
@@ -182,6 +182,7 @@ describe('when rendering table', () => {
}, },
}; };
//@ts-ignore
const renderer = new TableRenderer(panel, table, 'utc', sanitize, templateSrv); const renderer = new TableRenderer(panel, table, 'utc', sanitize, templateSrv);
it('time column should be formated', () => { it('time column should be formated', () => {
@@ -386,6 +387,6 @@ describe('when rendering table', () => {
}); });
}); });
function normalize(str) { function normalize(str: string) {
return str.replace(/\s+/gm, ' ').trim(); return str.replace(/\s+/gm, ' ').trim();
} }

View File

@@ -1,7 +1,20 @@
import TableModel from 'app/core/table_model'; import TableModel from 'app/core/table_model';
import { Column } from '@grafana/ui';
import { ColumnStyle } from '@grafana/ui/src/components/Table/TableCellBuilder';
export interface TableTransform { export interface TableTransform {
description: string; description: string;
getColumns(data?: any): any[]; getColumns(data?: any): any[];
transform(data: any, panel: any, model: TableModel): void; transform(data: any, panel: any, model: TableModel): void;
} }
export interface ColumnRender extends Column {
title: string;
style: ColumnStyle;
hidden: boolean;
}
export interface TableRenderModel {
columns: ColumnRender[];
rows: any[][];
}

View File

@@ -19,7 +19,7 @@ export class TablePanelEditor extends PureComponent<PanelEditorProps<Options>> {
this.props.onOptionsChange({ ...this.props.options, rotate: !this.props.options.rotate }); this.props.onOptionsChange({ ...this.props.options, rotate: !this.props.options.rotate });
}; };
onFixedColumnsChange = ({ target }) => { onFixedColumnsChange = ({ target }: any) => {
this.props.onOptionsChange({ ...this.props.options, fixedColumns: target.value }); this.props.onOptionsChange({ ...this.props.options, fixedColumns: target.value });
}; };

View File

@@ -19,7 +19,7 @@ interface State {
export class TextPanel extends PureComponent<Props, State> { export class TextPanel extends PureComponent<Props, State> {
remarkable: Remarkable; remarkable: Remarkable;
constructor(props) { constructor(props: Props) {
super(props); super(props);
this.state = { this.state = {

View File

@@ -25,17 +25,19 @@ import DashboardPage from '../features/dashboard/containers/DashboardPage';
import PluginPage from '../features/plugins/PluginPage'; import PluginPage from '../features/plugins/PluginPage';
import AppRootPage from 'app/features/plugins/AppRootPage'; import AppRootPage from 'app/features/plugins/AppRootPage';
import config from 'app/core/config'; import config from 'app/core/config';
import { route, ILocationProvider } from 'angular';
// Types // Types
import { DashboardRouteInfo } from 'app/types'; import { DashboardRouteInfo } from 'app/types';
/** @ngInject */ /** @ngInject */
export function setupAngularRoutes($routeProvider, $locationProvider) { export function setupAngularRoutes($routeProvider: route.IRouteProvider, $locationProvider: ILocationProvider) {
$locationProvider.html5Mode(true); $locationProvider.html5Mode(true);
$routeProvider $routeProvider
.when('/', { .when('/', {
template: '<react-container />', template: '<react-container />',
//@ts-ignore
pageClass: 'page-dashboard', pageClass: 'page-dashboard',
routeInfo: DashboardRouteInfo.Home, routeInfo: DashboardRouteInfo.Home,
reloadOnSearch: false, reloadOnSearch: false,

View File

@@ -1664,7 +1664,14 @@
react-input-autosize "^2.2.1" react-input-autosize "^2.2.1"
react-transition-group "^2.2.1" react-transition-group "^2.2.1"
"@types/angular@1.6.54": "@types/angular-route@1.7.0":
version "1.7.0"
resolved "https://registry.yarnpkg.com/@types/angular-route/-/angular-route-1.7.0.tgz#ba12d09df1aac3c88e3684500001daedfc97fb69"
integrity sha512-gctkSXUY7hDwFeW8il7f3+sdMmds0JaMnHvrZmqX79DHXf2D72+SHxJHIt5i6+0BrhoqdWdpgziSxuKnkAUAQw==
dependencies:
"@types/angular" "*"
"@types/angular@*", "@types/angular@1.6.54":
version "1.6.54" version "1.6.54"
resolved "https://registry.yarnpkg.com/@types/angular/-/angular-1.6.54.tgz#f9d5a03e4da7b021a6dabe5d63e899ed4567a5bd" resolved "https://registry.yarnpkg.com/@types/angular/-/angular-1.6.54.tgz#f9d5a03e4da7b021a6dabe5d63e899ed4567a5bd"