@pie-lib/rubric 0.28.3-next.2 → 0.28.3-next.203
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 +13 -67
- package/lib/authoring.js +222 -302
- package/lib/authoring.js.map +1 -1
- package/lib/index.js +1 -5
- package/lib/index.js.map +1 -1
- package/lib/point-menu.js +27 -74
- package/lib/point-menu.js.map +1 -1
- package/package.json +10 -17
- package/src/__tests__/rubric.test.jsx +127 -39
- package/src/authoring.jsx +163 -198
- package/src/point-menu.jsx +8 -15
- package/esm/index.css +0 -847
- package/esm/index.js +0 -226008
- package/esm/index.js.map +0 -1
- package/esm/package.json +0 -3
- package/src/__tests__/__snapshots__/rubric.test.jsx.snap +0 -48
|
@@ -1,18 +1,41 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { RawAuthoring } from '../authoring';
|
|
4
|
-
import { Draggable } from 'react-beautiful-dnd';
|
|
5
4
|
import _ from 'lodash';
|
|
5
|
+
import { ThemeProvider, createTheme } from '@mui/material/styles';
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
// Mock dependencies
|
|
8
|
+
jest.mock('@pie-lib/editable-html-tip-tap', () => {
|
|
9
|
+
return function EditableHtml(props) {
|
|
10
|
+
return <div data-testid="editable-html" data-markup={props.markup} />;
|
|
11
|
+
};
|
|
12
|
+
});
|
|
8
13
|
|
|
9
|
-
|
|
10
|
-
|
|
14
|
+
jest.mock('@pie-lib/config-ui', () => ({
|
|
15
|
+
FeedbackConfig: ({ feedback }) => <div data-testid="feedback-config">{JSON.stringify(feedback)}</div>,
|
|
16
|
+
}));
|
|
11
17
|
|
|
18
|
+
jest.mock('@hello-pangea/dnd', () => ({
|
|
19
|
+
DragDropContext: ({ children }) => <div data-testid="drag-drop-context">{children}</div>,
|
|
20
|
+
Droppable: ({ children }) => children({ droppableProps: {}, innerRef: () => {} }, {}),
|
|
21
|
+
Draggable: ({ children, index }) =>
|
|
22
|
+
children(
|
|
23
|
+
{
|
|
24
|
+
innerRef: () => {},
|
|
25
|
+
draggableProps: { 'data-draggable-index': index },
|
|
26
|
+
dragHandleProps: {},
|
|
27
|
+
},
|
|
28
|
+
{},
|
|
29
|
+
),
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
describe('Rubric', () => {
|
|
12
33
|
const points = ['nothing right', 'a teeny bit right', 'mostly right', 'bingo'];
|
|
13
34
|
const sampleAnswers = [null, 'just right', 'not left', null];
|
|
14
|
-
const
|
|
15
|
-
|
|
35
|
+
const theme = createTheme();
|
|
36
|
+
|
|
37
|
+
const renderComponent = (value = {}, props = {}) => {
|
|
38
|
+
const defaultProps = {
|
|
16
39
|
classes: {},
|
|
17
40
|
onChange: jest.fn(),
|
|
18
41
|
className: 'className',
|
|
@@ -22,38 +45,93 @@ describe('Rubric', () => {
|
|
|
22
45
|
sampleAnswers,
|
|
23
46
|
...value,
|
|
24
47
|
},
|
|
48
|
+
...props,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
...render(
|
|
53
|
+
<ThemeProvider theme={theme}>
|
|
54
|
+
<RawAuthoring {...defaultProps} />
|
|
55
|
+
</ThemeProvider>,
|
|
56
|
+
),
|
|
57
|
+
onChange: defaultProps.onChange,
|
|
58
|
+
props: defaultProps,
|
|
25
59
|
};
|
|
26
|
-
const fn = opts && opts.mount ? mount : shallow;
|
|
27
|
-
return fn(<RawAuthoring {...props} />, opts);
|
|
28
60
|
};
|
|
29
61
|
|
|
30
62
|
describe('render', () => {
|
|
31
|
-
it('
|
|
32
|
-
|
|
33
|
-
expect(
|
|
63
|
+
it('renders rubric title and main structure', () => {
|
|
64
|
+
renderComponent();
|
|
65
|
+
expect(screen.getByText('Rubric')).toBeInTheDocument();
|
|
66
|
+
expect(screen.getByLabelText('Max Points')).toBeInTheDocument();
|
|
67
|
+
expect(screen.getByLabelText('Exclude zeros')).toBeInTheDocument();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('renders all point configurations', () => {
|
|
71
|
+
const { container } = renderComponent();
|
|
72
|
+
// Verify DragDropContext is rendered
|
|
73
|
+
expect(container.querySelector('[data-testid="drag-drop-context"]')).toBeInTheDocument();
|
|
74
|
+
|
|
75
|
+
// Check that point labels are rendered (4 points = "3 pts", "2 pts", "1 pt", "0 pt")
|
|
76
|
+
// Note: The PointConfig component uses singular "pt" for 0 and 1, plural "pts" for 2+
|
|
77
|
+
expect(screen.getByText('3 pts')).toBeInTheDocument();
|
|
78
|
+
expect(screen.getByText('2 pts')).toBeInTheDocument();
|
|
79
|
+
expect(screen.getByText('1 pt')).toBeInTheDocument();
|
|
80
|
+
expect(screen.getByText('0 pt')).toBeInTheDocument();
|
|
34
81
|
});
|
|
35
82
|
|
|
36
83
|
describe('draggable', () => {
|
|
37
|
-
it('renders
|
|
38
|
-
|
|
39
|
-
|
|
84
|
+
it('renders 3 draggable items when excludeZero is true', () => {
|
|
85
|
+
const { container } = renderComponent({ excludeZero: true });
|
|
86
|
+
|
|
87
|
+
// When excludeZero is true, the last point (0 pts) should not be rendered
|
|
88
|
+
// So we should have 3 draggable items
|
|
89
|
+
const draggableItems = container.querySelectorAll('[data-draggable-index]');
|
|
90
|
+
expect(draggableItems.length).toEqual(3);
|
|
91
|
+
|
|
92
|
+
// Verify the 0 pt label is not rendered
|
|
93
|
+
expect(screen.queryByText('0 pt')).not.toBeInTheDocument();
|
|
40
94
|
});
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
95
|
+
|
|
96
|
+
it('renders 4 draggable items when excludeZero is false', () => {
|
|
97
|
+
const { container } = renderComponent({ excludeZero: false });
|
|
98
|
+
|
|
99
|
+
// When excludeZero is false, all points including 0 should be rendered
|
|
100
|
+
const draggableItems = container.querySelectorAll('[data-draggable-index]');
|
|
101
|
+
expect(draggableItems.length).toEqual(4);
|
|
102
|
+
|
|
103
|
+
// Verify all point labels are rendered
|
|
104
|
+
expect(screen.getByText('3 pts')).toBeInTheDocument();
|
|
105
|
+
expect(screen.getByText('2 pts')).toBeInTheDocument();
|
|
106
|
+
expect(screen.getByText('1 pt')).toBeInTheDocument();
|
|
107
|
+
expect(screen.getByText('0 pt')).toBeInTheDocument();
|
|
44
108
|
});
|
|
45
109
|
});
|
|
46
110
|
});
|
|
47
111
|
|
|
48
112
|
describe('logic', () => {
|
|
49
|
-
describe('rendering', () => {});
|
|
50
|
-
|
|
51
113
|
describe('changeMaxPoints', () => {
|
|
52
|
-
const
|
|
53
|
-
it(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
114
|
+
const testChangeMaxPoints = (maxPoints, excludeZero, expectedPoints, expectedSampleAnswers) => {
|
|
115
|
+
it(`maxPoints=${maxPoints}, excludeZero=${excludeZero} calls onChange correctly`, () => {
|
|
116
|
+
const { onChange, container } = renderComponent({ excludeZero });
|
|
117
|
+
|
|
118
|
+
// Get the component instance through the container
|
|
119
|
+
// We need to call the method directly since we're testing internal behavior
|
|
120
|
+
const instance = container.querySelector('[class*="MuiBox-root"]')?._owner;
|
|
121
|
+
|
|
122
|
+
// Since we can't easily access instance methods in RTL, we'll test the behavior
|
|
123
|
+
// by verifying the onChange prop receives the correct values
|
|
124
|
+
// For now, we'll directly test the logic
|
|
125
|
+
const component = new RawAuthoring({
|
|
126
|
+
value: { excludeZero, points, sampleAnswers },
|
|
127
|
+
onChange,
|
|
128
|
+
classes: {},
|
|
129
|
+
className: 'className',
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
component.changeMaxPoints(maxPoints);
|
|
133
|
+
|
|
134
|
+
expect(onChange).toHaveBeenCalledWith({
|
|
57
135
|
excludeZero,
|
|
58
136
|
points: expectedPoints,
|
|
59
137
|
sampleAnswers: expectedSampleAnswers,
|
|
@@ -62,19 +140,29 @@ describe('Rubric', () => {
|
|
|
62
140
|
});
|
|
63
141
|
};
|
|
64
142
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
143
|
+
testChangeMaxPoints(1, false, _.takeRight(points, 2), _.takeRight(sampleAnswers, 2));
|
|
144
|
+
testChangeMaxPoints(1, true, _.takeRight(points, 2), _.takeRight(sampleAnswers, 2));
|
|
145
|
+
testChangeMaxPoints(2, true, _.takeRight(points, 3), _.takeRight(sampleAnswers, 3));
|
|
146
|
+
testChangeMaxPoints(2, false, _.takeRight(points, 3), _.takeRight(sampleAnswers, 3));
|
|
147
|
+
testChangeMaxPoints(5, false, ['', ''].concat(points), [null, null].concat(sampleAnswers));
|
|
70
148
|
});
|
|
71
149
|
|
|
72
150
|
describe('changeSampleResponse', () => {
|
|
73
|
-
const
|
|
74
|
-
it(`Point ${index}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
151
|
+
const testChangeSampleResponse = (index, clickedItem, excludeZero, expectedPoints, expectedSampleAnswers) => {
|
|
152
|
+
it(`Point ${index} with clickedItem="${clickedItem}" calls onChange correctly`, () => {
|
|
153
|
+
const { onChange } = renderComponent({ excludeZero });
|
|
154
|
+
|
|
155
|
+
// Test the logic by creating a component instance and calling the method
|
|
156
|
+
const component = new RawAuthoring({
|
|
157
|
+
value: { excludeZero, points, sampleAnswers },
|
|
158
|
+
onChange,
|
|
159
|
+
classes: {},
|
|
160
|
+
className: 'className',
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
component.onPointMenuChange(index, clickedItem);
|
|
164
|
+
|
|
165
|
+
expect(onChange).toHaveBeenCalledWith({
|
|
78
166
|
excludeZero,
|
|
79
167
|
points: expectedPoints,
|
|
80
168
|
sampleAnswers: expectedSampleAnswers,
|
|
@@ -82,10 +170,10 @@ describe('Rubric', () => {
|
|
|
82
170
|
});
|
|
83
171
|
};
|
|
84
172
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
173
|
+
testChangeSampleResponse(0, 'sample', false, points, ['', 'just right', 'not left', null]);
|
|
174
|
+
testChangeSampleResponse(3, 'sample', false, points, [null, 'just right', 'not left', '']);
|
|
175
|
+
testChangeSampleResponse(1, 'sample', true, points, [null, null, 'not left', null]);
|
|
176
|
+
testChangeSampleResponse(3, 'sample', true, points, [null, 'just right', 'not left', '']);
|
|
89
177
|
});
|
|
90
178
|
});
|
|
91
179
|
});
|
package/src/authoring.jsx
CHANGED
|
@@ -1,27 +1,26 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import Select from '@material
|
|
8
|
-
import FormControl from '@material
|
|
9
|
-
import MenuItem from '@material
|
|
3
|
+
import { styled } from '@mui/material/styles';
|
|
4
|
+
|
|
5
|
+
import InputLabel from '@mui/material/InputLabel';
|
|
6
|
+
import OutlinedInput from '@mui/material/OutlinedInput';
|
|
7
|
+
import Select from '@mui/material/Select';
|
|
8
|
+
import FormControl from '@mui/material/FormControl';
|
|
9
|
+
import MenuItem from '@mui/material/MenuItem';
|
|
10
10
|
import times from 'lodash/times';
|
|
11
|
-
import Checkbox from '@material
|
|
12
|
-
import FormGroup from '@material
|
|
13
|
-
import FormControlLabel from '@material
|
|
14
|
-
import
|
|
15
|
-
import
|
|
16
|
-
import
|
|
17
|
-
import EditableHtml from '@pie-lib/editable-html';
|
|
18
|
-
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
|
|
11
|
+
import Checkbox from '@mui/material/Checkbox';
|
|
12
|
+
import FormGroup from '@mui/material/FormGroup';
|
|
13
|
+
import FormControlLabel from '@mui/material/FormControlLabel';
|
|
14
|
+
import Typography from '@mui/material/Typography';
|
|
15
|
+
import DragIndicator from '@mui/icons-material/DragIndicator';
|
|
16
|
+
import { DragDropContext, Droppable, Draggable } from '@hello-pangea/dnd';
|
|
19
17
|
import debug from 'debug';
|
|
20
18
|
import takeRight from 'lodash/takeRight';
|
|
21
19
|
import PointMenu from './point-menu';
|
|
22
|
-
|
|
23
20
|
import range from 'lodash/range';
|
|
21
|
+
import EditableHtml from '@pie-lib/editable-html-tip-tap';
|
|
24
22
|
import { InputContainer } from '@pie-lib/config-ui';
|
|
23
|
+
import { grey } from '@mui/material/colors';
|
|
25
24
|
|
|
26
25
|
const log = debug('pie-lib:rubric:authoring');
|
|
27
26
|
|
|
@@ -42,20 +41,21 @@ export const RubricType = PropTypes.shape({
|
|
|
42
41
|
rubriclessInstruction: PropTypes.string,
|
|
43
42
|
});
|
|
44
43
|
|
|
45
|
-
const MaxPoints =
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
margin: theme.spacing.unit,
|
|
49
|
-
},
|
|
50
|
-
}))((props) => {
|
|
51
|
-
const { value, onChange, max, classes } = props;
|
|
44
|
+
const MaxPoints = (props) => {
|
|
45
|
+
const { value, onChange, max } = props;
|
|
46
|
+
const labelId = 'max-points-label';
|
|
52
47
|
|
|
53
48
|
return (
|
|
54
|
-
<FormControl
|
|
55
|
-
<InputLabel
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
49
|
+
<FormControl sx={{ minWidth: 120, m: 1 }} variant="outlined">
|
|
50
|
+
<InputLabel id={labelId}>Max Points</InputLabel>
|
|
51
|
+
<Select
|
|
52
|
+
labelId={labelId}
|
|
53
|
+
label="Max Points"
|
|
54
|
+
value={value}
|
|
55
|
+
onChange={(e) => onChange(e.target.value)}
|
|
56
|
+
input={<OutlinedInput label="Max Points" />}
|
|
57
|
+
MenuProps={{ transitionDuration: { enter: 225, exit: 195 } }}
|
|
58
|
+
>
|
|
59
59
|
{range(1, max + 1).map((v) => (
|
|
60
60
|
<MenuItem key={`${v}`} value={v}>
|
|
61
61
|
{v}
|
|
@@ -64,97 +64,98 @@ const MaxPoints = withStyles((theme) => ({
|
|
|
64
64
|
</Select>
|
|
65
65
|
</FormControl>
|
|
66
66
|
);
|
|
67
|
-
}
|
|
67
|
+
};
|
|
68
68
|
|
|
69
69
|
// if the value is null or 'null', the Sample Answer input field for that point will not be dispalyed
|
|
70
70
|
// if the value is '', the Sample Answer input field will be empty
|
|
71
71
|
const checkSampleAnswer = (sampleAnswer) => sampleAnswer === null || sampleAnswer === 'null';
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
right: 0,
|
|
99
|
-
},
|
|
100
|
-
errorText: {
|
|
101
|
-
fontSize: theme.typography.fontSize - 2,
|
|
102
|
-
color: theme.palette.error.main,
|
|
103
|
-
paddingLeft: theme.spacing.unit * 3,
|
|
104
|
-
paddingTop: theme.spacing.unit,
|
|
105
|
-
},
|
|
106
|
-
}))((props) => {
|
|
107
|
-
const { points, content, classes, sampleAnswer, mathMlOptions = {}, error, pluginOpts = {} } = props;
|
|
73
|
+
const Row = styled('div')(() => ({ display: 'flex', width: '100%', position: 'relative' }));
|
|
74
|
+
|
|
75
|
+
const EditorDiv = styled('div')(({ theme }) => ({ width: '100%', backgroundColor: `${theme.palette.common.white}` }));
|
|
76
|
+
|
|
77
|
+
const DragIndicatorStyled = styled(DragIndicator)(({ theme }) => ({ paddingTop: theme.spacing(1), color: grey[500] }));
|
|
78
|
+
|
|
79
|
+
const PointsLabel = styled(Typography)(({ theme }) => ({
|
|
80
|
+
color: grey[500],
|
|
81
|
+
paddingBottom: theme.spacing(1),
|
|
82
|
+
textTransform: 'uppercase',
|
|
83
|
+
}));
|
|
84
|
+
|
|
85
|
+
const SampleAnswersEditor = styled('div')(({ theme }) => ({ paddingLeft: theme.spacing(3) }));
|
|
86
|
+
|
|
87
|
+
const ErrorText = styled('div')(({ theme }) => ({
|
|
88
|
+
fontSize: theme.typography.fontSize - 2,
|
|
89
|
+
color: theme.palette.error.main,
|
|
90
|
+
paddingLeft: theme.spacing(3),
|
|
91
|
+
paddingTop: theme.spacing(1),
|
|
92
|
+
}));
|
|
93
|
+
|
|
94
|
+
const PointMenuWrapper = styled('div')(() => ({ position: 'absolute', right: 0 }));
|
|
95
|
+
|
|
96
|
+
export const PointConfig = (props) => {
|
|
97
|
+
const { points, content, sampleAnswer, mathMlOptions = {}, error, pluginOpts = {} } = props;
|
|
108
98
|
const pointsLabel = `${points} ${points <= 1 ? 'pt' : 'pts'}`;
|
|
109
99
|
const showSampleAnswer = checkSampleAnswer(sampleAnswer);
|
|
110
100
|
|
|
111
101
|
return (
|
|
112
|
-
<div
|
|
113
|
-
<
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
<div className={classes.row}>
|
|
118
|
-
<DragIndicator className={classes.dragIndicator} />
|
|
119
|
-
<EditableHtml
|
|
120
|
-
className={classes.editor}
|
|
121
|
-
error={error}
|
|
122
|
-
pluginProps={pluginOpts}
|
|
123
|
-
markup={content}
|
|
124
|
-
onChange={props.onChange}
|
|
125
|
-
mathMlOptions={mathMlOptions}
|
|
126
|
-
/>
|
|
127
|
-
<PointMenu
|
|
128
|
-
classes={{
|
|
129
|
-
icon: classes.pointMenu,
|
|
130
|
-
}}
|
|
131
|
-
showSampleAnswer={showSampleAnswer}
|
|
132
|
-
onChange={props.onMenuChange}
|
|
133
|
-
/>
|
|
134
|
-
</div>
|
|
135
|
-
{error && <div className={classes.errorText}>{error}</div>}
|
|
136
|
-
{!showSampleAnswer && (
|
|
137
|
-
<div className={classes.sampleAnswersEditor}>
|
|
138
|
-
<Typography variant="overline" className={classes.dragIndicator}>
|
|
139
|
-
Sample Response
|
|
140
|
-
</Typography>
|
|
102
|
+
<div>
|
|
103
|
+
<PointsLabel variant="overline">{pointsLabel}</PointsLabel>
|
|
104
|
+
<Row>
|
|
105
|
+
<DragIndicatorStyled />
|
|
106
|
+
<EditorDiv>
|
|
141
107
|
<EditableHtml
|
|
142
|
-
|
|
143
|
-
markup={sampleAnswer}
|
|
108
|
+
error={error}
|
|
144
109
|
pluginProps={pluginOpts}
|
|
145
|
-
|
|
110
|
+
markup={content}
|
|
111
|
+
onChange={props.onChange}
|
|
146
112
|
mathMlOptions={mathMlOptions}
|
|
147
113
|
/>
|
|
148
|
-
</
|
|
114
|
+
</EditorDiv>
|
|
115
|
+
<PointMenuWrapper>
|
|
116
|
+
<PointMenu showSampleAnswer={showSampleAnswer} onChange={props.onMenuChange} />
|
|
117
|
+
</PointMenuWrapper>
|
|
118
|
+
</Row>
|
|
119
|
+
{error && <ErrorText>{error}</ErrorText>}
|
|
120
|
+
{!showSampleAnswer && (
|
|
121
|
+
<SampleAnswersEditor>
|
|
122
|
+
<DragIndicatorStyled as={Typography} variant="overline">
|
|
123
|
+
Sample Response
|
|
124
|
+
</DragIndicatorStyled>
|
|
125
|
+
<EditorDiv>
|
|
126
|
+
<EditableHtml
|
|
127
|
+
markup={sampleAnswer}
|
|
128
|
+
pluginProps={pluginOpts}
|
|
129
|
+
onChange={props.onSampleChange}
|
|
130
|
+
mathMlOptions={mathMlOptions}
|
|
131
|
+
/>
|
|
132
|
+
</EditorDiv>
|
|
133
|
+
</SampleAnswersEditor>
|
|
149
134
|
)}
|
|
150
135
|
</div>
|
|
151
136
|
);
|
|
152
|
-
}
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const Container = styled('div')(({ theme }) => ({
|
|
140
|
+
backgroundColor: grey[200],
|
|
141
|
+
borderWidth: 1,
|
|
142
|
+
borderStyle: 'solid',
|
|
143
|
+
borderColor: grey[300],
|
|
144
|
+
padding: theme.spacing(2),
|
|
145
|
+
margin: theme.spacing(1),
|
|
146
|
+
}));
|
|
147
|
+
const InputContainerWrapper = styled('div')(({ theme }) => ({
|
|
148
|
+
width: '100%',
|
|
149
|
+
paddingTop: theme.spacing(2),
|
|
150
|
+
marginBottom: theme.spacing(2),
|
|
151
|
+
'& MuiFormControl-root': { width: '100%' },
|
|
152
|
+
}));
|
|
153
|
+
const Rubricless = styled('div')(() => ({ display: 'none' }));
|
|
154
|
+
const ConfigHolder = styled('div')(({ theme }) => ({ paddingTop: theme.spacing(1), paddingBottom: theme.spacing(1) }));
|
|
155
|
+
const RubricTitle = styled(Typography)(({ theme }) => ({ paddingLeft: theme.spacing(1), margin: theme.spacing(1) }));
|
|
153
156
|
|
|
154
157
|
export class RawAuthoring extends React.Component {
|
|
155
158
|
static propTypes = {
|
|
156
|
-
classes: PropTypes.object.isRequired,
|
|
157
|
-
className: PropTypes.string,
|
|
158
159
|
value: RubricType,
|
|
159
160
|
config: PropTypes.object,
|
|
160
161
|
pluginOpts: PropTypes.object,
|
|
@@ -264,15 +265,7 @@ export class RawAuthoring extends React.Component {
|
|
|
264
265
|
};
|
|
265
266
|
|
|
266
267
|
render() {
|
|
267
|
-
const {
|
|
268
|
-
classes,
|
|
269
|
-
className,
|
|
270
|
-
value,
|
|
271
|
-
mathMlOptions = {},
|
|
272
|
-
config = {},
|
|
273
|
-
rubricless = false,
|
|
274
|
-
pluginOpts = {},
|
|
275
|
-
} = this.props;
|
|
268
|
+
const { value, mathMlOptions = {}, config = {}, rubricless = false, pluginOpts = {} } = this.props;
|
|
276
269
|
let {
|
|
277
270
|
excludeZeroEnabled = true,
|
|
278
271
|
maxPointsEnabled = true,
|
|
@@ -292,10 +285,8 @@ export class RawAuthoring extends React.Component {
|
|
|
292
285
|
const maxPointsValue = !rubricless ? value.points.length - 1 : maxPoints;
|
|
293
286
|
|
|
294
287
|
return (
|
|
295
|
-
<div
|
|
296
|
-
<
|
|
297
|
-
Rubric
|
|
298
|
-
</Typography>
|
|
288
|
+
<div>
|
|
289
|
+
<RubricTitle variant="h5">Rubric</RubricTitle>
|
|
299
290
|
<FormGroup row>
|
|
300
291
|
{maxPointsEnabled && (
|
|
301
292
|
<MaxPoints
|
|
@@ -314,93 +305,73 @@ export class RawAuthoring extends React.Component {
|
|
|
314
305
|
</FormGroup>
|
|
315
306
|
|
|
316
307
|
{rubriclessInstructionEnabled && rubricless && (
|
|
317
|
-
<
|
|
318
|
-
<
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
308
|
+
<InputContainerWrapper>
|
|
309
|
+
<InputContainer label={rubriclessInstruction.label}>
|
|
310
|
+
<EditableHtml
|
|
311
|
+
markup={value.rubriclessInstruction || ''}
|
|
312
|
+
onChange={this.changeRubriclessInstruction}
|
|
313
|
+
pluginProps={pluginOpts}
|
|
314
|
+
nonEmpty={false}
|
|
315
|
+
disableUnderline
|
|
316
|
+
languageCharactersProps={[{ language: 'spanish' }, { language: 'special' }]}
|
|
317
|
+
mathMlOptions={mathMlOptions}
|
|
318
|
+
/>
|
|
319
|
+
</InputContainer>
|
|
320
|
+
</InputContainerWrapper>
|
|
329
321
|
)}
|
|
330
322
|
|
|
331
|
-
<div
|
|
332
|
-
|
|
333
|
-
<
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
{
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
323
|
+
<div>
|
|
324
|
+
{rubricless ? (
|
|
325
|
+
<Rubricless />
|
|
326
|
+
) : (
|
|
327
|
+
<Container>
|
|
328
|
+
<DragDropContext onDragEnd={this.dragEnd}>
|
|
329
|
+
<Droppable droppableId="droppable">
|
|
330
|
+
{(provided) => (
|
|
331
|
+
<div {...provided.droppableProps} ref={provided.innerRef}>
|
|
332
|
+
{value.points.map(
|
|
333
|
+
(p, index) =>
|
|
334
|
+
this.shouldRenderPoint(index, value) && (
|
|
335
|
+
<Draggable key={`${p.points}-${index}`} index={index} draggableId={index.toString()}>
|
|
336
|
+
{(provided) => (
|
|
337
|
+
<ConfigHolder
|
|
338
|
+
ref={provided.innerRef}
|
|
339
|
+
{...provided.draggableProps}
|
|
340
|
+
{...provided.dragHandleProps}
|
|
341
|
+
>
|
|
342
|
+
<PointConfig
|
|
343
|
+
points={value.points.length - 1 - index}
|
|
344
|
+
content={p}
|
|
345
|
+
error={
|
|
346
|
+
pointsDescriptorsErrors &&
|
|
347
|
+
pointsDescriptorsErrors[value.points.length - 1 - index]
|
|
348
|
+
}
|
|
349
|
+
sampleAnswer={value.sampleAnswers && value.sampleAnswers[index]}
|
|
350
|
+
onChange={(content) => this.changeContent(index, content, 'points')}
|
|
351
|
+
onSampleChange={(content) => this.changeContent(index, content, 'sampleAnswers')}
|
|
352
|
+
onMenuChange={(clickedItem) => this.onPointMenuChange(index, clickedItem)}
|
|
353
|
+
mathMlOptions={mathMlOptions}
|
|
354
|
+
pluginOpts={pluginOpts}
|
|
355
|
+
/>
|
|
356
|
+
</ConfigHolder>
|
|
357
|
+
)}
|
|
358
|
+
</Draggable>
|
|
359
|
+
),
|
|
360
|
+
)}
|
|
361
|
+
{provided.placeholder}
|
|
362
|
+
</div>
|
|
364
363
|
)}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
</DragDropContext>
|
|
364
|
+
</Droppable>
|
|
365
|
+
</DragDropContext>
|
|
366
|
+
</Container>
|
|
367
|
+
)}
|
|
370
368
|
</div>
|
|
371
369
|
</div>
|
|
372
370
|
);
|
|
373
371
|
}
|
|
374
372
|
}
|
|
375
373
|
|
|
376
|
-
|
|
377
|
-
container: {
|
|
378
|
-
backgroundColor: grey[200],
|
|
379
|
-
borderWidth: 1,
|
|
380
|
-
borderStyle: 'solid',
|
|
381
|
-
borderColor: grey[300],
|
|
382
|
-
padding: theme.spacing.unit * 2,
|
|
383
|
-
margin: theme.spacing.unit,
|
|
384
|
-
},
|
|
385
|
-
inputContainer: {
|
|
386
|
-
width: '100%',
|
|
387
|
-
paddingTop: theme.spacing.unit * 2,
|
|
388
|
-
marginBottom: theme.spacing.unit * 2,
|
|
389
|
-
},
|
|
390
|
-
rubricless: {
|
|
391
|
-
display: 'none',
|
|
392
|
-
},
|
|
393
|
-
configHolder: {
|
|
394
|
-
paddingTop: theme.spacing.unit,
|
|
395
|
-
paddingBottom: theme.spacing.unit,
|
|
396
|
-
},
|
|
397
|
-
rubricTitle: {
|
|
398
|
-
paddingLeft: theme.spacing.unit,
|
|
399
|
-
margin: theme.spacing.unit,
|
|
400
|
-
},
|
|
401
|
-
});
|
|
402
|
-
|
|
403
|
-
const StyledRawAuthoring = withStyles(styles)(RawAuthoring);
|
|
374
|
+
// styles migrated to styled-components above
|
|
404
375
|
|
|
405
376
|
const Reverse = (props) => {
|
|
406
377
|
const { rubricless = false, config = {}, pluginOpts = {} } = props || {};
|
|
@@ -424,13 +395,7 @@ const Reverse = (props) => {
|
|
|
424
395
|
};
|
|
425
396
|
|
|
426
397
|
return (
|
|
427
|
-
<
|
|
428
|
-
value={value}
|
|
429
|
-
config={config}
|
|
430
|
-
onChange={onChange}
|
|
431
|
-
rubricless={rubricless}
|
|
432
|
-
pluginOpts={pluginOpts}
|
|
433
|
-
/>
|
|
398
|
+
<RawAuthoring value={value} config={config} onChange={onChange} rubricless={rubricless} pluginOpts={pluginOpts} />
|
|
434
399
|
);
|
|
435
400
|
};
|
|
436
401
|
|