@centreon/ui 24.4.72 → 24.4.74
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.
- package/package.json +24 -23
- package/src/Checkbox/fonts/roboto-bold-webfont.ttf +0 -0
- package/src/Checkbox/fonts/roboto-bold-webfont.woff +0 -0
- package/src/Checkbox/fonts/roboto-bold-webfont.woff2 +0 -0
- package/src/Checkbox/fonts/roboto-light-webfont.ttf +0 -0
- package/src/Checkbox/fonts/roboto-light-webfont.woff +0 -0
- package/src/Checkbox/fonts/roboto-light-webfont.woff2 +0 -0
- package/src/Checkbox/fonts/roboto-medium-webfont.ttf +0 -0
- package/src/Checkbox/fonts/roboto-medium-webfont.woff +0 -0
- package/src/Checkbox/fonts/roboto-medium-webfont.woff2 +0 -0
- package/src/Checkbox/fonts/roboto-regular-webfont.ttf +0 -0
- package/src/Checkbox/fonts/roboto-regular-webfont.woff +0 -0
- package/src/Checkbox/fonts/roboto-regular-webfont.woff2 +0 -0
- package/src/Dashboard/Item.tsx +2 -11
- package/src/Dashboard/Layout.tsx +2 -4
- package/src/Dashboard/utils.ts +1 -1
- package/src/Form/Inputs/Grid.tsx +8 -4
- package/src/Form/Inputs/models.ts +15 -14
- package/src/Graph/BarStack/BarStack.cypress.spec.tsx +87 -9
- package/src/Graph/BarStack/BarStack.stories.tsx +4 -2
- package/src/Graph/BarStack/BarStack.styles.ts +59 -30
- package/src/Graph/BarStack/Graph.tsx +176 -0
- package/src/Graph/BarStack/GraphAndLegend.tsx +119 -0
- package/src/Graph/BarStack/ResponsiveBarStack.tsx +62 -157
- package/src/Graph/BarStack/constants.ts +5 -0
- package/src/Graph/BarStack/models.ts +1 -1
- package/src/Graph/BarStack/useGraphAndLegend.ts +86 -0
- package/src/Graph/BarStack/useResponsiveBarStack.ts +74 -99
- package/src/Graph/HeatMap/ResponsiveHeatMap.tsx +19 -2
- package/src/Graph/HeatMap/model.ts +5 -1
- package/src/Graph/Legend/Legend.styles.ts +10 -0
- package/src/Graph/Legend/Legend.tsx +6 -1
- package/src/Graph/LineChart/BasicComponents/Lines/RegularLines/index.tsx +2 -1
- package/src/Graph/LineChart/BasicComponents/Lines/StackedLines/index.tsx +3 -4
- package/src/Graph/LineChart/Icons/Downtime.tsx +3 -3
- package/src/Graph/LineChart/InteractiveComponents/ZoomPreview/index.tsx +2 -1
- package/src/Graph/LineChart/Legend/Legend.styles.ts +16 -5
- package/src/Graph/LineChart/Legend/LegendHeader.tsx +4 -1
- package/src/Graph/LineChart/Legend/index.tsx +12 -5
- package/src/Graph/LineChart/LineChart.cypress.spec.tsx +53 -0
- package/src/Graph/LineChart/LineChart.tsx +10 -9
- package/src/Graph/LineChart/index.stories.tsx +13 -0
- package/src/Graph/LineChart/mockedData/curvesWithSameColor.json +252 -0
- package/src/Graph/LineChart/useLineChartData.ts +68 -18
- package/src/Graph/PieChart/ResponsivePie.tsx +3 -1
- package/src/Graph/PieChart/models.ts +1 -0
- package/src/Graph/Tree/DescendantNodes.tsx +0 -1
- package/src/Graph/Tree/Links.tsx +2 -15
- package/src/Graph/Tree/Tree.cypress.spec.tsx +0 -24
- package/src/Graph/Tree/Tree.stories.tsx +1 -17
- package/src/Graph/Tree/models.ts +0 -3
- package/src/Graph/common/utils.ts +51 -2
- package/src/Icon/BaseIcon.tsx +32 -0
- package/src/Icon/DowntimeIcon.tsx +14 -0
- package/src/InputField/Select/Autocomplete/Connected/Multi/index.test.tsx +21 -1
- package/src/InputField/Select/Autocomplete/Connected/index.test.tsx +2 -2
- package/src/InputField/Select/Autocomplete/Connected/index.tsx +10 -7
- package/src/InputField/Select/Autocomplete/Multi/index.stories.tsx +19 -0
- package/src/InputField/Select/Autocomplete/Multi/index.tsx +8 -5
- package/src/InputField/Text/index.tsx +7 -5
- package/src/Listing/ActionBar/index.tsx +1 -0
- package/src/PopoverMenu/index.tsx +4 -4
- package/src/RichTextEditor/ContentEditable.tsx +195 -195
- package/src/StoryBookThemeProvider/index.tsx +35 -36
- package/src/ThemeProvider/index.tsx +12 -12
- package/src/ThemeProvider/palettes.ts +11 -8
- package/src/TimePeriods/CustomTimePeriod/PopoverCustomTimePeriod/PickersStartEndDate.tsx +2 -3
- package/src/TimePeriods/DateTimePickerInput.tsx +4 -1
- package/src/TopCounterElements/TopCounterLayout.tsx +4 -3
- package/src/TopCounterElements/useCloseOnLegacyPage.tsx +9 -6
- package/src/api/buildListingEndpoint/getSearchQueryParameterValue.ts +7 -1
- package/src/api/buildListingEndpoint/models.ts +1 -0
- package/src/api/useGraphQuery/index.ts +1 -7
- package/src/components/Form/AccessRights/AccessRights.cypress.spec.tsx +13 -27
- package/src/components/Form/AccessRights/AccessRights.stories.tsx +19 -0
- package/src/components/Form/AccessRights/AccessRights.styles.ts +1 -1
- package/src/components/Form/AccessRights/AccessRights.tsx +5 -6
- package/src/components/Form/AccessRights/Actions/Actions.styles.ts +7 -3
- package/src/components/Form/AccessRights/Actions/Actions.tsx +32 -15
- package/src/components/Form/AccessRights/Actions/useActions.ts +37 -4
- package/src/components/Form/AccessRights/models.ts +3 -0
- package/src/components/Form/AccessRights/storiesData.ts +3 -0
- package/src/components/List/Item/ListItem.styles.ts +2 -2
- package/src/components/Zoom/Minimap.tsx +2 -4
- package/src/components/Zoom/Zoom.cypress.spec.tsx +13 -13
- package/src/components/Zoom/Zoom.tsx +1 -4
- package/src/components/Zoom/ZoomContent.tsx +2 -5
- package/src/components/index.ts +0 -1
- package/src/fonts/roboto-bold-webfont.ttf +0 -0
- package/src/fonts/roboto-bold-webfont.woff +0 -0
- package/src/fonts/roboto-bold-webfont.woff2 +0 -0
- package/src/fonts/roboto-light-webfont.ttf +0 -0
- package/src/fonts/roboto-light-webfont.woff +0 -0
- package/src/fonts/roboto-light-webfont.woff2 +0 -0
- package/src/fonts/roboto-medium-webfont.ttf +0 -0
- package/src/fonts/roboto-medium-webfont.woff +0 -0
- package/src/fonts/roboto-medium-webfont.woff2 +0 -0
- package/src/fonts/roboto-regular-webfont.ttf +0 -0
- package/src/fonts/roboto-regular-webfont.woff +0 -0
- package/src/fonts/roboto-regular-webfont.woff2 +0 -0
- package/src/index.ts +1 -0
- package/src/utils/index.ts +25 -25
- package/src/utils/useFullscreen/Fullscreen.cypress.spec.tsx +3 -0
- package/src/utils/useInfiniteScrollListing.ts +6 -1
- package/src/utils/useLocale/index.ts +10 -0
- package/src/utils/useLocale/useLocale.cypress.spec.tsx +40 -0
- package/src/utils/useLocaleDateTimeFormat/index.ts +5 -2
- package/src/utils/usePluralizedTranslation.ts +4 -21
- package/src/@assets/icons/downtime.icon.svg +0 -1
- package/src/components/Form/AccessRights/useAccessRightsChange.ts +0 -30
- package/src/components/Form/AccessRights/utils.ts +0 -18
- package/src/components/Tabs/Tab.styles.ts +0 -25
- package/src/components/Tabs/TabPanel.tsx +0 -22
- package/src/components/Tabs/Tabs.cypress.spec.tsx +0 -70
- package/src/components/Tabs/Tabs.stories.tsx +0 -55
- package/src/components/Tabs/Tabs.tsx +0 -55
- package/src/components/Tabs/index.ts +0 -6
- package/src/utils/resourcesStatusURL.ts +0 -166
- package/src/utils/usePluralizedTranslation.test.ts +0 -159
- /package/{public → src/Button}/fonts/roboto-bold-webfont.ttf +0 -0
- /package/{public → src/Button}/fonts/roboto-bold-webfont.woff +0 -0
- /package/{public → src/Button}/fonts/roboto-bold-webfont.woff2 +0 -0
- /package/{public → src/Button}/fonts/roboto-light-webfont.ttf +0 -0
- /package/{public → src/Button}/fonts/roboto-light-webfont.woff +0 -0
- /package/{public → src/Button}/fonts/roboto-light-webfont.woff2 +0 -0
- /package/{public → src/Button}/fonts/roboto-medium-webfont.ttf +0 -0
- /package/{public → src/Button}/fonts/roboto-medium-webfont.woff +0 -0
- /package/{public → src/Button}/fonts/roboto-medium-webfont.woff2 +0 -0
- /package/{public → src/Button}/fonts/roboto-regular-webfont.ttf +0 -0
- /package/{public → src/Button}/fonts/roboto-regular-webfont.woff +0 -0
- /package/{public → src/Button}/fonts/roboto-regular-webfont.woff2 +0 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { memo, useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
BarRounded,
|
|
5
|
+
BarStackHorizontal,
|
|
6
|
+
BarStack as BarStackVertical
|
|
7
|
+
} from '@visx/shape';
|
|
8
|
+
import { Text } from '@visx/text';
|
|
9
|
+
import { equals, props } from 'ramda';
|
|
10
|
+
|
|
11
|
+
import { Tooltip } from '../../components';
|
|
12
|
+
import { getValueByUnit } from '../common/utils';
|
|
13
|
+
|
|
14
|
+
import { useGraphStyles } from './BarStack.styles';
|
|
15
|
+
import { BarStackProps } from './models';
|
|
16
|
+
import { useGraphAndLegend } from './useGraphAndLegend';
|
|
17
|
+
|
|
18
|
+
interface Props
|
|
19
|
+
extends Pick<
|
|
20
|
+
BarStackProps,
|
|
21
|
+
| 'data'
|
|
22
|
+
| 'displayValues'
|
|
23
|
+
| 'onSingleBarClick'
|
|
24
|
+
| 'unit'
|
|
25
|
+
| 'TooltipContent'
|
|
26
|
+
| 'tooltipProps'
|
|
27
|
+
> {
|
|
28
|
+
colorScale;
|
|
29
|
+
height: number;
|
|
30
|
+
isVerticalBar: boolean;
|
|
31
|
+
total: number;
|
|
32
|
+
width: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const Graph = ({
|
|
36
|
+
width,
|
|
37
|
+
height,
|
|
38
|
+
isVerticalBar,
|
|
39
|
+
colorScale,
|
|
40
|
+
data,
|
|
41
|
+
total,
|
|
42
|
+
unit,
|
|
43
|
+
displayValues,
|
|
44
|
+
onSingleBarClick,
|
|
45
|
+
tooltipProps,
|
|
46
|
+
TooltipContent
|
|
47
|
+
}: Props): JSX.Element => {
|
|
48
|
+
const { classes } = useGraphStyles();
|
|
49
|
+
|
|
50
|
+
const BarStackComponent = useMemo(
|
|
51
|
+
() => (isVerticalBar ? BarStackVertical : BarStackHorizontal),
|
|
52
|
+
[isVerticalBar]
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const normalizedHeight = useMemo(() => height - 10, [height]);
|
|
56
|
+
|
|
57
|
+
const { barStackData, xScale, yScale, keys } = useGraphAndLegend({
|
|
58
|
+
data,
|
|
59
|
+
height: normalizedHeight,
|
|
60
|
+
isVerticalBar,
|
|
61
|
+
total,
|
|
62
|
+
width
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<svg height={normalizedHeight} width="100%">
|
|
67
|
+
<BarStackComponent
|
|
68
|
+
color={colorScale}
|
|
69
|
+
data={[barStackData]}
|
|
70
|
+
keys={keys}
|
|
71
|
+
{...(isVerticalBar ? { x: () => undefined } : { y: () => undefined })}
|
|
72
|
+
xScale={xScale}
|
|
73
|
+
yScale={yScale}
|
|
74
|
+
>
|
|
75
|
+
{(barStacks) =>
|
|
76
|
+
barStacks.map((barStack, index) =>
|
|
77
|
+
barStack.bars.map((bar) => {
|
|
78
|
+
const isFirstBar = equals(index, 0);
|
|
79
|
+
const isLastBar = equals(index, barStacks.length - 1);
|
|
80
|
+
const fitsInBar = isVerticalBar
|
|
81
|
+
? bar.height >= 18
|
|
82
|
+
: (equals(unit, 'number') && bar.width > 15) ||
|
|
83
|
+
(equals(unit, 'percentage') && bar.width > 35);
|
|
84
|
+
|
|
85
|
+
const textX = bar.x + bar.width / 2;
|
|
86
|
+
const textY = bar.y + bar.height / 2;
|
|
87
|
+
|
|
88
|
+
const click = onSingleBarClick
|
|
89
|
+
? (e: MouseEvent): void => {
|
|
90
|
+
if (!equals(e.button, 0)) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
onSingleBarClick(bar);
|
|
94
|
+
}
|
|
95
|
+
: undefined;
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<Tooltip
|
|
99
|
+
classes={classes}
|
|
100
|
+
followCursor={false}
|
|
101
|
+
key={`bar-stack-${barStack.index}-${bar.index}`}
|
|
102
|
+
label={
|
|
103
|
+
TooltipContent && (
|
|
104
|
+
<TooltipContent
|
|
105
|
+
color={bar.color}
|
|
106
|
+
label={bar.key}
|
|
107
|
+
total={total}
|
|
108
|
+
value={barStack.bars[0].bar.data[barStack.key]}
|
|
109
|
+
{...tooltipProps}
|
|
110
|
+
/>
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
position={isVerticalBar ? 'right' : 'bottom'}
|
|
114
|
+
>
|
|
115
|
+
<g data-testid={bar.key} key={bar.key}>
|
|
116
|
+
<BarRounded
|
|
117
|
+
bottom={isVerticalBar && isFirstBar}
|
|
118
|
+
cursor={onSingleBarClick ? 'pointer' : 'default'}
|
|
119
|
+
fill={bar.color}
|
|
120
|
+
height={bar.height}
|
|
121
|
+
key={`bar-stack-${barStack.index}-${bar.index}`}
|
|
122
|
+
left={!isVerticalBar && isFirstBar}
|
|
123
|
+
radius={8}
|
|
124
|
+
right={!isVerticalBar && isLastBar}
|
|
125
|
+
top={isVerticalBar && isLastBar}
|
|
126
|
+
width={isVerticalBar ? bar.width - 10 : bar.width}
|
|
127
|
+
x={bar.x}
|
|
128
|
+
y={bar.y}
|
|
129
|
+
onMouseDown={click}
|
|
130
|
+
/>
|
|
131
|
+
{displayValues && fitsInBar && (
|
|
132
|
+
<Text
|
|
133
|
+
cursor={onSingleBarClick ? 'pointer' : 'default'}
|
|
134
|
+
data-testid="value"
|
|
135
|
+
fill="#000"
|
|
136
|
+
fontSize={12}
|
|
137
|
+
fontWeight={600}
|
|
138
|
+
textAnchor="middle"
|
|
139
|
+
verticalAnchor="middle"
|
|
140
|
+
x={textX}
|
|
141
|
+
y={textY}
|
|
142
|
+
onMouseUp={click}
|
|
143
|
+
>
|
|
144
|
+
{getValueByUnit({
|
|
145
|
+
total,
|
|
146
|
+
unit: unit || 'number',
|
|
147
|
+
value: barStack.bars[0].bar.data[barStack.key]
|
|
148
|
+
})}
|
|
149
|
+
</Text>
|
|
150
|
+
)}
|
|
151
|
+
</g>
|
|
152
|
+
</Tooltip>
|
|
153
|
+
);
|
|
154
|
+
})
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
</BarStackComponent>
|
|
158
|
+
</svg>
|
|
159
|
+
);
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const propsToMemoize = [
|
|
163
|
+
'width',
|
|
164
|
+
'height',
|
|
165
|
+
'isVerticalBar',
|
|
166
|
+
'colorScale',
|
|
167
|
+
'data',
|
|
168
|
+
'total',
|
|
169
|
+
'unit',
|
|
170
|
+
'displayValues',
|
|
171
|
+
'tooltipProps'
|
|
172
|
+
];
|
|
173
|
+
|
|
174
|
+
export default memo(Graph, (prevProps, nextProps) =>
|
|
175
|
+
equals(props(propsToMemoize, prevProps), props(propsToMemoize, nextProps))
|
|
176
|
+
);
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { memo, useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
import { equals, props } from 'ramda';
|
|
4
|
+
|
|
5
|
+
import { useGraphAndLegendStyles } from './BarStack.styles';
|
|
6
|
+
import Graph from './Graph';
|
|
7
|
+
import { gap, legendMaxHeight, legendMaxWidth } from './constants';
|
|
8
|
+
import { BarStackProps } from './models';
|
|
9
|
+
|
|
10
|
+
interface Props
|
|
11
|
+
extends Pick<
|
|
12
|
+
BarStackProps,
|
|
13
|
+
| 'data'
|
|
14
|
+
| 'displayValues'
|
|
15
|
+
| 'onSingleBarClick'
|
|
16
|
+
| 'unit'
|
|
17
|
+
| 'TooltipContent'
|
|
18
|
+
| 'tooltipProps'
|
|
19
|
+
> {
|
|
20
|
+
colorScale;
|
|
21
|
+
displayLegend: boolean;
|
|
22
|
+
height: number;
|
|
23
|
+
isVerticalBar: boolean;
|
|
24
|
+
legend: JSX.Element;
|
|
25
|
+
total: number;
|
|
26
|
+
width: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const GraphAndLegend = ({
|
|
30
|
+
isVerticalBar,
|
|
31
|
+
legend,
|
|
32
|
+
displayLegend,
|
|
33
|
+
height,
|
|
34
|
+
width,
|
|
35
|
+
colorScale,
|
|
36
|
+
total,
|
|
37
|
+
unit,
|
|
38
|
+
data,
|
|
39
|
+
displayValues,
|
|
40
|
+
onSingleBarClick,
|
|
41
|
+
tooltipProps,
|
|
42
|
+
TooltipContent
|
|
43
|
+
}: Props): JSX.Element => {
|
|
44
|
+
const { classes } = useGraphAndLegendStyles();
|
|
45
|
+
|
|
46
|
+
const isSmall = useMemo(
|
|
47
|
+
() =>
|
|
48
|
+
(!isVerticalBar && Math.floor(height) <= 80) ||
|
|
49
|
+
(isVerticalBar && Math.floor(width) <= 150),
|
|
50
|
+
[isVerticalBar, height, width]
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const mustDisplayLegend = useMemo(
|
|
54
|
+
() => displayLegend && !isSmall,
|
|
55
|
+
[isSmall, displayLegend]
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const graphWidth = useMemo(
|
|
59
|
+
() =>
|
|
60
|
+
isVerticalBar ? width - (isSmall ? 0 : legendMaxWidth - gap) : width,
|
|
61
|
+
[width, isVerticalBar, isSmall]
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const graphHeight = useMemo(
|
|
65
|
+
() =>
|
|
66
|
+
isVerticalBar ? height : height - (isSmall ? 0 : legendMaxHeight - gap),
|
|
67
|
+
[height, isVerticalBar, isSmall]
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<div
|
|
72
|
+
className={classes.graphAndLegend}
|
|
73
|
+
data-display-legend={mustDisplayLegend}
|
|
74
|
+
data-is-vertical={isVerticalBar}
|
|
75
|
+
style={{ height }}
|
|
76
|
+
>
|
|
77
|
+
<Graph
|
|
78
|
+
TooltipContent={TooltipContent}
|
|
79
|
+
colorScale={colorScale}
|
|
80
|
+
data={data}
|
|
81
|
+
displayValues={displayValues}
|
|
82
|
+
height={graphHeight}
|
|
83
|
+
isVerticalBar={isVerticalBar}
|
|
84
|
+
tooltipProps={tooltipProps}
|
|
85
|
+
total={total}
|
|
86
|
+
unit={unit}
|
|
87
|
+
width={graphWidth}
|
|
88
|
+
onSingleBarClick={onSingleBarClick}
|
|
89
|
+
/>
|
|
90
|
+
{mustDisplayLegend && (
|
|
91
|
+
<div
|
|
92
|
+
className={classes.legend}
|
|
93
|
+
data-is-vertical={isVerticalBar}
|
|
94
|
+
data-testid="Legend"
|
|
95
|
+
>
|
|
96
|
+
{legend}
|
|
97
|
+
</div>
|
|
98
|
+
)}
|
|
99
|
+
</div>
|
|
100
|
+
);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const propsToMemoize = [
|
|
104
|
+
'width',
|
|
105
|
+
'height',
|
|
106
|
+
'isVerticalBar',
|
|
107
|
+
'colorScale',
|
|
108
|
+
'data',
|
|
109
|
+
'total',
|
|
110
|
+
'unit',
|
|
111
|
+
'displayValues',
|
|
112
|
+
'tooltipProps',
|
|
113
|
+
'displayLegend',
|
|
114
|
+
'legend'
|
|
115
|
+
];
|
|
116
|
+
|
|
117
|
+
export default memo(GraphAndLegend, (prevProps, nextProps) =>
|
|
118
|
+
equals(props(propsToMemoize, prevProps), props(propsToMemoize, nextProps))
|
|
119
|
+
);
|
|
@@ -1,19 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
2
|
|
|
3
|
-
import { BarStack as BarStackVertical, BarStackHorizontal } from '@visx/shape';
|
|
4
|
-
import { Group } from '@visx/group';
|
|
5
3
|
import numeral from 'numeral';
|
|
6
|
-
import { Text } from '@visx/text';
|
|
7
|
-
import { useTranslation } from 'react-i18next';
|
|
8
4
|
import { equals } from 'ramda';
|
|
5
|
+
import { useTranslation } from 'react-i18next';
|
|
6
|
+
|
|
7
|
+
import { Typography } from '@mui/material';
|
|
9
8
|
|
|
10
|
-
import { Tooltip } from '../../components';
|
|
11
|
-
import { LegendProps } from '../Legend/models';
|
|
12
9
|
import { Legend as LegendComponent } from '../Legend';
|
|
13
|
-
import {
|
|
10
|
+
import { LegendProps } from '../Legend/models';
|
|
14
11
|
|
|
12
|
+
import { useStyles } from './BarStack.styles';
|
|
13
|
+
import GraphAndLegend from './GraphAndLegend';
|
|
14
|
+
import { gap, smallTitleHeight, titleHeight } from './constants';
|
|
15
15
|
import { BarStackProps } from './models';
|
|
16
|
-
import { useBarStackStyles } from './BarStack.styles';
|
|
17
16
|
import useResponsiveBarStack from './useResponsiveBarStack';
|
|
18
17
|
|
|
19
18
|
const DefaultLengd = ({ scale, direction }: LegendProps): JSX.Element => (
|
|
@@ -23,9 +22,8 @@ const DefaultLengd = ({ scale, direction }: LegendProps): JSX.Element => (
|
|
|
23
22
|
const BarStack = ({
|
|
24
23
|
title,
|
|
25
24
|
data,
|
|
26
|
-
width,
|
|
27
25
|
height,
|
|
28
|
-
|
|
26
|
+
width,
|
|
29
27
|
onSingleBarClick,
|
|
30
28
|
displayLegend = true,
|
|
31
29
|
TooltipContent,
|
|
@@ -33,175 +31,82 @@ const BarStack = ({
|
|
|
33
31
|
unit = 'number',
|
|
34
32
|
displayValues,
|
|
35
33
|
variant = 'vertical',
|
|
36
|
-
legendDirection
|
|
34
|
+
legendDirection,
|
|
35
|
+
tooltipProps = {}
|
|
37
36
|
}: BarStackProps & { height: number; width: number }): JSX.Element => {
|
|
37
|
+
const { classes, cx } = useStyles();
|
|
38
38
|
const { t } = useTranslation();
|
|
39
|
-
const { classes } = useBarStackStyles();
|
|
40
|
-
|
|
41
|
-
const titleRef = useRef(null);
|
|
42
|
-
const legendRef = useRef(null);
|
|
43
39
|
|
|
44
40
|
const {
|
|
45
|
-
barSize,
|
|
46
|
-
colorScale,
|
|
47
|
-
input,
|
|
48
|
-
keys,
|
|
49
|
-
legendScale,
|
|
50
41
|
total,
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
42
|
+
titleVariant,
|
|
43
|
+
legendScale,
|
|
44
|
+
isVerticalBar,
|
|
45
|
+
colorScale,
|
|
46
|
+
formattedLegendDirection
|
|
56
47
|
} = useResponsiveBarStack({
|
|
57
48
|
data,
|
|
58
49
|
height,
|
|
59
|
-
|
|
60
|
-
size,
|
|
61
|
-
titleRef,
|
|
50
|
+
legendDirection,
|
|
62
51
|
unit,
|
|
63
52
|
variant,
|
|
64
53
|
width
|
|
65
54
|
});
|
|
66
55
|
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
return (
|
|
72
|
-
<div className={classes.container} style={{ height, width }}>
|
|
73
|
-
<div
|
|
74
|
-
className={classes.svgWrapper}
|
|
75
|
-
style={{
|
|
76
|
-
height,
|
|
77
|
-
width: svgWrapperWidth
|
|
78
|
-
}}
|
|
79
|
-
>
|
|
80
|
-
{title && (
|
|
81
|
-
<div className={classes.title} data-testid="Title" ref={titleRef}>
|
|
82
|
-
{`${numeral(total).format('0a').toUpperCase()} `} {t(title)}
|
|
83
|
-
</div>
|
|
84
|
-
)}
|
|
85
|
-
<div
|
|
86
|
-
className={classes.svgContainer}
|
|
87
|
-
data-testid="barStack"
|
|
88
|
-
style={svgContainerSize}
|
|
89
|
-
>
|
|
90
|
-
<svg
|
|
91
|
-
data-variant={variant}
|
|
92
|
-
height={barSize.height}
|
|
93
|
-
width={barSize.width}
|
|
94
|
-
>
|
|
95
|
-
<Group>
|
|
96
|
-
<BarStackComponent
|
|
97
|
-
color={colorScale}
|
|
98
|
-
data={[input]}
|
|
99
|
-
keys={keys}
|
|
100
|
-
{...(isVerticalBar
|
|
101
|
-
? { x: () => undefined }
|
|
102
|
-
: { y: () => undefined })}
|
|
103
|
-
xScale={xScale}
|
|
104
|
-
yScale={yScale}
|
|
105
|
-
>
|
|
106
|
-
{(barStacks) =>
|
|
107
|
-
barStacks.map((barStack) =>
|
|
108
|
-
barStack.bars.map((bar) => {
|
|
109
|
-
const shouldDisplayValues = (): boolean => {
|
|
110
|
-
if (bar.height < 10) {
|
|
111
|
-
return false;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return (
|
|
115
|
-
(equals(unit, 'number') && bar.width > 10) ||
|
|
116
|
-
(equals(unit, 'percentage') && bar.width > 25)
|
|
117
|
-
);
|
|
118
|
-
};
|
|
56
|
+
const graphAndLegendHeight = useMemo(() => {
|
|
57
|
+
if (equals(titleVariant, 'xs')) {
|
|
58
|
+
return height - 2 * smallTitleHeight - gap;
|
|
59
|
+
}
|
|
119
60
|
|
|
120
|
-
|
|
121
|
-
|
|
61
|
+
if (equals(titleVariant, 'sm')) {
|
|
62
|
+
return height - smallTitleHeight - gap;
|
|
63
|
+
}
|
|
122
64
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
};
|
|
65
|
+
return height - titleHeight - gap;
|
|
66
|
+
}, [titleVariant, height]);
|
|
126
67
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
height={
|
|
155
|
-
isVerticalBar ? bar.height - 1 : bar.height
|
|
156
|
-
}
|
|
157
|
-
key={`bar-stack-${barStack.index}-${bar.index}`}
|
|
158
|
-
ry={10}
|
|
159
|
-
width={isVerticalBar ? bar.width : bar.width - 1}
|
|
160
|
-
x={bar.x}
|
|
161
|
-
y={bar.y}
|
|
162
|
-
/>
|
|
163
|
-
{displayValues && shouldDisplayValues() && (
|
|
164
|
-
<Text
|
|
165
|
-
cursor="pointer"
|
|
166
|
-
data-testid="value"
|
|
167
|
-
fill="#000"
|
|
168
|
-
fontSize={12}
|
|
169
|
-
fontWeight={600}
|
|
170
|
-
textAnchor="middle"
|
|
171
|
-
verticalAnchor="middle"
|
|
172
|
-
x={TextX}
|
|
173
|
-
y={TextY}
|
|
174
|
-
>
|
|
175
|
-
{getValueByUnit({
|
|
176
|
-
total,
|
|
177
|
-
unit,
|
|
178
|
-
value: barStack.bars[0].bar.data[barStack.key]
|
|
179
|
-
})}
|
|
180
|
-
</Text>
|
|
181
|
-
)}
|
|
182
|
-
</g>
|
|
183
|
-
</Tooltip>
|
|
184
|
-
);
|
|
185
|
-
})
|
|
186
|
-
)
|
|
187
|
-
}
|
|
188
|
-
</BarStackComponent>
|
|
189
|
-
</Group>
|
|
190
|
-
</svg>
|
|
191
|
-
</div>
|
|
192
|
-
</div>
|
|
193
|
-
{displayLegend && (
|
|
194
|
-
<div data-testid="Legend" ref={legendRef}>
|
|
68
|
+
return (
|
|
69
|
+
<div
|
|
70
|
+
className={classes.container}
|
|
71
|
+
data-has-title={!!title}
|
|
72
|
+
data-title-variant={titleVariant}
|
|
73
|
+
data-variant={variant}
|
|
74
|
+
>
|
|
75
|
+
{title && (
|
|
76
|
+
<Typography
|
|
77
|
+
className={cx(equals(titleVariant, 'md') && classes.clippedTitle)}
|
|
78
|
+
data-testid="Title"
|
|
79
|
+
fontWeight="bold"
|
|
80
|
+
textAlign="center"
|
|
81
|
+
variant={equals(titleVariant, 'md') ? 'h6' : 'body1'}
|
|
82
|
+
>
|
|
83
|
+
{`${numeral(total).format('0a')}`} {t(title)}
|
|
84
|
+
</Typography>
|
|
85
|
+
)}
|
|
86
|
+
<GraphAndLegend
|
|
87
|
+
TooltipContent={TooltipContent}
|
|
88
|
+
colorScale={colorScale}
|
|
89
|
+
data={data}
|
|
90
|
+
displayLegend={displayLegend}
|
|
91
|
+
displayValues={displayValues}
|
|
92
|
+
height={graphAndLegendHeight}
|
|
93
|
+
isVerticalBar={isVerticalBar}
|
|
94
|
+
legend={
|
|
195
95
|
<Legend
|
|
196
96
|
data={data}
|
|
197
|
-
direction={
|
|
97
|
+
direction={formattedLegendDirection}
|
|
198
98
|
scale={legendScale}
|
|
199
99
|
title={title}
|
|
200
100
|
total={total}
|
|
201
101
|
unit={unit}
|
|
202
102
|
/>
|
|
203
|
-
|
|
204
|
-
|
|
103
|
+
}
|
|
104
|
+
tooltipProps={tooltipProps}
|
|
105
|
+
total={total}
|
|
106
|
+
unit={unit}
|
|
107
|
+
width={width}
|
|
108
|
+
onSingleBarClick={onSingleBarClick}
|
|
109
|
+
/>
|
|
205
110
|
</div>
|
|
206
111
|
);
|
|
207
112
|
};
|
|
@@ -12,8 +12,8 @@ export type BarStackProps = {
|
|
|
12
12
|
displayValues?: boolean;
|
|
13
13
|
legendDirection?: 'row' | 'column';
|
|
14
14
|
onSingleBarClick?: (barData) => void;
|
|
15
|
-
size?: number;
|
|
16
15
|
title?: string;
|
|
16
|
+
tooltipProps?: object;
|
|
17
17
|
unit?: 'percentage' | 'number';
|
|
18
18
|
variant?: 'vertical' | 'horizontal';
|
|
19
19
|
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
import { scaleBand, scaleLinear } from '@visx/scale';
|
|
4
|
+
import { pluck } from 'ramda';
|
|
5
|
+
|
|
6
|
+
import { BarType } from './models';
|
|
7
|
+
|
|
8
|
+
interface UseGraphandLegendProps {
|
|
9
|
+
data: Array<BarType>;
|
|
10
|
+
height: number;
|
|
11
|
+
isVerticalBar: boolean;
|
|
12
|
+
total: number;
|
|
13
|
+
width: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface UseGraphAndLegendState {
|
|
17
|
+
barStackData: Record<string, number>;
|
|
18
|
+
keys: Array<string>;
|
|
19
|
+
xScale;
|
|
20
|
+
yScale;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const useGraphAndLegend = ({
|
|
24
|
+
data,
|
|
25
|
+
isVerticalBar,
|
|
26
|
+
total,
|
|
27
|
+
width,
|
|
28
|
+
height
|
|
29
|
+
}: UseGraphandLegendProps): UseGraphAndLegendState => {
|
|
30
|
+
const dataWithNonEmptyValue = useMemo(
|
|
31
|
+
() => data.filter(({ value }) => value),
|
|
32
|
+
[data]
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const keys = useMemo(
|
|
36
|
+
() => pluck('label', dataWithNonEmptyValue),
|
|
37
|
+
[dataWithNonEmptyValue]
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const barStackData = useMemo(
|
|
41
|
+
() =>
|
|
42
|
+
dataWithNonEmptyValue.reduce((acc, { label, value }) => {
|
|
43
|
+
acc[label] = value;
|
|
44
|
+
|
|
45
|
+
return acc;
|
|
46
|
+
}, {}),
|
|
47
|
+
[dataWithNonEmptyValue]
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
const yScale = useMemo(
|
|
51
|
+
() =>
|
|
52
|
+
isVerticalBar
|
|
53
|
+
? scaleLinear({
|
|
54
|
+
domain: [0, total],
|
|
55
|
+
range: [height, 0]
|
|
56
|
+
})
|
|
57
|
+
: scaleBand({
|
|
58
|
+
domain: [0, 0],
|
|
59
|
+
padding: 0,
|
|
60
|
+
range: [height, 0]
|
|
61
|
+
}),
|
|
62
|
+
[isVerticalBar, total, height]
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const xScale = useMemo(
|
|
66
|
+
() =>
|
|
67
|
+
isVerticalBar
|
|
68
|
+
? scaleBand({
|
|
69
|
+
domain: [0, 0],
|
|
70
|
+
padding: 0,
|
|
71
|
+
range: [0, width]
|
|
72
|
+
})
|
|
73
|
+
: scaleLinear({
|
|
74
|
+
domain: [0, total],
|
|
75
|
+
range: [0, width]
|
|
76
|
+
}),
|
|
77
|
+
[total, width, isVerticalBar]
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
barStackData,
|
|
82
|
+
keys,
|
|
83
|
+
xScale,
|
|
84
|
+
yScale
|
|
85
|
+
};
|
|
86
|
+
};
|