@centreon/ui 23.10.1-MON-20802-fe-implement-new-visualization.2 → 23.10.1-MON-21543-implement-top-bottom-widget.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/Dashboard/Dashboard.styles.ts +6 -1
- package/src/Dashboard/Item.tsx +4 -1
- package/src/Dashboard/Layout.tsx +1 -1
- package/src/Graph/Gauge/Gauge.stories.tsx +10 -0
- package/src/Graph/Gauge/Gauge.tsx +10 -1
- package/src/Graph/Gauge/PieData.tsx +8 -8
- package/src/Graph/Gauge/ResponsiveGauge.tsx +32 -23
- package/src/Graph/Gauge/Thresholds.tsx +2 -25
- package/src/Graph/Gauge/models.ts +1 -0
- package/src/Graph/SingleBar/ResponsiveSingleBar.tsx +41 -24
- package/src/Graph/SingleBar/SingleBar.stories.tsx +46 -0
- package/src/Graph/SingleBar/ThresholdLine.tsx +31 -12
- package/src/Graph/SingleBar/Thresholds.tsx +5 -1
- package/src/Graph/SingleBar/models.ts +4 -0
- package/src/Graph/Text/Text.stories.tsx +13 -0
- package/src/Graph/Text/Text.tsx +19 -12
- package/src/Graph/common/timeSeries/index.test.ts +106 -3
- package/src/Graph/common/timeSeries/index.ts +67 -4
- package/src/Graph/common/timeSeries/models.ts +6 -3
- package/src/Graph/common/utils.ts +4 -2
- package/src/InputField/Select/Autocomplete/Multi/index.tsx +4 -2
- package/src/InputField/Select/Autocomplete/index.tsx +3 -1
- package/src/InputField/Text/index.tsx +10 -7
- package/src/RichTextEditor/ContentEditable.tsx +0 -1
- package/src/RichTextEditor/RichTextEditor.cypress.spec.tsx +5 -13
- package/src/RichTextEditor/RichTextEditor.tsx +5 -2
- package/src/RichTextEditor/plugins/AutoLinkPlugin/index.tsx +40 -1
- package/src/RichTextEditor/plugins/FloatingLinkEditorPlugin.tsx +33 -8
- package/src/RichTextEditor/plugins/ToolbarPlugin/index.tsx +10 -4
- package/src/ThemeProvider/index.tsx +6 -4
- package/src/TopCounterElements/index.tsx +0 -1
- package/src/api/useFetchQuery/index.ts +10 -8
- package/src/components/Inputs/Switch/Switch.stories.tsx +22 -0
- package/src/components/Inputs/Switch/Switch.styles.ts +13 -0
- package/src/components/Inputs/Switch/Switch.tsx +18 -0
- package/src/components/Inputs/index.ts +1 -0
- package/src/components/Modal/Modal.styles.ts +3 -1
- package/src/components/index.ts +1 -0
package/src/Graph/Text/Text.tsx
CHANGED
|
@@ -4,7 +4,7 @@ import { Typography, useTheme } from '@mui/material';
|
|
|
4
4
|
|
|
5
5
|
import { LineChartData, Thresholds } from '../common/models';
|
|
6
6
|
import {
|
|
7
|
-
|
|
7
|
+
formatMetricValueWithUnit,
|
|
8
8
|
getMetricWithLatestData
|
|
9
9
|
} from '../common/timeSeries';
|
|
10
10
|
import { getColorFromDataAndTresholds } from '../common/utils';
|
|
@@ -12,7 +12,9 @@ import { getColorFromDataAndTresholds } from '../common/utils';
|
|
|
12
12
|
import { useTextStyles } from './Text.styles';
|
|
13
13
|
|
|
14
14
|
interface Props {
|
|
15
|
+
baseColor?: string;
|
|
15
16
|
data?: LineChartData;
|
|
17
|
+
displayAsRaw?: boolean;
|
|
16
18
|
labels: {
|
|
17
19
|
critical: string;
|
|
18
20
|
warning: string;
|
|
@@ -23,7 +25,9 @@ interface Props {
|
|
|
23
25
|
export const Text = ({
|
|
24
26
|
thresholds,
|
|
25
27
|
data,
|
|
26
|
-
|
|
28
|
+
displayAsRaw,
|
|
29
|
+
labels,
|
|
30
|
+
baseColor
|
|
27
31
|
}: Props): JSX.Element | null => {
|
|
28
32
|
const theme = useTheme();
|
|
29
33
|
const { classes } = useTextStyles();
|
|
@@ -37,20 +41,21 @@ export const Text = ({
|
|
|
37
41
|
const metricUnit = metric?.unit ?? '';
|
|
38
42
|
|
|
39
43
|
const color = getColorFromDataAndTresholds({
|
|
44
|
+
baseColor,
|
|
40
45
|
data: metricValue,
|
|
41
46
|
theme,
|
|
42
47
|
thresholds
|
|
43
48
|
});
|
|
44
49
|
|
|
45
50
|
const warningThresholdLabels = thresholds.warning.map(({ value }) =>
|
|
46
|
-
|
|
51
|
+
formatMetricValueWithUnit({
|
|
47
52
|
unit: metricUnit,
|
|
48
53
|
value
|
|
49
54
|
})
|
|
50
55
|
);
|
|
51
56
|
|
|
52
57
|
const criticalThresholdLabels = thresholds.critical.map(({ value }) =>
|
|
53
|
-
|
|
58
|
+
formatMetricValueWithUnit({
|
|
54
59
|
unit: metricUnit,
|
|
55
60
|
value
|
|
56
61
|
})
|
|
@@ -58,24 +63,26 @@ export const Text = ({
|
|
|
58
63
|
|
|
59
64
|
return (
|
|
60
65
|
<div className={classes.graphText}>
|
|
61
|
-
<Typography sx={{ color }} variant="
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
<Typography sx={{ color }} variant="h2">
|
|
67
|
+
<strong>
|
|
68
|
+
{formatMetricValueWithUnit({
|
|
69
|
+
isRaw: displayAsRaw,
|
|
70
|
+
unit: metricUnit,
|
|
71
|
+
value: metricValue
|
|
72
|
+
})}
|
|
73
|
+
</strong>
|
|
67
74
|
</Typography>
|
|
68
75
|
{thresholds.enabled && (
|
|
69
76
|
<div className={classes.thresholds}>
|
|
70
77
|
<Typography sx={{ color: 'warning.main' }} variant="h5">
|
|
71
78
|
{labels.warning}
|
|
72
79
|
{': '}
|
|
73
|
-
{warningThresholdLabels.join(' - ')}
|
|
80
|
+
{warningThresholdLabels.join(' - ')}
|
|
74
81
|
</Typography>
|
|
75
82
|
<Typography sx={{ color: 'error.main' }} variant="h5">
|
|
76
83
|
{labels.critical}
|
|
77
84
|
{': '}
|
|
78
|
-
{criticalThresholdLabels.join(' - ')}
|
|
85
|
+
{criticalThresholdLabels.join(' - ')}
|
|
79
86
|
</Typography>
|
|
80
87
|
</div>
|
|
81
88
|
)}
|
|
@@ -335,10 +335,10 @@ describe('timeSeries', () => {
|
|
|
335
335
|
describe(timeSeries.formatMetricValue, () => {
|
|
336
336
|
const cases: Array<TestCase> = [
|
|
337
337
|
[218857269, '', 1000, '218.86m'],
|
|
338
|
-
[218857269, '', 1024, '208.
|
|
338
|
+
[218857269, '', 1024, '208.72 M'],
|
|
339
339
|
[0.12232323445, '', 1000, '0.12'],
|
|
340
|
-
[1024, 'B', 1000, '
|
|
341
|
-
[1024, 'B', 1024, '
|
|
340
|
+
[1024, 'B', 1000, '1 KB'],
|
|
341
|
+
[1024, 'B', 1024, '1 KB'],
|
|
342
342
|
[null, 'B', 1024, null]
|
|
343
343
|
];
|
|
344
344
|
|
|
@@ -504,3 +504,106 @@ describe('timeSeries', () => {
|
|
|
504
504
|
});
|
|
505
505
|
});
|
|
506
506
|
});
|
|
507
|
+
|
|
508
|
+
describe('Format value with unit', () => {
|
|
509
|
+
const units = [
|
|
510
|
+
'B',
|
|
511
|
+
'bytes',
|
|
512
|
+
'bytespersecond',
|
|
513
|
+
'B/s',
|
|
514
|
+
'B/sec',
|
|
515
|
+
'o',
|
|
516
|
+
'octets',
|
|
517
|
+
'b/s',
|
|
518
|
+
'b',
|
|
519
|
+
'ms',
|
|
520
|
+
'%',
|
|
521
|
+
''
|
|
522
|
+
];
|
|
523
|
+
|
|
524
|
+
const getExpectedResult = (unit): string => {
|
|
525
|
+
if (unit === '') {
|
|
526
|
+
return '324.23m';
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
return `309.21 M${unit}`;
|
|
530
|
+
};
|
|
531
|
+
|
|
532
|
+
const humanReadableTestCases = units.map((unit) => {
|
|
533
|
+
if (unit === '%') {
|
|
534
|
+
return {
|
|
535
|
+
expectedResult: '45.56%',
|
|
536
|
+
unit,
|
|
537
|
+
value: 45.55678
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
if (unit === 'ms') {
|
|
542
|
+
return {
|
|
543
|
+
expectedResult: '34.23 seconds',
|
|
544
|
+
unit,
|
|
545
|
+
value: 34232
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
return {
|
|
550
|
+
expectedResult: getExpectedResult(unit),
|
|
551
|
+
unit,
|
|
552
|
+
value: 324234232.34233
|
|
553
|
+
};
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
const rawTestCases = units.map((unit) => {
|
|
557
|
+
if (unit === '%') {
|
|
558
|
+
return {
|
|
559
|
+
expectedResult: '45.55678%',
|
|
560
|
+
unit,
|
|
561
|
+
value: 45.55678
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
if (unit === 'ms') {
|
|
566
|
+
return {
|
|
567
|
+
expectedResult: '34232 ms',
|
|
568
|
+
unit,
|
|
569
|
+
value: 34232
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
return {
|
|
574
|
+
expectedResult:
|
|
575
|
+
unit === '' ? '324234232.34233 ' : `324234232.34233 ${unit}`,
|
|
576
|
+
unit,
|
|
577
|
+
value: 324234232.34233
|
|
578
|
+
};
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
describe('Format the value as human readable', () => {
|
|
582
|
+
it.each(humanReadableTestCases)(
|
|
583
|
+
'formats the value with $unit',
|
|
584
|
+
({ value, unit, expectedResult }) => {
|
|
585
|
+
expect(
|
|
586
|
+
timeSeries.formatMetricValueWithUnit({
|
|
587
|
+
unit,
|
|
588
|
+
value
|
|
589
|
+
})
|
|
590
|
+
).toEqual(expectedResult);
|
|
591
|
+
}
|
|
592
|
+
);
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
describe('Format the value as raw', () => {
|
|
596
|
+
it.each(rawTestCases)(
|
|
597
|
+
'formats the value with $unit',
|
|
598
|
+
({ value, unit, expectedResult }) => {
|
|
599
|
+
expect(
|
|
600
|
+
timeSeries.formatMetricValueWithUnit({
|
|
601
|
+
isRaw: true,
|
|
602
|
+
unit,
|
|
603
|
+
value
|
|
604
|
+
})
|
|
605
|
+
).toEqual(expectedResult);
|
|
606
|
+
}
|
|
607
|
+
);
|
|
608
|
+
});
|
|
609
|
+
});
|
|
@@ -28,7 +28,10 @@ import {
|
|
|
28
28
|
lt,
|
|
29
29
|
identity,
|
|
30
30
|
head,
|
|
31
|
-
last
|
|
31
|
+
last,
|
|
32
|
+
cond,
|
|
33
|
+
always,
|
|
34
|
+
T
|
|
32
35
|
} from 'ramda';
|
|
33
36
|
|
|
34
37
|
import { margin } from '../../LineChart/common';
|
|
@@ -440,6 +443,37 @@ const getRightScale = ({
|
|
|
440
443
|
});
|
|
441
444
|
};
|
|
442
445
|
|
|
446
|
+
const formatTime = (value: number): string => {
|
|
447
|
+
if (value < 1000) {
|
|
448
|
+
return `${numeral(value).format('0.[00]a')} ms`;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const t = numeral(value / 1000).format('0.[00]a');
|
|
452
|
+
|
|
453
|
+
return `${t} seconds`;
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
const registerMsUnitToNumeral = (): null => {
|
|
457
|
+
try {
|
|
458
|
+
numeral.register('format', 'milliseconds', {
|
|
459
|
+
format: (value) => {
|
|
460
|
+
return formatTime(value);
|
|
461
|
+
},
|
|
462
|
+
regexps: {
|
|
463
|
+
format: /(ms)/,
|
|
464
|
+
unformat: /(ms)/
|
|
465
|
+
},
|
|
466
|
+
unformat: () => ''
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
return null;
|
|
470
|
+
} catch (_) {
|
|
471
|
+
return null;
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
registerMsUnitToNumeral();
|
|
476
|
+
|
|
443
477
|
const formatMetricValue = ({
|
|
444
478
|
value,
|
|
445
479
|
unit,
|
|
@@ -463,11 +497,14 @@ const formatMetricValue = ({
|
|
|
463
497
|
|
|
464
498
|
const base1024 = base2Units.includes(unit) || Number(base) === 1024;
|
|
465
499
|
|
|
466
|
-
const formatSuffix =
|
|
500
|
+
const formatSuffix = cond([
|
|
501
|
+
[equals('ms'), always(' ms')],
|
|
502
|
+
[T, always(base1024 ? ' ib' : 'a')]
|
|
503
|
+
])(unit);
|
|
467
504
|
|
|
468
505
|
const formattedMetricValue = numeral(Math.abs(value))
|
|
469
506
|
.format(`0.[00]${formatSuffix}`)
|
|
470
|
-
.replace(
|
|
507
|
+
.replace(/iB/g, unit);
|
|
471
508
|
|
|
472
509
|
if (lt(value, 0)) {
|
|
473
510
|
return `-${formattedMetricValue}`;
|
|
@@ -476,6 +513,31 @@ const formatMetricValue = ({
|
|
|
476
513
|
return formattedMetricValue;
|
|
477
514
|
};
|
|
478
515
|
|
|
516
|
+
const formatMetricValueWithUnit = ({
|
|
517
|
+
value,
|
|
518
|
+
unit,
|
|
519
|
+
base = 1000,
|
|
520
|
+
isRaw = false
|
|
521
|
+
}: FormatMetricValueProps & { isRaw?: boolean }): string | null => {
|
|
522
|
+
if (isNil(value)) {
|
|
523
|
+
return null;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
if (isRaw) {
|
|
527
|
+
const unitText = equals('%', unit) ? unit : ` ${unit}`;
|
|
528
|
+
|
|
529
|
+
return `${value}${unitText}`;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
if (equals('%', unit)) {
|
|
533
|
+
return `${numeral(Math.abs(value)).format('0.[00]')}%`;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
const formattedMetricValue = formatMetricValue({ base, unit, value });
|
|
537
|
+
|
|
538
|
+
return formattedMetricValue;
|
|
539
|
+
};
|
|
540
|
+
|
|
479
541
|
const getStackedYScale = ({
|
|
480
542
|
leftScale,
|
|
481
543
|
rightScale
|
|
@@ -557,5 +619,6 @@ export {
|
|
|
557
619
|
getStackedYScale,
|
|
558
620
|
getTimeValue,
|
|
559
621
|
bisectDate,
|
|
560
|
-
getMetricWithLatestData
|
|
622
|
+
getMetricWithLatestData,
|
|
623
|
+
formatMetricValueWithUnit
|
|
561
624
|
};
|
|
@@ -14,14 +14,17 @@ interface DsData {
|
|
|
14
14
|
export interface Metric {
|
|
15
15
|
average_value: number | null;
|
|
16
16
|
crit: number | null;
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
critical_high_threshold: number | null;
|
|
18
|
+
critical_low_threshold: number | null;
|
|
19
|
+
data: Array<number | null>;
|
|
20
|
+
ds_data?: DsData;
|
|
19
21
|
legend: string;
|
|
20
22
|
maximum_value: number | null;
|
|
21
23
|
metric: string;
|
|
22
24
|
minimum_value: number | null;
|
|
23
25
|
unit: string;
|
|
24
|
-
|
|
26
|
+
warning_high_threshold: number | null;
|
|
27
|
+
warning_low_threshold: number | null;
|
|
25
28
|
}
|
|
26
29
|
|
|
27
30
|
type TimeSeries = { timeTick: string };
|
|
@@ -17,6 +17,7 @@ import { Theme } from '@mui/material';
|
|
|
17
17
|
import { Thresholds } from './models';
|
|
18
18
|
|
|
19
19
|
interface GetColorFromDataAndThresholdsProps {
|
|
20
|
+
baseColor?: string;
|
|
20
21
|
data: number;
|
|
21
22
|
theme: Theme;
|
|
22
23
|
thresholds: Thresholds;
|
|
@@ -25,10 +26,11 @@ interface GetColorFromDataAndThresholdsProps {
|
|
|
25
26
|
export const getColorFromDataAndTresholds = ({
|
|
26
27
|
data,
|
|
27
28
|
thresholds,
|
|
28
|
-
theme
|
|
29
|
+
theme,
|
|
30
|
+
baseColor
|
|
29
31
|
}: GetColorFromDataAndThresholdsProps): string => {
|
|
30
32
|
if (!thresholds.enabled) {
|
|
31
|
-
return theme.palette.
|
|
33
|
+
return baseColor || theme.palette.primary.main;
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
const criticalValues = pluck('value', thresholds.critical).sort();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { includes, map, prop, reject } from 'ramda';
|
|
2
2
|
import { makeStyles } from 'tss-react/mui';
|
|
3
3
|
|
|
4
|
-
import { Chip } from '@mui/material';
|
|
4
|
+
import { Chip, ChipProps } from '@mui/material';
|
|
5
5
|
import { UseAutocompleteProps } from '@mui/material/useAutocomplete';
|
|
6
6
|
|
|
7
7
|
import Autocomplete, { Props as AutocompleteProps } from '..';
|
|
@@ -18,7 +18,6 @@ const useStyles = makeStyles()((theme) => ({
|
|
|
18
18
|
width: theme.spacing(1.5)
|
|
19
19
|
},
|
|
20
20
|
tag: {
|
|
21
|
-
backgroundColor: theme.palette.divider,
|
|
22
21
|
fontSize: theme.typography.caption.fontSize
|
|
23
22
|
}
|
|
24
23
|
}));
|
|
@@ -33,6 +32,7 @@ export interface Props
|
|
|
33
32
|
UseAutocompleteProps<SelectEntry, Multiple, DisableClearable, FreeSolo>,
|
|
34
33
|
'multiple'
|
|
35
34
|
> {
|
|
35
|
+
chipProps?: ChipProps;
|
|
36
36
|
disableSortedOptions?: boolean;
|
|
37
37
|
getTagLabel?: (option) => string;
|
|
38
38
|
optionProperty?: string;
|
|
@@ -45,6 +45,7 @@ const MultiAutocompleteField = ({
|
|
|
45
45
|
optionProperty = 'name',
|
|
46
46
|
getOptionLabel = (option): string => option.name,
|
|
47
47
|
getTagLabel = (option): string => option[optionProperty],
|
|
48
|
+
chipProps,
|
|
48
49
|
...props
|
|
49
50
|
}: Props): JSX.Element => {
|
|
50
51
|
const { classes } = useStyles();
|
|
@@ -60,6 +61,7 @@ const MultiAutocompleteField = ({
|
|
|
60
61
|
label={getTagLabel(option)}
|
|
61
62
|
size="medium"
|
|
62
63
|
{...getTagProps({ index })}
|
|
64
|
+
{...chipProps}
|
|
63
65
|
/>
|
|
64
66
|
));
|
|
65
67
|
|
|
@@ -32,6 +32,7 @@ export type Props = {
|
|
|
32
32
|
displayPopupIcon?: boolean;
|
|
33
33
|
endAdornment?: React.ReactElement;
|
|
34
34
|
error?: string;
|
|
35
|
+
getOptionItemLabel?: (option) => string;
|
|
35
36
|
hideInput?: boolean;
|
|
36
37
|
label: string;
|
|
37
38
|
loading?: boolean;
|
|
@@ -158,6 +159,7 @@ const AutocompleteField = ({
|
|
|
158
159
|
autoSize = false,
|
|
159
160
|
autoSizeDefaultWidth = 0,
|
|
160
161
|
autoSizeCustomPadding,
|
|
162
|
+
getOptionItemLabel = (option) => option.name,
|
|
161
163
|
...autocompleteProps
|
|
162
164
|
}: Props): JSX.Element => {
|
|
163
165
|
const { classes, cx } = useStyles({ hideInput });
|
|
@@ -249,7 +251,7 @@ const AutocompleteField = ({
|
|
|
249
251
|
<Option
|
|
250
252
|
thumbnailUrl={displayOptionThumbnail ? option.url : undefined}
|
|
251
253
|
>
|
|
252
|
-
{option
|
|
254
|
+
{getOptionItemLabel(option)}
|
|
253
255
|
</Option>
|
|
254
256
|
</li>
|
|
255
257
|
);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { forwardRef, useCallback } from 'react';
|
|
2
2
|
|
|
3
|
-
import { isNil } from 'ramda';
|
|
3
|
+
import { equals, isNil } from 'ramda';
|
|
4
4
|
import { makeStyles } from 'tss-react/mui';
|
|
5
5
|
|
|
6
6
|
import {
|
|
@@ -9,7 +9,8 @@ import {
|
|
|
9
9
|
TextFieldProps,
|
|
10
10
|
Theme,
|
|
11
11
|
Tooltip,
|
|
12
|
-
Typography
|
|
12
|
+
Typography,
|
|
13
|
+
Box
|
|
13
14
|
} from '@mui/material';
|
|
14
15
|
|
|
15
16
|
import { getNormalizedId } from '../../utils';
|
|
@@ -18,8 +19,9 @@ import useAutoSize from './useAutoSize';
|
|
|
18
19
|
|
|
19
20
|
const useStyles = makeStyles<{ displayAsBlock: boolean }>()(
|
|
20
21
|
(theme: Theme, { displayAsBlock }) => ({
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
autoSizeCompact: {
|
|
23
|
+
paddingRight: theme.spacing(1),
|
|
24
|
+
paddingTop: theme.spacing(0.6)
|
|
23
25
|
},
|
|
24
26
|
hiddenText: {
|
|
25
27
|
display: 'table',
|
|
@@ -140,7 +142,7 @@ const TextField = forwardRef(
|
|
|
140
142
|
}, [innerValue, debounced, defaultValue]);
|
|
141
143
|
|
|
142
144
|
return (
|
|
143
|
-
|
|
145
|
+
<Box sx={{ width: autoSize ? 'auto' : '100%' }}>
|
|
144
146
|
<Tooltip placement="top" title={tooltipTitle}>
|
|
145
147
|
<MuiTextField
|
|
146
148
|
data-testid={dataTestId}
|
|
@@ -162,7 +164,8 @@ const TextField = forwardRef(
|
|
|
162
164
|
className: cx(
|
|
163
165
|
classes.inputBase,
|
|
164
166
|
{
|
|
165
|
-
[classes.transparent]: transparent
|
|
167
|
+
[classes.transparent]: transparent,
|
|
168
|
+
[classes.autoSizeCompact]: autoSize && equals(size, 'compact')
|
|
166
169
|
},
|
|
167
170
|
className
|
|
168
171
|
),
|
|
@@ -195,7 +198,7 @@ const TextField = forwardRef(
|
|
|
195
198
|
{rest.value || externalValueForAutoSize || innerValue}
|
|
196
199
|
</Typography>
|
|
197
200
|
)}
|
|
198
|
-
|
|
201
|
+
</Box>
|
|
199
202
|
);
|
|
200
203
|
}
|
|
201
204
|
);
|
|
@@ -56,7 +56,7 @@ describe('Rich Text Editor', () => {
|
|
|
56
56
|
.should('be.visible')
|
|
57
57
|
.and('have.value', '');
|
|
58
58
|
|
|
59
|
-
cy.
|
|
59
|
+
cy.findByLabelText('RichTextEditor').parent().contains('Type here...');
|
|
60
60
|
});
|
|
61
61
|
|
|
62
62
|
it('displays changes with undo redo buttons', () => {
|
|
@@ -187,11 +187,6 @@ describe('Rich Text Editor', () => {
|
|
|
187
187
|
});
|
|
188
188
|
|
|
189
189
|
it('displays editor when editable props is false and an initialState exist', () => {
|
|
190
|
-
cy.get('[data-testid="RichTextEditor"]').should(
|
|
191
|
-
'have.attr',
|
|
192
|
-
'contenteditable',
|
|
193
|
-
'false'
|
|
194
|
-
);
|
|
195
190
|
cy.findByLabelText('Undo').should('not.exist');
|
|
196
191
|
cy.findByLabelText('Redo').should('not.exist');
|
|
197
192
|
cy.findByLabelText('bold').should('not.exist');
|
|
@@ -216,11 +211,6 @@ describe('Rich Text Editor', () => {
|
|
|
216
211
|
});
|
|
217
212
|
|
|
218
213
|
it('displays editor when editable props is false and an initialState exist', () => {
|
|
219
|
-
cy.get('[data-testid="RichTextEditor"]').should(
|
|
220
|
-
'have.attr',
|
|
221
|
-
'contenteditable',
|
|
222
|
-
'false'
|
|
223
|
-
);
|
|
224
214
|
cy.findByLabelText('Undo').should('be.disabled');
|
|
225
215
|
cy.findByLabelText('Redo').should('be.disabled');
|
|
226
216
|
cy.findByLabelText('bold').should('be.disabled');
|
|
@@ -245,12 +235,14 @@ describe('Rich Text Editor', () => {
|
|
|
245
235
|
});
|
|
246
236
|
});
|
|
247
237
|
it('displays editor with custom namespace', () => {
|
|
248
|
-
cy.get('
|
|
238
|
+
cy.get('[data-testid="CypressComponentTest"] > p').should('be.exist');
|
|
249
239
|
cy.get('[data-testid="CypressComponentTest"]').should('be.exist');
|
|
250
240
|
});
|
|
251
241
|
|
|
252
242
|
it('displays editor with custom placeholder', () => {
|
|
253
|
-
cy.get('
|
|
243
|
+
cy.get('[data-testid="CypressComponentTest"]')
|
|
244
|
+
.parent()
|
|
245
|
+
.contains('Type Cypress test...');
|
|
254
246
|
});
|
|
255
247
|
|
|
256
248
|
it('displays editor with custom minimum input height', () => {
|
|
@@ -23,6 +23,7 @@ import FloatingLinkEditorPlugin from './plugins/FloatingLinkEditorPlugin';
|
|
|
23
23
|
export interface RichTextEditorProps {
|
|
24
24
|
contentClassName?: string;
|
|
25
25
|
disabled?: boolean;
|
|
26
|
+
displayBlockButtons?: boolean;
|
|
26
27
|
displayMacrosButton?: boolean;
|
|
27
28
|
editable: boolean;
|
|
28
29
|
editorState?: string;
|
|
@@ -132,9 +133,10 @@ const RichTextEditor = ({
|
|
|
132
133
|
onBlur,
|
|
133
134
|
contentClassName,
|
|
134
135
|
displayMacrosButton = false,
|
|
135
|
-
disabled,
|
|
136
|
+
disabled = false,
|
|
136
137
|
openLinkInNewTab = true,
|
|
137
|
-
initialize
|
|
138
|
+
initialize,
|
|
139
|
+
displayBlockButtons = true
|
|
138
140
|
}: RichTextEditorProps): JSX.Element => {
|
|
139
141
|
const { classes } = useStyles({ toolbarPositions });
|
|
140
142
|
|
|
@@ -174,6 +176,7 @@ const RichTextEditor = ({
|
|
|
174
176
|
<div className={classes.toolbar}>
|
|
175
177
|
<ToolbarPlugin
|
|
176
178
|
disabled={disabled}
|
|
179
|
+
displayBlockButtons={displayBlockButtons}
|
|
177
180
|
displayMacrosButton={displayMacrosButton}
|
|
178
181
|
editable={editable}
|
|
179
182
|
/>
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
|
|
1
3
|
import {
|
|
2
4
|
AutoLinkPlugin,
|
|
3
5
|
LinkMatcher
|
|
4
6
|
} from '@lexical/react/LexicalAutoLinkPlugin';
|
|
7
|
+
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
8
|
+
import { BLUR_COMMAND, COMMAND_PRIORITY_LOW, FOCUS_COMMAND } from 'lexical';
|
|
5
9
|
|
|
6
10
|
interface LinkAttributes {
|
|
7
11
|
rel?: null | string;
|
|
@@ -56,7 +60,42 @@ const AutoCompleteLinkPlugin = ({
|
|
|
56
60
|
openLinkInNewTab
|
|
57
61
|
}: {
|
|
58
62
|
openLinkInNewTab: boolean;
|
|
59
|
-
}): JSX.Element => {
|
|
63
|
+
}): JSX.Element | null => {
|
|
64
|
+
const [editor] = useLexicalComposerContext();
|
|
65
|
+
const [hasFocus, setFocus] = useState(false);
|
|
66
|
+
|
|
67
|
+
useEffect(
|
|
68
|
+
() =>
|
|
69
|
+
editor.registerCommand(
|
|
70
|
+
BLUR_COMMAND,
|
|
71
|
+
() => {
|
|
72
|
+
setFocus(false);
|
|
73
|
+
|
|
74
|
+
return false;
|
|
75
|
+
},
|
|
76
|
+
COMMAND_PRIORITY_LOW
|
|
77
|
+
),
|
|
78
|
+
[]
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
useEffect(
|
|
82
|
+
() =>
|
|
83
|
+
editor.registerCommand(
|
|
84
|
+
FOCUS_COMMAND,
|
|
85
|
+
() => {
|
|
86
|
+
setFocus(true);
|
|
87
|
+
|
|
88
|
+
return false;
|
|
89
|
+
},
|
|
90
|
+
COMMAND_PRIORITY_LOW
|
|
91
|
+
),
|
|
92
|
+
[]
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
if (!hasFocus) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
|
|
60
99
|
return <AutoLinkPlugin matchers={getMatchers(openLinkInNewTab)} />;
|
|
61
100
|
};
|
|
62
101
|
|