diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/BoxPlot/constants.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/BoxPlot/constants.ts new file mode 100644 index 000000000000..7e3c8404c60c --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/BoxPlot/constants.ts @@ -0,0 +1,2 @@ +export const BOX_PLOT_PLUGIN_TYPE = 'v2-box-plot'; +export const BOX_PLOT_PLUGIN_LEGACY_TYPE = 'v2-box-plot/legacy'; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/BoxPlot/data.js b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/BoxPlot/data.js new file mode 100644 index 000000000000..fc1ee8f5d7d1 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/BoxPlot/data.js @@ -0,0 +1,80 @@ +/* eslint-disable sort-keys, no-magic-numbers */ +export default [ + { + label: 'East Asia & Pacific', + values: { + Q1: 1384725172.5, + Q2: 1717904169.0, + Q3: 2032724922.5, + whisker_high: 2240687901.0, + whisker_low: 1031863394.0, + outliers: [], + }, + }, + { + label: 'Europe & Central Asia', + values: { + Q1: 751386460.5, + Q2: 820716895.0, + Q3: 862814192.5, + whisker_high: 903095786.0, + whisker_low: 660881033.0, + outliers: [], + }, + }, + { + label: 'Latin America & Caribbean', + values: { + Q1: 313690832.5, + Q2: 421490233.0, + Q3: 529668114.5, + whisker_high: 626270167.0, + whisker_low: 220564224.0, + outliers: [], + }, + }, + { + label: 'Middle East & North Africa', + values: { + Q1: 152382756.5, + Q2: 232066828.0, + Q3: 318191071.5, + whisker_high: 417451428.0, + whisker_low: 105512645.0, + outliers: [], + }, + }, + { + label: 'North America', + values: { + Q1: 235506847.5, + Q2: 268896849.0, + Q3: 314553651.5, + whisker_high: 354462656.0, + whisker_low: 198624409.0, + outliers: [], + }, + }, + { + label: 'South Asia', + values: { + Q1: 772373036.5, + Q2: 1059570231.0, + Q3: 1398841234.0, + whisker_high: 1720976995.0, + whisker_low: 572036107.0, + outliers: [], + }, + }, + { + label: 'Sub-Saharan Africa', + values: { + Q1: 320037758.0, + Q2: 467337821.0, + Q3: 676768689.0, + whisker_high: 974315323.0, + whisker_low: 228268752.0, + outliers: [], + }, + }, +]; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/BoxPlot/index.js b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/BoxPlot/index.js new file mode 100644 index 000000000000..5ce989c0fab5 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/BoxPlot/index.js @@ -0,0 +1,12 @@ +import { BoxPlotChartPlugin as LegacyBoxPlotChartPlugin } from '../../../../../superset-ui-preset-chart-xy/src/legacy'; +import { BoxPlotChartPlugin } from '../../../../../superset-ui-preset-chart-xy/src'; +import Stories from './stories/Basic'; +import LegacyStories from './stories/Legacy'; +import { BOX_PLOT_PLUGIN_LEGACY_TYPE, BOX_PLOT_PLUGIN_TYPE } from './constants'; + +new LegacyBoxPlotChartPlugin().configure({ key: BOX_PLOT_PLUGIN_LEGACY_TYPE }).register(); +new BoxPlotChartPlugin().configure({ key: BOX_PLOT_PLUGIN_TYPE }).register(); + +export default { + examples: [...Stories, ...LegacyStories], +}; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/BoxPlot/stories/Basic.tsx b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/BoxPlot/stories/Basic.tsx new file mode 100644 index 000000000000..55776a9374ab --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/BoxPlot/stories/Basic.tsx @@ -0,0 +1,109 @@ +/* eslint-disable no-magic-numbers, sort-keys */ +import React from 'react'; +import { SuperChart, ChartProps } from '@superset-ui/chart'; +import data from '../data'; + +export default [ + { + renderStory: () => ( + + ), + storyName: 'Basic', + storyPath: 'preset-chart-xy|BoxPlotChartPlugin', + }, + { + renderStory: () => ( + + ), + storyName: 'Horizontal', + storyPath: 'preset-chart-xy|BoxPlotChartPlugin', + }, +]; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/BoxPlot/stories/Legacy.tsx b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/BoxPlot/stories/Legacy.tsx new file mode 100644 index 000000000000..59fbb55c33c9 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/BoxPlot/stories/Legacy.tsx @@ -0,0 +1,31 @@ +/* eslint-disable no-magic-numbers, sort-keys */ +import React from 'react'; +import { SuperChart, ChartProps } from '@superset-ui/chart'; +import data from '../data'; + +export default [ + { + renderStory: () => ( + + ), + storyName: 'Use Legacy API shim', + storyPath: 'preset-chart-xy|BoxPlotChartPlugin', + }, +]; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/BoxPlot/BoxPlot.tsx b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/BoxPlot/BoxPlot.tsx new file mode 100644 index 000000000000..a46036e4a21a --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/BoxPlot/BoxPlot.tsx @@ -0,0 +1,148 @@ +/** + * 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. + */ +/* eslint-disable sort-keys, no-magic-numbers, complexity */ +import React from 'react'; +import { BoxPlotSeries, XYChart } from '@data-ui/xy-chart'; +import { chartTheme, ChartTheme } from '@data-ui/theme'; +import { Margin, Dimension } from '@superset-ui/dimension'; +import { createSelector } from 'reselect'; +import createTooltip from './createTooltip'; +import XYChartLayout from '../utils/XYChartLayout'; +import WithLegend from '../components/WithLegend'; +import ChartLegend from '../components/ChartLegend'; +import Encoder, { ChannelTypes, Encoding, Outputs } from './Encoder'; +import { Dataset, PlainObject } from '../encodeable/types/Data'; + +chartTheme.gridStyles.stroke = '#f1f3f5'; + +const DEFAULT_MARGIN = { top: 20, right: 20, left: 20, bottom: 20 }; + +const defaultProps = { + className: '', + margin: DEFAULT_MARGIN, + theme: chartTheme, +} as const; + +type Props = { + className?: string; + width: string | number; + height: string | number; + margin?: Margin; + encoding: Encoding; + data: Dataset; + theme?: ChartTheme; +} & Readonly; + +export default class BoxPlot extends React.PureComponent { + static defaultProps = defaultProps; + + constructor(props: Props) { + super(props); + + const createEncoder = createSelector( + (enc: Encoding) => enc, + (enc: Encoding) => new Encoder({ encoding: enc }), + ); + + this.createEncoder = () => { + this.encoder = createEncoder(this.props.encoding); + }; + + this.encoder = createEncoder(this.props.encoding); + this.renderChart = this.renderChart.bind(this); + } + + encoder: Encoder; + private createEncoder: () => void; + + renderChart(dim: Dimension) { + const { width, height } = dim; + const { data, encoding, margin, theme } = this.props; + const { channels } = this.encoder; + + const isHorizontal = encoding.y.type === 'nominal'; + + const children = [ + ({ ...row, y: channels.y.get(row) })) + : data.map(row => ({ ...row, x: channels.x.get(row) })) + } + fill={(datum: PlainObject) => channels.color.encode(datum, '#55acee')} + fillOpacity={0.4} + stroke={(datum: PlainObject) => channels.color.encode(datum)} + strokeWidth={1} + widthRatio={0.6} + horizontal={encoding.y.type === 'nominal'} + />, + ]; + + const layout = new XYChartLayout({ + width, + height, + margin: { ...DEFAULT_MARGIN, ...margin }, + theme, + xEncoder: channels.x, + yEncoder: channels.y, + children, + }); + + return layout.renderChartWithFrame((chartDim: Dimension) => ( + + {children} + {layout.renderXAxis()} + {layout.renderYAxis()} + + )); + } + + render() { + const { className, data, width, height } = this.props; + + this.createEncoder(); + const renderLegend = this.encoder.hasLegend() + ? // eslint-disable-next-line react/jsx-props-no-multi-spaces + () => data={data} encoder={this.encoder} /> + : undefined; + + return ( + + ); + } +} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/BoxPlot/Encoder.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/BoxPlot/Encoder.ts new file mode 100644 index 000000000000..74c5342220a9 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/BoxPlot/Encoder.ts @@ -0,0 +1,42 @@ +import AbstractEncoder from '../encodeable/AbstractEncoder'; +import { PartialSpec } from '../encodeable/types/Specification'; +import { EncodingFromChannelsAndOutputs } from '../encodeable/types/Channel'; + +/** + * Define channel types + */ +const channelTypes = { + color: 'Color', + x: 'XBand', + y: 'YBand', +} as const; + +export type ChannelTypes = typeof channelTypes; + +/** + * Define output type for each channel + */ +export interface Outputs { + x: number | null; + y: number | null; + color: string; +} + +/** + * Derive encoding config + */ +export type Encoding = EncodingFromChannelsAndOutputs; + +export default class Encoder extends AbstractEncoder { + static readonly DEFAULT_ENCODINGS: Encoding = { + color: { value: '#222' }, + x: { field: 'x', type: 'nominal' }, + y: { field: 'y', type: 'quantitative' }, + }; + + static readonly CHANNEL_OPTIONS = {}; + + constructor(spec: PartialSpec) { + super(channelTypes, spec, Encoder.DEFAULT_ENCODINGS, Encoder.CHANNEL_OPTIONS); + } +} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/BoxPlot/createMetadata.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/BoxPlot/createMetadata.ts new file mode 100644 index 000000000000..52fd28fc88d7 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/BoxPlot/createMetadata.ts @@ -0,0 +1,12 @@ +import { t } from '@superset-ui/translation'; +import { ChartMetadata } from '@superset-ui/chart'; +import thumbnail from './images/thumbnail.png'; + +export default function createMetadata(useLegacyApi = false) { + return new ChartMetadata({ + description: '', + name: t('Box Plot'), + thumbnail, + useLegacyApi, + }); +} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/BoxPlot/createTooltip.tsx b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/BoxPlot/createTooltip.tsx new file mode 100644 index 000000000000..b3efd5070f7f --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/BoxPlot/createTooltip.tsx @@ -0,0 +1,44 @@ +import { isDefined } from '@superset-ui/core'; +import React from 'react'; +import TooltipFrame from '../components/tooltip/TooltipFrame'; +import TooltipTable from '../components/tooltip/TooltipTable'; +import Encoder from './Encoder'; +import { BoxPlotDataRow } from './types'; + +export default function createBoxPlotTooltip(encoder: Encoder) { + const { channels } = encoder; + + return function BoxPlotTooltip({ datum, color }: { datum: BoxPlotDataRow; color: string }) { + const { label, min, max, median, firstQuartile, thirdQuartile, outliers } = datum; + + const data = []; + if (isDefined(min)) { + data.push({ key: 'Min', valueColumn: channels.y.formatValue(min) }); + } + if (isDefined(max)) { + data.push({ key: 'Max', valueColumn: channels.y.formatValue(max) }); + } + if (isDefined(median)) { + data.push({ key: 'Median', valueColumn: channels.y.formatValue(median) }); + } + if (isDefined(firstQuartile)) { + data.push({ key: '1st Quartile', valueColumn: channels.y.formatValue(firstQuartile) }); + } + if (isDefined(thirdQuartile)) { + data.push({ key: '3rd Quartile', valueColumn: channels.y.formatValue(thirdQuartile) }); + } + if (isDefined(outliers) && outliers.length > 0) { + data.push({ key: '# Outliers', valueColumn: outliers.length }); + } + + return ( + +
+ {label} +
+ {data.length > 0 &&
} + +
+ ); + }; +} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/BoxPlot/images/thumbnail.png b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/BoxPlot/images/thumbnail.png new file mode 100644 index 000000000000..c6f8fdcfd616 Binary files /dev/null and b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/BoxPlot/images/thumbnail.png differ diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/BoxPlot/images/thumbnailLarge.png b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/BoxPlot/images/thumbnailLarge.png new file mode 100644 index 000000000000..f7bbe62407fe Binary files /dev/null and b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/BoxPlot/images/thumbnailLarge.png differ diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/BoxPlot/index.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/BoxPlot/index.ts new file mode 100644 index 000000000000..ee02dbbbe428 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/BoxPlot/index.ts @@ -0,0 +1,31 @@ +/** + * 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 { ChartPlugin } from '@superset-ui/chart'; +import createMetadata from './createMetadata'; +import transformProps from './transformProps'; + +export default class BoxPlotChartPlugin extends ChartPlugin { + constructor() { + super({ + loadChart: () => import('./BoxPlot'), + metadata: createMetadata(), + transformProps, + }); + } +} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/BoxPlot/legacy/index.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/BoxPlot/legacy/index.ts new file mode 100644 index 000000000000..6667d5ccff7d --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/BoxPlot/legacy/index.ts @@ -0,0 +1,31 @@ +/** + * 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 { ChartPlugin } from '@superset-ui/chart'; +import createMetadata from '../createMetadata'; +import transformProps from './transformProps'; + +export default class BoxPlotChartPlugin extends ChartPlugin { + constructor() { + super({ + loadChart: () => import('../BoxPlot'), + metadata: createMetadata(true), + transformProps, + }); + } +} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/BoxPlot/legacy/transformProps.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/BoxPlot/legacy/transformProps.ts new file mode 100644 index 000000000000..183230c94639 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/BoxPlot/legacy/transformProps.ts @@ -0,0 +1,94 @@ +/** + * 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. + */ +/* eslint-disable sort-keys, no-magic-numbers */ +import { ChartProps } from '@superset-ui/chart'; +import { RawBoxPlotDataRow, BoxPlotDataRow } from '../types'; + +export default function transformProps(chartProps: ChartProps) { + const { width, height, datasource = {}, formData, payload } = chartProps; + const { verboseMap = {} } = datasource; + const { colorScheme, groupby, metrics } = formData; + + const data = (payload.data as RawBoxPlotDataRow[]).map(({ label, values }) => ({ + label, + min: values.whisker_low, + max: values.whisker_high, + firstQuartile: values.Q1, + median: values.Q2, + thirdQuartile: values.Q3, + outliers: values.outliers, + })); + + const xAxisLabel = groupby.join('/'); + const yAxisLabel = metrics.length > 0 ? verboseMap[metrics[0]] || metrics[0] : ''; + + const boxPlotValues = data.reduce((r: number[], e: BoxPlotDataRow) => { + r.push(e.min, e.max, ...e.outliers); + + return r; + }, []); + + const minBoxPlotValue = Math.min(...boxPlotValues); + const maxBoxPlotValue = Math.max(...boxPlotValues); + const valueDomain = [ + minBoxPlotValue - 0.1 * Math.abs(minBoxPlotValue), + maxBoxPlotValue + 0.1 * Math.abs(maxBoxPlotValue), + ]; + + return { + data, + width, + height, + encoding: { + x: { + field: 'label', + type: 'nominal', + scale: { + type: 'band', + paddingInner: 0.15, + paddingOuter: 0.3, + }, + axis: { + title: xAxisLabel, + }, + }, + y: { + field: 'value', + type: 'quantitative', + scale: { + type: 'linear', + domain: valueDomain, + }, + axis: { + title: yAxisLabel, + numTicks: 5, + format: 'SMART_NUMBER', + }, + }, + color: { + field: 'label', + type: 'nominal', + scale: { + scheme: colorScheme, + }, + legend: false, + }, + }, + }; +} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/BoxPlot/transformProps.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/BoxPlot/transformProps.ts new file mode 100644 index 000000000000..959ca29b61db --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/BoxPlot/transformProps.ts @@ -0,0 +1,68 @@ +import { ChartProps } from '@superset-ui/chart'; +import { BoxPlotDataRow, RawBoxPlotDataRow } from './types'; + +/** + * 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. + */ + +/* eslint-disable sort-keys, no-magic-numbers */ + +export default function transformProps(chartProps: ChartProps) { + const { width, height, formData, payload } = chartProps; + const { encoding, margin, theme } = formData; + + const data = (payload.data as RawBoxPlotDataRow[]).map(({ label, values }) => ({ + label, + min: values.whisker_low, + max: values.whisker_high, + firstQuartile: values.Q1, + median: values.Q2, + thirdQuartile: values.Q3, + outliers: values.outliers, + })); + + const isHorizontal = encoding.y.type === 'nominal'; + + const boxPlotValues = data.reduce((r: number[], e: BoxPlotDataRow) => { + r.push(e.min, e.max, ...e.outliers); + + return r; + }, []); + + const minBoxPlotValue = Math.min(...boxPlotValues); + const maxBoxPlotValue = Math.max(...boxPlotValues); + const valueDomain = [ + minBoxPlotValue - 0.1 * Math.abs(minBoxPlotValue), + maxBoxPlotValue + 0.1 * Math.abs(maxBoxPlotValue), + ]; + + if (isHorizontal) { + encoding.x.scale.domain = valueDomain; + } else { + encoding.y.scale.domain = valueDomain; + } + + return { + data, + width, + height, + margin, + theme, + encoding, + }; +} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/BoxPlot/types.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/BoxPlot/types.ts new file mode 100644 index 000000000000..8b1839ee3c9c --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/BoxPlot/types.ts @@ -0,0 +1,23 @@ +export interface RawBoxPlotDataRow { + label: string; + values: { + Q1: number; + Q2: number; + Q3: number; + outliers: number[]; + // eslint-disable-next-line camelcase + whisker_high: number; + // eslint-disable-next-line camelcase + whisker_low: number; + }; +} + +export interface BoxPlotDataRow { + label: string; + min: number; + max: number; + firstQuartile: number; + median: number; + thirdQuartile: number; + outliers: number[]; +} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/createTooltip.tsx b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/createTooltip.tsx index d1de33e0d939..a373a021c9b5 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/createTooltip.tsx +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/createTooltip.tsx @@ -9,7 +9,7 @@ import Encoder from './Encoder'; const MARK_STYLE = { marginRight: 4 }; export default function createTooltip(encoder: Encoder, allSeries: Series[]) { - function LineTooltip({ + return function LineTooltip({ datum, series = {}, }: { @@ -57,7 +57,5 @@ export default function createTooltip(encoder: Encoder, allSeries: Series[]) { ); - } - - return LineTooltip; + }; } diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/components/tooltip/TooltipTable.tsx b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/components/tooltip/TooltipTable.tsx index b106a876b5cb..e0fc6c4d63c5 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/components/tooltip/TooltipTable.tsx +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/components/tooltip/TooltipTable.tsx @@ -4,7 +4,7 @@ type Props = { className?: string; data: { key: string | number; - keyColumn: ReactNode; + keyColumn?: ReactNode; keyStyle?: CSSProperties; valueColumn: ReactNode; valueStyle?: CSSProperties; @@ -27,7 +27,7 @@ export default class TooltipTable extends PureComponent { {data.map(({ key, keyColumn, keyStyle, valueColumn, valueStyle }, i) => ( - {keyColumn} + {keyColumn || key} {valueColumn} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/index.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/index.ts index ce786e69281a..498637196f70 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/index.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/index.ts @@ -1,2 +1,2 @@ -/* eslint-disable import/prefer-default-export */ +export { default as BoxPlotChartPlugin } from './BoxPlot'; export { default as LineChartPlugin } from './Line'; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/legacy.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/legacy.ts index 6a9010bda4c6..c0403f479c96 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/legacy.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/legacy.ts @@ -1,2 +1,2 @@ -/* eslint-disable import/prefer-default-export */ +export { default as BoxPlotChartPlugin } from './BoxPlot/legacy'; export { default as LineChartPlugin } from './Line/legacy'; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/types/@data-ui/xy-chart/index.d.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/types/@data-ui/xy-chart/index.d.ts index 3e48cb665a7e..504be9e51650 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/types/@data-ui/xy-chart/index.d.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/types/@data-ui/xy-chart/index.d.ts @@ -20,15 +20,16 @@ declare module '@data-ui/xy-chart' { onMouseMove?: (...args: any[]) => void; onMouseLeave?: (...args: any[]) => void; renderTooltip: any; - showYGrid: boolean; - snapTooltipToDataX: boolean; - theme: any; - tooltipData: any; + showYGrid?: boolean; + snapTooltipToDataX?: boolean; + theme?: any; + tooltipData?: any; xScale: any; yScale: any; } export class AreaSeries extends React.PureComponent {} + export class BoxPlotSeries extends React.PureComponent {} export class CrossHair extends React.PureComponent {} export class LinearGradient extends React.PureComponent {} export class LineSeries extends React.PureComponent {}