@pie-lib/charting 6.1.1-next.0 → 6.2.0-next.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/lib/actions-button.js +0 -5
- package/lib/actions-button.js.map +1 -1
- package/lib/axes.js +0 -23
- package/lib/axes.js.map +1 -1
- package/lib/bars/bar.js +0 -2
- package/lib/bars/bar.js.map +1 -1
- package/lib/bars/common/bars.js +0 -13
- package/lib/bars/common/bars.js.map +1 -1
- package/lib/bars/common/correct-check-icon.js +0 -1
- package/lib/bars/common/correct-check-icon.js.map +1 -1
- package/lib/bars/histogram.js +0 -2
- package/lib/bars/histogram.js.map +1 -1
- package/lib/chart-setup.js +0 -19
- package/lib/chart-setup.js.map +1 -1
- package/lib/chart-type.js +0 -1
- package/lib/chart-type.js.map +1 -1
- package/lib/chart-types.js +0 -1
- package/lib/chart-types.js.map +1 -1
- package/lib/chart.js +2 -18
- package/lib/chart.js.map +1 -1
- package/lib/common/correctness-indicators.js +0 -4
- package/lib/common/correctness-indicators.js.map +1 -1
- package/lib/common/drag-handle.js +0 -1
- package/lib/common/drag-handle.js.map +1 -1
- package/lib/common/drag-icon.js +0 -2
- package/lib/common/drag-icon.js.map +1 -1
- package/lib/common/styles.js +0 -1
- package/lib/common/styles.js.map +1 -1
- package/lib/grid.js +0 -4
- package/lib/grid.js.map +1 -1
- package/lib/index.js +0 -1
- package/lib/index.js.map +1 -1
- package/lib/key-legend.js +0 -1
- package/lib/key-legend.js.map +1 -1
- package/lib/line/common/drag-handle.js +0 -2
- package/lib/line/common/drag-handle.js.map +1 -1
- package/lib/line/common/line.js +2 -19
- package/lib/line/common/line.js.map +1 -1
- package/lib/line/line-cross.js +0 -16
- package/lib/line/line-cross.js.map +1 -1
- package/lib/line/line-dot.js +0 -2
- package/lib/line/line-dot.js.map +1 -1
- package/lib/mark-label.js +0 -21
- package/lib/mark-label.js.map +1 -1
- package/lib/plot/common/plot.js +0 -12
- package/lib/plot/common/plot.js.map +1 -1
- package/lib/plot/dot.js +0 -3
- package/lib/plot/dot.js.map +1 -1
- package/lib/plot/line.js +0 -3
- package/lib/plot/line.js.map +1 -1
- package/lib/tool-menu.js +0 -11
- package/lib/tool-menu.js.map +1 -1
- package/lib/utils.js +0 -12
- package/lib/utils.js.map +1 -1
- package/package.json +12 -9
- package/src/__tests__/actions-button.test.jsx +280 -0
- package/src/__tests__/axes.test.jsx +557 -16
- package/src/__tests__/chart-setup.test.jsx +495 -10
- package/src/__tests__/chart.test.jsx +1 -1
- package/src/__tests__/grid.test.jsx +2 -2
- package/src/__tests__/key-legend.test.jsx +223 -0
- package/src/__tests__/tool-menu.test.jsx +522 -0
- package/src/__tests__/utils.js +1 -1
- package/src/axes.jsx +10 -7
- package/src/bars/common/bars.jsx +32 -50
- package/src/chart-setup.jsx +6 -9
- package/src/chart-type.js +3 -6
- package/src/chart.jsx +2 -2
- package/src/common/__tests__/correctness-indicators.test.jsx +720 -0
- package/src/common/__tests__/drag-handle.test.jsx +0 -1
- package/src/common/correctness-indicators.jsx +8 -13
- package/src/common/drag-handle.jsx +2 -12
- package/src/grid.jsx +1 -1
- package/src/line/__tests__/line-cross.test.jsx +423 -1
- package/src/line/__tests__/utils.js +1 -1
- package/src/line/common/__tests__/drag-handle.test.jsx +1 -2
- package/src/line/common/drag-handle.jsx +2 -11
- package/src/line/common/line.jsx +2 -2
- package/src/line/line-cross.js +7 -13
- package/src/mark-label.jsx +3 -3
- package/src/plot/__tests__/dot.test.jsx +300 -1
- package/src/plot/__tests__/line.test.jsx +331 -1
- package/src/plot/common/plot.jsx +14 -13
- package/src/utils.js +0 -1
- package/NEXT.CHANGELOG.json +0 -16
|
@@ -1,4 +1,52 @@
|
|
|
1
|
-
import
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { fireEvent, render, waitFor } from '@testing-library/react';
|
|
3
|
+
import { createTheme, ThemeProvider } from '@mui/material/styles';
|
|
4
|
+
import ConfigureChartPanel, { resetValues } from '../chart-setup';
|
|
5
|
+
|
|
6
|
+
jest.mock('../chart-type', () => {
|
|
7
|
+
return function ChartType({ value, onChange }) {
|
|
8
|
+
return (
|
|
9
|
+
<select data-testid="chart-type-select" value={value} onChange={onChange}>
|
|
10
|
+
<option value="bar">Bar</option>
|
|
11
|
+
<option value="line">Line</option>
|
|
12
|
+
<option value="linePlot">Line Plot</option>
|
|
13
|
+
<option value="dotPlot">Dot Plot</option>
|
|
14
|
+
</select>
|
|
15
|
+
);
|
|
16
|
+
};
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
jest.mock('@pie-lib/config-ui', () => ({
|
|
20
|
+
NumberTextFieldCustom: ({ label, value, onChange, ...props }) => (
|
|
21
|
+
<input
|
|
22
|
+
data-testid={`number-field-${label.toLowerCase().replace(/\s+/g, '-')}`}
|
|
23
|
+
type="number"
|
|
24
|
+
value={value}
|
|
25
|
+
onChange={(e) => onChange(e, parseFloat(e.target.value) || 0)}
|
|
26
|
+
aria-label={label}
|
|
27
|
+
{...props}
|
|
28
|
+
/>
|
|
29
|
+
),
|
|
30
|
+
AlertDialog: ({ open, title, text, onClose, onConfirm }) =>
|
|
31
|
+
open ? (
|
|
32
|
+
<div data-testid="alert-dialog">
|
|
33
|
+
<h2>{title}</h2>
|
|
34
|
+
<p>{text}</p>
|
|
35
|
+
<button data-testid="alert-cancel" onClick={onClose}>
|
|
36
|
+
Cancel
|
|
37
|
+
</button>
|
|
38
|
+
<button data-testid="alert-confirm" onClick={onConfirm}>
|
|
39
|
+
Confirm
|
|
40
|
+
</button>
|
|
41
|
+
</div>
|
|
42
|
+
) : null,
|
|
43
|
+
}));
|
|
44
|
+
|
|
45
|
+
let theme;
|
|
46
|
+
|
|
47
|
+
beforeAll(() => {
|
|
48
|
+
theme = createTheme();
|
|
49
|
+
});
|
|
2
50
|
|
|
3
51
|
describe('resetValues', () => {
|
|
4
52
|
let data;
|
|
@@ -8,13 +56,7 @@ describe('resetValues', () => {
|
|
|
8
56
|
|
|
9
57
|
beforeEach(() => {
|
|
10
58
|
range = { min: 0, max: 10, step: 1 };
|
|
11
|
-
data = [
|
|
12
|
-
{ value: 2 },
|
|
13
|
-
{ value: 11 },
|
|
14
|
-
{ value: 5.5 },
|
|
15
|
-
{ value: 2.0000000001 }, // A float close to an integer
|
|
16
|
-
{ value: 2.9999999999 }, // Another float close to an integer
|
|
17
|
-
];
|
|
59
|
+
data = [{ value: 2 }, { value: 11 }, { value: 5.5 }, { value: 2.0000000001 }, { value: 2.9999999999 }];
|
|
18
60
|
model = { someField: 'someValue', data };
|
|
19
61
|
onChange = jest.fn();
|
|
20
62
|
});
|
|
@@ -36,12 +78,455 @@ describe('resetValues', () => {
|
|
|
36
78
|
|
|
37
79
|
it('should not reset floating point values that are close to multiples of range.step', () => {
|
|
38
80
|
resetValues(data, true, range, onChange, model);
|
|
39
|
-
expect(data[3].value).toBe(2.0000000001);
|
|
40
|
-
expect(data[4].value).toBe(2.9999999999);
|
|
81
|
+
expect(data[3].value).toBe(2.0000000001);
|
|
82
|
+
expect(data[4].value).toBe(2.9999999999);
|
|
41
83
|
});
|
|
42
84
|
|
|
43
85
|
it('should not call onChange when updateModel is false', () => {
|
|
44
86
|
resetValues(data, false, range, onChange, model);
|
|
45
87
|
expect(onChange).not.toHaveBeenCalled();
|
|
46
88
|
});
|
|
89
|
+
|
|
90
|
+
it('should handle null or undefined data array', () => {
|
|
91
|
+
expect(() => resetValues(null, true, range, onChange, model)).not.toThrow();
|
|
92
|
+
expect(() => resetValues(undefined, true, range, onChange, model)).not.toThrow();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('should handle empty data array', () => {
|
|
96
|
+
resetValues([], true, range, onChange, model);
|
|
97
|
+
expect(onChange).toHaveBeenCalledWith({ ...model, data: [] });
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should reset multiple invalid values', () => {
|
|
101
|
+
const multiData = [{ value: 11 }, { value: 12 }, { value: 5.5 }, { value: 3.7 }];
|
|
102
|
+
resetValues(multiData, true, range, onChange, { ...model, data: multiData });
|
|
103
|
+
expect(multiData.every((d) => d.value === 0)).toBe(true);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should handle decimal step values', () => {
|
|
107
|
+
const decimalRange = { min: 0, max: 10, step: 0.5 };
|
|
108
|
+
const decimalData = [{ value: 1.5 }, { value: 2.3 }, { value: 3.0 }];
|
|
109
|
+
resetValues(decimalData, true, decimalRange, onChange, model);
|
|
110
|
+
expect(decimalData[0].value).toBe(1.5);
|
|
111
|
+
expect(decimalData[1].value).toBe(0);
|
|
112
|
+
expect(decimalData[2].value).toBe(3.0);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe('ConfigureChartPanel', () => {
|
|
117
|
+
const defaultModel = {
|
|
118
|
+
chartType: 'bar',
|
|
119
|
+
range: { min: 0, max: 10, step: 1, labelStep: 1 },
|
|
120
|
+
graph: { width: 400, height: 500 },
|
|
121
|
+
data: [{ value: 5 }],
|
|
122
|
+
correctAnswer: { data: [{ value: 3 }] },
|
|
123
|
+
changeInteractiveEnabled: true,
|
|
124
|
+
changeEditableEnabled: true,
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const defaultProps = {
|
|
128
|
+
model: defaultModel,
|
|
129
|
+
onChange: jest.fn(),
|
|
130
|
+
chartDimensions: {
|
|
131
|
+
showInConfigPanel: true,
|
|
132
|
+
width: { min: 50, max: 700, step: 20 },
|
|
133
|
+
height: { min: 400, max: 700, step: 20 },
|
|
134
|
+
},
|
|
135
|
+
gridValues: { range: { min: 0, max: 10000 } },
|
|
136
|
+
labelValues: { range: { min: 0, max: 10000 } },
|
|
137
|
+
studentNewCategoryDefaultLabel: { label: 'Category' },
|
|
138
|
+
availableChartTypes: {},
|
|
139
|
+
chartTypeLabel: 'Chart Type',
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
beforeEach(() => {
|
|
143
|
+
jest.clearAllMocks();
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
const renderComponent = (extras = {}) => {
|
|
147
|
+
const props = { ...defaultProps, ...extras };
|
|
148
|
+
return render(
|
|
149
|
+
<ThemeProvider theme={theme}>
|
|
150
|
+
<ConfigureChartPanel {...props} />
|
|
151
|
+
</ThemeProvider>,
|
|
152
|
+
);
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
describe('rendering', () => {
|
|
156
|
+
it('should render without crashing', () => {
|
|
157
|
+
const { container } = renderComponent();
|
|
158
|
+
expect(container).toBeInTheDocument();
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('should render "Configure Chart" title', () => {
|
|
162
|
+
const { getByText } = renderComponent();
|
|
163
|
+
expect(getByText('Configure Chart')).toBeInTheDocument();
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('should render chart type selector', () => {
|
|
167
|
+
const { getByTestId } = renderComponent();
|
|
168
|
+
expect(getByTestId('chart-type-select')).toBeInTheDocument();
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('should render max value field', () => {
|
|
172
|
+
const { getByLabelText } = renderComponent();
|
|
173
|
+
expect(getByLabelText('Max Value')).toBeInTheDocument();
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('should render grid interval field for non-plot charts', () => {
|
|
177
|
+
const { getByLabelText } = renderComponent({
|
|
178
|
+
model: { ...defaultModel, chartType: 'bar' },
|
|
179
|
+
});
|
|
180
|
+
expect(getByLabelText('Grid Interval')).toBeInTheDocument();
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('should render label interval field for non-plot charts', () => {
|
|
184
|
+
const { getByLabelText } = renderComponent({
|
|
185
|
+
model: { ...defaultModel, chartType: 'bar' },
|
|
186
|
+
});
|
|
187
|
+
expect(getByLabelText('Label Interval')).toBeInTheDocument();
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('should not render step config for plot charts', () => {
|
|
191
|
+
const { queryByLabelText } = renderComponent({
|
|
192
|
+
model: { ...defaultModel, chartType: 'linePlot' },
|
|
193
|
+
});
|
|
194
|
+
expect(queryByLabelText('Grid Interval')).not.toBeInTheDocument();
|
|
195
|
+
expect(queryByLabelText('Label Interval')).not.toBeInTheDocument();
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it('should render dimensions section when showInConfigPanel is true', () => {
|
|
199
|
+
const { getByText } = renderComponent({
|
|
200
|
+
chartDimensions: {
|
|
201
|
+
showInConfigPanel: true,
|
|
202
|
+
width: { min: 50, max: 700, step: 20 },
|
|
203
|
+
height: { min: 400, max: 700, step: 20 },
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
expect(getByText('Dimensions(px)')).toBeInTheDocument();
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('should not render dimensions section when showInConfigPanel is false', () => {
|
|
210
|
+
const { queryByText } = renderComponent({
|
|
211
|
+
chartDimensions: {
|
|
212
|
+
showInConfigPanel: false,
|
|
213
|
+
width: { min: 50, max: 700, step: 20 },
|
|
214
|
+
height: { min: 400, max: 700, step: 20 },
|
|
215
|
+
},
|
|
216
|
+
});
|
|
217
|
+
expect(queryByText('Dimensions(px)')).not.toBeInTheDocument();
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('should render width field when dimensions are shown', () => {
|
|
221
|
+
const { getByLabelText } = renderComponent();
|
|
222
|
+
expect(getByLabelText('Width')).toBeInTheDocument();
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('should render height field when dimensions are shown', () => {
|
|
226
|
+
const { getByLabelText } = renderComponent();
|
|
227
|
+
expect(getByLabelText('Height')).toBeInTheDocument();
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it('should display width constraints', () => {
|
|
231
|
+
const { getByText } = renderComponent();
|
|
232
|
+
expect(getByText('Min 50, Max 700')).toBeInTheDocument();
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it('should display height constraints', () => {
|
|
236
|
+
const { getAllByText } = renderComponent();
|
|
237
|
+
const constraints = getAllByText('Min 400, Max 700');
|
|
238
|
+
expect(constraints.length).toBeGreaterThan(0);
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
describe('chart type changes', () => {
|
|
243
|
+
it('should call onChange when chart type is changed to non-plot', () => {
|
|
244
|
+
const onChange = jest.fn();
|
|
245
|
+
const { getByTestId } = renderComponent({ onChange });
|
|
246
|
+
|
|
247
|
+
fireEvent.change(getByTestId('chart-type-select'), { target: { value: 'line' } });
|
|
248
|
+
|
|
249
|
+
expect(onChange).toHaveBeenCalled();
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('should show warning when switching to plot with invalid config', async () => {
|
|
253
|
+
const { getByTestId, getByText } = renderComponent({
|
|
254
|
+
model: { ...defaultModel, range: { max: 20, step: 2, labelStep: 2 } },
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
fireEvent.change(getByTestId('chart-type-select'), { target: { value: 'linePlot' } });
|
|
258
|
+
|
|
259
|
+
await waitFor(() => {
|
|
260
|
+
expect(getByText('Warning')).toBeInTheDocument();
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it('should allow switching to plot with valid config', () => {
|
|
265
|
+
const onChange = jest.fn();
|
|
266
|
+
const { getByTestId } = renderComponent({
|
|
267
|
+
onChange,
|
|
268
|
+
model: { ...defaultModel, range: { max: 10, step: 1, labelStep: 1 } },
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
fireEvent.change(getByTestId('chart-type-select'), { target: { value: 'dotPlot' } });
|
|
272
|
+
|
|
273
|
+
expect(onChange).toHaveBeenCalledWith(
|
|
274
|
+
expect.objectContaining({
|
|
275
|
+
chartType: 'dotPlot',
|
|
276
|
+
}),
|
|
277
|
+
);
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
describe('range changes', () => {
|
|
282
|
+
it('should update max value', () => {
|
|
283
|
+
const onChange = jest.fn();
|
|
284
|
+
const { getByLabelText } = renderComponent({ onChange });
|
|
285
|
+
|
|
286
|
+
const maxInput = getByLabelText('Max Value');
|
|
287
|
+
fireEvent.change(maxInput, { target: { value: '15' } });
|
|
288
|
+
|
|
289
|
+
expect(onChange).toHaveBeenCalled();
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('should update step value', () => {
|
|
293
|
+
const onChange = jest.fn();
|
|
294
|
+
const { getByLabelText } = renderComponent({ onChange });
|
|
295
|
+
|
|
296
|
+
const stepInput = getByLabelText('Grid Interval');
|
|
297
|
+
fireEvent.change(stepInput, { target: { value: '2' } });
|
|
298
|
+
|
|
299
|
+
expect(onChange).toHaveBeenCalled();
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
it('should update label step value', () => {
|
|
303
|
+
const onChange = jest.fn();
|
|
304
|
+
const { getByLabelText } = renderComponent({ onChange });
|
|
305
|
+
|
|
306
|
+
const labelStepInput = getByLabelText('Label Interval');
|
|
307
|
+
fireEvent.change(labelStepInput, { target: { value: '2' } });
|
|
308
|
+
|
|
309
|
+
expect(onChange).toHaveBeenCalled();
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
it('should show alert when changing max causes data to be out of range', async () => {
|
|
313
|
+
const onChange = jest.fn();
|
|
314
|
+
const { getByLabelText, queryByTestId } = renderComponent({
|
|
315
|
+
onChange,
|
|
316
|
+
model: {
|
|
317
|
+
...defaultModel,
|
|
318
|
+
range: { min: 0, max: 10, step: 1, labelStep: 1 },
|
|
319
|
+
data: [{ value: 8 }],
|
|
320
|
+
correctAnswer: { data: [] },
|
|
321
|
+
},
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
const maxInput = getByLabelText('Max Value');
|
|
325
|
+
fireEvent.change(maxInput, { target: { value: '5' } });
|
|
326
|
+
|
|
327
|
+
await waitFor(() => {
|
|
328
|
+
expect(onChange).toHaveBeenCalled();
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
it('should show alert when changing step causes data to be out of range', async () => {
|
|
333
|
+
const onChange = jest.fn();
|
|
334
|
+
const { getByLabelText } = renderComponent({
|
|
335
|
+
onChange,
|
|
336
|
+
model: {
|
|
337
|
+
...defaultModel,
|
|
338
|
+
range: { min: 0, max: 10, step: 1, labelStep: 1 },
|
|
339
|
+
data: [{ value: 5 }],
|
|
340
|
+
correctAnswer: { data: [] },
|
|
341
|
+
},
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
const stepInput = getByLabelText('Grid Interval');
|
|
345
|
+
fireEvent.change(stepInput, { target: { value: '3' } });
|
|
346
|
+
|
|
347
|
+
await waitFor(() => {
|
|
348
|
+
expect(onChange).toHaveBeenCalled();
|
|
349
|
+
});
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
describe('dimensions changes', () => {
|
|
354
|
+
it('should update width', () => {
|
|
355
|
+
const onChange = jest.fn();
|
|
356
|
+
const { getByLabelText } = renderComponent({ onChange });
|
|
357
|
+
|
|
358
|
+
const widthInput = getByLabelText('Width');
|
|
359
|
+
fireEvent.change(widthInput, { target: { value: '500' } });
|
|
360
|
+
|
|
361
|
+
expect(onChange).toHaveBeenCalledWith(
|
|
362
|
+
expect.objectContaining({
|
|
363
|
+
graph: expect.objectContaining({ width: 500 }),
|
|
364
|
+
}),
|
|
365
|
+
);
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
it('should update height', () => {
|
|
369
|
+
const onChange = jest.fn();
|
|
370
|
+
const { getByLabelText } = renderComponent({ onChange });
|
|
371
|
+
|
|
372
|
+
const heightInput = getByLabelText('Height');
|
|
373
|
+
fireEvent.change(heightInput, { target: { value: '600' } });
|
|
374
|
+
|
|
375
|
+
expect(onChange).toHaveBeenCalledWith(
|
|
376
|
+
expect.objectContaining({
|
|
377
|
+
graph: expect.objectContaining({ height: 600 }),
|
|
378
|
+
}),
|
|
379
|
+
);
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
describe('alert dialog', () => {
|
|
384
|
+
it('should reset range on cancel', async () => {
|
|
385
|
+
const onChange = jest.fn();
|
|
386
|
+
const { getByLabelText, queryByTestId } = renderComponent({
|
|
387
|
+
onChange,
|
|
388
|
+
model: {
|
|
389
|
+
...defaultModel,
|
|
390
|
+
range: { min: 0, max: 10, step: 1, labelStep: 1 },
|
|
391
|
+
data: [{ value: 8 }],
|
|
392
|
+
correctAnswer: { data: [] },
|
|
393
|
+
},
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
const maxInput = getByLabelText('Max Value');
|
|
397
|
+
|
|
398
|
+
const event = { target: maxInput };
|
|
399
|
+
fireEvent.change(maxInput, { target: { value: '5' } });
|
|
400
|
+
|
|
401
|
+
await waitFor(
|
|
402
|
+
() => {
|
|
403
|
+
const dialog = queryByTestId('alert-dialog');
|
|
404
|
+
if (dialog) {
|
|
405
|
+
fireEvent.click(queryByTestId('alert-cancel'));
|
|
406
|
+
}
|
|
407
|
+
},
|
|
408
|
+
{ timeout: 100 },
|
|
409
|
+
).catch(() => {});
|
|
410
|
+
|
|
411
|
+
expect(onChange).toHaveBeenCalled();
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
it('should apply changes and reset data on confirm', async () => {
|
|
415
|
+
const onChange = jest.fn();
|
|
416
|
+
const { getByLabelText, queryByTestId } = renderComponent({
|
|
417
|
+
onChange,
|
|
418
|
+
model: {
|
|
419
|
+
...defaultModel,
|
|
420
|
+
range: { min: 0, max: 10, step: 1, labelStep: 1 },
|
|
421
|
+
data: [{ value: 8 }],
|
|
422
|
+
correctAnswer: { data: [] },
|
|
423
|
+
},
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
const maxInput = getByLabelText('Max Value');
|
|
427
|
+
fireEvent.change(maxInput, { target: { value: '5' } });
|
|
428
|
+
|
|
429
|
+
await waitFor(
|
|
430
|
+
() => {
|
|
431
|
+
const dialog = queryByTestId('alert-dialog');
|
|
432
|
+
if (dialog) {
|
|
433
|
+
fireEvent.click(queryByTestId('alert-confirm'));
|
|
434
|
+
}
|
|
435
|
+
},
|
|
436
|
+
{ timeout: 100 },
|
|
437
|
+
).catch(() => {});
|
|
438
|
+
|
|
439
|
+
expect(onChange).toHaveBeenCalled();
|
|
440
|
+
});
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
describe('edge cases', () => {
|
|
444
|
+
it('should handle missing chartDimensions', () => {
|
|
445
|
+
const { container } = renderComponent({ chartDimensions: undefined });
|
|
446
|
+
expect(container).toBeInTheDocument();
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
it('should handle missing gridValues', () => {
|
|
450
|
+
const { container } = renderComponent({ gridValues: undefined });
|
|
451
|
+
expect(container).toBeInTheDocument();
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
it('should handle missing labelValues', () => {
|
|
455
|
+
const { container } = renderComponent({ labelValues: undefined });
|
|
456
|
+
expect(container).toBeInTheDocument();
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
it('should handle empty model data', () => {
|
|
460
|
+
const { container } = renderComponent({
|
|
461
|
+
model: {
|
|
462
|
+
...defaultModel,
|
|
463
|
+
data: [],
|
|
464
|
+
correctAnswer: { data: [] },
|
|
465
|
+
},
|
|
466
|
+
});
|
|
467
|
+
expect(container).toBeInTheDocument();
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
it('should handle empty range object', () => {
|
|
471
|
+
const { container } = renderComponent({
|
|
472
|
+
model: {
|
|
473
|
+
...defaultModel,
|
|
474
|
+
range: {},
|
|
475
|
+
},
|
|
476
|
+
});
|
|
477
|
+
expect(container).toBeInTheDocument();
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
it('should apply width constraints correctly', () => {
|
|
481
|
+
const { container } = renderComponent({
|
|
482
|
+
chartDimensions: {
|
|
483
|
+
showInConfigPanel: true,
|
|
484
|
+
width: { min: 100, max: 600, step: 50 },
|
|
485
|
+
height: { min: 400, max: 700, step: 20 },
|
|
486
|
+
},
|
|
487
|
+
});
|
|
488
|
+
expect(container).toBeInTheDocument();
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
it('should apply height constraints correctly', () => {
|
|
492
|
+
const { container } = renderComponent({
|
|
493
|
+
chartDimensions: {
|
|
494
|
+
showInConfigPanel: true,
|
|
495
|
+
width: { min: 50, max: 700, step: 20 },
|
|
496
|
+
height: { min: 450, max: 650, step: 30 },
|
|
497
|
+
},
|
|
498
|
+
});
|
|
499
|
+
expect(container).toBeInTheDocument();
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
it('should handle very small step values', () => {
|
|
503
|
+
const { container } = renderComponent({
|
|
504
|
+
chartDimensions: {
|
|
505
|
+
showInConfigPanel: true,
|
|
506
|
+
width: { min: 50, max: 700, step: 0.5 },
|
|
507
|
+
height: { min: 400, max: 700, step: 0.5 },
|
|
508
|
+
},
|
|
509
|
+
});
|
|
510
|
+
expect(container).toBeInTheDocument();
|
|
511
|
+
});
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
describe('useEffect lifecycle', () => {
|
|
515
|
+
it('should call onChange on mount', () => {
|
|
516
|
+
const onChange = jest.fn();
|
|
517
|
+
renderComponent({ onChange });
|
|
518
|
+
|
|
519
|
+
expect(onChange).toHaveBeenCalled();
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
it('should set category default label on mount', () => {
|
|
523
|
+
const onChange = jest.fn();
|
|
524
|
+
renderComponent({
|
|
525
|
+
onChange,
|
|
526
|
+
studentNewCategoryDefaultLabel: { label: 'Test Category' },
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
expect(onChange).toHaveBeenCalled();
|
|
530
|
+
});
|
|
531
|
+
});
|
|
47
532
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { render } from '@pie-lib/test-utils';
|
|
3
3
|
import { Chart } from '../chart';
|
|
4
|
-
import {
|
|
4
|
+
import { createBandScale, graphProps } from './utils';
|
|
5
5
|
|
|
6
6
|
describe('ChartAxes', () => {
|
|
7
7
|
let onDataChange = jest.fn();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { render } from '@pie-lib/test-utils';
|
|
3
3
|
import { Grid } from '../grid';
|
|
4
|
-
import {
|
|
4
|
+
import { createBandScale, graphProps } from './utils';
|
|
5
5
|
|
|
6
6
|
describe('Grid', () => {
|
|
7
7
|
const renderComponent = (extras) => {
|
|
@@ -15,7 +15,7 @@ describe('Grid', () => {
|
|
|
15
15
|
return render(
|
|
16
16
|
<svg>
|
|
17
17
|
<Grid {...props} />
|
|
18
|
-
</svg
|
|
18
|
+
</svg>,
|
|
19
19
|
);
|
|
20
20
|
};
|
|
21
21
|
|