@pie-lib/graphing-solution-set 3.1.1-next.0 → 3.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/axis/arrow.js +0 -3
- package/lib/axis/arrow.js.map +1 -1
- package/lib/axis/axes.js +0 -16
- package/lib/axis/axes.js.map +1 -1
- package/lib/axis/index.js +0 -7
- package/lib/axis/index.js.map +1 -1
- package/lib/bg.js +0 -6
- package/lib/bg.js.map +1 -1
- package/lib/container/actions.js +0 -1
- package/lib/container/actions.js.map +1 -1
- package/lib/container/index.js +4 -10
- package/lib/container/index.js.map +1 -1
- package/lib/container/marks.js +0 -2
- package/lib/container/marks.js.map +1 -1
- package/lib/container/middleware.js +0 -1
- package/lib/container/middleware.js.map +1 -1
- package/lib/container/reducer.js +0 -1
- package/lib/container/reducer.js.map +1 -1
- package/lib/coordinates-label.js +0 -11
- package/lib/coordinates-label.js.map +1 -1
- package/lib/graph-with-controls.js +3 -22
- package/lib/graph-with-controls.js.map +1 -1
- package/lib/graph.js +7 -26
- package/lib/graph.js.map +1 -1
- package/lib/grid-setup.js +0 -11
- package/lib/grid-setup.js.map +1 -1
- package/lib/grid.js +0 -22
- package/lib/grid.js.map +1 -1
- package/lib/index.js +0 -7
- package/lib/index.js.map +1 -1
- package/lib/labels.js +0 -13
- package/lib/labels.js.map +1 -1
- package/lib/mark-label.js +0 -16
- package/lib/mark-label.js.map +1 -1
- package/lib/toggle-bar.js +0 -17
- package/lib/toggle-bar.js.map +1 -1
- package/lib/tool-menu.js +0 -3
- package/lib/tool-menu.js.map +1 -1
- package/lib/tools/index.js +0 -1
- package/lib/tools/index.js.map +1 -1
- package/lib/tools/line/component.js +0 -13
- package/lib/tools/line/component.js.map +1 -1
- package/lib/tools/line/index.js +0 -1
- package/lib/tools/line/index.js.map +1 -1
- package/lib/tools/polygon/component.js +5 -25
- package/lib/tools/polygon/component.js.map +1 -1
- package/lib/tools/polygon/index.js +0 -12
- package/lib/tools/polygon/index.js.map +1 -1
- package/lib/tools/polygon/line.js +0 -16
- package/lib/tools/polygon/line.js.map +1 -1
- package/lib/tools/polygon/polygon.js +0 -19
- package/lib/tools/polygon/polygon.js.map +1 -1
- package/lib/tools/shared/arrow-head.js +0 -3
- package/lib/tools/shared/arrow-head.js.map +1 -1
- package/lib/tools/shared/line/index.js +7 -22
- package/lib/tools/shared/line/index.js.map +1 -1
- package/lib/tools/shared/line/line-path.js +0 -16
- package/lib/tools/shared/line/line-path.js.map +1 -1
- package/lib/tools/shared/line/with-root-edge.js +0 -11
- package/lib/tools/shared/line/with-root-edge.js.map +1 -1
- package/lib/tools/shared/point/arrow-point.js +2 -5
- package/lib/tools/shared/point/arrow-point.js.map +1 -1
- package/lib/tools/shared/point/arrow.js +0 -3
- package/lib/tools/shared/point/arrow.js.map +1 -1
- package/lib/tools/shared/point/base-point.js +0 -11
- package/lib/tools/shared/point/base-point.js.map +1 -1
- package/lib/tools/shared/point/index.js +0 -16
- package/lib/tools/shared/point/index.js.map +1 -1
- package/lib/tools/shared/styles.js +0 -1
- package/lib/tools/shared/styles.js.map +1 -1
- package/lib/tools/shared/types.js +0 -1
- package/lib/tools/shared/types.js.map +1 -1
- package/lib/undo-redo.js +0 -2
- package/lib/undo-redo.js.map +1 -1
- package/lib/use-debounce.js +0 -2
- package/lib/use-debounce.js.map +1 -1
- package/lib/utils.js +8 -26
- package/lib/utils.js.map +1 -1
- package/package.json +14 -11
- package/src/__tests__/bg.test.jsx +250 -0
- package/src/__tests__/coordinates-label.test.jsx +243 -0
- package/src/__tests__/graph-with-controls.test.jsx +9 -10
- package/src/__tests__/graph.test.jsx +0 -2
- package/src/__tests__/grid-setup.test.jsx +645 -0
- package/src/__tests__/mark-label.test.jsx +1 -1
- package/src/__tests__/tool-menu.test.jsx +422 -2
- package/src/__tests__/use-debounce.test.js +1 -1
- package/src/__tests__/utils.test.js +15 -61
- package/src/axis/__tests__/axes.test.jsx +1 -1
- package/src/axis/axes.jsx +7 -21
- package/src/axis/index.js +1 -0
- package/src/bg.jsx +1 -1
- package/src/container/__tests__/actions.test.js +105 -0
- package/src/container/__tests__/index.test.jsx +227 -0
- package/src/container/__tests__/marks.test.js +172 -0
- package/src/container/__tests__/middleware.test.js +235 -0
- package/src/container/__tests__/reducer.test.js +324 -0
- package/src/container/index.jsx +3 -4
- package/src/coordinates-label.jsx +1 -7
- package/src/graph-with-controls.jsx +7 -25
- package/src/graph.jsx +3 -4
- package/src/grid-setup.jsx +1 -1
- package/src/mark-label.jsx +2 -2
- package/src/toggle-bar.jsx +8 -1
- package/src/tool-menu.jsx +1 -1
- package/src/tools/line/__tests__/component.test.jsx +1 -0
- package/src/tools/line/component.jsx +2 -2
- package/src/tools/polygon/__tests__/component.test.jsx +417 -5
- package/src/tools/polygon/__tests__/polygon.test.jsx +1 -1
- package/src/tools/polygon/component.jsx +4 -14
- package/src/tools/polygon/line.jsx +1 -1
- package/src/tools/shared/line/__tests__/index.test.jsx +460 -17
- package/src/tools/shared/line/__tests__/line-path.test.jsx +7 -4
- package/src/tools/shared/line/__tests__/with-root-edge.test.jsx +439 -14
- package/src/tools/shared/line/index.jsx +4 -6
- package/src/tools/shared/line/line-path.jsx +2 -8
- package/src/tools/shared/point/__tests__/arrow.test.jsx +469 -0
- package/src/tools/shared/point/arrow-point.jsx +2 -2
- package/src/tools/shared/point/base-point.jsx +1 -1
- package/src/tools/shared/point/index.jsx +1 -1
- package/src/undo-redo.jsx +1 -3
- package/src/use-debounce.js +1 -1
- package/src/utils.js +1 -5
- package/NEXT.CHANGELOG.json +0 -16
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
import { render } from '@pie-lib/test-utils';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import Arrow from '../arrow';
|
|
4
|
+
import { graphProps } from '../../../../__tests__/utils';
|
|
5
|
+
import { thinnerShapesNeeded } from '../../../../utils';
|
|
6
|
+
|
|
7
|
+
jest.mock('../../../../utils', () => ({
|
|
8
|
+
...jest.requireActual('../../../../utils'),
|
|
9
|
+
thinnerShapesNeeded: jest.fn(() => false),
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
describe('Arrow', () => {
|
|
13
|
+
let onChange = jest.fn();
|
|
14
|
+
|
|
15
|
+
const renderComponent = (extras) => {
|
|
16
|
+
const defaults = {
|
|
17
|
+
classes: {},
|
|
18
|
+
className: 'arrow-class',
|
|
19
|
+
onChange,
|
|
20
|
+
graphProps: graphProps(),
|
|
21
|
+
x: 5,
|
|
22
|
+
y: 10,
|
|
23
|
+
angle: 45,
|
|
24
|
+
};
|
|
25
|
+
const props = { ...defaults, ...extras };
|
|
26
|
+
return render(<Arrow {...props} />);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
onChange = jest.fn();
|
|
31
|
+
thinnerShapesNeeded.mockReturnValue(false);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe('rendering', () => {
|
|
35
|
+
it('renders without crashing', () => {
|
|
36
|
+
const { container } = renderComponent();
|
|
37
|
+
expect(container.firstChild).toBeInTheDocument();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('renders a g element with correct class names', () => {
|
|
41
|
+
const { container } = renderComponent({
|
|
42
|
+
classes: { point: 'point-class' },
|
|
43
|
+
className: 'custom-class',
|
|
44
|
+
});
|
|
45
|
+
const gElement = container.querySelector('g');
|
|
46
|
+
expect(gElement).toBeInTheDocument();
|
|
47
|
+
expect(gElement).toHaveClass('point-class');
|
|
48
|
+
expect(gElement).toHaveClass('custom-class');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('renders ArrowHead component', () => {
|
|
52
|
+
const { container } = renderComponent();
|
|
53
|
+
const polygon = container.querySelector('polygon');
|
|
54
|
+
expect(polygon).toBeInTheDocument();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('applies disabled class when disabled is true', () => {
|
|
58
|
+
const { container } = renderComponent({
|
|
59
|
+
classes: { point: 'point-class', disabled: 'disabled-class' },
|
|
60
|
+
disabled: true,
|
|
61
|
+
});
|
|
62
|
+
const gElement = container.querySelector('g');
|
|
63
|
+
expect(gElement).toHaveClass('disabled-class');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('does not apply disabled class when disabled is false', () => {
|
|
67
|
+
const { container } = renderComponent({
|
|
68
|
+
classes: { point: 'point-class', disabled: 'disabled-class' },
|
|
69
|
+
disabled: false,
|
|
70
|
+
});
|
|
71
|
+
const gElement = container.querySelector('g');
|
|
72
|
+
expect(gElement).not.toHaveClass('disabled-class');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('applies correctness class when provided', () => {
|
|
76
|
+
const { container } = renderComponent({
|
|
77
|
+
classes: { point: 'point-class', correct: 'correct-class' },
|
|
78
|
+
correctness: 'correct',
|
|
79
|
+
});
|
|
80
|
+
const gElement = container.querySelector('g');
|
|
81
|
+
expect(gElement).toHaveClass('correct-class');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('applies incorrect correctness class', () => {
|
|
85
|
+
const { container } = renderComponent({
|
|
86
|
+
classes: { point: 'point-class', incorrect: 'incorrect-class' },
|
|
87
|
+
correctness: 'incorrect',
|
|
88
|
+
});
|
|
89
|
+
const gElement = container.querySelector('g');
|
|
90
|
+
expect(gElement).toHaveClass('incorrect-class');
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('size calculation', () => {
|
|
95
|
+
it('uses size 14 when thinnerShapesNeeded returns false', () => {
|
|
96
|
+
thinnerShapesNeeded.mockReturnValue(false);
|
|
97
|
+
const { container } = renderComponent({ x: 0, y: 0 });
|
|
98
|
+
const polygon = container.querySelector('polygon');
|
|
99
|
+
|
|
100
|
+
// Expected points: "0,0 -14,-7 -14,7"
|
|
101
|
+
expect(polygon).toHaveAttribute('points');
|
|
102
|
+
const points = polygon.getAttribute('points');
|
|
103
|
+
expect(points).toContain('-14');
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('uses size 12 when thinnerShapesNeeded returns true', () => {
|
|
107
|
+
thinnerShapesNeeded.mockReturnValue(true);
|
|
108
|
+
const { container } = renderComponent({ x: 0, y: 0 });
|
|
109
|
+
const polygon = container.querySelector('polygon');
|
|
110
|
+
|
|
111
|
+
// Expected points: "0,0 -12,-6 -12,6"
|
|
112
|
+
expect(polygon).toHaveAttribute('points');
|
|
113
|
+
const points = polygon.getAttribute('points');
|
|
114
|
+
expect(points).toContain('-12');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('calls thinnerShapesNeeded with graphProps', () => {
|
|
118
|
+
const gp = graphProps();
|
|
119
|
+
renderComponent({ graphProps: gp });
|
|
120
|
+
expect(thinnerShapesNeeded).toHaveBeenCalledWith(gp);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe('coordinate scaling', () => {
|
|
125
|
+
it('scales x and y coordinates using graphProps.scale', () => {
|
|
126
|
+
const mockScale = {
|
|
127
|
+
x: jest.fn((n) => n * 10),
|
|
128
|
+
y: jest.fn((n) => n * 20),
|
|
129
|
+
};
|
|
130
|
+
const gp = {
|
|
131
|
+
...graphProps(),
|
|
132
|
+
scale: mockScale,
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
renderComponent({ x: 5, y: 10, graphProps: gp });
|
|
136
|
+
|
|
137
|
+
expect(mockScale.x).toHaveBeenCalledWith(5);
|
|
138
|
+
expect(mockScale.y).toHaveBeenCalledWith(10);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('renders arrow with scaled coordinates', () => {
|
|
142
|
+
const mockScale = {
|
|
143
|
+
x: jest.fn((n) => n * 2),
|
|
144
|
+
y: jest.fn((n) => n * 3),
|
|
145
|
+
};
|
|
146
|
+
const gp = {
|
|
147
|
+
...graphProps(),
|
|
148
|
+
scale: mockScale,
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const { container } = renderComponent({ x: 5, y: 10, graphProps: gp, angle: 0 });
|
|
152
|
+
const polygon = container.querySelector('polygon');
|
|
153
|
+
|
|
154
|
+
// scaledX = 5 * 2 = 10, scaledY = 10 * 3 = 30
|
|
155
|
+
// Expected points: "10,30 -4,23 -4,37" (with size 14)
|
|
156
|
+
const points = polygon.getAttribute('points');
|
|
157
|
+
expect(points).toContain('10');
|
|
158
|
+
expect(points).toContain('30');
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
describe('angle rotation', () => {
|
|
163
|
+
it('applies rotation transform with negative angle', () => {
|
|
164
|
+
const { container } = renderComponent({ x: 5, y: 10, angle: 45 });
|
|
165
|
+
const polygon = container.querySelector('polygon');
|
|
166
|
+
const transform = polygon.getAttribute('transform');
|
|
167
|
+
|
|
168
|
+
// Angle should be negated: rotate(-45, 5, 10)
|
|
169
|
+
expect(transform).toContain('rotate(-45');
|
|
170
|
+
expect(transform).toContain('5');
|
|
171
|
+
expect(transform).toContain('10');
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('handles zero angle', () => {
|
|
175
|
+
const { container } = renderComponent({ x: 3, y: 7, angle: 0 });
|
|
176
|
+
const polygon = container.querySelector('polygon');
|
|
177
|
+
const transform = polygon.getAttribute('transform');
|
|
178
|
+
|
|
179
|
+
// will convert -0 to 0
|
|
180
|
+
expect(transform).toMatch(/rotate\((-)?0/);
|
|
181
|
+
expect(transform).toContain('3');
|
|
182
|
+
expect(transform).toContain('7');
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('handles negative angle', () => {
|
|
186
|
+
const { container } = renderComponent({ x: 2, y: 4, angle: -30 });
|
|
187
|
+
const polygon = container.querySelector('polygon');
|
|
188
|
+
const transform = polygon.getAttribute('transform');
|
|
189
|
+
|
|
190
|
+
// -(-30) = 30
|
|
191
|
+
expect(transform).toContain('rotate(30');
|
|
192
|
+
expect(transform).toContain('2');
|
|
193
|
+
expect(transform).toContain('4');
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('handles 360 degree angle', () => {
|
|
197
|
+
const { container } = renderComponent({ x: 1, y: 1, angle: 360 });
|
|
198
|
+
const polygon = container.querySelector('polygon');
|
|
199
|
+
const transform = polygon.getAttribute('transform');
|
|
200
|
+
|
|
201
|
+
expect(transform).toContain('rotate(-360');
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
describe('arrow points calculation', () => {
|
|
206
|
+
it('calculates correct points for arrow head', () => {
|
|
207
|
+
thinnerShapesNeeded.mockReturnValue(false);
|
|
208
|
+
const { container } = renderComponent({ x: 0, y: 0, angle: 0 });
|
|
209
|
+
const polygon = container.querySelector('polygon');
|
|
210
|
+
|
|
211
|
+
// size = 14
|
|
212
|
+
// points = "0,0 -14,-7 -14, 7" (note the space after comma in the template literal)
|
|
213
|
+
const points = polygon.getAttribute('points');
|
|
214
|
+
expect(points).toContain('0,0');
|
|
215
|
+
expect(points).toContain('-14,-7');
|
|
216
|
+
expect(points).toMatch(/-14,\s*7/);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('calculates points with scaled coordinates', () => {
|
|
220
|
+
thinnerShapesNeeded.mockReturnValue(false);
|
|
221
|
+
const mockScale = {
|
|
222
|
+
x: jest.fn((n) => n),
|
|
223
|
+
y: jest.fn((n) => n),
|
|
224
|
+
};
|
|
225
|
+
const gp = {
|
|
226
|
+
...graphProps(),
|
|
227
|
+
scale: mockScale,
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
const { container } = renderComponent({ x: 10, y: 20, graphProps: gp });
|
|
231
|
+
const polygon = container.querySelector('polygon');
|
|
232
|
+
|
|
233
|
+
// scaledX = 10, scaledY = 20, size = 14
|
|
234
|
+
// points = "10,20 -4,13 -4, 27" (note the space after comma in the template literal)
|
|
235
|
+
const points = polygon.getAttribute('points');
|
|
236
|
+
expect(points).toContain('10,20');
|
|
237
|
+
expect(points).toContain('-4,13');
|
|
238
|
+
expect(points).toMatch(/-4,\s*27/);
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
describe('props handling', () => {
|
|
243
|
+
it('passes through additional props to g element', () => {
|
|
244
|
+
const { container } = renderComponent({
|
|
245
|
+
'data-test': 'arrow-test',
|
|
246
|
+
role: 'graphics-symbol',
|
|
247
|
+
});
|
|
248
|
+
const gElement = container.querySelector('g');
|
|
249
|
+
expect(gElement).toHaveAttribute('data-test', 'arrow-test');
|
|
250
|
+
expect(gElement).toHaveAttribute('role', 'graphics-symbol');
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('excludes filtered props from g element', () => {
|
|
254
|
+
const { container } = renderComponent({
|
|
255
|
+
x: 5,
|
|
256
|
+
y: 10,
|
|
257
|
+
angle: 45,
|
|
258
|
+
disabled: false,
|
|
259
|
+
correctness: 'correct',
|
|
260
|
+
graphProps: graphProps(),
|
|
261
|
+
});
|
|
262
|
+
const gElement = container.querySelector('g');
|
|
263
|
+
|
|
264
|
+
// These props should not be passed to the g element
|
|
265
|
+
expect(gElement).not.toHaveAttribute('x');
|
|
266
|
+
expect(gElement).not.toHaveAttribute('y');
|
|
267
|
+
expect(gElement).not.toHaveAttribute('angle');
|
|
268
|
+
expect(gElement).not.toHaveAttribute('disabled');
|
|
269
|
+
expect(gElement).not.toHaveAttribute('correctness');
|
|
270
|
+
expect(gElement).not.toHaveAttribute('graphProps');
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
describe('edge cases', () => {
|
|
275
|
+
it('handles zero coordinates', () => {
|
|
276
|
+
const { container } = renderComponent({ x: 0, y: 0, angle: 0 });
|
|
277
|
+
const polygon = container.querySelector('polygon');
|
|
278
|
+
expect(polygon).toBeInTheDocument();
|
|
279
|
+
expect(polygon.getAttribute('points')).toBeTruthy();
|
|
280
|
+
expect(polygon.getAttribute('transform')).toBeTruthy();
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it('handles negative coordinates', () => {
|
|
284
|
+
const { container } = renderComponent({ x: -5, y: -10, angle: 0 });
|
|
285
|
+
const polygon = container.querySelector('polygon');
|
|
286
|
+
expect(polygon).toBeInTheDocument();
|
|
287
|
+
expect(polygon.getAttribute('points')).toBeTruthy();
|
|
288
|
+
expect(polygon.getAttribute('transform')).toBeTruthy();
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it('handles large coordinates', () => {
|
|
292
|
+
const { container } = renderComponent({ x: 1000, y: 2000, angle: 0 });
|
|
293
|
+
const polygon = container.querySelector('polygon');
|
|
294
|
+
expect(polygon).toBeInTheDocument();
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('handles fractional coordinates', () => {
|
|
298
|
+
const { container } = renderComponent({ x: 3.5, y: 7.25, angle: 22.5 });
|
|
299
|
+
const polygon = container.querySelector('polygon');
|
|
300
|
+
expect(polygon).toBeInTheDocument();
|
|
301
|
+
expect(polygon.getAttribute('transform')).toContain('rotate(-22.5');
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it('renders with empty classes prop', () => {
|
|
305
|
+
const { container } = render(<Arrow x={5} y={10} angle={45} graphProps={graphProps()} classes={{}} />);
|
|
306
|
+
const gElement = container.querySelector('g');
|
|
307
|
+
expect(gElement).toBeInTheDocument();
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it('renders without className prop', () => {
|
|
311
|
+
const { container } = render(<Arrow x={5} y={10} angle={45} graphProps={graphProps()} classes={{}} />);
|
|
312
|
+
const gElement = container.querySelector('g');
|
|
313
|
+
expect(gElement).toBeInTheDocument();
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
describe('graphProps integration', () => {
|
|
318
|
+
it('works with different domain and range values', () => {
|
|
319
|
+
const gp = graphProps(-10, 10, -20, 20);
|
|
320
|
+
const { container } = renderComponent({ graphProps: gp });
|
|
321
|
+
expect(container.querySelector('polygon')).toBeInTheDocument();
|
|
322
|
+
expect(thinnerShapesNeeded).toHaveBeenCalledWith(gp);
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
it('works with custom size in graphProps', () => {
|
|
326
|
+
const gp = {
|
|
327
|
+
...graphProps(),
|
|
328
|
+
size: {
|
|
329
|
+
width: 800,
|
|
330
|
+
height: 600,
|
|
331
|
+
},
|
|
332
|
+
};
|
|
333
|
+
const { container } = renderComponent({ graphProps: gp });
|
|
334
|
+
expect(container.querySelector('polygon')).toBeInTheDocument();
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
it('handles graphProps with small step values', () => {
|
|
338
|
+
const gp = {
|
|
339
|
+
...graphProps(),
|
|
340
|
+
domain: { min: 0, max: 10, step: 0.1 },
|
|
341
|
+
range: { min: 0, max: 10, step: 0.1 },
|
|
342
|
+
};
|
|
343
|
+
thinnerShapesNeeded.mockReturnValue(true);
|
|
344
|
+
const { container } = renderComponent({ graphProps: gp });
|
|
345
|
+
const polygon = container.querySelector('polygon');
|
|
346
|
+
|
|
347
|
+
// Should use size 12 for thinner shapes
|
|
348
|
+
expect(polygon).toBeInTheDocument();
|
|
349
|
+
expect(thinnerShapesNeeded).toHaveBeenCalledWith(gp);
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
describe('multiple correctness states', () => {
|
|
354
|
+
it('handles partial correctness', () => {
|
|
355
|
+
const { container } = renderComponent({
|
|
356
|
+
classes: { point: 'point-class', partial: 'partial-class' },
|
|
357
|
+
correctness: 'partial',
|
|
358
|
+
});
|
|
359
|
+
const gElement = container.querySelector('g');
|
|
360
|
+
expect(gElement).toHaveClass('partial-class');
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it('handles undefined correctness', () => {
|
|
364
|
+
const { container } = renderComponent({
|
|
365
|
+
classes: { point: 'point-class' },
|
|
366
|
+
correctness: undefined,
|
|
367
|
+
});
|
|
368
|
+
const gElement = container.querySelector('g');
|
|
369
|
+
expect(gElement).toHaveClass('point-class');
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
it('handles null correctness', () => {
|
|
373
|
+
const { container } = renderComponent({
|
|
374
|
+
classes: { point: 'point-class' },
|
|
375
|
+
correctness: null,
|
|
376
|
+
});
|
|
377
|
+
const gElement = container.querySelector('g');
|
|
378
|
+
expect(gElement).toHaveClass('point-class');
|
|
379
|
+
});
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
describe('combined states', () => {
|
|
383
|
+
it('handles disabled and correct states together', () => {
|
|
384
|
+
const { container } = renderComponent({
|
|
385
|
+
classes: {
|
|
386
|
+
point: 'point-class',
|
|
387
|
+
disabled: 'disabled-class',
|
|
388
|
+
correct: 'correct-class',
|
|
389
|
+
},
|
|
390
|
+
disabled: true,
|
|
391
|
+
correctness: 'correct',
|
|
392
|
+
});
|
|
393
|
+
const gElement = container.querySelector('g');
|
|
394
|
+
expect(gElement).toHaveClass('point-class');
|
|
395
|
+
expect(gElement).toHaveClass('disabled-class');
|
|
396
|
+
expect(gElement).toHaveClass('correct-class');
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
it('handles disabled and incorrect states together', () => {
|
|
400
|
+
const { container } = renderComponent({
|
|
401
|
+
classes: {
|
|
402
|
+
point: 'point-class',
|
|
403
|
+
disabled: 'disabled-class',
|
|
404
|
+
incorrect: 'incorrect-class',
|
|
405
|
+
},
|
|
406
|
+
disabled: true,
|
|
407
|
+
correctness: 'incorrect',
|
|
408
|
+
});
|
|
409
|
+
const gElement = container.querySelector('g');
|
|
410
|
+
expect(gElement).toHaveClass('point-class');
|
|
411
|
+
expect(gElement).toHaveClass('disabled-class');
|
|
412
|
+
expect(gElement).toHaveClass('incorrect-class');
|
|
413
|
+
});
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
describe('re-rendering', () => {
|
|
417
|
+
it('updates when x coordinate changes', () => {
|
|
418
|
+
const { container, rerender } = renderComponent({ x: 5, y: 10, angle: 0 });
|
|
419
|
+
let polygon = container.querySelector('polygon');
|
|
420
|
+
const initialPoints = polygon.getAttribute('points');
|
|
421
|
+
|
|
422
|
+
rerender(<Arrow x={15} y={10} angle={0} graphProps={graphProps()} classes={{}} className="arrow-class" />);
|
|
423
|
+
|
|
424
|
+
polygon = container.querySelector('polygon');
|
|
425
|
+
const newPoints = polygon.getAttribute('points');
|
|
426
|
+
expect(newPoints).not.toBe(initialPoints);
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
it('updates when y coordinate changes', () => {
|
|
430
|
+
const { container, rerender } = renderComponent({ x: 5, y: 10, angle: 0 });
|
|
431
|
+
let polygon = container.querySelector('polygon');
|
|
432
|
+
const initialPoints = polygon.getAttribute('points');
|
|
433
|
+
|
|
434
|
+
rerender(<Arrow x={5} y={20} angle={0} graphProps={graphProps()} classes={{}} className="arrow-class" />);
|
|
435
|
+
|
|
436
|
+
polygon = container.querySelector('polygon');
|
|
437
|
+
const newPoints = polygon.getAttribute('points');
|
|
438
|
+
expect(newPoints).not.toBe(initialPoints);
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
it('updates when angle changes', () => {
|
|
442
|
+
const { container, rerender } = renderComponent({ x: 5, y: 10, angle: 0 });
|
|
443
|
+
let polygon = container.querySelector('polygon');
|
|
444
|
+
const initialTransform = polygon.getAttribute('transform');
|
|
445
|
+
|
|
446
|
+
rerender(<Arrow x={5} y={10} angle={90} graphProps={graphProps()} classes={{}} className="arrow-class" />);
|
|
447
|
+
|
|
448
|
+
polygon = container.querySelector('polygon');
|
|
449
|
+
const newTransform = polygon.getAttribute('transform');
|
|
450
|
+
expect(newTransform).not.toBe(initialTransform);
|
|
451
|
+
expect(newTransform).toContain('rotate(-90');
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
it('updates when thinnerShapesNeeded result changes', () => {
|
|
455
|
+
thinnerShapesNeeded.mockReturnValue(false);
|
|
456
|
+
const { container, rerender } = renderComponent({ x: 0, y: 0, angle: 0 });
|
|
457
|
+
let polygon = container.querySelector('polygon');
|
|
458
|
+
const initialPoints = polygon.getAttribute('points');
|
|
459
|
+
|
|
460
|
+
thinnerShapesNeeded.mockReturnValue(true);
|
|
461
|
+
rerender(<Arrow x={0} y={0} angle={0} graphProps={graphProps()} classes={{}} className="arrow-class" />);
|
|
462
|
+
|
|
463
|
+
polygon = container.querySelector('polygon');
|
|
464
|
+
const newPoints = polygon.getAttribute('points');
|
|
465
|
+
// Points should change due to size change (14 vs 12)
|
|
466
|
+
expect(newPoints).not.toBe(initialPoints);
|
|
467
|
+
});
|
|
468
|
+
});
|
|
469
|
+
});
|
|
@@ -2,8 +2,8 @@ import React from 'react';
|
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import classNames from 'classnames';
|
|
4
4
|
import { types } from '@pie-lib/plot';
|
|
5
|
-
import isEqual from 'lodash
|
|
6
|
-
import {
|
|
5
|
+
import { isEqual } from 'lodash-es';
|
|
6
|
+
import { arrowDimensions, getAngleDeg } from '../../../utils';
|
|
7
7
|
|
|
8
8
|
export class RawArrow extends React.Component {
|
|
9
9
|
static propTypes = {
|
|
@@ -6,7 +6,7 @@ import ReactDOM from 'react-dom';
|
|
|
6
6
|
import { thinnerShapesNeeded } from '../../../utils';
|
|
7
7
|
import { color } from '@pie-lib/render-ui';
|
|
8
8
|
import { styled } from '@mui/material/styles';
|
|
9
|
-
import { disabled as disabledStyle,
|
|
9
|
+
import { correct, disabled as disabledStyle, incorrect, missing } from '../styles';
|
|
10
10
|
|
|
11
11
|
const StyledPointGroup = styled('g')(({ disabled, correctness }) => ({
|
|
12
12
|
cursor: 'pointer',
|
|
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
|
|
|
3
3
|
import { styled } from '@mui/material/styles';
|
|
4
4
|
import { gridDraggable } from '@pie-lib/plot';
|
|
5
5
|
import * as utils from '../../../utils';
|
|
6
|
-
import {
|
|
6
|
+
import { correct, disabled, incorrect, missing } from '../styles';
|
|
7
7
|
import { RawBp } from './base-point';
|
|
8
8
|
import { RawArrow } from './arrow-point';
|
|
9
9
|
import { color } from '@pie-lib/render-ui';
|
package/src/undo-redo.jsx
CHANGED
|
@@ -28,9 +28,7 @@ export class UndoRedo extends React.Component {
|
|
|
28
28
|
const { className, onReset = false, language } = this.props;
|
|
29
29
|
return (
|
|
30
30
|
<div className={className}>
|
|
31
|
-
<StyledButton onClick={() => onReset()}>
|
|
32
|
-
{translator.t('graphing.reset', { lng: language })}
|
|
33
|
-
</StyledButton>
|
|
31
|
+
<StyledButton onClick={() => onReset()}>{translator.t('graphing.reset', { lng: language })}</StyledButton>
|
|
34
32
|
</div>
|
|
35
33
|
);
|
|
36
34
|
}
|
package/src/use-debounce.js
CHANGED
package/src/utils.js
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
import head from 'lodash
|
|
2
|
-
import tail from 'lodash/tail';
|
|
1
|
+
import { cloneDeep, head, isEmpty, isEqual, tail } from 'lodash-es';
|
|
3
2
|
import { utils } from '@pie-lib/plot';
|
|
4
3
|
import invariant from 'invariant';
|
|
5
|
-
import isEqual from 'lodash/isEqual';
|
|
6
|
-
import isEmpty from 'lodash/isEmpty';
|
|
7
|
-
import cloneDeep from 'lodash/cloneDeep';
|
|
8
4
|
|
|
9
5
|
export const bounds = utils.bounds;
|
|
10
6
|
export const point = utils.point;
|
package/NEXT.CHANGELOG.json
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
[
|
|
2
|
-
{
|
|
3
|
-
"type": "fix",
|
|
4
|
-
"scope": null,
|
|
5
|
-
"subject": "solve conflicts with dev",
|
|
6
|
-
"merge": null,
|
|
7
|
-
"header": "fix: solve conflicts with dev",
|
|
8
|
-
"body": null,
|
|
9
|
-
"footer": null,
|
|
10
|
-
"notes": [],
|
|
11
|
-
"hash": "50d83c31e1e4d5e719b714ff4bd7aafa797db6d2",
|
|
12
|
-
"gitTags": "",
|
|
13
|
-
"committerDate": "2026-01-28 12:46:38 +0200",
|
|
14
|
-
"isTagged": false
|
|
15
|
-
}
|
|
16
|
-
]
|