mirror of
https://github.com/apache/superset.git
synced 2024-09-17 11:09:47 -04:00
feat(plugin-chart-pivot-table): column, date and conditional formatting (#1217)
* feat(plugin-chart-pivot-table): implement conditional and date formatting * Use custom icons for expand/collapse * Fix tests * Revert changes to ControlForm * Fix tests * Rename variable
This commit is contained in:
parent
89474f84e5
commit
fe5f9b093e
@ -28,10 +28,11 @@
|
||||
"dependencies": {
|
||||
"@superset-ui/chart-controls": "0.17.67",
|
||||
"@superset-ui/core": "0.17.64",
|
||||
"@superset-ui/react-pivottable": "^0.12.8"
|
||||
"@superset-ui/react-pivottable": "^0.12.9"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.13.1"
|
||||
"react": "^16.13.1",
|
||||
"@ant-design/icons": "^4.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/types": "^7.13.12",
|
||||
|
@ -16,8 +16,15 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import React, { useCallback } from 'react';
|
||||
import { styled, AdhocMetric, getNumberFormatter, DataRecordValue } from '@superset-ui/core';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { PlusSquareOutlined, MinusSquareOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
styled,
|
||||
AdhocMetric,
|
||||
getNumberFormatter,
|
||||
DataRecordValue,
|
||||
NumberFormatter,
|
||||
} from '@superset-ui/core';
|
||||
// @ts-ignore
|
||||
import PivotTable from '@superset-ui/react-pivottable/PivotTable';
|
||||
// @ts-ignore
|
||||
@ -40,6 +47,52 @@ const Styles = styled.div<PivotTableStylesProps>`
|
||||
`;
|
||||
|
||||
const METRIC_KEY = 'metric';
|
||||
const iconStyle = { stroke: 'black', strokeWidth: '16px' };
|
||||
|
||||
const aggregatorsFactory = (formatter: NumberFormatter) => ({
|
||||
Count: aggregatorTemplates.count(formatter),
|
||||
'Count Unique Values': aggregatorTemplates.countUnique(formatter),
|
||||
'List Unique Values': aggregatorTemplates.listUnique(', '),
|
||||
Sum: aggregatorTemplates.sum(formatter),
|
||||
Average: aggregatorTemplates.average(formatter),
|
||||
Median: aggregatorTemplates.median(formatter),
|
||||
'Sample Variance': aggregatorTemplates.var(1, formatter),
|
||||
'Sample Standard Deviation': aggregatorTemplates.stdev(1, formatter),
|
||||
Minimum: aggregatorTemplates.min(formatter),
|
||||
Maximum: aggregatorTemplates.max(formatter),
|
||||
First: aggregatorTemplates.first(),
|
||||
Last: aggregatorTemplates.last(formatter),
|
||||
'Sum as Fraction of Total': aggregatorTemplates.fractionOf(
|
||||
aggregatorTemplates.sum(),
|
||||
'total',
|
||||
formatter,
|
||||
),
|
||||
'Sum as Fraction of Rows': aggregatorTemplates.fractionOf(
|
||||
aggregatorTemplates.sum(),
|
||||
'row',
|
||||
formatter,
|
||||
),
|
||||
'Sum as Fraction of Columns': aggregatorTemplates.fractionOf(
|
||||
aggregatorTemplates.sum(),
|
||||
'col',
|
||||
formatter,
|
||||
),
|
||||
'Count as Fraction of Total': aggregatorTemplates.fractionOf(
|
||||
aggregatorTemplates.count(),
|
||||
'total',
|
||||
formatter,
|
||||
),
|
||||
'Count as Fraction of Rows': aggregatorTemplates.fractionOf(
|
||||
aggregatorTemplates.count(),
|
||||
'row',
|
||||
formatter,
|
||||
),
|
||||
'Count as Fraction of Columns': aggregatorTemplates.fractionOf(
|
||||
aggregatorTemplates.count(),
|
||||
'col',
|
||||
formatter,
|
||||
),
|
||||
});
|
||||
|
||||
export default function PivotTableChart(props: PivotTableProps) {
|
||||
const {
|
||||
@ -49,11 +102,11 @@ export default function PivotTableChart(props: PivotTableProps) {
|
||||
groupbyRows,
|
||||
groupbyColumns,
|
||||
metrics,
|
||||
tableRenderer,
|
||||
colOrder,
|
||||
rowOrder,
|
||||
aggregateFunction,
|
||||
transposePivot,
|
||||
combineMetric,
|
||||
rowSubtotalPosition,
|
||||
colSubtotalPosition,
|
||||
colTotals,
|
||||
@ -63,54 +116,51 @@ export default function PivotTableChart(props: PivotTableProps) {
|
||||
setDataMask,
|
||||
selectedFilters,
|
||||
verboseMap,
|
||||
columnFormats,
|
||||
metricsLayout,
|
||||
metricColorFormatters,
|
||||
dateFormatters,
|
||||
} = props;
|
||||
|
||||
const adaptiveFormatter = getNumberFormatter(valueFormat);
|
||||
const defaultFormatter = getNumberFormatter(valueFormat);
|
||||
const columnFormatsArray = Object.entries(columnFormats);
|
||||
const hasCustomMetricFormatters = columnFormatsArray.length > 0;
|
||||
const metricFormatters =
|
||||
hasCustomMetricFormatters &&
|
||||
Object.fromEntries(
|
||||
columnFormatsArray.map(([metric, format]) => [metric, getNumberFormatter(format)]),
|
||||
);
|
||||
|
||||
const aggregators = (tpl => ({
|
||||
Count: tpl.count(adaptiveFormatter),
|
||||
'Count Unique Values': tpl.countUnique(adaptiveFormatter),
|
||||
'List Unique Values': tpl.listUnique(', '),
|
||||
Sum: tpl.sum(adaptiveFormatter),
|
||||
Average: tpl.average(adaptiveFormatter),
|
||||
Median: tpl.median(adaptiveFormatter),
|
||||
'Sample Variance': tpl.var(1, adaptiveFormatter),
|
||||
'Sample Standard Deviation': tpl.stdev(1, adaptiveFormatter),
|
||||
Minimum: tpl.min(adaptiveFormatter),
|
||||
Maximum: tpl.max(adaptiveFormatter),
|
||||
First: tpl.first(adaptiveFormatter),
|
||||
Last: tpl.last(adaptiveFormatter),
|
||||
'Sum as Fraction of Total': tpl.fractionOf(tpl.sum(), 'total', adaptiveFormatter),
|
||||
'Sum as Fraction of Rows': tpl.fractionOf(tpl.sum(), 'row', adaptiveFormatter),
|
||||
'Sum as Fraction of Columns': tpl.fractionOf(tpl.sum(), 'col', adaptiveFormatter),
|
||||
'Count as Fraction of Total': tpl.fractionOf(tpl.count(), 'total', adaptiveFormatter),
|
||||
'Count as Fraction of Rows': tpl.fractionOf(tpl.count(), 'row', adaptiveFormatter),
|
||||
'Count as Fraction of Columns': tpl.fractionOf(tpl.count(), 'col', adaptiveFormatter),
|
||||
}))(aggregatorTemplates);
|
||||
|
||||
const metricNames = metrics.map((metric: string | AdhocMetric) =>
|
||||
typeof metric === 'string' ? metric : (metric.label as string),
|
||||
const metricNames = useMemo(
|
||||
() =>
|
||||
metrics.map((metric: string | AdhocMetric) =>
|
||||
typeof metric === 'string' ? metric : (metric.label as string),
|
||||
),
|
||||
[metrics],
|
||||
);
|
||||
|
||||
const unpivotedData = data.reduce(
|
||||
(acc: Record<string, any>[], record: Record<string, any>) => [
|
||||
...acc,
|
||||
...metricNames.map((name: string) => ({
|
||||
...record,
|
||||
[METRIC_KEY]: name,
|
||||
value: record[name],
|
||||
})),
|
||||
],
|
||||
[],
|
||||
const unpivotedData = useMemo(
|
||||
() =>
|
||||
data.reduce(
|
||||
(acc: Record<string, any>[], record: Record<string, any>) => [
|
||||
...acc,
|
||||
...metricNames.map((name: string) => ({
|
||||
...record,
|
||||
[METRIC_KEY]: name,
|
||||
value: record[name],
|
||||
})),
|
||||
],
|
||||
[],
|
||||
),
|
||||
[data, metricNames],
|
||||
);
|
||||
|
||||
let [rows, cols] = transposePivot ? [groupbyColumns, groupbyRows] : [groupbyRows, groupbyColumns];
|
||||
|
||||
if (metricsLayout === MetricsLayoutEnum.ROWS) {
|
||||
rows = [METRIC_KEY, ...rows];
|
||||
rows = combineMetric ? [...rows, METRIC_KEY] : [METRIC_KEY, ...rows];
|
||||
} else {
|
||||
cols = [METRIC_KEY, ...cols];
|
||||
cols = combineMetric ? [...cols, METRIC_KEY] : [METRIC_KEY, ...cols];
|
||||
}
|
||||
|
||||
const handleChange = useCallback(
|
||||
@ -144,11 +194,6 @@ export default function PivotTableChart(props: PivotTableProps) {
|
||||
[setDataMask],
|
||||
);
|
||||
|
||||
const isActiveFilterValue = useCallback(
|
||||
(key: string, val: DataRecordValue) => !!selectedFilters && selectedFilters[key]?.includes(val),
|
||||
[selectedFilters],
|
||||
);
|
||||
|
||||
const toggleFilter = useCallback(
|
||||
(
|
||||
e: MouseEvent,
|
||||
@ -162,6 +207,9 @@ export default function PivotTableChart(props: PivotTableProps) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isActiveFilterValue = (key: string, val: DataRecordValue) =>
|
||||
!!selectedFilters && selectedFilters[key]?.includes(val);
|
||||
|
||||
const filtersCopy = { ...filters };
|
||||
delete filtersCopy[METRIC_KEY];
|
||||
|
||||
@ -201,10 +249,14 @@ export default function PivotTableChart(props: PivotTableProps) {
|
||||
data={unpivotedData}
|
||||
rows={rows}
|
||||
cols={cols}
|
||||
aggregators={aggregators}
|
||||
aggregatorsFactory={aggregatorsFactory}
|
||||
defaultFormatter={defaultFormatter}
|
||||
customFormatters={
|
||||
hasCustomMetricFormatters ? { [METRIC_KEY]: metricFormatters } : undefined
|
||||
}
|
||||
aggregatorName={aggregateFunction}
|
||||
vals={['value']}
|
||||
rendererName={tableRenderer}
|
||||
rendererName="Table With Subtotal"
|
||||
colOrder={colOrder}
|
||||
rowOrder={rowOrder}
|
||||
sorters={{
|
||||
@ -218,10 +270,14 @@ export default function PivotTableChart(props: PivotTableProps) {
|
||||
highlightHeaderCellsOnHover: emitFilter,
|
||||
highlightedHeaderCells: selectedFilters,
|
||||
omittedHighlightHeaderGroups: [METRIC_KEY],
|
||||
cellColorFormatters: { [METRIC_KEY]: metricColorFormatters },
|
||||
dateFormatters,
|
||||
}}
|
||||
subtotalOptions={{
|
||||
colSubtotalDisplay: { displayOnTop: colSubtotalPosition },
|
||||
rowSubtotalDisplay: { displayOnTop: rowSubtotalPosition },
|
||||
arrowCollapsed: <PlusSquareOutlined style={iconStyle} />,
|
||||
arrowExpanded: <MinusSquareOutlined style={iconStyle} />,
|
||||
}}
|
||||
namesMapping={verboseMap}
|
||||
/>
|
||||
|
@ -16,9 +16,17 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { FeatureFlag, isFeatureEnabled, t, validateNonEmpty } from '@superset-ui/core';
|
||||
import {
|
||||
FeatureFlag,
|
||||
isFeatureEnabled,
|
||||
QueryFormMetric,
|
||||
smartDateFormatter,
|
||||
t,
|
||||
validateNonEmpty,
|
||||
} from '@superset-ui/core';
|
||||
import {
|
||||
ControlPanelConfig,
|
||||
D3_TIME_FORMAT_OPTIONS,
|
||||
formatSelectOptions,
|
||||
sections,
|
||||
sharedControls,
|
||||
@ -140,6 +148,21 @@ const config: ControlPanelConfig = {
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'combineMetric',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Combine metrics'),
|
||||
default: false,
|
||||
description: t(
|
||||
'Display metrics side by side within each column, as ' +
|
||||
'opposed to each column being displayed side by side for each metric.',
|
||||
),
|
||||
renderTrigger: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -154,23 +177,16 @@ const config: ControlPanelConfig = {
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'tableRenderer',
|
||||
name: 'date_format',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
label: t('Pivot table type'),
|
||||
default: 'Table With Subtotal',
|
||||
choices: [
|
||||
// [value, label]
|
||||
['Table With Subtotal', t('Table')],
|
||||
['Table With Subtotal Heatmap', t('Table Heatmap')],
|
||||
['Table With Subtotal Col Heatmap', t('Table Col Heatmap')],
|
||||
['Table With Subtotal Row Heatmap', t('Table Row Heatmap')],
|
||||
['Table With Subtotal Barchart', t('Table Barchart')],
|
||||
['Table With Subtotal Col Barchart', t('Table Col Barchart')],
|
||||
['Table With Subtotal Row Barchart', t('Table Row Barchart')],
|
||||
],
|
||||
freeForm: true,
|
||||
label: t('Date format'),
|
||||
default: smartDateFormatter.id,
|
||||
renderTrigger: true,
|
||||
description: t('The type of pivot table visualization'),
|
||||
clearable: false,
|
||||
choices: D3_TIME_FORMAT_OPTIONS,
|
||||
description: t('D3 time format for datetime columns'),
|
||||
},
|
||||
},
|
||||
],
|
||||
@ -280,6 +296,31 @@ const config: ControlPanelConfig = {
|
||||
},
|
||||
]
|
||||
: [],
|
||||
[
|
||||
{
|
||||
name: 'conditional_formatting',
|
||||
config: {
|
||||
type: 'ConditionalFormattingControl',
|
||||
renderTrigger: true,
|
||||
label: t('Customize metrics'),
|
||||
description: t('Apply conditional color formatting to metrics'),
|
||||
mapStateToProps(explore) {
|
||||
const values = (explore?.controls?.metrics?.value as QueryFormMetric[]) ?? [];
|
||||
const verboseMap = explore?.datasource?.verbose_map ?? {};
|
||||
const metricColumn = values.map(value => {
|
||||
if (typeof value === 'string') {
|
||||
return { value, label: verboseMap[value] ?? value };
|
||||
}
|
||||
return { value: value.label, label: value.label };
|
||||
});
|
||||
return {
|
||||
columnOptions: metricColumn,
|
||||
verboseMap,
|
||||
};
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
],
|
||||
|
@ -16,14 +16,24 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { t, ChartMetadata, ChartPlugin, Behavior } from '@superset-ui/core';
|
||||
import {
|
||||
t,
|
||||
ChartMetadata,
|
||||
ChartPlugin,
|
||||
Behavior,
|
||||
ChartProps,
|
||||
QueryFormData,
|
||||
} from '@superset-ui/core';
|
||||
import buildQuery from './buildQuery';
|
||||
import controlPanel from './controlPanel';
|
||||
import transformProps from './transformProps';
|
||||
import thumbnail from '../images/thumbnail.png';
|
||||
import { PivotTableQueryFormData } from '../types';
|
||||
|
||||
export default class PivotTableChartPlugin extends ChartPlugin<PivotTableQueryFormData> {
|
||||
export default class PivotTableChartPlugin extends ChartPlugin<
|
||||
PivotTableQueryFormData,
|
||||
ChartProps<QueryFormData>
|
||||
> {
|
||||
/**
|
||||
* The constructor is used to pass relevant metadata and callbacks that get
|
||||
* registered in respective registries that are used throughout the library
|
||||
|
@ -16,9 +16,30 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { ChartProps, DataRecord } from '@superset-ui/core';
|
||||
import {
|
||||
ChartProps,
|
||||
DataRecord,
|
||||
extractTimegrain,
|
||||
GenericDataType,
|
||||
getTimeFormatter,
|
||||
getTimeFormatterForGranularity,
|
||||
QueryFormData,
|
||||
smartDateFormatter,
|
||||
TimeFormats,
|
||||
} from '@superset-ui/core';
|
||||
import { getColorFormatters } from '@superset-ui/chart-controls';
|
||||
import { DateFormatter } from '../types';
|
||||
|
||||
export default function transformProps(chartProps: ChartProps) {
|
||||
const { DATABASE_DATETIME } = TimeFormats;
|
||||
const TIME_COLUMN = '__timestamp';
|
||||
|
||||
function isNumeric(key: string, data: DataRecord[] = []) {
|
||||
return data.every(
|
||||
record => record[key] === null || record[key] === undefined || typeof record[key] === 'number',
|
||||
);
|
||||
}
|
||||
|
||||
export default function transformProps(chartProps: ChartProps<QueryFormData>) {
|
||||
/**
|
||||
* This function is called after a successful response has been
|
||||
* received from the chart data endpoint, and is used to transform
|
||||
@ -53,11 +74,12 @@ export default function transformProps(chartProps: ChartProps) {
|
||||
height,
|
||||
queriesData,
|
||||
formData,
|
||||
rawFormData,
|
||||
hooks: { setDataMask = () => {} },
|
||||
filterState,
|
||||
datasource: { verboseMap = {} },
|
||||
datasource: { verboseMap = {}, columnFormats = {} },
|
||||
} = chartProps;
|
||||
const data = queriesData[0].data as DataRecord[];
|
||||
const { data, colnames, coltypes } = queriesData[0];
|
||||
const {
|
||||
groupbyRows,
|
||||
groupbyColumns,
|
||||
@ -67,15 +89,43 @@ export default function transformProps(chartProps: ChartProps) {
|
||||
rowOrder,
|
||||
aggregateFunction,
|
||||
transposePivot,
|
||||
combineMetric,
|
||||
rowSubtotalPosition,
|
||||
colSubtotalPosition,
|
||||
colTotals,
|
||||
rowTotals,
|
||||
valueFormat,
|
||||
dateFormat,
|
||||
emitFilter,
|
||||
metricsLayout,
|
||||
conditionalFormatting,
|
||||
} = formData;
|
||||
const { selectedFilters } = filterState;
|
||||
const granularity = extractTimegrain(rawFormData);
|
||||
|
||||
const dateFormatters = colnames
|
||||
.filter((colname: string, index: number) => coltypes[index] === GenericDataType.TEMPORAL)
|
||||
.reduce((acc: Record<string, DateFormatter | undefined>, temporalColname: string) => {
|
||||
let formatter: DateFormatter | undefined;
|
||||
if (dateFormat === smartDateFormatter.id) {
|
||||
if (temporalColname === TIME_COLUMN) {
|
||||
// time column use formats based on granularity
|
||||
formatter = getTimeFormatterForGranularity(granularity);
|
||||
} else if (isNumeric(temporalColname, data)) {
|
||||
formatter = getTimeFormatter(DATABASE_DATETIME);
|
||||
} else {
|
||||
// if no column-specific format, print cell as is
|
||||
formatter = String;
|
||||
}
|
||||
} else if (dateFormat) {
|
||||
formatter = getTimeFormatter(dateFormat);
|
||||
}
|
||||
if (formatter) {
|
||||
acc[temporalColname] = formatter;
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
const metricColorFormatters = getColorFormatters(conditionalFormatting, data);
|
||||
|
||||
return {
|
||||
width,
|
||||
@ -89,6 +139,7 @@ export default function transformProps(chartProps: ChartProps) {
|
||||
rowOrder,
|
||||
aggregateFunction,
|
||||
transposePivot,
|
||||
combineMetric,
|
||||
rowSubtotalPosition,
|
||||
colSubtotalPosition,
|
||||
colTotals,
|
||||
@ -98,6 +149,9 @@ export default function transformProps(chartProps: ChartProps) {
|
||||
setDataMask,
|
||||
selectedFilters,
|
||||
verboseMap,
|
||||
columnFormats,
|
||||
metricsLayout,
|
||||
metricColorFormatters,
|
||||
dateFormatters,
|
||||
};
|
||||
}
|
||||
|
@ -23,7 +23,10 @@ import {
|
||||
SetDataMaskHook,
|
||||
DataRecordValue,
|
||||
JsonObject,
|
||||
TimeFormatter,
|
||||
NumberFormatter,
|
||||
} from '@superset-ui/core';
|
||||
import { ColorFormatters } from '@superset-ui/chart-controls';
|
||||
|
||||
export interface PivotTableStylesProps {
|
||||
height: number;
|
||||
@ -33,6 +36,7 @@ export interface PivotTableStylesProps {
|
||||
export type FilterType = Record<string, DataRecordValue>;
|
||||
export type SelectedFiltersType = Record<string, DataRecordValue[]>;
|
||||
|
||||
export type DateFormatter = TimeFormatter | NumberFormatter | ((value: DataRecordValue) => string);
|
||||
export enum MetricsLayoutEnum {
|
||||
ROWS = 'ROWS',
|
||||
COLUMNS = 'COLUMNS',
|
||||
@ -47,6 +51,7 @@ interface PivotTableCustomizeProps {
|
||||
rowOrder: string;
|
||||
aggregateFunction: string;
|
||||
transposePivot: boolean;
|
||||
combineMetric: boolean;
|
||||
rowSubtotalPosition: boolean;
|
||||
colSubtotalPosition: boolean;
|
||||
colTotals: boolean;
|
||||
@ -55,8 +60,11 @@ interface PivotTableCustomizeProps {
|
||||
setDataMask: SetDataMaskHook;
|
||||
emitFilter?: boolean;
|
||||
selectedFilters?: SelectedFiltersType;
|
||||
verboseMap?: JsonObject;
|
||||
verboseMap: JsonObject;
|
||||
columnFormats: JsonObject;
|
||||
metricsLayout?: MetricsLayoutEnum;
|
||||
metricColorFormatters: ColorFormatters;
|
||||
dateFormatters: Record<string, DateFormatter | undefined>;
|
||||
}
|
||||
|
||||
export type PivotTableQueryFormData = QueryFormData &
|
||||
|
@ -19,6 +19,12 @@ describe('PivotTableChart buildQuery', () => {
|
||||
viz_type: 'my_chart',
|
||||
width: 800,
|
||||
height: 600,
|
||||
combineMetric: false,
|
||||
verboseMap: {},
|
||||
columnFormats: {},
|
||||
metricColorFormatters: [],
|
||||
dateFormatters: {},
|
||||
setDataMask: () => {},
|
||||
};
|
||||
|
||||
it('should build groupby with series in form data', () => {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { ChartProps } from '@superset-ui/core';
|
||||
import { ChartProps, QueryFormData } from '@superset-ui/core';
|
||||
import transformProps from '../../src/plugin/transformProps';
|
||||
import { MetricsLayoutEnum } from '../../src/types';
|
||||
|
||||
@ -13,6 +13,7 @@ describe('PivotTableChart transformProps', () => {
|
||||
rowOrder: 'key_a_to_z',
|
||||
aggregateFunction: 'Sum',
|
||||
transposePivot: true,
|
||||
combineMetric: true,
|
||||
rowSubtotalPosition: true,
|
||||
colSubtotalPosition: true,
|
||||
colTotals: true,
|
||||
@ -20,19 +21,25 @@ describe('PivotTableChart transformProps', () => {
|
||||
valueFormat: 'SMART_NUMBER',
|
||||
emitFilter: false,
|
||||
metricsLayout: MetricsLayoutEnum.COLUMNS,
|
||||
viz_type: '',
|
||||
datasource: '',
|
||||
conditionalFormatting: [],
|
||||
dateFormat: '',
|
||||
};
|
||||
const chartProps = new ChartProps({
|
||||
const chartProps = new ChartProps<QueryFormData>({
|
||||
formData,
|
||||
width: 800,
|
||||
height: 600,
|
||||
queriesData: [
|
||||
{
|
||||
data: [{ name: 'Hulk', sum__num: 1, __timestamp: 599616000000 }],
|
||||
colnames: ['name', 'sum__num', '__timestamp'],
|
||||
coltypes: [1, 0, 2],
|
||||
},
|
||||
],
|
||||
hooks: { setDataMask },
|
||||
filterState: { selectedFilters: {} },
|
||||
datasource: { verboseMap: {} },
|
||||
datasource: { verboseMap: {}, columnFormats: {} },
|
||||
});
|
||||
|
||||
it('should transform chart props for viz', () => {
|
||||
@ -47,6 +54,7 @@ describe('PivotTableChart transformProps', () => {
|
||||
rowOrder: 'key_a_to_z',
|
||||
aggregateFunction: 'Sum',
|
||||
transposePivot: true,
|
||||
combineMetric: true,
|
||||
rowSubtotalPosition: true,
|
||||
colSubtotalPosition: true,
|
||||
colTotals: true,
|
||||
@ -58,6 +66,9 @@ describe('PivotTableChart transformProps', () => {
|
||||
selectedFilters: {},
|
||||
verboseMap: {},
|
||||
metricsLayout: MetricsLayoutEnum.COLUMNS,
|
||||
metricColorFormatters: [],
|
||||
dateFormatters: {},
|
||||
columnFormats: {},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -4396,10 +4396,10 @@
|
||||
d3-cloud "^1.2.1"
|
||||
prop-types "^15.6.2"
|
||||
|
||||
"@superset-ui/react-pivottable@^0.12.8":
|
||||
version "0.12.8"
|
||||
resolved "https://registry.yarnpkg.com/@superset-ui/react-pivottable/-/react-pivottable-0.12.8.tgz#3b7d6cd32719d6510b88b2334afacbb1735afee5"
|
||||
integrity sha512-7DRxX/w1uSQE1pibSe64t1o+fmiP7ZWT2FJkjK510bSJm8NUIPCXtmpK+NKtNZuCteE9sqE7bQxd54SSq2xWKw==
|
||||
"@superset-ui/react-pivottable@^0.12.9":
|
||||
version "0.12.9"
|
||||
resolved "https://registry.yarnpkg.com/@superset-ui/react-pivottable/-/react-pivottable-0.12.9.tgz#f46ceef940c2f99c197db4acd7487efc48be05bf"
|
||||
integrity sha512-wztcGEGg4Fc/zxHTDSP2ANyE9aZYiDPHf01FSx6rBapxtXP04NvyLPJl3RKNN/D5l98o5bj5xO/5KSailM9LjQ==
|
||||
dependencies:
|
||||
immutability-helper "^3.1.1"
|
||||
prop-types "^15.7.2"
|
||||
|
Loading…
Reference in New Issue
Block a user