@centreon/ui 24.4.1-sync-release-34022.1 → 24.4.1-test-code-coverage.0

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 (220) hide show
  1. package/package.json +48 -40
  2. package/src/ActionsList/ActionsList.styles.ts +40 -71
  3. package/src/Button/Icon/index.stories.tsx +1 -1
  4. package/src/Button/Icon/index.tsx +1 -1
  5. package/src/Dashboard/Dashboard.styles.ts +6 -5
  6. package/src/Dialog/Confirm/index.tsx +10 -2
  7. package/src/Dialog/UnsavedChanges/index.tsx +21 -20
  8. package/src/Dialog/UnsavedChanges/translatedLabels.ts +4 -6
  9. package/src/Dialog/index.tsx +8 -1
  10. package/src/Form/Inputs/CheckboxGroup.tsx +4 -1
  11. package/src/Form/Inputs/Text.tsx +3 -1
  12. package/src/Form/Inputs/models.ts +1 -0
  13. package/src/Graph/BarStack/BarStack.cypress.spec.tsx +139 -0
  14. package/src/Graph/BarStack/BarStack.stories.tsx +123 -0
  15. package/src/Graph/BarStack/BarStack.styles.ts +37 -0
  16. package/src/Graph/BarStack/BarStack.tsx +14 -0
  17. package/src/Graph/BarStack/ResponsiveBarStack.tsx +209 -0
  18. package/src/Graph/BarStack/index.ts +1 -0
  19. package/src/Graph/BarStack/models.ts +19 -0
  20. package/src/Graph/BarStack/useResponsiveBarStack.ts +131 -0
  21. package/src/Graph/Gauge/Gauge.cypress.spec.tsx +102 -0
  22. package/src/Graph/Gauge/Gauge.tsx +1 -1
  23. package/src/Graph/HeatMap/HeatMap.cypress.spec.tsx +145 -0
  24. package/src/Graph/HeatMap/HeatMap.stories.tsx +0 -25
  25. package/src/Graph/HeatMap/ResponsiveHeatMap.tsx +8 -2
  26. package/src/Graph/Legend/Legend.tsx +21 -0
  27. package/src/Graph/Legend/index.ts +1 -0
  28. package/src/Graph/Legend/models.ts +11 -0
  29. package/src/Graph/LineChart/BasicComponents/Lines/Threshold/index.tsx +4 -5
  30. package/src/Graph/LineChart/BasicComponents/ThresholdLine.tsx +3 -1
  31. package/src/Graph/LineChart/Header/index.tsx +3 -31
  32. package/src/Graph/LineChart/InteractiveComponents/AnchorPoint/useTickGraph.ts +9 -11
  33. package/src/Graph/LineChart/InteractiveComponents/Annotations/Annotation/index.tsx +3 -2
  34. package/src/Graph/LineChart/InteractiveComponents/GraphValueTooltip/GraphValueTooltip.tsx +68 -0
  35. package/src/Graph/LineChart/InteractiveComponents/GraphValueTooltip/useGraphValueTooltip.ts +27 -0
  36. package/src/Graph/LineChart/InteractiveComponents/GraphValueTooltip/useGraphValueTooltipStyles.ts +31 -0
  37. package/src/Graph/LineChart/InteractiveComponents/index.tsx +132 -17
  38. package/src/Graph/LineChart/InteractiveComponents/interactionWithGraphAtoms.ts +7 -27
  39. package/src/Graph/LineChart/Legend/Legend.styles.ts +5 -9
  40. package/src/Graph/LineChart/Legend/LegendHeader.tsx +10 -22
  41. package/src/Graph/LineChart/Legend/index.tsx +17 -55
  42. package/src/Graph/LineChart/LineChart.cypress.spec.tsx +91 -0
  43. package/src/Graph/LineChart/LineChart.styles.ts +8 -0
  44. package/src/Graph/LineChart/LineChart.tsx +106 -116
  45. package/src/Graph/LineChart/LoadingSkeleton.tsx +2 -2
  46. package/src/Graph/LineChart/index.tsx +6 -7
  47. package/src/Graph/LineChart/mockedData/lastDayWithIncompleteValues.json +1320 -0
  48. package/src/Graph/LineChart/mockedData/lastDayWithNullValues.json +1314 -0
  49. package/src/Graph/LineChart/models.ts +12 -1
  50. package/src/Graph/PieChart/PieChart.cypress.spec.tsx +154 -0
  51. package/src/Graph/PieChart/PieChart.stories.tsx +194 -0
  52. package/src/Graph/PieChart/PieChart.styles.ts +39 -0
  53. package/src/Graph/PieChart/PieChart.tsx +14 -0
  54. package/src/Graph/PieChart/ResponsivePie.tsx +243 -0
  55. package/src/Graph/PieChart/index.ts +1 -0
  56. package/src/Graph/PieChart/models.ts +19 -0
  57. package/src/Graph/PieChart/useResponsivePie.ts +81 -0
  58. package/src/Graph/SingleBar/SingleBar.cypress.spec.tsx +121 -0
  59. package/src/Graph/Text/Text.cypress.spec.tsx +101 -0
  60. package/src/Graph/Text/Text.styles.ts +12 -1
  61. package/src/Graph/Text/Text.tsx +17 -12
  62. package/src/Graph/Tree/DescendantNodes.tsx +89 -0
  63. package/src/Graph/Tree/Links.tsx +77 -0
  64. package/src/Graph/Tree/StandaloneTree.tsx +32 -0
  65. package/src/Graph/Tree/Tree.cypress.spec.tsx +195 -0
  66. package/src/Graph/Tree/Tree.stories.tsx +160 -0
  67. package/src/Graph/Tree/Tree.tsx +116 -0
  68. package/src/Graph/Tree/constants.ts +2 -0
  69. package/src/Graph/Tree/index.ts +4 -0
  70. package/src/Graph/Tree/models.ts +55 -0
  71. package/src/Graph/Tree/stories/contents.tsx +164 -0
  72. package/src/Graph/Tree/stories/datas.ts +305 -0
  73. package/src/Graph/Tree/utils.ts +49 -0
  74. package/src/Graph/common/testUtils.ts +71 -0
  75. package/src/Graph/common/timeSeries/index.ts +50 -12
  76. package/src/Graph/common/utils.ts +19 -0
  77. package/src/Graph/index.ts +4 -0
  78. package/src/InputField/Number/Number.cypress.spec.tsx +85 -0
  79. package/src/InputField/Number/Number.stories.tsx +66 -0
  80. package/src/InputField/Number/Number.tsx +74 -0
  81. package/src/InputField/Search/index.tsx +2 -2
  82. package/src/InputField/Select/Autocomplete/Multi/index.tsx +4 -2
  83. package/src/InputField/Select/Autocomplete/index.tsx +10 -3
  84. package/src/InputField/Select/IconPopover/index.tsx +1 -1
  85. package/src/InputField/Select/index.tsx +14 -1
  86. package/src/InputField/Text/index.tsx +38 -38
  87. package/src/Listing/ActionBar/index.tsx +10 -10
  88. package/src/Listing/Cell/DataCell.styles.ts +3 -0
  89. package/src/Listing/Cell/DataCell.tsx +8 -4
  90. package/src/Listing/Listing.cypress.spec.tsx +217 -33
  91. package/src/Listing/Listing.styles.ts +3 -5
  92. package/src/Listing/Row/Row.tsx +7 -3
  93. package/src/Listing/index.stories.tsx +25 -2
  94. package/src/Listing/index.test.tsx +1 -1
  95. package/src/Listing/index.tsx +202 -143
  96. package/src/Listing/models.ts +1 -0
  97. package/src/Listing/useStyleTable.ts +1 -0
  98. package/src/Panel/index.tsx +1 -1
  99. package/src/PopoverMenu/index.tsx +6 -5
  100. package/src/ThemeProvider/index.tsx +3 -0
  101. package/src/TimePeriods/CustomTimePeriod/CompactCustomTimePeriod.styles.ts +6 -7
  102. package/src/TimePeriods/ResolutionTimePeriod.cypress.spec.tsx +12 -9
  103. package/src/Typography/FluidTypography/FluidTypography.cypress.spec.tsx +27 -0
  104. package/src/Typography/FluidTypography/index.stories.tsx +2 -2
  105. package/src/Typography/FluidTypography/index.tsx +21 -28
  106. package/src/api/index.ts +3 -3
  107. package/src/api/useGraphQuery/index.ts +26 -5
  108. package/src/api/useGraphQuery/models.ts +5 -0
  109. package/src/api/useMutationQuery/index.test.ts +4 -4
  110. package/src/api/useMutationQuery/index.ts +24 -13
  111. package/src/components/CollapsibleItem/CollapsibleItem.cypress.spec.tsx +76 -0
  112. package/src/components/CollapsibleItem/CollapsibleItem.stories.tsx +26 -0
  113. package/src/components/CollapsibleItem/CollapsibleItem.tsx +43 -14
  114. package/src/components/CollapsibleItem/useCollapsibleItemStyles.ts +24 -1
  115. package/src/components/DataTable/DataTable.cypress.spec.tsx +14 -33
  116. package/src/components/DataTable/Item/DataTableItem.tsx +4 -60
  117. package/src/components/Form/{AccessRightsV2 → AccessRights}/AccessRights.cypress.spec.tsx +36 -13
  118. package/src/components/Form/{AccessRightsV2 → AccessRights}/ShareInput/ContactSwitch.tsx +11 -3
  119. package/src/components/Form/{AccessRightsV2 → AccessRights}/ShareInput/ShareInput.styles.ts +8 -0
  120. package/src/components/Form/{AccessRightsV2 → AccessRights}/ShareInput/ShareInput.tsx +1 -0
  121. package/src/components/Form/{AccessRightsV2 → AccessRights}/ShareInput/useShareInput.tsx +4 -0
  122. package/src/components/Form/{AccessRightsV2 → AccessRights}/models.ts +1 -0
  123. package/src/components/Form/{AccessRightsV2 → AccessRights}/storiesData.ts +23 -22
  124. package/src/components/Form/Dashboard/DashboardDuplicationForm.tsx +85 -0
  125. package/src/components/Form/Dashboard/index.ts +1 -0
  126. package/src/components/Form/FormActions.tsx +7 -2
  127. package/src/components/Form/index.ts +2 -2
  128. package/src/components/ItemComposition/Item.tsx +1 -1
  129. package/src/components/ItemComposition/ItemComposition.cypress.spec.tsx +113 -0
  130. package/src/components/ItemComposition/ItemComposition.stories.tsx +14 -0
  131. package/src/components/ItemComposition/ItemComposition.styles.ts +36 -3
  132. package/src/components/ItemComposition/ItemComposition.tsx +41 -17
  133. package/src/components/List/Item/ListItem.tsx +3 -3
  134. package/src/components/Modal/ConfirmationModal/ConfirmationModal.cypress.spec.tsx +168 -0
  135. package/src/components/Modal/ConfirmationModal/ConfirmationModal.stories.tsx +62 -0
  136. package/src/components/Modal/ConfirmationModal/ConfirmationModal.tsx +87 -0
  137. package/src/components/Modal/Modal.styles.ts +8 -3
  138. package/src/components/Modal/index.ts +2 -0
  139. package/src/components/Tooltip/ConfirmationTooltip/ConfirmationTooltip.stories.tsx +3 -3
  140. package/src/components/Tooltip/ConfirmationTooltip/ConfirmationTooltip.tsx +1 -1
  141. package/src/components/Tooltip/ConfirmationTooltip/models.ts +1 -1
  142. package/src/components/Zoom/Minimap.tsx +129 -0
  143. package/src/components/Zoom/Zoom.cypress.spec.tsx +246 -0
  144. package/src/components/Zoom/Zoom.stories.tsx +115 -0
  145. package/src/components/Zoom/Zoom.styles.tsx +68 -0
  146. package/src/components/Zoom/Zoom.tsx +64 -0
  147. package/src/components/Zoom/ZoomContent.tsx +170 -0
  148. package/src/components/Zoom/constants.ts +2 -0
  149. package/src/components/Zoom/localPoint.ts +51 -0
  150. package/src/components/Zoom/models.ts +25 -0
  151. package/src/components/Zoom/useMinimap.ts +156 -0
  152. package/src/components/Zoom/useZoom.ts +70 -0
  153. package/src/components/Zoom/utils.ts +55 -0
  154. package/src/components/index.ts +1 -0
  155. package/src/index.ts +1 -0
  156. package/src/utils/index.ts +3 -0
  157. package/src/utils/resourcesStatusURL.ts +166 -0
  158. package/src/utils/useFullscreen/Fullscreen.cypress.spec.tsx +130 -0
  159. package/src/utils/useFullscreen/atoms.ts +3 -0
  160. package/src/utils/useFullscreen/index.ts +2 -0
  161. package/src/utils/useFullscreen/translatedLabels.ts +1 -0
  162. package/src/utils/useFullscreen/useFullscreen.ts +73 -0
  163. package/src/utils/useFullscreen/useFullscreenListener.ts +62 -0
  164. package/src/utils/useInfiniteScrollListing.ts +4 -1
  165. package/src/Graph/LineChart/BasicComponents/LoadingProgress.tsx +0 -46
  166. package/src/Graph/LineChart/InteractiveComponents/AnchorPoint/TooltipAnchorPoint.tsx +0 -96
  167. package/src/Graph/LineChart/InteractiveComponents/AnchorPoint/useTooltipAnchorPoint.ts +0 -107
  168. package/src/Graph/LineChart/Legend/InteractiveValue.tsx +0 -22
  169. package/src/Graph/LineChart/Legend/useInteractiveValues.ts +0 -99
  170. package/src/Typography/FluidTypography/useFluidResizeObserver.ts +0 -56
  171. package/src/components/Form/AccessRights/AccessRights.resource.ts +0 -45
  172. package/src/components/Form/AccessRights/AccessRightsForm.stories.tsx +0 -59
  173. package/src/components/Form/AccessRights/AccessRightsForm.styles.ts +0 -21
  174. package/src/components/Form/AccessRights/AccessRightsForm.tsx +0 -67
  175. package/src/components/Form/AccessRights/AccessRightsFormActions.tsx +0 -80
  176. package/src/components/Form/AccessRights/Input/AddAction.tsx +0 -31
  177. package/src/components/Form/AccessRights/Input/ContactAccessRightInput.stories.tsx +0 -54
  178. package/src/components/Form/AccessRights/Input/ContactAccessRightInput.tsx +0 -72
  179. package/src/components/Form/AccessRights/Input/ContactAccessRightsInput.styles.ts +0 -22
  180. package/src/components/Form/AccessRights/Input/ContactInputField.tsx +0 -105
  181. package/src/components/Form/AccessRights/Input/RoleInputField.tsx +0 -29
  182. package/src/components/Form/AccessRights/List/ContactAccessRightsList.stories.tsx +0 -97
  183. package/src/components/Form/AccessRights/List/ContactAccessRightsList.styles.ts +0 -71
  184. package/src/components/Form/AccessRights/List/ContactAccessRightsList.tsx +0 -51
  185. package/src/components/Form/AccessRights/List/ContactAccessRightsListItem.stories.tsx +0 -116
  186. package/src/components/Form/AccessRights/List/ContactAccessRightsListItem.tsx +0 -118
  187. package/src/components/Form/AccessRights/List/ContactAccessRightsListItemSkeleton.tsx +0 -26
  188. package/src/components/Form/AccessRights/List/ContactAccessRightsListSkeleton.tsx +0 -28
  189. package/src/components/Form/AccessRights/Stats/AccessRightsStats.styles.ts +0 -18
  190. package/src/components/Form/AccessRights/Stats/AccessRightsStats.tsx +0 -41
  191. package/src/components/Form/AccessRights/__fixtures__/contactAccessRight.mock.ts +0 -54
  192. package/src/components/Form/AccessRights/common/GroupLabel.styles.ts +0 -18
  193. package/src/components/Form/AccessRights/common/GroupLabel.tsx +0 -15
  194. package/src/components/Form/AccessRights/common/Input.styles.ts +0 -48
  195. package/src/components/Form/AccessRights/common/RoleInputSelect.styles.ts +0 -11
  196. package/src/components/Form/AccessRights/common/RoleInputSelect.tsx +0 -57
  197. package/src/components/Form/AccessRights/index.ts +0 -3
  198. package/src/components/Form/AccessRights/useAccessRightsForm.test.tsx +0 -531
  199. package/src/components/Form/AccessRights/useAccessRightsForm.tsx +0 -282
  200. package/src/components/Form/AccessRights/useAccessRightsForm.utils.ts +0 -41
  201. /package/src/components/Form/{AccessRightsV2 → AccessRights}/AccessRights.stories.tsx +0 -0
  202. /package/src/components/Form/{AccessRightsV2 → AccessRights}/AccessRights.styles.ts +0 -0
  203. /package/src/components/Form/{AccessRightsV2 → AccessRights}/AccessRights.tsx +0 -0
  204. /package/src/components/Form/{AccessRightsV2 → AccessRights}/Actions/Actions.styles.ts +0 -0
  205. /package/src/components/Form/{AccessRightsV2 → AccessRights}/Actions/Actions.tsx +0 -0
  206. /package/src/components/Form/{AccessRightsV2 → AccessRights}/Actions/useActions.ts +0 -0
  207. /package/src/components/Form/{AccessRightsV2 → AccessRights}/List/Item.tsx +0 -0
  208. /package/src/components/Form/{AccessRightsV2 → AccessRights}/List/List.styles.tsx +0 -0
  209. /package/src/components/Form/{AccessRightsV2 → AccessRights}/List/List.tsx +0 -0
  210. /package/src/components/Form/{AccessRightsV2 → AccessRights}/List/ListItemSkeleton.tsx +0 -0
  211. /package/src/components/Form/{AccessRightsV2 → AccessRights}/List/ListSkeleton.tsx +0 -0
  212. /package/src/components/Form/{AccessRightsV2 → AccessRights}/List/RemoveAccessRight.tsx +0 -0
  213. /package/src/components/Form/{AccessRightsV2 → AccessRights}/List/StateChip.tsx +0 -0
  214. /package/src/components/Form/{AccessRightsV2 → AccessRights}/List/useItem.ts +0 -0
  215. /package/src/components/Form/{AccessRightsV2 → AccessRights}/Provider.tsx +0 -0
  216. /package/src/components/Form/{AccessRightsV2 → AccessRights}/Stats/Stats.tsx +0 -0
  217. /package/src/components/Form/{AccessRightsV2 → AccessRights}/atoms.ts +0 -0
  218. /package/src/components/Form/{AccessRightsV2 → AccessRights}/common/RoleSelectField.styles.tsx +0 -0
  219. /package/src/components/Form/{AccessRightsV2 → AccessRights}/common/RoleSelectField.tsx +0 -0
  220. /package/src/components/Form/{AccessRightsV2 → AccessRights}/useAccessRightsInitValues.ts +0 -0
@@ -88,7 +88,6 @@ export interface LineChartProps {
88
88
  displayAnchor?: DisplayAnchor;
89
89
  header?: LineChartHeader;
90
90
  height?: number | null;
91
- loading: boolean;
92
91
  timeShiftZones?: InteractedZone;
93
92
  tooltip?: Tooltip;
94
93
  width: number;
@@ -144,3 +143,15 @@ export interface GetDate {
144
143
  timeSeries: Array<TimeValue>;
145
144
  xScale: ScaleLinear<number, number>;
146
145
  }
146
+
147
+ export interface GraphTooltipData {
148
+ date: string;
149
+ highlightedMetricId: number | null;
150
+ metrics: Array<{
151
+ color: string;
152
+ id: number;
153
+ name: string;
154
+ unit: string;
155
+ value: number;
156
+ }>;
157
+ }
@@ -0,0 +1,154 @@
1
+ import numeral from 'numeral';
2
+
3
+ import PieChart from './PieChart';
4
+ import { ArcType, PieProps } from './models';
5
+
6
+ const defaultData = [
7
+ { color: '#88B922', label: 'Ok', value: 148 },
8
+ { color: '#999999', label: 'Unknown', value: 13 },
9
+ { color: '#F7931A', label: 'Warning', value: 16 },
10
+ { color: '#FF6666', label: 'Down', value: 62 }
11
+ ];
12
+
13
+ const total = Math.floor(
14
+ defaultData.reduce((acc, { value }) => acc + value, 0)
15
+ );
16
+
17
+ const TooltipContent = ({ label, color, value }: ArcType): JSX.Element => {
18
+ return (
19
+ <div data-testid={`tooltip-${label}`} style={{ color }}>
20
+ {label} : {value}
21
+ </div>
22
+ );
23
+ };
24
+
25
+ const initialize = ({
26
+ width = '500px',
27
+ height = '500px',
28
+ data = defaultData,
29
+ ...args
30
+ }: Omit<PieProps, 'data'> & {
31
+ data?;
32
+ height?: string;
33
+ width?: string;
34
+ }): void => {
35
+ cy.mount({
36
+ Component: (
37
+ <div style={{ height, width }}>
38
+ <PieChart {...args} data={data} />
39
+ </div>
40
+ )
41
+ });
42
+ };
43
+
44
+ describe('Pie chart', () => {
45
+ it('renders pie chart correctly with provided data', () => {
46
+ initialize({});
47
+
48
+ defaultData.forEach(({ label }) => {
49
+ cy.findByTestId(label).should('be.visible');
50
+ });
51
+
52
+ cy.makeSnapshot();
53
+ });
54
+
55
+ it('adjusts size based on the provided width and height', () => {
56
+ initialize({ displayLegend: false, height: '300px', width: '300px' });
57
+
58
+ cy.findByTestId('pieChart')
59
+ .should('have.css', 'width', '300px')
60
+ .and('have.css', 'height', '300px');
61
+
62
+ cy.makeSnapshot();
63
+ });
64
+
65
+ it('renders as a donut when variant is set to "donut"', () => {
66
+ initialize({ variant: 'donut' });
67
+ cy.get('[data-variant="donut"]').should('exist');
68
+
69
+ cy.makeSnapshot();
70
+ });
71
+
72
+ it('renders as a pie when variant is set to "pie"', () => {
73
+ initialize({ variant: 'pie' });
74
+ cy.get('[data-variant="pie"]').should('exist');
75
+
76
+ cy.makeSnapshot();
77
+ });
78
+
79
+ it('displays tooltip with correct information on hover', () => {
80
+ initialize({ TooltipContent });
81
+
82
+ defaultData.forEach(({ label, value }) => {
83
+ cy.findByTestId(label).trigger('mouseover', { force: true });
84
+
85
+ cy.findByTestId(`tooltip-${label}`)
86
+ .should('contain', label)
87
+ .and('contain', numeral(value).format('0a').toUpperCase());
88
+ });
89
+
90
+ cy.makeSnapshot();
91
+ });
92
+ it('conditionally displays values on arcs based on displayValues prop', () => {
93
+ initialize({ displayValues: true });
94
+ defaultData.forEach(({ value }, index) => {
95
+ cy.findAllByTestId('value')
96
+ .eq(index)
97
+ .children()
98
+ .eq(0)
99
+ .should('have.text', value);
100
+ });
101
+
102
+ initialize({ displayValues: false });
103
+ cy.findAllByTestId('value').should('not.exist');
104
+
105
+ cy.makeSnapshot();
106
+ });
107
+
108
+ it('displays values on arcs in percentage unit when displayValues is set to true and unit to percentage', () => {
109
+ initialize({ displayValues: true, unit: 'percentage' });
110
+ defaultData.forEach(({ value }, index) => {
111
+ cy.findAllByTestId('value')
112
+ .eq(index)
113
+ .children()
114
+ .eq(0)
115
+ .should('have.text', `${((value * 100) / total).toFixed(1)}%`);
116
+ });
117
+
118
+ cy.makeSnapshot();
119
+ });
120
+
121
+ it('displays Legend component based on displayLegend prop', () => {
122
+ initialize({ displayLegend: true });
123
+ cy.findByTestId('Legend').should('be.visible');
124
+
125
+ initialize({ displayLegend: false });
126
+ cy.findByTestId('Legend').should('not.exist');
127
+
128
+ cy.makeSnapshot();
129
+ });
130
+
131
+ it('displays the title when the title is giving', () => {
132
+ initialize({ title: 'host' });
133
+ cy.findByTestId('Title').should('be.visible');
134
+
135
+ initialize({});
136
+ cy.findByTestId('Title').should('not.exist');
137
+
138
+ cy.makeSnapshot();
139
+ });
140
+
141
+ it('adjusts outer radius when chart dimensions are too small', () => {
142
+ initialize({
143
+ displayLegend: false,
144
+ height: '120px',
145
+ title: 'hosts',
146
+ variant: 'donut',
147
+ width: '120px'
148
+ });
149
+
150
+ cy.get('[data-variant="donut"]').should('have.css', 'width', '100px');
151
+
152
+ cy.makeSnapshot();
153
+ });
154
+ });
@@ -0,0 +1,194 @@
1
+ import { Meta, StoryObj } from '@storybook/react';
2
+
3
+ import { ArcType } from './models';
4
+
5
+ import { PieChart } from '.';
6
+
7
+ const data = [
8
+ { color: '#88B922', label: 'Ok', value: 148 },
9
+ { color: '#999999', label: 'Unknown', value: 13 },
10
+ { color: '#F7931A', label: 'Warning', value: 16 },
11
+ { color: '#FF6666', label: 'Down', value: 62 }
12
+ ];
13
+
14
+ const dataWithBigNumbers = [
15
+ { color: '#88B922', label: 'Ok', value: 260000 },
16
+ { color: '#999999', label: 'Unknown', value: 1010900 },
17
+ { color: '#F7931A', label: 'Warning', value: 63114 },
18
+ { color: '#FF6666', label: 'Down', value: 122222 }
19
+ ];
20
+
21
+ const meta: Meta<typeof PieChart> = {
22
+ component: PieChart
23
+ };
24
+
25
+ export default meta;
26
+ type Story = StoryObj<typeof PieChart>;
27
+
28
+ const Template = (args): JSX.Element => {
29
+ return (
30
+ <div style={{ height: '350px', width: '350px' }}>
31
+ <PieChart {...args} />
32
+ </div>
33
+ );
34
+ };
35
+
36
+ export const Pie: Story = {
37
+ args: {
38
+ data,
39
+ title: 'hosts'
40
+ },
41
+ render: Template
42
+ };
43
+
44
+ export const Donut: Story = {
45
+ args: {
46
+ data,
47
+ title: 'hosts',
48
+ variant: 'donut'
49
+ },
50
+ render: Template
51
+ };
52
+
53
+ export const WithPencentage: Story = {
54
+ args: {
55
+ data,
56
+ title: 'hosts',
57
+ unit: 'percentage',
58
+ variant: 'donut'
59
+ },
60
+ render: Template
61
+ };
62
+
63
+ export const WithBigNumbers: Story = {
64
+ args: {
65
+ data: dataWithBigNumbers,
66
+ title: 'hosts',
67
+ unit: 'number',
68
+ variant: 'donut'
69
+ },
70
+ render: Template
71
+ };
72
+
73
+ export const WithoutLegend: Story = {
74
+ args: {
75
+ data,
76
+ displayLegend: false,
77
+ title: 'hosts',
78
+ variant: 'donut'
79
+ },
80
+ render: Template
81
+ };
82
+
83
+ export const DonutWithoutTitle: Story = {
84
+ args: {
85
+ data,
86
+ variant: 'donut'
87
+ },
88
+ render: Template
89
+ };
90
+
91
+ export const PieWithoutTitle: Story = {
92
+ args: {
93
+ data
94
+ },
95
+ render: Template
96
+ };
97
+
98
+ export const DonutWithDisplayedValues: Story = {
99
+ args: {
100
+ data,
101
+ displayValues: true,
102
+ variant: 'donut'
103
+ },
104
+ render: Template
105
+ };
106
+
107
+ export const PieWithDisplayedValues: Story = {
108
+ args: {
109
+ data,
110
+ displayValues: true
111
+ },
112
+ render: Template
113
+ };
114
+
115
+ const TooltipContent = ({ label, color, value }: ArcType): JSX.Element => {
116
+ return (
117
+ <div style={{ color }}>
118
+ {label} : {value}
119
+ </div>
120
+ );
121
+ };
122
+
123
+ export const PieWithTooltip: Story = {
124
+ args: {
125
+ TooltipContent,
126
+ data,
127
+ displayValues: true,
128
+ unit: 'percentage'
129
+ },
130
+ render: Template
131
+ };
132
+
133
+ export const DonutWithTooltip: Story = {
134
+ args: {
135
+ TooltipContent,
136
+ data,
137
+ displayValues: true,
138
+ variant: 'donut'
139
+ },
140
+ render: Template
141
+ };
142
+
143
+ const TemplateForSmallDimensions = (args): JSX.Element => {
144
+ return (
145
+ <div style={{ height: '130px', width: '130px' }}>
146
+ <PieChart {...args} />
147
+ </div>
148
+ );
149
+ };
150
+
151
+ export const PieWithSmallDimensions: Story = {
152
+ args: {
153
+ data,
154
+ displayLegend: false
155
+ },
156
+ render: TemplateForSmallDimensions
157
+ };
158
+
159
+ export const DonutWithSmallDimensions: Story = {
160
+ args: {
161
+ data,
162
+ displayLegend: false,
163
+ title: 'hosts',
164
+ variant: 'donut'
165
+ },
166
+ render: TemplateForSmallDimensions
167
+ };
168
+
169
+ const dataWidthOneNoZeroValue = [
170
+ { color: '#88B922', label: 'Ok', value: 13 },
171
+ { color: '#999999', label: 'Unknown', value: 0 },
172
+ { color: '#F7931A', label: 'Warning', value: 0 },
173
+ { color: '#FF6666', label: 'Down', value: 0 }
174
+ ];
175
+
176
+ export const PieWithOneNoZeroValue: Story = {
177
+ args: {
178
+ data: dataWidthOneNoZeroValue,
179
+ displayLegend: false,
180
+ title: 'hosts',
181
+ variant: 'pie'
182
+ },
183
+ render: Template
184
+ };
185
+
186
+ export const donutWithOneNoZeroValue: Story = {
187
+ args: {
188
+ data: dataWidthOneNoZeroValue,
189
+ displayLegend: false,
190
+ title: 'hosts',
191
+ variant: 'donut'
192
+ },
193
+ render: Template
194
+ };
@@ -0,0 +1,39 @@
1
+ import { lt } from 'ramda';
2
+ import { makeStyles } from 'tss-react/mui';
3
+
4
+ export const usePieStyles = makeStyles<{ svgSize: number }>()(
5
+ (theme, { svgSize }) => ({
6
+ container: {
7
+ alignItems: 'center',
8
+ display: 'flex',
9
+ gap: theme.spacing(2),
10
+ justifyContent: 'center'
11
+ },
12
+ pieChartTooltip: {
13
+ backgroundColor: theme.palette.background.paper,
14
+ color: theme.palette.text.primary,
15
+ padding: 0,
16
+ position: 'relative'
17
+ },
18
+ svgContainer: {
19
+ alignItems: 'center',
20
+ backgroundColor: theme.palette.background.panelGroups,
21
+ borderRadius: '100%',
22
+ display: 'flex',
23
+ justifyContent: 'center'
24
+ },
25
+ svgWrapper: {
26
+ alignItems: 'center',
27
+ display: 'flex',
28
+ flexDirection: 'column',
29
+ gap: theme.spacing(1),
30
+ justifyContent: 'center'
31
+ },
32
+ title: {
33
+ fontSize: lt(svgSize, 150)
34
+ ? theme.typography.body1.fontSize
35
+ : theme.typography.h6.fontSize,
36
+ fontWeight: theme.typography.fontWeightMedium
37
+ }
38
+ })
39
+ );
@@ -0,0 +1,14 @@
1
+ import { ParentSize } from '../..';
2
+
3
+ import ResponsivePie from './ResponsivePie';
4
+ import { PieProps } from './models';
5
+
6
+ const PieChart = (props: PieProps): JSX.Element => (
7
+ <ParentSize>
8
+ {({ width, height }) => (
9
+ <ResponsivePie {...props} height={height} width={width} />
10
+ )}
11
+ </ParentSize>
12
+ );
13
+
14
+ export default PieChart;
@@ -0,0 +1,243 @@
1
+ import { useRef } from 'react';
2
+
3
+ import { Pie } from '@visx/shape';
4
+ import { Group } from '@visx/group';
5
+ import { Text } from '@visx/text';
6
+ import numeral from 'numeral';
7
+ import { always, equals, gt, ifElse, lt } from 'ramda';
8
+ import { useTranslation } from 'react-i18next';
9
+
10
+ import { useTheme } from '@mui/material';
11
+
12
+ import { Tooltip } from '../../components';
13
+ import { Legend as LegendComponent } from '../Legend';
14
+ import { LegendProps } from '../Legend/models';
15
+ import { getValueByUnit } from '../common/utils';
16
+
17
+ import { PieProps } from './models';
18
+ import { usePieStyles } from './PieChart.styles';
19
+ import { useResponsivePie } from './useResponsivePie';
20
+
21
+ const DefaultLengd = ({ scale, direction }: LegendProps): JSX.Element => (
22
+ <LegendComponent direction={direction} scale={scale} />
23
+ );
24
+
25
+ type Placement = 'left' | 'right' | 'top' | 'bottom';
26
+
27
+ const getTooltipPlacement = ({ radianX, radianY }): Placement => {
28
+ if (gt(Math.abs(radianX), Math.abs(radianY))) {
29
+ return ifElse<[b: number], Placement, Placement>(
30
+ lt(0),
31
+ always<Placement>('right'),
32
+ always<Placement>('left')
33
+ )(radianX);
34
+ }
35
+
36
+ return ifElse<[b: number], Placement, Placement>(
37
+ lt(0),
38
+ always<Placement>('bottom'),
39
+ always<Placement>('top')
40
+ )(radianY);
41
+ };
42
+
43
+ const ResponsivePie = ({
44
+ title,
45
+ variant = 'pie',
46
+ width,
47
+ height,
48
+ data,
49
+ unit = 'number',
50
+ Legend = DefaultLengd,
51
+ displayLegend = true,
52
+ innerRadius: defaultInnerRadius = 40,
53
+ onArcClick,
54
+ displayValues,
55
+ TooltipContent,
56
+ legendDirection = 'column'
57
+ }: PieProps & { height: number; width: number }): JSX.Element => {
58
+ const { t } = useTranslation();
59
+ const theme = useTheme();
60
+
61
+ const legendRef = useRef(null);
62
+ const titleRef = useRef(null);
63
+
64
+ const {
65
+ half,
66
+ legendScale,
67
+ svgContainerSize,
68
+ svgSize,
69
+ svgWrapperWidth,
70
+ total,
71
+ innerRadius,
72
+ isContainsExactlyOneNonZeroValue
73
+ } = useResponsivePie({
74
+ data,
75
+ defaultInnerRadius,
76
+ height,
77
+ legendRef,
78
+ titleRef,
79
+ unit,
80
+ width
81
+ });
82
+
83
+ const { classes } = usePieStyles({ svgSize });
84
+
85
+ return (
86
+ <div
87
+ className={classes.container}
88
+ style={{
89
+ height,
90
+ width
91
+ }}
92
+ >
93
+ <div
94
+ className={classes.svgWrapper}
95
+ style={{
96
+ height,
97
+ width: svgWrapperWidth
98
+ }}
99
+ >
100
+ {equals(variant, 'pie') && title && (
101
+ <div className={classes.title} data-testid="Title" ref={titleRef}>
102
+ {`${numeral(total).format('0a').toUpperCase()} `} {t(title)}
103
+ </div>
104
+ )}
105
+ <div
106
+ className={classes.svgContainer}
107
+ data-testid="pieChart"
108
+ style={{
109
+ height: svgContainerSize,
110
+ width: svgContainerSize
111
+ }}
112
+ >
113
+ <svg data-variant={variant} height={svgSize} width={svgSize}>
114
+ <Group left={half} top={half}>
115
+ <Pie
116
+ cornerRadius={4}
117
+ data={data}
118
+ innerRadius={() => {
119
+ return equals(variant, 'pie') ? 0 : half - innerRadius;
120
+ }}
121
+ outerRadius={half}
122
+ pieValue={(items) => items.value}
123
+ >
124
+ {(pie) => {
125
+ return pie.arcs.map((arc) => {
126
+ const [centroidX, centroidY] = pie.path.centroid(arc);
127
+ const midAngle = Math.atan2(centroidY, centroidX);
128
+
129
+ const labelRadius = half * 0.8;
130
+
131
+ const labelX = Math.cos(midAngle) * labelRadius;
132
+ const labelY = Math.sin(midAngle) * labelRadius;
133
+
134
+ const angle = arc.endAngle - arc.startAngle;
135
+ const minAngle = 0.2;
136
+
137
+ const x = equals(variant, 'donut') ? centroidX : labelX;
138
+ const y = equals(variant, 'donut') ? centroidY : labelY;
139
+
140
+ const onClick = (): void => {
141
+ onArcClick?.(arc.data);
142
+ };
143
+
144
+ return (
145
+ <Tooltip
146
+ hasCaret
147
+ classes={{
148
+ tooltip: classes.pieChartTooltip
149
+ }}
150
+ followCursor={false}
151
+ key={arc.data.label}
152
+ label={
153
+ TooltipContent && (
154
+ <TooltipContent
155
+ color={arc.data.color}
156
+ label={arc.data.label}
157
+ title={title}
158
+ total={total}
159
+ value={arc.data.value}
160
+ />
161
+ )
162
+ }
163
+ leaveDelay={200}
164
+ placement={getTooltipPlacement({
165
+ radianX: Math.cos(midAngle),
166
+ radianY: Math.sin(midAngle)
167
+ })}
168
+ >
169
+ <g data-testid={arc.data.label} onClick={onClick}>
170
+ <path
171
+ cursor="pointer"
172
+ d={pie.path(arc) as string}
173
+ fill={arc.data.color}
174
+ />
175
+ {displayValues &&
176
+ !isContainsExactlyOneNonZeroValue &&
177
+ angle > minAngle && (
178
+ <Text
179
+ data-testid="value"
180
+ dy=".33em"
181
+ fill="#000"
182
+ fontSize={12}
183
+ fontWeight={600}
184
+ pointerEvents="none"
185
+ textAnchor="middle"
186
+ x={x}
187
+ y={y}
188
+ >
189
+ {getValueByUnit({
190
+ total,
191
+ unit,
192
+ value: arc.data.value
193
+ })}
194
+ </Text>
195
+ )}
196
+ </g>
197
+ </Tooltip>
198
+ );
199
+ });
200
+ }}
201
+ </Pie>
202
+ {equals(variant, 'donut') && title && (
203
+ <>
204
+ <Text
205
+ className={classes.title}
206
+ dy={lt(svgSize, 150) ? -10 : -15}
207
+ fill={theme.palette.text.primary}
208
+ textAnchor="middle"
209
+ >
210
+ {numeral(total).format('0a').toUpperCase()}
211
+ </Text>
212
+ <Text
213
+ className={classes.title}
214
+ data-testid="Title"
215
+ dy={lt(svgSize, 150) ? 10 : 15}
216
+ fill={theme.palette.text.primary}
217
+ textAnchor="middle"
218
+ >
219
+ {t(title)}
220
+ </Text>
221
+ </>
222
+ )}
223
+ </Group>
224
+ </svg>
225
+ </div>
226
+ </div>
227
+ {displayLegend && (
228
+ <div data-testid="Legend" ref={legendRef}>
229
+ <Legend
230
+ data={data}
231
+ direction={legendDirection}
232
+ scale={legendScale}
233
+ title={title}
234
+ total={total}
235
+ unit={unit}
236
+ />
237
+ </div>
238
+ )}
239
+ </div>
240
+ );
241
+ };
242
+
243
+ export default ResponsivePie;
@@ -0,0 +1 @@
1
+ export { default as PieChart } from './PieChart';
@@ -0,0 +1,19 @@
1
+ export interface ArcType {
2
+ color: string;
3
+ label: string;
4
+ value: number;
5
+ }
6
+
7
+ export interface PieProps {
8
+ Legend?: ({ scale, direction, data, title, total, unit }) => JSX.Element;
9
+ TooltipContent?: (arcData) => JSX.Element | boolean | null;
10
+ data: Array<ArcType>;
11
+ displayLegend?: boolean;
12
+ displayValues?: boolean;
13
+ innerRadius?: number;
14
+ legendDirection?: 'row' | 'column';
15
+ onArcClick?: (ardata) => void;
16
+ title?: string;
17
+ unit?: 'percentage' | 'number';
18
+ variant?: 'pie' | 'donut';
19
+ }