@centreon/ui 24.4.59 → 24.4.61

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 (163) hide show
  1. package/package.json +50 -42
  2. package/public/mockServiceWorker.js +1 -1
  3. package/src/ActionsList/ActionsList.styles.ts +40 -71
  4. package/src/Button/Icon/index.tsx +1 -1
  5. package/src/Button/Save/StartIcon.tsx +3 -3
  6. package/src/Button/Save/index.tsx +9 -5
  7. package/src/Checkbox/Checkbox.tsx +2 -2
  8. package/src/Checkbox/CheckboxGroup/index.tsx +2 -2
  9. package/src/Dashboard/Item.tsx +1 -1
  10. package/src/Dashboard/Layout.tsx +2 -2
  11. package/src/Dialog/Confirm/index.tsx +10 -2
  12. package/src/Dialog/UnsavedChanges/index.tsx +21 -20
  13. package/src/Dialog/UnsavedChanges/translatedLabels.ts +4 -6
  14. package/src/Dialog/index.tsx +9 -2
  15. package/src/FallbackPage/FallbackPage.tsx +3 -3
  16. package/src/FileDropZone/index.tsx +3 -1
  17. package/src/Form/Form.cypress.spec.tsx +133 -0
  18. package/src/Form/Inputs/List/Content.tsx +62 -0
  19. package/src/Form/Inputs/List/List.styles.ts +29 -0
  20. package/src/Form/Inputs/List/List.tsx +58 -0
  21. package/src/Form/Inputs/List/useList.ts +81 -0
  22. package/src/Form/Inputs/Text.tsx +3 -1
  23. package/src/Form/Inputs/index.tsx +3 -1
  24. package/src/Form/Inputs/models.ts +10 -1
  25. package/src/Graph/BarStack/BarStack.cypress.spec.tsx +154 -0
  26. package/src/Graph/BarStack/BarStack.stories.tsx +123 -0
  27. package/src/Graph/BarStack/BarStack.styles.ts +37 -0
  28. package/src/Graph/BarStack/BarStack.tsx +14 -0
  29. package/src/Graph/BarStack/ResponsiveBarStack.tsx +222 -0
  30. package/src/Graph/BarStack/index.ts +1 -0
  31. package/src/Graph/BarStack/models.ts +20 -0
  32. package/src/Graph/BarStack/useResponsiveBarStack.ts +137 -0
  33. package/src/Graph/Gauge/Gauge.cypress.spec.tsx +102 -0
  34. package/src/Graph/Gauge/Gauge.tsx +1 -1
  35. package/src/Graph/HeatMap/HeatMap.cypress.spec.tsx +145 -0
  36. package/src/Graph/HeatMap/HeatMap.stories.tsx +0 -25
  37. package/src/Graph/HeatMap/ResponsiveHeatMap.tsx +8 -2
  38. package/src/Graph/Legend/Legend.tsx +21 -0
  39. package/src/Graph/Legend/index.ts +1 -0
  40. package/src/Graph/Legend/models.ts +11 -0
  41. package/src/Graph/LineChart/BasicComponents/Lines/Threshold/Circle.tsx +2 -2
  42. package/src/Graph/LineChart/BasicComponents/Thresholds.tsx +2 -2
  43. package/src/Graph/LineChart/BasicComponents/useFilterLines.ts +1 -1
  44. package/src/Graph/LineChart/InteractiveComponents/AnchorPoint/GuidingLines.tsx +2 -2
  45. package/src/Graph/LineChart/InteractiveComponents/Annotations/EventAnnotations.tsx +1 -1
  46. package/src/Graph/LineChart/Legend/Legend.styles.ts +1 -1
  47. package/src/Graph/LineChart/Legend/LegendHeader.tsx +1 -1
  48. package/src/Graph/LineChart/Legend/useInteractiveValues.ts +2 -2
  49. package/src/Graph/LineChart/Legend/useLegend.ts +3 -3
  50. package/src/Graph/LineChart/helpers/doc.ts +16 -13
  51. package/src/Graph/LineChart/helpers/index.ts +1 -1
  52. package/src/Graph/LineChart/index.stories.tsx +4 -2
  53. package/src/Graph/LineChart/index.tsx +1 -1
  54. package/src/Graph/PieChart/PieChart.cypress.spec.tsx +169 -0
  55. package/src/Graph/PieChart/PieChart.stories.tsx +194 -0
  56. package/src/Graph/PieChart/PieChart.styles.ts +39 -0
  57. package/src/Graph/PieChart/PieChart.tsx +14 -0
  58. package/src/Graph/PieChart/ResponsivePie.tsx +254 -0
  59. package/src/Graph/PieChart/index.ts +1 -0
  60. package/src/Graph/PieChart/models.ts +20 -0
  61. package/src/Graph/PieChart/useResponsivePie.ts +85 -0
  62. package/src/Graph/SingleBar/SingleBar.cypress.spec.tsx +121 -0
  63. package/src/Graph/SingleBar/Thresholds.tsx +2 -2
  64. package/src/Graph/Text/Text.cypress.spec.tsx +101 -0
  65. package/src/Graph/Text/Text.stories.tsx +60 -4
  66. package/src/Graph/Text/Text.tsx +1 -1
  67. package/src/Graph/common/testUtils.ts +71 -0
  68. package/src/Graph/common/timeSeries/index.ts +22 -14
  69. package/src/Graph/common/utils.ts +19 -0
  70. package/src/Graph/index.ts +3 -0
  71. package/src/Graph/translatedLabels.ts +1 -0
  72. package/src/InputField/Select/Autocomplete/Connected/index.tsx +10 -7
  73. package/src/InputField/Select/Autocomplete/Draggable/SortableList.tsx +1 -1
  74. package/src/InputField/Select/Autocomplete/Draggable/SortableListContent.tsx +1 -1
  75. package/src/InputField/Select/Autocomplete/Draggable/index.tsx +1 -1
  76. package/src/InputField/Select/Autocomplete/Multi/index.tsx +4 -2
  77. package/src/InputField/Select/Autocomplete/index.tsx +129 -116
  78. package/src/InputField/Select/IconPopover/index.tsx +2 -2
  79. package/src/InputField/Select/index.tsx +15 -2
  80. package/src/InputField/Text/index.tsx +2 -2
  81. package/src/Listing/ActionBar/index.tsx +9 -8
  82. package/src/Listing/Cell/DataCell.styles.ts +3 -0
  83. package/src/Listing/Cell/DataCell.tsx +23 -5
  84. package/src/Listing/Header/ListingHeader.tsx +1 -1
  85. package/src/Listing/Listing.cypress.spec.tsx +218 -33
  86. package/src/Listing/Listing.styles.ts +4 -7
  87. package/src/Listing/Row/Row.tsx +7 -3
  88. package/src/Listing/index.stories.tsx +37 -3
  89. package/src/Listing/index.test.tsx +1 -1
  90. package/src/Listing/index.tsx +80 -36
  91. package/src/Listing/models.ts +1 -0
  92. package/src/Listing/useStyleTable.ts +1 -0
  93. package/src/Module/Module.cypress.spec.tsx +129 -0
  94. package/src/Module/index.tsx +2 -4
  95. package/src/PopoverMenu/index.tsx +6 -5
  96. package/src/RichTextEditor/RichTextEditor.tsx +12 -1
  97. package/src/SortableItems/index.tsx +2 -7
  98. package/src/ThemeProvider/index.tsx +24 -0
  99. package/src/TimePeriods/CustomTimePeriod/CompactCustomTimePeriod.styles.ts +6 -7
  100. package/src/TimePeriods/CustomTimePeriod/PopoverCustomTimePeriod/PickersStartEndDate.tsx +8 -3
  101. package/src/TimePeriods/CustomTimePeriod/PopoverCustomTimePeriod/models.ts +0 -2
  102. package/src/TimePeriods/DateTimePickerInput.tsx +56 -19
  103. package/src/TimePeriods/ResolutionTimePeriod.cypress.spec.tsx +12 -9
  104. package/src/TimePeriods/TimePeriods.cypress.spec.tsx +9 -33
  105. package/src/TimePeriods/helpers/index.ts +1 -1
  106. package/src/TimePeriods/index.stories.tsx +12 -4
  107. package/src/TimePeriods/index.tsx +2 -2
  108. package/src/Typography/FluidTypography/FluidTypography.cypress.spec.tsx +27 -0
  109. package/src/Typography/FluidTypography/index.stories.tsx +2 -2
  110. package/src/Typography/FluidTypography/index.tsx +21 -28
  111. package/src/api/QueryProvider.tsx +1 -1
  112. package/src/api/TestQueryProvider.tsx +1 -1
  113. package/src/api/index.ts +3 -3
  114. package/src/api/useFetchQuery/index.ts +27 -23
  115. package/src/api/useMutationQuery/index.test.ts +4 -4
  116. package/src/api/useMutationQuery/index.ts +60 -25
  117. package/src/components/Button/Icon/IconButton.tsx +6 -2
  118. package/src/components/DataTable/DataListing.tsx +6 -0
  119. package/src/components/DataTable/DataTable.cypress.spec.tsx +174 -0
  120. package/src/components/DataTable/DataTable.stories.tsx +40 -0
  121. package/src/components/DataTable/DataTable.styles.ts +3 -0
  122. package/src/components/DataTable/DataTable.tsx +3 -3
  123. package/src/components/DataTable/Item/DataTableItem.styles.ts +7 -2
  124. package/src/components/DataTable/Item/DataTableItem.tsx +4 -60
  125. package/src/components/DataTable/index.ts +3 -1
  126. package/src/components/Form/AccessRights/AccessRights.cypress.spec.tsx +13 -13
  127. package/src/components/Form/AccessRights/ShareInput/ContactSwitch.tsx +3 -3
  128. package/src/components/Form/AccessRights/ShareInput/ShareInput.tsx +1 -0
  129. package/src/components/Form/AccessRights/storiesData.ts +22 -22
  130. package/src/components/Form/Dashboard/DashboardDuplicationForm.tsx +85 -0
  131. package/src/components/Form/Dashboard/DashboardForm.tsx +15 -12
  132. package/src/components/Form/Dashboard/index.ts +1 -0
  133. package/src/components/Form/FormActions.tsx +7 -2
  134. package/src/components/ItemComposition/ItemComposition.styles.ts +2 -2
  135. package/src/components/Layout/PageLayout/PageLayout.tsx +1 -1
  136. package/src/components/Layout/PageLayout/PageLayoutActions.tsx +1 -0
  137. package/src/components/Layout/PageLayout/PageLayoutBody.tsx +1 -0
  138. package/src/components/Layout/PageLayout/PageLayoutHeader.tsx +5 -1
  139. package/src/components/Layout/PageLayout/PageQuickAccess.tsx +76 -0
  140. package/src/components/Layout/PageLayout/index.ts +3 -1
  141. package/src/components/Layout/PageLayout.cypress.spec.tsx +66 -0
  142. package/src/components/List/Item/ListItem.tsx +3 -3
  143. package/src/components/Modal/ConfirmationModal/ConfirmationModal.cypress.spec.tsx +168 -0
  144. package/src/components/Modal/ConfirmationModal/ConfirmationModal.stories.tsx +62 -0
  145. package/src/components/Modal/ConfirmationModal/ConfirmationModal.tsx +87 -0
  146. package/src/components/Modal/Modal.styles.ts +8 -3
  147. package/src/components/Modal/index.ts +2 -0
  148. package/src/components/Tooltip/ConfirmationTooltip/ConfirmationTooltip.stories.tsx +3 -3
  149. package/src/components/Tooltip/ConfirmationTooltip/ConfirmationTooltip.tsx +1 -1
  150. package/src/components/Tooltip/ConfirmationTooltip/models.ts +1 -1
  151. package/src/index.ts +2 -2
  152. package/src/queryParameters/url/index.ts +5 -1
  153. package/src/utils/index.ts +1 -1
  154. package/src/utils/useFullscreen/Fullscreen.cypress.spec.tsx +1 -3
  155. package/src/utils/useFullscreen/useFullscreenListener.ts +10 -7
  156. package/src/utils/useInfiniteScrollListing.ts +4 -1
  157. package/src/utils/{useLicenseExpirationWarning.cypress.spec.tsx → useLicenseExpirationWarning.test.tsx} +48 -37
  158. package/src/utils/useLicenseExpirationWarning.ts +18 -18
  159. package/src/utils/usePluralizedTranslation.ts +21 -0
  160. package/src/Typography/FluidTypography/useFluidResizeObserver.ts +0 -56
  161. package/src/screens/dashboard/DashboardsDetail.stories.tsx +0 -108
  162. package/src/screens/dashboard/DashboardsOverview.stories.tsx +0 -281
  163. package/src/utils/useDateTimePickerAdapter.ts +0 -309
@@ -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,254 @@
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 { Typography, 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
+ import { labelNoDataFound as defaultlabelNoDataFound } from '../translatedLabels';
17
+
18
+ import { PieProps } from './models';
19
+ import { usePieStyles } from './PieChart.styles';
20
+ import { useResponsivePie } from './useResponsivePie';
21
+
22
+ const DefaultLengd = ({ scale, direction }: LegendProps): JSX.Element => (
23
+ <LegendComponent direction={direction} scale={scale} />
24
+ );
25
+
26
+ type Placement = 'left' | 'right' | 'top' | 'bottom';
27
+
28
+ const getTooltipPlacement = ({ radianX, radianY }): Placement => {
29
+ if (gt(Math.abs(radianX), Math.abs(radianY))) {
30
+ return ifElse<[b: number], Placement, Placement>(
31
+ lt(0),
32
+ always<Placement>('right'),
33
+ always<Placement>('left')
34
+ )(radianX);
35
+ }
36
+
37
+ return ifElse<[b: number], Placement, Placement>(
38
+ lt(0),
39
+ always<Placement>('bottom'),
40
+ always<Placement>('top')
41
+ )(radianY);
42
+ };
43
+
44
+ const ResponsivePie = ({
45
+ title,
46
+ variant = 'pie',
47
+ width,
48
+ height,
49
+ data,
50
+ unit = 'number',
51
+ Legend = DefaultLengd,
52
+ displayLegend = true,
53
+ innerRadius: defaultInnerRadius = 40,
54
+ onArcClick,
55
+ displayValues,
56
+ TooltipContent,
57
+ legendDirection = 'column',
58
+ labelNoDataFound = defaultlabelNoDataFound
59
+ }: PieProps & { height: number; width: number }): JSX.Element => {
60
+ const { t } = useTranslation();
61
+ const theme = useTheme();
62
+
63
+ const legendRef = useRef(null);
64
+ const titleRef = useRef(null);
65
+
66
+ const {
67
+ half,
68
+ legendScale,
69
+ svgContainerSize,
70
+ svgSize,
71
+ svgWrapperWidth,
72
+ total,
73
+ innerRadius,
74
+ isContainsExactlyOneNonZeroValue,
75
+ areAllValuesNull
76
+ } = useResponsivePie({
77
+ data,
78
+ defaultInnerRadius,
79
+ height,
80
+ legendRef,
81
+ titleRef,
82
+ unit,
83
+ width
84
+ });
85
+
86
+ const { classes } = usePieStyles({ svgSize });
87
+
88
+ if (areAllValuesNull) {
89
+ return (
90
+ <div className={classes.container} style={{ height, width }}>
91
+ <Typography variant="h3">{t(labelNoDataFound)}</Typography>
92
+ </div>
93
+ );
94
+ }
95
+
96
+ return (
97
+ <div
98
+ className={classes.container}
99
+ style={{
100
+ height,
101
+ width
102
+ }}
103
+ >
104
+ <div
105
+ className={classes.svgWrapper}
106
+ style={{
107
+ height,
108
+ width: svgWrapperWidth
109
+ }}
110
+ >
111
+ {equals(variant, 'pie') && title && (
112
+ <div className={classes.title} data-testid="Title" ref={titleRef}>
113
+ {`${numeral(total).format('0a').toUpperCase()} `} {t(title)}
114
+ </div>
115
+ )}
116
+ <div
117
+ className={classes.svgContainer}
118
+ data-testid="pieChart"
119
+ style={{
120
+ height: svgContainerSize,
121
+ width: svgContainerSize
122
+ }}
123
+ >
124
+ <svg data-variant={variant} height={svgSize} width={svgSize}>
125
+ <Group left={half} top={half}>
126
+ <Pie
127
+ cornerRadius={4}
128
+ data={data}
129
+ innerRadius={() => {
130
+ return equals(variant, 'pie') ? 0 : half - innerRadius;
131
+ }}
132
+ outerRadius={half}
133
+ pieValue={(items) => items.value}
134
+ >
135
+ {(pie) => {
136
+ return pie.arcs.map((arc) => {
137
+ const [centroidX, centroidY] = pie.path.centroid(arc);
138
+ const midAngle = Math.atan2(centroidY, centroidX);
139
+
140
+ const labelRadius = half * 0.8;
141
+
142
+ const labelX = Math.cos(midAngle) * labelRadius;
143
+ const labelY = Math.sin(midAngle) * labelRadius;
144
+
145
+ const angle = arc.endAngle - arc.startAngle;
146
+ const minAngle = 0.2;
147
+
148
+ const x = equals(variant, 'donut') ? centroidX : labelX;
149
+ const y = equals(variant, 'donut') ? centroidY : labelY;
150
+
151
+ const onClick = (): void => {
152
+ onArcClick?.(arc.data);
153
+ };
154
+
155
+ return (
156
+ <Tooltip
157
+ hasCaret
158
+ classes={{
159
+ tooltip: classes.pieChartTooltip
160
+ }}
161
+ followCursor={false}
162
+ key={arc.data.label}
163
+ label={
164
+ TooltipContent && (
165
+ <TooltipContent
166
+ color={arc.data.color}
167
+ label={arc.data.label}
168
+ title={title}
169
+ total={total}
170
+ value={arc.data.value}
171
+ />
172
+ )
173
+ }
174
+ leaveDelay={200}
175
+ placement={getTooltipPlacement({
176
+ radianX: Math.cos(midAngle),
177
+ radianY: Math.sin(midAngle)
178
+ })}
179
+ >
180
+ <g data-testid={arc.data.label} onClick={onClick}>
181
+ <path
182
+ cursor="pointer"
183
+ d={pie.path(arc) as string}
184
+ fill={arc.data.color}
185
+ />
186
+ {displayValues &&
187
+ !isContainsExactlyOneNonZeroValue &&
188
+ angle > minAngle && (
189
+ <Text
190
+ data-testid="value"
191
+ dy=".33em"
192
+ fill="#000"
193
+ fontSize={12}
194
+ fontWeight={600}
195
+ pointerEvents="none"
196
+ textAnchor="middle"
197
+ x={x}
198
+ y={y}
199
+ >
200
+ {getValueByUnit({
201
+ total,
202
+ unit,
203
+ value: arc.data.value
204
+ })}
205
+ </Text>
206
+ )}
207
+ </g>
208
+ </Tooltip>
209
+ );
210
+ });
211
+ }}
212
+ </Pie>
213
+ {equals(variant, 'donut') && title && (
214
+ <>
215
+ <Text
216
+ className={classes.title}
217
+ dy={lt(svgSize, 150) ? -10 : -15}
218
+ fill={theme.palette.text.primary}
219
+ textAnchor="middle"
220
+ >
221
+ {numeral(total).format('0a').toUpperCase()}
222
+ </Text>
223
+ <Text
224
+ className={classes.title}
225
+ data-testid="Title"
226
+ dy={lt(svgSize, 150) ? 10 : 15}
227
+ fill={theme.palette.text.primary}
228
+ textAnchor="middle"
229
+ >
230
+ {t(title)}
231
+ </Text>
232
+ </>
233
+ )}
234
+ </Group>
235
+ </svg>
236
+ </div>
237
+ </div>
238
+ {displayLegend && (
239
+ <div data-testid="Legend" ref={legendRef}>
240
+ <Legend
241
+ data={data}
242
+ direction={legendDirection}
243
+ scale={legendScale}
244
+ title={title}
245
+ total={total}
246
+ unit={unit}
247
+ />
248
+ </div>
249
+ )}
250
+ </div>
251
+ );
252
+ };
253
+
254
+ export default ResponsivePie;
@@ -0,0 +1 @@
1
+ export { default as PieChart } from './PieChart';
@@ -0,0 +1,20 @@
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
+ labelNoDataFound?: string;
15
+ legendDirection?: 'row' | 'column';
16
+ onArcClick?: (ardata) => void;
17
+ title?: string;
18
+ unit?: 'percentage' | 'number';
19
+ variant?: 'pie' | 'donut';
20
+ }
@@ -0,0 +1,85 @@
1
+ import { equals, isEmpty, pluck, reject } from 'ramda';
2
+
3
+ import { LegendScale } from '../Legend/models';
4
+ import { getValueByUnit } from '../common/utils';
5
+
6
+ import { ArcType } from './models';
7
+
8
+ interface ResponsivePieProps {
9
+ data: Array<ArcType>;
10
+ defaultInnerRadius: number;
11
+ height: number;
12
+ legendRef;
13
+ titleRef;
14
+ unit: 'percentage' | 'number';
15
+ width: number;
16
+ }
17
+
18
+ interface ResponsivePieState {
19
+ areAllValuesNull: boolean;
20
+ half: number;
21
+ innerRadius: number;
22
+ isContainsExactlyOneNonZeroValue: boolean;
23
+ legendScale: LegendScale;
24
+ svgContainerSize: number;
25
+ svgSize: number;
26
+ svgWrapperWidth: number;
27
+ total: number;
28
+ }
29
+ export const useResponsivePie = ({
30
+ titleRef,
31
+ legendRef,
32
+ height,
33
+ width,
34
+ data,
35
+ unit,
36
+ defaultInnerRadius
37
+ }: ResponsivePieProps): ResponsivePieState => {
38
+ const heightOfTitle = titleRef.current?.offsetHeight || 0;
39
+ const widthOfLegend = legendRef.current?.offsetWidth || 0;
40
+
41
+ const horizontalGap = widthOfLegend > 0 ? 16 : 0;
42
+ const verticalGap = heightOfTitle > 0 ? 8 : 0;
43
+
44
+ const svgContainerSize = Math.min(
45
+ height - heightOfTitle - verticalGap,
46
+ width - widthOfLegend - horizontalGap
47
+ );
48
+ const svgWrapperWidth = svgContainerSize;
49
+
50
+ const outerRadius = Math.min(32, svgContainerSize / 6);
51
+
52
+ const svgSize = svgContainerSize - outerRadius;
53
+
54
+ const half = svgSize / 2;
55
+
56
+ const total = Math.floor(data.reduce((acc, { value }) => acc + value, 0));
57
+
58
+ const innerRadius = Math.min(defaultInnerRadius, svgSize / 5);
59
+
60
+ const legendScale = {
61
+ domain: data.map(({ value }) => getValueByUnit({ total, unit, value })),
62
+ range: pluck('color', data)
63
+ };
64
+
65
+ const values = pluck('value', data);
66
+
67
+ const isContainsExactlyOneNonZeroValue = equals(
68
+ reject((value) => equals(value, 0), values).length,
69
+ 1
70
+ );
71
+
72
+ const areAllValuesNull = isEmpty(reject((value) => equals(value, 0), values));
73
+
74
+ return {
75
+ areAllValuesNull,
76
+ half,
77
+ innerRadius,
78
+ isContainsExactlyOneNonZeroValue,
79
+ legendScale,
80
+ svgContainerSize,
81
+ svgSize,
82
+ svgWrapperWidth,
83
+ total
84
+ };
85
+ };