@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.
Files changed (63) hide show
  1. package/CHANGELOG.md +22 -47
  2. package/NEXT.CHANGELOG.json +1 -0
  3. package/package.json +9 -5
  4. package/src/__tests__/__snapshots__/drag-in-the-blank.test.js.snap +316 -0
  5. package/src/__tests__/__snapshots__/mask.test.js.snap +55 -0
  6. package/src/__tests__/__snapshots__/with-mask.test.js.snap +62 -0
  7. package/src/__tests__/drag-in-the-blank.test.js +71 -0
  8. package/src/__tests__/index.test.js +39 -0
  9. package/src/__tests__/mask.test.js +152 -0
  10. package/src/__tests__/serialization.test.js +54 -0
  11. package/src/__tests__/utils.js +1 -0
  12. package/src/__tests__/with-mask.test.js +51 -0
  13. package/src/choices/__tests__/__snapshots__/index.test.js.snap +209 -0
  14. package/src/choices/__tests__/index.test.js +62 -0
  15. package/src/choices/choice.jsx +60 -6
  16. package/src/choices/index.jsx +2 -2
  17. package/src/components/__tests__/__snapshots__/blank.test.js.snap +111 -0
  18. package/src/components/__tests__/__snapshots__/correct-input.test.js.snap +64 -0
  19. package/src/components/__tests__/__snapshots__/dropdown.test.js.snap +133 -0
  20. package/src/components/__tests__/__snapshots__/input.test.js.snap +34 -0
  21. package/src/components/__tests__/blank.test.js +202 -0
  22. package/src/components/__tests__/correct-input.test.js +49 -0
  23. package/src/components/__tests__/dropdown.test.js +51 -0
  24. package/src/components/__tests__/input.test.js +50 -0
  25. package/src/components/blank.jsx +139 -28
  26. package/src/components/correct-input.jsx +6 -1
  27. package/src/components/dropdown.jsx +192 -71
  28. package/src/constructed-response.jsx +76 -18
  29. package/src/customizable.jsx +35 -0
  30. package/src/drag-in-the-blank.jsx +26 -3
  31. package/src/index.js +10 -1
  32. package/src/inline-dropdown.jsx +2 -0
  33. package/src/mask.jsx +30 -5
  34. package/src/with-mask.jsx +39 -2
  35. package/README.md +0 -14
  36. package/lib/choices/choice.js +0 -158
  37. package/lib/choices/choice.js.map +0 -1
  38. package/lib/choices/index.js +0 -127
  39. package/lib/choices/index.js.map +0 -1
  40. package/lib/componentize.js +0 -25
  41. package/lib/componentize.js.map +0 -1
  42. package/lib/components/blank.js +0 -303
  43. package/lib/components/blank.js.map +0 -1
  44. package/lib/components/correct-input.js +0 -113
  45. package/lib/components/correct-input.js.map +0 -1
  46. package/lib/components/dropdown.js +0 -216
  47. package/lib/components/dropdown.js.map +0 -1
  48. package/lib/components/input.js +0 -57
  49. package/lib/components/input.js.map +0 -1
  50. package/lib/constructed-response.js +0 -52
  51. package/lib/constructed-response.js.map +0 -1
  52. package/lib/drag-in-the-blank.js +0 -191
  53. package/lib/drag-in-the-blank.js.map +0 -1
  54. package/lib/index.js +0 -54
  55. package/lib/index.js.map +0 -1
  56. package/lib/inline-dropdown.js +0 -46
  57. package/lib/inline-dropdown.js.map +0 -1
  58. package/lib/mask.js +0 -215
  59. package/lib/mask.js.map +0 -1
  60. package/lib/serialization.js +0 -207
  61. package/lib/serialization.js.map +0 -1
  62. package/lib/with-mask.js +0 -93
  63. 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 Select from '@material-ui/core/Select';
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 CorrectInput from './correct-input';
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
- showCheckmark: false,
26
- open: false,
32
+ anchorEl: null,
33
+ highlightedOptionId: null,
27
34
  };
35
+
36
+ this.elementRefs = [];
28
37
  }
29
38
 
30
- showCheckmarkAndOpen = () => {
31
- this.setState({
32
- showCheckmark: true,
33
- open: true,
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
- hideCheckmarkAndClose = () => {
38
- this.setState({
39
- showCheckmark: false,
40
- open: false,
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, onChange, choices, showCorrectAnswer } = this.props;
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
- const { showCheckmark, open } = this.state;
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
- <Select
51
- classes={{
52
- root: classes.root,
53
- icon: classes.icon,
54
- selectMenu: classes.selectMenu,
55
- select: classes.select,
56
- }}
57
- disabled={disabled}
58
- value={value || ''}
59
- onOpen={this.showCheckmarkAndOpen}
60
- onClose={this.hideCheckmarkAndClose}
61
- open={open}
62
- input={<CorrectInput correct={showCorrectAnswer || correct} />}
63
- MenuProps={{
64
- keepMounted: true,
65
- disablePortal: true,
66
- }}
67
- onChange={(e) => {
68
- onChange(id, e.target.value);
69
- }}
70
- >
71
- {(choices || []).map((c, index) => (
72
- <MenuItem
73
- classes={{ root: classes.menuRoot, selected: classes.selected }}
74
- key={`${c.label}-${index}`}
75
- value={c.value}
76
- >
77
- <span
78
- className={classes.label}
79
- dangerouslySetInnerHTML={{
80
- __html: c.label,
81
- }}
82
- />
83
- {showCheckmark && (
84
- <span
85
- className={classes.label}
86
- dangerouslySetInnerHTML={{ __html: c.value === value ? ' &check;' : '' }}
87
- />
88
- )}
89
- </MenuItem>
90
- ))}
91
- </Select>
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 ? ' &check;' : '' }}
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
- borderColor: color.secondaryLight(),
101
- '& ul': {
102
- paddingTop: 0,
103
- paddingBottom: 0,
104
- border: `1px solid ${color.text()}`,
105
- borderRadius: '5px',
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
- backgroundColor: color.background(),
199
+ marginLeft: '5px',
108
200
  },
109
201
  },
110
- select: {
111
- '&:focus': {
112
- borderRadius: '4px',
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 Input from './components/input';
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
- // 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;
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
- // the first answer is the correct one
13
- // eslint-disable-next-line react/prop-types
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
- <Input
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
- value={finalValue}
24
- id={dataset.id}
25
- onChange={onChange}
26
- showCorrectAnswer={showCorrectAnswer}
27
- width={width}
67
+ disableUnderline
68
+ onChange={handleInputChange}
69
+ markup={finalValue || ''}
28
70
  charactersLimit={adjustedLimit ? width : 25}
29
- isConstructedResponse={true}
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 { disabled, duplicates, correctResponse, feedback, showCorrectAnswer } = props;
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
- flexDirection: this.getPositionDirection(choicePosition),
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 { withMask, buildLayoutFromMarkup, DragInTheBlank, ConstructedResponse, InlineDropdown, componentize };
8
+ export {
9
+ withMask,
10
+ buildLayoutFromMarkup,
11
+ DragInTheBlank,
12
+ ConstructedResponse,
13
+ InlineDropdown,
14
+ componentize,
15
+ Customizable,
16
+ };
@@ -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
- }))((props) => <div className={props.classes.main}>{props.children}</div>);
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
  }