@pie-lib/mask-markup 1.13.47-next.1 → 1.13.47-next.1639
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 +1 -871
- package/CHANGELOG.md +170 -17
- package/NEXT.CHANGELOG.json +1 -0
- package/lib/choices/choice.js +80 -17
- package/lib/choices/choice.js.map +1 -1
- package/lib/choices/index.js +11 -3
- package/lib/choices/index.js.map +1 -1
- package/lib/components/blank.js +146 -34
- package/lib/components/blank.js.map +1 -1
- package/lib/components/correct-input.js +8 -3
- package/lib/components/correct-input.js.map +1 -1
- package/lib/components/dropdown.js +340 -58
- package/lib/components/dropdown.js.map +1 -1
- package/lib/constructed-response.js +87 -23
- package/lib/constructed-response.js.map +1 -1
- package/lib/customizable.js +48 -0
- package/lib/customizable.js.map +1 -0
- package/lib/drag-in-the-blank.js +34 -8
- package/lib/drag-in-the-blank.js.map +1 -1
- package/lib/index.js +8 -0
- package/lib/index.js.map +1 -1
- package/lib/inline-dropdown.js +3 -1
- package/lib/inline-dropdown.js.map +1 -1
- package/lib/mask.js +45 -6
- package/lib/mask.js.map +1 -1
- package/lib/with-mask.js +34 -2
- package/lib/with-mask.js.map +1 -1
- package/package.json +10 -6
- package/src/__tests__/__snapshots__/drag-in-the-blank.test.js.snap +316 -0
- package/src/__tests__/__snapshots__/mask.test.js.snap +55 -0
- package/src/__tests__/__snapshots__/with-mask.test.js.snap +62 -0
- package/src/__tests__/drag-in-the-blank.test.js +71 -0
- package/src/__tests__/index.test.js +39 -0
- package/src/__tests__/mask.test.js +152 -0
- package/src/__tests__/serialization.test.js +54 -0
- package/src/__tests__/utils.js +1 -0
- package/src/__tests__/with-mask.test.js +51 -0
- package/src/choices/__tests__/__snapshots__/index.test.js.snap +209 -0
- package/src/choices/__tests__/index.test.js +62 -0
- package/src/choices/choice.jsx +60 -6
- package/src/choices/index.jsx +2 -2
- package/src/components/__tests__/__snapshots__/blank.test.js.snap +111 -0
- package/src/components/__tests__/__snapshots__/correct-input.test.js.snap +64 -0
- package/src/components/__tests__/__snapshots__/dropdown.test.js.snap +136 -0
- package/src/components/__tests__/__snapshots__/input.test.js.snap +34 -0
- package/src/components/__tests__/blank.test.js +202 -0
- package/src/components/__tests__/correct-input.test.js +49 -0
- package/src/components/__tests__/dropdown.test.js +51 -0
- package/src/components/__tests__/input.test.js +50 -0
- package/src/components/blank.jsx +139 -28
- package/src/components/correct-input.jsx +6 -1
- package/src/components/dropdown.jsx +313 -79
- package/src/constructed-response.jsx +76 -18
- package/src/customizable.jsx +35 -0
- package/src/drag-in-the-blank.jsx +26 -3
- package/src/index.js +10 -1
- package/src/inline-dropdown.jsx +2 -0
- package/src/mask.jsx +30 -5
- package/src/with-mask.jsx +39 -2
- package/README.md +0 -14
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
|
-
import
|
|
3
|
+
import Button from '@material-ui/core/Button';
|
|
4
|
+
import InputLabel from '@material-ui/core/InputLabel';
|
|
5
|
+
import Menu from '@material-ui/core/Menu';
|
|
4
6
|
import MenuItem from '@material-ui/core/MenuItem';
|
|
5
|
-
import
|
|
7
|
+
import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown';
|
|
8
|
+
import ArrowDropUpIcon from '@material-ui/icons/ArrowDropUp';
|
|
9
|
+
import Close from '@material-ui/icons/Close';
|
|
10
|
+
import Check from '@material-ui/icons/Check';
|
|
6
11
|
import { withStyles } from '@material-ui/core/styles';
|
|
12
|
+
import classNames from 'classnames';
|
|
13
|
+
|
|
7
14
|
import { color } from '@pie-lib/render-ui';
|
|
15
|
+
import { renderMath } from '@pie-lib/math-rendering';
|
|
8
16
|
|
|
9
17
|
class Dropdown extends React.Component {
|
|
10
18
|
static propTypes = {
|
|
@@ -16,79 +24,253 @@ class Dropdown extends React.Component {
|
|
|
16
24
|
correct: PropTypes.bool,
|
|
17
25
|
choices: PropTypes.arrayOf(PropTypes.shape({ value: PropTypes.string, label: PropTypes.string })),
|
|
18
26
|
showCorrectAnswer: PropTypes.bool,
|
|
27
|
+
singleQuery: PropTypes.bool,
|
|
28
|
+
correctValue: PropTypes.string,
|
|
19
29
|
};
|
|
20
30
|
|
|
21
31
|
constructor(props) {
|
|
22
32
|
super(props);
|
|
23
33
|
|
|
24
34
|
this.state = {
|
|
25
|
-
|
|
26
|
-
|
|
35
|
+
anchorEl: null,
|
|
36
|
+
highlightedOptionId: null,
|
|
37
|
+
menuWidth: null,
|
|
38
|
+
previewValue: null,
|
|
27
39
|
};
|
|
40
|
+
this.hiddenRef = React.createRef();
|
|
41
|
+
this.buttonRef = React.createRef();
|
|
42
|
+
this.previewRef = React.createRef();
|
|
43
|
+
this.elementRefs = [];
|
|
28
44
|
}
|
|
29
45
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
46
|
+
componentDidMount() {
|
|
47
|
+
// measure hidden menu width once
|
|
48
|
+
if (this.hiddenRef.current && this.state.menuWidth === null) {
|
|
49
|
+
this.setState({ menuWidth: this.hiddenRef.current.clientWidth });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
componentDidUpdate(prevProps, prevState) {
|
|
54
|
+
const hiddenEl = this.hiddenRef.current;
|
|
55
|
+
|
|
56
|
+
const dropdownJustOpened = !prevState.anchorEl && this.state.anchorEl;
|
|
57
|
+
if (dropdownJustOpened) {
|
|
58
|
+
this.elementRefs.forEach((ref) => {
|
|
59
|
+
if (!ref) return;
|
|
60
|
+
|
|
61
|
+
const containsLatex = ref.querySelector('[data-latex], [data-raw]');
|
|
62
|
+
const hasMathJax = ref.querySelector('mjx-container');
|
|
63
|
+
const mathHandled = ref.querySelector('[data-math-handled="true"]');
|
|
64
|
+
|
|
65
|
+
if (containsLatex && (!mathHandled || !hasMathJax)) {
|
|
66
|
+
renderMath(ref);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (hiddenEl) {
|
|
72
|
+
const newWidth = hiddenEl.clientWidth;
|
|
73
|
+
if (newWidth !== this.state.menuWidth) {
|
|
74
|
+
this.elementRefs.forEach((ref) => {
|
|
75
|
+
if (ref) renderMath(ref);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
renderMath(hiddenEl);
|
|
79
|
+
this.setState({ menuWidth: newWidth });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
handleClick = (event) => this.setState({ anchorEl: event.currentTarget });
|
|
85
|
+
|
|
86
|
+
handleClose = () => {
|
|
87
|
+
const { value } = this.props;
|
|
88
|
+
this.setState({ anchorEl: null, previewValue: null, highlightedOptionId: null });
|
|
89
|
+
// clear displayed preview if no selection
|
|
90
|
+
if (!value && this.previewRef.current) {
|
|
91
|
+
this.previewRef.current.innerHTML = '';
|
|
92
|
+
}
|
|
35
93
|
};
|
|
36
94
|
|
|
37
|
-
|
|
38
|
-
this.
|
|
39
|
-
|
|
40
|
-
|
|
95
|
+
handleHighlight = (index) => {
|
|
96
|
+
const highlightedOptionId = `dropdown-option-${this.props.id}-${index}`;
|
|
97
|
+
|
|
98
|
+
// preview on hover if nothing selected
|
|
99
|
+
const stateUpdate = { highlightedOptionId };
|
|
100
|
+
if (!this.props.value) {
|
|
101
|
+
stateUpdate.previewValue = this.props.choices[index].value;
|
|
102
|
+
}
|
|
103
|
+
this.setState(stateUpdate);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
handleSelect = (value, index) => {
|
|
107
|
+
this.props.onChange(this.props.id, value);
|
|
108
|
+
this.handleHighlight(index);
|
|
109
|
+
this.handleClose();
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
handleHover = (index) => {
|
|
113
|
+
const selectedValue = this.props.value;
|
|
114
|
+
|
|
115
|
+
if (selectedValue) return;
|
|
116
|
+
|
|
117
|
+
const highlightedOptionId = `dropdown-option-${this.props.id}-${index}`;
|
|
118
|
+
const previewValue = this.state.previewValue;
|
|
119
|
+
|
|
120
|
+
this.setState({ highlightedOptionId, previewValue }, () => {
|
|
121
|
+
// On hover, preview the math-rendered content inside the button if no value is selected.
|
|
122
|
+
const ref = this.elementRefs[index];
|
|
123
|
+
const preview = this.previewRef.current;
|
|
124
|
+
|
|
125
|
+
if (ref && preview) {
|
|
126
|
+
preview.innerHTML = ref.innerHTML;
|
|
127
|
+
}
|
|
41
128
|
});
|
|
42
129
|
};
|
|
43
130
|
|
|
131
|
+
getLabel(choices, value) {
|
|
132
|
+
const found = (choices || []).find((choice) => choice.value === value);
|
|
133
|
+
|
|
134
|
+
return found ? found.label.trim() : undefined;
|
|
135
|
+
}
|
|
136
|
+
|
|
44
137
|
render() {
|
|
45
|
-
const { classes, id, correct, disabled, value,
|
|
138
|
+
const { classes, id, correct, disabled, value, choices, showCorrectAnswer, singleQuery, correctValue } = this.props;
|
|
139
|
+
const { anchorEl } = this.state;
|
|
140
|
+
const open = Boolean(anchorEl);
|
|
141
|
+
const buttonId = `dropdown-button-${id}`;
|
|
142
|
+
const menuId = `dropdown-menu-${id}`;
|
|
143
|
+
const valueDisplayId = `dropdown-value-${id}`;
|
|
144
|
+
|
|
145
|
+
// Determine the class for disabled state, view mode and evaluate mode
|
|
146
|
+
let disabledClass;
|
|
147
|
+
// Reset elementRefs before each render to avoid stale references
|
|
148
|
+
this.elementRefs = [];
|
|
46
149
|
|
|
47
|
-
|
|
150
|
+
if (disabled && correct !== undefined) {
|
|
151
|
+
disabledClass = correct || showCorrectAnswer ? classes.disabledCorrect : classes.disabledIncorrect;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Create distinct, visually hidden labels for each dropdown
|
|
155
|
+
const incrementedId = parseInt(id, 10) + 1;
|
|
156
|
+
const labelId = singleQuery ? 'Query-label' : `Query-label-${incrementedId}`;
|
|
157
|
+
const labelText = singleQuery ? 'Query' : `Query ${incrementedId}`;
|
|
158
|
+
|
|
159
|
+
// Changed from Select to Button for dropdown to enhance accessibility. This modification offers explicit control over aria attributes and focuses management, ensuring the dropdown is compliant with accessibility standards. The use of Button and Menu components allows for better handling of keyboard interactions and provides accessible labels and menus, aligning with WCAG guidelines and improving usability for assistive technology users.
|
|
160
|
+
let correctnessIcon = null;
|
|
161
|
+
if (disabled && correct !== undefined) {
|
|
162
|
+
correctnessIcon =
|
|
163
|
+
correct || showCorrectAnswer ? (
|
|
164
|
+
<Check className={classNames(classes.correctnessIndicatorIcon, classes.correctIcon)} />
|
|
165
|
+
) : (
|
|
166
|
+
<Close className={classNames(classes.correctnessIndicatorIcon, classes.incorrectIcon)} />
|
|
167
|
+
);
|
|
168
|
+
}
|
|
48
169
|
|
|
49
170
|
return (
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
171
|
+
<>
|
|
172
|
+
<div
|
|
173
|
+
ref={this.hiddenRef}
|
|
174
|
+
style={{ position: 'absolute', visibility: 'hidden', top: 0, left: 0 }}
|
|
175
|
+
tabIndex={-1}
|
|
176
|
+
aria-hidden="true"
|
|
177
|
+
>
|
|
178
|
+
{(choices || []).map((c, index) => (
|
|
179
|
+
<MenuItem
|
|
180
|
+
key={index}
|
|
181
|
+
classes={{ root: classes.menuRoot, selected: classes.selected }}
|
|
182
|
+
tabIndex={-1}
|
|
183
|
+
aria-hidden="true"
|
|
184
|
+
>
|
|
185
|
+
<span className={classes.label} dangerouslySetInnerHTML={{ __html: c.label }} />
|
|
186
|
+
</MenuItem>
|
|
187
|
+
))}
|
|
188
|
+
</div>
|
|
189
|
+
<InputLabel className={classes.srOnly} id={labelId} tabIndex={-1} aria-hidden="true">
|
|
190
|
+
{labelText}
|
|
191
|
+
</InputLabel>
|
|
192
|
+
<Button
|
|
193
|
+
ref={this.buttonRef}
|
|
194
|
+
style={{
|
|
195
|
+
...(this.state.menuWidth && { minWidth: `calc(${this.state.menuWidth}px + 8px)` }),
|
|
196
|
+
borderWidth: open ? '2px' : '1px',
|
|
197
|
+
transition: 'border-width 0.2s ease-in-out',
|
|
198
|
+
}}
|
|
199
|
+
aria-controls={open ? menuId : undefined}
|
|
200
|
+
aria-haspopup="listbox"
|
|
201
|
+
aria-expanded={open ? 'true' : undefined}
|
|
202
|
+
aria-activedescendant={this.state.highlightedOptionId}
|
|
203
|
+
onClick={this.handleClick}
|
|
204
|
+
classes={{
|
|
205
|
+
root: classes.root,
|
|
206
|
+
disabled: disabledClass,
|
|
207
|
+
}}
|
|
208
|
+
disabled={disabled}
|
|
209
|
+
id={buttonId}
|
|
210
|
+
role="combobox"
|
|
211
|
+
aria-label={`Select an option for ${labelText}`}
|
|
212
|
+
aria-labelledby={valueDisplayId}
|
|
213
|
+
>
|
|
214
|
+
{correctnessIcon}
|
|
215
|
+
<span
|
|
216
|
+
id={valueDisplayId}
|
|
217
|
+
ref={this.previewRef}
|
|
218
|
+
className={classes.label}
|
|
219
|
+
dangerouslySetInnerHTML={{
|
|
220
|
+
__html: correctValue
|
|
221
|
+
? correctValue
|
|
222
|
+
: open && this.state.previewValue
|
|
223
|
+
? this.getLabel(choices, this.state.previewValue)
|
|
224
|
+
: this.getLabel(choices, value) || '',
|
|
225
|
+
}}
|
|
226
|
+
/>
|
|
227
|
+
{open ? <ArrowDropUpIcon /> : <ArrowDropDownIcon />}
|
|
228
|
+
</Button>
|
|
229
|
+
<Menu
|
|
230
|
+
id={menuId}
|
|
231
|
+
anchorEl={anchorEl}
|
|
232
|
+
className={classes.selectMenu}
|
|
233
|
+
keepMounted
|
|
234
|
+
open={open}
|
|
235
|
+
onClose={this.handleClose}
|
|
236
|
+
getContentAnchorEl={null}
|
|
237
|
+
anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
|
|
238
|
+
transformOrigin={{ vertical: 'top', horizontal: 'left' }}
|
|
239
|
+
PaperProps={this.state.menuWidth ? { style: { minWidth: this.state.menuWidth, padding: '4px' } } : undefined}
|
|
240
|
+
MenuListProps={{
|
|
241
|
+
'aria-labelledby': buttonId,
|
|
242
|
+
role: 'listbox',
|
|
243
|
+
disablePadding: true,
|
|
244
|
+
}}
|
|
245
|
+
>
|
|
246
|
+
{(choices || []).map((c, index) => {
|
|
247
|
+
const optionId = `dropdown-option-${id}-${index}`;
|
|
248
|
+
|
|
249
|
+
return (
|
|
250
|
+
<MenuItem
|
|
251
|
+
id={optionId}
|
|
252
|
+
classes={{ root: classes.menuRoot, selected: classes.selected }}
|
|
253
|
+
key={`${c.label}-${index}`}
|
|
254
|
+
value={c.value}
|
|
255
|
+
onClick={() => this.handleSelect(c.value, index)}
|
|
256
|
+
role="option"
|
|
257
|
+
aria-selected={this.state.highlightedOptionId === optionId ? 'true' : undefined}
|
|
258
|
+
onMouseOver={() => this.handleHover(index)}
|
|
259
|
+
>
|
|
260
|
+
<span
|
|
261
|
+
ref={(ref) => (this.elementRefs[index] = ref)}
|
|
262
|
+
className={classes.label}
|
|
263
|
+
dangerouslySetInnerHTML={{ __html: c.label }}
|
|
264
|
+
/>
|
|
265
|
+
<span
|
|
266
|
+
className={classes.selectedIndicator}
|
|
267
|
+
dangerouslySetInnerHTML={{ __html: c.value === value ? ' ✓' : '' }}
|
|
268
|
+
/>
|
|
269
|
+
</MenuItem>
|
|
270
|
+
);
|
|
271
|
+
})}
|
|
272
|
+
</Menu>
|
|
273
|
+
</>
|
|
92
274
|
);
|
|
93
275
|
}
|
|
94
276
|
}
|
|
@@ -96,65 +278,117 @@ class Dropdown extends React.Component {
|
|
|
96
278
|
const styles = () => ({
|
|
97
279
|
root: {
|
|
98
280
|
color: color.text(),
|
|
281
|
+
border: `1px solid ${color.borderGray()}`,
|
|
282
|
+
borderRadius: '4px',
|
|
283
|
+
justifyContent: 'space-between',
|
|
99
284
|
backgroundColor: color.background(),
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
285
|
+
position: 'relative',
|
|
286
|
+
height: '45px',
|
|
287
|
+
width: 'fit-content',
|
|
288
|
+
margin: '2px',
|
|
289
|
+
textTransform: 'none',
|
|
290
|
+
'& span': {
|
|
291
|
+
paddingRight: '5px',
|
|
292
|
+
},
|
|
293
|
+
'& svg': {
|
|
294
|
+
position: 'absolute',
|
|
295
|
+
right: 0,
|
|
296
|
+
top: 'calc(50% - 12px)',
|
|
297
|
+
pointerEvents: 'none',
|
|
106
298
|
color: color.text(),
|
|
107
|
-
|
|
299
|
+
marginLeft: '5px',
|
|
108
300
|
},
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
301
|
+
'&:focus, &:focus-visible': {
|
|
302
|
+
outline: `3px solid ${color.tertiary()}`,
|
|
303
|
+
outlineOffset: '2px',
|
|
304
|
+
borderWidth: '3px',
|
|
113
305
|
},
|
|
114
306
|
},
|
|
307
|
+
disabledCorrect: {
|
|
308
|
+
borderWidth: '2px',
|
|
309
|
+
borderColor: color.correct(),
|
|
310
|
+
color: `${color.text()} !important`,
|
|
311
|
+
},
|
|
312
|
+
disabledIncorrect: {
|
|
313
|
+
borderWidth: '2px',
|
|
314
|
+
borderColor: color.incorrectWithIcon(),
|
|
315
|
+
color: `${color.text()} !important`,
|
|
316
|
+
},
|
|
115
317
|
selectMenu: {
|
|
116
318
|
backgroundColor: color.background(),
|
|
319
|
+
border: `1px solid ${color.correct()} !important`,
|
|
117
320
|
'&:hover': {
|
|
321
|
+
border: `1px solid ${color.text()} `,
|
|
118
322
|
borderColor: 'initial',
|
|
119
323
|
},
|
|
120
324
|
'&:focus': {
|
|
325
|
+
border: `1px solid ${color.text()}`,
|
|
121
326
|
borderColor: 'initial',
|
|
122
327
|
},
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
328
|
+
// remove default padding on the inner list
|
|
329
|
+
'& .MuiList-root': {
|
|
330
|
+
padding: 0,
|
|
331
|
+
},
|
|
126
332
|
},
|
|
127
333
|
selected: {
|
|
128
334
|
color: `${color.text()} !important`,
|
|
129
335
|
backgroundColor: `${color.background()} !important`,
|
|
130
336
|
'&:hover': {
|
|
131
337
|
color: color.text(),
|
|
132
|
-
backgroundColor: `${color.
|
|
338
|
+
backgroundColor: `${color.dropdownBackground()} !important`,
|
|
133
339
|
},
|
|
134
340
|
},
|
|
135
341
|
menuRoot: {
|
|
136
342
|
color: color.text(),
|
|
137
343
|
backgroundColor: color.background(),
|
|
344
|
+
'&:focus, &:focus-visible': {
|
|
345
|
+
outline: `3px solid ${color.tertiary()}`,
|
|
346
|
+
outlineOffset: '-1px', // keeps it inside the item
|
|
347
|
+
},
|
|
138
348
|
'&:focus': {
|
|
139
349
|
color: color.text(),
|
|
140
350
|
backgroundColor: color.background(),
|
|
141
351
|
},
|
|
142
352
|
'&:hover': {
|
|
143
353
|
color: color.text(),
|
|
144
|
-
backgroundColor: color.
|
|
354
|
+
backgroundColor: color.dropdownBackground(),
|
|
145
355
|
},
|
|
146
356
|
boxSizing: 'border-box',
|
|
147
357
|
padding: '25px',
|
|
148
|
-
'
|
|
149
|
-
borderRadius: '3px 3px 0 0',
|
|
150
|
-
},
|
|
151
|
-
'&:last-of-type': {
|
|
152
|
-
borderRadius: '0 0 3px 3px',
|
|
153
|
-
},
|
|
358
|
+
borderRadius: '4px',
|
|
154
359
|
},
|
|
155
360
|
label: {
|
|
156
361
|
fontSize: 'max(1rem, 14px)',
|
|
157
362
|
},
|
|
363
|
+
selectedIndicator: {
|
|
364
|
+
fontSize: 'max(1rem, 14px)',
|
|
365
|
+
position: 'absolute',
|
|
366
|
+
right: '10px',
|
|
367
|
+
},
|
|
368
|
+
srOnly: {
|
|
369
|
+
position: 'absolute',
|
|
370
|
+
left: '-10000px',
|
|
371
|
+
top: 'auto',
|
|
372
|
+
width: '1px',
|
|
373
|
+
height: '1px',
|
|
374
|
+
overflow: 'hidden',
|
|
375
|
+
},
|
|
376
|
+
correctnessIndicatorIcon: {
|
|
377
|
+
color: `${color.white()} !important`,
|
|
378
|
+
position: 'absolute',
|
|
379
|
+
top: '-8px !important',
|
|
380
|
+
left: '-8px',
|
|
381
|
+
marginLeft: '0 !important',
|
|
382
|
+
borderRadius: '50%',
|
|
383
|
+
fontSize: '16px',
|
|
384
|
+
padding: '2px',
|
|
385
|
+
},
|
|
386
|
+
correctIcon: {
|
|
387
|
+
backgroundColor: color.correct(),
|
|
388
|
+
},
|
|
389
|
+
incorrectIcon: {
|
|
390
|
+
backgroundColor: color.incorrectWithIcon(),
|
|
391
|
+
},
|
|
158
392
|
});
|
|
159
393
|
|
|
160
394
|
export default withStyles(styles)(Dropdown);
|
|
@@ -1,34 +1,92 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import
|
|
2
|
+
import { withStyles } from '@material-ui/core/styles';
|
|
3
|
+
import classnames from 'classnames';
|
|
4
|
+
|
|
5
|
+
import { color } from '@pie-lib/render-ui';
|
|
6
|
+
import EditableHtml from '@pie-lib/editable-html';
|
|
3
7
|
import { withMask } from './with-mask';
|
|
4
8
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
const styles = () => ({
|
|
10
|
+
editableHtmlCustom: {
|
|
11
|
+
display: 'inline-block',
|
|
12
|
+
verticalAlign: 'middle',
|
|
13
|
+
margin: '4px',
|
|
14
|
+
borderRadius: '4px',
|
|
15
|
+
border: `1px solid ${color.black()}`,
|
|
16
|
+
},
|
|
17
|
+
correct: {
|
|
18
|
+
border: `1px solid ${color.correct()}`,
|
|
19
|
+
},
|
|
20
|
+
incorrect: {
|
|
21
|
+
border: `1px solid ${color.incorrect()}`,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
11
24
|
|
|
12
|
-
|
|
13
|
-
|
|
25
|
+
const MaskedInput = (props) => (node, data) => {
|
|
26
|
+
const {
|
|
27
|
+
adjustedLimit,
|
|
28
|
+
disabled,
|
|
29
|
+
feedback,
|
|
30
|
+
showCorrectAnswer,
|
|
31
|
+
maxLength,
|
|
32
|
+
spellCheck,
|
|
33
|
+
classes,
|
|
34
|
+
pluginProps,
|
|
35
|
+
onChange,
|
|
36
|
+
} = props;
|
|
37
|
+
const dataset = node.data?.dataset || {};
|
|
38
|
+
|
|
39
|
+
if (dataset.component === 'input') {
|
|
14
40
|
const correctAnswer = ((props.choices && dataset && props.choices[dataset.id]) || [])[0];
|
|
15
41
|
const finalValue = showCorrectAnswer ? correctAnswer && correctAnswer.label : data[dataset.id] || '';
|
|
16
42
|
const width = maxLength && maxLength[dataset.id];
|
|
43
|
+
const feedbackStatus = feedback && feedback[dataset.id];
|
|
44
|
+
const isCorrect = showCorrectAnswer || feedbackStatus === 'correct';
|
|
45
|
+
const isIncorrect = !showCorrectAnswer && feedbackStatus === 'incorrect';
|
|
46
|
+
|
|
47
|
+
const handleInputChange = (newValue) => {
|
|
48
|
+
const updatedValue = {
|
|
49
|
+
...data,
|
|
50
|
+
[dataset.id]: newValue,
|
|
51
|
+
};
|
|
52
|
+
onChange(updatedValue);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const handleKeyDown = (event) => {
|
|
56
|
+
// the keyCode value for the Enter/Return key is 13
|
|
57
|
+
if (event.key === 'Enter' || event.keyCode === 13) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
17
61
|
|
|
18
62
|
return (
|
|
19
|
-
<
|
|
63
|
+
<EditableHtml
|
|
64
|
+
id={dataset.id}
|
|
20
65
|
key={`${node.type}-input-${dataset.id}`}
|
|
21
|
-
correct={feedback && feedback[dataset.id] && feedback[dataset.id] === 'correct'}
|
|
22
66
|
disabled={showCorrectAnswer || disabled}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
showCorrectAnswer={showCorrectAnswer}
|
|
27
|
-
width={width}
|
|
67
|
+
disableUnderline
|
|
68
|
+
onChange={handleInputChange}
|
|
69
|
+
markup={finalValue || ''}
|
|
28
70
|
charactersLimit={adjustedLimit ? width : 25}
|
|
29
|
-
|
|
71
|
+
activePlugins={['languageCharacters']}
|
|
72
|
+
pluginProps={pluginProps}
|
|
73
|
+
languageCharactersProps={[{ language: 'spanish' }]}
|
|
30
74
|
spellCheck={spellCheck}
|
|
75
|
+
width={`calc(${width}em + 42px)`} // added 42px for left and right padding of editable-html
|
|
76
|
+
onKeyDown={handleKeyDown}
|
|
77
|
+
autoWidthToolbar
|
|
78
|
+
toolbarOpts={{
|
|
79
|
+
minWidth: 'auto',
|
|
80
|
+
noBorder: true,
|
|
81
|
+
isHidden: !!pluginProps?.characters?.disabled,
|
|
82
|
+
}}
|
|
83
|
+
className={classnames(classes.editableHtmlCustom, {
|
|
84
|
+
[classes.correct]: isCorrect,
|
|
85
|
+
[classes.incorrect]: isIncorrect,
|
|
86
|
+
})}
|
|
31
87
|
/>
|
|
32
88
|
);
|
|
33
89
|
}
|
|
34
|
-
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export default withStyles(styles)(withMask('input', MaskedInput));
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
// import Input from './components/input';
|
|
3
|
+
import { withMask } from './with-mask';
|
|
4
|
+
|
|
5
|
+
// eslint-disable-next-line react/display-name
|
|
6
|
+
export default withMask('input', (props) => (node, data, onChange) => {
|
|
7
|
+
const dataset = node.data ? node.data.dataset || {} : {};
|
|
8
|
+
if (dataset.component === 'input') {
|
|
9
|
+
// eslint-disable-next-line react/prop-types
|
|
10
|
+
// const { adjustedLimit, disabled, feedback, showCorrectAnswer, maxLength, spellCheck } = props;
|
|
11
|
+
|
|
12
|
+
// the first answer is the correct one
|
|
13
|
+
// eslint-disable-next-line react/prop-types
|
|
14
|
+
// const correctAnswer = ((props.choices && dataset && props.choices[dataset.id]) || [])[0];
|
|
15
|
+
// const finalValue = showCorrectAnswer ? correctAnswer && correctAnswer.label : data[dataset.id] || '';
|
|
16
|
+
// const width = maxLength && maxLength[dataset.id];
|
|
17
|
+
|
|
18
|
+
return props.customMarkMarkupComponent(dataset.id);
|
|
19
|
+
// return (
|
|
20
|
+
// <Input
|
|
21
|
+
// key={`${node.type}-input-${dataset.id}`}
|
|
22
|
+
// correct={feedback && feedback[dataset.id] && feedback[dataset.id] === 'correct'}
|
|
23
|
+
// disabled={showCorrectAnswer || disabled}
|
|
24
|
+
// value={finalValue}
|
|
25
|
+
// id={dataset.id}
|
|
26
|
+
// onChange={onChange}
|
|
27
|
+
// showCorrectAnswer={showCorrectAnswer}
|
|
28
|
+
// width={width}
|
|
29
|
+
// charactersLimit={adjustedLimit ? width : 25}
|
|
30
|
+
// isConstructedResponse={true}
|
|
31
|
+
// spellCheck={spellCheck}
|
|
32
|
+
// />
|
|
33
|
+
// );
|
|
34
|
+
}
|
|
35
|
+
});
|