@imposium-hub/components 2.15.0-2 → 2.16.0-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/components/app-wrapper/AppWrapper.d.ts +0 -1
- package/dist/cjs/components/app-wrapper/AppWrapper.js +1 -1
- package/dist/cjs/components/app-wrapper/AppWrapper.js.map +1 -1
- package/dist/cjs/components/asset-details/AssetDetails.js +1 -1
- package/dist/cjs/components/assets/AssetsTableComplexTagCell.js +1 -1
- package/dist/cjs/components/assets/AssetsTableTagsPivot.js +1 -1
- package/dist/cjs/components/assets/AssetsTypeIcon.js +2 -0
- package/dist/cjs/components/assets/AssetsTypeIcon.js.map +1 -1
- package/dist/cjs/components/assets/AssetsUploadMenu.js +1 -1
- package/dist/cjs/components/change-report/ChangeReportTree.js +65 -10
- package/dist/cjs/components/change-report/ChangeReportTree.js.map +1 -1
- package/dist/cjs/components/controlled-list/ControlledList.stories.js +1 -1
- package/dist/cjs/components/number-field/NumberField.d.ts +2 -2
- package/dist/cjs/components/number-field/NumberField.js +7 -4
- package/dist/cjs/components/number-field/NumberField.js.map +1 -1
- package/dist/cjs/components/publish-wizard/PublishWizard.js +36 -38
- package/dist/cjs/components/publish-wizard/PublishWizard.js.map +1 -1
- package/dist/cjs/components/tag/Tag.js.map +1 -1
- package/dist/cjs/components/tag/Tag.test.js.map +1 -1
- package/dist/cjs/constants/copy.d.ts +1 -0
- package/dist/cjs/constants/copy.js +3 -2
- package/dist/cjs/constants/copy.js.map +1 -1
- package/dist/cjs/constants/icons.d.ts +5 -0
- package/dist/cjs/constants/icons.js +12 -2
- package/dist/cjs/constants/icons.js.map +1 -1
- package/dist/cjs/constants/snippets.d.ts +2 -0
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/redux/actions/publish.d.ts +1 -1
- package/dist/cjs/redux/actions/publish.js +2 -2
- package/dist/cjs/redux/actions/publish.js.map +1 -1
- package/dist/esm/components/app-wrapper/AppWrapper.d.ts +0 -1
- package/dist/esm/components/app-wrapper/AppWrapper.js +1 -1
- package/dist/esm/components/app-wrapper/AppWrapper.js.map +1 -1
- package/dist/esm/components/asset-details/AssetDetails.js +1 -1
- package/dist/esm/components/assets/AssetsTableComplexTagCell.js +1 -1
- package/dist/esm/components/assets/AssetsTableTagsPivot.js +1 -1
- package/dist/esm/components/assets/AssetsTypeIcon.js +2 -0
- package/dist/esm/components/assets/AssetsTypeIcon.js.map +1 -1
- package/dist/esm/components/assets/AssetsUploadMenu.js +1 -1
- package/dist/esm/components/change-report/ChangeReportTree.js +64 -10
- package/dist/esm/components/change-report/ChangeReportTree.js.map +1 -1
- package/dist/esm/components/controlled-list/ControlledList.stories.js +1 -1
- package/dist/esm/components/number-field/NumberField.d.ts +2 -2
- package/dist/esm/components/number-field/NumberField.js +5 -4
- package/dist/esm/components/number-field/NumberField.js.map +1 -1
- package/dist/esm/components/publish-wizard/PublishWizard.js +36 -38
- package/dist/esm/components/publish-wizard/PublishWizard.js.map +1 -1
- package/dist/esm/components/tag/Tag.js.map +1 -1
- package/dist/esm/components/tag/Tag.test.js.map +1 -1
- package/dist/esm/constants/copy.d.ts +1 -0
- package/dist/esm/constants/copy.js +3 -2
- package/dist/esm/constants/copy.js.map +1 -1
- package/dist/esm/constants/icons.d.ts +5 -0
- package/dist/esm/constants/icons.js +10 -0
- package/dist/esm/constants/icons.js.map +1 -1
- package/dist/esm/constants/snippets.d.ts +2 -0
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/redux/actions/publish.d.ts +1 -1
- package/dist/esm/redux/actions/publish.js +2 -2
- package/dist/esm/redux/actions/publish.js.map +1 -1
- package/dist/styles.css +54 -10
- package/dist/styles.less +65 -13
- package/less/components/change-report.less +50 -9
- package/less/components/publish-wizard.less +15 -4
- package/package.json +5 -1
- package/src/components/advanced-number-field/AdvancedNumberField.test.tsx +704 -0
- package/src/components/anchor-field/AnchorField.test.tsx +130 -0
- package/src/components/app-wrapper/AppWrapper.tsx +1 -6
- package/src/components/asset-details/AssetDetails.test.tsx +494 -0
- package/src/components/asset-details/AssetDetails.tsx +1 -1
- package/src/components/assets/AssetField.test.tsx +439 -0
- package/src/components/assets/AssetsTableAssetIdCell.test.tsx +134 -0
- package/src/components/assets/AssetsTableAssetIdFilter.test.tsx +95 -0
- package/src/components/assets/AssetsTableComplexTagCell.test.tsx +159 -0
- package/src/components/assets/AssetsTableComplexTagCell.tsx +1 -1
- package/src/components/assets/AssetsTableDateCell.test.tsx +106 -0
- package/src/components/assets/AssetsTableTagsPivot.tsx +1 -1
- package/src/components/assets/AssetsTypeIcon.tsx +2 -0
- package/src/components/assets/AssetsUploadMenu.tsx +1 -1
- package/src/components/change-report/ChangeReportTree.tsx +104 -16
- package/src/components/controlled-list/ControlledList.stories.tsx +1 -1
- package/src/components/number-field/NumberField.test.tsx +383 -0
- package/src/components/number-field/NumberField.tsx +15 -9
- package/src/components/publish-wizard/PublishWizard.tsx +52 -53
- package/src/components/text-field/TextField.test.tsx +988 -0
- package/src/constants/copy.ts +3 -2
- package/src/constants/icons.tsx +10 -0
- package/src/constants/snippets.ts +2 -0
- package/src/index.ts +1 -1
- package/src/redux/actions/publish.ts +7 -2
- package/src/test/setup.ts +91 -0
- package/src/test/utils.tsx +44 -0
- package/tsconfig.eslint.json +8 -0
- package/tsconfig.json +1 -1
- package/vitest.config.ts +31 -0
- package/src/components/service-icon/ServiceIcon.test.tsx +0 -0
- /package/src/components/{Tag → tag}/Tag.test.tsx +0 -0
- /package/src/components/{Tag → tag}/Tag.tsx +0 -0
|
@@ -0,0 +1,988 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
|
+
import { render, fireEvent, screen } from '../../test/utils';
|
|
4
|
+
import TextField from './TextField';
|
|
5
|
+
|
|
6
|
+
describe('TextField', () => {
|
|
7
|
+
const defaultProps = {
|
|
8
|
+
onChange: vi.fn(),
|
|
9
|
+
label: 'Test Text Field'
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
vi.clearAllMocks();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
describe('Rendering', () => {
|
|
17
|
+
it('renders without crashing', () => {
|
|
18
|
+
render(<TextField {...defaultProps} />);
|
|
19
|
+
const input = screen.getByRole('textbox');
|
|
20
|
+
expect(input).toBeDefined();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('displays the correct value', () => {
|
|
24
|
+
render(
|
|
25
|
+
<TextField
|
|
26
|
+
{...defaultProps}
|
|
27
|
+
value='test value'
|
|
28
|
+
/>
|
|
29
|
+
);
|
|
30
|
+
const input: HTMLInputElement = screen.getByRole('textbox');
|
|
31
|
+
expect(input.value).toBe('test value');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('displays placeholder when provided', () => {
|
|
35
|
+
render(
|
|
36
|
+
<TextField
|
|
37
|
+
{...defaultProps}
|
|
38
|
+
placeholder='Enter text'
|
|
39
|
+
/>
|
|
40
|
+
);
|
|
41
|
+
const input: HTMLInputElement = screen.getByRole('textbox');
|
|
42
|
+
expect(input.placeholder).toBe('Enter text');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('sets disabled state correctly', () => {
|
|
46
|
+
render(
|
|
47
|
+
<TextField
|
|
48
|
+
{...defaultProps}
|
|
49
|
+
disabled
|
|
50
|
+
/>
|
|
51
|
+
);
|
|
52
|
+
const input: HTMLInputElement = screen.getByRole('textbox');
|
|
53
|
+
expect(input.disabled).toBe(true);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('sets readOnly attribute when readOnly prop is true', () => {
|
|
57
|
+
render(
|
|
58
|
+
<TextField
|
|
59
|
+
{...defaultProps}
|
|
60
|
+
readOnly
|
|
61
|
+
/>
|
|
62
|
+
);
|
|
63
|
+
const input: HTMLInputElement = screen.getByRole('textbox');
|
|
64
|
+
expect(input.readOnly).toBe(true);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('applies maxLength attribute when provided', () => {
|
|
68
|
+
render(
|
|
69
|
+
<TextField
|
|
70
|
+
{...defaultProps}
|
|
71
|
+
maxLength={50}
|
|
72
|
+
/>
|
|
73
|
+
);
|
|
74
|
+
const input: HTMLInputElement = screen.getByRole('textbox');
|
|
75
|
+
expect(input.maxLength).toBe(50);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('focuses input on mount when focusOnMount is true', () => {
|
|
79
|
+
render(
|
|
80
|
+
<TextField
|
|
81
|
+
{...defaultProps}
|
|
82
|
+
focusOnMount
|
|
83
|
+
/>
|
|
84
|
+
);
|
|
85
|
+
const input: HTMLInputElement = screen.getByRole('textbox');
|
|
86
|
+
expect(document.activeElement).toBe(input);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe('Basic Interactions', () => {
|
|
91
|
+
it('calls onChange when value changes in uncontrolled mode', () => {
|
|
92
|
+
const onChange = vi.fn();
|
|
93
|
+
render(
|
|
94
|
+
<TextField
|
|
95
|
+
{...defaultProps}
|
|
96
|
+
onChange={onChange}
|
|
97
|
+
/>
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
const input: HTMLInputElement = screen.getByRole('textbox');
|
|
101
|
+
fireEvent.change(input, { target: { value: 'new text' } });
|
|
102
|
+
|
|
103
|
+
expect(onChange).toHaveBeenCalledWith('new text');
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('calls onFocus when input receives focus', () => {
|
|
107
|
+
const onFocus = vi.fn();
|
|
108
|
+
render(
|
|
109
|
+
<TextField
|
|
110
|
+
{...defaultProps}
|
|
111
|
+
onFocus={onFocus}
|
|
112
|
+
/>
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
const input: HTMLInputElement = screen.getByRole('textbox');
|
|
116
|
+
fireEvent.focus(input);
|
|
117
|
+
|
|
118
|
+
expect(onFocus).toHaveBeenCalled();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('calls onBlur when input loses focus', () => {
|
|
122
|
+
const onBlur = vi.fn();
|
|
123
|
+
render(
|
|
124
|
+
<TextField
|
|
125
|
+
{...defaultProps}
|
|
126
|
+
onBlur={onBlur}
|
|
127
|
+
/>
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
const input: HTMLInputElement = screen.getByRole('textbox');
|
|
131
|
+
fireEvent.blur(input);
|
|
132
|
+
|
|
133
|
+
expect(onBlur).toHaveBeenCalled();
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
describe('Pattern Validation', () => {
|
|
138
|
+
it('prevents input that does not match pattern', () => {
|
|
139
|
+
const onChange = vi.fn();
|
|
140
|
+
const numberPattern = /^\d*$/;
|
|
141
|
+
render(
|
|
142
|
+
<TextField
|
|
143
|
+
{...defaultProps}
|
|
144
|
+
onChange={onChange}
|
|
145
|
+
pattern={numberPattern}
|
|
146
|
+
/>
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
const input: HTMLInputElement = screen.getByRole('textbox');
|
|
150
|
+
fireEvent.change(input, { target: { value: 'abc123' } });
|
|
151
|
+
|
|
152
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('allows input that matches pattern', () => {
|
|
156
|
+
const onChange = vi.fn();
|
|
157
|
+
const numberPattern = /^\d*$/;
|
|
158
|
+
render(
|
|
159
|
+
<TextField
|
|
160
|
+
{...defaultProps}
|
|
161
|
+
onChange={onChange}
|
|
162
|
+
pattern={numberPattern}
|
|
163
|
+
/>
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
const input: HTMLInputElement = screen.getByRole('textbox');
|
|
167
|
+
fireEvent.change(input, { target: { value: '123' } });
|
|
168
|
+
|
|
169
|
+
expect(onChange).toHaveBeenCalledWith('123');
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
describe('Controlled Mode', () => {
|
|
174
|
+
it('renders edit button in controlled mode', () => {
|
|
175
|
+
const { container } = render(
|
|
176
|
+
<TextField
|
|
177
|
+
{...defaultProps}
|
|
178
|
+
controlled
|
|
179
|
+
value='test'
|
|
180
|
+
onSubmit={vi.fn()}
|
|
181
|
+
/>
|
|
182
|
+
);
|
|
183
|
+
const button = container.querySelector('[data-tooltip-content="Edit"]');
|
|
184
|
+
expect(button).toBeDefined();
|
|
185
|
+
expect(button?.getAttribute('data-tooltip-content')).toBe('Edit');
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
describe('Submittable Mode', () => {
|
|
190
|
+
it('renders submit button in submittable mode', () => {
|
|
191
|
+
const { container } = render(
|
|
192
|
+
<TextField
|
|
193
|
+
{...defaultProps}
|
|
194
|
+
submittable
|
|
195
|
+
doSubmit={vi.fn()}
|
|
196
|
+
/>
|
|
197
|
+
);
|
|
198
|
+
const button = container.querySelector('[data-tooltip-content="Confirm"]');
|
|
199
|
+
expect(button).toBeDefined();
|
|
200
|
+
expect(button?.getAttribute('data-tooltip-content')).toBe('Confirm');
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('renders search icon when submittableType is search', () => {
|
|
204
|
+
const { container } = render(
|
|
205
|
+
<TextField
|
|
206
|
+
{...defaultProps}
|
|
207
|
+
submittable
|
|
208
|
+
submittableType='search'
|
|
209
|
+
doSubmit={vi.fn()}
|
|
210
|
+
/>
|
|
211
|
+
);
|
|
212
|
+
const button = container.querySelector('[data-tooltip-content="Search"]');
|
|
213
|
+
expect(button).toBeDefined();
|
|
214
|
+
expect(button?.getAttribute('data-tooltip-content')).toBe('Search');
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
describe('Loading State', () => {
|
|
219
|
+
it('disables input when loading is true', () => {
|
|
220
|
+
render(
|
|
221
|
+
<TextField
|
|
222
|
+
{...defaultProps}
|
|
223
|
+
loading
|
|
224
|
+
/>
|
|
225
|
+
);
|
|
226
|
+
const input: HTMLInputElement = screen.getByRole('textbox');
|
|
227
|
+
expect(input.disabled).toBe(true);
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
describe('Constructor Validation', () => {
|
|
232
|
+
it('throws error when both controlled and submittable are true', () => {
|
|
233
|
+
expect(() => {
|
|
234
|
+
render(
|
|
235
|
+
<TextField
|
|
236
|
+
{...defaultProps}
|
|
237
|
+
controlled
|
|
238
|
+
submittable
|
|
239
|
+
/>
|
|
240
|
+
);
|
|
241
|
+
}).toThrow('Actionable TextField(s) must be one of either: controlled or submittable.');
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
describe('Controlled Mode', () => {
|
|
246
|
+
it('renders in controlled mode with edit button', () => {
|
|
247
|
+
const { container } = render(
|
|
248
|
+
<TextField
|
|
249
|
+
{...defaultProps}
|
|
250
|
+
controlled
|
|
251
|
+
value='test value'
|
|
252
|
+
/>
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
const editButton = container.querySelector('[data-tooltip-content="Edit"]');
|
|
256
|
+
expect(editButton).toBeDefined();
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it('toggles editing state when edit button is clicked', () => {
|
|
260
|
+
const { container } = render(
|
|
261
|
+
<TextField
|
|
262
|
+
{...defaultProps}
|
|
263
|
+
controlled
|
|
264
|
+
value='test value'
|
|
265
|
+
/>
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
const editButton = container.querySelector('[data-tooltip-content="Edit"]');
|
|
269
|
+
fireEvent.click(editButton);
|
|
270
|
+
|
|
271
|
+
// Should now show check icon instead of edit icon
|
|
272
|
+
const checkButton = container.querySelector('[data-tooltip-content="Edit"]');
|
|
273
|
+
expect(checkButton).toBeDefined();
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
it('calls onSubmit when editing is completed with changed value', () => {
|
|
277
|
+
const onSubmit = vi.fn();
|
|
278
|
+
const { container } = render(
|
|
279
|
+
<TextField
|
|
280
|
+
{...defaultProps}
|
|
281
|
+
controlled
|
|
282
|
+
value='original'
|
|
283
|
+
onSubmit={onSubmit}
|
|
284
|
+
/>
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
// Start editing
|
|
288
|
+
const editButton = container.querySelector('[data-tooltip-content="Edit"]');
|
|
289
|
+
fireEvent.click(editButton);
|
|
290
|
+
|
|
291
|
+
// Change value
|
|
292
|
+
const input = screen.getByRole('textbox');
|
|
293
|
+
fireEvent.change(input, { target: { value: 'modified' } });
|
|
294
|
+
|
|
295
|
+
// Complete editing
|
|
296
|
+
const checkButton = container.querySelector('[data-tooltip-content="Edit"]');
|
|
297
|
+
fireEvent.click(checkButton);
|
|
298
|
+
|
|
299
|
+
expect(onSubmit).toHaveBeenCalledWith('modified');
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
it('does not call onSubmit when editing is completed without changes', () => {
|
|
303
|
+
const onSubmit = vi.fn();
|
|
304
|
+
const { container } = render(
|
|
305
|
+
<TextField
|
|
306
|
+
{...defaultProps}
|
|
307
|
+
controlled
|
|
308
|
+
value='unchanged'
|
|
309
|
+
onSubmit={onSubmit}
|
|
310
|
+
/>
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
// Start editing
|
|
314
|
+
const editButton = container.querySelector('[data-tooltip-content="Edit"]');
|
|
315
|
+
fireEvent.click(editButton);
|
|
316
|
+
|
|
317
|
+
// Complete editing without changes
|
|
318
|
+
const checkButton = container.querySelector('[data-tooltip-content="Edit"]');
|
|
319
|
+
fireEvent.click(checkButton);
|
|
320
|
+
|
|
321
|
+
expect(onSubmit).not.toHaveBeenCalled();
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
it('calls onSubmit with null when empty value is submitted', () => {
|
|
325
|
+
const onSubmit = vi.fn();
|
|
326
|
+
const { container } = render(
|
|
327
|
+
<TextField
|
|
328
|
+
{...defaultProps}
|
|
329
|
+
controlled
|
|
330
|
+
value='original'
|
|
331
|
+
onSubmit={onSubmit}
|
|
332
|
+
/>
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
// Start editing
|
|
336
|
+
const editButton = container.querySelector('[data-tooltip-content="Edit"]');
|
|
337
|
+
fireEvent.click(editButton);
|
|
338
|
+
|
|
339
|
+
// Clear value
|
|
340
|
+
const input = screen.getByRole('textbox');
|
|
341
|
+
fireEvent.change(input, { target: { value: '' } });
|
|
342
|
+
|
|
343
|
+
// Complete editing
|
|
344
|
+
const checkButton = container.querySelector('[data-tooltip-content="Edit"]');
|
|
345
|
+
fireEvent.click(checkButton);
|
|
346
|
+
|
|
347
|
+
expect(onSubmit).toHaveBeenCalledWith(null);
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
describe('Submittable Mode', () => {
|
|
352
|
+
it('renders submittable mode with search icon', () => {
|
|
353
|
+
const { container } = render(
|
|
354
|
+
<TextField
|
|
355
|
+
{...defaultProps}
|
|
356
|
+
submittable
|
|
357
|
+
submittableType='search'
|
|
358
|
+
/>
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
const submitButton = container.querySelector('[data-tooltip-content="Search"]');
|
|
362
|
+
expect(submitButton).toBeDefined();
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
it('renders submittable mode with check icon', () => {
|
|
366
|
+
const { container } = render(
|
|
367
|
+
<TextField
|
|
368
|
+
{...defaultProps}
|
|
369
|
+
submittable
|
|
370
|
+
submittableType='check'
|
|
371
|
+
/>
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
const submitButton = container.querySelector('[data-tooltip-content="Confirm"]');
|
|
375
|
+
expect(submitButton).toBeDefined();
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
it('renders submittable mode with add icon', () => {
|
|
379
|
+
const { container } = render(
|
|
380
|
+
<TextField
|
|
381
|
+
{...defaultProps}
|
|
382
|
+
submittable
|
|
383
|
+
submittableType='add'
|
|
384
|
+
/>
|
|
385
|
+
);
|
|
386
|
+
|
|
387
|
+
const submitButton = container.querySelector('[data-tooltip-content="Confirm"]');
|
|
388
|
+
expect(submitButton).toBeDefined();
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
it('calls doSubmit when submit button is clicked with value', () => {
|
|
392
|
+
const doSubmit = vi.fn();
|
|
393
|
+
const { container } = render(
|
|
394
|
+
<TextField
|
|
395
|
+
{...defaultProps}
|
|
396
|
+
submittable
|
|
397
|
+
doSubmit={doSubmit}
|
|
398
|
+
/>
|
|
399
|
+
);
|
|
400
|
+
|
|
401
|
+
const input = screen.getByRole('textbox');
|
|
402
|
+
fireEvent.change(input, { target: { value: 'test submission' } });
|
|
403
|
+
|
|
404
|
+
const submitButton = container.querySelector('[data-tooltip-content="Confirm"]');
|
|
405
|
+
fireEvent.click(submitButton);
|
|
406
|
+
|
|
407
|
+
expect(doSubmit).toHaveBeenCalledWith('test submission');
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
it('clears value after submission when submittableType is add', () => {
|
|
411
|
+
const doSubmit = vi.fn();
|
|
412
|
+
const { container } = render(
|
|
413
|
+
<TextField
|
|
414
|
+
{...defaultProps}
|
|
415
|
+
submittable
|
|
416
|
+
submittableType='add'
|
|
417
|
+
doSubmit={doSubmit}
|
|
418
|
+
/>
|
|
419
|
+
);
|
|
420
|
+
|
|
421
|
+
const input: HTMLInputElement = screen.getByRole('textbox');
|
|
422
|
+
fireEvent.change(input, { target: { value: 'test value' } });
|
|
423
|
+
|
|
424
|
+
const submitButton = container.querySelector('[data-tooltip-content="Confirm"]');
|
|
425
|
+
fireEvent.click(submitButton);
|
|
426
|
+
|
|
427
|
+
expect(input.value).toBe('');
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
it('shows clear button when value exists', () => {
|
|
431
|
+
const { container } = render(
|
|
432
|
+
<TextField
|
|
433
|
+
{...defaultProps}
|
|
434
|
+
submittable
|
|
435
|
+
value='some value'
|
|
436
|
+
/>
|
|
437
|
+
);
|
|
438
|
+
|
|
439
|
+
const clearButton = container.querySelector('[data-tooltip-content="Clear"]');
|
|
440
|
+
expect(clearButton).toBeDefined();
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
it('calls doSubmit with empty string when clear button is clicked', () => {
|
|
444
|
+
const doSubmit = vi.fn();
|
|
445
|
+
const { container } = render(
|
|
446
|
+
<TextField
|
|
447
|
+
{...defaultProps}
|
|
448
|
+
submittable
|
|
449
|
+
value='some value'
|
|
450
|
+
doSubmit={doSubmit}
|
|
451
|
+
/>
|
|
452
|
+
);
|
|
453
|
+
|
|
454
|
+
const clearButton = container.querySelector('[data-tooltip-content="Clear"]');
|
|
455
|
+
fireEvent.click(clearButton);
|
|
456
|
+
|
|
457
|
+
expect(doSubmit).toHaveBeenCalledWith('');
|
|
458
|
+
});
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
describe('Copy Functionality', () => {
|
|
462
|
+
it('shows copy button when showCopy is true', () => {
|
|
463
|
+
const { container } = render(
|
|
464
|
+
<TextField
|
|
465
|
+
{...defaultProps}
|
|
466
|
+
showCopy
|
|
467
|
+
value='copyable text'
|
|
468
|
+
/>
|
|
469
|
+
);
|
|
470
|
+
|
|
471
|
+
const copyButton = container.querySelector('[data-tooltip-content*="Copy"]');
|
|
472
|
+
expect(copyButton).toBeDefined();
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
it('calls onNotification when copy succeeds', async () => {
|
|
476
|
+
const onNotification = vi.fn();
|
|
477
|
+
const { container } = render(
|
|
478
|
+
<TextField
|
|
479
|
+
{...defaultProps}
|
|
480
|
+
showCopy
|
|
481
|
+
value='copyable text'
|
|
482
|
+
onNotification={onNotification}
|
|
483
|
+
/>
|
|
484
|
+
);
|
|
485
|
+
|
|
486
|
+
const copyButton = container.querySelector('[data-tooltip-content*="Copy"]');
|
|
487
|
+
fireEvent.click(copyButton);
|
|
488
|
+
|
|
489
|
+
// Wait for clipboard operation
|
|
490
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
491
|
+
|
|
492
|
+
expect(onNotification).toHaveBeenCalledWith('Copied to clipboard!');
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
it('calls onError when copy fails', async () => {
|
|
496
|
+
// Mock clipboard to fail
|
|
497
|
+
const originalClipboard = navigator.clipboard;
|
|
498
|
+
Object.defineProperty(navigator, 'clipboard', {
|
|
499
|
+
value: {
|
|
500
|
+
writeText: vi.fn().mockRejectedValue(new Error('Copy failed'))
|
|
501
|
+
},
|
|
502
|
+
writable: true,
|
|
503
|
+
configurable: true
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
const onError = vi.fn();
|
|
507
|
+
const { container } = render(
|
|
508
|
+
<TextField
|
|
509
|
+
{...defaultProps}
|
|
510
|
+
showCopy
|
|
511
|
+
value='copyable text'
|
|
512
|
+
onError={onError}
|
|
513
|
+
/>
|
|
514
|
+
);
|
|
515
|
+
|
|
516
|
+
const copyButton = container.querySelector('[data-tooltip-content*="Copy"]');
|
|
517
|
+
fireEvent.click(copyButton);
|
|
518
|
+
|
|
519
|
+
// Wait for clipboard operation
|
|
520
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
521
|
+
|
|
522
|
+
expect(onError).toHaveBeenCalledWith('Error copying to clipboard.');
|
|
523
|
+
|
|
524
|
+
// Restore clipboard
|
|
525
|
+
Object.defineProperty(navigator, 'clipboard', {
|
|
526
|
+
value: originalClipboard,
|
|
527
|
+
writable: true,
|
|
528
|
+
configurable: true
|
|
529
|
+
});
|
|
530
|
+
});
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
describe('Pattern Validation', () => {
|
|
534
|
+
it('prevents input that does not match pattern', () => {
|
|
535
|
+
const onChange = vi.fn();
|
|
536
|
+
render(
|
|
537
|
+
<TextField
|
|
538
|
+
{...defaultProps}
|
|
539
|
+
pattern={/^\d+$/} // Only digits
|
|
540
|
+
onChange={onChange}
|
|
541
|
+
/>
|
|
542
|
+
);
|
|
543
|
+
|
|
544
|
+
const input = screen.getByRole('textbox');
|
|
545
|
+
fireEvent.change(input, { target: { value: 'abc123' } });
|
|
546
|
+
|
|
547
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
it('allows input that matches pattern', () => {
|
|
551
|
+
const onChange = vi.fn();
|
|
552
|
+
render(
|
|
553
|
+
<TextField
|
|
554
|
+
{...defaultProps}
|
|
555
|
+
pattern={/^\d+$/} // Only digits
|
|
556
|
+
onChange={onChange}
|
|
557
|
+
/>
|
|
558
|
+
);
|
|
559
|
+
|
|
560
|
+
const input = screen.getByRole('textbox');
|
|
561
|
+
fireEvent.change(input, { target: { value: '123' } });
|
|
562
|
+
|
|
563
|
+
expect(onChange).toHaveBeenCalledWith('123');
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
it('converts empty string to null for regular input', () => {
|
|
567
|
+
const onChange = vi.fn();
|
|
568
|
+
render(
|
|
569
|
+
<TextField
|
|
570
|
+
{...defaultProps}
|
|
571
|
+
onChange={onChange}
|
|
572
|
+
value='initial'
|
|
573
|
+
/>
|
|
574
|
+
);
|
|
575
|
+
|
|
576
|
+
const input = screen.getByRole('textbox');
|
|
577
|
+
fireEvent.change(input, { target: { value: '' } });
|
|
578
|
+
|
|
579
|
+
expect(onChange).toHaveBeenCalledWith(null);
|
|
580
|
+
});
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
describe('Button Visibility', () => {
|
|
584
|
+
it('hides all buttons when hideButtons is true', () => {
|
|
585
|
+
const { container } = render(
|
|
586
|
+
<TextField
|
|
587
|
+
{...defaultProps}
|
|
588
|
+
submittable
|
|
589
|
+
showCopy
|
|
590
|
+
hideButtons
|
|
591
|
+
value='test'
|
|
592
|
+
/>
|
|
593
|
+
);
|
|
594
|
+
|
|
595
|
+
const submitButton = container.querySelector('[data-tooltip-content="Confirm"]');
|
|
596
|
+
const copyButton = container.querySelector('[data-tooltip-content*="Copy"]');
|
|
597
|
+
|
|
598
|
+
expect(submitButton).toBeNull();
|
|
599
|
+
expect(copyButton).toBeNull();
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
it('renders custom buttons array', () => {
|
|
603
|
+
render(
|
|
604
|
+
<TextField
|
|
605
|
+
{...defaultProps}
|
|
606
|
+
buttons={[<button key='custom'>Custom</button>]}
|
|
607
|
+
/>
|
|
608
|
+
);
|
|
609
|
+
|
|
610
|
+
const button = screen.getByText('Custom');
|
|
611
|
+
expect(button).toBeDefined();
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
it('renders single custom button', () => {
|
|
615
|
+
render(
|
|
616
|
+
<TextField
|
|
617
|
+
{...defaultProps}
|
|
618
|
+
buttons={<button key='custom'>Single</button>}
|
|
619
|
+
/>
|
|
620
|
+
);
|
|
621
|
+
|
|
622
|
+
const button = screen.getByText('Single');
|
|
623
|
+
expect(button).toBeDefined();
|
|
624
|
+
});
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
describe('Suggestions/Autocomplete', () => {
|
|
628
|
+
it('renders with suggestions and autocomplete functionality', () => {
|
|
629
|
+
const suggestions = ['apple', 'banana', 'cherry'];
|
|
630
|
+
const { container } = render(
|
|
631
|
+
<TextField
|
|
632
|
+
{...defaultProps}
|
|
633
|
+
suggestions={suggestions}
|
|
634
|
+
/>
|
|
635
|
+
);
|
|
636
|
+
|
|
637
|
+
// Should render ReactTextareaAutocomplete (mocked as 'input' string)
|
|
638
|
+
const textContent = container.textContent;
|
|
639
|
+
expect(textContent).toContain('input');
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
it('handles suggestion click in submittable mode', () => {
|
|
643
|
+
const doSubmit = vi.fn();
|
|
644
|
+
const suggestions = ['apple', 'banana', 'cherry'];
|
|
645
|
+
const { container } = render(
|
|
646
|
+
<TextField
|
|
647
|
+
{...defaultProps}
|
|
648
|
+
submittable
|
|
649
|
+
suggestions={suggestions}
|
|
650
|
+
doSubmit={doSubmit}
|
|
651
|
+
/>
|
|
652
|
+
);
|
|
653
|
+
|
|
654
|
+
// Test that it renders with suggestions
|
|
655
|
+
expect(container.textContent).toContain('input');
|
|
656
|
+
});
|
|
657
|
+
|
|
658
|
+
it('handles Enter key in submittable mode with suggestions', () => {
|
|
659
|
+
const doSubmit = vi.fn();
|
|
660
|
+
const suggestions = ['apple', 'banana', 'cherry'];
|
|
661
|
+
render(
|
|
662
|
+
<TextField
|
|
663
|
+
{...defaultProps}
|
|
664
|
+
submittable
|
|
665
|
+
suggestions={suggestions}
|
|
666
|
+
doSubmit={doSubmit}
|
|
667
|
+
/>
|
|
668
|
+
);
|
|
669
|
+
|
|
670
|
+
// Test that component renders without errors
|
|
671
|
+
expect(doSubmit).toBeDefined();
|
|
672
|
+
});
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
describe('Keyboard Shortcuts', () => {
|
|
676
|
+
it('binds hotkeys on focus in submittable mode', () => {
|
|
677
|
+
const doSubmit = vi.fn();
|
|
678
|
+
render(
|
|
679
|
+
<TextField
|
|
680
|
+
{...defaultProps}
|
|
681
|
+
submittable
|
|
682
|
+
doSubmit={doSubmit}
|
|
683
|
+
/>
|
|
684
|
+
);
|
|
685
|
+
|
|
686
|
+
const input = screen.getByRole('textbox');
|
|
687
|
+
fireEvent.focus(input);
|
|
688
|
+
|
|
689
|
+
// Test that focus event was handled
|
|
690
|
+
expect(input).toBeDefined();
|
|
691
|
+
});
|
|
692
|
+
|
|
693
|
+
it('unbinds hotkeys on blur in submittable mode', () => {
|
|
694
|
+
const doSubmit = vi.fn();
|
|
695
|
+
const onBlur = vi.fn();
|
|
696
|
+
render(
|
|
697
|
+
<TextField
|
|
698
|
+
{...defaultProps}
|
|
699
|
+
submittable
|
|
700
|
+
doSubmit={doSubmit}
|
|
701
|
+
onBlur={onBlur}
|
|
702
|
+
/>
|
|
703
|
+
);
|
|
704
|
+
|
|
705
|
+
const input = screen.getByRole('textbox');
|
|
706
|
+
fireEvent.focus(input);
|
|
707
|
+
fireEvent.blur(input);
|
|
708
|
+
|
|
709
|
+
expect(onBlur).toHaveBeenCalled();
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
it('binds hotkeys on focus in controlled mode', () => {
|
|
713
|
+
const onSubmit = vi.fn();
|
|
714
|
+
render(
|
|
715
|
+
<TextField
|
|
716
|
+
{...defaultProps}
|
|
717
|
+
controlled
|
|
718
|
+
value='original'
|
|
719
|
+
onSubmit={onSubmit}
|
|
720
|
+
/>
|
|
721
|
+
);
|
|
722
|
+
|
|
723
|
+
const input = screen.getByRole('textbox');
|
|
724
|
+
fireEvent.focus(input);
|
|
725
|
+
|
|
726
|
+
// Test that focus event was handled
|
|
727
|
+
expect(input).toBeDefined();
|
|
728
|
+
});
|
|
729
|
+
|
|
730
|
+
it('unbinds hotkeys on blur in controlled mode', () => {
|
|
731
|
+
const onBlur = vi.fn();
|
|
732
|
+
render(
|
|
733
|
+
<TextField
|
|
734
|
+
{...defaultProps}
|
|
735
|
+
controlled
|
|
736
|
+
value='original'
|
|
737
|
+
onBlur={onBlur}
|
|
738
|
+
/>
|
|
739
|
+
);
|
|
740
|
+
|
|
741
|
+
const input = screen.getByRole('textbox');
|
|
742
|
+
fireEvent.focus(input);
|
|
743
|
+
fireEvent.blur(input);
|
|
744
|
+
|
|
745
|
+
expect(onBlur).toHaveBeenCalled();
|
|
746
|
+
});
|
|
747
|
+
});
|
|
748
|
+
|
|
749
|
+
describe('Lifecycle Methods', () => {
|
|
750
|
+
it('handles componentWillUnmount for submittable mode', () => {
|
|
751
|
+
const { unmount } = render(
|
|
752
|
+
<TextField
|
|
753
|
+
{...defaultProps}
|
|
754
|
+
submittable
|
|
755
|
+
/>
|
|
756
|
+
);
|
|
757
|
+
|
|
758
|
+
// Focus to bind hotkeys
|
|
759
|
+
const input = screen.getByRole('textbox');
|
|
760
|
+
fireEvent.focus(input);
|
|
761
|
+
|
|
762
|
+
// Unmount should clean up hotkey bindings
|
|
763
|
+
unmount();
|
|
764
|
+
});
|
|
765
|
+
|
|
766
|
+
it('handles componentWillUnmount for controlled mode', () => {
|
|
767
|
+
const { unmount } = render(
|
|
768
|
+
<TextField
|
|
769
|
+
{...defaultProps}
|
|
770
|
+
controlled
|
|
771
|
+
/>
|
|
772
|
+
);
|
|
773
|
+
|
|
774
|
+
// Focus to bind hotkeys
|
|
775
|
+
const input = screen.getByRole('textbox');
|
|
776
|
+
fireEvent.focus(input);
|
|
777
|
+
|
|
778
|
+
// Unmount should clean up hotkey bindings
|
|
779
|
+
unmount();
|
|
780
|
+
});
|
|
781
|
+
|
|
782
|
+
it('handles componentDidUpdate for controlled mode', () => {
|
|
783
|
+
const { rerender } = render(
|
|
784
|
+
<TextField
|
|
785
|
+
{...defaultProps}
|
|
786
|
+
controlled
|
|
787
|
+
value='initial'
|
|
788
|
+
/>
|
|
789
|
+
);
|
|
790
|
+
|
|
791
|
+
// Update component to trigger componentDidUpdate
|
|
792
|
+
rerender(
|
|
793
|
+
<TextField
|
|
794
|
+
{...defaultProps}
|
|
795
|
+
controlled
|
|
796
|
+
value='updated'
|
|
797
|
+
/>
|
|
798
|
+
);
|
|
799
|
+
|
|
800
|
+
const input: HTMLInputElement = screen.getByRole('textbox');
|
|
801
|
+
expect(input.value).toBe('updated');
|
|
802
|
+
});
|
|
803
|
+
});
|
|
804
|
+
|
|
805
|
+
describe('Edge Cases', () => {
|
|
806
|
+
it('handles null submittableType gracefully', () => {
|
|
807
|
+
render(
|
|
808
|
+
<TextField
|
|
809
|
+
{...defaultProps}
|
|
810
|
+
submittable
|
|
811
|
+
submittableType={null}
|
|
812
|
+
/>
|
|
813
|
+
);
|
|
814
|
+
|
|
815
|
+
// Should still render without crashing
|
|
816
|
+
const input: HTMLInputElement = screen.getByRole('textbox');
|
|
817
|
+
expect(input).toBeDefined();
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
it('handles empty suggestions array', () => {
|
|
821
|
+
const { container } = render(
|
|
822
|
+
<TextField
|
|
823
|
+
{...defaultProps}
|
|
824
|
+
suggestions={[]}
|
|
825
|
+
/>
|
|
826
|
+
);
|
|
827
|
+
|
|
828
|
+
// Should render ReactTextareaAutocomplete (mocked as 'input' string) even with empty suggestions
|
|
829
|
+
expect(container.textContent).toContain('input');
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
it('handles disabled state in controlled mode', () => {
|
|
833
|
+
render(
|
|
834
|
+
<TextField
|
|
835
|
+
{...defaultProps}
|
|
836
|
+
controlled
|
|
837
|
+
disabled
|
|
838
|
+
/>
|
|
839
|
+
);
|
|
840
|
+
|
|
841
|
+
const input: HTMLInputElement = screen.getByRole('textbox');
|
|
842
|
+
expect(input.disabled).toBe(true);
|
|
843
|
+
});
|
|
844
|
+
|
|
845
|
+
it('handles blur event with hotkey cleanup', () => {
|
|
846
|
+
const onBlur = vi.fn();
|
|
847
|
+
render(
|
|
848
|
+
<TextField
|
|
849
|
+
{...defaultProps}
|
|
850
|
+
submittable
|
|
851
|
+
onBlur={onBlur}
|
|
852
|
+
/>
|
|
853
|
+
);
|
|
854
|
+
|
|
855
|
+
const input: HTMLInputElement = screen.getByRole('textbox');
|
|
856
|
+
fireEvent.focus(input);
|
|
857
|
+
fireEvent.blur(input);
|
|
858
|
+
|
|
859
|
+
expect(onBlur).toHaveBeenCalled();
|
|
860
|
+
});
|
|
861
|
+
|
|
862
|
+
it('handles focus event with hotkey binding', () => {
|
|
863
|
+
const onFocus = vi.fn();
|
|
864
|
+
render(
|
|
865
|
+
<TextField
|
|
866
|
+
{...defaultProps}
|
|
867
|
+
controlled
|
|
868
|
+
onFocus={onFocus}
|
|
869
|
+
/>
|
|
870
|
+
);
|
|
871
|
+
|
|
872
|
+
const input = screen.getByRole('textbox');
|
|
873
|
+
fireEvent.focus(input);
|
|
874
|
+
|
|
875
|
+
expect(onFocus).toHaveBeenCalled();
|
|
876
|
+
});
|
|
877
|
+
|
|
878
|
+
it('handles setEditing method', () => {
|
|
879
|
+
const { container } = render(
|
|
880
|
+
<TextField
|
|
881
|
+
{...defaultProps}
|
|
882
|
+
controlled
|
|
883
|
+
value='test'
|
|
884
|
+
/>
|
|
885
|
+
);
|
|
886
|
+
|
|
887
|
+
// Start editing by clicking edit button
|
|
888
|
+
const editButton = container.querySelector('[data-tooltip-content="Edit"]');
|
|
889
|
+
fireEvent.click(editButton);
|
|
890
|
+
|
|
891
|
+
// Should be in editing mode
|
|
892
|
+
const input: HTMLInputElement = screen.getByRole('textbox');
|
|
893
|
+
expect(input.disabled).toBe(false);
|
|
894
|
+
});
|
|
895
|
+
|
|
896
|
+
it('handles getValue method for different modes', () => {
|
|
897
|
+
const { rerender } = render(
|
|
898
|
+
<TextField
|
|
899
|
+
{...defaultProps}
|
|
900
|
+
value='test'
|
|
901
|
+
/>
|
|
902
|
+
);
|
|
903
|
+
|
|
904
|
+
const input: HTMLInputElement = screen.getByRole('textbox');
|
|
905
|
+
expect(input.value).toBe('test');
|
|
906
|
+
|
|
907
|
+
// Test controlled mode
|
|
908
|
+
rerender(
|
|
909
|
+
<TextField
|
|
910
|
+
{...defaultProps}
|
|
911
|
+
controlled
|
|
912
|
+
value='controlled test'
|
|
913
|
+
/>
|
|
914
|
+
);
|
|
915
|
+
|
|
916
|
+
const controlledInput: HTMLInputElement = screen.getByRole('textbox');
|
|
917
|
+
expect(controlledInput.value).toBe('controlled test');
|
|
918
|
+
});
|
|
919
|
+
});
|
|
920
|
+
|
|
921
|
+
describe('Component Updates', () => {
|
|
922
|
+
it('updates internal value when props.value changes', () => {
|
|
923
|
+
const { rerender } = render(
|
|
924
|
+
<TextField
|
|
925
|
+
{...defaultProps}
|
|
926
|
+
value='initial'
|
|
927
|
+
/>
|
|
928
|
+
);
|
|
929
|
+
|
|
930
|
+
rerender(
|
|
931
|
+
<TextField
|
|
932
|
+
{...defaultProps}
|
|
933
|
+
value='updated'
|
|
934
|
+
/>
|
|
935
|
+
);
|
|
936
|
+
|
|
937
|
+
const input: HTMLInputElement = screen.getByRole('textbox');
|
|
938
|
+
expect(input.value).toBe('updated');
|
|
939
|
+
});
|
|
940
|
+
|
|
941
|
+
it('updates internal value when controlled mode value changes', () => {
|
|
942
|
+
const { rerender } = render(
|
|
943
|
+
<TextField
|
|
944
|
+
{...defaultProps}
|
|
945
|
+
controlled
|
|
946
|
+
value='initial'
|
|
947
|
+
/>
|
|
948
|
+
);
|
|
949
|
+
|
|
950
|
+
rerender(
|
|
951
|
+
<TextField
|
|
952
|
+
{...defaultProps}
|
|
953
|
+
controlled
|
|
954
|
+
value='updated'
|
|
955
|
+
/>
|
|
956
|
+
);
|
|
957
|
+
|
|
958
|
+
const input: HTMLInputElement = screen.getByRole('textbox');
|
|
959
|
+
expect(input.value).toBe('updated');
|
|
960
|
+
});
|
|
961
|
+
|
|
962
|
+
it('clears internal value when editing ends and value is empty', () => {
|
|
963
|
+
const { rerender, container } = render(
|
|
964
|
+
<TextField
|
|
965
|
+
{...defaultProps}
|
|
966
|
+
controlled
|
|
967
|
+
value='initial'
|
|
968
|
+
/>
|
|
969
|
+
);
|
|
970
|
+
|
|
971
|
+
// Start editing
|
|
972
|
+
const editButton = container.querySelector('[data-tooltip-content="Edit"]');
|
|
973
|
+
fireEvent.click(editButton);
|
|
974
|
+
|
|
975
|
+
// Update props to empty value and stop editing
|
|
976
|
+
rerender(
|
|
977
|
+
<TextField
|
|
978
|
+
{...defaultProps}
|
|
979
|
+
controlled
|
|
980
|
+
value=''
|
|
981
|
+
/>
|
|
982
|
+
);
|
|
983
|
+
|
|
984
|
+
const input: HTMLInputElement = screen.getByRole('textbox');
|
|
985
|
+
expect(input.value).toBe('');
|
|
986
|
+
});
|
|
987
|
+
});
|
|
988
|
+
});
|