@pie-lib/text-select 3.0.2-next.2 → 3.0.3-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dist/index.d.ts +15 -0
  2. package/dist/index.js +7 -0
  3. package/dist/legend.d.ts +13 -0
  4. package/dist/legend.js +64 -0
  5. package/dist/text-select.d.ts +34 -0
  6. package/dist/text-select.js +53 -0
  7. package/dist/token-select/index.d.ts +44 -0
  8. package/dist/token-select/index.js +170 -0
  9. package/dist/token-select/token.d.ts +32 -0
  10. package/dist/token-select/token.js +134 -0
  11. package/dist/tokenizer/builder.d.ts +27 -0
  12. package/dist/tokenizer/builder.js +124 -0
  13. package/dist/tokenizer/controls.d.ts +23 -0
  14. package/dist/tokenizer/controls.js +68 -0
  15. package/dist/tokenizer/index.d.ts +35 -0
  16. package/dist/tokenizer/index.js +91 -0
  17. package/dist/tokenizer/selection-utils.d.ts +10 -0
  18. package/dist/tokenizer/selection-utils.js +18 -0
  19. package/dist/tokenizer/token-text.d.ts +27 -0
  20. package/dist/tokenizer/token-text.js +85 -0
  21. package/dist/utils.d.ts +12 -0
  22. package/dist/utils.js +21 -0
  23. package/package.json +29 -28
  24. package/CHANGELOG.json +0 -1
  25. package/CHANGELOG.md +0 -942
  26. package/LICENSE.md +0 -5
  27. package/lib/index.js +0 -57
  28. package/lib/index.js.map +0 -1
  29. package/lib/legend.js +0 -119
  30. package/lib/legend.js.map +0 -1
  31. package/lib/text-select.js +0 -105
  32. package/lib/text-select.js.map +0 -1
  33. package/lib/token-select/index.js +0 -267
  34. package/lib/token-select/index.js.map +0 -1
  35. package/lib/token-select/token.js +0 -236
  36. package/lib/token-select/token.js.map +0 -1
  37. package/lib/tokenizer/builder.js +0 -265
  38. package/lib/tokenizer/builder.js.map +0 -1
  39. package/lib/tokenizer/controls.js +0 -106
  40. package/lib/tokenizer/controls.js.map +0 -1
  41. package/lib/tokenizer/index.js +0 -147
  42. package/lib/tokenizer/index.js.map +0 -1
  43. package/lib/tokenizer/selection-utils.js +0 -55
  44. package/lib/tokenizer/selection-utils.js.map +0 -1
  45. package/lib/tokenizer/token-text.js +0 -176
  46. package/lib/tokenizer/token-text.js.map +0 -1
  47. package/lib/utils.js +0 -51
  48. package/lib/utils.js.map +0 -1
  49. package/src/__tests__/legend.test.jsx +0 -211
  50. package/src/__tests__/text-select.test.jsx +0 -44
  51. package/src/__tests__/utils.test.jsx +0 -27
  52. package/src/index.js +0 -8
  53. package/src/legend.js +0 -102
  54. package/src/text-select.jsx +0 -79
  55. package/src/token-select/__tests__/index.test.jsx +0 -623
  56. package/src/token-select/__tests__/token.test.jsx +0 -236
  57. package/src/token-select/index.jsx +0 -242
  58. package/src/token-select/token.jsx +0 -223
  59. package/src/tokenizer/__tests__/builder.test.js +0 -256
  60. package/src/tokenizer/__tests__/controls.test.jsx +0 -27
  61. package/src/tokenizer/__tests__/index.test.jsx +0 -329
  62. package/src/tokenizer/__tests__/selection-utils.test.js +0 -145
  63. package/src/tokenizer/__tests__/token-text.test.jsx +0 -318
  64. package/src/tokenizer/builder.js +0 -258
  65. package/src/tokenizer/controls.jsx +0 -71
  66. package/src/tokenizer/index.jsx +0 -144
  67. package/src/tokenizer/selection-utils.js +0 -49
  68. package/src/tokenizer/token-text.jsx +0 -135
  69. package/src/utils.js +0 -56
@@ -1,145 +0,0 @@
1
- import { clearSelection, getCaretCharacterOffsetWithin } from '../selection-utils';
2
-
3
- describe('selection-utils', () => {
4
- let selection;
5
- let range;
6
-
7
- beforeEach(() => {
8
- selection = {
9
- removeAllRanges: jest.fn(),
10
- addRange: jest.fn(),
11
- getRangeAt: jest.fn().mockReturnValue(range),
12
- };
13
- global.document.getSelection = jest.fn().mockReturnValue(selection);
14
- global.document.createRange = jest.fn();
15
- });
16
-
17
- describe('clearSelection', () => {
18
- it('calls removeAllRanges and addRange with document.getSelection', () => {
19
- const mockRange = {};
20
- global.document.createRange = jest.fn().mockReturnValue(mockRange);
21
-
22
- clearSelection();
23
-
24
- expect(selection.removeAllRanges).toBeCalled();
25
- expect(selection.addRange).toHaveBeenCalledWith(mockRange);
26
- });
27
-
28
- it('uses window.getSelection if document.getSelection is not available', () => {
29
- global.document.getSelection = undefined;
30
- const windowSelection = {
31
- removeAllRanges: jest.fn(),
32
- addRange: jest.fn(),
33
- };
34
- global.window.getSelection = jest.fn().mockReturnValue(windowSelection);
35
- const mockRange = {};
36
- global.document.createRange = jest.fn().mockReturnValue(mockRange);
37
-
38
- clearSelection();
39
-
40
- expect(windowSelection.removeAllRanges).toBeCalled();
41
- expect(windowSelection.addRange).toHaveBeenCalledWith(mockRange);
42
- });
43
-
44
- it('uses window.getSelection().empty() if removeAllRanges is not available', () => {
45
- global.document.getSelection = undefined;
46
- const windowSelection = {
47
- empty: jest.fn(),
48
- };
49
- global.window.getSelection = jest.fn().mockReturnValue(windowSelection);
50
-
51
- clearSelection();
52
-
53
- expect(windowSelection.empty).toBeCalled();
54
- });
55
-
56
- it('uses document.selection.empty() for IE8- compatibility', () => {
57
- global.document.getSelection = undefined;
58
- global.window.getSelection = undefined;
59
- const docSelection = {
60
- empty: jest.fn(),
61
- };
62
- global.document.selection = docSelection;
63
-
64
- clearSelection();
65
-
66
- expect(docSelection.empty).toBeCalled();
67
-
68
- delete global.document.selection;
69
- });
70
- });
71
-
72
- describe('getCaretCharacterOffsetWithin', () => {
73
- let element;
74
- let mockRange;
75
- let mockSelection;
76
-
77
- beforeEach(() => {
78
- element = {
79
- ownerDocument: global.document,
80
- };
81
-
82
- mockRange = {
83
- toString: jest.fn().mockReturnValue(''),
84
- cloneRange: jest.fn(),
85
- endContainer: {},
86
- endOffset: 0,
87
- };
88
-
89
- const clonedRange = {
90
- selectNodeContents: jest.fn(),
91
- setEnd: jest.fn(),
92
- toString: jest.fn().mockReturnValue('some text'),
93
- };
94
-
95
- mockRange.cloneRange.mockReturnValue(clonedRange);
96
-
97
- mockSelection = {
98
- rangeCount: 1,
99
- getRangeAt: jest.fn().mockReturnValue(mockRange),
100
- };
101
-
102
- global.window.getSelection = jest.fn().mockReturnValue(mockSelection);
103
- });
104
-
105
- it('returns 0 when rangeCount is 0', () => {
106
- mockSelection.rangeCount = 0;
107
-
108
- const offset = getCaretCharacterOffsetWithin(element);
109
-
110
- expect(offset).toBe(0);
111
- });
112
-
113
- it('returns caret offset for unselected text', () => {
114
- mockRange.toString.mockReturnValue('');
115
- const clonedRange = mockRange.cloneRange();
116
- clonedRange.toString.mockReturnValue('hello');
117
-
118
- const offset = getCaretCharacterOffsetWithin(element);
119
-
120
- expect(offset).toBe(5);
121
- });
122
-
123
- it('returns caret offset when text is selected', () => {
124
- mockRange.toString.mockReturnValue('world');
125
- const clonedRange = mockRange.cloneRange();
126
- clonedRange.toString.mockReturnValue('hello world');
127
-
128
- const offset = getCaretCharacterOffsetWithin(element);
129
-
130
- expect(offset).toBe(6); // 11 - 5 = 6
131
- });
132
-
133
- it('returns 0 when selection type is Control in IE', () => {
134
- global.window.getSelection = undefined;
135
- const docSelection = {
136
- type: 'Control',
137
- };
138
- element.ownerDocument.selection = docSelection;
139
-
140
- const offset = getCaretCharacterOffsetWithin(element);
141
-
142
- expect(offset).toBe(0);
143
- });
144
- });
145
- });
@@ -1,318 +0,0 @@
1
- import React from 'react';
2
- import { fireEvent, render } from '@testing-library/react';
3
- import TokenText, { Text } from '../token-text';
4
-
5
- const tokens = () => [
6
- {
7
- start: 0,
8
- end: 5,
9
- text: 'lorem',
10
- },
11
- ];
12
-
13
- describe('token-text', () => {
14
- const defaultProps = {
15
- onTokenClick: jest.fn(),
16
- onSelectToken: jest.fn(),
17
- text: `lorem ipsum dolor`,
18
- tokens: tokens(),
19
- };
20
-
21
- beforeEach(() => {
22
- jest.clearAllMocks();
23
- if (typeof global.window !== 'undefined') {
24
- global.window.getSelection = jest.fn().mockReturnValue({
25
- toString: () => '',
26
- });
27
- }
28
- });
29
-
30
- describe('rendering', () => {
31
- it('renders with text and tokens', () => {
32
- const { container } = render(<TokenText {...defaultProps} />);
33
- expect(container.firstChild).toBeInTheDocument();
34
- });
35
-
36
- it('renders with empty tokens', () => {
37
- const { container } = render(<TokenText {...defaultProps} tokens={[]} />);
38
- expect(container.firstChild).toBeInTheDocument();
39
- });
40
-
41
- it('renders with multiline text', () => {
42
- const { container } = render(<TokenText {...defaultProps} text="Line 1\nLine 2\nLine 3" />);
43
- expect(container.firstChild).toBeInTheDocument();
44
- });
45
-
46
- it('renders with custom className', () => {
47
- const { container } = render(<TokenText {...defaultProps} className="custom-class" />);
48
- expect(container.firstChild).toHaveClass('custom-class');
49
- });
50
-
51
- it('renders normalized tokens', () => {
52
- const { container } = render(<TokenText {...defaultProps} />);
53
- const spans = container.querySelectorAll('span');
54
- expect(spans.length).toBeGreaterThan(0);
55
- });
56
- });
57
-
58
- describe('Text component', () => {
59
- it('renders plain text when not predefined', () => {
60
- const { container } = render(<Text text="hello" predefined={false} />);
61
- expect(container.querySelector('span')).toBeInTheDocument();
62
- expect(container.querySelector('span')).not.toHaveClass('predefined');
63
- });
64
-
65
- it('renders predefined text with correct class', () => {
66
- const { container } = render(<Text text="hello" predefined={true} onClick={jest.fn()} />);
67
- expect(container.querySelector('span')).toHaveClass('predefined');
68
- });
69
-
70
- it('renders correct text with both classes', () => {
71
- const { container } = render(<Text text="hello" predefined={true} correct={true} onClick={jest.fn()} />);
72
- const span = container.querySelector('span');
73
- expect(span).toHaveClass('predefined');
74
- expect(span).toHaveClass('correct');
75
- });
76
-
77
- it('calls onClick when predefined text is clicked', () => {
78
- const onClick = jest.fn();
79
- const { container } = render(<Text text="hello" predefined={true} onClick={onClick} />);
80
- const span = container.querySelector('span');
81
- fireEvent.click(span);
82
- expect(onClick).toHaveBeenCalled();
83
- });
84
- });
85
-
86
- describe('token interaction', () => {
87
- it('calls onTokenClick when clicking on a predefined token', () => {
88
- const onTokenClick = jest.fn();
89
- const { container } = render(<TokenText {...defaultProps} onTokenClick={onTokenClick} />);
90
- const predefinedSpan = container.querySelector('.predefined');
91
- if (predefinedSpan) {
92
- fireEvent.click(predefinedSpan);
93
- expect(onTokenClick).toHaveBeenCalled();
94
- }
95
- });
96
-
97
- it('handles click without window object (SSR)', () => {
98
- const originalWindow = global.window;
99
- global.window = undefined;
100
-
101
- const { container } = render(<TokenText {...defaultProps} />);
102
- expect(() => fireEvent.click(container.firstChild)).not.toThrow();
103
-
104
- global.window = originalWindow;
105
- });
106
- });
107
-
108
- describe('text selection', () => {
109
- beforeEach(() => {
110
- global.window.getSelection = jest.fn();
111
- });
112
-
113
- it('does not call onSelectToken when no text is selected', () => {
114
- const onSelectToken = jest.fn();
115
- global.window.getSelection.mockReturnValue({
116
- toString: () => '',
117
- });
118
-
119
- const { container } = render(<TokenText {...defaultProps} onSelectToken={onSelectToken} />);
120
- fireEvent.click(container.firstChild);
121
-
122
- expect(onSelectToken).not.toHaveBeenCalled();
123
- });
124
-
125
- it('does not call onSelectToken for newline character selection', () => {
126
- const onSelectToken = jest.fn();
127
- global.window.getSelection.mockReturnValue({
128
- toString: () => '\n',
129
- });
130
-
131
- const { container } = render(<TokenText {...defaultProps} onSelectToken={onSelectToken} />);
132
- fireEvent.click(container.firstChild);
133
-
134
- expect(onSelectToken).not.toHaveBeenCalled();
135
- });
136
-
137
- it('does not call onSelectToken for space character selection', () => {
138
- const onSelectToken = jest.fn();
139
- global.window.getSelection.mockReturnValue({
140
- toString: () => ' ',
141
- });
142
-
143
- const { container } = render(<TokenText {...defaultProps} onSelectToken={onSelectToken} />);
144
- fireEvent.click(container.firstChild);
145
-
146
- expect(onSelectToken).not.toHaveBeenCalled();
147
- });
148
-
149
- it('does not call onSelectToken for tab character selection', () => {
150
- const onSelectToken = jest.fn();
151
- global.window.getSelection.mockReturnValue({
152
- toString: () => '\t',
153
- });
154
-
155
- const { container } = render(<TokenText {...defaultProps} onSelectToken={onSelectToken} />);
156
- fireEvent.click(container.firstChild);
157
-
158
- expect(onSelectToken).not.toHaveBeenCalled();
159
- });
160
- });
161
-
162
- describe('props handling', () => {
163
- it('accepts and uses all required props', () => {
164
- const props = {
165
- text: 'test text',
166
- tokens: [],
167
- onTokenClick: jest.fn(),
168
- onSelectToken: jest.fn(),
169
- };
170
-
171
- const { container } = render(<TokenText {...props} />);
172
- expect(container.firstChild).toBeInTheDocument();
173
- });
174
-
175
- it('handles multiple tokens', () => {
176
- const multipleTokens = [
177
- { start: 0, end: 5, text: 'lorem' },
178
- { start: 6, end: 11, text: 'ipsum' },
179
- ];
180
-
181
- const { container } = render(<TokenText {...defaultProps} tokens={multipleTokens} />);
182
- expect(container.firstChild).toBeInTheDocument();
183
- });
184
- });
185
-
186
- describe('text selection with valid text', () => {
187
- let mockGetCaretCharacterOffsetWithin;
188
-
189
- beforeEach(() => {
190
- mockGetCaretCharacterOffsetWithin = jest.fn().mockReturnValue(6);
191
- jest.mock('../selection-utils', () => ({
192
- clearSelection: jest.fn(),
193
- getCaretCharacterOffsetWithin: mockGetCaretCharacterOffsetWithin,
194
- }));
195
- });
196
-
197
- it('calls onSelectToken when valid text is selected without overlap', () => {
198
- const onSelectToken = jest.fn();
199
- const clearSelectionMock = jest.fn();
200
-
201
- const selectionUtils = require('../selection-utils');
202
- selectionUtils.clearSelection = clearSelectionMock;
203
- selectionUtils.getCaretCharacterOffsetWithin = jest.fn().mockReturnValue(6);
204
-
205
- global.window.getSelection.mockReturnValue({
206
- toString: () => 'ipsum',
207
- });
208
-
209
- const { container } = render(
210
- <TokenText {...defaultProps} text="lorem ipsum dolor" tokens={[]} onSelectToken={onSelectToken} />,
211
- );
212
-
213
- fireEvent.click(container.firstChild);
214
-
215
- expect(onSelectToken).toHaveBeenCalledWith(
216
- expect.objectContaining({
217
- text: 'ipsum',
218
- start: expect.any(Number),
219
- end: expect.any(Number),
220
- }),
221
- expect.any(Array),
222
- );
223
- });
224
-
225
- it('handles text selection at the end of text', () => {
226
- const onSelectToken = jest.fn();
227
- const selectionUtils = require('../selection-utils');
228
- selectionUtils.getCaretCharacterOffsetWithin = jest.fn().mockReturnValue(12);
229
-
230
- global.window.getSelection.mockReturnValue({
231
- toString: () => 'dolor',
232
- });
233
-
234
- const { container } = render(
235
- <TokenText {...defaultProps} text="lorem ipsum dolor" tokens={[]} onSelectToken={onSelectToken} />,
236
- );
237
-
238
- fireEvent.click(container.firstChild);
239
-
240
- expect(onSelectToken).toHaveBeenCalled();
241
- });
242
-
243
- it('handles newline offset calculation', () => {
244
- const onSelectToken = jest.fn();
245
- const selectionUtils = require('../selection-utils');
246
- selectionUtils.getCaretCharacterOffsetWithin = jest.fn().mockReturnValue(0);
247
-
248
- global.window.getSelection.mockReturnValue({
249
- toString: () => 'ipsum',
250
- });
251
-
252
- const { container } = render(
253
- <TokenText {...defaultProps} text="lorem\nipsum dolor" tokens={[]} onSelectToken={onSelectToken} />,
254
- );
255
-
256
- fireEvent.click(container.firstChild);
257
-
258
- if (onSelectToken.mock.calls.length > 0) {
259
- const token = onSelectToken.mock.calls[0][0];
260
- expect(token.text).toBe('ipsum');
261
- }
262
- });
263
-
264
- it('calls onSelectToken with tokensToRemove for surrounded tokens', () => {
265
- const onSelectToken = jest.fn();
266
- const selectionUtils = require('../selection-utils');
267
- selectionUtils.getCaretCharacterOffsetWithin = jest.fn().mockReturnValue(0);
268
-
269
- global.window.getSelection.mockReturnValue({
270
- toString: () => 'lorem ipsum',
271
- });
272
-
273
- const existingTokens = [{ start: 6, end: 11, text: 'ipsum' }];
274
-
275
- const { container } = render(
276
- <TokenText {...defaultProps} text="lorem ipsum dolor" tokens={existingTokens} onSelectToken={onSelectToken} />,
277
- );
278
-
279
- fireEvent.click(container.firstChild);
280
-
281
- expect(onSelectToken).toHaveBeenCalled();
282
- });
283
-
284
- it('does not call onSelectToken when root is not available', () => {
285
- const onSelectToken = jest.fn();
286
-
287
- global.window.getSelection.mockReturnValue({
288
- toString: () => 'ipsum',
289
- });
290
-
291
- const { container } = render(
292
- <TokenText {...defaultProps} text="lorem ipsum" tokens={[]} onSelectToken={onSelectToken} />,
293
- );
294
-
295
- const instance = container.querySelector('div');
296
- if (instance) {
297
- fireEvent.click(instance);
298
- }
299
- });
300
- });
301
-
302
- describe('Text component edge cases', () => {
303
- it('handles null text gracefully', () => {
304
- const { container } = render(<Text text={null} predefined={false} />);
305
- expect(container.querySelector('span')).toBeInTheDocument();
306
- });
307
-
308
- it('handles undefined text gracefully', () => {
309
- const { container } = render(<Text text={undefined} predefined={false} />);
310
- expect(container.querySelector('span')).toBeInTheDocument();
311
- });
312
-
313
- it('renders empty string text', () => {
314
- const { container } = render(<Text text="" predefined={false} />);
315
- expect(container.querySelector('span')).toBeInTheDocument();
316
- });
317
- });
318
- });