From cf07d9d5a81ef5a296ceaf82cee5892f4fd0062d Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Sun, 12 May 2019 00:52:03 -0700 Subject: [PATCH] feat: add scatter plot (#90) * feat: bubble in progress * feat: working scatter plot with basic and bubble demo * feat: add legacy shim * feat: support group as separate channel * fix: box plot formatting * fix: lint * fix: remove/clarify comments --- .../preset-chart-xy/ScatterPlot/constants.ts | 2 + .../preset-chart-xy/ScatterPlot/data/data.js | 178 +++++++++ .../ScatterPlot/data/legacyData.js | 338 ++++++++++++++++++ .../preset-chart-xy/ScatterPlot/index.js | 13 + .../ScatterPlot/stories/basic.jsx | 59 +++ .../ScatterPlot/stories/bubble.jsx | 67 ++++ .../ScatterPlot/stories/legacy.tsx | 49 +++ .../src/BoxPlot/BoxPlot.tsx | 39 +- .../src/BoxPlot/createTooltip.tsx | 13 +- .../src/Line/Line.tsx | 15 +- .../src/ScatterPlot/Encoder.ts | 48 +++ .../src/ScatterPlot/ScatterPlot.tsx | 150 ++++++++ .../src/ScatterPlot/createMetadata.ts | 12 + .../src/ScatterPlot/createTooltip.tsx | 57 +++ .../src/ScatterPlot/images/thumbnail.png | Bin 0 -> 31155 bytes .../src/ScatterPlot/images/thumbnailLarge.png | Bin 0 -> 31155 bytes .../src/ScatterPlot/index.ts | 13 + .../src/ScatterPlot/legacy/index.ts | 13 + .../src/ScatterPlot/legacy/transformProps.ts | 96 +++++ .../src/ScatterPlot/transformProps.ts | 18 + .../src/encodeable/AbstractEncoder.ts | 33 +- .../src/encodeable/AxisAgent.ts | 2 +- .../src/encodeable/types/Specification.ts | 10 + .../superset-ui-preset-chart-xy/src/index.ts | 1 + .../superset-ui-preset-chart-xy/src/legacy.ts | 1 + .../types/@data-ui/xy-chart/index.d.ts | 1 + 26 files changed, 1187 insertions(+), 41 deletions(-) create mode 100644 packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/ScatterPlot/constants.ts create mode 100644 packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/ScatterPlot/data/data.js create mode 100644 packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/ScatterPlot/data/legacyData.js create mode 100644 packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/ScatterPlot/index.js create mode 100644 packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/ScatterPlot/stories/basic.jsx create mode 100644 packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/ScatterPlot/stories/bubble.jsx create mode 100644 packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/ScatterPlot/stories/legacy.tsx create mode 100644 packages/superset-ui-preset-chart-xy/src/ScatterPlot/Encoder.ts create mode 100644 packages/superset-ui-preset-chart-xy/src/ScatterPlot/ScatterPlot.tsx create mode 100644 packages/superset-ui-preset-chart-xy/src/ScatterPlot/createMetadata.ts create mode 100644 packages/superset-ui-preset-chart-xy/src/ScatterPlot/createTooltip.tsx create mode 100644 packages/superset-ui-preset-chart-xy/src/ScatterPlot/images/thumbnail.png create mode 100644 packages/superset-ui-preset-chart-xy/src/ScatterPlot/images/thumbnailLarge.png create mode 100644 packages/superset-ui-preset-chart-xy/src/ScatterPlot/index.ts create mode 100644 packages/superset-ui-preset-chart-xy/src/ScatterPlot/legacy/index.ts create mode 100644 packages/superset-ui-preset-chart-xy/src/ScatterPlot/legacy/transformProps.ts create mode 100644 packages/superset-ui-preset-chart-xy/src/ScatterPlot/transformProps.ts diff --git a/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/ScatterPlot/constants.ts b/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/ScatterPlot/constants.ts new file mode 100644 index 000000000..70083c631 --- /dev/null +++ b/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/ScatterPlot/constants.ts @@ -0,0 +1,2 @@ +export const SCATTER_PLOT_PLUGIN_TYPE = 'v2-scatter-plot'; +export const SCATTER_PLOT_PLUGIN_LEGACY_TYPE = 'v2-scatter-plot/legacy'; diff --git a/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/ScatterPlot/data/data.js b/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/ScatterPlot/data/data.js new file mode 100644 index 000000000..e9aff588c --- /dev/null +++ b/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/ScatterPlot/data/data.js @@ -0,0 +1,178 @@ +/* eslint-disable sort-keys, no-magic-numbers */ +export default [ + { + country_name: 'China', + region: 'East Asia & Pacific', + sum__SP_POP_TOTL: 1344130000, + sum__SP_RUR_TOTL_ZS: 49.427, + sum__SP_DYN_LE00_IN: 75.042, + }, + { + country_name: 'Indonesia', + region: 'East Asia & Pacific', + sum__SP_POP_TOTL: 244808254, + sum__SP_RUR_TOTL_ZS: 49.288, + sum__SP_DYN_LE00_IN: 70.3915609756, + }, + { + country_name: 'Japan', + region: 'East Asia & Pacific', + sum__SP_POP_TOTL: 127817277, + sum__SP_RUR_TOTL_ZS: 8.752, + sum__SP_DYN_LE00_IN: 82.5912195122, + }, + { + country_name: 'Philippines', + region: 'East Asia & Pacific', + sum__SP_POP_TOTL: 94501233, + sum__SP_RUR_TOTL_ZS: 54.983, + sum__SP_DYN_LE00_IN: 68.3914878049, + }, + { + country_name: 'Vietnam', + region: 'East Asia & Pacific', + sum__SP_POP_TOTL: 87840000, + sum__SP_RUR_TOTL_ZS: 68.971, + sum__SP_DYN_LE00_IN: 75.457902439, + }, + { + country_name: 'Thailand', + region: 'East Asia & Pacific', + sum__SP_POP_TOTL: 66902958, + sum__SP_RUR_TOTL_ZS: 54.606, + sum__SP_DYN_LE00_IN: 74.008902439, + }, + { + country_name: 'Myanmar', + region: 'East Asia & Pacific', + sum__SP_POP_TOTL: 52125411, + sum__SP_RUR_TOTL_ZS: 68.065, + sum__SP_DYN_LE00_IN: 64.7612439024, + }, + { + country_name: 'India', + region: 'South Asia', + sum__SP_POP_TOTL: 1247446011, + sum__SP_RUR_TOTL_ZS: 68.724, + sum__SP_DYN_LE00_IN: 65.9584878049, + }, + { + country_name: 'Pakistan', + region: 'South Asia', + sum__SP_POP_TOTL: 173669648, + sum__SP_RUR_TOTL_ZS: 62.993, + sum__SP_DYN_LE00_IN: 66.2838780488, + }, + { + country_name: 'Bangladesh', + region: 'South Asia', + sum__SP_POP_TOTL: 153405612, + sum__SP_RUR_TOTL_ZS: 68.775, + sum__SP_DYN_LE00_IN: 69.891804878, + }, + { + country_name: 'United States', + region: 'North America', + sum__SP_POP_TOTL: 311721632, + sum__SP_RUR_TOTL_ZS: 19.06, + sum__SP_DYN_LE00_IN: 78.6414634146, + }, + { + country_name: 'Brazil', + region: 'Latin America & Caribbean', + sum__SP_POP_TOTL: 200517584, + sum__SP_RUR_TOTL_ZS: 15.377, + sum__SP_DYN_LE00_IN: 73.3473658537, + }, + { + country_name: 'Mexico', + region: 'Latin America & Caribbean', + sum__SP_POP_TOTL: 120365271, + sum__SP_RUR_TOTL_ZS: 21.882, + sum__SP_DYN_LE00_IN: 76.9141707317, + }, + { + country_name: 'Nigeria', + region: 'Sub-Saharan Africa', + sum__SP_POP_TOTL: 163770669, + sum__SP_RUR_TOTL_ZS: 55.638, + sum__SP_DYN_LE00_IN: 51.7102439024, + }, + { + country_name: 'Ethiopia', + region: 'Sub-Saharan Africa', + sum__SP_POP_TOTL: 89858696, + sum__SP_RUR_TOTL_ZS: 82.265, + sum__SP_DYN_LE00_IN: 62.2528536585, + }, + { + country_name: 'Congo, Dem. Rep.', + region: 'Sub-Saharan Africa', + sum__SP_POP_TOTL: 68087376, + sum__SP_RUR_TOTL_ZS: 59.558, + sum__SP_DYN_LE00_IN: 49.3007073171, + }, + { + country_name: 'South Africa', + region: 'Sub-Saharan Africa', + sum__SP_POP_TOTL: 51553479, + sum__SP_RUR_TOTL_ZS: 37.254, + sum__SP_DYN_LE00_IN: 55.2956585366, + }, + { + country_name: 'Russian Federation', + region: 'Europe & Central Asia', + sum__SP_POP_TOTL: 142960868, + sum__SP_RUR_TOTL_ZS: 26.268, + sum__SP_DYN_LE00_IN: 69.6585365854, + }, + { + country_name: 'Germany', + region: 'Europe & Central Asia', + sum__SP_POP_TOTL: 81797673, + sum__SP_RUR_TOTL_ZS: 25.512, + sum__SP_DYN_LE00_IN: 80.7414634146, + }, + { + country_name: 'Turkey', + region: 'Europe & Central Asia', + sum__SP_POP_TOTL: 73199372, + sum__SP_RUR_TOTL_ZS: 28.718, + sum__SP_DYN_LE00_IN: 74.5404878049, + }, + { + country_name: 'France', + region: 'Europe & Central Asia', + sum__SP_POP_TOTL: 65342776, + sum__SP_RUR_TOTL_ZS: 21.416, + sum__SP_DYN_LE00_IN: 82.1146341463, + }, + { + country_name: 'United Kingdom', + region: 'Europe & Central Asia', + sum__SP_POP_TOTL: 63258918, + sum__SP_RUR_TOTL_ZS: 18.43, + sum__SP_DYN_LE00_IN: 80.9512195122, + }, + { + country_name: 'Italy', + region: 'Europe & Central Asia', + sum__SP_POP_TOTL: 59379449, + sum__SP_RUR_TOTL_ZS: 31.556, + sum__SP_DYN_LE00_IN: 82.187804878, + }, + { + country_name: 'Egypt, Arab Rep.', + region: 'Middle East & North Africa', + sum__SP_POP_TOTL: 83787634, + sum__SP_RUR_TOTL_ZS: 57, + sum__SP_DYN_LE00_IN: 70.6785609756, + }, + { + country_name: 'Iran, Islamic Rep.', + region: 'Middle East & North Africa', + sum__SP_POP_TOTL: 75184322, + sum__SP_RUR_TOTL_ZS: 28.8, + sum__SP_DYN_LE00_IN: 73.4493170732, + }, +]; diff --git a/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/ScatterPlot/data/legacyData.js b/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/ScatterPlot/data/legacyData.js new file mode 100644 index 000000000..37a26d3ec --- /dev/null +++ b/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/ScatterPlot/data/legacyData.js @@ -0,0 +1,338 @@ +/* eslint-disable sort-keys, no-magic-numbers */ +export default [ + { + key: 'East Asia & Pacific', + values: [ + { + country_name: 'China', + region: 'East Asia & Pacific', + sum__SP_POP_TOTL: 1344130000.0, + sum__SP_RUR_TOTL_ZS: 49.427, + sum__SP_DYN_LE00_IN: 75.042, + x: 49.427, + y: 75.042, + size: 1344130000.0, + shape: 'circle', + group: 'East Asia & Pacific', + }, + { + country_name: 'Indonesia', + region: 'East Asia & Pacific', + sum__SP_POP_TOTL: 244808254.0, + sum__SP_RUR_TOTL_ZS: 49.288, + sum__SP_DYN_LE00_IN: 70.3915609756, + x: 49.288, + y: 70.3915609756, + size: 244808254.0, + shape: 'circle', + group: 'East Asia & Pacific', + }, + { + country_name: 'Japan', + region: 'East Asia & Pacific', + sum__SP_POP_TOTL: 127817277.0, + sum__SP_RUR_TOTL_ZS: 8.752, + sum__SP_DYN_LE00_IN: 82.5912195122, + x: 8.752, + y: 82.5912195122, + size: 127817277.0, + shape: 'circle', + group: 'East Asia & Pacific', + }, + { + country_name: 'Philippines', + region: 'East Asia & Pacific', + sum__SP_POP_TOTL: 94501233.0, + sum__SP_RUR_TOTL_ZS: 54.983, + sum__SP_DYN_LE00_IN: 68.3914878049, + x: 54.983, + y: 68.3914878049, + size: 94501233.0, + shape: 'circle', + group: 'East Asia & Pacific', + }, + { + country_name: 'Vietnam', + region: 'East Asia & Pacific', + sum__SP_POP_TOTL: 87840000.0, + sum__SP_RUR_TOTL_ZS: 68.971, + sum__SP_DYN_LE00_IN: 75.457902439, + x: 68.971, + y: 75.457902439, + size: 87840000.0, + shape: 'circle', + group: 'East Asia & Pacific', + }, + { + country_name: 'Thailand', + region: 'East Asia & Pacific', + sum__SP_POP_TOTL: 66902958.0, + sum__SP_RUR_TOTL_ZS: 54.606, + sum__SP_DYN_LE00_IN: 74.008902439, + x: 54.606, + y: 74.008902439, + size: 66902958.0, + shape: 'circle', + group: 'East Asia & Pacific', + }, + { + country_name: 'Myanmar', + region: 'East Asia & Pacific', + sum__SP_POP_TOTL: 52125411.0, + sum__SP_RUR_TOTL_ZS: 68.065, + sum__SP_DYN_LE00_IN: 64.7612439024, + x: 68.065, + y: 64.7612439024, + size: 52125411.0, + shape: 'circle', + group: 'East Asia & Pacific', + }, + ], + }, + { + key: 'South Asia', + values: [ + { + country_name: 'India', + region: 'South Asia', + sum__SP_POP_TOTL: 1247446011.0, + sum__SP_RUR_TOTL_ZS: 68.724, + sum__SP_DYN_LE00_IN: 65.9584878049, + x: 68.724, + y: 65.9584878049, + size: 1247446011.0, + shape: 'circle', + group: 'South Asia', + }, + { + country_name: 'Pakistan', + region: 'South Asia', + sum__SP_POP_TOTL: 173669648.0, + sum__SP_RUR_TOTL_ZS: 62.993, + sum__SP_DYN_LE00_IN: 66.2838780488, + x: 62.993, + y: 66.2838780488, + size: 173669648.0, + shape: 'circle', + group: 'South Asia', + }, + { + country_name: 'Bangladesh', + region: 'South Asia', + sum__SP_POP_TOTL: 153405612.0, + sum__SP_RUR_TOTL_ZS: 68.775, + sum__SP_DYN_LE00_IN: 69.891804878, + x: 68.775, + y: 69.891804878, + size: 153405612.0, + shape: 'circle', + group: 'South Asia', + }, + ], + }, + { + key: 'North America', + values: [ + { + country_name: 'United States', + region: 'North America', + sum__SP_POP_TOTL: 311721632.0, + sum__SP_RUR_TOTL_ZS: 19.06, + sum__SP_DYN_LE00_IN: 78.6414634146, + x: 19.06, + y: 78.6414634146, + size: 311721632.0, + shape: 'circle', + group: 'North America', + }, + ], + }, + { + key: 'Latin America & Caribbean', + values: [ + { + country_name: 'Brazil', + region: 'Latin America & Caribbean', + sum__SP_POP_TOTL: 200517584.0, + sum__SP_RUR_TOTL_ZS: 15.377, + sum__SP_DYN_LE00_IN: 73.3473658537, + x: 15.377, + y: 73.3473658537, + size: 200517584.0, + shape: 'circle', + group: 'Latin America & Caribbean', + }, + { + country_name: 'Mexico', + region: 'Latin America & Caribbean', + sum__SP_POP_TOTL: 120365271.0, + sum__SP_RUR_TOTL_ZS: 21.882, + sum__SP_DYN_LE00_IN: 76.9141707317, + x: 21.882, + y: 76.9141707317, + size: 120365271.0, + shape: 'circle', + group: 'Latin America & Caribbean', + }, + ], + }, + { + key: 'Sub-Saharan Africa', + values: [ + { + country_name: 'Nigeria', + region: 'Sub-Saharan Africa', + sum__SP_POP_TOTL: 163770669.0, + sum__SP_RUR_TOTL_ZS: 55.638, + sum__SP_DYN_LE00_IN: 51.7102439024, + x: 55.638, + y: 51.7102439024, + size: 163770669.0, + shape: 'circle', + group: 'Sub-Saharan Africa', + }, + { + country_name: 'Ethiopia', + region: 'Sub-Saharan Africa', + sum__SP_POP_TOTL: 89858696.0, + sum__SP_RUR_TOTL_ZS: 82.265, + sum__SP_DYN_LE00_IN: 62.2528536585, + x: 82.265, + y: 62.2528536585, + size: 89858696.0, + shape: 'circle', + group: 'Sub-Saharan Africa', + }, + { + country_name: 'Congo, Dem. Rep.', + region: 'Sub-Saharan Africa', + sum__SP_POP_TOTL: 68087376.0, + sum__SP_RUR_TOTL_ZS: 59.558, + sum__SP_DYN_LE00_IN: 49.3007073171, + x: 59.558, + y: 49.3007073171, + size: 68087376.0, + shape: 'circle', + group: 'Sub-Saharan Africa', + }, + { + country_name: 'South Africa', + region: 'Sub-Saharan Africa', + sum__SP_POP_TOTL: 51553479.0, + sum__SP_RUR_TOTL_ZS: 37.254, + sum__SP_DYN_LE00_IN: 55.2956585366, + x: 37.254, + y: 55.2956585366, + size: 51553479.0, + shape: 'circle', + group: 'Sub-Saharan Africa', + }, + ], + }, + { + key: 'Europe & Central Asia', + values: [ + { + country_name: 'Russian Federation', + region: 'Europe & Central Asia', + sum__SP_POP_TOTL: 142960868.0, + sum__SP_RUR_TOTL_ZS: 26.268, + sum__SP_DYN_LE00_IN: 69.6585365854, + x: 26.268, + y: 69.6585365854, + size: 142960868.0, + shape: 'circle', + group: 'Europe & Central Asia', + }, + { + country_name: 'Germany', + region: 'Europe & Central Asia', + sum__SP_POP_TOTL: 81797673.0, + sum__SP_RUR_TOTL_ZS: 25.512, + sum__SP_DYN_LE00_IN: 80.7414634146, + x: 25.512, + y: 80.7414634146, + size: 81797673.0, + shape: 'circle', + group: 'Europe & Central Asia', + }, + { + country_name: 'Turkey', + region: 'Europe & Central Asia', + sum__SP_POP_TOTL: 73199372.0, + sum__SP_RUR_TOTL_ZS: 28.718, + sum__SP_DYN_LE00_IN: 74.5404878049, + x: 28.718, + y: 74.5404878049, + size: 73199372.0, + shape: 'circle', + group: 'Europe & Central Asia', + }, + { + country_name: 'France', + region: 'Europe & Central Asia', + sum__SP_POP_TOTL: 65342776.0, + sum__SP_RUR_TOTL_ZS: 21.416, + sum__SP_DYN_LE00_IN: 82.1146341463, + x: 21.416, + y: 82.1146341463, + size: 65342776.0, + shape: 'circle', + group: 'Europe & Central Asia', + }, + { + country_name: 'United Kingdom', + region: 'Europe & Central Asia', + sum__SP_POP_TOTL: 63258918.0, + sum__SP_RUR_TOTL_ZS: 18.43, + sum__SP_DYN_LE00_IN: 80.9512195122, + x: 18.43, + y: 80.9512195122, + size: 63258918.0, + shape: 'circle', + group: 'Europe & Central Asia', + }, + { + country_name: 'Italy', + region: 'Europe & Central Asia', + sum__SP_POP_TOTL: 59379449.0, + sum__SP_RUR_TOTL_ZS: 31.556, + sum__SP_DYN_LE00_IN: 82.187804878, + x: 31.556, + y: 82.187804878, + size: 59379449.0, + shape: 'circle', + group: 'Europe & Central Asia', + }, + ], + }, + { + key: 'Middle East & North Africa', + values: [ + { + country_name: 'Egypt, Arab Rep.', + region: 'Middle East & North Africa', + sum__SP_POP_TOTL: 83787634.0, + sum__SP_RUR_TOTL_ZS: 57.0, + sum__SP_DYN_LE00_IN: 70.6785609756, + x: 57.0, + y: 70.6785609756, + size: 83787634.0, + shape: 'circle', + group: 'Middle East & North Africa', + }, + { + country_name: 'Iran, Islamic Rep.', + region: 'Middle East & North Africa', + sum__SP_POP_TOTL: 75184322.0, + sum__SP_RUR_TOTL_ZS: 28.8, + sum__SP_DYN_LE00_IN: 73.4493170732, + x: 28.8, + y: 73.4493170732, + size: 75184322.0, + shape: 'circle', + group: 'Middle East & North Africa', + }, + ], + }, +]; diff --git a/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/ScatterPlot/index.js b/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/ScatterPlot/index.js new file mode 100644 index 000000000..ce1aec40b --- /dev/null +++ b/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/ScatterPlot/index.js @@ -0,0 +1,13 @@ +import { ScatterPlotPlugin as LegacyScatterPlotPlugin } from '../../../../../superset-ui-preset-chart-xy/src/legacy'; +import { ScatterPlotPlugin } from '../../../../../superset-ui-preset-chart-xy/src'; +import BasicStories from './stories/basic'; +import BubbleStories from './stories/bubble'; +import LegacyStories from './stories/legacy'; +import { SCATTER_PLOT_PLUGIN_TYPE, SCATTER_PLOT_PLUGIN_LEGACY_TYPE } from './constants'; + +new LegacyScatterPlotPlugin().configure({ key: SCATTER_PLOT_PLUGIN_LEGACY_TYPE }).register(); +new ScatterPlotPlugin().configure({ key: SCATTER_PLOT_PLUGIN_TYPE }).register(); + +export default { + examples: [...BasicStories, ...BubbleStories, ...LegacyStories], +}; diff --git a/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/ScatterPlot/stories/basic.jsx b/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/ScatterPlot/stories/basic.jsx new file mode 100644 index 000000000..ff9c12ec3 --- /dev/null +++ b/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/ScatterPlot/stories/basic.jsx @@ -0,0 +1,59 @@ +/* eslint-disable no-magic-numbers, sort-keys */ +import * as React from 'react'; +import { SuperChart, ChartProps } from '@superset-ui/chart'; +import { radios } from '@storybook/addon-knobs'; +import data from '../data/data'; +import { SCATTER_PLOT_PLUGIN_TYPE } from '../constants'; + +export default [ + { + renderStory: () => [ + , + ], + storyName: 'Basic', + storyPath: 'preset-chart-xy|ScatterPlotPlugin', + }, +]; diff --git a/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/ScatterPlot/stories/bubble.jsx b/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/ScatterPlot/stories/bubble.jsx new file mode 100644 index 000000000..d4c98b0ff --- /dev/null +++ b/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/ScatterPlot/stories/bubble.jsx @@ -0,0 +1,67 @@ +/* eslint-disable no-magic-numbers, sort-keys */ +import * as React from 'react'; +import { SuperChart, ChartProps } from '@superset-ui/chart'; +import { radios } from '@storybook/addon-knobs'; +import data from '../data/data'; +import { SCATTER_PLOT_PLUGIN_TYPE } from '../constants'; + +export default [ + { + renderStory: () => [ + , + ], + storyName: 'Bubble', + storyPath: 'preset-chart-xy|ScatterPlotPlugin', + }, +]; diff --git a/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/ScatterPlot/stories/legacy.tsx b/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/ScatterPlot/stories/legacy.tsx new file mode 100644 index 000000000..6e6d51a0f --- /dev/null +++ b/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/ScatterPlot/stories/legacy.tsx @@ -0,0 +1,49 @@ +/* eslint-disable no-magic-numbers */ +import * as React from 'react'; +import { SuperChart, ChartProps } from '@superset-ui/chart'; +import data from '../data/legacyData'; +import { SCATTER_PLOT_PLUGIN_LEGACY_TYPE } from '../constants'; + +export default [ + { + renderStory: () => [ + , + ], + storyName: 'Use Legacy API shim', + storyPath: 'preset-chart-xy|ScatterPlotPlugin', + }, +]; diff --git a/packages/superset-ui-preset-chart-xy/src/BoxPlot/BoxPlot.tsx b/packages/superset-ui-preset-chart-xy/src/BoxPlot/BoxPlot.tsx index 58676abfb..089f40d2e 100644 --- a/packages/superset-ui-preset-chart-xy/src/BoxPlot/BoxPlot.tsx +++ b/packages/superset-ui-preset-chart-xy/src/BoxPlot/BoxPlot.tsx @@ -1,21 +1,3 @@ -/** - * 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'; @@ -28,6 +10,7 @@ import WithLegend from '../components/WithLegend'; import ChartLegend from '../components/legend/ChartLegend'; import Encoder, { ChannelTypes, Encoding, Outputs } from './Encoder'; import { Dataset, PlainObject } from '../encodeable/types/Data'; +import { PartialSpec } from '../encodeable/types/Specification'; chartTheme.gridStyles.stroke = '#f1f3f5'; @@ -44,10 +27,10 @@ type Props = { width: string | number; height: string | number; margin?: Margin; - encoding: Encoding; data: Dataset; theme?: ChartTheme; -} & Readonly; +} & PartialSpec & + Readonly; export default class BoxPlot extends React.PureComponent { static defaultProps = defaultProps; @@ -59,24 +42,26 @@ export default class BoxPlot extends React.PureComponent { super(props); const createEncoder = createSelector( - (enc: Encoding) => enc, - (enc: Encoding) => new Encoder({ encoding: enc }), + (p: PartialSpec) => p.encoding, + p => p.commonEncoding, + p => p.options, + (encoding, commonEncoding, options) => new Encoder({ encoding, commonEncoding, options }), ); this.createEncoder = () => { - this.encoder = createEncoder(this.props.encoding); + this.encoder = createEncoder(this.props); }; - this.encoder = createEncoder(this.props.encoding); + this.encoder = createEncoder(this.props); this.renderChart = this.renderChart.bind(this); } renderChart(dim: Dimension) { const { width, height } = dim; - const { data, encoding, margin, theme } = this.props; + const { data, margin, theme } = this.props; const { channels } = this.encoder; - const isHorizontal = encoding.y.type === 'nominal'; + const isHorizontal = channels.y.definition.type === 'nominal'; const children = [ { stroke={(datum: PlainObject) => channels.color.encode(datum)} strokeWidth={1} widthRatio={0.6} - horizontal={encoding.y.type === 'nominal'} + horizontal={channels.y.definition.type === 'nominal'} />, ]; diff --git a/packages/superset-ui-preset-chart-xy/src/BoxPlot/createTooltip.tsx b/packages/superset-ui-preset-chart-xy/src/BoxPlot/createTooltip.tsx index b3efd5070..23c0633cd 100644 --- a/packages/superset-ui-preset-chart-xy/src/BoxPlot/createTooltip.tsx +++ b/packages/superset-ui-preset-chart-xy/src/BoxPlot/createTooltip.tsx @@ -11,21 +11,24 @@ export default function createBoxPlotTooltip(encoder: Encoder) { return function BoxPlotTooltip({ datum, color }: { datum: BoxPlotDataRow; color: string }) { const { label, min, max, median, firstQuartile, thirdQuartile, outliers } = datum; + const formatValue = + channels.y.definition.type === 'nominal' ? channels.x.formatValue : channels.y.formatValue; + const data = []; if (isDefined(min)) { - data.push({ key: 'Min', valueColumn: channels.y.formatValue(min) }); + data.push({ key: 'Min', valueColumn: formatValue(min) }); } if (isDefined(max)) { - data.push({ key: 'Max', valueColumn: channels.y.formatValue(max) }); + data.push({ key: 'Max', valueColumn: formatValue(max) }); } if (isDefined(median)) { - data.push({ key: 'Median', valueColumn: channels.y.formatValue(median) }); + data.push({ key: 'Median', valueColumn: formatValue(median) }); } if (isDefined(firstQuartile)) { - data.push({ key: '1st Quartile', valueColumn: channels.y.formatValue(firstQuartile) }); + data.push({ key: '1st Quartile', valueColumn: formatValue(firstQuartile) }); } if (isDefined(thirdQuartile)) { - data.push({ key: '3rd Quartile', valueColumn: channels.y.formatValue(thirdQuartile) }); + data.push({ key: '3rd Quartile', valueColumn: formatValue(thirdQuartile) }); } if (isDefined(outliers) && outliers.length > 0) { data.push({ key: '# Outliers', valueColumn: outliers.length }); diff --git a/packages/superset-ui-preset-chart-xy/src/Line/Line.tsx b/packages/superset-ui-preset-chart-xy/src/Line/Line.tsx index 69accd8dc..8bcd81b81 100644 --- a/packages/superset-ui-preset-chart-xy/src/Line/Line.tsx +++ b/packages/superset-ui-preset-chart-xy/src/Line/Line.tsx @@ -19,6 +19,7 @@ import WithLegend from '../components/WithLegend'; import Encoder, { ChannelTypes, Encoding, Outputs } from './Encoder'; import { Dataset, PlainObject } from '../encodeable/types/Data'; import ChartLegend from '../components/legend/ChartLegend'; +import { PartialSpec } from '../encodeable/types/Specification'; chartTheme.gridStyles.stroke = '#f1f3f5'; @@ -35,10 +36,10 @@ type Props = { width: string | number; height: string | number; margin?: Margin; - encoding: Encoding; data: Dataset; theme?: ChartTheme; -} & Readonly; +} & PartialSpec & + Readonly; export interface Series { key: string; @@ -67,15 +68,17 @@ class LineChart extends PureComponent { super(props); const createEncoder = createSelector( - (enc: Encoding) => enc, - (enc: Encoding) => new Encoder({ encoding: enc }), + (p: PartialSpec) => p.encoding, + p => p.commonEncoding, + p => p.options, + (encoding, commonEncoding, options) => new Encoder({ encoding, commonEncoding, options }), ); this.createEncoder = () => { - this.encoder = createEncoder(this.props.encoding); + this.encoder = createEncoder(this.props); }; - this.encoder = createEncoder(this.props.encoding); + this.encoder = createEncoder(this.props); this.renderChart = this.renderChart.bind(this); } diff --git a/packages/superset-ui-preset-chart-xy/src/ScatterPlot/Encoder.ts b/packages/superset-ui-preset-chart-xy/src/ScatterPlot/Encoder.ts new file mode 100644 index 000000000..bbda94638 --- /dev/null +++ b/packages/superset-ui-preset-chart-xy/src/ScatterPlot/Encoder.ts @@ -0,0 +1,48 @@ +import AbstractEncoder from '../encodeable/AbstractEncoder'; +import { PartialSpec } from '../encodeable/types/Specification'; +import { EncodingFromChannelsAndOutputs } from '../encodeable/types/Channel'; + +/** + * Define channel types + */ +const channelTypes = { + fill: 'Color', + size: 'Numeric', + stroke: 'Color', + x: 'X', + y: 'Y', +} as const; + +export type ChannelTypes = typeof channelTypes; + +/** + * Define output type for each channel + */ +export interface Outputs { + fill: string; + size: number; + stroke: string; + x: number | null; + y: number | null; +} + +/** + * Derive encoding config + */ +export type Encoding = EncodingFromChannelsAndOutputs; + +export default class Encoder extends AbstractEncoder { + static readonly DEFAULT_ENCODINGS: Encoding = { + fill: { value: '#222' }, + size: { value: 5 }, + stroke: { value: 'none' }, + x: { field: 'x', type: 'quantitative' }, + y: { field: 'y', type: 'quantitative' }, + }; + + static readonly CHANNEL_OPTIONS = {}; + + constructor(spec: PartialSpec) { + super(channelTypes, spec, Encoder.DEFAULT_ENCODINGS, Encoder.CHANNEL_OPTIONS); + } +} diff --git a/packages/superset-ui-preset-chart-xy/src/ScatterPlot/ScatterPlot.tsx b/packages/superset-ui-preset-chart-xy/src/ScatterPlot/ScatterPlot.tsx new file mode 100644 index 000000000..1ba6316f4 --- /dev/null +++ b/packages/superset-ui-preset-chart-xy/src/ScatterPlot/ScatterPlot.tsx @@ -0,0 +1,150 @@ +/* eslint-disable sort-keys, no-magic-numbers, complexity */ +import React, { PureComponent } from 'react'; +import { XYChart, PointSeries } from '@data-ui/xy-chart'; +import { chartTheme, ChartTheme } from '@data-ui/theme'; +import { Margin, Dimension } from '@superset-ui/dimension'; +import { extent as d3Extent } from 'd3-array'; +import { createSelector } from 'reselect'; +import createTooltip from './createTooltip'; +import XYChartLayout from '../utils/XYChartLayout'; +import WithLegend from '../components/WithLegend'; +import Encoder, { ChannelTypes, Encoding, Outputs } from './Encoder'; +import { Dataset, PlainObject } from '../encodeable/types/Data'; +import ChartLegend from '../components/legend/ChartLegend'; +import { PartialSpec } from '../encodeable/types/Specification'; + +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; + data: Dataset; + theme?: ChartTheme; +} & PartialSpec & + Readonly; + +export interface EncodedPoint { + x: Outputs['x']; + y: Outputs['y']; + size: Outputs['size']; + fill: Outputs['fill']; + stroke: Outputs['stroke']; + data: PlainObject; +} + +export default class ScatterPlot extends PureComponent { + static defaultProps = defaultProps; + + encoder: Encoder; + private createEncoder: () => void; + + constructor(props: Props) { + super(props); + + const createEncoder = createSelector( + (p: PartialSpec) => p.encoding, + p => p.commonEncoding, + p => p.options, + (encoding, commonEncoding, options) => new Encoder({ encoding, commonEncoding, options }), + ); + + this.createEncoder = () => { + this.encoder = createEncoder(this.props); + }; + + this.encoder = createEncoder(this.props); + this.renderChart = this.renderChart.bind(this); + } + + renderChart(dim: Dimension) { + const { width, height } = dim; + const { data, margin, theme } = this.props; + const { channels } = this.encoder; + + if (typeof channels.size.scale !== 'undefined') { + const domain = d3Extent(data, d => channels.size.get(d)); + const [min, max] = domain; + const adjustedDomain = [Math.min(min || 0, 0), Math.max(max || 1, 1)]; + channels.size.scale.setDomain(adjustedDomain); + } + + const encodedData = data.map(d => ({ + x: channels.x.get(d), + y: channels.y.get(d), + size: channels.size.encode(d), + fill: channels.fill.encode(d), + stroke: channels.stroke.encode(d), + data: d, + })); + + const children = [ + d.fill} + fillOpacity={0.5} + stroke={(d: EncodedPoint) => d.stroke} + size={(d: EncodedPoint) => d.size} + />, + ]; + + 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/packages/superset-ui-preset-chart-xy/src/ScatterPlot/createMetadata.ts b/packages/superset-ui-preset-chart-xy/src/ScatterPlot/createMetadata.ts new file mode 100644 index 000000000..29a790802 --- /dev/null +++ b/packages/superset-ui-preset-chart-xy/src/ScatterPlot/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('Scatter Plot'), + thumbnail, + useLegacyApi, + }); +} diff --git a/packages/superset-ui-preset-chart-xy/src/ScatterPlot/createTooltip.tsx b/packages/superset-ui-preset-chart-xy/src/ScatterPlot/createTooltip.tsx new file mode 100644 index 000000000..aabb41f00 --- /dev/null +++ b/packages/superset-ui-preset-chart-xy/src/ScatterPlot/createTooltip.tsx @@ -0,0 +1,57 @@ +/* eslint-disable no-magic-numbers */ + +import React from 'react'; +import TooltipFrame from '../components/tooltip/TooltipFrame'; +import TooltipTable from '../components/tooltip/TooltipTable'; +import Encoder from './Encoder'; +import { isFieldDef } from '../encodeable/types/ChannelDef'; +import { EncodedPoint } from './ScatterPlot'; + +export default function createTooltip(encoder: Encoder) { + function Tooltip({ datum }: { datum: EncodedPoint }) { + const { channels, commonChannels } = encoder; + const { x, y, size, fill, stroke } = channels; + + const tooltipRows = [ + { key: 'x', keyColumn: x.getTitle(), valueColumn: x.format(datum.data) }, + { key: 'y', keyColumn: y.getTitle(), valueColumn: y.format(datum.data) }, + ]; + + if (isFieldDef(fill.definition)) { + tooltipRows.push({ + key: 'fill', + keyColumn: fill.getTitle(), + valueColumn: fill.format(datum.data), + }); + } + if (isFieldDef(stroke.definition)) { + tooltipRows.push({ + key: 'stroke', + keyColumn: stroke.getTitle(), + valueColumn: stroke.format(datum.data), + }); + } + if (isFieldDef(size.definition)) { + tooltipRows.push({ + key: 'size', + keyColumn: size.getTitle(), + valueColumn: size.format(datum.data), + }); + } + commonChannels.group.forEach(g => { + tooltipRows.push({ + key: `${g.name}`, + keyColumn: g.getTitle(), + valueColumn: g.format(datum.data), + }); + }); + + return ( + + + + ); + } + + return Tooltip; +} diff --git a/packages/superset-ui-preset-chart-xy/src/ScatterPlot/images/thumbnail.png b/packages/superset-ui-preset-chart-xy/src/ScatterPlot/images/thumbnail.png new file mode 100644 index 0000000000000000000000000000000000000000..ecb8dd566e97e8490a4cc41f59a7a53588818be4 GIT binary patch literal 31155 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 literal 0 HcmV?d00001 diff --git a/packages/superset-ui-preset-chart-xy/src/ScatterPlot/images/thumbnailLarge.png b/packages/superset-ui-preset-chart-xy/src/ScatterPlot/images/thumbnailLarge.png new file mode 100644 index 0000000000000000000000000000000000000000..ecb8dd566e97e8490a4cc41f59a7a53588818be4 GIT binary patch literal 31155 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 literal 0 HcmV?d00001 diff --git a/packages/superset-ui-preset-chart-xy/src/ScatterPlot/index.ts b/packages/superset-ui-preset-chart-xy/src/ScatterPlot/index.ts new file mode 100644 index 000000000..08a9615de --- /dev/null +++ b/packages/superset-ui-preset-chart-xy/src/ScatterPlot/index.ts @@ -0,0 +1,13 @@ +import { ChartPlugin } from '@superset-ui/chart'; +import createMetadata from './createMetadata'; +import transformProps from './transformProps'; + +export default class LineChartPlugin extends ChartPlugin { + constructor() { + super({ + loadChart: () => import('./ScatterPlot'), + metadata: createMetadata(), + transformProps, + }); + } +} diff --git a/packages/superset-ui-preset-chart-xy/src/ScatterPlot/legacy/index.ts b/packages/superset-ui-preset-chart-xy/src/ScatterPlot/legacy/index.ts new file mode 100644 index 000000000..571dbef72 --- /dev/null +++ b/packages/superset-ui-preset-chart-xy/src/ScatterPlot/legacy/index.ts @@ -0,0 +1,13 @@ +import { ChartPlugin } from '@superset-ui/chart'; +import createMetadata from '../createMetadata'; +import transformProps from './transformProps'; + +export default class LineChartPlugin extends ChartPlugin { + constructor() { + super({ + loadChart: () => import('../ScatterPlot'), + metadata: createMetadata(true), + transformProps, + }); + } +} diff --git a/packages/superset-ui-preset-chart-xy/src/ScatterPlot/legacy/transformProps.ts b/packages/superset-ui-preset-chart-xy/src/ScatterPlot/legacy/transformProps.ts new file mode 100644 index 000000000..8c2cc418b --- /dev/null +++ b/packages/superset-ui-preset-chart-xy/src/ScatterPlot/legacy/transformProps.ts @@ -0,0 +1,96 @@ +/* eslint-disable sort-keys */ +import { ChartProps } from '@superset-ui/chart'; +import { flatMap } from 'lodash'; + +interface DataRow { + key: string[]; + values: { + [key: string]: any; + }[]; +} + +export default function transformProps(chartProps: ChartProps) { + const { width, height, formData, payload } = chartProps; + const { + colorScheme, + entity, + maxBubbleSize, + series, + showLegend, + size, + x, + xAxisFormat, + xAxisLabel, + // TODO: These fields are not supported yet + // xAxisShowminmax, + // xLogScale, + y, + yAxisLabel, + yAxisFormat, + // TODO: These fields are not supported yet + // yAxisShowminmax, + // yLogScale, + } = formData; + const data = payload.data as DataRow[]; + + return { + data: flatMap( + data.map((row: DataRow) => + row.values.map(v => ({ + [x]: v[x], + [y]: v[y], + [series]: v[series], + [size]: v[size], + [entity]: v[entity], + })), + ), + ), + width, + height, + encoding: { + x: { + field: x, + type: 'quantitive', + format: xAxisFormat, + scale: { + type: 'linear', + }, + axis: { + orient: 'bottom', + title: xAxisLabel, + }, + }, + y: { + field: y, + type: 'quantitative', + format: yAxisFormat, + scale: { + type: 'linear', + }, + axis: { + orient: 'left', + title: yAxisLabel, + }, + }, + size: { + field: size, + type: 'quantitative', + scale: { + type: 'linear', + range: [0, maxBubbleSize], + }, + }, + fill: { + field: series, + type: 'nominal', + scale: { + scheme: colorScheme, + }, + legend: showLegend, + }, + }, + commonEncoding: { + group: [{ field: entity }], + }, + }; +} diff --git a/packages/superset-ui-preset-chart-xy/src/ScatterPlot/transformProps.ts b/packages/superset-ui-preset-chart-xy/src/ScatterPlot/transformProps.ts new file mode 100644 index 000000000..94f2c864e --- /dev/null +++ b/packages/superset-ui-preset-chart-xy/src/ScatterPlot/transformProps.ts @@ -0,0 +1,18 @@ +import { ChartProps } from '@superset-ui/chart'; + +/* eslint-disable sort-keys */ + +export default function transformProps(chartProps: ChartProps) { + const { width, height, formData, payload } = chartProps; + const { encoding, commonEncoding, margin } = formData; + const { data } = payload; + + return { + data, + width, + height, + encoding, + commonEncoding, + margin, + }; +} diff --git a/packages/superset-ui-preset-chart-xy/src/encodeable/AbstractEncoder.ts b/packages/superset-ui-preset-chart-xy/src/encodeable/AbstractEncoder.ts index cd9f95394..4a290a362 100644 --- a/packages/superset-ui-preset-chart-xy/src/encodeable/AbstractEncoder.ts +++ b/packages/superset-ui-preset-chart-xy/src/encodeable/AbstractEncoder.ts @@ -7,7 +7,7 @@ import { ChannelInput, } from './types/Channel'; import { FullSpec, BaseOptions, PartialSpec } from './types/Specification'; -import { isFieldDef, isTypedFieldDef } from './types/ChannelDef'; +import { isFieldDef, isTypedFieldDef, FieldDef } from './types/ChannelDef'; import ChannelEncoder from './ChannelEncoder'; import { Dataset } from './types/Data'; @@ -28,6 +28,11 @@ export default abstract class AbstractEncoder< readonly [k in keyof ChannelTypes]: ChannelEncoder }; + readonly commonChannels: { + group: ChannelEncoder[]; + tooltip: ChannelEncoder[]; + }; + readonly legends: { [key: string]: (keyof ChannelTypes)[]; }; @@ -67,6 +72,25 @@ export default abstract class AbstractEncoder< return all; }, {}) as Channels; + this.commonChannels = { + group: this.spec.commonEncoding.group.map( + (def, i) => + new ChannelEncoder({ + definition: def, + name: `group${i}`, + type: 'Text', + }), + ), + tooltip: this.spec.commonEncoding.tooltip.map( + (def, i) => + new ChannelEncoder({ + definition: def, + name: `tooltip${i}`, + type: 'Text', + }), + ), + }; + // Group the channels that use the same field together // so they can share the same legend. this.legends = {}; @@ -94,9 +118,14 @@ export default abstract class AbstractEncoder< return spec as FullSpec; } - const { encoding, ...rest } = spec; + const { encoding, commonEncoding = {}, ...rest } = spec; + const { group = [], tooltip = [] } = commonEncoding; return { + commonEncoding: { + group, + tooltip, + }, ...rest, encoding: { ...defaultEncoding, diff --git a/packages/superset-ui-preset-chart-xy/src/encodeable/AxisAgent.ts b/packages/superset-ui-preset-chart-xy/src/encodeable/AxisAgent.ts index 94c1737c3..ffc15404f 100644 --- a/packages/superset-ui-preset-chart-xy/src/encodeable/AxisAgent.ts +++ b/packages/superset-ui-preset-chart-xy/src/encodeable/AxisAgent.ts @@ -142,7 +142,7 @@ export default class AxisAgent, Output extends Va if (this.channelEncoder.isX()) { if (strategyForLabelOverlap === 'flat') { - const labelHeight = labelDimensions[0].height; + const labelHeight = labelDimensions.length > 0 ? labelDimensions[0].height : 0; labelOffset = labelHeight + labelPadding; requiredMargin += labelHeight; } else if (strategyForLabelOverlap === 'rotate') { diff --git a/packages/superset-ui-preset-chart-xy/src/encodeable/types/Specification.ts b/packages/superset-ui-preset-chart-xy/src/encodeable/types/Specification.ts index 7631de62e..f8d7427d0 100644 --- a/packages/superset-ui-preset-chart-xy/src/encodeable/types/Specification.ts +++ b/packages/superset-ui-preset-chart-xy/src/encodeable/types/Specification.ts @@ -1,13 +1,23 @@ +import { FieldDef } from './ChannelDef'; + export interface BaseOptions { namespace?: string; } export interface PartialSpec { encoding: Partial; + commonEncoding?: Partial<{ + group: FieldDef[]; + tooltip: FieldDef[]; + }>; options?: Options; } export interface FullSpec { encoding: Encoding; + commonEncoding: { + group: FieldDef[]; + tooltip: FieldDef[]; + }; options?: Options; } diff --git a/packages/superset-ui-preset-chart-xy/src/index.ts b/packages/superset-ui-preset-chart-xy/src/index.ts index 498637196..3932d61ae 100644 --- a/packages/superset-ui-preset-chart-xy/src/index.ts +++ b/packages/superset-ui-preset-chart-xy/src/index.ts @@ -1,2 +1,3 @@ export { default as BoxPlotChartPlugin } from './BoxPlot'; +export { default as ScatterPlotPlugin } from './ScatterPlot'; export { default as LineChartPlugin } from './Line'; diff --git a/packages/superset-ui-preset-chart-xy/src/legacy.ts b/packages/superset-ui-preset-chart-xy/src/legacy.ts index c0403f479..24c83c6d3 100644 --- a/packages/superset-ui-preset-chart-xy/src/legacy.ts +++ b/packages/superset-ui-preset-chart-xy/src/legacy.ts @@ -1,2 +1,3 @@ export { default as BoxPlotChartPlugin } from './BoxPlot/legacy'; +export { default as ScatterPlotPlugin } from './ScatterPlot/legacy'; export { default as LineChartPlugin } from './Line/legacy'; diff --git a/packages/superset-ui-preset-chart-xy/types/@data-ui/xy-chart/index.d.ts b/packages/superset-ui-preset-chart-xy/types/@data-ui/xy-chart/index.d.ts index 504be9e51..308a07385 100644 --- a/packages/superset-ui-preset-chart-xy/types/@data-ui/xy-chart/index.d.ts +++ b/packages/superset-ui-preset-chart-xy/types/@data-ui/xy-chart/index.d.ts @@ -33,6 +33,7 @@ declare module '@data-ui/xy-chart' { export class CrossHair extends React.PureComponent {} export class LinearGradient extends React.PureComponent {} export class LineSeries extends React.PureComponent {} + export class PointSeries extends React.PureComponent {} export class WithTooltip extends React.PureComponent {} export class XYChart extends React.PureComponent {} export class XAxis extends React.PureComponent {}