From 1ecdc150021512ea7954d3c208d2814ee2d9395d Mon Sep 17 00:00:00 2001 From: Coltin Kifer Date: Thu, 28 Dec 2023 23:59:06 +0000 Subject: [PATCH 1/3] docs: add controlled brush storybook entry --- .../cartesian/Brush/Brush.stories.tsx | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 storybook/stories/Examples/cartesian/Brush/Brush.stories.tsx diff --git a/storybook/stories/Examples/cartesian/Brush/Brush.stories.tsx b/storybook/stories/Examples/cartesian/Brush/Brush.stories.tsx new file mode 100644 index 0000000000..07eaeab246 --- /dev/null +++ b/storybook/stories/Examples/cartesian/Brush/Brush.stories.tsx @@ -0,0 +1,45 @@ +import React, { useState } from 'react'; +import { ComposedChart, ResponsiveContainer, Line, Brush } from '../../../../../src'; +import { pageData } from '../../../data'; + +export default { + component: Brush, +}; + +export const ControlledBrush = { + render: () => { + const [startIndex, setStartIndex] = useState(3); + const [endIndex, setEndIndex] = useState(pageData.length - 1); + + return ( + <> + + + + + { + setEndIndex(e.endIndex); + setStartIndex(e.startIndex); + }} + /> + + + setStartIndex(Number(evt.target.value))} + /> + setEndIndex(Number(evt.target.value))} + /> + + ); + }, +}; From 7dcf5a854200a6de160abaf6e115d322c6e5ebed Mon Sep 17 00:00:00 2001 From: Coltin Kifer Date: Tue, 2 Jan 2024 23:06:25 +0000 Subject: [PATCH 2/3] test: controlled brush storybook test --- src/chart/generateCategoricalChart.tsx | 15 ++++++-- .../cartesian/Brush/Brush.stories.tsx | 34 ++++++++++++++++--- 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/chart/generateCategoricalChart.tsx b/src/chart/generateCategoricalChart.tsx index 1803503a26..d5df274c16 100644 --- a/src/chart/generateCategoricalChart.tsx +++ b/src/chart/generateCategoricalChart.tsx @@ -1199,10 +1199,10 @@ export const generateCategoricalChart = ({ prevState: CategoricalChartState, ): CategoricalChartState => { const { dataKey, data, children, width, height, layout, stackOffset, margin } = nextProps; + const { dataStartIndex, dataEndIndex } = prevState; if (prevState.updateId === undefined) { const defaultState = createDefaultState(nextProps); - return { ...defaultState, updateId: 0, @@ -1280,9 +1280,16 @@ export const generateCategoricalChart = ({ }; } if (!isChildrenEqual(children, prevState.prevChildren)) { + // specifically check for Brush - if it exists and the start and end indexes are different, re-render with the new ones + const brush = findChildByType(children, Brush); + + const startIndex = brush ? brush.props?.startIndex ?? dataStartIndex : dataStartIndex; + const endIndex = brush ? brush.props?.endIndex ?? dataEndIndex : dataEndIndex; + const hasDifferentStartOrEndIndex = startIndex !== dataStartIndex || endIndex !== dataEndIndex; + // update configuration in children const hasGlobalData = !isNil(data); - const newUpdateId = hasGlobalData ? prevState.updateId : prevState.updateId + 1; + const newUpdateId = hasGlobalData && !hasDifferentStartOrEndIndex ? prevState.updateId : prevState.updateId + 1; return { updateId: newUpdateId, @@ -1291,10 +1298,14 @@ export const generateCategoricalChart = ({ props: nextProps, ...prevState, updateId: newUpdateId, + dataStartIndex: startIndex, + dataEndIndex: endIndex, }, prevState, ), prevChildren: children, + dataStartIndex: startIndex, + dataEndIndex: endIndex, }; } diff --git a/storybook/stories/Examples/cartesian/Brush/Brush.stories.tsx b/storybook/stories/Examples/cartesian/Brush/Brush.stories.tsx index 07eaeab246..488acc7172 100644 --- a/storybook/stories/Examples/cartesian/Brush/Brush.stories.tsx +++ b/storybook/stories/Examples/cartesian/Brush/Brush.stories.tsx @@ -1,4 +1,6 @@ +import { expect } from '@storybook/jest'; import React, { useState } from 'react'; +import { within, userEvent } from '@storybook/testing-library'; import { ComposedChart, ResponsiveContainer, Line, Brush } from '../../../../../src'; import { pageData } from '../../../data'; @@ -15,7 +17,7 @@ export const ControlledBrush = { <> - + @@ -31,15 +34,38 @@ export const ControlledBrush = { type="number" aria-label="startIndex" value={startIndex} - onChange={evt => setStartIndex(Number(evt.target.value))} + onChange={evt => { + const num = Number(evt.target.value); + if (Number.isInteger(num)) setStartIndex(num); + }} /> setEndIndex(Number(evt.target.value))} + onChange={evt => { + const num = Number(evt.target.value); + if (Number.isInteger(num)) setEndIndex(num); + }} /> ); }, + play: async ({ canvasElement }: { canvasElement: HTMLElement }) => { + const user = userEvent.setup(); + const { getByLabelText } = within(canvasElement); + + const startIndexInput = getByLabelText('startIndex'); + const endIndexInput = getByLabelText('endIndex'); + + await user.clear(startIndexInput); + await user.type(startIndexInput, '2'); + await user.clear(endIndexInput); + await user.type(endIndexInput, '5'); + + const brushTexts = document.getElementsByClassName('recharts-brush-texts').item(0).children; + expect(brushTexts.item(0)).toBeInTheDocument(); + + expect(brushTexts.item(0).textContent).toContain('2'); + expect(brushTexts.item(1).textContent).toContain('5'); + }, }; From 19a73dd5e32a5b0ab0dbca59448ef2b979cbe3a0 Mon Sep 17 00:00:00 2001 From: Coltin Kifer Date: Wed, 3 Jan 2024 18:17:11 +0000 Subject: [PATCH 3/3] test: add unit test similar to sb test --- test/cartesian/Brush.spec.tsx | 68 +++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/test/cartesian/Brush.spec.tsx b/test/cartesian/Brush.spec.tsx index ddc40d10f8..ee995dcffd 100644 --- a/test/cartesian/Brush.spec.tsx +++ b/test/cartesian/Brush.spec.tsx @@ -1,6 +1,8 @@ -import React from 'react'; +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import React, { useState } from 'react'; import { fireEvent, render, screen, waitFor } from '@testing-library/react'; -import { Brush, LineChart, Line, BarChart } from '../../src'; +import userEvent from '@testing-library/user-event'; +import { Brush, LineChart, Line, BarChart, ComposedChart } from '../../src'; import { assertNotNull } from '../helper/assertNotNull'; describe('', () => { @@ -182,5 +184,67 @@ describe('', () => { expect(text.textContent).toBe('10'); }); }); + + const ControlledBrush = () => { + const [startIndex, setStartIndex] = useState(3); + const [endIndex, setEndIndex] = useState(data.length - 1); + + return ( + <> + + + + { + setEndIndex(e.endIndex); + setStartIndex(e.startIndex); + }} + alwaysShowText + /> + + { + const num = Number(evt.target.value); + if (Number.isInteger(num)) setStartIndex(num); + }} + /> + { + const num = Number(evt.target.value); + if (Number.isInteger(num)) setEndIndex(num); + }} + /> + + ); + }; + + test('Travellers should move and chart should update when brush start and end indexes are controlled', async () => { + const user = userEvent.setup(); + const { container } = render(); + + const traveller = container.querySelector('.recharts-brush-traveller') as SVGGElement; + fireEvent.focus(traveller); + + const startIndexInput = screen.getByLabelText('startIndex'); + const endIndexInput = screen.getByLabelText('endIndex'); + + await user.clear(startIndexInput); + await user.type(startIndexInput, '2'); + await user.clear(endIndexInput); + await user.type(endIndexInput, '5'); + + const brushTexts = container.getElementsByClassName('recharts-brush-texts').item(0)!.children; + expect(brushTexts.item(0)).toBeInTheDocument(); + + expect(brushTexts.item(0)?.textContent).toContain('2'); + expect(brushTexts.item(1)?.textContent).toContain('5'); + }); }); });