@pie-lib/config-ui 11.30.3-next.2 → 11.30.3-next.205

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 (123) hide show
  1. package/CHANGELOG.md +19 -67
  2. package/lib/alert-dialog.js +35 -42
  3. package/lib/alert-dialog.js.map +1 -1
  4. package/lib/checkbox.js +56 -71
  5. package/lib/checkbox.js.map +1 -1
  6. package/lib/choice-configuration/feedback-menu.js +29 -64
  7. package/lib/choice-configuration/feedback-menu.js.map +1 -1
  8. package/lib/choice-configuration/index.js +202 -262
  9. package/lib/choice-configuration/index.js.map +1 -1
  10. package/lib/choice-utils.js +6 -18
  11. package/lib/choice-utils.js.map +1 -1
  12. package/lib/feedback-config/feedback-selector.js +79 -115
  13. package/lib/feedback-config/feedback-selector.js.map +1 -1
  14. package/lib/feedback-config/group.js +26 -40
  15. package/lib/feedback-config/group.js.map +1 -1
  16. package/lib/feedback-config/index.js +47 -90
  17. package/lib/feedback-config/index.js.map +1 -1
  18. package/lib/form-section.js +31 -33
  19. package/lib/form-section.js.map +1 -1
  20. package/lib/help.js +39 -80
  21. package/lib/help.js.map +1 -1
  22. package/lib/index.js +1 -31
  23. package/lib/index.js.map +1 -1
  24. package/lib/input.js +21 -54
  25. package/lib/input.js.map +1 -1
  26. package/lib/inputs.js +61 -95
  27. package/lib/inputs.js.map +1 -1
  28. package/lib/langs.js +58 -101
  29. package/lib/langs.js.map +1 -1
  30. package/lib/layout/config-layout.js +40 -70
  31. package/lib/layout/config-layout.js.map +1 -1
  32. package/lib/layout/index.js +0 -3
  33. package/lib/layout/index.js.map +1 -1
  34. package/lib/layout/layout-contents.js +72 -103
  35. package/lib/layout/layout-contents.js.map +1 -1
  36. package/lib/layout/settings-box.js +27 -56
  37. package/lib/layout/settings-box.js.map +1 -1
  38. package/lib/mui-box/index.js +41 -57
  39. package/lib/mui-box/index.js.map +1 -1
  40. package/lib/number-text-field-custom.js +79 -161
  41. package/lib/number-text-field-custom.js.map +1 -1
  42. package/lib/number-text-field.js +80 -114
  43. package/lib/number-text-field.js.map +1 -1
  44. package/lib/radio-with-label.js +30 -31
  45. package/lib/radio-with-label.js.map +1 -1
  46. package/lib/settings/display-size.js +16 -32
  47. package/lib/settings/display-size.js.map +1 -1
  48. package/lib/settings/index.js +14 -47
  49. package/lib/settings/index.js.map +1 -1
  50. package/lib/settings/panel.js +159 -229
  51. package/lib/settings/panel.js.map +1 -1
  52. package/lib/settings/settings-radio-label.js +28 -30
  53. package/lib/settings/settings-radio-label.js.map +1 -1
  54. package/lib/settings/toggle.js +35 -46
  55. package/lib/settings/toggle.js.map +1 -1
  56. package/lib/tabs/index.js +22 -57
  57. package/lib/tabs/index.js.map +1 -1
  58. package/lib/tags-input/index.js +50 -99
  59. package/lib/tags-input/index.js.map +1 -1
  60. package/lib/two-choice.js +46 -90
  61. package/lib/two-choice.js.map +1 -1
  62. package/lib/with-stateful-model.js +8 -31
  63. package/lib/with-stateful-model.js.map +1 -1
  64. package/package.json +12 -20
  65. package/src/__tests__/alert-dialog.test.jsx +283 -0
  66. package/src/__tests__/checkbox.test.jsx +249 -0
  67. package/src/__tests__/form-section.test.jsx +334 -0
  68. package/src/__tests__/help.test.jsx +184 -0
  69. package/src/__tests__/input.test.jsx +192 -0
  70. package/src/__tests__/langs.test.jsx +435 -15
  71. package/src/__tests__/number-text-field-custom.test.jsx +438 -0
  72. package/src/__tests__/number-text-field.test.jsx +295 -102
  73. package/src/__tests__/radio-with-label.test.jsx +259 -0
  74. package/src/__tests__/settings-panel.test.js +66 -83
  75. package/src/__tests__/settings.test.jsx +515 -0
  76. package/src/__tests__/tabs.test.jsx +193 -0
  77. package/src/__tests__/two-choice.test.js +104 -18
  78. package/src/__tests__/with-stateful-model.test.jsx +145 -0
  79. package/src/alert-dialog.jsx +21 -19
  80. package/src/checkbox.jsx +42 -46
  81. package/src/choice-configuration/__tests__/feedback-menu.test.jsx +157 -4
  82. package/src/choice-configuration/__tests__/index.test.jsx +198 -56
  83. package/src/choice-configuration/feedback-menu.jsx +6 -6
  84. package/src/choice-configuration/index.jsx +201 -203
  85. package/src/feedback-config/__tests__/feedback-config.test.jsx +130 -60
  86. package/src/feedback-config/__tests__/feedback-selector.test.jsx +81 -44
  87. package/src/feedback-config/feedback-selector.jsx +50 -55
  88. package/src/feedback-config/group.jsx +21 -22
  89. package/src/feedback-config/index.jsx +27 -29
  90. package/src/form-section.jsx +26 -18
  91. package/src/help.jsx +20 -28
  92. package/src/input.jsx +1 -1
  93. package/src/inputs.jsx +34 -50
  94. package/src/langs.jsx +41 -46
  95. package/src/layout/__tests__/config.layout.test.jsx +55 -38
  96. package/src/layout/config-layout.jsx +38 -32
  97. package/src/layout/layout-contents.jsx +38 -39
  98. package/src/layout/settings-box.jsx +16 -19
  99. package/src/mui-box/index.jsx +35 -43
  100. package/src/number-text-field-custom.jsx +30 -36
  101. package/src/number-text-field.jsx +45 -29
  102. package/src/radio-with-label.jsx +25 -13
  103. package/src/settings/display-size.jsx +12 -11
  104. package/src/settings/panel.jsx +97 -91
  105. package/src/settings/settings-radio-label.jsx +25 -13
  106. package/src/settings/toggle.jsx +30 -29
  107. package/src/tabs/index.jsx +8 -8
  108. package/src/tags-input/__tests__/index.test.jsx +88 -37
  109. package/src/tags-input/index.jsx +35 -38
  110. package/src/two-choice.jsx +15 -19
  111. package/esm/index.css +0 -847
  112. package/esm/index.js +0 -213950
  113. package/esm/index.js.map +0 -1
  114. package/esm/package.json +0 -3
  115. package/src/__tests__/__snapshots__/langs.test.jsx.snap +0 -32
  116. package/src/__tests__/__snapshots__/settings-panel.test.js.snap +0 -115
  117. package/src/__tests__/__snapshots__/two-choice.test.js.snap +0 -171
  118. package/src/choice-configuration/__tests__/__snapshots__/feedback-menu.test.jsx.snap +0 -51
  119. package/src/choice-configuration/__tests__/__snapshots__/index.test.jsx.snap +0 -519
  120. package/src/feedback-config/__tests__/__snapshots__/feedback-config.test.jsx.snap +0 -27
  121. package/src/feedback-config/__tests__/__snapshots__/feedback-selector.test.jsx.snap +0 -38
  122. package/src/layout/__tests__/__snapshots__/config.layout.test.jsx.snap +0 -59
  123. package/src/tags-input/__tests__/__snapshots__/index.test.jsx.snap +0 -170
@@ -1,148 +1,341 @@
1
1
  import React from 'react';
2
- import { shallow } from 'enzyme';
2
+ import { render, screen, waitFor, fireEvent } from '@testing-library/react';
3
+ import userEvent from '@testing-library/user-event';
3
4
  import { NumberTextField } from '../number-text-field';
4
- import TextField from '@material-ui/core/TextField';
5
5
 
6
6
  describe('NumberTextField', () => {
7
- describe('render', () => {
8
- let value = 1;
9
- let onChange, component, textField, onBlur;
10
- let min = 1;
11
- let max = 10;
12
-
13
- describe('TextField', () => {
14
- beforeEach(() => {
15
- onChange = jest.fn();
16
- onBlur = jest.fn();
17
- component = shallow(
18
- <NumberTextField value={value} min={min} max={max} classes={{}} onChange={onChange} onBlur={onBlur} />,
19
- );
20
- textField = component.find(TextField);
21
- });
7
+ const defaultProps = {
8
+ value: 1,
9
+ min: 1,
10
+ max: 10,
11
+ classes: {},
12
+ onChange: jest.fn(),
13
+ };
14
+
15
+ beforeEach(() => {
16
+ jest.clearAllMocks();
17
+ });
22
18
 
23
- it('should render mui TextField', () => {
24
- expect(textField.length).toEqual(1);
25
- });
19
+ describe('rendering', () => {
20
+ it('renders TextField with correct value', () => {
21
+ render(<NumberTextField {...defaultProps} />);
22
+ const input = screen.getByRole('spinbutton');
23
+ expect(input).toHaveValue(1);
24
+ });
26
25
 
27
- describe('props', () => {
28
- let props;
26
+ it('renders with custom label', () => {
27
+ render(<NumberTextField {...defaultProps} label="Custom Label" />);
28
+ expect(screen.getByLabelText('Custom Label')).toBeInTheDocument();
29
+ });
29
30
 
30
- beforeEach(() => {
31
- props = textField.props();
32
- });
31
+ it('renders with suffix', () => {
32
+ const { container } = render(
33
+ <NumberTextField {...defaultProps} suffix="px" />,
34
+ );
35
+ expect(screen.getByText('px')).toBeInTheDocument();
36
+ });
33
37
 
34
- it('should contain value', () => {
35
- expect(props.value).toEqual(value);
36
- });
37
- });
38
+ it('renders without suffix when not provided', () => {
39
+ const { container } = render(
40
+ <NumberTextField {...defaultProps} />,
41
+ );
42
+ const input = screen.getByRole('spinbutton');
43
+ expect(input).toBeInTheDocument();
44
+ });
38
45
 
39
- describe('logic', () => {
40
- describe('clamp', () => {
41
- const assertClamp = (input, expected) => {
42
- it(`${input} => ${expected}`, () => {
43
- const result = component.instance().clamp(input);
44
- expect(result).toEqual(expected);
45
- });
46
- };
46
+ it('renders as disabled when disabled prop is true', () => {
47
+ render(<NumberTextField {...defaultProps} disabled={true} />);
48
+ const input = screen.getByRole('spinbutton');
49
+ expect(input).toBeDisabled();
50
+ });
47
51
 
48
- assertClamp('foo', 1);
49
- assertClamp(1, 1);
50
- assertClamp(2, 2);
51
- assertClamp(0, 1);
52
- assertClamp(-1, 1);
53
- assertClamp(10, 10);
54
- assertClamp(11, 10);
55
- });
52
+ it('renders as enabled when disabled prop is false', () => {
53
+ render(<NumberTextField {...defaultProps} disabled={false} />);
54
+ const input = screen.getByRole('spinbutton');
55
+ expect(input).not.toBeDisabled();
56
+ });
56
57
 
57
- describe('onBlur', () => {
58
- const event = (value) => ({
59
- target: { value },
60
- });
58
+ it('renders with custom variant', () => {
59
+ const { container } = render(
60
+ <NumberTextField {...defaultProps} variant="outlined" />,
61
+ );
62
+ expect(container.querySelector('input')).toBeInTheDocument();
63
+ });
61
64
 
62
- describe('called with valid string representation of int', () => {
63
- it('should be called with parsed int', () => {
64
- const e = event('1');
65
+ it('renders with disableUnderline variant', () => {
66
+ const { container } = render(
67
+ <NumberTextField {...defaultProps} disableUnderline={true} />,
68
+ );
69
+ expect(container.querySelector('input')).toBeInTheDocument();
70
+ });
65
71
 
66
- textField.simulate('change', e);
72
+ it('renders with custom className', () => {
73
+ const { container } = render(
74
+ <NumberTextField {...defaultProps} className="custom-class" />,
75
+ );
76
+ expect(container.querySelector('.custom-class')).toBeInTheDocument();
77
+ });
67
78
 
68
- expect(component.state('value')).toEqual('1');
79
+ it('renders with custom inputClassName', () => {
80
+ const { container } = render(
81
+ <NumberTextField {...defaultProps} inputClassName="input-class" />,
82
+ );
83
+ expect(container.querySelector('.input-class')).toBeInTheDocument();
84
+ });
69
85
 
70
- e.target.value = '100';
86
+ it('renders with ShrinkLabel InputLabelProps', () => {
87
+ render(<NumberTextField {...defaultProps} label="Shrink Label" />);
88
+ expect(screen.getByLabelText('Shrink Label')).toBeInTheDocument();
89
+ });
71
90
 
72
- textField.simulate('change', e);
91
+ it('renders with margin normal', () => {
92
+ const { container } = render(
93
+ <NumberTextField {...defaultProps} />,
94
+ );
95
+ expect(container.querySelector('input')).toBeInTheDocument();
96
+ });
97
+ });
73
98
 
74
- expect(component.state('value')).toEqual('100');
99
+ describe('onChange callback', () => {
100
+ it('calls onChange with clamped value on blur', async () => {
101
+ const user = userEvent.setup();
102
+ const onChange = jest.fn();
103
+ render(<NumberTextField {...defaultProps} onChange={onChange} />);
75
104
 
76
- const beforeMax = (max - 1).toString();
77
- e.target.value = beforeMax;
105
+ const input = screen.getByRole('spinbutton');
106
+ await user.clear(input);
107
+ await user.type(input, '15');
108
+ fireEvent.blur(input);
78
109
 
79
- textField.simulate('blur', e);
110
+ await waitFor(() => {
111
+ expect(onChange).toHaveBeenCalledWith(expect.anything(), 10);
112
+ });
113
+ });
80
114
 
81
- expect(component.state('value')).toEqual(beforeMax);
82
- });
83
- });
115
+ it('calls onChange on Enter key press', async () => {
116
+ const user = userEvent.setup();
117
+ const onChange = jest.fn();
118
+ render(<NumberTextField {...defaultProps} onChange={onChange} />);
84
119
 
85
- describe('called with invalid string representation of int', () => {
86
- it('should be called with min value', () => {
87
- const e = event('aa');
120
+ const input = screen.getByRole('spinbutton');
121
+ await user.clear(input);
122
+ await user.type(input, '5');
123
+ await user.keyboard('{Enter}');
88
124
 
89
- textField.simulate('change', e);
125
+ expect(input).toHaveValue(5);
126
+ });
90
127
 
91
- expect(component.state('value')).toEqual('aa');
128
+ it('calls onChange during typing (unvalidated)', async () => {
129
+ const user = userEvent.setup();
130
+ const onChange = jest.fn();
131
+ render(<NumberTextField {...defaultProps} onChange={onChange} />);
92
132
 
93
- textField.simulate('blur', e);
133
+ const input = screen.getByRole('spinbutton');
134
+ await user.clear(input);
135
+ await user.type(input, '7');
94
136
 
95
- expect(component.state('value')).toEqual(min.toString());
96
- });
97
- });
137
+ // onChange should be called at least once during typing
138
+ expect(input).toHaveValue(7);
139
+ });
98
140
 
99
- describe('called with empty string', () => {
100
- it('should be called with min value', () => {
101
- const e = event('');
141
+ it('does not call onChange when value does not change on blur', async () => {
142
+ const user = userEvent.setup();
143
+ const onChange = jest.fn();
144
+ render(<NumberTextField {...defaultProps} value={5} onChange={onChange} />);
102
145
 
103
- textField.simulate('change', e);
146
+ const input = screen.getByRole('spinbutton');
147
+ fireEvent.blur(input);
104
148
 
105
- expect(component.state('value')).toEqual('');
149
+ expect(onChange).not.toHaveBeenCalled();
150
+ });
151
+ });
106
152
 
107
- textField.simulate('blur', e);
153
+ describe('range constraints', () => {
154
+ it('handles only min constraint', async () => {
155
+ const user = userEvent.setup();
156
+ const { container } = render(
157
+ <NumberTextField
158
+ value={5}
159
+ min={3}
160
+ onChange={jest.fn()}
161
+ />,
162
+ );
163
+
164
+ const input = screen.getByRole('spinbutton');
165
+ expect(input).toHaveValue(5);
166
+ });
167
+
168
+ it('handles only max constraint', async () => {
169
+ const user = userEvent.setup();
170
+ const { container } = render(
171
+ <NumberTextField
172
+ value={5}
173
+ max={10}
174
+ onChange={jest.fn()}
175
+ />,
176
+ );
177
+
178
+ const input = screen.getByRole('spinbutton');
179
+ expect(input).toHaveValue(5);
180
+ });
108
181
 
109
- expect(component.state('value')).toEqual(min.toString());
110
- });
111
- });
182
+ it('handles no constraints', () => {
183
+ const { container } = render(
184
+ <NumberTextField
185
+ value={100}
186
+ onChange={jest.fn()}
187
+ />,
188
+ );
112
189
 
113
- describe('string int less than min', () => {
114
- const value = (min - 1).toString();
190
+ const input = screen.getByRole('spinbutton');
191
+ expect(input).toHaveValue(100);
192
+ });
193
+ });
115
194
 
116
- it('should be called with value of min', () => {
117
- const e = event(value);
195
+ describe('keyboard interactions', () => {
196
+ it('handles Enter key to blur', async () => {
197
+ const user = userEvent.setup();
198
+ const onChange = jest.fn();
199
+ render(<NumberTextField {...defaultProps} onChange={onChange} />);
118
200
 
119
- textField.simulate('change', e);
201
+ const input = screen.getByRole('spinbutton');
202
+ input.focus();
203
+ await user.type(input, '{Enter}');
120
204
 
121
- expect(component.state('value')).toEqual(value);
205
+ expect(input).not.toHaveFocus();
206
+ });
122
207
 
123
- textField.simulate('blur', e);
208
+ it('allows typing numbers', async () => {
209
+ const user = userEvent.setup();
210
+ render(<NumberTextField {...defaultProps} />);
124
211
 
125
- expect(component.state('value')).toEqual(min.toString());
126
- });
127
- });
212
+ const input = screen.getByRole('spinbutton');
213
+ await user.clear(input);
214
+ await user.type(input, '42');
128
215
 
129
- describe('string int exceeds max', () => {
130
- const value = (max + 1).toString();
216
+ expect(input).toHaveValue(42);
217
+ });
218
+
219
+ it('allows negative sign', async () => {
220
+ const user = userEvent.setup();
221
+ render(<NumberTextField {...defaultProps} min={-100} value={0} />);
222
+
223
+ const input = screen.getByRole('spinbutton');
224
+ await user.clear(input);
225
+ await user.type(input, '-5');
226
+
227
+ expect(input).toHaveValue(-5);
228
+ });
229
+ });
131
230
 
132
- it('should be called with value of max', () => {
133
- const e = event(value);
231
+ describe('props updates', () => {
232
+ it('updates value when prop changes', () => {
233
+ const { rerender } = render(
234
+ <NumberTextField {...defaultProps} value={5} />,
235
+ );
134
236
 
135
- textField.simulate('change', e);
237
+ let input = screen.getByRole('spinbutton');
238
+ expect(input).toHaveValue(5);
136
239
 
137
- expect(component.state('value')).toEqual(value);
240
+ rerender(<NumberTextField {...defaultProps} value={8} />);
138
241
 
139
- textField.simulate('blur', e);
242
+ input = screen.getByRole('spinbutton');
243
+ expect(input).toHaveValue(8);
244
+ });
140
245
 
141
- expect(component.state('value')).toEqual(max.toString());
142
- });
143
- });
144
- });
246
+ it('updates constraints when min/max props change', async () => {
247
+ const user = userEvent.setup();
248
+ const onChange = jest.fn();
249
+ const { rerender } = render(
250
+ <NumberTextField
251
+ value={5}
252
+ min={1}
253
+ max={10}
254
+ onChange={onChange}
255
+ />,
256
+ );
257
+
258
+ rerender(
259
+ <NumberTextField
260
+ value={5}
261
+ min={1}
262
+ max={8}
263
+ onChange={onChange}
264
+ />,
265
+ );
266
+
267
+ const input = screen.getByRole('spinbutton');
268
+ await user.clear(input);
269
+ await user.type(input, '15');
270
+ fireEvent.blur(input);
271
+
272
+ await waitFor(() => {
273
+ expect(input).toHaveValue(8); // Clamped to new max
145
274
  });
146
275
  });
276
+
277
+ it('re-clamps value when constraints become more restrictive', () => {
278
+ const onChange = jest.fn();
279
+ const { rerender } = render(
280
+ <NumberTextField
281
+ value={8}
282
+ min={1}
283
+ max={10}
284
+ onChange={onChange}
285
+ />,
286
+ );
287
+
288
+ rerender(
289
+ <NumberTextField
290
+ value={8}
291
+ min={1}
292
+ max={5}
293
+ onChange={onChange}
294
+ />,
295
+ );
296
+
297
+ // Component should re-clamp the value
298
+ expect(screen.getByRole('spinbutton')).toBeInTheDocument();
299
+ });
300
+ });
301
+
302
+ describe('fallback number logic', () => {
303
+ it('defaults to 0 when no min or max is provided', () => {
304
+ render(
305
+ <NumberTextField
306
+ value={undefined}
307
+ onChange={jest.fn()}
308
+ />,
309
+ );
310
+
311
+ const input = screen.getByRole('spinbutton');
312
+ expect(input).toHaveValue(0);
313
+ });
314
+
315
+ it('defaults to max when only max is provided', () => {
316
+ render(
317
+ <NumberTextField
318
+ value={undefined}
319
+ max={15}
320
+ onChange={jest.fn()}
321
+ />,
322
+ );
323
+
324
+ const input = screen.getByRole('spinbutton');
325
+ expect(input).toHaveValue(15);
326
+ });
327
+
328
+ it('defaults to min when only min is provided', () => {
329
+ render(
330
+ <NumberTextField
331
+ value={undefined}
332
+ min={5}
333
+ onChange={jest.fn()}
334
+ />,
335
+ );
336
+
337
+ const input = screen.getByRole('spinbutton');
338
+ expect(input).toHaveValue(5);
339
+ });
147
340
  });
148
341
  });