@pie-element/drawing-response 12.1.2-next.2 → 12.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/CHANGELOG.json +547 -0
  2. package/CHANGELOG.md +1966 -0
  3. package/LICENSE.md +5 -0
  4. package/README.md +1 -0
  5. package/configure/CHANGELOG.json +367 -0
  6. package/configure/CHANGELOG.md +1766 -0
  7. package/configure/lib/button.js +37 -0
  8. package/configure/lib/button.js.map +1 -0
  9. package/configure/lib/defaults.js +116 -0
  10. package/configure/lib/defaults.js.map +1 -0
  11. package/configure/lib/image-container.js +307 -0
  12. package/configure/lib/image-container.js.map +1 -0
  13. package/configure/lib/index.js +147 -0
  14. package/configure/lib/index.js.map +1 -0
  15. package/configure/lib/root.js +219 -0
  16. package/configure/lib/root.js.map +1 -0
  17. package/configure/package.json +24 -0
  18. package/configure/src/__tests__/image-container.test.jsx +231 -0
  19. package/configure/src/__tests__/index.test.js +72 -0
  20. package/configure/src/__tests__/root.test.jsx +110 -0
  21. package/configure/src/button.jsx +33 -0
  22. package/configure/src/defaults.js +80 -0
  23. package/configure/src/image-container.jsx +316 -0
  24. package/configure/src/index.js +167 -0
  25. package/configure/src/root.jsx +198 -0
  26. package/controller/CHANGELOG.json +52 -0
  27. package/controller/CHANGELOG.md +261 -0
  28. package/controller/lib/defaults.js +22 -0
  29. package/controller/lib/defaults.js.map +1 -0
  30. package/controller/lib/index.js +84 -0
  31. package/controller/lib/index.js.map +1 -0
  32. package/controller/package.json +16 -0
  33. package/controller/src/__tests__/index.test.js +172 -0
  34. package/controller/src/defaults.js +12 -0
  35. package/controller/src/index.js +71 -0
  36. package/docs/config-schema.json +1766 -0
  37. package/docs/config-schema.json.md +1313 -0
  38. package/docs/demo/config.js +8 -0
  39. package/docs/demo/generate.js +14 -0
  40. package/docs/demo/index.html +1 -0
  41. package/docs/demo/session.js +37 -0
  42. package/docs/pie-schema.json +823 -0
  43. package/docs/pie-schema.json.md +606 -0
  44. package/lib/drawing-response/button.js +49 -0
  45. package/lib/drawing-response/button.js.map +1 -0
  46. package/lib/drawing-response/constants.js +43 -0
  47. package/lib/drawing-response/constants.js.map +1 -0
  48. package/lib/drawing-response/container.js +327 -0
  49. package/lib/drawing-response/container.js.map +1 -0
  50. package/lib/drawing-response/drawable-circle.js +80 -0
  51. package/lib/drawing-response/drawable-circle.js.map +1 -0
  52. package/lib/drawing-response/drawable-eraser.js +64 -0
  53. package/lib/drawing-response/drawable-eraser.js.map +1 -0
  54. package/lib/drawing-response/drawable-free-path.js +71 -0
  55. package/lib/drawing-response/drawable-free-path.js.map +1 -0
  56. package/lib/drawing-response/drawable-helper.js +34 -0
  57. package/lib/drawing-response/drawable-helper.js.map +1 -0
  58. package/lib/drawing-response/drawable-image.js +41 -0
  59. package/lib/drawing-response/drawable-image.js.map +1 -0
  60. package/lib/drawing-response/drawable-line.js +75 -0
  61. package/lib/drawing-response/drawable-line.js.map +1 -0
  62. package/lib/drawing-response/drawable-main.js +345 -0
  63. package/lib/drawing-response/drawable-main.js.map +1 -0
  64. package/lib/drawing-response/drawable-palette.js +140 -0
  65. package/lib/drawing-response/drawable-palette.js.map +1 -0
  66. package/lib/drawing-response/drawable-rectangle.js +80 -0
  67. package/lib/drawing-response/drawable-rectangle.js.map +1 -0
  68. package/lib/drawing-response/drawable-text.js +295 -0
  69. package/lib/drawing-response/drawable-text.js.map +1 -0
  70. package/lib/drawing-response/drawable-transformer.js +52 -0
  71. package/lib/drawing-response/drawable-transformer.js.map +1 -0
  72. package/lib/drawing-response/factory.js +22 -0
  73. package/lib/drawing-response/factory.js.map +1 -0
  74. package/lib/drawing-response/icon.js +55 -0
  75. package/lib/drawing-response/icon.js.map +1 -0
  76. package/lib/drawing-response/index.js +91 -0
  77. package/lib/drawing-response/index.js.map +1 -0
  78. package/lib/index.js +68 -0
  79. package/lib/index.js.map +1 -0
  80. package/package.json +19 -82
  81. package/src/__tests__/drawing-index-test.jsx +103 -0
  82. package/src/drawing-response/__tests__/container.test.jsx +82 -0
  83. package/src/drawing-response/__tests__/drawable-helper.test.jsx +24 -0
  84. package/src/drawing-response/__tests__/drawing-main.test.jsx +855 -0
  85. package/src/drawing-response/__tests__/factory.test.js +13 -0
  86. package/src/drawing-response/button.jsx +45 -0
  87. package/src/drawing-response/constants.js +44 -0
  88. package/src/drawing-response/container.jsx +301 -0
  89. package/src/drawing-response/drawable-circle.jsx +74 -0
  90. package/src/drawing-response/drawable-eraser.jsx +53 -0
  91. package/src/drawing-response/drawable-free-path.jsx +59 -0
  92. package/src/drawing-response/drawable-helper.jsx +21 -0
  93. package/src/drawing-response/drawable-image.jsx +36 -0
  94. package/src/drawing-response/drawable-line.jsx +62 -0
  95. package/src/drawing-response/drawable-main.jsx +333 -0
  96. package/src/drawing-response/drawable-palette.jsx +99 -0
  97. package/src/drawing-response/drawable-rectangle.jsx +69 -0
  98. package/src/drawing-response/drawable-text.jsx +318 -0
  99. package/src/drawing-response/drawable-transformer.jsx +52 -0
  100. package/src/drawing-response/factory.js +17 -0
  101. package/src/drawing-response/icon.jsx +52 -0
  102. package/src/drawing-response/index.jsx +92 -0
  103. package/src/index.js +72 -0
  104. package/configure.js +0 -2
  105. package/controller.js +0 -1
  106. package/dist/author/button.d.ts +0 -29
  107. package/dist/author/button.js +0 -25
  108. package/dist/author/defaults.d.ts +0 -118
  109. package/dist/author/defaults.js +0 -86
  110. package/dist/author/image-container.d.ts +0 -42
  111. package/dist/author/image-container.js +0 -183
  112. package/dist/author/index.d.ts +0 -37
  113. package/dist/author/index.js +0 -79
  114. package/dist/author/root.d.ts +0 -17
  115. package/dist/author/root.js +0 -142
  116. package/dist/browser/MenuItem-N0xdzWSs.js +0 -10483
  117. package/dist/browser/MenuItem-N0xdzWSs.js.map +0 -1
  118. package/dist/browser/author/index.js +0 -37837
  119. package/dist/browser/author/index.js.map +0 -1
  120. package/dist/browser/browser-kkT1XVKw.js +0 -219
  121. package/dist/browser/browser-kkT1XVKw.js.map +0 -1
  122. package/dist/browser/controller/index.js +0 -61
  123. package/dist/browser/controller/index.js.map +0 -1
  124. package/dist/browser/delivery/index.js +0 -13560
  125. package/dist/browser/delivery/index.js.map +0 -1
  126. package/dist/browser/drawing-response.css +0 -2
  127. package/dist/controller/defaults.d.ts +0 -24
  128. package/dist/controller/defaults.js +0 -18
  129. package/dist/controller/index.d.ts +0 -13
  130. package/dist/controller/index.js +0 -46
  131. package/dist/delivery/drawing-response/button.d.ts +0 -31
  132. package/dist/delivery/drawing-response/button.js +0 -36
  133. package/dist/delivery/drawing-response/constants.d.ts +0 -16
  134. package/dist/delivery/drawing-response/constants.js +0 -40
  135. package/dist/delivery/drawing-response/container.d.ts +0 -32
  136. package/dist/delivery/drawing-response/container.js +0 -212
  137. package/dist/delivery/drawing-response/drawable-circle.d.ts +0 -18
  138. package/dist/delivery/drawing-response/drawable-circle.js +0 -51
  139. package/dist/delivery/drawing-response/drawable-eraser.d.ts +0 -17
  140. package/dist/delivery/drawing-response/drawable-eraser.js +0 -52
  141. package/dist/delivery/drawing-response/drawable-free-path.d.ts +0 -18
  142. package/dist/delivery/drawing-response/drawable-free-path.js +0 -52
  143. package/dist/delivery/drawing-response/drawable-helper.d.ts +0 -17
  144. package/dist/delivery/drawing-response/drawable-helper.js +0 -12
  145. package/dist/delivery/drawing-response/drawable-image.d.ts +0 -24
  146. package/dist/delivery/drawing-response/drawable-image.js +0 -28
  147. package/dist/delivery/drawing-response/drawable-line.d.ts +0 -18
  148. package/dist/delivery/drawing-response/drawable-line.js +0 -53
  149. package/dist/delivery/drawing-response/drawable-main.d.ts +0 -25
  150. package/dist/delivery/drawing-response/drawable-main.js +0 -210
  151. package/dist/delivery/drawing-response/drawable-palette.d.ts +0 -14
  152. package/dist/delivery/drawing-response/drawable-palette.js +0 -89
  153. package/dist/delivery/drawing-response/drawable-rectangle.d.ts +0 -18
  154. package/dist/delivery/drawing-response/drawable-rectangle.js +0 -49
  155. package/dist/delivery/drawing-response/drawable-text.d.ts +0 -30
  156. package/dist/delivery/drawing-response/drawable-text.js +0 -189
  157. package/dist/delivery/drawing-response/drawable-transformer.d.ts +0 -16
  158. package/dist/delivery/drawing-response/drawable-transformer.js +0 -35
  159. package/dist/delivery/drawing-response/factory.d.ts +0 -11
  160. package/dist/delivery/drawing-response/factory.js +0 -19
  161. package/dist/delivery/drawing-response/icon.d.ts +0 -19
  162. package/dist/delivery/drawing-response/icon.js +0 -52
  163. package/dist/delivery/drawing-response/index.d.ts +0 -18
  164. package/dist/delivery/drawing-response/index.js +0 -70
  165. package/dist/delivery/index.d.ts +0 -19
  166. package/dist/delivery/index.js +0 -47
  167. package/dist/index.d.ts +0 -1
  168. package/dist/index.iife.d.ts +0 -8
  169. package/dist/index.iife.js +0 -165
  170. package/dist/index.js +0 -2
  171. package/dist/runtime-support.d.ts +0 -12
  172. package/dist/runtime-support.js +0 -12
@@ -0,0 +1,855 @@
1
+ import React from 'react';
2
+ import { render } from '@testing-library/react';
3
+ import { ThemeProvider, createTheme } from '@mui/material/styles';
4
+ import { cloneDeep } from 'lodash-es';
5
+ import FreePathDrawable from '../drawable-free-path';
6
+ import LineDrawable from '../drawable-line';
7
+ import RectangleDrawable from '../drawable-rectangle';
8
+ import CircleDrawable from '../drawable-circle';
9
+ import EraserDrawable from '../drawable-eraser';
10
+ import DrawableText from '../drawable-text';
11
+ import { DrawableMain } from '../drawable-main';
12
+
13
+ jest.mock('react-konva', () => ({
14
+ Stage: (props) => <div data-testid="konva-stage" {...props}>{props.children}</div>,
15
+ Layer: (props) => <div data-testid="konva-layer" {...props}>{props.children}</div>,
16
+ Circle: (props) => <circle data-testid="konva-circle" {...props} />,
17
+ Line: (props) => <line data-testid="konva-line" {...props} />,
18
+ Arrow: (props) => <line data-testid="konva-arrow" {...props} />,
19
+ Rect: (props) => <rect data-testid="konva-rect" {...props} />,
20
+ Group: (props) => <g data-testid="konva-group" {...props}>{props.children}</g>,
21
+ Text: (props) => <text data-testid="konva-text" {...props}>{props.children}</text>,
22
+ Transformer: (props) => <g data-testid="konva-transformer" {...props} />,
23
+ }));
24
+
25
+ jest.mock('../drawable-transformer', () => {
26
+ return (props) => <g data-testid="drawable-transformer" {...props} />;
27
+ });
28
+
29
+ const theme = createTheme();
30
+
31
+ const drawableClasses = {
32
+ FreePathDrawable,
33
+ LineDrawable,
34
+ RectangleDrawable,
35
+ CircleDrawable,
36
+ EraserDrawable,
37
+ DrawableText,
38
+ };
39
+
40
+ describe('DrawingResponse', () => {
41
+ let element;
42
+
43
+ describe('DrawableMain', () => {
44
+ let onSessionChange = jest.fn();
45
+ const TextEntry = new DrawableText();
46
+
47
+ beforeEach(() => {
48
+ jest.clearAllMocks();
49
+ });
50
+
51
+ const renderDrawableMain = (props = {}) => {
52
+ const defaultProps = {
53
+ disabled: false,
54
+ className: 'className',
55
+ onSessionChange,
56
+ imageDimensions: {},
57
+ imageUrl: 'url',
58
+ drawableDimensions: { height: 350, width: 353 },
59
+ fillColor: 'white',
60
+ outlineColor: 'black',
61
+ paintColor: 'red',
62
+ session: {},
63
+ toolActive: { type: 'Select', label: 'Select', icon: 'mdiCursorDefault' },
64
+ TextEntry,
65
+ ...props,
66
+ };
67
+ return render(
68
+ <ThemeProvider theme={theme}>
69
+ <DrawableMain {...defaultProps} />
70
+ </ThemeProvider>
71
+ );
72
+ };
73
+
74
+ describe('logic', () => {
75
+ const handleSessionChange = jest.fn();
76
+ const forceUpdate = jest.fn();
77
+ const props = {
78
+ handleSessionChange,
79
+ forceUpdate,
80
+ paint: true,
81
+ paintColor: 'green',
82
+ startx: 200,
83
+ starty: 200,
84
+ x: 300,
85
+ y: 300,
86
+ createdAt: new Date(),
87
+ };
88
+
89
+ it('handleUndo removes the last drawable', () => {
90
+ const testInstance = new DrawableMain({
91
+ onSessionChange,
92
+ imageDimensions: {},
93
+ imageUrl: 'url',
94
+ drawableDimensions: { height: 350, width: 353 },
95
+ fillColor: 'white',
96
+ outlineColor: 'black',
97
+ paintColor: 'red',
98
+ session: {},
99
+ toolActive: { type: 'Select', label: 'Select', icon: 'mdiCursorDefault' },
100
+ TextEntry,
101
+ });
102
+
103
+ const drawable1 = new RectangleDrawable(props);
104
+ const drawable2 = new RectangleDrawable(props);
105
+
106
+ // Set initial state
107
+ testInstance.state = { drawables: [drawable1, drawable2], newDrawable: [], textIsSelected: false };
108
+
109
+ // Spy on setState
110
+ const setStateSpy = jest.spyOn(testInstance, 'setState');
111
+
112
+ // First undo - should remove drawable2
113
+ testInstance.handleUndo();
114
+
115
+ expect(setStateSpy).toHaveBeenCalledWith(
116
+ { drawables: [drawable1] },
117
+ testInstance.handleSessionChange
118
+ );
119
+
120
+ // Update state to reflect what would have happened
121
+ testInstance.state.drawables = [drawable1];
122
+ setStateSpy.mockClear();
123
+
124
+ // Second undo - should remove drawable1
125
+ testInstance.handleUndo();
126
+
127
+ expect(setStateSpy).toHaveBeenCalledWith(
128
+ { drawables: [] },
129
+ testInstance.handleSessionChange
130
+ );
131
+ });
132
+
133
+ it('handleClearAll removes all drawables', () => {
134
+ const testInstance = new DrawableMain({
135
+ onSessionChange,
136
+ imageDimensions: {},
137
+ imageUrl: 'url',
138
+ drawableDimensions: { height: 350, width: 353 },
139
+ fillColor: 'white',
140
+ outlineColor: 'black',
141
+ paintColor: 'red',
142
+ session: {},
143
+ toolActive: { type: 'Select', label: 'Select', icon: 'mdiCursorDefault' },
144
+ TextEntry,
145
+ });
146
+
147
+ const drawable1 = new RectangleDrawable(props);
148
+ const drawable2 = new RectangleDrawable(props);
149
+
150
+ // Set initial state
151
+ testInstance.state = { drawables: [drawable1, drawable2], newDrawable: [], textIsSelected: false };
152
+
153
+ // Spy on setState
154
+ const setStateSpy = jest.spyOn(testInstance, 'setState');
155
+
156
+ testInstance.handleClearAll();
157
+
158
+ expect(setStateSpy).toHaveBeenCalledWith(
159
+ { drawables: [], updatedAt: expect.any(Date) },
160
+ testInstance.handleSessionChange
161
+ );
162
+ expect(TextEntry.all).toEqual([]);
163
+ });
164
+ });
165
+ });
166
+
167
+ describe('CircleDrawable', () => {
168
+ let handleSessionChange = jest.fn();
169
+ let forceUpdate = jest.fn();
170
+ let props;
171
+
172
+ beforeEach(() => {
173
+ props = {
174
+ handleSessionChange,
175
+ forceUpdate,
176
+ paint: true,
177
+ paintColor: 'green',
178
+ startx: 200,
179
+ starty: 200,
180
+ x: 300,
181
+ y: 200,
182
+ createdAt: new Date(),
183
+ };
184
+ element = new CircleDrawable(props);
185
+ });
186
+
187
+ describe('logic', () => {
188
+ it('changes x and y', () => {
189
+ element.registerMovement(400, 400);
190
+
191
+ expect(element.x).toEqual(400);
192
+ expect(element.y).toEqual(400);
193
+ });
194
+
195
+ it('changes session when needed', () => {
196
+ const event = {
197
+ target: {
198
+ getX: jest.fn().mockReturnValue(300),
199
+ getY: jest.fn().mockReturnValue(300),
200
+ },
201
+ };
202
+
203
+ element.handleDragEnd(props, event);
204
+
205
+ expect(event.target.getX).toHaveBeenCalled();
206
+ expect(event.target.getY).toHaveBeenCalled();
207
+
208
+ expect(handleSessionChange).toHaveBeenCalled();
209
+ });
210
+
211
+ it('changes startx and starty', () => {
212
+ const event = {
213
+ target: {
214
+ getX: jest.fn().mockReturnValue(300),
215
+ getY: jest.fn().mockReturnValue(300),
216
+ },
217
+ };
218
+
219
+ element.handleDragEnd(props, event);
220
+
221
+ expect(element.startx).toEqual(300);
222
+ expect(element.starty).toEqual(300);
223
+ expect(element.x).toEqual(400);
224
+ expect(element.y).toEqual(300);
225
+ });
226
+ });
227
+ });
228
+
229
+ describe('EraserDrawable', () => {
230
+ let handleSessionChange = jest.fn();
231
+ let forceUpdate = jest.fn();
232
+ let props;
233
+
234
+ beforeEach(() => {
235
+ props = {
236
+ handleSessionChange,
237
+ forceUpdate,
238
+ points: [200, 200, 300, 300],
239
+ posX: 200,
240
+ posY: 200,
241
+ startx: 200,
242
+ starty: 200,
243
+ createdAt: new Date(),
244
+ };
245
+ element = new EraserDrawable(props);
246
+ });
247
+
248
+ describe('logic', () => {
249
+ it('changes points', () => {
250
+ element.registerMovement(400, 400);
251
+
252
+ expect(element.points).toEqual([200, 200, 300, 300, 400, 400]);
253
+ });
254
+
255
+ it('changes session when needed', () => {
256
+ const event = {
257
+ target: {
258
+ getX: jest.fn().mockReturnValue(300),
259
+ getY: jest.fn().mockReturnValue(300),
260
+ },
261
+ };
262
+
263
+ element.handleDragEnd(props, event);
264
+
265
+ expect(event.target.getX).toHaveBeenCalled();
266
+ expect(event.target.getY).toHaveBeenCalled();
267
+
268
+ expect(handleSessionChange).toHaveBeenCalled();
269
+ });
270
+
271
+ it('changes posX and posY', () => {
272
+ const event = {
273
+ target: {
274
+ getX: jest.fn().mockReturnValue(300),
275
+ getY: jest.fn().mockReturnValue(300),
276
+ },
277
+ };
278
+
279
+ element.handleDragEnd(props, event);
280
+
281
+ expect(element.posX).toEqual(300);
282
+ expect(element.posY).toEqual(300);
283
+ });
284
+ });
285
+ });
286
+
287
+ describe('FreePathDrawable', () => {
288
+ let handleSessionChange = jest.fn();
289
+ let forceUpdate = jest.fn();
290
+ let props;
291
+
292
+ beforeEach(() => {
293
+ props = {
294
+ handleSessionChange,
295
+ forceUpdate,
296
+ paint: true,
297
+ paintColor: 'green',
298
+ points: [200, 200, 300, 300],
299
+ posX: 200,
300
+ posY: 200,
301
+ startx: 200,
302
+ starty: 200,
303
+ createdAt: new Date(),
304
+ };
305
+ element = new FreePathDrawable(props);
306
+ });
307
+
308
+ describe('logic', () => {
309
+ it('changes points', () => {
310
+ element.registerMovement(400, 400);
311
+
312
+ expect(element.points).toEqual([200, 200, 300, 300, 400, 400]);
313
+ });
314
+
315
+ it('changes session when needed', () => {
316
+ const event = {
317
+ target: {
318
+ getX: jest.fn().mockReturnValue(300),
319
+ getY: jest.fn().mockReturnValue(300),
320
+ },
321
+ };
322
+
323
+ element.handleDragEnd(props, event);
324
+
325
+ expect(event.target.getX).toHaveBeenCalled();
326
+ expect(event.target.getY).toHaveBeenCalled();
327
+
328
+ expect(handleSessionChange).toHaveBeenCalled();
329
+ });
330
+
331
+ it('changes posX and posY', () => {
332
+ const event = {
333
+ target: {
334
+ getX: jest.fn().mockReturnValue(300),
335
+ getY: jest.fn().mockReturnValue(300),
336
+ },
337
+ };
338
+
339
+ element.handleDragEnd(props, event);
340
+
341
+ expect(element.posX).toEqual(300);
342
+ expect(element.posY).toEqual(300);
343
+ });
344
+ });
345
+ });
346
+
347
+ describe('LineDrawable', () => {
348
+ let handleSessionChange = jest.fn();
349
+ let forceUpdate = jest.fn();
350
+ let props;
351
+
352
+ beforeEach(() => {
353
+ props = {
354
+ handleSessionChange,
355
+ forceUpdate,
356
+ paint: true,
357
+ paintColor: 'green',
358
+ startx: 200,
359
+ starty: 200,
360
+ posX: 200,
361
+ posY: 200,
362
+ x: 300,
363
+ y: 200,
364
+ createdAt: new Date(),
365
+ };
366
+ element = new LineDrawable(props);
367
+ });
368
+
369
+ describe('logic', () => {
370
+ it('changes x and y', () => {
371
+ element.registerMovement(400, 400);
372
+
373
+ expect(element.x).toEqual(400);
374
+ expect(element.y).toEqual(400);
375
+ });
376
+
377
+ it('changes session when needed', () => {
378
+ const event = {
379
+ target: {
380
+ getX: jest.fn().mockReturnValue(300),
381
+ getY: jest.fn().mockReturnValue(300),
382
+ },
383
+ };
384
+
385
+ element.handleDragEnd(props, event);
386
+
387
+ expect(event.target.getX).toHaveBeenCalled();
388
+ expect(event.target.getY).toHaveBeenCalled();
389
+
390
+ expect(handleSessionChange).toHaveBeenCalled();
391
+ });
392
+
393
+ it('changes posX and posY', () => {
394
+ const event = {
395
+ target: {
396
+ getX: jest.fn().mockReturnValue(300),
397
+ getY: jest.fn().mockReturnValue(300),
398
+ },
399
+ };
400
+
401
+ element.handleDragEnd(props, event);
402
+
403
+ expect(element.posX).toEqual(300);
404
+ expect(element.posY).toEqual(300);
405
+ });
406
+ });
407
+ });
408
+
409
+ describe('RectangleDrawable', () => {
410
+ let handleSessionChange = jest.fn();
411
+ let forceUpdate = jest.fn();
412
+ let props;
413
+
414
+ beforeEach(() => {
415
+ props = {
416
+ handleSessionChange,
417
+ forceUpdate,
418
+ paint: true,
419
+ paintColor: 'green',
420
+ startx: 200,
421
+ starty: 200,
422
+ x: 300,
423
+ y: 300,
424
+ createdAt: new Date(),
425
+ };
426
+ element = new RectangleDrawable(props);
427
+ });
428
+
429
+ describe('logic', () => {
430
+ it('changes x and y', () => {
431
+ element.registerMovement(400, 400);
432
+
433
+ expect(element.x).toEqual(400);
434
+ expect(element.y).toEqual(400);
435
+ });
436
+
437
+ it('changes session when needed', () => {
438
+ const event = {
439
+ target: {
440
+ getX: jest.fn().mockReturnValue(300),
441
+ getY: jest.fn().mockReturnValue(300),
442
+ },
443
+ };
444
+
445
+ element.handleDragEnd(props, event);
446
+
447
+ expect(event.target.getX).toHaveBeenCalled();
448
+ expect(event.target.getY).toHaveBeenCalled();
449
+
450
+ expect(handleSessionChange).toHaveBeenCalled();
451
+ });
452
+
453
+ it('changes posX and posY', () => {
454
+ const event = {
455
+ target: {
456
+ getX: jest.fn().mockReturnValue(300),
457
+ getY: jest.fn().mockReturnValue(300),
458
+ },
459
+ };
460
+
461
+ element.handleDragEnd(props, event);
462
+
463
+ expect(element.startx).toEqual(300);
464
+ expect(element.starty).toEqual(300);
465
+ expect(element.x).toEqual(400);
466
+ expect(element.y).toEqual(400);
467
+ });
468
+ });
469
+ });
470
+
471
+ describe('DrawableText', () => {
472
+ const all = [
473
+ {
474
+ id: 'gcifqhhimf8k2d6g8hs',
475
+ isDefault: true,
476
+ label: 'Double click to edit this text. Press Enter to submit.',
477
+ value: 'This is what the user entered',
478
+ width: 200,
479
+ x: 2 * 5 + 50,
480
+ y: 2 * 5 + 50,
481
+ textVisible: true,
482
+ transformerVisible: true,
483
+ textareaVisible: false,
484
+ createdAt: new Date(),
485
+ type: 'text-entry',
486
+ },
487
+ ];
488
+ let handleSessionChange = jest.fn();
489
+ let forceUpdate = jest.fn();
490
+ let toggleTextSelected = jest.fn();
491
+ let props;
492
+ let stage;
493
+
494
+ beforeEach(() => {
495
+ stage = {
496
+ on: jest.fn(),
497
+ off: jest.fn(),
498
+ };
499
+
500
+ props = {
501
+ all,
502
+ handleSessionChange,
503
+ forceUpdate,
504
+ stage,
505
+ toggleTextSelected,
506
+ paint: true,
507
+ paintColor: 'green',
508
+ startx: 200,
509
+ starty: 200,
510
+ x: 300,
511
+ y: 300,
512
+ scale: 1,
513
+ };
514
+ element = new DrawableText(props);
515
+ });
516
+
517
+ describe('when there is no value', () => {
518
+ beforeEach(() => {
519
+ props.all.forEach((a) => {
520
+ delete a.value;
521
+ });
522
+ element = new DrawableText(props);
523
+ });
524
+ it('renders without crashing', () => {
525
+ const content = element.render(props);
526
+ const { container } = render(<div>{content}</div>);
527
+ expect(container).toBeInTheDocument();
528
+ });
529
+ });
530
+
531
+ describe('logic', () => {
532
+ it('shoud change the all property and call forceUpdate', () => {
533
+ element.setInitialProps(props);
534
+ element.setAll([]);
535
+
536
+ expect(element.all).toEqual([]);
537
+ expect(forceUpdate).toHaveBeenCalled();
538
+ });
539
+
540
+ describe('addNewTextEntry', () => {
541
+ it('shoud add a new element and call the appropriate functions', () => {
542
+ element.setInitialProps(props);
543
+ element.addNewTextEntry();
544
+
545
+ expect(element.all.length).toEqual(2);
546
+ expect(element.all).toEqual(
547
+ expect.arrayContaining([
548
+ expect.objectContaining({
549
+ isDefault: true,
550
+ label: 'Double click to edit this text. Press Enter to submit.',
551
+ width: 200,
552
+ textVisible: true,
553
+ transformerVisible: true,
554
+ textareaVisible: false,
555
+ type: 'text-entry',
556
+ }),
557
+ ]),
558
+ );
559
+
560
+ expect(handleSessionChange).toHaveBeenCalled();
561
+ });
562
+ });
563
+
564
+ describe('showOnlyTextNodes', () => {
565
+ it('should change properties for the "all" array', () => {
566
+ element.showOnlyTextNodes();
567
+
568
+ expect(element.all).toEqual(
569
+ expect.arrayContaining([
570
+ expect.objectContaining({
571
+ textVisible: true,
572
+ transformerVisible: false,
573
+ textareaVisible: false,
574
+ }),
575
+ ]),
576
+ );
577
+ });
578
+ });
579
+
580
+ describe('showOnltoggleTextareayTextNodes', () => {
581
+ it('should change the item with the right id in the "all" array and call forceUpdate', () => {
582
+ element.setInitialProps(props);
583
+ element.toggleTextarea('gcifqhhimf8k2d6g8hs', true);
584
+
585
+ expect(element.all[0]).toEqual(
586
+ expect.objectContaining({
587
+ textVisible: false,
588
+ transformerVisible: false,
589
+ textareaVisible: true,
590
+ }),
591
+ );
592
+
593
+ expect(forceUpdate).toHaveBeenCalled();
594
+
595
+ element.toggleTextarea('gcifqhhimf8k2d6g8hs', false);
596
+
597
+ expect(element.all[0]).toEqual(
598
+ expect.objectContaining({
599
+ textVisible: true,
600
+ transformerVisible: true,
601
+ textareaVisible: false,
602
+ }),
603
+ );
604
+
605
+ expect(forceUpdate).toHaveBeenCalled();
606
+ });
607
+ });
608
+
609
+ describe('initializeDefault', () => {
610
+ it('should make the item with the right id default', () => {
611
+ element.initializeDefault('gcifqhhimf8k2d6g8hs', true);
612
+
613
+ expect(element.all[0]).toEqual(
614
+ expect.objectContaining({
615
+ isDefault: false,
616
+ }),
617
+ );
618
+ });
619
+ });
620
+
621
+ describe('saveValue', () => {
622
+ it('should save the value and call handleSessionChange', () => {
623
+ element.setInitialProps(props);
624
+ const textareaNode = {
625
+ value: 'Foo bar',
626
+ };
627
+
628
+ element.saveValue('gcifqhhimf8k2d6g8hs', textareaNode);
629
+
630
+ expect(element.all[0].text).toEqual('Foo bar');
631
+ expect(handleSessionChange).toHaveBeenCalled();
632
+ expect(forceUpdate).toHaveBeenCalled();
633
+
634
+ // Reset mock
635
+ handleSessionChange.mockClear();
636
+ forceUpdate.mockClear();
637
+
638
+ // Test with empty value - should remove the item
639
+ textareaNode.value = '';
640
+ element.saveValue('gcifqhhimf8k2d6g8hs', textareaNode);
641
+
642
+ expect(element.all).toEqual(
643
+ expect.not.arrayContaining([
644
+ expect.objectContaining({
645
+ id: 'gcifqhhimf8k2d6g8hs',
646
+ }),
647
+ ]),
648
+ );
649
+ expect(forceUpdate).toHaveBeenCalled();
650
+ expect(handleSessionChange).toHaveBeenCalled();
651
+ });
652
+ });
653
+
654
+ describe('handleMouseEvents', () => {
655
+ it('should call the right functions on mouse down and up', () => {
656
+ element.setInitialProps(props);
657
+ element.handleMouseDown();
658
+ expect(toggleTextSelected).toHaveBeenCalledWith(true);
659
+
660
+ element.handleMouseUp();
661
+ expect(toggleTextSelected).toHaveBeenCalledWith(false);
662
+ });
663
+
664
+ it('should call the right functions onClick', () => {
665
+ element.setInitialProps(props);
666
+ element.handleClick(null, 'gcifqhhimf8k2d6g8hs');
667
+
668
+ expect(element.all).toEqual(
669
+ expect.arrayContaining([
670
+ expect.objectContaining({
671
+ transformerVisible: true,
672
+ }),
673
+ ]),
674
+ );
675
+
676
+ expect(forceUpdate).toHaveBeenCalled();
677
+ });
678
+
679
+ it('should call the right functions onDblClick', () => {
680
+ element.setInitialProps(props);
681
+
682
+ // Call render to set up stage reference
683
+ element.render(props);
684
+
685
+ const text = {
686
+ id: 'gcifqhhimf8k2d6g8hs',
687
+ isDefault: false,
688
+ text: 'foo bar',
689
+ };
690
+
691
+ const textNode = (element[`text_${text.id}`] = {
692
+ _lastPos: {
693
+ x: 200,
694
+ y: 200,
695
+ },
696
+ align: jest.fn().mockReturnValue('center'),
697
+ fill: jest.fn().mockReturnValue('green'),
698
+ fontFamily: jest.fn().mockReturnValue('FooBarFamily'),
699
+ fontSize: jest.fn().mockReturnValue('16'),
700
+ height: jest.fn().mockReturnValue(40),
701
+ lineHeight: jest.fn().mockReturnValue('40px'),
702
+ padding: jest.fn().mockReturnValue(0),
703
+ rotation: jest.fn().mockReturnValue(0),
704
+ text: jest.fn().mockReturnValue('foo bar'),
705
+ width: jest.fn().mockReturnValue(200),
706
+ getAbsolutePosition: jest.fn().mockReturnValue({ x: 200, y: 200 }),
707
+ });
708
+ const textareaNode = (element[`textarea_${text.id}`] = {
709
+ focus: jest.fn(),
710
+ addEventListener: jest.fn(),
711
+ style: {},
712
+ scrollHeight: 205,
713
+ });
714
+
715
+ const initSpy = jest.spyOn(element, 'initializeDefault');
716
+ const saveValueSpy = jest.spyOn(element, 'saveValue');
717
+ const toggleSpy = jest.spyOn(element, 'toggleTextarea');
718
+
719
+ element.handleDblClick(null, text);
720
+
721
+ expect(toggleSpy).toHaveBeenCalledWith(text.id, true);
722
+
723
+ expect(textareaNode.value).toEqual('foo bar');
724
+ expect(textareaNode.style).toMatchObject({
725
+ background: 'none',
726
+ border: 'none',
727
+ color: 'green',
728
+ fontFamily: 'FooBarFamily',
729
+ fontSize: '16px',
730
+ left: '200px',
731
+ lineHeight: '40px',
732
+ margin: '0px',
733
+ outline: 'none',
734
+ overflow: 'hidden',
735
+ padding: '0px',
736
+ position: 'absolute',
737
+ resize: 'none',
738
+ top: '200px',
739
+ transformOrigin: 'left top',
740
+ textAlign: 'center',
741
+ width: '200px',
742
+ });
743
+
744
+ expect(textareaNode.focus).toHaveBeenCalled();
745
+ expect(stage.on).toHaveBeenCalled();
746
+ expect(initSpy).toHaveBeenCalledWith('gcifqhhimf8k2d6g8hs', false);
747
+ expect(forceUpdate).toHaveBeenCalled();
748
+ expect(textareaNode.addEventListener.mock.calls[0][0]).toEqual('keydown');
749
+ expect(textareaNode.addEventListener.mock.calls[1][0]).toEqual('blur');
750
+
751
+ const event = {
752
+ key: 'Enter',
753
+ shiftKey: true,
754
+ };
755
+
756
+ // Clear spy to ignore the initial toggleTextarea(id, true) call from handleDblClick
757
+ toggleSpy.mockClear();
758
+ saveValueSpy.mockClear();
759
+
760
+ textareaNode.addEventListener.mock.calls[0][1](event);
761
+
762
+ // Make sure toggleTextarea and saveValue are not called when shift+enter is pressed
763
+ expect(toggleSpy).not.toHaveBeenCalled();
764
+ expect(saveValueSpy).not.toHaveBeenCalled();
765
+
766
+ event.shiftKey = false;
767
+
768
+ textareaNode.addEventListener.mock.calls[0][1](event);
769
+
770
+ // Enter without shift calls saveValue, which in turn calls toggleTextarea
771
+ expect(saveValueSpy).toHaveBeenCalledWith('gcifqhhimf8k2d6g8hs', textareaNode);
772
+ expect(toggleSpy).toHaveBeenCalledWith('gcifqhhimf8k2d6g8hs', false);
773
+
774
+ // Clear spy before testing Escape key
775
+ toggleSpy.mockClear();
776
+ saveValueSpy.mockClear();
777
+
778
+ event.key = 'Escape';
779
+
780
+ textareaNode.addEventListener.mock.calls[0][1](event);
781
+
782
+ // Escape only calls toggleTextarea, not saveValue
783
+ expect(toggleSpy).toHaveBeenCalledWith('gcifqhhimf8k2d6g8hs', false);
784
+ expect(saveValueSpy).not.toHaveBeenCalled();
785
+
786
+ const showTextSpy = jest.spyOn(element, 'showOnlyTextNodes');
787
+
788
+ // Clear spies before testing stage click handler
789
+ toggleSpy.mockClear();
790
+ saveValueSpy.mockClear();
791
+
792
+ event.target = stage;
793
+
794
+ stage.on.mock.calls[0][1](event);
795
+
796
+ // Stage click handler only calls showOnlyTextNodes and forceUpdate, not saveValue
797
+ expect(showTextSpy).toHaveBeenCalled();
798
+ expect(forceUpdate).toHaveBeenCalled();
799
+ expect(saveValueSpy).not.toHaveBeenCalled();
800
+ });
801
+ });
802
+
803
+ describe('handleTransform', () => {
804
+ it('should change attrs when called', () => {
805
+ element.setInitialProps(props);
806
+ const textNode = (element[`text_gcifqhhimf8k2d6g8hs`] = {
807
+ setAttrs: jest.fn(),
808
+ width: jest.fn().mockReturnValue(100),
809
+ scaleX: jest.fn().mockReturnValue(1),
810
+ });
811
+
812
+ element.handleTransform(null, 'text_gcifqhhimf8k2d6g8hs');
813
+
814
+ expect(textNode.setAttrs).toHaveBeenCalledWith({
815
+ width: 100,
816
+ scaleX: 1,
817
+ });
818
+ });
819
+ });
820
+
821
+ describe('setInitialProps', () => {
822
+ it('should set the props if not set already', () => {
823
+ element.props = undefined;
824
+
825
+ element.setInitialProps(props);
826
+
827
+ expect(element.props).toEqual(props);
828
+ });
829
+ });
830
+
831
+ describe('render', () => {
832
+ it('should set the stage listener only once', () => {
833
+ const separateStage = {
834
+ on: jest.fn(),
835
+ off: jest.fn(),
836
+ };
837
+ const newProps = {
838
+ ...cloneDeep(props),
839
+ stage: separateStage,
840
+ };
841
+ const newElement = new drawableClasses['DrawableText'](newProps);
842
+
843
+ expect(newElement.stage).toEqual(undefined);
844
+
845
+ newElement.render(newProps);
846
+ expect(newElement.stage).toEqual(separateStage);
847
+
848
+ newElement.render(newProps);
849
+
850
+ expect(separateStage.on.mock.calls.length).toEqual(1);
851
+ });
852
+ });
853
+ });
854
+ });
855
+ });