@pie-lib/mask-markup 1.33.3-next.2 → 1.33.3-next.205

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 (61) hide show
  1. package/CHANGELOG.md +13 -67
  2. package/lib/choices/choice.js +98 -203
  3. package/lib/choices/choice.js.map +1 -1
  4. package/lib/choices/index.js +21 -53
  5. package/lib/choices/index.js.map +1 -1
  6. package/lib/componentize.js +1 -5
  7. package/lib/componentize.js.map +1 -1
  8. package/lib/components/blank.js +303 -361
  9. package/lib/components/blank.js.map +1 -1
  10. package/lib/components/correct-input.js +41 -65
  11. package/lib/components/correct-input.js.map +1 -1
  12. package/lib/components/dropdown.js +218 -257
  13. package/lib/components/dropdown.js.map +1 -1
  14. package/lib/components/input.js +10 -17
  15. package/lib/components/input.js.map +1 -1
  16. package/lib/constructed-response.js +40 -54
  17. package/lib/constructed-response.js.map +1 -1
  18. package/lib/customizable.js +5 -9
  19. package/lib/customizable.js.map +1 -1
  20. package/lib/drag-in-the-blank.js +140 -105
  21. package/lib/drag-in-the-blank.js.map +1 -1
  22. package/lib/index.js +0 -7
  23. package/lib/index.js.map +1 -1
  24. package/lib/inline-dropdown.js +4 -12
  25. package/lib/inline-dropdown.js.map +1 -1
  26. package/lib/mask.js +60 -118
  27. package/lib/mask.js.map +1 -1
  28. package/lib/serialization.js +8 -48
  29. package/lib/serialization.js.map +1 -1
  30. package/lib/with-mask.js +30 -58
  31. package/lib/with-mask.js.map +1 -1
  32. package/package.json +12 -20
  33. package/src/__tests__/drag-in-the-blank.test.js +66 -26
  34. package/src/__tests__/mask.test.js +147 -112
  35. package/src/__tests__/with-mask.test.js +44 -19
  36. package/src/choices/__tests__/index.test.js +38 -25
  37. package/src/choices/choice.jsx +86 -153
  38. package/src/choices/index.jsx +9 -3
  39. package/src/components/__tests__/blank.test.js +92 -156
  40. package/src/components/__tests__/correct-input.test.js +60 -19
  41. package/src/components/__tests__/dropdown.test.js +61 -19
  42. package/src/components/__tests__/input.test.js +72 -20
  43. package/src/components/blank.jsx +273 -272
  44. package/src/components/correct-input.jsx +33 -39
  45. package/src/components/dropdown.jsx +173 -161
  46. package/src/constructed-response.jsx +25 -30
  47. package/src/drag-in-the-blank.jsx +131 -42
  48. package/src/mask.jsx +38 -29
  49. package/src/with-mask.jsx +7 -4
  50. package/esm/index.css +0 -847
  51. package/esm/index.js +0 -195939
  52. package/esm/index.js.map +0 -1
  53. package/esm/package.json +0 -3
  54. package/src/__tests__/__snapshots__/drag-in-the-blank.test.js.snap +0 -316
  55. package/src/__tests__/__snapshots__/mask.test.js.snap +0 -55
  56. package/src/__tests__/__snapshots__/with-mask.test.js.snap +0 -62
  57. package/src/choices/__tests__/__snapshots__/index.test.js.snap +0 -209
  58. package/src/components/__tests__/__snapshots__/blank.test.js.snap +0 -111
  59. package/src/components/__tests__/__snapshots__/correct-input.test.js.snap +0 -64
  60. package/src/components/__tests__/__snapshots__/dropdown.test.js.snap +0 -136
  61. package/src/components/__tests__/__snapshots__/input.test.js.snap +0 -34
@@ -1,13 +1,15 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
- import { renderMath } from '@pie-lib/math-rendering';
3
+ import { DragProvider } from '@pie-lib/drag';
4
+ import { DragOverlay, closestCenter } from '@dnd-kit/core';
5
+
4
6
  import Choices from './choices';
7
+ import Choice from './choices/choice';
5
8
  import Blank from './components/blank';
6
9
  import { withMask } from './with-mask';
7
10
 
8
- // eslint-disable-next-line react/display-name
9
11
  const Masked = withMask('blank', (props) => (node, data, onChange) => {
10
- const dataset = node.data ? node.data.dataset || {} : {};
12
+ const dataset = node.data?.dataset || {};
11
13
  if (dataset.component === 'blank') {
12
14
  // eslint-disable-next-line react/prop-types
13
15
  const {
@@ -18,6 +20,8 @@ const Masked = withMask('blank', (props) => (node, data, onChange) => {
18
20
  showCorrectAnswer,
19
21
  emptyResponseAreaWidth,
20
22
  emptyResponseAreaHeight,
23
+ instanceId,
24
+ isDragging
21
25
  } = props;
22
26
  const choiceId = showCorrectAnswer ? correctResponse[dataset.id] : data[dataset.id];
23
27
  // eslint-disable-next-line react/prop-types
@@ -33,18 +37,35 @@ const Masked = withMask('blank', (props) => (node, data, onChange) => {
33
37
  id={dataset.id}
34
38
  emptyResponseAreaWidth={emptyResponseAreaWidth}
35
39
  emptyResponseAreaHeight={emptyResponseAreaHeight}
36
- onChange={onChange}
40
+ onChange={(id, choiceId) => {
41
+ const newData = { ...data };
42
+ if (choiceId === undefined) {
43
+ delete newData[id];
44
+ } else {
45
+ newData[id] = choiceId;
46
+ }
47
+ onChange(newData);
48
+ }}
49
+ instanceId={instanceId}
50
+ isDragging={isDragging}
37
51
  />
38
52
  );
39
53
  }
40
54
  });
41
55
 
42
56
  export default class DragInTheBlank extends React.Component {
57
+ constructor(props) {
58
+ super(props);
59
+ this.state = {
60
+ activeDragItem: null,
61
+ };
62
+ }
63
+
43
64
  static propTypes = {
44
65
  markup: PropTypes.string,
45
66
  layout: PropTypes.object,
46
67
  choicesPosition: PropTypes.string,
47
- choices: PropTypes.arrayOf(PropTypes.shape({ label: PropTypes.string, value: PropTypes.string })),
68
+ choices: PropTypes.array,
48
69
  value: PropTypes.object,
49
70
  onChange: PropTypes.func,
50
71
  duplicates: PropTypes.bool,
@@ -54,17 +75,76 @@ export default class DragInTheBlank extends React.Component {
54
75
  showCorrectAnswer: PropTypes.bool,
55
76
  emptyResponseAreaWidth: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
56
77
  emptyResponseAreaHeight: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
78
+ instanceId: PropTypes.string,
79
+ };
80
+
81
+ static defaultProps = {
82
+ instanceId: 'drag-in-the-blank',
83
+ };
84
+
85
+ handleDragStart = (event) => {
86
+ const { active } = event;
87
+
88
+ if (active?.data?.current) {
89
+ this.setState({
90
+ activeDragItem: active.data.current,
91
+ });
92
+ }
57
93
  };
58
94
 
59
- UNSAFE_componentWillReceiveProps() {
60
- if (this.rootRef) {
61
- renderMath(this.rootRef);
95
+ renderDragOverlay = () => {
96
+ const { activeDragItem } = this.state;
97
+ if (!activeDragItem) return null;
98
+
99
+ if (activeDragItem.type === 'MaskBlank') {
100
+ return (
101
+ <Choice
102
+ disabled={activeDragItem.disabled}
103
+ choice={activeDragItem.choice}
104
+ instanceId={activeDragItem.instanceId}
105
+ />
106
+ );
62
107
  }
63
- }
64
108
 
65
- componentDidUpdate() {
66
- renderMath(this.rootRef);
67
- }
109
+ return null;
110
+ };
111
+
112
+ handleDragEnd = (event) => {
113
+ const { active, over } = event;
114
+ const { onChange, value } = this.props;
115
+
116
+ if (!over || !active || !onChange) {
117
+ return;
118
+ }
119
+
120
+ const draggedData = active.data.current;
121
+ const dropData = over.data.current;
122
+
123
+ if (draggedData?.type === 'MaskBlank' && dropData?.accepts?.includes('MaskBlank')) {
124
+ const draggedItem = draggedData;
125
+ const targetId = dropData.id;
126
+
127
+ // drop from choice to blank (placing choice into response)
128
+ if (draggedItem.fromChoice === true) {
129
+ const newValue = { ...value };
130
+ newValue[targetId] = draggedItem.choice.id;
131
+ onChange(newValue);
132
+ } else if (dropData.toChoiceBoard === true) {
133
+ // handle drop from blank to choice board (removal from blank)
134
+ const newValue = { ...value };
135
+ delete newValue[draggedItem.id];
136
+ onChange(newValue);
137
+ }
138
+ // handle drop from blank to blank (changing position)
139
+ else if (draggedItem.id !== targetId) {
140
+ const newValue = { ...value };
141
+ newValue[targetId] = draggedItem.choice.id;
142
+ delete newValue[draggedItem.id];
143
+ onChange(newValue);
144
+ }
145
+ }
146
+ this.setState({ activeDragItem: null });
147
+ };
68
148
 
69
149
  getPositionDirection = (choicePosition) => {
70
150
  let flexDirection;
@@ -97,7 +177,6 @@ export default class DragInTheBlank extends React.Component {
97
177
  const {
98
178
  markup,
99
179
  duplicates,
100
- layout,
101
180
  value,
102
181
  onChange,
103
182
  choicesPosition,
@@ -108,40 +187,50 @@ export default class DragInTheBlank extends React.Component {
108
187
  showCorrectAnswer,
109
188
  emptyResponseAreaWidth,
110
189
  emptyResponseAreaHeight,
190
+ layout,
191
+ instanceId
111
192
  } = this.props;
112
193
 
113
194
  const choicePosition = choicesPosition || 'below';
114
- const style = {
115
- display: 'flex',
116
- minWidth: '100px',
117
- ...this.getPositionDirection(choicePosition),
118
- };
195
+ const style = { display: 'flex', minWidth: '100px', ...this.getPositionDirection(choicePosition) };
119
196
 
120
197
  return (
121
- <div ref={(ref) => ref && (this.rootRef = ref)} style={style}>
122
- <Choices
123
- choicePosition={choicePosition}
124
- duplicates={duplicates}
125
- choices={choices}
126
- value={value}
127
- disabled={disabled}
128
- />
129
- <Masked
130
- elementType={'drag-in-the-blank'}
131
- markup={markup}
132
- layout={layout}
133
- value={value}
134
- choices={choices}
135
- onChange={onChange}
136
- disabled={disabled}
137
- duplicates={duplicates}
138
- feedback={feedback}
139
- correctResponse={correctResponse}
140
- showCorrectAnswer={showCorrectAnswer}
141
- emptyResponseAreaWidth={emptyResponseAreaWidth}
142
- emptyResponseAreaHeight={emptyResponseAreaHeight}
143
- />
144
- </div>
198
+ <DragProvider
199
+ onDragStart={this.handleDragStart}
200
+ onDragEnd={this.handleDragEnd}
201
+ collisionDetection={closestCenter}
202
+ >
203
+ <div ref={(ref) => (this.rootRef = ref)} style={style}>
204
+ <Choices
205
+ choicePosition={choicePosition}
206
+ choices={choices}
207
+ value={value}
208
+ duplicates={duplicates}
209
+ disabled={disabled}
210
+ instanceId={instanceId}
211
+ />
212
+ <Masked
213
+ elementType="drag-in-the-blank"
214
+ markup={markup}
215
+ layout={layout}
216
+ value={value}
217
+ choices={choices}
218
+ onChange={onChange}
219
+ disabled={disabled}
220
+ duplicates={duplicates}
221
+ feedback={feedback}
222
+ correctResponse={correctResponse}
223
+ showCorrectAnswer={showCorrectAnswer}
224
+ emptyResponseAreaWidth={emptyResponseAreaWidth}
225
+ emptyResponseAreaHeight={emptyResponseAreaHeight}
226
+ instanceId={instanceId}
227
+ isDragging={!!this.state.activeDragItem}
228
+ />
229
+ <DragOverlay style={{ pointerEvents: "none" }}>
230
+ {this.renderDragOverlay()}
231
+ </DragOverlay>
232
+ </div>
233
+ </DragProvider>
145
234
  );
146
235
  }
147
236
  }
package/src/mask.jsx CHANGED
@@ -1,23 +1,19 @@
1
1
  import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import get from 'lodash/get';
4
- import { withStyles } from '@material-ui/core/styles';
4
+ import { styled } from '@mui/material/styles';
5
+ import { renderMath } from '@pie-lib/math-rendering';
5
6
  import { MARK_TAGS } from './serialization';
6
- import cx from 'classnames';
7
7
 
8
- const Paragraph = withStyles((theme) => ({
9
- para: {
10
- paddingTop: 2 * theme.spacing.unit,
11
- paddingBottom: 2 * theme.spacing.unit,
12
- },
13
- }))((props) => <div className={props.classes.para}>{props.children}</div>);
8
+ const Paragraph = styled('div')(({ theme }) => ({
9
+ paddingTop: theme.spacing(2),
10
+ paddingBottom: theme.spacing(2),
11
+ }));
14
12
 
15
- const Spacer = withStyles(() => ({
16
- spacer: {
17
- display: 'inline-block',
18
- width: '.75em',
19
- },
20
- }))((props) => <span className={props.classes.spacer} />);
13
+ const Spacer = styled('span')(() => ({
14
+ display: 'inline-block',
15
+ width: '.75em',
16
+ }));
21
17
 
22
18
  const restrictWhitespaceTypes = ['tbody', 'tr'];
23
19
 
@@ -120,43 +116,56 @@ export const renderChildren = (layout, value, onChange, rootRenderChildren, pare
120
116
  return children;
121
117
  };
122
118
 
123
- const MaskContainer = withStyles(() => ({
124
- main: {
125
- display: 'initial',
119
+ const MaskContainer = styled('div')(() => ({
120
+ display: 'initial',
121
+ '&:not(.MathJax) table': {
122
+ borderCollapse: 'collapse',
126
123
  },
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
- },
124
+ // align table content to left as per STAR requirement PD-3687
125
+ '&:not(.MathJax) table td, &:not(.MathJax) table th': {
126
+ padding: '8px 12px',
127
+ textAlign: 'left',
136
128
  },
137
- }))((props) => <div className={cx(props.classes.main, props.classes.tableStyle)}>{props.children}</div>);
129
+ }));
138
130
 
139
131
  /**
140
132
  * Renders a layout that uses the slate.js Value model structure.
141
133
  */
142
134
  export default class Mask extends React.Component {
135
+ constructor(props) {
136
+ super(props);
137
+ this.internalContainerRef = React.createRef();
138
+ }
139
+
143
140
  static propTypes = {
144
141
  renderChildren: PropTypes.func,
145
142
  layout: PropTypes.object,
146
143
  value: PropTypes.object,
147
144
  onChange: PropTypes.func,
148
145
  elementType: PropTypes.string,
146
+ containerRef: PropTypes.oneOfType([
147
+ PropTypes.func,
148
+ PropTypes.shape({ current: PropTypes.instanceOf(Element) }),
149
+ ]),
149
150
  };
150
151
 
152
+ componentDidMount() {
153
+ const containerRef = this.props.containerRef || this.internalContainerRef;
154
+ if (containerRef.current && typeof renderMath === 'function') {
155
+ renderMath(containerRef.current);
156
+ }
157
+ }
158
+
151
159
  handleChange = (id, value) => {
152
160
  const data = { ...this.props.value, [id]: value };
153
161
  this.props.onChange(data);
154
162
  };
155
163
 
156
164
  render() {
157
- const { value, layout, elementType } = this.props;
165
+ const { value, layout, elementType, containerRef } = this.props;
158
166
  const children = renderChildren(layout, value, this.handleChange, this.props.renderChildren, null, elementType);
167
+ const ref = containerRef || this.internalContainerRef;
159
168
 
160
- return <MaskContainer>{children}</MaskContainer>;
169
+ return <MaskContainer ref={ref}>{children}</MaskContainer>;
161
170
  }
162
171
  }
package/src/with-mask.jsx CHANGED
@@ -1,5 +1,4 @@
1
1
  import React from 'react';
2
- import ReactDOM from 'react-dom';
3
2
  import PropTypes from 'prop-types';
4
3
  import Mask from './mask';
5
4
  import componentize from './componentize';
@@ -28,11 +27,14 @@ export const withMask = (type, renderChildren) => {
28
27
  elementType: PropTypes.string,
29
28
  };
30
29
 
30
+ constructor(props) {
31
+ super(props);
32
+ this.containerRef = React.createRef();
33
+ }
34
+
31
35
  componentDidUpdate(prevProps) {
32
36
  if (this.props.markup !== prevProps.markup) {
33
- // eslint-disable-next-line
34
- const domNode = ReactDOM.findDOMNode(this);
35
- // Query all elements that may contain outdated MathJax renderings
37
+ const domNode = this.containerRef.current;
36
38
  const mathElements = domNode && domNode.querySelectorAll('[data-latex][data-math-handled="true"]');
37
39
 
38
40
  // Clean up for fresh MathJax processing
@@ -60,6 +62,7 @@ export const withMask = (type, renderChildren) => {
60
62
  const maskLayout = layout ? layout : buildLayoutFromMarkup(markup, type);
61
63
  return (
62
64
  <Mask
65
+ containerRef={this.containerRef}
63
66
  elementType={elementType}
64
67
  layout={maskLayout}
65
68
  value={value}