From 010b227b9d1a27356ab697ad0e23de7d2a157094 Mon Sep 17 00:00:00 2001 From: "Atishay Jain (atisjai)" <98592573+AtishayMsft@users.noreply.github.com> Date: Mon, 6 May 2024 10:37:13 +0530 Subject: [PATCH 001/113] Draft implementation of charts in v9 --- .github/CODEOWNERS | 1 + .../react-charts-preview/.babelrc.json | 4 + .../react-charts-preview/.eslintrc.json | 4 + .../react-charts-preview/.storybook/main.js | 14 + .../.storybook/preview.js | 7 + .../.storybook/tsconfig.json | 10 + .../react-charts-preview/.swcrc | 30 + .../react-charts-preview/LICENSE | 15 + .../react-charts-preview/README.md | 5 + .../config/api-extractor.json | 4 + .../react-charts-preview/config/tests.js | 1 + .../react-charts-preview/docs/Spec.md | 63 + .../etc/react-charts-preview.api.md | 0 .../react-charts-preview/jest.config.js | 21 + .../react-charts-preview/just.config.ts | 5 + .../react-charts-preview/package.json | 88 + .../react-charts-preview/project.json | 8 + .../react-charts-preview/src/AreaChart.ts | 1 + .../src/CartesianChart.ts | 1 + .../react-charts-preview/src/DonutChart.ts | 1 + .../react-charts-preview/src/GaugeChart.ts | 1 + .../src/GroupedVerticalBarChart.ts | 1 + .../react-charts-preview/src/HeatMapChart.ts | 1 + .../src/HorizontalBarChart.ts | 1 + .../src/HorizontalBarChartWithAxis.ts | 1 + .../react-charts-preview/src/Legends.ts | 1 + .../react-charts-preview/src/LineChart.ts | 1 + .../react-charts-preview/src/PieChart.ts | 1 + .../react-charts-preview/src/SankeyChart.ts | 1 + .../react-charts-preview/src/Sparkline.ts | 1 + .../src/StackedBarChart.ts | 1 + .../react-charts-preview/src/Styling.ts | 99 + .../react-charts-preview/src/TreeChart.ts | 1 + .../react-charts-preview/src/Utilities.ts | 238 + .../src/VerticalBarChart.ts | 1 + .../src/VerticalStackedBarChart.ts | 1 + .../components/AreaChart/AreaChart.base.tsx | 924 + .../components/AreaChart/AreaChart.styles.ts | 18 + .../components/AreaChart/AreaChart.test.tsx | 436 + .../src/components/AreaChart/AreaChart.tsx | 16 + .../components/AreaChart/AreaChart.types.ts | 85 + .../AreaChart/AreaChartRTL.test.tsx | 578 + .../__snapshots__/AreaChart.test.tsx.snap | 4983 ++++++ .../__snapshots__/AreaChartRTL.test.tsx.snap | 10757 +++++++++++ .../src/components/AreaChart/index.ts | 2 + .../CommonComponents/CartesianChart.base.tsx | 916 + .../CommonComponents/CartesianChart.styles.ts | 194 + .../CommonComponents/CartesianChart.tsx | 15 + .../CommonComponents/CartesianChart.types.ts | 617 + .../src/components/CommonComponents/index.ts | 2 + .../components/DonutChart/Arc/Arc.styles.ts | 29 + .../src/components/DonutChart/Arc/Arc.tsx | 134 + .../components/DonutChart/Arc/Arc.types.ts | 146 + .../src/components/DonutChart/Arc/index.ts | 2 + .../components/DonutChart/DonutChart.base.tsx | 369 + .../DonutChart/DonutChart.styles.ts | 31 + .../components/DonutChart/DonutChart.test.tsx | 266 + .../src/components/DonutChart/DonutChart.tsx | 16 + .../components/DonutChart/DonutChart.types.ts | 86 + .../DonutChart/DonutChartRTL.test.tsx | 199 + .../components/DonutChart/Pie/Pie.styles.ts | 19 + .../src/components/DonutChart/Pie/Pie.tsx | 110 + .../components/DonutChart/Pie/Pie.types.ts | 107 + .../src/components/DonutChart/Pie/index.ts | 2 + .../__snapshots__/DonutChart.test.tsx.snap | 5680 ++++++ .../__snapshots__/DonutChartRTL.test.tsx.snap | 1133 ++ .../src/components/DonutChart/index.ts | 3 + .../components/GaugeChart/GaugeChart.base.tsx | 716 + .../GaugeChart/GaugeChart.styles.ts | 142 + .../components/GaugeChart/GaugeChart.test.tsx | 447 + .../src/components/GaugeChart/GaugeChart.tsx | 15 + .../components/GaugeChart/GaugeChart.types.ts | 291 + .../__snapshots__/GaugeChart.test.tsx.snap | 5975 +++++++ .../src/components/GaugeChart/index.ts | 2 + .../GroupedVerticalBarChart.base.tsx | 652 + .../GroupedVerticalBarChart.styles.ts | 30 + .../GroupedVerticalBarChart.test.tsx | 365 + .../GroupedVerticalBarChart.tsx | 20 + .../GroupedVerticalBarChart.types.tsx | 134 + .../GroupedVerticalBarChartRTL.test.tsx | 526 + .../GroupedVerticalBarChart.test.tsx.snap | 5601 ++++++ .../GroupedVerticalBarChartRTL.test.tsx.snap | 10024 +++++++++++ .../GroupedVerticalBarChart/index.ts | 2 + .../HeatMapChart/HeatMapChart.base.tsx | 729 + .../HeatMapChart/HeatMapChart.styles.ts | 25 + .../HeatMapChart/HeatMapChart.test.tsx | 391 + .../components/HeatMapChart/HeatMapChart.ts | 15 + .../HeatMapChart/HeatMapChart.types.ts | 130 + .../HeatMapChart/HeatMapChartRTL.test.tsx | 401 + .../__snapshots__/HeatMapChart.test.tsx.snap | 3037 ++++ .../HeatMapChartRTL.test.tsx.snap | 1749 ++ .../src/components/HeatMapChart/index.ts | 2 + .../HorizontalBarChart.base.tsx | 434 + .../HorizontalBarChart.styles.ts | 78 + .../HorizontalBarChart.test.tsx | 205 + .../HorizontalBarChart/HorizontalBarChart.tsx | 20 + .../HorizontalBarChart.types.ts | 225 + .../HorizontalBarChartRTL.test.tsx | 380 + .../HorizontalBarChart.test.tsx.snap | 1530 ++ .../HorizontalBarChartRTL.test.tsx.snap | 2127 +++ .../components/HorizontalBarChart/index.ts | 3 + .../HorizontalBarChartWithAxis.base.tsx | 688 + .../HorizontalBarChartWithAxis.styles.ts | 25 + .../HorizontalBarChartWithAxis.test.tsx | 195 + .../HorizontalBarChartWithAxis.tsx | 20 + .../HorizontalBarChartWithAxis.types.ts | 137 + .../HorizontalBarChartWithAxisRTL.test.tsx | 454 + .../HorizontalBarChartWithAxis.test.tsx.snap | 1974 +++ ...orizontalBarChartWithAxisRTL.test.tsx.snap | 5877 ++++++ .../HorizontalBarChartWithAxis/index.ts | 2 + .../src/components/Legends/Legends.base.tsx | 436 + .../src/components/Legends/Legends.styles.ts | 118 + .../src/components/Legends/Legends.test.tsx | 236 + .../src/components/Legends/Legends.tsx | 14 + .../src/components/Legends/Legends.types.ts | 254 + .../__snapshots__/Legends.test.tsx.snap | 7333 ++++++++ .../src/components/Legends/index.ts | 3 + .../src/components/Legends/shape.tsx | 44 + .../components/LineChart/LineChart.base.tsx | 1432 ++ .../components/LineChart/LineChart.styles.ts | 18 + .../components/LineChart/LineChart.test.tsx | 370 + .../src/components/LineChart/LineChart.tsx | 16 + .../components/LineChart/LineChart.types.ts | 133 + .../LineChart/LineChartRTL.test.tsx | 703 + .../__snapshots__/LineChart.test.tsx.snap | 4138 +++++ .../__snapshots__/LineChartRTL.test.tsx.snap | 8174 +++++++++ .../eventAnnotation/EventAnnotation.tsx | 134 + .../LineChart/eventAnnotation/LabelLink.tsx | 89 + .../LineChart/eventAnnotation/Textbox.tsx | 52 + .../src/components/LineChart/index.ts | 3 + .../src/components/PieChart/Arc/Arc.styles.ts | 11 + .../src/components/PieChart/Arc/Arc.tsx | 119 + .../src/components/PieChart/Arc/Arc.types.ts | 84 + .../src/components/PieChart/Arc/index.ts | 2 + .../src/components/PieChart/Pie/Pie.styles.ts | 7 + .../src/components/PieChart/Pie/Pie.tsx | 65 + .../src/components/PieChart/Pie/Pie.types.ts | 52 + .../src/components/PieChart/Pie/index.ts | 2 + .../src/components/PieChart/PieChart.base.tsx | 73 + .../components/PieChart/PieChart.styles.ts | 34 + .../src/components/PieChart/PieChart.test.tsx | 115 + .../src/components/PieChart/PieChart.tsx | 16 + .../src/components/PieChart/PieChart.types.ts | 89 + .../components/PieChart/PieChartRTL.test.tsx | 54 + .../__snapshots__/PieChart.test.tsx.snap | 343 + .../__snapshots__/PieChartRTL.test.tsx.snap | 511 + .../src/components/PieChart/index.ts | 2 + .../SankeyChart/SankeyChart.base.tsx | 1289 ++ .../SankeyChart/SankeyChart.styles.ts | 69 + .../SankeyChart/SankeyChart.test.tsx | 451 + .../components/SankeyChart/SankeyChart.tsx | 16 + .../SankeyChart/SankeyChart.types.ts | 162 + .../SankeyChart/SankeyChartRTL.test.tsx | 172 + .../__snapshots__/SankeyChart.test.tsx.snap | 9659 ++++++++++ .../SankeyChartRTL.test.tsx.snap | 1182 ++ .../src/components/SankeyChart/index.ts | 2 + .../components/Sparkline/Sparkline.base.tsx | 169 + .../components/Sparkline/Sparkline.styles.ts | 13 + .../components/Sparkline/Sparkline.test.tsx | 138 + .../src/components/Sparkline/Sparkline.tsx | 16 + .../components/Sparkline/Sparkline.types.ts | 69 + .../Sparkline/SparklineRTL.test.tsx | 45 + .../__snapshots__/Sparkline.test.tsx.snap | 128 + .../__snapshots__/SparklineRTL.test.tsx.snap | 172 + .../src/components/Sparkline/index.ts | 2 + .../MultiStackedBarChart.base.tsx | 651 + .../MultiStackedBarChart.styles.ts | 93 + .../MultiStackedBarChart.test.tsx | 289 + .../StackedBarChart/MultiStackedBarChart.tsx | 20 + .../MultiStackedBarChart.types.ts | 257 + .../MultiStackedBarChartRTL.test.tsx | 411 + .../StackedBarChart/StackedBarChart.base.tsx | 525 + .../StackedBarChart/StackedBarChart.styles.ts | 118 + .../StackedBarChart/StackedBarChart.test.tsx | 243 + .../StackedBarChart/StackedBarChart.tsx | 16 + .../StackedBarChart/StackedBarChart.types.ts | 269 + .../StackedBarChartRTL.test.tsx | 421 + .../MultiStackedBarChart.test.tsx.snap | 4772 +++++ .../MultiStackedBarChartRTL.test.tsx.snap | 4835 +++++ .../StackedBarChart.test.tsx.snap | 2344 +++ .../StackedBarChartRTL.test.tsx.snap | 3272 ++++ .../src/components/StackedBarChart/index.ts | 5 + .../components/TreeChart/TreeChart.base.tsx | 569 + .../src/components/TreeChart/TreeChart.md | 188 + .../components/TreeChart/TreeChart.styles.ts | 41 + .../components/TreeChart/TreeChart.test.tsx | 217 + .../src/components/TreeChart/TreeChart.tsx | 14 + .../components/TreeChart/TreeChart.types.ts | 225 + .../__snapshots__/TreeChart.test.tsx.snap | 3245 ++++ .../src/components/TreeChart/index.ts | 2 + .../VerticalBarChart.base.tsx | 1050 ++ .../VerticalBarChart.styles.ts | 43 + .../VerticalBarChart.test.tsx | 269 + .../VerticalBarChart/VerticalBarChart.tsx | 16 + .../VerticalBarChart.types.ts | 177 + .../VerticalBarChartRTL.test.tsx | 820 + .../VerticalBarChart.test.tsx.snap | 3574 ++++ .../VerticalBarChartRTL.test.tsx.snap | 14729 ++++++++++++++++ .../src/components/VerticalBarChart/index.ts | 2 + .../components/VerticalBarChart/react-dom.jsx | 15 + .../VerticalStackedBarChart.base.tsx | 1113 ++ .../VerticalStackedBarChart.styles.ts | 31 + .../VerticalStackedBarChart.test.tsx | 280 + .../VerticalStackedBarChart.tsx | 20 + .../VerticalStackedBarChart.types.ts | 210 + .../VerticalStackedBarChartRTL.test.tsx | 768 + .../VerticalStackedBarChart.test.tsx.snap | 3212 ++++ .../VerticalStackedBarChartRTL.test.tsx.snap | 12564 +++++++++++++ .../VerticalStackedBarChart/index.ts | 2 + .../react-charts-preview/src/index.ts | 22 + .../src/types/IDataPoint.ts | 786 + .../src/types/IEventAnnotation.ts | 7 + .../src/types/ILegendDataItem.ts | 11 + .../react-charts-preview/src/types/index.ts | 2 + .../ChartHoverCard/ChartHoverCard.base.tsx | 42 + .../ChartHoverCard/ChartHoverCard.styles.ts | 98 + .../ChartHoverCard/ChartHoverCard.tsx | 12 + .../ChartHoverCard/ChartHoverCard.types.ts | 119 + .../ChartHoverCard/ChartHoverCardRTL.test.tsx | 106 + .../src/utilities/ChartHoverCard/index.ts | 3 + .../src/utilities/FocusableTooltipText.tsx | 80 + .../src/utilities/SVGTooltipText.tsx | 305 + .../src/utilities/TestUtility.test.tsx | 102 + .../src/utilities/UtilityUnitTests.test.ts | 1355 ++ .../UtilityUnitTests.test.ts.snap | 4409 +++++ .../src/utilities/colors.ts | 143 + .../src/utilities/index.ts | 4 + .../src/utilities/locale-util.ts | 16 + .../src/utilities/test-data.ts | 232 + .../src/utilities/utilities.ts | 1520 ++ .../src/utilities/vbc-utils.ts | 33 + .../react-charts-preview/src/version.ts | 4 + .../react-charts-preview/stories/.gitkeep | 0 .../react-charts-preview/tsconfig.json | 25 + .../react-charts-preview/tsconfig.lib.json | 22 + .../react-charts-preview/tsconfig.spec.json | 17 + tsconfig.base.all.json | 3 +- tsconfig.base.json | 1 + yarn.lock | 125 +- 239 files changed, 184850 insertions(+), 28 deletions(-) create mode 100644 packages/react-components/react-charts-preview/.babelrc.json create mode 100644 packages/react-components/react-charts-preview/.eslintrc.json create mode 100644 packages/react-components/react-charts-preview/.storybook/main.js create mode 100644 packages/react-components/react-charts-preview/.storybook/preview.js create mode 100644 packages/react-components/react-charts-preview/.storybook/tsconfig.json create mode 100644 packages/react-components/react-charts-preview/.swcrc create mode 100644 packages/react-components/react-charts-preview/LICENSE create mode 100644 packages/react-components/react-charts-preview/README.md create mode 100644 packages/react-components/react-charts-preview/config/api-extractor.json create mode 100644 packages/react-components/react-charts-preview/config/tests.js create mode 100644 packages/react-components/react-charts-preview/docs/Spec.md create mode 100644 packages/react-components/react-charts-preview/etc/react-charts-preview.api.md create mode 100644 packages/react-components/react-charts-preview/jest.config.js create mode 100644 packages/react-components/react-charts-preview/just.config.ts create mode 100644 packages/react-components/react-charts-preview/package.json create mode 100644 packages/react-components/react-charts-preview/project.json create mode 100644 packages/react-components/react-charts-preview/src/AreaChart.ts create mode 100644 packages/react-components/react-charts-preview/src/CartesianChart.ts create mode 100644 packages/react-components/react-charts-preview/src/DonutChart.ts create mode 100644 packages/react-components/react-charts-preview/src/GaugeChart.ts create mode 100644 packages/react-components/react-charts-preview/src/GroupedVerticalBarChart.ts create mode 100644 packages/react-components/react-charts-preview/src/HeatMapChart.ts create mode 100644 packages/react-components/react-charts-preview/src/HorizontalBarChart.ts create mode 100644 packages/react-components/react-charts-preview/src/HorizontalBarChartWithAxis.ts create mode 100644 packages/react-components/react-charts-preview/src/Legends.ts create mode 100644 packages/react-components/react-charts-preview/src/LineChart.ts create mode 100644 packages/react-components/react-charts-preview/src/PieChart.ts create mode 100644 packages/react-components/react-charts-preview/src/SankeyChart.ts create mode 100644 packages/react-components/react-charts-preview/src/Sparkline.ts create mode 100644 packages/react-components/react-charts-preview/src/StackedBarChart.ts create mode 100644 packages/react-components/react-charts-preview/src/Styling.ts create mode 100644 packages/react-components/react-charts-preview/src/TreeChart.ts create mode 100644 packages/react-components/react-charts-preview/src/Utilities.ts create mode 100644 packages/react-components/react-charts-preview/src/VerticalBarChart.ts create mode 100644 packages/react-components/react-charts-preview/src/VerticalStackedBarChart.ts create mode 100644 packages/react-components/react-charts-preview/src/components/AreaChart/AreaChart.base.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/AreaChart/AreaChart.styles.ts create mode 100644 packages/react-components/react-charts-preview/src/components/AreaChart/AreaChart.test.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/AreaChart/AreaChart.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/AreaChart/AreaChart.types.ts create mode 100644 packages/react-components/react-charts-preview/src/components/AreaChart/AreaChartRTL.test.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/AreaChart/__snapshots__/AreaChart.test.tsx.snap create mode 100644 packages/react-components/react-charts-preview/src/components/AreaChart/__snapshots__/AreaChartRTL.test.tsx.snap create mode 100644 packages/react-components/react-charts-preview/src/components/AreaChart/index.ts create mode 100644 packages/react-components/react-charts-preview/src/components/CommonComponents/CartesianChart.base.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/CommonComponents/CartesianChart.styles.ts create mode 100644 packages/react-components/react-charts-preview/src/components/CommonComponents/CartesianChart.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/CommonComponents/CartesianChart.types.ts create mode 100644 packages/react-components/react-charts-preview/src/components/CommonComponents/index.ts create mode 100644 packages/react-components/react-charts-preview/src/components/DonutChart/Arc/Arc.styles.ts create mode 100644 packages/react-components/react-charts-preview/src/components/DonutChart/Arc/Arc.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/DonutChart/Arc/Arc.types.ts create mode 100644 packages/react-components/react-charts-preview/src/components/DonutChart/Arc/index.ts create mode 100644 packages/react-components/react-charts-preview/src/components/DonutChart/DonutChart.base.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/DonutChart/DonutChart.styles.ts create mode 100644 packages/react-components/react-charts-preview/src/components/DonutChart/DonutChart.test.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/DonutChart/DonutChart.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/DonutChart/DonutChart.types.ts create mode 100644 packages/react-components/react-charts-preview/src/components/DonutChart/DonutChartRTL.test.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/DonutChart/Pie/Pie.styles.ts create mode 100644 packages/react-components/react-charts-preview/src/components/DonutChart/Pie/Pie.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/DonutChart/Pie/Pie.types.ts create mode 100644 packages/react-components/react-charts-preview/src/components/DonutChart/Pie/index.ts create mode 100644 packages/react-components/react-charts-preview/src/components/DonutChart/__snapshots__/DonutChart.test.tsx.snap create mode 100644 packages/react-components/react-charts-preview/src/components/DonutChart/__snapshots__/DonutChartRTL.test.tsx.snap create mode 100644 packages/react-components/react-charts-preview/src/components/DonutChart/index.ts create mode 100644 packages/react-components/react-charts-preview/src/components/GaugeChart/GaugeChart.base.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/GaugeChart/GaugeChart.styles.ts create mode 100644 packages/react-components/react-charts-preview/src/components/GaugeChart/GaugeChart.test.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/GaugeChart/GaugeChart.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/GaugeChart/GaugeChart.types.ts create mode 100644 packages/react-components/react-charts-preview/src/components/GaugeChart/__snapshots__/GaugeChart.test.tsx.snap create mode 100644 packages/react-components/react-charts-preview/src/components/GaugeChart/index.ts create mode 100644 packages/react-components/react-charts-preview/src/components/GroupedVerticalBarChart/GroupedVerticalBarChart.base.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/GroupedVerticalBarChart/GroupedVerticalBarChart.styles.ts create mode 100644 packages/react-components/react-charts-preview/src/components/GroupedVerticalBarChart/GroupedVerticalBarChart.test.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/GroupedVerticalBarChart/GroupedVerticalBarChart.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/GroupedVerticalBarChart/GroupedVerticalBarChart.types.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/GroupedVerticalBarChart/GroupedVerticalBarChartRTL.test.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/GroupedVerticalBarChart/__snapshots__/GroupedVerticalBarChart.test.tsx.snap create mode 100644 packages/react-components/react-charts-preview/src/components/GroupedVerticalBarChart/__snapshots__/GroupedVerticalBarChartRTL.test.tsx.snap create mode 100644 packages/react-components/react-charts-preview/src/components/GroupedVerticalBarChart/index.ts create mode 100644 packages/react-components/react-charts-preview/src/components/HeatMapChart/HeatMapChart.base.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/HeatMapChart/HeatMapChart.styles.ts create mode 100644 packages/react-components/react-charts-preview/src/components/HeatMapChart/HeatMapChart.test.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/HeatMapChart/HeatMapChart.ts create mode 100644 packages/react-components/react-charts-preview/src/components/HeatMapChart/HeatMapChart.types.ts create mode 100644 packages/react-components/react-charts-preview/src/components/HeatMapChart/HeatMapChartRTL.test.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/HeatMapChart/__snapshots__/HeatMapChart.test.tsx.snap create mode 100644 packages/react-components/react-charts-preview/src/components/HeatMapChart/__snapshots__/HeatMapChartRTL.test.tsx.snap create mode 100644 packages/react-components/react-charts-preview/src/components/HeatMapChart/index.ts create mode 100644 packages/react-components/react-charts-preview/src/components/HorizontalBarChart/HorizontalBarChart.base.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/HorizontalBarChart/HorizontalBarChart.styles.ts create mode 100644 packages/react-components/react-charts-preview/src/components/HorizontalBarChart/HorizontalBarChart.test.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/HorizontalBarChart/HorizontalBarChart.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/HorizontalBarChart/HorizontalBarChart.types.ts create mode 100644 packages/react-components/react-charts-preview/src/components/HorizontalBarChart/HorizontalBarChartRTL.test.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/HorizontalBarChart/__snapshots__/HorizontalBarChart.test.tsx.snap create mode 100644 packages/react-components/react-charts-preview/src/components/HorizontalBarChart/__snapshots__/HorizontalBarChartRTL.test.tsx.snap create mode 100644 packages/react-components/react-charts-preview/src/components/HorizontalBarChart/index.ts create mode 100644 packages/react-components/react-charts-preview/src/components/HorizontalBarChartWithAxis/HorizontalBarChartWithAxis.base.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/HorizontalBarChartWithAxis/HorizontalBarChartWithAxis.styles.ts create mode 100644 packages/react-components/react-charts-preview/src/components/HorizontalBarChartWithAxis/HorizontalBarChartWithAxis.test.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/HorizontalBarChartWithAxis/HorizontalBarChartWithAxis.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/HorizontalBarChartWithAxis/HorizontalBarChartWithAxis.types.ts create mode 100644 packages/react-components/react-charts-preview/src/components/HorizontalBarChartWithAxis/HorizontalBarChartWithAxisRTL.test.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/HorizontalBarChartWithAxis/__snapshots__/HorizontalBarChartWithAxis.test.tsx.snap create mode 100644 packages/react-components/react-charts-preview/src/components/HorizontalBarChartWithAxis/__snapshots__/HorizontalBarChartWithAxisRTL.test.tsx.snap create mode 100644 packages/react-components/react-charts-preview/src/components/HorizontalBarChartWithAxis/index.ts create mode 100644 packages/react-components/react-charts-preview/src/components/Legends/Legends.base.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/Legends/Legends.styles.ts create mode 100644 packages/react-components/react-charts-preview/src/components/Legends/Legends.test.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/Legends/Legends.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/Legends/Legends.types.ts create mode 100644 packages/react-components/react-charts-preview/src/components/Legends/__snapshots__/Legends.test.tsx.snap create mode 100644 packages/react-components/react-charts-preview/src/components/Legends/index.ts create mode 100644 packages/react-components/react-charts-preview/src/components/Legends/shape.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/LineChart/LineChart.base.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/LineChart/LineChart.styles.ts create mode 100644 packages/react-components/react-charts-preview/src/components/LineChart/LineChart.test.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/LineChart/LineChart.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/LineChart/LineChart.types.ts create mode 100644 packages/react-components/react-charts-preview/src/components/LineChart/LineChartRTL.test.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/LineChart/__snapshots__/LineChart.test.tsx.snap create mode 100644 packages/react-components/react-charts-preview/src/components/LineChart/__snapshots__/LineChartRTL.test.tsx.snap create mode 100644 packages/react-components/react-charts-preview/src/components/LineChart/eventAnnotation/EventAnnotation.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/LineChart/eventAnnotation/LabelLink.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/LineChart/eventAnnotation/Textbox.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/LineChart/index.ts create mode 100644 packages/react-components/react-charts-preview/src/components/PieChart/Arc/Arc.styles.ts create mode 100644 packages/react-components/react-charts-preview/src/components/PieChart/Arc/Arc.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/PieChart/Arc/Arc.types.ts create mode 100644 packages/react-components/react-charts-preview/src/components/PieChart/Arc/index.ts create mode 100644 packages/react-components/react-charts-preview/src/components/PieChart/Pie/Pie.styles.ts create mode 100644 packages/react-components/react-charts-preview/src/components/PieChart/Pie/Pie.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/PieChart/Pie/Pie.types.ts create mode 100644 packages/react-components/react-charts-preview/src/components/PieChart/Pie/index.ts create mode 100644 packages/react-components/react-charts-preview/src/components/PieChart/PieChart.base.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/PieChart/PieChart.styles.ts create mode 100644 packages/react-components/react-charts-preview/src/components/PieChart/PieChart.test.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/PieChart/PieChart.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/PieChart/PieChart.types.ts create mode 100644 packages/react-components/react-charts-preview/src/components/PieChart/PieChartRTL.test.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/PieChart/__snapshots__/PieChart.test.tsx.snap create mode 100644 packages/react-components/react-charts-preview/src/components/PieChart/__snapshots__/PieChartRTL.test.tsx.snap create mode 100644 packages/react-components/react-charts-preview/src/components/PieChart/index.ts create mode 100644 packages/react-components/react-charts-preview/src/components/SankeyChart/SankeyChart.base.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/SankeyChart/SankeyChart.styles.ts create mode 100644 packages/react-components/react-charts-preview/src/components/SankeyChart/SankeyChart.test.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/SankeyChart/SankeyChart.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/SankeyChart/SankeyChart.types.ts create mode 100644 packages/react-components/react-charts-preview/src/components/SankeyChart/SankeyChartRTL.test.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/SankeyChart/__snapshots__/SankeyChart.test.tsx.snap create mode 100644 packages/react-components/react-charts-preview/src/components/SankeyChart/__snapshots__/SankeyChartRTL.test.tsx.snap create mode 100644 packages/react-components/react-charts-preview/src/components/SankeyChart/index.ts create mode 100644 packages/react-components/react-charts-preview/src/components/Sparkline/Sparkline.base.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/Sparkline/Sparkline.styles.ts create mode 100644 packages/react-components/react-charts-preview/src/components/Sparkline/Sparkline.test.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/Sparkline/Sparkline.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/Sparkline/Sparkline.types.ts create mode 100644 packages/react-components/react-charts-preview/src/components/Sparkline/SparklineRTL.test.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/Sparkline/__snapshots__/Sparkline.test.tsx.snap create mode 100644 packages/react-components/react-charts-preview/src/components/Sparkline/__snapshots__/SparklineRTL.test.tsx.snap create mode 100644 packages/react-components/react-charts-preview/src/components/Sparkline/index.ts create mode 100644 packages/react-components/react-charts-preview/src/components/StackedBarChart/MultiStackedBarChart.base.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/StackedBarChart/MultiStackedBarChart.styles.ts create mode 100644 packages/react-components/react-charts-preview/src/components/StackedBarChart/MultiStackedBarChart.test.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/StackedBarChart/MultiStackedBarChart.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/StackedBarChart/MultiStackedBarChart.types.ts create mode 100644 packages/react-components/react-charts-preview/src/components/StackedBarChart/MultiStackedBarChartRTL.test.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/StackedBarChart/StackedBarChart.base.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/StackedBarChart/StackedBarChart.styles.ts create mode 100644 packages/react-components/react-charts-preview/src/components/StackedBarChart/StackedBarChart.test.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/StackedBarChart/StackedBarChart.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/StackedBarChart/StackedBarChart.types.ts create mode 100644 packages/react-components/react-charts-preview/src/components/StackedBarChart/StackedBarChartRTL.test.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/StackedBarChart/__snapshots__/MultiStackedBarChart.test.tsx.snap create mode 100644 packages/react-components/react-charts-preview/src/components/StackedBarChart/__snapshots__/MultiStackedBarChartRTL.test.tsx.snap create mode 100644 packages/react-components/react-charts-preview/src/components/StackedBarChart/__snapshots__/StackedBarChart.test.tsx.snap create mode 100644 packages/react-components/react-charts-preview/src/components/StackedBarChart/__snapshots__/StackedBarChartRTL.test.tsx.snap create mode 100644 packages/react-components/react-charts-preview/src/components/StackedBarChart/index.ts create mode 100644 packages/react-components/react-charts-preview/src/components/TreeChart/TreeChart.base.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/TreeChart/TreeChart.md create mode 100644 packages/react-components/react-charts-preview/src/components/TreeChart/TreeChart.styles.ts create mode 100644 packages/react-components/react-charts-preview/src/components/TreeChart/TreeChart.test.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/TreeChart/TreeChart.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/TreeChart/TreeChart.types.ts create mode 100644 packages/react-components/react-charts-preview/src/components/TreeChart/__snapshots__/TreeChart.test.tsx.snap create mode 100644 packages/react-components/react-charts-preview/src/components/TreeChart/index.ts create mode 100644 packages/react-components/react-charts-preview/src/components/VerticalBarChart/VerticalBarChart.base.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/VerticalBarChart/VerticalBarChart.styles.ts create mode 100644 packages/react-components/react-charts-preview/src/components/VerticalBarChart/VerticalBarChart.test.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/VerticalBarChart/VerticalBarChart.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/VerticalBarChart/VerticalBarChart.types.ts create mode 100644 packages/react-components/react-charts-preview/src/components/VerticalBarChart/VerticalBarChartRTL.test.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/VerticalBarChart/__snapshots__/VerticalBarChart.test.tsx.snap create mode 100644 packages/react-components/react-charts-preview/src/components/VerticalBarChart/__snapshots__/VerticalBarChartRTL.test.tsx.snap create mode 100644 packages/react-components/react-charts-preview/src/components/VerticalBarChart/index.ts create mode 100644 packages/react-components/react-charts-preview/src/components/VerticalBarChart/react-dom.jsx create mode 100644 packages/react-components/react-charts-preview/src/components/VerticalStackedBarChart/VerticalStackedBarChart.base.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/VerticalStackedBarChart/VerticalStackedBarChart.styles.ts create mode 100644 packages/react-components/react-charts-preview/src/components/VerticalStackedBarChart/VerticalStackedBarChart.test.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/VerticalStackedBarChart/VerticalStackedBarChart.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/VerticalStackedBarChart/VerticalStackedBarChart.types.ts create mode 100644 packages/react-components/react-charts-preview/src/components/VerticalStackedBarChart/VerticalStackedBarChartRTL.test.tsx create mode 100644 packages/react-components/react-charts-preview/src/components/VerticalStackedBarChart/__snapshots__/VerticalStackedBarChart.test.tsx.snap create mode 100644 packages/react-components/react-charts-preview/src/components/VerticalStackedBarChart/__snapshots__/VerticalStackedBarChartRTL.test.tsx.snap create mode 100644 packages/react-components/react-charts-preview/src/components/VerticalStackedBarChart/index.ts create mode 100644 packages/react-components/react-charts-preview/src/index.ts create mode 100644 packages/react-components/react-charts-preview/src/types/IDataPoint.ts create mode 100644 packages/react-components/react-charts-preview/src/types/IEventAnnotation.ts create mode 100644 packages/react-components/react-charts-preview/src/types/ILegendDataItem.ts create mode 100644 packages/react-components/react-charts-preview/src/types/index.ts create mode 100644 packages/react-components/react-charts-preview/src/utilities/ChartHoverCard/ChartHoverCard.base.tsx create mode 100644 packages/react-components/react-charts-preview/src/utilities/ChartHoverCard/ChartHoverCard.styles.ts create mode 100644 packages/react-components/react-charts-preview/src/utilities/ChartHoverCard/ChartHoverCard.tsx create mode 100644 packages/react-components/react-charts-preview/src/utilities/ChartHoverCard/ChartHoverCard.types.ts create mode 100644 packages/react-components/react-charts-preview/src/utilities/ChartHoverCard/ChartHoverCardRTL.test.tsx create mode 100644 packages/react-components/react-charts-preview/src/utilities/ChartHoverCard/index.ts create mode 100644 packages/react-components/react-charts-preview/src/utilities/FocusableTooltipText.tsx create mode 100644 packages/react-components/react-charts-preview/src/utilities/SVGTooltipText.tsx create mode 100644 packages/react-components/react-charts-preview/src/utilities/TestUtility.test.tsx create mode 100644 packages/react-components/react-charts-preview/src/utilities/UtilityUnitTests.test.ts create mode 100644 packages/react-components/react-charts-preview/src/utilities/__snapshots__/UtilityUnitTests.test.ts.snap create mode 100644 packages/react-components/react-charts-preview/src/utilities/colors.ts create mode 100644 packages/react-components/react-charts-preview/src/utilities/index.ts create mode 100644 packages/react-components/react-charts-preview/src/utilities/locale-util.ts create mode 100644 packages/react-components/react-charts-preview/src/utilities/test-data.ts create mode 100644 packages/react-components/react-charts-preview/src/utilities/utilities.ts create mode 100644 packages/react-components/react-charts-preview/src/utilities/vbc-utils.ts create mode 100644 packages/react-components/react-charts-preview/src/version.ts create mode 100644 packages/react-components/react-charts-preview/stories/.gitkeep create mode 100644 packages/react-components/react-charts-preview/tsconfig.json create mode 100644 packages/react-components/react-charts-preview/tsconfig.lib.json create mode 100644 packages/react-components/react-charts-preview/tsconfig.spec.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 0613f3f0734b1..04ad439eeab34 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -252,6 +252,7 @@ packages/react-components/react-timepicker-compat @microsoft/teams-prg packages/react-components/react-icons-compat @microsoft/cxe-red @tomi-msft packages/react-components/react-tag-picker-preview @microsoft/teams-prg packages/react-components/react-carousel-preview @microsoft/xc-uxe @Mitch-At-Work +packages/react-components/react-charts-preview @microsoft/charting-team # <%= NX-CODEOWNER-PLACEHOLDER %> ## Components diff --git a/packages/react-components/react-charts-preview/.babelrc.json b/packages/react-components/react-charts-preview/.babelrc.json new file mode 100644 index 0000000000000..45fb71ca16d2c --- /dev/null +++ b/packages/react-components/react-charts-preview/.babelrc.json @@ -0,0 +1,4 @@ +{ + "extends": "../../../.babelrc-v9.json", + "plugins": ["annotate-pure-calls", "@babel/transform-react-pure-annotations"] +} diff --git a/packages/react-components/react-charts-preview/.eslintrc.json b/packages/react-components/react-charts-preview/.eslintrc.json new file mode 100644 index 0000000000000..ceea884c70dcc --- /dev/null +++ b/packages/react-components/react-charts-preview/.eslintrc.json @@ -0,0 +1,4 @@ +{ + "extends": ["plugin:@fluentui/eslint-plugin/react"], + "root": true +} diff --git a/packages/react-components/react-charts-preview/.storybook/main.js b/packages/react-components/react-charts-preview/.storybook/main.js new file mode 100644 index 0000000000000..26536b61b387f --- /dev/null +++ b/packages/react-components/react-charts-preview/.storybook/main.js @@ -0,0 +1,14 @@ +const rootMain = require('../../../../.storybook/main'); + +module.exports = /** @type {Omit} */ ({ + ...rootMain, + stories: [...rootMain.stories, '../stories/**/*.stories.mdx', '../stories/**/index.stories.@(ts|tsx)'], + addons: [...rootMain.addons], + webpackFinal: (config, options) => { + const localConfig = { ...rootMain.webpackFinal(config, options) }; + + // add your own webpack tweaks if needed + + return localConfig; + }, +}); diff --git a/packages/react-components/react-charts-preview/.storybook/preview.js b/packages/react-components/react-charts-preview/.storybook/preview.js new file mode 100644 index 0000000000000..1939500a3d18c --- /dev/null +++ b/packages/react-components/react-charts-preview/.storybook/preview.js @@ -0,0 +1,7 @@ +import * as rootPreview from '../../../../.storybook/preview'; + +/** @type {typeof rootPreview.decorators} */ +export const decorators = [...rootPreview.decorators]; + +/** @type {typeof rootPreview.parameters} */ +export const parameters = { ...rootPreview.parameters }; diff --git a/packages/react-components/react-charts-preview/.storybook/tsconfig.json b/packages/react-components/react-charts-preview/.storybook/tsconfig.json new file mode 100644 index 0000000000000..ea89218a3d916 --- /dev/null +++ b/packages/react-components/react-charts-preview/.storybook/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "", + "allowJs": true, + "checkJs": true, + "types": ["static-assets", "environment", "storybook__addons"] + }, + "include": ["../stories/**/*.stories.ts", "../stories/**/*.stories.tsx", "*.js"] +} diff --git a/packages/react-components/react-charts-preview/.swcrc b/packages/react-components/react-charts-preview/.swcrc new file mode 100644 index 0000000000000..b4ffa86dee306 --- /dev/null +++ b/packages/react-components/react-charts-preview/.swcrc @@ -0,0 +1,30 @@ +{ + "$schema": "https://json.schemastore.org/swcrc", + "exclude": [ + "/testing", + "/**/*.cy.ts", + "/**/*.cy.tsx", + "/**/*.spec.ts", + "/**/*.spec.tsx", + "/**/*.test.ts", + "/**/*.test.tsx" + ], + "jsc": { + "parser": { + "syntax": "typescript", + "tsx": true, + "decorators": false, + "dynamicImport": false + }, + "externalHelpers": true, + "transform": { + "react": { + "runtime": "classic", + "useSpread": true + } + }, + "target": "es2019" + }, + "minify": false, + "sourceMaps": true +} diff --git a/packages/react-components/react-charts-preview/LICENSE b/packages/react-components/react-charts-preview/LICENSE new file mode 100644 index 0000000000000..e21684b95d1c7 --- /dev/null +++ b/packages/react-components/react-charts-preview/LICENSE @@ -0,0 +1,15 @@ +@fluentui/react-charts-preview + +Copyright (c) Microsoft Corporation + +All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED _AS IS_, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Note: Usage of the fonts and icons referenced in Fluent UI React is subject to the terms listed at https://aka.ms/fluentui-assets-license diff --git a/packages/react-components/react-charts-preview/README.md b/packages/react-components/react-charts-preview/README.md new file mode 100644 index 0000000000000..6d6798421c55e --- /dev/null +++ b/packages/react-components/react-charts-preview/README.md @@ -0,0 +1,5 @@ +# @fluentui/react-charts-preview + +**React Charts components for [Fluent UI React](https://react.fluentui.dev/)** + +These are not production-ready components and **should never be used in product**. This space is useful for testing new components whose APIs might change before final release. diff --git a/packages/react-components/react-charts-preview/config/api-extractor.json b/packages/react-components/react-charts-preview/config/api-extractor.json new file mode 100644 index 0000000000000..e533bf30b48a2 --- /dev/null +++ b/packages/react-components/react-charts-preview/config/api-extractor.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "extends": "@fluentui/scripts-api-extractor/api-extractor.common.v-next.json" +} diff --git a/packages/react-components/react-charts-preview/config/tests.js b/packages/react-components/react-charts-preview/config/tests.js new file mode 100644 index 0000000000000..2e211ae9e2142 --- /dev/null +++ b/packages/react-components/react-charts-preview/config/tests.js @@ -0,0 +1 @@ +/** Jest test setup file. */ diff --git a/packages/react-components/react-charts-preview/docs/Spec.md b/packages/react-components/react-charts-preview/docs/Spec.md new file mode 100644 index 0000000000000..fd3ced6bd5457 --- /dev/null +++ b/packages/react-components/react-charts-preview/docs/Spec.md @@ -0,0 +1,63 @@ +# @fluentui/react-charts-preview Spec + +## Background + +_Description and use cases of this component_ + +## Prior Art + +_Include background research done for this component_ + +- _Link to Open UI research_ +- _Link to comparison of v7 and v0_ +- _Link to GitHub epic issue for the converged component_ + +## Sample Code + +_Provide some representative example code that uses the proposed API for the component_ + +## Variants + +_Describe visual or functional variants of this control, if applicable. For example, a slider could have a 2D variant._ + +## API + +_List the **Props** and **Slots** proposed for the component. Ideally this would just be a link to the component's `.types.ts` file_ + +## Structure + +- _**Public**_ +- _**Internal**_ +- _**DOM** - how the component will be rendered as HTML elements_ + +## Migration + +_Describe what will need to be done to upgrade from the existing implementations:_ + +- _Migration from v8_ +- _Migration from v0_ + +## Behaviors + +_Explain how the component will behave in use, including:_ + +- _Component States_ +- _Interaction_ + - _Keyboard_ + - _Cursor_ + - _Touch_ + - _Screen readers_ + +## Accessibility + +Base accessibility information is included in the design document. After the spec is filled and review, outcomes from it need to be communicated to design and incorporated in the design document. + +- Decide whether to use **native element** or folow **ARIA** and provide reasons +- Identify the **[ARIA](https://www.w3.org/TR/wai-aria-practices-1.2/) pattern** and, if the component is listed there, follow its specification as possible. +- Identify accessibility **variants**, the `role` ([ARIA roles](https://www.w3.org/TR/wai-aria-1.1/#role_definitions)) of the component, its `slots` and `aria-*` props. +- Describe the **keyboard navigation**: Tab Oder and Arrow Key Navigation. Describe any other keyboard **shortcuts** used +- Specify texts for **state change announcements** - [ARIA live regions + ](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions) (number of available items in dropdown, error messages, confirmations, ...) +- Identify UI parts that appear on **hover or focus** and specify keyboard and screen reader interaction with them +- List cases when **focus** needs to be **trapped** in sections of the UI (for dialogs and popups or for hierarchical navigation) +- List cases when **focus** needs to be **moved programatically** (if parts of the UI are appearing/disappearing or other cases) diff --git a/packages/react-components/react-charts-preview/etc/react-charts-preview.api.md b/packages/react-components/react-charts-preview/etc/react-charts-preview.api.md new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/packages/react-components/react-charts-preview/jest.config.js b/packages/react-components/react-charts-preview/jest.config.js new file mode 100644 index 0000000000000..f9c781d66563a --- /dev/null +++ b/packages/react-components/react-charts-preview/jest.config.js @@ -0,0 +1,21 @@ +// @ts-check + +/** + * @type {import('@jest/types').Config.InitialOptions} + */ +module.exports = { + displayName: 'react-charts-preview', + preset: '../../../jest.preset.js', + transform: { + '^.+\\.tsx?$': [ + 'ts-jest', + { + tsconfig: '/tsconfig.spec.json', + isolatedModules: true, + }, + ], + }, + coverageDirectory: './coverage', + setupFilesAfterEnv: ['./config/tests.js'], + snapshotSerializers: ['@griffel/jest-serializer'], +}; diff --git a/packages/react-components/react-charts-preview/just.config.ts b/packages/react-components/react-charts-preview/just.config.ts new file mode 100644 index 0000000000000..b7b2c9a33bf43 --- /dev/null +++ b/packages/react-components/react-charts-preview/just.config.ts @@ -0,0 +1,5 @@ +import { preset, task } from '@fluentui/scripts-tasks'; + +preset(); + +task('build', 'build:react-components').cached?.(); diff --git a/packages/react-components/react-charts-preview/package.json b/packages/react-components/react-charts-preview/package.json new file mode 100644 index 0000000000000..68e708066f9f3 --- /dev/null +++ b/packages/react-components/react-charts-preview/package.json @@ -0,0 +1,88 @@ +{ + "name": "@fluentui/react-charts-preview", + "version": "0.0.0", + "private": true, + "description": "New fluentui react package", + "main": "lib-commonjs/index.js", + "module": "lib/index.js", + "typings": "./dist/index.d.ts", + "sideEffects": false, + "files": [ + "*.md", + "dist/*.d.ts", + "lib", + "lib-commonjs" + ], + "repository": { + "type": "git", + "url": "https://github.com/microsoft/fluentui" + }, + "license": "MIT", + "scripts": { + "build": "just-scripts build", + "clean": "just-scripts clean", + "generate-api": "just-scripts generate-api", + "lint": "just-scripts lint", + "start": "yarn storybook", + "storybook": "start-storybook", + "test": "jest --passWithNoTests", + "test-ssr": "test-ssr \"./stories/**/*.stories.tsx\"", + "type-check": "tsc -b tsconfig.json" + }, + "devDependencies": { + "@fluentui/eslint-plugin": "*", + "@fluentui/react-conformance": "*", + "@fluentui/react-conformance-griffel": "*", + "@fluentui/scripts-api-extractor": "*", + "@fluentui/scripts-tasks": "*" + }, + "dependencies": { + "@fluentui/react-jsx-runtime": "^9.0.36", + "@fluentui/react-shared-contexts": "^9.17.0", + "@fluentui/react-theme": "^9.1.19", + "@fluentui/react-utilities": "^9.18.7", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1", + "@types/d3-array": "3", + "@types/d3-axis": "3", + "@types/d3-format": "3", + "@types/d3-hierarchy": "3", + "@types/d3-sankey": "^0.12.3", + "@types/d3-scale": "4", + "@types/d3-selection": "3", + "@types/d3-shape": "3", + "@types/d3-time-format": "3", + "@types/d3-time": "3", + "d3-array": "3", + "d3-axis": "3", + "d3-format": "3", + "d3-hierarchy": "3", + "d3-sankey": "^0.12.3", + "d3-scale": "4", + "d3-selection": "3", + "d3-shape": "3", + "d3-time-format": "3", + "d3-time": "3" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.9.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + }, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "node": "./lib-commonjs/index.js", + "import": "./lib/index.js", + "require": "./lib-commonjs/index.js" + }, + "./package.json": "./package.json" + }, + "beachball": { + "disallowedChangeTypes": [ + "major", + "prerelease" + ] + } +} diff --git a/packages/react-components/react-charts-preview/project.json b/packages/react-components/react-charts-preview/project.json new file mode 100644 index 0000000000000..30a78dff15808 --- /dev/null +++ b/packages/react-components/react-charts-preview/project.json @@ -0,0 +1,8 @@ +{ + "name": "@fluentui/react-charts-preview", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "library", + "sourceRoot": "packages/react-components/react-charts-preview/src", + "tags": ["platform:web", "vNext"], + "implicitDependencies": [] +} diff --git a/packages/react-components/react-charts-preview/src/AreaChart.ts b/packages/react-components/react-charts-preview/src/AreaChart.ts new file mode 100644 index 0000000000000..d856e8a534aea --- /dev/null +++ b/packages/react-components/react-charts-preview/src/AreaChart.ts @@ -0,0 +1 @@ +export * from './components/AreaChart/index'; diff --git a/packages/react-components/react-charts-preview/src/CartesianChart.ts b/packages/react-components/react-charts-preview/src/CartesianChart.ts new file mode 100644 index 0000000000000..e3da5ee3dd987 --- /dev/null +++ b/packages/react-components/react-charts-preview/src/CartesianChart.ts @@ -0,0 +1 @@ +export * from './components/CommonComponents/index'; diff --git a/packages/react-components/react-charts-preview/src/DonutChart.ts b/packages/react-components/react-charts-preview/src/DonutChart.ts new file mode 100644 index 0000000000000..5b791cc5402ec --- /dev/null +++ b/packages/react-components/react-charts-preview/src/DonutChart.ts @@ -0,0 +1 @@ +export * from './components/DonutChart/index'; diff --git a/packages/react-components/react-charts-preview/src/GaugeChart.ts b/packages/react-components/react-charts-preview/src/GaugeChart.ts new file mode 100644 index 0000000000000..b613bf14bd406 --- /dev/null +++ b/packages/react-components/react-charts-preview/src/GaugeChart.ts @@ -0,0 +1 @@ +export * from './components/GaugeChart/index'; diff --git a/packages/react-components/react-charts-preview/src/GroupedVerticalBarChart.ts b/packages/react-components/react-charts-preview/src/GroupedVerticalBarChart.ts new file mode 100644 index 0000000000000..af93f3307bcc7 --- /dev/null +++ b/packages/react-components/react-charts-preview/src/GroupedVerticalBarChart.ts @@ -0,0 +1 @@ +export * from './components/GroupedVerticalBarChart/index'; diff --git a/packages/react-components/react-charts-preview/src/HeatMapChart.ts b/packages/react-components/react-charts-preview/src/HeatMapChart.ts new file mode 100644 index 0000000000000..5aa76d0e92cc8 --- /dev/null +++ b/packages/react-components/react-charts-preview/src/HeatMapChart.ts @@ -0,0 +1 @@ +export * from './components/HeatMapChart/index'; diff --git a/packages/react-components/react-charts-preview/src/HorizontalBarChart.ts b/packages/react-components/react-charts-preview/src/HorizontalBarChart.ts new file mode 100644 index 0000000000000..25f19e9d313d4 --- /dev/null +++ b/packages/react-components/react-charts-preview/src/HorizontalBarChart.ts @@ -0,0 +1 @@ +export * from './components/HorizontalBarChart/index'; diff --git a/packages/react-components/react-charts-preview/src/HorizontalBarChartWithAxis.ts b/packages/react-components/react-charts-preview/src/HorizontalBarChartWithAxis.ts new file mode 100644 index 0000000000000..19a24a4082f18 --- /dev/null +++ b/packages/react-components/react-charts-preview/src/HorizontalBarChartWithAxis.ts @@ -0,0 +1 @@ +export * from './components/HorizontalBarChartWithAxis/index'; diff --git a/packages/react-components/react-charts-preview/src/Legends.ts b/packages/react-components/react-charts-preview/src/Legends.ts new file mode 100644 index 0000000000000..d0b3c58ffecd2 --- /dev/null +++ b/packages/react-components/react-charts-preview/src/Legends.ts @@ -0,0 +1 @@ +export * from './components/Legends/index'; diff --git a/packages/react-components/react-charts-preview/src/LineChart.ts b/packages/react-components/react-charts-preview/src/LineChart.ts new file mode 100644 index 0000000000000..2105ceffef3a5 --- /dev/null +++ b/packages/react-components/react-charts-preview/src/LineChart.ts @@ -0,0 +1 @@ +export * from './components/LineChart/index'; diff --git a/packages/react-components/react-charts-preview/src/PieChart.ts b/packages/react-components/react-charts-preview/src/PieChart.ts new file mode 100644 index 0000000000000..a2320e43243b5 --- /dev/null +++ b/packages/react-components/react-charts-preview/src/PieChart.ts @@ -0,0 +1 @@ +export * from './components/PieChart/index'; diff --git a/packages/react-components/react-charts-preview/src/SankeyChart.ts b/packages/react-components/react-charts-preview/src/SankeyChart.ts new file mode 100644 index 0000000000000..5a7f70abd7f54 --- /dev/null +++ b/packages/react-components/react-charts-preview/src/SankeyChart.ts @@ -0,0 +1 @@ +export * from './components/SankeyChart/index'; diff --git a/packages/react-components/react-charts-preview/src/Sparkline.ts b/packages/react-components/react-charts-preview/src/Sparkline.ts new file mode 100644 index 0000000000000..9ff982be9d710 --- /dev/null +++ b/packages/react-components/react-charts-preview/src/Sparkline.ts @@ -0,0 +1 @@ +export * from './components/Sparkline/index'; diff --git a/packages/react-components/react-charts-preview/src/StackedBarChart.ts b/packages/react-components/react-charts-preview/src/StackedBarChart.ts new file mode 100644 index 0000000000000..982091b79951d --- /dev/null +++ b/packages/react-components/react-charts-preview/src/StackedBarChart.ts @@ -0,0 +1 @@ +export * from './components/StackedBarChart/index'; diff --git a/packages/react-components/react-charts-preview/src/Styling.ts b/packages/react-components/react-charts-preview/src/Styling.ts new file mode 100644 index 0000000000000..31e6ed2005a05 --- /dev/null +++ b/packages/react-components/react-charts-preview/src/Styling.ts @@ -0,0 +1,99 @@ +export { + AnimationClassNames, + AnimationStyles, + AnimationVariables, + ColorClassNames, + DefaultEffects, + DefaultFontStyles, + DefaultPalette, + // eslint-disable-next-line deprecation/deprecation + EdgeChromiumHighContrastSelector, + FontClassNames, + FontSizes, + FontWeights, + HighContrastSelector, + HighContrastSelectorBlack, + HighContrastSelectorWhite, + IconFontSizes, + InjectionMode, + PulsingBeaconAnimationStyles, + ScreenWidthMaxLarge, + ScreenWidthMaxMedium, + ScreenWidthMaxSmall, + ScreenWidthMaxXLarge, + ScreenWidthMaxXXLarge, + ScreenWidthMinLarge, + ScreenWidthMinMedium, + ScreenWidthMinSmall, + ScreenWidthMinUhfMobile, + ScreenWidthMinXLarge, + ScreenWidthMinXXLarge, + ScreenWidthMinXXXLarge, + Stylesheet, + ThemeSettingName, + ZIndexes, + buildClassMap, + concatStyleSets, + concatStyleSetsWithProps, + createFontStyles, + createTheme, + focusClear, + fontFace, + // eslint-disable-next-line deprecation/deprecation + getEdgeChromiumNoHighContrastAdjustSelector, + getFadedOverflowStyle, + getFocusOutlineStyle, + // eslint-disable-next-line deprecation/deprecation + getFocusStyle, + getGlobalClassNames, + getHighContrastNoAdjustStyle, + getIcon, + getIconClassName, + getInputFocusStyle, + getPlaceholderStyles, + getScreenSelector, + getTheme, + getThemedContext, + hiddenContentStyle, + keyframes, + loadTheme, + mergeStyleSets, + mergeStyles, + noWrap, + normalize, + registerDefaultFontFaces, + registerIconAlias, + registerIcons, + registerOnThemeChangeCallback, + removeOnThemeChangeCallback, + setIconOptions, + unregisterIcons, +} from '@fluentui/react/lib/Styling'; +export type { + GlobalClassNames, + IAnimationStyles, + IAnimationVariables, + ICSPSettings, + IEffects, + IFontFace, + IFontStyles, + IFontWeight, + IGetFocusStylesOptions, + IIconOptions, + IIconRecord, + IIconSubset, + IIconSubsetRecord, + IPalette, + IPartialTheme, + IProcessedStyleSet, + IRawStyle, + IScheme, + ISchemeNames, + ISemanticColors, + ISemanticTextColors, + ISpacing, + IStyle, + IStyleSet, + IStyleSheetConfig, + ITheme, +} from '@fluentui/react/lib/Styling'; diff --git a/packages/react-components/react-charts-preview/src/TreeChart.ts b/packages/react-components/react-charts-preview/src/TreeChart.ts new file mode 100644 index 0000000000000..45b31905337f7 --- /dev/null +++ b/packages/react-components/react-charts-preview/src/TreeChart.ts @@ -0,0 +1 @@ +export * from './components/TreeChart/index'; diff --git a/packages/react-components/react-charts-preview/src/Utilities.ts b/packages/react-components/react-charts-preview/src/Utilities.ts new file mode 100644 index 0000000000000..0850eaa19b063 --- /dev/null +++ b/packages/react-components/react-charts-preview/src/Utilities.ts @@ -0,0 +1,238 @@ +export { + Async, + AutoScroll, + // eslint-disable-next-line deprecation/deprecation + BaseComponent, + Customizations, + // eslint-disable-next-line deprecation/deprecation + Customizer, + CustomizerContext, + DATA_IS_SCROLLABLE_ATTRIBUTE, + DATA_PORTAL_ATTRIBUTE, + DelayedRender, + EventGroup, + FabricPerformance, + FocusRects, + GlobalSettings, + IsFocusVisibleClassName, + KeyCodes, + Rectangle, + SELECTION_CHANGE, + Selection, + SelectionDirection, + SelectionMode, + addDirectionalKeyCode, + addElementAtIndex, + allowOverscrollOnElement, + allowScrollOnElement, + anchorProperties, + appendFunction, + arraysEqual, + asAsync, + assertNever, + assign, + audioProperties, + baseElementEvents, + baseElementProperties, + buttonProperties, + calculatePrecision, + canUseDOM, + classNamesFunction, + colGroupProperties, + colProperties, + composeComponentAs, + composeRenderFunction, + createArray, + createMemoizer, + createMergedRef, + css, + customizable, + disableBodyScroll, + divProperties, + doesElementContainFocus, + elementContains, + elementContainsAttribute, + enableBodyScroll, + extendComponent, + filteredAssign, + find, + findElementRecursive, + findIndex, + findScrollableParent, + fitContentToBounds, + flatten, + focusAsync, + focusFirstChild, + formProperties, + format, + getChildren, + getDistanceBetweenPoints, + getDocument, + getElementIndexPath, + getFirstFocusable, + getFirstTabbable, + getFirstVisibleElementFromSelector, + getFocusableByIndexPath, + getId, + getInitials, + getLanguage, + getLastFocusable, + getLastTabbable, + getNativeElementProps, + getNativeProps, + getNextElement, + getParent, + getPreviousElement, + getPropsWithDefaults, + getRTL, + getRTLSafeKeyCode, + getRect, + // eslint-disable-next-line deprecation/deprecation + getResourceUrl, + getScrollbarWidth, + getVirtualParent, + getWindow, + hasHorizontalOverflow, + hasOverflow, + hasVerticalOverflow, + hoistMethods, + hoistStatics, + htmlElementProperties, + iframeProperties, + // eslint-disable-next-line deprecation/deprecation + imageProperties, + imgProperties, + initializeComponentRef, + // eslint-disable-next-line deprecation/deprecation + initializeFocusRects, + inputProperties, + isControlled, + isDirectionalKeyCode, + isElementFocusSubZone, + isElementFocusZone, + isElementTabbable, + isElementVisible, + isElementVisibleAndNotHidden, + isIE11, + isIOS, + isMac, + isVirtualElement, + labelProperties, + liProperties, + mapEnumByName, + memoize, + memoizeFunction, + merge, + mergeAriaAttributeValues, + mergeCustomizations, + mergeScopedSettings, + mergeSettings, + modalize, + nullRender, + olProperties, + omit, + on, + optionProperties, + portalContainsElement, + precisionRound, + // eslint-disable-next-line deprecation/deprecation + raiseClick, + removeIndex, + replaceElement, + resetControlledWarnings, + resetIds, + resetMemoizations, + safeRequestAnimationFrame, + safeSetTimeout, + selectProperties, + // eslint-disable-next-line deprecation/deprecation + setBaseUrl, + setFocusVisibility, + // eslint-disable-next-line deprecation/deprecation + setLanguage, + setMemoizeWeakMap, + setPortalAttribute, + setRTL, + // eslint-disable-next-line deprecation/deprecation + setSSR, + setVirtualParent, + setWarningCallback, + shallowCompare, + shouldWrapFocus, + styled, + tableProperties, + tdProperties, + textAreaProperties, + thProperties, + toMatrix, + trProperties, + unhoistMethods, + useCustomizationSettings, + useFocusRects, + values, + videoProperties, + warn, + warnConditionallyRequiredProps, + warnControlledUsage, + warnDeprecations, + warnMutuallyExclusive, +} from '@fluentui/react/lib/Utilities'; +export type { + FitMode, + IAsAsyncOptions, + IBaseProps, + ICancelable, + IChangeDescription, + IChangeEventCallback, + // eslint-disable-next-line deprecation/deprecation + IClassNames, + IClassNamesFunctionOptions, + IComponentAs, + IComponentAsProps, + ICssInput, + ICustomizableProps, + ICustomizations, + ICustomizerContext, + ICustomizerProps, + IDeclaredEventsByName, + IDelayedRenderProps, + IDelayedRenderState, + IDictionary, + IDisposable, + IEventRecord, + IEventRecordList, + IEventRecordsByName, + IFitContentToBoundsOptions, + IObjectWithKey, + IPerfData, + IPerfMeasurement, + IPerfSummary, + // eslint-disable-next-line deprecation/deprecation + IPoint, + IPropsWithStyles, + IRectangle, + IRefObject, + IRenderComponent, + IRenderFunction, + ISelection, + ISelectionOptions, + ISelectionOptionsWithRequiredGetKey, + ISerializableObject, + ISettings, + ISettingsFunction, + ISettingsMap, + ISize, + IStyleFunction, + IStyleFunctionOrObject, + IVirtualElement, + IWarnControlledUsageParams, + // eslint-disable-next-line deprecation/deprecation + Omit, + Point, + RefObject, + // eslint-disable-next-line deprecation/deprecation + Settings, + // eslint-disable-next-line deprecation/deprecation + SettingsFunction, + StyleFunction, +} from '@fluentui/react/lib/Utilities'; diff --git a/packages/react-components/react-charts-preview/src/VerticalBarChart.ts b/packages/react-components/react-charts-preview/src/VerticalBarChart.ts new file mode 100644 index 0000000000000..64e4264d310b9 --- /dev/null +++ b/packages/react-components/react-charts-preview/src/VerticalBarChart.ts @@ -0,0 +1 @@ +export * from './components/VerticalBarChart/index'; diff --git a/packages/react-components/react-charts-preview/src/VerticalStackedBarChart.ts b/packages/react-components/react-charts-preview/src/VerticalStackedBarChart.ts new file mode 100644 index 0000000000000..5d7c20fd7951c --- /dev/null +++ b/packages/react-components/react-charts-preview/src/VerticalStackedBarChart.ts @@ -0,0 +1 @@ +export * from './components/VerticalStackedBarChart/index'; diff --git a/packages/react-components/react-charts-preview/src/components/AreaChart/AreaChart.base.tsx b/packages/react-components/react-charts-preview/src/components/AreaChart/AreaChart.base.tsx new file mode 100644 index 0000000000000..8c99b98300071 --- /dev/null +++ b/packages/react-components/react-charts-preview/src/components/AreaChart/AreaChart.base.tsx @@ -0,0 +1,924 @@ +import * as React from 'react'; +import { max as d3Max, bisector } from 'd3-array'; +import { pointer } from 'd3-selection'; +import { select as d3Select } from 'd3-selection'; +import { area as d3Area, stack as d3Stack, curveMonotoneX as d3CurveBasis, line as d3Line } from 'd3-shape'; +import { classNamesFunction, find, getId, memoizeFunction } from '@fluentui/react/lib/Utilities'; +import { + IAccessibilityProps, + CartesianChart, + ICustomizedCalloutData, + IAreaChartProps, + IBasestate, + ILineChartDataPoint, + ILineChartPoints, + IChildProps, + IMargins, + IAreaChartStyleProps, + IAreaChartStyles, +} from '../../index'; +import { warnDeprecations } from '@fluentui/react/lib/Utilities'; +import { + calloutData, + getXAxisType, + ChartTypes, + XAxisTypes, + getTypeOfAxis, + tooltipOfXAxislabels, + getNextColor, + getColorFromToken, +} from '../../utilities/index'; +import { ILegend, Legends } from '../Legends/index'; +import { DirectionalHint } from '@fluentui/react/lib/Callout'; + +const getClassNames = classNamesFunction(); + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const bisect = bisector((d: any) => d.x).left; + +const COMPONENT_NAME = 'AREA CHART'; + +enum InterceptVisibility { + show = 'visibility', + hide = 'hidden', +} + +export interface IAreaChartAreaPoint { + xVal: string | number; + values: IAreaChartDataSetPoint; +} +export interface IAreaChartDataSetPoint { + [key: string]: number | string; +} +export interface IDPointType { + values: { 0: number; 1: number; data: {} }; + xVal: number | Date; +} +export interface IMapXToDataSet { + [key: string]: ILineChartDataPoint[]; + [key: number]: ILineChartDataPoint[]; +} + +//by default d3-shape 3.2.0 limits the< path> data point precision to 3 digits(d3/d3-path#10) + +export interface IAreaChartState extends IBasestate { + lineXValue: number; + displayOfLine: InterceptVisibility; + isCircleClicked: boolean; + dataPointCalloutProps?: ICustomizedCalloutData; + stackCalloutProps?: ICustomizedCalloutData; + nearestCircleToHighlight: number | string | Date | null; + xAxisCalloutAccessibilityData?: IAccessibilityProps; + isShowCalloutPending: boolean; + /** focused point */ + activePoint: string; +} + +export class AreaChartBase extends React.Component { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private _calloutPoints: any; + private _createSet: (data: ILineChartPoints[]) => { + colors: string[]; + opacity: number[]; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + stackedInfo: any; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + calloutPoints: any; + }; + private _colors: string[]; + private _opacity: number[]; + private _uniqueIdForGraph: string; + private _verticalLineId: string; + private _circleId: string; + private _uniqueCallOutID: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private _stackedData: any; + private _chart: JSX.Element[]; + private margins: IMargins; + private _rectId: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private _xAxisRectScale: any; + // determines if the given area chart has multiple stacked bar charts + private _isMultiStackChart: boolean; + private _tooltipId: string; + private _highlightedCircleId: string; + //enableComputationOptimization is used for optimized code to group data points by x value + //from O(n^2) to O(n) using a map. + private _enableComputationOptimization: boolean; + private _firstRenderOptimization: boolean; + private _emptyChartId: string; + + public constructor(props: IAreaChartProps) { + super(props); + this._createSet = memoizeFunction(this._createDataSet); + this.state = { + selectedLegend: '', + activeLegend: '', + hoverXValue: '', + isCalloutVisible: false, + refSelected: null, + YValueHover: [], + lineXValue: 0, + displayOfLine: InterceptVisibility.hide, + isCircleClicked: false, + nearestCircleToHighlight: null, + isShowCalloutPending: false, + activePoint: '', + }; + warnDeprecations(COMPONENT_NAME, props, { + showYAxisGridLines: 'Dont use this property. Lines are drawn by default', + }); + this._uniqueIdForGraph = getId('areaChart_'); + this._verticalLineId = getId('verticalLine_'); + this._circleId = getId('circle'); + this._rectId = getId('rectangle'); + this._tooltipId = getId('AreaChartTooltipID'); + this._enableComputationOptimization = true; + this._firstRenderOptimization = true; + this._emptyChartId = getId('_AreaChart_empty'); + } + + public componentDidUpdate() { + if (this.state.isShowCalloutPending) { + this.setState({ + refSelected: `#${this._highlightedCircleId}`, + isCalloutVisible: true, + isShowCalloutPending: false, + }); + } + } + + public render(): JSX.Element { + if (!this._isChartEmpty()) { + const { lineChartData, chartTitle } = this.props.data; + const points = this._addDefaultColors(lineChartData); + const { colors, opacity, stackedInfo, calloutPoints } = this._createSet(points); + this._calloutPoints = calloutPoints; + const isXAxisDateType = getXAxisType(points); + this._colors = colors; + this._opacity = opacity; + this._stackedData = stackedInfo.stackedData; + const legends: JSX.Element = this._getLegendData(points); + + const tickParams = { + tickValues: this.props.tickValues, + tickFormat: this.props.tickFormat, + }; + + const calloutProps = { + target: this.state.refSelected, + isCalloutVisible: this.state.isCalloutVisible, + directionalHint: DirectionalHint.topAutoEdge, + YValueHover: this.state.YValueHover, + hoverXValue: this.state.hoverXValue, + id: `toolTip${this._uniqueCallOutID}`, + gapSpace: 15, + isBeakVisible: false, + onDismiss: this._closeCallout, + 'data-is-focusable': true, + xAxisCalloutAccessibilityData: this.state.xAxisCalloutAccessibilityData, + ...this.props.calloutProps, + }; + return ( + { + this._xAxisRectScale = props.xScale; + const ticks = this._xAxisRectScale.ticks(); + const width1 = this._xAxisRectScale(ticks[ticks.length - 1]); + const rectHeight = props.containerHeight! - this.margins.top!; + return ( + <> + + + + {this._chart} + + ); + }} + /> + ); + } + return ( +
+ ); + } + + private _getMargins = (margins: IMargins) => { + this.margins = margins; + }; + + private _onRectMouseMove = (mouseEvent: React.MouseEvent) => { + mouseEvent.persist(); + const { data } = this.props; + const { lineChartData } = data; + // This will get the value of the X when mouse is on the chart + const xOffset = this._xAxisRectScale.invert(pointer(mouseEvent)[0], document.getElementById(this._rectId)!); + const i = bisect(lineChartData![0].data, xOffset); + const d0 = lineChartData![0].data[i - 1] as ILineChartDataPoint; + const d1 = lineChartData![0].data[i] as ILineChartDataPoint; + let pointToHighlight: string | Date | number | null = null; + let index: null | number = null; + const axisType = + lineChartData![0].data.length > 0 ? (getTypeOfAxis(lineChartData![0].data[0].x, true) as XAxisTypes) : null; + if (d0 === undefined && d1 !== undefined) { + pointToHighlight = d1.x; + index = i; + } else if (d0 !== undefined && d1 === undefined) { + pointToHighlight = d0.x; + index = i - 1; + } else { + let x0; + let point0; + let point1; + switch (axisType) { + case XAxisTypes.DateAxis: + x0 = new Date(xOffset).getTime(); + point0 = (d0.x as Date).getTime(); + point1 = (d1.x as Date).getTime(); + pointToHighlight = Math.abs(x0 - point0) > Math.abs(x0 - point1) ? d1.x : d0.x; + index = Math.abs(x0 - point0) > Math.abs(x0 - point1) ? i : i - 1; + break; + case XAxisTypes.NumericAxis: + x0 = xOffset as number; + point0 = d0.x as number; + point1 = d1.x as number; + pointToHighlight = Math.abs(x0 - point0) > Math.abs(x0 - point1) ? d1.x : d0.x; + index = Math.abs(x0 - point0) > Math.abs(x0 - point1) ? i : i - 1; + break; + default: + break; + } + } + + const { xAxisCalloutData, xAxisCalloutAccessibilityData } = lineChartData![0].data[index as number]; + const formattedDate = pointToHighlight instanceof Date ? pointToHighlight.toLocaleString() : pointToHighlight; + const modifiedXVal = pointToHighlight instanceof Date ? pointToHighlight.getTime() : pointToHighlight; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const found: any = find(this._calloutPoints, (element: { x: string | number }) => { + return element.x === modifiedXVal; + }); + const nearestCircleToHighlight = + axisType === XAxisTypes.DateAxis ? (pointToHighlight as Date).getTime() : pointToHighlight; + const pointToHighlightUpdated = this.state.nearestCircleToHighlight !== nearestCircleToHighlight; + // if no points need to be called out then don't show vertical line and callout card + if (found && pointToHighlightUpdated && !this.state.isShowCalloutPending) { + this.setState({ + nearestCircleToHighlight: nearestCircleToHighlight, + isCalloutVisible: false, + isShowCalloutPending: true, + lineXValue: this._xAxisRectScale(pointToHighlight), + displayOfLine: InterceptVisibility.show, + isCircleClicked: false, + stackCalloutProps: found!, + YValueHover: found.values, + dataPointCalloutProps: found!, + hoverXValue: xAxisCalloutData ? xAxisCalloutData : formattedDate, + xAxisCalloutAccessibilityData, + activePoint: '', + }); + } else { + /* + When above if condition is false but found=true, it means either + + 1). pointToHighlightUpdated is false. + For this case we dont need to do anything. + + 2). isShowCalloutPending is true. + For this case there will be no callout updation for the event. + This condition has been added to prevent repeated callout flashing. + Currently there is a fraction of second delay between hover event and subsequent callout refresh. + In the meantime if another event is received, the callout continues to flash for the set of + intermediate hover events. + + This does not cause any issue as the user interaction takes atleast a fraction of second and the final + callout state is ultimately achieved. + If a user performs very swift mouse maneuver, the intermediate events will be lost but the callout experience + remains smooth. + */ + } + if (!found) { + this.setState({ + isCalloutVisible: false, + nearestCircleToHighlight: nearestCircleToHighlight, + displayOfLine: InterceptVisibility.hide, + isCircleClicked: false, + }); + } + }; + /** + * just cleaning up the state which we have set in the mouse move event + */ + private _onRectMouseOut = () => { + /**/ + }; + + private _handleChartMouseLeave = () => { + this.setState({ + refSelected: null, + isCalloutVisible: false, + nearestCircleToHighlight: null, + lineXValue: 0, + displayOfLine: InterceptVisibility.hide, + isCircleClicked: false, + stackCalloutProps: undefined, + dataPointCalloutProps: undefined, + hoverXValue: undefined, + YValueHover: [], + }); + }; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private _getStackedData = (keys: string[], dataSet: any) => { + const stackedValues = d3Stack().keys(keys)(dataSet); + const maxOfYVal = d3Max(stackedValues[stackedValues.length - 1], dp => dp[1])!; + const stackedData: Array = []; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + stackedValues.forEach((layer: any) => { + const currentStack: IAreaChartDataSetPoint[] = []; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + layer.forEach((d: any) => { + currentStack.push({ + values: d, + xVal: d.data.xVal, + }); + }); + stackedData.push(currentStack); + }); + this._isMultiStackChart = stackedData && stackedData.length > 1 ? true : false; + return { + stackedData, + maxOfYVal, + }; + }; + + private _createDataSet = (points: ILineChartPoints[]) => { + if (this.props.enablePerfOptimization && this._enableComputationOptimization) { + const allChartPoints: ILineChartDataPoint[] = []; + const dataSet: IAreaChartDataSetPoint[] = []; + const colors: string[] = []; + const opacity: number[] = []; + const calloutPoints = calloutData(points!); + + points && + points.length && + points.forEach((singleChartPoint: ILineChartPoints) => { + colors.push(singleChartPoint.color!); + opacity.push(singleChartPoint.opacity || 1); + allChartPoints.push(...singleChartPoint.data); + }); + + const mapOfXvalToListOfDataPoints: IMapXToDataSet = {}; + allChartPoints.forEach((dataPoint: ILineChartDataPoint) => { + const xValue = dataPoint.x instanceof Date ? dataPoint.x.toLocaleString() : dataPoint.x; + // map of x value to the list of data points which share the same x value . + if (mapOfXvalToListOfDataPoints[xValue]) { + mapOfXvalToListOfDataPoints[xValue].push(dataPoint); + } else { + mapOfXvalToListOfDataPoints[xValue] = [dataPoint]; + } + }); + + Object.keys(mapOfXvalToListOfDataPoints).forEach((key: number | string) => { + const value: ILineChartDataPoint[] = mapOfXvalToListOfDataPoints[key]; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const singleDataset: any = {}; + value.forEach((singleDataPoint: ILineChartDataPoint, index: number) => { + singleDataset.xVal = singleDataPoint.x; + singleDataset[`chart${index}`] = singleDataPoint.y; + }); + dataSet.push(singleDataset); + }); + + // get keys from dataset, used to create stacked data + const keysLength: number = dataSet && Object.keys(dataSet[0])!.length; + const keys: string[] = []; + for (let i = 0; i < keysLength - 1; i++) { + const keyVal = `chart${i}`; + keys.push(keyVal); + } + + // Stacked Info used to draw graph + const stackedInfo = this._getStackedData(keys, dataSet); + + return { + colors, + opacity, + keys, + stackedInfo, + calloutPoints, + }; + } else { + const allChartPoints: ILineChartDataPoint[] = []; + const dataSet: IAreaChartDataSetPoint[] = []; + const colors: string[] = []; + const opacity: number[] = []; + const calloutPoints = calloutData(points!); + + points && + points.length && + points.forEach((singleChartPoint: ILineChartPoints) => { + colors.push(singleChartPoint.color!); + opacity.push(singleChartPoint.opacity || 1); + allChartPoints.push(...singleChartPoint.data); + }); + + let tempArr = allChartPoints; + while (tempArr.length) { + const valToCheck = tempArr[0].x instanceof Date ? tempArr[0].x.toLocaleString() : tempArr[0].x; + const filteredChartPoints: ILineChartDataPoint[] = tempArr.filter( + (point: ILineChartDataPoint) => (point.x instanceof Date ? point.x.toLocaleString() : point.x) === valToCheck, + ); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const singleDataset: any = {}; + filteredChartPoints.forEach((singleDataPoint: ILineChartDataPoint, index: number) => { + singleDataset.xVal = singleDataPoint.x; + singleDataset[`chart${index}`] = singleDataPoint.y; + }); + dataSet.push(singleDataset); + // removing compared objects from array + const val = tempArr[0].x instanceof Date ? tempArr[0].x.toLocaleString() : tempArr[0].x; + tempArr = tempArr.filter( + (point: ILineChartDataPoint) => (point.x instanceof Date ? point.x.toLocaleString() : point.x) !== val, + ); + } + + // get keys from dataset, used to create stacked data + const keysLength: number = dataSet && Object.keys(dataSet[0])!.length; + const keys: string[] = []; + for (let i = 0; i < keysLength - 1; i++) { + const keyVal = `chart${i}`; + keys.push(keyVal); + } + + // Stacked Info used to draw graph + const stackedInfo = this._getStackedData(keys, dataSet); + + return { + colors, + opacity, + keys, + stackedInfo, + calloutPoints, + }; + } + }; + + private _getCustomizedCallout = () => { + return this.props.onRenderCalloutPerStack + ? this.props.onRenderCalloutPerStack(this.state.stackCalloutProps) + : this.props.onRenderCalloutPerDataPoint + ? this.props.onRenderCalloutPerDataPoint(this.state.dataPointCalloutProps) + : null; + }; + + private _getGraphData = ( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + xAxis: any, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + yAxis: any, + containerHeight: number, + containerWidth: number, + xElement: SVGElement | null, + ) => { + this._chart = this._drawGraph(containerHeight, xAxis, yAxis, xElement!); + }; + + private _onLegendClick(legend: string): void { + if (this.state.selectedLegend === legend) { + this.setState({ + selectedLegend: '', + }); + } else { + this.setState({ + selectedLegend: legend, + }); + } + } + + private _onLegendHover(legend: string): void { + this.setState({ + activeLegend: legend, + }); + } + + private _onLegendLeave(): void { + this.setState({ + activeLegend: '', + }); + } + + private _getLegendData = (points: ILineChartPoints[]): JSX.Element => { + const data = points; + const actions: ILegend[] = []; + + data.forEach((singleChartData: ILineChartPoints) => { + const color: string = singleChartData.color!; + const checkSimilarLegends = actions.filter( + (leg: ILegend) => leg.title === singleChartData.legend && leg.color === color, + ); + if (checkSimilarLegends!.length > 0) { + return; + } + + const legend: ILegend = { + title: singleChartData.legend, + color: color, + action: () => { + this._onLegendClick(singleChartData.legend); + }, + hoverAction: () => { + this._handleChartMouseLeave(); + this._onLegendHover(singleChartData.legend); + }, + onMouseOutAction: () => { + this._onLegendLeave(); + }, + }; + + actions.push(legend); + }); + return ( + + ); + }; + + private _onDataPointClick = (func: (() => void) | undefined) => { + if (func) { + func(); + } + this.setState({ isCircleClicked: true }); + }; + + private _getOpacity = (legend: string): number => { + if (!this._isMultiStackChart) { + return 0.7; + } else { + const opacity = this._legendHighlighted(legend) || this._noLegendHighlighted() ? 0.7 : 0.1; + return opacity; + } + }; + + private _getLineOpacity = (legend: string): number => { + if (!this._isMultiStackChart) { + return 1; + } else { + let opacity = 0.3; + if (this.state.isCalloutVisible) { + opacity = 1; + } + if (!this._noLegendHighlighted()) { + opacity = this._legendHighlighted(legend) ? 0 : 0.1; + } + return opacity; + } + }; + + private _updateCircleFillColor = (xDataPoint: number | Date, lineColor: string, circleId: string): string => { + let fillColor = lineColor; + if (this.state.nearestCircleToHighlight === xDataPoint || this.state.activePoint === circleId) { + this._highlightedCircleId = circleId; + if (!this.state.isCircleClicked) { + fillColor = this.props.theme!.semanticColors.bodyBackground; + } + } + + return fillColor; + }; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private _drawGraph = (containerHeight: number, xScale: any, yScale: any, xElement: SVGElement): JSX.Element[] => { + const points = this._addDefaultColors(this.props.data.lineChartData); + const { pointOptions, pointLineOptions } = this.props.data; + const area = d3Area() + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .x((d: any) => xScale(d.xVal)) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .y0((d: any) => yScale(d.values[0])) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .y1((d: any) => yScale(d.values[1])) + .curve(d3CurveBasis); + const line = d3Line() + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .x((d: any) => xScale(d.xVal)) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .y((d: any) => yScale(d.values[1])) + .curve(d3CurveBasis); + + const graph: JSX.Element[] = []; + let lineColor: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + this._stackedData.forEach((singleStackedData: Array, index: number) => { + graph.push( + + {this.props.enableGradient && ( + + + + + + + )} + + {singleStackedData.length === 1 ? ( + + ) : ( + + )} + , + ); + }); + + const circleRadius = pointOptions && pointOptions.r ? Number(pointOptions.r) : 8; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + this._stackedData.forEach((singleStackedData: Array, index: number) => { + if (points.length === index) { + return; + } + + if (!this.props.optimizeLargeData || singleStackedData.length === 1) { + // Render circles for all data points + graph.push( + + {singleStackedData.map((singlePoint: IDPointType, pointIndex: number) => { + const circleId = `${this._circleId}_${index * this._stackedData[0].length + pointIndex}`; + const xDataPoint = singlePoint.xVal instanceof Date ? singlePoint.xVal.getTime() : singlePoint.xVal; + lineColor = points[index]!.color!; + return ( + this._handleFocus(index, pointIndex, circleId)} + onBlur={this._handleBlur} + {...pointOptions} + r={this._getCircleRadius(xDataPoint, circleRadius, circleId)} + role="img" + aria-label={this._getAriaLabel(index, pointIndex)} + /> + ); + })} + , + ); + } else { + // Render circles for data points close to the mouse pointer only + singleStackedData.forEach((singlePoint: IDPointType, pointIndex: number) => { + const xDataPoint = singlePoint.xVal instanceof Date ? singlePoint.xVal.getTime() : singlePoint.xVal; + if (this.state.nearestCircleToHighlight === xDataPoint) { + const circleId = `${this._circleId}_${index * this._stackedData[0].length + pointIndex}`; + lineColor = points[index]!.color!; + graph.push( + , + ); + } + }); + } + }); + graph.push( + , + ); + const classNames = getClassNames(this.props.styles!, { + theme: this.props.theme!, + }); + // Removing un wanted tooltip div from DOM, when prop not provided. + if (!this.props.showXAxisLablesTooltip) { + try { + document.getElementById(this._tooltipId) && document.getElementById(this._tooltipId)!.remove(); + // eslint-disable-next-line no-empty + } catch (e) {} + } + // Used to display tooltip at x axis labels. + if (!this.props.wrapXAxisLables && this.props.showXAxisLablesTooltip) { + const xAxisElement = d3Select(xElement).call(xScale); + try { + document.getElementById(this._tooltipId) && document.getElementById(this._tooltipId)!.remove(); + // eslint-disable-next-line no-empty + } catch (e) {} + const tooltipProps = { + tooltipCls: classNames.tooltip!, + id: this._tooltipId, + xAxis: xAxisElement, + }; + xAxisElement && tooltipOfXAxislabels(tooltipProps); + } + return graph; + }; + + private _getCircleRadius = (xDataPoint: number, circleRadius: number, circleId: string): number => { + const { isCircleClicked, nearestCircleToHighlight, activePoint } = this.state; + if (isCircleClicked && nearestCircleToHighlight === xDataPoint) { + return 1; + } else if (nearestCircleToHighlight === xDataPoint || activePoint === circleId) { + return circleRadius; + } else { + return 0; + } + }; + + private _closeCallout = () => { + this.setState({ + isCalloutVisible: false, + }); + }; + + /** + * This function checks if the given legend is highlighted or not. + * A legend can be highlighted in 2 ways: + * 1. selection: if the user clicks on it + * 2. hovering: if there is no selected legend and the user hovers over it + */ + private _legendHighlighted = (legend: string) => { + return ( + this.state.selectedLegend === legend || (this.state.selectedLegend === '' && this.state.activeLegend === legend) + ); + }; + + /** + * This function checks if none of the legends is selected or hovered. + */ + private _noLegendHighlighted = () => { + return this.state.selectedLegend === '' && this.state.activeLegend === ''; + }; + + private _addDefaultColors = (lineChartData?: ILineChartPoints[]): ILineChartPoints[] => { + return lineChartData + ? lineChartData.map((item, index) => { + let color: string; + // isInverted property is applicable to v8 themes only + if (typeof item.color === 'undefined') { + color = getNextColor(index, 0, this.props.theme?.isInverted); + } else { + color = getColorFromToken(item.color, this.props.theme?.isInverted); + } + + return { ...item, color }; + }) + : []; + }; + + private _handleFocus = (lineIndex: number, pointIndex: number, circleId: string) => { + const { x, y, xAxisCalloutData } = this.props.data.lineChartData![lineIndex].data[pointIndex]; + const formattedDate = x instanceof Date ? x.toLocaleString() : x; + const modifiedXVal = x instanceof Date ? x.getTime() : x; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const found: any = this._calloutPoints.find((e: { x: string | number }) => e.x === modifiedXVal); + // Show details in the callout for the focused point only + found.values = found.values.filter((e: { y: number }) => e.y === y); + + this.setState({ + refSelected: `#${circleId}`, + isCalloutVisible: true, + hoverXValue: xAxisCalloutData ? xAxisCalloutData : formattedDate, + YValueHover: found.values, + stackCalloutProps: found, + dataPointCalloutProps: found, + activePoint: circleId, + }); + }; + + private _handleBlur = () => { + this.setState({ + refSelected: null, + isCalloutVisible: false, + hoverXValue: undefined, + YValueHover: [], + stackCalloutProps: undefined, + dataPointCalloutProps: undefined, + activePoint: '', + }); + }; + + private _getAriaLabel(lineIndex: number, pointIndex: number): string { + const line = this.props.data.lineChartData![lineIndex]; + const point = line.data[pointIndex]; + const formattedDate = point.x instanceof Date ? point.x.toLocaleString() : point.x; + const xValue = point.xAxisCalloutData || formattedDate; + const legend = line.legend; + const yValue = point.yAxisCalloutData || point.y; + return point.callOutAccessibilityData?.ariaLabel || `${xValue}. ${legend}, ${yValue}.`; + } + + private _isChartEmpty(): boolean { + return !( + ( + this.props.data && + this.props.data.lineChartData && + this.props.data.lineChartData.length > 0 && + this.props.data.lineChartData.filter(item => item.data.length === 0).length === 0 + ) + // if all the data sets have no data + // filtering all items which have no data and checking if the length of the filtered array is 0 + // which means chart is not empty + ); + } +} diff --git a/packages/react-components/react-charts-preview/src/components/AreaChart/AreaChart.styles.ts b/packages/react-components/react-charts-preview/src/components/AreaChart/AreaChart.styles.ts new file mode 100644 index 0000000000000..32dc1fe8d60c3 --- /dev/null +++ b/packages/react-components/react-charts-preview/src/components/AreaChart/AreaChart.styles.ts @@ -0,0 +1,18 @@ +import { IAreaChartStyleProps, IAreaChartStyles } from './AreaChart.types'; + +export const getStyles = (props: IAreaChartStyleProps): IAreaChartStyles => { + return { + tooltip: { + ...props.theme!.fonts.medium, + display: 'flex', + flexDirection: 'column', + padding: '8px', + position: 'absolute', + textAlign: 'center', + top: '0px', + background: props.theme!.semanticColors.bodyBackground, + borderRadius: '2px', + pointerEvents: 'none', + }, + }; +}; diff --git a/packages/react-components/react-charts-preview/src/components/AreaChart/AreaChart.test.tsx b/packages/react-components/react-charts-preview/src/components/AreaChart/AreaChart.test.tsx new file mode 100644 index 0000000000000..326aa38bd049d --- /dev/null +++ b/packages/react-components/react-charts-preview/src/components/AreaChart/AreaChart.test.tsx @@ -0,0 +1,436 @@ +jest.mock('react-dom'); +import * as React from 'react'; +import { resetIds } from '../../Utilities'; +import { mount, ReactWrapper } from 'enzyme'; +import { IAreaChartProps, AreaChart } from './index'; +import { IAreaChartState, AreaChartBase } from './AreaChart.base'; +import { ICustomizedCalloutData, ILineChartPoints } from '../../index'; +import toJson from 'enzyme-to-json'; +import { act } from 'react-dom/test-utils'; + +// Wrapper of the AreaChart to be tested. +let wrapper: ReactWrapper | undefined; +const originalRAF = window.requestAnimationFrame; + +function sharedBeforeEach() { + resetIds(); + jest.useFakeTimers(); + Object.defineProperty(window, 'requestAnimationFrame', { + writable: true, + value: (callback: FrameRequestCallback) => callback(0), + }); +} + +function sharedAfterEach() { + if (wrapper) { + wrapper.unmount(); + wrapper = undefined; + } + + // Do this after unmounting the wrapper to make sure if any timers cleaned up on unmount are + // cleaned up in fake timers world + // eslint-disable-next-line @typescript-eslint/no-explicit-any + if ((global.setTimeout as any).mock) { + jest.useRealTimers(); + } + jest.useRealTimers(); + window.requestAnimationFrame = originalRAF; +} + +const points: ILineChartPoints[] = [ + { + legend: 'metaData1', + data: [ + { x: 20, y: 50 }, + { x: 40, y: 80 }, + ], + color: 'red', + }, +]; +export const chartPoints = { + chartTitle: 'AreaChart', + lineChartData: points, +}; + +const singlePoint = [ + { + legend: 'metaData1', + data: [{ x: 20, y: 50 }], + color: 'red', + }, +]; +const singleChartPoint = { + chartTitle: 'AreaChart', + lineChartData: singlePoint, +}; + +const emptyPoint = [ + { + legend: 'metaData1', + data: [], + color: 'red', + }, +]; +export const emptyChartPoints = { + chartTitle: 'EmptyAreaChart', + lineChartData: emptyPoint, +}; + +// FIXME - non deterministic snapshots causing master pipeline breaks +describe.skip('AreaChart snapShot testing', () => { + beforeEach(() => { + resetIds(); + }); + afterEach(() => { + if (wrapper) { + wrapper.unmount(); + wrapper = undefined; + } + + // Do this after unmounting the wrapper to make sure if any timers cleaned up on unmount are + // cleaned up in fake timers world + // eslint-disable-next-line @typescript-eslint/no-explicit-any + if ((global.setTimeout as any).mock) { + jest.useRealTimers(); + } + }); + + it('renders Areachart correctly', async () => { + await act(async () => { + wrapper = mount(); + await new Promise(resolve => setTimeout(resolve)); + wrapper.update(); + }); + const tree = toJson(wrapper!, { mode: 'deep' }); + expect(tree).toMatchSnapshot(); + }); + it('renders hideLegend correctly', async () => { + await act(async () => { + wrapper = mount(); + await new Promise(resolve => setTimeout(resolve)); + wrapper.update(); + }); + const tree = toJson(wrapper!, { mode: 'deep' }); + expect(tree).toMatchSnapshot(); + }); + + it('renders hideTooltip correctly', async () => { + await act(async () => { + wrapper = mount(); + await new Promise(resolve => setTimeout(resolve)); + wrapper.update(); + }); + const tree = toJson(wrapper!, { mode: 'deep' }); + expect(tree).toMatchSnapshot(); + }); + + it('renders enabledLegendsWrapLines correctly', async () => { + await act(async () => { + wrapper = mount(); + await new Promise(resolve => setTimeout(resolve)); + wrapper.update(); + }); + const tree = toJson(wrapper!, { mode: 'deep' }); + expect(tree).toMatchSnapshot(); + }); + it('renders yAxisTickFormat correctly', async () => { + await act(async () => { + wrapper = mount(); + await new Promise(resolve => setTimeout(resolve)); + wrapper.update(); + }); + const tree = toJson(wrapper!, { mode: 'deep' }); + expect(tree).toMatchSnapshot(); + }); + + it('renders Areachart with single point correctly', async () => { + await act(async () => { + wrapper = mount(); + await new Promise(resolve => setTimeout(resolve)); + wrapper.update(); + }); + const tree = toJson(wrapper!, { mode: 'deep' }); + expect(tree).toMatchSnapshot(); + }); + + it('Should render with default colors when line color is not provided', async () => { + const lineColor = points[0].color; + delete points[0].color; + + await act(async () => { + wrapper = mount(); + await new Promise(resolve => setTimeout(resolve)); + wrapper.update(); + }); + + const tree = toJson(wrapper!, { mode: 'deep' }); + expect(tree).toMatchSnapshot(); + + points[0].color = lineColor; + }); + + it('Should not render circles when optimizeLargeData is true', async () => { + await act(async () => { + wrapper = mount(); + await new Promise(resolve => setTimeout(resolve)); + wrapper.update(); + }); + const tree = toJson(wrapper!, { mode: 'deep' }); + expect(tree).toMatchSnapshot(); + }); + + it('renders showXAxisLablesTooltip correctly', async () => { + await act(async () => { + wrapper = mount(); + await new Promise(resolve => setTimeout(resolve)); + wrapper.update(); + }); + if (wrapper) { + const tree = toJson(wrapper, { mode: 'deep' }); + expect(tree).toMatchSnapshot(); + } + }); + + it('renders wrapXAxisLables correctly', async () => { + const mockGetComputedTextLength = jest.fn().mockReturnValue(100); + + // Replace the original method with the mock implementation + Object.defineProperty( + Object.getPrototypeOf(document.createElementNS('http://www.w3.org/2000/svg', 'tspan')), + 'getComputedTextLength', + { + value: mockGetComputedTextLength, + }, + ); + await act(async () => { + wrapper = mount(); + await new Promise(resolve => setTimeout(resolve)); + wrapper.update(); + }); + const tree = toJson(wrapper!, { mode: 'deep' }); + expect(tree).toMatchSnapshot(); + }); +}); + +describe('AreaChart - basic props', () => { + beforeEach(sharedBeforeEach); + afterEach(sharedAfterEach); + + it('Should not mount legend when hideLegend true ', () => { + act(() => { + wrapper = mount(); + }); + const hideLegendDOM = wrapper!.getDOMNode().querySelectorAll('[class^="legendContainer"]'); + expect(hideLegendDOM!.length).toBe(0); + }); + + it('Should mount legend when hideLegend false ', () => { + act(() => { + wrapper = mount(); + }); + const hideLegendDOM = wrapper!.getDOMNode().querySelectorAll('[class^="legendContainer"]'); + expect(hideLegendDOM).toBeDefined(); + }); + it('Should mount callout when hideTootip false ', () => { + act(() => { + wrapper = mount(); + }); + const hideLegendDOM = wrapper!.getDOMNode().querySelectorAll('[class^="ms-Layer"]'); + expect(hideLegendDOM).toBeDefined(); + }); + it('Should not mount callout when hideTootip true ', () => { + act(() => { + wrapper = mount(); + }); + const hideLegendDOM = wrapper!.getDOMNode().querySelectorAll('[class^="ms-Layer"]'); + expect(hideLegendDOM.length).toBe(0); + }); + + it('Should render onRenderCalloutPerStack ', () => { + act(() => { + wrapper = mount( + + props ? ( +
+

Custom Callout Content

+
+ ) : null + } + />, + ); + }); + const renderedDOM = wrapper!.getDOMNode().getElementsByClassName('.onRenderCalloutPerStack'); + expect(renderedDOM).toBeDefined(); + }); + it('Should not render onRenderCalloutPerStack ', () => { + act(() => { + wrapper = mount(); + }); + const renderedDOM = wrapper!.getDOMNode().getElementsByClassName('.onRenderCalloutPerStack'); + expect(renderedDOM!.length).toBe(0); + }); + + it('Should render onRenderCalloutPerDataPoint ', () => { + act(() => { + wrapper = mount( + + props ? ( +
+

Custom Callout Content

+
+ ) : null + } + />, + ); + }); + const renderedDOM = wrapper!.getDOMNode().getElementsByClassName('.onRenderCalloutPerDataPoint'); + expect(renderedDOM).toBeDefined(); + }); + + it('Should not render onRenderCalloutPerDataPoint ', () => { + act(() => { + wrapper = mount(); + }); + const renderedDOM = wrapper!.getDOMNode().getElementsByClassName('.onRenderCalloutPerDataPoint'); + expect(renderedDOM!.length).toBe(0); + }); +}); + +describe('Render calling with respective to props', () => { + beforeEach(() => { + resetIds(); + }); + it('No prop changes', () => { + const renderMock = jest.spyOn(AreaChartBase.prototype, 'render'); + const props = { + data: chartPoints, + height: 300, + width: 600, + }; + act(() => { + const component = mount(); + component.setProps({ ...props }); + }); + expect(renderMock).toHaveBeenCalledTimes(2); + renderMock.mockRestore(); + }); + it('prop changes', () => { + const renderMock = jest.spyOn(AreaChartBase.prototype, 'render'); + const props = { + data: chartPoints, + height: 300, + width: 600, + hideLegend: true, + }; + act(() => { + const component = mount(); + component.setProps({ ...props, hideTooltip: true }); + }); + renderMock.mockRestore(); + }); +}); + +describe('AreaChart - mouse events', () => { + let root: HTMLDivElement | null; + + beforeEach(() => { + sharedBeforeEach(); + + root = document.createElement('div'); + document.body.appendChild(root); + }); + + afterEach(() => { + sharedAfterEach(); + + if (root) { + document.body.removeChild(root); + root = null; + } + }); + + it('Should render callout correctly on mouseover', () => { + act(() => { + wrapper = mount(, { attachTo: root }); + }); + wrapper!.find('rect').simulate('mouseover'); + const tree = toJson(wrapper!, { mode: 'deep' }); + expect(tree).toMatchSnapshot(); + }); + + it('Should render callout correctly on mousemove', () => { + act(() => { + wrapper = mount(, { attachTo: root }); + }); + wrapper!.find('rect').simulate('mousemove', { clientX: 40, clientY: 0 }); + const html1 = wrapper!.html(); + wrapper!.find('rect').simulate('mousemove', { clientX: -20, clientY: 0 }); + const html2 = wrapper!.html(); + expect(html1).not.toBe(html2); + }); + + it('Should render customized callout on mouseover', () => { + act(() => { + wrapper = mount( + + props ? ( +
+
{JSON.stringify(props, null, 2)}
+
+ ) : null + } + />, + { attachTo: root }, + ); + }); + wrapper!.find('rect').simulate('mouseover'); + const tree = toJson(wrapper!, { mode: 'deep' }); + expect(tree).toMatchSnapshot(); + }); + + it('Should render customized callout per stack on mouseover', () => { + act(() => { + wrapper = mount( + + props ? ( +
+
{JSON.stringify(props, null, 2)}
+
+ ) : null + } + />, + { attachTo: root }, + ); + }); + wrapper!.find('rect').simulate('mouseover'); + const tree = toJson(wrapper!, { mode: 'deep' }); + expect(tree).toMatchSnapshot(); + }); + + describe('Render empty chart aria label div when chart is empty', () => { + it('No empty chart aria label div rendered', () => { + act(() => { + wrapper = mount(); + }); + const renderedDOM = wrapper!.findWhere(node => node.prop('aria-label') === 'Graph has no data to display'); + expect(renderedDOM!.length).toBe(0); + }); + it('Empty chart aria label div rendered', () => { + act(() => { + wrapper = mount(); + }); + const renderedDOM = wrapper!.findWhere(node => node.prop('aria-label') === 'Graph has no data to display'); + expect(renderedDOM!.length).toBe(1); + }); + }); +}); diff --git a/packages/react-components/react-charts-preview/src/components/AreaChart/AreaChart.tsx b/packages/react-components/react-charts-preview/src/components/AreaChart/AreaChart.tsx new file mode 100644 index 0000000000000..f15f1db066945 --- /dev/null +++ b/packages/react-components/react-charts-preview/src/components/AreaChart/AreaChart.tsx @@ -0,0 +1,16 @@ +import * as React from 'react'; +import { styled } from '@fluentui/react/lib/Utilities'; +import { IAreaChartProps, IAreaChartStyleProps, IAreaChartStyles } from '../../index'; +import { AreaChartBase } from '../AreaChart/AreaChart.base'; +import { getStyles } from './AreaChart.styles'; + +// Create a AreaChart variant which uses these default styles and this styled subcomponent. +/** + * Areachart component. + * {@docCategory AreaChart} + */ +export const AreaChart: React.FunctionComponent = styled< + IAreaChartProps, + IAreaChartStyleProps, + IAreaChartStyles +>(AreaChartBase, getStyles); diff --git a/packages/react-components/react-charts-preview/src/components/AreaChart/AreaChart.types.ts b/packages/react-components/react-charts-preview/src/components/AreaChart/AreaChart.types.ts new file mode 100644 index 0000000000000..f0aa8a6e77eaf --- /dev/null +++ b/packages/react-components/react-charts-preview/src/components/AreaChart/AreaChart.types.ts @@ -0,0 +1,85 @@ +import { IRenderFunction, IStyleFunctionOrObject } from '@fluentui/react/lib/Utilities'; +import { + IChartProps, + IRefArrayData, + IBasestate, + ILineChartDataPoint, + ILineChartPoints, + ICustomizedCalloutData, + IMargins, +} from '../../types/index'; +import { + ICartesianChartStyles, + ICartesianChartStyleProps, + ICartesianChartProps, + IChildProps, +} from '../CommonComponents/CartesianChart.types'; + +export type { IChildProps, IRefArrayData, IBasestate, ILineChartDataPoint, ILineChartPoints, IMargins }; + +/** + * Area Chart properties. + * {@docCategory AreaChart} + */ +export interface IAreaChartProps extends ICartesianChartProps { + /** + * Data to render in the chart. + */ + data: IChartProps; + + /** + * This prop is used to draw Y axis grid lines on the chart. Default value will be false + * @deprecated now lines are shown by default + * no need to use this prop + */ + showYAxisGridLines?: boolean; + + /** + * Call to provide customized styling that will layer on top of the variant rules. + */ + styles?: IStyleFunctionOrObject; + + /** + * Define a custom callout renderer for a data point + */ + onRenderCalloutPerDataPoint?: IRenderFunction; + + /** + * Define a custom callout renderer for a stack; default is to render per data point + */ + onRenderCalloutPerStack?: IRenderFunction; + + /** + * The prop used to define the culture to localized the numbers + */ + culture?: string; + + /** + * @default false + * The prop used to enable the perf optimization + */ + enablePerfOptimization?: boolean; + + /* + * Optimize area chart rendering for large data set. + */ + optimizeLargeData?: boolean; + + /** + * @default false + * The prop used to enable gradient fill color for the chart. + */ + enableGradient?: boolean; +} + +/** + * Area Chart styles + * {@docCategory AreaChart} + */ +export interface IAreaChartStyles extends ICartesianChartStyles {} + +/** + * Area Chart style properties + * {@docCategory AreaChart} + */ +export interface IAreaChartStyleProps extends ICartesianChartStyleProps {} diff --git a/packages/react-components/react-charts-preview/src/components/AreaChart/AreaChartRTL.test.tsx b/packages/react-components/react-charts-preview/src/components/AreaChart/AreaChartRTL.test.tsx new file mode 100644 index 0000000000000..63327b0634790 --- /dev/null +++ b/packages/react-components/react-charts-preview/src/components/AreaChart/AreaChartRTL.test.tsx @@ -0,0 +1,578 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { render, screen, fireEvent, act } from '@testing-library/react'; +import * as React from 'react'; +import { DarkTheme } from '@fluentui/theme-samples'; +import { ThemeProvider, resetIds } from '@fluentui/react'; +import { AreaChart, IAreaChartProps } from './index'; +import { DefaultPalette } from '@fluentui/react/lib/Styling'; + +import { + forEachTimezone, + getByClass, + getById, + isTimezoneSet, + testWithWait, + testWithoutWait, + isTestEnv, +} from '../../utilities/TestUtility.test'; +import { axe, toHaveNoViolations } from 'jest-axe'; +const { Timezone } = require('../../../scripts/constants'); + +expect.extend(toHaveNoViolations); + +const originalRAF = window.requestAnimationFrame; + +function updateChartWidthAndHeight() { + jest.useFakeTimers(); + Object.defineProperty(window, 'requestAnimationFrame', { + writable: true, + value: (callback: FrameRequestCallback) => callback(0), + }); + window.HTMLElement.prototype.getBoundingClientRect = () => + ({ + bottom: 44, + height: 50, + left: 10, + right: 35.67, + top: 20, + width: 650, + } as DOMRect); +} +beforeEach(() => { + resetIds(); +}); + +function sharedAfterEach() { + jest.useRealTimers(); + window.requestAnimationFrame = originalRAF; +} + +const chart1Points = [ + { + x: 20, + y: 9, + }, + { + x: 25, + y: 14, + }, + { + x: 30, + y: 14, + }, + { + x: 35, + y: 23, + }, + { + x: 40, + y: 20, + }, + { + x: 45, + y: 31, + }, + { + x: 50, + y: 29, + }, + { + x: 55, + y: 27, + }, + { + x: 60, + y: 37, + }, + { + x: 65, + y: 51, + }, +]; + +const chart2Points = [ + { + x: 20, + y: 21, + }, + { + x: 25, + y: 25, + }, + { + x: 30, + y: 10, + }, + { + x: 35, + y: 10, + }, + { + x: 40, + y: 14, + }, + { + x: 45, + y: 18, + }, + { + x: 50, + y: 9, + }, + { + x: 55, + y: 23, + }, + { + x: 60, + y: 7, + }, + { + x: 65, + y: 55, + }, +]; + +const chart3Points = [ + { + x: 20, + y: 30, + }, + { + x: 25, + y: 35, + }, + { + x: 30, + y: 33, + }, + { + x: 35, + y: 40, + }, + { + x: 40, + y: 10, + }, + { + x: 45, + y: 40, + }, + { + x: 50, + y: 34, + }, + { + x: 55, + y: 40, + }, + { + x: 60, + y: 60, + }, + { + x: 65, + y: 40, + }, +]; + +const chartPoints = [ + { + legend: 'legend1', + data: chart1Points, + color: 'green', + }, + { + legend: 'legend2', + data: chart2Points, + color: 'yellow', + }, + { + legend: 'legend3', + data: chart3Points, + color: 'blue', + }, +]; + +const chartData = { + chartTitle: 'Area chart multiple example', + lineChartData: chartPoints, +}; + +const chart1PointsWithDate = [ + { + x: new Date('01/06/2018'), + y: 5, + }, + { + x: new Date('01/08/2018'), + y: 16, + }, + { + x: new Date('01/16/2018'), + y: 6, + }, + { + x: new Date('02/06/2018'), + y: 30, + }, + { + x: new Date('02/16/2018'), + y: 10, + }, +]; + +const chart2PointsWithDate = [ + { + x: new Date('01/06/2018'), + y: 10, + }, + { + x: new Date('01/08/2018'), + y: 33, + }, + { + x: new Date('01/16/2018'), + y: 21, + }, + { + x: new Date('02/06/2018'), + y: 44, + }, + { + x: new Date('02/16/2018'), + y: 22, + }, +]; + +const chartPointsWithDate = [ + { + legend: 'legend1', + data: chart1PointsWithDate, + color: '#0099BC', + opacity: 0.7, + lineOptions: { + strokeWidth: 2, + strokeDasharray: '5 5', + }, + }, + { + legend: 'legend2', + data: chart2PointsWithDate, + color: '#77004D', + opacity: 0.8, + lineOptions: { + strokeWidth: 5, + stroke: DefaultPalette.blueDark, + }, + }, +]; + +const tickValues = [ + new Date('2020-01-06T00:00:00.000Z'), + new Date('2020-01-08T00:00:00.000Z'), + new Date('2020-01-15T00:00:00.000Z'), + new Date('2020-02-06T00:00:00.000Z'), + new Date('2020-02-15T00:00:00.000Z'), +]; + +const chartDataWithDates = { + chartTitle: 'Area chart styled example', + lineChartData: chartPointsWithDate, + pointOptions: { r: 10, strokeWidth: 3, opacity: 1, stroke: DefaultPalette.blueDark }, + pointLineOptions: { strokeWidth: 2, strokeDasharray: '10 10', stroke: DefaultPalette.blueDark }, +}; + +describe('Area chart rendering', () => { + beforeEach(updateChartWidthAndHeight); + afterEach(sharedAfterEach); + + testWithoutWait( + 'Should render the area chart with numeric x-axis data', + AreaChart, + { data: chartData }, + container => { + expect(container).toMatchSnapshot(); + }, + ); + + forEachTimezone((tzName, tzIdentifier) => { + beforeEach(updateChartWidthAndHeight); + afterEach(sharedAfterEach); + + testWithoutWait( + `Should render the area chart with date x-axis data in ${tzName} timezone`, + AreaChart, + { data: chartDataWithDates }, + container => { + expect(container).toMatchSnapshot(); + }, + undefined, + undefined, + !(isTimezoneSet(tzIdentifier) && isTestEnv()), + ); + }); + + const testCases = [ + ['when tick Values is given', { data: chartDataWithDates, tickValues, tickFormat: '%m/%d' }], + ['when tick Values not given and tick format is given', { data: chartDataWithDates, tickFormat: '%m/%d' }], + ['when tick Values is given and tick format not given', { data: chartDataWithDates, tickValues }], + ['when tick Values given and tick format is %m/%d/%y', { data: chartDataWithDates, tickFormat: '%m/%d/%y' }], + ['when tick Values given and tick format is %d', { data: chartDataWithDates, tickValues, tickFormat: '%d' }], + ['when tick Values given and tick format is %m', { data: chartDataWithDates, tickValues, tickFormat: '%m' }], + ['when tick Values given and tick format is %m/%y', { data: chartDataWithDates, tickValues, tickFormat: '%m/%y' }], + ]; + + testCases.forEach(([testcase, props]) => { + testWithWait( + `Should render the Area chart with date x-axis data ${testcase}`, + AreaChart, + props, + container => { + // Assert + expect(container).toMatchSnapshot(); + }, + undefined, + undefined, + !(isTimezoneSet(Timezone.UTC) && isTestEnv()), + ); + }); +}); + +describe('Area chart - Subcomponent Area', () => { + testWithoutWait('Should render the Areas with the specified colors', AreaChart, { data: chartData }, container => { + const areas = getById(container, /graph-areaChart/i); + // Assert + expect(areas[0].getAttribute('fill')).toEqual('green'); + expect(areas[1].getAttribute('fill')).toEqual('yellow'); + expect(areas[2].getAttribute('fill')).toEqual('blue'); + }); +}); + +describe('Area chart - Subcomponent legend', () => { + testWithoutWait( + 'Should highlight the corresponding Area on mouse over on legends', + AreaChart, + { data: chartData }, + container => { + const legend = screen.queryByText('legend1'); + expect(legend).toBeDefined(); + fireEvent.mouseOver(legend!); + // Assert + const areas = getById(container, /graph-areaChart/i); + expect(areas[0].getAttribute('fill-opacity')).toEqual('0.7'); + expect(areas[1].getAttribute('fill-opacity')).toEqual('0.1'); + expect(areas[2].getAttribute('fill-opacity')).toEqual('0.1'); + }, + ); + + testWithoutWait( + 'Should reduce opacity of the other lines in Area chat and opacity should be zero for selected Area', + AreaChart, + { data: chartData }, + container => { + const legend = screen.queryByText('legend1'); + expect(legend).toBeDefined(); + fireEvent.mouseOver(legend!); + // Assert + const areaLines = getById(container, /line-areaChart/i); + expect(areaLines[0].getAttribute('opacity')).toEqual('0'); + expect(areaLines[1].getAttribute('opacity')).toEqual('0.1'); + expect(areaLines[2].getAttribute('opacity')).toEqual('0.1'); + }, + ); + + testWithoutWait( + 'Should highlight the corresponding Legend on mouse over on legends', + AreaChart, + { data: chartData }, + container => { + const legend1 = screen.queryByText('legend1'); + expect(legend1).toBeDefined(); + fireEvent.mouseOver(legend1!); + // Assert + expect(screen.queryByText('legend2')).toHaveStyle('opacity: 0.67'); + }, + ); + + testWithoutWait( + 'Should select legend on single mouse click on legends', + AreaChart, + { data: chartData, hideLegend: false }, + container => { + const legend = screen.queryByText('legend1'); + expect(legend).toBeDefined(); + fireEvent.click(legend!); + // Assert + expect(getById(container, /graph-areaChart/i)[1]).toHaveAttribute('fill-opacity', '0.1'); + const firstLegend = screen.queryByText('legend1')?.closest('button'); + expect(firstLegend).toHaveAttribute('aria-selected', 'true'); + expect(firstLegend).toHaveAttribute('tabIndex', '0'); + }, + ); + + testWithoutWait( + 'Should deselect legend on double mouse click on legends', + AreaChart, + { data: chartData, hideLegend: false }, + container => { + const legend = screen.queryByText('legend1'); + expect(legend).toBeDefined(); + + //single click on first legend + fireEvent.click(legend!); + expect(getById(container, /graph-areaChart/i)[1]).toHaveAttribute('fill-opacity', '0.1'); + const firstLegend = screen.queryByText('legend1')?.closest('button'); + expect(firstLegend).toHaveAttribute('aria-selected', 'true'); + expect(firstLegend).toHaveAttribute('tabIndex', '0'); + // double click on same first legend + fireEvent.click(legend!); + // Assert + expect(firstLegend).toHaveAttribute('aria-selected', 'false'); + }, + ); +}); + +describe('Area chart - Subcomponent callout', () => { + testWithWait( + 'Should show the callout over the area on mouse over', + AreaChart, + { data: chartData, calloutProps: { doNotLayer: true } }, + container => { + // Arrange + const areas = getById(container, /graph-areaChart/i); + fireEvent.mouseOver(areas[0]); + // Assert + expect(getById(container, /toolTipcallout/i)).toBeDefined(); + }, + ); + + testWithWait( + 'Should show the stacked callout over the are on mouse over', + AreaChart, + { data: chartData, calloutProps: { doNotLayer: true } }, + container => { + // Arrange + const areas = getById(container, /graph-areaChart/i); + expect(areas).toHaveLength(3); + fireEvent.mouseOver(areas[0]); + // Assert + expect(getByClass(container, /calloutlegendText/i)).toBeDefined(); + expect(getByClass(container, /calloutlegendText/i)).toHaveLength(3); + }, + ); + + testWithWait( + 'Should show the custom callout over the Area on mouse over', + AreaChart, + { + data: chartData, + calloutProps: { doNotLayer: true }, + onRenderCalloutPerDataPoint: (props: IAreaChartProps) => + props ? ( +
+

Custom Callout Content

+
+ ) : null, + }, + container => { + const areas = getById(container, /graph-areaChart/i); + fireEvent.mouseOver(areas[0]); + // Assert + expect(getById(container, /toolTipcallout/i)).toBeDefined(); + }, + ); +}); + +describe('Area chart - Subcomponent xAxis Labels', () => { + testWithWait( + 'Should show the x-axis labels tooltip when hovered', + AreaChart, + { data: chartDataWithDates, showXAxisLablesTooltip: true }, + container => { + const xAxisLabels = getById(container, /showDots/i); + fireEvent.mouseOver(xAxisLabels[0]); + // Assert + expect(getById(container, /showDots/i)[0]!.textContent!).toEqual('Jan ...'); + }, + ); + + testWithWait( + 'Should show rotated x-axis labels', + AreaChart, + { data: chartDataWithDates, rotateXAxisLables: true }, + container => { + // FIXME - Bad check. Not the best way to check result from a third party utility. + // If there are any changes, the value must be manually adjusted to ensure the test passes. + // Assert + expect(getByClass(container, /tick/i)[0].getAttribute('transform')).toContain('translate(54.890243902439025,0)'); + }, + undefined, + undefined, + !(isTimezoneSet(Timezone.UTC) && isTestEnv()), + ); +}); + +describe('Screen resolution', () => { + beforeEach(updateChartWidthAndHeight); + afterEach(sharedAfterEach); + + testWithWait( + 'Should remain unchanged on zoom in', + AreaChart, + { data: chartData, rotateXAxisLables: true, width: 300, height: 300 }, + container => { + // Arrange + global.innerWidth = window.innerWidth / 2; + global.innerHeight = window.innerHeight / 2; + act(() => { + global.dispatchEvent(new Event('resize')); + }); + // Assert + expect(container).toMatchSnapshot(); + }, + ); + + testWithWait( + 'Should remain unchanged on zoom out', + AreaChart, + { data: chartData, rotateXAxisLables: true, width: 300, height: 300 }, + container => { + // Arrange + global.innerWidth = window.innerWidth * 2; + global.innerHeight = window.innerHeight * 2; + act(() => { + global.dispatchEvent(new Event('resize')); + }); + // Assert + expect(container).toMatchSnapshot(); + }, + ); +}); + +describe('AreaChart - Theme', () => { + beforeEach(updateChartWidthAndHeight); + afterEach(sharedAfterEach); + + test('Should reflect theme change', () => { + // Arrange + const { container } = render( + + + , + ); + // Assert + expect(container).toMatchSnapshot(); + }); +}); + +describe('AreaChart - Accessibility tests', () => { + test('Should pass accessibility tests', async () => { + const { container } = render(); + let axeResults; + await act(async () => { + axeResults = await axe(container); + }); + expect(axeResults).toHaveNoViolations(); + }); +}); diff --git a/packages/react-components/react-charts-preview/src/components/AreaChart/__snapshots__/AreaChart.test.tsx.snap b/packages/react-components/react-charts-preview/src/components/AreaChart/__snapshots__/AreaChart.test.tsx.snap new file mode 100644 index 0000000000000..2e5c2a9aadd46 --- /dev/null +++ b/packages/react-components/react-charts-preview/src/components/AreaChart/__snapshots__/AreaChart.test.tsx.snap @@ -0,0 +1,4983 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AreaChart - mouse events Should render callout correctly on mouseover 1`] = ` + +`; + +exports[`AreaChart - mouse events Should render customized callout on mouseover 1`] = ` + +`; + +exports[`AreaChart - mouse events Should render customized callout per stack on mouseover 1`] = ` + +`; + +exports[`AreaChart snapShot testing Should not render circles when optimizeLargeData is true 1`] = ` +