@pie-lib/mask-markup 1.13.47-next.1 → 1.14.0-beta.2
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.md +22 -47
- package/NEXT.CHANGELOG.json +1 -0
- package/package.json +9 -5
- 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 +133 -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 +192 -71
- 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
- package/lib/choices/choice.js +0 -158
- package/lib/choices/choice.js.map +0 -1
- package/lib/choices/index.js +0 -127
- package/lib/choices/index.js.map +0 -1
- package/lib/componentize.js +0 -25
- package/lib/componentize.js.map +0 -1
- package/lib/components/blank.js +0 -303
- package/lib/components/blank.js.map +0 -1
- package/lib/components/correct-input.js +0 -113
- package/lib/components/correct-input.js.map +0 -1
- package/lib/components/dropdown.js +0 -216
- package/lib/components/dropdown.js.map +0 -1
- package/lib/components/input.js +0 -57
- package/lib/components/input.js.map +0 -1
- package/lib/constructed-response.js +0 -52
- package/lib/constructed-response.js.map +0 -1
- package/lib/drag-in-the-blank.js +0 -191
- package/lib/drag-in-the-blank.js.map +0 -1
- package/lib/index.js +0 -54
- package/lib/index.js.map +0 -1
- package/lib/inline-dropdown.js +0 -46
- package/lib/inline-dropdown.js.map +0 -1
- package/lib/mask.js +0 -215
- package/lib/mask.js.map +0 -1
- package/lib/serialization.js +0 -207
- package/lib/serialization.js.map +0 -1
- package/lib/with-mask.js +0 -93
- package/lib/with-mask.js.map +0 -1
|
@@ -1,10 +1,17 @@
|
|
|
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 Close from '@material-ui/icons/Close';
|
|
9
|
+
import Check from '@material-ui/icons/Check';
|
|
6
10
|
import { withStyles } from '@material-ui/core/styles';
|
|
11
|
+
import classNames from 'classnames';
|
|
12
|
+
|
|
7
13
|
import { color } from '@pie-lib/render-ui';
|
|
14
|
+
import { renderMath } from '@pie-lib/math-rendering';
|
|
8
15
|
|
|
9
16
|
class Dropdown extends React.Component {
|
|
10
17
|
static propTypes = {
|
|
@@ -22,73 +29,148 @@ class Dropdown extends React.Component {
|
|
|
22
29
|
super(props);
|
|
23
30
|
|
|
24
31
|
this.state = {
|
|
25
|
-
|
|
26
|
-
|
|
32
|
+
anchorEl: null,
|
|
33
|
+
highlightedOptionId: null,
|
|
27
34
|
};
|
|
35
|
+
|
|
36
|
+
this.elementRefs = [];
|
|
28
37
|
}
|
|
29
38
|
|
|
30
|
-
|
|
31
|
-
this.
|
|
32
|
-
|
|
33
|
-
|
|
39
|
+
componentDidUpdate() {
|
|
40
|
+
this.elementRefs.forEach((ref) => {
|
|
41
|
+
if (ref) {
|
|
42
|
+
renderMath(ref);
|
|
43
|
+
}
|
|
34
44
|
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
handleClick = (event) => this.setState({ anchorEl: event.currentTarget });
|
|
48
|
+
|
|
49
|
+
handleClose = () => this.setState({ anchorEl: null });
|
|
50
|
+
|
|
51
|
+
handleHighlight = (index) => {
|
|
52
|
+
const highlightedOptionId = `dropdown-option-${this.props.id}-${index}`;
|
|
53
|
+
|
|
54
|
+
this.setState({ highlightedOptionId });
|
|
35
55
|
};
|
|
36
56
|
|
|
37
|
-
|
|
38
|
-
this.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
});
|
|
57
|
+
handleSelect = (value, index) => {
|
|
58
|
+
this.props.onChange(this.props.id, value);
|
|
59
|
+
this.handleHighlight(index);
|
|
60
|
+
this.handleClose();
|
|
42
61
|
};
|
|
43
62
|
|
|
63
|
+
getLabel(choices, value) {
|
|
64
|
+
const found = (choices || []).find((choice) => choice.value === value);
|
|
65
|
+
|
|
66
|
+
return found ? found.label.trim() : undefined;
|
|
67
|
+
}
|
|
68
|
+
|
|
44
69
|
render() {
|
|
45
|
-
const { classes, id, correct, disabled, value,
|
|
70
|
+
const { classes, id, correct, disabled, value, choices, showCorrectAnswer, singleQuery, correctValue } = this.props;
|
|
71
|
+
|
|
72
|
+
const { anchorEl } = this.state;
|
|
73
|
+
const open = Boolean(anchorEl);
|
|
74
|
+
const buttonId = `dropdown-button-${id}`;
|
|
75
|
+
const menuId = `dropdown-menu-${id}`;
|
|
76
|
+
const valueDisplayId = `dropdown-value-${id}`;
|
|
77
|
+
|
|
78
|
+
// Determine the class for disabled state, view mode and evaluate mode
|
|
79
|
+
let disabledClass;
|
|
80
|
+
// Reset elementRefs before each render to avoid stale references
|
|
81
|
+
this.elementRefs = [];
|
|
82
|
+
|
|
83
|
+
if (disabled && correct !== undefined) {
|
|
84
|
+
disabledClass = correct || showCorrectAnswer ? classes.disabledCorrect : classes.disabledIncorrect;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Create distinct, visually hidden labels for each dropdown
|
|
88
|
+
const incrementedId = parseInt(id, 10) + 1;
|
|
89
|
+
const labelId = singleQuery ? 'Query-label' : `Query-label-${incrementedId}`;
|
|
90
|
+
const labelText = singleQuery ? 'Query' : `Query ${incrementedId}`;
|
|
91
|
+
|
|
92
|
+
// 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.
|
|
46
93
|
|
|
47
|
-
|
|
94
|
+
let correctnessIcon = null;
|
|
95
|
+
if (disabled && correct !== undefined) {
|
|
96
|
+
correctnessIcon =
|
|
97
|
+
correct || showCorrectAnswer ? (
|
|
98
|
+
<Check className={classNames(classes.correctnessIndicatorIcon, classes.correctIcon)} />
|
|
99
|
+
) : (
|
|
100
|
+
<Close className={classNames(classes.correctnessIndicatorIcon, classes.incorrectIcon)} />
|
|
101
|
+
);
|
|
102
|
+
}
|
|
48
103
|
|
|
49
104
|
return (
|
|
50
|
-
|
|
51
|
-
classes={
|
|
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
|
-
|
|
105
|
+
<>
|
|
106
|
+
<InputLabel className={classes.srOnly} id={labelId}>
|
|
107
|
+
{labelText}
|
|
108
|
+
</InputLabel>
|
|
109
|
+
<Button
|
|
110
|
+
aria-controls={open ? menuId : undefined}
|
|
111
|
+
aria-haspopup="listbox"
|
|
112
|
+
aria-expanded={open ? 'true' : undefined}
|
|
113
|
+
aria-activedescendant={this.state.highlightedOptionId}
|
|
114
|
+
onClick={this.handleClick}
|
|
115
|
+
classes={{
|
|
116
|
+
root: classes.root,
|
|
117
|
+
disabled: disabledClass,
|
|
118
|
+
}}
|
|
119
|
+
disabled={disabled}
|
|
120
|
+
id={buttonId}
|
|
121
|
+
role="combobox"
|
|
122
|
+
aria-label="Select answer"
|
|
123
|
+
aria-labelledby={valueDisplayId}
|
|
124
|
+
>
|
|
125
|
+
{correctnessIcon}
|
|
126
|
+
<span
|
|
127
|
+
id={valueDisplayId}
|
|
128
|
+
className={classes.label}
|
|
129
|
+
dangerouslySetInnerHTML={{
|
|
130
|
+
__html: correctValue ? correctValue : this.getLabel(choices, value) ? this.getLabel(choices, value) : '',
|
|
131
|
+
}}
|
|
132
|
+
/>
|
|
133
|
+
<ArrowDropDownIcon />
|
|
134
|
+
</Button>
|
|
135
|
+
<Menu
|
|
136
|
+
id={menuId}
|
|
137
|
+
anchorEl={anchorEl}
|
|
138
|
+
className={classes.selectMenu}
|
|
139
|
+
keepMounted
|
|
140
|
+
open={open}
|
|
141
|
+
onClose={this.handleClose}
|
|
142
|
+
MenuListProps={{
|
|
143
|
+
'aria-labelledby': buttonId,
|
|
144
|
+
role: 'listbox',
|
|
145
|
+
}}
|
|
146
|
+
>
|
|
147
|
+
{(choices || []).map((c, index) => {
|
|
148
|
+
const optionId = `dropdown-option-${id}-${index}`;
|
|
149
|
+
|
|
150
|
+
return (
|
|
151
|
+
<MenuItem
|
|
152
|
+
id={optionId}
|
|
153
|
+
classes={{ root: classes.menuRoot, selected: classes.selected }}
|
|
154
|
+
key={`${c.label}-${index}`}
|
|
155
|
+
value={c.value}
|
|
156
|
+
onClick={() => this.handleSelect(c.value, index)}
|
|
157
|
+
role="option"
|
|
158
|
+
aria-selected={this.state.highlightedOptionId === optionId ? 'true' : undefined}
|
|
159
|
+
>
|
|
160
|
+
<span
|
|
161
|
+
ref={(ref) => (this.elementRefs[index] = ref)}
|
|
162
|
+
className={classes.label}
|
|
163
|
+
dangerouslySetInnerHTML={{ __html: c.label }}
|
|
164
|
+
/>
|
|
165
|
+
<span
|
|
166
|
+
className={classes.label}
|
|
167
|
+
dangerouslySetInnerHTML={{ __html: c.value === value ? ' ✓' : '' }}
|
|
168
|
+
/>
|
|
169
|
+
</MenuItem>
|
|
170
|
+
);
|
|
171
|
+
})}
|
|
172
|
+
</Menu>
|
|
173
|
+
</>
|
|
92
174
|
);
|
|
93
175
|
}
|
|
94
176
|
}
|
|
@@ -96,34 +178,49 @@ class Dropdown extends React.Component {
|
|
|
96
178
|
const styles = () => ({
|
|
97
179
|
root: {
|
|
98
180
|
color: color.text(),
|
|
181
|
+
border: `1px solid ${color.text()}`,
|
|
182
|
+
borderRadius: '4px',
|
|
183
|
+
justifyContent: 'space-between',
|
|
99
184
|
backgroundColor: color.background(),
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
185
|
+
position: 'relative',
|
|
186
|
+
height: '45px',
|
|
187
|
+
width: 'fit-content',
|
|
188
|
+
margin: '2px',
|
|
189
|
+
textTransform: 'none',
|
|
190
|
+
'& span': {
|
|
191
|
+
paddingRight: '5px',
|
|
192
|
+
},
|
|
193
|
+
'& svg': {
|
|
194
|
+
position: 'absolute',
|
|
195
|
+
right: 0,
|
|
196
|
+
top: 'calc(50% - 12px)',
|
|
197
|
+
pointerEvents: 'none',
|
|
106
198
|
color: color.text(),
|
|
107
|
-
|
|
199
|
+
marginLeft: '5px',
|
|
108
200
|
},
|
|
109
201
|
},
|
|
110
|
-
|
|
111
|
-
'
|
|
112
|
-
|
|
113
|
-
}
|
|
202
|
+
disabledCorrect: {
|
|
203
|
+
borderWidth: '2px',
|
|
204
|
+
borderColor: color.correct(),
|
|
205
|
+
color: `${color.text()} !important`,
|
|
206
|
+
},
|
|
207
|
+
disabledIncorrect: {
|
|
208
|
+
borderWidth: '2px',
|
|
209
|
+
borderColor: color.incorrectWithIcon(),
|
|
210
|
+
color: `${color.text()} !important`,
|
|
114
211
|
},
|
|
115
212
|
selectMenu: {
|
|
116
213
|
backgroundColor: color.background(),
|
|
214
|
+
border: `1px solid ${color.correct()} !important`,
|
|
117
215
|
'&:hover': {
|
|
216
|
+
border: `1px solid ${color.text()} `,
|
|
118
217
|
borderColor: 'initial',
|
|
119
218
|
},
|
|
120
219
|
'&:focus': {
|
|
220
|
+
border: `1px solid ${color.text()}`,
|
|
121
221
|
borderColor: 'initial',
|
|
122
222
|
},
|
|
123
223
|
},
|
|
124
|
-
icon: {
|
|
125
|
-
color: color.text(),
|
|
126
|
-
},
|
|
127
224
|
selected: {
|
|
128
225
|
color: `${color.text()} !important`,
|
|
129
226
|
backgroundColor: `${color.background()} !important`,
|
|
@@ -155,6 +252,30 @@ const styles = () => ({
|
|
|
155
252
|
label: {
|
|
156
253
|
fontSize: 'max(1rem, 14px)',
|
|
157
254
|
},
|
|
255
|
+
srOnly: {
|
|
256
|
+
position: 'absolute',
|
|
257
|
+
left: '-10000px',
|
|
258
|
+
top: 'auto',
|
|
259
|
+
width: '1px',
|
|
260
|
+
height: '1px',
|
|
261
|
+
overflow: 'hidden',
|
|
262
|
+
},
|
|
263
|
+
correctnessIndicatorIcon: {
|
|
264
|
+
color: `${color.white()} !important`,
|
|
265
|
+
position: 'absolute',
|
|
266
|
+
top: '-8px !important',
|
|
267
|
+
left: '-8px',
|
|
268
|
+
marginLeft: '0 !important',
|
|
269
|
+
borderRadius: '50%',
|
|
270
|
+
fontSize: '16px',
|
|
271
|
+
padding: '2px',
|
|
272
|
+
},
|
|
273
|
+
correctIcon: {
|
|
274
|
+
backgroundColor: color.correct(),
|
|
275
|
+
},
|
|
276
|
+
incorrectIcon: {
|
|
277
|
+
backgroundColor: color.incorrectWithIcon(),
|
|
278
|
+
},
|
|
158
279
|
});
|
|
159
280
|
|
|
160
281
|
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
|
+
});
|
|
@@ -10,7 +10,15 @@ const Masked = withMask('blank', (props) => (node, data, onChange) => {
|
|
|
10
10
|
const dataset = node.data ? node.data.dataset || {} : {};
|
|
11
11
|
if (dataset.component === 'blank') {
|
|
12
12
|
// eslint-disable-next-line react/prop-types
|
|
13
|
-
const {
|
|
13
|
+
const {
|
|
14
|
+
disabled,
|
|
15
|
+
duplicates,
|
|
16
|
+
correctResponse,
|
|
17
|
+
feedback,
|
|
18
|
+
showCorrectAnswer,
|
|
19
|
+
emptyResponseAreaWidth,
|
|
20
|
+
emptyResponseAreaHeight,
|
|
21
|
+
} = props;
|
|
14
22
|
const choiceId = showCorrectAnswer ? correctResponse[dataset.id] : data[dataset.id];
|
|
15
23
|
// eslint-disable-next-line react/prop-types
|
|
16
24
|
const choice = choiceId && props.choices.find((c) => c.id === choiceId);
|
|
@@ -23,6 +31,8 @@ const Masked = withMask('blank', (props) => (node, data, onChange) => {
|
|
|
23
31
|
duplicates={duplicates}
|
|
24
32
|
choice={choice}
|
|
25
33
|
id={dataset.id}
|
|
34
|
+
emptyResponseAreaWidth={emptyResponseAreaWidth}
|
|
35
|
+
emptyResponseAreaHeight={emptyResponseAreaHeight}
|
|
26
36
|
onChange={onChange}
|
|
27
37
|
/>
|
|
28
38
|
);
|
|
@@ -42,6 +52,8 @@ export default class DragInTheBlank extends React.Component {
|
|
|
42
52
|
feedback: PropTypes.object,
|
|
43
53
|
correctResponse: PropTypes.object,
|
|
44
54
|
showCorrectAnswer: PropTypes.bool,
|
|
55
|
+
emptyResponseAreaWidth: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
56
|
+
emptyResponseAreaHeight: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
45
57
|
};
|
|
46
58
|
|
|
47
59
|
UNSAFE_componentWillReceiveProps() {
|
|
@@ -56,13 +68,18 @@ export default class DragInTheBlank extends React.Component {
|
|
|
56
68
|
|
|
57
69
|
getPositionDirection = (choicePosition) => {
|
|
58
70
|
let flexDirection;
|
|
71
|
+
let justifyContent;
|
|
72
|
+
let alignItems;
|
|
59
73
|
|
|
60
74
|
switch (choicePosition) {
|
|
61
75
|
case 'left':
|
|
62
76
|
flexDirection = 'row';
|
|
77
|
+
alignItems = 'center';
|
|
63
78
|
break;
|
|
64
79
|
case 'right':
|
|
65
80
|
flexDirection = 'row-reverse';
|
|
81
|
+
justifyContent = 'flex-end';
|
|
82
|
+
alignItems = 'center';
|
|
66
83
|
break;
|
|
67
84
|
case 'below':
|
|
68
85
|
flexDirection = 'column-reverse';
|
|
@@ -73,7 +90,7 @@ export default class DragInTheBlank extends React.Component {
|
|
|
73
90
|
break;
|
|
74
91
|
}
|
|
75
92
|
|
|
76
|
-
return flexDirection;
|
|
93
|
+
return { flexDirection, justifyContent, alignItems };
|
|
77
94
|
};
|
|
78
95
|
|
|
79
96
|
render() {
|
|
@@ -89,12 +106,15 @@ export default class DragInTheBlank extends React.Component {
|
|
|
89
106
|
disabled,
|
|
90
107
|
feedback,
|
|
91
108
|
showCorrectAnswer,
|
|
109
|
+
emptyResponseAreaWidth,
|
|
110
|
+
emptyResponseAreaHeight,
|
|
92
111
|
} = this.props;
|
|
93
112
|
|
|
94
113
|
const choicePosition = choicesPosition || 'below';
|
|
95
114
|
const style = {
|
|
96
115
|
display: 'flex',
|
|
97
|
-
|
|
116
|
+
minWidth: '100px',
|
|
117
|
+
...this.getPositionDirection(choicePosition),
|
|
98
118
|
};
|
|
99
119
|
|
|
100
120
|
return (
|
|
@@ -107,6 +127,7 @@ export default class DragInTheBlank extends React.Component {
|
|
|
107
127
|
disabled={disabled}
|
|
108
128
|
/>
|
|
109
129
|
<Masked
|
|
130
|
+
elementType={'drag-in-the-blank'}
|
|
110
131
|
markup={markup}
|
|
111
132
|
layout={layout}
|
|
112
133
|
value={value}
|
|
@@ -117,6 +138,8 @@ export default class DragInTheBlank extends React.Component {
|
|
|
117
138
|
feedback={feedback}
|
|
118
139
|
correctResponse={correctResponse}
|
|
119
140
|
showCorrectAnswer={showCorrectAnswer}
|
|
141
|
+
emptyResponseAreaWidth={emptyResponseAreaWidth}
|
|
142
|
+
emptyResponseAreaHeight={emptyResponseAreaHeight}
|
|
120
143
|
/>
|
|
121
144
|
</div>
|
|
122
145
|
);
|
package/src/index.js
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import { withMask, buildLayoutFromMarkup } from './with-mask';
|
|
2
2
|
import DragInTheBlank from './drag-in-the-blank';
|
|
3
3
|
import ConstructedResponse from './constructed-response';
|
|
4
|
+
import Customizable from './customizable';
|
|
4
5
|
import InlineDropdown from './inline-dropdown';
|
|
5
6
|
import componentize from './componentize';
|
|
6
7
|
|
|
7
|
-
export {
|
|
8
|
+
export {
|
|
9
|
+
withMask,
|
|
10
|
+
buildLayoutFromMarkup,
|
|
11
|
+
DragInTheBlank,
|
|
12
|
+
ConstructedResponse,
|
|
13
|
+
InlineDropdown,
|
|
14
|
+
componentize,
|
|
15
|
+
Customizable,
|
|
16
|
+
};
|
package/src/inline-dropdown.jsx
CHANGED
|
@@ -17,10 +17,12 @@ export default withMask('dropdown', (props) => (node, data, onChange) => {
|
|
|
17
17
|
correct={feedback && feedback[dataset.id] && feedback[dataset.id] === 'correct'}
|
|
18
18
|
disabled={disabled || showCorrectAnswer}
|
|
19
19
|
value={finalChoice}
|
|
20
|
+
correctValue={showCorrectAnswer ? correctAnswer && correctAnswer.label : undefined}
|
|
20
21
|
id={dataset.id}
|
|
21
22
|
onChange={onChange}
|
|
22
23
|
choices={choices[dataset.id]}
|
|
23
24
|
showCorrectAnswer={showCorrectAnswer}
|
|
25
|
+
singleQuery={Object.keys(choices).length == 1}
|
|
24
26
|
/>
|
|
25
27
|
);
|
|
26
28
|
}
|
package/src/mask.jsx
CHANGED
|
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
|
|
3
3
|
import get from 'lodash/get';
|
|
4
4
|
import { withStyles } from '@material-ui/core/styles';
|
|
5
5
|
import { MARK_TAGS } from './serialization';
|
|
6
|
+
import cx from 'classnames';
|
|
6
7
|
|
|
7
8
|
const Paragraph = withStyles((theme) => ({
|
|
8
9
|
para: {
|
|
@@ -11,6 +12,13 @@ const Paragraph = withStyles((theme) => ({
|
|
|
11
12
|
},
|
|
12
13
|
}))((props) => <div className={props.classes.para}>{props.children}</div>);
|
|
13
14
|
|
|
15
|
+
const Spacer = withStyles(() => ({
|
|
16
|
+
spacer: {
|
|
17
|
+
display: 'inline-block',
|
|
18
|
+
width: '.75em',
|
|
19
|
+
},
|
|
20
|
+
}))((props) => <span className={props.classes.spacer} />);
|
|
21
|
+
|
|
14
22
|
const restrictWhitespaceTypes = ['tbody', 'tr'];
|
|
15
23
|
|
|
16
24
|
const addText = (parentNode, text) => {
|
|
@@ -34,7 +42,7 @@ const getMark = (n) => {
|
|
|
34
42
|
return null;
|
|
35
43
|
};
|
|
36
44
|
|
|
37
|
-
export const renderChildren = (layout, value, onChange, rootRenderChildren, parentNode) => {
|
|
45
|
+
export const renderChildren = (layout, value, onChange, rootRenderChildren, parentNode, elementType) => {
|
|
38
46
|
if (!value) {
|
|
39
47
|
return null;
|
|
40
48
|
}
|
|
@@ -59,6 +67,9 @@ export const renderChildren = (layout, value, onChange, rootRenderChildren, pare
|
|
|
59
67
|
const c = rootRenderChildren(n, value, onChange);
|
|
60
68
|
if (c) {
|
|
61
69
|
children.push(c);
|
|
70
|
+
if (parentNode?.type !== 'td' && elementType === 'drag-in-the-blank') {
|
|
71
|
+
children.push(<Spacer key={`spacer-${index}`} />);
|
|
72
|
+
}
|
|
62
73
|
return;
|
|
63
74
|
}
|
|
64
75
|
}
|
|
@@ -84,9 +95,12 @@ export const renderChildren = (layout, value, onChange, rootRenderChildren, pare
|
|
|
84
95
|
}
|
|
85
96
|
} else if (content.length > 0) {
|
|
86
97
|
children.push(content);
|
|
98
|
+
if (parentNode?.type !== 'td' && elementType === 'drag-in-the-blank') {
|
|
99
|
+
children.push(<Spacer key={`spacer-${index}`} />);
|
|
100
|
+
}
|
|
87
101
|
}
|
|
88
102
|
} else {
|
|
89
|
-
const subNodes = renderChildren(n, value, onChange, rootRenderChildren, n);
|
|
103
|
+
const subNodes = renderChildren(n, value, onChange, rootRenderChildren, n, elementType);
|
|
90
104
|
if (n.type === 'p' || n.type === 'paragraph') {
|
|
91
105
|
children.push(<Paragraph key={key}>{subNodes}</Paragraph>);
|
|
92
106
|
} else {
|
|
@@ -110,7 +124,17 @@ const MaskContainer = withStyles(() => ({
|
|
|
110
124
|
main: {
|
|
111
125
|
display: 'initial',
|
|
112
126
|
},
|
|
113
|
-
|
|
127
|
+
tableStyle: {
|
|
128
|
+
'&:not(.MathJax) table': {
|
|
129
|
+
borderCollapse: 'collapse',
|
|
130
|
+
},
|
|
131
|
+
// align table content to left as per STAR requirement PD-3687
|
|
132
|
+
'&:not(.MathJax) table td, &:not(.MathJax) table th': {
|
|
133
|
+
padding: '8px 12px',
|
|
134
|
+
textAlign: 'left',
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
}))((props) => <div className={cx(props.classes.main, props.classes.tableStyle)}>{props.children}</div>);
|
|
114
138
|
|
|
115
139
|
/**
|
|
116
140
|
* Renders a layout that uses the slate.js Value model structure.
|
|
@@ -121,6 +145,7 @@ export default class Mask extends React.Component {
|
|
|
121
145
|
layout: PropTypes.object,
|
|
122
146
|
value: PropTypes.object,
|
|
123
147
|
onChange: PropTypes.func,
|
|
148
|
+
elementType: PropTypes.string,
|
|
124
149
|
};
|
|
125
150
|
|
|
126
151
|
handleChange = (id, value) => {
|
|
@@ -129,8 +154,8 @@ export default class Mask extends React.Component {
|
|
|
129
154
|
};
|
|
130
155
|
|
|
131
156
|
render() {
|
|
132
|
-
const { value, layout } = this.props;
|
|
133
|
-
const children = renderChildren(layout, value, this.handleChange, this.props.renderChildren);
|
|
157
|
+
const { value, layout, elementType } = this.props;
|
|
158
|
+
const children = renderChildren(layout, value, this.handleChange, this.props.renderChildren, null, elementType);
|
|
134
159
|
|
|
135
160
|
return <MaskContainer>{children}</MaskContainer>;
|
|
136
161
|
}
|