From 8ee6cc9ef8c3557428b9ca0fbd38b9373ab4ea1e Mon Sep 17 00:00:00 2001 From: Grzegorz Chudzinski-Pawlowski <112354940+grzegorz-cp@users.noreply.github.com> Date: Thu, 12 Dec 2024 11:23:12 +1100 Subject: [PATCH 01/13] Charts - Adding basic pie chart --- pnpm-lock.yaml | 3 + .../bar-chart/stories/index.stories.tsx | 10 +- .../bar-chart/stories/sample-data.ts | 311 ++++++++++++++++++ .../line-chart/stories/index.stories.tsx | 15 +- .../line-chart/stories/sample-data.ts | 173 ++++++++++ .../charts/src/components/pie-chart/index.tsx | 1 + .../src/components/pie-chart/pie-chart.tsx | 118 +++++++ .../pie-chart/stories/index.stories.tsx | 45 +++ .../charts/src/components/tooltip/index.tsx | 1 + projects/js-packages/charts/src/index.ts | 13 +- projects/js-packages/storybook/package.json | 1 + 11 files changed, 675 insertions(+), 16 deletions(-) create mode 100644 projects/js-packages/charts/src/components/bar-chart/stories/sample-data.ts create mode 100644 projects/js-packages/charts/src/components/line-chart/stories/sample-data.ts create mode 100644 projects/js-packages/charts/src/components/pie-chart/index.tsx create mode 100644 projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx create mode 100644 projects/js-packages/charts/src/components/pie-chart/stories/index.stories.tsx diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 57a36196e0416..256e58fcb4b3e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1406,6 +1406,9 @@ importers: projects/js-packages/storybook: dependencies: + '@visx/responsive': + specifier: 3.12.0 + version: 3.12.0(react@18.3.1) '@wordpress/api-fetch': specifier: 7.13.0 version: 7.13.0 diff --git a/projects/js-packages/charts/src/components/bar-chart/stories/index.stories.tsx b/projects/js-packages/charts/src/components/bar-chart/stories/index.stories.tsx index 3ad3aa21d7f9d..45ae2a989c737 100644 --- a/projects/js-packages/charts/src/components/bar-chart/stories/index.stories.tsx +++ b/projects/js-packages/charts/src/components/bar-chart/stories/index.stories.tsx @@ -1,4 +1,5 @@ import BarChart from '../index'; +import data from './sample-data'; import type { Meta } from '@storybook/react'; export default { @@ -23,14 +24,7 @@ Default.args = { width: 500, height: 300, showTooltips: false, - data: [ - { label: 'Jan', value: 12 }, - { label: 'Feb', value: 18 }, - { label: 'Mar', value: 29 }, - { label: 'Apr', value: 33 }, - { label: 'May', value: 45 }, - { label: 'Jun', value: 52 }, - ], + data: data[ 0 ].data, }; export const WithTooltips = Template.bind( {} ); diff --git a/projects/js-packages/charts/src/components/bar-chart/stories/sample-data.ts b/projects/js-packages/charts/src/components/bar-chart/stories/sample-data.ts new file mode 100644 index 0000000000000..61e9478b82757 --- /dev/null +++ b/projects/js-packages/charts/src/components/bar-chart/stories/sample-data.ts @@ -0,0 +1,311 @@ +/** + * Olympic medals data for top countries (1896-2020) + * Total medals (Gold + Silver + Bronze) for Summer Olympics + * Sample data, might not be accurate. + */ +const olympicMedals = [ + { + group: 'United States', + data: [ + { label: '2024', value: 126 }, // France + { label: '2020', value: 113 }, // Tokyo + { label: '2016', value: 121 }, // Rio + { label: '2012', value: 104 }, // London + { label: '2008', value: 112 }, // Beijing + { label: '2004', value: 101 }, // Athens + { label: '2000', value: 93 }, // Sydney + { label: '1996', value: 101 }, // Atlanta + { label: '1992', value: 108 }, // Barcelona + { label: '1988', value: 94 }, // Seoul + { label: '1984', value: 174 }, // Los Angeles + { label: '1980', value: 0 }, // Moscow (boycott) + { label: '1976', value: 94 }, // Montreal + { label: '1972', value: 94 }, // Munich + { label: '1968', value: 107 }, // Mexico City + { label: '1964', value: 90 }, // Tokyo + { label: '1960', value: 71 }, // Rome + { label: '1956', value: 74 }, // Melbourne + { label: '1952', value: 76 }, // Helsinki + { label: '1948', value: 84 }, // London + { label: '1936', value: 56 }, // Berlin + { label: '1932', value: 103 }, // Los Angeles + { label: '1928', value: 56 }, // Amsterdam + { label: '1924', value: 99 }, // Paris + { label: '1920', value: 95 }, // Antwerp + { label: '1912', value: 63 }, // Stockholm + { label: '1908', value: 47 }, // London + { label: '1904', value: 239 }, // St. Louis + { label: '1900', value: 47 }, // Paris + { label: '1896', value: 20 }, // Athens + ], + }, + { + group: 'China', + data: [ + { label: '2024', value: 91 }, + { label: '2020', value: 88 }, + { label: '2016', value: 70 }, + { label: '2012', value: 91 }, + { label: '2008', value: 100 }, // Host + { label: '2004', value: 63 }, + { label: '2000', value: 59 }, + { label: '1996', value: 50 }, + { label: '1992', value: 54 }, + { label: '1988', value: 28 }, + { label: '1984', value: 32 }, + { label: '1980', value: 0 }, // Did not participate + { label: '1976', value: 0 }, // Did not participate + { label: '1972', value: 0 }, // Did not participate + { label: '1968', value: 0 }, // Did not participate + { label: '1964', value: 0 }, // Did not participate + { label: '1960', value: 0 }, // Did not participate + { label: '1956', value: 0 }, // Did not participate + { label: '1952', value: 0 }, // First participation in 1984 + // No participation before 1984 + ], + }, + { + group: 'Great Britain', + data: [ + { label: '2024', value: 60 }, + { label: '2020', value: 65 }, + { label: '2016', value: 67 }, + { label: '2012', value: 65 }, + { label: '2008', value: 47 }, + { label: '2004', value: 30 }, + { label: '2000', value: 28 }, + { label: '1996', value: 15 }, + { label: '1992', value: 20 }, + { label: '1988', value: 24 }, + { label: '1984', value: 37 }, + { label: '1980', value: 21 }, + { label: '1976', value: 13 }, + { label: '1972', value: 18 }, + { label: '1968', value: 13 }, + { label: '1964', value: 18 }, + { label: '1960', value: 20 }, + { label: '1956', value: 24 }, + { label: '1952', value: 11 }, + { label: '1948', value: 23 }, + { label: '1936', value: 14 }, + { label: '1932', value: 16 }, + { label: '1928', value: 20 }, + { label: '1924', value: 34 }, + { label: '1920', value: 43 }, + { label: '1912', value: 41 }, + { label: '1908', value: 146 }, // Host + { label: '1904', value: 1 }, + { label: '1900', value: 30 }, + { label: '1896', value: 7 }, + ], + }, + { + group: 'Japan', + data: [ + { label: '2024', value: 45 }, + { label: '2020', value: 58 }, // Host + { label: '2016', value: 41 }, + { label: '2012', value: 38 }, + { label: '2008', value: 25 }, + { label: '2004', value: 37 }, + { label: '2000', value: 18 }, + { label: '1996', value: 14 }, + { label: '1992', value: 22 }, + { label: '1988', value: 14 }, + { label: '1984', value: 32 }, + { label: '1980', value: 0 }, // Boycott + { label: '1976', value: 25 }, + { label: '1972', value: 29 }, + { label: '1968', value: 25 }, + { label: '1964', value: 29 }, // Host + { label: '1960', value: 18 }, + { label: '1956', value: 19 }, + { label: '1952', value: 9 }, + { label: '1948', value: 0 }, // Excluded + { label: '1936', value: 20 }, + { label: '1932', value: 18 }, + { label: '1928', value: 5 }, + { label: '1924', value: 4 }, + { label: '1920', value: 0 }, // Did not participate + { label: '1912', value: 2 }, + { label: '1908', value: 0 }, // Did not participate + { label: '1904', value: 0 }, // Did not participate + { label: '1900', value: 0 }, // Did not participate + { label: '1896', value: 0 }, // Did not participate + ], + }, + { + group: 'France', + data: [ + { label: '2024', value: 55 }, // Host + { label: '2020', value: 33 }, + { label: '2016', value: 42 }, + { label: '2012', value: 35 }, + { label: '2008', value: 43 }, + { label: '2004', value: 33 }, + { label: '2000', value: 38 }, + { label: '1996', value: 37 }, + { label: '1992', value: 29 }, + { label: '1988', value: 16 }, + { label: '1984', value: 28 }, + { label: '1980', value: 14 }, + { label: '1976', value: 9 }, + { label: '1972', value: 13 }, + { label: '1968', value: 15 }, + { label: '1964', value: 15 }, + { label: '1960', value: 5 }, + { label: '1956', value: 14 }, + { label: '1952', value: 18 }, + { label: '1948', value: 29 }, + { label: '1936', value: 19 }, + { label: '1932', value: 19 }, + { label: '1928', value: 21 }, + { label: '1924', value: 38 }, // Host + { label: '1920', value: 41 }, + { label: '1912', value: 14 }, + { label: '1908', value: 19 }, + { label: '1904', value: 0 }, + { label: '1900', value: 101 }, // Host + { label: '1896', value: 11 }, + ], + }, + { + group: 'Germany', + data: [ + { label: '2024', value: 40 }, + { label: '2020', value: 37 }, + { label: '2016', value: 42 }, + { label: '2012', value: 44 }, + { label: '2008', value: 41 }, + { label: '2004', value: 48 }, + { label: '2000', value: 56 }, + { label: '1996', value: 65 }, + { label: '1992', value: 82 }, // Unified team + { label: '1988', value: 142 }, // East and West combined + { label: '1984', value: 59 }, // West Germany only + { label: '1980', value: 126 }, // East Germany only + { label: '1976', value: 125 }, // East and West combined + { label: '1972', value: 133 }, // Host (West Germany) + { label: '1968', value: 91 }, // East and West combined + { label: '1964', value: 96 }, // United Team + { label: '1960', value: 87 }, // United Team + { label: '1956', value: 64 }, + { label: '1952', value: 24 }, + { label: '1936', value: 89 }, // Host + { label: '1932', value: 20 }, + { label: '1928', value: 31 }, + { label: '1912', value: 25 }, + { label: '1908', value: 22 }, + { label: '1904', value: 4 }, + { label: '1900', value: 8 }, + { label: '1896', value: 13 }, + ], + }, + { + group: 'Australia', + data: [ + { label: '2024', value: 42 }, + { label: '2020', value: 46 }, + { label: '2016', value: 29 }, + { label: '2012', value: 35 }, + { label: '2008', value: 46 }, + { label: '2004', value: 49 }, + { label: '2000', value: 58 }, // Host + { label: '1996', value: 41 }, + { label: '1992', value: 27 }, + { label: '1988', value: 14 }, + { label: '1984', value: 24 }, + { label: '1980', value: 9 }, + { label: '1976', value: 5 }, + { label: '1972', value: 8 }, + { label: '1968', value: 17 }, + { label: '1964', value: 18 }, + { label: '1960', value: 22 }, + { label: '1956', value: 35 }, // Host + { label: '1952', value: 11 }, + { label: '1948', value: 13 }, + { label: '1936', value: 3 }, + { label: '1932', value: 5 }, + { label: '1928', value: 4 }, + { label: '1924', value: 6 }, + { label: '1920', value: 3 }, + { label: '1912', value: 7 }, + { label: '1908', value: 15 }, + { label: '1904', value: 0 }, + { label: '1900', value: 2 }, + { label: '1896', value: 0 }, + ], + }, + { + group: 'Poland', + data: [ + { label: '2024', value: 15 }, + { label: '2020', value: 14 }, + { label: '2016', value: 11 }, + { label: '2012', value: 10 }, + { label: '2008', value: 10 }, + { label: '2004', value: 10 }, + { label: '2000', value: 14 }, + { label: '1996', value: 17 }, + { label: '1992', value: 19 }, + { label: '1988', value: 16 }, + { label: '1984', value: 0 }, // Boycott + { label: '1980', value: 32 }, + { label: '1976', value: 26 }, + { label: '1972', value: 21 }, + { label: '1968', value: 18 }, + { label: '1964', value: 23 }, + { label: '1960', value: 21 }, + { label: '1956', value: 9 }, + { label: '1952', value: 4 }, + { label: '1948', value: 0 }, + { label: '1936', value: 6 }, + { label: '1932', value: 7 }, + { label: '1928', value: 5 }, + { label: '1924', value: 0 }, + { label: '1920', value: 0 }, + { label: '1912', value: 0 }, // Part of Russian Empire + { label: '1908', value: 0 }, + { label: '1904', value: 0 }, + { label: '1900', value: 0 }, + { label: '1896', value: 0 }, + ], + }, + { + group: 'Jamaica', + data: [ + { label: '2024', value: 10 }, + { label: '2020', value: 9 }, + { label: '2016', value: 11 }, + { label: '2012', value: 12 }, + { label: '2008', value: 11 }, + { label: '2004', value: 5 }, + { label: '2000', value: 9 }, + { label: '1996', value: 6 }, + { label: '1992', value: 4 }, + { label: '1988', value: 2 }, + { label: '1984', value: 3 }, + { label: '1980', value: 3 }, + { label: '1976', value: 2 }, + { label: '1972', value: 1 }, + { label: '1968', value: 1 }, + { label: '1964', value: 2 }, + { label: '1960', value: 0 }, + { label: '1956', value: 0 }, + { label: '1952', value: 2 }, + { label: '1948', value: 3 }, // First participation + { label: '1936', value: 0 }, + { label: '1932', value: 0 }, + { label: '1928', value: 0 }, + { label: '1924', value: 0 }, + { label: '1920', value: 0 }, + { label: '1912', value: 0 }, + { label: '1908', value: 0 }, + { label: '1904', value: 0 }, + { label: '1900', value: 0 }, + { label: '1896', value: 0 }, + ], + }, +]; + +export default olympicMedals; diff --git a/projects/js-packages/charts/src/components/line-chart/stories/index.stories.tsx b/projects/js-packages/charts/src/components/line-chart/stories/index.stories.tsx index 40a11b9c67364..cdf6adf0f4f8a 100644 --- a/projects/js-packages/charts/src/components/line-chart/stories/index.stories.tsx +++ b/projects/js-packages/charts/src/components/line-chart/stories/index.stories.tsx @@ -1,12 +1,13 @@ import { LineChart } from '../index'; +import sampleData from './sample-data'; import type { Meta } from '@storybook/react'; -const data = [ - { date: new Date( '2023-01-01' ), value: 10 }, - { date: new Date( '2023-02-01' ), value: 20 }, - { date: new Date( '2023-03-01' ), value: 15 }, - { date: new Date( '2023-04-01' ), value: 25 }, -]; +// const data = [ +// { date: new Date( '2023-01-01' ), value: 10 }, +// { date: new Date( '2023-02-01' ), value: 20 }, +// { date: new Date( '2023-03-01' ), value: 15 }, +// { date: new Date( '2023-04-01' ), value: 25 }, +// ]; export default { title: 'JS Packages/Charts/Types/Line Chart', @@ -30,5 +31,5 @@ Default.args = { width: 500, height: 300, margin: { top: 20, right: 20, bottom: 30, left: 40 }, - data, + data: sampleData.mars, }; diff --git a/projects/js-packages/charts/src/components/line-chart/stories/sample-data.ts b/projects/js-packages/charts/src/components/line-chart/stories/sample-data.ts new file mode 100644 index 0000000000000..53d7f8d97d72e --- /dev/null +++ b/projects/js-packages/charts/src/components/line-chart/stories/sample-data.ts @@ -0,0 +1,173 @@ +// Data from UK Met Office (London/Heathrow), Australian Bureau of Meteorology (Canberra), +// and NASA Mars Curiosity Rover (Gale Crater) +const temperatureData = { + london: [ + { date: new Date( '2023-01-01' ), value: 8.2 }, + { date: new Date( '2023-01-08' ), value: 7.9 }, + { date: new Date( '2023-01-15' ), value: 5.1 }, + { date: new Date( '2023-01-22' ), value: 4.8 }, + { date: new Date( '2023-01-29' ), value: 6.3 }, + { date: new Date( '2023-02-05' ), value: 7.2 }, + { date: new Date( '2023-02-12' ), value: 9.4 }, + { date: new Date( '2023-02-19' ), value: 8.7 }, + { date: new Date( '2023-02-26' ), value: 7.1 }, + { date: new Date( '2023-03-05' ), value: 8.3 }, + { date: new Date( '2023-03-12' ), value: 9.5 }, + { date: new Date( '2023-03-19' ), value: 11.2 }, + { date: new Date( '2023-03-26' ), value: 12.8 }, + { date: new Date( '2023-04-02' ), value: 13.4 }, + { date: new Date( '2023-04-09' ), value: 14.1 }, + { date: new Date( '2023-04-16' ), value: 15.3 }, + { date: new Date( '2023-04-23' ), value: 14.8 }, + { date: new Date( '2023-04-30' ), value: 15.7 }, + { date: new Date( '2023-05-07' ), value: 16.9 }, + { date: new Date( '2023-05-14' ), value: 17.2 }, + { date: new Date( '2023-05-21' ), value: 18.4 }, + { date: new Date( '2023-05-28' ), value: 19.1 }, + { date: new Date( '2023-06-04' ), value: 20.3 }, + { date: new Date( '2023-06-11' ), value: 21.5 }, + { date: new Date( '2023-06-18' ), value: 22.8 }, + { date: new Date( '2023-06-25' ), value: 21.9 }, + { date: new Date( '2023-07-02' ), value: 23.1 }, + { date: new Date( '2023-07-09' ), value: 22.7 }, + { date: new Date( '2023-07-16' ), value: 24.2 }, + { date: new Date( '2023-07-23' ), value: 23.8 }, + { date: new Date( '2023-07-30' ), value: 22.9 }, + { date: new Date( '2023-08-06' ), value: 23.4 }, + { date: new Date( '2023-08-13' ), value: 22.8 }, + { date: new Date( '2023-08-20' ), value: 21.9 }, + { date: new Date( '2023-08-27' ), value: 20.7 }, + { date: new Date( '2023-09-03' ), value: 19.8 }, + { date: new Date( '2023-09-10' ), value: 18.9 }, + { date: new Date( '2023-09-17' ), value: 17.6 }, + { date: new Date( '2023-09-24' ), value: 16.8 }, + { date: new Date( '2023-10-01' ), value: 15.9 }, + { date: new Date( '2023-10-08' ), value: 14.7 }, + { date: new Date( '2023-10-15' ), value: 13.8 }, + { date: new Date( '2023-10-22' ), value: 12.9 }, + { date: new Date( '2023-10-29' ), value: 11.7 }, + { date: new Date( '2023-11-05' ), value: 10.8 }, + { date: new Date( '2023-11-12' ), value: 9.9 }, + { date: new Date( '2023-11-19' ), value: 8.7 }, + { date: new Date( '2023-11-26' ), value: 7.8 }, + { date: new Date( '2023-12-03' ), value: 6.9 }, + { date: new Date( '2023-12-10' ), value: 5.8 }, + { date: new Date( '2023-12-17' ), value: 4.9 }, + { date: new Date( '2023-12-24' ), value: 5.7 }, + { date: new Date( '2023-12-31' ), value: 6.2 }, + ], + + canberra: [ + { date: new Date( '2023-01-01' ), value: 28.5 }, + { date: new Date( '2023-01-08' ), value: 29.2 }, + { date: new Date( '2023-01-15' ), value: 30.1 }, + { date: new Date( '2023-01-22' ), value: 29.8 }, + { date: new Date( '2023-01-29' ), value: 28.9 }, + { date: new Date( '2023-02-05' ), value: 27.8 }, + { date: new Date( '2023-02-12' ), value: 26.9 }, + { date: new Date( '2023-02-19' ), value: 25.7 }, + { date: new Date( '2023-02-26' ), value: 24.8 }, + { date: new Date( '2023-03-05' ), value: 23.9 }, + { date: new Date( '2023-03-12' ), value: 22.8 }, + { date: new Date( '2023-03-19' ), value: 21.7 }, + { date: new Date( '2023-03-26' ), value: 20.8 }, + { date: new Date( '2023-04-02' ), value: 19.6 }, + { date: new Date( '2023-04-09' ), value: 18.4 }, + { date: new Date( '2023-04-16' ), value: 17.2 }, + { date: new Date( '2023-04-23' ), value: 16.1 }, + { date: new Date( '2023-04-30' ), value: 15.3 }, + { date: new Date( '2023-05-07' ), value: 14.2 }, + { date: new Date( '2023-05-14' ), value: 13.1 }, + { date: new Date( '2023-05-21' ), value: 12.3 }, + { date: new Date( '2023-05-28' ), value: 11.4 }, + { date: new Date( '2023-06-04' ), value: 10.2 }, + { date: new Date( '2023-06-11' ), value: 9.1 }, + { date: new Date( '2023-06-18' ), value: 8.3 }, + { date: new Date( '2023-06-25' ), value: 7.8 }, + { date: new Date( '2023-07-02' ), value: 7.1 }, + { date: new Date( '2023-07-09' ), value: 6.9 }, + { date: new Date( '2023-07-16' ), value: 7.2 }, + { date: new Date( '2023-07-23' ), value: 8.1 }, + { date: new Date( '2023-07-30' ), value: 9.3 }, + { date: new Date( '2023-08-06' ), value: 10.4 }, + { date: new Date( '2023-08-13' ), value: 11.6 }, + { date: new Date( '2023-08-20' ), value: 12.8 }, + { date: new Date( '2023-08-27' ), value: 13.9 }, + { date: new Date( '2023-09-03' ), value: 15.2 }, + { date: new Date( '2023-09-10' ), value: 16.4 }, + { date: new Date( '2023-09-17' ), value: 17.6 }, + { date: new Date( '2023-09-24' ), value: 18.9 }, + { date: new Date( '2023-10-01' ), value: 20.1 }, + { date: new Date( '2023-10-08' ), value: 21.3 }, + { date: new Date( '2023-10-15' ), value: 22.5 }, + { date: new Date( '2023-10-22' ), value: 23.7 }, + { date: new Date( '2023-10-29' ), value: 24.8 }, + { date: new Date( '2023-11-05' ), value: 25.9 }, + { date: new Date( '2023-11-12' ), value: 26.7 }, + { date: new Date( '2023-11-19' ), value: 27.8 }, + { date: new Date( '2023-11-26' ), value: 28.6 }, + { date: new Date( '2023-12-03' ), value: 29.4 }, + { date: new Date( '2023-12-10' ), value: 30.2 }, + { date: new Date( '2023-12-17' ), value: 29.8 }, + { date: new Date( '2023-12-24' ), value: 28.9 }, + { date: new Date( '2023-12-31' ), value: 29.3 }, + ], + + mars: [ + { date: new Date( '2023-01-01' ), value: -63 }, + { date: new Date( '2023-01-08' ), value: -64 }, + { date: new Date( '2023-01-15' ), value: -65 }, + { date: new Date( '2023-01-22' ), value: -63 }, + { date: new Date( '2023-01-29' ), value: -62 }, + { date: new Date( '2023-02-05' ), value: -60 }, + { date: new Date( '2023-02-12' ), value: -58 }, + { date: new Date( '2023-02-19' ), value: -55 }, + { date: new Date( '2023-02-26' ), value: -52 }, + { date: new Date( '2023-03-05' ), value: -48 }, + { date: new Date( '2023-03-12' ), value: -45 }, + { date: new Date( '2023-03-19' ), value: -42 }, + { date: new Date( '2023-03-26' ), value: -38 }, + { date: new Date( '2023-04-02' ), value: -35 }, + { date: new Date( '2023-04-09' ), value: -32 }, + { date: new Date( '2023-04-16' ), value: -28 }, + { date: new Date( '2023-04-23' ), value: -25 }, + { date: new Date( '2023-04-30' ), value: -22 }, + { date: new Date( '2023-05-07' ), value: -18 }, + { date: new Date( '2023-05-14' ), value: -15 }, + { date: new Date( '2023-05-21' ), value: -12 }, + { date: new Date( '2023-05-28' ), value: -8 }, + { date: new Date( '2023-06-04' ), value: -5 }, + { date: new Date( '2023-06-11' ), value: -2 }, + { date: new Date( '2023-06-18' ), value: 0 }, + { date: new Date( '2023-06-25' ), value: 2 }, + { date: new Date( '2023-07-02' ), value: 5 }, + { date: new Date( '2023-07-09' ), value: 8 }, + { date: new Date( '2023-07-16' ), value: 10 }, + { date: new Date( '2023-07-23' ), value: 12 }, + { date: new Date( '2023-07-30' ), value: 15 }, + { date: new Date( '2023-08-06' ), value: 17 }, + { date: new Date( '2023-08-13' ), value: 20 }, + { date: new Date( '2023-08-20' ), value: 22 }, + { date: new Date( '2023-08-27' ), value: 20 }, + { date: new Date( '2023-09-03' ), value: 18 }, + { date: new Date( '2023-09-10' ), value: 15 }, + { date: new Date( '2023-09-17' ), value: 12 }, + { date: new Date( '2023-09-24' ), value: 8 }, + { date: new Date( '2023-10-01' ), value: 5 }, + { date: new Date( '2023-10-08' ), value: 2 }, + { date: new Date( '2023-10-15' ), value: -2 }, + { date: new Date( '2023-10-22' ), value: -5 }, + { date: new Date( '2023-10-29' ), value: -8 }, + { date: new Date( '2023-11-05' ), value: -12 }, + { date: new Date( '2023-11-12' ), value: -15 }, + { date: new Date( '2023-11-19' ), value: -18 }, + { date: new Date( '2023-11-26' ), value: -22 }, + { date: new Date( '2023-12-03' ), value: -25 }, + { date: new Date( '2023-12-10' ), value: -28 }, + { date: new Date( '2023-12-17' ), value: -32 }, + { date: new Date( '2023-12-24' ), value: -35 }, + { date: new Date( '2023-12-31' ), value: -38 }, + ], +}; + +export default temperatureData; diff --git a/projects/js-packages/charts/src/components/pie-chart/index.tsx b/projects/js-packages/charts/src/components/pie-chart/index.tsx new file mode 100644 index 0000000000000..c5b0025459ea3 --- /dev/null +++ b/projects/js-packages/charts/src/components/pie-chart/index.tsx @@ -0,0 +1 @@ +export { default as PieChart } from './pie-chart'; diff --git a/projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx b/projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx new file mode 100644 index 0000000000000..52385712f61c0 --- /dev/null +++ b/projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx @@ -0,0 +1,118 @@ +import { localPoint } from '@visx/event'; +import { Group } from '@visx/group'; +import { Pie } from '@visx/shape'; +import { useTooltip } from '@visx/tooltip'; +import React from 'react'; +import { useChartTheme } from '../../providers/theme/theme-provider'; +import { Tooltip } from '../tooltip'; +import type { DataPoint } from '../shared/types'; + +type PieChartProps = { + /** + * Array of data points to display in the chart + */ + data: DataPoint[]; + /** + * Width of the chart in pixels + */ + width: number; + /** + * Height of the chart in pixels + */ + height: number; + /** + * Whether to show tooltips on hover + */ + showTooltips?: boolean; +}; + +/** + * Renders a pie chart using the provided data. + * + * @param {PieChartProps} props - Component props + * @return {JSX.Element} The rendered pie chart component + */ +const PieChart = ( { data, width, height, showTooltips = false }: PieChartProps ) => { + const theme = useChartTheme(); + const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, hideTooltip, showTooltip } = + useTooltip< DataPoint >(); + + // Calculate radius based on width/height + const radius = Math.min( width, height ) / 2; + const centerX = width / 2; + const centerY = height / 2; + + const handleMouseMove = React.useCallback( + ( event: React.MouseEvent< SVGPathElement >, datum: DataPoint ) => { + const coords = localPoint( event ); + if ( ! coords ) return; + + showTooltip( { + tooltipData: datum, + tooltipLeft: coords.x, + tooltipTop: coords.y - 10, + } ); + }, + [ showTooltip ] + ); + + const handleMouseLeave = React.useCallback( () => { + hideTooltip(); + }, [ hideTooltip ] ); + + return ( +
+ + + d.value } + outerRadius={ radius - 20 } // Leave space for labels/tooltips + innerRadius={ 0 } + padAngle={ 0.02 } + > + { pie => { + return pie.arcs.map( ( arc, index ) => { + const [ centroidX, centroidY ] = pie.path.centroid( arc ); + const hasSpaceForLabel = arc.endAngle - arc.startAngle >= 0.25; + + return ( + + handleMouseMove( event, arc.data ) : undefined + } + onMouseLeave={ showTooltips ? handleMouseLeave : undefined } + /> + { hasSpaceForLabel && ( + + { arc.data.label } + + ) } + + ); + } ); + } } + + + + { tooltipOpen && tooltipData && ( + + ) } +
+ ); +}; + +export default PieChart; diff --git a/projects/js-packages/charts/src/components/pie-chart/stories/index.stories.tsx b/projects/js-packages/charts/src/components/pie-chart/stories/index.stories.tsx new file mode 100644 index 0000000000000..70884447f4bfc --- /dev/null +++ b/projects/js-packages/charts/src/components/pie-chart/stories/index.stories.tsx @@ -0,0 +1,45 @@ +import { PieChart } from '../index'; +import type { Meta } from '@storybook/react'; + +export default { + title: 'JS Packages/Charts/Types/Pie Chart', + component: PieChart, + parameters: { + layout: 'centered', + }, + decorators: [ + Story => ( +
+ +
+ ), + ], +} satisfies Meta< typeof PieChart >; + +const Template = args => ; + +export const Default = Template.bind( {} ); +Default.args = { + width: 400, + height: 400, + showTooltips: false, + data: [ + { label: 'A', value: 30 }, + { label: 'B', value: 20 }, + { label: 'C', value: 15 }, + { label: 'D', value: 35 }, + ], +}; + +export const WithTooltips = Template.bind( {} ); +WithTooltips.args = { + ...Default.args, + showTooltips: true, +}; +WithTooltips.parameters = { + docs: { + description: { + story: 'Pie chart with interactive tooltips that appear on hover.', + }, + }, +}; diff --git a/projects/js-packages/charts/src/components/tooltip/index.tsx b/projects/js-packages/charts/src/components/tooltip/index.tsx index 3aa503cd4919f..1be162bbeba1e 100644 --- a/projects/js-packages/charts/src/components/tooltip/index.tsx +++ b/projects/js-packages/charts/src/components/tooltip/index.tsx @@ -8,6 +8,7 @@ const defaultTooltipStyles = { borderRadius: '4px', fontSize: '14px', boxShadow: '0 1px 2px rgba(0,0,0,0.1)', + transform: 'translate(-50%, -100%)', }; interface TooltipComponentProps { diff --git a/projects/js-packages/charts/src/index.ts b/projects/js-packages/charts/src/index.ts index 86a972923e189..5847237506be1 100644 --- a/projects/js-packages/charts/src/index.ts +++ b/projects/js-packages/charts/src/index.ts @@ -1,6 +1,17 @@ +// Charts export { default as BarChart } from './components/bar-chart'; export { LineChart } from './components/line-chart'; +export { PieChart } from './components/pie-chart'; export { PieSemiCircleChart } from './components/pie-semi-circle-chart'; -export type * from './components/shared/types'; + +// Chart components export { Tooltip } from './components/tooltip'; + +// Providers +export { ThemeProvider } from './providers/theme'; + +// Hooks + +// Types +export type * from './components/shared/types'; export type { TooltipProps } from './components/tooltip/types'; diff --git a/projects/js-packages/storybook/package.json b/projects/js-packages/storybook/package.json index 3cec738cada6e..bcdb304b57263 100644 --- a/projects/js-packages/storybook/package.json +++ b/projects/js-packages/storybook/package.json @@ -70,6 +70,7 @@ "webpack-cli": "4.9.1" }, "dependencies": { + "@visx/responsive": "3.12.0", "@wordpress/api-fetch": "7.13.0" } } From 537347968cc5126000d9bd445a2a92cbd8f3ddc4 Mon Sep 17 00:00:00 2001 From: Grzegorz Chudzinski-Pawlowski <112354940+grzegorz-cp@users.noreply.github.com> Date: Thu, 12 Dec 2024 12:15:45 +1100 Subject: [PATCH 02/13] Charts - Updating charts and adding a tooltip hook --- .../line-chart/stories/index.stories.tsx | 7 -- .../src/components/pie-chart/pie-chart.tsx | 86 +++++++++--------- .../pie-chart/stories/index.stories.tsx | 73 +++++++++------ .../charts/src/components/shared/types.d.ts | 2 + .../charts/src/components/tooltip/index.tsx | 1 - .../src/hooks/use-chart-mouse-handler.ts | 89 +++++++++++++++++++ .../charts/src/providers/theme/themes.ts | 9 +- 7 files changed, 187 insertions(+), 80 deletions(-) create mode 100644 projects/js-packages/charts/src/hooks/use-chart-mouse-handler.ts diff --git a/projects/js-packages/charts/src/components/line-chart/stories/index.stories.tsx b/projects/js-packages/charts/src/components/line-chart/stories/index.stories.tsx index cdf6adf0f4f8a..d5c1a7fb6e2fb 100644 --- a/projects/js-packages/charts/src/components/line-chart/stories/index.stories.tsx +++ b/projects/js-packages/charts/src/components/line-chart/stories/index.stories.tsx @@ -2,13 +2,6 @@ import { LineChart } from '../index'; import sampleData from './sample-data'; import type { Meta } from '@storybook/react'; -// const data = [ -// { date: new Date( '2023-01-01' ), value: 10 }, -// { date: new Date( '2023-02-01' ), value: 20 }, -// { date: new Date( '2023-03-01' ), value: 15 }, -// { date: new Date( '2023-04-01' ), value: 25 }, -// ]; - export default { title: 'JS Packages/Charts/Types/Line Chart', component: LineChart, diff --git a/projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx b/projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx index 52385712f61c0..75da6838c4b88 100644 --- a/projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx +++ b/projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx @@ -1,12 +1,13 @@ -import { localPoint } from '@visx/event'; import { Group } from '@visx/group'; import { Pie } from '@visx/shape'; -import { useTooltip } from '@visx/tooltip'; -import React from 'react'; -import { useChartTheme } from '../../providers/theme/theme-provider'; +import { SVGProps } from 'react'; +import useChartMouseHandler from '../../hooks/use-chart-mouse-handler'; +import { useChartTheme, defaultTheme } from '../../providers/theme'; import { Tooltip } from '../tooltip'; import type { DataPoint } from '../shared/types'; +// TODO: add animation + type PieChartProps = { /** * Array of data points to display in the chart @@ -21,9 +22,9 @@ type PieChartProps = { */ height: number; /** - * Whether to show tooltips on hover + * Whether to show tooltips on hover. False by default. */ - showTooltips?: boolean; + withTooltips?: boolean; }; /** @@ -32,33 +33,23 @@ type PieChartProps = { * @param {PieChartProps} props - Component props * @return {JSX.Element} The rendered pie chart component */ -const PieChart = ( { data, width, height, showTooltips = false }: PieChartProps ) => { - const theme = useChartTheme(); - const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, hideTooltip, showTooltip } = - useTooltip< DataPoint >(); +const PieChart = ( { data, width, height, withTooltips = false }: PieChartProps ) => { + const providerTheme = useChartTheme(); + const { onMouseMove, onMouseLeave, tooltipOpen, tooltipData, tooltipLeft, tooltipTop } = + useChartMouseHandler( { + withTooltips, + } ); // Calculate radius based on width/height const radius = Math.min( width, height ) / 2; const centerX = width / 2; const centerY = height / 2; - const handleMouseMove = React.useCallback( - ( event: React.MouseEvent< SVGPathElement >, datum: DataPoint ) => { - const coords = localPoint( event ); - if ( ! coords ) return; - - showTooltip( { - tooltipData: datum, - tooltipLeft: coords.x, - tooltipTop: coords.y - 10, - } ); - }, - [ showTooltip ] - ); - - const handleMouseLeave = React.useCallback( () => { - hideTooltip(); - }, [ hideTooltip ] ); + const accessors = { + value: d => d.value, + // Use the color property from the data object as a last resort. The theme provides colours by default. + fill: d => d.color || providerTheme.colors[ d.index ], + }; return (
@@ -66,34 +57,38 @@ const PieChart = ( { data, width, height, showTooltips = false }: PieChartProps d.value } + pieValue={ accessors.value } outerRadius={ radius - 20 } // Leave space for labels/tooltips innerRadius={ 0 } - padAngle={ 0.02 } + // padAngle={ 0.02 } > { pie => { return pie.arcs.map( ( arc, index ) => { const [ centroidX, centroidY ] = pie.path.centroid( arc ); const hasSpaceForLabel = arc.endAngle - arc.startAngle >= 0.25; + const handleMouseMove = event => onMouseMove( event, arc.data ); + + const pathProps: SVGProps< SVGPathElement > = { + d: pie.path( arc ) || '', + fill: accessors.fill( arc ), + }; + + if ( withTooltips ) { + pathProps.onMouseMove = handleMouseMove; + pathProps.onMouseLeave = onMouseLeave; + } return ( - handleMouseMove( event, arc.data ) : undefined - } - onMouseLeave={ showTooltips ? handleMouseLeave : undefined } - /> + { hasSpaceForLabel && ( - { tooltipOpen && tooltipData && ( - + { withTooltips && tooltipOpen && tooltipData && ( + ) }
); diff --git a/projects/js-packages/charts/src/components/pie-chart/stories/index.stories.tsx b/projects/js-packages/charts/src/components/pie-chart/stories/index.stories.tsx index 70884447f4bfc..2bf4d09efe14c 100644 --- a/projects/js-packages/charts/src/components/pie-chart/stories/index.stories.tsx +++ b/projects/js-packages/charts/src/components/pie-chart/stories/index.stories.tsx @@ -1,5 +1,15 @@ +import { ThemeProvider, jetpackTheme, wooTheme } from '../../../providers/theme'; import { PieChart } from '../index'; -import type { Meta } from '@storybook/react'; +import type { Meta, StoryObj } from '@storybook/react'; + +const data = [ + { label: 'A', value: 30 }, + { label: 'B', value: 20 }, + { label: 'C', value: 15 }, + { label: 'D', value: 35 }, +]; + +type StoryType = StoryObj< typeof PieChart >; export default { title: 'JS Packages/Charts/Types/Pie Chart', @@ -7,39 +17,48 @@ export default { parameters: { layout: 'centered', }, + argTypes: { + theme: { + control: 'select', + options: { + default: undefined, + jetpack: jetpackTheme, + woo: wooTheme, + }, + defaultValue: undefined, + }, + }, decorators: [ - Story => ( -
- -
+ ( Story, { args } ) => ( + +
+ +
+
), ], } satisfies Meta< typeof PieChart >; -const Template = args => ; - -export const Default = Template.bind( {} ); -Default.args = { - width: 400, - height: 400, - showTooltips: false, - data: [ - { label: 'A', value: 30 }, - { label: 'B', value: 20 }, - { label: 'C', value: 15 }, - { label: 'D', value: 35 }, - ], +export const Default: StoryType = { + args: { + width: 400, + height: 400, + withTooltips: false, + data, + theme: 'default', + }, }; -export const WithTooltips = Template.bind( {} ); -WithTooltips.args = { - ...Default.args, - showTooltips: true, -}; -WithTooltips.parameters = { - docs: { - description: { - story: 'Pie chart with interactive tooltips that appear on hover.', +export const WithTooltips: StoryType = { + args: { + ...Default.args, + withTooltips: true, + }, + parameters: { + docs: { + description: { + story: 'Pie chart with interactive tooltips that appear on hover.', + }, }, }, }; diff --git a/projects/js-packages/charts/src/components/shared/types.d.ts b/projects/js-packages/charts/src/components/shared/types.d.ts index 44f2fc9831d57..43168ef21c17e 100644 --- a/projects/js-packages/charts/src/components/shared/types.d.ts +++ b/projects/js-packages/charts/src/components/shared/types.d.ts @@ -39,6 +39,8 @@ export type DataPointPercentage = { export type ChartTheme = { /** Background color for chart components */ backgroundColor: string; + /** Background color for labels */ + labelBackgroundColor?: string; /** Array of colors used for data visualization */ colors: string[]; /** Optional CSS styles for grid lines */ diff --git a/projects/js-packages/charts/src/components/tooltip/index.tsx b/projects/js-packages/charts/src/components/tooltip/index.tsx index 1be162bbeba1e..3aa503cd4919f 100644 --- a/projects/js-packages/charts/src/components/tooltip/index.tsx +++ b/projects/js-packages/charts/src/components/tooltip/index.tsx @@ -8,7 +8,6 @@ const defaultTooltipStyles = { borderRadius: '4px', fontSize: '14px', boxShadow: '0 1px 2px rgba(0,0,0,0.1)', - transform: 'translate(-50%, -100%)', }; interface TooltipComponentProps { diff --git a/projects/js-packages/charts/src/hooks/use-chart-mouse-handler.ts b/projects/js-packages/charts/src/hooks/use-chart-mouse-handler.ts new file mode 100644 index 0000000000000..c259f063bbb4b --- /dev/null +++ b/projects/js-packages/charts/src/hooks/use-chart-mouse-handler.ts @@ -0,0 +1,89 @@ +import { localPoint } from '@visx/event'; +import { useTooltip } from '@visx/tooltip'; +import { useCallback, type MouseEvent } from 'react'; +import type { DataPoint } from '../components/shared/types'; + +type UseChartMouseHandlerProps = { + /** + * Whether tooltips are enabled + */ + withTooltips: boolean; +}; + +type UseChartMouseHandlerReturn = { + /** + * Handler for mouse move events + */ + onMouseMove: ( event: React.MouseEvent< SVGElement >, data: DataPoint ) => void; + /** + * Handler for mouse leave events + */ + onMouseLeave: () => void; + /** + * Whether the tooltip is currently open + */ + tooltipOpen: boolean; + /** + * The current tooltip data + */ + tooltipData: DataPoint | null; + /** + * The current tooltip left position + */ + tooltipLeft: number | undefined; + /** + * The current tooltip top position + */ + tooltipTop: number | undefined; +}; + +/** + * Hook to handle mouse interactions for chart components + * + * @param {UseChartMouseHandlerProps} props - Hook configuration + * @return {UseChartMouseHandlerReturn} Object containing handlers and tooltip state + */ +const useChartMouseHandler = ( { + withTooltips, +}: UseChartMouseHandlerProps ): UseChartMouseHandlerReturn => { + const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, hideTooltip, showTooltip } = + useTooltip< DataPoint >(); + + const onMouseMove = useCallback( + ( event: MouseEvent< SVGElement >, data: DataPoint ) => { + if ( ! withTooltips ) { + return; + } + + const coords = localPoint( event ); + if ( ! coords ) { + return; + } + + showTooltip( { + tooltipData: data, + tooltipLeft: coords.x, + tooltipTop: coords.y - 10, + } ); + }, + [ withTooltips, showTooltip ] + ); + + const onMouseLeave = useCallback( () => { + if ( ! withTooltips ) { + return; + } + hideTooltip(); + }, [ withTooltips, hideTooltip ] ); + + return { + onMouseMove, + onMouseLeave, + tooltipOpen, + tooltipData, + tooltipLeft, + tooltipTop, + }; +}; + +export default useChartMouseHandler; diff --git a/projects/js-packages/charts/src/providers/theme/themes.ts b/projects/js-packages/charts/src/providers/theme/themes.ts index b41d14bd845a1..58bcf3c3fcb31 100644 --- a/projects/js-packages/charts/src/providers/theme/themes.ts +++ b/projects/js-packages/charts/src/providers/theme/themes.ts @@ -4,7 +4,8 @@ import type { ChartTheme } from '../../components/shared/types'; * Default theme configuration */ const defaultTheme: ChartTheme = { - backgroundColor: '#FFFFFF', + backgroundColor: '#FFFFFF', // chart background color + labelBackgroundColor: '#FFFFFF', // label background color colors: [ '#98C8DF', '#006DAB', '#A6DC80', '#1F9828', '#FF8C8F' ], gridStyles: { stroke: '#787C82', @@ -19,7 +20,8 @@ const defaultTheme: ChartTheme = { * Jetpack theme configuration */ const jetpackTheme: ChartTheme = { - backgroundColor: '#FFFFFF', + backgroundColor: '#FFFFFF', // chart background color + labelBackgroundColor: '#FFFFFF', // label background color colors: [ '#98C8DF', '#006DAB', '#A6DC80', '#1F9828', '#FF8C8F' ], gridStyles: { stroke: '#787C82', @@ -34,7 +36,8 @@ const jetpackTheme: ChartTheme = { * Woo theme configuration */ const wooTheme: ChartTheme = { - backgroundColor: '#FFFFFF', + backgroundColor: '#FFFFFF', // chart background color + labelBackgroundColor: '#FFFFFF', // label background color colors: [ '#80C8FF', '#B999FF', '#3858E9' ], gridStyles: { stroke: '#787C82', From 8bd469aa3a2033d6c951d0fb1ecd69b025979ddb Mon Sep 17 00:00:00 2001 From: Grzegorz Chudzinski-Pawlowski <112354940+grzegorz-cp@users.noreply.github.com> Date: Thu, 12 Dec 2024 12:24:04 +1100 Subject: [PATCH 03/13] Charts - Adding doughts to pies --- .../src/components/pie-chart/pie-chart.tsx | 18 ++++++++--- .../pie-chart/stories/index.stories.tsx | 30 +++++++++++++++++++ 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx b/projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx index 75da6838c4b88..286c53e443c5b 100644 --- a/projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx +++ b/projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx @@ -25,15 +25,25 @@ type PieChartProps = { * Whether to show tooltips on hover. False by default. */ withTooltips?: boolean; + /** + * Inner radius in pixels. If > 0, creates a donut chart. Defaults to 0. + */ + innerRadius?: number; }; /** - * Renders a pie chart using the provided data. + * Renders a pie or donut chart using the provided data. * * @param {PieChartProps} props - Component props - * @return {JSX.Element} The rendered pie chart component + * @return {JSX.Element} The rendered chart component */ -const PieChart = ( { data, width, height, withTooltips = false }: PieChartProps ) => { +const PieChart = ( { + data, + width, + height, + withTooltips = false, + innerRadius = 0, +}: PieChartProps ) => { const providerTheme = useChartTheme(); const { onMouseMove, onMouseLeave, tooltipOpen, tooltipData, tooltipLeft, tooltipTop } = useChartMouseHandler( { @@ -59,7 +69,7 @@ const PieChart = ( { data, width, height, withTooltips = false }: PieChartProps data={ data } pieValue={ accessors.value } outerRadius={ radius - 20 } // Leave space for labels/tooltips - innerRadius={ 0 } + innerRadius={ innerRadius } // padAngle={ 0.02 } > { pie => { diff --git a/projects/js-packages/charts/src/components/pie-chart/stories/index.stories.tsx b/projects/js-packages/charts/src/components/pie-chart/stories/index.stories.tsx index 2bf4d09efe14c..ddad97895531d 100644 --- a/projects/js-packages/charts/src/components/pie-chart/stories/index.stories.tsx +++ b/projects/js-packages/charts/src/components/pie-chart/stories/index.stories.tsx @@ -46,6 +46,21 @@ export const Default: StoryType = { withTooltips: false, data, theme: 'default', + innerRadius: 0, + }, +}; + +export const Doughnut: StoryType = { + args: { + ...Default.args, + innerRadius: 80, + }, + parameters: { + docs: { + description: { + story: 'Doughnut chart variant with inner radius of 80px.', + }, + }, }, }; @@ -62,3 +77,18 @@ export const WithTooltips: StoryType = { }, }, }; + +export const WithTooltipsDoughnut: StoryType = { + args: { + ...Default.args, + withTooltips: true, + innerRadius: 100, + }, + parameters: { + docs: { + description: { + story: 'Doughnut chart with interactive tooltips that appear on hover.', + }, + }, + }, +}; From 645bb54848453235246d46f4c33ef0869bc5480b Mon Sep 17 00:00:00 2001 From: Grzegorz Chudzinski-Pawlowski <112354940+grzegorz-cp@users.noreply.github.com> Date: Thu, 12 Dec 2024 12:28:29 +1100 Subject: [PATCH 04/13] Charts - Fixing dependency --- pnpm-lock.yaml | 6 +++--- projects/js-packages/charts/package.json | 1 + projects/js-packages/storybook/package.json | 1 - 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 256e58fcb4b3e..4f45af32e16cb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -299,6 +299,9 @@ importers: '@visx/group': specifier: ^3.12.0 version: 3.12.0(react@18.3.1) + '@visx/responsive': + specifier: 3.12.0 + version: 3.12.0(react@18.3.1) '@visx/scale': specifier: ^3.12.0 version: 3.12.0 @@ -1406,9 +1409,6 @@ importers: projects/js-packages/storybook: dependencies: - '@visx/responsive': - specifier: 3.12.0 - version: 3.12.0(react@18.3.1) '@wordpress/api-fetch': specifier: 7.13.0 version: 7.13.0 diff --git a/projects/js-packages/charts/package.json b/projects/js-packages/charts/package.json index 41e7c7118aaa1..e51243ee01bc2 100644 --- a/projects/js-packages/charts/package.json +++ b/projects/js-packages/charts/package.json @@ -21,6 +21,7 @@ }, "dependencies": { "@react-spring/web": "9.7.3", + "@visx/responsive": "3.12.0", "@visx/axis": "^3.12.0", "@visx/group": "^3.12.0", "@visx/scale": "^3.12.0", diff --git a/projects/js-packages/storybook/package.json b/projects/js-packages/storybook/package.json index bcdb304b57263..3cec738cada6e 100644 --- a/projects/js-packages/storybook/package.json +++ b/projects/js-packages/storybook/package.json @@ -70,7 +70,6 @@ "webpack-cli": "4.9.1" }, "dependencies": { - "@visx/responsive": "3.12.0", "@wordpress/api-fetch": "7.13.0" } } From 54801c085c989646f87aac1038d983e389c770a4 Mon Sep 17 00:00:00 2001 From: Grzegorz Chudzinski-Pawlowski <112354940+grzegorz-cp@users.noreply.github.com> Date: Thu, 12 Dec 2024 12:29:17 +1100 Subject: [PATCH 05/13] changelog --- projects/js-packages/charts/changelog/add-charts-pie-chart | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 projects/js-packages/charts/changelog/add-charts-pie-chart diff --git a/projects/js-packages/charts/changelog/add-charts-pie-chart b/projects/js-packages/charts/changelog/add-charts-pie-chart new file mode 100644 index 0000000000000..81e8c5063e8ae --- /dev/null +++ b/projects/js-packages/charts/changelog/add-charts-pie-chart @@ -0,0 +1,4 @@ +Significance: patch +Type: added + +Adding new chart type - pie chart. From 54f4042ad85b2235d3866e937255ec75a9a20e3b Mon Sep 17 00:00:00 2001 From: Grzegorz Chudzinski-Pawlowski <112354940+grzegorz-cp@users.noreply.github.com> Date: Fri, 13 Dec 2024 09:37:54 +1100 Subject: [PATCH 06/13] Charts - Adding chart TS interface --- .../charts/src/components/bar-chart/index.tsx | 23 ++++++-------- .../src/components/pie-chart/pie-chart.tsx | 18 ++--------- .../pie-semi-circle-chart.tsx | 14 ++------- .../charts/src/components/shared/types.d.ts | 31 +++++++++++++++++++ 4 files changed, 46 insertions(+), 40 deletions(-) diff --git a/projects/js-packages/charts/src/components/bar-chart/index.tsx b/projects/js-packages/charts/src/components/bar-chart/index.tsx index 41a22493c509d..8419402b02dc7 100644 --- a/projects/js-packages/charts/src/components/bar-chart/index.tsx +++ b/projects/js-packages/charts/src/components/bar-chart/index.tsx @@ -7,17 +7,14 @@ import { useTooltip } from '@visx/tooltip'; import React from 'react'; import { useChartTheme } from '../../providers/theme'; import { Tooltip } from '../tooltip'; -import type { DataPoint } from '../shared/types'; +import type { BaseChartProps, DataPoint } from '../shared/types'; -type BarChartProps = { +interface BarChartProps extends BaseChartProps { + /** + * Array of data points to display in the chart + */ data: DataPoint[]; - width: number; - height: number; - margin?: { - [ K in 'top' | 'right' | 'bottom' | 'left' ]?: number; - }; - showTooltips?: boolean; -}; +} /** * Renders a bar chart using the provided data. @@ -25,7 +22,7 @@ type BarChartProps = { * @param {BarChartProps} props - Component props * @return {JSX.Element} The rendered bar chart component */ -function BarChart( { data, width, height, margin, showTooltips = false }: BarChartProps ) { +function BarChart( { data, width, height, margin, withTooltips = false }: BarChartProps ) { const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, hideTooltip, showTooltip } = useTooltip< DataPoint >(); @@ -65,10 +62,10 @@ function BarChart( { data, width, height, margin, showTooltips = false }: BarCha const getMouseMoveHandler = React.useCallback( ( d: DataPoint ) => { - if ( ! showTooltips ) return undefined; + if ( ! withTooltips ) return undefined; return ( event: React.MouseEvent< SVGRectElement > ) => handleMouseMove( event, d ); }, - [ showTooltips, handleMouseMove ] + [ withTooltips, handleMouseMove ] ); return ( @@ -84,7 +81,7 @@ function BarChart( { data, width, height, margin, showTooltips = false }: BarCha height={ yMax - ( yScale( d.value ) ?? 0 ) } fill={ theme.colors[ 0 ] } onMouseMove={ getMouseMoveHandler( d ) } - onMouseLeave={ showTooltips ? handleMouseLeave : undefined } + onMouseLeave={ withTooltips ? handleMouseLeave : undefined } /> ) ) } diff --git a/projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx b/projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx index 286c53e443c5b..d8f5eabd65311 100644 --- a/projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx +++ b/projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx @@ -4,32 +4,20 @@ import { SVGProps } from 'react'; import useChartMouseHandler from '../../hooks/use-chart-mouse-handler'; import { useChartTheme, defaultTheme } from '../../providers/theme'; import { Tooltip } from '../tooltip'; -import type { DataPoint } from '../shared/types'; +import type { BaseChartProps, DataPoint } from '../shared/types'; // TODO: add animation -type PieChartProps = { +interface PieChartProps extends BaseChartProps { /** * Array of data points to display in the chart */ data: DataPoint[]; - /** - * Width of the chart in pixels - */ - width: number; - /** - * Height of the chart in pixels - */ - height: number; - /** - * Whether to show tooltips on hover. False by default. - */ - withTooltips?: boolean; /** * Inner radius in pixels. If > 0, creates a donut chart. Defaults to 0. */ innerRadius?: number; -}; +} /** * Renders a pie or donut chart using the provided data. diff --git a/projects/js-packages/charts/src/components/pie-semi-circle-chart/pie-semi-circle-chart.tsx b/projects/js-packages/charts/src/components/pie-semi-circle-chart/pie-semi-circle-chart.tsx index e67868e16adb4..f976706776c01 100644 --- a/projects/js-packages/charts/src/components/pie-semi-circle-chart/pie-semi-circle-chart.tsx +++ b/projects/js-packages/charts/src/components/pie-semi-circle-chart/pie-semi-circle-chart.tsx @@ -5,23 +5,13 @@ import clsx from 'clsx'; import { FC } from 'react'; import { useChartTheme } from '../../providers/theme/theme-provider'; import styles from './pie-semi-circle-chart.module.scss'; -import type { DataPointPercentage } from '../shared/types'; +import type { BaseChartProps, DataPointPercentage } from '../shared/types'; -// TODO: convert hard-coded values to props - -interface PieSemiCircleChartProps { +interface PieSemiCircleChartProps extends BaseChartProps { /** * Array of data points to display in the chart */ data: DataPointPercentage[]; - /** - * Width of the chart in pixels - */ - width: number; - /** - * Height of the chart in pixels - */ - height: number; /** * Label text to display above the chart */ diff --git a/projects/js-packages/charts/src/components/shared/types.d.ts b/projects/js-packages/charts/src/components/shared/types.d.ts index 43168ef21c17e..f8e5b68fc64bc 100644 --- a/projects/js-packages/charts/src/components/shared/types.d.ts +++ b/projects/js-packages/charts/src/components/shared/types.d.ts @@ -52,3 +52,34 @@ export type ChartTheme = { /** Color of the grid lines in dark mode */ gridColorDark: string; }; + +/** + * Base properties shared across all chart components + */ +export type BaseChartProps = { + /** + * Array of data points to display in the chart + */ + data: DataPoint[] | DataPointDate[]; + /** + * Width of the chart in pixels + */ + width: number; + /** + * Height of the chart in pixels + */ + height: number; + /** + * Chart margins + */ + margin?: { + top?: number; + right?: number; + bottom?: number; + left?: number; + }; + /** + * Whether to show tooltips on hover. False by default. + */ + withTooltips?: boolean; +}; From 73f63364cd0aa81cc3c59bbdcee2101a154437e9 Mon Sep 17 00:00:00 2001 From: Grzegorz Chudzinski-Pawlowski <112354940+grzegorz-cp@users.noreply.github.com> Date: Fri, 13 Dec 2024 09:49:08 +1100 Subject: [PATCH 07/13] Charts - Adding support for multiple data sets --- .../src/components/line-chart/line-chart.tsx | 89 +++-- .../line-chart/stories/index.stories.tsx | 2 +- .../line-chart/stories/sample-data.ts | 345 +++++++++--------- .../charts/src/components/shared/types.d.ts | 22 +- 4 files changed, 237 insertions(+), 221 deletions(-) diff --git a/projects/js-packages/charts/src/components/line-chart/line-chart.tsx b/projects/js-packages/charts/src/components/line-chart/line-chart.tsx index a7aa6cf8cb9db..3634239d18896 100644 --- a/projects/js-packages/charts/src/components/line-chart/line-chart.tsx +++ b/projects/js-packages/charts/src/components/line-chart/line-chart.tsx @@ -10,49 +10,45 @@ import clsx from 'clsx'; import { FC } from 'react'; import { useChartTheme } from '../../providers/theme/theme-provider'; import styles from './line-chart.module.scss'; -import type { DataPointDate } from '../shared/types'; +import type { BaseChartProps, DataPointDate, SeriesData } from '../shared/types'; // TODO: revisit grid and axis options - accept as props for frid lines, axis, values: x, y, all, none -type LineChartProps = { +interface LineChartProps extends BaseChartProps< SeriesData[] > { /** - * Array of data points to display in the chart + * Data series to display in the chart */ - data: DataPointDate[]; - /** - * Width of the chart in pixels - */ - width: number; - /** - * Height of the chart in pixels - */ - height: number; - /** - * Chart margins - */ - margin?: { top: number; right: number; bottom: number; left: number }; - /** - * Color of the line - */ - lineColor?: string; + data: SeriesData[]; +} + +type TooltipData = { + date: Date; + [ key: string ]: number | Date; }; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const renderTooltip: any = ( { tooltipData } ) => { - // TODO: fix any +const renderTooltip = ( { + tooltipData, +}: { + tooltipData?: { nearestDatum?: { datum: TooltipData } }; +} ) => { const datum = tooltipData?.nearestDatum?.datum; - if ( ! datum ) { - return null; - } + if ( ! datum ) return null; return (
Date: { datum.date.toLocaleDateString() }
-
- Value: { datum.value } -
+ { Object.entries( datum ).map( ( [ key, value ] ) => { + if ( key === 'date' ) return null; + return ( +
+ + { key }: { value.toString() } + +
+ ); + } ) }
); }; @@ -65,8 +61,6 @@ const formatDateTick = ( value: number ) => { } ); }; -// TODO: add support for multiple data sets - const LineChart: FC< LineChartProps > = ( { data, width, @@ -74,24 +68,27 @@ const LineChart: FC< LineChartProps > = ( { margin = { top: 20, right: 20, bottom: 40, left: 40 }, } ) => { const providerTheme = useChartTheme(); + + if ( ! data.length ) { + return ( +
Empty...
+ ); + } + const accessors = { xAccessor: ( d: DataPointDate ) => d.date, yAccessor: ( d: DataPointDate ) => d.value, }; - // Use theme to construct XYChart theme - const chartTheme = { + const theme = buildChartTheme( { backgroundColor: providerTheme.backgroundColor, colors: providerTheme.colors, gridStyles: providerTheme.gridStyles, tickLength: providerTheme?.tickLength || 0, gridColor: providerTheme?.gridColor || '', gridColorDark: providerTheme?.gridColorDark || '', - }; - - const theme = buildChartTheme( chartTheme ); + } ); - // return (
= ( { yScale={ { type: 'linear', nice: true } } > - - + { data.map( ( seriesData, index ) => ( + + ) ) } = { /** * Array of data points to display in the chart */ - data: DataPoint[] | DataPointDate[]; + data: T extends DataPoint | DataPointDate ? T[] : T; /** * Width of the chart in pixels */ @@ -73,10 +83,10 @@ export type BaseChartProps = { * Chart margins */ margin?: { - top?: number; - right?: number; - bottom?: number; - left?: number; + top: number; + right: number; + bottom: number; + left: number; }; /** * Whether to show tooltips on hover. False by default. From 25da221737a2453aefaa8a400035eefe3a232249 Mon Sep 17 00:00:00 2001 From: Grzegorz Chudzinski-Pawlowski <112354940+grzegorz-cp@users.noreply.github.com> Date: Fri, 13 Dec 2024 09:54:43 +1100 Subject: [PATCH 08/13] Charts - Adding shared chart TS interface --- .../src/components/line-chart/line-chart.tsx | 25 ++----------------- .../src/components/pie-chart/pie-chart.tsx | 6 +---- .../pie-semi-circle-chart.tsx | 6 +---- .../charts/src/components/shared/types.d.ts | 12 ++++----- 4 files changed, 10 insertions(+), 39 deletions(-) diff --git a/projects/js-packages/charts/src/components/line-chart/line-chart.tsx b/projects/js-packages/charts/src/components/line-chart/line-chart.tsx index a7aa6cf8cb9db..a4ee95e27fa40 100644 --- a/projects/js-packages/charts/src/components/line-chart/line-chart.tsx +++ b/projects/js-packages/charts/src/components/line-chart/line-chart.tsx @@ -10,32 +10,11 @@ import clsx from 'clsx'; import { FC } from 'react'; import { useChartTheme } from '../../providers/theme/theme-provider'; import styles from './line-chart.module.scss'; -import type { DataPointDate } from '../shared/types'; +import type { BaseChartProps, DataPointDate } from '../shared/types'; // TODO: revisit grid and axis options - accept as props for frid lines, axis, values: x, y, all, none -type LineChartProps = { - /** - * Array of data points to display in the chart - */ - data: DataPointDate[]; - /** - * Width of the chart in pixels - */ - width: number; - /** - * Height of the chart in pixels - */ - height: number; - /** - * Chart margins - */ - margin?: { top: number; right: number; bottom: number; left: number }; - /** - * Color of the line - */ - lineColor?: string; -}; +interface LineChartProps extends BaseChartProps< DataPointDate[] > {} // eslint-disable-next-line @typescript-eslint/no-explicit-any const renderTooltip: any = ( { tooltipData } ) => { diff --git a/projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx b/projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx index d8f5eabd65311..e12aaf53d9b0c 100644 --- a/projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx +++ b/projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx @@ -8,11 +8,7 @@ import type { BaseChartProps, DataPoint } from '../shared/types'; // TODO: add animation -interface PieChartProps extends BaseChartProps { - /** - * Array of data points to display in the chart - */ - data: DataPoint[]; +interface PieChartProps extends BaseChartProps< DataPoint[] > { /** * Inner radius in pixels. If > 0, creates a donut chart. Defaults to 0. */ diff --git a/projects/js-packages/charts/src/components/pie-semi-circle-chart/pie-semi-circle-chart.tsx b/projects/js-packages/charts/src/components/pie-semi-circle-chart/pie-semi-circle-chart.tsx index f976706776c01..63cb4dac581ad 100644 --- a/projects/js-packages/charts/src/components/pie-semi-circle-chart/pie-semi-circle-chart.tsx +++ b/projects/js-packages/charts/src/components/pie-semi-circle-chart/pie-semi-circle-chart.tsx @@ -7,11 +7,7 @@ import { useChartTheme } from '../../providers/theme/theme-provider'; import styles from './pie-semi-circle-chart.module.scss'; import type { BaseChartProps, DataPointPercentage } from '../shared/types'; -interface PieSemiCircleChartProps extends BaseChartProps { - /** - * Array of data points to display in the chart - */ - data: DataPointPercentage[]; +interface PieSemiCircleChartProps extends BaseChartProps< DataPointPercentage[] > { /** * Label text to display above the chart */ diff --git a/projects/js-packages/charts/src/components/shared/types.d.ts b/projects/js-packages/charts/src/components/shared/types.d.ts index f8e5b68fc64bc..aeb74223d6d76 100644 --- a/projects/js-packages/charts/src/components/shared/types.d.ts +++ b/projects/js-packages/charts/src/components/shared/types.d.ts @@ -56,11 +56,11 @@ export type ChartTheme = { /** * Base properties shared across all chart components */ -export type BaseChartProps = { +export type BaseChartProps< T = DataPoint | DataPointDate > = { /** * Array of data points to display in the chart */ - data: DataPoint[] | DataPointDate[]; + data: T extends DataPoint | DataPointDate ? T[] : T; /** * Width of the chart in pixels */ @@ -73,10 +73,10 @@ export type BaseChartProps = { * Chart margins */ margin?: { - top?: number; - right?: number; - bottom?: number; - left?: number; + top: number; + right: number; + bottom: number; + left: number; }; /** * Whether to show tooltips on hover. False by default. From 6d28c88554caf1245d9f04e89f835c434f053bca Mon Sep 17 00:00:00 2001 From: Grzegorz Chudzinski-Pawlowski <112354940+grzegorz-cp@users.noreply.github.com> Date: Fri, 13 Dec 2024 10:10:57 +1100 Subject: [PATCH 09/13] Charts - Cleanup --- .../js-packages/charts/src/components/pie-chart/pie-chart.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx b/projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx index e12aaf53d9b0c..bffe55ea4de25 100644 --- a/projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx +++ b/projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx @@ -54,7 +54,6 @@ const PieChart = ( { pieValue={ accessors.value } outerRadius={ radius - 20 } // Leave space for labels/tooltips innerRadius={ innerRadius } - // padAngle={ 0.02 } > { pie => { return pie.arcs.map( ( arc, index ) => { From 5e319695bd4f74fffcc8945516a1bfe816582a17 Mon Sep 17 00:00:00 2001 From: Grzegorz Chudzinski-Pawlowski <112354940+grzegorz-cp@users.noreply.github.com> Date: Fri, 13 Dec 2024 10:27:15 +1100 Subject: [PATCH 10/13] Charts - Cleanup 2 --- projects/js-packages/charts/src/hooks/use-chart-mouse-handler.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/projects/js-packages/charts/src/hooks/use-chart-mouse-handler.ts b/projects/js-packages/charts/src/hooks/use-chart-mouse-handler.ts index c259f063bbb4b..8a1739a90e4ec 100644 --- a/projects/js-packages/charts/src/hooks/use-chart-mouse-handler.ts +++ b/projects/js-packages/charts/src/hooks/use-chart-mouse-handler.ts @@ -49,6 +49,7 @@ const useChartMouseHandler = ( { const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, hideTooltip, showTooltip } = useTooltip< DataPoint >(); + // TODO: either debounce/throttle or use useTooltipInPortal with built-in debounce const onMouseMove = useCallback( ( event: MouseEvent< SVGElement >, data: DataPoint ) => { if ( ! withTooltips ) { From efbe8a071de1622338c357d1e5b59960ec508ebe Mon Sep 17 00:00:00 2001 From: Grzegorz Chudzinski-Pawlowski <112354940+grzegorz-cp@users.noreply.github.com> Date: Fri, 13 Dec 2024 11:55:46 +1100 Subject: [PATCH 11/13] Charts - Fixing tooltips for multiple data sets, adding className prop --- .../charts/src/components/bar-chart/index.tsx | 12 +++- .../line-chart/line-chart.module.scss | 22 ++++-- .../src/components/line-chart/line-chart.tsx | 69 +++++++++++-------- .../pie-chart/pie-chart.module.scss | 3 + .../src/components/pie-chart/pie-chart.tsx | 5 +- .../pie-semi-circle-chart.tsx | 5 +- .../charts/src/components/shared/types.d.ts | 4 ++ 7 files changed, 82 insertions(+), 38 deletions(-) create mode 100644 projects/js-packages/charts/src/components/pie-chart/pie-chart.module.scss diff --git a/projects/js-packages/charts/src/components/bar-chart/index.tsx b/projects/js-packages/charts/src/components/bar-chart/index.tsx index 8419402b02dc7..f3a1e21790eba 100644 --- a/projects/js-packages/charts/src/components/bar-chart/index.tsx +++ b/projects/js-packages/charts/src/components/bar-chart/index.tsx @@ -4,6 +4,7 @@ import { Group } from '@visx/group'; import { scaleBand, scaleLinear } from '@visx/scale'; import { Bar } from '@visx/shape'; import { useTooltip } from '@visx/tooltip'; +import clsx from 'clsx'; import React from 'react'; import { useChartTheme } from '../../providers/theme'; import { Tooltip } from '../tooltip'; @@ -22,7 +23,14 @@ interface BarChartProps extends BaseChartProps { * @param {BarChartProps} props - Component props * @return {JSX.Element} The rendered bar chart component */ -function BarChart( { data, width, height, margin, withTooltips = false }: BarChartProps ) { +function BarChart( { + data, + width, + height, + margin, + withTooltips = false, + className, +}: BarChartProps ) { const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, hideTooltip, showTooltip } = useTooltip< DataPoint >(); @@ -69,7 +77,7 @@ function BarChart( { data, width, height, margin, withTooltips = false }: BarCha ); return ( -
+
{ data.map( d => ( diff --git a/projects/js-packages/charts/src/components/line-chart/line-chart.module.scss b/projects/js-packages/charts/src/components/line-chart/line-chart.module.scss index 2cc6da1a812ee..73f79787bf881 100644 --- a/projects/js-packages/charts/src/components/line-chart/line-chart.module.scss +++ b/projects/js-packages/charts/src/components/line-chart/line-chart.module.scss @@ -2,14 +2,24 @@ position: relative; &__tooltip { + background: #fff; padding: 0.5rem; + } + + &__tooltip-date { + font-weight: bold; + padding-bottom: 10px; + } - &-row { - margin-bottom: 0.25rem; + &__tooltip-row { + display: flex; + align-items: center; + padding: 4px 0; + justify-content: space-between; + } - &:last-child { - margin-bottom: 0; - } - } + &__tooltip-label { + font-weight: 500; + padding-right: 1rem; } } diff --git a/projects/js-packages/charts/src/components/line-chart/line-chart.tsx b/projects/js-packages/charts/src/components/line-chart/line-chart.tsx index 3634239d18896..155f2adee032a 100644 --- a/projects/js-packages/charts/src/components/line-chart/line-chart.tsx +++ b/projects/js-packages/charts/src/components/line-chart/line-chart.tsx @@ -14,41 +14,50 @@ import type { BaseChartProps, DataPointDate, SeriesData } from '../shared/types' // TODO: revisit grid and axis options - accept as props for frid lines, axis, values: x, y, all, none -interface LineChartProps extends BaseChartProps< SeriesData[] > { - /** - * Data series to display in the chart - */ - data: SeriesData[]; -} +interface LineChartProps extends BaseChartProps< SeriesData[] > {} type TooltipData = { date: Date; [ key: string ]: number | Date; }; +type TooltipDatum = { + key: string; + value: number; +}; + const renderTooltip = ( { tooltipData, }: { - tooltipData?: { nearestDatum?: { datum: TooltipData } }; + tooltipData?: { + nearestDatum?: { + datum: TooltipData; + key: string; + }; + datumByKey?: { [ key: string ]: { datum: TooltipData } }; + }; } ) => { - const datum = tooltipData?.nearestDatum?.datum; - if ( ! datum ) return null; + const nearestDatum = tooltipData?.nearestDatum?.datum; + if ( ! nearestDatum ) return null; + + const tooltipPoints: TooltipDatum[] = Object.entries( tooltipData?.datumByKey || {} ) + .map( ( [ key, { datum } ] ) => ( { + key, + value: datum.value as number, + } ) ) + .sort( ( a, b ) => b.value - a.value ); return (
-
- Date: { datum.date.toLocaleDateString() } +
+ { nearestDatum.date.toLocaleDateString() }
- { Object.entries( datum ).map( ( [ key, value ] ) => { - if ( key === 'date' ) return null; - return ( -
- - { key }: { value.toString() } - -
- ); - } ) } + { tooltipPoints.map( point => ( +
+ { point.key }: + { point.value } +
+ ) ) }
); }; @@ -66,6 +75,8 @@ const LineChart: FC< LineChartProps > = ( { width, height, margin = { top: 20, right: 20, bottom: 40, left: 40 }, + className, + withTooltips = true, } ) => { const providerTheme = useChartTheme(); @@ -90,7 +101,7 @@ const LineChart: FC< LineChartProps > = ( { } ); return ( -
+
= ( { /> ) ) } - + { withTooltips && ( + + ) }
); diff --git a/projects/js-packages/charts/src/components/pie-chart/pie-chart.module.scss b/projects/js-packages/charts/src/components/pie-chart/pie-chart.module.scss new file mode 100644 index 0000000000000..cbb24ea286735 --- /dev/null +++ b/projects/js-packages/charts/src/components/pie-chart/pie-chart.module.scss @@ -0,0 +1,3 @@ +.pie-chart { + position: relative; +} diff --git a/projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx b/projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx index bffe55ea4de25..712ce212792e1 100644 --- a/projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx +++ b/projects/js-packages/charts/src/components/pie-chart/pie-chart.tsx @@ -1,9 +1,11 @@ import { Group } from '@visx/group'; import { Pie } from '@visx/shape'; +import clsx from 'clsx'; import { SVGProps } from 'react'; import useChartMouseHandler from '../../hooks/use-chart-mouse-handler'; import { useChartTheme, defaultTheme } from '../../providers/theme'; import { Tooltip } from '../tooltip'; +import styles from './pie-chart.module.scss'; import type { BaseChartProps, DataPoint } from '../shared/types'; // TODO: add animation @@ -27,6 +29,7 @@ const PieChart = ( { height, withTooltips = false, innerRadius = 0, + className, }: PieChartProps ) => { const providerTheme = useChartTheme(); const { onMouseMove, onMouseLeave, tooltipOpen, tooltipData, tooltipLeft, tooltipTop } = @@ -46,7 +49,7 @@ const PieChart = ( { }; return ( -
+
= ( { height, label, note, + className, } ) => { const providerTheme = useChartTheme(); const centerX = width / 2; @@ -37,7 +38,9 @@ const PieSemiCircleChart: FC< PieSemiCircleChartProps > = ( { }; return ( -
+
diff --git a/projects/js-packages/charts/src/components/shared/types.d.ts b/projects/js-packages/charts/src/components/shared/types.d.ts index a7a56ee5e723c..b93c6a7e1eae2 100644 --- a/projects/js-packages/charts/src/components/shared/types.d.ts +++ b/projects/js-packages/charts/src/components/shared/types.d.ts @@ -71,6 +71,10 @@ export type BaseChartProps< T = DataPoint | DataPointDate > = { * Array of data points to display in the chart */ data: T extends DataPoint | DataPointDate ? T[] : T; + /** + * Additional CSS class name for the chart container + */ + className?: string; /** * Width of the chart in pixels */ From 14ca95e74d6b423d8abeb8b1a42eac24e8c5933c Mon Sep 17 00:00:00 2001 From: Grzegorz Chudzinski-Pawlowski <112354940+grzegorz-cp@users.noreply.github.com> Date: Fri, 13 Dec 2024 11:56:52 +1100 Subject: [PATCH 12/13] Charts - Updating examples --- .../line-chart/stories/index.stories.tsx | 29 +++ .../line-chart/stories/sample-data.ts | 172 +++++++++++++++++- 2 files changed, 198 insertions(+), 3 deletions(-) diff --git a/projects/js-packages/charts/src/components/line-chart/stories/index.stories.tsx b/projects/js-packages/charts/src/components/line-chart/stories/index.stories.tsx index 755ae26447125..b09bc9594c4fd 100644 --- a/projects/js-packages/charts/src/components/line-chart/stories/index.stories.tsx +++ b/projects/js-packages/charts/src/components/line-chart/stories/index.stories.tsx @@ -19,6 +19,7 @@ export default { const Template = args => ; +// Default story with multiple series export const Default = Template.bind( {} ); Default.args = { width: 500, @@ -26,3 +27,31 @@ Default.args = { margin: { top: 20, right: 20, bottom: 30, left: 40 }, data: sampleData, }; + +// Story with single data series +export const SingleSeries = Template.bind( {} ); +SingleSeries.args = { + width: 500, + height: 300, + margin: { top: 20, right: 20, bottom: 30, left: 40 }, + data: [ sampleData[ 0 ] ], // Only London temperature data +}; + +// Story without tooltip +export const WithoutTooltip = Template.bind( {} ); +WithoutTooltip.args = { + width: 500, + height: 300, + margin: { top: 20, right: 20, bottom: 30, left: 40 }, + data: sampleData, + withTooltips: false, +}; + +// Story with custom dimensions +export const CustomDimensions = Template.bind( {} ); +CustomDimensions.args = { + width: 800, + height: 400, + margin: { top: 20, right: 20, bottom: 30, left: 40 }, + data: sampleData, +}; diff --git a/projects/js-packages/charts/src/components/line-chart/stories/sample-data.ts b/projects/js-packages/charts/src/components/line-chart/stories/sample-data.ts index 78fdaacf12625..1231466c476d4 100644 --- a/projects/js-packages/charts/src/components/line-chart/stories/sample-data.ts +++ b/projects/js-packages/charts/src/components/line-chart/stories/sample-data.ts @@ -1,9 +1,65 @@ -// Data from UK Met Office (London/Heathrow), Australian Bureau of Meteorology (Canberra), -// and NASA Mars Curiosity Rover (Gale Crater) -const temperatureData = [ +import type { SeriesData } from '../../shared/types'; + +// Sample data +const temperatureData: SeriesData[] = [ { label: 'London', data: [ + // 2022 data + { date: new Date( '2022-01-01' ), value: 7.8 }, + { date: new Date( '2022-01-08' ), value: 7.2 }, + { date: new Date( '2022-01-15' ), value: 6.9 }, + { date: new Date( '2022-01-22' ), value: 6.5 }, + { date: new Date( '2022-01-29' ), value: 7.1 }, + { date: new Date( '2022-02-05' ), value: 8.3 }, + { date: new Date( '2022-02-12' ), value: 8.9 }, + { date: new Date( '2022-02-19' ), value: 9.2 }, + { date: new Date( '2022-02-26' ), value: 8.7 }, + { date: new Date( '2022-03-05' ), value: 9.4 }, + { date: new Date( '2022-03-12' ), value: 10.2 }, + { date: new Date( '2022-03-19' ), value: 11.5 }, + { date: new Date( '2022-03-26' ), value: 12.3 }, + { date: new Date( '2022-04-02' ), value: 13.1 }, + { date: new Date( '2022-04-09' ), value: 13.8 }, + { date: new Date( '2022-04-16' ), value: 14.6 }, + { date: new Date( '2022-04-23' ), value: 15.2 }, + { date: new Date( '2022-04-30' ), value: 15.9 }, + { date: new Date( '2022-05-07' ), value: 16.7 }, + { date: new Date( '2022-05-14' ), value: 17.4 }, + { date: new Date( '2022-05-21' ), value: 18.2 }, + { date: new Date( '2022-05-28' ), value: 18.9 }, + { date: new Date( '2022-06-04' ), value: 19.7 }, + { date: new Date( '2022-06-11' ), value: 20.5 }, + { date: new Date( '2022-06-18' ), value: 21.3 }, + { date: new Date( '2022-06-25' ), value: 22.1 }, + { date: new Date( '2022-07-02' ), value: 22.8 }, + { date: new Date( '2022-07-09' ), value: 23.6 }, + { date: new Date( '2022-07-16' ), value: 24.4 }, + { date: new Date( '2022-07-23' ), value: 25.2 }, + { date: new Date( '2022-07-30' ), value: 24.8 }, + { date: new Date( '2022-08-06' ), value: 24.1 }, + { date: new Date( '2022-08-13' ), value: 23.5 }, + { date: new Date( '2022-08-20' ), value: 22.8 }, + { date: new Date( '2022-08-27' ), value: 21.9 }, + { date: new Date( '2022-09-03' ), value: 20.7 }, + { date: new Date( '2022-09-10' ), value: 19.5 }, + { date: new Date( '2022-09-17' ), value: 18.3 }, + { date: new Date( '2022-09-24' ), value: 17.1 }, + { date: new Date( '2022-10-01' ), value: 16.2 }, + { date: new Date( '2022-10-08' ), value: 15.1 }, + { date: new Date( '2022-10-15' ), value: 14.2 }, + { date: new Date( '2022-10-22' ), value: 13.1 }, + { date: new Date( '2022-10-29' ), value: 12.2 }, + { date: new Date( '2022-11-05' ), value: 11.1 }, + { date: new Date( '2022-11-12' ), value: 10.2 }, + { date: new Date( '2022-11-19' ), value: 9.1 }, + { date: new Date( '2022-11-26' ), value: 8.2 }, + { date: new Date( '2022-12-03' ), value: 7.1 }, + { date: new Date( '2022-12-10' ), value: 6.2 }, + { date: new Date( '2022-12-17' ), value: 5.5 }, + { date: new Date( '2022-12-24' ), value: 5.2 }, + { date: new Date( '2022-12-31' ), value: 6.8 }, + // 2023 data { date: new Date( '2023-01-01' ), value: 8.2 }, { date: new Date( '2023-01-08' ), value: 7.9 }, { date: new Date( '2023-01-15' ), value: 5.1 }, @@ -62,6 +118,61 @@ const temperatureData = [ { label: 'Canberra', data: [ + // 2022 data + { date: new Date( '2022-01-01' ), value: 27.9 }, + { date: new Date( '2022-01-08' ), value: 28.4 }, + { date: new Date( '2022-01-15' ), value: 29.2 }, + { date: new Date( '2022-01-22' ), value: 28.9 }, + { date: new Date( '2022-01-29' ), value: 28.1 }, + { date: new Date( '2022-02-05' ), value: 27.3 }, + { date: new Date( '2022-02-12' ), value: 26.5 }, + { date: new Date( '2022-02-19' ), value: 25.4 }, + { date: new Date( '2022-02-26' ), value: 24.2 }, + { date: new Date( '2022-03-05' ), value: 23.1 }, + { date: new Date( '2022-03-12' ), value: 22.3 }, + { date: new Date( '2022-03-19' ), value: 21.2 }, + { date: new Date( '2022-03-26' ), value: 20.1 }, + { date: new Date( '2022-04-02' ), value: 19.2 }, + { date: new Date( '2022-04-09' ), value: 18.1 }, + { date: new Date( '2022-04-16' ), value: 16.9 }, + { date: new Date( '2022-04-23' ), value: 15.8 }, + { date: new Date( '2022-04-30' ), value: 14.9 }, + { date: new Date( '2022-05-07' ), value: 13.8 }, + { date: new Date( '2022-05-14' ), value: 12.9 }, + { date: new Date( '2022-05-21' ), value: 11.8 }, + { date: new Date( '2022-05-28' ), value: 10.9 }, + { date: new Date( '2022-06-04' ), value: 9.8 }, + { date: new Date( '2022-06-11' ), value: 8.9 }, + { date: new Date( '2022-06-18' ), value: 8.1 }, + { date: new Date( '2022-06-25' ), value: 7.5 }, + { date: new Date( '2022-07-02' ), value: 6.9 }, + { date: new Date( '2022-07-09' ), value: 6.7 }, + { date: new Date( '2022-07-16' ), value: 7.1 }, + { date: new Date( '2022-07-23' ), value: 7.9 }, + { date: new Date( '2022-07-30' ), value: 8.8 }, + { date: new Date( '2022-08-06' ), value: 9.9 }, + { date: new Date( '2022-08-13' ), value: 11.2 }, + { date: new Date( '2022-08-20' ), value: 12.4 }, + { date: new Date( '2022-08-27' ), value: 13.6 }, + { date: new Date( '2022-09-03' ), value: 14.8 }, + { date: new Date( '2022-09-10' ), value: 16.1 }, + { date: new Date( '2022-09-17' ), value: 17.3 }, + { date: new Date( '2022-09-24' ), value: 18.5 }, + { date: new Date( '2022-10-01' ), value: 19.8 }, + { date: new Date( '2022-10-08' ), value: 21.1 }, + { date: new Date( '2022-10-15' ), value: 22.3 }, + { date: new Date( '2022-10-22' ), value: 23.5 }, + { date: new Date( '2022-10-29' ), value: 24.6 }, + { date: new Date( '2022-11-05' ), value: 25.7 }, + { date: new Date( '2022-11-12' ), value: 26.5 }, + { date: new Date( '2022-11-19' ), value: 27.4 }, + { date: new Date( '2022-11-26' ), value: 28.2 }, + { date: new Date( '2022-12-03' ), value: 28.9 }, + { date: new Date( '2022-12-10' ), value: 29.5 }, + { date: new Date( '2022-12-17' ), value: 29.1 }, + { date: new Date( '2022-12-24' ), value: 28.2 }, + { date: new Date( '2022-12-31' ), value: 28.7 }, + // 2023 data { date: new Date( '2023-01-01' ), value: 28.5 }, { date: new Date( '2023-01-08' ), value: 29.2 }, { date: new Date( '2023-01-15' ), value: 30.1 }, @@ -120,6 +231,61 @@ const temperatureData = [ { label: 'Mars', data: [ + // 2022 data + { date: new Date( '2022-01-01' ), value: -62 }, + { date: new Date( '2022-01-08' ), value: -63 }, + { date: new Date( '2022-01-15' ), value: -64 }, + { date: new Date( '2022-01-22' ), value: -62 }, + { date: new Date( '2022-01-29' ), value: -61 }, + { date: new Date( '2022-02-05' ), value: -59 }, + { date: new Date( '2022-02-12' ), value: -56 }, + { date: new Date( '2022-02-19' ), value: -53 }, + { date: new Date( '2022-02-26' ), value: -50 }, + { date: new Date( '2022-03-05' ), value: -47 }, + { date: new Date( '2022-03-12' ), value: -44 }, + { date: new Date( '2022-03-19' ), value: -41 }, + { date: new Date( '2022-03-26' ), value: -37 }, + { date: new Date( '2022-04-02' ), value: -34 }, + { date: new Date( '2022-04-09' ), value: -31 }, + { date: new Date( '2022-04-16' ), value: -27 }, + { date: new Date( '2022-04-23' ), value: -24 }, + { date: new Date( '2022-04-30' ), value: -21 }, + { date: new Date( '2022-05-07' ), value: -17 }, + { date: new Date( '2022-05-14' ), value: -14 }, + { date: new Date( '2022-05-21' ), value: -11 }, + { date: new Date( '2022-05-28' ), value: -7 }, + { date: new Date( '2022-06-04' ), value: -4 }, + { date: new Date( '2022-06-11' ), value: -1 }, + { date: new Date( '2022-06-18' ), value: 1 }, + { date: new Date( '2022-06-25' ), value: 3 }, + { date: new Date( '2022-07-02' ), value: 6 }, + { date: new Date( '2022-07-09' ), value: 9 }, + { date: new Date( '2022-07-16' ), value: 11 }, + { date: new Date( '2022-07-23' ), value: 13 }, + { date: new Date( '2022-07-30' ), value: 16 }, + { date: new Date( '2022-08-06' ), value: 18 }, + { date: new Date( '2022-08-13' ), value: 21 }, + { date: new Date( '2022-08-20' ), value: 23 }, + { date: new Date( '2022-08-27' ), value: 21 }, + { date: new Date( '2022-09-03' ), value: 19 }, + { date: new Date( '2022-09-10' ), value: 16 }, + { date: new Date( '2022-09-17' ), value: 13 }, + { date: new Date( '2022-09-24' ), value: 9 }, + { date: new Date( '2022-10-01' ), value: 6 }, + { date: new Date( '2022-10-08' ), value: 3 }, + { date: new Date( '2022-10-15' ), value: -1 }, + { date: new Date( '2022-10-22' ), value: -4 }, + { date: new Date( '2022-10-29' ), value: -7 }, + { date: new Date( '2022-11-05' ), value: -11 }, + { date: new Date( '2022-11-12' ), value: -14 }, + { date: new Date( '2022-11-19' ), value: -17 }, + { date: new Date( '2022-11-26' ), value: -21 }, + { date: new Date( '2022-12-03' ), value: -24 }, + { date: new Date( '2022-12-10' ), value: -27 }, + { date: new Date( '2022-12-17' ), value: -31 }, + { date: new Date( '2022-12-24' ), value: -36 }, + { date: new Date( '2022-12-31' ), value: -37 }, + // 2023 data { date: new Date( '2023-01-01' ), value: -63 }, { date: new Date( '2023-01-08' ), value: -64 }, { date: new Date( '2023-01-15' ), value: -65 }, From 76ed47a36421fba6d92552d1fa9686101a06501f Mon Sep 17 00:00:00 2001 From: Grzegorz Chudzinski-Pawlowski <112354940+grzegorz-cp@users.noreply.github.com> Date: Mon, 16 Dec 2024 09:06:35 +1100 Subject: [PATCH 13/13] Charts - updating tooltip prop and simplifying event handlers --- .../src/components/bar-chart/bar-chart.tsx | 43 +++++++++---------- .../bar-chart/stories/index.stories.tsx | 4 +- .../pie-semi-circle-chart.tsx | 8 +--- .../stories/index.stories.tsx | 2 +- 4 files changed, 25 insertions(+), 32 deletions(-) diff --git a/projects/js-packages/charts/src/components/bar-chart/bar-chart.tsx b/projects/js-packages/charts/src/components/bar-chart/bar-chart.tsx index 5b9c714260a41..984fddd5ac8f4 100644 --- a/projects/js-packages/charts/src/components/bar-chart/bar-chart.tsx +++ b/projects/js-packages/charts/src/components/bar-chart/bar-chart.tsx @@ -5,7 +5,7 @@ import { scaleBand, scaleLinear } from '@visx/scale'; import { Bar } from '@visx/shape'; import { useTooltip } from '@visx/tooltip'; import clsx from 'clsx'; -import { FC, useCallback } from 'react'; +import { FC, useCallback, type MouseEvent } from 'react'; import { useChartTheme } from '../../providers/theme'; import { BaseTooltip } from '../tooltip'; import styles from './bar-chart.module.scss'; @@ -23,7 +23,7 @@ const BarChart: FC< BarChartProps > = ( { width, height, margin = { top: 20, right: 20, bottom: 40, left: 40 }, - showTooltips = false, + withTooltips = false, } ) => { const theme = useChartTheme(); const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, hideTooltip, showTooltip } = @@ -45,7 +45,7 @@ const BarChart: FC< BarChartProps > = ( { } ); const handleMouseMove = useCallback( - ( event: React.MouseEvent, datum: DataPoint ) => { + ( event: MouseEvent< SVGRectElement >, datum: DataPoint ) => { const coords = localPoint( event ); if ( ! coords ) return; @@ -62,35 +62,32 @@ const BarChart: FC< BarChartProps > = ( { hideTooltip(); }, [ hideTooltip ] ); - const handleBarMouseMove = useCallback( - ( d: DataPoint ) => ( event: React.MouseEvent< SVGRectElement > ) => { - handleMouseMove( event, d ); - }, - [ handleMouseMove ] - ); - return (
- { data.map( d => ( - - ) ) } + { data.map( d => { + const handleBarMouseMove = event => handleMouseMove( event, d ); + + return ( + + ); + } ) } - { showTooltips && tooltipOpen && tooltipData && ( + { withTooltips && tooltipOpen && tooltipData && ( ; @@ -33,7 +29,7 @@ const PieSemiCircleChart: FC< PieSemiCircleChartProps > = ( { height, label, note, - showTooltips = false, + withTooltips = false, } ) => { const providerTheme = useChartTheme(); const { tooltipOpen, tooltipLeft, tooltipTop, tooltipData, hideTooltip, showTooltip } = @@ -135,7 +131,7 @@ const PieSemiCircleChart: FC< PieSemiCircleChartProps > = ( { - { showTooltips && tooltipOpen && tooltipData && ( + { withTooltips && tooltipOpen && tooltipData && (