@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
|
@@ -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(
|
|
121
|
-
|
|
122
|
-
|
|
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,
|
|
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 {
|
|
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(
|
|
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
|
});
|
|
@@ -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,
|
|
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) {
|
package/src/line/common/line.jsx
CHANGED
|
@@ -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
|
|
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}`}
|
package/src/line/line-cross.js
CHANGED
|
@@ -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 &&
|
|
25
|
-
|
|
26
|
-
|
|
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}
|
package/src/mark-label.jsx
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import 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,
|
|
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=
|
|
177
|
+
name="mark-label-input"
|
|
178
178
|
autoFocus={isEditing || autoFocus}
|
|
179
179
|
disabled={disabled}
|
|
180
180
|
inputClassName={classNames(
|