@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.
Files changed (85) hide show
  1. package/lib/actions-button.js +0 -5
  2. package/lib/actions-button.js.map +1 -1
  3. package/lib/axes.js +0 -23
  4. package/lib/axes.js.map +1 -1
  5. package/lib/bars/bar.js +0 -2
  6. package/lib/bars/bar.js.map +1 -1
  7. package/lib/bars/common/bars.js +0 -13
  8. package/lib/bars/common/bars.js.map +1 -1
  9. package/lib/bars/common/correct-check-icon.js +0 -1
  10. package/lib/bars/common/correct-check-icon.js.map +1 -1
  11. package/lib/bars/histogram.js +0 -2
  12. package/lib/bars/histogram.js.map +1 -1
  13. package/lib/chart-setup.js +0 -19
  14. package/lib/chart-setup.js.map +1 -1
  15. package/lib/chart-type.js +0 -1
  16. package/lib/chart-type.js.map +1 -1
  17. package/lib/chart-types.js +0 -1
  18. package/lib/chart-types.js.map +1 -1
  19. package/lib/chart.js +2 -18
  20. package/lib/chart.js.map +1 -1
  21. package/lib/common/correctness-indicators.js +0 -4
  22. package/lib/common/correctness-indicators.js.map +1 -1
  23. package/lib/common/drag-handle.js +0 -1
  24. package/lib/common/drag-handle.js.map +1 -1
  25. package/lib/common/drag-icon.js +0 -2
  26. package/lib/common/drag-icon.js.map +1 -1
  27. package/lib/common/styles.js +0 -1
  28. package/lib/common/styles.js.map +1 -1
  29. package/lib/grid.js +0 -4
  30. package/lib/grid.js.map +1 -1
  31. package/lib/index.js +0 -1
  32. package/lib/index.js.map +1 -1
  33. package/lib/key-legend.js +0 -1
  34. package/lib/key-legend.js.map +1 -1
  35. package/lib/line/common/drag-handle.js +0 -2
  36. package/lib/line/common/drag-handle.js.map +1 -1
  37. package/lib/line/common/line.js +2 -19
  38. package/lib/line/common/line.js.map +1 -1
  39. package/lib/line/line-cross.js +0 -16
  40. package/lib/line/line-cross.js.map +1 -1
  41. package/lib/line/line-dot.js +0 -2
  42. package/lib/line/line-dot.js.map +1 -1
  43. package/lib/mark-label.js +0 -21
  44. package/lib/mark-label.js.map +1 -1
  45. package/lib/plot/common/plot.js +0 -12
  46. package/lib/plot/common/plot.js.map +1 -1
  47. package/lib/plot/dot.js +0 -3
  48. package/lib/plot/dot.js.map +1 -1
  49. package/lib/plot/line.js +0 -3
  50. package/lib/plot/line.js.map +1 -1
  51. package/lib/tool-menu.js +0 -11
  52. package/lib/tool-menu.js.map +1 -1
  53. package/lib/utils.js +0 -12
  54. package/lib/utils.js.map +1 -1
  55. package/package.json +12 -9
  56. package/src/__tests__/actions-button.test.jsx +280 -0
  57. package/src/__tests__/axes.test.jsx +557 -16
  58. package/src/__tests__/chart-setup.test.jsx +495 -10
  59. package/src/__tests__/chart.test.jsx +1 -1
  60. package/src/__tests__/grid.test.jsx +2 -2
  61. package/src/__tests__/key-legend.test.jsx +223 -0
  62. package/src/__tests__/tool-menu.test.jsx +522 -0
  63. package/src/__tests__/utils.js +1 -1
  64. package/src/axes.jsx +10 -7
  65. package/src/bars/common/bars.jsx +32 -50
  66. package/src/chart-setup.jsx +6 -9
  67. package/src/chart-type.js +3 -6
  68. package/src/chart.jsx +2 -2
  69. package/src/common/__tests__/correctness-indicators.test.jsx +720 -0
  70. package/src/common/__tests__/drag-handle.test.jsx +0 -1
  71. package/src/common/correctness-indicators.jsx +8 -13
  72. package/src/common/drag-handle.jsx +2 -12
  73. package/src/grid.jsx +1 -1
  74. package/src/line/__tests__/line-cross.test.jsx +423 -1
  75. package/src/line/__tests__/utils.js +1 -1
  76. package/src/line/common/__tests__/drag-handle.test.jsx +1 -2
  77. package/src/line/common/drag-handle.jsx +2 -11
  78. package/src/line/common/line.jsx +2 -2
  79. package/src/line/line-cross.js +7 -13
  80. package/src/mark-label.jsx +3 -3
  81. package/src/plot/__tests__/dot.test.jsx +300 -1
  82. package/src/plot/__tests__/line.test.jsx +331 -1
  83. package/src/plot/common/plot.jsx +14 -13
  84. package/src/utils.js +0 -1
  85. package/NEXT.CHANGELOG.json +0 -16
@@ -3,7 +3,6 @@ import React from 'react';
3
3
  import DragHandle from '../drag-handle';
4
4
  import { gridDraggable } from '@pie-lib/plot';
5
5
  import { graphProps } from './utils';
6
- import { bounds } from '../../utils';
7
6
 
8
7
  jest.mock('../../utils', () => {
9
8
  const { point } = jest.requireActual('../../utils');
@@ -52,11 +52,7 @@ export const CorrectnessIndicator = ({ scale, x, y, correctness, interactive })
52
52
  // the icon is 16px + 2px padding + 1px border, so total size is 22px
53
53
  return (
54
54
  <foreignObject x={cx - 11} y={cy - 11} width={22} height={22}>
55
- {isCorrect ? (
56
- <StyledCorrectIcon title={correctness.label} />
57
- ) : (
58
- <StyledIncorrectIcon title={correctness.label} />
59
- )}
55
+ {isCorrect ? <StyledCorrectIcon title={correctness.label} /> : <StyledIncorrectIcon title={correctness.label} />}
60
56
  </foreignObject>
61
57
  );
62
58
  };
@@ -72,10 +68,7 @@ export const SmallCorrectPointIndicator = ({ scale, x, correctness, correctData,
72
68
  // small circle has 10px font + 2px padding + 1px border, so total size is 15px
73
69
  return (
74
70
  <foreignObject x={xToRender} y={yToRender} width={15} height={15}>
75
- <StyledCorrectIcon
76
- className="small"
77
- title={correctness.label}
78
- />
71
+ <StyledCorrectIcon className="small" title={correctness.label} />
79
72
  </foreignObject>
80
73
  );
81
74
  }
@@ -117,10 +110,12 @@ SmallCorrectPointIndicator.propTypes = {
117
110
  value: PropTypes.string,
118
111
  label: PropTypes.string,
119
112
  }),
120
- correctData: PropTypes.arrayOf(PropTypes.shape({
121
- label: PropTypes.string,
122
- value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
123
- })),
113
+ correctData: PropTypes.arrayOf(
114
+ PropTypes.shape({
115
+ label: PropTypes.string,
116
+ value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
117
+ }),
118
+ ),
124
119
  label: PropTypes.string,
125
120
  };
126
121
 
@@ -4,7 +4,7 @@ import { styled } from '@mui/material/styles';
4
4
  import Check from '@mui/icons-material/Check';
5
5
  import Close from '@mui/icons-material/Close';
6
6
 
7
- import { gridDraggable, utils, types } from '@pie-lib/plot';
7
+ import { gridDraggable, types, utils } from '@pie-lib/plot';
8
8
  import { color as enumColor } from '@pie-lib/render-ui';
9
9
  import { getScale } from '../utils';
10
10
  import DragIcon from './drag-icon';
@@ -42,17 +42,7 @@ const StyledIncorrectIcon = styled(Close)(() => ({
42
42
  boxSizing: 'unset', // to override the default border-box in IBX
43
43
  }));
44
44
 
45
- const RawDragHandle = ({
46
- x,
47
- y,
48
- width,
49
- graphProps,
50
- interactive,
51
- isHovered,
52
- correctness,
53
- isPlot,
54
- ...rest
55
- }) => {
45
+ const RawDragHandle = ({ x, y, width, graphProps, interactive, isHovered, correctness, isPlot, ...rest }) => {
56
46
  const { scale } = graphProps;
57
47
  const scaleValue = getScale(width)?.scale;
58
48
 
package/src/grid.jsx CHANGED
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { styled } from '@mui/material/styles';
4
- import { GridRows, GridColumns } from '@visx/grid';
4
+ import { GridColumns, GridRows } from '@visx/grid';
5
5
 
6
6
  import { types } from '@pie-lib/plot';
7
7
  import { color } from '@pie-lib/render-ui';
@@ -1,8 +1,97 @@
1
1
  import React from 'react';
2
2
  import { render } from '@pie-lib/test-utils';
3
+ import { createTheme, ThemeProvider } from '@mui/material/styles';
3
4
  import Line, { LineCross as LineChart } from '../line-cross';
4
5
  import { graphProps } from './utils';
5
6
 
7
+ // Mock dependencies
8
+ jest.mock('@visx/shape', () => ({
9
+ LinePath: ({ data, x, y, strokeWidth, style, ...props }) => (
10
+ <path
11
+ data-testid="line-path"
12
+ d={data.map((d, i) => `${i === 0 ? 'M' : 'L'}${x(d)},${y(d)}`).join(' ')}
13
+ strokeWidth={strokeWidth}
14
+ {...props}
15
+ />
16
+ ),
17
+ }));
18
+
19
+ jest.mock('@visx/group', () => ({
20
+ Group: ({ children, ...props }) => (
21
+ <g data-testid="visx-group" {...props}>
22
+ {children}
23
+ </g>
24
+ ),
25
+ }));
26
+
27
+ jest.mock('@pie-lib/render-ui', () => ({
28
+ color: {
29
+ text: () => '#000000',
30
+ defaults: {
31
+ TEXT: '#000000',
32
+ BLACK: '#000000',
33
+ BORDER_GRAY: '#cccccc',
34
+ },
35
+ disabled: () => '#cccccc',
36
+ },
37
+ }));
38
+
39
+ jest.mock('../../utils', () => ({
40
+ dataToXBand: jest.fn((scale, data, width, type) => {
41
+ return {
42
+ bandwidth: () => 20,
43
+ };
44
+ }),
45
+ }));
46
+
47
+ jest.mock('../common/line', () => {
48
+ return function RawLine({ CustomDraggableComponent, data, graphProps, ...props }) {
49
+ return (
50
+ <svg data-testid="raw-line">
51
+ {data && data.length > 0 ? (
52
+ data.map((d, i) => (
53
+ <CustomDraggableComponent
54
+ key={i}
55
+ scale={graphProps.scale}
56
+ x={d.x || i}
57
+ y={d.y || i}
58
+ r={8}
59
+ correctness={d.correctness}
60
+ interactive={d.interactive}
61
+ correctData={props.correctData}
62
+ label={d.label}
63
+ {...props}
64
+ />
65
+ ))
66
+ ) : (
67
+ <text>No data</text>
68
+ )}
69
+ </svg>
70
+ );
71
+ };
72
+ });
73
+
74
+ jest.mock('../../common/correctness-indicators', () => ({
75
+ CorrectnessIndicator: ({ scale, x, y, correctness, interactive }) =>
76
+ correctness && interactive ? (
77
+ <g data-testid="correctness-indicator">
78
+ <circle cx={scale.x(x)} cy={scale.y(y)} r={5} />
79
+ </g>
80
+ ) : null,
81
+ SmallCorrectPointIndicator: ({ scale, x, correctness, correctData, label }) =>
82
+ correctness && correctness.value === 'incorrect' && correctData ? (
83
+ <g data-testid="small-correct-indicator">
84
+ <circle cx={scale.x(x)} cy={scale.y(0)} r={3} />
85
+ </g>
86
+ ) : null,
87
+ }));
88
+
89
+ let theme;
90
+
91
+ beforeAll(() => {
92
+ theme = createTheme();
93
+ });
94
+
6
95
  describe('LineChart', () => {
7
96
  const renderComponent = (extras) => {
8
97
  const defaults = {
@@ -17,7 +106,11 @@ describe('LineChart', () => {
17
106
  },
18
107
  };
19
108
  const props = { ...defaults, ...extras };
20
- return render(<LineChart {...props} />);
109
+ return render(
110
+ <ThemeProvider theme={theme}>
111
+ <LineChart {...props} />
112
+ </ThemeProvider>,
113
+ );
21
114
  };
22
115
 
23
116
  describe('rendering', () => {
@@ -25,6 +118,45 @@ describe('LineChart', () => {
25
118
  const { container } = renderComponent();
26
119
  expect(container.firstChild).toBeInTheDocument();
27
120
  });
121
+
122
+ it('renders with data points', () => {
123
+ const data = [
124
+ { x: 1, y: 2, label: 'A' },
125
+ { x: 2, y: 3, label: 'B' },
126
+ ];
127
+ const { getByTestId } = renderComponent({ data });
128
+ expect(getByTestId('raw-line')).toBeInTheDocument();
129
+ });
130
+
131
+ it('renders without data', () => {
132
+ const { getByTestId } = renderComponent({ data: [] });
133
+ expect(getByTestId('raw-line')).toBeInTheDocument();
134
+ });
135
+
136
+ it('passes graphProps to RawLine', () => {
137
+ const customGraphProps = graphProps(0, 10, 0, 10);
138
+ const { getByTestId } = renderComponent({ graphProps: customGraphProps });
139
+ expect(getByTestId('raw-line')).toBeInTheDocument();
140
+ });
141
+
142
+ it('passes onChange to RawLine', () => {
143
+ const onChange = jest.fn();
144
+ const { getByTestId } = renderComponent({ onChange });
145
+ expect(getByTestId('raw-line')).toBeInTheDocument();
146
+ });
147
+ });
148
+
149
+ describe('xBand calculation', () => {
150
+ it('calculates xBand from data', () => {
151
+ const data = [{ x: 1, y: 2 }];
152
+ const { getByTestId } = renderComponent({ data });
153
+ expect(getByTestId('raw-line')).toBeInTheDocument();
154
+ });
155
+
156
+ it('handles empty data for xBand', () => {
157
+ const { getByTestId } = renderComponent({ data: [] });
158
+ expect(getByTestId('raw-line')).toBeInTheDocument();
159
+ });
28
160
  });
29
161
 
30
162
  describe('component', () => {
@@ -37,5 +169,295 @@ describe('LineChart', () => {
37
169
  name: 'Line Cross',
38
170
  });
39
171
  });
172
+
173
+ it('returns object with correct type', () => {
174
+ const chart = Line();
175
+ expect(chart.type).toBe('lineCross');
176
+ });
177
+
178
+ it('returns object with correct name', () => {
179
+ const chart = Line();
180
+ expect(chart.name).toBe('Line Cross');
181
+ });
182
+
183
+ it('returns object with LineCross component', () => {
184
+ const chart = Line();
185
+ expect(chart.Component).toBe(LineChart);
186
+ });
187
+ });
188
+
189
+ describe('edge cases', () => {
190
+ it('handles null data', () => {
191
+ const { getByTestId } = renderComponent({ data: null });
192
+ expect(getByTestId('raw-line')).toBeInTheDocument();
193
+ });
194
+
195
+ it('handles undefined graphProps', () => {
196
+ const { container } = renderComponent({ graphProps: undefined });
197
+ expect(container).toBeInTheDocument();
198
+ });
199
+
200
+ it('handles missing scale in graphProps', () => {
201
+ const { container } = renderComponent({
202
+ graphProps: { size: { width: 400, height: 400 } },
203
+ });
204
+ expect(container).toBeInTheDocument();
205
+ });
206
+
207
+ it('handles missing size in graphProps', () => {
208
+ const { container } = renderComponent({
209
+ graphProps: { scale: graphProps().scale },
210
+ });
211
+ expect(container).toBeInTheDocument();
212
+ });
213
+
214
+ it('renders with className', () => {
215
+ const { container } = renderComponent({ className: 'custom-class' });
216
+ expect(container).toBeInTheDocument();
217
+ });
218
+ });
219
+
220
+ describe('data with correctness', () => {
221
+ it('renders data with correct answers', () => {
222
+ const data = [
223
+ { x: 1, y: 2, label: 'A', correctness: { value: 'correct', label: 'Correct!' }, interactive: true },
224
+ ];
225
+ const { getByTestId } = renderComponent({ data });
226
+ expect(getByTestId('raw-line')).toBeInTheDocument();
227
+ });
228
+
229
+ it('renders data with incorrect answers', () => {
230
+ const data = [{ x: 1, y: 2, label: 'A', correctness: { value: 'incorrect', label: 'Wrong' }, interactive: true }];
231
+ const correctData = [{ label: 'A', value: 3 }];
232
+ const { getByTestId } = renderComponent({ data, correctData });
233
+ expect(getByTestId('raw-line')).toBeInTheDocument();
234
+ });
235
+
236
+ it('renders non-interactive data', () => {
237
+ const data = [{ x: 1, y: 2, label: 'A', interactive: false }];
238
+ const { getByTestId } = renderComponent({ data });
239
+ expect(getByTestId('raw-line')).toBeInTheDocument();
240
+ });
241
+ });
242
+ });
243
+
244
+ describe('DraggableComponent', () => {
245
+ // We need to test the DraggableComponent through LineCross since it's not exported
246
+ const renderLineCrossWithData = (dataExtras = {}) => {
247
+ const data = [{ x: 5, y: 10, label: 'A', r: 8, interactive: true, ...dataExtras }];
248
+ const props = {
249
+ graphProps: graphProps(),
250
+ data,
251
+ };
252
+ return render(
253
+ <ThemeProvider theme={theme}>
254
+ <LineChart {...props} />
255
+ </ThemeProvider>,
256
+ );
257
+ };
258
+
259
+ describe('rendering', () => {
260
+ it('renders cross lines', () => {
261
+ const { getAllByTestId } = renderLineCrossWithData();
262
+ const linePaths = getAllByTestId('line-path');
263
+ expect(linePaths.length).toBeGreaterThan(0);
264
+ });
265
+
266
+ it('renders visx group', () => {
267
+ const { getByTestId } = renderLineCrossWithData();
268
+ expect(getByTestId('visx-group')).toBeInTheDocument();
269
+ });
270
+
271
+ it('renders transparent handle circle', () => {
272
+ const { container } = renderLineCrossWithData();
273
+ const circles = container.querySelectorAll('circle');
274
+ expect(circles.length).toBeGreaterThan(0);
275
+ });
276
+ });
277
+
278
+ describe('hover interactions', () => {
279
+ it('renders interactive elements for hover', () => {
280
+ const { container } = renderLineCrossWithData();
281
+ // The component should render, even if we can't test hover through the mock
282
+ expect(container).toBeInTheDocument();
283
+ });
284
+
285
+ it('component supports hover state changes', () => {
286
+ const { container } = renderLineCrossWithData();
287
+ // Verify the component renders with interactive data
288
+ const svg = container.querySelector('svg');
289
+ expect(svg).toBeInTheDocument();
290
+ });
291
+ });
292
+
293
+ describe('correctness indicators', () => {
294
+ it('renders correctness indicator for correct answer', () => {
295
+ const { getByTestId } = renderLineCrossWithData({
296
+ correctness: { value: 'correct', label: 'Correct!' },
297
+ });
298
+ expect(getByTestId('correctness-indicator')).toBeInTheDocument();
299
+ });
300
+
301
+ it('does not render correctness indicator when not interactive', () => {
302
+ const { queryByTestId } = renderLineCrossWithData({
303
+ correctness: { value: 'correct', label: 'Correct!' },
304
+ interactive: false,
305
+ });
306
+ expect(queryByTestId('correctness-indicator')).not.toBeInTheDocument();
307
+ });
308
+
309
+ it('renders small correct indicator for incorrect answer', () => {
310
+ const data = [
311
+ {
312
+ x: 5,
313
+ y: 10,
314
+ label: 'A',
315
+ correctness: { value: 'incorrect', label: 'Wrong' },
316
+ interactive: true,
317
+ },
318
+ ];
319
+ const correctData = [{ label: 'A', value: 15 }];
320
+ const props = {
321
+ graphProps: graphProps(),
322
+ data,
323
+ correctData,
324
+ };
325
+ const { getByTestId } = render(
326
+ <ThemeProvider theme={theme}>
327
+ <LineChart {...props} />
328
+ </ThemeProvider>,
329
+ );
330
+ expect(getByTestId('small-correct-indicator')).toBeInTheDocument();
331
+ });
332
+
333
+ it('does not render small correct indicator for correct answer', () => {
334
+ const data = [
335
+ {
336
+ x: 5,
337
+ y: 10,
338
+ label: 'A',
339
+ correctness: { value: 'correct', label: 'Correct!' },
340
+ interactive: true,
341
+ },
342
+ ];
343
+ const correctData = [{ label: 'A', value: 15 }];
344
+ const props = {
345
+ graphProps: graphProps(),
346
+ data,
347
+ correctData,
348
+ };
349
+ const { queryByTestId } = render(
350
+ <ThemeProvider theme={theme}>
351
+ <LineChart {...props} />
352
+ </ThemeProvider>,
353
+ );
354
+ expect(queryByTestId('small-correct-indicator')).not.toBeInTheDocument();
355
+ });
356
+ });
357
+
358
+ describe('cross lines positioning', () => {
359
+ it('renders two cross lines for each point', () => {
360
+ const { getAllByTestId } = renderLineCrossWithData();
361
+ const linePaths = getAllByTestId('line-path');
362
+ expect(linePaths.length).toBe(2); // Two lines per cross
363
+ });
364
+
365
+ it('positions cross lines correctly with scale', () => {
366
+ const { getAllByTestId } = renderLineCrossWithData({ x: 3, y: 7 });
367
+ const linePaths = getAllByTestId('line-path');
368
+ expect(linePaths.length).toBe(2);
369
+ });
370
+ });
371
+
372
+ describe('edge cases', () => {
373
+ it('renders with zero coordinates', () => {
374
+ const { container } = renderLineCrossWithData({ x: 0, y: 0 });
375
+ expect(container).toBeInTheDocument();
376
+ });
377
+
378
+ it('renders with negative coordinates', () => {
379
+ const { container } = renderLineCrossWithData({ x: -5, y: -10 });
380
+ expect(container).toBeInTheDocument();
381
+ });
382
+
383
+ it('renders with large coordinates', () => {
384
+ const { container } = renderLineCrossWithData({ x: 1000, y: 2000 });
385
+ expect(container).toBeInTheDocument();
386
+ });
387
+
388
+ it('renders without correctness', () => {
389
+ const { container } = renderLineCrossWithData({ correctness: null });
390
+ expect(container).toBeInTheDocument();
391
+ });
392
+
393
+ it('renders without label', () => {
394
+ const { container } = renderLineCrossWithData({ label: undefined });
395
+ expect(container).toBeInTheDocument();
396
+ });
397
+
398
+ it('handles custom radius', () => {
399
+ const { container } = renderLineCrossWithData({ r: 12 });
400
+ expect(container).toBeInTheDocument();
401
+ });
402
+
403
+ it('applies className', () => {
404
+ const { getByTestId } = renderLineCrossWithData({ className: 'custom-cross' });
405
+ const group = getByTestId('visx-group');
406
+ expect(group).toBeInTheDocument();
407
+ });
408
+ });
409
+
410
+ describe('hover rect sizing', () => {
411
+ it('component supports hover rect with proper sizing', () => {
412
+ const { container } = renderLineCrossWithData({ r: 10 });
413
+ // Verify the component renders with custom radius
414
+ expect(container).toBeInTheDocument();
415
+ });
416
+
417
+ it('component supports hover rect centering', () => {
418
+ const { container } = renderLineCrossWithData({ x: 5, y: 10, r: 8 });
419
+ // Verify the component renders at specified coordinates
420
+ expect(container).toBeInTheDocument();
421
+ });
422
+ });
423
+
424
+ describe('multiple data points', () => {
425
+ it('renders multiple crosses for multiple data points', () => {
426
+ const data = [
427
+ { x: 1, y: 2, label: 'A', interactive: true },
428
+ { x: 3, y: 4, label: 'B', interactive: true },
429
+ { x: 5, y: 6, label: 'C', interactive: true },
430
+ ];
431
+ const props = {
432
+ graphProps: graphProps(),
433
+ data,
434
+ };
435
+ const { getAllByTestId } = render(
436
+ <ThemeProvider theme={theme}>
437
+ <LineChart {...props} />
438
+ </ThemeProvider>,
439
+ );
440
+ const groups = getAllByTestId('visx-group');
441
+ expect(groups.length).toBe(3);
442
+ });
443
+
444
+ it('renders multiple data points with proper structure', () => {
445
+ const data = [
446
+ { x: 1, y: 2, label: 'A', interactive: true },
447
+ { x: 3, y: 4, label: 'B', interactive: true },
448
+ ];
449
+ const props = {
450
+ graphProps: graphProps(),
451
+ data,
452
+ };
453
+ const { container } = render(
454
+ <ThemeProvider theme={theme}>
455
+ <LineChart {...props} />
456
+ </ThemeProvider>,
457
+ );
458
+
459
+ // Should render the component with multiple data points
460
+ expect(container).toBeInTheDocument();
461
+ });
40
462
  });
41
463
  });
@@ -1,4 +1,4 @@
1
- import { scaleLinear, scaleBand } from 'd3-scale';
1
+ import { scaleBand, scaleLinear } from 'd3-scale';
2
2
 
3
3
  export const scaleMock = () => {
4
4
  const fn = jest.fn((n) => n);
@@ -3,7 +3,6 @@ import React from 'react';
3
3
  import DragHandle from '../drag-handle';
4
4
  import { gridDraggable } from '@pie-lib/plot';
5
5
  import { graphProps } from './utils';
6
- import { bounds } from '../../../utils';
7
6
 
8
7
  jest.mock('../../../utils', () => {
9
8
  const { point } = jest.requireActual('../../../utils');
@@ -39,7 +38,7 @@ describe('BasePoint', () => {
39
38
  return render(
40
39
  <svg>
41
40
  <DragHandle {...props} />
42
- </svg>
41
+ </svg>,
43
42
  );
44
43
  };
45
44
 
@@ -3,7 +3,7 @@ import classNames from 'classnames';
3
3
  import PropTypes from 'prop-types';
4
4
  import { styled } from '@mui/material/styles';
5
5
 
6
- import { gridDraggable, utils, types } from '@pie-lib/plot';
6
+ import { gridDraggable, types, utils } from '@pie-lib/plot';
7
7
  import { color } from '@pie-lib/render-ui';
8
8
  import { disabled } from '../../common/styles';
9
9
 
@@ -67,16 +67,7 @@ class RawDragHandle extends React.Component {
67
67
  };
68
68
 
69
69
  render() {
70
- const {
71
- x,
72
- y,
73
- graphProps,
74
- className,
75
- interactive,
76
- CustomDraggableComponent,
77
- correctness,
78
- ...rest
79
- } = this.props;
70
+ const { x, y, graphProps, className, interactive, CustomDraggableComponent, correctness, ...rest } = this.props;
80
71
  const { scale } = graphProps;
81
72
 
82
73
  if (!CustomDraggableComponent) {
@@ -5,7 +5,7 @@ import PropTypes from 'prop-types';
5
5
  import { types } from '@pie-lib/plot';
6
6
  import DraggableHandle, { DragHandle } from './drag-handle';
7
7
  import { styled } from '@mui/material/styles';
8
- import isEqual from 'lodash/isEqual';
8
+ import { isEqual } from 'lodash-es';
9
9
  import { color } from '@pie-lib/render-ui';
10
10
 
11
11
  const getData = (data, domain) => {
@@ -103,7 +103,7 @@ export class RawLine extends React.Component {
103
103
  const r = 6;
104
104
  const enableDraggable = defineChart || point.interactive;
105
105
  const Component = enableDraggable ? DraggableHandle : DragHandle;
106
-
106
+
107
107
  return (
108
108
  <Component
109
109
  key={`point-${point.x}-${i}`}
@@ -21,10 +21,11 @@ const StyledLinePath = styled(LinePath)(() => ({
21
21
  const StyledGroup = styled(Group)(({ correctness, interactive }) => ({
22
22
  stroke: color.defaults.TEXT,
23
23
  transition: 'fill 200ms linear, height 200ms linear',
24
- ...(correctness && !interactive && {
25
- fill: `${color.defaults.BLACK} !important`,
26
- stroke: `${color.defaults.BLACK} !important`,
27
- }),
24
+ ...(correctness &&
25
+ !interactive && {
26
+ fill: `${color.defaults.BLACK} !important`,
27
+ stroke: `${color.defaults.BLACK} !important`,
28
+ }),
28
29
  '&.non-interactive': {
29
30
  stroke: color.disabled?.() || '#ccc',
30
31
  },
@@ -44,7 +45,7 @@ const DraggableComponent = (props) => {
44
45
  const squareHalf = squareSize / 2;
45
46
  const cx = scale.x(x);
46
47
  const cy = scale.y(y);
47
-
48
+
48
49
  return (
49
50
  <StyledGroup className={className} correctness={correctness} interactive={interactive}>
50
51
  <StyledLinePath
@@ -90,14 +91,7 @@ const DraggableComponent = (props) => {
90
91
  {...rest}
91
92
  />
92
93
  {/* show correctness indicators */}
93
- <CorrectnessIndicator
94
- scale={scale}
95
- x={x}
96
- y={y}
97
- r={r}
98
- correctness={correctness}
99
- interactive={interactive}
100
- />
94
+ <CorrectnessIndicator scale={scale} x={x} y={y} r={r} correctness={correctness} interactive={interactive} />
101
95
  {/* show correct point if answer was incorrect */}
102
96
  <SmallCorrectPointIndicator
103
97
  scale={scale}
@@ -1,11 +1,11 @@
1
- import React, { useState, useCallback, useEffect, useRef } from 'react';
1
+ import React, { useCallback, useEffect, useRef, useState } from 'react';
2
2
  import classNames from 'classnames';
3
3
  import { styled } from '@mui/material/styles';
4
4
  import AutosizeInput from 'react-input-autosize';
5
5
  import PropTypes from 'prop-types';
6
6
 
7
7
  import { types } from '@pie-lib/plot';
8
- import { correct, incorrect, disabled } from './common/styles';
8
+ import { correct, disabled, incorrect } from './common/styles';
9
9
  import { color } from '@pie-lib/render-ui';
10
10
  import { renderMath } from '@pie-lib/math-rendering';
11
11
 
@@ -174,7 +174,7 @@ export const MarkLabel = (props) => {
174
174
  externalInputRef(r);
175
175
  }
176
176
  }}
177
- name='mark-label-input'
177
+ name="mark-label-input"
178
178
  autoFocus={isEditing || autoFocus}
179
179
  disabled={disabled}
180
180
  inputClassName={classNames(