From 049b40bc80e71728227177d7a97589db3058e015 Mon Sep 17 00:00:00 2001 From: Krist Wongsuphasawat Date: Thu, 28 Mar 2019 16:58:22 -0700 Subject: [PATCH] feat: line chart with revised encodeable utilities (#26) * feat: line chart * feat: implement scale extraction * refactor: no error * fix: handle null * fix: nicing and demo * fix: legend and demo * fix: remove commented code * fix: clean * fix: reviewer comments * fix: legend and series * docs: make demos tsx * fix: reactnode * fix: label angle * fix: resolve labelxxx field names * docs: try knobs * feat: improve axis * refactor: combine computelayout into axisagent * refactor: cleaner use of scale * fix: proptypes --- .../plugins/superset-ui-plugins/package.json | 3 +- .../superset-ui-plugins-demo/package.json | 3 +- .../stories/preset-chart-xy/Line/Stories.jsx | 42 --- .../stories/preset-chart-xy/Line/constants.ts | 2 + .../stories/preset-chart-xy/Line/data/data.js | 226 +++++++++++++++ .../preset-chart-xy/Line/data/data2.js | 94 +++++++ .../Line/{data.js => data/legacyData.js} | 0 .../stories/preset-chart-xy/Line/index.js | 12 +- .../preset-chart-xy/Line/stories/basic.jsx | 59 ++++ .../preset-chart-xy/Line/stories/legacy.tsx | 78 +++++ .../preset-chart-xy/Line/stories/missing.tsx | 67 +++++ .../Line/stories/timeShift.tsx | 78 +++++ .../superset-ui-preset-chart-xy/README.md | 4 +- .../src/Line/Encoder.ts | 53 ++-- .../src/Line/Line.jsx | 136 --------- .../src/Line/Line.tsx | 226 +++++++++++++++ .../src/Line/createTooltip.jsx | 5 - .../src/Line/createTooltip.tsx | 50 ++++ .../src/Line/index.js | 39 --- .../src/Line/index.ts | 13 + .../src/Line/legacy/index.ts | 13 + .../src/Line/legacy/metadata.ts | 6 + .../src/Line/legacy/transformProps.ts | 67 +++++ .../src/Line/metadata.ts | 11 + .../src/Line/transformProps.js | 47 ---- .../src/Line/transformProps.ts | 16 ++ .../src/components/ChartFrame.tsx | 6 +- .../src/components/ChartLegend.tsx | 91 ++++++ .../src/components/WithLegend.tsx | 60 ++-- .../src/components/tooltip/TooltipFrame.tsx | 4 +- .../src/components/tooltip/TooltipTable.tsx | 4 +- .../src/encodeable/AbstractEncoder.ts | 109 +++---- .../src/encodeable/AxisAgent.ts | 177 ++++++++++++ .../src/encodeable/ChannelEncoder.ts | 125 ++++---- .../src/encodeable/parsers/extractAxis.ts | 35 --- .../src/encodeable/parsers/extractFormat.ts | 30 +- .../src/encodeable/parsers/extractScale.ts | 266 ++++++++++++++++-- .../src/encodeable/types/Axis.ts | 42 ++- .../src/encodeable/types/Base.ts | 2 + .../src/encodeable/types/Channel.ts | 39 +++ .../src/encodeable/types/FieldDef.ts | 20 +- .../src/encodeable/types/Scale.ts | 11 +- .../superset-ui-preset-chart-xy/src/legacy.js | 2 + .../superset-ui-preset-chart-xy/src/types.ts | 0 .../src/utils/XYChartLayout.jsx | 140 --------- .../src/utils/XYChartLayout.tsx | 182 ++++++++++++ .../src/utils/adjustMargin.ts | 26 -- .../src/utils/computeChartLayout.jsx | 147 ---------- .../src/utils/computeXAxisLayout.js | 75 ----- .../src/utils/computeYAxisLayout.js | 35 --- .../src/utils/constants.ts | 2 + .../src/utils/createTickComponent.jsx | 46 --- .../src/utils/createTickComponent.tsx | 48 ++++ .../src/utils/createTickLabelProps.ts | 30 ++ .../src/utils/getTickLabels.js | 13 - .../src/utils/renderLegend.jsx | 58 ---- .../esm/utils/collectScalesFromProps.d.ts | 4 +- .../types/@data-ui/xy-chart/index.d.ts | 13 +- .../types/@vx/legend/index.d.ts | 12 +- .../types/@vx/responsive/index.d.ts | 4 +- 60 files changed, 2110 insertions(+), 1098 deletions(-) delete mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/Stories.jsx create mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/constants.ts create mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/data/data.js create mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/data/data2.js rename superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/{data.js => data/legacyData.js} (100%) create mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/stories/basic.jsx create mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/stories/legacy.tsx create mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/stories/missing.tsx create mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/stories/timeShift.tsx delete mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/Line.jsx create mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/Line.tsx delete mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/createTooltip.jsx create mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/createTooltip.tsx delete mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/index.js create mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/index.ts create mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/legacy/index.ts create mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/legacy/metadata.ts create mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/legacy/transformProps.ts create mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/metadata.ts delete mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/transformProps.js create mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/transformProps.ts create mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/components/ChartLegend.tsx create mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/AxisAgent.ts delete mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/parsers/extractAxis.ts create mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/types/Base.ts create mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/legacy.js create mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/types.ts delete mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/XYChartLayout.jsx create mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/XYChartLayout.tsx delete mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/adjustMargin.ts delete mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/computeChartLayout.jsx delete mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/computeXAxisLayout.js delete mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/computeYAxisLayout.js create mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/constants.ts delete mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/createTickComponent.jsx create mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/createTickComponent.tsx create mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/createTickLabelProps.ts delete mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/getTickLabels.js delete mode 100644 superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/renderLegend.jsx diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/package.json b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/package.json index ca1bb7cbe3..d77b411f6f 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/package.json +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/package.json @@ -39,7 +39,7 @@ "license": "Apache-2.0", "devDependencies": { "@superset-ui/build-config": "^0.0.4", - "@superset-ui/chart": "^0.10.2", + "@superset-ui/chart": "^0.10.8", "@superset-ui/color": "^0.10.0", "@superset-ui/connection": "^0.10.0", "@superset-ui/core": "^0.10.0", @@ -47,6 +47,7 @@ "@superset-ui/number-format": "^0.10.0", "@superset-ui/time-format": "^0.10.0", "@superset-ui/translation": "^0.10.0", + "@types/react": "^16.8.8", "csstype": "^2.6.3", "fast-glob": "^2.2.6", "fs-extra": "^7.0.1", diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/package.json b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/package.json index 7902f2be2a..8886dee54f 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/package.json +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/package.json @@ -35,10 +35,11 @@ "@storybook/addon-knobs": "^4.0.2", "@storybook/addon-options": "^4.0.3", "@storybook/react": "^4.1.11", - "@superset-ui/chart": "^0.10.2", + "@superset-ui/chart": "^0.10.8", "@superset-ui/color": "^0.10.1", "@superset-ui/time-format": "^0.10.1", "@superset-ui/translation": "^0.10.0", + "@types/react": "^16.8.8", "babel-loader": "^8.0.4", "bootstrap": "^3.3.6", "cache-loader": "^1.2.2", diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/Stories.jsx b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/Stories.jsx deleted file mode 100644 index 1118f6302f..0000000000 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/Stories.jsx +++ /dev/null @@ -1,42 +0,0 @@ -/* eslint-disable no-magic-numbers */ -import React from 'react'; -import { SuperChart } from '@superset-ui/chart'; -import data from './data'; - -export default [ - { - renderStory: () => ( - - ), - storyName: 'Basic', - storyPath: 'preset-chart-xy|LineChartPlugin', - }, -]; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/constants.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/constants.ts new file mode 100644 index 0000000000..2109c66670 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/constants.ts @@ -0,0 +1,2 @@ +export const LINE_PLUGIN_TYPE = 'v2-line'; +export const LINE_PLUGIN_LEGACY_TYPE = 'v2-line/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/Line/data/data.js b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/data/data.js new file mode 100644 index 0000000000..f14581b7fd --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/data/data.js @@ -0,0 +1,226 @@ +/* eslint-disable sort-keys, no-magic-numbers */ +export default { + keys: ['name', 'x', 'y'], + values: [ + { x: -157766400000, y: 24703, name: 'Christopher' }, + { x: -126230400000, y: 27861, name: 'Christopher' }, + { x: -94694400000, y: 29436, name: 'Christopher' }, + { x: -63158400000, y: 31463, name: 'Christopher' }, + { x: -31536000000, y: 35718, name: 'Christopher' }, + { x: 0, y: 41758, name: 'Christopher' }, + { x: 31536000000, y: 48172, name: 'Christopher' }, + { x: 63072000000, y: 52092, name: 'Christopher' }, + { x: 94694400000, y: 48217, name: 'Christopher' }, + { x: 126230400000, y: 48476, name: 'Christopher' }, + { x: 157766400000, y: 46438, name: 'Christopher' }, + { x: 189302400000, y: 45086, name: 'Christopher' }, + { x: 220924800000, y: 46610, name: 'Christopher' }, + { x: 252460800000, y: 47107, name: 'Christopher' }, + { x: 283996800000, y: 50514, name: 'Christopher' }, + { x: 315532800000, y: 48969, name: 'Christopher' }, + { x: 347155200000, y: 50108, name: 'Christopher' }, + { x: 378691200000, y: 59055, name: 'Christopher' }, + { x: 410227200000, y: 59188, name: 'Christopher' }, + { x: 441763200000, y: 59859, name: 'Christopher' }, + { x: 473385600000, y: 59516, name: 'Christopher' }, + { x: 504921600000, y: 56633, name: 'Christopher' }, + { x: 536457600000, y: 54466, name: 'Christopher' }, + { x: 567993600000, y: 52996, name: 'Christopher' }, + { x: 599616000000, y: 53205, name: 'Christopher' }, + { x: 631152000000, y: 52322, name: 'Christopher' }, + { x: 662688000000, y: 47109, name: 'Christopher' }, + { x: 694224000000, y: 42470, name: 'Christopher' }, + { x: 725846400000, y: 38257, name: 'Christopher' }, + { x: 757382400000, y: 34823, name: 'Christopher' }, + { x: 788918400000, y: 32728, name: 'Christopher' }, + { x: 820454400000, y: 30988, name: 'Christopher' }, + { x: 852076800000, y: 29179, name: 'Christopher' }, + { x: 883612800000, y: 27083, name: 'Christopher' }, + { x: 915148800000, y: 25700, name: 'Christopher' }, + { x: 946684800000, y: 24959, name: 'Christopher' }, + { x: 978307200000, y: 23180, name: 'Christopher' }, + { x: 1009843200000, y: 21731, name: 'Christopher' }, + { x: 1041379200000, y: 20793, name: 'Christopher' }, + { x: 1072915200000, y: 19739, name: 'Christopher' }, + { x: 1104537600000, y: 19190, name: 'Christopher' }, + { x: 1136073600000, y: 19674, name: 'Christopher' }, + { x: 1167609600000, y: 19986, name: 'Christopher' }, + { x: 1199145600000, y: 17771, name: 'Christopher' }, + { x: -157766400000, y: 67646, name: 'David' }, + { x: -126230400000, y: 66207, name: 'David' }, + { x: -94694400000, y: 66581, name: 'David' }, + { x: -63158400000, y: 63531, name: 'David' }, + { x: -31536000000, y: 63502, name: 'David' }, + { x: 0, y: 61570, name: 'David' }, + { x: 31536000000, y: 52948, name: 'David' }, + { x: 63072000000, y: 46218, name: 'David' }, + { x: 94694400000, y: 40968, name: 'David' }, + { x: 126230400000, y: 41654, name: 'David' }, + { x: 157766400000, y: 39019, name: 'David' }, + { x: 189302400000, y: 39165, name: 'David' }, + { x: 220924800000, y: 40407, name: 'David' }, + { x: 252460800000, y: 40533, name: 'David' }, + { x: 283996800000, y: 41898, name: 'David' }, + { x: 315532800000, y: 41743, name: 'David' }, + { x: 347155200000, y: 40486, name: 'David' }, + { x: 378691200000, y: 40283, name: 'David' }, + { x: 410227200000, y: 39048, name: 'David' }, + { x: 441763200000, y: 38346, name: 'David' }, + { x: 473385600000, y: 38395, name: 'David' }, + { x: 504921600000, y: 37021, name: 'David' }, + { x: 536457600000, y: 36672, name: 'David' }, + { x: 567993600000, y: 35214, name: 'David' }, + { x: 599616000000, y: 35139, name: 'David' }, + { x: 631152000000, y: 33661, name: 'David' }, + { x: 662688000000, y: 30347, name: 'David' }, + { x: 694224000000, y: 28344, name: 'David' }, + { x: 725846400000, y: 26947, name: 'David' }, + { x: 757382400000, y: 24784, name: 'David' }, + { x: 788918400000, y: 22967, name: 'David' }, + { x: 820454400000, y: 22941, name: 'David' }, + { x: 852076800000, y: 21824, name: 'David' }, + { x: 883612800000, y: 20816, name: 'David' }, + { x: 915148800000, y: 20267, name: 'David' }, + { x: 946684800000, y: 19695, name: 'David' }, + { x: 978307200000, y: 19281, name: 'David' }, + { x: 1009843200000, y: 18600, name: 'David' }, + { x: 1041379200000, y: 18557, name: 'David' }, + { x: 1072915200000, y: 18315, name: 'David' }, + { x: 1104537600000, y: 18017, name: 'David' }, + { x: 1136073600000, y: 17510, name: 'David' }, + { x: 1167609600000, y: 17400, name: 'David' }, + { x: 1199145600000, y: 16049, name: 'David' }, + { x: -157766400000, y: 67506, name: 'James' }, + { x: -126230400000, y: 65036, name: 'James' }, + { x: -94694400000, y: 61554, name: 'James' }, + { x: -63158400000, y: 60584, name: 'James' }, + { x: -31536000000, y: 59824, name: 'James' }, + { x: 0, y: 61597, name: 'James' }, + { x: 31536000000, y: 54463, name: 'James' }, + { x: 63072000000, y: 46960, name: 'James' }, + { x: 94694400000, y: 42782, name: 'James' }, + { x: 126230400000, y: 41258, name: 'James' }, + { x: 157766400000, y: 39471, name: 'James' }, + { x: 189302400000, y: 38203, name: 'James' }, + { x: 220924800000, y: 39916, name: 'James' }, + { x: 252460800000, y: 39783, name: 'James' }, + { x: 283996800000, y: 39237, name: 'James' }, + { x: 315532800000, y: 39185, name: 'James' }, + { x: 347155200000, y: 38176, name: 'James' }, + { x: 378691200000, y: 38750, name: 'James' }, + { x: 410227200000, y: 36228, name: 'James' }, + { x: 441763200000, y: 35728, name: 'James' }, + { x: 473385600000, y: 35750, name: 'James' }, + { x: 504921600000, y: 33955, name: 'James' }, + { x: 536457600000, y: 32552, name: 'James' }, + { x: 567993600000, y: 32418, name: 'James' }, + { x: 599616000000, y: 32658, name: 'James' }, + { x: 631152000000, y: 32288, name: 'James' }, + { x: 662688000000, y: 30460, name: 'James' }, + { x: 694224000000, y: 28450, name: 'James' }, + { x: 725846400000, y: 26193, name: 'James' }, + { x: 757382400000, y: 24706, name: 'James' }, + { x: 788918400000, y: 22691, name: 'James' }, + { x: 820454400000, y: 21122, name: 'James' }, + { x: 852076800000, y: 20368, name: 'James' }, + { x: 883612800000, y: 19651, name: 'James' }, + { x: 915148800000, y: 18508, name: 'James' }, + { x: 946684800000, y: 17939, name: 'James' }, + { x: 978307200000, y: 17023, name: 'James' }, + { x: 1009843200000, y: 16905, name: 'James' }, + { x: 1041379200000, y: 16832, name: 'James' }, + { x: 1072915200000, y: 16459, name: 'James' }, + { x: 1104537600000, y: 16046, name: 'James' }, + { x: 1136073600000, y: 16139, name: 'James' }, + { x: 1167609600000, y: 15821, name: 'James' }, + { x: 1199145600000, y: 14920, name: 'James' }, + { x: -157766400000, y: 71390, name: 'John' }, + { x: -126230400000, y: 64858, name: 'John' }, + { x: -94694400000, y: 61480, name: 'John' }, + { x: -63158400000, y: 60754, name: 'John' }, + { x: -31536000000, y: 58644, name: 'John' }, + { x: 0, y: 58348, name: 'John' }, + { x: 31536000000, y: 51382, name: 'John' }, + { x: 63072000000, y: 43028, name: 'John' }, + { x: 94694400000, y: 39061, name: 'John' }, + { x: 126230400000, y: 37553, name: 'John' }, + { x: 157766400000, y: 34970, name: 'John' }, + { x: 189302400000, y: 33876, name: 'John' }, + { x: 220924800000, y: 34103, name: 'John' }, + { x: 252460800000, y: 33895, name: 'John' }, + { x: 283996800000, y: 35305, name: 'John' }, + { x: 315532800000, y: 35131, name: 'John' }, + { x: 347155200000, y: 34761, name: 'John' }, + { x: 378691200000, y: 34560, name: 'John' }, + { x: 410227200000, y: 33047, name: 'John' }, + { x: 441763200000, y: 32484, name: 'John' }, + { x: 473385600000, y: 31397, name: 'John' }, + { x: 504921600000, y: 30103, name: 'John' }, + { x: 536457600000, y: 29462, name: 'John' }, + { x: 567993600000, y: 29301, name: 'John' }, + { x: 599616000000, y: 29751, name: 'John' }, + { x: 631152000000, y: 29011, name: 'John' }, + { x: 662688000000, y: 27727, name: 'John' }, + { x: 694224000000, y: 26156, name: 'John' }, + { x: 725846400000, y: 24918, name: 'John' }, + { x: 757382400000, y: 24119, name: 'John' }, + { x: 788918400000, y: 23174, name: 'John' }, + { x: 820454400000, y: 22104, name: 'John' }, + { x: 852076800000, y: 21330, name: 'John' }, + { x: 883612800000, y: 20556, name: 'John' }, + { x: 915148800000, y: 20280, name: 'John' }, + { x: 946684800000, y: 20032, name: 'John' }, + { x: 978307200000, y: 18839, name: 'John' }, + { x: 1009843200000, y: 17400, name: 'John' }, + { x: 1041379200000, y: 17170, name: 'John' }, + { x: 1072915200000, y: 16381, name: 'John' }, + { x: 1104537600000, y: 15692, name: 'John' }, + { x: 1136073600000, y: 15083, name: 'John' }, + { x: 1167609600000, y: 14348, name: 'John' }, + { x: 1199145600000, y: 13110, name: 'John' }, + { x: -157766400000, y: 80812, name: 'Michael' }, + { x: -126230400000, y: 79709, name: 'Michael' }, + { x: -94694400000, y: 82204, name: 'Michael' }, + { x: -63158400000, y: 81785, name: 'Michael' }, + { x: -31536000000, y: 84893, name: 'Michael' }, + { x: 0, y: 85015, name: 'Michael' }, + { x: 31536000000, y: 77321, name: 'Michael' }, + { x: 63072000000, y: 71197, name: 'Michael' }, + { x: 94694400000, y: 67598, name: 'Michael' }, + { x: 126230400000, y: 67304, name: 'Michael' }, + { x: 157766400000, y: 68149, name: 'Michael' }, + { x: 189302400000, y: 66686, name: 'Michael' }, + { x: 220924800000, y: 67344, name: 'Michael' }, + { x: 252460800000, y: 66875, name: 'Michael' }, + { x: 283996800000, y: 67473, name: 'Michael' }, + { x: 315532800000, y: 68375, name: 'Michael' }, + { x: 347155200000, y: 68467, name: 'Michael' }, + { x: 378691200000, y: 67904, name: 'Michael' }, + { x: 410227200000, y: 67708, name: 'Michael' }, + { x: 441763200000, y: 67457, name: 'Michael' }, + { x: 473385600000, y: 64667, name: 'Michael' }, + { x: 504921600000, y: 63959, name: 'Michael' }, + { x: 536457600000, y: 63442, name: 'Michael' }, + { x: 567993600000, y: 63924, name: 'Michael' }, + { x: 599616000000, y: 65233, name: 'Michael' }, + { x: 631152000000, y: 65138, name: 'Michael' }, + { x: 662688000000, y: 60646, name: 'Michael' }, + { x: 694224000000, y: 54216, name: 'Michael' }, + { x: 725846400000, y: 49443, name: 'Michael' }, + { x: 757382400000, y: 44361, name: 'Michael' }, + { x: 788918400000, y: 41311, name: 'Michael' }, + { x: 820454400000, y: 38284, name: 'Michael' }, + { x: 852076800000, y: 37459, name: 'Michael' }, + { x: 883612800000, y: 36525, name: 'Michael' }, + { x: 915148800000, y: 33820, name: 'Michael' }, + { x: 946684800000, y: 31956, name: 'Michael' }, + { x: 978307200000, y: 29612, name: 'Michael' }, + { x: 1009843200000, y: 28156, name: 'Michael' }, + { x: 1041379200000, y: 27031, name: 'Michael' }, + { x: 1072915200000, y: 25418, name: 'Michael' }, + { x: 1104537600000, y: 23678, name: 'Michael' }, + { x: 1136073600000, y: 22498, name: 'Michael' }, + { x: 1167609600000, y: 21805, name: 'Michael' }, + { x: 1199145600000, y: 20271, name: 'Michael' }, + ], +}; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/data/data2.js b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/data/data2.js new file mode 100644 index 0000000000..fda02b8afb --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/data/data2.js @@ -0,0 +1,94 @@ +/* eslint-disable sort-keys, no-magic-numbers */ +export default { + keys: ['snapshot', 'x', 'y'], + values: [ + { x: -157766400000, y: 24703, snapshot: 'Last year' }, + { x: -126230400000, y: 27861, snapshot: 'Last year' }, + { x: -94694400000, y: 29436, snapshot: 'Last year' }, + { x: -63158400000, y: 31463, snapshot: 'Last year' }, + { x: -31536000000, y: 35718, snapshot: 'Last year' }, + { x: 0, y: 41758, snapshot: 'Last year' }, + { x: 31536000000, y: 48172, snapshot: 'Last year' }, + { x: 63072000000, y: 52092, snapshot: 'Last year' }, + { x: 94694400000, y: 48217, snapshot: 'Last year' }, + { x: 126230400000, y: 48476, snapshot: 'Last year' }, + { x: 157766400000, y: 46438, snapshot: 'Last year' }, + { x: 189302400000, y: 45086, snapshot: 'Last year' }, + { x: 220924800000, y: 46610, snapshot: 'Last year' }, + { x: 252460800000, y: 47107, snapshot: 'Last year' }, + { x: 283996800000, y: 50514, snapshot: 'Last year' }, + { x: 315532800000, y: 48969, snapshot: 'Last year' }, + { x: 347155200000, y: 50108, snapshot: 'Last year' }, + { x: 378691200000, y: 59055, snapshot: 'Last year' }, + { x: 410227200000, y: 59188, snapshot: 'Last year' }, + { x: 441763200000, y: 59859, snapshot: 'Last year' }, + { x: 473385600000, y: 59516, snapshot: 'Last year' }, + { x: 504921600000, y: 56633, snapshot: 'Last year' }, + { x: 536457600000, y: 54466, snapshot: 'Last year' }, + { x: 567993600000, y: 52996, snapshot: 'Last year' }, + { x: 599616000000, y: 53205, snapshot: 'Last year' }, + { x: 631152000000, y: 52322, snapshot: 'Last year' }, + { x: 662688000000, y: 47109, snapshot: 'Last year' }, + { x: 694224000000, y: 42470, snapshot: 'Last year' }, + { x: 725846400000, y: 38257, snapshot: 'Last year' }, + { x: 757382400000, y: 34823, snapshot: 'Last year' }, + { x: 788918400000, y: 32728, snapshot: 'Last year' }, + { x: 820454400000, y: 30988, snapshot: 'Last year' }, + { x: 852076800000, y: 29179, snapshot: 'Last year' }, + { x: 883612800000, y: 27083, snapshot: 'Last year' }, + { x: 915148800000, y: 25700, snapshot: 'Last year' }, + { x: 946684800000, y: 24959, snapshot: 'Last year' }, + { x: 978307200000, y: 23180, snapshot: 'Last year' }, + { x: 1009843200000, y: 21731, snapshot: 'Last year' }, + { x: 1041379200000, y: 20793, snapshot: 'Last year' }, + { x: 1072915200000, y: 19739, snapshot: 'Last year' }, + { x: 1104537600000, y: 19190, snapshot: 'Last year' }, + { x: 1136073600000, y: 19674, snapshot: 'Last year' }, + { x: 1167609600000, y: 19986, snapshot: 'Last year' }, + { x: 1199145600000, y: 17771, snapshot: 'Last year' }, + { x: -157766400000, y: 80812, snapshot: 'Current' }, + { x: -126230400000, y: 79709, snapshot: 'Current' }, + { x: -94694400000, y: 82204, snapshot: 'Current' }, + { x: -63158400000, y: 81785, snapshot: 'Current' }, + { x: -31536000000, y: 84893, snapshot: 'Current' }, + { x: 0, y: 85015, snapshot: 'Current' }, + { x: 31536000000, y: 77321, snapshot: 'Current' }, + { x: 63072000000, y: 71197, snapshot: 'Current' }, + { x: 94694400000, y: 67598, snapshot: 'Current' }, + { x: 126230400000, y: 67304, snapshot: 'Current' }, + { x: 157766400000, y: 68149, snapshot: 'Current' }, + { x: 189302400000, y: 66686, snapshot: 'Current' }, + { x: 220924800000, y: 67344, snapshot: 'Current' }, + { x: 252460800000, y: 66875, snapshot: 'Current' }, + { x: 283996800000, y: 67473, snapshot: 'Current' }, + { x: 315532800000, y: 68375, snapshot: 'Current' }, + { x: 347155200000, y: 68467, snapshot: 'Current' }, + { x: 378691200000, y: 67904, snapshot: 'Current' }, + { x: 410227200000, y: 67708, snapshot: 'Current' }, + { x: 441763200000, y: 67457, snapshot: 'Current' }, + { x: 473385600000, y: 64667, snapshot: 'Current' }, + { x: 504921600000, y: 63959, snapshot: 'Current' }, + { x: 536457600000, y: 63442, snapshot: 'Current' }, + { x: 567993600000, y: 63924, snapshot: 'Current' }, + { x: 599616000000, y: 65233, snapshot: 'Current' }, + { x: 631152000000, y: 65138, snapshot: 'Current' }, + { x: 662688000000, y: 60646, snapshot: 'Current' }, + { x: 694224000000, y: 54216, snapshot: 'Current' }, + { x: 725846400000, y: 49443, snapshot: 'Current' }, + { x: 757382400000, y: 44361, snapshot: 'Current' }, + { x: 788918400000, y: 41311, snapshot: 'Current' }, + { x: 820454400000, y: 38284, snapshot: 'Current' }, + { x: 852076800000, y: 37459, snapshot: 'Current' }, + { x: 883612800000, y: 36525, snapshot: 'Current' }, + { x: 915148800000, y: 33820, snapshot: 'Current' }, + { x: 946684800000, y: 31956, snapshot: 'Current' }, + { x: 978307200000, y: 29612, snapshot: 'Current' }, + { x: 1009843200000, y: 28156, snapshot: 'Current' }, + { x: 1041379200000, y: 27031, snapshot: 'Current' }, + { x: 1072915200000, y: 25418, snapshot: 'Current' }, + { x: 1104537600000, y: 23678, snapshot: 'Current' }, + { x: 1136073600000, y: 22498, snapshot: 'Current' }, + { x: 1167609600000, y: 21805, snapshot: 'Current' }, + { x: 1199145600000, y: 20271, snapshot: 'Current' }, + ], +}; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/data.js b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/data/legacyData.js similarity index 100% rename from superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/data.js rename to superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/data/legacyData.js diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/index.js b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/index.js index eee6be318c..b7b5ff1873 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/index.js +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/index.js @@ -1,8 +1,14 @@ +import { LineChartPlugin as LegacyLineChartPlugin } from '../../../../../superset-ui-preset-chart-xy/src/legacy'; import { LineChartPlugin } from '../../../../../superset-ui-preset-chart-xy/src'; -import Stories from './Stories'; +import BasicStories from './stories/basic'; +import LegacyStories from './stories/legacy'; +import MissingStories from './stories/missing'; +import TimeShiftStories from './stories/timeShift'; +import { LINE_PLUGIN_TYPE, LINE_PLUGIN_LEGACY_TYPE } from './constants'; -new LineChartPlugin().configure({ key: 'line2' }).register(); +new LegacyLineChartPlugin().configure({ key: LINE_PLUGIN_LEGACY_TYPE }).register(); +new LineChartPlugin().configure({ key: LINE_PLUGIN_TYPE }).register(); export default { - examples: [...Stories], + examples: [...BasicStories, ...MissingStories, ...TimeShiftStories, ...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/Line/stories/basic.jsx b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/stories/basic.jsx new file mode 100644 index 0000000000..a4194d9db1 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/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 { LINE_PLUGIN_TYPE } from '../constants'; + +export default [ + { + renderStory: () => [ + , + ], + storyName: 'Basic', + storyPath: 'preset-chart-xy|LineChartPlugin', + }, +]; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/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/Line/stories/legacy.tsx new file mode 100644 index 0000000000..3ba98239a3 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/stories/legacy.tsx @@ -0,0 +1,78 @@ +/* eslint-disable no-magic-numbers */ +import * as React from 'react'; +import { SuperChart, ChartProps } from '@superset-ui/chart'; +import data from '../data/legacyData'; +import { LINE_PLUGIN_LEGACY_TYPE } from '../constants'; + +export default [ + { + renderStory: () => [ + , + , + ], + storyName: 'Use Legacy API shim', + storyPath: 'preset-chart-xy|LineChartPlugin', + }, +]; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/stories/missing.tsx b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/stories/missing.tsx new file mode 100644 index 0000000000..1f383eabdd --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/stories/missing.tsx @@ -0,0 +1,67 @@ +/* eslint-disable no-magic-numbers, sort-keys */ +import * as React from 'react'; +import { SuperChart, ChartProps } from '@superset-ui/chart'; +import data from '../data/data'; +import { LINE_PLUGIN_TYPE } from '../constants'; + +const missingData = { + keys: data.keys, + values: data.values.map(({ y, ...rest }) => ({ + ...rest, + y: Math.random() < 0.05 ? null : y, + })), +}; + +export default [ + { + renderStory: () => [ + , + ], + storyName: 'with missing data', + storyPath: 'preset-chart-xy|LineChartPlugin', + }, +]; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/stories/timeShift.tsx b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/stories/timeShift.tsx new file mode 100644 index 0000000000..24b3c59a6c --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-plugins-demo/storybook/stories/preset-chart-xy/Line/stories/timeShift.tsx @@ -0,0 +1,78 @@ +/* eslint-disable no-magic-numbers, sort-keys */ +import * as React from 'react'; +import { SuperChart, ChartProps } from '@superset-ui/chart'; +import data from '../data/data2'; +import { LINE_PLUGIN_TYPE } from '../constants'; + +export default [ + { + renderStory: () => [ + , + ], + storyName: 'with time shift', + storyPath: 'preset-chart-xy|LineChartPlugin', + }, +]; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/README.md b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/README.md index c6871a50e2..3b15b180c8 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/README.md +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/README.md @@ -3,7 +3,9 @@ [![Version](https://img.shields.io/npm/v/@superset-ui/preset-chart-xy.svg?style=flat-square)](https://img.shields.io/npm/v/@superset-ui/preset-chart-xy.svg?style=flat-square) [![David (path)](https://img.shields.io/david/apache-superset/superset-ui-plugins.svg?path=packages%2Fsuperset-ui-preset-chart-xy&style=flat-square)](https://david-dm.org/apache-superset/superset-ui-plugins?path=packages/superset-ui-preset-chart-xy) -This plugin provides Box Plot for Superset. +This plugin provides basic charts on cartesian coordinates (Line, Box Plot) for Superset. + +> DISCLAIMER: It is still under heavy development and the APIs are subject to changes. ### Usage diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/Encoder.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/Encoder.ts index c0e99eeccb..99d9ed3b8b 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/Encoder.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/Encoder.ts @@ -1,6 +1,27 @@ -import { MarkPropChannelDef, XFieldDef, YFieldDef } from '../encodeable/types/FieldDef'; import AbstractEncoder from '../encodeable/AbstractEncoder'; import { PartialSpec } from '../encodeable/types/Specification'; +import { EncodingFromChannelsAndOutputs } from '../encodeable/types/Channel'; + +/** + * Define channel types + */ +// This is a workaround until TypeScript 3.4 which has const context +// which will allow use to derive type from object literal +// without type widening (e.g. 'X' instead of string). +// Now we have to define class with readonly fields +// to be able to use "typeof" to infer strict types +// See more details from +// https://github.com/Microsoft/TypeScript/issues/20195 +// https://github.com/Microsoft/TypeScript/pull/29510 +const channelTypes = new class Channels { + readonly x = 'X'; + readonly y = 'Y'; + readonly color = 'Color'; + readonly fill = 'Category'; + readonly strokeDasharray = 'Category'; +}(); + +export type ChannelTypes = typeof channelTypes; /** * Define output type for each channel @@ -14,18 +35,12 @@ export interface Outputs { } /** - * Define encoding config for each channel + * Derive encoding config */ -export interface Encoding { - x: XFieldDef; - y: YFieldDef; - color: MarkPropChannelDef; - fill: MarkPropChannelDef; - strokeDasharray: MarkPropChannelDef; -} +export type Encoding = EncodingFromChannelsAndOutputs; -export default class Encoder extends AbstractEncoder { - static DEFAULT_ENCODINGS: Encoding = { +export default class Encoder extends AbstractEncoder { + static readonly DEFAULT_ENCODINGS: Encoding = { color: { value: '#222' }, fill: { value: false }, strokeDasharray: { value: '' }, @@ -33,17 +48,11 @@ export default class Encoder extends AbstractEncoder { y: { field: 'y', type: 'quantitative' }, }; - constructor(spec: PartialSpec) { - super(spec, Encoder.DEFAULT_ENCODINGS); - } + static readonly CHANNEL_OPTIONS = { + fill: { legend: false }, + }; - createChannels() { - return { - color: this.createChannel('color'), - fill: this.createChannel('fill', { legend: false }), - strokeDasharray: this.createChannel('strokeDasharray'), - x: this.createChannel('x'), - y: this.createChannel('y'), - }; + 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/Line/Line.jsx b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/Line.jsx deleted file mode 100644 index 5e1a4aba7c..0000000000 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/Line.jsx +++ /dev/null @@ -1,136 +0,0 @@ -/** - * 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 PropTypes from 'prop-types'; -import React from 'react'; -import { LineSeries, XYChart } from '@data-ui/xy-chart'; -import { themeShape } from '@data-ui/xy-chart/esm/utils/propShapes'; -import { chartTheme } from '@data-ui/theme'; -import { CategoricalColorNamespace } from '@superset-ui/color'; -import createTooltip from './createTooltip'; -import renderLegend from '../utils/renderLegend'; -import XYChartLayout from '../utils/XYChartLayout'; -import WithLegend from '../components/WithLegend'; - -chartTheme.gridStyles.stroke = '#f1f3f5'; - -const propTypes = { - className: PropTypes.string, - data: PropTypes.arrayOf( - PropTypes.shape({ - key: PropTypes.string, - values: PropTypes.arrayOf(PropTypes.number), - }), - ).isRequired, - width: PropTypes.number.isRequired, - height: PropTypes.number.isRequired, - margin: PropTypes.shape({ - top: PropTypes.number, - left: PropTypes.number, - bottom: PropTypes.number, - right: PropTypes.number, - }), - encoding: PropTypes.shape({ - x: PropTypes.object, - y: PropTypes.object, - color: PropTypes.object, - }).isRequired, - theme: themeShape, -}; - -const defaultProps = { - className: '', - margin: { top: 10, right: 10, left: 10, bottom: 10 }, - theme: chartTheme, -}; - -class LineChart extends React.PureComponent { - renderChart({ width, height }) { - const { data, encoding, margin, theme } = this.props; - - const config = { - width, - height, - minContentWidth: 0, - minContentHeight: 0, - margin, - theme, - encoding, - }; - - const colorFn = CategoricalColorNamespace.getScale( - encoding.color.scale.scheme, - encoding.color.scale.namespace, - ); - - const colorAccessor = encoding.color.accessor; - - const children = data.map(series => ( - - )); - - const layout = new XYChartLayout({ ...config, children }); - - return layout.createChartWithFrame(dim => ( - - {children} - {layout.createXAxis()} - {layout.createYAxis()} - - )); - } - - render() { - const { className, data, width, height, encoding } = this.props; - - return ( - renderLegend(data, encoding.color)} - renderChart={parent => this.renderChart(parent)} - hideLegend={!encoding.color.legend} - /> - ); - } -} - -LineChart.propTypes = propTypes; -LineChart.defaultProps = defaultProps; - -export default LineChart; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/Line.tsx b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/Line.tsx new file mode 100644 index 0000000000..26d5113ab7 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/Line.tsx @@ -0,0 +1,226 @@ +/* eslint-disable sort-keys, no-magic-numbers, complexity */ + +import React, { PureComponent } from 'react'; +import { + AreaSeries, + LinearGradient, + LineSeries, + XYChart, + CrossHair, + WithTooltip, +} from '@data-ui/xy-chart'; +import { chartTheme, ChartTheme } from '@data-ui/theme'; +import { Margin, Dimension } from '@superset-ui/dimension'; +import { groupBy, flatMap, uniqueId, values } from 'lodash'; +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/ChartLegend'; + +chartTheme.gridStyles.stroke = '#f1f3f5'; + +const defaultProps = { + className: '', + margin: { top: 20, right: 20, left: 20, bottom: 20 }, + theme: chartTheme, +}; + +type Props = { + className?: string; + width: string | number; + height: string | number; + margin?: Margin; + encoding: Encoding; + data: Dataset; + theme?: ChartTheme; +} & Readonly; + +export interface Series { + key: string; + color: Outputs['color']; + fill: Outputs['fill']; + strokeDasharray: Outputs['strokeDasharray']; + values: SeriesValue[]; +} + +export interface SeriesValue { + x: Outputs['x']; + y: Outputs['y']; + data: PlainObject; + parent: Series; +} + +const CIRCLE_STYLE = { strokeWidth: 1.5 }; + +class LineChart extends PureComponent { + static defaultProps = defaultProps; + + constructor(props: Props) { + super(props); + const { encoding } = this.props; + + this.encoder = new Encoder({ encoding }); + this.renderChart = this.renderChart.bind(this); + } + + encoder: Encoder; + + renderChart(dim: Dimension) { + const { width, height } = dim; + const { data, encoding, margin, theme } = this.props; + + const fieldNames = data.keys + .filter(k => k !== encoding.x.field && k !== encoding.y.field) + .sort((a, b) => a.localeCompare(b)); + + const groups = groupBy(data.values, row => fieldNames.map(f => `${f}=${row[f]}`).join(',')); + + const allSeries = values(groups).map(seriesData => { + const firstDatum = seriesData[0]; + + const series: Series = { + key: fieldNames.map(f => firstDatum[f]).join(','), + color: this.encoder.channels.color.encode(firstDatum), + fill: this.encoder.channels.fill.encode(firstDatum, false), + strokeDasharray: this.encoder.channels.strokeDasharray.encode(firstDatum), + values: [], + }; + + series.values = seriesData.map(v => ({ + x: this.encoder.channels.x.encode(v), + y: this.encoder.channels.y.encode(v), + data: v, + parent: series, + })); + + return series; + }); + + const filledSeries = flatMap( + allSeries + .filter(({ fill }) => fill) + .map(series => { + const gradientId = uniqueId(`gradient-${series.key}`); + + return [ + , + , + ]; + }), + ); + + const unfilledSeries = allSeries + .filter(({ fill }) => !fill) + .map(series => ( + + )); + + const children = filledSeries.concat(unfilledSeries); + + const layout = new XYChartLayout({ + width, + height, + margin, + theme, + xEncoder: this.encoder.channels.x, + yEncoder: this.encoder.channels.y, + children, + }); + + return layout.renderChartWithFrame((chartDim: Dimension) => ( + + {({ + onMouseLeave, + onMouseMove, + tooltipData, + }: { + onMouseLeave: (...args: any[]) => void; + onMouseMove: (...args: any[]) => void; + tooltipData: any; + }) => ( + + {children} + {layout.renderXAxis()} + {layout.renderYAxis()} + + d.y === tooltipData.datum.y ? d.parent.color : '#fff' + } + circleSize={(d: SeriesValue) => (d.y === tooltipData.datum.y ? 6 : 4)} + circleStroke={(d: SeriesValue) => + d.y === tooltipData.datum.y ? '#fff' : d.parent.color + } + circleStyles={CIRCLE_STYLE} + stroke="#ccc" + showCircle + showMultipleCircles + /> + + )} + + )); + } + + render() { + const { className, data, width, height, encoding } = this.props; + + this.encoder = new Encoder({ encoding }); + const renderLegend = this.encoder.hasLegend() + ? // eslint-disable-next-line react/jsx-props-no-multi-spaces + () => data={data} encoder={this.encoder} /> + : undefined; + + return ( + + ); + } +} + +export default LineChart; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/createTooltip.jsx b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/createTooltip.jsx deleted file mode 100644 index ebe3839be5..0000000000 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/createTooltip.jsx +++ /dev/null @@ -1,5 +0,0 @@ -import React from 'react'; - -export default function createTooltip() { - return () =>
tooltip!
; -} 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 new file mode 100644 index 0000000000..cf4c01d9ed --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/createTooltip.tsx @@ -0,0 +1,50 @@ +/* eslint-disable no-magic-numbers */ + +import React from 'react'; +import TooltipFrame from '../components/tooltip/TooltipFrame'; +import TooltipTable from '../components/tooltip/TooltipTable'; +import { Series, SeriesValue } from './Line'; +import Encoder from './Encoder'; + +export default function createTooltip(encoder: Encoder, allSeries: Series[]) { + function LineTooltip({ + datum, + series = {}, + }: { + datum: SeriesValue; + series: { + [key: string]: { + y: number; + }; + }; + }) { + return ( + + +
+ {encoder.channels.x.formatValue(datum.x)} +
+
+ {series && ( + series[key]) + .concat() + .sort((a, b) => series[b.key].y - series[a.key].y) + .map(({ key, color }) => ({ + key, + keyStyle: { + color, + fontWeight: series[key] === datum ? 600 : 200, + }, + value: encoder.channels.y.formatValue(series[key].y), + }))} + /> + )} +
+
+ ); + } + + return LineTooltip; +} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/index.js b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/index.js deleted file mode 100644 index 30953c12aa..0000000000 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/index.js +++ /dev/null @@ -1,39 +0,0 @@ -/** - * 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 { t } from '@superset-ui/translation'; -import { ChartMetadata, ChartPlugin } from '@superset-ui/chart'; -import transformProps from './transformProps'; -import thumbnail from './images/thumbnail.png'; - -const metadata = new ChartMetadata({ - description: '', - name: t('Box Plot'), - thumbnail, - useLegacyApi: true, -}); - -export default class LineChartPlugin extends ChartPlugin { - constructor() { - super({ - loadChart: () => import('./Line'), - metadata, - transformProps, - }); - } -} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/index.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/index.ts new file mode 100644 index 0000000000..1acc16a916 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/index.ts @@ -0,0 +1,13 @@ +import { ChartPlugin } from '@superset-ui/chart'; +import metadata from './metadata'; +import transformProps from './transformProps'; + +export default class LineChartPlugin extends ChartPlugin { + constructor() { + super({ + loadChart: () => import('./Line'), + metadata, + transformProps, + }); + } +} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/legacy/index.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/legacy/index.ts new file mode 100644 index 0000000000..1cd56c6be6 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/legacy/index.ts @@ -0,0 +1,13 @@ +import { ChartPlugin } from '@superset-ui/chart'; +import metadata from './metadata'; +import transformProps from './transformProps'; + +export default class LineChartPlugin extends ChartPlugin { + constructor() { + super({ + loadChart: () => import('../Line'), + metadata, + transformProps, + }); + } +} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/legacy/metadata.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/legacy/metadata.ts new file mode 100644 index 0000000000..2f532e6a23 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/legacy/metadata.ts @@ -0,0 +1,6 @@ +import metadata from '../metadata'; + +const legacyMetadata = metadata.clone(); +legacyMetadata.useLegacyApi = true; + +export default legacyMetadata; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/legacy/transformProps.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/legacy/transformProps.ts new file mode 100644 index 0000000000..a9e07df784 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/legacy/transformProps.ts @@ -0,0 +1,67 @@ +/* eslint-disable sort-keys */ +import { ChartProps } from '@superset-ui/chart'; +import { flatMap } from 'lodash'; + +interface DataRow { + key: string[]; + values: { + x: number; + y: number; + }[]; +} + +export default function transformProps(chartProps: ChartProps) { + const { width, height, formData, payload } = chartProps; + const { colorScheme, xAxisLabel, xAxisFormat, yAxisLabel, yAxisFormat } = formData; + const data = payload.data as DataRow[]; + + return { + data: { + keys: ['name', 'x', 'y'], + values: flatMap( + data.map((row: DataRow) => + row.values.map(v => ({ + ...v, + name: row.key[0], + })), + ), + ), + }, + width, + height, + encoding: { + x: { + field: 'x', + type: 'temporal', + format: xAxisFormat, + scale: { + type: 'time', + }, + axis: { + orient: 'bottom', + title: xAxisLabel, + }, + }, + y: { + field: 'y', + type: 'quantitative', + format: yAxisFormat, + scale: { + type: 'linear', + }, + axis: { + orient: 'left', + title: yAxisLabel, + }, + }, + color: { + field: 'name', + type: 'nominal', + scale: { + scheme: colorScheme, + }, + legend: true, + }, + }, + }; +} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/metadata.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/metadata.ts new file mode 100644 index 0000000000..651c7709cc --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/metadata.ts @@ -0,0 +1,11 @@ +import { t } from '@superset-ui/translation'; +import { ChartMetadata } from '@superset-ui/chart'; +import thumbnail from './images/thumbnail.png'; + +const metadata = new ChartMetadata({ + description: '', + name: t('Line Chart'), + thumbnail, +}); + +export default metadata; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/transformProps.js b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/transformProps.js deleted file mode 100644 index d9cd8f0a47..0000000000 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/transformProps.js +++ /dev/null @@ -1,47 +0,0 @@ -import { getNumberFormatter } from '@superset-ui/number-format'; -import { getTimeFormatter } from '@superset-ui/time-format'; - -/* eslint-disable sort-keys */ - -export default function transformProps(chartProps) { - const { width, height, formData, payload } = chartProps; - const { colorScheme, xAxisLabel, xAxisFormat, yAxisLabel, yAxisFormat } = formData; - - return { - data: payload.data, - width, - height, - encoding: { - x: { - accessor: d => d.x, - scale: { - type: 'time', - }, - axis: { - orientation: 'bottom', - label: xAxisLabel, - numTicks: 5, - tickFormat: getTimeFormatter(xAxisFormat), - }, - }, - y: { - accessor: d => d.y, - scale: { - type: 'linear', - }, - axis: { - orientation: 'left', - label: yAxisLabel, - tickFormat: getNumberFormatter(yAxisFormat), - }, - }, - color: { - accessor: d => d.key.join('/'), - scale: { - scheme: colorScheme, - }, - legend: true, - }, - }, - }; -} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/transformProps.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/transformProps.ts new file mode 100644 index 0000000000..d75251efac --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/Line/transformProps.ts @@ -0,0 +1,16 @@ +import { ChartProps } from '@superset-ui/chart'; + +/* eslint-disable sort-keys */ + +export default function transformProps(chartProps: ChartProps) { + const { width, height, formData, payload } = chartProps; + const { encoding } = formData; + const { data } = payload; + + return { + data, + width, + height, + encoding, + }; +} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/components/ChartFrame.tsx b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/components/ChartFrame.tsx index 29974570bf..6165ade37d 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/components/ChartFrame.tsx +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/components/ChartFrame.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { PureComponent } from 'react'; import { isDefined } from '@superset-ui/core'; function checkNumber(input: any): input is number { @@ -9,11 +9,11 @@ type Props = { contentWidth?: number; contentHeight?: number; height: number; - renderContent: ({ height, width }: { height: number; width: number }) => React.ReactElement; + renderContent: ({ height, width }: { height: number; width: number }) => React.ReactNode; width: number; }; -export default class ChartFrame extends React.PureComponent { +export default class ChartFrame extends PureComponent { static defaultProps = { renderContent() {}, }; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/components/ChartLegend.tsx b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/components/ChartLegend.tsx new file mode 100644 index 0000000000..78b4277f92 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/components/ChartLegend.tsx @@ -0,0 +1,91 @@ +import React, { CSSProperties, PureComponent } from 'react'; +import { scaleOrdinal } from '@vx/scale'; +import { LegendOrdinal, LegendItem, LegendLabel } from '@vx/legend'; +import { Value } from 'vega-lite/build/src/fielddef'; +import AbstractEncoder from '../encodeable/AbstractEncoder'; +import { Dataset } from '../encodeable/types/Data'; +import { ObjectWithKeysFromAndValueType } from '../encodeable/types/Base'; +import { ChannelType, EncodingFromChannelsAndOutputs } from '../encodeable/types/Channel'; +import { BaseOptions } from '../encodeable/types/Specification'; + +type Props = { + data: Dataset; + encoder: Encoder; +}; + +interface Label { + text: string; + value: string; +} + +const MARK_SIZE = 8; + +const MARK_STYLE: CSSProperties = { display: 'inline-block' }; + +const LABEL_STYLE: CSSProperties = { display: 'flex', flexDirection: 'row', flexWrap: 'wrap' }; + +const LEGEND_CONTAINER_STYLE: CSSProperties = { + maxHeight: 100, + overflowY: 'hidden', + paddingLeft: 14, + paddingTop: 6, + position: 'relative', +}; + +export default class ChartLegend< + ChannelTypes extends ObjectWithKeysFromAndValueType, + Outputs extends ObjectWithKeysFromAndValueType, + Encoding extends EncodingFromChannelsAndOutputs< + ChannelTypes, + Outputs + > = EncodingFromChannelsAndOutputs, + Options extends BaseOptions = BaseOptions +> extends PureComponent>, {}> { + render() { + const { data, encoder } = this.props; + + const legends = Object.keys(encoder.legends).map((field: string) => { + const channelNames = encoder.legends[field]; + const channelEncoder = encoder.channels[channelNames[0]]; + const domain = Array.from(new Set(data.values.map(channelEncoder.get))); + const scale = scaleOrdinal({ + domain, + range: domain.map((key: string) => channelEncoder.encodeValue(key)), + }); + + return ( +
+ + {(labels: Label[]) => ( +
+ {labels.map((label: Label) => ( + { + alert(`clicked: ${JSON.stringify(label)}`); + }} + > + + + + + {label.text} + + + ))} +
+ )} +
+
+ ); + }); + + return {legends}; + } +} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/components/WithLegend.tsx b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/components/WithLegend.tsx index bac3829925..5e3cf6c4a6 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/components/WithLegend.tsx +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/components/WithLegend.tsx @@ -1,27 +1,17 @@ -/** - * 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 */ -import React, { CSSProperties, ReactNode } from 'react'; +import React, { CSSProperties, ReactNode, PureComponent } from 'react'; import { ParentSize } from '@vx/responsive'; // eslint-disable-next-line import/no-unresolved import * as CSS from 'csstype'; +const defaultProps = { + className: '', + height: 'auto' as number | string, + width: 'auto' as number | string, + legendJustifyContent: undefined, + position: 'top', +}; + type Props = { className: string; width: number | string; @@ -29,35 +19,27 @@ type Props = { legendJustifyContent: 'center' | 'flex-start' | 'flex-end'; position: 'top' | 'left' | 'bottom' | 'right'; renderChart: (dim: { width: number; height: number }) => ReactNode; - renderLegend: (params: { direction: string }) => ReactNode; - hideLegend: boolean; -}; + renderLegend?: (params: { direction: string }) => ReactNode; +} & Readonly; const LEGEND_STYLE_BASE: CSSProperties = { display: 'flex', flexGrow: 0, flexShrink: 0, + fontSize: '0.9em', order: -1, paddingTop: '5px', - fontSize: '0.9em', }; const CHART_STYLE_BASE: CSSProperties = { + flexBasis: 'auto', flexGrow: 1, flexShrink: 1, - flexBasis: 'auto', position: 'relative', }; -class WithLegend extends React.PureComponent { - static defaultProps = { - className: '', - width: 'auto', - height: 'auto', - legendJustifyContent: undefined, - position: 'top', - hideLegend: false, - }; +class WithLegend extends PureComponent { + static defaultProps = defaultProps; getContainerDirection(): CSS.FlexDirectionProperty { const { position } = this.props; @@ -93,15 +75,7 @@ class WithLegend extends React.PureComponent { } render() { - const { - className, - width, - height, - position, - renderChart, - renderLegend, - hideLegend, - } = this.props; + const { className, width, height, position, renderChart, renderLegend } = this.props; const isHorizontal = position === 'left' || position === 'right'; @@ -132,7 +106,7 @@ class WithLegend extends React.PureComponent { return (
- {!hideLegend && ( + {renderLegend && (
{renderLegend({ // Pass flexDirection for @vx/legend to arrange legend items diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/components/tooltip/TooltipFrame.tsx b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/components/tooltip/TooltipFrame.tsx index a37df9ab38..e4e3857959 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/components/tooltip/TooltipFrame.tsx +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/components/tooltip/TooltipFrame.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { PureComponent } from 'react'; type Props = { className?: string; @@ -7,7 +7,7 @@ type Props = { const CONTAINER_STYLE = { padding: 8 }; -class TooltipFrame extends React.PureComponent { +class TooltipFrame extends PureComponent { static defaultProps = { className: '', }; 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 aeee7f88ad..6d7ddfa5ee 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 @@ -1,4 +1,4 @@ -import React, { CSSProperties } from 'react'; +import React, { CSSProperties, PureComponent } from 'react'; type Props = { className?: string; @@ -12,7 +12,7 @@ type Props = { const VALUE_CELL_STYLE: CSSProperties = { paddingLeft: 8, textAlign: 'right' }; -export default class TooltipTable extends React.PureComponent { +export default class TooltipTable extends PureComponent { static defaultProps = { className: '', data: [], diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/AbstractEncoder.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/AbstractEncoder.ts index 6e041dfc79..4be464c009 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/AbstractEncoder.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/AbstractEncoder.ts @@ -1,52 +1,80 @@ import { Value } from 'vega-lite/build/src/fielddef'; -import ChannelEncoder from './ChannelEncoder'; -import { ChannelOptions } from './types/Channel'; -import { ChannelDef, isFieldDef } from './types/FieldDef'; +import { ObjectWithKeysFromAndValueType } from './types/Base'; +import { ChannelOptions, EncodingFromChannelsAndOutputs, ChannelType } from './types/Channel'; import { FullSpec, BaseOptions, PartialSpec } from './types/Specification'; - -export type ObjectWithKeysFromAndValueType = { [key in keyof T]: V }; - -export type ChannelOutputs = ObjectWithKeysFromAndValueType; - -export type BaseEncoding> = { - [key in keyof Output]: ChannelDef -}; - -export type Channels< - Outputs extends ChannelOutputs, - Encoding extends BaseEncoding -> = { readonly [k in keyof Outputs]: ChannelEncoder }; +import { isFieldDef } from './types/FieldDef'; +import ChannelEncoder from './ChannelEncoder'; export default abstract class AbstractEncoder< - Outputs extends ChannelOutputs, - Encoding extends BaseEncoding, + // The first 3 generics depends on each other + // to ensure all of them will have the exact same keys + ChannelTypes extends ObjectWithKeysFromAndValueType, + Outputs extends ObjectWithKeysFromAndValueType, + Encoding extends EncodingFromChannelsAndOutputs< + ChannelTypes, + Outputs + > = EncodingFromChannelsAndOutputs, Options extends BaseOptions = BaseOptions > { + readonly channelTypes: ChannelTypes; readonly spec: FullSpec; - readonly channels: Channels; - - readonly legends: { - [key: string]: (keyof Encoding)[]; + readonly channels: { + readonly [k in keyof ChannelTypes]: ChannelEncoder }; - constructor(spec: PartialSpec, defaultEncoding?: Encoding) { + readonly legends: { + [key: string]: (keyof ChannelTypes)[]; + }; + + constructor( + channelTypes: ChannelTypes, + spec: PartialSpec, + defaultEncoding?: Encoding, + channelOptions: Partial<{ [k in keyof ChannelTypes]: ChannelOptions }> = {}, + ) { + this.channelTypes = channelTypes; this.spec = this.createFullSpec(spec, defaultEncoding); - this.channels = this.createChannels(); - this.legends = {}; + + type ChannelName = keyof ChannelTypes; + type Channels = { readonly [k in ChannelName]: ChannelEncoder }; + + const channelNames = Object.keys(this.channelTypes) as ChannelName[]; + + const { encoding } = this.spec; + this.channels = channelNames + .map( + (name: ChannelName) => + new ChannelEncoder({ + definition: encoding[name], + name, + options: { + ...this.spec.options, + ...channelOptions[name], + }, + type: channelTypes[name], + }), + ) + .reduce((prev: Partial, curr) => { + const all = prev; + all[curr.name as ChannelName] = curr; + + return all; + }, {}) as Channels; // Group the channels that use the same field together // so they can share the same legend. - (Object.keys(this.channels) as (keyof Encoding)[]) - .map((key: keyof Encoding) => this.channels[key]) + this.legends = {}; + channelNames + .map((name: ChannelName) => this.channels[name]) .filter(c => c.hasLegend()) .forEach(c => { if (isFieldDef(c.definition)) { - const key = c.name as keyof Encoding; + const name = c.name as ChannelName; const { field } = c.definition; if (this.legends[field]) { - this.legends[field].push(key); + this.legends[field].push(name); } else { - this.legends[field] = [key]; + this.legends[field] = [name]; } } }); @@ -71,27 +99,6 @@ export default abstract class AbstractEncoder< }; } - protected createChannel( - name: ChannelName, - options?: ChannelOptions, - ) { - const { encoding } = this.spec; - - return new ChannelEncoder( - `${name}`, - encoding[name], - { - ...this.spec.options, - ...options, - }, - ); - } - - /** - * subclass should override this - */ - protected abstract createChannels(): Channels; - hasLegend() { return Object.keys(this.legends).length > 0; } diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/AxisAgent.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/AxisAgent.ts new file mode 100644 index 0000000000..0c60aeef71 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/AxisAgent.ts @@ -0,0 +1,177 @@ +/* eslint-disable no-magic-numbers */ +import { CSSProperties } from 'react'; +import { Value } from 'vega-lite/build/src/fielddef'; +import { getTextDimension } from '@superset-ui/dimension'; +import { CategoricalColorScale } from '@superset-ui/color'; +import { extractFormatFromTypeAndFormat } from './parsers/extractFormat'; +import { CoreAxis, LabelOverlapStrategy } from './types/Axis'; +import { PositionFieldDef, ChannelDef } from './types/FieldDef'; +import ChannelEncoder from './ChannelEncoder'; +import { DEFAULT_LABEL_ANGLE } from '../utils/constants'; + +const DEFAULT_BASE_CONFIG: { + labelOverlap: LabelOverlapStrategy; + labelPadding: number; + tickCount: number; +} = { + labelOverlap: 'auto', + labelPadding: 4, + tickCount: 5, +}; + +const DEFAULT_X_CONFIG: CoreAxis = { + ...DEFAULT_BASE_CONFIG, + labelAngle: DEFAULT_LABEL_ANGLE, + orient: 'bottom', +}; + +const DEFAULT_Y_CONFIG: CoreAxis = { + ...DEFAULT_BASE_CONFIG, + labelAngle: 0, + orient: 'left', +}; + +export default class AxisAgent, Output extends Value = Value> { + private readonly channelEncoder: ChannelEncoder; + private readonly format?: (value: any) => string; + readonly config: CoreAxis; + + constructor(channelEncoder: ChannelEncoder) { + this.channelEncoder = channelEncoder; + const definition = channelEncoder.definition as PositionFieldDef; + const { type, axis = {} } = definition; + + this.config = this.channelEncoder.isX() + ? { ...DEFAULT_X_CONFIG, ...axis } + : { ...DEFAULT_Y_CONFIG, ...axis }; + + if (typeof axis.format !== 'undefined') { + this.format = extractFormatFromTypeAndFormat(type, axis.format); + } + } + + getFormat() { + return this.format || this.channelEncoder.formatValue; + } + + getTitle() { + return this.config.title || this.channelEncoder.getTitle(); + } + + getTickLabels() { + const { tickCount, values } = this.config; + + const format = this.getFormat(); + if (typeof values !== 'undefined') { + return (values as any[]).map(format); + } + + if (typeof this.channelEncoder.scale !== 'undefined') { + const { scale } = this.channelEncoder.scale; + if (typeof scale !== 'undefined' && !(scale instanceof CategoricalColorScale)) { + return ('ticks' in scale && typeof scale.ticks !== 'undefined' + ? scale.ticks(tickCount) + : scale.domain() + ).map(format); + } + } + + return []; + } + + computeLayout({ + axisLabelHeight = 20, + axisWidth, + gapBetweenAxisLabelAndBorder = 8, + gapBetweenTickAndTickLabel = 4, + labelAngle = this.config.labelAngle, + tickLength, + tickTextStyle, + }: { + axisLabelHeight?: number; + axisWidth: number; + gapBetweenAxisLabelAndBorder?: number; + gapBetweenTickAndTickLabel?: number; + labelAngle?: number; + tickLength: number; + tickTextStyle: CSSProperties; + }) { + const tickLabels = this.getTickLabels(); + + const labelDimensions = tickLabels.map((text: string) => + getTextDimension({ + style: tickTextStyle, + text, + }), + ); + + const { labelOverlap, labelPadding, orient } = this.config; + + const maxWidth = Math.max(...labelDimensions.map(d => d.width)); + + // TODO: Add other strategies: stagger, chop, wrap. + let strategy = labelOverlap; + if (strategy === 'auto') { + // cheap heuristic, can improve + const widthPerTick = axisWidth / tickLabels.length; + if (this.channelEncoder.isY() || maxWidth <= widthPerTick) { + strategy = 'flat'; + } else { + strategy = 'rotate'; + } + } + + if (this.channelEncoder.isX()) { + let labelOffset = 0; + let layout: { + labelAngle: number; + tickTextAnchor?: string; + } = { labelAngle }; + + if (strategy === 'flat') { + labelOffset = labelDimensions[0].height + labelPadding; + layout = { labelAngle: 0 }; + } else if (strategy === 'rotate') { + const labelHeight = Math.ceil(Math.abs(maxWidth * Math.sin((labelAngle * Math.PI) / 180))); + labelOffset = labelHeight + labelPadding; + layout = { + labelAngle, + tickTextAnchor: + (orient === 'top' && labelAngle > 0) || (orient === 'bottom' && labelAngle < 0) + ? 'end' + : 'start', + }; + } + + return { + ...layout, + labelOffset, + labelOverlap: strategy, + minMargin: { + [orient]: Math.ceil( + tickLength + + gapBetweenTickAndTickLabel + + labelOffset + + axisLabelHeight + + gapBetweenAxisLabelAndBorder + + 8, + ), + }, + orient, + }; + } + + const labelOffset = Math.ceil(maxWidth + labelPadding + axisLabelHeight); + + return { + labelAngle, + labelOffset, + labelOverlap, + minMargin: { + [orient]: + tickLength + gapBetweenTickAndTickLabel + labelOffset + gapBetweenAxisLabelAndBorder, + }, + orient, + }; + } +} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/ChannelEncoder.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/ChannelEncoder.ts index 6e5d989042..b2fbb95482 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/ChannelEncoder.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/ChannelEncoder.ts @@ -1,76 +1,77 @@ import { Value } from 'vega-lite/build/src/fielddef'; -import { CategoricalColorScale } from '@superset-ui/color'; -import { ScaleOrdinal } from 'd3-scale'; -import { TimeFormatter } from '@superset-ui/time-format'; -import { NumberFormatter } from '@superset-ui/number-format'; +import { extractFormatFromChannelDef } from './parsers/extractFormat'; +import extractScale, { ScaleAgent } from './parsers/extractScale'; +import extractGetter from './parsers/extractGetter'; +import { ChannelOptions, ChannelType } from './types/Channel'; +import { PlainObject } from './types/Data'; import { ChannelDef, - Formatter, isScaleFieldDef, isMarkPropFieldDef, isValueDef, + isFieldDef, + isNonValueDef, } from './types/FieldDef'; -import { PlainObject } from './types/Data'; -import extractScale from './parsers/extractScale'; -import extractGetter from './parsers/extractGetter'; -import extractFormat from './parsers/extractFormat'; -import extractAxis, { isXYChannel } from './parsers/extractAxis'; import isEnabled from './utils/isEnabled'; import isDisabled from './utils/isDisabled'; -import { ChannelOptions } from './types/Channel'; import identity from './utils/identity'; +import AxisAgent from './AxisAgent'; export default class ChannelEncoder, Output extends Value = Value> { - readonly name: string; + readonly name: string | Symbol | number; + readonly type: ChannelType; readonly definition: Def; readonly options: ChannelOptions; - readonly axis?: PlainObject; - protected readonly getValue: (datum: PlainObject) => Value; - readonly scale?: ScaleOrdinal | CategoricalColorScale | ((x: any) => Output); - readonly formatter: Formatter; - - readonly encodeValue: (value: any) => Output; + protected readonly getValue: (datum: PlainObject) => Value | undefined; + readonly encodeValue: (value: any) => Output | null | undefined; readonly formatValue: (value: any) => string; + readonly scale?: ScaleAgent; + readonly axis?: AxisAgent; - constructor(name: string, definition: Def, options: ChannelOptions = {}) { + constructor({ + name, + type, + definition, + options = {}, + }: { + name: string | Symbol | number; + type: ChannelType; + definition: Def; + options?: ChannelOptions; + }) { this.name = name; + this.type = type; this.definition = definition; this.options = options; this.getValue = extractGetter(definition); + this.formatValue = extractFormatFromChannelDef(definition); - const formatter = extractFormat(definition); - this.formatter = formatter; - if (formatter instanceof NumberFormatter) { - this.formatValue = (value: any) => formatter(value); - } else if (formatter instanceof TimeFormatter) { - this.formatValue = (value: any) => formatter(value); - } else { - this.formatValue = formatter; + this.scale = extractScale(this.type, definition, options.namespace); + // Has to extract axis after format and scale + if ( + this.isXY() && + isNonValueDef(this.definition) && + (('axis' in this.definition && isEnabled(this.definition.axis)) || + !('axis' in this.definition)) + ) { + this.axis = new AxisAgent(this); } - const scale = extractScale(definition, options.namespace); - this.scale = scale; - if (typeof scale === 'undefined') { - this.encodeValue = identity; - } else if (scale instanceof CategoricalColorScale) { - this.encodeValue = (value: any) => scale(`${value}`); - } else { - this.encodeValue = (value: any) => scale(value); - } - - this.axis = extractAxis(name, definition, this.formatter); - } - - get(datum: PlainObject, otherwise?: any) { - const value = this.getValue(datum); - - return otherwise !== undefined && (value === null || value === undefined) ? otherwise : value; + this.encodeValue = this.scale ? this.scale.encodeValue : identity; + this.encode = this.encode.bind(this); + this.format = this.format.bind(this); + this.get = this.get.bind(this); } encode(datum: PlainObject, otherwise?: Output) { - const output = this.encodeValue(this.get(datum)); + const value = this.get(datum); + if (value === null || value === undefined) { + return value; + } + + const output = this.encodeValue(value); return otherwise !== undefined && (output === null || output === undefined) ? otherwise @@ -81,14 +82,22 @@ export default class ChannelEncoder, Output exten return this.formatValue(this.get(datum)); } + get(datum: PlainObject, otherwise?: any) { + const value = this.getValue(datum); + + return otherwise !== undefined && (value === null || value === undefined) ? otherwise : value; + } + + getTitle() { + if (isFieldDef(this.definition)) { + return this.definition.title || this.definition.field; + } + + return undefined; + } + hasLegend() { - if (isDisabled(this.options.legend)) { - return false; - } - if (isXYChannel(this.name)) { - return false; - } - if (isValueDef(this.definition)) { + if (isDisabled(this.options.legend) || this.isXY() || isValueDef(this.definition)) { return false; } if (isMarkPropFieldDef(this.definition)) { @@ -97,4 +106,16 @@ export default class ChannelEncoder, Output exten return isScaleFieldDef(this.definition); } + + isX() { + return this.type === 'X' || this.type === 'XBand'; + } + + isXY() { + return this.type === 'X' || this.type === 'Y' || this.type === 'XBand' || this.type === 'YBand'; + } + + isY() { + return this.type === 'Y' || this.type === 'YBand'; + } } diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/parsers/extractAxis.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/parsers/extractAxis.ts deleted file mode 100644 index 358871709d..0000000000 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/parsers/extractAxis.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { cloneDeep } from 'lodash'; -import { Axis } from 'vega-lite/build/src/axis'; -import { ChannelDef, isPositionFieldDef, Formatter } from '../types/FieldDef'; -import extractFormat from './extractFormat'; -import { PlainObject } from '../types/Data'; - -export function isXYChannel(channelName: string) { - return channelName === 'x' || channelName === 'y'; -} - -function isAxis(axis: Axis | null | undefined | false): axis is Axis { - return axis !== false && axis !== null && axis !== undefined; -} - -export default function extractAxis( - channelName: string, - definition: ChannelDef, - defaultFormatter: Formatter, -) { - if (isXYChannel(channelName) && isPositionFieldDef(definition)) { - const { type, axis } = definition; - if (isAxis(axis)) { - const parsedAxis: PlainObject = cloneDeep(axis); - const { labels } = parsedAxis; - const { format } = labels; - parsedAxis.format = format - ? extractFormat({ field: definition.field, format: axis.format, type }) - : defaultFormatter; - - return parsedAxis; - } - } - - return undefined; -} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/parsers/extractFormat.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/parsers/extractFormat.ts index fedcc8e391..996bd281d1 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/parsers/extractFormat.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/parsers/extractFormat.ts @@ -1,20 +1,32 @@ import { getNumberFormatter } from '@superset-ui/number-format'; import { getTimeFormatter } from '@superset-ui/time-format'; +import { Type } from 'vega-lite/build/src/type'; import { isTypedFieldDef, ChannelDef } from '../types/FieldDef'; -export default function extractFormat(definition: ChannelDef) { +const fallbackFormatter = (v: any) => `${v}`; + +export function extractFormatFromTypeAndFormat(type: Type, format: string) { + if (type === 'quantitative') { + const formatter = getNumberFormatter(format); + + return (value: any) => formatter(value); + } else if (type === 'temporal') { + const formatter = getTimeFormatter(format); + + return (value: any) => formatter(value); + } + + return fallbackFormatter; +} + +export function extractFormatFromChannelDef(definition: ChannelDef) { if (isTypedFieldDef(definition)) { const { type } = definition; const format = 'format' in definition && definition.format !== undefined ? definition.format : ''; - switch (type) { - case 'quantitative': - return getNumberFormatter(format); - case 'temporal': - return getTimeFormatter(format); - default: - } + + return extractFormatFromTypeAndFormat(type, format); } - return (v: any) => `${v}`; + return fallbackFormatter; } diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/parsers/extractScale.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/parsers/extractScale.ts index 8be76db4fa..9de493a7e5 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/parsers/extractScale.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/parsers/extractScale.ts @@ -1,38 +1,246 @@ -import { CategoricalColorNamespace } from '@superset-ui/color'; -import { scaleOrdinal } from 'd3-scale'; +import { CategoricalColorNamespace, CategoricalColorScale } from '@superset-ui/color'; +import { + ScaleOrdinal, + ScaleLinear, + ScaleLogarithmic, + ScalePower, + ScaleTime, + ScaleQuantile, + ScaleQuantize, + ScaleThreshold, + ScalePoint, + ScaleBand, + scaleLinear, + scaleLog, + scalePow, + scaleSqrt, + scaleTime, + scaleUtc, + scaleQuantile, + scaleQuantize, + scaleThreshold, + scaleOrdinal, + scalePoint, + scaleBand, +} from 'd3-scale'; import { Value } from 'vega-lite/build/src/fielddef'; -import isEnabled from '../utils/isEnabled'; -import { isScaleFieldDef, ChannelDef, isPositionFieldDef } from '../types/FieldDef'; +import { Type } from 'vega-lite/build/src/type'; +import { ScaleType } from 'vega-lite/build/src/scale'; +import { isNonValueDef, ChannelDef } from '../types/FieldDef'; +import isDisabled from '../utils/isDisabled'; +import { ChannelType } from '../types/Channel'; +import { Scale } from '../types/Scale'; -export default function extractScale( - definition: ChannelDef, - namespace?: string, -) { - if (isScaleFieldDef(definition)) { - const { scale, type } = definition; - if (isEnabled(scale) && !isPositionFieldDef(definition)) { - if (scale) { - const { domain, range, scheme } = scale; - if (type === 'nominal') { - if (scheme) { - return CategoricalColorNamespace.getScale(scheme, namespace); - } +export interface ScaleAgent { + config: Scale; + setDomain: (newDomain: number[] | string[] | boolean[] | Date[]) => void; + encodeValue: (value: number | string | boolean | null | undefined | Date) => Output; + scale: + | CategoricalColorScale + | ScaleLinear + | ScaleLogarithmic + | ScalePower + | ScaleLogarithmic + | ScaleTime + | ScaleQuantile + | ScaleQuantize + | ScaleThreshold + | ScaleOrdinal<{ toString(): string }, Output> + | ScalePoint<{ toString(): string }> + | ScaleBand<{ toString(): string }>; +} - const scaleFn = scaleOrdinal(); - if (domain) { - scaleFn.domain(domain); - } - if (range) { - scaleFn.range(range); - } +export interface ScaleTypeToD3ScaleType { + [ScaleType.LINEAR]: ScaleLinear; + [ScaleType.LOG]: ScaleLogarithmic; + [ScaleType.POW]: ScalePower; + [ScaleType.SQRT]: ScalePower; + [ScaleType.SYMLOG]: ScaleLogarithmic; + [ScaleType.TIME]: ScaleTime; + [ScaleType.UTC]: ScaleTime; + [ScaleType.QUANTILE]: ScaleQuantile; + [ScaleType.QUANTIZE]: ScaleQuantize; + [ScaleType.THRESHOLD]: ScaleThreshold; + [ScaleType.BIN_ORDINAL]: ScaleOrdinal<{ toString(): string }, Output>; + [ScaleType.ORDINAL]: ScaleOrdinal<{ toString(): string }, Output>; + [ScaleType.POINT]: ScalePoint<{ toString(): string }>; + [ScaleType.BAND]: ScaleBand<{ toString(): string }>; +} - return scaleFn; - } - } else if (type === 'nominal') { - return CategoricalColorNamespace.getScale(undefined, namespace); - } +// eslint-disable-next-line complexity +export function deriveScaleTypeFromDataTypeAndChannelType( + dataType: Type | undefined, + channelType: ChannelType, + isBinned: boolean = false, +): ScaleType | undefined { + if (typeof dataType === 'undefined') { + return undefined; + } else if (dataType === 'nominal' || dataType === 'ordinal') { + switch (channelType) { + case 'XBand': + case 'YBand': + return ScaleType.POINT; + case 'X': + case 'Y': + case 'Numeric': + return ScaleType.POINT; + case 'Color': + case 'Category': + return ScaleType.ORDINAL; + default: + } + } else if (dataType === 'quantitative') { + switch (channelType) { + case 'XBand': + case 'YBand': + case 'X': + case 'Y': + case 'Numeric': + return ScaleType.LINEAR; + case 'Color': + return isBinned ? ScaleType.LINEAR : ScaleType.BIN_ORDINAL; + default: + } + } else if (dataType === 'temporal') { + switch (channelType) { + case 'XBand': + case 'YBand': + case 'X': + case 'Y': + case 'Numeric': + return ScaleType.TIME; + case 'Color': + return ScaleType.LINEAR; + default: } } return undefined; } + +// eslint-disable-next-line complexity +function createScaleFromType(type: ScaleType) { + switch (type) { + case ScaleType.LINEAR: + return scaleLinear(); + case ScaleType.LOG: + return scaleLog(); + case ScaleType.POW: + return scalePow(); + case ScaleType.SQRT: + return scaleSqrt(); + case ScaleType.SYMLOG: + return undefined; + case ScaleType.TIME: + return scaleTime(); + case ScaleType.UTC: + return scaleUtc(); + case ScaleType.QUANTILE: + return scaleQuantile(); + case ScaleType.QUANTIZE: + return scaleQuantize(); + case ScaleType.THRESHOLD: + return scaleThreshold(); + case ScaleType.BIN_ORDINAL: + return scaleOrdinal<{ toString(): string }, Output>(); + case ScaleType.ORDINAL: + return scaleOrdinal<{ toString(): string }, Output>(); + case ScaleType.POINT: + return scalePoint<{ toString(): string }>(); + case ScaleType.BAND: + return scaleBand<{ toString(): string }>(); + default: + return undefined; + } +} + +// eslint-disable-next-line complexity +function createScale( + channelType: ChannelType, + scaleType: ScaleType, + config: Scale, +) { + const { namespace } = config; + + if (channelType === 'Color') { + const { scheme } = config; + + return typeof scheme === 'string' || typeof scheme === 'undefined' + ? CategoricalColorNamespace.getScale(scheme, namespace) + : // TODO: fully use SchemeParams + CategoricalColorNamespace.getScale(scheme.name, namespace); + } + + const scale = createScaleFromType(scaleType); + + if (typeof scale !== 'undefined') { + if (scale.domain && typeof config.domain !== 'undefined') { + scale.domain(config.domain as any[]); + } + if (scale.range && typeof config.range !== 'undefined') { + scale.range(config.range as any[]); + } + if ('nice' in scale && scale.nice && config.nice !== false) { + scale.nice(); + } + if ( + 'clamp' in scale && + typeof scale.clamp !== 'undefined' && + typeof config.clamp !== 'undefined' + ) { + scale.clamp(config.clamp); + } + } + + return scale; +} + +export default function extractScale( + channelType: ChannelType, + definition: ChannelDef, + namespace?: string, +) { + if (isNonValueDef(definition)) { + const scaleConfig = + 'scale' in definition && typeof definition.scale !== 'undefined' ? definition.scale : {}; + + // return if scale is disabled + if (isDisabled(scaleConfig)) { + return undefined; + } + + let scaleType = scaleConfig.type; + + if (typeof scaleType === 'undefined') { + // If scale type is not defined, try to derive scale type from field type + const dataType = 'type' in definition ? definition.type : undefined; + scaleType = deriveScaleTypeFromDataTypeAndChannelType(dataType, channelType); + + // If still do not have scale type, cannot create scale + if (typeof scaleType === 'undefined') { + return undefined; + } + } + + const scale = createScale(channelType, scaleType, { namespace, ...scaleConfig }); + + if (scale) { + const setDomain = + scale instanceof CategoricalColorScale || typeof scale.domain === 'undefined' + ? () => {} + : scale.domain; + + return { + config: { ...scaleConfig, type: scaleType }, + encodeValue: (scale as unknown) as ( + value: number | string | boolean | null | undefined | Date, + ) => Output, + scale, + setDomain, + }; + } + } + + // ValueDef does not have scale + return undefined; +} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/types/Axis.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/types/Axis.ts index c64f1d0a6f..fbcf5f7cd5 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/types/Axis.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/types/Axis.ts @@ -1,22 +1,38 @@ -interface Axis { - title: string; +import { DateTime } from 'vega-lite/build/src/datetime'; +import { AxisOrient } from 'vega'; + +export type LabelOverlapStrategy = 'auto' | 'flat' | 'rotate'; + +export interface CoreAxis { + format?: string; + labelAngle: number; + labelOverlap: LabelOverlapStrategy; + /** The padding, in pixels, between axis and text labels. */ + labelPadding: number; + orient: AxisOrient; tickCount: number; - format: string; + title?: string; + /** Explicitly set the visible axis tick values. */ + values?: string[] | number[] | boolean[] | DateTime[]; } -export type XAxis = Axis & { - orient: 'top' | 'bottom'; - labelAngle: number; - labelOverlap: string; -}; +export type Axis = Partial; + +export interface XAxis extends Axis { + orient?: 'top' | 'bottom'; + labelAngle?: number; + labelOverlap?: LabelOverlapStrategy; +} export interface WithXAxis { axis?: XAxis; } -export type YAxis = Axis & { - orient: 'left' | 'right'; -}; +export interface YAxis extends Axis { + orient?: 'left' | 'right'; + labelAngle?: 0; + labelOverlap?: 'auto' | 'flat'; +} export interface WithYAxis { axis?: YAxis; @@ -25,3 +41,7 @@ export interface WithYAxis { export interface WithAxis { axis?: XAxis | YAxis; } + +export function isAxis(axis: Axis | null | undefined | false): axis is Axis { + return axis !== false && axis !== null && axis !== undefined; +} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/types/Base.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/types/Base.ts new file mode 100644 index 0000000000..a69ba6e59e --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/types/Base.ts @@ -0,0 +1,2 @@ +// eslint-disable-next-line import/prefer-default-export +export type ObjectWithKeysFromAndValueType = { [key in keyof T]: V }; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/types/Channel.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/types/Channel.ts index 8342892522..a680d7bb7c 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/types/Channel.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/types/Channel.ts @@ -1,5 +1,44 @@ +import { Value } from 'vega-lite/build/src/fielddef'; +import { XFieldDef, YFieldDef, ChannelDef, MarkPropChannelDef, TextChannelDef } from './FieldDef'; +import { ObjectWithKeysFromAndValueType } from './Base'; + // eslint-disable-next-line import/prefer-default-export export interface ChannelOptions { namespace?: string; legend?: boolean; } + +/** + * Define all channel types and mapping to available definition grammar + */ +export interface ChannelTypeToDefMap + extends ObjectWithKeysFromAndValueType, ChannelDef> { + // position on x-axis + X: XFieldDef; + // position on y-axis + Y: YFieldDef; + // position on x-axis but as a range, e.g., bar chart or heat map + XBand: XFieldDef; + // position on y-axis but as a range, e.g., bar chart or heat map + YBand: YFieldDef; + // numeric attributes of the mark, e.g., size, opacity + Numeric: MarkPropChannelDef; + // categorical attributes of the mark, e.g., color, visibility, shape + Category: MarkPropChannelDef; + // color of the mark + Color: MarkPropChannelDef; + // plain text, e.g., tooltip, key + Text: TextChannelDef; +} + +export type ChannelType = keyof ChannelTypeToDefMap; + +export type ChannelDefFromType< + T extends keyof ChannelTypeToDefMap, + Output extends Value +> = ChannelTypeToDefMap[T]; + +export type EncodingFromChannelsAndOutputs< + Channels extends ObjectWithKeysFromAndValueType, + Outputs extends ObjectWithKeysFromAndValueType +> = { [key in keyof Channels]: ChannelDefFromType }; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/types/FieldDef.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/types/FieldDef.ts index 7cce9f7fa9..6992f11218 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/types/FieldDef.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/types/FieldDef.ts @@ -14,6 +14,7 @@ export type Formatter = NumberFormatter | TimeFormatter | ((d: any) => string); export interface FieldDef { field: string; format?: string; + title?: string; } export interface TypedFieldDef extends FieldDef { @@ -22,8 +23,6 @@ export interface TypedFieldDef extends FieldDef { export type TextFieldDef = FieldDef; -// PropFieldDef is { field: 'fieldName', scale: xxx } - type ScaleFieldDef = TypedFieldDef & WithScale; export type MarkPropFieldDef = ScaleFieldDef & WithLegend; @@ -42,19 +41,26 @@ export type MarkPropChannelDef = | MarkPropFieldDef | ValueDef; -export type TextChannelDef = TextFieldDef | ValueDef; +export type TextChannelDef = TextFieldDef | ValueDef; -export type ChannelDef = +export type NonValueDef = | XFieldDef | YFieldDef | MarkPropFieldDef - | TextFieldDef - | ValueDef; + | TextFieldDef; + +export type ChannelDef = NonValueDef | ValueDef; export function isValueDef( channelDef: ChannelDef, ): channelDef is ValueDef { - return channelDef && 'value' in channelDef && !!channelDef.value; + return channelDef && 'value' in channelDef; +} + +export function isNonValueDef( + channelDef: ChannelDef, +): channelDef is NonValueDef { + return channelDef && !('value' in channelDef); } export function isFieldDef( diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/types/Scale.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/types/Scale.ts index 466ce07af7..6be3849767 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/types/Scale.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/encodeable/types/Scale.ts @@ -1,11 +1,16 @@ -import { ScaleType } from 'vega-lite/build/src/scale'; import { Value } from 'vega-lite/build/src/fielddef'; +import { DateTime } from 'vega-lite/build/src/datetime'; +import { SchemeParams, ScaleType } from 'vega-lite/build/src/scale'; export interface Scale { type?: ScaleType; - domain?: any[]; + domain?: number[] | string[] | boolean[] | DateTime[]; range?: Output[]; - scheme?: string; + clamp?: boolean; + nice?: boolean; + scheme?: string | SchemeParams; + // vega-lite does not have this + namespace?: string; } export interface WithScale { diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/legacy.js b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/legacy.js new file mode 100644 index 0000000000..6a9010bda4 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/legacy.js @@ -0,0 +1,2 @@ +/* eslint-disable import/prefer-default-export */ +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/src/types.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/types.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/XYChartLayout.jsx b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/XYChartLayout.jsx deleted file mode 100644 index 1099ebffa0..0000000000 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/XYChartLayout.jsx +++ /dev/null @@ -1,140 +0,0 @@ -/* eslint-disable sort-keys, no-magic-numbers */ - -import React from 'react'; -import collectScalesFromProps from '@data-ui/xy-chart/esm/utils/collectScalesFromProps'; -import { XAxis, YAxis } from '@data-ui/xy-chart'; -import adjustMargin from './adjustMargin'; -import computeXAxisLayout from './computeXAxisLayout'; -import computeYAxisLayout from './computeYAxisLayout'; -import createTickComponent from './createTickComponent'; -import getTickLabels from './getTickLabels'; -import ChartFrame from '../components/ChartFrame'; - -// Additional margin to avoid content hidden behind scroll bar -const OVERFLOW_MARGIN = 8; - -export default class XYChartLayout { - constructor(config) { - this.config = config; - - const { - width, - height, - minContentWidth = 0, - minContentHeight = 0, - margin, - encoding, - children, - theme, - } = config; - const { x, y } = encoding; - - const { xScale, yScale } = collectScalesFromProps({ - width, - height, - margin, - xScale: x.scale, - yScale: y.scale, - theme, - children, - }); - - const { axis: yAxis = {} } = y; - - const yLayout = computeYAxisLayout({ - orientation: yAxis.orientation, - tickLabels: getTickLabels(yScale, yAxis), - tickLength: theme.yTickStyles.length, - tickTextStyle: theme.yTickStyles.label.right, - }); - - const secondMargin = adjustMargin(margin, yLayout.minMargin); - const { left, right } = secondMargin; - - const { axis: xAxis = {} } = x; - - const { orientation: xOrientation = 'bottom' } = xAxis; - const AUTO_ROTATION = - (yLayout.orientation === 'right' && xOrientation === 'bottom') || - (yLayout.orientation === 'left' && xOrientation === 'top') - ? 40 - : -40; - const { rotation = AUTO_ROTATION } = xAxis; - - const innerWidth = Math.max(width - left - right, minContentWidth); - - const xLayout = computeXAxisLayout({ - axisWidth: innerWidth, - orientation: xOrientation, - rotation, - tickLabels: getTickLabels(xScale, xAxis), - tickLength: theme.xTickStyles.length, - tickTextStyle: theme.xTickStyles.label.bottom, - }); - - const finalMargin = adjustMargin(secondMargin, xLayout.minMargin); - const innerHeight = Math.max(height - finalMargin.top - finalMargin.bottom, minContentHeight); - - const chartWidth = Math.round(innerWidth + finalMargin.left + finalMargin.right); - const chartHeight = Math.round(innerHeight + finalMargin.top + finalMargin.bottom); - - const isOverFlowX = chartWidth > width; - const isOverFlowY = chartHeight > height; - if (isOverFlowX) { - finalMargin.bottom += OVERFLOW_MARGIN; - } - if (isOverFlowY) { - finalMargin.right += OVERFLOW_MARGIN; - } - - this.chartWidth = isOverFlowX ? chartWidth + OVERFLOW_MARGIN : chartWidth; - this.chartHeight = isOverFlowY ? chartHeight + OVERFLOW_MARGIN : chartHeight; - this.containerWidth = width; - this.containerHeight = height; - this.margin = finalMargin; - this.xLayout = xLayout; - this.yLayout = yLayout; - } - - createChartWithFrame(renderChart) { - return ( - - ); - } - - createXAxis(props) { - const { axis } = this.config.encoding.x; - - return ( - - ); - } - - createYAxis(props) { - const { axis } = this.config.encoding.y; - - return ( - - ); - } -} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/XYChartLayout.tsx b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/XYChartLayout.tsx new file mode 100644 index 0000000000..fd3f188c43 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/XYChartLayout.tsx @@ -0,0 +1,182 @@ +/* eslint-disable sort-keys, no-magic-numbers */ + +import React, { ReactNode } from 'react'; +import collectScalesFromProps from '@data-ui/xy-chart/esm/utils/collectScalesFromProps'; +import { XAxis, YAxis } from '@data-ui/xy-chart'; +import { ChartTheme } from '@data-ui/theme'; +import { Margin, mergeMargin } from '@superset-ui/dimension'; +import { AxisOrient } from 'vega'; +import createTickComponent from './createTickComponent'; +import ChartFrame from '../components/ChartFrame'; +import ChannelEncoder from '../encodeable/ChannelEncoder'; +import { XFieldDef, YFieldDef } from '../encodeable/types/FieldDef'; +import { PlainObject } from '../encodeable/types/Data'; +import { DEFAULT_LABEL_ANGLE } from './constants'; + +// Additional margin to avoid content hidden behind scroll bar +const OVERFLOW_MARGIN = 8; + +interface Input { + width: number; + height: number; + minContentWidth?: number; + minContentHeight?: number; + margin: Margin; + xEncoder: ChannelEncoder; + yEncoder: ChannelEncoder; + children: ReactNode[]; + theme: ChartTheme; +} + +export default class XYChartLayout { + chartWidth: number; + chartHeight: number; + containerWidth: number; + containerHeight: number; + margin: Margin; + spec: Input; + + xLayout?: { + labelOffset: number; + labelOverlap: string; + labelAngle: number; + tickTextAnchor?: string; + minMargin: Partial; + orient: AxisOrient; + }; + + yLayout?: { + labelOffset: number; + minMargin: Partial; + orient: AxisOrient; + }; + + // eslint-disable-next-line complexity + constructor(spec: Input) { + this.spec = spec; + + const { + width, + height, + minContentWidth = 0, + minContentHeight = 0, + margin, + xEncoder, + yEncoder, + children, + theme, + } = spec; + + const { xScale, yScale } = collectScalesFromProps({ + width, + height, + margin, + xScale: xEncoder.definition.scale || {}, + yScale: yEncoder.definition.scale || {}, + theme, + children, + }); + + if (typeof yEncoder.scale !== 'undefined') { + yEncoder.scale.setDomain(yScale.domain()); + } + if (typeof yEncoder.axis !== 'undefined') { + this.yLayout = yEncoder.axis.computeLayout({ + axisWidth: Math.max(height - margin.top - margin.bottom), + tickLength: theme.yTickStyles.length, + tickTextStyle: theme.yTickStyles.label.right, + }); + } + + const secondMargin = this.yLayout ? mergeMargin(margin, this.yLayout.minMargin) : margin; + const innerWidth = Math.max(width - secondMargin.left - secondMargin.right, minContentWidth); + + if (typeof xEncoder.scale !== 'undefined') { + xEncoder.scale.setDomain(xScale.domain()); + } + if (typeof xEncoder.axis !== 'undefined') { + this.xLayout = xEncoder.axis.computeLayout({ + axisWidth: innerWidth, + labelAngle: this.recommendXLabelAngle(xEncoder.axis.config.orient as 'top' | 'bottom'), + tickLength: theme.xTickStyles.length, + tickTextStyle: theme.xTickStyles.label.bottom, + }); + } + + const finalMargin = this.xLayout + ? mergeMargin(secondMargin, this.xLayout.minMargin) + : secondMargin; + const innerHeight = Math.max(height - finalMargin.top - finalMargin.bottom, minContentHeight); + + const chartWidth = Math.round(innerWidth + finalMargin.left + finalMargin.right); + const chartHeight = Math.round(innerHeight + finalMargin.top + finalMargin.bottom); + + const isOverFlowX = chartWidth > width; + const isOverFlowY = chartHeight > height; + if (isOverFlowX) { + finalMargin.bottom += OVERFLOW_MARGIN; + } + if (isOverFlowY) { + finalMargin.right += OVERFLOW_MARGIN; + } + this.chartWidth = isOverFlowX ? chartWidth + OVERFLOW_MARGIN : chartWidth; + this.chartHeight = isOverFlowY ? chartHeight + OVERFLOW_MARGIN : chartHeight; + this.containerWidth = width; + this.containerHeight = height; + this.margin = finalMargin; + } + + recommendXLabelAngle(xOrient: 'top' | 'bottom' = 'bottom') { + const { axis } = this.spec.yEncoder; + + return !this.yLayout || + (typeof axis !== 'undefined' && + ((axis.config.orient === 'right' && xOrient === 'bottom') || + (axis.config.orient === 'left' && xOrient === 'top'))) + ? DEFAULT_LABEL_ANGLE + : -DEFAULT_LABEL_ANGLE; + } + + renderChartWithFrame(renderChart: (input: { width: number; height: number }) => ReactNode) { + return ( + + ); + } + + renderXAxis(props?: PlainObject) { + const { axis } = this.spec.xEncoder; + + return axis && this.xLayout ? ( + + ) : null; + } + + renderYAxis(props?: PlainObject) { + const { axis } = this.spec.yEncoder; + + return axis && this.yLayout ? ( + + ) : null; + } +} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/adjustMargin.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/adjustMargin.ts deleted file mode 100644 index 4e92a2de71..0000000000 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/adjustMargin.ts +++ /dev/null @@ -1,26 +0,0 @@ -interface Margin { - top: number; - left: number; - bottom: number; - right: number; -} - -export default function adjustMargin( - baseMargin: Partial = {}, - minMargin: Partial = {}, -) { - const { top = 0, left = 0, bottom = 0, right = 0 } = baseMargin; - const { - top: minTop = 0, - left: minLeft = 0, - bottom: minBottom = 0, - right: minRight = 0, - } = minMargin; - - return { - bottom: Math.max(bottom, minBottom), - left: Math.max(left, minLeft), - right: Math.max(right, minRight), - top: Math.max(top, minTop), - }; -} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/computeChartLayout.jsx b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/computeChartLayout.jsx deleted file mode 100644 index ca55e5e660..0000000000 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/computeChartLayout.jsx +++ /dev/null @@ -1,147 +0,0 @@ -/* eslint-disable sort-keys, no-magic-numbers */ - -import React from 'react'; -import collectScalesFromProps from '@data-ui/xy-chart/esm/utils/collectScalesFromProps'; -import { XAxis, YAxis } from '@data-ui/xy-chart'; -import adjustMargin from './adjustMargin'; -import computeXAxisLayout from './computeXAxisLayout'; -import computeYAxisLayout from './computeYAxisLayout'; -import createTickComponent from './createTickComponent'; -import getTickLabels from './getTickLabels'; - -const OVERFLOW_MARGIN = 8; - -// { -// width, -// height, -// margin: -// encoding: { -// x: { -// scale: -// axis: { -// labellingStrategy: -// rotation: -// orientation: -// scaleConfig: -// tickFormat: -// tickValues: -// numTicks: -// } -// }, -// y: { -// scale: -// axis: { -// tickFormat: -// tickValues: -// numTicks: -// orientation: -// } -// }, -// } -// children: -// theme: -// } - -export default function computeChartLayout(config) { - const { - width, - height, - minContentWidth = 0, - minContentHeight = 0, - margin, - encoding, - children, - theme, - } = config; - const { x, y } = encoding; - - const { xScale, yScale } = collectScalesFromProps({ - width, - height, - margin, - xScale: x.scale, - yScale: y.scale, - theme, - children, - }); - - const { axis: yAxis = {} } = y; - - const yLayout = computeYAxisLayout({ - orientation: yAxis.orientation, - tickLabels: getTickLabels(yScale, y.axis), - tickLength: theme.xTickStyles.length, - tickTextStyle: theme.yTickStyles.label.right, - }); - - const secondMargin = adjustMargin(margin, yLayout.minMargin); - const { left, right } = secondMargin; - - const innerWidth = Math.max(width - left - right, minContentWidth); - - const { axis: xAxis = {} } = x; - const { orientation: xOrientation = 'bottom' } = xAxis; - const AUTO_ROTATION = - (yLayout.orientation === 'right' && xOrientation === 'bottom') || - (yLayout.orientation === 'left' && xOrientation === 'top') - ? 40 - : -40; - const { rotation = AUTO_ROTATION } = xAxis; - - const xLayout = computeXAxisLayout({ - axisWidth: innerWidth, - orientation: xOrientation, - rotation, - tickLabels: getTickLabels(xScale, x.axis), - tickLength: theme.xTickStyles.length, - tickTextStyle: theme.xTickStyles.label.bottom, - }); - - const finalMargin = adjustMargin(secondMargin, xLayout.minMargin); - const innerHeight = Math.max(height - finalMargin.top - finalMargin.bottom, minContentHeight); - - const createXAxis = props => ( - - ); - - const createYAxis = props => ( - - ); - - const chartWidth = Math.round(innerWidth + finalMargin.left + finalMargin.right); - const chartHeight = Math.round(innerHeight + finalMargin.top + finalMargin.bottom); - - const isOverFlowX = chartWidth > width; - const isOverFlowY = chartHeight > height; - if (isOverFlowX) { - finalMargin.bottom += OVERFLOW_MARGIN; - } - if (isOverFlowY) { - finalMargin.right += OVERFLOW_MARGIN; - } - - return { - chartWidth: isOverFlowX ? chartWidth + OVERFLOW_MARGIN : chartWidth, - chartHeight: isOverFlowY ? chartHeight + OVERFLOW_MARGIN : chartHeight, - containerWidth: width, - containerHeight: height, - margin: finalMargin, - x: xLayout, - y: yLayout, - createXAxis, - createYAxis, - }; -} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/computeXAxisLayout.js b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/computeXAxisLayout.js deleted file mode 100644 index 1997910fa2..0000000000 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/computeXAxisLayout.js +++ /dev/null @@ -1,75 +0,0 @@ -/* eslint-disable no-magic-numbers */ - -import { getTextDimension } from '@superset-ui/dimension'; - -export default function computeXAxisLayout({ - axisLabelHeight = 20, - axisWidth, - gapBetweenAxisLabelAndBorder = 8, - gapBetweenTickAndTickLabel = 4, - gapBetweenTickLabelsAndAxisLabel = 4, - labellingStrategy = 'auto', - orientation = 'bottom', - rotation = -40, - tickLabels, - tickLength, - tickTextStyle, -}) { - const labelDimensions = tickLabels.map(text => - getTextDimension({ - style: tickTextStyle, - text, - }), - ); - - const maxWidth = Math.max(...labelDimensions.map(d => d.width)); - // cheap heuristic, can improve - const widthPerTick = axisWidth / tickLabels.length; - - let finalStrategy; - if (labellingStrategy !== 'auto') { - finalStrategy = labellingStrategy; - } else if (maxWidth <= widthPerTick) { - finalStrategy = 'flat'; - } else { - finalStrategy = 'rotate'; - } - // TODO: Add other strategies: stagger, chop, wrap. - - let layout = { labelOffset: 0 }; - if (finalStrategy === 'flat') { - const labelHeight = labelDimensions[0].height; - const labelOffset = labelHeight + gapBetweenTickLabelsAndAxisLabel; - layout = { labelOffset }; - } else if (finalStrategy === 'rotate') { - const labelHeight = Math.ceil(Math.abs(maxWidth * Math.sin((rotation * Math.PI) / 180))); - const labelOffset = labelHeight + gapBetweenTickLabelsAndAxisLabel; - const tickTextAnchor = - (orientation === 'top' && rotation > 0) || (orientation === 'bottom' && rotation < 0) - ? 'end' - : 'start'; - layout = { - labelOffset, - rotation, - tickTextAnchor, - }; - } - - const { labelOffset } = layout; - - return { - ...layout, - labellingStrategy: finalStrategy, - minMargin: { - [orientation]: Math.ceil( - tickLength + - gapBetweenTickAndTickLabel + - labelOffset + - axisLabelHeight + - gapBetweenAxisLabelAndBorder + - 8, - ), - }, - orientation, - }; -} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/computeYAxisLayout.js b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/computeYAxisLayout.js deleted file mode 100644 index 948030e5cc..0000000000 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/computeYAxisLayout.js +++ /dev/null @@ -1,35 +0,0 @@ -/* eslint-disable no-magic-numbers */ - -import { getTextDimension } from '@superset-ui/dimension'; - -export default function computeYAxisLayout({ - axisLabelHeight = 20, - gapBetweenAxisLabelAndBorder = 8, - gapBetweenTickAndTickLabel = 4, - gapBetweenTickLabelsAndAxisLabel = 4, - orientation = 'left', - tickLabels, - tickLength, - tickTextStyle, -}) { - const labelDimensions = tickLabels.map(text => - getTextDimension({ - style: tickTextStyle, - text, - }), - ); - - const maxWidth = Math.ceil(Math.max(...labelDimensions.map(d => d.width))); - const labelOffset = Math.ceil(maxWidth + gapBetweenTickLabelsAndAxisLabel + axisLabelHeight); - - const margin = - tickLength + gapBetweenTickAndTickLabel + labelOffset + gapBetweenAxisLabelAndBorder; - - return { - labelOffset, - minMargin: { - [orientation]: margin, - }, - orientation, - }; -} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/constants.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/constants.ts new file mode 100644 index 0000000000..29b3214e3d --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/constants.ts @@ -0,0 +1,2 @@ +// eslint-disable-next-line import/prefer-default-export +export const DEFAULT_LABEL_ANGLE = 40; diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/createTickComponent.jsx b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/createTickComponent.jsx deleted file mode 100644 index ef6614f7ec..0000000000 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/createTickComponent.jsx +++ /dev/null @@ -1,46 +0,0 @@ -/* eslint-disable no-magic-numbers */ - -import React from 'react'; -import PropTypes from 'prop-types'; - -export default function createTickComponent({ - labellingStrategy, - orientation = 'bottom', - rotation = 40, - tickTextAnchor = 'start', -}) { - if (labellingStrategy === 'rotate' && rotation !== 0) { - let xOffset = rotation > 0 ? -6 : 6; - if (orientation === 'top') { - xOffset = 0; - } - const yOffset = orientation === 'top' ? -3 : 0; - - const propTypes = { - dy: PropTypes.number, - formattedValue: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - x: PropTypes.number.isRequired, - y: PropTypes.number.isRequired, - }; - const defaultProps = { - dy: null, - formattedValue: '', - }; - - const TickComponent = ({ x, y, dy, formattedValue, ...textStyle }) => ( - - - {formattedValue} - - - ); - - TickComponent.propTypes = propTypes; - TickComponent.defaultProps = defaultProps; - - return TickComponent; - } - - // This will render the tick as horizontal string. - return null; -} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/createTickComponent.tsx b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/createTickComponent.tsx new file mode 100644 index 0000000000..88fa92d2df --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/createTickComponent.tsx @@ -0,0 +1,48 @@ +/* eslint-disable no-magic-numbers */ + +import React, { CSSProperties } from 'react'; + +export default function createTickComponent({ + labelAngle, + labelOverlap, + orient, + tickTextAnchor = 'start', +}: { + labelAngle: number; + labelOverlap: string; + orient: string; + tickTextAnchor?: string; +}) { + if (labelOverlap === 'rotate' && labelAngle !== 0) { + let xOffset = labelAngle > 0 ? -6 : 6; + if (orient === 'top') { + xOffset = 0; + } + const yOffset = orient === 'top' ? -3 : 0; + + const TickComponent = ({ + x, + y, + dy, + formattedValue = '', + ...textStyle + }: { + x: number; + y: number; + dy?: number; + formattedValue: string; + textStyle: CSSProperties; + }) => ( + + + {formattedValue} + + + ); + + return TickComponent; + } + + // This will render the tick as horizontal string. + return null; +} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/createTickLabelProps.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/createTickLabelProps.ts new file mode 100644 index 0000000000..ec005b3ea4 --- /dev/null +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/createTickLabelProps.ts @@ -0,0 +1,30 @@ +/* eslint-disable no-magic-numbers */ + +export default function createTickLabelProps({ + labelAngle, + labelOverlap, + orient, + tickTextAnchor = 'start', +}: { + labelAngle: number; + labelOverlap: string; + orient: string; + tickTextAnchor?: string; +}) { + let dx = 0; + let dy = 0; + if (labelOverlap === 'rotate' && labelAngle !== 0) { + dx = labelAngle > 0 ? -6 : 6; + if (orient === 'top') { + dx = 0; + } + dy = orient === 'top' ? -3 : 0; + } + + return { + angle: labelAngle, + dx, + dy, + textAnchor: tickTextAnchor, + }; +} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/getTickLabels.js b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/getTickLabels.js deleted file mode 100644 index ba43793b5c..0000000000 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/getTickLabels.js +++ /dev/null @@ -1,13 +0,0 @@ -/* eslint-disable no-magic-numbers */ -import identity from '@vx/axis/build/utils/identity'; - -export default function getTickLabels(scale, axisConfig) { - const { numTicks = 10, tickValues, tickFormat } = axisConfig; - let values = scale.ticks ? scale.ticks(numTicks) : scale.domain(); - if (tickValues) values = tickValues; - - let format = scale.tickFormat ? scale.tickFormat() : identity; - if (tickFormat) format = tickFormat; - - return values.map(format); -} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/renderLegend.jsx b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/renderLegend.jsx deleted file mode 100644 index d4e0f51377..0000000000 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/src/utils/renderLegend.jsx +++ /dev/null @@ -1,58 +0,0 @@ -import React from 'react'; -import { CategoricalColorNamespace } from '@superset-ui/color'; -import { LegendOrdinal, LegendItem, LegendLabel } from '@vx/legend'; -import { scaleOrdinal } from '@vx/scale'; - -export default function renderLegend(data, colorEncoding) { - const { accessor, field, scale } = colorEncoding; - const { scheme, namespace } = scale; - const colorFn = CategoricalColorNamespace.getScale(scheme, namespace); - const keySet = new Set(); - data.forEach(d => { - keySet.add(accessor ? accessor(d) : d[field]); - }); - const keys = [...keySet.values()]; - const colorScale = scaleOrdinal({ - domain: keys, - range: keys.map(colorFn), - }); - - return ( -
- label}> - {labels => ( -
- {labels.map(label => { - const size = 8; - - return ( - { - alert(`clicked: ${JSON.stringify(label)}`); - }} - > - - - - - {label.text} - - - ); - })} -
- )} -
-
- ); -} 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/esm/utils/collectScalesFromProps.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/esm/utils/collectScalesFromProps.d.ts index c573a534d5..41de53a6b5 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/types/@data-ui/xy-chart/esm/utils/collectScalesFromProps.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/esm/utils/collectScalesFromProps.d.ts @@ -1,7 +1,7 @@ declare module '@data-ui/xy-chart/esm/utils/collectScalesFromProps' { import { ScaleLinear, ScaleBand, ScaleContinuousNumeric, ScaleDiverging, ScaleIdentity, ScaleLogarithmic, ScaleOrdinal, ScalePoint, ScalePower, ScaleQuantile, ScaleQuantize, ScaleSequential, ScaleThreshold, ScaleTime } from "d3-scale"; - import React from "react"; + import { ReactNode } from "react"; import { ChartTheme } from "@data-ui/theme"; interface ScaleConfig { @@ -26,7 +26,7 @@ declare module '@data-ui/xy-chart/esm/utils/collectScalesFromProps' { xScale: ScaleConfig, yScale: ScaleConfig, theme: ChartTheme, - children: React.ReactElement[], + children: ReactNode[], }): { xScale: Scale; yScale: Scale; 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 d94976a3a2..3e48cb665a 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 @@ -7,20 +7,25 @@ declare module '@data-ui/xy-chart' { }; interface XYChartProps { - theme: any; width: number; height: number; + ariaLabel: string; + eventTrigger?: any; margin?: { top?: number; right?: number; bottom?: number; left?: number; }; - ariaLabel: string; + onMouseMove?: (...args: any[]) => void; + onMouseLeave?: (...args: any[]) => void; + renderTooltip: any; + showYGrid: boolean; + snapTooltipToDataX: boolean; + theme: any; + tooltipData: any; xScale: any; yScale: any; - renderTooltip: any; - eventTrigger?: any; } export class AreaSeries extends React.PureComponent {} diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/types/@vx/legend/index.d.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/types/@vx/legend/index.d.ts index a21af2dfa2..79a0933b6e 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/types/@vx/legend/index.d.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/types/@vx/legend/index.d.ts @@ -1,16 +1,16 @@ /* eslint-disable react/no-multi-comp */ declare module '@vx/legend' { - import React from 'react'; + import { ReactNode, ReactElement } from 'react'; - export function LegendOrdinal(props: { [key: string]: any }): React.ReactNode; + export function LegendOrdinal(props: { [key: string]: any }): ReactElement; - export function LegendItem(props: { [key: string]: any }): React.ReactNode; + export function LegendItem(props: { [key: string]: any }): ReactElement; export function LegendLabel(props: { align: string; - label?: React.ReactNode; + label?: ReactNode; flex?: string | number; margin?: string | number; - children?: React.ReactNode; - }): React.ReactNode; + children?: ReactNode; + }): ReactElement; } diff --git a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/types/@vx/responsive/index.d.ts b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/types/@vx/responsive/index.d.ts index a23c5028cc..a6a5cc3606 100644 --- a/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/types/@vx/responsive/index.d.ts +++ b/superset-frontend/temporary_superset_ui/superset-ui/plugins/superset-ui-plugins/packages/superset-ui-preset-chart-xy/types/@vx/responsive/index.d.ts @@ -1,10 +1,10 @@ declare module '@vx/responsive' { import React from 'react'; - // eslint-disable-next-line import/prefer-default-export interface ParentSizeProps { children: (renderProps: { width: number; height: number }) => React.ReactNode; } - const ParentSize: React.ComponentType; + // eslint-disable-next-line import/prefer-default-export + export const ParentSize: React.ComponentType; }