@scality/core-ui 0.165.0 → 0.167.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/.storybook/preview.js +6 -4
- package/README.md +27 -80
- package/dist/components/barchart/BarChart.component.d.ts +5 -0
- package/dist/components/barchart/BarChart.component.d.ts.map +1 -1
- package/dist/components/barchart/BarChart.component.js +5 -0
- package/dist/components/barchartv2/Barchart.component.d.ts +19 -0
- package/dist/components/barchartv2/Barchart.component.d.ts.map +1 -1
- package/dist/components/barchartv2/Barchart.component.js +31 -5
- package/dist/components/barchartv2/ChartTooltip.d.ts +18 -0
- package/dist/components/barchartv2/ChartTooltip.d.ts.map +1 -0
- package/dist/components/barchartv2/ChartTooltip.js +31 -0
- package/dist/components/barchartv2/utils.d.ts +0 -7
- package/dist/components/barchartv2/utils.d.ts.map +1 -1
- package/dist/components/barchartv2/utils.js +1 -29
- package/dist/components/button/Button.component.d.ts +2 -1
- package/dist/components/button/Button.component.d.ts.map +1 -1
- package/dist/components/button/Button.component.js +2 -1
- package/dist/components/chartlegend/ChartLegend.d.ts +9 -0
- package/dist/components/chartlegend/ChartLegend.d.ts.map +1 -1
- package/dist/components/chartlegend/ChartLegend.js +32 -9
- package/dist/components/chartlegend/ChartLegendWrapper.d.ts +3 -0
- package/dist/components/chartlegend/ChartLegendWrapper.d.ts.map +1 -1
- package/dist/components/chartlegend/ChartLegendWrapper.js +18 -2
- package/dist/components/constants.d.ts +2 -0
- package/dist/components/constants.d.ts.map +1 -1
- package/dist/components/constants.js +6 -0
- package/dist/components/constrainedtext/Constrainedtext.component.d.ts +3 -1
- package/dist/components/constrainedtext/Constrainedtext.component.d.ts.map +1 -1
- package/dist/components/constrainedtext/Constrainedtext.component.js +2 -2
- package/dist/components/date/FormattedDateTime.d.ts +2 -1
- package/dist/components/date/FormattedDateTime.d.ts.map +1 -1
- package/dist/components/date/FormattedDateTime.js +10 -0
- package/dist/components/icon/Icon.component.d.ts +9 -11
- package/dist/components/icon/Icon.component.d.ts.map +1 -1
- package/dist/components/icon/Icon.component.js +2 -0
- package/dist/components/linetemporalchart/ChartUtil.d.ts +3 -2
- package/dist/components/linetemporalchart/ChartUtil.d.ts.map +1 -1
- package/dist/components/linetemporalchart/ChartUtil.js +24 -20
- package/dist/components/linetemporalchart/LineTemporalChart.component.d.ts +4 -0
- package/dist/components/linetemporalchart/LineTemporalChart.component.d.ts.map +1 -1
- package/dist/components/linetemporalchart/LineTemporalChart.component.js +4 -0
- package/dist/components/linetimeseriechart/linetimeseriechart.component.d.ts +4 -2
- package/dist/components/linetimeseriechart/linetimeseriechart.component.d.ts.map +1 -1
- package/dist/components/linetimeseriechart/linetimeseriechart.component.js +5 -7
- package/dist/components/text/Text.component.js +1 -1
- package/dist/components/toast/Toast.component.d.ts.map +1 -1
- package/dist/components/toast/Toast.component.js +24 -11
- package/dist/index.d.ts +1 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -11
- package/dist/next.d.ts +2 -0
- package/dist/next.d.ts.map +1 -1
- package/dist/next.js +1 -0
- package/jest.config.js +1 -0
- package/package.json +16 -38
- package/src/lib/components/barchart/BarChart.component.tsx +5 -0
- package/src/lib/components/barchartv2/Barchart.component.test.tsx +99 -1
- package/src/lib/components/barchartv2/Barchart.component.tsx +39 -11
- package/src/lib/components/barchartv2/ChartTooltip.tsx +76 -0
- package/src/lib/components/barchartv2/utils.ts +2 -33
- package/src/lib/components/button/Button.component.tsx +2 -1
- package/src/lib/components/chartlegend/ChartLegend.test.tsx +235 -0
- package/src/lib/components/chartlegend/ChartLegend.tsx +36 -8
- package/src/lib/components/chartlegend/ChartLegendWrapper.tsx +67 -29
- package/src/lib/components/constants.ts +11 -0
- package/src/lib/components/constrainedtext/Constrainedtext.component.tsx +5 -2
- package/src/lib/components/date/FormattedDateTime.tsx +15 -1
- package/src/lib/components/icon/Icon.component.tsx +12 -1
- package/src/lib/components/linetemporalchart/ChartUtil.test.ts +23 -35
- package/src/lib/components/linetemporalchart/ChartUtil.ts +32 -26
- package/src/lib/components/linetemporalchart/LineTemporalChart.component.tsx +19 -15
- package/src/lib/components/linetimeseriechart/linetimeseriechart.component.tsx +13 -11
- package/src/lib/components/selectv2/selectv2.test.tsx +1 -1
- package/src/lib/components/text/Text.component.tsx +1 -1
- package/src/lib/components/toast/Toast.component.tsx +27 -19
- package/src/lib/components/toast/useMutationsHandler.test.tsx +22 -32
- package/src/lib/index.ts +6 -11
- package/src/lib/next.ts +2 -0
- package/stories/barchart.stories.tsx +1 -1
- package/stories/card.stories.tsx +7 -5
- package/stories/constrainedtext.stories.tsx +4 -1
- package/stories/controls.ts +19 -20
- package/stories/linecharttemporal.stories.tsx +1 -1
- package/stories/linetimeseriechart.stories.tsx +79 -25
- package/stories/navbar.stories.tsx +103 -0
- package/stories/tabsv2.stories.tsx +51 -56
- package/dist/components/areachart/AreaChart.component.d.ts +0 -13
- package/dist/components/areachart/AreaChart.component.d.ts.map +0 -1
- package/dist/components/areachart/AreaChart.component.js +0 -27
- package/dist/components/chips/Chips.component.d.ts +0 -21
- package/dist/components/chips/Chips.component.d.ts.map +0 -1
- package/dist/components/chips/Chips.component.js +0 -105
- package/dist/components/cloudprogressbar/CloudProgressBar.component.d.ts +0 -10
- package/dist/components/cloudprogressbar/CloudProgressBar.component.d.ts.map +0 -1
- package/dist/components/cloudprogressbar/CloudProgressBar.component.js +0 -38
- package/dist/components/collapsiblepanel/CollapsiblePanel.component.d.ts +0 -9
- package/dist/components/collapsiblepanel/CollapsiblePanel.component.d.ts.map +0 -1
- package/dist/components/collapsiblepanel/CollapsiblePanel.component.js +0 -44
- package/dist/components/linechart/LineChart.component.d.ts +0 -21
- package/dist/components/linechart/LineChart.component.d.ts.map +0 -1
- package/dist/components/linechart/LineChart.component.js +0 -109
- package/dist/components/multiselect/MultiSelect.component.d.ts +0 -28
- package/dist/components/multiselect/MultiSelect.component.d.ts.map +0 -1
- package/dist/components/multiselect/MultiSelect.component.js +0 -73
- package/dist/components/select/Select.component.d.ts +0 -14
- package/dist/components/select/Select.component.d.ts.map +0 -1
- package/dist/components/select/Select.component.js +0 -71
- package/dist/components/spacedbox/SpacedBox.d.ts +0 -34
- package/dist/components/spacedbox/SpacedBox.d.ts.map +0 -1
- package/dist/components/spacedbox/SpacedBox.js +0 -64
- package/dist/components/sparkline/SparkLine.component.d.ts +0 -18
- package/dist/components/sparkline/SparkLine.component.d.ts.map +0 -1
- package/dist/components/sparkline/SparkLine.component.js +0 -148
- package/dist/components/vegachart/VegaChart.component.d.ts +0 -13
- package/dist/components/vegachart/VegaChart.component.d.ts.map +0 -1
- package/dist/components/vegachart/VegaChart.component.js +0 -120
- package/plopfile.js +0 -38
- package/src/lib/components/areachart/AreaChart.component.tsx +0 -49
- package/src/lib/components/chips/Chips.component.tsx +0 -169
- package/src/lib/components/cloudprogressbar/CloudProgressBar.component.tsx +0 -105
- package/src/lib/components/collapsiblepanel/CollapsiblePanel.component.tsx +0 -77
- package/src/lib/components/linechart/LineChart.component.tsx +0 -152
- package/src/lib/components/multiselect/MultiSelect.component.tsx +0 -158
- package/src/lib/components/select/Select.component.tsx +0 -98
- package/src/lib/components/spacedbox/SpacedBox.ts +0 -116
- package/src/lib/components/sparkline/SparkLine.component.tsx +0 -176
- package/src/lib/components/vegachart/VegaChart.component.tsx +0 -146
- package/stories/areachart.stories.tsx +0 -120
- package/stories/chips.stories.tsx +0 -107
- package/stories/cloudprogressbar.stories.tsx +0 -93
- package/stories/collapsiblepanel.stories.tsx +0 -57
- package/stories/data/areachart.ts +0 -122
- package/stories/data/sparklinechart.ts +0 -164
- package/stories/linechart.stories.tsx +0 -319
- package/stories/multiselect.stories.tsx +0 -126
- package/stories/select.stories.tsx +0 -52
- package/stories/sparkline.stories.tsx +0 -85
- package/stories/vegachart.stories.tsx +0 -98
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import { ChartLegend } from './ChartLegend';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { ChartLegendWrapper } from './ChartLegendWrapper';
|
|
5
|
+
import userEvent from '@testing-library/user-event';
|
|
6
|
+
|
|
7
|
+
const colorSet = {
|
|
8
|
+
CPU: 'red',
|
|
9
|
+
Memory: 'blue',
|
|
10
|
+
Disk: 'green',
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
describe('ChartLegend', () => {
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
jest.clearAllMocks();
|
|
16
|
+
});
|
|
17
|
+
it('should render all legend items', () => {
|
|
18
|
+
render(
|
|
19
|
+
<ChartLegendWrapper colorSet={colorSet}>
|
|
20
|
+
<ChartLegend shape="line" />
|
|
21
|
+
</ChartLegendWrapper>,
|
|
22
|
+
);
|
|
23
|
+
expect(screen.getByText('CPU')).toBeInTheDocument();
|
|
24
|
+
expect(screen.getByText('Memory')).toBeInTheDocument();
|
|
25
|
+
expect(screen.getByText('Disk')).toBeInTheDocument();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should have all items selected by default', () => {
|
|
29
|
+
render(
|
|
30
|
+
<ChartLegendWrapper colorSet={colorSet}>
|
|
31
|
+
<ChartLegend shape="line" />
|
|
32
|
+
</ChartLegendWrapper>,
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
expect(screen.getByLabelText('CPU selected')).toBeInTheDocument();
|
|
36
|
+
expect(screen.getByLabelText('Memory selected')).toBeInTheDocument();
|
|
37
|
+
expect(screen.getByLabelText('Disk selected')).toBeInTheDocument();
|
|
38
|
+
});
|
|
39
|
+
it('should not respond to clicks when disabled', () => {
|
|
40
|
+
render(
|
|
41
|
+
<ChartLegendWrapper colorSet={colorSet}>
|
|
42
|
+
<ChartLegend shape="line" disabled />
|
|
43
|
+
</ChartLegendWrapper>,
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
userEvent.click(screen.getByText('CPU'));
|
|
47
|
+
// If disabled, should not select any items
|
|
48
|
+
expect(screen.getByLabelText('CPU selected')).toBeInTheDocument();
|
|
49
|
+
expect(screen.getByLabelText('Memory selected')).toBeInTheDocument();
|
|
50
|
+
expect(screen.getByLabelText('Disk selected')).toBeInTheDocument();
|
|
51
|
+
|
|
52
|
+
userEvent.click(screen.getByText('Memory'), { metaKey: true });
|
|
53
|
+
// If disabled, should not select any items
|
|
54
|
+
expect(screen.getByLabelText('CPU selected')).toBeInTheDocument();
|
|
55
|
+
expect(screen.getByLabelText('Memory selected')).toBeInTheDocument();
|
|
56
|
+
expect(screen.getByLabelText('Disk selected')).toBeInTheDocument();
|
|
57
|
+
});
|
|
58
|
+
describe('Normal Click Behavior', () => {
|
|
59
|
+
it('should select only clicked item when all are selected', () => {
|
|
60
|
+
render(
|
|
61
|
+
<ChartLegendWrapper colorSet={colorSet}>
|
|
62
|
+
<ChartLegend shape="line" />
|
|
63
|
+
</ChartLegendWrapper>,
|
|
64
|
+
);
|
|
65
|
+
expect(screen.getByLabelText('CPU selected')).toBeInTheDocument();
|
|
66
|
+
expect(screen.getByLabelText('Memory selected')).toBeInTheDocument();
|
|
67
|
+
expect(screen.getByLabelText('Disk selected')).toBeInTheDocument();
|
|
68
|
+
|
|
69
|
+
// Click on CPU should select only CPU
|
|
70
|
+
userEvent.click(screen.getByLabelText('CPU selected'));
|
|
71
|
+
|
|
72
|
+
expect(screen.getByLabelText('CPU selected')).toBeInTheDocument();
|
|
73
|
+
expect(screen.getByLabelText('Memory not selected')).toBeInTheDocument();
|
|
74
|
+
expect(screen.getByLabelText('Disk not selected')).toBeInTheDocument();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should select all items when clicking on the only selected item', () => {
|
|
78
|
+
render(
|
|
79
|
+
<ChartLegendWrapper colorSet={colorSet}>
|
|
80
|
+
<ChartLegend shape="line" />
|
|
81
|
+
</ChartLegendWrapper>,
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
// First click to select only CPU
|
|
85
|
+
userEvent.click(screen.getByLabelText('CPU selected'));
|
|
86
|
+
|
|
87
|
+
// Second click should select all
|
|
88
|
+
userEvent.click(screen.getByLabelText('CPU selected'));
|
|
89
|
+
|
|
90
|
+
expect(screen.getByLabelText('CPU selected')).toBeInTheDocument();
|
|
91
|
+
expect(screen.getByLabelText('Memory selected')).toBeInTheDocument();
|
|
92
|
+
expect(screen.getByLabelText('Disk selected')).toBeInTheDocument();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('should select only clicked item when clicking unselected item', () => {
|
|
96
|
+
render(
|
|
97
|
+
<ChartLegendWrapper colorSet={colorSet}>
|
|
98
|
+
<ChartLegend shape="line" />
|
|
99
|
+
</ChartLegendWrapper>,
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
// Select only CPU first
|
|
103
|
+
userEvent.click(screen.getByLabelText('CPU selected'));
|
|
104
|
+
expect(screen.getByLabelText('CPU selected')).toBeInTheDocument();
|
|
105
|
+
expect(screen.getByLabelText('Memory not selected')).toBeInTheDocument();
|
|
106
|
+
|
|
107
|
+
// Click on unselected Memory should select only Memory
|
|
108
|
+
userEvent.click(screen.getByText('Memory'));
|
|
109
|
+
expect(screen.getByLabelText('CPU not selected')).toBeInTheDocument();
|
|
110
|
+
expect(screen.getByLabelText('Memory selected')).toBeInTheDocument();
|
|
111
|
+
expect(screen.getByLabelText('Disk not selected')).toBeInTheDocument();
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe('Cmd+Click Behavior', () => {
|
|
116
|
+
it('should add unselected item to selection when cmd+clicking', () => {
|
|
117
|
+
render(
|
|
118
|
+
<ChartLegendWrapper colorSet={colorSet}>
|
|
119
|
+
<ChartLegend shape="line" />
|
|
120
|
+
</ChartLegendWrapper>,
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
// Select only CPU first
|
|
124
|
+
userEvent.click(screen.getByText('CPU'));
|
|
125
|
+
expect(screen.getByLabelText('CPU selected')).toBeInTheDocument();
|
|
126
|
+
expect(screen.getByLabelText('Memory not selected')).toBeInTheDocument();
|
|
127
|
+
|
|
128
|
+
// Cmd+click Memory should add it to selection
|
|
129
|
+
userEvent.click(screen.getByText('Memory'), { metaKey: true });
|
|
130
|
+
expect(screen.getByLabelText('CPU selected')).toBeInTheDocument();
|
|
131
|
+
expect(screen.getByLabelText('Memory selected')).toBeInTheDocument();
|
|
132
|
+
expect(screen.getByLabelText('Disk not selected')).toBeInTheDocument();
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('should remove selected item from selection when cmd+clicking with multiple selected', () => {
|
|
136
|
+
render(
|
|
137
|
+
<ChartLegendWrapper colorSet={colorSet}>
|
|
138
|
+
<ChartLegend shape="line" />
|
|
139
|
+
</ChartLegendWrapper>,
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
// Start with all selected, select only CPU, then add Memory
|
|
143
|
+
userEvent.click(screen.getByText('CPU'));
|
|
144
|
+
userEvent.click(screen.getByText('Memory'), { metaKey: true });
|
|
145
|
+
|
|
146
|
+
expect(screen.getByLabelText('CPU selected')).toBeInTheDocument();
|
|
147
|
+
expect(screen.getByLabelText('Memory selected')).toBeInTheDocument();
|
|
148
|
+
expect(screen.getByLabelText('Disk not selected')).toBeInTheDocument();
|
|
149
|
+
|
|
150
|
+
// Cmd+click CPU should remove it
|
|
151
|
+
userEvent.click(screen.getByText('CPU'), { metaKey: true });
|
|
152
|
+
expect(screen.getByLabelText('CPU not selected')).toBeInTheDocument();
|
|
153
|
+
expect(screen.getByLabelText('Memory selected')).toBeInTheDocument();
|
|
154
|
+
expect(screen.getByLabelText('Disk not selected')).toBeInTheDocument();
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('should select all when cmd+clicking the only selected item', () => {
|
|
158
|
+
render(
|
|
159
|
+
<ChartLegendWrapper colorSet={colorSet}>
|
|
160
|
+
<ChartLegend shape="line" />
|
|
161
|
+
</ChartLegendWrapper>,
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
// Select only CPU
|
|
165
|
+
userEvent.click(screen.getByText('CPU'));
|
|
166
|
+
expect(screen.getByLabelText('CPU selected')).toBeInTheDocument();
|
|
167
|
+
expect(screen.getByLabelText('Memory not selected')).toBeInTheDocument();
|
|
168
|
+
expect(screen.getByLabelText('Disk not selected')).toBeInTheDocument();
|
|
169
|
+
|
|
170
|
+
// Cmd+click the only selected item should select all
|
|
171
|
+
userEvent.click(screen.getByText('CPU'), { metaKey: true });
|
|
172
|
+
expect(screen.getByLabelText('CPU selected')).toBeInTheDocument();
|
|
173
|
+
expect(screen.getByLabelText('Memory selected')).toBeInTheDocument();
|
|
174
|
+
expect(screen.getByLabelText('Disk selected')).toBeInTheDocument();
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('should work with ctrl+click on Windows/Linux', () => {
|
|
178
|
+
render(
|
|
179
|
+
<ChartLegendWrapper colorSet={colorSet}>
|
|
180
|
+
<ChartLegend shape="line" />
|
|
181
|
+
</ChartLegendWrapper>,
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
// Select only CPU first
|
|
185
|
+
userEvent.click(screen.getByText('CPU'));
|
|
186
|
+
|
|
187
|
+
// Ctrl+click Memory should add it to selection
|
|
188
|
+
userEvent.click(screen.getByText('Memory'), { ctrlKey: true });
|
|
189
|
+
expect(screen.getByLabelText('CPU selected')).toBeInTheDocument();
|
|
190
|
+
expect(screen.getByLabelText('Memory selected')).toBeInTheDocument();
|
|
191
|
+
expect(screen.getByLabelText('Disk not selected')).toBeInTheDocument();
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('should handle mixed normal and cmd+clicks correctly', () => {
|
|
195
|
+
render(
|
|
196
|
+
<ChartLegendWrapper colorSet={colorSet}>
|
|
197
|
+
<ChartLegend shape="line" />
|
|
198
|
+
</ChartLegendWrapper>,
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
// Normal click to select CPU only
|
|
202
|
+
userEvent.click(screen.getByText('CPU'));
|
|
203
|
+
expect(screen.getByLabelText('CPU selected')).toBeInTheDocument();
|
|
204
|
+
expect(screen.getByLabelText('Memory not selected')).toBeInTheDocument();
|
|
205
|
+
|
|
206
|
+
// Cmd+click to add Memory
|
|
207
|
+
userEvent.click(screen.getByText('Memory'), { metaKey: true });
|
|
208
|
+
expect(screen.getByLabelText('CPU selected')).toBeInTheDocument();
|
|
209
|
+
expect(screen.getByLabelText('Memory selected')).toBeInTheDocument();
|
|
210
|
+
|
|
211
|
+
// Normal click on Disk should select only Disk
|
|
212
|
+
userEvent.click(screen.getByText('Disk'));
|
|
213
|
+
expect(screen.getByLabelText('CPU not selected')).toBeInTheDocument();
|
|
214
|
+
expect(screen.getByLabelText('Memory not selected')).toBeInTheDocument();
|
|
215
|
+
expect(screen.getByLabelText('Disk selected')).toBeInTheDocument();
|
|
216
|
+
});
|
|
217
|
+
it('should select one item when clicking on selected item and other items are selected', () => {
|
|
218
|
+
render(
|
|
219
|
+
<ChartLegendWrapper colorSet={colorSet}>
|
|
220
|
+
<ChartLegend shape="line" />
|
|
221
|
+
</ChartLegendWrapper>,
|
|
222
|
+
);
|
|
223
|
+
userEvent.click(screen.getByText('CPU'));
|
|
224
|
+
userEvent.click(screen.getByText('Memory'), { metaKey: true });
|
|
225
|
+
|
|
226
|
+
expect(screen.getByLabelText('CPU selected')).toBeInTheDocument();
|
|
227
|
+
expect(screen.getByLabelText('Memory selected')).toBeInTheDocument();
|
|
228
|
+
expect(screen.getByLabelText('Disk not selected')).toBeInTheDocument();
|
|
229
|
+
userEvent.click(screen.getByText('CPU'));
|
|
230
|
+
expect(screen.getByLabelText('CPU selected')).toBeInTheDocument();
|
|
231
|
+
expect(screen.getByLabelText('Memory not selected')).toBeInTheDocument();
|
|
232
|
+
expect(screen.getByLabelText('Disk not selected')).toBeInTheDocument();
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
});
|
|
@@ -18,7 +18,10 @@ const Legend = styled.div<{ direction: 'horizontal' | 'vertical' }>`
|
|
|
18
18
|
flex-wrap: wrap;
|
|
19
19
|
`;
|
|
20
20
|
|
|
21
|
-
const LegendItem = styled.div<{
|
|
21
|
+
export const LegendItem = styled.div<{
|
|
22
|
+
disabled?: boolean;
|
|
23
|
+
selected?: boolean;
|
|
24
|
+
}>`
|
|
22
25
|
display: flex;
|
|
23
26
|
align-items: center;
|
|
24
27
|
gap: 8px;
|
|
@@ -31,7 +34,7 @@ const LegendItem = styled.div<{ disabled?: boolean; selected?: boolean }>`
|
|
|
31
34
|
}
|
|
32
35
|
`;
|
|
33
36
|
|
|
34
|
-
const LegendShape = styled.div<{
|
|
37
|
+
export const LegendShape = styled.div<{
|
|
35
38
|
color?: string;
|
|
36
39
|
shape: 'line' | 'rectangle';
|
|
37
40
|
chartColors: Record<string, string>;
|
|
@@ -69,21 +72,45 @@ export const ChartLegend = ({
|
|
|
69
72
|
isSelected,
|
|
70
73
|
addSelectedResource,
|
|
71
74
|
removeSelectedResource,
|
|
75
|
+
selectAllResources,
|
|
76
|
+
selectOnlyResource,
|
|
77
|
+
isOnlyOneSelected,
|
|
72
78
|
} = useChartLegend();
|
|
73
79
|
|
|
74
80
|
const resources = listResources();
|
|
75
81
|
|
|
76
82
|
const handleLegendClick = useCallback(
|
|
77
|
-
(resource: string) => {
|
|
83
|
+
(resource: string, event: React.MouseEvent) => {
|
|
78
84
|
if (disabled) return;
|
|
79
85
|
|
|
80
|
-
|
|
81
|
-
|
|
86
|
+
const isModifierClick = event.metaKey || event.ctrlKey;
|
|
87
|
+
const itemIsSelected = isSelected(resource);
|
|
88
|
+
|
|
89
|
+
if (isModifierClick) {
|
|
90
|
+
if (itemIsSelected) {
|
|
91
|
+
if (isOnlyOneSelected()) {
|
|
92
|
+
selectAllResources();
|
|
93
|
+
} else {
|
|
94
|
+
removeSelectedResource(resource);
|
|
95
|
+
}
|
|
96
|
+
} else {
|
|
97
|
+
addSelectedResource(resource);
|
|
98
|
+
}
|
|
99
|
+
} else if (itemIsSelected && isOnlyOneSelected()) {
|
|
100
|
+
selectAllResources();
|
|
82
101
|
} else {
|
|
83
|
-
|
|
102
|
+
selectOnlyResource(resource);
|
|
84
103
|
}
|
|
85
104
|
},
|
|
86
|
-
[
|
|
105
|
+
[
|
|
106
|
+
disabled,
|
|
107
|
+
isSelected,
|
|
108
|
+
addSelectedResource,
|
|
109
|
+
removeSelectedResource,
|
|
110
|
+
selectAllResources,
|
|
111
|
+
selectOnlyResource,
|
|
112
|
+
isOnlyOneSelected,
|
|
113
|
+
],
|
|
87
114
|
);
|
|
88
115
|
|
|
89
116
|
return (
|
|
@@ -97,7 +124,8 @@ export const ChartLegend = ({
|
|
|
97
124
|
key={resource}
|
|
98
125
|
disabled={disabled}
|
|
99
126
|
selected={selected}
|
|
100
|
-
|
|
127
|
+
aria-label={`${resource} ${selected ? 'selected' : 'not selected'}`}
|
|
128
|
+
onClick={(event) => handleLegendClick(resource, event)}
|
|
101
129
|
>
|
|
102
130
|
<LegendShape
|
|
103
131
|
color={color}
|
|
@@ -1,13 +1,23 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
createContext,
|
|
3
|
+
useContext,
|
|
4
|
+
useState,
|
|
5
|
+
ReactNode,
|
|
6
|
+
useMemo,
|
|
7
|
+
useCallback,
|
|
8
|
+
} from 'react';
|
|
2
9
|
import { ChartColors } from '../../style/theme';
|
|
3
10
|
|
|
4
11
|
export type ChartLegendState = {
|
|
5
12
|
selectedResources: string[];
|
|
6
13
|
addSelectedResource: (resource: string) => void;
|
|
7
14
|
removeSelectedResource: (resource: string) => void;
|
|
15
|
+
selectAllResources: () => void;
|
|
16
|
+
selectOnlyResource: (resource: string) => void;
|
|
8
17
|
isSelected: (resource: string) => boolean;
|
|
9
18
|
getColor: (resource: string) => string | undefined;
|
|
10
19
|
listResources: () => string[];
|
|
20
|
+
isOnlyOneSelected: () => boolean;
|
|
11
21
|
};
|
|
12
22
|
|
|
13
23
|
const ChartLegendContext = createContext<ChartLegendState | null>(null);
|
|
@@ -21,7 +31,9 @@ export const ChartLegendWrapper = ({
|
|
|
21
31
|
children,
|
|
22
32
|
colorSet,
|
|
23
33
|
}: ChartLegendWrapperProps) => {
|
|
24
|
-
const
|
|
34
|
+
const allResources = Object.keys(colorSet);
|
|
35
|
+
const [selectedResources, setSelectedResources] =
|
|
36
|
+
useState<string[]>(allResources);
|
|
25
37
|
|
|
26
38
|
const addSelectedResource = useCallback((resource: string) => {
|
|
27
39
|
setSelectedResources((prev) =>
|
|
@@ -33,40 +45,66 @@ export const ChartLegendWrapper = ({
|
|
|
33
45
|
setSelectedResources((prev) => prev.filter((r) => r !== resource));
|
|
34
46
|
}, []);
|
|
35
47
|
|
|
36
|
-
const
|
|
37
|
-
|
|
48
|
+
const selectAllResources = useCallback(() => {
|
|
49
|
+
setSelectedResources(allResources);
|
|
50
|
+
}, [allResources]);
|
|
51
|
+
|
|
52
|
+
const selectOnlyResource = useCallback((resource: string) => {
|
|
53
|
+
setSelectedResources([resource]);
|
|
54
|
+
}, []);
|
|
55
|
+
|
|
56
|
+
const isOnlyOneSelected = useCallback(() => {
|
|
57
|
+
return selectedResources.length === 1;
|
|
38
58
|
}, [selectedResources]);
|
|
59
|
+
const isSelected = useCallback(
|
|
60
|
+
(resource: string) => {
|
|
61
|
+
return selectedResources.includes(resource);
|
|
62
|
+
},
|
|
63
|
+
[selectedResources],
|
|
64
|
+
);
|
|
39
65
|
|
|
40
|
-
const getColor = useCallback(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
66
|
+
const getColor = useCallback(
|
|
67
|
+
(resource: string) => {
|
|
68
|
+
const color = colorSet[resource];
|
|
69
|
+
if (!color) {
|
|
70
|
+
console.warn(
|
|
71
|
+
`ChartLegendWrapper: No color defined for resource "${resource}"`,
|
|
72
|
+
);
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
return color;
|
|
76
|
+
},
|
|
77
|
+
[colorSet],
|
|
78
|
+
);
|
|
50
79
|
|
|
51
80
|
const listResources = useCallback(() => {
|
|
52
81
|
return Object.keys(colorSet);
|
|
53
82
|
}, [colorSet]);
|
|
54
83
|
|
|
55
|
-
const chartLegendState = useMemo(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
84
|
+
const chartLegendState = useMemo(
|
|
85
|
+
() => ({
|
|
86
|
+
selectedResources,
|
|
87
|
+
addSelectedResource,
|
|
88
|
+
removeSelectedResource,
|
|
89
|
+
selectAllResources,
|
|
90
|
+
selectOnlyResource,
|
|
91
|
+
isSelected,
|
|
92
|
+
getColor,
|
|
93
|
+
listResources,
|
|
94
|
+
isOnlyOneSelected,
|
|
95
|
+
}),
|
|
96
|
+
[
|
|
97
|
+
selectedResources,
|
|
98
|
+
addSelectedResource,
|
|
99
|
+
removeSelectedResource,
|
|
100
|
+
selectAllResources,
|
|
101
|
+
selectOnlyResource,
|
|
102
|
+
isSelected,
|
|
103
|
+
getColor,
|
|
104
|
+
listResources,
|
|
105
|
+
isOnlyOneSelected,
|
|
106
|
+
],
|
|
107
|
+
);
|
|
70
108
|
|
|
71
109
|
return (
|
|
72
110
|
<ChartLegendContext.Provider value={chartLegendState}>
|
|
@@ -40,29 +40,40 @@ export type QueryTimeSpan = {
|
|
|
40
40
|
label: string;
|
|
41
41
|
// the label display in the timespan selector
|
|
42
42
|
duration: number;
|
|
43
|
+
// time difference between two samples in second
|
|
44
|
+
interval: number;
|
|
43
45
|
// time span in second
|
|
46
|
+
/** @deprecated Use `interval` instead */
|
|
44
47
|
frequency: number;
|
|
45
48
|
};
|
|
49
|
+
|
|
46
50
|
export const queryTimeSpansCodes: QueryTimeSpan[] = [
|
|
47
51
|
{
|
|
48
52
|
query: QUERY_LAST_SEVEN_DAYS,
|
|
49
53
|
label: LAST_SEVEN_DAYS,
|
|
50
54
|
duration: SAMPLE_DURATION_LAST_SEVEN_DAYS,
|
|
55
|
+
interval: SAMPLE_FREQUENCY_LAST_SEVEN_DAYS,
|
|
56
|
+
/** @deprecated Use `interval` instead */
|
|
51
57
|
frequency: SAMPLE_FREQUENCY_LAST_SEVEN_DAYS,
|
|
52
58
|
},
|
|
53
59
|
{
|
|
54
60
|
query: QUERY_LAST_TWENTY_FOUR_HOURS,
|
|
55
61
|
label: LAST_TWENTY_FOUR_HOURS,
|
|
56
62
|
duration: SAMPLE_DURATION_LAST_TWENTY_FOUR_HOURS,
|
|
63
|
+
interval: SAMPLE_FREQUENCY_LAST_TWENTY_FOUR_HOURS,
|
|
64
|
+
/** @deprecated Use `interval` instead */
|
|
57
65
|
frequency: SAMPLE_FREQUENCY_LAST_TWENTY_FOUR_HOURS,
|
|
58
66
|
},
|
|
59
67
|
{
|
|
60
68
|
query: QUERY_LAST_ONE_HOUR,
|
|
61
69
|
label: LAST_ONE_HOUR,
|
|
62
70
|
duration: SAMPLE_DURATION_LAST_ONE_HOUR,
|
|
71
|
+
interval: SAMPLE_FREQUENCY_LAST_ONE_HOUR,
|
|
72
|
+
/** @deprecated Use `interval` instead */
|
|
63
73
|
frequency: SAMPLE_FREQUENCY_LAST_ONE_HOUR,
|
|
64
74
|
},
|
|
65
75
|
];
|
|
76
|
+
|
|
66
77
|
export const NAN_STRING = 'NAN';
|
|
67
78
|
|
|
68
79
|
export const STATUS_CRITICAL = 'critical';
|
|
@@ -6,6 +6,7 @@ import styled from 'styled-components';
|
|
|
6
6
|
import { Tooltip } from '../tooltip/Tooltip.component';
|
|
7
7
|
import { Props as TooltipProps } from '../tooltip/Tooltip.component';
|
|
8
8
|
import { Text } from '../text/Text.component';
|
|
9
|
+
import { CoreUITheme } from '../../style/theme';
|
|
9
10
|
|
|
10
11
|
type Props = {
|
|
11
12
|
text: string | number | JSX.Element | JSX.Element[];
|
|
@@ -13,6 +14,7 @@ type Props = {
|
|
|
13
14
|
tooltipPlacement?: $PropertyType<TooltipProps, 'placement'>;
|
|
14
15
|
lineClamp?: number;
|
|
15
16
|
centered?: boolean;
|
|
17
|
+
color?: keyof CoreUITheme;
|
|
16
18
|
};
|
|
17
19
|
// for lineClamp cf https://css-tricks.com/almanac/properties/l/line-clamp/
|
|
18
20
|
// it should work on all major navigator, despite the --webkit prefix
|
|
@@ -74,6 +76,7 @@ function ConstrainedText({
|
|
|
74
76
|
tooltipStyle,
|
|
75
77
|
tooltipPlacement,
|
|
76
78
|
lineClamp = 1,
|
|
79
|
+
color,
|
|
77
80
|
centered = false,
|
|
78
81
|
}: Props): JSX.Element {
|
|
79
82
|
const [displayToolTip, setDisplayToolTip] = useState(false);
|
|
@@ -91,7 +94,7 @@ function ConstrainedText({
|
|
|
91
94
|
overlayStyle={tooltipStyle}
|
|
92
95
|
placement={tooltipPlacement}
|
|
93
96
|
>
|
|
94
|
-
<Text>
|
|
97
|
+
<Text color={color}>
|
|
95
98
|
{getConstrainedTextContainer(
|
|
96
99
|
constrainedTextRef,
|
|
97
100
|
lineClamp,
|
|
@@ -101,7 +104,7 @@ function ConstrainedText({
|
|
|
101
104
|
</Text>
|
|
102
105
|
</Tooltip>
|
|
103
106
|
) : (
|
|
104
|
-
<Text>
|
|
107
|
+
<Text color={color}>
|
|
105
108
|
{getConstrainedTextContainer(
|
|
106
109
|
constrainedTextRef,
|
|
107
110
|
lineClamp,
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { getDateDaysDiff } from './dateDiffer';
|
|
2
2
|
import { Tooltip } from '../tooltip/Tooltip.component';
|
|
3
3
|
|
|
4
|
+
export const LONG_DATE_FORMATER = Intl.DateTimeFormat('en-GB', {
|
|
5
|
+
weekday: 'long',
|
|
6
|
+
year: 'numeric',
|
|
7
|
+
month: 'long',
|
|
8
|
+
day: 'numeric',
|
|
9
|
+
});
|
|
10
|
+
|
|
4
11
|
export const DATE_FORMATER = Intl.DateTimeFormat('fr-CA', {
|
|
5
12
|
year: 'numeric',
|
|
6
13
|
month: '2-digit',
|
|
@@ -56,7 +63,10 @@ type FormattedDateTimeProps = {
|
|
|
56
63
|
| 'time-second'
|
|
57
64
|
| 'relative'
|
|
58
65
|
| 'day-month-abbreviated-hour-minute'
|
|
59
|
-
| 'day-month-abbreviated-hour-minute-second'
|
|
66
|
+
| 'day-month-abbreviated-hour-minute-second'
|
|
67
|
+
| 'long-date'
|
|
68
|
+
| 'chart-date';
|
|
69
|
+
|
|
60
70
|
value: Date;
|
|
61
71
|
};
|
|
62
72
|
|
|
@@ -184,6 +194,10 @@ export const FormattedDateTime = ({
|
|
|
184
194
|
)}
|
|
185
195
|
</>
|
|
186
196
|
);
|
|
197
|
+
case 'long-date':
|
|
198
|
+
return <>{LONG_DATE_FORMATER.format(value)}</>;
|
|
199
|
+
case 'chart-date':
|
|
200
|
+
return <>{DAY_MONTH_FORMATER.format(value).replace(/[ ,]/g, '')}</>;
|
|
187
201
|
default:
|
|
188
202
|
return <></>;
|
|
189
203
|
}
|
|
@@ -142,7 +142,15 @@ export const iconTable = {
|
|
|
142
142
|
Mail: 'fas faEnvelope',
|
|
143
143
|
};
|
|
144
144
|
|
|
145
|
-
|
|
145
|
+
type IconProps = {
|
|
146
|
+
'aria-label'?: string;
|
|
147
|
+
color?: string;
|
|
148
|
+
size?: string;
|
|
149
|
+
icon?: string;
|
|
150
|
+
title?: string;
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
export const customIcons: Record<string, ((props: IconProps) => JSX.Element) & { displayName?: string }> = {
|
|
146
154
|
'Remote-user': ({ 'aria-label': ariaLabel, color, size }) => (
|
|
147
155
|
<RemoteUser ariaLabel={ariaLabel} color={color} size={size} />
|
|
148
156
|
),
|
|
@@ -151,6 +159,9 @@ export const customIcons = {
|
|
|
151
159
|
),
|
|
152
160
|
};
|
|
153
161
|
|
|
162
|
+
customIcons['Remote-user'].displayName = 'RemoteUser';
|
|
163
|
+
customIcons['Remote-group'].displayName = 'RemoteGroup';
|
|
164
|
+
|
|
154
165
|
const IconStyled = styled(FontAwesomeIcon)`
|
|
155
166
|
${(props) => {
|
|
156
167
|
const theme = props.theme;
|
|
@@ -165,33 +165,33 @@ it('returns the unit label GiB/Sec', () => {
|
|
|
165
165
|
expect(valueBase).toEqual(1024 * 1024 * 1024);
|
|
166
166
|
});
|
|
167
167
|
// test for addMissingDataPoint function
|
|
168
|
-
const originalValue = [
|
|
169
|
-
[0, 0],
|
|
170
|
-
[1, 1],
|
|
171
|
-
[2, 2],
|
|
172
|
-
[3, 3],
|
|
173
|
-
[4, 4],
|
|
174
|
-
[5, 5],
|
|
175
|
-
[6, 6],
|
|
176
|
-
[8, 8],
|
|
177
|
-
[9, 9],
|
|
178
|
-
[10, 10],
|
|
168
|
+
const originalValue: [number, number | string | null][] = [
|
|
169
|
+
[0, '0'],
|
|
170
|
+
[1, '1'],
|
|
171
|
+
[2, '2'],
|
|
172
|
+
[3, '3'],
|
|
173
|
+
[4, '4'],
|
|
174
|
+
[5, '5'],
|
|
175
|
+
[6, '6'],
|
|
176
|
+
[8, '8'],
|
|
177
|
+
[9, '9'],
|
|
178
|
+
[10, '10'],
|
|
179
179
|
];
|
|
180
180
|
const startingTimeStamp = 0;
|
|
181
181
|
const sampleDuration = 10;
|
|
182
182
|
const sampleFrequency = 1;
|
|
183
183
|
const newValues = [
|
|
184
|
-
[0, 0],
|
|
185
|
-
[1, 1],
|
|
186
|
-
[2, 2],
|
|
187
|
-
[3, 3],
|
|
188
|
-
[4, 4],
|
|
189
|
-
[5, 5],
|
|
190
|
-
[6, 6],
|
|
184
|
+
[0, '0'],
|
|
185
|
+
[1, '1'],
|
|
186
|
+
[2, '2'],
|
|
187
|
+
[3, '3'],
|
|
188
|
+
[4, '4'],
|
|
189
|
+
[5, '5'],
|
|
190
|
+
[6, '6'],
|
|
191
191
|
[7, 'NAN'],
|
|
192
|
-
[8, 8],
|
|
193
|
-
[9, 9],
|
|
194
|
-
[10, 10],
|
|
192
|
+
[8, '8'],
|
|
193
|
+
[9, '9'],
|
|
194
|
+
[10, '10'],
|
|
195
195
|
];
|
|
196
196
|
it('should add missing data point with null', () => {
|
|
197
197
|
const result = addMissingDataPoint(
|
|
@@ -210,19 +210,7 @@ it('should return the array with string NAN when the original dataset is empty',
|
|
|
210
210
|
sampleDuration,
|
|
211
211
|
sampleFrequency,
|
|
212
212
|
);
|
|
213
|
-
expect(result).toEqual([
|
|
214
|
-
[0, 'NAN'],
|
|
215
|
-
[1, 'NAN'],
|
|
216
|
-
[2, 'NAN'],
|
|
217
|
-
[3, 'NAN'],
|
|
218
|
-
[4, 'NAN'],
|
|
219
|
-
[5, 'NAN'],
|
|
220
|
-
[6, 'NAN'],
|
|
221
|
-
[7, 'NAN'],
|
|
222
|
-
[8, 'NAN'],
|
|
223
|
-
[9, 'NAN'],
|
|
224
|
-
[10, 'NAN'],
|
|
225
|
-
]);
|
|
213
|
+
expect(result).toEqual([]);
|
|
226
214
|
});
|
|
227
215
|
it('should return an empty array when the starting timestamp is undefined', () => {
|
|
228
216
|
const result = addMissingDataPoint(
|
|
@@ -259,4 +247,4 @@ it('should return an empty array when sample frequency is undefined', () => {
|
|
|
259
247
|
undefined,
|
|
260
248
|
);
|
|
261
249
|
expect(result).toEqual([]);
|
|
262
|
-
});
|
|
250
|
+
});
|