diff --git a/packages/demo/config/load-links.js b/packages/demo/config/load-links.js index 3f1fc80e3..9f51a45b4 100644 --- a/packages/demo/config/load-links.js +++ b/packages/demo/config/load-links.js @@ -18,7 +18,6 @@ const rawLinks = [ { label: 'math-rendering', path: '/math-rendering' }, { label: 'math-toolbar', path: '/math-toolbar' }, { label: 'render-ui', path: '/render-ui' }, - { label: 'rubric', path: '/rubric' }, { label: 'scoring-config', path: '/scoring-config' }, { label: 'text-select', path: '/text-select' }, { label: 'text-select math', path: '/text-select-math' }, diff --git a/packages/demo/pages/rubric/index.js b/packages/demo/pages/rubric/index.js deleted file mode 100644 index 69261fd47..000000000 --- a/packages/demo/pages/rubric/index.js +++ /dev/null @@ -1,46 +0,0 @@ -import { Authoring } from '@pie-lib/pie-toolbox/rubric'; -import React from 'react'; -import withRoot from '../../source/withRoot'; -import { withStyles } from '@material-ui/core'; -import Section from '../../source/formatting/section'; -import Pre from '../../source/formatting/pre'; - -class Demo extends React.Component { - constructor(props) { - super(props); - this.state = { - rubric: { - excludeZero: false, - // the index is the points - points: ['nothing right', 'a teeny bit right', 'mostly right', 'bingo'], - // if the value is null or 'null', the Sample Answer input field for that point will not be dispalyed - // if the value is '', the Sample Answer input field will be empty - sampleAnswers: [null, 'just right', 'not left', null], - }, - }; - } - - componentDidMount() { - this.setState({ mounted: true }); - } - - render() { - const { mounted, rubric } = this.state; - // TODO: check similar comps to see what they support... - return mounted ? ( -
-
-
- this.setState({ rubric })} /> -
-        
-
- ) : ( -
- ); - } -} - -const Styled = withStyles((theme) => ({}))(Demo); - -export default withRoot(Styled); diff --git a/packages/pie-toolbox/src/code/rubric/__tests__/__snapshots__/rubric.test.jsx.snap b/packages/pie-toolbox/src/code/rubric/__tests__/__snapshots__/rubric.test.jsx.snap deleted file mode 100644 index 46d73199c..000000000 --- a/packages/pie-toolbox/src/code/rubric/__tests__/__snapshots__/rubric.test.jsx.snap +++ /dev/null @@ -1,48 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Rubric render snapshot 1`] = ` -
- - Rubric - - - - - } - label="Exclude zeros" - /> - -
- - - - - -
-
-`; diff --git a/packages/pie-toolbox/src/code/rubric/__tests__/rubric.test.jsx b/packages/pie-toolbox/src/code/rubric/__tests__/rubric.test.jsx deleted file mode 100644 index 7033bc74a..000000000 --- a/packages/pie-toolbox/src/code/rubric/__tests__/rubric.test.jsx +++ /dev/null @@ -1,91 +0,0 @@ -import { shallow, mount } from 'enzyme'; -import React from 'react'; -import { RawAuthoring } from '../authoring'; -import { Draggable } from 'react-beautiful-dnd'; -import _ from 'lodash'; - -jest.mock('../../editable-html', () => () =>
); - -describe('Rubric', () => { - let w; - - const points = ['nothing right', 'a teeny bit right', 'mostly right', 'bingo']; - const sampleAnswers = [null, 'just right', 'not left', null]; - const wrapper = (value, opts) => { - const props = { - classes: {}, - onChange: jest.fn(), - className: 'className', - value: { - excludeZero: false, - points, - sampleAnswers, - ...value, - }, - }; - const fn = opts && opts.mount ? mount : shallow; - return fn(, opts); - }; - - describe('render', () => { - it('snapshot', () => { - w = wrapper(); - expect(w).toMatchSnapshot(); - }); - - describe('draggable', () => { - it('renders correctly for excluded zeroes', () => { - let w = wrapper({ excludeZero: true }, { mount: true }); - expect(w.find(Draggable).length).toEqual(3); - }); - it('renders correctly for excluded zeroes', () => { - let w = wrapper({ excludeZero: false }, { mount: true }); - expect(w.find(Draggable).length).toEqual(4); - }); - }); - }); - - describe('logic', () => { - describe('rendering', () => {}); - - describe('changeMaxPoints', () => { - const assertChangeMax = (points, excludeZero, expectedPoints, expectedSampleAnswers) => { - it(`${points} calls onChange with: ${expectedPoints} and ${expectedSampleAnswers}`, () => { - let w = wrapper({ excludeZero }); - w.instance().changeMaxPoints(points); - expect(w.instance().props.onChange).toHaveBeenCalledWith({ - excludeZero, - points: expectedPoints, - sampleAnswers: expectedSampleAnswers, - maxPoints: expectedPoints.length - 1, - }); - }); - }; - - assertChangeMax(1, false, _.takeRight(points, 2), _.takeRight(sampleAnswers, 2)); - assertChangeMax(1, true, _.takeRight(points, 2), _.takeRight(sampleAnswers, 2)); - assertChangeMax(2, true, _.takeRight(points, 3), _.takeRight(sampleAnswers, 3)); - assertChangeMax(2, false, _.takeRight(points, 3), _.takeRight(sampleAnswers, 3)); - assertChangeMax(5, false, ['', ''].concat(points), [null, null].concat(sampleAnswers)); - }); - - describe('changeSampleResponse', () => { - const assertChangeSample = (index, clickedItem, excludeZero, expectedPoints, expectedSampleAnswers) => { - it(`Point ${index} calls onChange with: ${expectedPoints} and ${expectedSampleAnswers}`, () => { - let w = wrapper({ excludeZero }); - w.instance().onPointMenuChange(index, clickedItem); - expect(w.instance().props.onChange).toHaveBeenCalledWith({ - excludeZero, - points: expectedPoints, - sampleAnswers: expectedSampleAnswers, - }); - }); - }; - - assertChangeSample(0, 'sample', false, points, ['', 'just right', 'not left', null]); - assertChangeSample(3, 'sample', false, points, [null, 'just right', 'not left', '']); - assertChangeSample(1, 'sample', true, points, [null, null, 'not left', null]); - assertChangeSample(3, 'sample', true, points, [null, 'just right', 'not left', '']); - }); - }); -}); diff --git a/packages/pie-toolbox/src/code/rubric/authoring.jsx b/packages/pie-toolbox/src/code/rubric/authoring.jsx deleted file mode 100644 index 23a3649de..000000000 --- a/packages/pie-toolbox/src/code/rubric/authoring.jsx +++ /dev/null @@ -1,446 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { withStyles } from '@material-ui/core/styles'; -import classNames from 'classnames'; -import OutlinedInput from '@material-ui/core/OutlinedInput'; -import InputLabel from '@material-ui/core/InputLabel'; -import Select from '@material-ui/core/Select'; -import FormControl from '@material-ui/core/FormControl'; -import MenuItem from '@material-ui/core/MenuItem'; -import times from 'lodash/times'; -import Checkbox from '@material-ui/core/Checkbox'; -import FormGroup from '@material-ui/core/FormGroup'; -import FormControlLabel from '@material-ui/core/FormControlLabel'; -import grey from '@material-ui/core/colors/grey'; -import Typography from '@material-ui/core/Typography'; -import DragIndicator from '@material-ui/icons/DragIndicator'; -import EditableHtml from '../editable-html'; -import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd'; -import debug from 'debug'; -import takeRight from 'lodash/takeRight'; -import PointMenu from './point-menu'; - -import range from 'lodash/range'; -import { InputContainer } from '../config-ui'; - -const log = debug('pie-lib:rubric:authoring'); - -const reorder = (list, startIndex, endIndex) => { - const result = Array.from(list); - const [removed] = result.splice(startIndex, 1); - - result.splice(endIndex, 0, removed); - - return result; -}; - -export const RubricType = PropTypes.shape({ - excludeZero: PropTypes.bool, - points: PropTypes.arrayOf(PropTypes.string), - sampleAnswers: PropTypes.arrayOf(PropTypes.string), - maxPoints: PropTypes.number, - rubriclessInstruction: PropTypes.string, -}); - -const MaxPoints = withStyles((theme) => ({ - formControl: { - minWidth: '120px', - margin: theme.spacing.unit, - }, -}))((props) => { - const { value, onChange, max, classes } = props; - - return ( - - - Max Points - - - - ); -}); - -// if the value is null or 'null', the Sample Answer input field for that point will not be dispalyed -// if the value is '', the Sample Answer input field will be empty -const checkSampleAnswer = (sampleAnswer) => sampleAnswer === null || sampleAnswer === 'null'; - -export const PointConfig = withStyles((theme) => ({ - pointConfig: {}, - row: { - display: 'flex', - width: '100%', - position: 'relative', - }, - editor: { - width: '100%', - backgroundColor: `${theme.palette.common.white} !important`, - }, - dragIndicator: { - paddingTop: theme.spacing.unit, - color: grey[500], - }, - pointsLabel: { - color: grey[500], - paddingBottom: theme.spacing.unit, - textTransform: 'uppercase', - }, - sampleAnswersEditor: { - paddingLeft: theme.spacing.unit * 3, - }, - pointMenu: { - position: 'absolute', - right: 0, - }, - errorText: { - fontSize: theme.typography.fontSize - 2, - color: theme.palette.error.main, - paddingLeft: theme.spacing.unit * 3, - paddingTop: theme.spacing.unit, - }, -}))((props) => { - const { points, content, classes, sampleAnswer, mathMlOptions = {}, error, pluginOpts = {} } = props; - const pointsLabel = `${points} ${points <= 1 ? 'pt' : 'pts'}`; - const showSampleAnswer = checkSampleAnswer(sampleAnswer); - - return ( -
- - {pointsLabel} - - -
- - - -
- {error &&
{error}
} - {!showSampleAnswer && ( -
- - Sample Response - - -
- )} -
- ); -}); - -export class RawAuthoring extends React.Component { - static propTypes = { - classes: PropTypes.object.isRequired, - className: PropTypes.string, - value: RubricType, - config: PropTypes.object, - pluginOpts: PropTypes.object, - rubricless: PropTypes.bool, - onChange: PropTypes.func, - }; - - static defaultProps = {}; - - dragEnd = (result) => { - if (!result.destination) { - return; - } - - const { value, onChange } = this.props; - - const points = reorder(value.points, result.source.index, result.destination.index); - const sampleAnswers = reorder(value.sampleAnswers, result.source.index, result.destination.index); - - onChange({ ...value, points, sampleAnswers }); - }; - - changeRubriclessInstruction = (input) => { - const { value, onChange } = this.props; - onChange({ ...value, rubriclessInstruction: input }); - }; - - changeMaxPoints = (maxPoints) => { - const { value, onChange, rubricless } = this.props; - const currentMax = value.points.length - 1; - - log('current', currentMax, 'new: ', maxPoints); - - let points, sampleAnswers; - if (maxPoints > currentMax) { - points = times(maxPoints - currentMax) - .map(() => '') - .concat(value.points); - sampleAnswers = times(maxPoints - currentMax) - .map(() => null) - .concat(value.sampleAnswers); - } - - if (maxPoints < currentMax) { - log('less than'); - points = takeRight(value.points, maxPoints + 1); - sampleAnswers = takeRight(value.sampleAnswers, maxPoints + 1); - } - - if (points && !rubricless) { - onChange({ ...value, points, sampleAnswers, maxPoints }); - } else { - onChange({ ...value, maxPoints }); - } - }; - - changeContent = (index, content, type) => { - // type could be 'points' or 'sampleAnswers' - log(`changeModel[${type}]:`, index, content); - - if (type !== 'points' && type !== 'sampleAnswers') { - return; - } - - const { value, onChange } = this.props; - const items = value[type] && Array.from(value[type]); - - items.splice(index, 1, content); - log(`changeModel[${type}]:`, items); - - onChange({ ...value, [type]: items }); - }; - - excludeZeros = () => { - const { value, onChange } = this.props; - - onChange({ ...value, excludeZero: !value.excludeZero }); - }; - - shouldRenderPoint = (index, value) => { - if (!value.excludeZero) { - return true; - } else { - if (index < value.points.length - 1) { - return true; - } else if (index === value.points.length - 1) { - return false; - } - - return true; - } - }; - - onPointMenuChange = (index, clickedItem) => { - if (clickedItem === 'sample') { - const { value } = this.props; - const sampleAnswers = Array.from(value.sampleAnswers || []); - - if (checkSampleAnswer(sampleAnswers[index])) { - // an empty string will display an empty Sample Answer input field - this.changeContent(index, '', 'sampleAnswers'); - } else { - // when the content is null or 'null', the Sample Answer input field will not be displayed - this.changeContent(index, null, 'sampleAnswers'); - } - } - }; - - render() { - const { - classes, - className, - value, - mathMlOptions = {}, - config = {}, - rubricless = false, - pluginOpts = {}, - } = this.props; - let { - excludeZeroEnabled = true, - maxPointsEnabled = true, - errors = {}, - rubriclessInstructionEnabled = false, - maxPoints = 10, - } = value || {}; - // rubric will contain a max value for maxPoints - const { rubriclessInstruction = {}, maxMaxPoints = 10 } = config || {}; - const { pointsDescriptorsErrors } = errors || {}; - if (value && Number.isFinite(value.maxPoints)) { - // eslint-disable-next-line no-console - console.warn('maxPoints is deprecated - remove from model'); - } - - // for rubric value is computed based on points - const maxPointsValue = !rubricless ? value.points.length - 1 : maxPoints; - - return ( -
- - Rubric - - - {maxPointsEnabled && ( - - )} - {excludeZeroEnabled && ( - } - /> - )} - - - {rubriclessInstructionEnabled && rubricless && ( - - - - )} - -
- - - {(provided) => ( -
- {value.points.map( - (p, index) => - this.shouldRenderPoint(index, value) && ( - - {(provided) => ( -
- this.changeContent(index, content, 'points')} - onSampleChange={(content) => this.changeContent(index, content, 'sampleAnswers')} - onMenuChange={(clickedItem) => this.onPointMenuChange(index, clickedItem)} - mathMlOptions={mathMlOptions} - pluginOpts={pluginOpts} - /> -
- )} -
- ), - )} - {provided.placeholder} -
- )} -
-
-
-
- ); - } -} - -const styles = (theme) => ({ - container: { - backgroundColor: grey[200], - borderWidth: 1, - borderStyle: 'solid', - borderColor: grey[300], - padding: theme.spacing.unit * 2, - margin: theme.spacing.unit, - }, - inputContainer: { - width: '100%', - paddingTop: theme.spacing.unit * 2, - marginBottom: theme.spacing.unit * 2, - }, - rubricless: { - display: 'none', - }, - configHolder: { - paddingTop: theme.spacing.unit, - paddingBottom: theme.spacing.unit, - }, - rubricTitle: { - paddingLeft: theme.spacing.unit, - margin: theme.spacing.unit, - }, -}); - -const StyledRawAuthoring = withStyles(styles)(RawAuthoring); - -const Reverse = (props) => { - const { rubricless = false, config = {}, pluginOpts = {} } = props || {}; - const points = Array.from(props.value.points || []).reverse(); - let sampleAnswers = Array.from(props.value.sampleAnswers || []).reverse(); - - if (points.length > sampleAnswers.length) { - sampleAnswers = times(points.length - sampleAnswers.length) - .map(() => null) - .concat(sampleAnswers); - } - - const value = { ...props.value, points, sampleAnswers }; - - const onChange = (value) => { - props.onChange({ - ...value, - points: Array.from(value.points || []).reverse(), - sampleAnswers: Array.from(value.sampleAnswers || []).reverse(), - }); - }; - - return ( - - ); -}; - -Reverse.propTypes = { - value: RubricType, - config: PropTypes.object, - pluginOpts: PropTypes.object, - rubricless: PropTypes.bool, - getIndex: PropTypes.func, - onChange: PropTypes.func, -}; - -export default Reverse; diff --git a/packages/pie-toolbox/src/code/rubric/index.js b/packages/pie-toolbox/src/code/rubric/index.js deleted file mode 100644 index fcf988b48..000000000 --- a/packages/pie-toolbox/src/code/rubric/index.js +++ /dev/null @@ -1,9 +0,0 @@ -import Authoring from './authoring'; - -const RUBRIC_TYPES = { - SIMPLE_RUBRIC: 'simpleRubric', - MULTI_TRAIT_RUBRIC: 'multiTraitRubric', - RUBRICLESS: 'rubricless', -}; - -export { Authoring, RUBRIC_TYPES }; diff --git a/packages/pie-toolbox/src/code/rubric/point-menu.jsx b/packages/pie-toolbox/src/code/rubric/point-menu.jsx deleted file mode 100644 index 26454b7fc..000000000 --- a/packages/pie-toolbox/src/code/rubric/point-menu.jsx +++ /dev/null @@ -1,94 +0,0 @@ -import Menu from '@material-ui/core/Menu'; -import MenuItem from '@material-ui/core/MenuItem'; -import MoreVertIcon from '@material-ui/icons/MoreVert'; -import MoreHorizIcon from '@material-ui/icons/MoreHoriz'; -import IconButton from '@material-ui/core/IconButton'; -import PropTypes from 'prop-types'; -import React from 'react'; - -export class IconMenu extends React.Component { - static propTypes = { - opts: PropTypes.object, - onClick: PropTypes.func.isRequired, - classes: PropTypes.object.isRequired, - }; - - constructor(props) { - super(props); - this.state = { - anchorEl: undefined, - open: false, - }; - } - - handleClick = (event) => this.setState({ open: true, anchorEl: event.currentTarget }); - - handleRequestClose = () => this.setState({ open: false }); - - render() { - const { opts, onClick, classes } = this.props; - const { open, anchorEl } = this.state; - const keys = Object.keys(opts) || []; - - const handleMenuClick = (key) => () => { - onClick(key); - this.handleRequestClose(); - }; - - const iconColor = open ? 'inherit' : 'disabled'; - - return ( -
-
- - {open ? : } - -
- - {keys.map((k, index) => ( - - {opts[k]} - - ))} - -
- ); - } -} - -export default class PointMenu extends React.Component { - static propTypes = { - onChange: PropTypes.func.isRequired, - classes: PropTypes.object.isRequired, - showSampleAnswer: PropTypes.bool.isRequired, - }; - - static defaultProps = { - classes: {}, - }; - - render() { - const { onChange, classes, showSampleAnswer } = this.props; - const sampleText = showSampleAnswer ? 'Provide Sample Response' : 'Remove Sample Response'; - - return ( - onChange(key)} - opts={{ - sample: sampleText, - }} - classes={classes} - /> - ); - } -} diff --git a/packages/pie-toolbox/src/rubric.js b/packages/pie-toolbox/src/rubric.js deleted file mode 100644 index 630c5a95a..000000000 --- a/packages/pie-toolbox/src/rubric.js +++ /dev/null @@ -1,3 +0,0 @@ -import { Authoring, RUBRIC_TYPES } from './code/rubric'; - -export { Authoring, RUBRIC_TYPES };