@centreon/ui 24.4.1-MON-34459-fullscreen-mode.0 → 24.4.1-MON-develop-dashboard.1
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 +4 -1
- package/src/Graph/BarStack/BarStack.cypress.spec.tsx +154 -0
- package/src/Graph/BarStack/BarStack.stories.tsx +123 -0
- package/src/Graph/BarStack/BarStack.styles.ts +36 -0
- package/src/Graph/BarStack/BarStack.tsx +14 -0
- package/src/Graph/BarStack/ResponsiveBarStack.tsx +208 -0
- package/src/Graph/BarStack/index.ts +1 -0
- package/src/Graph/BarStack/models.ts +19 -0
- package/src/Graph/BarStack/useResponsiveBarStack.ts +139 -0
- package/src/Graph/Legend/Legend.tsx +21 -0
- package/src/Graph/Legend/index.ts +1 -0
- package/src/Graph/Legend/models.ts +11 -0
- package/src/Graph/PieChart/PieChart.cypress.spec.tsx +169 -0
- package/src/Graph/PieChart/PieChart.stories.tsx +194 -0
- package/src/Graph/PieChart/PieChart.styles.ts +39 -0
- package/src/Graph/PieChart/PieChart.tsx +14 -0
- package/src/Graph/PieChart/ResponsivePie.tsx +251 -0
- package/src/Graph/PieChart/index.ts +1 -0
- package/src/Graph/PieChart/models.ts +19 -0
- package/src/Graph/PieChart/useResponsivePie.ts +86 -0
- package/src/Graph/common/utils.ts +19 -0
- package/src/Graph/index.ts +2 -0
- package/src/Graph/translatedLabels.ts +1 -0
- package/src/Listing/ActionBar/index.tsx +9 -8
- package/src/Listing/Cell/DataCell.styles.ts +3 -0
- package/src/Listing/Cell/DataCell.tsx +8 -4
- package/src/Listing/Listing.cypress.spec.tsx +80 -4
- package/src/Listing/Listing.styles.ts +3 -5
- package/src/Listing/index.stories.tsx +25 -2
- package/src/Listing/index.test.tsx +1 -1
- package/src/Listing/index.tsx +3 -1
- package/src/Listing/models.ts +1 -0
- package/src/components/Form/AccessRights/ShareInput/ContactSwitch.tsx +3 -3
- package/src/components/Form/AccessRights/ShareInput/ShareInput.tsx +1 -0
- package/src/utils/index.ts +0 -1
- package/src/utils/useFullscreen/Fullscreen.cypress.spec.tsx +0 -109
- package/src/utils/useFullscreen/atoms.ts +0 -3
- package/src/utils/useFullscreen/index.ts +0 -2
- package/src/utils/useFullscreen/translatedLabels.ts +0 -1
- package/src/utils/useFullscreen/useFullscreen.ts +0 -73
- package/src/utils/useFullscreen/useFullscreenListener.ts +0 -54
|
@@ -0,0 +1,251 @@
|
|
|
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 } 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
|
+
}: PieProps & { height: number; width: number }): JSX.Element => {
|
|
59
|
+
const { t } = useTranslation();
|
|
60
|
+
const theme = useTheme();
|
|
61
|
+
|
|
62
|
+
const legendRef = useRef(null);
|
|
63
|
+
const titleRef = useRef(null);
|
|
64
|
+
|
|
65
|
+
const {
|
|
66
|
+
half,
|
|
67
|
+
legendScale,
|
|
68
|
+
svgContainerSize,
|
|
69
|
+
svgSize,
|
|
70
|
+
svgWrapperWidth,
|
|
71
|
+
total,
|
|
72
|
+
innerRadius,
|
|
73
|
+
isContainsExactlyOneNonZeroValue,
|
|
74
|
+
areAllValuesNull
|
|
75
|
+
} = useResponsivePie({
|
|
76
|
+
data,
|
|
77
|
+
defaultInnerRadius,
|
|
78
|
+
height,
|
|
79
|
+
legendRef,
|
|
80
|
+
titleRef,
|
|
81
|
+
unit,
|
|
82
|
+
width
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const { classes } = usePieStyles({ svgSize });
|
|
86
|
+
|
|
87
|
+
if (areAllValuesNull) {
|
|
88
|
+
return (
|
|
89
|
+
<div className={classes.container} style={{ height, width }}>
|
|
90
|
+
<Typography variant="h3">{t(labelNoDataFound)}</Typography>
|
|
91
|
+
</div>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
<div
|
|
97
|
+
className={classes.container}
|
|
98
|
+
style={{
|
|
99
|
+
height,
|
|
100
|
+
width
|
|
101
|
+
}}
|
|
102
|
+
>
|
|
103
|
+
<div
|
|
104
|
+
className={classes.svgWrapper}
|
|
105
|
+
style={{
|
|
106
|
+
height,
|
|
107
|
+
width: svgWrapperWidth
|
|
108
|
+
}}
|
|
109
|
+
>
|
|
110
|
+
{equals(variant, 'pie') && title && (
|
|
111
|
+
<div className={classes.title} data-testid="Title" ref={titleRef}>
|
|
112
|
+
{`${numeral(total).format('0a').toUpperCase()} `} {title}
|
|
113
|
+
</div>
|
|
114
|
+
)}
|
|
115
|
+
<div
|
|
116
|
+
className={classes.svgContainer}
|
|
117
|
+
data-testid="pieChart"
|
|
118
|
+
style={{
|
|
119
|
+
height: svgContainerSize,
|
|
120
|
+
width: svgContainerSize
|
|
121
|
+
}}
|
|
122
|
+
>
|
|
123
|
+
<svg data-variant={variant} height={svgSize} width={svgSize}>
|
|
124
|
+
<Group left={half} top={half}>
|
|
125
|
+
<Pie
|
|
126
|
+
cornerRadius={4}
|
|
127
|
+
data={data}
|
|
128
|
+
innerRadius={() => {
|
|
129
|
+
return equals(variant, 'pie') ? 0 : half - innerRadius;
|
|
130
|
+
}}
|
|
131
|
+
outerRadius={half}
|
|
132
|
+
pieValue={(items) => items.value}
|
|
133
|
+
>
|
|
134
|
+
{(pie) => {
|
|
135
|
+
return pie.arcs.map((arc) => {
|
|
136
|
+
const [centroidX, centroidY] = pie.path.centroid(arc);
|
|
137
|
+
const midAngle = Math.atan2(centroidY, centroidX);
|
|
138
|
+
|
|
139
|
+
const labelRadius = half * 0.8;
|
|
140
|
+
|
|
141
|
+
const labelX = Math.cos(midAngle) * labelRadius;
|
|
142
|
+
const labelY = Math.sin(midAngle) * labelRadius;
|
|
143
|
+
|
|
144
|
+
const angle = arc.endAngle - arc.startAngle;
|
|
145
|
+
const minAngle = 0.2;
|
|
146
|
+
|
|
147
|
+
const x = equals(variant, 'donut') ? centroidX : labelX;
|
|
148
|
+
const y = equals(variant, 'donut') ? centroidY : labelY;
|
|
149
|
+
|
|
150
|
+
const onClick = (): void => {
|
|
151
|
+
onArcClick?.(arc.data);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
return (
|
|
155
|
+
<Tooltip
|
|
156
|
+
hasCaret
|
|
157
|
+
classes={{
|
|
158
|
+
tooltip: classes.pieChartTooltip
|
|
159
|
+
}}
|
|
160
|
+
followCursor={false}
|
|
161
|
+
key={arc.data.label}
|
|
162
|
+
label={
|
|
163
|
+
TooltipContent && (
|
|
164
|
+
<TooltipContent
|
|
165
|
+
color={arc.data.color}
|
|
166
|
+
label={arc.data.label}
|
|
167
|
+
title={title}
|
|
168
|
+
total={total}
|
|
169
|
+
value={arc.data.value}
|
|
170
|
+
/>
|
|
171
|
+
)
|
|
172
|
+
}
|
|
173
|
+
leaveDelay={200}
|
|
174
|
+
placement={getTooltipPlacement({
|
|
175
|
+
radianX: Math.cos(midAngle),
|
|
176
|
+
radianY: Math.sin(midAngle)
|
|
177
|
+
})}
|
|
178
|
+
>
|
|
179
|
+
<g data-testid={arc.data.label} onClick={onClick}>
|
|
180
|
+
<path
|
|
181
|
+
d={pie.path(arc) as string}
|
|
182
|
+
fill={arc.data.color}
|
|
183
|
+
/>
|
|
184
|
+
{displayValues &&
|
|
185
|
+
!isContainsExactlyOneNonZeroValue &&
|
|
186
|
+
angle > minAngle && (
|
|
187
|
+
<Text
|
|
188
|
+
data-testid="value"
|
|
189
|
+
dy=".33em"
|
|
190
|
+
fill="#000"
|
|
191
|
+
fontSize={12}
|
|
192
|
+
pointerEvents="none"
|
|
193
|
+
textAnchor="middle"
|
|
194
|
+
x={x}
|
|
195
|
+
y={y}
|
|
196
|
+
>
|
|
197
|
+
{getValueByUnit({
|
|
198
|
+
total,
|
|
199
|
+
unit,
|
|
200
|
+
value: arc.data.value
|
|
201
|
+
})}
|
|
202
|
+
</Text>
|
|
203
|
+
)}
|
|
204
|
+
</g>
|
|
205
|
+
</Tooltip>
|
|
206
|
+
);
|
|
207
|
+
});
|
|
208
|
+
}}
|
|
209
|
+
</Pie>
|
|
210
|
+
{equals(variant, 'donut') && title && (
|
|
211
|
+
<>
|
|
212
|
+
<Text
|
|
213
|
+
className={classes.title}
|
|
214
|
+
dy={lt(svgSize, 150) ? -10 : -15}
|
|
215
|
+
fill={theme.palette.text.primary}
|
|
216
|
+
textAnchor="middle"
|
|
217
|
+
>
|
|
218
|
+
{numeral(total).format('0a').toUpperCase()}
|
|
219
|
+
</Text>
|
|
220
|
+
<Text
|
|
221
|
+
className={classes.title}
|
|
222
|
+
data-testid="Title"
|
|
223
|
+
dy={lt(svgSize, 150) ? 10 : 15}
|
|
224
|
+
fill={theme.palette.text.primary}
|
|
225
|
+
textAnchor="middle"
|
|
226
|
+
>
|
|
227
|
+
{title}
|
|
228
|
+
</Text>
|
|
229
|
+
</>
|
|
230
|
+
)}
|
|
231
|
+
</Group>
|
|
232
|
+
</svg>
|
|
233
|
+
</div>
|
|
234
|
+
</div>
|
|
235
|
+
{displayLegend && (
|
|
236
|
+
<div data-testid="Legend" ref={legendRef}>
|
|
237
|
+
<Legend
|
|
238
|
+
data={data}
|
|
239
|
+
direction={legendDirection}
|
|
240
|
+
scale={legendScale}
|
|
241
|
+
title={title}
|
|
242
|
+
total={total}
|
|
243
|
+
unit={unit}
|
|
244
|
+
/>
|
|
245
|
+
</div>
|
|
246
|
+
)}
|
|
247
|
+
</div>
|
|
248
|
+
);
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
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
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
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 svgWrapperWidth = width - widthOfLegend - horizontalGap;
|
|
45
|
+
|
|
46
|
+
const svgContainerSize = Math.min(
|
|
47
|
+
height - heightOfTitle - verticalGap,
|
|
48
|
+
width - widthOfLegend - horizontalGap
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
const outerRadius = Math.min(32, svgContainerSize / 6);
|
|
52
|
+
|
|
53
|
+
const svgSize = svgContainerSize - outerRadius;
|
|
54
|
+
|
|
55
|
+
const half = svgSize / 2;
|
|
56
|
+
|
|
57
|
+
const total = Math.floor(data.reduce((acc, { value }) => acc + value, 0));
|
|
58
|
+
|
|
59
|
+
const innerRadius = Math.min(defaultInnerRadius, svgSize / 5);
|
|
60
|
+
|
|
61
|
+
const legendScale = {
|
|
62
|
+
domain: data.map(({ value }) => getValueByUnit({ total, unit, value })),
|
|
63
|
+
range: pluck('color', data)
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const values = pluck('value', data);
|
|
67
|
+
|
|
68
|
+
const isContainsExactlyOneNonZeroValue = equals(
|
|
69
|
+
reject((value) => equals(value, 0), values).length,
|
|
70
|
+
1
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const areAllValuesNull = isEmpty(reject((value) => equals(value, 0), values));
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
areAllValuesNull,
|
|
77
|
+
half,
|
|
78
|
+
innerRadius,
|
|
79
|
+
isContainsExactlyOneNonZeroValue,
|
|
80
|
+
legendScale,
|
|
81
|
+
svgContainerSize,
|
|
82
|
+
svgSize,
|
|
83
|
+
svgWrapperWidth,
|
|
84
|
+
total
|
|
85
|
+
};
|
|
86
|
+
};
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
lte,
|
|
12
12
|
pluck
|
|
13
13
|
} from 'ramda';
|
|
14
|
+
import numeral from 'numeral';
|
|
14
15
|
|
|
15
16
|
import { Theme } from '@mui/material';
|
|
16
17
|
|
|
@@ -73,3 +74,21 @@ export const getColorFromDataAndTresholds = ({
|
|
|
73
74
|
[T, always(theme.palette.error.main)]
|
|
74
75
|
])(data);
|
|
75
76
|
};
|
|
77
|
+
|
|
78
|
+
interface ValueByUnitProps {
|
|
79
|
+
total: number;
|
|
80
|
+
unit: 'percentage' | 'number';
|
|
81
|
+
value: number;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export const getValueByUnit = ({
|
|
85
|
+
unit,
|
|
86
|
+
value,
|
|
87
|
+
total
|
|
88
|
+
}: ValueByUnitProps): string => {
|
|
89
|
+
if (equals(unit, 'number')) {
|
|
90
|
+
return numeral(value).format('0a').toUpperCase();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return `${((value * 100) / total).toFixed(1)}%`;
|
|
94
|
+
};
|
package/src/Graph/index.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const labelNoDataFound = 'No Data Available';
|
|
@@ -174,14 +174,15 @@ const MemoListingActionBar = ({
|
|
|
174
174
|
</IconButton>
|
|
175
175
|
)}
|
|
176
176
|
<div className={classes.ModeViewer} />
|
|
177
|
-
{columnConfiguration?.selectedColumnIds &&
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
177
|
+
{columnConfiguration?.selectedColumnIds &&
|
|
178
|
+
columnConfiguration?.sortable && (
|
|
179
|
+
<ColumnMultiSelect
|
|
180
|
+
columnConfiguration={columnConfiguration}
|
|
181
|
+
columns={columns}
|
|
182
|
+
onResetColumns={onResetColumns}
|
|
183
|
+
onSelectColumns={onSelectColumns}
|
|
184
|
+
/>
|
|
185
|
+
)}
|
|
185
186
|
{paginated && (
|
|
186
187
|
<StyledPagination
|
|
187
188
|
ActionsComponent={PaginationActions}
|
|
@@ -46,7 +46,7 @@ const DataCell = ({
|
|
|
46
46
|
labelCollapse,
|
|
47
47
|
labelExpand
|
|
48
48
|
}: Props): JSX.Element | null => {
|
|
49
|
-
const { classes } = useStyles();
|
|
49
|
+
const { classes, cx } = useStyles();
|
|
50
50
|
const { dataStyle } = useStyleTable({ listingVariant });
|
|
51
51
|
|
|
52
52
|
const commonCellProps = {
|
|
@@ -64,7 +64,7 @@ const DataCell = ({
|
|
|
64
64
|
|
|
65
65
|
const cellByColumnType = {
|
|
66
66
|
[ColumnType.string]: (): JSX.Element => {
|
|
67
|
-
const { getFormattedString, isTruncated, getColSpan } = column;
|
|
67
|
+
const { getFormattedString, isTruncated, getColSpan, align } = column;
|
|
68
68
|
|
|
69
69
|
const colSpan = getColSpan?.(isRowSelected);
|
|
70
70
|
|
|
@@ -87,6 +87,7 @@ const DataCell = ({
|
|
|
87
87
|
isRowHighlighted={isRowHighlighted}
|
|
88
88
|
listingVariant={listingVariant}
|
|
89
89
|
style={{
|
|
90
|
+
alignItems: align,
|
|
90
91
|
gridColumn
|
|
91
92
|
}}
|
|
92
93
|
{...commonCellProps}
|
|
@@ -99,7 +100,7 @@ const DataCell = ({
|
|
|
99
100
|
);
|
|
100
101
|
},
|
|
101
102
|
[ColumnType.component]: (): JSX.Element | null => {
|
|
102
|
-
const { getHiddenCondition, clickable } = column;
|
|
103
|
+
const { getHiddenCondition, clickable, align } = column;
|
|
103
104
|
const Component = column.Component as (
|
|
104
105
|
props: ComponentColumnProps
|
|
105
106
|
) => JSX.Element;
|
|
@@ -126,9 +127,12 @@ const DataCell = ({
|
|
|
126
127
|
|
|
127
128
|
return (
|
|
128
129
|
<Cell
|
|
129
|
-
className={classes.cell}
|
|
130
|
+
className={cx(classes.cell, clickable && classes.clickable)}
|
|
130
131
|
isRowHighlighted={isRowHighlighted}
|
|
131
132
|
listingVariant={listingVariant}
|
|
133
|
+
style={{
|
|
134
|
+
alignItems: align
|
|
135
|
+
}}
|
|
132
136
|
onClick={(e): void => {
|
|
133
137
|
if (!clickable) {
|
|
134
138
|
return;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { Button } from '@mui/material';
|
|
1
|
+
import { Button, Typography } from '@mui/material';
|
|
2
|
+
|
|
3
|
+
import { ListingVariant } from '@centreon/ui-context';
|
|
2
4
|
|
|
3
5
|
import { ColumnType } from './models';
|
|
4
6
|
|
|
@@ -12,6 +14,12 @@ const ButtonColumn = ({ row }: Props): JSX.Element => (
|
|
|
12
14
|
<Button size="small">Click to reveal details about {row.name}</Button>
|
|
13
15
|
);
|
|
14
16
|
|
|
17
|
+
const LargeText = (): JSX.Element => (
|
|
18
|
+
<Typography sx={{ whiteSpace: 'normal' }}>
|
|
19
|
+
This is a large text that fills the content
|
|
20
|
+
</Typography>
|
|
21
|
+
);
|
|
22
|
+
|
|
15
23
|
const generateSubItems = (parentIndex: number): Array<unknown> => {
|
|
16
24
|
return tenElements.map((__, subIndex) => ({
|
|
17
25
|
active: false,
|
|
@@ -37,6 +45,28 @@ const listingWithSubItems = tenElements.map((_, index) => ({
|
|
|
37
45
|
subItems: index % 2 === 0 ? generateSubItems(index) : undefined
|
|
38
46
|
}));
|
|
39
47
|
|
|
48
|
+
const defaultColumn = [
|
|
49
|
+
{
|
|
50
|
+
getFormattedString: ({ name }): string => name,
|
|
51
|
+
id: 'name',
|
|
52
|
+
label: 'Name',
|
|
53
|
+
type: ColumnType.string
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
getFormattedString: ({ description }): string => description,
|
|
57
|
+
id: 'description',
|
|
58
|
+
label: 'Description',
|
|
59
|
+
type: ColumnType.string
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
Component: LargeText,
|
|
63
|
+
id: '#',
|
|
64
|
+
label: 'Custom',
|
|
65
|
+
type: ColumnType.component,
|
|
66
|
+
width: '100px'
|
|
67
|
+
}
|
|
68
|
+
];
|
|
69
|
+
|
|
40
70
|
const columnsWithSubItems = [
|
|
41
71
|
{
|
|
42
72
|
getFormattedString: ({ name }): string => name,
|
|
@@ -62,7 +92,27 @@ const columnsWithSubItems = [
|
|
|
62
92
|
|
|
63
93
|
const expandedItems = [0, 8];
|
|
64
94
|
|
|
65
|
-
const
|
|
95
|
+
const mountListingResponsive = (listingVariant: ListingVariant): void => {
|
|
96
|
+
cy.viewport('macbook-13');
|
|
97
|
+
|
|
98
|
+
cy.mount({
|
|
99
|
+
Component: (
|
|
100
|
+
<div style={{ height: '100vh' }}>
|
|
101
|
+
<Listing
|
|
102
|
+
isResponsive
|
|
103
|
+
columns={defaultColumn}
|
|
104
|
+
currentPage={1}
|
|
105
|
+
limit={10}
|
|
106
|
+
listingVariant={listingVariant}
|
|
107
|
+
rows={listingWithSubItems}
|
|
108
|
+
totalRows={10}
|
|
109
|
+
/>
|
|
110
|
+
</div>
|
|
111
|
+
)
|
|
112
|
+
});
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const mountListingForSubItems = (): void => {
|
|
66
116
|
cy.viewport('macbook-13');
|
|
67
117
|
|
|
68
118
|
cy.mount({
|
|
@@ -89,9 +139,9 @@ const mountListing = (): void => {
|
|
|
89
139
|
};
|
|
90
140
|
|
|
91
141
|
describe('Listing', () => {
|
|
92
|
-
beforeEach(mountListing);
|
|
93
|
-
|
|
94
142
|
it('expands the row when the corresponding icon si clicked', () => {
|
|
143
|
+
mountListingForSubItems();
|
|
144
|
+
|
|
95
145
|
cy.contains('E0').should('be.visible');
|
|
96
146
|
|
|
97
147
|
expandedItems.forEach((index) => {
|
|
@@ -111,6 +161,8 @@ describe('Listing', () => {
|
|
|
111
161
|
});
|
|
112
162
|
|
|
113
163
|
it('collapses the row when the corresponding icon si clicked', () => {
|
|
164
|
+
mountListingForSubItems();
|
|
165
|
+
|
|
114
166
|
cy.contains('Sub item 100').should('be.visible');
|
|
115
167
|
|
|
116
168
|
cy.findByLabelText('Collapse 0').click();
|
|
@@ -119,4 +171,28 @@ describe('Listing', () => {
|
|
|
119
171
|
|
|
120
172
|
cy.makeSnapshot();
|
|
121
173
|
});
|
|
174
|
+
|
|
175
|
+
it('displays the last column on several lines in compact mode when the isResponsive prop is set', () => {
|
|
176
|
+
mountListingResponsive(ListingVariant.compact);
|
|
177
|
+
|
|
178
|
+
cy.get('.MuiTable-root').should(
|
|
179
|
+
'have.css',
|
|
180
|
+
'grid-template-rows',
|
|
181
|
+
'30px 85px 85px 85px 85px 85px 85px 85px 85px 85px 85px'
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
cy.makeSnapshot();
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('displays the last column on several lines in extended mode when the isResponsive prop is set', () => {
|
|
188
|
+
mountListingResponsive(ListingVariant.extended);
|
|
189
|
+
|
|
190
|
+
cy.get('.MuiTable-root').should(
|
|
191
|
+
'have.css',
|
|
192
|
+
'grid-template-rows',
|
|
193
|
+
'38px 85px 85px 85px 85px 85px 85px 85px 85px 85px 85px'
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
cy.makeSnapshot();
|
|
197
|
+
});
|
|
122
198
|
});
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { makeStyles } from 'tss-react/mui';
|
|
2
2
|
|
|
3
|
-
import { ListingVariant } from '@centreon/ui-context';
|
|
4
|
-
|
|
5
3
|
import { TableStyleAtom as TableStyle } from './models';
|
|
6
4
|
|
|
7
5
|
const loadingIndicatorHeight = 3;
|
|
@@ -9,12 +7,12 @@ const loadingIndicatorHeight = 3;
|
|
|
9
7
|
interface StylesProps {
|
|
10
8
|
dataStyle: TableStyle;
|
|
11
9
|
getGridTemplateColumn: string;
|
|
12
|
-
|
|
10
|
+
isResponsive: string;
|
|
13
11
|
rows: Array<unknown>;
|
|
14
12
|
}
|
|
15
13
|
|
|
16
14
|
const useListingStyles = makeStyles<StylesProps>()(
|
|
17
|
-
(theme, { dataStyle, getGridTemplateColumn, rows }) => ({
|
|
15
|
+
(theme, { dataStyle, getGridTemplateColumn, rows, isResponsive }) => ({
|
|
18
16
|
actionBar: {
|
|
19
17
|
alignItems: 'center',
|
|
20
18
|
display: 'flex'
|
|
@@ -56,7 +54,7 @@ const useListingStyles = makeStyles<StylesProps>()(
|
|
|
56
54
|
gridTemplateColumns: getGridTemplateColumn,
|
|
57
55
|
gridTemplateRows: `${theme.spacing(dataStyle.header.height / 8)} repeat(${
|
|
58
56
|
rows?.length || 1
|
|
59
|
-
}, ${dataStyle.body.height}px)`,
|
|
57
|
+
}, ${isResponsive ? 'auto' : `${dataStyle.body.height}px`})`,
|
|
60
58
|
position: 'relative'
|
|
61
59
|
},
|
|
62
60
|
tableBody: {
|
|
@@ -40,7 +40,7 @@ const useStyles = makeStyles()((theme) => ({
|
|
|
40
40
|
}));
|
|
41
41
|
|
|
42
42
|
const ComponentColumn = ({ row, isSelected }): JSX.Element => (
|
|
43
|
-
|
|
43
|
+
<div style={{ display: 'flex', flexDirection: 'row', flexWrap: 'wrap' }}>
|
|
44
44
|
<span>
|
|
45
45
|
{'I am '}
|
|
46
46
|
<b>{`${isSelected ? 'selected' : 'not selected'}`}</b>
|
|
@@ -50,7 +50,7 @@ const ComponentColumn = ({ row, isSelected }): JSX.Element => (
|
|
|
50
50
|
{'I am '}
|
|
51
51
|
<b>{`${row.active ? 'active' : 'not active'}`}</b>
|
|
52
52
|
</span>
|
|
53
|
-
|
|
53
|
+
</div>
|
|
54
54
|
);
|
|
55
55
|
|
|
56
56
|
const ButtonColumn = ({ row }): JSX.Element => (
|
|
@@ -395,3 +395,26 @@ export const ListingWithSubItems = {
|
|
|
395
395
|
},
|
|
396
396
|
render: TemplateSubItems
|
|
397
397
|
};
|
|
398
|
+
|
|
399
|
+
export const ListingWithResponsive = {
|
|
400
|
+
args: {
|
|
401
|
+
checkable: true,
|
|
402
|
+
columns: [
|
|
403
|
+
...defaultColumns,
|
|
404
|
+
{
|
|
405
|
+
Component: ComponentColumn,
|
|
406
|
+
id: '##',
|
|
407
|
+
label: 'Responsive',
|
|
408
|
+
type: ColumnType.component,
|
|
409
|
+
width: '140px'
|
|
410
|
+
}
|
|
411
|
+
],
|
|
412
|
+
currentPage: 1,
|
|
413
|
+
isResponsive: true,
|
|
414
|
+
limit: 10,
|
|
415
|
+
loading: false,
|
|
416
|
+
rows: listingWithSubItems,
|
|
417
|
+
totalRows: 10
|
|
418
|
+
},
|
|
419
|
+
render: TemplateSubItems
|
|
420
|
+
};
|