@pie-lib/render-ui 5.1.1-next.0 → 5.2.0-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 (48) hide show
  1. package/lib/assets/enableAudioAutoplayImage.js +0 -1
  2. package/lib/assets/enableAudioAutoplayImage.js.map +1 -1
  3. package/lib/collapsible/index.js +0 -3
  4. package/lib/collapsible/index.js.map +1 -1
  5. package/lib/color.js +0 -4
  6. package/lib/color.js.map +1 -1
  7. package/lib/feedback.js +0 -9
  8. package/lib/feedback.js.map +1 -1
  9. package/lib/has-media.js +0 -1
  10. package/lib/has-media.js.map +1 -1
  11. package/lib/has-text.js +0 -2
  12. package/lib/has-text.js.map +1 -1
  13. package/lib/html-and-math.js +0 -2
  14. package/lib/html-and-math.js.map +1 -1
  15. package/lib/index.js +0 -7
  16. package/lib/index.js.map +1 -1
  17. package/lib/input-container.js +0 -1
  18. package/lib/input-container.js.map +1 -1
  19. package/lib/preview-layout.js +0 -2
  20. package/lib/preview-layout.js.map +1 -1
  21. package/lib/preview-prompt.js +0 -26
  22. package/lib/preview-prompt.js.map +1 -1
  23. package/lib/purpose.js +0 -3
  24. package/lib/purpose.js.map +1 -1
  25. package/lib/readable.js +0 -3
  26. package/lib/readable.js.map +1 -1
  27. package/lib/response-indicators.js +0 -8
  28. package/lib/response-indicators.js.map +1 -1
  29. package/lib/ui-layout.js +0 -3
  30. package/lib/ui-layout.js.map +1 -1
  31. package/lib/withUndoReset.js +2 -14
  32. package/lib/withUndoReset.js.map +1 -1
  33. package/package.json +11 -7
  34. package/src/__tests__/color.test.js +248 -1
  35. package/src/__tests__/feedback.test.jsx +279 -0
  36. package/src/__tests__/has-media.test.js +0 -1
  37. package/src/__tests__/has-text.test.js +0 -1
  38. package/src/__tests__/input-container.test.jsx +328 -0
  39. package/src/__tests__/preview-layout.test.jsx +349 -0
  40. package/src/__tests__/preview-prompt.test.jsx +320 -0
  41. package/src/__tests__/response-indicators.test.jsx +1 -1
  42. package/src/__tests__/ui-layout.test.jsx +3 -1
  43. package/src/color.js +9 -7
  44. package/src/feedback.jsx +4 -14
  45. package/src/input-container.jsx +2 -5
  46. package/src/preview-layout.jsx +6 -1
  47. package/src/ui-layout.jsx +1 -2
  48. package/NEXT.CHANGELOG.json +0 -1
@@ -0,0 +1,328 @@
1
+ import React from 'react';
2
+ import { renderWithTheme, screen } from '@pie-lib/test-utils';
3
+ import InputContainer from '../input-container';
4
+
5
+ describe('InputContainer', () => {
6
+ const defaultProps = {
7
+ label: 'Test Label',
8
+ children: <input data-testid="test-input" type="text" />,
9
+ };
10
+
11
+ describe('rendering', () => {
12
+ it('should render with label and children', () => {
13
+ renderWithTheme(<InputContainer {...defaultProps} />);
14
+ expect(screen.getByText('Test Label')).toBeInTheDocument();
15
+ expect(screen.getByTestId('test-input')).toBeInTheDocument();
16
+ });
17
+
18
+ it('should render with string label', () => {
19
+ renderWithTheme(
20
+ <InputContainer label="String Label">
21
+ <div data-testid="child">Content</div>
22
+ </InputContainer>,
23
+ );
24
+ expect(screen.getByText('String Label')).toBeInTheDocument();
25
+ });
26
+
27
+ it('should render with object label', () => {
28
+ const objectLabel = <span data-testid="label-object">Object Label</span>;
29
+ renderWithTheme(
30
+ <InputContainer label={objectLabel}>
31
+ <div data-testid="child">Content</div>
32
+ </InputContainer>,
33
+ );
34
+ expect(screen.getByTestId('label-object')).toBeInTheDocument();
35
+ expect(screen.getByText('Object Label')).toBeInTheDocument();
36
+ });
37
+
38
+ it('should render with single child node', () => {
39
+ renderWithTheme(
40
+ <InputContainer label="Label">
41
+ <input data-testid="single-child" type="text" />
42
+ </InputContainer>,
43
+ );
44
+ expect(screen.getByTestId('single-child')).toBeInTheDocument();
45
+ });
46
+
47
+ it('should render with multiple children', () => {
48
+ renderWithTheme(
49
+ <InputContainer label="Label">
50
+ <input data-testid="child-1" type="text" />
51
+ <button data-testid="child-2">Button</button>
52
+ <span data-testid="child-3">Text</span>
53
+ </InputContainer>,
54
+ );
55
+ expect(screen.getByTestId('child-1')).toBeInTheDocument();
56
+ expect(screen.getByTestId('child-2')).toBeInTheDocument();
57
+ expect(screen.getByTestId('child-3')).toBeInTheDocument();
58
+ });
59
+
60
+ it('should render with array of children', () => {
61
+ const children = [
62
+ <div key="1" data-testid="array-child-1">
63
+ First
64
+ </div>,
65
+ <div key="2" data-testid="array-child-2">
66
+ Second
67
+ </div>,
68
+ ];
69
+ renderWithTheme(<InputContainer label="Label">{children}</InputContainer>);
70
+ expect(screen.getByTestId('array-child-1')).toBeInTheDocument();
71
+ expect(screen.getByTestId('array-child-2')).toBeInTheDocument();
72
+ });
73
+
74
+ it('should render with complex children', () => {
75
+ renderWithTheme(
76
+ <InputContainer label="Complex">
77
+ <div>
78
+ <input data-testid="nested-input" type="text" />
79
+ <div>
80
+ <button data-testid="nested-button">Click</button>
81
+ </div>
82
+ </div>
83
+ </InputContainer>,
84
+ );
85
+ expect(screen.getByTestId('nested-input')).toBeInTheDocument();
86
+ expect(screen.getByTestId('nested-button')).toBeInTheDocument();
87
+ });
88
+ });
89
+
90
+ describe('className prop', () => {
91
+ it('should apply custom className', () => {
92
+ const { container } = renderWithTheme(<InputContainer {...defaultProps} className="custom-class" />);
93
+ const formControl = container.querySelector('.custom-class');
94
+ expect(formControl).toBeInTheDocument();
95
+ });
96
+
97
+ it('should work without className', () => {
98
+ const { container } = renderWithTheme(<InputContainer {...defaultProps} />);
99
+ const formControl = container.querySelector('.MuiFormControl-root');
100
+ expect(formControl).toBeInTheDocument();
101
+ });
102
+
103
+ it('should apply multiple classNames', () => {
104
+ const { container } = renderWithTheme(<InputContainer {...defaultProps} className="class-one class-two" />);
105
+ const formControl = container.querySelector('.class-one.class-two');
106
+ expect(formControl).toBeInTheDocument();
107
+ });
108
+ });
109
+
110
+ describe('label styling', () => {
111
+ it('should apply shrink prop to InputLabel', () => {
112
+ const { container } = renderWithTheme(<InputContainer {...defaultProps} />);
113
+ const label = container.querySelector('.MuiInputLabel-shrink');
114
+ expect(label).toBeInTheDocument();
115
+ });
116
+
117
+ it('should render label with correct text content', () => {
118
+ renderWithTheme(<InputContainer label="My Label" children={<div />} />);
119
+ expect(screen.getByText('My Label')).toBeInTheDocument();
120
+ });
121
+
122
+ it('should render label with HTML content', () => {
123
+ const htmlLabel = (
124
+ <span>
125
+ Label with <strong data-testid="bold">bold</strong> text
126
+ </span>
127
+ );
128
+ renderWithTheme(<InputContainer label={htmlLabel} children={<div />} />);
129
+ expect(screen.getByTestId('bold')).toBeInTheDocument();
130
+ expect(screen.getByText('bold')).toBeInTheDocument();
131
+ });
132
+ });
133
+
134
+ describe('MUI FormControl integration', () => {
135
+ it('should render as a FormControl', () => {
136
+ const { container } = renderWithTheme(<InputContainer {...defaultProps} />);
137
+ const formControl = container.querySelector('.MuiFormControl-root');
138
+ expect(formControl).toBeInTheDocument();
139
+ });
140
+
141
+ it('should have correct FormControl structure', () => {
142
+ const { container } = renderWithTheme(<InputContainer {...defaultProps} />);
143
+ const formControl = container.querySelector('.MuiFormControl-root');
144
+ const label = formControl?.querySelector('.MuiInputLabel-root');
145
+ expect(label).toBeInTheDocument();
146
+ });
147
+ });
148
+
149
+ describe('edge cases', () => {
150
+ it('should handle empty string label', () => {
151
+ renderWithTheme(<InputContainer label="" children={<div data-testid="child" />} />);
152
+ expect(screen.getByTestId('child')).toBeInTheDocument();
153
+ });
154
+
155
+ it('should handle label with special characters', () => {
156
+ const specialLabel = 'Label with <>&"\'';
157
+ renderWithTheme(<InputContainer label={specialLabel} children={<div />} />);
158
+ expect(screen.getByText(specialLabel)).toBeInTheDocument();
159
+ });
160
+
161
+ it('should handle null children within array', () => {
162
+ const children = [
163
+ <div key="1" data-testid="valid-child">
164
+ Valid
165
+ </div>,
166
+ null,
167
+ <div key="2" data-testid="another-valid">
168
+ Another
169
+ </div>,
170
+ ];
171
+ renderWithTheme(<InputContainer label="Label">{children}</InputContainer>);
172
+ expect(screen.getByTestId('valid-child')).toBeInTheDocument();
173
+ expect(screen.getByTestId('another-valid')).toBeInTheDocument();
174
+ });
175
+
176
+ it('should handle fragment children', () => {
177
+ renderWithTheme(
178
+ <InputContainer label="Label">
179
+ <>
180
+ <div data-testid="fragment-child-1">First</div>
181
+ <div data-testid="fragment-child-2">Second</div>
182
+ </>
183
+ </InputContainer>,
184
+ );
185
+ expect(screen.getByTestId('fragment-child-1')).toBeInTheDocument();
186
+ expect(screen.getByTestId('fragment-child-2')).toBeInTheDocument();
187
+ });
188
+
189
+ it('should handle very long label text', () => {
190
+ const longLabel = 'A'.repeat(200);
191
+ renderWithTheme(<InputContainer label={longLabel} children={<div />} />);
192
+ expect(screen.getByText(longLabel)).toBeInTheDocument();
193
+ });
194
+ });
195
+
196
+ describe('accessibility', () => {
197
+ it('should have proper label association structure', () => {
198
+ renderWithTheme(<InputContainer {...defaultProps} />);
199
+ const label = screen.getByText('Test Label');
200
+ expect(label).toBeInTheDocument();
201
+ expect(label.tagName).toBe('LABEL');
202
+ });
203
+
204
+ it('should render as a form control with label', () => {
205
+ const { container } = renderWithTheme(<InputContainer {...defaultProps} />);
206
+ const formControl = container.querySelector('.MuiFormControl-root');
207
+ const label = container.querySelector('.MuiInputLabel-root');
208
+ expect(formControl).toContainElement(label);
209
+ });
210
+
211
+ it('should maintain label visibility', () => {
212
+ renderWithTheme(<InputContainer {...defaultProps} />);
213
+ const label = screen.getByText('Test Label');
214
+ expect(label).toBeVisible();
215
+ });
216
+ });
217
+
218
+ describe('re-rendering', () => {
219
+ it('should update when label changes', () => {
220
+ const { rerender } = renderWithTheme(<InputContainer {...defaultProps} />);
221
+ expect(screen.getByText('Test Label')).toBeInTheDocument();
222
+
223
+ rerender(
224
+ <InputContainer label="Updated Label">
225
+ <input data-testid="test-input" type="text" />
226
+ </InputContainer>,
227
+ );
228
+ expect(screen.getByText('Updated Label')).toBeInTheDocument();
229
+ expect(screen.queryByText('Test Label')).not.toBeInTheDocument();
230
+ });
231
+
232
+ it('should update when children change', () => {
233
+ const { rerender } = renderWithTheme(<InputContainer {...defaultProps} />);
234
+ expect(screen.getByTestId('test-input')).toBeInTheDocument();
235
+
236
+ rerender(
237
+ <InputContainer label="Test Label">
238
+ <button data-testid="test-button">New Child</button>
239
+ </InputContainer>,
240
+ );
241
+ expect(screen.getByTestId('test-button')).toBeInTheDocument();
242
+ expect(screen.queryByTestId('test-input')).not.toBeInTheDocument();
243
+ });
244
+
245
+ it('should update when className changes', () => {
246
+ const { container, rerender } = renderWithTheme(<InputContainer {...defaultProps} className="class-a" />);
247
+ expect(container.querySelector('.class-a')).toBeInTheDocument();
248
+
249
+ rerender(<InputContainer {...defaultProps} className="class-b" />);
250
+ expect(container.querySelector('.class-b')).toBeInTheDocument();
251
+ expect(container.querySelector('.class-a')).not.toBeInTheDocument();
252
+ });
253
+ });
254
+
255
+ describe('styling and layout', () => {
256
+ it('should render with expected structure', () => {
257
+ const { container } = renderWithTheme(<InputContainer {...defaultProps} />);
258
+ const formControl = container.querySelector('.MuiFormControl-root');
259
+ const label = container.querySelector('.MuiInputLabel-root');
260
+ const input = screen.getByTestId('test-input');
261
+
262
+ expect(formControl).toBeInTheDocument();
263
+ expect(formControl).toContainElement(label);
264
+ expect(formControl).toContainElement(input);
265
+ });
266
+
267
+ it('should apply styled components correctly', () => {
268
+ const { container } = renderWithTheme(<InputContainer {...defaultProps} />);
269
+ const formControl = container.querySelector('.MuiFormControl-root');
270
+ expect(formControl).toBeInTheDocument();
271
+ // StyledFormControl should be applied
272
+ expect(formControl).toHaveClass('MuiFormControl-root');
273
+ });
274
+
275
+ it('should have label with shrink class', () => {
276
+ const { container } = renderWithTheme(<InputContainer {...defaultProps} />);
277
+ const label = container.querySelector('.MuiInputLabel-root');
278
+ expect(label).toHaveClass('MuiInputLabel-shrink');
279
+ });
280
+ });
281
+
282
+ describe('integration', () => {
283
+ it('should work with form elements', () => {
284
+ renderWithTheme(
285
+ <InputContainer label="Form Field">
286
+ <input data-testid="text-input" type="text" />
287
+ </InputContainer>,
288
+ );
289
+ expect(screen.getByTestId('text-input')).toBeInTheDocument();
290
+ });
291
+
292
+ it('should work with custom components', () => {
293
+ const CustomComponent = () => <div data-testid="custom">Custom</div>;
294
+ renderWithTheme(
295
+ <InputContainer label="Custom Field">
296
+ <CustomComponent />
297
+ </InputContainer>,
298
+ );
299
+ expect(screen.getByTestId('custom')).toBeInTheDocument();
300
+ });
301
+
302
+ it('should work with nested InputContainers', () => {
303
+ renderWithTheme(
304
+ <InputContainer label="Outer">
305
+ <div>
306
+ <InputContainer label="Inner">
307
+ <input data-testid="inner-input" type="text" />
308
+ </InputContainer>
309
+ </div>
310
+ </InputContainer>,
311
+ );
312
+ expect(screen.getByText('Outer')).toBeInTheDocument();
313
+ expect(screen.getByText('Inner')).toBeInTheDocument();
314
+ expect(screen.getByTestId('inner-input')).toBeInTheDocument();
315
+ });
316
+
317
+ it('should work with MUI components as children', () => {
318
+ renderWithTheme(
319
+ <InputContainer label="MUI Field">
320
+ <div data-testid="mui-child" className="MuiInput-root">
321
+ MUI Component
322
+ </div>
323
+ </InputContainer>,
324
+ );
325
+ expect(screen.getByTestId('mui-child')).toBeInTheDocument();
326
+ });
327
+ });
328
+ });
@@ -0,0 +1,349 @@
1
+ import React from 'react';
2
+ import { renderWithTheme, screen } from '@pie-lib/test-utils';
3
+ import PreviewLayout from '../preview-layout';
4
+
5
+ jest.mock('../ui-layout', () => {
6
+ return function UiLayout({ children, extraCSSRules, fontSizeFactor, classes, ...props }) {
7
+ return (
8
+ <div
9
+ data-testid="ui-layout"
10
+ data-extra-css-rules={extraCSSRules ? JSON.stringify(extraCSSRules) : undefined}
11
+ data-font-size-factor={fontSizeFactor}
12
+ data-classes={classes ? JSON.stringify(classes) : undefined}
13
+ {...props}
14
+ >
15
+ {children}
16
+ </div>
17
+ );
18
+ };
19
+ });
20
+
21
+ describe('PreviewLayout', () => {
22
+ const defaultProps = {
23
+ children: <div data-testid="child-content">Test Content</div>,
24
+ };
25
+
26
+ describe('rendering', () => {
27
+ it('should render children correctly', () => {
28
+ renderWithTheme(<PreviewLayout {...defaultProps} />);
29
+ expect(screen.getByTestId('child-content')).toBeInTheDocument();
30
+ expect(screen.getByText('Test Content')).toBeInTheDocument();
31
+ });
32
+
33
+ it('should render UiLayout component', () => {
34
+ renderWithTheme(<PreviewLayout {...defaultProps} />);
35
+ expect(screen.getByTestId('ui-layout')).toBeInTheDocument();
36
+ });
37
+
38
+ it('should render with string children', () => {
39
+ renderWithTheme(<PreviewLayout>Simple text content</PreviewLayout>);
40
+ expect(screen.getByText('Simple text content')).toBeInTheDocument();
41
+ });
42
+
43
+ it('should render with multiple children', () => {
44
+ renderWithTheme(
45
+ <PreviewLayout>
46
+ <div data-testid="child-1">First</div>
47
+ <div data-testid="child-2">Second</div>
48
+ <div data-testid="child-3">Third</div>
49
+ </PreviewLayout>,
50
+ );
51
+ expect(screen.getByTestId('child-1')).toBeInTheDocument();
52
+ expect(screen.getByTestId('child-2')).toBeInTheDocument();
53
+ expect(screen.getByTestId('child-3')).toBeInTheDocument();
54
+ });
55
+
56
+ it('should render with array of children', () => {
57
+ const children = [
58
+ <div key="1" data-testid="array-child-1">
59
+ First
60
+ </div>,
61
+ <div key="2" data-testid="array-child-2">
62
+ Second
63
+ </div>,
64
+ ];
65
+ renderWithTheme(<PreviewLayout>{children}</PreviewLayout>);
66
+ expect(screen.getByTestId('array-child-1')).toBeInTheDocument();
67
+ expect(screen.getByTestId('array-child-2')).toBeInTheDocument();
68
+ });
69
+ });
70
+
71
+ describe('accessibility props', () => {
72
+ it('should apply aria-label and role when ariaLabel is provided', () => {
73
+ renderWithTheme(
74
+ <PreviewLayout ariaLabel="Test Label" role="region">
75
+ <div>Content</div>
76
+ </PreviewLayout>,
77
+ );
78
+ const uiLayout = screen.getByTestId('ui-layout');
79
+ expect(uiLayout).toHaveAttribute('aria-label', 'Test Label');
80
+ expect(uiLayout).toHaveAttribute('role', 'region');
81
+ });
82
+
83
+ it('should not apply accessibility attributes when ariaLabel is not provided', () => {
84
+ renderWithTheme(
85
+ <PreviewLayout role="region">
86
+ <div>Content</div>
87
+ </PreviewLayout>,
88
+ );
89
+ const uiLayout = screen.getByTestId('ui-layout');
90
+ expect(uiLayout).not.toHaveAttribute('aria-label');
91
+ expect(uiLayout).not.toHaveAttribute('role');
92
+ });
93
+
94
+ it('should handle ariaLabel with role', () => {
95
+ renderWithTheme(
96
+ <PreviewLayout ariaLabel="Navigation" role="navigation">
97
+ <div>Content</div>
98
+ </PreviewLayout>,
99
+ );
100
+ const uiLayout = screen.getByTestId('ui-layout');
101
+ expect(uiLayout).toHaveAttribute('aria-label', 'Navigation');
102
+ expect(uiLayout).toHaveAttribute('role', 'navigation');
103
+ });
104
+
105
+ it('should handle different role values', () => {
106
+ const roles = ['main', 'complementary', 'article', 'banner'];
107
+ roles.forEach((role) => {
108
+ const { unmount } = renderWithTheme(
109
+ <PreviewLayout ariaLabel={`Test ${role}`} role={role}>
110
+ <div>Content</div>
111
+ </PreviewLayout>,
112
+ );
113
+ const uiLayout = screen.getByTestId('ui-layout');
114
+ expect(uiLayout).toHaveAttribute('role', role);
115
+ unmount();
116
+ });
117
+ });
118
+ });
119
+
120
+ describe('extraCSSRules prop', () => {
121
+ it('should pass extraCSSRules to UiLayout', () => {
122
+ const extraCSSRules = {
123
+ names: ['rule1', 'rule2'],
124
+ rules: '.custom { color: red; }',
125
+ };
126
+ renderWithTheme(
127
+ <PreviewLayout extraCSSRules={extraCSSRules}>
128
+ <div>Content</div>
129
+ </PreviewLayout>,
130
+ );
131
+ const uiLayout = screen.getByTestId('ui-layout');
132
+ expect(uiLayout).toHaveAttribute('data-extra-css-rules', JSON.stringify(extraCSSRules));
133
+ });
134
+
135
+ it('should handle extraCSSRules with empty names array', () => {
136
+ const extraCSSRules = {
137
+ names: [],
138
+ rules: '.test { margin: 0; }',
139
+ };
140
+ renderWithTheme(
141
+ <PreviewLayout extraCSSRules={extraCSSRules}>
142
+ <div>Content</div>
143
+ </PreviewLayout>,
144
+ );
145
+ const uiLayout = screen.getByTestId('ui-layout');
146
+ expect(uiLayout).toHaveAttribute('data-extra-css-rules', JSON.stringify(extraCSSRules));
147
+ });
148
+
149
+ it('should handle extraCSSRules with multiple rule names', () => {
150
+ const extraCSSRules = {
151
+ names: ['header', 'footer', 'sidebar'],
152
+ rules: '.header { padding: 10px; } .footer { margin: 5px; }',
153
+ };
154
+ renderWithTheme(
155
+ <PreviewLayout extraCSSRules={extraCSSRules}>
156
+ <div>Content</div>
157
+ </PreviewLayout>,
158
+ );
159
+ const uiLayout = screen.getByTestId('ui-layout');
160
+ const parsedRules = JSON.parse(uiLayout.getAttribute('data-extra-css-rules'));
161
+ expect(parsedRules.names).toHaveLength(3);
162
+ expect(parsedRules.names).toContain('header');
163
+ expect(parsedRules.names).toContain('footer');
164
+ expect(parsedRules.names).toContain('sidebar');
165
+ });
166
+
167
+ it('should work without extraCSSRules', () => {
168
+ renderWithTheme(
169
+ <PreviewLayout>
170
+ <div>Content</div>
171
+ </PreviewLayout>,
172
+ );
173
+ const uiLayout = screen.getByTestId('ui-layout');
174
+ expect(uiLayout).not.toHaveAttribute('data-extra-css-rules');
175
+ });
176
+ });
177
+
178
+ describe('fontSizeFactor prop', () => {
179
+ it('should pass fontSizeFactor to UiLayout', () => {
180
+ renderWithTheme(
181
+ <PreviewLayout fontSizeFactor={1.5}>
182
+ <div>Content</div>
183
+ </PreviewLayout>,
184
+ );
185
+ const uiLayout = screen.getByTestId('ui-layout');
186
+ expect(uiLayout).toHaveAttribute('data-font-size-factor', '1.5');
187
+ });
188
+
189
+ it('should handle fontSizeFactor of 1', () => {
190
+ renderWithTheme(
191
+ <PreviewLayout fontSizeFactor={1}>
192
+ <div>Content</div>
193
+ </PreviewLayout>,
194
+ );
195
+ const uiLayout = screen.getByTestId('ui-layout');
196
+ expect(uiLayout).toHaveAttribute('data-font-size-factor', '1');
197
+ });
198
+
199
+ it('should handle fontSizeFactor less than 1', () => {
200
+ renderWithTheme(
201
+ <PreviewLayout fontSizeFactor={0.8}>
202
+ <div>Content</div>
203
+ </PreviewLayout>,
204
+ );
205
+ const uiLayout = screen.getByTestId('ui-layout');
206
+ expect(uiLayout).toHaveAttribute('data-font-size-factor', '0.8');
207
+ });
208
+
209
+ it('should handle fontSizeFactor greater than 2', () => {
210
+ renderWithTheme(
211
+ <PreviewLayout fontSizeFactor={2.5}>
212
+ <div>Content</div>
213
+ </PreviewLayout>,
214
+ );
215
+ const uiLayout = screen.getByTestId('ui-layout');
216
+ expect(uiLayout).toHaveAttribute('data-font-size-factor', '2.5');
217
+ });
218
+ });
219
+
220
+ describe('classes prop', () => {
221
+ it('should pass classes to UiLayout', () => {
222
+ const classes = { root: 'custom-root', container: 'custom-container' };
223
+ renderWithTheme(
224
+ <PreviewLayout classes={classes}>
225
+ <div>Content</div>
226
+ </PreviewLayout>,
227
+ );
228
+ const uiLayout = screen.getByTestId('ui-layout');
229
+ expect(uiLayout).toHaveAttribute('data-classes', JSON.stringify(classes));
230
+ });
231
+ });
232
+
233
+ describe('all props together', () => {
234
+ it('should handle all props simultaneously', () => {
235
+ const extraCSSRules = {
236
+ names: ['custom'],
237
+ rules: '.custom { color: blue; }',
238
+ };
239
+ const classes = { root: 'test-root' };
240
+
241
+ renderWithTheme(
242
+ <PreviewLayout
243
+ ariaLabel="Complete Layout"
244
+ role="main"
245
+ extraCSSRules={extraCSSRules}
246
+ fontSizeFactor={1.2}
247
+ classes={classes}
248
+ >
249
+ <div data-testid="all-props-child">All props content</div>
250
+ </PreviewLayout>,
251
+ );
252
+
253
+ const uiLayout = screen.getByTestId('ui-layout');
254
+ expect(uiLayout).toHaveAttribute('aria-label', 'Complete Layout');
255
+ expect(uiLayout).toHaveAttribute('role', 'main');
256
+ expect(uiLayout).toHaveAttribute('data-extra-css-rules', JSON.stringify(extraCSSRules));
257
+ expect(uiLayout).toHaveAttribute('data-font-size-factor', '1.2');
258
+ expect(uiLayout).toHaveAttribute('data-classes', JSON.stringify(classes));
259
+ expect(screen.getByTestId('all-props-child')).toBeInTheDocument();
260
+ });
261
+ });
262
+
263
+ describe('edge cases', () => {
264
+ it('should handle null children gracefully', () => {
265
+ renderWithTheme(<PreviewLayout>{null}</PreviewLayout>);
266
+ expect(screen.getByTestId('ui-layout')).toBeInTheDocument();
267
+ });
268
+
269
+ it('should handle undefined children gracefully', () => {
270
+ renderWithTheme(<PreviewLayout>{undefined}</PreviewLayout>);
271
+ expect(screen.getByTestId('ui-layout')).toBeInTheDocument();
272
+ });
273
+
274
+ it('should handle empty string ariaLabel', () => {
275
+ renderWithTheme(
276
+ <PreviewLayout ariaLabel="" role="region">
277
+ <div>Content</div>
278
+ </PreviewLayout>,
279
+ );
280
+ const uiLayout = screen.getByTestId('ui-layout');
281
+ expect(uiLayout).not.toHaveAttribute('aria-label');
282
+ expect(uiLayout).not.toHaveAttribute('role');
283
+ });
284
+
285
+ it('should handle zero fontSizeFactor', () => {
286
+ renderWithTheme(
287
+ <PreviewLayout fontSizeFactor={0}>
288
+ <div>Content</div>
289
+ </PreviewLayout>,
290
+ );
291
+ const uiLayout = screen.getByTestId('ui-layout');
292
+ expect(uiLayout).toHaveAttribute('data-font-size-factor', '0');
293
+ });
294
+
295
+ it('should handle negative fontSizeFactor', () => {
296
+ renderWithTheme(
297
+ <PreviewLayout fontSizeFactor={-1}>
298
+ <div>Content</div>
299
+ </PreviewLayout>,
300
+ );
301
+ const uiLayout = screen.getByTestId('ui-layout');
302
+ expect(uiLayout).toHaveAttribute('data-font-size-factor', '-1');
303
+ });
304
+ });
305
+
306
+ describe('re-rendering', () => {
307
+ it('should update when props change', () => {
308
+ const { rerender } = renderWithTheme(
309
+ <PreviewLayout ariaLabel="Initial" role="region">
310
+ <div data-testid="content">Initial content</div>
311
+ </PreviewLayout>,
312
+ );
313
+
314
+ expect(screen.getByText('Initial content')).toBeInTheDocument();
315
+ const uiLayout = screen.getByTestId('ui-layout');
316
+ expect(uiLayout).toHaveAttribute('aria-label', 'Initial');
317
+
318
+ rerender(
319
+ <PreviewLayout ariaLabel="Updated" role="main">
320
+ <div data-testid="content">Updated content</div>
321
+ </PreviewLayout>,
322
+ );
323
+
324
+ expect(screen.getByText('Updated content')).toBeInTheDocument();
325
+ expect(uiLayout).toHaveAttribute('aria-label', 'Updated');
326
+ expect(uiLayout).toHaveAttribute('role', 'main');
327
+ });
328
+
329
+ it('should update fontSizeFactor dynamically', () => {
330
+ const { rerender } = renderWithTheme(
331
+ <PreviewLayout fontSizeFactor={1}>
332
+ <div>Content</div>
333
+ </PreviewLayout>,
334
+ );
335
+
336
+ let uiLayout = screen.getByTestId('ui-layout');
337
+ expect(uiLayout).toHaveAttribute('data-font-size-factor', '1');
338
+
339
+ rerender(
340
+ <PreviewLayout fontSizeFactor={2}>
341
+ <div>Content</div>
342
+ </PreviewLayout>,
343
+ );
344
+
345
+ uiLayout = screen.getByTestId('ui-layout');
346
+ expect(uiLayout).toHaveAttribute('data-font-size-factor', '2');
347
+ });
348
+ });
349
+ });