Skip to content

Commit

Permalink
Implement Pinterest delta table tooltip feature on timeseries charts
Browse files Browse the repository at this point in the history
  • Loading branch information
kgopal492 committed Nov 7, 2024
1 parent b935c63 commit 3e09215
Show file tree
Hide file tree
Showing 13 changed files with 537 additions and 98 deletions.
22 changes: 18 additions & 4 deletions superset-frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions superset-frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@
"dom-to-image-more": "^3.2.0",
"dom-to-pdf": "^0.3.1",
"emotion-rgba": "0.0.12",
"escape-html": "^1.0.3",
"fast-glob": "^3.2.7",
"fontsource-fira-code": "^4.0.0",
"fs-extra": "^10.0.0",
Expand Down Expand Up @@ -248,6 +249,7 @@
"@types/dom-to-image": "^2.6.7",
"@types/enzyme": "^3.10.18",
"@types/enzyme-adapter-react-16": "^1.0.6",
"@types/escape-html": "^1.0.4",
"@types/fetch-mock": "^7.3.2",
"@types/jest": "^26.0.3",
"@types/jquery": "^3.5.8",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {
xAxisBounds,
xAxisLabelRotation,
} from '../controls';
import { pinterestCustomConfig } from '../pinterest-utils/controls';

const {
area,
Expand Down Expand Up @@ -448,6 +449,7 @@ const config: ControlPanelConfig = {
},
},
],
...pinterestCustomConfig,
],
},
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ import {
getXAxisFormatter,
getYAxisFormatter,
} from '../utils/formatters';
import { getDeltaTableTooltipFormatter } from '../pinterest-utils/tooltip';

const getFormatter = (
customFormatters: Record<string, ValueFormatter>,
Expand Down Expand Up @@ -201,6 +202,7 @@ export default function transformProps(
percentageThreshold,
metrics = [],
metricsB = [],
pinterestDeltaTable,
}: EchartsMixedTimeseriesFormData = { ...DEFAULT_FORM_DATA, ...formData };

const refs: Refs = {};
Expand Down Expand Up @@ -573,59 +575,61 @@ export default function transformProps(
...getDefaultTooltip(refs),
show: !inContextMenu,
trigger: richTooltip ? 'axis' : 'item',
formatter: (params: any) => {
const xValue: number = richTooltip
? params[0].value[0]
: params.value[0];
const forecastValue: any[] = richTooltip ? params : [params];
formatter: pinterestDeltaTable
? getDeltaTableTooltipFormatter(chartProps)
: (params: any) => {
const xValue: number = richTooltip
? params[0].value[0]
: params.value[0];
const forecastValue: any[] = richTooltip ? params : [params];

if (richTooltip && tooltipSortByMetric) {
forecastValue.sort((a, b) => b.data[1] - a.data[1]);
}
if (richTooltip && tooltipSortByMetric) {
forecastValue.sort((a, b) => b.data[1] - a.data[1]);
}

const rows: Array<string> = [`${tooltipFormatter(xValue)}`];
const forecastValues =
extractForecastValuesFromTooltipParams(forecastValue);
const rows: Array<string> = [`${tooltipFormatter(xValue)}`];
const forecastValues =
extractForecastValuesFromTooltipParams(forecastValue);

Object.keys(forecastValues).forEach(key => {
const value = forecastValues[key];
// if there are no dimensions, key is a verbose name of a metric,
// otherwise it is a comma separated string where the first part is metric name
let formatterKey;
if (primarySeries.has(key)) {
formatterKey =
groupby.length === 0 ? inverted[key] : labelMap[key]?.[0];
} else {
formatterKey =
groupbyB.length === 0 ? inverted[key] : labelMapB[key]?.[0];
}
const tooltipFormatter = getFormatter(
customFormatters,
formatter,
metrics,
formatterKey,
!!contributionMode,
);
const tooltipFormatterSecondary = getFormatter(
customFormattersSecondary,
formatterSecondary,
metricsB,
formatterKey,
!!contributionMode,
);
const content = formatForecastTooltipSeries({
...value,
seriesName: key,
formatter: primarySeries.has(key)
? tooltipFormatter
: tooltipFormatterSecondary,
});
const contentStyle =
key === focusedSeries ? 'font-weight: 700' : 'opacity: 0.7';
rows.push(`<span style="${contentStyle}">${content}</span>`);
});
return rows.join('<br />');
},
Object.keys(forecastValues).forEach(key => {
const value = forecastValues[key];
// if there are no dimensions, key is a verbose name of a metric,
// otherwise it is a comma separated string where the first part is metric name
let formatterKey;
if (primarySeries.has(key)) {
formatterKey =
groupby.length === 0 ? inverted[key] : labelMap[key]?.[0];
} else {
formatterKey =
groupbyB.length === 0 ? inverted[key] : labelMapB[key]?.[0];
}
const tooltipFormatter = getFormatter(
customFormatters,
formatter,
metrics,
formatterKey,
!!contributionMode,
);
const tooltipFormatterSecondary = getFormatter(
customFormattersSecondary,
formatterSecondary,
metricsB,
formatterKey,
!!contributionMode,
);
const content = formatForecastTooltipSeries({
...value,
seriesName: key,
formatter: primarySeries.has(key)
? tooltipFormatter
: tooltipFormatterSecondary,
});
const contentStyle =
key === focusedSeries ? 'font-weight: 700' : 'opacity: 0.7';
rows.push(`<span style="${contentStyle}">${content}</span>`);
});
return rows.join('<br />');
},
},
legend: {
...getLegendProps(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {
DEFAULT_TITLE_FORM_DATA,
DEFAULT_FORM_DATA as TIMESERIES_DEFAULTS,
} from '../constants';
import { PinterestFormData } from '../pinterest-utils/types';

export type EchartsMixedTimeseriesFormData = QueryFormData & {
annotationLayers: AnnotationLayer[];
Expand Down Expand Up @@ -88,7 +89,8 @@ export type EchartsMixedTimeseriesFormData = QueryFormData & {
groupby: QueryFormColumn[];
groupbyB: QueryFormColumn[];
} & LegendFormData &
TitleFormData;
TitleFormData &
PinterestFormData;

// @ts-ignore
export const DEFAULT_FORM_DATA: EchartsMixedTimeseriesFormData = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import {
minorTicks,
} from '../../controls';
import { AreaChartStackControlOptions } from '../../constants';
import { pinterestCustomConfig } from '../../pinterest-utils/controls';

const {
logAxis,
Expand Down Expand Up @@ -259,6 +260,7 @@ const config: ControlPanelConfig = {
},
},
],
...pinterestCustomConfig,
],
},
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
sharedControls,
} from '@superset-ui/chart-controls';

import { pinterestCustomConfig } from '../../../pinterest-utils/controls';
import { EchartsTimeseriesSeriesType } from '../../types';
import {
DEFAULT_FORM_DATA,
Expand Down Expand Up @@ -247,6 +248,7 @@ const config: ControlPanelConfig = {
},
},
],
...pinterestCustomConfig,
],
},
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ import {
getXAxisFormatter,
getYAxisFormatter,
} from '../utils/formatters';
import { getDeltaTableTooltipFormatter } from '../pinterest-utils/tooltip';

export default function transformProps(
chartProps: EchartsTimeseriesChartProps,
Expand Down Expand Up @@ -183,6 +184,7 @@ export default function transformProps(
yAxisTitleMargin,
yAxisTitlePosition,
zoomable,
pinterestDeltaTable,
}: EchartsTimeseriesFormData = { ...DEFAULT_FORM_DATA, ...formData };
const refs: Refs = {};
const groupBy = ensureIsArray(groupby);
Expand Down Expand Up @@ -527,48 +529,56 @@ export default function transformProps(
...getDefaultTooltip(refs),
show: !inContextMenu,
trigger: richTooltip ? 'axis' : 'item',
formatter: (params: any) => {
const [xIndex, yIndex] = isHorizontal ? [1, 0] : [0, 1];
const xValue: number = richTooltip
? params[0].value[xIndex]
: params.value[xIndex];
const forecastValue: any[] = richTooltip ? params : [params];

if (richTooltip && tooltipSortByMetric) {
forecastValue.sort((a, b) => b.data[yIndex] - a.data[yIndex]);
}

const rows: string[] = [];
const forecastValues: Record<string, ForecastValue> =
extractForecastValuesFromTooltipParams(forecastValue, isHorizontal);

Object.keys(forecastValues).forEach(key => {
const value = forecastValues[key];
if (value.observation === 0 && stack) {
return;
}
// if there are no dimensions, key is a verbose name of a metric,
// otherwise it is a comma separated string where the first part is metric name
const formatterKey =
groupBy.length === 0 ? inverted[key] : labelMap[key]?.[0];
const content = formatForecastTooltipSeries({
...value,
seriesName: key,
formatter: forcePercentFormatter
? percentFormatter
: getCustomFormatter(customFormatters, metrics, formatterKey) ??
defaultFormatter,
});
const contentStyle =
key === focusedSeries ? 'font-weight: 700' : 'opacity: 0.7';
rows.push(`<span style="${contentStyle}">${content}</span>`);
});
if (stack) {
rows.reverse();
}
rows.unshift(`${tooltipFormatter(xValue)}`);
return rows.join('<br />');
},
formatter: pinterestDeltaTable
? getDeltaTableTooltipFormatter(chartProps)
: (params: any) => {
const [xIndex, yIndex] = isHorizontal ? [1, 0] : [0, 1];
const xValue: number = richTooltip
? params[0].value[xIndex]
: params.value[xIndex];
const forecastValue: any[] = richTooltip ? params : [params];

if (richTooltip && tooltipSortByMetric) {
forecastValue.sort((a, b) => b.data[yIndex] - a.data[yIndex]);
}

const rows: string[] = [];
const forecastValues: Record<string, ForecastValue> =
extractForecastValuesFromTooltipParams(
forecastValue,
isHorizontal,
);

Object.keys(forecastValues).forEach(key => {
const value = forecastValues[key];
if (value.observation === 0 && stack) {
return;
}
// if there are no dimensions, key is a verbose name of a metric,
// otherwise it is a comma separated string where the first part is metric name
const formatterKey =
groupBy.length === 0 ? inverted[key] : labelMap[key]?.[0];
const content = formatForecastTooltipSeries({
...value,
seriesName: key,
formatter: forcePercentFormatter
? percentFormatter
: getCustomFormatter(
customFormatters,
metrics,
formatterKey,
) ?? defaultFormatter,
});
const contentStyle =
key === focusedSeries ? 'font-weight: 700' : 'opacity: 0.7';
rows.push(`<span style="${contentStyle}">${content}</span>`);
});
if (stack) {
rows.reverse();
}
rows.unshift(`${tooltipFormatter(xValue)}`);
return rows.join('<br />');
},
},
legend: {
...getLegendProps(
Expand Down
Loading

0 comments on commit 3e09215

Please sign in to comment.