From e308d73cace918606d7c1677e7131ff989a9c0a1 Mon Sep 17 00:00:00 2001 From: Aleksandr Lesnenko <alxnddr@users.noreply.github.com> Date: Fri, 10 May 2024 15:40:04 -0300 Subject: [PATCH] use echarts tree shaking api (#42520) * use echarts tree shaking api * fix specs * update types * move echarts module registration back to individual components * Transpile echarts in jest --------- Co-authored-by: Uladzimir Havenchyk <uladzimir.dev@gmail.com> --- .../components/ComboChart/ComboChart.tsx | 5 +++- .../components/ScatterPlot/ScatterPlot.tsx | 5 +++- .../WaterfallChart/WaterfallChart.tsx | 5 +++- frontend/src/metabase/static-viz/index.js | 2 +- .../EChartsRenderer/EChartsRenderer.tsx | 9 ++++-- .../echarts/cartesian/option/goal-line.ts | 4 +-- .../echarts/cartesian/option/index.ts | 4 +-- .../echarts/cartesian/option/series.ts | 13 +++----- .../echarts/cartesian/option/trend-line.ts | 4 +-- .../echarts/cartesian/option/types.ts | 15 ++++++---- .../echarts/cartesian/scatter/series.ts | 4 +-- .../cartesian/timeline-events/option.ts | 2 +- .../cartesian/waterfall/option/index.ts | 16 +++++----- .../metabase/visualizations/echarts/index.ts | 30 +++++++++++++++++++ .../metabase/visualizations/echarts/types.ts | 14 ++++++++- .../CartesianChart/CartesianChart.tsx | 2 +- .../CartesianChart/use-chart-debug.ts | 4 +-- .../CartesianChart/use-chart-events.ts | 4 +-- .../visualizations/CartesianChart/utils.ts | 4 +-- jest.config.js | 2 +- 20 files changed, 101 insertions(+), 47 deletions(-) create mode 100644 frontend/src/metabase/visualizations/echarts/index.ts diff --git a/frontend/src/metabase/static-viz/components/ComboChart/ComboChart.tsx b/frontend/src/metabase/static-viz/components/ComboChart/ComboChart.tsx index cd56ed3a785..44c86efd18c 100644 --- a/frontend/src/metabase/static-viz/components/ComboChart/ComboChart.tsx +++ b/frontend/src/metabase/static-viz/components/ComboChart/ComboChart.tsx @@ -1,8 +1,9 @@ import { Group } from "@visx/group"; -import { init } from "echarts"; +import { init } from "echarts/core"; import type { StaticChartProps } from "metabase/static-viz/components/StaticVisualization"; import { sanitizeSvgForBatik } from "metabase/static-viz/lib/svg"; +import { registerEChartsModules } from "metabase/visualizations/echarts"; import { getChartMeasurements } from "metabase/visualizations/echarts/cartesian/chart-measurements"; import { getCartesianChartModel } from "metabase/visualizations/echarts/cartesian/model"; import { getLegendItems } from "metabase/visualizations/echarts/cartesian/model/legend"; @@ -17,6 +18,8 @@ const WIDTH = 540; const HEIGHT = 360; const LEGEND_PADDING = 8; +registerEChartsModules(); + export const ComboChart = ({ rawSeries, dashcardSettings, diff --git a/frontend/src/metabase/static-viz/components/ScatterPlot/ScatterPlot.tsx b/frontend/src/metabase/static-viz/components/ScatterPlot/ScatterPlot.tsx index f991286123e..25ff4a83dbe 100644 --- a/frontend/src/metabase/static-viz/components/ScatterPlot/ScatterPlot.tsx +++ b/frontend/src/metabase/static-viz/components/ScatterPlot/ScatterPlot.tsx @@ -1,8 +1,9 @@ import { Group } from "@visx/group"; -import { init } from "echarts"; +import { init } from "echarts/core"; import type { StaticChartProps } from "metabase/static-viz/components/StaticVisualization"; import { sanitizeSvgForBatik } from "metabase/static-viz/lib/svg"; +import { registerEChartsModules } from "metabase/visualizations/echarts"; import { getChartMeasurements } from "metabase/visualizations/echarts/cartesian/chart-measurements"; import { getCartesianChartModel } from "metabase/visualizations/echarts/cartesian/model"; import { getLegendItems } from "metabase/visualizations/echarts/cartesian/model/legend"; @@ -12,6 +13,8 @@ import { computeStaticComboChartSettings } from "../ComboChart/settings"; import { Legend } from "../Legend"; import { calculateLegendRows } from "../Legend/utils"; +registerEChartsModules(); + const WIDTH = 540; const HEIGHT = 360; const LEGEND_PADDING = 8; diff --git a/frontend/src/metabase/static-viz/components/WaterfallChart/WaterfallChart.tsx b/frontend/src/metabase/static-viz/components/WaterfallChart/WaterfallChart.tsx index faeb24fb31a..074b6ad48d6 100644 --- a/frontend/src/metabase/static-viz/components/WaterfallChart/WaterfallChart.tsx +++ b/frontend/src/metabase/static-viz/components/WaterfallChart/WaterfallChart.tsx @@ -1,13 +1,16 @@ -import { init } from "echarts"; +import { init } from "echarts/core"; import type { StaticChartProps } from "metabase/static-viz/components/StaticVisualization"; import { sanitizeSvgForBatik } from "metabase/static-viz/lib/svg"; +import { registerEChartsModules } from "metabase/visualizations/echarts"; import { getChartMeasurements } from "metabase/visualizations/echarts/cartesian/chart-measurements"; import { getWaterfallChartModel } from "metabase/visualizations/echarts/cartesian/waterfall/model"; import { getWaterfallChartOption } from "metabase/visualizations/echarts/cartesian/waterfall/option"; import { computeStaticWaterfallChartSettings } from "./settings"; +registerEChartsModules(); + const WIDTH = 540; const HEIGHT = 360; diff --git a/frontend/src/metabase/static-viz/index.js b/frontend/src/metabase/static-viz/index.js index 441d4fadb95..356713e5ff4 100644 --- a/frontend/src/metabase/static-viz/index.js +++ b/frontend/src/metabase/static-viz/index.js @@ -1,4 +1,4 @@ -import { setPlatformAPI } from "echarts"; +import { setPlatformAPI } from "echarts/core"; import ReactDOMServer from "react-dom/server"; import "metabase/lib/dayjs"; diff --git a/frontend/src/metabase/visualizations/components/EChartsRenderer/EChartsRenderer.tsx b/frontend/src/metabase/visualizations/components/EChartsRenderer/EChartsRenderer.tsx index e87e48fae77..317db84ead6 100644 --- a/frontend/src/metabase/visualizations/components/EChartsRenderer/EChartsRenderer.tsx +++ b/frontend/src/metabase/visualizations/components/EChartsRenderer/EChartsRenderer.tsx @@ -1,8 +1,9 @@ -import type { EChartsType, EChartsOption } from "echarts"; -import { init } from "echarts"; +import type { EChartsCoreOption, EChartsType } from "echarts/core"; +import { init } from "echarts/core"; import { useEffect, useRef } from "react"; import { useMount, useUpdateEffect } from "react-use"; +import { registerEChartsModules } from "metabase/visualizations/echarts"; import type { EChartsEventHandler, ZREventHandler, @@ -10,8 +11,10 @@ import type { import { EChartsRendererRoot } from "./EChartsRenderer.styled"; +registerEChartsModules(); + export interface EChartsRendererProps { - option: EChartsOption; + option: EChartsCoreOption; eventHandlers?: EChartsEventHandler[]; zrEventHandlers?: ZREventHandler[]; width: number | "auto"; diff --git a/frontend/src/metabase/visualizations/echarts/cartesian/option/goal-line.ts b/frontend/src/metabase/visualizations/echarts/cartesian/option/goal-line.ts index 00dfe4c2a0a..55a467b14ba 100644 --- a/frontend/src/metabase/visualizations/echarts/cartesian/option/goal-line.ts +++ b/frontend/src/metabase/visualizations/echarts/cartesian/option/goal-line.ts @@ -1,4 +1,4 @@ -import type { RegisteredSeriesOption } from "echarts"; +import type { CustomSeriesOption } from "echarts/charts"; import type { ComputedVisualizationSettings, @@ -30,7 +30,7 @@ export function getGoalLineSeriesOption( chartModel: CartesianChartModel, settings: ComputedVisualizationSettings, renderingContext: RenderingContext, -): RegisteredSeriesOption["custom"] | null { +): CustomSeriesOption | null { if (!settings["graph.show_goal"] || settings["graph.goal_value"] == null) { return null; } diff --git a/frontend/src/metabase/visualizations/echarts/cartesian/option/index.ts b/frontend/src/metabase/visualizations/echarts/cartesian/option/index.ts index 30fbccfeea2..acf68df1655 100644 --- a/frontend/src/metabase/visualizations/echarts/cartesian/option/index.ts +++ b/frontend/src/metabase/visualizations/echarts/cartesian/option/index.ts @@ -1,4 +1,4 @@ -import type { EChartsOption } from "echarts"; +import type { EChartsCoreOption } from "echarts/core"; import type { OptionSourceData } from "echarts/types/src/util/types"; import { @@ -51,7 +51,7 @@ export const getCartesianChartOption = ( isPlaceholder: boolean, hoveredSeriesDataKey: DataKey | null, renderingContext: RenderingContext, -): EChartsOption => { +): EChartsCoreOption => { const hasTimelineEvents = timelineEventsModel != null; const timelineEventsSeries = hasTimelineEvents ? getTimelineEventsSeries( diff --git a/frontend/src/metabase/visualizations/echarts/cartesian/option/series.ts b/frontend/src/metabase/visualizations/echarts/cartesian/option/series.ts index 14d51dcdf2d..28722055412 100644 --- a/frontend/src/metabase/visualizations/echarts/cartesian/option/series.ts +++ b/frontend/src/metabase/visualizations/echarts/cartesian/option/series.ts @@ -1,9 +1,4 @@ -import type { RegisteredSeriesOption } from "echarts"; -import type { - BarSeriesOption, - LineSeriesOption, - SeriesOption, -} from "echarts/types/dist/echarts"; +import type { BarSeriesOption, LineSeriesOption } from "echarts/charts"; import type { CallbackDataParams } from "echarts/types/dist/shared"; import type { SeriesLabelOption } from "echarts/types/src/util/types"; import _ from "underscore"; @@ -62,7 +57,7 @@ export const getBarLabelLayout = dataset: ChartDataset, settings: ComputedVisualizationSettings, seriesDataKey: DataKey, - ): SeriesOption["labelLayout"] => + ): BarSeriesOption["labelLayout"] => params => { const { dataIndex, rect } = params; if (dataIndex == null) { @@ -258,7 +253,7 @@ const buildEChartsBarSeries = ( yAxisWithBarSeriesCount: number, hasMultipleSeries: boolean, renderingContext: RenderingContext, -): RegisteredSeriesOption["bar"] => { +): BarSeriesOption => { const stackName = settings["stackable.stack_type"] != null ? `bar_${yAxisIndex}` : undefined; @@ -344,7 +339,7 @@ const buildEChartsLineAreaSeries = ( hasMultipleSeries: boolean, chartWidth: number, renderingContext: RenderingContext, -): RegisteredSeriesOption["line"] => { +): LineSeriesOption => { const display = seriesSettings?.display ?? "line"; const stackName = diff --git a/frontend/src/metabase/visualizations/echarts/cartesian/option/trend-line.ts b/frontend/src/metabase/visualizations/echarts/cartesian/option/trend-line.ts index b748e61b759..da9adf745f9 100644 --- a/frontend/src/metabase/visualizations/echarts/cartesian/option/trend-line.ts +++ b/frontend/src/metabase/visualizations/echarts/cartesian/option/trend-line.ts @@ -1,4 +1,4 @@ -import type { RegisteredSeriesOption } from "echarts"; +import type { LineSeriesOption } from "echarts/charts"; import _ from "underscore"; import { X_AXIS_DATA_KEY } from "metabase/visualizations/echarts/cartesian/constants/dataset"; @@ -12,7 +12,7 @@ export const TREND_LINE_DASH = [5, 5]; export function getTrendLinesOption( chartModel: CartesianChartModel, -): RegisteredSeriesOption["line"][] { +): LineSeriesOption[] { return ( chartModel.trendLinesModel?.seriesModels.map(trendSeries => ({ type: "line", diff --git a/frontend/src/metabase/visualizations/echarts/cartesian/option/types.ts b/frontend/src/metabase/visualizations/echarts/cartesian/option/types.ts index f5d726e455a..5beab155504 100644 --- a/frontend/src/metabase/visualizations/echarts/cartesian/option/types.ts +++ b/frontend/src/metabase/visualizations/echarts/cartesian/option/types.ts @@ -1,7 +1,12 @@ -import type { RegisteredSeriesOption } from "echarts"; +import type { + CustomSeriesOption, + LineSeriesOption, + BarSeriesOption, + ScatterSeriesOption, +} from "echarts/charts"; export type EChartsSeriesOption = - | RegisteredSeriesOption["line"] - | RegisteredSeriesOption["bar"] - | RegisteredSeriesOption["scatter"] - | RegisteredSeriesOption["custom"]; + | LineSeriesOption + | BarSeriesOption + | ScatterSeriesOption + | CustomSeriesOption; diff --git a/frontend/src/metabase/visualizations/echarts/cartesian/scatter/series.ts b/frontend/src/metabase/visualizations/echarts/cartesian/scatter/series.ts index c91767a4a30..39bedf531cd 100644 --- a/frontend/src/metabase/visualizations/echarts/cartesian/scatter/series.ts +++ b/frontend/src/metabase/visualizations/echarts/cartesian/scatter/series.ts @@ -1,5 +1,5 @@ import d3 from "d3"; -import type { RegisteredSeriesOption } from "echarts"; +import type { ScatterSeriesOption } from "echarts/charts"; import { X_AXIS_DATA_KEY } from "metabase/visualizations/echarts/cartesian/constants/dataset"; import type { RenderingContext } from "metabase/visualizations/types"; @@ -51,7 +51,7 @@ export function buildEChartsScatterSeries( bubbleSizeDomain: Extent | null, yAxisIndex: number, renderingContext: RenderingContext, -): RegisteredSeriesOption["scatter"] { +): ScatterSeriesOption { const bubbleSizeDataKey = "bubbleSizeDataKey" in seriesModel ? seriesModel.bubbleSizeDataKey diff --git a/frontend/src/metabase/visualizations/echarts/cartesian/timeline-events/option.ts b/frontend/src/metabase/visualizations/echarts/cartesian/timeline-events/option.ts index d5dc74c9ee6..26e887996f6 100644 --- a/frontend/src/metabase/visualizations/echarts/cartesian/timeline-events/option.ts +++ b/frontend/src/metabase/visualizations/echarts/cartesian/timeline-events/option.ts @@ -1,4 +1,4 @@ -import type { LineSeriesOption } from "echarts"; +import type { LineSeriesOption } from "echarts/charts"; import type { MarkLine1DDataItemOption } from "echarts/types/src/component/marker/MarkLineModel"; import type { IconName } from "metabase/ui/components/icons/Icon/icons"; diff --git a/frontend/src/metabase/visualizations/echarts/cartesian/waterfall/option/index.ts b/frontend/src/metabase/visualizations/echarts/cartesian/waterfall/option/index.ts index 143de461ef5..bc2b4c61d8d 100644 --- a/frontend/src/metabase/visualizations/echarts/cartesian/waterfall/option/index.ts +++ b/frontend/src/metabase/visualizations/echarts/cartesian/waterfall/option/index.ts @@ -1,5 +1,4 @@ -import type { EChartsOption, SeriesOption } from "echarts"; -import type { DatasetOption } from "echarts/types/dist/shared"; +import type { EChartsCoreOption } from "echarts/core"; import type { LabelLayoutOptionCallback } from "echarts/types/src/util/types"; import { X_AXIS_DATA_KEY } from "metabase/visualizations/echarts/cartesian/constants/dataset"; @@ -21,6 +20,7 @@ import { WATERFALL_TOTAL_KEY, WATERFALL_VALUE_KEY, } from "metabase/visualizations/echarts/cartesian/waterfall/constants"; +import type { WaterfallSeriesOption } from "metabase/visualizations/echarts/types"; import { getNumberOr } from "metabase/visualizations/lib/settings/row-values"; import type { ComputedVisualizationSettings, @@ -127,7 +127,7 @@ export const buildEChartsWaterfallSeries = ( ), }); - const series: SeriesOption[] = [ + const series: WaterfallSeriesOption[] = [ { id: seriesModel.dataKey, type: "custom", @@ -214,7 +214,7 @@ export const getWaterfallChartOption = ( settings: ComputedVisualizationSettings, isPlaceholder: boolean, renderingContext: RenderingContext, -): EChartsOption => { +): EChartsCoreOption => { const hasTimelineEvents = timelineEventsModel != null; const timelineEventsSeries = hasTimelineEvents ? getTimelineEventsSeries( @@ -231,7 +231,7 @@ export const getWaterfallChartOption = ( renderingContext, ); - const seriesOption: SeriesOption[] = [ + const seriesOption: WaterfallSeriesOption[] = [ dataSeriesOptions, timelineEventsSeries, ].flatMap(option => option ?? []); @@ -243,8 +243,8 @@ export const getWaterfallChartOption = ( grid: { ...chartMeasurements.padding, }, - dataset: echartsDataset as DatasetOption, - series: seriesOption as SeriesOption, + dataset: echartsDataset, + series: seriesOption, ...buildAxes( chartModel, chartWidth, @@ -254,5 +254,5 @@ export const getWaterfallChartOption = ( null, renderingContext, ), - } as EChartsOption; + }; }; diff --git a/frontend/src/metabase/visualizations/echarts/index.ts b/frontend/src/metabase/visualizations/echarts/index.ts new file mode 100644 index 00000000000..a4653052024 --- /dev/null +++ b/frontend/src/metabase/visualizations/echarts/index.ts @@ -0,0 +1,30 @@ +import { LineChart, BarChart, ScatterChart, CustomChart } from "echarts/charts"; +import { + BrushComponent, + DataZoomComponent, + GridComponent, + MarkLineComponent, + ToolboxComponent, + DatasetComponent, +} from "echarts/components"; +import { use } from "echarts/core"; +import { LabelLayout } from "echarts/features"; +import { SVGRenderer } from "echarts/renderers"; + +export const registerEChartsModules = () => { + use([ + LineChart, + BarChart, + ScatterChart, + CustomChart, + GridComponent, + BarChart, + SVGRenderer, + MarkLineComponent, + DataZoomComponent, + ToolboxComponent, + BrushComponent, + DatasetComponent, + LabelLayout, + ]); +}; diff --git a/frontend/src/metabase/visualizations/echarts/types.ts b/frontend/src/metabase/visualizations/echarts/types.ts index afcb12766a5..943da56476d 100644 --- a/frontend/src/metabase/visualizations/echarts/types.ts +++ b/frontend/src/metabase/visualizations/echarts/types.ts @@ -1,4 +1,10 @@ -import type { ElementEvent } from "echarts"; +import type { + CustomSeriesOption, + ScatterSeriesOption, + BarSeriesOption, + LineSeriesOption, +} from "echarts/charts"; +import type { ElementEvent } from "echarts/core"; import type { BrushAreaParam } from "echarts/types/src/component/brush/BrushModel"; import type { ZRRawMouseEvent } from "zrender/lib/core/types"; @@ -23,3 +29,9 @@ export type EChartsCartesianCoordinateSystem = { width: number; height: number; }; + +export type WaterfallSeriesOption = + | CustomSeriesOption + | ScatterSeriesOption + | BarSeriesOption + | LineSeriesOption; diff --git a/frontend/src/metabase/visualizations/visualizations/CartesianChart/CartesianChart.tsx b/frontend/src/metabase/visualizations/visualizations/CartesianChart/CartesianChart.tsx index a234b6a7972..9756feb1415 100644 --- a/frontend/src/metabase/visualizations/visualizations/CartesianChart/CartesianChart.tsx +++ b/frontend/src/metabase/visualizations/visualizations/CartesianChart/CartesianChart.tsx @@ -1,4 +1,4 @@ -import type { EChartsType } from "echarts"; +import type { EChartsType } from "echarts/core"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { ChartRenderingErrorBoundary } from "metabase/visualizations/components/ChartRenderingErrorBoundary"; diff --git a/frontend/src/metabase/visualizations/visualizations/CartesianChart/use-chart-debug.ts b/frontend/src/metabase/visualizations/visualizations/CartesianChart/use-chart-debug.ts index b8d2dcd3814..dfc64ccccf8 100644 --- a/frontend/src/metabase/visualizations/visualizations/CartesianChart/use-chart-debug.ts +++ b/frontend/src/metabase/visualizations/visualizations/CartesianChart/use-chart-debug.ts @@ -1,5 +1,5 @@ /* eslint-disable no-console */ -import type { EChartsOption } from "echarts"; +import type { EChartsCoreOption } from "echarts/core"; import { useEffect } from "react"; import { isChartsDebugLoggingEnabled } from "metabase/env"; @@ -14,7 +14,7 @@ export function useChartDebug({ }: { isQueryBuilder: boolean; rawSeries: RawSeries; - option: EChartsOption; + option: EChartsCoreOption; chartModel: BaseCartesianChartModel; }) { useEffect(() => { diff --git a/frontend/src/metabase/visualizations/visualizations/CartesianChart/use-chart-events.ts b/frontend/src/metabase/visualizations/visualizations/CartesianChart/use-chart-events.ts index e8c00b70e7c..d4a8052203a 100644 --- a/frontend/src/metabase/visualizations/visualizations/CartesianChart/use-chart-events.ts +++ b/frontend/src/metabase/visualizations/visualizations/CartesianChart/use-chart-events.ts @@ -1,4 +1,4 @@ -import type { EChartsOption, EChartsType } from "echarts"; +import type { EChartsCoreOption, EChartsType } from "echarts/core"; import type * as React from "react"; import { useCallback, useEffect, useMemo, useRef } from "react"; @@ -35,7 +35,7 @@ export const useChartEvents = ( chartRef: React.MutableRefObject<EChartsType | undefined>, chartModel: BaseCartesianChartModel, timelineEventsModel: TimelineEventsModel | null, - option: EChartsOption, + option: EChartsCoreOption, { card, rawSeries, diff --git a/frontend/src/metabase/visualizations/visualizations/CartesianChart/utils.ts b/frontend/src/metabase/visualizations/visualizations/CartesianChart/utils.ts index 101a83df91b..88f3ef8f04c 100644 --- a/frontend/src/metabase/visualizations/visualizations/CartesianChart/utils.ts +++ b/frontend/src/metabase/visualizations/visualizations/CartesianChart/utils.ts @@ -1,4 +1,4 @@ -import type { EChartsOption } from "echarts"; +import type { EChartsCoreOption } from "echarts/core"; import { t } from "ttag"; import { isNotNull } from "metabase/lib/types"; @@ -79,7 +79,7 @@ export const getHoveredSeriesDataKey = ( export const getHoveredEChartsSeriesIndex = ( seriesModels: SeriesModel[], - option: EChartsOption, + option: EChartsCoreOption, hovered: HoveredObject | undefined, ): number | null => { const hoveredSeriesDataKey = getHoveredSeriesDataKey(seriesModels, hovered); diff --git a/jest.config.js b/jest.config.js index fe54535209b..764f506964d 100644 --- a/jest.config.js +++ b/jest.config.js @@ -17,7 +17,7 @@ const config = { "<rootDir>/frontend/test/__mocks__/svgMock.jsx", }, transformIgnorePatterns: [ - "<rootDir>/node_modules/(?!(rehype-external-links|hast.*|devlop|property-information|comma-separated-tokens|space-separated-tokens|vfile|vfile-message|html-void-elements|stringify-entities|character-entities-html4)/)", + "<rootDir>/node_modules/(?!(echarts|zrender|rehype-external-links|hast.*|devlop|property-information|comma-separated-tokens|space-separated-tokens|vfile|vfile-message|html-void-elements|stringify-entities|character-entities-html4)/)", ], testPathIgnorePatterns: [ "<rootDir>/frontend/.*/.*.tz.unit.spec.{js,jsx,ts,tsx}", -- GitLab