mirror of
https://github.com/hustcc/echarts-for-react.git
synced 2025-12-08 20:16:09 +00:00
Pass unknown (non-ECharts) props through to the div element (#602)
* Pass unknown props through to the div element This fits common React conventions and allows using attributes such as `data-testid` for testing purposes. Related changes: - Adding HTMLAttributes<HTMLDivElement> to EChartsReactProps meant that EChartsReactProps was no longer usable as a `Record<string, unknown>`, which caused problems for the `pick` function. (See https://stackoverflow.com/q/65799316/25507.). I updated the pick function to use a generic type instead. - Add [ignoreRestSiblings](https://eslint.org/docs/latest/rules/no-unused-vars#ignorerestsiblings) so ESLint is happy with the new code. - Fix an unrelated Prettier warning in `componentDidUpdate`. There's a slight risk of backwards-incompatible changes in this release: IF the user previously passed unknown props (e.g., due to typos or mistaken props spreads), they were ignored, but they're now passed through to the div element. Fixes #546 * Update tests * Code review
This commit is contained in:
parent
a6945e8c49
commit
809fc46d4b
@ -18,7 +18,7 @@
|
||||
"no-unused-vars": 0, // @typescript-eslint/no-unused-vars
|
||||
"no-inner-declarations": 0,
|
||||
"prettier/prettier": 2,
|
||||
"@typescript-eslint/no-unused-vars": 1,
|
||||
"@typescript-eslint/no-unused-vars": [1, { "ignoreRestSiblings": true }],
|
||||
"@typescript-eslint/no-non-null-assertion": 0,
|
||||
"@typescript-eslint/no-explicit-any": 0,
|
||||
"@typescript-eslint/no-use-before-define": [2, { "functions": false }],
|
||||
|
||||
@ -191,4 +191,29 @@ describe('chart', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('props', () => {
|
||||
it('default', () => {
|
||||
let instance;
|
||||
const div = createDiv();
|
||||
div.style.display = 'block';
|
||||
div.style.width = '1200px';
|
||||
div.style.height = '720px';
|
||||
|
||||
const opts = {
|
||||
width: null,
|
||||
height: null,
|
||||
};
|
||||
|
||||
const Comp = <ReactECharts ref={(e) => (instance = e)} option={options} opts={opts} data-testid="props-test" />;
|
||||
render(Comp, div);
|
||||
|
||||
expect(div.querySelector('[data-testid="props-test"]')).toBe(instance.ele);
|
||||
|
||||
destroy(div);
|
||||
expect(div.querySelector('*')).toBe(null);
|
||||
|
||||
removeDom(div);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -2,9 +2,10 @@ import { pick } from '../../src/helper/pick';
|
||||
|
||||
describe('pick', () => {
|
||||
it('pick', () => {
|
||||
expect(pick({ a: 1 }, [])).toEqual({});
|
||||
expect(pick({ a: 1 }, ['b'])).toEqual({});
|
||||
expect(pick({ a: 1 }, ['a'])).toEqual({ a: 1 });
|
||||
expect(pick({ a: 1 }, ['a', 'b'])).toEqual({ a: 1 });
|
||||
const obj: { a: number; b?: number } = { a: 1 };
|
||||
expect(pick(obj, [])).toEqual({});
|
||||
expect(pick(obj, ['b'])).toEqual({});
|
||||
expect(pick(obj, ['a'])).toEqual({ a: 1 });
|
||||
expect(pick(obj, ['a', 'b'])).toEqual({ a: 1 });
|
||||
});
|
||||
});
|
||||
|
||||
92
docs/examples/html-props.md
Normal file
92
docs/examples/html-props.md
Normal file
@ -0,0 +1,92 @@
|
||||
---
|
||||
title: HTML Properties
|
||||
order: 10
|
||||
---
|
||||
|
||||
## HTML Properties
|
||||
|
||||
Unknown (non-ECharts) props are passed through to the div element.
|
||||
|
||||
```tsx
|
||||
import React from 'react';
|
||||
import ReactECharts from 'echarts-for-react';
|
||||
|
||||
const Page: React.FC = () => {
|
||||
const option = {
|
||||
title: {
|
||||
text: '堆叠区域图'
|
||||
},
|
||||
tooltip : {
|
||||
trigger: 'axis'
|
||||
},
|
||||
legend: {
|
||||
data:['邮件营销','联盟广告','视频广告']
|
||||
},
|
||||
toolbox: {
|
||||
feature: {
|
||||
saveAsImage: {}
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis : [
|
||||
{
|
||||
type : 'category',
|
||||
boundaryGap : false,
|
||||
data : ['周一','周二','周三','周四','周五','周六','周日']
|
||||
}
|
||||
],
|
||||
yAxis : [
|
||||
{
|
||||
type : 'value'
|
||||
}
|
||||
],
|
||||
series : [
|
||||
{
|
||||
name:'邮件营销',
|
||||
type:'line',
|
||||
stack: '总量',
|
||||
areaStyle: {normal: {}},
|
||||
data:[120, 132, 101, 134, 90, 230, 210]
|
||||
},
|
||||
{
|
||||
name:'联盟广告',
|
||||
type:'line',
|
||||
stack: '总量',
|
||||
areaStyle: {normal: {}},
|
||||
data:[220, 182, 191, 234, 290, 330, 310]
|
||||
},
|
||||
{
|
||||
name:'视频广告',
|
||||
type:'line',
|
||||
stack: '总量',
|
||||
areaStyle: {normal: {}},
|
||||
data:[150, 232, 201, 154, 190, 330, 410]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const handleDemoButton = () => {
|
||||
console.log(document.querySelector(['[data-testid="html-props-demo"]']));
|
||||
window.alert('Open console, see the log detail.')
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ReactECharts
|
||||
option={option}
|
||||
style={{ height: 400 }}
|
||||
role="figure"
|
||||
data-testid="html-props-demo"
|
||||
/>
|
||||
<button type="button" onClick={handleDemoButton}>Demo</button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Page;
|
||||
```
|
||||
29
src/core.tsx
29
src/core.tsx
@ -52,10 +52,7 @@ export default class EChartsReactCore extends PureComponent<EChartsReactProps> {
|
||||
// 以下属性修改的时候,需要 dispose 之后再新建
|
||||
// 1. 切换 theme 的时候
|
||||
// 2. 修改 opts 的时候
|
||||
if (
|
||||
!isEqual(prevProps.theme, this.props.theme) ||
|
||||
!isEqual(prevProps.opts, this.props.opts)
|
||||
) {
|
||||
if (!isEqual(prevProps.theme, this.props.theme) || !isEqual(prevProps.opts, this.props.opts)) {
|
||||
this.dispose();
|
||||
|
||||
this.renderNewEcharts(); // 重建
|
||||
@ -70,8 +67,8 @@ export default class EChartsReactCore extends PureComponent<EChartsReactProps> {
|
||||
}
|
||||
|
||||
// when these props are not isEqual, update echarts
|
||||
const pickKeys = ['option', 'notMerge', 'replaceMerge', 'lazyUpdate', 'showLoading', 'loadingOption'];
|
||||
if (!isEqual(pick(this.props, pickKeys), pick(prevProps, pickKeys))) {
|
||||
const pickKeys = ['option', 'notMerge', 'replaceMerge', 'lazyUpdate', 'showLoading', 'loadingOption'] as const;
|
||||
if (!isEqual(pick(this.props, pickKeys), pick(prevProps as any, pickKeys))) {
|
||||
this.updateEChartsOption();
|
||||
}
|
||||
|
||||
@ -243,7 +240,24 @@ export default class EChartsReactCore extends PureComponent<EChartsReactProps> {
|
||||
}
|
||||
|
||||
render(): JSX.Element {
|
||||
const { style, className = '' } = this.props;
|
||||
const {
|
||||
style,
|
||||
className = '',
|
||||
echarts,
|
||||
option,
|
||||
theme,
|
||||
notMerge,
|
||||
replaceMerge,
|
||||
lazyUpdate,
|
||||
showLoading,
|
||||
loadingOption,
|
||||
opts,
|
||||
onChartReady,
|
||||
onEvents,
|
||||
shouldSetOption,
|
||||
autoResize,
|
||||
...divHTMLAttributes
|
||||
} = this.props;
|
||||
// default height = 300
|
||||
const newStyle = { height: 300, ...style };
|
||||
|
||||
@ -254,6 +268,7 @@ export default class EChartsReactCore extends PureComponent<EChartsReactProps> {
|
||||
}}
|
||||
style={newStyle}
|
||||
className={`echarts-for-react ${className}`}
|
||||
{...divHTMLAttributes}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -3,8 +3,8 @@
|
||||
* @param obj
|
||||
* @param keys
|
||||
*/
|
||||
export function pick(obj: Record<string, unknown>, keys: string[]): Record<string, unknown> {
|
||||
const r = {};
|
||||
export function pick<T extends object>(obj: T, keys: readonly (keyof T)[]): Partial<T> {
|
||||
const r = {} as Partial<T>;
|
||||
keys.forEach((key) => {
|
||||
r[key] = obj[key];
|
||||
});
|
||||
|
||||
11
src/types.ts
11
src/types.ts
@ -1,4 +1,3 @@
|
||||
import type { CSSProperties } from 'react';
|
||||
import type { EChartsType } from 'echarts';
|
||||
|
||||
/**
|
||||
@ -16,19 +15,11 @@ export type Opts = {
|
||||
readonly locale?: string;
|
||||
};
|
||||
|
||||
export type EChartsReactProps = {
|
||||
export type EChartsReactProps = React.HTMLAttributes<HTMLDivElement> & {
|
||||
/**
|
||||
* echarts library entry, use it for import necessary.
|
||||
*/
|
||||
readonly echarts?: any;
|
||||
/**
|
||||
* `className` for container
|
||||
*/
|
||||
readonly className?: string;
|
||||
/**
|
||||
* `style` for container
|
||||
*/
|
||||
readonly style?: CSSProperties;
|
||||
/**
|
||||
* echarts option
|
||||
*/
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user