@centreon/ui 25.7.2 → 25.8.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.
- package/package.json +1 -1
- package/src/Form/CollapsibleGroup.tsx +10 -10
- package/src/Form/Form.cypress.spec.tsx +137 -2
- package/src/Form/Form.stories.tsx +11 -31
- package/src/Form/Form.tsx +2 -0
- package/src/Form/Inputs/Grid.tsx +10 -28
- package/src/Form/Inputs/SubGroupDivider.tsx +7 -0
- package/src/Form/Inputs/Text.tsx +1 -0
- package/src/Form/Inputs/index.tsx +6 -0
- package/src/Form/Inputs/models.ts +2 -1
- package/src/Form/Section/FormSection.tsx +34 -0
- package/src/Form/Section/PanelTabs.tsx +13 -0
- package/src/Form/Section/navigateToSection.ts +9 -0
- package/src/Form/storiesData.tsx +12 -2
- package/src/Graph/BarChart/BarChart.cypress.spec.tsx +27 -2
- package/src/Graph/BarChart/BarChart.stories.tsx +10 -0
- package/src/Graph/BarChart/BarChart.tsx +19 -3
- package/src/Graph/BarChart/ResponsiveBarChart.tsx +12 -2
- package/src/Graph/Chart/BasicComponents/Lines/StackedLines/index.tsx +7 -1
- package/src/Graph/Chart/BasicComponents/Lines/index.tsx +10 -2
- package/src/Graph/Chart/Chart.cypress.spec.tsx +66 -13
- package/src/Graph/Chart/Chart.stories.tsx +41 -0
- package/src/Graph/Chart/Chart.tsx +15 -3
- package/src/Graph/Chart/InteractiveComponents/AnchorPoint/RegularAnchorPoint.tsx +8 -2
- package/src/Graph/Chart/InteractiveComponents/AnchorPoint/StackedAnchorPoint.tsx +9 -2
- package/src/Graph/Chart/InteractiveComponents/AnchorPoint/useTickGraph.ts +19 -2
- package/src/Graph/Chart/InteractiveComponents/index.tsx +16 -3
- package/src/Graph/Chart/index.tsx +6 -0
- package/src/Graph/Chart/models.ts +3 -0
- package/src/Graph/common/BaseChart/ChartSvgWrapper.tsx +5 -4
- package/src/Graph/common/timeSeries/index.ts +105 -34
- package/src/Graph/common/utils.ts +19 -0
- package/src/InputField/Select/Autocomplete/Connected/index.tsx +26 -21
- package/src/ThemeProvider/palettes.ts +1 -1
- package/src/api/models.ts +6 -6
- package/src/components/Modal/Modal.stories.tsx +20 -0
- package/src/components/Modal/Modal.styles.ts +2 -23
- package/src/components/Modal/Modal.tsx +1 -1
- package/src/components/Modal/ModalBody.tsx +6 -4
- package/src/components/Modal/ModalHeader.tsx +5 -5
- package/src/components/Modal/modal.module.css +16 -0
- package/src/components/Tabs/Tab.styles.ts +0 -6
- package/src/components/Tabs/Tabs.tsx +31 -10
|
@@ -20,7 +20,6 @@ import {
|
|
|
20
20
|
includes,
|
|
21
21
|
isEmpty,
|
|
22
22
|
isNil,
|
|
23
|
-
isNotNil,
|
|
24
23
|
keys,
|
|
25
24
|
last,
|
|
26
25
|
lt,
|
|
@@ -353,6 +352,19 @@ const getScaleType = (
|
|
|
353
352
|
const hasOnlyZeroesHasValue = (graphValues: Array<number>): boolean =>
|
|
354
353
|
graphValues.every((value) => equals(value, 0) || equals(value, null));
|
|
355
354
|
|
|
355
|
+
const getSanitizedValues = reject(
|
|
356
|
+
(
|
|
357
|
+
value:
|
|
358
|
+
| number
|
|
359
|
+
| boolean
|
|
360
|
+
| typeof Number.POSITIVE_INFINITY
|
|
361
|
+
| typeof Number.NEGATIVE_INFINITY
|
|
362
|
+
) =>
|
|
363
|
+
equals(value, false) ||
|
|
364
|
+
equals(value, Number.POSITIVE_INFINITY) ||
|
|
365
|
+
equals(value, Number.NEGATIVE_INFINITY)
|
|
366
|
+
);
|
|
367
|
+
|
|
356
368
|
const getScale = ({
|
|
357
369
|
graphValues,
|
|
358
370
|
height,
|
|
@@ -363,23 +375,39 @@ const getScale = ({
|
|
|
363
375
|
scaleLogarithmicBase,
|
|
364
376
|
isHorizontal,
|
|
365
377
|
invert,
|
|
366
|
-
hasDisplayAsBar
|
|
378
|
+
hasDisplayAsBar,
|
|
379
|
+
hasLineFilled,
|
|
380
|
+
min,
|
|
381
|
+
max
|
|
367
382
|
}): ScaleLinear<number, number> => {
|
|
368
383
|
const isLogScale = equals(scale, 'logarithmic');
|
|
369
|
-
const
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
384
|
+
const sanitizedValuesForMinimum = min
|
|
385
|
+
? [min]
|
|
386
|
+
: getSanitizedValues([
|
|
387
|
+
invert && graphValues.every(lt(0))
|
|
388
|
+
? negate(getMax(graphValues))
|
|
389
|
+
: getMin(graphValues),
|
|
390
|
+
!isEmpty(stackedValues) &&
|
|
391
|
+
!equals(stackedValues, [0]) &&
|
|
392
|
+
getMin(stackedValues),
|
|
393
|
+
Math.min(...thresholds)
|
|
394
|
+
]);
|
|
395
|
+
const minValue = Math.min(...sanitizedValuesForMinimum);
|
|
396
|
+
const minValueWithMargin =
|
|
397
|
+
(hasDisplayAsBar || hasLineFilled) && minValue > 0 && !min
|
|
398
|
+
? 0
|
|
399
|
+
: minValue - Math.abs(minValue) * 0.05;
|
|
400
|
+
|
|
401
|
+
const sanitizedValuesForMaximum = max
|
|
402
|
+
? [max]
|
|
403
|
+
: getSanitizedValues([
|
|
404
|
+
getMax(graphValues),
|
|
405
|
+
getMax(stackedValues),
|
|
406
|
+
hasOnlyZeroesHasValue(graphValues) ? 1 : 0,
|
|
407
|
+
Math.max(...thresholds)
|
|
408
|
+
]);
|
|
409
|
+
const maxValue = Math.max(...sanitizedValuesForMaximum);
|
|
410
|
+
const maxValueWithMargin = maxValue + Math.abs(maxValue) * 0.05;
|
|
383
411
|
|
|
384
412
|
const scaleType = getScaleType(scale);
|
|
385
413
|
|
|
@@ -387,21 +415,26 @@ const getScale = ({
|
|
|
387
415
|
const range = [height, upperRangeValue];
|
|
388
416
|
|
|
389
417
|
if (isCenteredZero) {
|
|
390
|
-
const greatestValue = Math.max(
|
|
418
|
+
const greatestValue = Math.max(
|
|
419
|
+
Math.abs(maxValueWithMargin),
|
|
420
|
+
Math.abs(minValueWithMargin)
|
|
421
|
+
);
|
|
391
422
|
|
|
392
423
|
return scaleType<number>({
|
|
393
424
|
base: scaleLogarithmicBase || 2,
|
|
394
425
|
domain: [-greatestValue, greatestValue],
|
|
395
|
-
range: isHorizontal ? range : range.reverse()
|
|
426
|
+
range: isHorizontal ? range : range.reverse(),
|
|
427
|
+
clamp: min || max
|
|
396
428
|
});
|
|
397
429
|
}
|
|
398
430
|
|
|
399
|
-
const domain = [isLogScale ? 0.001 :
|
|
431
|
+
const domain = [isLogScale ? 0.001 : minValueWithMargin, maxValueWithMargin];
|
|
400
432
|
|
|
401
433
|
return scaleType<number>({
|
|
402
434
|
base: scaleLogarithmicBase || 2,
|
|
403
435
|
domain,
|
|
404
|
-
range: isHorizontal ? range : range.reverse()
|
|
436
|
+
range: isHorizontal ? range : range.reverse(),
|
|
437
|
+
clamp: min || max
|
|
405
438
|
});
|
|
406
439
|
};
|
|
407
440
|
|
|
@@ -437,11 +470,19 @@ const getYScaleUnit = ({
|
|
|
437
470
|
scaleLogarithmicBase,
|
|
438
471
|
isHorizontal = true,
|
|
439
472
|
unit,
|
|
440
|
-
invert
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
473
|
+
invert,
|
|
474
|
+
min,
|
|
475
|
+
max,
|
|
476
|
+
isBarChart,
|
|
477
|
+
boundariesUnit
|
|
478
|
+
}: AxeScale & {
|
|
479
|
+
invert?: boolean | string | null;
|
|
480
|
+
unit: string;
|
|
481
|
+
max?: number;
|
|
482
|
+
min?: number;
|
|
483
|
+
boundariesUnit?: string;
|
|
484
|
+
isBarChart?: boolean;
|
|
485
|
+
}): ScaleLinear<number, number> => {
|
|
445
486
|
const [firstUnit] = getUnits(dataLines);
|
|
446
487
|
const shouldApplyThresholds =
|
|
447
488
|
equals(unit, thresholdUnit) || (!thresholdUnit && equals(firstUnit, unit));
|
|
@@ -468,11 +509,14 @@ const getYScaleUnit = ({
|
|
|
468
509
|
|
|
469
510
|
return getScale({
|
|
470
511
|
graphValues,
|
|
471
|
-
hasDisplayAsBar:
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
512
|
+
hasDisplayAsBar:
|
|
513
|
+
isBarChart ||
|
|
514
|
+
dataLines.some(
|
|
515
|
+
({ displayAs, unit: lineUnit }) =>
|
|
516
|
+
equals(unit, lineUnit) && equals(displayAs, 'bar')
|
|
517
|
+
),
|
|
518
|
+
hasLineFilled: dataLines.some(
|
|
519
|
+
({ unit: lineUnit, filled }) => equals(unit, lineUnit) && filled
|
|
476
520
|
),
|
|
477
521
|
height: valueGraphHeight,
|
|
478
522
|
invert,
|
|
@@ -481,10 +525,24 @@ const getYScaleUnit = ({
|
|
|
481
525
|
scale,
|
|
482
526
|
scaleLogarithmicBase,
|
|
483
527
|
stackedValues,
|
|
484
|
-
thresholds: shouldApplyThresholds ? thresholds : []
|
|
528
|
+
thresholds: shouldApplyThresholds ? thresholds : [],
|
|
529
|
+
min: boundaryToApplyToUnit({ unit, boundariesUnit, boundary: min }),
|
|
530
|
+
max: boundaryToApplyToUnit({ unit, boundariesUnit, boundary: max })
|
|
485
531
|
});
|
|
486
532
|
};
|
|
487
533
|
|
|
534
|
+
const boundaryToApplyToUnit = ({
|
|
535
|
+
boundary,
|
|
536
|
+
boundariesUnit,
|
|
537
|
+
unit
|
|
538
|
+
}): number | undefined => {
|
|
539
|
+
if (!boundariesUnit) {
|
|
540
|
+
return boundary;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
return equals(boundariesUnit, unit) ? boundary : undefined;
|
|
544
|
+
};
|
|
545
|
+
|
|
488
546
|
const getYScalePerUnit = ({
|
|
489
547
|
dataLines,
|
|
490
548
|
dataTimeSeries,
|
|
@@ -494,8 +552,17 @@ const getYScalePerUnit = ({
|
|
|
494
552
|
isCenteredZero,
|
|
495
553
|
scale,
|
|
496
554
|
scaleLogarithmicBase,
|
|
497
|
-
isHorizontal = true
|
|
498
|
-
|
|
555
|
+
isHorizontal = true,
|
|
556
|
+
isBarChart,
|
|
557
|
+
min,
|
|
558
|
+
max,
|
|
559
|
+
boundariesUnit
|
|
560
|
+
}: AxeScale & {
|
|
561
|
+
min?: number;
|
|
562
|
+
max?: number;
|
|
563
|
+
isBarChart?: boolean;
|
|
564
|
+
boundariesUnit?: string;
|
|
565
|
+
}): Record<string, ScaleLinear<number, number>> => {
|
|
499
566
|
const units = getUnits(dataLines);
|
|
500
567
|
|
|
501
568
|
const scalePerUnit = units.reduce((acc, unit) => {
|
|
@@ -514,7 +581,11 @@ const getYScalePerUnit = ({
|
|
|
514
581
|
thresholdUnit,
|
|
515
582
|
thresholds,
|
|
516
583
|
unit,
|
|
517
|
-
valueGraphHeight
|
|
584
|
+
valueGraphHeight,
|
|
585
|
+
min,
|
|
586
|
+
max,
|
|
587
|
+
isBarChart,
|
|
588
|
+
boundariesUnit
|
|
518
589
|
})
|
|
519
590
|
};
|
|
520
591
|
}, {});
|
|
@@ -20,7 +20,9 @@ import {
|
|
|
20
20
|
|
|
21
21
|
import { Theme, darken, getLuminance, lighten } from '@mui/material';
|
|
22
22
|
|
|
23
|
+
import dayjs from 'dayjs';
|
|
23
24
|
import { BarStyle } from '../BarChart/models';
|
|
25
|
+
import { margin } from '../Chart/common';
|
|
24
26
|
import { LineStyle } from '../Chart/models';
|
|
25
27
|
import { Threshold, Thresholds } from './models';
|
|
26
28
|
import { formatMetricValue } from './timeSeries';
|
|
@@ -256,3 +258,20 @@ export const getFormattedAxisValues = ({
|
|
|
256
258
|
.concat(formattedThresholdValues)
|
|
257
259
|
.filter((v) => v) as Array<string>;
|
|
258
260
|
};
|
|
261
|
+
|
|
262
|
+
interface ComputeGElementMarginLeftProps {
|
|
263
|
+
maxCharacters: number;
|
|
264
|
+
hasSecondUnit?: boolean;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export const computeGElementMarginLeft = ({
|
|
268
|
+
maxCharacters,
|
|
269
|
+
hasSecondUnit
|
|
270
|
+
}: ComputeGElementMarginLeftProps): number =>
|
|
271
|
+
maxCharacters * 5 + (hasSecondUnit ? margin.top * 0.8 : margin.top * 0.6);
|
|
272
|
+
|
|
273
|
+
export const computPixelsToShiftMouse = (xScale): number => {
|
|
274
|
+
const domain = xScale.domain();
|
|
275
|
+
|
|
276
|
+
return Math.round(8 / dayjs(domain[1]).diff(domain[0], 'h'));
|
|
277
|
+
};
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
import { CircularProgress, useTheme } from '@mui/material';
|
|
18
18
|
|
|
19
19
|
import { Props as AutocompleteFieldProps } from '..';
|
|
20
|
-
import {
|
|
20
|
+
import { ListingMapModel, ListingModel, SelectEntry } from '../../../..';
|
|
21
21
|
import {
|
|
22
22
|
ConditionsSearchParameter,
|
|
23
23
|
SearchParameter
|
|
@@ -122,26 +122,31 @@ const ConnectedAutocompleteField = (
|
|
|
122
122
|
}
|
|
123
123
|
});
|
|
124
124
|
|
|
125
|
-
const getOptionResult = useCallback(
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
125
|
+
const getOptionResult = useCallback(
|
|
126
|
+
(
|
|
127
|
+
newOptions: ListingModel<TData> | ListingMapModel<TData>
|
|
128
|
+
): OptionResult<TData> => {
|
|
129
|
+
if ('result' in newOptions)
|
|
130
|
+
return {
|
|
131
|
+
result: newOptions.result || [],
|
|
132
|
+
total: newOptions.meta.total || 1,
|
|
133
|
+
limit: newOptions.meta.limit || 1
|
|
134
|
+
};
|
|
135
|
+
if ('content' in newOptions)
|
|
136
|
+
return {
|
|
137
|
+
result: newOptions.content || [],
|
|
138
|
+
total: newOptions.totalElements || 1,
|
|
139
|
+
limit: newOptions.size || 1
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
result: [],
|
|
144
|
+
total: 1,
|
|
145
|
+
limit: 1
|
|
146
|
+
};
|
|
147
|
+
},
|
|
148
|
+
[]
|
|
149
|
+
);
|
|
145
150
|
|
|
146
151
|
const lastOptionRef = useIntersectionObserver({
|
|
147
152
|
action: () => setPage(page + 1),
|
package/src/api/models.ts
CHANGED
|
@@ -10,10 +10,10 @@ export interface Listing<TEntity> {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export interface ListingMap<TEntity> {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
content: Array<TEntity>;
|
|
14
|
+
totalPages: number;
|
|
15
|
+
totalElements: number;
|
|
16
|
+
size: number;
|
|
17
|
+
number: number;
|
|
18
|
+
numberOfElements: number;
|
|
19
19
|
}
|
|
@@ -4,6 +4,7 @@ import { Button } from '../Button';
|
|
|
4
4
|
|
|
5
5
|
import { Modal } from '.';
|
|
6
6
|
import '../../ThemeProvider/tailwindcss.css';
|
|
7
|
+
import { basicFormWithCollapsibleGroups } from '../../Form/Form.stories';
|
|
7
8
|
|
|
8
9
|
const meta: Meta<typeof Modal> = {
|
|
9
10
|
argTypes: {
|
|
@@ -45,6 +46,25 @@ export const Default: Story = {
|
|
|
45
46
|
)
|
|
46
47
|
};
|
|
47
48
|
|
|
49
|
+
export const WithForm: Story = {
|
|
50
|
+
args: {
|
|
51
|
+
...Default.args
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
render: (args) => (
|
|
55
|
+
<Modal {...args}>
|
|
56
|
+
<Modal.Header>Modal title</Modal.Header>
|
|
57
|
+
<Modal.Body>{basicFormWithCollapsibleGroups()}</Modal.Body>
|
|
58
|
+
<Modal.Actions
|
|
59
|
+
labels={{
|
|
60
|
+
cancel: 'Cancel',
|
|
61
|
+
confirm: 'Confirm'
|
|
62
|
+
}}
|
|
63
|
+
/>
|
|
64
|
+
</Modal>
|
|
65
|
+
)
|
|
66
|
+
};
|
|
67
|
+
|
|
48
68
|
export const AsDangerAction: Story = {
|
|
49
69
|
args: {
|
|
50
70
|
...Default.args
|
|
@@ -8,7 +8,7 @@ const useStyles = makeStyles<{
|
|
|
8
8
|
}>()((theme, props) => ({
|
|
9
9
|
modal: {
|
|
10
10
|
'& .MuiDialog-paper': {
|
|
11
|
-
gap: theme.spacing(
|
|
11
|
+
gap: theme.spacing(3),
|
|
12
12
|
padding: theme.spacing(2.5)
|
|
13
13
|
},
|
|
14
14
|
'&[data-size="fullscreen"]': {
|
|
@@ -65,15 +65,6 @@ const useStyles = makeStyles<{
|
|
|
65
65
|
right: 0,
|
|
66
66
|
zIndex: theme.zIndex.modal
|
|
67
67
|
},
|
|
68
|
-
modalBody: {
|
|
69
|
-
'& > p': {
|
|
70
|
-
'&:first-of-type': {
|
|
71
|
-
margin: theme.spacing(0, 0, 1, 0)
|
|
72
|
-
},
|
|
73
|
-
margin: theme.spacing(1, 0, 1, 0),
|
|
74
|
-
width: '90%'
|
|
75
|
-
}
|
|
76
|
-
},
|
|
77
68
|
modalCloseButton: {
|
|
78
69
|
position: 'absolute',
|
|
79
70
|
right: theme.spacing(1),
|
|
@@ -82,19 +73,7 @@ const useStyles = makeStyles<{
|
|
|
82
73
|
},
|
|
83
74
|
top: theme.spacing(1)
|
|
84
75
|
},
|
|
85
|
-
|
|
86
|
-
'& .MuiDialogTitle-root': {
|
|
87
|
-
padding: theme.spacing(0)
|
|
88
|
-
},
|
|
89
|
-
display: 'flex',
|
|
90
|
-
gap: theme.spacing(2),
|
|
91
|
-
|
|
92
|
-
justifyContent: 'space-between'
|
|
93
|
-
},
|
|
94
|
-
modalTitle: {
|
|
95
|
-
fontSize: theme.typography.h5.fontSize,
|
|
96
|
-
fontWeight: theme.typography.fontWeightBold
|
|
97
|
-
}
|
|
76
|
+
|
|
98
77
|
}));
|
|
99
78
|
|
|
100
79
|
export { useStyles };
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import { ReactElement, ReactNode } from 'react';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { modalBody } from './modal.module.css';
|
|
4
4
|
|
|
5
5
|
export type ModalHeaderProps = {
|
|
6
6
|
children?: ReactNode;
|
|
7
7
|
};
|
|
8
8
|
|
|
9
9
|
const ModalBody = ({ children }: ModalHeaderProps): ReactElement => {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
return (
|
|
11
|
+
<div className={modalBody} data-testid="modal-body">
|
|
12
|
+
{children}
|
|
13
|
+
</div>
|
|
14
|
+
);
|
|
13
15
|
};
|
|
14
16
|
|
|
15
17
|
export { ModalBody };
|
|
@@ -2,7 +2,9 @@ import { ReactElement, ReactNode } from 'react';
|
|
|
2
2
|
|
|
3
3
|
import { DialogTitleProps, DialogTitle as MuiDialogTitle } from '@mui/material';
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import '../../../src/ThemeProvider/tailwindcss.css';
|
|
6
|
+
|
|
7
|
+
import { modalHeader } from './modal.module.css';
|
|
6
8
|
|
|
7
9
|
export type ModalHeaderProps = {
|
|
8
10
|
children?: ReactNode;
|
|
@@ -12,11 +14,9 @@ const ModalHeader = ({
|
|
|
12
14
|
children,
|
|
13
15
|
...rest
|
|
14
16
|
}: ModalHeaderProps & DialogTitleProps): ReactElement => {
|
|
15
|
-
const { classes } = useStyles();
|
|
16
|
-
|
|
17
17
|
return (
|
|
18
|
-
<div className={
|
|
19
|
-
<MuiDialogTitle color="primary"
|
|
18
|
+
<div className={modalHeader}>
|
|
19
|
+
<MuiDialogTitle className="p-0 font-bold text-2xl" color="primary" {...rest}>
|
|
20
20
|
{children}
|
|
21
21
|
</MuiDialogTitle>
|
|
22
22
|
</div>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
.modalHeader {
|
|
2
|
+
& .MuiDialogTitle-root {
|
|
3
|
+
padding: 0;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
display: flex;
|
|
7
|
+
gap: var(--spacing-4);
|
|
8
|
+
justify-content: space-between;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.modalBody {
|
|
12
|
+
overflow-y: auto;
|
|
13
|
+
overflow-x: hidden;
|
|
14
|
+
height: 100%;
|
|
15
|
+
padding-right: var(--spacing-4); /* To prevent scrollbar from overlapping content */
|
|
16
|
+
}
|
|
@@ -5,12 +5,6 @@ export const useTabsStyles = makeStyles()((theme) => ({
|
|
|
5
5
|
bottom: 'unset'
|
|
6
6
|
},
|
|
7
7
|
tab: {
|
|
8
|
-
'&[aria-selected="true"]': {
|
|
9
|
-
color: theme.palette.text.primary,
|
|
10
|
-
fontWeight: theme.typography.fontWeightBold
|
|
11
|
-
},
|
|
12
|
-
color: theme.palette.text.primary,
|
|
13
|
-
fontWeight: theme.typography.fontWeightRegular,
|
|
14
8
|
marginRight: theme.spacing(2),
|
|
15
9
|
minHeight: 0,
|
|
16
10
|
minWidth: 0,
|
|
@@ -5,33 +5,53 @@ import { Tabs as MuiTabs, Tab, TabsProps } from '@mui/material';
|
|
|
5
5
|
|
|
6
6
|
import { useTabsStyles } from './Tab.styles';
|
|
7
7
|
|
|
8
|
+
import '../../ThemeProvider/tailwindcss.css';
|
|
9
|
+
|
|
10
|
+
export interface TabI {
|
|
11
|
+
label: string;
|
|
12
|
+
value: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
8
15
|
type Props = {
|
|
9
|
-
children
|
|
16
|
+
children?: Array<JSX.Element>;
|
|
10
17
|
defaultTab: string;
|
|
11
18
|
tabList?: TabsProps;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
19
|
+
variant?: 'standard' | 'scrollable' | 'fullWidth';
|
|
20
|
+
scrollButtons?: boolean | 'auto';
|
|
21
|
+
tabs: TabI[];
|
|
22
|
+
onChange?: (newValue: string) => void;
|
|
16
23
|
};
|
|
17
24
|
|
|
18
25
|
export const Tabs = ({
|
|
19
26
|
children,
|
|
20
27
|
defaultTab,
|
|
21
28
|
tabs,
|
|
22
|
-
tabList
|
|
29
|
+
tabList,
|
|
30
|
+
variant,
|
|
31
|
+
scrollButtons = 'auto',
|
|
32
|
+
onChange
|
|
23
33
|
}: Props): JSX.Element => {
|
|
24
34
|
const { classes } = useTabsStyles();
|
|
25
35
|
|
|
26
36
|
const [selectedTab, setSelectedTab] = useState(defaultTab);
|
|
27
37
|
|
|
28
|
-
const changeTab = useCallback(
|
|
29
|
-
|
|
30
|
-
|
|
38
|
+
const changeTab = useCallback(
|
|
39
|
+
(_, newValue: string): void => {
|
|
40
|
+
if (onChange) onChange(newValue);
|
|
41
|
+
|
|
42
|
+
setSelectedTab(newValue);
|
|
43
|
+
},
|
|
44
|
+
[onChange]
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const selectedTabStyle = ' font-bold text-primary-main';
|
|
48
|
+
const defaultTabStyle = ' font-normal';
|
|
31
49
|
|
|
32
50
|
return (
|
|
33
51
|
<TabContext value={selectedTab}>
|
|
34
52
|
<MuiTabs
|
|
53
|
+
scrollButtons={scrollButtons}
|
|
54
|
+
variant={variant}
|
|
35
55
|
classes={{
|
|
36
56
|
indicator: classes.indicator,
|
|
37
57
|
root: classes.tabs
|
|
@@ -43,10 +63,11 @@ export const Tabs = ({
|
|
|
43
63
|
{tabs.map(({ value, label }) => (
|
|
44
64
|
<Tab
|
|
45
65
|
aria-label={label}
|
|
46
|
-
className={classes.tab}
|
|
66
|
+
className={`${classes.tab} ${selectedTab === value ? selectedTabStyle : defaultTabStyle}`}
|
|
47
67
|
key={value}
|
|
48
68
|
label={label}
|
|
49
69
|
value={value}
|
|
70
|
+
data-testid={`tab-${value}`}
|
|
50
71
|
/>
|
|
51
72
|
))}
|
|
52
73
|
</MuiTabs>
|