@pie-lib/config-ui 12.0.0-beta.5 → 12.0.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.
- package/CHANGELOG.json +8 -1653
- package/CHANGELOG.md +345 -4
- package/LICENSE.md +5 -0
- package/NEXT.CHANGELOG.json +1 -0
- package/lib/alert-dialog.js +40 -10
- package/lib/alert-dialog.js.map +1 -1
- package/lib/checkbox.js +58 -48
- package/lib/checkbox.js.map +1 -1
- package/lib/choice-configuration/feedback-menu.js +24 -26
- package/lib/choice-configuration/feedback-menu.js.map +1 -1
- package/lib/choice-configuration/index.js +182 -185
- package/lib/choice-configuration/index.js.map +1 -1
- package/lib/choice-utils.js +5 -7
- package/lib/choice-utils.js.map +1 -1
- package/lib/feedback-config/feedback-selector.js +69 -73
- package/lib/feedback-config/feedback-selector.js.map +1 -1
- package/lib/feedback-config/group.js +22 -25
- package/lib/feedback-config/group.js.map +1 -1
- package/lib/feedback-config/index.js +41 -44
- package/lib/feedback-config/index.js.map +1 -1
- package/lib/form-section.js +31 -25
- package/lib/form-section.js.map +1 -1
- package/lib/help.js +37 -47
- package/lib/help.js.map +1 -1
- package/lib/index.js +1 -2
- package/lib/index.js.map +1 -1
- package/lib/input.js +12 -17
- package/lib/input.js.map +1 -1
- package/lib/inputs.js +58 -67
- package/lib/inputs.js.map +1 -1
- package/lib/langs.js +56 -70
- package/lib/langs.js.map +1 -1
- package/lib/layout/config-layout.js +78 -47
- package/lib/layout/config-layout.js.map +1 -1
- package/lib/layout/index.js.map +1 -1
- package/lib/layout/layout-contents.js +58 -60
- package/lib/layout/layout-contents.js.map +1 -1
- package/lib/layout/settings-box.js +25 -33
- package/lib/layout/settings-box.js.map +1 -1
- package/lib/mui-box/index.js +41 -50
- package/lib/mui-box/index.js.map +1 -1
- package/lib/number-text-field-custom.js +151 -89
- package/lib/number-text-field-custom.js.map +1 -1
- package/lib/number-text-field.js +74 -63
- package/lib/number-text-field.js.map +1 -1
- package/lib/radio-with-label.js +30 -16
- package/lib/radio-with-label.js.map +1 -1
- package/lib/settings/display-size.js +16 -20
- package/lib/settings/display-size.js.map +1 -1
- package/lib/settings/index.js +13 -19
- package/lib/settings/index.js.map +1 -1
- package/lib/settings/panel.js +140 -141
- package/lib/settings/panel.js.map +1 -1
- package/lib/settings/settings-radio-label.js +29 -16
- package/lib/settings/settings-radio-label.js.map +1 -1
- package/lib/settings/toggle.js +39 -25
- package/lib/settings/toggle.js.map +1 -1
- package/lib/tabs/index.js +18 -30
- package/lib/tabs/index.js.map +1 -1
- package/lib/tags-input/index.js +49 -61
- package/lib/tags-input/index.js.map +1 -1
- package/lib/two-choice.js +33 -43
- package/lib/two-choice.js.map +1 -1
- package/lib/with-stateful-model.js +8 -12
- package/lib/with-stateful-model.js.map +1 -1
- package/package.json +22 -11
- package/src/__tests__/alert-dialog.test.jsx +283 -0
- package/src/__tests__/checkbox.test.jsx +249 -0
- package/src/__tests__/choice-utils.test.js +12 -0
- package/src/__tests__/form-section.test.jsx +334 -0
- package/src/__tests__/help.test.jsx +184 -0
- package/src/__tests__/input.test.jsx +192 -0
- package/src/__tests__/langs.test.jsx +457 -0
- package/src/__tests__/number-text-field-custom.test.jsx +438 -0
- package/src/__tests__/number-text-field.test.jsx +341 -0
- package/src/__tests__/radio-with-label.test.jsx +259 -0
- package/src/__tests__/settings-panel.test.js +187 -0
- package/src/__tests__/settings.test.jsx +515 -0
- package/src/__tests__/tabs.test.jsx +193 -0
- package/src/__tests__/two-choice.test.js +110 -0
- package/src/__tests__/with-stateful-model.test.jsx +145 -0
- package/src/alert-dialog.jsx +30 -8
- package/src/checkbox.jsx +43 -37
- package/src/choice-configuration/__tests__/feedback-menu.test.jsx +163 -0
- package/src/choice-configuration/__tests__/index.test.jsx +234 -0
- package/src/choice-configuration/feedback-menu.jsx +6 -6
- package/src/choice-configuration/index.jsx +208 -192
- package/src/feedback-config/__tests__/feedback-config.test.jsx +141 -0
- package/src/feedback-config/__tests__/feedback-selector.test.jsx +107 -0
- package/src/feedback-config/feedback-selector.jsx +52 -53
- package/src/feedback-config/group.jsx +21 -22
- package/src/feedback-config/index.jsx +27 -29
- package/src/form-section.jsx +26 -18
- package/src/help.jsx +20 -28
- package/src/input.jsx +1 -1
- package/src/inputs.jsx +35 -44
- package/src/langs.jsx +41 -46
- package/src/layout/__tests__/config.layout.test.jsx +59 -0
- package/src/layout/__tests__/layout-content.test.jsx +3 -0
- package/src/layout/config-layout.jsx +53 -23
- package/src/layout/layout-contents.jsx +38 -40
- package/src/layout/settings-box.jsx +16 -19
- package/src/mui-box/index.jsx +35 -43
- package/src/number-text-field-custom.jsx +117 -65
- package/src/number-text-field.jsx +51 -34
- package/src/radio-with-label.jsx +26 -10
- package/src/settings/display-size.jsx +12 -11
- package/src/settings/index.js +2 -1
- package/src/settings/panel.jsx +101 -92
- package/src/settings/settings-radio-label.jsx +26 -10
- package/src/settings/toggle.jsx +37 -18
- package/src/tabs/index.jsx +8 -8
- package/src/tags-input/__tests__/index.test.jsx +113 -0
- package/src/tags-input/index.jsx +35 -38
- package/src/two-choice.jsx +15 -19
- package/README.md +0 -12
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import { Panel } from '../settings/panel';
|
|
4
|
+
import { toggle, radio, dropdown, numberField, numberFields } from '../settings';
|
|
5
|
+
|
|
6
|
+
describe('Settings Panel', () => {
|
|
7
|
+
let onChange;
|
|
8
|
+
let configure;
|
|
9
|
+
let model;
|
|
10
|
+
let groups;
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
onChange = jest.fn();
|
|
14
|
+
configure = {
|
|
15
|
+
orientationLabel: 'Orientation',
|
|
16
|
+
settingsOrientation: true,
|
|
17
|
+
editChoiceLabel: false,
|
|
18
|
+
};
|
|
19
|
+
model = {
|
|
20
|
+
choiceAreaLayout: 'vertical',
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
groups = ({ configure }) => ({
|
|
24
|
+
'Group 1': {
|
|
25
|
+
choiceAreaLayout: configure.settingsOrientation && {
|
|
26
|
+
type: 'radio',
|
|
27
|
+
label: configure.orientationLabel,
|
|
28
|
+
choices: [
|
|
29
|
+
{ label: 'opt1', value: 'opt1' },
|
|
30
|
+
{ label: 'opt2', value: 'opt2' },
|
|
31
|
+
],
|
|
32
|
+
},
|
|
33
|
+
editChoiceLabel: { type: 'toggle', label: 'Edit choice label', isConfigProperty: true },
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const renderComponent = (extras = {}) => {
|
|
39
|
+
return render(
|
|
40
|
+
<Panel
|
|
41
|
+
model={model}
|
|
42
|
+
configuration={configure}
|
|
43
|
+
onChangeModel={onChange}
|
|
44
|
+
onChangeConfiguration={onChange}
|
|
45
|
+
groups={groups({ configure })}
|
|
46
|
+
{...extras}
|
|
47
|
+
/>,
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
describe('rendering', () => {
|
|
52
|
+
it('renders settings panel', () => {
|
|
53
|
+
renderComponent();
|
|
54
|
+
|
|
55
|
+
expect(screen.getByText('Group 1')).toBeInTheDocument();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('renders toggle settings', () => {
|
|
59
|
+
renderComponent();
|
|
60
|
+
|
|
61
|
+
// Toggle components render with Switch role in MUI v5
|
|
62
|
+
const toggles = screen.getAllByRole('switch');
|
|
63
|
+
expect(toggles.length).toBeGreaterThan(0);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('conditionally renders radio buttons based on configuration', () => {
|
|
67
|
+
renderComponent();
|
|
68
|
+
|
|
69
|
+
// Should render radio when settingsOrientation is true
|
|
70
|
+
expect(screen.getByText('Orientation')).toBeInTheDocument();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('does not render radio buttons when disabled in configuration', () => {
|
|
74
|
+
const disabledGroups = groups({
|
|
75
|
+
configure: {
|
|
76
|
+
...configure,
|
|
77
|
+
settingsOrientation: false,
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
render(
|
|
82
|
+
<Panel
|
|
83
|
+
model={model}
|
|
84
|
+
configuration={{ ...configure, settingsOrientation: false }}
|
|
85
|
+
onChangeModel={onChange}
|
|
86
|
+
onChangeConfiguration={onChange}
|
|
87
|
+
groups={disabledGroups}
|
|
88
|
+
/>,
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
// Should not render radio when settingsOrientation is false
|
|
92
|
+
expect(screen.queryByText('Orientation')).not.toBeInTheDocument();
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Utility function tests - these are simple unit tests that don't need RTL
|
|
98
|
+
describe('toggle', () => {
|
|
99
|
+
it('returns a toggle type object', () => {
|
|
100
|
+
const setting = toggle('Label');
|
|
101
|
+
|
|
102
|
+
expect(setting).toEqual({
|
|
103
|
+
label: 'Label',
|
|
104
|
+
type: 'toggle',
|
|
105
|
+
isConfigProperty: false,
|
|
106
|
+
disabled: false,
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
describe('radio', () => {
|
|
112
|
+
it('returns a radio type object', () => {
|
|
113
|
+
const setting = radio('Radio', ['one', 'two']);
|
|
114
|
+
|
|
115
|
+
expect(setting).toEqual({
|
|
116
|
+
label: 'Radio',
|
|
117
|
+
type: 'radio',
|
|
118
|
+
isConfigProperty: false,
|
|
119
|
+
choices: [
|
|
120
|
+
{
|
|
121
|
+
label: 'one',
|
|
122
|
+
value: 'one',
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
label: 'two',
|
|
126
|
+
value: 'two',
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe('dropdown', () => {
|
|
134
|
+
it('returns a dropdown type object', () => {
|
|
135
|
+
const setting = dropdown('Dropdown', ['one', 'two']);
|
|
136
|
+
|
|
137
|
+
expect(setting).toEqual({
|
|
138
|
+
label: 'Dropdown',
|
|
139
|
+
type: 'dropdown',
|
|
140
|
+
isConfigProperty: false,
|
|
141
|
+
choices: ['one', 'two'],
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
describe('numberField', () => {
|
|
147
|
+
it('returns a numberField type object', () => {
|
|
148
|
+
const setting = numberField('Number Field', { max: 12 }, true);
|
|
149
|
+
|
|
150
|
+
expect(setting).toEqual({
|
|
151
|
+
label: 'Number Field',
|
|
152
|
+
type: 'numberField',
|
|
153
|
+
isConfigProperty: true,
|
|
154
|
+
max: 12,
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
describe('numberFields', () => {
|
|
160
|
+
it('returns a numberFields type object', () => {
|
|
161
|
+
const setting = numberFields('Number Fields', {
|
|
162
|
+
one: {
|
|
163
|
+
label: 'One',
|
|
164
|
+
},
|
|
165
|
+
two: {
|
|
166
|
+
label: 'Two',
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
expect(setting).toEqual({
|
|
171
|
+
label: 'Number Fields',
|
|
172
|
+
type: 'numberFields',
|
|
173
|
+
fields: {
|
|
174
|
+
one: {
|
|
175
|
+
type: 'numberField',
|
|
176
|
+
label: 'One',
|
|
177
|
+
isConfigProperty: false,
|
|
178
|
+
},
|
|
179
|
+
two: {
|
|
180
|
+
type: 'numberField',
|
|
181
|
+
label: 'Two',
|
|
182
|
+
isConfigProperty: false,
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
});
|
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
import Toggle from '../settings/toggle';
|
|
5
|
+
import DisplaySize from '../settings/display-size';
|
|
6
|
+
import SettingsRadioLabel from '../settings/settings-radio-label';
|
|
7
|
+
import { Panel } from '../settings/panel';
|
|
8
|
+
|
|
9
|
+
describe('Settings Components', () => {
|
|
10
|
+
describe('Toggle', () => {
|
|
11
|
+
it('renders toggle with label', () => {
|
|
12
|
+
const toggle = jest.fn();
|
|
13
|
+
render(<Toggle label="Enable Feature" checked={false} toggle={toggle} />);
|
|
14
|
+
|
|
15
|
+
expect(screen.getByText('Enable Feature')).toBeInTheDocument();
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe('DisplaySize', () => {
|
|
20
|
+
const defaultProps = {
|
|
21
|
+
size: { width: 500, height: 400 },
|
|
22
|
+
label: 'Display Size',
|
|
23
|
+
onChange: jest.fn(),
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
jest.clearAllMocks();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('renders label and input fields', () => {
|
|
31
|
+
render(<DisplaySize {...defaultProps} />);
|
|
32
|
+
|
|
33
|
+
expect(screen.getByText('Display Size')).toBeInTheDocument();
|
|
34
|
+
expect(screen.getByLabelText('Width')).toBeInTheDocument();
|
|
35
|
+
expect(screen.getByLabelText('Height')).toBeInTheDocument();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('displays width and height values', () => {
|
|
39
|
+
render(<DisplaySize {...defaultProps} />);
|
|
40
|
+
|
|
41
|
+
const widthInput = screen.getByLabelText('Width');
|
|
42
|
+
const heightInput = screen.getByLabelText('Height');
|
|
43
|
+
|
|
44
|
+
expect(widthInput).toHaveValue(500);
|
|
45
|
+
expect(heightInput).toHaveValue(400);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('calls onChange when width changes', async () => {
|
|
49
|
+
const user = userEvent.setup();
|
|
50
|
+
const onChange = jest.fn();
|
|
51
|
+
render(<DisplaySize {...defaultProps} onChange={onChange} />);
|
|
52
|
+
|
|
53
|
+
const widthInput = screen.getByLabelText('Width');
|
|
54
|
+
await user.clear(widthInput);
|
|
55
|
+
await user.type(widthInput, '600');
|
|
56
|
+
fireEvent.blur(widthInput);
|
|
57
|
+
|
|
58
|
+
expect(onChange).toHaveBeenCalledWith(expect.objectContaining({ width: 600 }));
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('calls onChange when height changes', async () => {
|
|
62
|
+
const user = userEvent.setup();
|
|
63
|
+
const onChange = jest.fn();
|
|
64
|
+
render(<DisplaySize {...defaultProps} onChange={onChange} />);
|
|
65
|
+
|
|
66
|
+
const heightInput = screen.getByLabelText('Height');
|
|
67
|
+
await user.clear(heightInput);
|
|
68
|
+
await user.type(heightInput, '500');
|
|
69
|
+
fireEvent.blur(heightInput);
|
|
70
|
+
|
|
71
|
+
expect(onChange).toHaveBeenCalledWith(expect.objectContaining({ height: 500 }));
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('maintains other dimension when one changes', async () => {
|
|
75
|
+
const user = userEvent.setup();
|
|
76
|
+
const onChange = jest.fn();
|
|
77
|
+
render(<DisplaySize {...defaultProps} onChange={onChange} />);
|
|
78
|
+
|
|
79
|
+
const widthInput = screen.getByLabelText('Width');
|
|
80
|
+
await user.clear(widthInput);
|
|
81
|
+
await user.type(widthInput, '700');
|
|
82
|
+
fireEvent.blur(widthInput);
|
|
83
|
+
|
|
84
|
+
expect(onChange).toHaveBeenCalledWith({
|
|
85
|
+
width: 700,
|
|
86
|
+
height: 400,
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('enforces min value constraint', async () => {
|
|
91
|
+
const user = userEvent.setup();
|
|
92
|
+
const onChange = jest.fn();
|
|
93
|
+
render(<DisplaySize {...defaultProps} onChange={onChange} />);
|
|
94
|
+
|
|
95
|
+
const widthInput = screen.getByLabelText('Width');
|
|
96
|
+
await user.clear(widthInput);
|
|
97
|
+
await user.type(widthInput, '100');
|
|
98
|
+
fireEvent.blur(widthInput);
|
|
99
|
+
|
|
100
|
+
// Should clamp to minimum 150
|
|
101
|
+
expect(widthInput).toHaveValue(150);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('enforces max value constraint', async () => {
|
|
105
|
+
const user = userEvent.setup();
|
|
106
|
+
const onChange = jest.fn();
|
|
107
|
+
render(<DisplaySize {...defaultProps} onChange={onChange} />);
|
|
108
|
+
|
|
109
|
+
const widthInput = screen.getByLabelText('Width');
|
|
110
|
+
await user.clear(widthInput);
|
|
111
|
+
await user.type(widthInput, '2000');
|
|
112
|
+
fireEvent.blur(widthInput);
|
|
113
|
+
|
|
114
|
+
// Should clamp to maximum 1000
|
|
115
|
+
expect(widthInput).toHaveValue(1000);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('updates when size prop changes', () => {
|
|
119
|
+
const { rerender } = render(<DisplaySize {...defaultProps} />);
|
|
120
|
+
|
|
121
|
+
let widthInput = screen.getByLabelText('Width');
|
|
122
|
+
expect(widthInput).toHaveValue(500);
|
|
123
|
+
|
|
124
|
+
rerender(
|
|
125
|
+
<DisplaySize {...defaultProps} size={{ width: 800, height: 600 }} />
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
widthInput = screen.getByLabelText('Width');
|
|
129
|
+
expect(widthInput).toHaveValue(800);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('handles boundary values at minimum', async () => {
|
|
133
|
+
const user = userEvent.setup();
|
|
134
|
+
const onChange = jest.fn();
|
|
135
|
+
render(<DisplaySize {...defaultProps} onChange={onChange} />);
|
|
136
|
+
|
|
137
|
+
const widthInput = screen.getByLabelText('Width');
|
|
138
|
+
const heightInput = screen.getByLabelText('Height');
|
|
139
|
+
|
|
140
|
+
await user.clear(widthInput);
|
|
141
|
+
await user.type(widthInput, '150');
|
|
142
|
+
fireEvent.blur(widthInput);
|
|
143
|
+
|
|
144
|
+
await user.clear(heightInput);
|
|
145
|
+
await user.type(heightInput, '150');
|
|
146
|
+
fireEvent.blur(heightInput);
|
|
147
|
+
|
|
148
|
+
expect(widthInput).toHaveValue(150);
|
|
149
|
+
expect(heightInput).toHaveValue(150);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('handles boundary values at maximum', async () => {
|
|
153
|
+
const user = userEvent.setup();
|
|
154
|
+
const onChange = jest.fn();
|
|
155
|
+
render(<DisplaySize {...defaultProps} onChange={onChange} />);
|
|
156
|
+
|
|
157
|
+
const widthInput = screen.getByLabelText('Width');
|
|
158
|
+
const heightInput = screen.getByLabelText('Height');
|
|
159
|
+
|
|
160
|
+
await user.clear(widthInput);
|
|
161
|
+
await user.type(widthInput, '1000');
|
|
162
|
+
fireEvent.blur(widthInput);
|
|
163
|
+
|
|
164
|
+
await user.clear(heightInput);
|
|
165
|
+
await user.type(heightInput, '1000');
|
|
166
|
+
fireEvent.blur(heightInput);
|
|
167
|
+
|
|
168
|
+
expect(widthInput).toHaveValue(1000);
|
|
169
|
+
expect(heightInput).toHaveValue(1000);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('handles decimal input values', async () => {
|
|
173
|
+
const user = userEvent.setup();
|
|
174
|
+
const onChange = jest.fn();
|
|
175
|
+
render(<DisplaySize {...defaultProps} onChange={onChange} />);
|
|
176
|
+
|
|
177
|
+
const widthInput = screen.getByLabelText('Width');
|
|
178
|
+
await user.clear(widthInput);
|
|
179
|
+
await user.type(widthInput, '500.5');
|
|
180
|
+
fireEvent.blur(widthInput);
|
|
181
|
+
|
|
182
|
+
expect(widthInput).toBeInTheDocument();
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('handles empty input and resets to min', async () => {
|
|
186
|
+
const user = userEvent.setup();
|
|
187
|
+
const onChange = jest.fn();
|
|
188
|
+
render(<DisplaySize {...defaultProps} onChange={onChange} />);
|
|
189
|
+
|
|
190
|
+
const widthInput = screen.getByLabelText('Width');
|
|
191
|
+
await user.clear(widthInput);
|
|
192
|
+
fireEvent.blur(widthInput);
|
|
193
|
+
|
|
194
|
+
// Should reset to minimum value
|
|
195
|
+
expect(widthInput).toHaveValue(150);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it('handles rapid width and height changes', async () => {
|
|
199
|
+
const user = userEvent.setup();
|
|
200
|
+
const onChange = jest.fn();
|
|
201
|
+
render(<DisplaySize {...defaultProps} onChange={onChange} />);
|
|
202
|
+
|
|
203
|
+
const widthInput = screen.getByLabelText('Width');
|
|
204
|
+
|
|
205
|
+
await user.clear(widthInput);
|
|
206
|
+
await user.type(widthInput, '6');
|
|
207
|
+
await user.type(widthInput, '0');
|
|
208
|
+
await user.type(widthInput, '0');
|
|
209
|
+
fireEvent.blur(widthInput);
|
|
210
|
+
|
|
211
|
+
expect(onChange).toHaveBeenCalled();
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('clamps height at both boundaries', async () => {
|
|
215
|
+
const user = userEvent.setup();
|
|
216
|
+
const onChange = jest.fn();
|
|
217
|
+
render(<DisplaySize {...defaultProps} onChange={onChange} />);
|
|
218
|
+
|
|
219
|
+
const heightInput = screen.getByLabelText('Height');
|
|
220
|
+
|
|
221
|
+
// Test below min
|
|
222
|
+
await user.clear(heightInput);
|
|
223
|
+
await user.type(heightInput, '50');
|
|
224
|
+
fireEvent.blur(heightInput);
|
|
225
|
+
expect(heightInput).toHaveValue(150);
|
|
226
|
+
|
|
227
|
+
// Test above max
|
|
228
|
+
await user.clear(heightInput);
|
|
229
|
+
await user.type(heightInput, '2500');
|
|
230
|
+
fireEvent.blur(heightInput);
|
|
231
|
+
expect(heightInput).toHaveValue(1000);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('displays custom label text', () => {
|
|
235
|
+
render(
|
|
236
|
+
<DisplaySize
|
|
237
|
+
size={{ width: 500, height: 400 }}
|
|
238
|
+
label="Custom Size Settings"
|
|
239
|
+
onChange={jest.fn()}
|
|
240
|
+
/>
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
expect(screen.getByText('Custom Size Settings')).toBeInTheDocument();
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('renders with variant outlined', () => {
|
|
247
|
+
const { container } = render(<DisplaySize {...defaultProps} />);
|
|
248
|
+
|
|
249
|
+
const inputs = container.querySelectorAll('input');
|
|
250
|
+
expect(inputs.length).toBe(2);
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
describe('SettingsRadioLabel', () => {
|
|
255
|
+
it('renders radio label', () => {
|
|
256
|
+
const onChange = jest.fn();
|
|
257
|
+
render(
|
|
258
|
+
<SettingsRadioLabel
|
|
259
|
+
label="Option 1"
|
|
260
|
+
value="option1"
|
|
261
|
+
checked={false}
|
|
262
|
+
onChange={onChange}
|
|
263
|
+
/>
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
expect(screen.getByText('Option 1')).toBeInTheDocument();
|
|
267
|
+
expect(screen.getByRole('radio')).toBeInTheDocument();
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('renders checked state', () => {
|
|
271
|
+
const onChange = jest.fn();
|
|
272
|
+
render(
|
|
273
|
+
<SettingsRadioLabel
|
|
274
|
+
label="Option 1"
|
|
275
|
+
value="option1"
|
|
276
|
+
checked={true}
|
|
277
|
+
onChange={onChange}
|
|
278
|
+
/>
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
const radio = screen.getByRole('radio');
|
|
282
|
+
expect(radio).toBeChecked();
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it('renders unchecked state', () => {
|
|
286
|
+
const onChange = jest.fn();
|
|
287
|
+
render(
|
|
288
|
+
<SettingsRadioLabel
|
|
289
|
+
label="Option 1"
|
|
290
|
+
value="option1"
|
|
291
|
+
checked={false}
|
|
292
|
+
onChange={onChange}
|
|
293
|
+
/>
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
const radio = screen.getByRole('radio');
|
|
297
|
+
expect(radio).not.toBeChecked();
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
it('calls onChange when radio is clicked', async () => {
|
|
301
|
+
const user = userEvent.setup();
|
|
302
|
+
const onChange = jest.fn();
|
|
303
|
+
render(
|
|
304
|
+
<SettingsRadioLabel
|
|
305
|
+
label="Option 1"
|
|
306
|
+
value="option1"
|
|
307
|
+
checked={false}
|
|
308
|
+
onChange={onChange}
|
|
309
|
+
/>
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
const radio = screen.getByRole('radio');
|
|
313
|
+
await user.click(radio);
|
|
314
|
+
|
|
315
|
+
expect(onChange).toHaveBeenCalled();
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
it('displays label text correctly', () => {
|
|
319
|
+
const onChange = jest.fn();
|
|
320
|
+
const labelText = 'Custom Option Label';
|
|
321
|
+
render(
|
|
322
|
+
<SettingsRadioLabel
|
|
323
|
+
label={labelText}
|
|
324
|
+
value="custom"
|
|
325
|
+
checked={false}
|
|
326
|
+
onChange={onChange}
|
|
327
|
+
/>
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
expect(screen.getByText(labelText)).toBeInTheDocument();
|
|
331
|
+
});
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
describe('Panel', () => {
|
|
335
|
+
const defaultProps = {
|
|
336
|
+
model: { toggleSetting: true, numberValue: 5 },
|
|
337
|
+
configuration: { configValue: 'test' },
|
|
338
|
+
groups: {
|
|
339
|
+
'General Settings': {
|
|
340
|
+
toggleSetting: {
|
|
341
|
+
type: 'toggle',
|
|
342
|
+
label: 'Enable Feature',
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
},
|
|
346
|
+
onChangeModel: jest.fn(),
|
|
347
|
+
onChangeConfiguration: jest.fn(),
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
beforeEach(() => {
|
|
351
|
+
jest.clearAllMocks();
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it('renders panel with group label', () => {
|
|
355
|
+
render(<Panel {...defaultProps} />);
|
|
356
|
+
expect(screen.getByText('General Settings')).toBeInTheDocument();
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
it('renders empty when groups is undefined', () => {
|
|
360
|
+
const { container } = render(
|
|
361
|
+
<Panel
|
|
362
|
+
{...defaultProps}
|
|
363
|
+
groups={undefined}
|
|
364
|
+
/>
|
|
365
|
+
);
|
|
366
|
+
expect(container.firstChild.children.length).toBe(0);
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it('renders empty when groups is empty', () => {
|
|
370
|
+
const { container } = render(
|
|
371
|
+
<Panel
|
|
372
|
+
{...defaultProps}
|
|
373
|
+
groups={{}}
|
|
374
|
+
/>
|
|
375
|
+
);
|
|
376
|
+
expect(container.firstChild.children.length).toBe(0);
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
it('calls onChangeModel when model changes', async () => {
|
|
380
|
+
const user = userEvent.setup();
|
|
381
|
+
const onChangeModel = jest.fn();
|
|
382
|
+
const { container } = render(
|
|
383
|
+
<Panel
|
|
384
|
+
{...defaultProps}
|
|
385
|
+
onChangeModel={onChangeModel}
|
|
386
|
+
/>
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
const switchElement = container.querySelector('input[type="checkbox"]');
|
|
390
|
+
await user.click(switchElement);
|
|
391
|
+
|
|
392
|
+
expect(onChangeModel).toHaveBeenCalled();
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
it('calls onChangeConfiguration when configuration changes', async () => {
|
|
396
|
+
const user = userEvent.setup();
|
|
397
|
+
const onChangeConfiguration = jest.fn();
|
|
398
|
+
const { container } = render(
|
|
399
|
+
<Panel
|
|
400
|
+
{...defaultProps}
|
|
401
|
+
groups={{
|
|
402
|
+
'Config Group': {
|
|
403
|
+
configValue: {
|
|
404
|
+
type: 'toggle',
|
|
405
|
+
label: 'Config Toggle',
|
|
406
|
+
isConfigProperty: true,
|
|
407
|
+
},
|
|
408
|
+
},
|
|
409
|
+
}}
|
|
410
|
+
onChangeConfiguration={onChangeConfiguration}
|
|
411
|
+
/>
|
|
412
|
+
);
|
|
413
|
+
|
|
414
|
+
const switchElement = container.querySelector('input[type="checkbox"]');
|
|
415
|
+
await user.click(switchElement);
|
|
416
|
+
|
|
417
|
+
expect(onChangeConfiguration).toHaveBeenCalled();
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
it('renders with modal when provided', () => {
|
|
421
|
+
const modal = <div data-testid="test-modal">Modal Content</div>;
|
|
422
|
+
render(
|
|
423
|
+
<Panel
|
|
424
|
+
{...defaultProps}
|
|
425
|
+
modal={modal}
|
|
426
|
+
/>
|
|
427
|
+
);
|
|
428
|
+
|
|
429
|
+
expect(screen.getByTestId('test-modal')).toBeInTheDocument();
|
|
430
|
+
expect(screen.getByText('Modal Content')).toBeInTheDocument();
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
it('renders multiple groups', () => {
|
|
434
|
+
render(
|
|
435
|
+
<Panel
|
|
436
|
+
{...defaultProps}
|
|
437
|
+
groups={{
|
|
438
|
+
'Group One': {
|
|
439
|
+
setting1: {
|
|
440
|
+
type: 'toggle',
|
|
441
|
+
label: 'Setting One',
|
|
442
|
+
},
|
|
443
|
+
},
|
|
444
|
+
'Group Two': {
|
|
445
|
+
setting2: {
|
|
446
|
+
type: 'toggle',
|
|
447
|
+
label: 'Setting Two',
|
|
448
|
+
},
|
|
449
|
+
},
|
|
450
|
+
}}
|
|
451
|
+
model={{ setting1: true, setting2: false }}
|
|
452
|
+
/>
|
|
453
|
+
);
|
|
454
|
+
|
|
455
|
+
expect(screen.getByText('Group One')).toBeInTheDocument();
|
|
456
|
+
expect(screen.getByText('Group Two')).toBeInTheDocument();
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
it('uses default callbacks when not provided', () => {
|
|
460
|
+
const { container } = render(
|
|
461
|
+
<Panel
|
|
462
|
+
model={{ test: true }}
|
|
463
|
+
groups={{
|
|
464
|
+
'Test Group': {
|
|
465
|
+
test: { type: 'toggle', label: 'Test' },
|
|
466
|
+
},
|
|
467
|
+
}}
|
|
468
|
+
/>
|
|
469
|
+
);
|
|
470
|
+
|
|
471
|
+
expect(container.firstChild).toBeInTheDocument();
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
it('filters out empty groups', () => {
|
|
475
|
+
const { container } = render(
|
|
476
|
+
<Panel
|
|
477
|
+
{...defaultProps}
|
|
478
|
+
groups={{
|
|
479
|
+
'Empty Group': {},
|
|
480
|
+
'General Settings': {
|
|
481
|
+
toggleSetting: {
|
|
482
|
+
type: 'toggle',
|
|
483
|
+
label: 'Enable Feature',
|
|
484
|
+
},
|
|
485
|
+
},
|
|
486
|
+
}}
|
|
487
|
+
model={{ toggleSetting: true }}
|
|
488
|
+
/>
|
|
489
|
+
);
|
|
490
|
+
|
|
491
|
+
expect(screen.getByText('General Settings')).toBeInTheDocument();
|
|
492
|
+
expect(screen.queryByText('Empty Group')).not.toBeInTheDocument();
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
it('renders with null model', () => {
|
|
496
|
+
const { container } = render(
|
|
497
|
+
<Panel
|
|
498
|
+
model={null}
|
|
499
|
+
configuration={{}}
|
|
500
|
+
groups={{
|
|
501
|
+
'General Settings': {
|
|
502
|
+
toggleSetting: {
|
|
503
|
+
type: 'toggle',
|
|
504
|
+
label: 'Enable Feature',
|
|
505
|
+
},
|
|
506
|
+
},
|
|
507
|
+
}}
|
|
508
|
+
onChangeModel={jest.fn()}
|
|
509
|
+
/>
|
|
510
|
+
);
|
|
511
|
+
|
|
512
|
+
expect(screen.getByText('General Settings')).toBeInTheDocument();
|
|
513
|
+
});
|
|
514
|
+
});
|
|
515
|
+
});
|