@agions/taroviz 1.3.1 → 1.6.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.
- package/README.md +4 -4
- package/dist/cjs/index.js +1 -1
- package/dist/esm/index.js +3814 -2724
- package/package.json +1 -1
- package/src/__tests__/integration.test.tsx +12 -10
- package/src/adapters/BaseAdapter.ts +116 -0
- package/src/adapters/__tests__/index.test.ts +10 -10
- package/src/adapters/index.ts +63 -74
- package/src/adapters/swan/index.ts +26 -223
- package/src/adapters/tt/index.ts +28 -225
- package/src/adapters/types.ts +36 -0
- package/src/adapters/weapp/index.ts +29 -189
- package/src/charts/bar/index.tsx +5 -9
- package/src/charts/boxplot/__tests__/index.test.tsx +130 -0
- package/src/charts/boxplot/index.tsx +18 -0
- package/src/charts/boxplot/types.ts +46 -0
- package/src/charts/candlestick/__tests__/index.test.tsx +37 -0
- package/src/charts/candlestick/index.tsx +13 -0
- package/src/charts/common/BaseChartWrapper.tsx +47 -38
- package/src/charts/funnel/index.tsx +5 -9
- package/src/charts/gauge/index.tsx +5 -9
- package/src/charts/graph/__tests__/index.test.tsx +41 -0
- package/src/charts/graph/index.tsx +13 -0
- package/src/charts/heatmap/index.tsx +5 -9
- package/src/charts/index.ts +10 -1
- package/src/charts/line/index.tsx +4 -7
- package/src/charts/parallel/__tests__/index.test.tsx +164 -0
- package/src/charts/parallel/index.tsx +18 -0
- package/src/charts/parallel/types.ts +73 -0
- package/src/charts/pie/index.tsx +5 -10
- package/src/charts/radar/index.tsx +5 -9
- package/src/charts/scatter/index.tsx +5 -9
- package/src/charts/types.ts +48 -4
- package/src/charts/wordcloud/__tests__/index.test.tsx +36 -0
- package/src/charts/wordcloud/index.tsx +13 -0
- package/src/core/animation/AnimationManager.ts +15 -0
- package/src/core/components/Annotation.tsx +26 -21
- package/src/core/components/BaseChart.tsx +280 -1105
- package/src/core/components/ErrorBoundary.tsx +4 -1
- package/src/core/components/LazyChart.tsx +42 -55
- package/src/core/components/hooks/index.ts +20 -0
- package/src/core/components/hooks/useChartEvents.ts +143 -0
- package/src/core/components/hooks/useChartInit.ts +80 -0
- package/src/core/components/hooks/usePerformance.ts +186 -0
- package/src/core/components/hooks/useVirtualScroll.ts +156 -0
- package/src/core/echarts.ts +1 -1
- package/src/core/themes/ThemeManager.ts +31 -15
- package/src/core/types/index.ts +2 -2
- package/src/core/utils/chartInstances.ts +10 -3
- package/src/core/utils/chartUtils.ts +46 -0
- package/src/core/utils/common.ts +14 -1
- package/src/core/utils/export/ExportUtils.ts +13 -22
- package/src/core/utils/performance/PerformanceAnalyzer.ts +32 -5
- package/src/core/utils/uuid.ts +1 -1
- package/src/editor/EnhancedThemeEditor.tsx +624 -0
- package/src/editor/ThemeEditor.tsx +1 -6
- package/src/hooks/__tests__/index.test.tsx +14 -11
- package/src/hooks/__tests__/useDataTransform.test.ts +159 -0
- package/src/hooks/index.ts +54 -19
- package/src/hooks/useDataTransform.ts +503 -0
- package/src/index.ts +27 -9
- package/src/main.tsx +4 -4
- package/src/themes/__tests__/index.test.ts +2 -2
- package/src/themes/index.ts +13 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import { render } from '@testing-library/react';
|
|
6
|
+
import '@testing-library/jest-dom';
|
|
7
|
+
import BoxplotChart from '../index';
|
|
8
|
+
|
|
9
|
+
// Mock ECharts and adapters
|
|
10
|
+
jest.mock('echarts/core', () => ({
|
|
11
|
+
use: jest.fn(),
|
|
12
|
+
init: jest.fn(() => ({
|
|
13
|
+
setOption: jest.fn(),
|
|
14
|
+
showLoading: jest.fn(),
|
|
15
|
+
hideLoading: jest.fn(),
|
|
16
|
+
on: jest.fn(),
|
|
17
|
+
off: jest.fn(),
|
|
18
|
+
dispose: jest.fn(),
|
|
19
|
+
resize: jest.fn(),
|
|
20
|
+
})),
|
|
21
|
+
getInstanceByDom: jest.fn(),
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
// Mock ECharts components
|
|
25
|
+
jest.mock('echarts/components', () => ({
|
|
26
|
+
GridComponent: jest.fn(),
|
|
27
|
+
TooltipComponent: jest.fn(),
|
|
28
|
+
TitleComponent: jest.fn(),
|
|
29
|
+
LegendComponent: jest.fn(),
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
// Mock BaseChartWrapper
|
|
33
|
+
jest.mock('../../common/BaseChartWrapper', () => ({
|
|
34
|
+
__esModule: true,
|
|
35
|
+
default: (props: any) => (
|
|
36
|
+
<div
|
|
37
|
+
data-testid="boxplot-chart"
|
|
38
|
+
className={`taroviz-boxplot ${props.className || ''}`}
|
|
39
|
+
style={{ width: props.width || '100%', height: props.height || 300, ...props.style }}
|
|
40
|
+
>
|
|
41
|
+
<div data-testid="chart-option">{JSON.stringify(props.option)}</div>
|
|
42
|
+
</div>
|
|
43
|
+
),
|
|
44
|
+
}));
|
|
45
|
+
|
|
46
|
+
describe('BoxplotChart', () => {
|
|
47
|
+
const basicOption = {
|
|
48
|
+
title: { text: '箱线图测试' },
|
|
49
|
+
xAxis: { type: 'category' as const, data: ['A', 'B', 'C'] },
|
|
50
|
+
yAxis: { type: 'value' as const },
|
|
51
|
+
series: [{
|
|
52
|
+
type: 'boxplot' as const,
|
|
53
|
+
data: [
|
|
54
|
+
[850, 940, 980, 1050, 1130],
|
|
55
|
+
[920, 1000, 1050, 1150, 1200],
|
|
56
|
+
[780, 850, 920, 1050, 1150],
|
|
57
|
+
],
|
|
58
|
+
}],
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
describe('Basic Rendering', () => {
|
|
62
|
+
it('should render without crashing', () => {
|
|
63
|
+
const { getByTestId } = render(<BoxplotChart option={basicOption} />);
|
|
64
|
+
expect(getByTestId('boxplot-chart')).toBeInTheDocument();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should render with custom width and height', () => {
|
|
68
|
+
const { getByTestId } = render(<BoxplotChart option={basicOption} width={600} height={500} />);
|
|
69
|
+
expect(getByTestId('boxplot-chart')).toBeInTheDocument();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should have correct display name', () => {
|
|
73
|
+
expect(BoxplotChart.displayName).toBe('BoxplotChart');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('should pass option to wrapper', () => {
|
|
77
|
+
const { getByTestId } = render(<BoxplotChart option={basicOption} />);
|
|
78
|
+
expect(getByTestId('chart-option')).toHaveTextContent('箱线图测试');
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe('Props', () => {
|
|
83
|
+
it('should accept className prop', () => {
|
|
84
|
+
const { getByTestId } = render(<BoxplotChart option={basicOption} className="test-class" />);
|
|
85
|
+
expect(getByTestId('boxplot-chart')).toHaveClass('test-class');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should accept style prop', () => {
|
|
89
|
+
const { getByTestId } = render(<BoxplotChart option={basicOption} style={{ padding: '10px' }} />);
|
|
90
|
+
expect(getByTestId('boxplot-chart')).toBeInTheDocument();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should accept loading prop', () => {
|
|
94
|
+
const { getByTestId } = render(<BoxplotChart option={basicOption} loading={true} />);
|
|
95
|
+
expect(getByTestId('boxplot-chart')).toBeInTheDocument();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should accept theme prop', () => {
|
|
99
|
+
const { getByTestId } = render(<BoxplotChart option={basicOption} theme="dark" />);
|
|
100
|
+
expect(getByTestId('boxplot-chart')).toBeInTheDocument();
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe('Chart Options', () => {
|
|
105
|
+
it('should render with multiple data series', () => {
|
|
106
|
+
const multiOption = {
|
|
107
|
+
...basicOption,
|
|
108
|
+
series: [
|
|
109
|
+
{ type: 'boxplot' as const, name: '2024', data: [[850, 940, 980, 1050, 1130]] },
|
|
110
|
+
{ type: 'boxplot' as const, name: '2025', data: [[920, 1000, 1050, 1150, 1200]] },
|
|
111
|
+
],
|
|
112
|
+
};
|
|
113
|
+
const { getByTestId } = render(<BoxplotChart option={multiOption} />);
|
|
114
|
+
expect(getByTestId('boxplot-chart')).toBeInTheDocument();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should render with custom itemStyle', () => {
|
|
118
|
+
const optionWithStyle = {
|
|
119
|
+
...basicOption,
|
|
120
|
+
series: [{
|
|
121
|
+
type: 'boxplot' as const,
|
|
122
|
+
data: [[850, 940, 980, 1050, 1130]],
|
|
123
|
+
itemStyle: { color: '#1890ff', borderColor: '#000' },
|
|
124
|
+
}],
|
|
125
|
+
};
|
|
126
|
+
const { getByTestId } = render(<BoxplotChart option={optionWithStyle} />);
|
|
127
|
+
expect(getByTestId('boxplot-chart')).toBeInTheDocument();
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 箱线图组件
|
|
3
|
+
* 用于展示数据的分布情况,包括最小值、Q1、中位数、Q3、最大值
|
|
4
|
+
*/
|
|
5
|
+
import React, { memo } from 'react';
|
|
6
|
+
import BaseChartWrapper from '../common/BaseChartWrapper';
|
|
7
|
+
import { BoxplotChartProps } from './types';
|
|
8
|
+
|
|
9
|
+
const BoxplotChart: React.FC<BoxplotChartProps> = memo((props) => (
|
|
10
|
+
<BaseChartWrapper {...props} chartType="boxplot" />
|
|
11
|
+
));
|
|
12
|
+
|
|
13
|
+
BoxplotChart.displayName = 'BoxplotChart';
|
|
14
|
+
|
|
15
|
+
export default BoxplotChart;
|
|
16
|
+
|
|
17
|
+
// 导出类型
|
|
18
|
+
export type { BoxplotChartProps, BoxplotOption, BoxplotSeriesItem } from './types';
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 箱线图类型定义
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export type BoxplotChartProps = {
|
|
6
|
+
option?: BoxplotOption;
|
|
7
|
+
width?: string | number;
|
|
8
|
+
height?: string | number;
|
|
9
|
+
className?: string;
|
|
10
|
+
style?: React.CSSProperties;
|
|
11
|
+
onEvents?: Record<string, (params: any) => void>;
|
|
12
|
+
loading?: boolean;
|
|
13
|
+
loadingOption?: any;
|
|
14
|
+
theme?: string;
|
|
15
|
+
onChartReady?: (chart: any) => void;
|
|
16
|
+
opts?: {
|
|
17
|
+
devicePixelRatio?: number;
|
|
18
|
+
renderer?: 'canvas' | 'svg';
|
|
19
|
+
useDirtyRect?: boolean;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export interface BoxplotOption {
|
|
24
|
+
title?: any;
|
|
25
|
+
legend?: any;
|
|
26
|
+
grid?: any;
|
|
27
|
+
xAxis?: any;
|
|
28
|
+
yAxis?: any;
|
|
29
|
+
tooltip?: any;
|
|
30
|
+
series: BoxplotSeriesItem[];
|
|
31
|
+
dataset?: any;
|
|
32
|
+
color?: string[];
|
|
33
|
+
backgroundColor?: any;
|
|
34
|
+
textStyle?: any;
|
|
35
|
+
[key: string]: any;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface BoxplotSeriesItem {
|
|
39
|
+
type: 'boxplot';
|
|
40
|
+
name?: string;
|
|
41
|
+
data?: any[];
|
|
42
|
+
itemStyle?: any;
|
|
43
|
+
emphasis?: any;
|
|
44
|
+
dimensions?: string[];
|
|
45
|
+
encode?: any;
|
|
46
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @version v1.5.0
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import { render } from '@testing-library/react';
|
|
7
|
+
import CandlestickChart from '../index';
|
|
8
|
+
|
|
9
|
+
describe('CandlestickChart', () => {
|
|
10
|
+
it('renders without crashing', () => {
|
|
11
|
+
const { container } = render(<CandlestickChart />);
|
|
12
|
+
expect(container).toBeTruthy();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('renders with custom width and height', () => {
|
|
16
|
+
const { container } = render(<CandlestickChart width={500} height={400} />);
|
|
17
|
+
expect(container.firstChild).toHaveStyle({ width: '500px', height: '400px' });
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('renders with stock data', () => {
|
|
21
|
+
const option = {
|
|
22
|
+
xAxis: { data: ['2024-01', '2024-02', '2024-03'] },
|
|
23
|
+
series: [
|
|
24
|
+
{
|
|
25
|
+
type: 'candlestick' as const,
|
|
26
|
+
data: [
|
|
27
|
+
[20, 30, 15, 35], // [open, close, lowest, highest]
|
|
28
|
+
[25, 35, 20, 40],
|
|
29
|
+
[30, 25, 20, 35],
|
|
30
|
+
],
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
};
|
|
34
|
+
const { container } = render(<CandlestickChart option={option} />);
|
|
35
|
+
expect(container).toBeTruthy();
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CandlestickChart组件
|
|
3
|
+
*/
|
|
4
|
+
import React, { memo } from 'react';
|
|
5
|
+
import BaseChartWrapper from '../common/BaseChartWrapper';
|
|
6
|
+
import { CandlestickChartProps } from '../types';
|
|
7
|
+
|
|
8
|
+
const CandlestickChart: React.FC<CandlestickChartProps> = memo((props) => (
|
|
9
|
+
<BaseChartWrapper {...props} chartType="candlestick-chart" />
|
|
10
|
+
));
|
|
11
|
+
CandlestickChart.displayName = 'CandlestickChart';
|
|
12
|
+
|
|
13
|
+
export default CandlestickChart;
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* 基础图表包装组件
|
|
3
3
|
* 提供统一的图表初始化、渲染和生命周期管理
|
|
4
4
|
*/
|
|
5
|
-
import * as echarts from 'echarts/core';
|
|
6
5
|
import React, { useEffect, useRef, useMemo } from 'react';
|
|
7
6
|
|
|
8
7
|
import { getAdapter } from '../../adapters';
|
|
@@ -31,7 +30,7 @@ const BaseChartWrapper: React.FC<BaseChartProps & { chartType: string }> = ({
|
|
|
31
30
|
chartType = 'chart',
|
|
32
31
|
}) => {
|
|
33
32
|
const chartId = useRef<string>(`${chartType}-${uuid()}`);
|
|
34
|
-
const chartInstance = useRef<
|
|
33
|
+
const chartInstance = useRef<any>(null);
|
|
35
34
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
36
35
|
|
|
37
36
|
// 使用 useMemo 缓存适配器配置,并处理类型问题
|
|
@@ -46,56 +45,66 @@ const BaseChartWrapper: React.FC<BaseChartProps & { chartType: string }> = ({
|
|
|
46
45
|
renderer,
|
|
47
46
|
option,
|
|
48
47
|
});
|
|
49
|
-
}, [width, height, theme, autoResize, renderer, option
|
|
48
|
+
}, [width, height, theme, autoResize, renderer, option]);
|
|
50
49
|
|
|
51
50
|
// 处理图表初始化
|
|
52
51
|
useEffect(() => {
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
// 绑定事件
|
|
59
|
-
if (onEvents) {
|
|
60
|
-
Object.keys(onEvents).forEach((eventName) => {
|
|
61
|
-
instance.on(eventName, onEvents[eventName]);
|
|
62
|
-
});
|
|
63
|
-
}
|
|
52
|
+
const initChart = async () => {
|
|
53
|
+
const initConfig = processAdapterConfig({
|
|
54
|
+
...adapterConfig,
|
|
55
|
+
onInit: (instance: any) => {
|
|
56
|
+
chartInstance.current = instance;
|
|
64
57
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
58
|
+
// 绑定事件
|
|
59
|
+
if (onEvents) {
|
|
60
|
+
Object.keys(onEvents).forEach((eventName) => {
|
|
61
|
+
instance.on(eventName, (onEvents as any)[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 = await getAdapter(initConfig);
|
|
79
|
+
adapter.init();
|
|
69
80
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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;
|
|
73
92
|
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
93
|
+
};
|
|
94
|
+
};
|
|
76
95
|
|
|
77
|
-
//
|
|
78
|
-
const
|
|
79
|
-
adapter.init();
|
|
96
|
+
// 执行异步初始化并获取清理函数
|
|
97
|
+
const cleanupPromise = initChart();
|
|
80
98
|
|
|
81
|
-
//
|
|
99
|
+
// 返回清理函数
|
|
82
100
|
return () => {
|
|
83
|
-
|
|
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
|
-
}
|
|
101
|
+
cleanupPromise.then((cleanup) => cleanup?.());
|
|
93
102
|
};
|
|
94
103
|
}, [adapterConfig, onChartInit, onChartReady, onEvents]);
|
|
95
104
|
|
|
96
105
|
// 更新配置
|
|
97
106
|
useEffect(() => {
|
|
98
|
-
if (chartInstance.current) {
|
|
107
|
+
if (chartInstance.current && option) {
|
|
99
108
|
chartInstance.current.setOption(option, true);
|
|
100
109
|
}
|
|
101
110
|
}, [option]);
|
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* FunnelChart组件
|
|
3
3
|
*/
|
|
4
|
-
import React from 'react';
|
|
5
|
-
|
|
4
|
+
import React, { memo } from 'react';
|
|
6
5
|
import BaseChartWrapper from '../common/BaseChartWrapper';
|
|
7
6
|
import { FunnelChartProps } from '../types';
|
|
8
|
-
|
|
9
7
|
import '@/core/echarts';
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
* 漏斗图组件
|
|
13
|
-
*/
|
|
14
|
-
const FunnelChart: React.FC<FunnelChartProps> = (props) => (
|
|
9
|
+
const FunnelChart: React.FC<FunnelChartProps> = memo((props) => (
|
|
15
10
|
<BaseChartWrapper {...props} chartType="funnel-chart" />
|
|
16
|
-
);
|
|
11
|
+
));
|
|
12
|
+
FunnelChart.displayName = 'FunnelChart';
|
|
17
13
|
|
|
18
14
|
export default FunnelChart;
|
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* GaugeChart组件
|
|
3
3
|
*/
|
|
4
|
-
import React from 'react';
|
|
5
|
-
|
|
4
|
+
import React, { memo } from 'react';
|
|
6
5
|
import BaseChartWrapper from '../common/BaseChartWrapper';
|
|
7
6
|
import { GaugeChartProps } from '../types';
|
|
8
|
-
|
|
9
7
|
import '@/core/echarts';
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
* 仪表盘组件
|
|
13
|
-
*/
|
|
14
|
-
const GaugeChart: React.FC<GaugeChartProps> = (props) => (
|
|
9
|
+
const GaugeChart: React.FC<GaugeChartProps> = memo((props) => (
|
|
15
10
|
<BaseChartWrapper {...props} chartType="gauge-chart" />
|
|
16
|
-
);
|
|
11
|
+
));
|
|
12
|
+
GaugeChart.displayName = 'GaugeChart';
|
|
17
13
|
|
|
18
14
|
export default GaugeChart;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @version v1.5.0
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import { render } from '@testing-library/react';
|
|
7
|
+
import GraphChart from '../index';
|
|
8
|
+
|
|
9
|
+
describe('GraphChart', () => {
|
|
10
|
+
it('renders without crashing', () => {
|
|
11
|
+
const { container } = render(<GraphChart />);
|
|
12
|
+
expect(container).toBeTruthy();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('renders with custom className', () => {
|
|
16
|
+
const { container } = render(<GraphChart className="test-graph" />);
|
|
17
|
+
expect(container.firstChild).toHaveClass('test-graph');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('renders with custom width and height', () => {
|
|
21
|
+
const { container } = render(<GraphChart width={500} height={400} />);
|
|
22
|
+
expect(container.firstChild).toHaveStyle({ width: '500px', height: '400px' });
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('renders with basic option', () => {
|
|
26
|
+
const option = {
|
|
27
|
+
series: [
|
|
28
|
+
{
|
|
29
|
+
type: 'graph' as const,
|
|
30
|
+
nodes: [
|
|
31
|
+
{ id: '1', name: 'Node 1' },
|
|
32
|
+
{ id: '2', name: 'Node 2' },
|
|
33
|
+
],
|
|
34
|
+
links: [{ source: '1', target: '2' }],
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
};
|
|
38
|
+
const { container } = render(<GraphChart option={option} />);
|
|
39
|
+
expect(container).toBeTruthy();
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GraphChart组件
|
|
3
|
+
*/
|
|
4
|
+
import React, { memo } from 'react';
|
|
5
|
+
import BaseChartWrapper from '../common/BaseChartWrapper';
|
|
6
|
+
import { GraphChartProps } from '../types';
|
|
7
|
+
|
|
8
|
+
const GraphChart: React.FC<GraphChartProps> = memo((props) => (
|
|
9
|
+
<BaseChartWrapper {...props} chartType="graph-chart" />
|
|
10
|
+
));
|
|
11
|
+
GraphChart.displayName = 'GraphChart';
|
|
12
|
+
|
|
13
|
+
export default GraphChart;
|
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* HeatmapChart组件
|
|
3
3
|
*/
|
|
4
|
-
import React from 'react';
|
|
5
|
-
|
|
4
|
+
import React, { memo } from 'react';
|
|
6
5
|
import BaseChartWrapper from '../common/BaseChartWrapper';
|
|
7
6
|
import { HeatmapChartProps } from '../types';
|
|
8
|
-
|
|
9
7
|
import '@/core/echarts';
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
* 热力图组件
|
|
13
|
-
*/
|
|
14
|
-
const HeatmapChart: React.FC<HeatmapChartProps> = (props) => (
|
|
9
|
+
const HeatmapChart: React.FC<HeatmapChartProps> = memo((props) => (
|
|
15
10
|
<BaseChartWrapper {...props} chartType="heatmap-chart" />
|
|
16
|
-
);
|
|
11
|
+
));
|
|
12
|
+
HeatmapChart.displayName = 'HeatmapChart';
|
|
17
13
|
|
|
18
14
|
export default HeatmapChart;
|
package/src/charts/index.ts
CHANGED
|
@@ -17,10 +17,19 @@ export { default as TreeMapChart } from './treemap';
|
|
|
17
17
|
export { default as SunburstChart } from './sunburst';
|
|
18
18
|
export { default as SankeyChart } from './sankey';
|
|
19
19
|
|
|
20
|
+
// 导出新增图表组件
|
|
21
|
+
export { default as GraphChart } from './graph';
|
|
22
|
+
export { default as CandlestickChart } from './candlestick';
|
|
23
|
+
export { default as WordCloudChart } from './wordcloud';
|
|
24
|
+
|
|
25
|
+
// 导出 v1.6.0 新增图表组件
|
|
26
|
+
export { default as BoxplotChart } from './boxplot';
|
|
27
|
+
export { default as ParallelChart } from './parallel';
|
|
28
|
+
|
|
20
29
|
// 导出类型定义
|
|
21
30
|
export * from './types';
|
|
22
31
|
|
|
23
32
|
/**
|
|
24
33
|
* 版本信息
|
|
25
34
|
*/
|
|
26
|
-
export const version = '1.
|
|
35
|
+
export const version = '1.6.0';
|
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 折线图组件
|
|
3
3
|
*/
|
|
4
|
-
import React from 'react';
|
|
5
|
-
|
|
4
|
+
import React, { memo } from 'react';
|
|
6
5
|
import BaseChartWrapper from '../common/BaseChartWrapper';
|
|
7
6
|
import { LineChartProps } from '../types';
|
|
8
7
|
|
|
9
8
|
import '@/core/echarts';
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
* 折线图组件
|
|
13
|
-
*/
|
|
14
|
-
const LineChart: React.FC<LineChartProps> = (props) => (
|
|
10
|
+
const LineChart: React.FC<LineChartProps> = memo((props) => (
|
|
15
11
|
<BaseChartWrapper {...props} chartType="line-chart" />
|
|
16
|
-
);
|
|
12
|
+
));
|
|
13
|
+
LineChart.displayName = 'LineChart';
|
|
17
14
|
|
|
18
15
|
export default LineChart;
|