@pie-lib/config-ui 12.0.0-beta.5 → 12.1.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/__tests__/alert-dialog.test.js +262 -0
- package/lib/__tests__/checkbox.test.js +227 -0
- package/lib/__tests__/choice-utils.test.js +14 -0
- package/lib/__tests__/form-section.test.js +252 -0
- package/lib/__tests__/help.test.js +270 -0
- package/lib/__tests__/input.test.js +268 -0
- package/lib/__tests__/langs.test.js +541 -0
- package/lib/__tests__/number-text-field-custom.test.js +362 -0
- package/lib/__tests__/number-text-field.test.js +421 -0
- package/lib/__tests__/radio-with-label.test.js +233 -0
- package/lib/__tests__/settings-panel.test.js +184 -0
- package/lib/__tests__/settings.test.js +653 -0
- package/lib/__tests__/tabs.test.js +211 -0
- package/lib/__tests__/two-choice.test.js +124 -0
- package/lib/__tests__/with-stateful-model.test.js +221 -0
- package/lib/alert-dialog.js +41 -11
- package/lib/alert-dialog.js.map +1 -1
- package/lib/checkbox.js +59 -49
- package/lib/checkbox.js.map +1 -1
- package/lib/choice-configuration/__tests__/feedback-menu.test.js +287 -0
- package/lib/choice-configuration/__tests__/index.test.js +253 -0
- package/lib/choice-configuration/feedback-menu.js +25 -27
- package/lib/choice-configuration/feedback-menu.js.map +1 -1
- package/lib/choice-configuration/index.js +183 -186
- package/lib/choice-configuration/index.js.map +1 -1
- package/lib/choice-utils.js +6 -8
- package/lib/choice-utils.js.map +1 -1
- package/lib/feedback-config/__tests__/feedback-config.test.js +201 -0
- package/lib/feedback-config/__tests__/feedback-selector.test.js +177 -0
- package/lib/feedback-config/feedback-selector.js +70 -74
- package/lib/feedback-config/feedback-selector.js.map +1 -1
- package/lib/feedback-config/group.js +23 -26
- package/lib/feedback-config/group.js.map +1 -1
- package/lib/feedback-config/index.js +42 -45
- package/lib/feedback-config/index.js.map +1 -1
- package/lib/form-section.js +32 -26
- package/lib/form-section.js.map +1 -1
- package/lib/help.js +38 -48
- package/lib/help.js.map +1 -1
- package/lib/index.js +2 -3
- package/lib/index.js.map +1 -1
- package/lib/input.js +13 -18
- package/lib/input.js.map +1 -1
- package/lib/inputs.js +59 -68
- package/lib/inputs.js.map +1 -1
- package/lib/langs.js +57 -71
- package/lib/langs.js.map +1 -1
- package/lib/layout/__tests__/config.layout.test.js +70 -0
- package/lib/layout/__tests__/layout-content.test.js +6 -0
- package/lib/layout/config-layout.js +79 -48
- package/lib/layout/config-layout.js.map +1 -1
- package/lib/layout/index.js +1 -1
- package/lib/layout/index.js.map +1 -1
- package/lib/layout/layout-contents.js +59 -61
- package/lib/layout/layout-contents.js.map +1 -1
- package/lib/layout/settings-box.js +26 -34
- package/lib/layout/settings-box.js.map +1 -1
- package/lib/mui-box/index.js +42 -51
- package/lib/mui-box/index.js.map +1 -1
- package/lib/number-text-field-custom.js +152 -90
- package/lib/number-text-field-custom.js.map +1 -1
- package/lib/number-text-field.js +75 -64
- package/lib/number-text-field.js.map +1 -1
- package/lib/radio-with-label.js +31 -17
- package/lib/radio-with-label.js.map +1 -1
- package/lib/settings/display-size.js +17 -21
- package/lib/settings/display-size.js.map +1 -1
- package/lib/settings/index.js +14 -20
- package/lib/settings/index.js.map +1 -1
- package/lib/settings/panel.js +141 -142
- package/lib/settings/panel.js.map +1 -1
- package/lib/settings/settings-radio-label.js +30 -17
- package/lib/settings/settings-radio-label.js.map +1 -1
- package/lib/settings/toggle.js +40 -26
- package/lib/settings/toggle.js.map +1 -1
- package/lib/tabs/index.js +19 -31
- package/lib/tabs/index.js.map +1 -1
- package/lib/tags-input/__tests__/index.test.js +183 -0
- package/lib/tags-input/index.js +50 -62
- package/lib/tags-input/index.js.map +1 -1
- package/lib/two-choice.js +34 -44
- package/lib/two-choice.js.map +1 -1
- package/lib/with-stateful-model.js +9 -13
- package/lib/with-stateful-model.js.map +1 -1
- package/package.json +14 -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 -199
- package/src/feedback-config/__tests__/feedback-config.test.jsx +141 -0
- package/src/feedback-config/__tests__/feedback-selector.test.jsx +97 -0
- package/src/feedback-config/feedback-selector.jsx +50 -55
- 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,176 @@
|
|
|
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-tip-tap';
|
|
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-tip-tap')['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
|
+
return (
|
|
42
|
+
<InputContainer label={label} className={className}>
|
|
43
|
+
<StyledEditorHolder>
|
|
44
|
+
<EditableHtml
|
|
45
|
+
markup={value || ''}
|
|
46
|
+
disabled={disabled}
|
|
47
|
+
spellCheck={spellCheck}
|
|
48
|
+
nonEmpty={nonEmpty}
|
|
49
|
+
onChange={onChange}
|
|
50
|
+
imageSupport={imageSupport}
|
|
51
|
+
disableImageAlignmentButtons={disableImageAlignmentButtons}
|
|
52
|
+
pluginProps={pluginOpts || {}}
|
|
53
|
+
toolbarOpts={toolbarOpts}
|
|
54
|
+
error={error}
|
|
55
|
+
maxImageWidth={maxImageWidth}
|
|
56
|
+
maxImageHeight={maxImageHeight}
|
|
57
|
+
uploadSoundSupport={uploadSoundSupport}
|
|
58
|
+
languageCharactersProps={[{ language: 'spanish' }, { language: 'special' }]}
|
|
59
|
+
mathMlOptions={mathMlOptions}
|
|
60
|
+
/>
|
|
61
|
+
</StyledEditorHolder>
|
|
62
|
+
</InputContainer>
|
|
63
|
+
);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const StyledFeedbackContainer = styled('div')(() => ({
|
|
67
|
+
position: 'relative',
|
|
68
|
+
}));
|
|
69
|
+
|
|
70
|
+
const StyledArrowIcon = styled(ArrowRight)(({ theme }) => ({
|
|
71
|
+
fill: theme.palette.grey[400],
|
|
72
|
+
left: -56,
|
|
73
|
+
position: 'absolute',
|
|
74
|
+
top: 40,
|
|
75
|
+
}));
|
|
76
|
+
|
|
77
|
+
const StyledTextField = styled(TextField)(({ theme }) => ({
|
|
78
|
+
width: '100%',
|
|
79
|
+
marginTop: theme.spacing(2),
|
|
80
|
+
}));
|
|
81
|
+
|
|
82
|
+
const StyledEditableHtmlContainer = styled(EditableHtmlContainer)(({ theme }) => ({
|
|
83
|
+
width: '100%',
|
|
84
|
+
marginTop: theme.spacing(2),
|
|
85
|
+
}));
|
|
86
|
+
|
|
87
|
+
const Feedback = ({ value, onChange, type, correct, defaults, toolbarOpts, mathMlOptions = {} }) => {
|
|
80
88
|
if (!type || type === 'none') {
|
|
81
89
|
return null;
|
|
82
90
|
} else if (type === 'default') {
|
|
83
91
|
return (
|
|
84
|
-
<
|
|
85
|
-
<
|
|
86
|
-
<
|
|
87
|
-
className={classes.text}
|
|
92
|
+
<StyledFeedbackContainer>
|
|
93
|
+
<StyledArrowIcon />
|
|
94
|
+
<StyledTextField
|
|
88
95
|
label="Feedback Text"
|
|
89
96
|
value={correct ? defaults.correct : defaults.incorrect}
|
|
97
|
+
variant="standard"
|
|
90
98
|
/>
|
|
91
|
-
</
|
|
99
|
+
</StyledFeedbackContainer>
|
|
92
100
|
);
|
|
93
101
|
} else {
|
|
94
102
|
return (
|
|
95
|
-
<
|
|
96
|
-
<
|
|
97
|
-
<
|
|
98
|
-
className={classes.text}
|
|
103
|
+
<StyledFeedbackContainer>
|
|
104
|
+
<StyledArrowIcon />
|
|
105
|
+
<StyledEditableHtmlContainer
|
|
99
106
|
label="Feedback Text"
|
|
100
107
|
value={value}
|
|
101
108
|
onChange={onChange}
|
|
102
109
|
toolbarOpts={toolbarOpts}
|
|
103
110
|
mathMlOptions={mathMlOptions}
|
|
104
111
|
/>
|
|
105
|
-
</
|
|
112
|
+
</StyledFeedbackContainer>
|
|
106
113
|
);
|
|
107
114
|
}
|
|
108
|
-
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const StyledIndex = styled('span')(({ theme }) => ({
|
|
118
|
+
paddingRight: theme.spacing(1),
|
|
119
|
+
paddingTop: theme.spacing(3),
|
|
120
|
+
}));
|
|
121
|
+
|
|
122
|
+
const StyledTopRow = styled('div')(() => ({
|
|
123
|
+
display: 'flex',
|
|
124
|
+
alignItems: 'center',
|
|
125
|
+
}));
|
|
126
|
+
|
|
127
|
+
const StyledToggle = styled('div')(({ theme }) => ({
|
|
128
|
+
flex: '0 1 auto',
|
|
129
|
+
paddingTop: theme.spacing(0.5),
|
|
130
|
+
paddingBottom: 0,
|
|
131
|
+
marginRight: 0,
|
|
132
|
+
marginLeft: theme.spacing(1),
|
|
133
|
+
}));
|
|
134
|
+
|
|
135
|
+
const StyledFeedback = styled('div')(({ theme }) => ({
|
|
136
|
+
flex: '0 1 auto',
|
|
137
|
+
paddingTop: theme.spacing(2),
|
|
138
|
+
paddingLeft: 0,
|
|
139
|
+
marginLeft: 0,
|
|
140
|
+
marginRight: theme.spacing(1),
|
|
141
|
+
}));
|
|
142
|
+
|
|
143
|
+
const StyledFeedbackIcon = styled('div')(() => ({
|
|
144
|
+
margin: 0,
|
|
145
|
+
width: 'inherit',
|
|
146
|
+
}));
|
|
147
|
+
|
|
148
|
+
const StyledDeleteIcon = styled('div')(() => ({
|
|
149
|
+
margin: 0,
|
|
150
|
+
width: 'inherit',
|
|
151
|
+
}));
|
|
152
|
+
|
|
153
|
+
const StyledDelete = styled('div')(({ theme }) => ({
|
|
154
|
+
flex: '0 1 auto',
|
|
155
|
+
paddingTop: theme.spacing(2),
|
|
156
|
+
paddingLeft: 0,
|
|
157
|
+
marginLeft: 0,
|
|
158
|
+
}));
|
|
159
|
+
|
|
160
|
+
const StyledMiddleColumn = styled('div')(({ theme }) => ({
|
|
161
|
+
display: 'flex',
|
|
162
|
+
flex: 1,
|
|
163
|
+
flexDirection: 'column',
|
|
164
|
+
marginRight: theme.spacing(1),
|
|
165
|
+
}));
|
|
166
|
+
|
|
167
|
+
const StyledErrorText = styled('div')(({ theme }) => ({
|
|
168
|
+
fontSize: theme.typography.fontSize - 2,
|
|
169
|
+
color: theme.palette.error.main,
|
|
170
|
+
}));
|
|
109
171
|
|
|
110
172
|
export class ChoiceConfiguration extends React.Component {
|
|
111
173
|
static propTypes = {
|
|
112
|
-
classes: PropTypes.object.isRequired,
|
|
113
174
|
noLabels: PropTypes.bool,
|
|
114
175
|
useLetterOrdering: PropTypes.bool,
|
|
115
176
|
className: PropTypes.string,
|
|
@@ -139,8 +200,11 @@ export class ChoiceConfiguration extends React.Component {
|
|
|
139
200
|
allowDelete: PropTypes.bool,
|
|
140
201
|
noCorrectAnswerError: PropTypes.string,
|
|
141
202
|
spellCheck: PropTypes.bool,
|
|
203
|
+
pluginOpts: PropTypes.object,
|
|
142
204
|
toolbarOpts: PropTypes.object,
|
|
143
205
|
uploadSoundSupport: PropTypes.object,
|
|
206
|
+
maxImageWidth: PropTypes.number,
|
|
207
|
+
maxImageHeight: PropTypes.number,
|
|
144
208
|
};
|
|
145
209
|
|
|
146
210
|
static defaultProps = {
|
|
@@ -196,7 +260,6 @@ export class ChoiceConfiguration extends React.Component {
|
|
|
196
260
|
render() {
|
|
197
261
|
const {
|
|
198
262
|
data,
|
|
199
|
-
classes,
|
|
200
263
|
mode,
|
|
201
264
|
onDelete,
|
|
202
265
|
defaultFeedback,
|
|
@@ -211,144 +274,90 @@ export class ChoiceConfiguration extends React.Component {
|
|
|
211
274
|
nonEmpty,
|
|
212
275
|
allowFeedBack,
|
|
213
276
|
allowDelete,
|
|
277
|
+
pluginOpts,
|
|
214
278
|
toolbarOpts,
|
|
215
279
|
error,
|
|
216
280
|
noCorrectAnswerError,
|
|
217
281
|
uploadSoundSupport,
|
|
282
|
+
maxImageWidth,
|
|
283
|
+
maxImageHeight,
|
|
218
284
|
mathMlOptions = {},
|
|
219
285
|
} = this.props;
|
|
220
286
|
|
|
221
287
|
const InputToggle = mode === 'checkbox' ? InputCheckbox : InputRadio;
|
|
222
|
-
const names = classNames(classes.choiceConfiguration, className);
|
|
223
288
|
|
|
224
289
|
return (
|
|
225
|
-
<
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
)}
|
|
290
|
+
<StyledTopRow>
|
|
291
|
+
{index > 0 && (
|
|
292
|
+
<StyledIndex type="title">
|
|
293
|
+
{useLetterOrdering ? String.fromCharCode(96 + index).toUpperCase() : index}
|
|
294
|
+
</StyledIndex>
|
|
295
|
+
)}
|
|
232
296
|
|
|
297
|
+
<StyledToggle>
|
|
233
298
|
<InputToggle
|
|
234
|
-
className={classes.toggle}
|
|
235
299
|
onChange={this.onCheckedChange}
|
|
236
300
|
label={!noLabels ? 'Correct' : ''}
|
|
237
301
|
checked={!!data.correct}
|
|
238
302
|
error={noCorrectAnswerError}
|
|
239
303
|
/>
|
|
304
|
+
</StyledToggle>
|
|
240
305
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
306
|
+
<StyledMiddleColumn>
|
|
307
|
+
<EditableHtmlContainer
|
|
308
|
+
label={!noLabels ? 'Label' : ''}
|
|
309
|
+
value={data.label}
|
|
310
|
+
onChange={this.onLabelChange}
|
|
311
|
+
imageSupport={imageSupport}
|
|
312
|
+
disableImageAlignmentButtons={disableImageAlignmentButtons}
|
|
313
|
+
disabled={disabled}
|
|
314
|
+
spellCheck={spellCheck}
|
|
315
|
+
nonEmpty={nonEmpty}
|
|
316
|
+
pluginOpts={pluginOpts}
|
|
317
|
+
toolbarOpts={toolbarOpts}
|
|
318
|
+
error={error}
|
|
319
|
+
uploadSoundSupport={uploadSoundSupport}
|
|
320
|
+
mathMlOptions={mathMlOptions}
|
|
321
|
+
maxImageWidth={maxImageWidth}
|
|
322
|
+
maxImageHeight={maxImageHeight}
|
|
323
|
+
/>
|
|
324
|
+
{error && <StyledErrorText>{error}</StyledErrorText>}
|
|
325
|
+
|
|
326
|
+
{allowFeedBack && (
|
|
327
|
+
<Feedback
|
|
328
|
+
{...data.feedback}
|
|
329
|
+
correct={data.correct}
|
|
330
|
+
defaults={defaultFeedback}
|
|
331
|
+
onChange={this.onFeedbackValueChange}
|
|
252
332
|
toolbarOpts={toolbarOpts}
|
|
253
|
-
error={error}
|
|
254
|
-
uploadSoundSupport={uploadSoundSupport}
|
|
255
|
-
mathMlOptions={mathMlOptions}
|
|
256
333
|
/>
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
{allowFeedBack && (
|
|
260
|
-
<Feedback
|
|
261
|
-
{...data.feedback}
|
|
262
|
-
correct={data.correct}
|
|
263
|
-
defaults={defaultFeedback}
|
|
264
|
-
onChange={this.onFeedbackValueChange}
|
|
265
|
-
toolbarOpts={toolbarOpts}
|
|
266
|
-
/>
|
|
267
|
-
)}
|
|
268
|
-
</div>
|
|
334
|
+
)}
|
|
335
|
+
</StyledMiddleColumn>
|
|
269
336
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
value={data.feedback}
|
|
275
|
-
|
|
276
|
-
icon: classes.feedbackIcon,
|
|
277
|
-
}}
|
|
278
|
-
/>
|
|
337
|
+
{allowFeedBack && (
|
|
338
|
+
<StyledFeedback>
|
|
339
|
+
<InputContainer label={!noLabels ? 'Feedback' : ''}>
|
|
340
|
+
<StyledFeedbackIcon>
|
|
341
|
+
<FeedbackMenu onChange={this.onFeedbackTypeChange} value={data.feedback} />
|
|
342
|
+
</StyledFeedbackIcon>
|
|
279
343
|
</InputContainer>
|
|
280
|
-
|
|
344
|
+
</StyledFeedback>
|
|
345
|
+
)}
|
|
281
346
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
347
|
+
{allowDelete && (
|
|
348
|
+
<StyledDelete>
|
|
349
|
+
<InputContainer label={!noLabels ? 'Delete' : ''}>
|
|
350
|
+
<StyledDeleteIcon>
|
|
351
|
+
<IconButton aria-label="delete" onClick={onDelete} size="large">
|
|
352
|
+
<ActionDelete />
|
|
353
|
+
</IconButton>
|
|
354
|
+
</StyledDeleteIcon>
|
|
287
355
|
</InputContainer>
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
</
|
|
356
|
+
</StyledDelete>
|
|
357
|
+
)}
|
|
358
|
+
</StyledTopRow>
|
|
291
359
|
);
|
|
292
360
|
}
|
|
293
361
|
}
|
|
294
362
|
|
|
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);
|
|
363
|
+
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
|
+
});
|