@agions/taroviz 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/README.md +318 -53
  2. package/dist/index.esm.js +67955 -3318
  3. package/package.json +102 -20
  4. package/src/__tests__/integration.test.tsx +168 -0
  5. package/src/adapters/__tests__/index.test.ts +91 -0
  6. package/src/adapters/h5/__tests__/index.test.ts +156 -0
  7. package/src/adapters/h5/index.ts +301 -0
  8. package/src/adapters/harmony/index.ts +274 -0
  9. package/src/adapters/index.ts +234 -0
  10. package/src/adapters/swan/index.ts +274 -0
  11. package/src/adapters/tt/index.ts +274 -0
  12. package/src/adapters/types.ts +162 -0
  13. package/src/adapters/weapp/index.ts +237 -0
  14. package/src/charts/bar/__tests__/index.test.tsx +113 -0
  15. package/src/charts/bar/index.tsx +27 -0
  16. package/src/charts/common/BaseChartWrapper.tsx +136 -0
  17. package/src/charts/funnel/index.tsx +33 -0
  18. package/src/charts/gauge/index.tsx +33 -0
  19. package/src/charts/heatmap/index.tsx +33 -0
  20. package/src/charts/index.ts +21 -0
  21. package/src/charts/line/__tests__/index.test.tsx +107 -0
  22. package/src/charts/line/index.tsx +27 -0
  23. package/src/charts/pie/__tests__/index.test.tsx +112 -0
  24. package/src/charts/pie/index.tsx +22 -0
  25. package/src/charts/radar/index.tsx +33 -0
  26. package/src/charts/scatter/index.tsx +33 -0
  27. package/src/charts/types.ts +146 -0
  28. package/src/charts/utils.ts +56 -0
  29. package/src/core/__tests__/platform.test.ts +48 -0
  30. package/src/core/animation/AnimationManager.ts +391 -0
  31. package/src/core/animation/index.ts +20 -0
  32. package/src/core/animation/types.ts +248 -0
  33. package/src/core/components/BaseChart.tsx +1319 -0
  34. package/src/core/index.ts +19 -0
  35. package/src/core/types/chart.ts +66 -0
  36. package/src/core/types/common.ts +224 -0
  37. package/src/core/types/index.ts +281 -0
  38. package/src/core/types/platform.ts +325 -0
  39. package/src/core/utils/__tests__/common.test.ts +52 -0
  40. package/src/core/utils/__tests__/environment.test.ts +94 -0
  41. package/src/core/utils/__tests__/i18n.test.ts +247 -0
  42. package/src/core/utils/__tests__/index.test.ts +219 -0
  43. package/src/core/utils/__tests__/uuid.test.ts +78 -0
  44. package/src/core/utils/chartInstances.ts +69 -0
  45. package/src/core/utils/codeGenerator/CodeGenerator.ts +655 -0
  46. package/src/core/utils/codeGenerator/index.ts +13 -0
  47. package/src/core/utils/codeGenerator/types.ts +200 -0
  48. package/src/core/utils/common.ts +58 -0
  49. package/src/core/utils/configGenerator/ConfigGenerator.ts +583 -0
  50. package/src/core/utils/configGenerator/index.ts +13 -0
  51. package/src/core/utils/configGenerator/types.ts +445 -0
  52. package/src/core/utils/debug/DebugPanel.tsx +637 -0
  53. package/src/core/utils/debug/debugger.ts +322 -0
  54. package/src/core/utils/debug/index.ts +21 -0
  55. package/src/core/utils/debug/types.ts +142 -0
  56. package/src/core/utils/i18n.ts +452 -0
  57. package/src/core/utils/index.ts +162 -0
  58. package/src/core/utils/performance/PerformanceAnalyzer.ts +586 -0
  59. package/src/core/utils/performance/index.ts +13 -0
  60. package/src/core/utils/performance/types.ts +180 -0
  61. package/src/core/utils/uuid.ts +30 -0
  62. package/src/editor/ThemeEditor.tsx +449 -0
  63. package/src/editor/index.ts +10 -0
  64. package/src/hooks/__tests__/index.test.tsx +333 -0
  65. package/src/hooks/index.ts +214 -0
  66. package/src/index.ts +75 -0
  67. package/src/main.tsx +247 -0
  68. package/src/react-dom.d.ts +7 -0
  69. package/src/themes/__tests__/index.test.ts +91 -0
  70. package/src/themes/index.ts +465 -0
  71. package/dist/index.esm.js.map +0 -1
  72. package/dist/index.js +0 -4012
  73. package/dist/index.js.map +0 -1
@@ -0,0 +1,113 @@
1
+ import { render } from '@testing-library/react';
2
+ import React from 'react';
3
+
4
+ import BarChart from '../index';
5
+
6
+ // Mock ECharts and adapters
7
+ jest.mock('echarts/core', () => ({
8
+ use: jest.fn(),
9
+ init: jest.fn(() => ({
10
+ setOption: jest.fn(),
11
+ showLoading: jest.fn(),
12
+ hideLoading: jest.fn(),
13
+ on: jest.fn(),
14
+ off: jest.fn(),
15
+ dispose: jest.fn(),
16
+ resize: jest.fn(),
17
+ })),
18
+ getInstanceByDom: jest.fn(),
19
+ }));
20
+
21
+ // Mock specific ECharts chart imports
22
+ jest.mock('echarts/charts', () => ({
23
+ BarChart: jest.fn(),
24
+ }));
25
+
26
+ jest.mock('echarts/components', () => ({
27
+ GridComponent: jest.fn(),
28
+ TooltipComponent: jest.fn(),
29
+ TitleComponent: jest.fn(),
30
+ LegendComponent: jest.fn(),
31
+ }));
32
+
33
+ jest.mock('../../common/BaseChartWrapper', () => ({
34
+ __esModule: true,
35
+ default: (props: any) => (
36
+ <div
37
+ data-testid="base-chart-wrapper"
38
+ className={`taroviz-${props.chartType} ${props.className || ''}`}
39
+ style={{ width: props.width, height: props.height }}
40
+ >
41
+ <div data-testid="chart-option">{JSON.stringify(props.option)}</div>
42
+ </div>
43
+ ),
44
+ }));
45
+
46
+ describe('BarChart Component', () => {
47
+ const mockOption = {
48
+ xAxis: {
49
+ type: 'category' as const,
50
+ data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
51
+ },
52
+ yAxis: {
53
+ type: 'value' as const,
54
+ },
55
+ series: [
56
+ {
57
+ data: [150, 230, 224, 218, 135, 147, 260],
58
+ type: 'bar' as const,
59
+ },
60
+ ],
61
+ };
62
+
63
+ it('should render correctly with default props', () => {
64
+ const { getByTestId } = render(<BarChart option={mockOption} />);
65
+
66
+ const chartWrapper = getByTestId('base-chart-wrapper');
67
+ expect(chartWrapper).toBeInTheDocument();
68
+ expect(chartWrapper).toHaveClass('taroviz-bar-chart');
69
+ });
70
+
71
+ it('should pass the correct option to BaseChartWrapper', () => {
72
+ const { getByTestId } = render(<BarChart option={mockOption} />);
73
+
74
+ const chartOption = getByTestId('chart-option');
75
+ expect(JSON.parse(chartOption.textContent || '')).toEqual(mockOption);
76
+ });
77
+
78
+ it('should render with custom width and height', () => {
79
+ const customWidth = '600px';
80
+ const customHeight = '450px';
81
+
82
+ const { getByTestId } = render(
83
+ <BarChart option={mockOption} width={customWidth} height={customHeight} />
84
+ );
85
+
86
+ const chartWrapper = getByTestId('base-chart-wrapper');
87
+ expect(chartWrapper).toHaveStyle(`width: ${customWidth}`);
88
+ expect(chartWrapper).toHaveStyle(`height: ${customHeight}`);
89
+ });
90
+
91
+ it('should render with custom className', () => {
92
+ const customClass = 'custom-bar-chart';
93
+
94
+ const { getByTestId } = render(<BarChart option={mockOption} className={customClass} />);
95
+
96
+ const chartWrapper = getByTestId('base-chart-wrapper');
97
+ expect(chartWrapper).toHaveClass(customClass);
98
+ });
99
+
100
+ it('should render with loading state', () => {
101
+ const { getByTestId } = render(<BarChart option={mockOption} loading={true} />);
102
+
103
+ const chartWrapper = getByTestId('base-chart-wrapper');
104
+ expect(chartWrapper).toBeInTheDocument();
105
+ });
106
+
107
+ it('should render with svg renderer', () => {
108
+ const { getByTestId } = render(<BarChart option={mockOption} renderer="svg" />);
109
+
110
+ const chartWrapper = getByTestId('base-chart-wrapper');
111
+ expect(chartWrapper).toBeInTheDocument();
112
+ });
113
+ });
@@ -0,0 +1,27 @@
1
+ /**
2
+ * 柱状图组件
3
+ */
4
+ import { BarChart as BarChartComponent } from 'echarts/charts';
5
+ import {
6
+ GridComponent,
7
+ TooltipComponent,
8
+ TitleComponent,
9
+ LegendComponent,
10
+ } from 'echarts/components';
11
+ import * as echarts from 'echarts/core';
12
+ import React from 'react';
13
+
14
+ import BaseChartWrapper from '../common/BaseChartWrapper';
15
+ import { BarChartProps } from '../types';
16
+
17
+ // 注册必要的组件
18
+ echarts.use([BarChartComponent, GridComponent, TooltipComponent, TitleComponent, LegendComponent]);
19
+
20
+ /**
21
+ * 柱状图组件
22
+ */
23
+ const BarChart: React.FC<BarChartProps> = props => (
24
+ <BaseChartWrapper {...props} chartType="bar-chart" />
25
+ );
26
+
27
+ export default BarChart;
@@ -0,0 +1,136 @@
1
+ /**
2
+ * 基础图表包装组件
3
+ * 提供统一的图表初始化、渲染和生命周期管理
4
+ */
5
+ import * as echarts from 'echarts/core';
6
+ import React, { useEffect, useRef, useMemo } from 'react';
7
+
8
+ import { getAdapter } from '../../adapters';
9
+ import { uuid } from '../../core/utils';
10
+ import { BaseChartProps } from '../types';
11
+ import { processAdapterConfig, safeRenderAdapter } from '../utils';
12
+
13
+ /**
14
+ * 基础图表包装组件
15
+ * 提供统一的图表初始化、渲染和生命周期管理
16
+ */
17
+ const BaseChartWrapper: React.FC<BaseChartProps & { chartType: string }> = ({
18
+ option,
19
+ width = '100%',
20
+ height = '300px',
21
+ theme,
22
+ style = {},
23
+ className = '',
24
+ autoResize = true,
25
+ loading = false,
26
+ loadingOption,
27
+ onChartInit,
28
+ onChartReady,
29
+ renderer = 'canvas',
30
+ onEvents = {},
31
+ chartType = 'chart',
32
+ }) => {
33
+ const chartId = useRef<string>(`${chartType}-${uuid()}`);
34
+ const chartInstance = useRef<echarts.ECharts | null>(null);
35
+ const containerRef = useRef<HTMLDivElement>(null);
36
+
37
+ // 使用 useMemo 缓存适配器配置,并处理类型问题
38
+ const adapterConfig = useMemo(() => {
39
+ return processAdapterConfig({
40
+ canvasId: chartId.current,
41
+ containerRef,
42
+ width,
43
+ height,
44
+ theme,
45
+ autoResize,
46
+ renderer,
47
+ option,
48
+ });
49
+ }, [width, height, theme, autoResize, renderer, option, chartType]);
50
+
51
+ // 处理图表初始化
52
+ useEffect(() => {
53
+ const initConfig = processAdapterConfig({
54
+ ...adapterConfig,
55
+ onInit: (instance: echarts.ECharts) => {
56
+ chartInstance.current = instance;
57
+
58
+ // 绑定事件
59
+ if (onEvents) {
60
+ Object.keys(onEvents).forEach(eventName => {
61
+ instance.on(eventName, onEvents[eventName]);
62
+ });
63
+ }
64
+
65
+ // 初始化回调
66
+ if (onChartInit) {
67
+ onChartInit(instance);
68
+ }
69
+
70
+ // 准备好回调
71
+ if (onChartReady) {
72
+ onChartReady(instance);
73
+ }
74
+ },
75
+ });
76
+
77
+ // 获取适配器并初始化
78
+ const adapter = getAdapter(initConfig);
79
+ adapter.init();
80
+
81
+ // 组件卸载时清理
82
+ return () => {
83
+ if (chartInstance.current) {
84
+ // 解绑事件
85
+ if (onEvents) {
86
+ Object.keys(onEvents).forEach(eventName => {
87
+ chartInstance.current?.off(eventName);
88
+ });
89
+ }
90
+ chartInstance.current.dispose();
91
+ chartInstance.current = null;
92
+ }
93
+ };
94
+ }, [adapterConfig, onChartInit, onChartReady, onEvents]);
95
+
96
+ // 更新配置
97
+ useEffect(() => {
98
+ if (chartInstance.current) {
99
+ chartInstance.current.setOption(option, true);
100
+ }
101
+ }, [option]);
102
+
103
+ // 控制加载状态
104
+ useEffect(() => {
105
+ if (chartInstance.current) {
106
+ if (loading) {
107
+ chartInstance.current.showLoading(loadingOption);
108
+ } else {
109
+ chartInstance.current.hideLoading();
110
+ }
111
+ }
112
+ }, [loading, loadingOption]);
113
+
114
+ // 自定义样式
115
+ const mergedStyle = {
116
+ width: typeof width === 'number' ? `${width}px` : width,
117
+ height: typeof height === 'number' ? `${height}px` : height,
118
+ ...style,
119
+ };
120
+
121
+ // 使用带有处理的适配器配置创建适配器实例
122
+ const adapter = getAdapter(adapterConfig);
123
+
124
+ return (
125
+ <div
126
+ className={`taroviz-${chartType} ${className}`}
127
+ style={mergedStyle}
128
+ ref={containerRef as React.RefObject<HTMLDivElement>}
129
+ >
130
+ {/* 安全地渲染适配器 */}
131
+ {safeRenderAdapter(adapter)}
132
+ </div>
133
+ );
134
+ };
135
+
136
+ export default BaseChartWrapper;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * 漏斗图组件
3
+ */
4
+ import { FunnelChart as FunnelChartComponent } from 'echarts/charts';
5
+ import {
6
+ GridComponent,
7
+ TooltipComponent,
8
+ TitleComponent,
9
+ LegendComponent,
10
+ } from 'echarts/components';
11
+ import * as echarts from 'echarts/core';
12
+ import React from 'react';
13
+
14
+ import BaseChartWrapper from '../common/BaseChartWrapper';
15
+ import { FunnelChartProps } from '../types';
16
+
17
+ // 注册必要的组件
18
+ echarts.use([
19
+ FunnelChartComponent,
20
+ GridComponent,
21
+ TooltipComponent,
22
+ TitleComponent,
23
+ LegendComponent,
24
+ ]);
25
+
26
+ /**
27
+ * 漏斗图组件
28
+ */
29
+ const FunnelChart: React.FC<FunnelChartProps> = props => (
30
+ <BaseChartWrapper {...props} chartType="funnel-chart" />
31
+ );
32
+
33
+ export default FunnelChart;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * 仪表盘组件
3
+ */
4
+ import { GaugeChart as GaugeChartComponent } from 'echarts/charts';
5
+ import {
6
+ GridComponent,
7
+ TooltipComponent,
8
+ TitleComponent,
9
+ LegendComponent,
10
+ } from 'echarts/components';
11
+ import * as echarts from 'echarts/core';
12
+ import React from 'react';
13
+
14
+ import BaseChartWrapper from '../common/BaseChartWrapper';
15
+ import { GaugeChartProps } from '../types';
16
+
17
+ // 注册必要的组件
18
+ echarts.use([
19
+ GaugeChartComponent,
20
+ GridComponent,
21
+ TooltipComponent,
22
+ TitleComponent,
23
+ LegendComponent,
24
+ ]);
25
+
26
+ /**
27
+ * 仪表盘组件
28
+ */
29
+ const GaugeChart: React.FC<GaugeChartProps> = props => (
30
+ <BaseChartWrapper {...props} chartType="gauge-chart" />
31
+ );
32
+
33
+ export default GaugeChart;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * 热力图组件
3
+ */
4
+ import { HeatmapChart as HeatmapChartComponent } from 'echarts/charts';
5
+ import {
6
+ GridComponent,
7
+ TooltipComponent,
8
+ TitleComponent,
9
+ LegendComponent,
10
+ } from 'echarts/components';
11
+ import * as echarts from 'echarts/core';
12
+ import React from 'react';
13
+
14
+ import BaseChartWrapper from '../common/BaseChartWrapper';
15
+ import { HeatmapChartProps } from '../types';
16
+
17
+ // 注册必要的组件
18
+ echarts.use([
19
+ HeatmapChartComponent,
20
+ GridComponent,
21
+ TooltipComponent,
22
+ TitleComponent,
23
+ LegendComponent,
24
+ ]);
25
+
26
+ /**
27
+ * 热力图组件
28
+ */
29
+ const HeatmapChart: React.FC<HeatmapChartProps> = props => (
30
+ <BaseChartWrapper {...props} chartType="heatmap-chart" />
31
+ );
32
+
33
+ export default HeatmapChart;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * TaroViz 图表组件集合
3
+ */
4
+
5
+ // 导出基础图表组件
6
+ export { default as LineChart } from './line';
7
+ export { default as BarChart } from './bar';
8
+ export { default as PieChart } from './pie';
9
+ export { default as ScatterChart } from './scatter';
10
+ export { default as RadarChart } from './radar';
11
+ export { default as HeatmapChart } from './heatmap';
12
+ export { default as GaugeChart } from './gauge';
13
+ export { default as FunnelChart } from './funnel';
14
+
15
+ // 导出类型定义
16
+ export * from './types';
17
+
18
+ /**
19
+ * 版本信息
20
+ */
21
+ export const version = '1.1.1';
@@ -0,0 +1,107 @@
1
+ import { render } from '@testing-library/react';
2
+ import React from 'react';
3
+
4
+ import LineChart from '../index';
5
+
6
+ // Mock ECharts and adapters
7
+ jest.mock('echarts/core', () => ({
8
+ use: jest.fn(),
9
+ init: jest.fn(() => ({
10
+ setOption: jest.fn(),
11
+ showLoading: jest.fn(),
12
+ hideLoading: jest.fn(),
13
+ on: jest.fn(),
14
+ off: jest.fn(),
15
+ dispose: jest.fn(),
16
+ resize: jest.fn(),
17
+ })),
18
+ getInstanceByDom: jest.fn(),
19
+ }));
20
+
21
+ // Mock specific ECharts chart imports
22
+ jest.mock('echarts/charts', () => ({
23
+ LineChart: jest.fn(),
24
+ }));
25
+
26
+ jest.mock('echarts/components', () => ({
27
+ GridComponent: jest.fn(),
28
+ TooltipComponent: jest.fn(),
29
+ TitleComponent: jest.fn(),
30
+ LegendComponent: jest.fn(),
31
+ }));
32
+
33
+ jest.mock('../../common/BaseChartWrapper', () => ({
34
+ __esModule: true,
35
+ default: (props: any) => (
36
+ <div
37
+ data-testid="base-chart-wrapper"
38
+ className={`taroviz-${props.chartType} ${props.className || ''}`}
39
+ style={{ width: props.width, height: props.height }}
40
+ >
41
+ <div data-testid="chart-option">{JSON.stringify(props.option)}</div>
42
+ </div>
43
+ ),
44
+ }));
45
+
46
+ describe('LineChart Component', () => {
47
+ const mockOption = {
48
+ xAxis: {
49
+ type: 'category' as const,
50
+ data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
51
+ },
52
+ yAxis: {
53
+ type: 'value' as const,
54
+ },
55
+ series: [
56
+ {
57
+ data: [150, 230, 224, 218, 135, 147, 260],
58
+ type: 'line' as const,
59
+ },
60
+ ],
61
+ };
62
+
63
+ it('should render correctly with default props', () => {
64
+ const { getByTestId } = render(<LineChart option={mockOption} />);
65
+
66
+ const chartWrapper = getByTestId('base-chart-wrapper');
67
+ expect(chartWrapper).toBeInTheDocument();
68
+ expect(chartWrapper).toHaveClass('taroviz-line-chart');
69
+ });
70
+
71
+ it('should pass the correct option to BaseChartWrapper', () => {
72
+ const { getByTestId } = render(<LineChart option={mockOption} />);
73
+
74
+ const chartOption = getByTestId('chart-option');
75
+ expect(JSON.parse(chartOption.textContent || '')).toEqual(mockOption);
76
+ });
77
+
78
+ it('should render with custom width and height', () => {
79
+ const customWidth = '500px';
80
+ const customHeight = '400px';
81
+
82
+ const { getByTestId } = render(
83
+ <LineChart option={mockOption} width={customWidth} height={customHeight} />
84
+ );
85
+
86
+ const chartWrapper = getByTestId('base-chart-wrapper');
87
+ expect(chartWrapper).toHaveStyle(`width: ${customWidth}`);
88
+ expect(chartWrapper).toHaveStyle(`height: ${customHeight}`);
89
+ });
90
+
91
+ it('should render with custom className', () => {
92
+ const customClass = 'custom-line-chart';
93
+
94
+ const { getByTestId } = render(<LineChart option={mockOption} className={customClass} />);
95
+
96
+ const chartWrapper = getByTestId('base-chart-wrapper');
97
+ expect(chartWrapper).toHaveClass(customClass);
98
+ });
99
+
100
+ it('should render with loading state', () => {
101
+ const { getByTestId } = render(<LineChart option={mockOption} loading={true} />);
102
+
103
+ const chartWrapper = getByTestId('base-chart-wrapper');
104
+ expect(chartWrapper).toBeInTheDocument();
105
+ // The loading state is handled by BaseChartWrapper, which we've mocked
106
+ });
107
+ });
@@ -0,0 +1,27 @@
1
+ /**
2
+ * 折线图组件
3
+ */
4
+ import { LineChart as LineChartComponent } from 'echarts/charts';
5
+ import {
6
+ GridComponent,
7
+ TooltipComponent,
8
+ TitleComponent,
9
+ LegendComponent,
10
+ } from 'echarts/components';
11
+ import * as echarts from 'echarts/core';
12
+ import React from 'react';
13
+
14
+ import BaseChartWrapper from '../common/BaseChartWrapper';
15
+ import { LineChartProps } from '../types';
16
+
17
+ // 注册必要的组件
18
+ echarts.use([LineChartComponent, GridComponent, TooltipComponent, TitleComponent, LegendComponent]);
19
+
20
+ /**
21
+ * 折线图组件
22
+ */
23
+ const LineChart: React.FC<LineChartProps> = props => (
24
+ <BaseChartWrapper {...props} chartType="line-chart" />
25
+ );
26
+
27
+ export default LineChart;
@@ -0,0 +1,112 @@
1
+ import { render } from '@testing-library/react';
2
+ import React from 'react';
3
+
4
+ import PieChart from '../index';
5
+
6
+ // Mock ECharts and adapters
7
+ jest.mock('echarts/core', () => ({
8
+ use: jest.fn(),
9
+ init: jest.fn(() => ({
10
+ setOption: jest.fn(),
11
+ showLoading: jest.fn(),
12
+ hideLoading: jest.fn(),
13
+ on: jest.fn(),
14
+ off: jest.fn(),
15
+ dispose: jest.fn(),
16
+ resize: jest.fn(),
17
+ })),
18
+ getInstanceByDom: jest.fn(),
19
+ }));
20
+
21
+ // Mock specific ECharts chart imports
22
+ jest.mock('echarts/charts', () => ({
23
+ PieChart: jest.fn(),
24
+ }));
25
+
26
+ jest.mock('echarts/components', () => ({
27
+ GridComponent: jest.fn(),
28
+ TooltipComponent: jest.fn(),
29
+ TitleComponent: jest.fn(),
30
+ LegendComponent: jest.fn(),
31
+ }));
32
+
33
+ jest.mock('../../common/BaseChartWrapper', () => ({
34
+ __esModule: true,
35
+ default: (props: any) => (
36
+ <div
37
+ data-testid="base-chart-wrapper"
38
+ className={`taroviz-${props.chartType} ${props.className || ''}`}
39
+ style={{ width: props.width, height: props.height }}
40
+ >
41
+ <div data-testid="chart-option">{JSON.stringify(props.option)}</div>
42
+ </div>
43
+ ),
44
+ }));
45
+
46
+ describe('PieChart Component', () => {
47
+ const mockOption = {
48
+ series: [
49
+ {
50
+ type: 'pie' as const,
51
+ data: [
52
+ { value: 1048, name: 'Search Engine' },
53
+ { value: 735, name: 'Direct' },
54
+ { value: 580, name: 'Email' },
55
+ { value: 484, name: 'Union Ads' },
56
+ { value: 300, name: 'Video Ads' },
57
+ ],
58
+ },
59
+ ],
60
+ };
61
+
62
+ it('should render correctly with default props', () => {
63
+ const { getByTestId } = render(<PieChart option={mockOption} />);
64
+
65
+ const chartWrapper = getByTestId('base-chart-wrapper');
66
+ expect(chartWrapper).toBeInTheDocument();
67
+ expect(chartWrapper).toHaveClass('taroviz-pie-chart');
68
+ });
69
+
70
+ it('should pass the correct option to BaseChartWrapper', () => {
71
+ const { getByTestId } = render(<PieChart option={mockOption} />);
72
+
73
+ const chartOption = getByTestId('chart-option');
74
+ expect(JSON.parse(chartOption.textContent || '')).toEqual(mockOption);
75
+ });
76
+
77
+ it('should render with custom width and height', () => {
78
+ const customWidth = '400px';
79
+ const customHeight = '400px';
80
+
81
+ const { getByTestId } = render(
82
+ <PieChart option={mockOption} width={customWidth} height={customHeight} />
83
+ );
84
+
85
+ const chartWrapper = getByTestId('base-chart-wrapper');
86
+ expect(chartWrapper).toHaveStyle(`width: ${customWidth}`);
87
+ expect(chartWrapper).toHaveStyle(`height: ${customHeight}`);
88
+ });
89
+
90
+ it('should render with custom className', () => {
91
+ const customClass = 'custom-pie-chart';
92
+
93
+ const { getByTestId } = render(<PieChart option={mockOption} className={customClass} />);
94
+
95
+ const chartWrapper = getByTestId('base-chart-wrapper');
96
+ expect(chartWrapper).toHaveClass(customClass);
97
+ });
98
+
99
+ it('should render with loading state', () => {
100
+ const { getByTestId } = render(<PieChart option={mockOption} loading={true} />);
101
+
102
+ const chartWrapper = getByTestId('base-chart-wrapper');
103
+ expect(chartWrapper).toBeInTheDocument();
104
+ });
105
+
106
+ it('should render with theme', () => {
107
+ const { getByTestId } = render(<PieChart option={mockOption} theme="dark" />);
108
+
109
+ const chartWrapper = getByTestId('base-chart-wrapper');
110
+ expect(chartWrapper).toBeInTheDocument();
111
+ });
112
+ });
@@ -0,0 +1,22 @@
1
+ /**
2
+ * 饼图组件
3
+ */
4
+ import { PieChart as PieChartComponent } from 'echarts/charts';
5
+ import { TooltipComponent, TitleComponent, LegendComponent } from 'echarts/components';
6
+ import * as echarts from 'echarts/core';
7
+ import React from 'react';
8
+
9
+ import BaseChartWrapper from '../common/BaseChartWrapper';
10
+ import { BaseChartProps } from '../types';
11
+
12
+ // 注册必要的组件
13
+ echarts.use([PieChartComponent, TooltipComponent, TitleComponent, LegendComponent]);
14
+
15
+ /**
16
+ * 饼图组件
17
+ */
18
+ const PieChart: React.FC<BaseChartProps> = props => (
19
+ <BaseChartWrapper {...props} chartType="pie-chart" />
20
+ );
21
+
22
+ export default PieChart;