@pie-lib/mask-markup 2.0.0-beta.2 → 2.0.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/CHANGELOG.json +1 -871
  2. package/CHANGELOG.md +296 -2
  3. package/LICENSE.md +5 -0
  4. package/NEXT.CHANGELOG.json +1 -0
  5. package/lib/choices/choice.js +99 -118
  6. package/lib/choices/choice.js.map +1 -1
  7. package/lib/choices/index.js +23 -19
  8. package/lib/choices/index.js.map +1 -1
  9. package/lib/componentize.js +1 -2
  10. package/lib/componentize.js.map +1 -1
  11. package/lib/components/blank.js +315 -221
  12. package/lib/components/blank.js.map +1 -1
  13. package/lib/components/correct-input.js +39 -42
  14. package/lib/components/correct-input.js.map +1 -1
  15. package/lib/components/dropdown.js +393 -124
  16. package/lib/components/dropdown.js.map +1 -1
  17. package/lib/components/input.js +1 -2
  18. package/lib/components/input.js.map +1 -1
  19. package/lib/constructed-response.js +82 -26
  20. package/lib/constructed-response.js.map +1 -1
  21. package/lib/customizable.js +44 -0
  22. package/lib/customizable.js.map +1 -0
  23. package/lib/drag-in-the-blank.js +154 -61
  24. package/lib/drag-in-the-blank.js.map +1 -1
  25. package/lib/index.js +7 -0
  26. package/lib/index.js.map +1 -1
  27. package/lib/inline-dropdown.js +4 -3
  28. package/lib/inline-dropdown.js.map +1 -1
  29. package/lib/mask.js +89 -56
  30. package/lib/mask.js.map +1 -1
  31. package/lib/serialization.js +30 -42
  32. package/lib/serialization.js.map +1 -1
  33. package/lib/with-mask.js +48 -20
  34. package/lib/with-mask.js.map +1 -1
  35. package/package.json +26 -15
  36. package/src/__tests__/drag-in-the-blank.test.js +111 -0
  37. package/src/__tests__/index.test.js +39 -0
  38. package/src/__tests__/mask.test.js +187 -0
  39. package/src/__tests__/serialization.test.js +54 -0
  40. package/src/__tests__/utils.js +1 -0
  41. package/src/__tests__/with-mask.test.js +76 -0
  42. package/src/choices/__tests__/index.test.js +75 -0
  43. package/src/choices/choice.jsx +83 -96
  44. package/src/choices/index.jsx +11 -5
  45. package/src/components/__tests__/blank.test.js +138 -0
  46. package/src/components/__tests__/correct-input.test.js +90 -0
  47. package/src/components/__tests__/dropdown.test.js +93 -0
  48. package/src/components/__tests__/input.test.js +102 -0
  49. package/src/components/blank.jsx +316 -204
  50. package/src/components/correct-input.jsx +37 -38
  51. package/src/components/dropdown.jsx +371 -125
  52. package/src/constructed-response.jsx +80 -18
  53. package/src/customizable.jsx +35 -0
  54. package/src/drag-in-the-blank.jsx +152 -40
  55. package/src/index.js +10 -1
  56. package/src/inline-dropdown.jsx +2 -0
  57. package/src/mask.jsx +71 -25
  58. package/src/serialization.js +22 -34
  59. package/src/with-mask.jsx +43 -3
  60. package/README.md +0 -14
  61. package/lib/new-serialization.js +0 -267
  62. package/lib/new-serialization.js.map +0 -1
  63. package/lib/parse-html.js +0 -17
  64. package/lib/parse-html.js.map +0 -1
  65. package/lib/test-serializer.js +0 -164
  66. package/lib/test-serializer.js.map +0 -1
  67. package/src/new-serialization.jsx +0 -291
  68. package/src/parse-html.js +0 -8
  69. package/src/test-serializer.js +0 -163
@@ -1,249 +1,361 @@
1
- import React from 'react';
2
- import ReactDOM from 'react-dom';
1
+ import React, { useRef, useState, useEffect } from 'react';
3
2
  import PropTypes from 'prop-types';
4
3
  import { renderMath } from '@pie-lib/math-rendering';
5
4
  import debug from 'debug';
6
- import { DragSource, DropTarget } from '@pie-lib/drag';
7
- import { withStyles } from '@material-ui/core/styles';
8
- import Chip from '@material-ui/core/Chip';
5
+ import { useDraggable, useDroppable } from '@dnd-kit/core';
6
+ import { CSS } from '@dnd-kit/utilities';
7
+ import { styled } from '@mui/material/styles';
8
+ import Chip from '@mui/material/Chip';
9
9
  import classnames from 'classnames';
10
10
  import { color } from '@pie-lib/render-ui';
11
+ import { grey } from '@mui/material/colors';
12
+
11
13
  const log = debug('pie-lib:mask-markup:blank');
12
- export const DRAG_TYPE = 'MaskBlank';
13
14
 
14
- const useStyles = withStyles(() => ({
15
- content: {
16
- border: `solid 0px ${color.primary()}`,
17
- minWidth: '200px',
18
- touchAction: 'none',
15
+ const StyledContent = styled('span')(({ dragged, over }) => ({
16
+ border: `solid 0px ${color.primary()}`,
17
+ minWidth: '200px',
18
+ touchAction: 'none',
19
+ overflow: 'hidden',
20
+ whiteSpace: 'nowrap',
21
+ opacity: 1,
22
+ ...(over && {
23
+ whiteSpace: 'nowrap',
24
+ overflow: 'hidden',
25
+ }),
26
+ ...(dragged && {
27
+ opacity: 0.5,
28
+ }),
29
+ }));
30
+
31
+ const StyledChip = styled(Chip)(() => ({
32
+ backgroundColor: color.background(),
33
+ border: `2px dashed ${color.text()}`,
34
+ color: color.text(),
35
+ fontSize: 'inherit',
36
+ maxWidth: '374px',
37
+ position: 'relative',
38
+ borderRadius: '3px',
39
+ '&.over': {
40
+ whiteSpace: 'nowrap',
19
41
  overflow: 'hidden',
20
42
  },
21
- chip: {
22
- backgroundColor: color.background(),
23
- border: `1px solid ${color.text()}`,
24
- color: color.text(),
25
- minWidth: '90px',
26
- fontSize: 'inherit',
27
- minHeight: '32px',
28
- height: 'auto',
29
- maxWidth: '374px',
30
- position: 'relative',
43
+ '&.parentOver': {
44
+ border: `1px solid ${grey[500]}`,
45
+ backgroundColor: `${grey[300]}`,
31
46
  },
32
- chipLabel: {
33
- whiteSpace: 'pre-wrap',
34
- // Added for touch devices, for image content.
35
- // This will prevent the context menu from appearing and not allowing other interactions with the image.
36
- // If interactions with the image in the token will be requested we should handle only the context Menu.
37
- pointerEvents: 'none',
38
- '& img': {
39
- display: 'block',
40
- padding: '2px 0',
41
- },
47
+ '&.correct': {
48
+ border: `solid 1px ${color.correct()}`,
42
49
  },
43
- hidden: {
44
- color: 'transparent',
45
- opacity: 0,
50
+ '&.incorrect': {
51
+ border: `solid 1px ${color.incorrect()}`,
46
52
  },
47
- dragged: {
48
- position: 'absolute',
49
- left: 16,
50
- maxWidth: '60px',
53
+ '&.Mui-disabled': {
54
+ opacity: 1,
51
55
  },
52
- correct: {
53
- border: `solid 1px ${color.correct()}`,
56
+ }));
57
+
58
+ const StyledChipLabel = styled('span')(() => ({
59
+ whiteSpace: 'normal',
60
+ // Added for touch devices, for image content.
61
+ // This will prevent the context menu from appearing and not allowing other interactions with the image.
62
+ // If interactions with the image in the token will be requested we should handle only the context Menu.
63
+ pointerEvents: 'none',
64
+ '& img': {
65
+ display: 'block',
66
+ padding: '2px 0',
54
67
  },
55
- incorrect: {
56
- border: `solid 1px ${color.incorrect()}`,
68
+ // Remove default <p> margins to ensure consistent spacing across all wrapped content (p, span, div, math)
69
+ // Padding for top and bottom will instead be controlled by the container for consistent layout
70
+ // Ensures consistent behavior with pie-api-browser, where marginTop is already removed by a Bootstrap stylesheet
71
+ '& p': {
72
+ marginTop: '0',
73
+ marginBottom: '0',
74
+ },
75
+ '& mjx-frac': {
76
+ fontSize: '120% !important',
57
77
  },
58
- over: {
78
+ '&.over': {
59
79
  whiteSpace: 'nowrap',
60
80
  overflow: 'hidden',
61
81
  },
82
+ '&.hidden': {
83
+ color: 'transparent',
84
+ opacity: 0,
85
+ },
86
+ '&.dragged': {
87
+ position: 'absolute',
88
+ left: 16,
89
+ maxWidth: '60px',
90
+ },
62
91
  }));
63
92
 
64
- export class BlankContent extends React.Component {
65
- static propTypes = {
66
- id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
67
- disabled: PropTypes.bool,
68
- duplicates: PropTypes.bool,
69
- choice: PropTypes.object,
70
- classes: PropTypes.object,
71
- isOver: PropTypes.bool,
72
- dragItem: PropTypes.object,
73
- correct: PropTypes.bool,
74
- onChange: PropTypes.func,
93
+ function BlankContent({
94
+ disabled,
95
+ choice,
96
+ isOver,
97
+ isDragging,
98
+ dragItem,
99
+ correct,
100
+ emptyResponseAreaWidth,
101
+ emptyResponseAreaHeight,
102
+ }) {
103
+ const rootRef = useRef(null);
104
+ const spanRef = useRef(null);
105
+ const frozenRef = useRef(null); // to use during dragging to prevent flickering
106
+ const [dimensions, setDimensions] = useState({ height: 0, width: 0 });
107
+
108
+ const handleImageLoad = () => {
109
+ updateDimensions();
75
110
  };
76
111
 
77
- constructor() {
78
- super();
79
- this.state = {
80
- height: 0,
81
- };
82
- }
83
-
84
- componentDidUpdate(prevProps) {
85
- renderMath(this.rootRef);
86
- const { choice: currentChoice } = this.props;
87
- const { choice: prevChoice } = prevProps;
88
-
89
- if (JSON.stringify(currentChoice) !== JSON.stringify(prevChoice)) {
90
- if (!currentChoice) {
91
- this.setState({
92
- height: 0,
93
- });
94
- return;
95
- }
112
+ const handleElements = () => {
113
+ const imageElement = spanRef.current?.querySelector('img');
114
+ if (imageElement) {
115
+ imageElement.onload = handleImageLoad;
116
+ } else {
96
117
  setTimeout(() => {
97
- this.setState({
98
- height: this.spanRef.offsetHeight,
99
- });
118
+ updateDimensions();
100
119
  }, 300);
101
120
  }
102
- }
121
+ };
103
122
 
104
- addDraggableFalseAttributes(parent) {
105
- parent.childNodes.forEach((elem) => {
106
- if (elem instanceof Element || elem instanceof HTMLDocument) {
107
- elem.setAttribute('draggable', false);
108
- }
109
- });
110
- }
111
-
112
- render() {
113
- const { disabled, choice, classes, isOver, dragItem, correct } = this.props;
114
- const draggedLabel = dragItem && isOver && dragItem.choice.value;
115
- const label = choice && choice.value;
116
-
117
- return (
118
- // TODO the Chip element is causing drag problems on touch devices. Avoid using Chip and consider refactoring the code. Keep in mind that Chip is a span with a button role, which interferes with seamless touch device dragging.
119
- <Chip
120
- clickable={false}
121
- disabled={true}
122
- ref={(ref) => {
123
- //eslint-disable-next-line
124
- this.rootRef = ReactDOM.findDOMNode(ref);
125
- }}
126
- component="span"
127
- label={
128
- <React.Fragment>
129
- <span
130
- className={classnames(classes.chipLabel, isOver && classes.over, {
131
- [classes.hidden]: draggedLabel,
132
- })}
133
- ref={(ref) => {
134
- if (ref) {
135
- //eslint-disable-next-line
136
- this.spanRef = ReactDOM.findDOMNode(ref);
137
- ref.innerHTML = label || '';
138
- this.addDraggableFalseAttributes(ref);
139
- }
140
- }}
141
- >
142
- {' '}
143
- </span>
144
- {draggedLabel && (
145
- <span
146
- className={classnames(classes.chipLabel, isOver && classes.over, classes.dragged)}
147
- ref={(ref) => {
148
- if (ref) {
149
- //eslint-disable-next-line
150
- this.spanRef = ReactDOM.findDOMNode(ref);
151
- ref.innerHTML = draggedLabel || '';
152
- this.addDraggableFalseAttributes(ref);
153
- }
154
- }}
155
- >
156
- {' '}
157
- </span>
158
- )}
159
- </React.Fragment>
160
- }
161
- className={classnames(classes.chip, isOver && classes.over, {
162
- [classes.correct]: correct !== undefined && correct,
163
- [classes.incorrect]: correct !== undefined && !correct,
164
- })}
165
- variant={disabled ? 'outlined' : undefined}
166
- style={{
167
- ...(this.state.height ? { height: this.state.height } : {}),
168
- }}
169
- classes={{
170
- label: isOver && classes.over,
171
- }}
172
- />
173
- );
174
- }
175
- }
123
+ const updateDimensions = () => {
124
+ if (spanRef.current && rootRef.current) {
125
+ // Temporarily set rootRef width to 'auto' for natural measurement
126
+ rootRef.current.style.width = 'auto';
176
127
 
177
- const StyledBlankContent = useStyles(BlankContent);
128
+ // Get the natural dimensions of the content
129
+ const width = spanRef.current.offsetWidth || 0;
130
+ const height = spanRef.current.offsetHeight || 0;
178
131
 
179
- const connectedBlankContent = useStyles(({ connectDragSource, connectDropTarget, ...props }) => {
180
- const { classes, isOver } = props;
132
+ const widthWithPadding = width + 24; // 12px padding on each side
133
+ const heightWithPadding = height + 24; // 12px padding on top and bottom
181
134
 
182
- return connectDropTarget(
183
- connectDragSource(
184
- <span className={classnames(classes.content, isOver && classes.over)}>
185
- <StyledBlankContent {...props} />
186
- </span>,
187
- ),
188
- );
189
- });
135
+ const responseAreaWidth = parseFloat(emptyResponseAreaWidth) || 0;
136
+ const responseAreaHeight = parseFloat(emptyResponseAreaHeight) || 0;
190
137
 
191
- const tileTarget = {
192
- drop(props, monitor) {
193
- const draggedItem = monitor.getItem();
138
+ const adjustedWidth = widthWithPadding <= responseAreaWidth ? responseAreaWidth : widthWithPadding;
139
+ const adjustedHeight = heightWithPadding <= responseAreaHeight ? responseAreaHeight : heightWithPadding;
194
140
 
195
- log('props.instanceId', props.instanceId, 'draggedItem.instanceId:', draggedItem.instanceId);
141
+ setDimensions((prevState) => ({
142
+ width: adjustedWidth > responseAreaWidth ? adjustedWidth : prevState.width,
143
+ height: adjustedHeight > responseAreaHeight ? adjustedHeight : prevState.height,
144
+ }));
196
145
 
197
- if (draggedItem.id !== props.id) {
198
- props.onChange(props.id, draggedItem.choice.id);
146
+ rootRef.current.style.width = `${adjustedWidth}px`;
147
+ rootRef.current.style.height = `${adjustedHeight}px`;
199
148
  }
149
+ };
150
+
151
+ const getRootDimensions = () => {
152
+ // Handle potential non-numeric values
153
+ const responseAreaWidth = !isNaN(parseFloat(emptyResponseAreaWidth)) ? parseFloat(emptyResponseAreaWidth) : 0;
154
+ const responseAreaHeight = !isNaN(parseFloat(emptyResponseAreaHeight)) ? parseFloat(emptyResponseAreaHeight) : 0;
200
155
 
156
+ const rootStyle = {
157
+ height: dimensions.height || responseAreaHeight,
158
+ width: dimensions.width || responseAreaWidth,
159
+ };
160
+
161
+ // add minWidth, minHeight if width and height are not defined
201
162
  return {
202
- dropped: draggedItem.id !== props.id,
163
+ ...rootStyle,
164
+ ...(responseAreaWidth ? {} : { minWidth: 90 }),
165
+ ...(responseAreaHeight ? {} : { minHeight: 32 }),
203
166
  };
204
- },
205
- canDrop(props, monitor) {
206
- const draggedItem = monitor.getItem();
167
+ };
207
168
 
208
- return draggedItem.instanceId === props.instanceId;
209
- },
210
- };
169
+ useEffect(() => {
170
+ handleElements();
171
+ }, []);
211
172
 
212
- const DropTile = DropTarget(DRAG_TYPE, tileTarget, (connect, monitor) => ({
213
- connectDropTarget: connect.dropTarget(),
214
- isOver: monitor.isOver(),
215
- dragItem: monitor.getItem(),
216
- }))(connectedBlankContent);
173
+ // Render math for the placeholder/preview when dragging over
174
+ useEffect(() => {
175
+ if (rootRef.current) {
176
+ renderMath(rootRef.current);
177
+ }
178
+ }, [isOver, dragItem?.choice?.value]);
217
179
 
218
- const tileSource = {
219
- canDrag(props) {
220
- return !props.disabled && !!props.choice;
221
- },
222
- beginDrag(props) {
223
- return {
224
- id: props.id,
225
- choice: props.choice,
226
- instanceId: props.instanceId,
227
- fromChoice: true,
228
- };
229
- },
230
- endDrag(props, monitor) {
231
- // this will be null if it did not drop
232
- const dropResult = monitor.getDropResult();
180
+ useEffect(() => {
181
+ if (!choice) {
182
+ setDimensions({ height: 0, width: 0 });
183
+ return;
184
+ }
185
+ handleElements();
186
+ }, [choice]);
233
187
 
234
- if (!dropResult || (dropResult.dropped && !props.duplicates)) {
235
- const draggedItem = monitor.getItem();
188
+ useEffect(() => {
189
+ if (!isOver && !isDragging) {
190
+ frozenRef.current = {
191
+ width: rootRef.current.offsetWidth,
192
+ height: rootRef.current.offsetHeight,
193
+ };
194
+ }
195
+ }, [choice, isOver, isDragging]);
196
+
197
+ const draggedLabel = dragItem && isOver && dragItem.choice && dragItem.choice.value;
198
+ const label = choice && choice.value;
199
+ const style =
200
+ isOver || isDragging
201
+ ? {
202
+ width: frozenRef.current?.width,
203
+ height: frozenRef.current?.height,
204
+ }
205
+ : getRootDimensions();
236
206
 
237
- if (draggedItem.fromChoice) {
238
- props.onChange(props.id, undefined);
207
+ return (
208
+ <StyledChip
209
+ clickable={false}
210
+ disabled={disabled}
211
+ ref={rootRef}
212
+ component="span"
213
+ label={
214
+ <React.Fragment>
215
+ <StyledChipLabel
216
+ ref={spanRef}
217
+ draggable={true}
218
+ className={classnames({
219
+ over: isOver,
220
+ hidden: draggedLabel,
221
+ })}
222
+ dangerouslySetInnerHTML={{ __html: label || '' }}
223
+ />
224
+ {draggedLabel && (
225
+ <StyledChipLabel
226
+ draggable={true}
227
+ className={classnames({
228
+ over: isOver,
229
+ dragged: true,
230
+ })}
231
+ dangerouslySetInnerHTML={{ __html: draggedLabel || '' }}
232
+ />
233
+ )}
234
+ </React.Fragment>
239
235
  }
240
- }
241
- },
236
+ className={classnames({
237
+ over: isOver,
238
+ parentOver: isOver,
239
+ correct: correct !== undefined && correct,
240
+ incorrect: correct !== undefined && !correct,
241
+ })}
242
+ variant={disabled ? 'outlined' : undefined}
243
+ style={style}
244
+ />
245
+ );
246
+ }
247
+
248
+ BlankContent.defaultProps = {
249
+ emptyResponseAreaWidth: 0,
250
+ emptyResponseAreaHeight: 0,
242
251
  };
243
252
 
244
- const DragDropTile = DragSource(DRAG_TYPE, tileSource, (connect, monitor) => ({
245
- connectDragSource: connect.dragSource(),
246
- isDragging: monitor.isDragging(),
247
- }))(DropTile);
253
+ BlankContent.propTypes = {
254
+ id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
255
+ disabled: PropTypes.bool,
256
+ duplicates: PropTypes.bool,
257
+ choice: PropTypes.object,
258
+ isOver: PropTypes.bool,
259
+ dragItem: PropTypes.object,
260
+ correct: PropTypes.bool,
261
+ onChange: PropTypes.func,
262
+ emptyResponseAreaWidth: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
263
+ emptyResponseAreaHeight: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
264
+ instanceId: PropTypes.string,
265
+ };
266
+
267
+ // New functional component using @dnd-kit hooks
268
+ function DragDropBlank({
269
+ id,
270
+ disabled,
271
+ duplicates,
272
+ choice,
273
+ correct,
274
+ onChange,
275
+ emptyResponseAreaWidth,
276
+ emptyResponseAreaHeight,
277
+ instanceId,
278
+ }) {
279
+ // Setup draggable functionality
280
+ const {
281
+ attributes: dragAttributes,
282
+ listeners: dragListeners,
283
+ setNodeRef: setDragNodeRef,
284
+ transform,
285
+ isDragging,
286
+ } = useDraggable({
287
+ id: `mask-blank-drag-${id}`,
288
+ disabled: disabled || !choice,
289
+ data: {
290
+ id: id,
291
+ choice: choice,
292
+ instanceId: instanceId,
293
+ fromChoice: false, // This is from a blank, not from choices
294
+ type: 'MaskBlank',
295
+ },
296
+ });
297
+
298
+ // Setup droppable functionality
299
+ const { setNodeRef: setDropNodeRef, isOver, active: dragItem } = useDroppable({
300
+ id: `mask-blank-drop-${id}`,
301
+ data: {
302
+ id: id,
303
+ accepts: ['MaskBlank'],
304
+ instanceId: instanceId,
305
+ },
306
+ });
307
+
308
+ // Combine refs for both drag and drop
309
+ const setNodeRef = (node) => {
310
+ setDragNodeRef(node);
311
+ setDropNodeRef(node);
312
+ };
313
+
314
+ const style = {
315
+ transform: CSS.Translate.toString(transform),
316
+ };
317
+
318
+ return (
319
+ <StyledContent
320
+ ref={setNodeRef}
321
+ style={style}
322
+ dragged={isDragging}
323
+ over={isOver}
324
+ {...dragAttributes}
325
+ {...dragListeners}
326
+ >
327
+ <BlankContent
328
+ id={id}
329
+ disabled={disabled}
330
+ duplicates={duplicates}
331
+ choice={choice}
332
+ isOver={isOver}
333
+ dragItem={dragItem?.data?.current}
334
+ correct={correct}
335
+ onChange={onChange}
336
+ emptyResponseAreaWidth={emptyResponseAreaWidth}
337
+ emptyResponseAreaHeight={emptyResponseAreaHeight}
338
+ instanceId={instanceId}
339
+ />
340
+ </StyledContent>
341
+ );
342
+ }
343
+
344
+ DragDropBlank.defaultProps = {
345
+ emptyResponseAreaWidth: 0,
346
+ emptyResponseAreaHeight: 0,
347
+ };
348
+
349
+ DragDropBlank.propTypes = {
350
+ id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
351
+ disabled: PropTypes.bool,
352
+ duplicates: PropTypes.bool,
353
+ choice: PropTypes.object,
354
+ correct: PropTypes.bool,
355
+ onChange: PropTypes.func,
356
+ emptyResponseAreaWidth: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
357
+ emptyResponseAreaHeight: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
358
+ instanceId: PropTypes.string,
359
+ };
248
360
 
249
- export default DragDropTile;
361
+ export default DragDropBlank;
@@ -1,15 +1,19 @@
1
1
  import React from 'react';
2
- import OutlinedInput from '@material-ui/core/OutlinedInput';
2
+ import OutlinedInput from '@mui/material/OutlinedInput';
3
3
  import classnames from 'classnames';
4
- import { withStyles } from '@material-ui/core/styles';
4
+ import { styled } from '@mui/material/styles';
5
5
  import { color } from '@pie-lib/render-ui';
6
6
 
7
- const correctStyle = (color) => ({
8
- borderColor: `${color} !important`,
9
- });
10
-
11
- export default withStyles(() => ({
12
- input: {
7
+ const StyledOutlinedInput = styled(OutlinedInput)(() => ({
8
+ padding: '2px',
9
+ borderRadius: '4px',
10
+ fontSize: 'inherit',
11
+ display: 'inline-block',
12
+ verticalAlign: 'middle',
13
+ '& fieldset': {
14
+ border: 0,
15
+ },
16
+ '& .MuiOutlinedInput-input': {
13
17
  color: color.text(),
14
18
  backgroundColor: color.background(),
15
19
  borderRadius: '4px !important',
@@ -26,33 +30,25 @@ export default withStyles(() => ({
26
30
  borderColor: 'initial',
27
31
  },
28
32
  },
29
- '&:focus': {
33
+ '&.Mui-focused': {
30
34
  borderColor: color.primaryDark(),
31
35
  },
32
- },
33
- crInput: {
34
- padding: '8px !important',
35
- },
36
- correct: correctStyle(color.correct()),
37
- incorrect: correctStyle(color.incorrect()),
38
- box: {
39
- fontSize: 'inherit',
40
- },
41
- outlinedInput: {
42
- padding: '2px',
43
- borderRadius: '4px',
44
- '& fieldset': {
45
- border: 0,
36
+ '&.crInput': {
37
+ padding: '8px !important',
38
+ },
39
+ '&.correct': {
40
+ borderColor: `${color.correct()} !important`,
41
+ },
42
+ '&.incorrect': {
43
+ borderColor: `${color.incorrect()} !important`,
46
44
  },
47
45
  },
48
- notchedOutline: {
49
- borderColor: color.correct(),
50
- },
51
- }))((props) => {
46
+ }));
47
+
48
+ const CorrectInput = (props) => {
52
49
  const {
53
50
  correct,
54
51
  charactersLimit,
55
- classes,
56
52
  disabled,
57
53
  isBox,
58
54
  isConstructedResponse,
@@ -60,8 +56,11 @@ export default withStyles(() => ({
60
56
  spellCheck,
61
57
  ...rest
62
58
  } = props;
59
+
63
60
  const label = typeof correct === 'boolean' ? (correct ? 'correct' : 'incorrect') : undefined;
64
- const inputProps = charactersLimit ? { maxLength: charactersLimit } : {};
61
+ const inputProps = charactersLimit
62
+ ? { maxLength: charactersLimit, 'aria-label': 'Enter answer' }
63
+ : { 'aria-label': 'Enter answer' };
65
64
 
66
65
  if (width) {
67
66
  inputProps.style = {
@@ -70,24 +69,24 @@ export default withStyles(() => ({
70
69
  }
71
70
 
72
71
  return (
73
- <OutlinedInput
72
+ <StyledOutlinedInput
74
73
  className={classnames({
75
- [classes.disabledInput]: disabled,
76
- [classes.box]: isBox,
77
- [classes.outlinedInput]: true,
74
+ disabledInput: disabled,
75
+ box: isBox,
78
76
  })}
79
77
  classes={{
80
78
  input: classnames({
81
- [classes.input]: true,
82
- [classes[label]]: label,
83
- [classes.crInput]: isConstructedResponse,
79
+ [label]: label,
80
+ crInput: isConstructedResponse,
84
81
  }),
85
82
  }}
86
83
  inputProps={inputProps}
87
- labelWidth={0}
88
84
  disabled={disabled}
89
85
  spellCheck={spellCheck}
90
86
  {...rest}
91
87
  />
92
88
  );
93
- });
89
+ };
90
+
91
+ export default CorrectInput;
92
+