mirror of
https://github.com/apache/superset.git
synced 2024-09-19 12:09:42 -04:00
feat: implement ECharts pie chart (#772)
This commit is contained in:
parent
6ac6880fd9
commit
2169a0b37e
@ -25,7 +25,7 @@ export default {
|
||||
{
|
||||
label: t('Query'),
|
||||
expanded: true,
|
||||
controlSetRows: [['metric'], ['adhoc_filters'], ['groupby'], ['row_limit']],
|
||||
controlSetRows: [['groupby'], ['metric'], ['adhoc_filters'], ['row_limit']],
|
||||
},
|
||||
{
|
||||
label: t('Chart Options'),
|
||||
|
@ -17,9 +17,9 @@
|
||||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { EchartsPieProps } from './types';
|
||||
import { EchartsProps } from '../types';
|
||||
import Echart from '../components/Echart';
|
||||
|
||||
export default function EchartsPie({ height, width, echartOptions }: EchartsPieProps) {
|
||||
export default function EchartsPie({ height, width, echartOptions }: EchartsProps) {
|
||||
return <Echart height={height} width={width} echartOptions={echartOptions} />;
|
||||
}
|
||||
|
@ -17,19 +17,98 @@
|
||||
* under the License.
|
||||
*/
|
||||
import { t, validateNonEmpty } from '@superset-ui/core';
|
||||
import { ControlPanelConfig } from '@superset-ui/chart-controls';
|
||||
import { ControlPanelConfig, D3_FORMAT_OPTIONS } from '@superset-ui/chart-controls';
|
||||
|
||||
const config: ControlPanelConfig = {
|
||||
controlPanelSections: [
|
||||
{
|
||||
label: t('Query'),
|
||||
expanded: true,
|
||||
controlSetRows: [['groupby'], ['metrics'], ['adhoc_filters'], ['row_limit', null]],
|
||||
controlSetRows: [['groupby'], ['metric'], ['adhoc_filters'], ['row_limit', null]],
|
||||
},
|
||||
{
|
||||
label: t('Chart Options'),
|
||||
expanded: true,
|
||||
controlSetRows: [
|
||||
[
|
||||
{
|
||||
name: 'pie_label_type',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
label: t('Label Type'),
|
||||
default: 'key',
|
||||
renderTrigger: true,
|
||||
choices: [
|
||||
['key', 'Category Name'],
|
||||
['value', 'Value'],
|
||||
['percent', 'Percentage'],
|
||||
['key_value', 'Category and Value'],
|
||||
['key_percent', 'Category and Percentage'],
|
||||
['key_value_percent', 'Category, Value and Percentage'],
|
||||
],
|
||||
description: t('What should be shown on the label?'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'number_format',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
freeForm: true,
|
||||
label: t('Number format'),
|
||||
renderTrigger: true,
|
||||
default: 'SMART_NUMBER',
|
||||
choices: D3_FORMAT_OPTIONS,
|
||||
description: `${t('D3 format syntax: https://github.com/d3/d3-format')} ${t(
|
||||
'Only applies when "Label Type" is set to show values.',
|
||||
)}`,
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'donut',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Donut'),
|
||||
default: false,
|
||||
renderTrigger: true,
|
||||
description: t('Do you want a donut or a pie?'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'show_legend',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Legend'),
|
||||
renderTrigger: true,
|
||||
default: true,
|
||||
description: t('Whether to display a legend for the chart'),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'show_labels',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Show Labels'),
|
||||
renderTrigger: true,
|
||||
default: true,
|
||||
description: t('Whether to display the labels.'),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'labels_outside',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Put labels outside'),
|
||||
default: true,
|
||||
renderTrigger: true,
|
||||
description: t('Put the labels outside of the pie?'),
|
||||
},
|
||||
},
|
||||
],
|
||||
['color_scheme', 'label_colors'],
|
||||
[
|
||||
{
|
||||
name: 'outerRadius',
|
||||
@ -40,10 +119,12 @@ const config: ControlPanelConfig = {
|
||||
min: 10,
|
||||
max: 100,
|
||||
step: 1,
|
||||
default: 70,
|
||||
default: 80,
|
||||
description: t('Outer edge of Pie chart'),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'innerRadius',
|
||||
config: {
|
||||
@ -53,7 +134,7 @@ const config: ControlPanelConfig = {
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
default: 50,
|
||||
default: 40,
|
||||
description: t('Inner radius of donut hole'),
|
||||
},
|
||||
},
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 102 KiB |
@ -22,12 +22,6 @@ import controlPanel from './controlPanel';
|
||||
import transformProps from './transformProps';
|
||||
import thumbnail from './images/thumbnail.png';
|
||||
|
||||
const metadata = new ChartMetadata({
|
||||
description: 'Pie chart using ECharts',
|
||||
name: t('EchartsPie'),
|
||||
thumbnail,
|
||||
});
|
||||
|
||||
export default class EchartsPieChartPlugin extends ChartPlugin {
|
||||
/**
|
||||
* The constructor is used to pass relevant metadata and callbacks that get
|
||||
@ -44,7 +38,11 @@ export default class EchartsPieChartPlugin extends ChartPlugin {
|
||||
buildQuery,
|
||||
controlPanel,
|
||||
loadChart: () => import('./EchartsPie'),
|
||||
metadata,
|
||||
metadata: new ChartMetadata({
|
||||
description: 'Pie chart using ECharts',
|
||||
name: t('ECharts Pie'),
|
||||
thumbnail,
|
||||
}),
|
||||
transformProps,
|
||||
});
|
||||
}
|
||||
|
@ -16,60 +16,126 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { ChartProps, DataRecord } from '@superset-ui/core';
|
||||
import { EchartsPieProps } from './types';
|
||||
import {
|
||||
CategoricalColorNamespace,
|
||||
ChartProps,
|
||||
convertMetric,
|
||||
DataRecord,
|
||||
getNumberFormatter,
|
||||
NumberFormats,
|
||||
NumberFormatter,
|
||||
} from '@superset-ui/core';
|
||||
import { EchartsPieLabelType, PieChartFormData } from './types';
|
||||
import { EchartsProps } from '../types';
|
||||
import { extractGroupbyLabel } from '../utils/series';
|
||||
|
||||
export default function transformProps(chartProps: ChartProps): EchartsPieProps {
|
||||
/*
|
||||
TODO:
|
||||
- add support for multiple groupby (requires post transform op)
|
||||
- add support for ad-hoc metrics (currently only supports datasource metrics)
|
||||
- add support for superset colors
|
||||
- add support for control values in legacy pie chart
|
||||
*/
|
||||
const percentFormatter = getNumberFormatter(NumberFormats.PERCENT_2_POINT);
|
||||
|
||||
export function formatPieLabel({
|
||||
params,
|
||||
pieLabelType,
|
||||
numberFormatter,
|
||||
}: {
|
||||
params: echarts.EChartOption.Tooltip.Format;
|
||||
pieLabelType: EchartsPieLabelType;
|
||||
numberFormatter: NumberFormatter;
|
||||
}): string {
|
||||
const { name = '', value, percent } = params;
|
||||
const formattedValue = numberFormatter(value as number);
|
||||
const formattedPercent = percentFormatter((percent as number) / 100);
|
||||
if (pieLabelType === 'key') return name;
|
||||
if (pieLabelType === 'value') return formattedValue;
|
||||
if (pieLabelType === 'percent') return formattedPercent;
|
||||
if (pieLabelType === 'key_value') return `${name}: ${formattedValue}`;
|
||||
if (pieLabelType === 'key_value_percent')
|
||||
return `${name}: ${formattedValue} (${formattedPercent})`;
|
||||
if (pieLabelType === 'key_percent') return `${name}: ${formattedPercent}`;
|
||||
return name;
|
||||
}
|
||||
|
||||
export default function transformProps(chartProps: ChartProps): EchartsProps {
|
||||
const { width, height, formData, queryData } = chartProps;
|
||||
const data: DataRecord[] = queryData.data || [];
|
||||
|
||||
const { innerRadius = 50, outerRadius = 70, groupby = [], metrics = [] } = formData;
|
||||
const {
|
||||
colorScheme,
|
||||
donut = false,
|
||||
groupby,
|
||||
innerRadius = 40,
|
||||
labelsOutside = true,
|
||||
metric,
|
||||
numberFormat,
|
||||
outerRadius = 80,
|
||||
pieLabelType = 'value',
|
||||
showLabels = true,
|
||||
showLegend = false,
|
||||
} = formData as PieChartFormData;
|
||||
const { label: metricLabel } = convertMetric(metric);
|
||||
|
||||
const keys = Array.from(new Set(data.map(datum => datum[groupby[0]])));
|
||||
const keys = data.map(datum => extractGroupbyLabel(datum, groupby));
|
||||
const colorFn = CategoricalColorNamespace.getScale(colorScheme as string);
|
||||
const numberFormatter = getNumberFormatter(numberFormat);
|
||||
|
||||
const transformedData = data.map(datum => {
|
||||
const name = extractGroupbyLabel(datum, groupby);
|
||||
return {
|
||||
value: datum[metrics[0]],
|
||||
name: datum[groupby[0]],
|
||||
value: datum[metricLabel],
|
||||
name,
|
||||
itemStyle: {
|
||||
color: colorFn(name),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const echartOptions = {
|
||||
const formatter = (params: { name: string; value: number; percent: number }) =>
|
||||
formatPieLabel({ params, numberFormatter, pieLabelType });
|
||||
|
||||
const echartOptions: echarts.EChartOption<echarts.EChartOption.SeriesPie> = {
|
||||
tooltip: {
|
||||
confine: true,
|
||||
trigger: 'item',
|
||||
formatter: '{b}: {c} ({d}%)',
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 10,
|
||||
data: keys,
|
||||
formatter: params => {
|
||||
return formatPieLabel({
|
||||
params: params as echarts.EChartOption.Tooltip.Format,
|
||||
numberFormatter,
|
||||
pieLabelType: 'key_value_percent',
|
||||
});
|
||||
},
|
||||
},
|
||||
legend: showLegend
|
||||
? {
|
||||
orient: 'horizontal',
|
||||
left: 10,
|
||||
data: keys,
|
||||
}
|
||||
: undefined,
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
radius: [`${innerRadius}%`, `${outerRadius}%`],
|
||||
avoidLabelOverlap: false,
|
||||
label: {
|
||||
show: false,
|
||||
position: 'center',
|
||||
},
|
||||
radius: [`${donut ? innerRadius : 0}%`, `${outerRadius}%`],
|
||||
avoidLabelOverlap: true,
|
||||
labelLine: labelsOutside ? { show: true } : { show: false },
|
||||
label: labelsOutside
|
||||
? {
|
||||
formatter,
|
||||
position: 'outer',
|
||||
show: showLabels,
|
||||
alignTo: 'none',
|
||||
bleedMargin: 5,
|
||||
}
|
||||
: {
|
||||
formatter,
|
||||
position: 'inner',
|
||||
show: showLabels,
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: '30',
|
||||
fontSize: 30,
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
},
|
||||
labelLine: {
|
||||
show: false,
|
||||
},
|
||||
// @ts-ignore
|
||||
data: transformedData,
|
||||
},
|
||||
],
|
||||
@ -78,7 +144,6 @@ export default function transformProps(chartProps: ChartProps): EchartsPieProps
|
||||
return {
|
||||
width,
|
||||
height,
|
||||
// @ts-ignore
|
||||
echartOptions,
|
||||
};
|
||||
}
|
||||
|
@ -17,26 +17,24 @@
|
||||
* under the License.
|
||||
*/
|
||||
import { QueryFormData } from '@superset-ui/core';
|
||||
import { EchartsProps } from '../types';
|
||||
|
||||
export type PieChartFormData = QueryFormData & {
|
||||
groupby?: string[];
|
||||
metrics?: string[];
|
||||
groupby: string[];
|
||||
metric: string;
|
||||
outerRadius?: number;
|
||||
innerRadius?: number;
|
||||
colorScheme?: string;
|
||||
donut?: boolean;
|
||||
showLegend?: boolean;
|
||||
showLabels?: boolean;
|
||||
labelsOutside?: boolean;
|
||||
numberFormat?: string;
|
||||
};
|
||||
|
||||
export type EchartsPieProps = EchartsProps & {
|
||||
formData: PieChartFormData;
|
||||
area: number;
|
||||
colorScheme: string;
|
||||
contributionMode?: string;
|
||||
zoomable?: boolean;
|
||||
seriesType: string;
|
||||
logAxis: boolean;
|
||||
stack: boolean;
|
||||
markerEnabled: boolean;
|
||||
markerSize: number;
|
||||
minorSplitLine: boolean;
|
||||
opacity: number;
|
||||
};
|
||||
export type EchartsPieLabelType =
|
||||
| 'key'
|
||||
| 'value'
|
||||
| 'percent'
|
||||
| 'key_value'
|
||||
| 'key_percent'
|
||||
| 'key_value_percent';
|
||||
|
@ -40,7 +40,7 @@ export default function buildQuery(formData: QueryFormData) {
|
||||
return [
|
||||
{
|
||||
...baseQueryObject,
|
||||
// Time series charts need to set the `is_timeseries` flag to true
|
||||
groupby: formData.series,
|
||||
is_timeseries: true,
|
||||
post_processing: [
|
||||
{
|
||||
|
@ -22,12 +22,6 @@ import controlPanel from './controlPanel';
|
||||
import transformProps from './transformProps';
|
||||
import thumbnail from './images/thumbnail.png';
|
||||
|
||||
const metadata = new ChartMetadata({
|
||||
description: 'ECharts Timeseries',
|
||||
name: t('ECharts Timeseries'),
|
||||
thumbnail,
|
||||
});
|
||||
|
||||
export default class EchartsTimeseriesChartPlugin extends ChartPlugin {
|
||||
/**
|
||||
* The constructor is used to pass relevant metadata and callbacks that get
|
||||
@ -44,7 +38,11 @@ export default class EchartsTimeseriesChartPlugin extends ChartPlugin {
|
||||
buildQuery,
|
||||
controlPanel,
|
||||
loadChart: () => import('./EchartsTimeseries'),
|
||||
metadata,
|
||||
metadata: new ChartMetadata({
|
||||
description: 'ECharts Timeseries',
|
||||
name: t('ECharts Timeseries'),
|
||||
thumbnail,
|
||||
}),
|
||||
transformProps,
|
||||
});
|
||||
}
|
||||
|
@ -52,7 +52,6 @@ export default function transformProps(chartProps: ChartProps): EchartsTimeserie
|
||||
|
||||
const colorFn = CategoricalColorNamespace.getScale(colorScheme as string);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||
const rebasedData = rebaseTimeseriesDatum(queryData.data || []);
|
||||
const rawSeries = extractTimeseriesSeries(rebasedData);
|
||||
|
||||
|
@ -17,9 +17,8 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { TimeseriesDataRecord } from '@superset-ui/core';
|
||||
import { DataRecord, TimeseriesDataRecord } from '@superset-ui/core';
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export function extractTimeseriesSeries(
|
||||
data: TimeseriesDataRecord[],
|
||||
): echarts.EChartOption.Series[] {
|
||||
@ -39,3 +38,8 @@ export function extractTimeseriesSeries(
|
||||
data: rows.map(datum => [datum.__timestamp, datum[key]]),
|
||||
}));
|
||||
}
|
||||
|
||||
export function extractGroupbyLabel(datum: DataRecord, groupby: string[]): string {
|
||||
// TODO: apply formatting to dates and numbers
|
||||
return groupby.map(val => `${datum[val]}`).join(', ');
|
||||
}
|
||||
|
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import buildQuery from '../../src/Pie/buildQuery';
|
||||
|
||||
describe('Pie buildQuery', () => {
|
||||
const formData = {
|
||||
datasource: '5__table',
|
||||
granularity_sqla: 'ds',
|
||||
metric: 'foo',
|
||||
groupby: ['bar'],
|
||||
viz_type: 'my_chart',
|
||||
};
|
||||
|
||||
it('should build groupby with series in form data', () => {
|
||||
const queryContext = buildQuery(formData);
|
||||
const [query] = queryContext.queries;
|
||||
expect(query.metrics).toEqual([{ label: 'foo' }]);
|
||||
expect(query.groupby).toEqual(['bar']);
|
||||
});
|
||||
});
|
@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import 'babel-polyfill';
|
||||
import { ChartProps, getNumberFormatter } from '@superset-ui/core';
|
||||
import transformProps, { formatPieLabel } from '../../src/Pie/transformProps';
|
||||
|
||||
describe('Pie tranformProps', () => {
|
||||
const formData = {
|
||||
colorScheme: 'bnbColors',
|
||||
datasource: '3__table',
|
||||
granularity_sqla: 'ds',
|
||||
metric: 'sum__num',
|
||||
groupby: ['foo', 'bar'],
|
||||
};
|
||||
const chartProps = new ChartProps({
|
||||
formData,
|
||||
width: 800,
|
||||
height: 600,
|
||||
queryData: {
|
||||
data: [
|
||||
{ foo: 'Sylvester', bar: 1, sum__num: 10 },
|
||||
{ foo: 'Arnold', bar: 2, sum__num: 2.5 },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
it('should tranform chart props for viz', () => {
|
||||
expect(transformProps(chartProps)).toEqual(
|
||||
expect.objectContaining({
|
||||
width: 800,
|
||||
height: 600,
|
||||
echartOptions: expect.objectContaining({
|
||||
series: [
|
||||
expect.objectContaining({
|
||||
avoidLabelOverlap: true,
|
||||
data: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
name: 'Arnold, 2',
|
||||
value: 2.5,
|
||||
}),
|
||||
expect.objectContaining({
|
||||
name: 'Sylvester, 1',
|
||||
value: 10,
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatPieLabel', () => {
|
||||
it('should generate a valid pie chart label', () => {
|
||||
const numberFormatter = getNumberFormatter();
|
||||
const params = { name: 'My Label', value: 1234, percent: 12.34 };
|
||||
expect(formatPieLabel({ params, numberFormatter, pieLabelType: 'key' })).toEqual('My Label');
|
||||
expect(formatPieLabel({ params, numberFormatter, pieLabelType: 'value' })).toEqual('1.23k');
|
||||
expect(formatPieLabel({ params, numberFormatter, pieLabelType: 'percent' })).toEqual('12.34%');
|
||||
expect(formatPieLabel({ params, numberFormatter, pieLabelType: 'key_value' })).toEqual(
|
||||
'My Label: 1.23k',
|
||||
);
|
||||
expect(formatPieLabel({ params, numberFormatter, pieLabelType: 'key_percent' })).toEqual(
|
||||
'My Label: 12.34%',
|
||||
);
|
||||
expect(formatPieLabel({ params, numberFormatter, pieLabelType: 'key_value_percent' })).toEqual(
|
||||
'My Label: 1.23k (12.34%)',
|
||||
);
|
||||
});
|
||||
});
|
@ -18,18 +18,19 @@
|
||||
*/
|
||||
import buildQuery from '../../src/Timeseries/buildQuery';
|
||||
|
||||
describe('EchartsTimeseries buildQuery', () => {
|
||||
describe('Timeseries buildQuery', () => {
|
||||
const formData = {
|
||||
datasource: '5__table',
|
||||
granularity_sqla: 'ds',
|
||||
series: 'foo',
|
||||
series: ['foo'],
|
||||
metrics: ['bar', 'baz'],
|
||||
viz_type: 'my_chart',
|
||||
queryFields: { series: 'groupby' },
|
||||
};
|
||||
|
||||
it('should build groupby with series in form data', () => {
|
||||
const queryContext = buildQuery(formData);
|
||||
const [query] = queryContext.queries;
|
||||
expect(query.groupby).toEqual(['foo']);
|
||||
expect(query.metrics).toEqual([{ label: 'bar' }, { label: 'baz' }]);
|
||||
});
|
||||
});
|
||||
|
@ -26,14 +26,17 @@ describe('EchartsTimeseries tranformProps', () => {
|
||||
datasource: '3__table',
|
||||
granularity_sqla: 'ds',
|
||||
metric: 'sum__num',
|
||||
series: 'name',
|
||||
series: ['foo', 'bar'],
|
||||
};
|
||||
const chartProps = new ChartProps({
|
||||
formData,
|
||||
width: 800,
|
||||
height: 600,
|
||||
queryData: {
|
||||
data: [{ sum__num: 1, __timestamp: 599616000000 }],
|
||||
data: [
|
||||
{ 'San Francisco': 1, 'New York': 2, __timestamp: 599616000000 },
|
||||
{ 'San Francisco': 3, 'New York': 4, __timestamp: 599916000000 },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
@ -43,10 +46,23 @@ describe('EchartsTimeseries tranformProps', () => {
|
||||
width: 800,
|
||||
height: 600,
|
||||
echartOptions: expect.objectContaining({
|
||||
legend: expect.objectContaining({
|
||||
data: ['San Francisco', 'New York'],
|
||||
}),
|
||||
series: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
data: [[new Date(599616000000), 1]],
|
||||
id: 'sum__num',
|
||||
data: [
|
||||
[new Date(599616000000), 1],
|
||||
[new Date(599916000000), 3],
|
||||
],
|
||||
name: 'San Francisco',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
data: [
|
||||
[new Date(599616000000), 2],
|
||||
[new Date(599916000000), 4],
|
||||
],
|
||||
name: 'New York',
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
|
@ -16,7 +16,7 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { extractTimeseriesSeries } from '../../src/utils/series';
|
||||
import { extractGroupbyLabel, extractTimeseriesSeries } from '../../src/utils/series';
|
||||
|
||||
describe('extractTimeseriesSeries', () => {
|
||||
it('should generate a valid ECharts timeseries series object', () => {
|
||||
@ -57,3 +57,23 @@ describe('extractTimeseriesSeries', () => {
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('extractGroupbyLabel', () => {
|
||||
it('should join together multiple groupby labels', () => {
|
||||
expect(extractGroupbyLabel({ a: 'abc', b: 'qwerty' }, ['a', 'b'])).toEqual('abc, qwerty');
|
||||
});
|
||||
|
||||
it('should handle a single groupby', () => {
|
||||
expect(extractGroupbyLabel({ xyz: 'qqq' }, ['xyz'])).toEqual('qqq');
|
||||
});
|
||||
|
||||
it('should handle mixed types', () => {
|
||||
expect(
|
||||
extractGroupbyLabel({ strcol: 'abc', intcol: 123, floatcol: 0.123 }, [
|
||||
'strcol',
|
||||
'intcol',
|
||||
'floatcol',
|
||||
]),
|
||||
).toEqual('abc, 123, 0.123');
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user