@redsift/charts 9.2.3-patch → 9.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (270) hide show
  1. package/coverage/clover.xml +1096 -0
  2. package/coverage/coverage-final.json +60 -0
  3. package/coverage/lcov-report/base.css +224 -0
  4. package/coverage/lcov-report/block-navigation.js +87 -0
  5. package/coverage/lcov-report/components/Arc/Arc.tsx.html +304 -0
  6. package/coverage/lcov-report/components/Arc/index.html +146 -0
  7. package/coverage/lcov-report/components/Arc/index.ts.html +94 -0
  8. package/coverage/lcov-report/components/Arc/styles.ts.html +208 -0
  9. package/coverage/lcov-report/components/Arcs/Arcs.tsx.html +409 -0
  10. package/coverage/lcov-report/components/Arcs/index.html +146 -0
  11. package/coverage/lcov-report/components/Arcs/index.ts.html +94 -0
  12. package/coverage/lcov-report/components/Arcs/styles.ts.html +106 -0
  13. package/coverage/lcov-report/components/Axis/Axis.tsx.html +754 -0
  14. package/coverage/lcov-report/components/Axis/computeTicks.ts.html +481 -0
  15. package/coverage/lcov-report/components/Axis/index.html +176 -0
  16. package/coverage/lcov-report/components/Axis/index.ts.html +94 -0
  17. package/coverage/lcov-report/components/Axis/styles.ts.html +148 -0
  18. package/coverage/lcov-report/components/Axis/types.ts.html +253 -0
  19. package/coverage/lcov-report/components/Bar/Bar.tsx.html +421 -0
  20. package/coverage/lcov-report/components/Bar/index.html +161 -0
  21. package/coverage/lcov-report/components/Bar/index.ts.html +94 -0
  22. package/coverage/lcov-report/components/Bar/styles.ts.html +247 -0
  23. package/coverage/lcov-report/components/Bar/types.ts.html +178 -0
  24. package/coverage/lcov-report/components/BarChart/BarChart.tsx.html +352 -0
  25. package/coverage/lcov-report/components/BarChart/EmptyBarChart.tsx.html +259 -0
  26. package/coverage/lcov-report/components/BarChart/LoadingBarChart.tsx.html +145 -0
  27. package/coverage/lcov-report/components/BarChart/RenderedBarChart.tsx.html +430 -0
  28. package/coverage/lcov-report/components/BarChart/index.html +206 -0
  29. package/coverage/lcov-report/components/BarChart/index.ts.html +94 -0
  30. package/coverage/lcov-report/components/BarChart/styles.ts.html +190 -0
  31. package/coverage/lcov-report/components/BarChart/utils.ts.html +145 -0
  32. package/coverage/lcov-report/components/BottomAxis/BottomAxis.tsx.html +247 -0
  33. package/coverage/lcov-report/components/BottomAxis/index.html +146 -0
  34. package/coverage/lcov-report/components/BottomAxis/index.ts.html +94 -0
  35. package/coverage/lcov-report/components/BottomAxis/styles.ts.html +139 -0
  36. package/coverage/lcov-report/components/ChartContainer/ChartContainer.tsx.html +361 -0
  37. package/coverage/lcov-report/components/ChartContainer/index.html +146 -0
  38. package/coverage/lcov-report/components/ChartContainer/index.ts.html +94 -0
  39. package/coverage/lcov-report/components/ChartContainer/intl/index.html +116 -0
  40. package/coverage/lcov-report/components/ChartContainer/intl/index.ts.html +106 -0
  41. package/coverage/lcov-report/components/ChartContainer/styles.ts.html +208 -0
  42. package/coverage/lcov-report/components/DataPoint/DataPoint.tsx.html +391 -0
  43. package/coverage/lcov-report/components/DataPoint/index.html +146 -0
  44. package/coverage/lcov-report/components/DataPoint/index.ts.html +94 -0
  45. package/coverage/lcov-report/components/DataPoint/styles.ts.html +109 -0
  46. package/coverage/lcov-report/components/Dot/Dot.tsx.html +232 -0
  47. package/coverage/lcov-report/components/Dot/index.html +146 -0
  48. package/coverage/lcov-report/components/Dot/index.ts.html +94 -0
  49. package/coverage/lcov-report/components/Dot/styles.ts.html +184 -0
  50. package/coverage/lcov-report/components/HorizontalBar/HorizontalBar.tsx.html +541 -0
  51. package/coverage/lcov-report/components/HorizontalBar/index.html +161 -0
  52. package/coverage/lcov-report/components/HorizontalBar/index.ts.html +94 -0
  53. package/coverage/lcov-report/components/HorizontalBar/styles.ts.html +187 -0
  54. package/coverage/lcov-report/components/HorizontalBar/types.ts.html +301 -0
  55. package/coverage/lcov-report/components/HorizontalBarChart/HorizontalBarChart.tsx.html +691 -0
  56. package/coverage/lcov-report/components/HorizontalBarChart/index.html +146 -0
  57. package/coverage/lcov-report/components/HorizontalBarChart/index.ts.html +94 -0
  58. package/coverage/lcov-report/components/HorizontalBarChart/styles.ts.html +196 -0
  59. package/coverage/lcov-report/components/HorizontalBarChart/types.ts.html +301 -0
  60. package/coverage/lcov-report/components/Legend/Legend.tsx.html +268 -0
  61. package/coverage/lcov-report/components/Legend/index.html +146 -0
  62. package/coverage/lcov-report/components/Legend/index.ts.html +94 -0
  63. package/coverage/lcov-report/components/Legend/styles.ts.html +130 -0
  64. package/coverage/lcov-report/components/LegendItem/LegendItem.tsx.html +325 -0
  65. package/coverage/lcov-report/components/LegendItem/index.html +146 -0
  66. package/coverage/lcov-report/components/LegendItem/index.ts.html +94 -0
  67. package/coverage/lcov-report/components/LegendItem/styles.ts.html +205 -0
  68. package/coverage/lcov-report/components/PieChart/EmptyPieChart.tsx.html +343 -0
  69. package/coverage/lcov-report/components/PieChart/LoadingPieChart.tsx.html +145 -0
  70. package/coverage/lcov-report/components/PieChart/PieChart.tsx.html +385 -0
  71. package/coverage/lcov-report/components/PieChart/RenderedPieChart.tsx.html +607 -0
  72. package/coverage/lcov-report/components/PieChart/index.html +221 -0
  73. package/coverage/lcov-report/components/PieChart/index.ts.html +94 -0
  74. package/coverage/lcov-report/components/PieChart/styles.ts.html +370 -0
  75. package/coverage/lcov-report/components/PieChart/types.ts.html +367 -0
  76. package/coverage/lcov-report/components/PieChart/utils.ts.html +199 -0
  77. package/coverage/lcov-report/components/ScatterPlot/EmptyScatterPlot.tsx.html +295 -0
  78. package/coverage/lcov-report/components/ScatterPlot/LoadingScatterPlot.tsx.html +145 -0
  79. package/coverage/lcov-report/components/ScatterPlot/RenderedScatterPlot.tsx.html +1048 -0
  80. package/coverage/lcov-report/components/ScatterPlot/ScatterPlot.tsx.html +367 -0
  81. package/coverage/lcov-report/components/ScatterPlot/index.html +221 -0
  82. package/coverage/lcov-report/components/ScatterPlot/index.ts.html +94 -0
  83. package/coverage/lcov-report/components/ScatterPlot/styles.ts.html +190 -0
  84. package/coverage/lcov-report/components/ScatterPlot/types.ts.html +394 -0
  85. package/coverage/lcov-report/components/ScatterPlot/utils.ts.html +388 -0
  86. package/coverage/lcov-report/core/Arc/Arc.tsx.html +304 -0
  87. package/coverage/lcov-report/core/Arc/index.html +146 -0
  88. package/coverage/lcov-report/core/Arc/index.ts.html +94 -0
  89. package/coverage/lcov-report/core/Arc/styles.ts.html +202 -0
  90. package/coverage/lcov-report/core/Arcs/Arcs.tsx.html +421 -0
  91. package/coverage/lcov-report/core/Arcs/index.html +146 -0
  92. package/coverage/lcov-report/core/Arcs/index.ts.html +94 -0
  93. package/coverage/lcov-report/core/Arcs/styles.ts.html +106 -0
  94. package/coverage/lcov-report/core/Axis/Axis.tsx.html +754 -0
  95. package/coverage/lcov-report/core/Axis/computeTicks.ts.html +481 -0
  96. package/coverage/lcov-report/core/Axis/index.html +176 -0
  97. package/coverage/lcov-report/core/Axis/index.ts.html +94 -0
  98. package/coverage/lcov-report/core/Axis/styles.ts.html +145 -0
  99. package/coverage/lcov-report/core/Axis/types.ts.html +253 -0
  100. package/coverage/lcov-report/core/Bar/Bar.tsx.html +421 -0
  101. package/coverage/lcov-report/core/Bar/index.html +161 -0
  102. package/coverage/lcov-report/core/Bar/index.ts.html +94 -0
  103. package/coverage/lcov-report/core/Bar/styles.ts.html +190 -0
  104. package/coverage/lcov-report/core/Bar/types.ts.html +178 -0
  105. package/coverage/lcov-report/core/BottomAxis/BottomAxis.tsx.html +277 -0
  106. package/coverage/lcov-report/core/BottomAxis/index.html +146 -0
  107. package/coverage/lcov-report/core/BottomAxis/index.ts.html +94 -0
  108. package/coverage/lcov-report/core/BottomAxis/styles.ts.html +139 -0
  109. package/coverage/lcov-report/core/ChartConfig/ChartConfig.ts.html +97 -0
  110. package/coverage/lcov-report/core/ChartConfig/index.html +131 -0
  111. package/coverage/lcov-report/core/ChartConfig/index.ts.html +88 -0
  112. package/coverage/lcov-report/core/ChartContainer/ChartContainer.tsx.html +421 -0
  113. package/coverage/lcov-report/core/ChartContainer/index.html +146 -0
  114. package/coverage/lcov-report/core/ChartContainer/index.ts.html +94 -0
  115. package/coverage/lcov-report/core/ChartContainer/intl/index.html +116 -0
  116. package/coverage/lcov-report/core/ChartContainer/intl/index.ts.html +106 -0
  117. package/coverage/lcov-report/core/ChartContainer/styles.ts.html +205 -0
  118. package/coverage/lcov-report/core/DataPoint/DataPoint.tsx.html +442 -0
  119. package/coverage/lcov-report/core/DataPoint/index.html +146 -0
  120. package/coverage/lcov-report/core/DataPoint/index.ts.html +94 -0
  121. package/coverage/lcov-report/core/DataPoint/styles.ts.html +109 -0
  122. package/coverage/lcov-report/core/Dot/Dot.tsx.html +232 -0
  123. package/coverage/lcov-report/core/Dot/index.html +146 -0
  124. package/coverage/lcov-report/core/Dot/index.ts.html +94 -0
  125. package/coverage/lcov-report/core/Dot/styles.ts.html +184 -0
  126. package/coverage/lcov-report/core/Legend/Legend.tsx.html +277 -0
  127. package/coverage/lcov-report/core/Legend/index.html +146 -0
  128. package/coverage/lcov-report/core/Legend/index.ts.html +94 -0
  129. package/coverage/lcov-report/core/Legend/styles.ts.html +172 -0
  130. package/coverage/lcov-report/core/Point/Point.tsx.html +484 -0
  131. package/coverage/lcov-report/core/Point/index.html +146 -0
  132. package/coverage/lcov-report/core/Point/index.ts.html +94 -0
  133. package/coverage/lcov-report/core/Point/styles.ts.html +169 -0
  134. package/coverage/lcov-report/favicon.png +0 -0
  135. package/coverage/lcov-report/gallery/HorizontalBarChart/HorizontalBarChart.tsx.html +706 -0
  136. package/coverage/lcov-report/gallery/HorizontalBarChart/index.html +161 -0
  137. package/coverage/lcov-report/gallery/HorizontalBarChart/index.ts.html +94 -0
  138. package/coverage/lcov-report/gallery/HorizontalBarChart/styles.ts.html +316 -0
  139. package/coverage/lcov-report/gallery/HorizontalBarChart/types.ts.html +310 -0
  140. package/coverage/lcov-report/gallery/PieChart/PieChart.tsx.html +1252 -0
  141. package/coverage/lcov-report/gallery/PieChart/index.html +161 -0
  142. package/coverage/lcov-report/gallery/PieChart/index.ts.html +94 -0
  143. package/coverage/lcov-report/gallery/PieChart/styles.ts.html +517 -0
  144. package/coverage/lcov-report/gallery/PieChart/types.ts.html +388 -0
  145. package/coverage/lcov-report/gallery/ScatterPlot/index.html +146 -0
  146. package/coverage/lcov-report/gallery/ScatterPlot/index.ts.html +94 -0
  147. package/coverage/lcov-report/gallery/ScatterPlot/styles.ts.html +343 -0
  148. package/coverage/lcov-report/gallery/ScatterPlot/types.ts.html +433 -0
  149. package/coverage/lcov-report/hooks/index.html +176 -0
  150. package/coverage/lcov-report/hooks/index.ts.html +97 -0
  151. package/coverage/lcov-report/hooks/useBrush.tsx.html +430 -0
  152. package/coverage/lcov-report/hooks/useColor.tsx.html +163 -0
  153. package/coverage/lcov-report/hooks/useFormatCategoricalData.tsx.html +289 -0
  154. package/coverage/lcov-report/hooks/useIsEmpty.tsx.html +118 -0
  155. package/coverage/lcov-report/hooks/useZoom.tsx.html +235 -0
  156. package/coverage/lcov-report/index.html +311 -0
  157. package/coverage/lcov-report/prettify.css +1 -0
  158. package/coverage/lcov-report/prettify.js +2 -0
  159. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  160. package/coverage/lcov-report/sorter.js +196 -0
  161. package/coverage/lcov.info +2345 -0
  162. package/coverage/storybook/coverage-storybook.json +79382 -0
  163. package/dist/package.json +98 -0
  164. package/index.ts +1 -0
  165. package/jest.config.js +3 -0
  166. package/package.json +2 -3
  167. package/rollup.config.js +13 -0
  168. package/src/components/Arc/Arc.test.tsx +80 -0
  169. package/src/components/Arc/Arc.tsx +73 -0
  170. package/src/components/Arc/index.ts +3 -0
  171. package/src/components/Arc/styles.ts +41 -0
  172. package/src/components/Arc/types.ts +18 -0
  173. package/src/components/Arcs/Arcs.stories.tsx +177 -0
  174. package/src/components/Arcs/Arcs.tsx +108 -0
  175. package/src/components/Arcs/index.ts +3 -0
  176. package/src/components/Arcs/styles.ts +7 -0
  177. package/src/components/Arcs/types.ts +26 -0
  178. package/src/components/Axis/Axis.stories.tsx +297 -0
  179. package/src/components/Axis/Axis.tsx +223 -0
  180. package/src/components/Axis/computeTicks.ts +132 -0
  181. package/src/components/Axis/index.ts +3 -0
  182. package/src/components/Axis/styles.ts +21 -0
  183. package/src/components/Axis/types.ts +56 -0
  184. package/src/components/Bar/Bar.stories.tsx +152 -0
  185. package/src/components/Bar/Bar.test.tsx +156 -0
  186. package/src/components/Bar/Bar.tsx +112 -0
  187. package/src/components/Bar/index.ts +3 -0
  188. package/src/components/Bar/styles.ts +54 -0
  189. package/src/components/Bar/types.ts +31 -0
  190. package/src/components/BarChart/BarChart.stories.tsx +74 -0
  191. package/src/components/BarChart/BarChart.test.tsx +178 -0
  192. package/src/components/BarChart/BarChart.tsx +89 -0
  193. package/src/components/BarChart/EmptyBarChart.tsx +58 -0
  194. package/src/components/BarChart/LoadingBarChart.tsx +20 -0
  195. package/src/components/BarChart/RenderedBarChart.tsx +115 -0
  196. package/src/components/BarChart/__snapshots__/BarChart.test.tsx.snap +7028 -0
  197. package/src/components/BarChart/index.ts +3 -0
  198. package/src/components/BarChart/styles.ts +35 -0
  199. package/src/components/BarChart/types.ts +58 -0
  200. package/src/components/BarChart/utils.ts +20 -0
  201. package/src/components/ChartContainer/ChartContainer.stories.tsx +81 -0
  202. package/src/components/ChartContainer/ChartContainer.test.tsx +71 -0
  203. package/src/components/ChartContainer/ChartContainer.tsx +92 -0
  204. package/src/components/ChartContainer/index.ts +3 -0
  205. package/src/components/ChartContainer/intl/en-US.json +3 -0
  206. package/src/components/ChartContainer/intl/fr-FR.json +3 -0
  207. package/src/components/ChartContainer/intl/index.ts +7 -0
  208. package/src/components/ChartContainer/styles.ts +41 -0
  209. package/src/components/ChartContainer/types.ts +26 -0
  210. package/src/components/DataPoint/DataPoint.tsx +102 -0
  211. package/src/components/DataPoint/index.ts +3 -0
  212. package/src/components/DataPoint/styles.ts +8 -0
  213. package/src/components/DataPoint/types.ts +33 -0
  214. package/src/components/Dot/Dot.stories.tsx +157 -0
  215. package/src/components/Dot/Dot.test.tsx +136 -0
  216. package/src/components/Dot/Dot.tsx +49 -0
  217. package/src/components/Dot/index.ts +3 -0
  218. package/src/components/Dot/styles.ts +33 -0
  219. package/src/components/Dot/types.ts +16 -0
  220. package/src/components/Legend/Legend.stories.tsx +108 -0
  221. package/src/components/Legend/Legend.tsx +61 -0
  222. package/src/components/Legend/index.ts +3 -0
  223. package/src/components/Legend/styles.ts +15 -0
  224. package/src/components/Legend/types.ts +27 -0
  225. package/src/components/LegendItem/LegendItem.test.tsx +69 -0
  226. package/src/components/LegendItem/LegendItem.tsx +80 -0
  227. package/src/components/LegendItem/index.ts +3 -0
  228. package/src/components/LegendItem/styles.ts +40 -0
  229. package/src/components/LegendItem/types.ts +30 -0
  230. package/src/components/PieChart/EmptyPieChart.tsx +86 -0
  231. package/src/components/PieChart/LoadingPieChart.tsx +20 -0
  232. package/src/components/PieChart/PieChart.stories.tsx +85 -0
  233. package/src/components/PieChart/PieChart.test.tsx +183 -0
  234. package/src/components/PieChart/PieChart.tsx +100 -0
  235. package/src/components/PieChart/RenderedPieChart.tsx +174 -0
  236. package/src/components/PieChart/__snapshots__/PieChart.test.tsx.snap +21020 -0
  237. package/src/components/PieChart/index.ts +3 -0
  238. package/src/components/PieChart/styles.ts +95 -0
  239. package/src/components/PieChart/types.ts +94 -0
  240. package/src/components/PieChart/utils.ts +38 -0
  241. package/src/components/ScatterPlot/EmptyScatterPlot.tsx +70 -0
  242. package/src/components/ScatterPlot/LoadingScatterPlot.tsx +20 -0
  243. package/src/components/ScatterPlot/RenderedScatterPlot.tsx +321 -0
  244. package/src/components/ScatterPlot/ScatterPlot.stories.tsx +101 -0
  245. package/src/components/ScatterPlot/ScatterPlot.test.tsx +61 -0
  246. package/src/components/ScatterPlot/ScatterPlot.tsx +94 -0
  247. package/src/components/ScatterPlot/__snapshots__/ScatterPlot.test.tsx.snap +91712 -0
  248. package/src/components/ScatterPlot/index.ts +3 -0
  249. package/src/components/ScatterPlot/styles.ts +35 -0
  250. package/src/components/ScatterPlot/types.ts +103 -0
  251. package/src/components/ScatterPlot/utils.ts +101 -0
  252. package/src/config.ts +10 -0
  253. package/src/hooks/index.ts +4 -0
  254. package/src/hooks/useBrush.tsx +115 -0
  255. package/src/hooks/useColor.tsx +26 -0
  256. package/src/hooks/useFormatCategoricalData.tsx +68 -0
  257. package/src/hooks/useZoom.tsx +50 -0
  258. package/src/index.ts +15 -0
  259. package/src/scheme.ts +213 -0
  260. package/src/types/data.ts +52 -0
  261. package/src/types/index.ts +5 -0
  262. package/src/types/legend.ts +19 -0
  263. package/src/types/scale.ts +79 -0
  264. package/src/types/size.ts +17 -0
  265. package/src/types/theme.ts +27 -0
  266. package/tsconfig.json +3 -0
  267. /package/{CONTRIBUTING.md → dist/CONTRIBUTING.md} +0 -0
  268. /package/{index.d.ts → dist/index.d.ts} +0 -0
  269. /package/{index.js → dist/index.js} +0 -0
  270. /package/{index.js.map → dist/index.js.map} +0 -0
@@ -0,0 +1,80 @@
1
+ import React, { KeyboardEventHandler, forwardRef } from 'react';
2
+ import classNames from 'classnames';
3
+
4
+ import { Comp, Number, Text, useId } from '@redsift/design-system';
5
+
6
+ import { LegendItemProps } from './types';
7
+ import { StyledLegendItem } from './styles';
8
+ import { monochrome } from '../../scheme';
9
+ import { LabelVariant } from '../../types';
10
+
11
+ const COMPONENT_NAME = 'LegendItem';
12
+ const CLASSNAME = 'redsift-legend-item';
13
+ const DEFAULT_PROPS: Partial<LegendItemProps> = {
14
+ color: monochrome,
15
+ index: 0,
16
+ };
17
+
18
+ export const LegendItem: Comp<LegendItemProps, HTMLLIElement> = forwardRef((props, ref) => {
19
+ const { index, isSelected: propsIsSelected, labelDecorator, onClick, role } = props;
20
+
21
+ const { className, color, data, id: propsId, total, variant, ...forwardedProps } = props;
22
+ const [_id] = useId();
23
+ const id = propsId ?? _id;
24
+
25
+ const text = labelDecorator ? labelDecorator(data) : data.data.key;
26
+ const isEmpty = data.data.value === 0;
27
+
28
+ const isSelectable = role === 'option';
29
+ const isSelected = isSelectable && propsIsSelected === true;
30
+ const isDeselected = isSelectable && propsIsSelected === false;
31
+
32
+ const onKeyDown: KeyboardEventHandler<HTMLLIElement> = (event) => {
33
+ if (onClick) {
34
+ event.stopPropagation();
35
+
36
+ if (event.code === 'Enter' || event.code === 'Space') {
37
+ event.preventDefault();
38
+ onClick(data);
39
+ }
40
+ }
41
+ };
42
+
43
+ return (
44
+ <StyledLegendItem
45
+ {...forwardedProps}
46
+ className={classNames(LegendItem.className, className, `_${index}`, {
47
+ selected: isSelected,
48
+ deselected: isDeselected,
49
+ })}
50
+ color={isDeselected ? 'var(--redsift-color-neutral-lightgrey)' : color}
51
+ ref={ref}
52
+ aria-labelledby={!isEmpty ? `${id}-title` : undefined}
53
+ aria-selected={isSelected ? true : isDeselected ? false : undefined}
54
+ id={id}
55
+ onClick={onClick ? () => onClick(data) : undefined}
56
+ onKeyDown={onClick ? (e) => onKeyDown(e) : undefined}
57
+ role={role ? role : onClick ? 'button' : undefined}
58
+ tabIndex={onClick ? 0 : undefined}
59
+ $clickable={Boolean(onClick)}
60
+ >
61
+ <div />
62
+ {variant === LabelVariant.value ? (
63
+ <>
64
+ <Number as="b" maximumFractionDigits={2} value={data.data.value} variant="inherit" />
65
+ <Text variant="caption">{text}</Text>
66
+ </>
67
+ ) : variant === LabelVariant.percent && total ? (
68
+ <>
69
+ <Number as="b" maximumFractionDigits={2} type="percent" value={data.data.value / total} variant="inherit" />
70
+ <Text variant="caption">{text}</Text>
71
+ </>
72
+ ) : (
73
+ <Text variant="caption">{text}</Text>
74
+ )}
75
+ </StyledLegendItem>
76
+ );
77
+ });
78
+ LegendItem.className = CLASSNAME;
79
+ LegendItem.defaultProps = DEFAULT_PROPS;
80
+ LegendItem.displayName = COMPONENT_NAME;
@@ -0,0 +1,3 @@
1
+ export * from './LegendItem';
2
+ export * from './types';
3
+ export * from './styles';
@@ -0,0 +1,40 @@
1
+ import styled, { css } from 'styled-components';
2
+ import { StyledLegendItemProps } from './types';
3
+
4
+ /**
5
+ * Component style.
6
+ */
7
+ export const StyledLegendItem = styled.li<StyledLegendItemProps>`
8
+ display: flex;
9
+ align-items: center;
10
+ gap: 8px;
11
+ font-size: var(--redsift-typography-caption-font-size);
12
+
13
+ > div {
14
+ height: 16px;
15
+ width: 16px;
16
+ min-width: 16px;
17
+ background-color: ${({ color }) => color};
18
+ }
19
+
20
+ ${({ $clickable }) =>
21
+ $clickable
22
+ ? css`
23
+ cursor: pointer;
24
+
25
+ &:hover,
26
+ &:focus {
27
+ outline: none;
28
+ > div {
29
+ opacity: 0.7;
30
+ }
31
+ }
32
+
33
+ &:focus-visible {
34
+ > div {
35
+ outline: 4px solid var(--redsift-color-default-primary);
36
+ }
37
+ }
38
+ `
39
+ : ''}}
40
+ `;
@@ -0,0 +1,30 @@
1
+ import { ComponentProps } from 'react';
2
+ import { LabelVariant, LegendItemDatum } from '../../types';
3
+ import { DataPointProps, StyledDataPointProps } from '../DataPoint';
4
+
5
+ /**
6
+ * Component props.
7
+ */
8
+ export interface LegendItemProps
9
+ extends Pick<
10
+ DataPointProps<LegendItemDatum>,
11
+ | 'color'
12
+ | 'data'
13
+ | 'id'
14
+ | 'index'
15
+ | 'isSelected'
16
+ | 'labelDecorator'
17
+ | 'onClick'
18
+ | 'previousData'
19
+ | 'role'
20
+ | 'tooltipVariant'
21
+ >,
22
+ Omit<ComponentProps<'li'>, 'onClick' | 'role'> {
23
+ /** Total used to compute percentage. */
24
+ total?: number;
25
+ /** Variant. */
26
+ variant?: LabelVariant;
27
+ }
28
+
29
+ export type StyledLegendItemProps = Pick<StyledDataPointProps, '$clickable'> &
30
+ Omit<LegendItemProps, 'data'>;
@@ -0,0 +1,86 @@
1
+ import React, { forwardRef, RefObject } from 'react';
2
+ import { arc as d3arc } from 'd3';
3
+
4
+ import { ArcDatum } from '../../types';
5
+ import { PieChartProps, PieChartVariant } from './types';
6
+ import { StyledPieChart, StyledPieChartEmptyText } from './styles';
7
+ import { Arcs } from '../Arcs';
8
+ import { empty } from '../../scheme';
9
+ import { sizeToDimension } from './utils';
10
+
11
+ export const EmptyPieChart = forwardRef<HTMLDivElement, PieChartProps>(
12
+ (props, ref) => {
13
+ const {
14
+ className,
15
+ emptyComponent,
16
+ localeText,
17
+ size,
18
+ variant,
19
+ ...forwardedProps
20
+ } = props;
21
+
22
+ const isDonut =
23
+ variant === PieChartVariant.donut ||
24
+ variant === PieChartVariant.spacedDonut;
25
+
26
+ const { width, height, fontSize, innerRadius } = sizeToDimension(size!);
27
+ const externalRadiusPadding = 8;
28
+
29
+ const createArc = d3arc<ArcDatum>()
30
+ .innerRadius(isDonut ? innerRadius : 0)
31
+ .outerRadius(width / 2 - externalRadiusPadding);
32
+
33
+ return (
34
+ <StyledPieChart
35
+ {...forwardedProps}
36
+ className={className}
37
+ ref={ref as RefObject<HTMLDivElement>}
38
+ >
39
+ {emptyComponent ?? (
40
+ <>
41
+ <StyledPieChartEmptyText
42
+ $maxWidth={innerRadius * 2}
43
+ $textSize={fontSize / 2}
44
+ $isDonut={isDonut}
45
+ >
46
+ <span>{localeText?.emptyChartText}</span>
47
+ </StyledPieChartEmptyText>
48
+ <svg width={width} height={height}>
49
+ <Arcs
50
+ arcs={[
51
+ {
52
+ createArc,
53
+ previousData: {
54
+ data: {
55
+ key: '',
56
+ value: 0,
57
+ },
58
+ startAngle: 0,
59
+ endAngle: 0,
60
+ padAngle: 0,
61
+ value: 0,
62
+ index: 0,
63
+ },
64
+ data: {
65
+ data: {
66
+ key: 'No Data',
67
+ value: 0,
68
+ },
69
+ index: 0,
70
+ value: 0,
71
+ startAngle: 0,
72
+ endAngle: 2 * Math.PI,
73
+ padAngle: 0,
74
+ },
75
+ color: empty,
76
+ },
77
+ ]}
78
+ transform={`translate(${width / 2} ${height / 2})`}
79
+ />
80
+ </svg>
81
+ </>
82
+ )}
83
+ </StyledPieChart>
84
+ );
85
+ }
86
+ );
@@ -0,0 +1,20 @@
1
+ import React, { forwardRef, RefObject } from 'react';
2
+
3
+ import { PieChartProps } from './types';
4
+ import { StyledPieChart } from './styles';
5
+
6
+ export const LoadingPieChart = forwardRef<HTMLDivElement, PieChartProps>(
7
+ (props, ref) => {
8
+ const { className, ...forwardedProps } = props;
9
+
10
+ return (
11
+ <StyledPieChart
12
+ {...forwardedProps}
13
+ className={className}
14
+ ref={ref as RefObject<HTMLDivElement>}
15
+ >
16
+ Loading...
17
+ </StyledPieChart>
18
+ );
19
+ }
20
+ );
@@ -0,0 +1,85 @@
1
+ import { Meta, StoryObj } from '@storybook/react';
2
+
3
+ import { PresentationColorPalette } from '@redsift/design-system';
4
+ import { PieChart } from '.';
5
+ import { PieChartLabelVariant, PieChartVariant } from './types';
6
+ import { CategoryData, ChartSize, ChartTheme, ColorTheme, TooltipVariant } from '../../types';
7
+
8
+ const themes = {
9
+ ...ColorTheme,
10
+ 'success-warning-danger': {
11
+ success: 'Bread',
12
+ warning: 'Tea',
13
+ danger: 'Coffee',
14
+ },
15
+ 'success-warning-danger-neutral': {
16
+ success: 'Bread',
17
+ warning: 'Tea',
18
+ danger: 'Coffee',
19
+ neutral: 'Pastry',
20
+ },
21
+ custom: {
22
+ Coffee: PresentationColorPalette.purple,
23
+ Bread: PresentationColorPalette.pink,
24
+ Tea: PresentationColorPalette.aqua,
25
+ },
26
+ };
27
+
28
+ const meta: Meta<typeof PieChart> = {
29
+ title: 'Charts/PieChart',
30
+ component: PieChart as any,
31
+ argTypes: {
32
+ variant: {
33
+ options: Object.values(PieChartVariant),
34
+ control: { type: 'select' },
35
+ },
36
+ labelVariant: {
37
+ options: Object.values(PieChartLabelVariant),
38
+ control: { type: 'select' },
39
+ },
40
+ tooltipVariant: {
41
+ options: Object.values(TooltipVariant),
42
+ control: { type: 'select' },
43
+ },
44
+ size: {
45
+ options: Object.values(ChartSize),
46
+ control: { type: 'select' },
47
+ },
48
+ theme: {
49
+ options: Object.keys(themes),
50
+ mapping: themes,
51
+ control: {
52
+ type: 'select',
53
+ },
54
+ },
55
+ },
56
+ };
57
+ export default meta;
58
+ type Story = StoryObj<typeof PieChart>;
59
+
60
+ const data: CategoryData = [
61
+ { key: 'Cookies', value: 540 },
62
+ { key: 'Coffee', value: 5471 },
63
+ { key: 'Bread', value: 3325 },
64
+ { key: 'Tea', value: 1435 },
65
+ { key: 'Pastry', value: 856 },
66
+ { key: 'Sandwich', value: 771 },
67
+ { key: 'Croissant', value: 771 },
68
+ { key: 'Hot chocolate', value: 588 },
69
+ { key: 'Toast', value: 318 },
70
+ ];
71
+
72
+ export const Controls: Story = {
73
+ args: {
74
+ title: 'Pie Chart',
75
+ variant: 'spacedDonut',
76
+ labelVariant: 'externalLabelValue',
77
+ tooltipVariant: 'value',
78
+ size: 'medium',
79
+ theme: 'success-warning-danger-neutral' as ChartTheme,
80
+ caping: 4,
81
+ others: true,
82
+ onSliceClick: (datum) => console.log(datum),
83
+ data: data,
84
+ },
85
+ };
@@ -0,0 +1,183 @@
1
+ import React from 'react';
2
+ import { render, screen, waitFor } from '@testing-library/react';
3
+ import userEvent from '@testing-library/user-event';
4
+
5
+ import { composeStory } from '@storybook/react';
6
+ import Meta, { Controls } from './PieChart.stories';
7
+
8
+ import { PieChart } from './PieChart';
9
+ import { PieChartLabelVariant, PieChartVariant } from './types';
10
+ import { ChartSize, ChartTheme, ColorTheme, TooltipVariant } from '../../types';
11
+ import { PresentationColorPalette } from '@redsift/design-system';
12
+
13
+ jest.mock('@redsift/design-system', () => ({
14
+ ...jest.requireActual('@redsift/design-system'),
15
+ useId: () => ['123'],
16
+ }));
17
+
18
+ const themes = {
19
+ ...ColorTheme,
20
+ 'success-warning-danger': {
21
+ success: 'Bread',
22
+ warning: 'Tea',
23
+ danger: 'Coffee',
24
+ },
25
+ 'success-warning-danger-neutral': {
26
+ success: 'Bread',
27
+ warning: 'Tea',
28
+ danger: 'Coffee',
29
+ neutral: 'Pastry',
30
+ },
31
+ custom: {
32
+ Coffee: PresentationColorPalette.purple,
33
+ Bread: PresentationColorPalette.pink,
34
+ Tea: PresentationColorPalette.aqua,
35
+ },
36
+ };
37
+
38
+ const data = [
39
+ { key: 'Dogs', value: 300 },
40
+ { key: 'Cats', value: 500 },
41
+ { key: 'Fish', value: 150 },
42
+ ];
43
+
44
+ describe('PieChart', () => {
45
+ const onClickSpy = jest.fn();
46
+
47
+ afterEach(() => {
48
+ onClickSpy.mockClear();
49
+ });
50
+
51
+ it('should match snapshot', () => {
52
+ const ComposedStory = composeStory(Controls, Meta);
53
+ const tree = render(
54
+ <>
55
+ {Object.values(PieChartVariant).map((variant, i) =>
56
+ Object.values(ChartSize).map((size, j) =>
57
+ Object.values(PieChartLabelVariant).map((labelVariant, k) => (
58
+ <ComposedStory
59
+ key={`${variant}-${size}-${labelVariant}-${Object.values(TooltipVariant)[k]}`}
60
+ variant={variant}
61
+ labelVariant={labelVariant}
62
+ tooltipVariant={Object.values(TooltipVariant)[k]}
63
+ size={size}
64
+ theme={
65
+ Object.keys(themes)[
66
+ (i * Object.values(ChartSize).length + j * Object.values(PieChartLabelVariant).length + k) %
67
+ Object.keys(themes).length
68
+ ] as ChartTheme
69
+ }
70
+ />
71
+ ))
72
+ )
73
+ )}
74
+ </>
75
+ );
76
+ expect(tree).toMatchSnapshot();
77
+ });
78
+
79
+ it('handles default', () => {
80
+ const { queryByText } = render(<PieChart aria-label="Chart" data={data} />);
81
+
82
+ expect(queryByText('Cats')).toBe(null);
83
+ expect(queryByText('Dogs')).toBe(null);
84
+ expect(queryByText('Fish')).toBe(null);
85
+ });
86
+
87
+ it('handles internal labels', () => {
88
+ const { getByText } = render(
89
+ <PieChart aria-label="Chart" data={data} labelVariant={PieChartLabelVariant.internal} />
90
+ );
91
+
92
+ expect(getByText('Cats')).toBeVisible();
93
+ expect(getByText('Dogs')).toBeVisible();
94
+ });
95
+
96
+ it('supports custom className', () => {
97
+ const tree = render(<PieChart aria-label="Chart" data={data} className="test" />);
98
+ const component = tree.asFragment().firstChild;
99
+
100
+ expect(component).toHaveAttribute('class', expect.stringContaining('test'));
101
+ });
102
+
103
+ it('supports custom data attributes', () => {
104
+ const tree = render(<PieChart aria-label="Chart" data={data} data-testid="test" />);
105
+ const component = tree.asFragment().firstChild;
106
+ expect(component).toHaveAttribute('data-testid', 'test');
107
+ });
108
+
109
+ it('can be labelled using a title', () => {
110
+ const { getByLabelText } = render(<PieChart data={data} title="Label" />);
111
+ const component = getByLabelText('Label');
112
+ expect(component).toHaveAttribute('aria-labelledby', 'id123__title');
113
+ });
114
+
115
+ it('can be labelled using an aria-label', () => {
116
+ const { getByLabelText } = render(<PieChart data={data} aria-label="Label" />);
117
+ const component = getByLabelText('Label');
118
+ expect(component).toHaveAttribute('aria-label', 'Label');
119
+ });
120
+
121
+ it('can be labelled using an aria-labelledby', () => {
122
+ const { getByLabelText } = render(
123
+ <>
124
+ <h1 id="123">Label</h1>
125
+ <PieChart data={data} aria-labelledby="123" />
126
+ </>
127
+ );
128
+ const component = getByLabelText('Label');
129
+ expect(component).toHaveAttribute('aria-labelledby', '123');
130
+ });
131
+
132
+ it('supports click on the pie slices', async () => {
133
+ const user = userEvent.setup();
134
+ const { getAllByRole } = render(<PieChart aria-label="Chart" data={data} onSliceClick={onClickSpy} />);
135
+
136
+ const slices = getAllByRole('button');
137
+ expect(slices.length).toBe(3);
138
+
139
+ await user.click(slices[0]);
140
+ await user.click(slices[1]);
141
+ await user.click(slices[2]);
142
+
143
+ expect(onClickSpy).toHaveBeenCalledTimes(3);
144
+ });
145
+
146
+ it('supports keydown on the pie slices', async () => {
147
+ const { getAllByRole } = render(<PieChart aria-label="Chart" data={data} onSliceClick={onClickSpy} />);
148
+
149
+ const slices = getAllByRole('button');
150
+ expect(slices.length).toBe(3);
151
+
152
+ userEvent.keyboard('[Tab]');
153
+ userEvent.keyboard('[Enter]');
154
+ userEvent.keyboard('[Tab]');
155
+ userEvent.keyboard('[Space]');
156
+ userEvent.keyboard('[Tab]');
157
+ userEvent.keyboard('[Enter]');
158
+
159
+ expect(onClickSpy).toHaveBeenCalledTimes(3);
160
+ });
161
+
162
+ it('shows a tooltip when hovering on the pie slices ', async () => {
163
+ const user = userEvent.setup();
164
+ const { getAllByRole } = render(
165
+ <PieChart
166
+ aria-label="Chart"
167
+ data={data}
168
+ // The onSliceClick gives the slice the role "button" which is then used to access the slices.
169
+ onSliceClick={onClickSpy}
170
+ />
171
+ );
172
+
173
+ const slices = getAllByRole('button');
174
+ expect(slices.length).toBe(3);
175
+
176
+ await user.hover(slices[0]);
177
+ await waitFor(() => {
178
+ expect(screen.getByRole('tooltip', { exact: true })).toBeInTheDocument();
179
+ });
180
+ const tooltip = screen.getByRole('tooltip');
181
+ expect(tooltip.textContent).toContain('Cats');
182
+ });
183
+ });
@@ -0,0 +1,100 @@
1
+ import React, { forwardRef } from 'react';
2
+ import classNames from 'classnames';
3
+
4
+ import { Comp, useId } from '@redsift/design-system';
5
+
6
+ import { ChartSize, ColorTheme, TooltipVariant } from '../../types';
7
+ import { PieChartLabelVariant, PieChartProps, PieChartVariant } from './types';
8
+ import { LoadingPieChart } from './LoadingPieChart';
9
+ import { EmptyPieChart } from './EmptyPieChart';
10
+ import { RenderedPieChart } from './RenderedPieChart';
11
+
12
+ const COMPONENT_NAME = 'PieChart';
13
+ const CLASSNAME = 'redsift-piechart';
14
+ const DEFAULT_PROPS: Partial<PieChartProps> = {
15
+ isSliceSelected: () => true,
16
+ labelVariant: PieChartLabelVariant.none,
17
+ others: true,
18
+ size: ChartSize.medium,
19
+ theme: ColorTheme.default,
20
+ variant: PieChartVariant.plain,
21
+ tooltipVariant: TooltipVariant.value,
22
+ localeText: {
23
+ emptyChartText: 'No Data',
24
+ },
25
+ };
26
+
27
+ export const PieChart: Comp<PieChartProps, HTMLDivElement> = forwardRef((props, ref) => {
28
+ const {
29
+ caping,
30
+ chartRef,
31
+ className,
32
+ data: propsData,
33
+ emptyComponent,
34
+ id: propsId,
35
+ isSliceSelected,
36
+ labelDecorator,
37
+ labelVariant,
38
+ localeText,
39
+ middleText,
40
+ onSliceClick,
41
+ others,
42
+ size,
43
+ sliceRole,
44
+ subtext,
45
+ text,
46
+ theme,
47
+ tooltipVariant,
48
+ variant,
49
+ ...forwardedProps
50
+ } = props;
51
+ const [_id] = useId();
52
+ const id = propsId ?? _id;
53
+
54
+ if (propsData === undefined || propsData === null) {
55
+ return <LoadingPieChart id={id} {...forwardedProps} ref={ref} />;
56
+ }
57
+
58
+ if (propsData.length === 0) {
59
+ return (
60
+ <EmptyPieChart
61
+ data={propsData}
62
+ emptyComponent={emptyComponent}
63
+ id={id}
64
+ localeText={localeText}
65
+ size={size}
66
+ variant={variant}
67
+ {...forwardedProps}
68
+ ref={ref}
69
+ />
70
+ );
71
+ }
72
+
73
+ return (
74
+ <RenderedPieChart
75
+ caping={caping}
76
+ chartRef={chartRef}
77
+ className={classNames(PieChart.className, className)}
78
+ data={propsData}
79
+ id={id}
80
+ isSliceSelected={isSliceSelected}
81
+ labelDecorator={labelDecorator}
82
+ labelVariant={labelVariant}
83
+ middleText={middleText}
84
+ onSliceClick={onSliceClick}
85
+ others={others}
86
+ size={size}
87
+ sliceRole={sliceRole}
88
+ subtext={subtext}
89
+ text={text}
90
+ theme={theme}
91
+ tooltipVariant={tooltipVariant}
92
+ variant={variant}
93
+ {...forwardedProps}
94
+ ref={ref}
95
+ />
96
+ );
97
+ });
98
+ PieChart.className = CLASSNAME;
99
+ PieChart.defaultProps = DEFAULT_PROPS;
100
+ PieChart.displayName = COMPONENT_NAME;