@pie-lib/mask-markup 3.1.0-beta.7 → 3.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.
Files changed (35) hide show
  1. package/lib/keyboard-coordinates.js +156 -0
  2. package/package.json +2 -2
  3. package/lib/__tests__/drag-in-the-blank.test.js +0 -129
  4. package/lib/__tests__/drag-in-the-blank.test.js.map +0 -1
  5. package/lib/__tests__/index.test.js +0 -39
  6. package/lib/__tests__/index.test.js.map +0 -1
  7. package/lib/__tests__/mask.test.js +0 -344
  8. package/lib/__tests__/mask.test.js.map +0 -1
  9. package/lib/__tests__/serialization.test.js +0 -44
  10. package/lib/__tests__/serialization.test.js.map +0 -1
  11. package/lib/__tests__/utils.js +0 -14
  12. package/lib/__tests__/utils.js.map +0 -1
  13. package/lib/__tests__/with-mask.test.js +0 -110
  14. package/lib/__tests__/with-mask.test.js.map +0 -1
  15. package/lib/choices/__tests__/index.test.js +0 -139
  16. package/lib/choices/__tests__/index.test.js.map +0 -1
  17. package/lib/components/__tests__/blank.test.js +0 -293
  18. package/lib/components/__tests__/blank.test.js.map +0 -1
  19. package/lib/components/__tests__/correct-input.test.js +0 -132
  20. package/lib/components/__tests__/correct-input.test.js.map +0 -1
  21. package/lib/components/__tests__/dropdown.test.js +0 -202
  22. package/lib/components/__tests__/dropdown.test.js.map +0 -1
  23. package/lib/components/__tests__/input.test.js +0 -129
  24. package/lib/components/__tests__/input.test.js.map +0 -1
  25. package/src/__tests__/drag-in-the-blank.test.js +0 -111
  26. package/src/__tests__/index.test.js +0 -38
  27. package/src/__tests__/mask.test.js +0 -381
  28. package/src/__tests__/serialization.test.js +0 -54
  29. package/src/__tests__/utils.js +0 -1
  30. package/src/__tests__/with-mask.test.js +0 -76
  31. package/src/choices/__tests__/index.test.js +0 -109
  32. package/src/components/__tests__/blank.test.js +0 -232
  33. package/src/components/__tests__/correct-input.test.js +0 -90
  34. package/src/components/__tests__/dropdown.test.js +0 -129
  35. package/src/components/__tests__/input.test.js +0 -102
@@ -1,381 +0,0 @@
1
- import * as React from 'react';
2
- import { render, screen } from '@testing-library/react';
3
- import Mask from '../mask';
4
-
5
- describe('Mask', () => {
6
- // Don't mock renderChildren - let the component render naturally
7
- const onChange = jest.fn();
8
- const defaultProps = {
9
- onChange,
10
- layout: {
11
- nodes: [
12
- {
13
- object: 'text',
14
- leaves: [
15
- {
16
- text: 'Foo',
17
- },
18
- ],
19
- },
20
- ],
21
- },
22
- value: {},
23
- };
24
-
25
- beforeEach(() => {
26
- onChange.mockClear();
27
- });
28
-
29
- describe('rendering', () => {
30
- it('renders with default props', () => {
31
- const { container } = render(<Mask {...defaultProps} />);
32
- expect(container.firstChild).toBeInTheDocument();
33
- });
34
-
35
- it('renders text content', () => {
36
- render(<Mask {...defaultProps} />);
37
- expect(screen.getByText('Foo')).toBeInTheDocument();
38
- });
39
-
40
- it('renders a paragraph element', () => {
41
- const { container } = render(
42
- <Mask
43
- {...defaultProps}
44
- layout={{
45
- nodes: [
46
- {
47
- type: 'p',
48
- nodes: [
49
- {
50
- object: 'text',
51
- leaves: [
52
- {
53
- text: 'Foo',
54
- },
55
- ],
56
- },
57
- ],
58
- },
59
- ],
60
- }}
61
- />,
62
- );
63
-
64
- // Paragraph is rendered as a styled div, not a <p> tag
65
- expect(screen.getByText('Foo')).toBeInTheDocument();
66
- });
67
-
68
- it('renders nested div and paragraph', () => {
69
- const { container } = render(
70
- <Mask
71
- {...defaultProps}
72
- layout={{
73
- nodes: [
74
- {
75
- type: 'div',
76
- data: {
77
- attributes: {},
78
- },
79
- nodes: [
80
- {
81
- type: 'p',
82
- data: {
83
- attributes: {},
84
- },
85
- nodes: [
86
- {
87
- object: 'text',
88
- leaves: [
89
- {
90
- text: 'Foo',
91
- },
92
- ],
93
- },
94
- ],
95
- },
96
- ],
97
- },
98
- ],
99
- }}
100
- />,
101
- );
102
-
103
- expect(container.querySelector('div')).toBeInTheDocument();
104
- // Paragraph is rendered as a styled div, not a <p> tag
105
- expect(screen.getByText('Foo')).toBeInTheDocument();
106
- });
107
-
108
- it('renders text with italic marks', () => {
109
- const { container } = render(
110
- <Mask
111
- {...defaultProps}
112
- layout={{
113
- nodes: [
114
- {
115
- leaves: [{ text: 'Foo ' }],
116
- object: 'text',
117
- },
118
- {
119
- leaves: [
120
- {
121
- marks: [
122
- {
123
- data: undefined,
124
- type: 'italic',
125
- },
126
- ],
127
- text: 'x',
128
- },
129
- ],
130
- object: 'text',
131
- },
132
- {
133
- leaves: [{ text: ' bar' }],
134
- object: 'text',
135
- },
136
- ],
137
- object: 'block',
138
- type: 'div',
139
- }}
140
- />,
141
- );
142
-
143
- // Text "Foo " is split with spaces, use regex
144
- expect(screen.getByText(/Foo/)).toBeInTheDocument();
145
- expect(screen.getByText('x')).toBeInTheDocument();
146
- expect(screen.getByText(/bar/)).toBeInTheDocument();
147
- // Check for italic/em element
148
- const em = container.querySelector('em, i');
149
- expect(em).toBeInTheDocument();
150
- expect(em.textContent).toBe('x');
151
- });
152
-
153
- it('renders tbody without extra space', () => {
154
- const da = () => ({ data: { attributes: {} } });
155
- const { container } = render(
156
- <Mask
157
- {...defaultProps}
158
- layout={{
159
- nodes: [
160
- {
161
- type: 'table',
162
- ...da(),
163
- nodes: [
164
- {
165
- type: 'tbody',
166
- ...da(),
167
- nodes: [
168
- {
169
- object: 'text',
170
- leaves: [{ text: ' ' }],
171
- },
172
- { type: 'tr', ...da(), nodes: [] },
173
- ],
174
- },
175
- ],
176
- },
177
- ],
178
- }}
179
- />,
180
- );
181
-
182
- expect(container.querySelector('table')).toBeInTheDocument();
183
- expect(container.querySelector('tbody')).toBeInTheDocument();
184
- expect(container.querySelector('tr')).toBeInTheDocument();
185
- });
186
- });
187
-
188
- describe('spacer rendering for DnD components', () => {
189
- it('adds spacers before and after DnD blank components', () => {
190
- const mockRenderChildren = jest.fn((n) => {
191
- if (n.data?.dataset?.component === 'blank') {
192
- return <span data-testid="blank-component">Blank</span>;
193
- }
194
- return null;
195
- });
196
-
197
- const { container } = render(
198
- <Mask
199
- {...defaultProps}
200
- renderChildren={mockRenderChildren}
201
- layout={{
202
- nodes: [
203
- {
204
- type: 'div',
205
- data: {
206
- dataset: { component: 'blank' },
207
- attributes: {},
208
- },
209
- nodes: [],
210
- },
211
- ],
212
- }}
213
- />,
214
- );
215
-
216
- // Check that renderChildren was called and spacers are present
217
- // Count all children in the container - should be: spacer + blank + spacer = 3 elements
218
- const maskContainer = container.firstChild;
219
- expect(maskContainer.childNodes.length).toBe(3);
220
- expect(screen.getByTestId('blank-component')).toBeInTheDocument();
221
- });
222
-
223
- it('does not add spacers for non-DnD components', () => {
224
- const mockRenderChildren = jest.fn((n) => {
225
- return <span data-testid="regular-component">Regular</span>;
226
- });
227
-
228
- const { container } = render(
229
- <Mask
230
- {...defaultProps}
231
- renderChildren={mockRenderChildren}
232
- layout={{
233
- nodes: [
234
- {
235
- type: 'div',
236
- data: {
237
- attributes: {},
238
- },
239
- nodes: [],
240
- },
241
- ],
242
- }}
243
- />,
244
- );
245
-
246
- // Should not have spacers - only the regular component
247
- const maskContainer = container.firstChild;
248
- expect(maskContainer.childNodes.length).toBe(1);
249
- expect(screen.getByTestId('regular-component')).toBeInTheDocument();
250
- });
251
-
252
- it('adds spacers regardless of parent node type', () => {
253
- const mockRenderChildren = jest.fn((n) => {
254
- if (n.data?.dataset?.component === 'blank') {
255
- return <span data-testid="blank-in-td">Blank in TD</span>;
256
- }
257
- return null;
258
- });
259
-
260
- const { container } = render(
261
- <Mask
262
- {...defaultProps}
263
- renderChildren={mockRenderChildren}
264
- elementType="drag-in-the-blank"
265
- layout={{
266
- nodes: [
267
- {
268
- type: 'table',
269
- data: { attributes: {} },
270
- nodes: [
271
- {
272
- type: 'tbody',
273
- data: { attributes: {} },
274
- nodes: [
275
- {
276
- type: 'tr',
277
- data: { attributes: {} },
278
- nodes: [
279
- {
280
- type: 'td',
281
- data: { attributes: {} },
282
- nodes: [
283
- {
284
- type: 'div',
285
- data: {
286
- dataset: { component: 'blank' },
287
- attributes: {},
288
- },
289
- nodes: [],
290
- },
291
- ],
292
- },
293
- ],
294
- },
295
- ],
296
- },
297
- ],
298
- },
299
- ],
300
- }}
301
- />,
302
- );
303
-
304
- // Should have spacers even inside td element
305
- const td = container.querySelector('td');
306
- expect(td.childNodes.length).toBe(3); // spacer + blank + spacer
307
- expect(screen.getByTestId('blank-in-td')).toBeInTheDocument();
308
- });
309
-
310
- it('does not add spacers for text content', () => {
311
- const { container } = render(
312
- <Mask
313
- {...defaultProps}
314
- elementType="drag-in-the-blank"
315
- layout={{
316
- nodes: [
317
- {
318
- object: 'text',
319
- leaves: [
320
- {
321
- text: 'Some text',
322
- },
323
- ],
324
- },
325
- ],
326
- }}
327
- />,
328
- );
329
-
330
- // Should not have spacers for plain text - just text node
331
- const maskContainer = container.firstChild;
332
- expect(maskContainer.childNodes.length).toBe(1);
333
- expect(maskContainer.childNodes[0].nodeType).toBe(Node.TEXT_NODE);
334
- expect(screen.getByText('Some text')).toBeInTheDocument();
335
- });
336
-
337
- it('handles multiple DnD components with correct spacer placement', () => {
338
- const mockRenderChildren = jest.fn((n) => {
339
- if (n.data?.dataset?.component === 'blank') {
340
- return <span data-testid={`blank-${n.data.testId}`}>Blank</span>;
341
- }
342
- return null;
343
- });
344
-
345
- const { container } = render(
346
- <Mask
347
- {...defaultProps}
348
- renderChildren={mockRenderChildren}
349
- layout={{
350
- nodes: [
351
- {
352
- type: 'div',
353
- data: {
354
- dataset: { component: 'blank' },
355
- attributes: {},
356
- testId: '1',
357
- },
358
- nodes: [],
359
- },
360
- {
361
- type: 'div',
362
- data: {
363
- dataset: { component: 'blank' },
364
- attributes: {},
365
- testId: '2',
366
- },
367
- nodes: [],
368
- },
369
- ],
370
- }}
371
- />,
372
- );
373
-
374
- // Should have 2 spacers per component = 4 spacers + 2 blanks = 6 total children
375
- const maskContainer = container.firstChild;
376
- expect(maskContainer.childNodes.length).toBe(6);
377
- expect(screen.getByTestId('blank-1')).toBeInTheDocument();
378
- expect(screen.getByTestId('blank-2')).toBeInTheDocument();
379
- });
380
- });
381
- });
@@ -1,54 +0,0 @@
1
- import { deserialize } from '../serialization';
2
-
3
- describe('serialization', () => {
4
- it('ignores comments', () => {
5
- const out = deserialize(`<!-- hi -->`);
6
- expect(out.document.nodes[0]).toEqual(expect.objectContaining({ type: 'span' }));
7
- });
8
-
9
- it('ignores comments', () => {
10
- const out = deserialize(`<!-- hi --><div>foo</div>`);
11
- expect(out.document.nodes[0]).toEqual(
12
- expect.objectContaining({
13
- type: 'div',
14
- nodes: [
15
- expect.objectContaining({
16
- object: 'text',
17
- leaves: [{ text: 'foo' }],
18
- }),
19
- ],
20
- }),
21
- );
22
- });
23
-
24
- it('deserializes an em', () => {
25
- const out = deserialize(`<!-- hi --><div> <em>x</em> </div>`);
26
- expect(out.document.nodes[0]).toEqual(
27
- expect.objectContaining({
28
- type: 'div',
29
- nodes: [
30
- expect.objectContaining({
31
- object: 'text',
32
- }),
33
- expect.objectContaining({
34
- leaves: [
35
- {
36
- marks: [
37
- {
38
- data: undefined,
39
- type: 'italic',
40
- },
41
- ],
42
- text: 'x',
43
- },
44
- ],
45
- object: 'text',
46
- }),
47
- expect.objectContaining({
48
- object: 'text',
49
- }),
50
- ],
51
- }),
52
- );
53
- });
54
- });
@@ -1 +0,0 @@
1
- export const choice = (v, id) => ({ label: v, value: v, id });
@@ -1,76 +0,0 @@
1
- import * as React from 'react';
2
- import { render, screen } from '@testing-library/react';
3
- import userEvent from '@testing-library/user-event';
4
- import { withMask } from '../with-mask';
5
-
6
- describe('WithMask', () => {
7
- const onChange = jest.fn();
8
- const defaultProps = {
9
- markup: '<p>Foo bar {{0}} over the moon;</p>',
10
- value: {
11
- 0: 'blank',
12
- },
13
- onChange,
14
- };
15
-
16
- const Masked = withMask('foo', (props) => (node) => {
17
- const dataset = node.data ? node.data.dataset || {} : {};
18
-
19
- if (dataset.component === 'foo') {
20
- return <input type="text" data-testid="masked-input" defaultValue="Foo" onChange={props.onChange} />;
21
- }
22
- });
23
-
24
- beforeEach(() => {
25
- onChange.mockClear();
26
- });
27
-
28
- describe('rendering', () => {
29
- it('renders with default props', () => {
30
- const { container } = render(<Masked {...defaultProps} />);
31
- expect(container.firstChild).toBeInTheDocument();
32
- });
33
-
34
- it('renders markup content', () => {
35
- render(<Masked {...defaultProps} />);
36
- expect(screen.getByText(/Foo bar/)).toBeInTheDocument();
37
- });
38
-
39
- it('renders paragraph content', () => {
40
- const { container } = render(<Masked {...defaultProps} />);
41
- // Paragraph is rendered as a styled div, not a <p> tag
42
- expect(container.firstChild).toBeInTheDocument();
43
- expect(screen.getByText(/Foo bar/)).toBeInTheDocument();
44
- });
45
- });
46
-
47
- describe('onChange handler', () => {
48
- it('calls onChange when value changes', async () => {
49
- const user = userEvent.setup();
50
- render(<Masked {...defaultProps} />);
51
-
52
- const input = screen.queryByTestId('masked-input');
53
- if (input) {
54
- await user.clear(input);
55
- await user.type(input, 'ceva');
56
-
57
- expect(onChange).toHaveBeenCalled();
58
- }
59
- });
60
-
61
- it('passes event to onChange', async () => {
62
- const user = userEvent.setup();
63
- render(<Masked {...defaultProps} />);
64
-
65
- const input = screen.queryByTestId('masked-input');
66
- if (input) {
67
- await user.clear(input);
68
- await user.type(input, 'test');
69
-
70
- expect(onChange).toHaveBeenCalled();
71
- const lastCall = onChange.mock.calls[onChange.mock.calls.length - 1][0];
72
- expect(lastCall).toHaveProperty('target');
73
- }
74
- });
75
- });
76
- });
@@ -1,109 +0,0 @@
1
- import * as React from 'react';
2
- import { render, screen } from '@testing-library/react';
3
- import Choice from '../choice';
4
- import { choice } from '../../__tests__/utils';
5
- import Choices from '../index';
6
-
7
- // Collect the CSS rules emotion/MUI injected into the document (jsdom uses insertRule).
8
- const collectEmotionRules = () => {
9
- const rules = [];
10
- for (const sheet of Array.from(document.styleSheets)) {
11
- try {
12
- for (const rule of Array.from(sheet.cssRules)) {
13
- rules.push(rule.cssText);
14
- }
15
- } catch (e) {
16
- /* inaccessible stylesheet */
17
- }
18
- }
19
- return rules;
20
- };
21
-
22
- // Mock @dnd-kit hooks to avoid DndContext requirement
23
- jest.mock('@dnd-kit/core', () => ({
24
- useDraggable: jest.fn(() => ({
25
- attributes: {},
26
- listeners: {},
27
- setNodeRef: jest.fn(),
28
- isDragging: false,
29
- })),
30
- useDroppable: jest.fn(() => ({
31
- setNodeRef: jest.fn(),
32
- isOver: false,
33
- active: null,
34
- })),
35
- }));
36
-
37
- describe('index', () => {
38
- describe('Choices', () => {
39
- const defaultProps = {
40
- disabled: false,
41
- choices: [choice('Jumped', '0'), choice('Laughed', '1'), choice('Spoon', '2')],
42
- choicePosition: 'below',
43
- instanceId: 'test-instance',
44
- };
45
-
46
- it('renders correctly with default props', () => {
47
- const { container } = render(<Choices {...defaultProps} />);
48
- expect(container.firstChild).toBeInTheDocument();
49
- expect(screen.getByText('Jumped')).toBeInTheDocument();
50
- expect(screen.getByText('Laughed')).toBeInTheDocument();
51
- expect(screen.getByText('Spoon')).toBeInTheDocument();
52
- });
53
-
54
- it('renders correctly with disabled prop as true', () => {
55
- const { container } = render(<Choices {...defaultProps} disabled={true} />);
56
- expect(container.firstChild).toBeInTheDocument();
57
- });
58
-
59
- it('renders without duplicates', () => {
60
- const { container } = render(<Choices {...defaultProps} duplicates={undefined} value={{ 0: '0', 1: '1' }} />);
61
- expect(container.firstChild).toBeInTheDocument();
62
- });
63
-
64
- it('renders with duplicates', () => {
65
- const { container } = render(<Choices {...defaultProps} duplicates={true} value={{ 0: '0', 1: '1' }} />);
66
- expect(container.firstChild).toBeInTheDocument();
67
- });
68
- });
69
-
70
- describe('Choice', () => {
71
- const defaultProps = {
72
- disabled: false,
73
- choice: choice('Label', '1'),
74
- instanceId: 'test-instance',
75
- };
76
-
77
- describe('render', () => {
78
- it('renders correctly with default props', () => {
79
- const { container } = render(<Choice {...defaultProps} />);
80
- expect(container.firstChild).toBeInTheDocument();
81
- expect(screen.getByText('Label')).toBeInTheDocument();
82
- });
83
-
84
- it('renders correctly with disabled prop as true', () => {
85
- const { container } = render(<Choice {...defaultProps} disabled={true} />);
86
- expect(container.firstChild).toBeInTheDocument();
87
- });
88
- });
89
-
90
- describe('fraction math styling', () => {
91
- it('enlarges numerator/denominator digits adjacent to a fraction to 120%', () => {
92
- render(<Choice {...defaultProps} />);
93
- // The new rule targets mjx-mn digits that sit next to an mjx-mfrac.
94
- const rule = collectEmotionRules().find((r) => r.includes('mjx-mn') && r.includes('mjx-mfrac'));
95
- expect(rule).toBeDefined();
96
- expect(rule).toMatch(/mjx-mn:has\(~\s*mjx-mfrac\)/);
97
- expect(rule).toMatch(/mjx-mfrac\s*~\s*mjx-mn/);
98
- expect(rule).toMatch(/font-size:\s*120%\s*!important/i);
99
- });
100
-
101
- it('keeps the existing mjx-frac 120% rule', () => {
102
- render(<Choice {...defaultProps} />);
103
- const rule = collectEmotionRules().find((r) => /(^|[^-])mjx-frac/.test(r) && !r.includes('mjx-mfrac'));
104
- expect(rule).toBeDefined();
105
- expect(rule).toMatch(/font-size:\s*120%\s*!important/i);
106
- });
107
- });
108
- });
109
- });