@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
|
@@ -1,115 +1,177 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
|
-
import {
|
|
4
|
-
import TextField from '@material
|
|
5
|
-
import
|
|
3
|
+
import { styled } from '@mui/material/styles';
|
|
4
|
+
import TextField from '@mui/material/TextField';
|
|
5
|
+
import ActionDelete from '@mui/icons-material/Delete';
|
|
6
|
+
import ArrowRight from '@mui/icons-material/SubdirectoryArrowRight';
|
|
7
|
+
import IconButton from '@mui/material/IconButton';
|
|
6
8
|
import { InputContainer } from '@pie-lib/render-ui';
|
|
7
|
-
import EditableHtml from '@pie-lib/editable-html';
|
|
9
|
+
// import EditableHtml from '@pie-lib/editable-html';
|
|
8
10
|
import { InputCheckbox, InputRadio } from '../inputs';
|
|
9
11
|
import FeedbackMenu from './feedback-menu';
|
|
10
|
-
import ActionDelete from '@material-ui/icons/Delete';
|
|
11
|
-
import ArrowRight from '@material-ui/icons/SubdirectoryArrowRight';
|
|
12
|
-
import IconButton from '@material-ui/core/IconButton';
|
|
13
|
-
|
|
14
|
-
const EditableHtmlContainer = withStyles((theme) => ({
|
|
15
|
-
labelContainer: {},
|
|
16
|
-
editorHolder: {
|
|
17
|
-
marginTop: theme.spacing.unit * 2,
|
|
18
|
-
},
|
|
19
|
-
}))(
|
|
20
|
-
({
|
|
21
|
-
label,
|
|
22
|
-
classes,
|
|
23
|
-
onChange,
|
|
24
|
-
value,
|
|
25
|
-
className,
|
|
26
|
-
imageSupport,
|
|
27
|
-
disableImageAlignmentButtons,
|
|
28
|
-
disabled,
|
|
29
|
-
spellCheck,
|
|
30
|
-
nonEmpty,
|
|
31
|
-
toolbarOpts,
|
|
32
|
-
error,
|
|
33
|
-
maxImageWidth,
|
|
34
|
-
maxImageHeight,
|
|
35
|
-
uploadSoundSupport,
|
|
36
|
-
mathMlOptions = {},
|
|
37
|
-
}) => {
|
|
38
|
-
const names = classNames(classes.labelContainer, className);
|
|
39
12
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
13
|
+
// - mathquill error window not defined
|
|
14
|
+
let EditableHtml;
|
|
15
|
+
if (typeof window !== 'undefined') {
|
|
16
|
+
EditableHtml = require('@pie-lib/editable-html')['default'];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const StyledEditorHolder = styled('div')(({ theme }) => ({
|
|
20
|
+
marginTop: theme.spacing(2),
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
const EditableHtmlContainer = ({
|
|
24
|
+
label,
|
|
25
|
+
onChange,
|
|
26
|
+
value,
|
|
27
|
+
className,
|
|
28
|
+
imageSupport,
|
|
29
|
+
disableImageAlignmentButtons,
|
|
30
|
+
disabled,
|
|
31
|
+
spellCheck,
|
|
32
|
+
nonEmpty,
|
|
33
|
+
pluginOpts,
|
|
34
|
+
toolbarOpts,
|
|
35
|
+
error,
|
|
36
|
+
maxImageWidth,
|
|
37
|
+
maxImageHeight,
|
|
38
|
+
uploadSoundSupport,
|
|
39
|
+
mathMlOptions = {},
|
|
40
|
+
}) => {
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<InputContainer label={label} className={className}>
|
|
44
|
+
<StyledEditorHolder>
|
|
45
|
+
<EditableHtml
|
|
46
|
+
markup={value || ''}
|
|
47
|
+
disabled={disabled}
|
|
48
|
+
spellCheck={spellCheck}
|
|
49
|
+
nonEmpty={nonEmpty}
|
|
50
|
+
onChange={onChange}
|
|
51
|
+
imageSupport={imageSupport}
|
|
52
|
+
disableImageAlignmentButtons={disableImageAlignmentButtons}
|
|
53
|
+
pluginProps={pluginOpts || {}}
|
|
54
|
+
toolbarOpts={toolbarOpts}
|
|
55
|
+
error={error}
|
|
56
|
+
maxImageWidth={maxImageWidth}
|
|
57
|
+
maxImageHeight={maxImageHeight}
|
|
58
|
+
uploadSoundSupport={uploadSoundSupport}
|
|
59
|
+
languageCharactersProps={[{ language: 'spanish' }, { language: 'special' }]}
|
|
60
|
+
mathMlOptions={mathMlOptions}
|
|
61
|
+
/>
|
|
62
|
+
</StyledEditorHolder>
|
|
63
|
+
</InputContainer>
|
|
64
|
+
);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const StyledFeedbackContainer = styled('div')(() => ({
|
|
68
|
+
position: 'relative',
|
|
69
|
+
}));
|
|
70
|
+
|
|
71
|
+
const StyledArrowIcon = styled(ArrowRight)(({ theme }) => ({
|
|
72
|
+
fill: theme.palette.grey[400],
|
|
73
|
+
left: -56,
|
|
74
|
+
position: 'absolute',
|
|
75
|
+
top: 40,
|
|
76
|
+
}));
|
|
77
|
+
|
|
78
|
+
const StyledTextField = styled(TextField)(({ theme }) => ({
|
|
79
|
+
width: '100%',
|
|
80
|
+
marginTop: theme.spacing(2),
|
|
81
|
+
}));
|
|
82
|
+
|
|
83
|
+
const StyledEditableHtmlContainer = styled(EditableHtmlContainer)(({ theme }) => ({
|
|
84
|
+
width: '100%',
|
|
85
|
+
marginTop: theme.spacing(2),
|
|
86
|
+
}));
|
|
87
|
+
|
|
88
|
+
const Feedback = ({ value, onChange, type, correct, defaults, toolbarOpts, mathMlOptions = {} }) => {
|
|
80
89
|
if (!type || type === 'none') {
|
|
81
90
|
return null;
|
|
82
91
|
} else if (type === 'default') {
|
|
83
92
|
return (
|
|
84
|
-
<
|
|
85
|
-
<
|
|
86
|
-
<
|
|
87
|
-
className={classes.text}
|
|
93
|
+
<StyledFeedbackContainer>
|
|
94
|
+
<StyledArrowIcon />
|
|
95
|
+
<StyledTextField
|
|
88
96
|
label="Feedback Text"
|
|
89
97
|
value={correct ? defaults.correct : defaults.incorrect}
|
|
98
|
+
variant="standard"
|
|
90
99
|
/>
|
|
91
|
-
</
|
|
100
|
+
</StyledFeedbackContainer>
|
|
92
101
|
);
|
|
93
102
|
} else {
|
|
94
103
|
return (
|
|
95
|
-
<
|
|
96
|
-
<
|
|
97
|
-
<
|
|
98
|
-
className={classes.text}
|
|
104
|
+
<StyledFeedbackContainer>
|
|
105
|
+
<StyledArrowIcon />
|
|
106
|
+
<StyledEditableHtmlContainer
|
|
99
107
|
label="Feedback Text"
|
|
100
108
|
value={value}
|
|
101
109
|
onChange={onChange}
|
|
102
110
|
toolbarOpts={toolbarOpts}
|
|
103
111
|
mathMlOptions={mathMlOptions}
|
|
104
112
|
/>
|
|
105
|
-
</
|
|
113
|
+
</StyledFeedbackContainer>
|
|
106
114
|
);
|
|
107
115
|
}
|
|
108
|
-
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const StyledIndex = styled('span')(({ theme }) => ({
|
|
119
|
+
paddingRight: theme.spacing(1),
|
|
120
|
+
paddingTop: theme.spacing(3),
|
|
121
|
+
}));
|
|
122
|
+
|
|
123
|
+
const StyledTopRow = styled('div')(() => ({
|
|
124
|
+
display: 'flex',
|
|
125
|
+
alignItems: 'center',
|
|
126
|
+
}));
|
|
127
|
+
|
|
128
|
+
const StyledToggle = styled('div')(({ theme }) => ({
|
|
129
|
+
flex: '0 1 auto',
|
|
130
|
+
paddingTop: theme.spacing(0.5),
|
|
131
|
+
paddingBottom: 0,
|
|
132
|
+
marginRight: 0,
|
|
133
|
+
marginLeft: theme.spacing(1),
|
|
134
|
+
}));
|
|
135
|
+
|
|
136
|
+
const StyledFeedback = styled('div')(({ theme }) => ({
|
|
137
|
+
flex: '0 1 auto',
|
|
138
|
+
paddingTop: theme.spacing(2),
|
|
139
|
+
paddingLeft: 0,
|
|
140
|
+
marginLeft: 0,
|
|
141
|
+
marginRight: theme.spacing(1),
|
|
142
|
+
}));
|
|
143
|
+
|
|
144
|
+
const StyledFeedbackIcon = styled('div')(() => ({
|
|
145
|
+
margin: 0,
|
|
146
|
+
width: 'inherit',
|
|
147
|
+
}));
|
|
148
|
+
|
|
149
|
+
const StyledDeleteIcon = styled('div')(() => ({
|
|
150
|
+
margin: 0,
|
|
151
|
+
width: 'inherit',
|
|
152
|
+
}));
|
|
153
|
+
|
|
154
|
+
const StyledDelete = styled('div')(({ theme }) => ({
|
|
155
|
+
flex: '0 1 auto',
|
|
156
|
+
paddingTop: theme.spacing(2),
|
|
157
|
+
paddingLeft: 0,
|
|
158
|
+
marginLeft: 0,
|
|
159
|
+
}));
|
|
160
|
+
|
|
161
|
+
const StyledMiddleColumn = styled('div')(({ theme }) => ({
|
|
162
|
+
display: 'flex',
|
|
163
|
+
flex: 1,
|
|
164
|
+
flexDirection: 'column',
|
|
165
|
+
marginRight: theme.spacing(1),
|
|
166
|
+
}));
|
|
167
|
+
|
|
168
|
+
const StyledErrorText = styled('div')(({ theme }) => ({
|
|
169
|
+
fontSize: theme.typography.fontSize - 2,
|
|
170
|
+
color: theme.palette.error.main,
|
|
171
|
+
}));
|
|
109
172
|
|
|
110
173
|
export class ChoiceConfiguration extends React.Component {
|
|
111
174
|
static propTypes = {
|
|
112
|
-
classes: PropTypes.object.isRequired,
|
|
113
175
|
noLabels: PropTypes.bool,
|
|
114
176
|
useLetterOrdering: PropTypes.bool,
|
|
115
177
|
className: PropTypes.string,
|
|
@@ -139,8 +201,11 @@ export class ChoiceConfiguration extends React.Component {
|
|
|
139
201
|
allowDelete: PropTypes.bool,
|
|
140
202
|
noCorrectAnswerError: PropTypes.string,
|
|
141
203
|
spellCheck: PropTypes.bool,
|
|
204
|
+
pluginOpts: PropTypes.object,
|
|
142
205
|
toolbarOpts: PropTypes.object,
|
|
143
206
|
uploadSoundSupport: PropTypes.object,
|
|
207
|
+
maxImageWidth: PropTypes.number,
|
|
208
|
+
maxImageHeight: PropTypes.number,
|
|
144
209
|
};
|
|
145
210
|
|
|
146
211
|
static defaultProps = {
|
|
@@ -196,7 +261,6 @@ export class ChoiceConfiguration extends React.Component {
|
|
|
196
261
|
render() {
|
|
197
262
|
const {
|
|
198
263
|
data,
|
|
199
|
-
classes,
|
|
200
264
|
mode,
|
|
201
265
|
onDelete,
|
|
202
266
|
defaultFeedback,
|
|
@@ -211,50 +275,54 @@ export class ChoiceConfiguration extends React.Component {
|
|
|
211
275
|
nonEmpty,
|
|
212
276
|
allowFeedBack,
|
|
213
277
|
allowDelete,
|
|
278
|
+
pluginOpts,
|
|
214
279
|
toolbarOpts,
|
|
215
280
|
error,
|
|
216
281
|
noCorrectAnswerError,
|
|
217
282
|
uploadSoundSupport,
|
|
283
|
+
maxImageWidth,
|
|
284
|
+
maxImageHeight,
|
|
218
285
|
mathMlOptions = {},
|
|
219
286
|
} = this.props;
|
|
220
287
|
|
|
221
288
|
const InputToggle = mode === 'checkbox' ? InputCheckbox : InputRadio;
|
|
222
|
-
const names = classNames(classes.choiceConfiguration, className);
|
|
223
289
|
|
|
224
290
|
return (
|
|
225
|
-
|
|
226
|
-
<div className={classes.topRow}>
|
|
291
|
+
<StyledTopRow>
|
|
227
292
|
{index > 0 && (
|
|
228
|
-
<
|
|
293
|
+
<StyledIndex type="title">
|
|
229
294
|
{useLetterOrdering ? String.fromCharCode(96 + index).toUpperCase() : index}
|
|
230
|
-
</
|
|
295
|
+
</StyledIndex>
|
|
231
296
|
)}
|
|
232
297
|
|
|
233
|
-
<
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
/>
|
|
240
|
-
|
|
241
|
-
<div className={classes.middleColumn}>
|
|
242
|
-
<EditableHtmlContainer
|
|
243
|
-
className={classes.input}
|
|
244
|
-
label={!noLabels ? 'Label' : ''}
|
|
245
|
-
value={data.label}
|
|
246
|
-
onChange={this.onLabelChange}
|
|
247
|
-
imageSupport={imageSupport}
|
|
248
|
-
disableImageAlignmentButtons={disableImageAlignmentButtons}
|
|
249
|
-
disabled={disabled}
|
|
250
|
-
spellCheck={spellCheck}
|
|
251
|
-
nonEmpty={nonEmpty}
|
|
252
|
-
toolbarOpts={toolbarOpts}
|
|
253
|
-
error={error}
|
|
254
|
-
uploadSoundSupport={uploadSoundSupport}
|
|
255
|
-
mathMlOptions={mathMlOptions}
|
|
298
|
+
<StyledToggle>
|
|
299
|
+
<InputToggle
|
|
300
|
+
onChange={this.onCheckedChange}
|
|
301
|
+
label={!noLabels ? 'Correct' : ''}
|
|
302
|
+
checked={!!data.correct}
|
|
303
|
+
error={noCorrectAnswerError}
|
|
256
304
|
/>
|
|
257
|
-
|
|
305
|
+
</StyledToggle>
|
|
306
|
+
|
|
307
|
+
<StyledMiddleColumn>
|
|
308
|
+
<EditableHtmlContainer
|
|
309
|
+
label={!noLabels ? 'Label' : ''}
|
|
310
|
+
value={data.label}
|
|
311
|
+
onChange={this.onLabelChange}
|
|
312
|
+
imageSupport={imageSupport}
|
|
313
|
+
disableImageAlignmentButtons={disableImageAlignmentButtons}
|
|
314
|
+
disabled={disabled}
|
|
315
|
+
spellCheck={spellCheck}
|
|
316
|
+
nonEmpty={nonEmpty}
|
|
317
|
+
pluginOpts={pluginOpts}
|
|
318
|
+
toolbarOpts={toolbarOpts}
|
|
319
|
+
error={error}
|
|
320
|
+
uploadSoundSupport={uploadSoundSupport}
|
|
321
|
+
mathMlOptions={mathMlOptions}
|
|
322
|
+
maxImageWidth={maxImageWidth}
|
|
323
|
+
maxImageHeight={maxImageHeight}
|
|
324
|
+
/>
|
|
325
|
+
{error && <StyledErrorText>{error}</StyledErrorText>}
|
|
258
326
|
|
|
259
327
|
{allowFeedBack && (
|
|
260
328
|
<Feedback
|
|
@@ -265,90 +333,38 @@ export class ChoiceConfiguration extends React.Component {
|
|
|
265
333
|
toolbarOpts={toolbarOpts}
|
|
266
334
|
/>
|
|
267
335
|
)}
|
|
268
|
-
</
|
|
336
|
+
</StyledMiddleColumn>
|
|
269
337
|
|
|
270
338
|
{allowFeedBack && (
|
|
271
|
-
<
|
|
272
|
-
<
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
339
|
+
<StyledFeedback>
|
|
340
|
+
<InputContainer label={!noLabels ? 'Feedback' : ''}>
|
|
341
|
+
<StyledFeedbackIcon>
|
|
342
|
+
<FeedbackMenu
|
|
343
|
+
onChange={this.onFeedbackTypeChange}
|
|
344
|
+
value={data.feedback}
|
|
345
|
+
/>
|
|
346
|
+
</StyledFeedbackIcon>
|
|
347
|
+
</InputContainer>
|
|
348
|
+
</StyledFeedback>
|
|
280
349
|
)}
|
|
281
350
|
|
|
282
351
|
{allowDelete && (
|
|
283
|
-
<
|
|
284
|
-
<
|
|
285
|
-
<
|
|
286
|
-
|
|
287
|
-
|
|
352
|
+
<StyledDelete>
|
|
353
|
+
<InputContainer label={!noLabels ? 'Delete' : ''}>
|
|
354
|
+
<StyledDeleteIcon>
|
|
355
|
+
<IconButton
|
|
356
|
+
aria-label="delete"
|
|
357
|
+
onClick={onDelete}
|
|
358
|
+
size="large">
|
|
359
|
+
<ActionDelete />
|
|
360
|
+
</IconButton>
|
|
361
|
+
</StyledDeleteIcon>
|
|
362
|
+
</InputContainer>
|
|
363
|
+
</StyledDelete>
|
|
288
364
|
)}
|
|
289
|
-
</
|
|
290
|
-
</div>
|
|
365
|
+
</StyledTopRow>
|
|
291
366
|
);
|
|
292
367
|
}
|
|
293
368
|
}
|
|
294
369
|
|
|
295
|
-
|
|
296
|
-
index: {
|
|
297
|
-
paddingRight: theme.spacing.unit,
|
|
298
|
-
paddingTop: theme.spacing.unit * 3.5,
|
|
299
|
-
},
|
|
300
|
-
choiceConfiguration: {},
|
|
301
|
-
topRow: {
|
|
302
|
-
display: 'flex',
|
|
303
|
-
},
|
|
304
|
-
value: {
|
|
305
|
-
flex: '0.5',
|
|
306
|
-
paddingRight: theme.spacing.unit,
|
|
307
|
-
},
|
|
308
|
-
editorHolder: {
|
|
309
|
-
marginTop: theme.spacing.unit * 2,
|
|
310
|
-
},
|
|
311
|
-
toggle: {
|
|
312
|
-
flex: '0 1 auto',
|
|
313
|
-
paddingTop: theme.spacing.unit / 2,
|
|
314
|
-
paddingBottom: 0,
|
|
315
|
-
marginRight: 0,
|
|
316
|
-
marginLeft: theme.spacing.unit,
|
|
317
|
-
},
|
|
318
|
-
feedback: {
|
|
319
|
-
flex: '0 1 auto',
|
|
320
|
-
paddingTop: theme.spacing.unit * 2,
|
|
321
|
-
paddingLeft: 0,
|
|
322
|
-
marginLeft: 0,
|
|
323
|
-
marginRight: theme.spacing.unit,
|
|
324
|
-
},
|
|
325
|
-
feedbackIcon: {
|
|
326
|
-
margin: 0,
|
|
327
|
-
width: 'inherit',
|
|
328
|
-
},
|
|
329
|
-
deleteIcon: {
|
|
330
|
-
margin: 0,
|
|
331
|
-
width: 'inherit',
|
|
332
|
-
},
|
|
333
|
-
delete: {
|
|
334
|
-
flex: '0 1 auto',
|
|
335
|
-
paddingTop: theme.spacing.unit * 2,
|
|
336
|
-
paddingLeft: 0,
|
|
337
|
-
marginLeft: 0,
|
|
338
|
-
},
|
|
339
|
-
middleColumn: {
|
|
340
|
-
display: 'flex',
|
|
341
|
-
flex: 1,
|
|
342
|
-
flexDirection: 'column',
|
|
343
|
-
marginRight: theme.spacing.unit,
|
|
344
|
-
},
|
|
345
|
-
input: {
|
|
346
|
-
marginRight: 0,
|
|
347
|
-
},
|
|
348
|
-
errorText: {
|
|
349
|
-
fontSize: theme.typography.fontSize - 2,
|
|
350
|
-
color: theme.palette.error.main,
|
|
351
|
-
},
|
|
352
|
-
});
|
|
353
|
-
|
|
354
|
-
export default withStyles(styles)(ChoiceConfiguration);
|
|
370
|
+
export default ChoiceConfiguration;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen, within } from '@testing-library/react';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
import { FeedbackConfig, buildDefaults } from '../index';
|
|
5
|
+
|
|
6
|
+
// Mock FeedbackSelector to simplify testing
|
|
7
|
+
jest.mock('../feedback-selector', () => {
|
|
8
|
+
return function FeedbackSelector({ label, feedback, onChange }) {
|
|
9
|
+
return (
|
|
10
|
+
<div data-testid="feedback-selector">
|
|
11
|
+
<label>{label}</label>
|
|
12
|
+
<select
|
|
13
|
+
aria-label={label}
|
|
14
|
+
value={feedback.type}
|
|
15
|
+
onChange={(e) => onChange({ ...feedback, type: e.target.value })}
|
|
16
|
+
>
|
|
17
|
+
<option value="default">Default</option>
|
|
18
|
+
<option value="custom">Custom</option>
|
|
19
|
+
<option value="none">None</option>
|
|
20
|
+
</select>
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe('FeedbackConfig', () => {
|
|
27
|
+
const onChange = jest.fn();
|
|
28
|
+
|
|
29
|
+
beforeEach(() => {
|
|
30
|
+
onChange.mockClear();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe('rendering', () => {
|
|
34
|
+
it('renders with default feedback types', () => {
|
|
35
|
+
const feedback = buildDefaults();
|
|
36
|
+
|
|
37
|
+
render(<FeedbackConfig feedback={feedback} onChange={onChange} />);
|
|
38
|
+
|
|
39
|
+
expect(screen.getByText('Feedback')).toBeInTheDocument();
|
|
40
|
+
expect(screen.getByText('If correct, show')).toBeInTheDocument();
|
|
41
|
+
expect(screen.getByText('If partially correct, show')).toBeInTheDocument();
|
|
42
|
+
expect(screen.getByText('If incorrect, show')).toBeInTheDocument();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('renders all three feedback selectors by default', () => {
|
|
46
|
+
const feedback = buildDefaults();
|
|
47
|
+
|
|
48
|
+
render(<FeedbackConfig feedback={feedback} onChange={onChange} />);
|
|
49
|
+
|
|
50
|
+
const selectors = screen.getAllByTestId('feedback-selector');
|
|
51
|
+
expect(selectors).toHaveLength(3);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('does not render partial feedback selector when allowPartial is false', () => {
|
|
55
|
+
const feedback = buildDefaults();
|
|
56
|
+
|
|
57
|
+
render(<FeedbackConfig allowPartial={false} feedback={feedback} onChange={onChange} />);
|
|
58
|
+
|
|
59
|
+
expect(screen.getByText('If correct, show')).toBeInTheDocument();
|
|
60
|
+
expect(screen.queryByText('If partially correct, show')).not.toBeInTheDocument();
|
|
61
|
+
expect(screen.getByText('If incorrect, show')).toBeInTheDocument();
|
|
62
|
+
|
|
63
|
+
const selectors = screen.getAllByTestId('feedback-selector');
|
|
64
|
+
expect(selectors).toHaveLength(2);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
describe('user interactions', () => {
|
|
69
|
+
it('calls onChange when correct feedback type changes', async () => {
|
|
70
|
+
const user = userEvent.setup();
|
|
71
|
+
const feedback = buildDefaults();
|
|
72
|
+
|
|
73
|
+
render(<FeedbackConfig feedback={feedback} onChange={onChange} />);
|
|
74
|
+
|
|
75
|
+
const correctSelect = screen.getByLabelText('If correct, show');
|
|
76
|
+
await user.selectOptions(correctSelect, 'custom');
|
|
77
|
+
|
|
78
|
+
expect(onChange).toHaveBeenCalledWith(
|
|
79
|
+
expect.objectContaining({
|
|
80
|
+
correct: expect.objectContaining({ type: 'custom' }),
|
|
81
|
+
}),
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('calls onChange when incorrect feedback type changes', async () => {
|
|
86
|
+
const user = userEvent.setup();
|
|
87
|
+
const feedback = buildDefaults();
|
|
88
|
+
|
|
89
|
+
render(<FeedbackConfig feedback={feedback} onChange={onChange} />);
|
|
90
|
+
|
|
91
|
+
const incorrectSelect = screen.getByLabelText('If incorrect, show');
|
|
92
|
+
await user.selectOptions(incorrectSelect, 'none');
|
|
93
|
+
|
|
94
|
+
expect(onChange).toHaveBeenCalledWith(
|
|
95
|
+
expect.objectContaining({
|
|
96
|
+
incorrect: expect.objectContaining({ type: 'none' }),
|
|
97
|
+
}),
|
|
98
|
+
);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('calls onChange when partial feedback type changes', async () => {
|
|
102
|
+
const user = userEvent.setup();
|
|
103
|
+
const feedback = buildDefaults();
|
|
104
|
+
|
|
105
|
+
render(<FeedbackConfig feedback={feedback} onChange={onChange} />);
|
|
106
|
+
|
|
107
|
+
const partialSelect = screen.getByLabelText('If partially correct, show');
|
|
108
|
+
await user.selectOptions(partialSelect, 'custom');
|
|
109
|
+
|
|
110
|
+
expect(onChange).toHaveBeenCalledWith(
|
|
111
|
+
expect.objectContaining({
|
|
112
|
+
partial: expect.objectContaining({ type: 'custom' }),
|
|
113
|
+
}),
|
|
114
|
+
);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe('buildDefaults helper', () => {
|
|
119
|
+
it('returns default feedback configuration', () => {
|
|
120
|
+
const defaults = buildDefaults();
|
|
121
|
+
|
|
122
|
+
expect(defaults).toEqual({
|
|
123
|
+
correct: { type: 'default', default: 'Correct' },
|
|
124
|
+
incorrect: { type: 'default', default: 'Incorrect' },
|
|
125
|
+
partial: { type: 'default', default: 'Nearly' },
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('merges custom values with defaults', () => {
|
|
130
|
+
const defaults = buildDefaults({
|
|
131
|
+
correct: { type: 'custom', custom: 'Great job!' },
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
expect(defaults).toEqual({
|
|
135
|
+
correct: { type: 'custom', default: 'Correct', custom: 'Great job!' },
|
|
136
|
+
incorrect: { type: 'default', default: 'Incorrect' },
|
|
137
|
+
partial: { type: 'default', default: 'Nearly' },
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
});
|