@pie-lib/plot 4.1.0-beta.8 → 4.1.0-beta.9
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/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "4.1.0-beta.
|
|
6
|
+
"version": "4.1.0-beta.9",
|
|
7
7
|
"description": "Some underlying components for building charts/graphs",
|
|
8
8
|
"keywords": [
|
|
9
9
|
"react",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"@mapbox/point-geometry": "^1.1.0",
|
|
22
22
|
"@mui/icons-material": "^7.3.4",
|
|
23
23
|
"@mui/material": "^7.3.4",
|
|
24
|
-
"@pie-lib/editable-html-tip-tap": "^2.2.0-beta.
|
|
24
|
+
"@pie-lib/editable-html-tip-tap": "^2.2.0-beta.9",
|
|
25
25
|
"@pie-lib/render-ui": "^6.1.0",
|
|
26
26
|
"assert": "^1.4.1",
|
|
27
27
|
"d3-scale": "^4.0.2",
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { render } from '@testing-library/react';
|
|
3
|
-
import Draggable from '../draggable';
|
|
4
|
-
|
|
5
|
-
describe('draggable', () => {
|
|
6
|
-
it('renders with children', () => {
|
|
7
|
-
const { container } = render(
|
|
8
|
-
<Draggable>
|
|
9
|
-
<div>hellow</div>
|
|
10
|
-
</Draggable>,
|
|
11
|
-
);
|
|
12
|
-
expect(container.firstChild).toBeInTheDocument();
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
describe('local', () => {
|
|
16
|
-
it('resets position state when receiving new props', () => {
|
|
17
|
-
// Render with initial props
|
|
18
|
-
const { rerender, container } = render(
|
|
19
|
-
<Draggable position={{ x: 100, y: 100 }}>
|
|
20
|
-
<div data-testid="draggable-child">content</div>
|
|
21
|
-
</Draggable>,
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
// Verify initial render
|
|
25
|
-
expect(container.firstChild).toBeInTheDocument();
|
|
26
|
-
|
|
27
|
-
// Update props - this triggers componentWillReceiveProps
|
|
28
|
-
// which should reset internal x/y state to 0
|
|
29
|
-
rerender(
|
|
30
|
-
<Draggable position={{ x: 200, y: 200 }}>
|
|
31
|
-
<div data-testid="draggable-child">content</div>
|
|
32
|
-
</Draggable>,
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
// The component should still render correctly after prop change
|
|
36
|
-
// The internal state reset is tested by ensuring no errors occur
|
|
37
|
-
// and the component continues to function properly
|
|
38
|
-
expect(container.firstChild).toBeInTheDocument();
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
});
|
|
@@ -1,487 +0,0 @@
|
|
|
1
|
-
import { render } from '@testing-library/react';
|
|
2
|
-
import React from 'react';
|
|
3
|
-
import { gridDraggable } from '../grid-draggable';
|
|
4
|
-
import { getDelta } from '../utils';
|
|
5
|
-
import { pointer } from 'd3-selection';
|
|
6
|
-
|
|
7
|
-
jest.mock('d3-selection', () => ({
|
|
8
|
-
pointer: jest.fn().mockReturnValue([0, 0]),
|
|
9
|
-
}));
|
|
10
|
-
|
|
11
|
-
let mockDraggableCoreProps;
|
|
12
|
-
jest.mock('../draggable', () => ({
|
|
13
|
-
DraggableCore: (props) => {
|
|
14
|
-
// Store the props so tests can call the handlers
|
|
15
|
-
mockDraggableCoreProps = props;
|
|
16
|
-
return props.children;
|
|
17
|
-
},
|
|
18
|
-
}));
|
|
19
|
-
|
|
20
|
-
// Reusable mock start event with a target that supports addEventListener/removeEventListener
|
|
21
|
-
const mockStartEvent = (overrides = {}) => ({
|
|
22
|
-
clientX: 0,
|
|
23
|
-
clientY: 0,
|
|
24
|
-
target: { addEventListener: jest.fn(), removeEventListener: jest.fn() },
|
|
25
|
-
...overrides,
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
jest.mock('../utils', () => ({
|
|
29
|
-
getDelta: jest.fn(),
|
|
30
|
-
}));
|
|
31
|
-
|
|
32
|
-
const xyFn = () => {
|
|
33
|
-
const out = jest.fn((n) => n);
|
|
34
|
-
out.invert = jest.fn((n) => n);
|
|
35
|
-
return out;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
const getGraphProps = () => ({
|
|
39
|
-
scale: {
|
|
40
|
-
x: xyFn(),
|
|
41
|
-
y: xyFn(),
|
|
42
|
-
},
|
|
43
|
-
snap: {
|
|
44
|
-
x: xyFn(),
|
|
45
|
-
y: xyFn(),
|
|
46
|
-
},
|
|
47
|
-
domain: {
|
|
48
|
-
min: 0,
|
|
49
|
-
max: 1,
|
|
50
|
-
step: 1,
|
|
51
|
-
},
|
|
52
|
-
range: {
|
|
53
|
-
min: 0,
|
|
54
|
-
max: 1,
|
|
55
|
-
step: 1,
|
|
56
|
-
},
|
|
57
|
-
size: {
|
|
58
|
-
width: 500,
|
|
59
|
-
height: 500,
|
|
60
|
-
},
|
|
61
|
-
getRootNode: () => ({
|
|
62
|
-
ownerSVGElement: null,
|
|
63
|
-
getBoundingClientRect: () => ({
|
|
64
|
-
left: 0,
|
|
65
|
-
top: 0,
|
|
66
|
-
right: 100,
|
|
67
|
-
bottom: 100,
|
|
68
|
-
}),
|
|
69
|
-
}),
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
describe('gridDraggable', () => {
|
|
73
|
-
let defaultOptions;
|
|
74
|
-
let defaultProps;
|
|
75
|
-
|
|
76
|
-
beforeEach(() => {
|
|
77
|
-
mockDraggableCoreProps = null;
|
|
78
|
-
defaultOptions = {
|
|
79
|
-
anchorPoint: jest.fn().mockReturnValue({ x: 0, y: 0 }),
|
|
80
|
-
bounds: jest.fn().mockReturnValue({ left: 0, top: 0, bottom: 0, right: 0 }),
|
|
81
|
-
fromDelta: jest.fn(),
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
defaultProps = {
|
|
85
|
-
graphProps: getGraphProps(),
|
|
86
|
-
};
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it('renders regular component', () => {
|
|
90
|
-
const Comp = gridDraggable(defaultOptions)(() => <div>Test</div>);
|
|
91
|
-
const { container } = render(<Comp {...defaultProps} />);
|
|
92
|
-
expect(container.firstChild).toBeInTheDocument();
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it('renders with decimal domain and range', () => {
|
|
96
|
-
const props = {
|
|
97
|
-
...defaultProps,
|
|
98
|
-
graphProps: {
|
|
99
|
-
...getGraphProps(),
|
|
100
|
-
domain: {
|
|
101
|
-
min: -1.5,
|
|
102
|
-
max: 1.6,
|
|
103
|
-
step: 0.3,
|
|
104
|
-
},
|
|
105
|
-
range: {
|
|
106
|
-
min: -2,
|
|
107
|
-
max: 3,
|
|
108
|
-
step: 0.2,
|
|
109
|
-
},
|
|
110
|
-
},
|
|
111
|
-
};
|
|
112
|
-
const Comp = gridDraggable(defaultOptions)(() => <div>Test</div>);
|
|
113
|
-
const { container } = render(<Comp {...props} />);
|
|
114
|
-
expect(container.firstChild).toBeInTheDocument();
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
describe('logic', () => {
|
|
118
|
-
describe('grid calculation', () => {
|
|
119
|
-
it('passes correct grid to DraggableCore based on domain and range step', () => {
|
|
120
|
-
const Comp = gridDraggable(defaultOptions)(() => <div>Test</div>);
|
|
121
|
-
render(<Comp {...defaultProps} />);
|
|
122
|
-
|
|
123
|
-
// Grid is calculated as: scale.x(domain.step) - scale.x(0)
|
|
124
|
-
// With our mock xyFn that returns n, this is: step - 0 = step
|
|
125
|
-
expect(mockDraggableCoreProps.grid).toEqual([1, 1]);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it('calculates grid with decimal steps', () => {
|
|
129
|
-
const props = {
|
|
130
|
-
...defaultProps,
|
|
131
|
-
graphProps: {
|
|
132
|
-
...getGraphProps(),
|
|
133
|
-
domain: { min: -1.5, max: 1.6, step: 0.3 },
|
|
134
|
-
range: { min: -2, max: 3, step: 0.2 },
|
|
135
|
-
},
|
|
136
|
-
};
|
|
137
|
-
const Comp = gridDraggable(defaultOptions)(() => <div>Test</div>);
|
|
138
|
-
render(<Comp {...props} />);
|
|
139
|
-
|
|
140
|
-
expect(mockDraggableCoreProps.grid).toEqual([0.3, 0.2]);
|
|
141
|
-
});
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
describe('onStart', () => {
|
|
145
|
-
it('calls onDragStart handler when drag starts', () => {
|
|
146
|
-
const onDragStart = jest.fn();
|
|
147
|
-
const props = { ...defaultProps, onDragStart };
|
|
148
|
-
const Comp = gridDraggable(defaultOptions)(() => <div>Test</div>);
|
|
149
|
-
render(<Comp {...props} />);
|
|
150
|
-
|
|
151
|
-
// Simulate drag start
|
|
152
|
-
mockDraggableCoreProps.onStart(mockStartEvent({ clientX: 100, clientY: 100 }));
|
|
153
|
-
|
|
154
|
-
expect(onDragStart).toHaveBeenCalled();
|
|
155
|
-
});
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
describe('onDrag', () => {
|
|
159
|
-
it('calls onDrag callback with result from fromDelta', () => {
|
|
160
|
-
const onDrag = jest.fn();
|
|
161
|
-
const fromDelta = jest.fn().mockReturnValue('delta-result');
|
|
162
|
-
const bounds = jest.fn().mockReturnValue({ left: -100, top: -100, bottom: 100, right: 100 });
|
|
163
|
-
const options = { ...defaultOptions, fromDelta, bounds };
|
|
164
|
-
const props = { ...defaultProps, onDrag };
|
|
165
|
-
|
|
166
|
-
const Comp = gridDraggable(options)(() => <div>Test</div>);
|
|
167
|
-
render(<Comp {...props} />);
|
|
168
|
-
|
|
169
|
-
// Set up drag start state
|
|
170
|
-
mockDraggableCoreProps.onStart(mockStartEvent());
|
|
171
|
-
|
|
172
|
-
// Simulate drag
|
|
173
|
-
mockDraggableCoreProps.onDrag({}, { deltaX: 10, deltaY: 10 });
|
|
174
|
-
|
|
175
|
-
expect(fromDelta).toHaveBeenCalled();
|
|
176
|
-
expect(onDrag).toHaveBeenCalledWith('delta-result');
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
it('does not call onDrag when no onDrag handler is provided', () => {
|
|
180
|
-
const fromDelta = jest.fn();
|
|
181
|
-
const options = { ...defaultOptions, fromDelta };
|
|
182
|
-
const Comp = gridDraggable(options)(() => <div>Test</div>);
|
|
183
|
-
render(<Comp {...defaultProps} />);
|
|
184
|
-
|
|
185
|
-
mockDraggableCoreProps.onStart(mockStartEvent());
|
|
186
|
-
mockDraggableCoreProps.onDrag({}, { deltaX: 10, deltaY: 10 });
|
|
187
|
-
|
|
188
|
-
expect(fromDelta).not.toHaveBeenCalled();
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
describe('bounds checking', () => {
|
|
192
|
-
it('does not call onDrag when dragging left beyond left bound', () => {
|
|
193
|
-
const onDrag = jest.fn();
|
|
194
|
-
const bounds = jest.fn().mockReturnValue({ left: 0, top: 0, bottom: 0, right: 0 });
|
|
195
|
-
const fromDelta = jest.fn().mockReturnValue('result');
|
|
196
|
-
const options = { ...defaultOptions, bounds, fromDelta };
|
|
197
|
-
const props = { ...defaultProps, onDrag };
|
|
198
|
-
|
|
199
|
-
const Comp = gridDraggable(options)(() => <div>Test</div>);
|
|
200
|
-
render(<Comp {...props} />);
|
|
201
|
-
|
|
202
|
-
mockDraggableCoreProps.onStart(mockStartEvent());
|
|
203
|
-
// deltaX < 0 and deltaX < scaled bounds.left (0), so -10 < 0 triggers early return
|
|
204
|
-
mockDraggableCoreProps.onDrag({}, { deltaX: -10, deltaY: 0 });
|
|
205
|
-
|
|
206
|
-
expect(onDrag).not.toHaveBeenCalled();
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
it('does not call onDrag when dragging right beyond right bound', () => {
|
|
210
|
-
const onDrag = jest.fn();
|
|
211
|
-
const bounds = jest.fn().mockReturnValue({ left: 0, top: 0, bottom: 0, right: 0 });
|
|
212
|
-
const fromDelta = jest.fn().mockReturnValue('result');
|
|
213
|
-
const options = { ...defaultOptions, bounds, fromDelta };
|
|
214
|
-
const props = { ...defaultProps, onDrag };
|
|
215
|
-
|
|
216
|
-
const Comp = gridDraggable(options)(() => <div>Test</div>);
|
|
217
|
-
render(<Comp {...props} />);
|
|
218
|
-
|
|
219
|
-
mockDraggableCoreProps.onStart(mockStartEvent());
|
|
220
|
-
// deltaX > 0 and deltaX > scaled bounds.right (0), so 10 > 0 triggers early return
|
|
221
|
-
mockDraggableCoreProps.onDrag({}, { deltaX: 10, deltaY: 0 });
|
|
222
|
-
|
|
223
|
-
expect(onDrag).not.toHaveBeenCalled();
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
it('does not call onDrag when dragging up beyond top bound', () => {
|
|
227
|
-
const onDrag = jest.fn();
|
|
228
|
-
const bounds = jest.fn().mockReturnValue({ left: -100, top: 0, bottom: 0, right: 100 });
|
|
229
|
-
const fromDelta = jest.fn().mockReturnValue('result');
|
|
230
|
-
const options = { ...defaultOptions, bounds, fromDelta };
|
|
231
|
-
const props = { ...defaultProps, onDrag };
|
|
232
|
-
|
|
233
|
-
const Comp = gridDraggable(options)(() => <div>Test</div>);
|
|
234
|
-
render(<Comp {...props} />);
|
|
235
|
-
|
|
236
|
-
mockDraggableCoreProps.onStart(mockStartEvent());
|
|
237
|
-
// deltaY < 0 and deltaY < scaled bounds.top (0), so -10 < 0 triggers early return
|
|
238
|
-
mockDraggableCoreProps.onDrag({}, { deltaX: 0, deltaY: -10 });
|
|
239
|
-
|
|
240
|
-
expect(onDrag).not.toHaveBeenCalled();
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
it('does not call onDrag when dragging down beyond bottom bound', () => {
|
|
244
|
-
const onDrag = jest.fn();
|
|
245
|
-
const bounds = jest.fn().mockReturnValue({ left: -100, top: -100, bottom: 0, right: 100 });
|
|
246
|
-
const fromDelta = jest.fn().mockReturnValue('result');
|
|
247
|
-
const options = { ...defaultOptions, bounds, fromDelta };
|
|
248
|
-
const props = { ...defaultProps, onDrag };
|
|
249
|
-
|
|
250
|
-
const Comp = gridDraggable(options)(() => <div>Test</div>);
|
|
251
|
-
render(<Comp {...props} />);
|
|
252
|
-
|
|
253
|
-
mockDraggableCoreProps.onStart(mockStartEvent());
|
|
254
|
-
// deltaY > 0 and deltaY > scaled bounds.bottom (0), so 10 > 0 triggers early return
|
|
255
|
-
mockDraggableCoreProps.onDrag({}, { deltaX: 0, deltaY: 10 });
|
|
256
|
-
|
|
257
|
-
expect(onDrag).not.toHaveBeenCalled();
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
it('calls onDrag when dragging within bounds', () => {
|
|
261
|
-
const onDrag = jest.fn();
|
|
262
|
-
const bounds = jest.fn().mockReturnValue({ left: -100, top: -100, bottom: 100, right: 100 });
|
|
263
|
-
const fromDelta = jest.fn().mockReturnValue('result');
|
|
264
|
-
const options = { ...defaultOptions, bounds, fromDelta };
|
|
265
|
-
const props = { ...defaultProps, onDrag };
|
|
266
|
-
|
|
267
|
-
const Comp = gridDraggable(options)(() => <div>Test</div>);
|
|
268
|
-
render(<Comp {...props} />);
|
|
269
|
-
|
|
270
|
-
mockDraggableCoreProps.onStart(mockStartEvent());
|
|
271
|
-
// All bound checks pass: deltaX (-10) is NOT < bounds.left (-100) and NOT > bounds.right (100)
|
|
272
|
-
// Similarly for deltaY
|
|
273
|
-
mockDraggableCoreProps.onDrag({}, { deltaX: -10, deltaY: 10 });
|
|
274
|
-
|
|
275
|
-
expect(onDrag).toHaveBeenCalled();
|
|
276
|
-
});
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
describe('skipDragOutsideOfBounds', () => {
|
|
280
|
-
it('skips drag when moving left and x is below domain.min', () => {
|
|
281
|
-
const onDrag = jest.fn();
|
|
282
|
-
const graphProps = {
|
|
283
|
-
...getGraphProps(),
|
|
284
|
-
domain: { min: -5, max: 5, step: 1 },
|
|
285
|
-
range: { min: -5, max: 5, step: 1 },
|
|
286
|
-
};
|
|
287
|
-
graphProps.scale.x.invert = jest.fn().mockReturnValue(-6); // Below min
|
|
288
|
-
|
|
289
|
-
const props = { ...defaultProps, graphProps, onDrag };
|
|
290
|
-
const bounds = jest.fn().mockReturnValue({ left: 100, top: 100, bottom: 100, right: 100 });
|
|
291
|
-
const options = { ...defaultOptions, bounds };
|
|
292
|
-
|
|
293
|
-
const Comp = gridDraggable(options)(() => <div>Test</div>);
|
|
294
|
-
render(<Comp {...props} />);
|
|
295
|
-
|
|
296
|
-
mockDraggableCoreProps.onStart(mockStartEvent());
|
|
297
|
-
mockDraggableCoreProps.onDrag({}, { deltaX: 1, deltaY: 0 });
|
|
298
|
-
|
|
299
|
-
expect(onDrag).not.toHaveBeenCalled();
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
it('skips drag when moving right and x is above domain.max', () => {
|
|
303
|
-
const onDrag = jest.fn();
|
|
304
|
-
const graphProps = {
|
|
305
|
-
...getGraphProps(),
|
|
306
|
-
domain: { min: -5, max: 5, step: 1 },
|
|
307
|
-
range: { min: -5, max: 5, step: 1 },
|
|
308
|
-
};
|
|
309
|
-
graphProps.scale.x.invert = jest.fn().mockReturnValue(6); // Above max
|
|
310
|
-
|
|
311
|
-
const props = { ...defaultProps, graphProps, onDrag };
|
|
312
|
-
const bounds = jest.fn().mockReturnValue({ left: 100, top: 100, bottom: 100, right: 100 });
|
|
313
|
-
const options = { ...defaultOptions, bounds };
|
|
314
|
-
|
|
315
|
-
const Comp = gridDraggable(options)(() => <div>Test</div>);
|
|
316
|
-
render(<Comp {...props} />);
|
|
317
|
-
|
|
318
|
-
mockDraggableCoreProps.onStart(mockStartEvent());
|
|
319
|
-
mockDraggableCoreProps.onDrag({}, { deltaX: -1, deltaY: 0 });
|
|
320
|
-
|
|
321
|
-
expect(onDrag).not.toHaveBeenCalled();
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
it('skips drag when moving up and y is above range.max', () => {
|
|
325
|
-
const onDrag = jest.fn();
|
|
326
|
-
const graphProps = {
|
|
327
|
-
...getGraphProps(),
|
|
328
|
-
domain: { min: -5, max: 5, step: 1 },
|
|
329
|
-
range: { min: -5, max: 5, step: 1 },
|
|
330
|
-
};
|
|
331
|
-
graphProps.scale.y.invert = jest.fn().mockReturnValue(6); // Above max
|
|
332
|
-
|
|
333
|
-
const props = { ...defaultProps, graphProps, onDrag };
|
|
334
|
-
const bounds = jest.fn().mockReturnValue({ left: 100, top: 100, bottom: 100, right: 100 });
|
|
335
|
-
const options = { ...defaultOptions, bounds };
|
|
336
|
-
|
|
337
|
-
const Comp = gridDraggable(options)(() => <div>Test</div>);
|
|
338
|
-
render(<Comp {...props} />);
|
|
339
|
-
|
|
340
|
-
mockDraggableCoreProps.onStart(mockStartEvent());
|
|
341
|
-
mockDraggableCoreProps.onDrag({}, { deltaX: 0, deltaY: 1 });
|
|
342
|
-
|
|
343
|
-
expect(onDrag).not.toHaveBeenCalled();
|
|
344
|
-
});
|
|
345
|
-
|
|
346
|
-
it('skips drag when moving down and y is below range.min', () => {
|
|
347
|
-
const onDrag = jest.fn();
|
|
348
|
-
const graphProps = {
|
|
349
|
-
...getGraphProps(),
|
|
350
|
-
domain: { min: -5, max: 5, step: 1 },
|
|
351
|
-
range: { min: -5, max: 5, step: 1 },
|
|
352
|
-
};
|
|
353
|
-
graphProps.scale.y.invert = jest.fn().mockReturnValue(-6); // Below min
|
|
354
|
-
|
|
355
|
-
const props = { ...defaultProps, graphProps, onDrag };
|
|
356
|
-
const bounds = jest.fn().mockReturnValue({ left: 100, top: 100, bottom: 100, right: 100 });
|
|
357
|
-
const options = { ...defaultOptions, bounds };
|
|
358
|
-
|
|
359
|
-
const Comp = gridDraggable(options)(() => <div>Test</div>);
|
|
360
|
-
render(<Comp {...props} />);
|
|
361
|
-
|
|
362
|
-
mockDraggableCoreProps.onStart(mockStartEvent());
|
|
363
|
-
mockDraggableCoreProps.onDrag({}, { deltaX: 0, deltaY: -1 });
|
|
364
|
-
|
|
365
|
-
expect(onDrag).not.toHaveBeenCalled();
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
it('allows drag when within domain and range', () => {
|
|
369
|
-
const onDrag = jest.fn();
|
|
370
|
-
const graphProps = {
|
|
371
|
-
...getGraphProps(),
|
|
372
|
-
domain: { min: -5, max: 5, step: 1 },
|
|
373
|
-
range: { min: -5, max: 5, step: 1 },
|
|
374
|
-
};
|
|
375
|
-
graphProps.scale.x.invert = jest.fn().mockReturnValue(3); // Within bounds
|
|
376
|
-
graphProps.scale.y.invert = jest.fn().mockReturnValue(2); // Within bounds
|
|
377
|
-
|
|
378
|
-
const props = { ...defaultProps, graphProps, onDrag };
|
|
379
|
-
const bounds = jest.fn().mockReturnValue({ left: -100, top: -100, bottom: 100, right: 100 });
|
|
380
|
-
const options = { ...defaultOptions, bounds };
|
|
381
|
-
|
|
382
|
-
const Comp = gridDraggable(options)(() => <div>Test</div>);
|
|
383
|
-
render(<Comp {...props} />);
|
|
384
|
-
|
|
385
|
-
mockDraggableCoreProps.onStart(mockStartEvent());
|
|
386
|
-
mockDraggableCoreProps.onDrag({}, { deltaX: 1, deltaY: -1 });
|
|
387
|
-
|
|
388
|
-
expect(onDrag).toHaveBeenCalled();
|
|
389
|
-
});
|
|
390
|
-
});
|
|
391
|
-
});
|
|
392
|
-
|
|
393
|
-
describe('getDelta and applyDelta', () => {
|
|
394
|
-
it('calls utils.getDelta when processing drag', () => {
|
|
395
|
-
getDelta.mockClear();
|
|
396
|
-
const onDrag = jest.fn();
|
|
397
|
-
const props = { ...defaultProps, onDrag };
|
|
398
|
-
const bounds = jest.fn().mockReturnValue({ left: -100, top: -100, bottom: 100, right: 100 });
|
|
399
|
-
const options = { ...defaultOptions, bounds };
|
|
400
|
-
|
|
401
|
-
const Comp = gridDraggable(options)(() => <div>Test</div>);
|
|
402
|
-
render(<Comp {...props} />);
|
|
403
|
-
|
|
404
|
-
mockDraggableCoreProps.onStart(mockStartEvent());
|
|
405
|
-
mockDraggableCoreProps.onDrag({}, { deltaX: 10, deltaY: 10 });
|
|
406
|
-
|
|
407
|
-
expect(getDelta).toHaveBeenCalled();
|
|
408
|
-
});
|
|
409
|
-
|
|
410
|
-
it('calls fromDelta with result from getDelta', () => {
|
|
411
|
-
const fromDelta = jest.fn();
|
|
412
|
-
getDelta.mockReturnValue({ x: 5, y: 5 });
|
|
413
|
-
const bounds = jest.fn().mockReturnValue({ left: -100, top: -100, bottom: 100, right: 100 });
|
|
414
|
-
const options = { ...defaultOptions, fromDelta, bounds };
|
|
415
|
-
const props = { ...defaultProps, onDrag: jest.fn() };
|
|
416
|
-
|
|
417
|
-
const Comp = gridDraggable(options)(() => <div>Test</div>);
|
|
418
|
-
render(<Comp {...props} />);
|
|
419
|
-
|
|
420
|
-
mockDraggableCoreProps.onStart(mockStartEvent());
|
|
421
|
-
mockDraggableCoreProps.onDrag({}, { deltaX: 10, deltaY: 10 });
|
|
422
|
-
|
|
423
|
-
expect(fromDelta).toHaveBeenCalled();
|
|
424
|
-
});
|
|
425
|
-
});
|
|
426
|
-
|
|
427
|
-
describe('onStop', () => {
|
|
428
|
-
it('calls onDragStop when drag stops', () => {
|
|
429
|
-
const onDragStop = jest.fn();
|
|
430
|
-
const props = { ...defaultProps, onDragStop };
|
|
431
|
-
|
|
432
|
-
const Comp = gridDraggable(defaultOptions)(() => <div>Test</div>);
|
|
433
|
-
render(<Comp {...props} />);
|
|
434
|
-
|
|
435
|
-
// Start to set up state
|
|
436
|
-
mockDraggableCoreProps.onStart(mockStartEvent());
|
|
437
|
-
|
|
438
|
-
// Stop with large movement (not tiny)
|
|
439
|
-
mockDraggableCoreProps.onStop({ clientX: 100, clientY: 100, stopPropagation: jest.fn() }, {});
|
|
440
|
-
|
|
441
|
-
expect(onDragStop).toHaveBeenCalled();
|
|
442
|
-
});
|
|
443
|
-
|
|
444
|
-
it('calls onClick instead of onDragStop when movement is tiny', () => {
|
|
445
|
-
const onClick = jest.fn();
|
|
446
|
-
const onDragStop = jest.fn();
|
|
447
|
-
const props = { ...defaultProps, onClick, onDragStop };
|
|
448
|
-
|
|
449
|
-
pointer.mockReturnValue([0, 0]);
|
|
450
|
-
|
|
451
|
-
const Comp = gridDraggable(defaultOptions)(() => <div>Test</div>);
|
|
452
|
-
render(<Comp {...props} />);
|
|
453
|
-
|
|
454
|
-
// Start and stop at almost the same position (tiny movement)
|
|
455
|
-
// Grid is 1x1, tiny threshold is grid/10 = 0.1
|
|
456
|
-
mockDraggableCoreProps.onStart(mockStartEvent());
|
|
457
|
-
mockDraggableCoreProps.onStop({ clientX: 0.05, clientY: 0.05, target: {}, stopPropagation: jest.fn() }, {});
|
|
458
|
-
|
|
459
|
-
expect(onClick).toHaveBeenCalledWith({ x: 0, y: 0 });
|
|
460
|
-
});
|
|
461
|
-
|
|
462
|
-
it('calls onClick with snapped coordinates', () => {
|
|
463
|
-
const onClick = jest.fn();
|
|
464
|
-
const props = { ...defaultProps, onClick };
|
|
465
|
-
const graphProps = getGraphProps();
|
|
466
|
-
graphProps.scale.x.invert = jest.fn().mockReturnValue(1.7);
|
|
467
|
-
graphProps.scale.y.invert = jest.fn().mockReturnValue(2.3);
|
|
468
|
-
graphProps.snap.x = jest.fn().mockReturnValue(2);
|
|
469
|
-
graphProps.snap.y = jest.fn().mockReturnValue(2);
|
|
470
|
-
|
|
471
|
-
pointer.mockReturnValue([1.7, 2.3]);
|
|
472
|
-
|
|
473
|
-
const propsWithGraphProps = { ...props, graphProps };
|
|
474
|
-
const Comp = gridDraggable(defaultOptions)(() => <div>Test</div>);
|
|
475
|
-
render(<Comp {...propsWithGraphProps} />);
|
|
476
|
-
|
|
477
|
-
// Start and stop at almost the same position (tiny movement)
|
|
478
|
-
mockDraggableCoreProps.onStart(mockStartEvent());
|
|
479
|
-
mockDraggableCoreProps.onStop({ clientX: 0.05, clientY: 0.05, target: {}, stopPropagation: jest.fn() }, {});
|
|
480
|
-
|
|
481
|
-
expect(graphProps.snap.x).toHaveBeenCalledWith(1.7);
|
|
482
|
-
expect(graphProps.snap.y).toHaveBeenCalledWith(2.3);
|
|
483
|
-
expect(onClick).toHaveBeenCalledWith({ x: 2, y: 2 });
|
|
484
|
-
});
|
|
485
|
-
});
|
|
486
|
-
});
|
|
487
|
-
});
|
|
@@ -1,277 +0,0 @@
|
|
|
1
|
-
import { cleanup, render } from '@testing-library/react';
|
|
2
|
-
import React from 'react';
|
|
3
|
-
import { Root } from '../root';
|
|
4
|
-
import { pointer, select } from 'd3-selection';
|
|
5
|
-
|
|
6
|
-
jest.mock('d3-selection', () => ({
|
|
7
|
-
select: jest.fn(),
|
|
8
|
-
pointer: jest.fn(),
|
|
9
|
-
}));
|
|
10
|
-
|
|
11
|
-
const scaleMock = () => {
|
|
12
|
-
const fn = jest.fn((n) => n);
|
|
13
|
-
fn.invert = jest.fn((n) => n);
|
|
14
|
-
return fn;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
const graphProps = () => ({
|
|
18
|
-
scale: {
|
|
19
|
-
x: scaleMock(),
|
|
20
|
-
y: scaleMock(),
|
|
21
|
-
},
|
|
22
|
-
snap: {
|
|
23
|
-
x: jest.fn((n) => n),
|
|
24
|
-
y: jest.fn((n) => n),
|
|
25
|
-
},
|
|
26
|
-
domain: {
|
|
27
|
-
min: 0,
|
|
28
|
-
max: 1,
|
|
29
|
-
step: 1,
|
|
30
|
-
},
|
|
31
|
-
range: {
|
|
32
|
-
min: 0,
|
|
33
|
-
max: 1,
|
|
34
|
-
step: 1,
|
|
35
|
-
},
|
|
36
|
-
size: {
|
|
37
|
-
width: 400,
|
|
38
|
-
height: 400,
|
|
39
|
-
},
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
describe('root', () => {
|
|
43
|
-
let mockOn;
|
|
44
|
-
let defaultProps;
|
|
45
|
-
|
|
46
|
-
beforeEach(() => {
|
|
47
|
-
mockOn = jest.fn();
|
|
48
|
-
select.mockReturnValue({
|
|
49
|
-
on: mockOn,
|
|
50
|
-
});
|
|
51
|
-
pointer.mockReturnValue([0, 0]);
|
|
52
|
-
|
|
53
|
-
defaultProps = {
|
|
54
|
-
classes: {},
|
|
55
|
-
graphProps: graphProps(),
|
|
56
|
-
};
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
afterEach(() => {
|
|
60
|
-
cleanup();
|
|
61
|
-
jest.clearAllMocks();
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it('renders with children', () => {
|
|
65
|
-
const { container, getByText } = render(<Root {...defaultProps}>hi</Root>);
|
|
66
|
-
expect(container.firstChild).toBeInTheDocument();
|
|
67
|
-
expect(getByText('hi')).toBeInTheDocument();
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
describe('logic', () => {
|
|
71
|
-
describe('mousemove', () => {
|
|
72
|
-
describe('mount/unmount', () => {
|
|
73
|
-
it('adds mousemove listener on componentDidMount', () => {
|
|
74
|
-
render(<Root {...defaultProps}>hi</Root>);
|
|
75
|
-
|
|
76
|
-
// Verify that select was called with the g element
|
|
77
|
-
expect(select).toHaveBeenCalled();
|
|
78
|
-
|
|
79
|
-
// Verify that on() was called with 'mousemove' and a function
|
|
80
|
-
expect(mockOn).toHaveBeenCalledWith('mousemove', expect.any(Function));
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('removes mousemove listener on componentWillUnmount', () => {
|
|
84
|
-
const { unmount } = render(<Root {...defaultProps}>hi</Root>);
|
|
85
|
-
|
|
86
|
-
// Clear previous calls to isolate unmount behavior
|
|
87
|
-
mockOn.mockClear();
|
|
88
|
-
select.mockClear();
|
|
89
|
-
|
|
90
|
-
unmount();
|
|
91
|
-
|
|
92
|
-
// Verify that select was called during unmount
|
|
93
|
-
expect(select).toHaveBeenCalled();
|
|
94
|
-
|
|
95
|
-
// Verify that on() was called with 'mousemove' and null to remove the listener
|
|
96
|
-
expect(mockOn).toHaveBeenCalledWith('mousemove', null);
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
describe('mouseMove function', () => {
|
|
101
|
-
it('calls pointer with correct arguments', () => {
|
|
102
|
-
const onMouseMove = jest.fn();
|
|
103
|
-
const gp = graphProps();
|
|
104
|
-
const props = {
|
|
105
|
-
...defaultProps,
|
|
106
|
-
onMouseMove,
|
|
107
|
-
graphProps: gp,
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
const mockNode = document.createElement('div');
|
|
111
|
-
const mockEvent = { clientX: 10, clientY: 20 };
|
|
112
|
-
const mockSelection = {
|
|
113
|
-
_groups: [[mockNode]],
|
|
114
|
-
node: () => mockNode,
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
// Mock select to return our mockSelection
|
|
118
|
-
select.mockReturnValue({
|
|
119
|
-
...mockSelection,
|
|
120
|
-
on: (event, handler) => {
|
|
121
|
-
mockOn(event, handler);
|
|
122
|
-
// When 'mousemove' is registered, immediately test it
|
|
123
|
-
if (event === 'mousemove' && handler) {
|
|
124
|
-
pointer.mockReturnValue([10, 20]);
|
|
125
|
-
// Handler is bound with mockSelection as first arg, so call with event
|
|
126
|
-
handler(mockEvent);
|
|
127
|
-
}
|
|
128
|
-
},
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
render(<Root {...props}>hi</Root>);
|
|
132
|
-
|
|
133
|
-
// Verify pointer was called with the event and correct node
|
|
134
|
-
expect(pointer).toHaveBeenCalledWith(mockEvent, mockNode);
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
it('calls scale.x.invert and scale.y.invert', () => {
|
|
138
|
-
const onMouseMove = jest.fn();
|
|
139
|
-
const gp = graphProps();
|
|
140
|
-
const props = {
|
|
141
|
-
...defaultProps,
|
|
142
|
-
onMouseMove,
|
|
143
|
-
graphProps: gp,
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
const mockNode = document.createElement('div');
|
|
147
|
-
const mockEvent = { clientX: 15, clientY: 25 };
|
|
148
|
-
const mockSelection = {
|
|
149
|
-
_groups: [[mockNode]],
|
|
150
|
-
node: () => mockNode,
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
select.mockReturnValue({
|
|
154
|
-
...mockSelection,
|
|
155
|
-
on: (event, handler) => {
|
|
156
|
-
mockOn(event, handler);
|
|
157
|
-
if (event === 'mousemove' && handler) {
|
|
158
|
-
pointer.mockReturnValue([15, 25]);
|
|
159
|
-
handler(mockEvent);
|
|
160
|
-
}
|
|
161
|
-
},
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
render(<Root {...props}>hi</Root>);
|
|
165
|
-
|
|
166
|
-
expect(gp.scale.x.invert).toHaveBeenCalledWith(15);
|
|
167
|
-
expect(gp.scale.y.invert).toHaveBeenCalledWith(25);
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
it('calls snap.x and snap.y with inverted coordinates', () => {
|
|
171
|
-
const onMouseMove = jest.fn();
|
|
172
|
-
const gp = graphProps();
|
|
173
|
-
gp.scale.x.invert = jest.fn().mockReturnValue(5);
|
|
174
|
-
gp.scale.y.invert = jest.fn().mockReturnValue(10);
|
|
175
|
-
const props = {
|
|
176
|
-
...defaultProps,
|
|
177
|
-
onMouseMove,
|
|
178
|
-
graphProps: gp,
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
const mockNode = document.createElement('div');
|
|
182
|
-
const mockEvent = { clientX: 100, clientY: 200 };
|
|
183
|
-
const mockSelection = {
|
|
184
|
-
_groups: [[mockNode]],
|
|
185
|
-
node: () => mockNode,
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
select.mockReturnValue({
|
|
189
|
-
...mockSelection,
|
|
190
|
-
on: (event, handler) => {
|
|
191
|
-
mockOn(event, handler);
|
|
192
|
-
if (event === 'mousemove' && handler) {
|
|
193
|
-
pointer.mockReturnValue([100, 200]);
|
|
194
|
-
handler();
|
|
195
|
-
}
|
|
196
|
-
},
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
render(<Root {...props}>hi</Root>);
|
|
200
|
-
|
|
201
|
-
expect(gp.snap.x).toHaveBeenCalledWith(5);
|
|
202
|
-
expect(gp.snap.y).toHaveBeenCalledWith(10);
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
it('calls onMouseMove handler with snapped coordinates', () => {
|
|
206
|
-
const onMouseMove = jest.fn();
|
|
207
|
-
const gp = graphProps();
|
|
208
|
-
gp.scale.x.invert = jest.fn().mockReturnValue(7);
|
|
209
|
-
gp.scale.y.invert = jest.fn().mockReturnValue(14);
|
|
210
|
-
gp.snap.x = jest.fn().mockReturnValue(10);
|
|
211
|
-
gp.snap.y = jest.fn().mockReturnValue(15);
|
|
212
|
-
|
|
213
|
-
const props = {
|
|
214
|
-
...defaultProps,
|
|
215
|
-
onMouseMove,
|
|
216
|
-
graphProps: gp,
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
const mockNode = document.createElement('div');
|
|
220
|
-
const mockEvent = { clientX: 100, clientY: 200 };
|
|
221
|
-
const mockSelection = {
|
|
222
|
-
_groups: [[mockNode]],
|
|
223
|
-
node: () => mockNode,
|
|
224
|
-
};
|
|
225
|
-
|
|
226
|
-
select.mockReturnValue({
|
|
227
|
-
...mockSelection,
|
|
228
|
-
on: (event, handler) => {
|
|
229
|
-
mockOn(event, handler);
|
|
230
|
-
if (event === 'mousemove' && handler) {
|
|
231
|
-
pointer.mockReturnValue([100, 200]);
|
|
232
|
-
handler(mockEvent);
|
|
233
|
-
}
|
|
234
|
-
},
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
render(<Root {...props}>hi</Root>);
|
|
238
|
-
|
|
239
|
-
expect(onMouseMove).toHaveBeenCalledWith({ x: 10, y: 15 });
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
it('does not call onMouseMove when handler is not provided', () => {
|
|
243
|
-
const gp = graphProps();
|
|
244
|
-
const props = {
|
|
245
|
-
...defaultProps,
|
|
246
|
-
graphProps: gp,
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
const mockNode = document.createElement('div');
|
|
250
|
-
const mockEvent = { clientX: 100, clientY: 200 };
|
|
251
|
-
const mockSelection = {
|
|
252
|
-
_groups: [[mockNode]],
|
|
253
|
-
node: () => mockNode,
|
|
254
|
-
};
|
|
255
|
-
|
|
256
|
-
select.mockReturnValue({
|
|
257
|
-
...mockSelection,
|
|
258
|
-
on: (event, handler) => {
|
|
259
|
-
mockOn(event, handler);
|
|
260
|
-
if (event === 'mousemove' && handler) {
|
|
261
|
-
pointer.mockReturnValue([100, 200]);
|
|
262
|
-
// Should not throw error when onMouseMove is not provided
|
|
263
|
-
expect(() => handler(mockEvent)).not.toThrow();
|
|
264
|
-
}
|
|
265
|
-
},
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
render(<Root {...props}>hi</Root>);
|
|
269
|
-
|
|
270
|
-
// Verify scale methods were not called (early return in mouseMove)
|
|
271
|
-
expect(gp.scale.x.invert).not.toHaveBeenCalled();
|
|
272
|
-
expect(gp.scale.y.invert).not.toHaveBeenCalled();
|
|
273
|
-
});
|
|
274
|
-
});
|
|
275
|
-
});
|
|
276
|
-
});
|
|
277
|
-
});
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
import { acuteXAngle, acuteYAngle, angle, diffEdge, edges, getOpposingSide, toDegrees, toRadians } from '../trig';
|
|
2
|
-
import { xy } from '../utils';
|
|
3
|
-
import debug from 'debug';
|
|
4
|
-
|
|
5
|
-
const log = debug('pie-lib:plot:trig:test');
|
|
6
|
-
|
|
7
|
-
const vs = (v) => {
|
|
8
|
-
if (!v) {
|
|
9
|
-
return '';
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
if (Number.isFinite(v.x) && Number.isFinite(v.y)) {
|
|
13
|
-
return `[${v.x},${v.y}]`;
|
|
14
|
-
}
|
|
15
|
-
return JSON.stringify(v);
|
|
16
|
-
};
|
|
17
|
-
const p = (strings, ...values) => {
|
|
18
|
-
return strings.reduce((acc, s, index) => {
|
|
19
|
-
return `${acc}${s}${vs(values[index])}`;
|
|
20
|
-
}, '');
|
|
21
|
-
};
|
|
22
|
-
describe('trig', () => {
|
|
23
|
-
describe('angle', () => {
|
|
24
|
-
const assertAngle = (a, b, expected) => {
|
|
25
|
-
it(p`${a}, ${b} => ${toDegrees(expected)}`, () => {
|
|
26
|
-
const result = angle(a, b);
|
|
27
|
-
expect(result).toBeCloseTo(expected);
|
|
28
|
-
});
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
assertAngle(xy(0, 0), xy(1, 1), toRadians(45));
|
|
32
|
-
assertAngle(xy(0, 0), xy(0, 1), toRadians(90));
|
|
33
|
-
assertAngle(xy(0, 0), xy(-1, 1), toRadians(135));
|
|
34
|
-
assertAngle(xy(0, 0), xy(-1, 0), toRadians(180));
|
|
35
|
-
assertAngle(xy(0, 0), xy(-1, -1), toRadians(225));
|
|
36
|
-
assertAngle(xy(0, 0), xy(0, -1), toRadians(270));
|
|
37
|
-
assertAngle(xy(0, 0), xy(1, -1), toRadians(315));
|
|
38
|
-
assertAngle(xy(1, 1), xy(0, 0), toRadians(225));
|
|
39
|
-
assertAngle(xy(0, 0), xy(1, 1), toRadians(45));
|
|
40
|
-
assertAngle(xy(0, 0), xy(2, 1), toRadians(26.565));
|
|
41
|
-
assertAngle(xy(0, 0), xy(3, 1), toRadians(18.434));
|
|
42
|
-
assertAngle(xy(0, 0), xy(4, 1), toRadians(14.036));
|
|
43
|
-
assertAngle(xy(0, 0), xy(5, 1), toRadians(11.309));
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
describe('acuteXAngle', () => {
|
|
47
|
-
const assertAcute = (input, expected) => {
|
|
48
|
-
it(`${toDegrees(input)} => ${toDegrees(expected)}`, () => {
|
|
49
|
-
const result = acuteXAngle(input);
|
|
50
|
-
log(`result: ${toDegrees(result)}`);
|
|
51
|
-
expect(result).toBeCloseTo(expected);
|
|
52
|
-
});
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
assertAcute(toRadians(45), toRadians(45));
|
|
56
|
-
assertAcute(toRadians(100), toRadians(80));
|
|
57
|
-
assertAcute(toRadians(190), toRadians(10));
|
|
58
|
-
assertAcute(toRadians(350), toRadians(10));
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
describe('acuteYAngle', () => {
|
|
62
|
-
const assertAcute = (input, expected) => {
|
|
63
|
-
it(`${toDegrees(input)} => ${toDegrees(expected)}`, () => {
|
|
64
|
-
const result = acuteYAngle(input);
|
|
65
|
-
log(`result: ${toDegrees(result)}`);
|
|
66
|
-
expect(result).toBeCloseTo(expected);
|
|
67
|
-
});
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
assertAcute(toRadians(45), toRadians(45));
|
|
71
|
-
assertAcute(toRadians(100), toRadians(10));
|
|
72
|
-
assertAcute(toRadians(190), toRadians(80));
|
|
73
|
-
assertAcute(toRadians(350), toRadians(80));
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
describe('edges', () => {
|
|
77
|
-
const assertEdges = (domain, range) => (from, to, expected) => {
|
|
78
|
-
it(p`${domain}, ${range} + ${from} -> ${to} => ${expected[0]}${expected[1]}`, () => {
|
|
79
|
-
const result = edges(domain, range)(from, to);
|
|
80
|
-
expect(result[0].x).toBeCloseTo(expected[0].x);
|
|
81
|
-
expect(result[0].y).toBeCloseTo(expected[0].y);
|
|
82
|
-
expect(result[1].x).toBeCloseTo(expected[1].x);
|
|
83
|
-
expect(result[1].y).toBeCloseTo(expected[1].y);
|
|
84
|
-
});
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
const one = assertEdges({ min: -4, max: 4 }, { min: -4, max: 4 });
|
|
88
|
-
one(xy(0, 0), xy(1, 1), [xy(4, 4), xy(-4, -4)]);
|
|
89
|
-
one(xy(0, 0), xy(2, 1), [xy(4, 2), xy(-4, -2)]);
|
|
90
|
-
one(xy(1, 1), xy(2, 2), [xy(4, 4), xy(-4, -4)]);
|
|
91
|
-
one(xy(1, 0), xy(2, 0), [xy(4, 0), xy(-4, 0)]);
|
|
92
|
-
one(xy(1, 0), xy(-2, 0), [xy(-4, 0), xy(4, 0)]);
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* domain {min: -5, max: 5, padding: 0, step: 1, labelStep: 1}labelStep: 1max: 5min: -5padding: 0step: 1__proto__: Object range: {min: -5, max: 5, padding: 0, step: 1, labelStep: 1} a: {x: -5, y: 0} b: {x: -5, y: 2} edges: Point {x: -5, y: 5} Point {x: -5, y: 2}
|
|
96
|
-
*/
|
|
97
|
-
const lineIssue = assertEdges({ min: -5, max: 5 }, { min: -5, max: 5 });
|
|
98
|
-
lineIssue(xy(-5, -0), xy(-5, 2), [xy(-5, 5), xy(-5, -5)]);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
describe('diffEdge', () => {
|
|
102
|
-
const assertDiffEdge = (bounds, from, to, expected) => {
|
|
103
|
-
it(p`<${bounds}> ${from} -> ${to} => ${expected}`, () => {
|
|
104
|
-
const result = diffEdge(bounds, from, to);
|
|
105
|
-
expect(result.x).toBeCloseTo(expected.x);
|
|
106
|
-
expect(result.y).toBeCloseTo(expected.y);
|
|
107
|
-
});
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
const twoTwo = assertDiffEdge.bind(null, xy(2, 2));
|
|
111
|
-
twoTwo(xy(0, 0), xy(1, 1), xy(2, 2));
|
|
112
|
-
twoTwo(xy(0, 0), xy(1, 2), xy(1, 2));
|
|
113
|
-
twoTwo(xy(0, 0), xy(2, 2), xy(2, 2));
|
|
114
|
-
twoTwo(xy(0, 0), xy(2, 2), xy(2, 2));
|
|
115
|
-
twoTwo(xy(0, 0), xy(-1, 1), xy(-2, 2));
|
|
116
|
-
twoTwo(xy(0, 0), xy(-1, -1), xy(-2, -2));
|
|
117
|
-
const fourFour = assertDiffEdge.bind(null, xy(4, 4));
|
|
118
|
-
|
|
119
|
-
fourFour(xy(0, 0), xy(1, 1), xy(4, 4));
|
|
120
|
-
fourFour(xy(0, 0), xy(1, 2), xy(2, 4));
|
|
121
|
-
fourFour(xy(0, 0), xy(2, 1), xy(4, 2));
|
|
122
|
-
fourFour(xy(0, 0), xy(-1, 1), xy(-4, 4));
|
|
123
|
-
fourFour(xy(0, 0), xy(-1, 1), xy(-4, 4));
|
|
124
|
-
|
|
125
|
-
assertDiffEdge(xy(-4, -4), xy(0, 0), xy(-1, -1), xy(-4, -4));
|
|
126
|
-
assertDiffEdge(xy(4, 4), xy(1, 1), xy(2, 2), xy(4, 4));
|
|
127
|
-
assertDiffEdge(xy(4, 4), xy(2, 2), xy(3, 3), xy(4, 4));
|
|
128
|
-
assertDiffEdge(xy(-4, -4), xy(-1, -1), xy(-2, -2), xy(-4, -4));
|
|
129
|
-
assertDiffEdge(xy(-4, 4), xy(-1, -1), xy(-2, 0), xy(-4, 2));
|
|
130
|
-
|
|
131
|
-
const lineIssue = assertDiffEdge.bind(null, xy(-5, -5));
|
|
132
|
-
|
|
133
|
-
lineIssue(xy(-5, 2), xy(-5, 0), xy(-5, -5));
|
|
134
|
-
//Top Right
|
|
135
|
-
assertDiffEdge(xy(5, 5), xy(0, 5), xy(2, 5), xy(5, 5));
|
|
136
|
-
assertDiffEdge(xy(5, 5), xy(5, 0), xy(5, 1), xy(5, 5));
|
|
137
|
-
|
|
138
|
-
// //Bottom Right
|
|
139
|
-
assertDiffEdge(xy(5, -5), xy(5, 0), xy(5, -0.1), xy(5, -5));
|
|
140
|
-
assertDiffEdge(xy(5, -5), xy(0, -5), xy(1, -5), xy(5, -5));
|
|
141
|
-
|
|
142
|
-
//Top Left
|
|
143
|
-
assertDiffEdge(xy(-5, 5), xy(-5, 0), xy(-5, 0.1), xy(-5, 5));
|
|
144
|
-
assertDiffEdge(xy(-5, 5), xy(0, 5), xy(-1, 5), xy(-5, 5));
|
|
145
|
-
|
|
146
|
-
//Bottom Left
|
|
147
|
-
assertDiffEdge(xy(-5, -5), xy(-5, 0), xy(-5, -0.1), xy(-5, -5));
|
|
148
|
-
assertDiffEdge(xy(-5, -5), xy(0, -5), xy(-1, -5), xy(-5, -5));
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
describe('getOpposingSide', () => {
|
|
152
|
-
const assertOpposingSide = (hyp, angle, expected) => {
|
|
153
|
-
it(`${hyp}, ${angle} = ${expected}`, () => {
|
|
154
|
-
const radians = toRadians(angle);
|
|
155
|
-
const result = getOpposingSide(hyp, radians);
|
|
156
|
-
expect(result).toBeCloseTo(expected);
|
|
157
|
-
});
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
assertOpposingSide(1, 45, 0.707);
|
|
161
|
-
assertOpposingSide(1.25, 45, 0.88);
|
|
162
|
-
});
|
|
163
|
-
});
|
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
import * as utils from '../utils';
|
|
2
|
-
|
|
3
|
-
const xy = utils.xy;
|
|
4
|
-
|
|
5
|
-
const tick = (isMajor, v) => ({
|
|
6
|
-
major: isMajor,
|
|
7
|
-
value: v,
|
|
8
|
-
x: v,
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
const major = tick.bind(null, true);
|
|
12
|
-
const minor = tick.bind(null, false);
|
|
13
|
-
|
|
14
|
-
describe('utils', () => {
|
|
15
|
-
describe('getDelta', () => {
|
|
16
|
-
const assertDelta = (from, to, delta) => {
|
|
17
|
-
it(`returns a delta of: ${delta} for ${from} -> ${to}`, () => {
|
|
18
|
-
const d = utils.getDelta(from, to);
|
|
19
|
-
expect(d.x).toEqual(delta.x);
|
|
20
|
-
expect(d.y).toEqual(delta.y);
|
|
21
|
-
});
|
|
22
|
-
};
|
|
23
|
-
assertDelta(xy(0, 0), xy(0, 1), xy(0, 1));
|
|
24
|
-
assertDelta(xy(0, 1), xy(0, 0), xy(0, -1));
|
|
25
|
-
assertDelta(xy(1, 1), xy(3, 3), xy(2, 2));
|
|
26
|
-
assertDelta(xy(-1, -1), xy(3, 3), xy(4, 4));
|
|
27
|
-
assertDelta(xy(-1, -1), xy(-2, -5), xy(-1, -4));
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
describe('polygonToArea', () => {
|
|
31
|
-
const assertPolygon = (points, area) => {
|
|
32
|
-
it(`converts ${points} -> ${area}`, () => {
|
|
33
|
-
const result = utils.polygonToArea(points);
|
|
34
|
-
expect(result).toEqual(area);
|
|
35
|
-
});
|
|
36
|
-
};
|
|
37
|
-
assertPolygon([xy(0, 0), xy(1, 1), xy(1, -1)], {
|
|
38
|
-
left: 0,
|
|
39
|
-
top: 1,
|
|
40
|
-
bottom: -1,
|
|
41
|
-
right: 1,
|
|
42
|
-
});
|
|
43
|
-
assertPolygon([xy(0, 0), xy(3, 0), xy(2, -1), xy(4, -3), xy(1, -4), xy(2, -2)], {
|
|
44
|
-
left: 0,
|
|
45
|
-
top: 0,
|
|
46
|
-
bottom: -4,
|
|
47
|
-
right: 4,
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
describe('buildTickModel', () => {
|
|
52
|
-
let scaleFn;
|
|
53
|
-
|
|
54
|
-
beforeEach(() => {
|
|
55
|
-
scaleFn = jest.fn(function (v) {
|
|
56
|
-
return v;
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('builds major only ticks', () => {
|
|
61
|
-
let result = utils.buildTickModel({ min: 0, max: 2 }, { minor: 0 }, 1, scaleFn);
|
|
62
|
-
expect(result).toEqual([major(0), major(1), major(2)]);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('builds minor + major ticks', () => {
|
|
66
|
-
let result = utils.buildTickModel({ min: 0, max: 2 }, { minor: 1 }, 0.5, scaleFn);
|
|
67
|
-
expect(result).toEqual([major(0), minor(0.5), major(1), minor(1.5), major(2)]);
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
describe('snapTo', () => {
|
|
72
|
-
let assertSnapTo = (min, max, interval, value, expected) => {
|
|
73
|
-
it(`snaps ${value} to ${expected} with domain ${min}<->${max} with interval: ${interval} `, () => {
|
|
74
|
-
let result = utils.snapTo(min, max, interval, value);
|
|
75
|
-
expect(result).toEqual(expected);
|
|
76
|
-
});
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
describe('with 0, 10, 0.25', () => {
|
|
80
|
-
let a = assertSnapTo.bind(null, 0, 10, 0.25);
|
|
81
|
-
a(1, 1);
|
|
82
|
-
a(1.2, 1.25);
|
|
83
|
-
a(0.2, 0.25);
|
|
84
|
-
a(5.2, 5.25);
|
|
85
|
-
a(5.125, 5.25);
|
|
86
|
-
a(5.124, 5);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
describe('with 0, 10, 1', () => {
|
|
90
|
-
let a = assertSnapTo.bind(null, 0, 10, 1);
|
|
91
|
-
a(0, 0);
|
|
92
|
-
a(10, 10);
|
|
93
|
-
a(100, 10);
|
|
94
|
-
a(1, 1);
|
|
95
|
-
a(1.2, 1);
|
|
96
|
-
a(0.2, 0);
|
|
97
|
-
a(5.2, 5);
|
|
98
|
-
a(5.001, 5);
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
describe('getInterval', () => {
|
|
103
|
-
let assertGetInterval = (min, max, ticks, expected) => {
|
|
104
|
-
let paramsDescription = JSON.stringify(ticks);
|
|
105
|
-
it(`converts: ${paramsDescription} to ${JSON.stringify(expected)}`, () => {
|
|
106
|
-
let result = utils.getInterval({ min: min, max: max }, ticks);
|
|
107
|
-
expect(result).toEqual(expected);
|
|
108
|
-
});
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
describe('with bad params', () => {
|
|
112
|
-
it('throws an error if min > max', () => {
|
|
113
|
-
expect(() => {
|
|
114
|
-
let result = utils.convertFrequencyToInterval(
|
|
115
|
-
{ min: 11, max: 10, tickFrequency: 1, betweenTickCount: 0 },
|
|
116
|
-
{ interval: 10, major: 10 },
|
|
117
|
-
);
|
|
118
|
-
}).toThrow(Error);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it('throws an error if min = max', () => {
|
|
122
|
-
expect(() => {
|
|
123
|
-
let result = utils.convertFrequencyToInterval(
|
|
124
|
-
{ min: 10, max: 10, tickFrequency: 1, betweenTickCount: 0 },
|
|
125
|
-
{ interval: 10, major: 10 },
|
|
126
|
-
);
|
|
127
|
-
}).toThrow(Error);
|
|
128
|
-
});
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
describe('with domain 0 -> 1', () => {
|
|
132
|
-
let a = assertGetInterval.bind(null, 0, 1);
|
|
133
|
-
a({ major: 2, minor: 0 }, 1);
|
|
134
|
-
a({ major: 2, minor: 1 }, 0.5);
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
describe('with domain 0 -> 10', () => {
|
|
138
|
-
let a = assertGetInterval.bind(null, 0, 10);
|
|
139
|
-
|
|
140
|
-
it('throws an error if the tick frequency is less than 2', () => {
|
|
141
|
-
expect(() => {
|
|
142
|
-
let result = utils.convertFrequencyToInterval(
|
|
143
|
-
{ min: 0, max: 10, tickFrequency: 1, betweenTickCount: 0 },
|
|
144
|
-
{ interval: 10, major: 10 },
|
|
145
|
-
);
|
|
146
|
-
}).toThrow(Error);
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
a({ major: 2, minor: 9 }, 1);
|
|
150
|
-
a({ major: 2, minor: 0 }, 10);
|
|
151
|
-
a({ major: 3, minor: 0 }, 5);
|
|
152
|
-
a({ major: 3, minor: 1 }, 2.5);
|
|
153
|
-
a({ major: 4, minor: 0 }, 3.3333);
|
|
154
|
-
a({ major: 5, minor: 0 }, 2.5);
|
|
155
|
-
a({ major: 6, minor: 0 }, 2);
|
|
156
|
-
a({ major: 7, minor: 0 }, 1.6667);
|
|
157
|
-
a({ major: 8, minor: 0 }, 1.4286);
|
|
158
|
-
a({ major: 9, minor: 0 }, 1.25);
|
|
159
|
-
a({ major: 10, minor: 0 }, 1.1111);
|
|
160
|
-
a({ major: 11, minor: 0 }, 1);
|
|
161
|
-
a({ major: 11, minor: 1 }, 0.5);
|
|
162
|
-
a({ major: 11, minor: 2 }, 0.3333);
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
describe('with domain 0 -> 100', () => {
|
|
166
|
-
let a = assertGetInterval.bind(null, 0, 100);
|
|
167
|
-
a({ major: 11, minor: 1 }, 5);
|
|
168
|
-
a({ major: 101, minor: 0 }, 1);
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
describe('with domain -5 - 5', () => {
|
|
172
|
-
let a = assertGetInterval.bind(null, -5, 5);
|
|
173
|
-
a({ major: 11, minor: 0 }, 1);
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
describe('with domain 0 - 5', () => {
|
|
177
|
-
let a = assertGetInterval.bind(null, 0, 5);
|
|
178
|
-
a({ major: 11, minor: 0 }, 0.5);
|
|
179
|
-
a({ major: 11, minor: 2 }, 0.1667);
|
|
180
|
-
a({ major: 11, minor: 1 }, 0.25);
|
|
181
|
-
});
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
describe('findLongestWord', () => {
|
|
185
|
-
it('should return 0 if label is undefined', () => {
|
|
186
|
-
const label = undefined;
|
|
187
|
-
const result = utils.findLongestWord(label);
|
|
188
|
-
|
|
189
|
-
expect(result).toEqual(0);
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
it('should return 0 if label is null', () => {
|
|
193
|
-
const label = null;
|
|
194
|
-
const result = utils.findLongestWord(label);
|
|
195
|
-
|
|
196
|
-
expect(result).toEqual(0);
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
it('should return 6 if the longest word from label has 6 letters', () => {
|
|
200
|
-
const label = 'Number of months';
|
|
201
|
-
const result = utils.findLongestWord(label);
|
|
202
|
-
|
|
203
|
-
expect(result).toEqual(6);
|
|
204
|
-
});
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
describe('amountToIncreaseWidth', () => {
|
|
208
|
-
it('should return 0 if longestWord is undefined', () => {
|
|
209
|
-
const longestWord = undefined;
|
|
210
|
-
const result = utils.amountToIncreaseWidth(longestWord);
|
|
211
|
-
|
|
212
|
-
expect(result).toEqual(0);
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
it('should return 0 if longestWord is null', () => {
|
|
216
|
-
const longestWord = null;
|
|
217
|
-
const result = utils.amountToIncreaseWidth(longestWord);
|
|
218
|
-
|
|
219
|
-
expect(result).toEqual(0);
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
it('should return 150 if longestWord is 10', () => {
|
|
223
|
-
const longestWord = 10;
|
|
224
|
-
const result = utils.amountToIncreaseWidth(longestWord);
|
|
225
|
-
|
|
226
|
-
expect(result).toEqual(200);
|
|
227
|
-
});
|
|
228
|
-
});
|
|
229
|
-
});
|