From e62d0cf8aaecdc135615c40ae6d95288026d97d5 Mon Sep 17 00:00:00 2001 From: Jean-Michel FRANCOIS Date: Thu, 28 Jul 2022 17:28:46 +0200 Subject: [PATCH 1/7] doc: Update CHANGELOG.md about d3 7.x (#2919) --- CHANGELOG.md | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e23fe54d5..6a4a0af385 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,71 @@ # fix - update react-smooth version +- update d3 from 6.x to 7.x it may break some tools like jest + +fix config for jest is to add the following configuration + +```javascript +const path = require('path'); +// took from d3/package.json +const d3Pkgs = [ + 'd3', + 'd3-array', + 'd3-axis', + 'd3-brush', + 'd3-chord', + 'd3-color', + 'd3-contour', + 'd3-delaunay', + 'd3-dispatch', + 'd3-drag', + 'd3-dsv', + 'd3-ease', + 'd3-fetch', + 'd3-force', + 'd3-format', + 'd3-geo', + 'd3-hierarchy', + 'd3-interpolate', + 'd3-path', + 'd3-polygon', + 'd3-quadtree', + 'd3-random', + 'd3-scale', + 'd3-scale-chromatic', + 'd3-selection', + 'd3-shape', + 'd3-time', + 'd3-time-format', + 'd3-timer', + 'd3-transition', + 'd3-zoom', +]; + +// option 1 map module to an bundled version of the package which is es5 +const moduleNameMapper = d3Pkgs.reduce((acc, pkg) => { + acc[`^${pkg}$`] = path.join(require.resolve(pkg), `../../dist/${pkg}.min.js`); + return acc; +}, {}); + +module.exports = { + moduleNameMapper: { + // option 1 + // ...moduleNameMapper + }, + transform: { + // match mjs js jsx ts tsx + '^.+\\.m?[jt]sx?$': 'babel-jest', + }, + // stop ignore node_modules transform since d3 and others start to put es6 as main of packages + transformIgnorePatterns: [ + // option 2, stop ignore transform on es6 packages + `/node_modules/(?!${d3Pkgs.join('|')}|internmap|d3-delaunay|delaunator|robust-predicates)`, + // option 3, stop ignore transform on all node_modules + // `/node_modules/(?!.*)`, + ], +}; +``` ## 2.1.11 (Jun 24, 2022) From 6b3cce4b2b9198dce5423b74d457e67ea3e4197c Mon Sep 17 00:00:00 2001 From: Dalton Craven Date: Thu, 28 Jul 2022 17:29:13 +0200 Subject: [PATCH 2/7] Add formatter function type to tooltip props (#2916) --- src/component/DefaultTooltipContent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/component/DefaultTooltipContent.tsx b/src/component/DefaultTooltipContent.tsx index f8ab0e763d..2870d83b8c 100644 --- a/src/component/DefaultTooltipContent.tsx +++ b/src/component/DefaultTooltipContent.tsx @@ -37,7 +37,7 @@ export interface Props { separator?: string; wrapperClassName?: string; labelClassName?: string; - formatter?: Function; + formatter?: Formatter; contentStyle?: CSSProperties; itemStyle?: CSSProperties; labelStyle?: CSSProperties; From 6be367c5bb4c88475c8d33d5f4f9ee1983db7bae Mon Sep 17 00:00:00 2001 From: saghan Date: Wed, 17 Aug 2022 18:49:54 -0700 Subject: [PATCH 3/7] Take letter-spacing and font-size into consideration while rendering ticks (#2898) * accessibility fix for letterSpacing * fixed line ending * no exporting state interface * fixed scope of getElement * rolling back eslint changes * test added --- src/cartesian/CartesianAxis.tsx | 86 +++++++++++++++++++---- src/container/Layer.tsx | 6 +- test/specs/cartesian/CartesianAxisSpec.js | 19 +++++ 3 files changed, 93 insertions(+), 18 deletions(-) diff --git a/src/cartesian/CartesianAxis.tsx b/src/cartesian/CartesianAxis.tsx index f3be476109..caca6cf115 100644 --- a/src/cartesian/CartesianAxis.tsx +++ b/src/cartesian/CartesianAxis.tsx @@ -52,9 +52,13 @@ export interface CartesianAxisProps { interval?: number | 'preserveStart' | 'preserveEnd' | 'preserveStartEnd'; } +interface IState { + fontSize: string; + letterSpacing: string; +} export type Props = Omit, 'viewBox'> & CartesianAxisProps; -export class CartesianAxis extends Component { +export class CartesianAxis extends Component { static displayName = 'CartesianAxis'; static defaultProps = { @@ -81,8 +85,15 @@ export class CartesianAxis extends Component { interval: 'preserveEnd', }; + private layerReference: any; + + constructor(props: Props) { + super(props); + this.state = { fontSize: '', letterSpacing: '' }; + } + // todo Array - static getTicks(props: Props): any[] { + static getTicks(props: Props, fontSize?: string, letterSpacing?: string): any[] { const { tick, ticks, viewBox, minTickGap, orientation, interval, tickFormatter, unit } = props; if (!ticks || !ticks.length || !tick) { @@ -105,6 +116,8 @@ export class CartesianAxis extends Component { orientation, minTickGap, unit, + fontSize, + letterSpacing, }, true, ); @@ -117,6 +130,8 @@ export class CartesianAxis extends Component { orientation, minTickGap, unit, + fontSize, + letterSpacing, }); } @@ -127,6 +142,8 @@ export class CartesianAxis extends Component { orientation, minTickGap, unit, + fontSize, + letterSpacing, }); } @@ -135,14 +152,23 @@ export class CartesianAxis extends Component { } static getTicksStart( - { ticks, tickFormatter, viewBox, orientation, minTickGap, unit }: Omit, + { + ticks, + tickFormatter, + viewBox, + orientation, + minTickGap, + unit, + fontSize, + letterSpacing, + }: Omit, preserveEnd?: boolean, ) { const { x, y, width, height } = viewBox; const sizeKey = orientation === 'top' || orientation === 'bottom' ? 'width' : 'height'; const result = (ticks || []).slice(); // we need add the width of 'unit' only when sizeKey === 'width' - const unitSize = unit && sizeKey === 'width' ? getStringSize(unit)[sizeKey] : 0; + const unitSize = unit && sizeKey === 'width' ? getStringSize(unit, { fontSize, letterSpacing })[sizeKey] : 0; const len = result.length; const sign = len >= 2 ? mathSign(result[1].coordinate - result[0].coordinate) : 1; @@ -160,7 +186,7 @@ export class CartesianAxis extends Component { // Try to guarantee the tail to be displayed let tail = ticks[len - 1]; const tailContent = _.isFunction(tickFormatter) ? tickFormatter(tail.value, len - 1) : tail.value; - const tailSize = getStringSize(tailContent)[sizeKey] + unitSize; + const tailSize = getStringSize(tailContent, { fontSize, letterSpacing })[sizeKey] + unitSize; const tailGap = sign * (tail.coordinate + (sign * tailSize) / 2 - end); result[len - 1] = tail = { ...tail, @@ -181,7 +207,7 @@ export class CartesianAxis extends Component { for (let i = 0; i < count; i++) { let entry = result[i]; const content = _.isFunction(tickFormatter) ? tickFormatter(entry.value, i) : entry.value; - const size = getStringSize(content)[sizeKey] + unitSize; + const size = getStringSize(content, { fontSize, letterSpacing })[sizeKey] + unitSize; if (i === 0) { const gap = sign * (entry.coordinate - (sign * size) / 2 - start); @@ -206,11 +232,20 @@ export class CartesianAxis extends Component { return result.filter(entry => entry.isShow); } - static getTicksEnd({ ticks, tickFormatter, viewBox, orientation, minTickGap, unit }: Omit) { + static getTicksEnd({ + ticks, + tickFormatter, + viewBox, + orientation, + minTickGap, + unit, + fontSize, + letterSpacing, + }: Omit) { const { x, y, width, height } = viewBox; const sizeKey = orientation === 'top' || orientation === 'bottom' ? 'width' : 'height'; // we need add the width of 'unit' only when sizeKey === 'width' - const unitSize = unit && sizeKey === 'width' ? getStringSize(unit)[sizeKey] : 0; + const unitSize = unit && sizeKey === 'width' ? getStringSize(unit, { fontSize, letterSpacing })[sizeKey] : 0; const result = (ticks || []).slice(); const len = result.length; const sign = len >= 2 ? mathSign(result[1].coordinate - result[0].coordinate) : 1; @@ -228,7 +263,7 @@ export class CartesianAxis extends Component { for (let i = len - 1; i >= 0; i--) { let entry = result[i]; const content = _.isFunction(tickFormatter) ? tickFormatter(entry.value, len - i - 1) : entry.value; - const size = getStringSize(content)[sizeKey] + unitSize; + const size = getStringSize(content, { fontSize, letterSpacing })[sizeKey] + unitSize; if (i === len - 1) { const gap = sign * (entry.coordinate + (sign * size) / 2 - end); @@ -253,11 +288,27 @@ export class CartesianAxis extends Component { return result.filter(entry => entry.isShow); } - shouldComponentUpdate({ viewBox, ...restProps }: Props) { + shouldComponentUpdate({ viewBox, ...restProps }: Props, nextState: IState) { // props.viewBox is sometimes generated every time - // check that specially as object equality is likely to fail const { viewBox: viewBoxOld, ...restPropsOld } = this.props; - return !shallowEqual(viewBox, viewBoxOld) || !shallowEqual(restProps, restPropsOld); + return ( + !shallowEqual(viewBox, viewBoxOld) || + !shallowEqual(restProps, restPropsOld) || + !shallowEqual(nextState, this.state) + ); + } + + componentDidMount() { + const htmlLayer: SVGElement = this.layerReference; + if (!htmlLayer) return; + const tick: Element = htmlLayer.getElementsByClassName('recharts-cartesian-axis-tick-value')[0]; + if (tick) { + this.setState({ + fontSize: window.getComputedStyle(tick).fontSize, + letterSpacing: window.getComputedStyle(tick).letterSpacing, + }); + } } /** @@ -401,9 +452,9 @@ export class CartesianAxis extends Component { * @param {Array} ticks The ticks to actually render (overrides what was passed in props) * @return {ReactComponent} renderedTicks */ - renderTicks(ticks: CartesianTickItem[]) { + renderTicks(ticks: CartesianTickItem[], fontSize: string, letterSpacing: string) { const { tickLine, stroke, tick, tickFormatter, unit } = this.props; - const finalTicks = CartesianAxis.getTicks({ ...this.props, ticks }); + const finalTicks = CartesianAxis.getTicks({ ...this.props, ticks }, fontSize, letterSpacing); const textAnchor = this.getTickTextAnchor(); const verticalAnchor = this.getTickVerticalAnchor(); const axisProps = filterProps(this.props); @@ -474,9 +525,14 @@ export class CartesianAxis extends Component { } return ( - + { + this.layerReference = ref; + }} + > {axisLine && this.renderAxisLine()} - {this.renderTicks(finalTicks)} + {this.renderTicks(finalTicks, this.state.fontSize, this.state.letterSpacing)} {Label.renderCallByParent(this.props)} ); diff --git a/src/container/Layer.tsx b/src/container/Layer.tsx index ebed65d613..6a3228a9dc 100644 --- a/src/container/Layer.tsx +++ b/src/container/Layer.tsx @@ -12,13 +12,13 @@ interface LayerProps { export type Props = SVGProps & LayerProps; -export function Layer(props: Props) { +export const Layer = React.forwardRef((props: Props, ref: any) => { const { children, className, ...others } = props; const layerClass = classNames('recharts-layer', className); return ( - + {children} ); -} +}); diff --git a/test/specs/cartesian/CartesianAxisSpec.js b/test/specs/cartesian/CartesianAxisSpec.js index 2e7219d84c..885cfb0d7c 100644 --- a/test/specs/cartesian/CartesianAxisSpec.js +++ b/test/specs/cartesian/CartesianAxisSpec.js @@ -2,6 +2,7 @@ import React from 'react'; import { expect } from 'chai'; import { Surface, CartesianAxis } from 'recharts'; import { mount, render } from 'enzyme'; +import sinon from 'sinon'; describe('', () => { const ticks = [ @@ -68,6 +69,24 @@ describe('', () => { expect(wrapper.find('.recharts-cartesian-axis-tick').length).to.equal(5); }); + it('gets font states from its ComputedStyle', () => { + const stub = sinon.stub(window, 'getComputedStyle').returns({ fontSize: '14px', letterSpacing: '0.5em' }); + const wrapper = mount( + , + ); + + expect(wrapper.state().fontSize).to.equal('14px'); + expect(wrapper.state().letterSpacing).to.equal('0.5em'); + + stub.restore(); + }); + it('Renders ticks when interval="preserveStart"', () => { const wrapper = render( From 3edc3a6b4b41b5db766a9e324ca5628510925cc6 Mon Sep 17 00:00:00 2001 From: saghan Date: Wed, 17 Aug 2022 18:51:52 -0700 Subject: [PATCH 4/7] done (#2936) --- src/chart/generateCategoricalChart.tsx | 8 +++++--- src/container/Surface.tsx | 4 ++++ test/specs/chart/LineChartSpec.js | 10 ++++++++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/chart/generateCategoricalChart.tsx b/src/chart/generateCategoricalChart.tsx index 62dff4c157..d99110256f 100644 --- a/src/chart/generateCategoricalChart.tsx +++ b/src/chart/generateCategoricalChart.tsx @@ -746,6 +746,8 @@ export interface CategoricalChartProps { cy?: number | string; innerRadius?: number | string; outerRadius?: number | string; + title?: string; + desc?: string; } export const generateCategoricalChart = ({ @@ -2091,7 +2093,7 @@ export const generateCategoricalChart = ({ return null; } - const { children, className, width, height, style, compact, ...others } = this.props; + const { children, className, width, height, style, compact, title, desc, ...others } = this.props; const attrs = filterProps(others); const map = { CartesianGrid: { handler: this.renderGrid, once: true }, @@ -2119,7 +2121,7 @@ export const generateCategoricalChart = ({ // The "compact" mode is mainly used as the panorama within Brush if (compact) { return ( - + {this.renderClipPath()} {renderByOrder(children, map)} @@ -2136,7 +2138,7 @@ export const generateCategoricalChart = ({ this.container = node; }} > - + {this.renderClipPath()} {renderByOrder(children, map)} diff --git a/src/container/Surface.tsx b/src/container/Surface.tsx index b3867eafaf..a9d79fa21e 100644 --- a/src/container/Surface.tsx +++ b/src/container/Surface.tsx @@ -17,6 +17,8 @@ interface SurfaceProps { className?: string; style?: CSSProperties; children?: ReactNode; + title?: string; + desc?: string; } export type Props = Omit, 'viewBox'> & SurfaceProps; @@ -36,6 +38,8 @@ export function Surface(props: Props) { viewBox={`${svgView.x} ${svgView.y} ${svgView.width} ${svgView.height}`} version="1.1" > + {props.title} + {props.desc} {children} ); diff --git a/test/specs/chart/LineChartSpec.js b/test/specs/chart/LineChartSpec.js index 2cc8a99929..43a17c7673 100644 --- a/test/specs/chart/LineChartSpec.js +++ b/test/specs/chart/LineChartSpec.js @@ -36,6 +36,16 @@ describe('', () => { expect(wrapper.find('.recharts-line .recharts-line-curve').length).to.equal(1); }); + it('Sets title and description correctly', () => { + const wrapper = mount( + + + , + ); + expect(wrapper.find('title').text()).to.equal('Chart title'); + expect(wrapper.find('desc').text()).to.equal('Chart description'); + }); + it('Render smooth curve when type of Line is monotone', () => { const wrapper = render( From da6b9e2394cd55169ecdf31baf51bfa021eedb18 Mon Sep 17 00:00:00 2001 From: Dalton Craven Date: Mon, 22 Aug 2022 18:15:36 +0200 Subject: [PATCH 5/7] Fix typing of default tooltip formatter (#2924) --- src/component/DefaultTooltipContent.tsx | 8 ++++---- src/component/Tooltip.tsx | 6 +++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/component/DefaultTooltipContent.tsx b/src/component/DefaultTooltipContent.tsx index 2870d83b8c..0b6724f561 100644 --- a/src/component/DefaultTooltipContent.tsx +++ b/src/component/DefaultTooltipContent.tsx @@ -6,8 +6,8 @@ import React, { PureComponent, CSSProperties, ReactNode } from 'react'; import classNames from 'classnames'; import { isNumOrStr } from '../util/DataUtils'; -function defaultFormatter(value: T) { - return _.isArray(value) && isNumOrStr(value[0]) && isNumOrStr(value[1]) ? value.join(' ~ ') : value; +function defaultFormatter(value: TValue) { + return _.isArray(value) && isNumOrStr(value[0]) && isNumOrStr(value[1]) ? (value.join(' ~ ') as TValue) : value; } export type TooltipType = 'none'; @@ -19,7 +19,7 @@ export type Formatter = ( item: Payload, index: number, payload: Array>, -) => [ReactNode, ReactNode] | ReactNode; +) => [TValue, TName] | TValue; export interface Payload { type?: TooltipType; @@ -82,7 +82,7 @@ export class DefaultTooltipContent extends P }); return ( + // ESLint is disabled to allow listening to the `Escape` key. Refer to + // https://github.com/recharts/recharts/pull/2925 + // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
{ if (event.key === 'Escape') { this.setState({ From 2f1a6e9a43de4b0905c0ef5856aa145f795338bd Mon Sep 17 00:00:00 2001 From: Stian Jensen Date: Mon, 22 Aug 2022 18:17:05 +0200 Subject: [PATCH 6/7] Revert "chore: move type deps into devDependencies (#2843)" (#2942) --- package-lock.json | 30 +++++++++--------------------- package.json | 6 +++--- 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2467a6f325..4268c028fe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,9 @@ "version": "2.1.12", "license": "MIT", "dependencies": { + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", "classnames": "^2.2.5", "d3-interpolate": "^3.0.1", "d3-scale": "^4.0.2", @@ -35,9 +38,6 @@ "@babel/preset-typescript": "^7.6.0", "@babel/runtime": "^7.6.3", "@types/classnames": "^2.2.9", - "@types/d3-interpolate": "^3.0.1", - "@types/d3-scale": "^4.0.2", - "@types/d3-shape": "^3.1.0", "@types/lodash": "^4.14.144", "@types/react": "^16.0.0", "@types/react-dom": "^16.0.0", @@ -1769,14 +1769,12 @@ "node_modules/@types/d3-color": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-2.0.3.tgz", - "integrity": "sha512-+0EtEjBfKEDtH9Rk3u3kLOUXM5F+iZK+WvASPb0MhIZl8J8NUvGeZRwKCXl+P3HkYx5TdU4YtcibpqHkSR9n7w==", - "dev": true + "integrity": "sha512-+0EtEjBfKEDtH9Rk3u3kLOUXM5F+iZK+WvASPb0MhIZl8J8NUvGeZRwKCXl+P3HkYx5TdU4YtcibpqHkSR9n7w==" }, "node_modules/@types/d3-interpolate": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.1.tgz", "integrity": "sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==", - "dev": true, "dependencies": { "@types/d3-color": "*" } @@ -1784,14 +1782,12 @@ "node_modules/@types/d3-path": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-2.0.1.tgz", - "integrity": "sha512-6K8LaFlztlhZO7mwsZg7ClRsdLg3FJRzIIi6SZXDWmmSJc2x8dd2VkESbLXdk3p8cuvz71f36S0y8Zv2AxqvQw==", - "dev": true + "integrity": "sha512-6K8LaFlztlhZO7mwsZg7ClRsdLg3FJRzIIi6SZXDWmmSJc2x8dd2VkESbLXdk3p8cuvz71f36S0y8Zv2AxqvQw==" }, "node_modules/@types/d3-scale": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.2.tgz", "integrity": "sha512-Yk4htunhPAwN0XGlIwArRomOjdoBFXC3+kCxK2Ubg7I9shQlVSJy/pG/Ht5ASN+gdMIalpk8TJ5xV74jFsetLA==", - "dev": true, "dependencies": { "@types/d3-time": "*" } @@ -1800,7 +1796,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.0.tgz", "integrity": "sha512-jYIYxFFA9vrJ8Hd4Se83YI6XF+gzDL1aC5DCsldai4XYYiVNdhtpGbA/GM6iyQ8ayhSp3a148LY34hy7A4TxZA==", - "dev": true, "dependencies": { "@types/d3-path": "*" } @@ -1808,8 +1803,7 @@ "node_modules/@types/d3-time": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-2.0.0.tgz", - "integrity": "sha512-Abz8bTzy8UWDeYs9pCa3D37i29EWDjNTjemdk0ei1ApYVNqulYlGUKip/jLOpogkPSsPz/GvZCYiC7MFlEk0iQ==", - "dev": true + "integrity": "sha512-Abz8bTzy8UWDeYs9pCa3D37i29EWDjNTjemdk0ei1ApYVNqulYlGUKip/jLOpogkPSsPz/GvZCYiC7MFlEk0iQ==" }, "node_modules/@types/eslint": { "version": "7.2.7", @@ -15367,14 +15361,12 @@ "@types/d3-color": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-2.0.3.tgz", - "integrity": "sha512-+0EtEjBfKEDtH9Rk3u3kLOUXM5F+iZK+WvASPb0MhIZl8J8NUvGeZRwKCXl+P3HkYx5TdU4YtcibpqHkSR9n7w==", - "dev": true + "integrity": "sha512-+0EtEjBfKEDtH9Rk3u3kLOUXM5F+iZK+WvASPb0MhIZl8J8NUvGeZRwKCXl+P3HkYx5TdU4YtcibpqHkSR9n7w==" }, "@types/d3-interpolate": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.1.tgz", "integrity": "sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==", - "dev": true, "requires": { "@types/d3-color": "*" } @@ -15382,14 +15374,12 @@ "@types/d3-path": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-2.0.1.tgz", - "integrity": "sha512-6K8LaFlztlhZO7mwsZg7ClRsdLg3FJRzIIi6SZXDWmmSJc2x8dd2VkESbLXdk3p8cuvz71f36S0y8Zv2AxqvQw==", - "dev": true + "integrity": "sha512-6K8LaFlztlhZO7mwsZg7ClRsdLg3FJRzIIi6SZXDWmmSJc2x8dd2VkESbLXdk3p8cuvz71f36S0y8Zv2AxqvQw==" }, "@types/d3-scale": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.2.tgz", "integrity": "sha512-Yk4htunhPAwN0XGlIwArRomOjdoBFXC3+kCxK2Ubg7I9shQlVSJy/pG/Ht5ASN+gdMIalpk8TJ5xV74jFsetLA==", - "dev": true, "requires": { "@types/d3-time": "*" } @@ -15398,7 +15388,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.0.tgz", "integrity": "sha512-jYIYxFFA9vrJ8Hd4Se83YI6XF+gzDL1aC5DCsldai4XYYiVNdhtpGbA/GM6iyQ8ayhSp3a148LY34hy7A4TxZA==", - "dev": true, "requires": { "@types/d3-path": "*" } @@ -15406,8 +15395,7 @@ "@types/d3-time": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-2.0.0.tgz", - "integrity": "sha512-Abz8bTzy8UWDeYs9pCa3D37i29EWDjNTjemdk0ei1ApYVNqulYlGUKip/jLOpogkPSsPz/GvZCYiC7MFlEk0iQ==", - "dev": true + "integrity": "sha512-Abz8bTzy8UWDeYs9pCa3D37i29EWDjNTjemdk0ei1ApYVNqulYlGUKip/jLOpogkPSsPz/GvZCYiC7MFlEk0iQ==" }, "@types/eslint": { "version": "7.2.7", diff --git a/package.json b/package.json index 42bece35f9..1dbfa57f74 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,9 @@ "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" }, "dependencies": { + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", "classnames": "^2.2.5", "d3-interpolate": "^3.0.1", "d3-scale": "^4.0.2", @@ -81,9 +84,6 @@ "@babel/preset-typescript": "^7.6.0", "@babel/runtime": "^7.6.3", "@types/classnames": "^2.2.9", - "@types/d3-interpolate": "^3.0.1", - "@types/d3-scale": "^4.0.2", - "@types/d3-shape": "^3.1.0", "@types/lodash": "^4.14.144", "@types/react": "^16.0.0", "@types/react-dom": "^16.0.0", From deee55f1a298196f8fd611f55bd27163f3e622c8 Mon Sep 17 00:00:00 2001 From: rockcs1992 Date: Thu, 1 Sep 2022 01:14:43 -0400 Subject: [PATCH 7/7] Add inactiveShape prop to Pie component (#2900) --- src/polar/Pie.tsx | 12 +++-- test/specs/polar/PieSpec.js | 101 ++++++++++++++++++++++++++++++++---- 2 files changed, 101 insertions(+), 12 deletions(-) diff --git a/src/polar/Pie.tsx b/src/polar/Pie.tsx index 4cf231ce96..9839156036 100644 --- a/src/polar/Pie.tsx +++ b/src/polar/Pie.tsx @@ -87,6 +87,7 @@ interface PieProps extends PieDef { data?: any[]; sectors?: PieSectorDataItem[]; activeShape?: PieActiveShape; + inactiveShape?: PieActiveShape; labelLine?: PieLabelLine; label?: PieLabel; @@ -349,6 +350,11 @@ export class Pie extends PureComponent { return i === activeIndex; } + hasActiveIndex() { + const { activeIndex } = this.props; + return Array.isArray(activeIndex) ? activeIndex.length !== 0 : activeIndex || activeIndex === 0; + } + handleAnimationEnd = () => { const { onAnimationEnd } = this.props; @@ -472,10 +478,10 @@ export class Pie extends PureComponent { } renderSectorsStatically(sectors: PieSectorDataItem[]) { - const { activeShape, blendStroke } = this.props; - + const { activeShape, blendStroke, inactiveShape: inactiveShapeProp } = this.props; return sectors.map((entry, i) => { - const sectorOptions = this.isActiveIndex(i) ? activeShape : null; + const inactiveShape = inactiveShapeProp && this.hasActiveIndex() ? inactiveShapeProp : null; + const sectorOptions = this.isActiveIndex(i) ? activeShape : inactiveShape; const sectorProps = { ...entry, stroke: blendStroke ? entry.fill : entry.stroke, diff --git a/test/specs/polar/PieSpec.js b/test/specs/polar/PieSpec.js index 984548cfab..f2131568e0 100644 --- a/test/specs/polar/PieSpec.js +++ b/test/specs/polar/PieSpec.js @@ -34,7 +34,7 @@ describe('', () => { expect(wrapper.find('.recharts-pie-sector').length).to.equal(sectors.length); }); - it('Render customized active sector when activeShape is set to be a element', () => { + it('Render customized active sector when activeShape is set to be an element', () => { const ActiveShape = props => ; @@ -78,7 +78,7 @@ describe('', () => { expect(wrapper.find('.customized-active-shape').length).to.equal(1); }); - it('Render customized active sector when activeShape is set to be a object', () => { + it('Render customized active sector when activeShape is set to be an object', () => { const wrapper = render( ', () => { expect(wrapper.find('.customized-active-shape').length).to.equal(0); }); + it('Render customized active sector when inactiveShape is set to be an element', () => { + const ActiveShape = props => ; + const InactiveShape = props => ; + + const wrapper = render( + + } + inactiveShape={} + cx={250} + cy={250} + innerRadius={0} + outerRadius={200} + sectors={sectors} + /> + , + ); + expect(wrapper.find('.customized-inactive-shape').length).to.equal(4); + }); + + it('Render customized inactive sector when inactiveShape is set to be a function', () => { + const renderActiveShape = props => ; + const renderInactiveShape = props => ; + const wrapper = render( + + + , + ); + expect(wrapper.find('.customized-inactive-shape').length).to.equal(4); + }); + + it('Render customized inactive sector when inactiveShape is set to be an object', () => { + const wrapper = render( + + + , + ); + + expect(wrapper.find('.customized-inactive-shape').length).to.equal(0); + }); + + it('should not render customized inactive sectors if there is no active index', () => { + const renderActiveShape = props => ; + const renderInactiveShape = props => ; + const wrapper = render( + + + , + ); + expect(wrapper.find('.customized-inactive-shape').length).to.equal(0); + }); + it('Support multiple active sectors', () => { const ActiveShape = props => @@ -113,7 +196,7 @@ describe('', () => { outerRadius={200} sectors={sectors} /> - + , ); expect(wrapper.find('.customized-active-shape').length).to.equal(2); @@ -135,7 +218,7 @@ describe('', () => { outerRadius={200} sectors={sectors} /> - + , ); expect(wrapper.find('.customized-label').length).to.equal(sectors.length); @@ -157,7 +240,7 @@ describe('', () => { outerRadius={200} sectors={sectors} /> - + , ); setTimeout(() => { @@ -182,7 +265,7 @@ describe('', () => { outerRadius={200} sectors={sectors} /> - + , ); expect(wrapper.find('.customized-label').length).to.equal(sectors.length); @@ -206,7 +289,7 @@ describe('', () => { outerRadius={200} sectors={sectors} /> - + , ); expect(wrapper.find('.customized-label-line').length).to.equal(sectors.length); @@ -230,7 +313,7 @@ describe('', () => { outerRadius={200} sectors={sectors} /> - + , ); expect(wrapper.find('.customized-label-line').length).to.equal(sectors.length); @@ -265,7 +348,7 @@ describe('', () => { onMouseLeave={onMouseLeave} onClick={onClick} /> - + , ); const se = wrapper.find(Layer).at(3);