diff --git a/editors/demo/src/HHMI/HHMI.js b/editors/demo/src/HHMI/HHMI.js index 1b9ce865f386cfeb6241f51e97f78f3652137516..4c1d96095280c4c17b57306e3de83d781cecafdc 100644 --- a/editors/demo/src/HHMI/HHMI.js +++ b/editors/demo/src/HHMI/HHMI.js @@ -94,6 +94,30 @@ const initialContent = `<p class="paragraph"></p> <p class="paragraph">first <span id="16ec8f33-db5b-4839-9567-8aa73b776bcf" class="fill-the-gap" answer="">answer1; answer2; answer3</span> second <span id="72f23a71-e774-4834-acba-f357afb6a243" class="fill-the-gap" answer="">answer 4; answer5;</span></p> </div>`; +// const initialContent = { +// type: 'doc', +// content: [ +// { +// type: 'numerical_answer_container', +// attrs: { +// id: 'f9c33d03-68ee-4c27-8a03-5072447fac1a', +// class: 'numerical-answer', +// feedback: '', +// answerType: 'exactAnswer', +// answersExact: [], +// }, +// content: [ +// { +// type: 'paragraph', +// attrs: { +// class: 'paragraph', +// }, +// }, +// ], +// }, +// ], +// }; + const Hhmi = () => { const [stateProps, setStateProps] = useState({ readOnly: false, @@ -168,9 +192,10 @@ const Hhmi = () => { customValues={{ showFeedBack: submitted, testMode }} fileUpload={file => renderImage(file)} value={content} + // targetFormat="JSON" readonly={readOnly} layout={HhmiLayout} - // onChange={source => console.log(source)} + onChange={source => console.log(source)} /> </> ); diff --git a/wax-prosemirror-services/src/FindAndReplaceService/components/ExpandedFindAndReplaceComponent.js b/wax-prosemirror-services/src/FindAndReplaceService/components/ExpandedFindAndReplaceComponent.js index 6c4bb7ac7bdb6e38334f0b4ad703792dbb862a9e..036b75013ad31e45767284dc6da37f1eff0cd128 100644 --- a/wax-prosemirror-services/src/FindAndReplaceService/components/ExpandedFindAndReplaceComponent.js +++ b/wax-prosemirror-services/src/FindAndReplaceService/components/ExpandedFindAndReplaceComponent.js @@ -269,6 +269,7 @@ const ExpandedFindAndReplaceComponent = ({ const results = DocumentHelpers.findMatches( singleView.state.doc, searchValue, + matchCaseOption, ); const { state: { tr }, @@ -370,11 +371,11 @@ const ExpandedFindAndReplaceComponent = ({ <ControlContainer> <ButtonReplace onClick={replace}> {' '} - <Translation label="Replace with" /> + <Translation label="Replace" /> </ButtonReplace> <ButtonReplaceAll onClick={replaceAll}> {' '} - <Translation label="Replace with" /> <Translation label="All" /> + <Translation label="Replace" /> <Translation label="All" /> </ButtonReplaceAll> <PreviousNextContainer> <IconWrapper diff --git a/wax-questions-service/src/FillTheGapQuestionService/schema/fillTheGapContainerNode.js b/wax-questions-service/src/FillTheGapQuestionService/schema/fillTheGapContainerNode.js index 783e2be959e028bf9432f9691ec389ecc0f30624..6f7d74042386967120299be144ad86340e799f6c 100644 --- a/wax-questions-service/src/FillTheGapQuestionService/schema/fillTheGapContainerNode.js +++ b/wax-questions-service/src/FillTheGapQuestionService/schema/fillTheGapContainerNode.js @@ -5,10 +5,7 @@ const fillTheGapContainerNode = { feedback: { default: '' }, }, group: 'block questions', - atom: true, - draggable: false, - selectable: true, - defining: true, + isolating: true, content: 'paragraph+', parseDOM: [ { diff --git a/wax-questions-service/src/MatchingService/schema/matchingContainerNode.js b/wax-questions-service/src/MatchingService/schema/matchingContainerNode.js index 3dfa5d9402991290858cf1f8811288238450d713..b4e6d8490d249317d3fc4e753c0b76bd732c85d5 100644 --- a/wax-questions-service/src/MatchingService/schema/matchingContainerNode.js +++ b/wax-questions-service/src/MatchingService/schema/matchingContainerNode.js @@ -6,9 +6,7 @@ const matchingContainerNode = { feedback: { default: '' }, }, group: 'block questions', - atom: true, - selectable: true, - draggable: false, + isolating: true, content: 'block*', parseDOM: [ { diff --git a/wax-questions-service/src/MultipleDropDownService/schema/multipleDropDownContainerNode.js b/wax-questions-service/src/MultipleDropDownService/schema/multipleDropDownContainerNode.js index 2382e92142fdeccd595ba2e82078f9f50f3095d4..098f83754465150dc1dd46fe41c2d7f6baf472c0 100644 --- a/wax-questions-service/src/MultipleDropDownService/schema/multipleDropDownContainerNode.js +++ b/wax-questions-service/src/MultipleDropDownService/schema/multipleDropDownContainerNode.js @@ -5,10 +5,7 @@ const multipleDropDownContainerNode = { feedback: { default: '' }, }, group: 'block questions', - atom: true, - selectable: false, - draggable: false, - defining: true, + isolating: true, // content: 'paragraph* bulletlist* orderedlist*', content: 'block*', parseDOM: [ diff --git a/wax-questions-service/src/NumericalAnswerService/components/ExactAnswerComponent.js b/wax-questions-service/src/NumericalAnswerService/components/ExactAnswerComponent.js index 070b6156956e6a8ad04fe804545de613414ce072..6b03d59b9e74b940bc9d529ca6a35eb640960660 100644 --- a/wax-questions-service/src/NumericalAnswerService/components/ExactAnswerComponent.js +++ b/wax-questions-service/src/NumericalAnswerService/components/ExactAnswerComponent.js @@ -1,5 +1,7 @@ -import React, { useRef, useState } from 'react'; +/* eslint-disable react/prop-types */ +import React, { useRef, useState, useContext } from 'react'; import styled from 'styled-components'; +import { DocumentHelpers, WaxContext } from 'wax-prosemirror-core'; const AnswerContainer = styled.div` display: flex; @@ -25,53 +27,172 @@ const ValueInnerContainer = styled.div` flex-direction: column; `; -const ExactAnswerComponent = () => { - const [exact, setExact] = useState(''); - const [marginError, setMarginError] = useState(''); +const ResultContainer = styled.div` + display: flex; + flex-direction: column; +`; + +const FinalResult = styled.span` + color: ${props => (props.isCorrect ? ' #008000' : 'red')}; + font-weight: 999; +`; +const ExactAnswerComponent = ({ node, readOnly, testMode, showFeedBack }) => { + const context = useContext(WaxContext); + const [exact, setExact] = useState(node.attrs.answersExact.exactAnswer || ''); + const [marginError, setMarginError] = useState( + node.attrs.answersExact.marginError || '', + ); + const [exactStudent, setExactStudent] = useState( + node.attrs.answerExact || '', + ); const exactRef = useRef(null); const errorRef = useRef(null); + const exactStudentRef = useRef(null); + + const onlyNumbers = value => { + return value + .replace(/[^0-9.]/g, '') + .replace(/(\..*?)\..*/g, '$1') + .replace(/^0[^.]/, '0'); + }; + + const SaveValuesToNode = () => { + const allNodes = getNodes(context.pmViews.main); + allNodes.forEach(singleNode => { + if (singleNode.node.attrs.id === node.attrs.id) { + const obj = { + exactAnswer: onlyNumbers(exactRef.current.value), + marginError: onlyNumbers(errorRef.current.value), + }; + + context.pmViews.main.dispatch( + context.pmViews.main.state.tr.setNodeMarkup( + singleNode.pos, + undefined, + { + ...singleNode.node.attrs, + answersExact: obj, + }, + ), + ); + } + }); + }; const onChangeExact = () => { - setExact(exactRef.current.value); + setExact(onlyNumbers(exactRef.current.value)); + SaveValuesToNode(); }; const onChangeError = () => { - setMarginError(errorRef.current.value); + setMarginError(onlyNumbers(errorRef.current.value)); + SaveValuesToNode(); + }; + + const onChangeExactStudent = () => { + setExactStudent(onlyNumbers(exactStudentRef.current.value)); + const allNodes = getNodes(context.pmViews.main); + allNodes.forEach(singleNode => { + if (singleNode.node.attrs.id === node.attrs.id) { + context.pmViews.main.dispatch( + context.pmViews.main.state.tr.setNodeMarkup( + singleNode.pos, + undefined, + { + ...singleNode.node.attrs, + answerExact: onlyNumbers(exactStudentRef.current.value), + }, + ), + ); + } + }); }; + // SUBMIT + const exactMultMargin = parseFloat((exact * marginError) / 100); + const computedMaxValue = Number(exactMultMargin) + Number(exact); + const computedMinValue = Number(exact) - Number(exactMultMargin); + const isCorrect = !!( + exactStudent <= computedMaxValue && exactStudent >= computedMinValue + ); + return ( <AnswerContainer> - <ValueContainer> - <label htmlFor="exactAnswer"> - <ValueInnerContainer> - <span>Exact Answer</span> - <input - name="exactAnswer" - onChange={onChangeExact} - ref={exactRef} - type="text" - value={exact} - /> - </ValueInnerContainer> - </label> - </ValueContainer> - <ValueContainer> - <label htmlFor="errorAnswer"> - <ValueInnerContainer> - <span>Margin of error (%)</span> - <input - name="errorAnswer" - onChange={onChangeError} - ref={errorRef} - type="text" - value={marginError} - /> - </ValueInnerContainer> - </label> - </ValueContainer> + {!testMode && !showFeedBack && ( + <> + <ValueContainer> + <label htmlFor="exactAnswer"> + <ValueInnerContainer> + <span>Exact Answer</span> + <input + disabled={readOnly} + name="exactAnswer" + onChange={onChangeExact} + ref={exactRef} + type="text" + value={exact} + /> + </ValueInnerContainer> + </label> + </ValueContainer> + <ValueContainer> + <label htmlFor="errorAnswer"> + <ValueInnerContainer> + <span>Margin of error (%)</span> + <input + disabled={readOnly} + name="errorAnswer" + onChange={onChangeError} + ref={errorRef} + type="text" + value={marginError} + /> + </ValueInnerContainer> + </label> + </ValueContainer> + </> + )} + {testMode && ( + <ValueContainer> + <label htmlFor="exactAnswerStudent"> + <ValueInnerContainer> + <span>Exact Answer</span> + <input + name="exactAnswerStudent" + onChange={onChangeExactStudent} + ref={exactStudentRef} + type="text" + value={exactStudent} + /> + </ValueInnerContainer> + </label> + </ValueContainer> + )} + {readOnly && showFeedBack && ( + <ResultContainer> + <span> + Accepted Answer Range: {computedMinValue} - {computedMaxValue} + </span> + <span> + Answer:{' '} + <FinalResult isCorrect={isCorrect}>{exactStudent}</FinalResult> + </span> + </ResultContainer> + )} </AnswerContainer> ); }; +const getNodes = view => { + const allNodes = DocumentHelpers.findBlockNodes(view.state.doc); + const numericalAnswerpContainerNodes = []; + allNodes.forEach(node => { + if (node.node.type.name === 'numerical_answer_container') { + numericalAnswerpContainerNodes.push(node); + } + }); + return numericalAnswerpContainerNodes; +}; + export default ExactAnswerComponent; diff --git a/wax-questions-service/src/NumericalAnswerService/components/NumericalAnswerContainerComponent.js b/wax-questions-service/src/NumericalAnswerService/components/NumericalAnswerContainerComponent.js index 8d05991f482d633391cdfd437b9caf52893a00bb..336863d49c60b6add2592ca84f8157ff86ae2839 100644 --- a/wax-questions-service/src/NumericalAnswerService/components/NumericalAnswerContainerComponent.js +++ b/wax-questions-service/src/NumericalAnswerService/components/NumericalAnswerContainerComponent.js @@ -1,4 +1,4 @@ -import React, { useContext } from 'react'; +import React, { useContext, useEffect, useState } from 'react'; import { WaxContext, DocumentHelpers, Icon } from 'wax-prosemirror-core'; import styled from 'styled-components'; import EditorComponent from '../../MultipleChoiceQuestionService/components/EditorComponent'; @@ -54,10 +54,12 @@ export default ({ node, view, getPos }) => { const { options, pmViews: { main }, + setOption, } = context; const customProps = main.props.customValues; - const { testMode } = customProps; + + const { testMode, showFeedBack } = customProps; const isEditable = main.props.editable(editable => { return editable; @@ -80,12 +82,18 @@ export default ({ node, view, getPos }) => { }); }; + useEffect(() => { + setOption({ + [node.attrs.id]: { numericalAnswer: node.attrs.answerType }, + }); + }, []); + return ( <NumericalAnswerWrapper> <div> {!testMode && !readOnly && ( <NumericalAnswerContainerTool> - <NumericalAnswerDropDownCompontent nodeId={node.attrs.id} /> + <NumericalAnswerDropDownCompontent node={node} /> <ActionButton aria-label="delete this question" onClick={removeQuestion} @@ -100,19 +108,36 @@ export default ({ node, view, getPos }) => { <EditorComponent getPos={getPos} node={node} - type="NumericalAnswer" + QuestionType="NumericalAnswer" view={view} /> <NumericalAnswerOption> - {!options[node.attrs.id] && <>No Type Selected</>} + {options[node.attrs.id]?.numericalAnswer === '' && ( + <>No Type Selected</> + )} {options[node.attrs.id]?.numericalAnswer === 'exactAnswer' && ( - <ExactAnswerComponent /> + <ExactAnswerComponent + node={node} + readOnly={readOnly} + showFeedBack={showFeedBack} + testMode={testMode} + /> )} {options[node.attrs.id]?.numericalAnswer === 'rangeAnswer' && ( - <RangeAnswerComponent /> + <RangeAnswerComponent + node={node} + readOnly={readOnly} + showFeedBack={showFeedBack} + testMode={testMode} + /> )} {options[node.attrs.id]?.numericalAnswer === 'preciseAnswer' && ( - <PreciseAnswerComponent /> + <PreciseAnswerComponent + node={node} + readOnly={readOnly} + showFeedBack={showFeedBack} + testMode={testMode} + /> )} </NumericalAnswerOption> {!testMode && !(readOnly && feedback === '') && ( diff --git a/wax-questions-service/src/NumericalAnswerService/components/NumericalAnswerDropDownCompontent.js b/wax-questions-service/src/NumericalAnswerService/components/NumericalAnswerDropDownCompontent.js index aad0a1c803b8454a53e42549d16b5ac9e7907ca7..87c21f7f92515382c4c00f91096d5ac56cbcaddd 100644 --- a/wax-questions-service/src/NumericalAnswerService/components/NumericalAnswerDropDownCompontent.js +++ b/wax-questions-service/src/NumericalAnswerService/components/NumericalAnswerDropDownCompontent.js @@ -74,7 +74,7 @@ const StyledIcon = styled(Icon)` top: 1px; `; -const NumericalAnswerDropDownCompontent = ({ nodeId }) => { +const NumericalAnswerDropDownCompontent = ({ node }) => { const dropDownOptions = [ { label: 'Exact answer with margin of error', @@ -94,6 +94,8 @@ const NumericalAnswerDropDownCompontent = ({ nodeId }) => { const { activeView, pmViews: { main }, + setOption, + options, } = context; const itemRefs = useRef([]); @@ -109,14 +111,19 @@ const NumericalAnswerDropDownCompontent = ({ nodeId }) => { useEffect(() => { setLabel('Select Type'); + setOption({ + [node.attrs.id]: { numericalAnswer: node.attrs.answerType }, + }); + dropDownOptions.forEach(option => { - if (context.options?.numericalAnswer === option.value) { + if (options[node.attrs.id]?.numericalAnswer === option.value) { setLabel(option.label); } }); }, []); - let isDisabled = false; + const isDisabled = !isEditable; + // if (activeView.props?.type !== 'NumericalAnswer') isDisabled = true; useEffect(() => { if (isDisabled) setIsOpen(false); @@ -161,11 +168,33 @@ const NumericalAnswerDropDownCompontent = ({ nodeId }) => { } }; + const SaveTypeToNode = option => { + const allNodes = getNodes(context.pmViews.main); + allNodes.forEach(singleNode => { + if (singleNode.node.attrs.id === node.attrs.id) { + context.pmViews.main.dispatch( + context.pmViews.main.state.tr.setNodeMarkup( + singleNode.pos, + undefined, + { + ...singleNode.node.attrs, + answerType: option, + answersExact: [], + answersRange: [], + answersPrecise: [], + }, + ), + ); + } + }); + }; + const onChange = option => { - context.setOption({ [nodeId]: { numericalAnswer: option.value } }); - main.dispatch(main.state.tr.setMeta('addToHistory', false)); + context.setOption({ [node.attrs.id]: { numericalAnswer: option.value } }); setLabel(option.label); openCloseMenu(); + SaveTypeToNode(option.value); + activeView.focus(); }; const NumericalAnswerDropDown = useMemo( diff --git a/wax-questions-service/src/NumericalAnswerService/components/PreciseAnswerComponent.js b/wax-questions-service/src/NumericalAnswerService/components/PreciseAnswerComponent.js index 2ab9c1a5ff0467e798aac4b760c333fd2bd9c19d..3ebc0a4ab314d3f509e723d46bb31e19b774a80c 100644 --- a/wax-questions-service/src/NumericalAnswerService/components/PreciseAnswerComponent.js +++ b/wax-questions-service/src/NumericalAnswerService/components/PreciseAnswerComponent.js @@ -1,5 +1,7 @@ -import React, { useRef, useState } from 'react'; +/* eslint-disable react/prop-types */ +import React, { useRef, useState, useContext } from 'react'; import styled from 'styled-components'; +import { DocumentHelpers, WaxContext } from 'wax-prosemirror-core'; const AnswerContainer = styled.div` display: flex; @@ -25,33 +27,146 @@ const ValueInnerContainer = styled.div` flex-direction: column; `; -const PreciseAnswerComponent = () => { - const [precise, setPrecise] = useState(''); +const ResultContainer = styled.div` + display: flex; + flex-direction: column; +`; + +const FinalResult = styled.span` + color: ${props => (props.isCorrect ? ' #008000' : 'red')}; + font-weight: 999; +`; + +const PreciseAnswerComponent = ({ node, readOnly, testMode, showFeedBack }) => { + const context = useContext(WaxContext); + const [precise, setPrecise] = useState( + node.attrs.answersPrecise.preciseAnswer || '', + ); + + const [preciseStudent, setPreciseStudent] = useState( + node.attrs.answerPrecise || '', + ); const preciseRef = useRef(null); + const preciseStudentRef = useRef(null); + + const onlyNumbers = value => { + return value + .replace(/[^0-9.;]/g, '') + .replace(/(\..*?)\..*/g, '$1') + .replace(/^0[^.]/, '0'); + }; + + const SaveValuesToNode = () => { + const allNodes = getNodes(context.pmViews.main); + allNodes.forEach(singleNode => { + if (singleNode.node.attrs.id === node.attrs.id) { + const obj = { + preciseAnswer: onlyNumbers(preciseRef.current.value), + }; + + context.pmViews.main.dispatch( + context.pmViews.main.state.tr.setNodeMarkup( + singleNode.pos, + undefined, + { + ...singleNode.node.attrs, + answersPrecise: obj, + }, + ), + ); + } + }); + }; const onChangePrecice = () => { - setPrecise(preciseRef.current.value); + setPrecise(onlyNumbers(preciseRef.current.value)); + SaveValuesToNode(); }; + const onChangePreciseStudent = () => { + setPreciseStudent(onlyNumbers(preciseStudentRef.current.value)); + const allNodes = getNodes(context.pmViews.main); + allNodes.forEach(singleNode => { + if (singleNode.node.attrs.id === node.attrs.id) { + context.pmViews.main.dispatch( + context.pmViews.main.state.tr.setNodeMarkup( + singleNode.pos, + undefined, + { + ...singleNode.node.attrs, + answerPrecise: onlyNumbers(preciseStudentRef.current.value), + }, + ), + ); + } + }); + }; + + const isCorrect = precise + .split(';') + .find(element => element === preciseStudent.trim()); + return ( <AnswerContainer> - <ValueContainer> - <label htmlFor="preciseAnswer"> - <ValueInnerContainer> - <span>Precise Answer</span> - <input - name="preciseAnswer" - onChange={onChangePrecice} - ref={preciseRef} - type="text" - value={precise} - /> - </ValueInnerContainer> - </label> - </ValueContainer> + {!testMode && !showFeedBack && ( + <ValueContainer> + <label htmlFor="preciseAnswer"> + <ValueInnerContainer> + <span>Precise Answer</span> + <input + disabled={readOnly} + name="preciseAnswer" + onChange={onChangePrecice} + ref={preciseRef} + type="text" + value={precise} + /> + </ValueInnerContainer> + </label> + </ValueContainer> + )} + {testMode && ( + <ValueContainer> + <label htmlFor="exactAnswerStudent"> + <ValueInnerContainer> + <span>Precise Answer</span> + <input + name="exactAnswerStudent" + onChange={onChangePreciseStudent} + ref={preciseStudentRef} + type="text" + value={preciseStudent} + /> + </ValueInnerContainer> + </label> + </ValueContainer> + )} + {readOnly && showFeedBack && ( + <ResultContainer> + <span> + Accepted Answer Range:{' '} + {`(Accepted Answers : ${precise.replaceAll(';', ' -')})`} + </span> + <span> + Answer:{' '} + <FinalResult isCorrect={isCorrect}>{preciseStudent}</FinalResult> + </span> + </ResultContainer> + )} </AnswerContainer> ); }; +const getNodes = view => { + const allNodes = DocumentHelpers.findBlockNodes(view.state.doc); + const numericalAnswerpContainerNodes = []; + allNodes.forEach(node => { + if (node.node.type.name === 'numerical_answer_container') { + numericalAnswerpContainerNodes.push(node); + } + }); + return numericalAnswerpContainerNodes; +}; + export default PreciseAnswerComponent; diff --git a/wax-questions-service/src/NumericalAnswerService/components/RangeAnswerComponent.js b/wax-questions-service/src/NumericalAnswerService/components/RangeAnswerComponent.js index 8b98b87a1f4e5e00fc6b57fa247bec302df202f8..612e0702d33a0adabb8c666dc189e313a2fbdc06 100644 --- a/wax-questions-service/src/NumericalAnswerService/components/RangeAnswerComponent.js +++ b/wax-questions-service/src/NumericalAnswerService/components/RangeAnswerComponent.js @@ -1,5 +1,7 @@ -import React, { useRef, useState } from 'react'; +/* eslint-disable react/prop-types */ +import React, { useRef, useState, useContext } from 'react'; import styled from 'styled-components'; +import { DocumentHelpers, WaxContext } from 'wax-prosemirror-core'; const AnswerContainer = styled.div` display: flex; @@ -25,53 +27,174 @@ const ValueInnerContainer = styled.div` flex-direction: column; `; -const RangeAnswerComponent = () => { - const [minValue, setMinValue] = useState(''); - const [maxValue, setMaxValue] = useState(''); +const ResultContainer = styled.div` + display: flex; + flex-direction: column; +`; + +const FinalResult = styled.span` + color: ${props => (props.isCorrect ? ' #008000' : 'red')}; + font-weight: 999; +`; + +const RangeAnswerComponent = ({ node, readOnly, testMode, showFeedBack }) => { + const context = useContext(WaxContext); + const [minValue, setMinValue] = useState( + node.attrs.answersRange.minAnswer || '', + ); + const [maxValue, setMaxValue] = useState( + node.attrs.answersRange.maxAnswer || '', + ); + + const [rangeStudentValue, setRangeStudentValue] = useState( + node.attrs.answerRange || '', + ); const minRef = useRef(null); const maxRef = useRef(null); + const rangeStudentRef = useRef(null); + + const onlyNumbers = value => { + return value + .replace(/[^0-9.]/g, '') + .replace(/(\..*?)\..*/g, '$1') + .replace(/^0[^.]/, '0'); + }; + + const SaveValuesToNode = () => { + const allNodes = getNodes(context.pmViews.main); + allNodes.forEach(singleNode => { + if (singleNode.node.attrs.id === node.attrs.id) { + const obj = { + minAnswer: onlyNumbers(minRef.current.value), + maxAnswer: onlyNumbers(maxRef.current.value), + }; + + context.pmViews.main.dispatch( + context.pmViews.main.state.tr.setNodeMarkup( + singleNode.pos, + undefined, + { + ...singleNode.node.attrs, + answersRange: obj, + }, + ), + ); + } + }); + }; const onChangeMin = () => { - setMinValue(minRef.current.value); + setMinValue(onlyNumbers(minRef.current.value)); + SaveValuesToNode(); }; const onChangeMax = () => { - setMaxValue(maxRef.current.value); + setMaxValue(onlyNumbers(maxRef.current.value)); + SaveValuesToNode(); + }; + + const onChangeRangeStudent = () => { + setRangeStudentValue(onlyNumbers(rangeStudentRef.current.value)); + const allNodes = getNodes(context.pmViews.main); + allNodes.forEach(singleNode => { + if (singleNode.node.attrs.id === node.attrs.id) { + context.pmViews.main.dispatch( + context.pmViews.main.state.tr.setNodeMarkup( + singleNode.pos, + undefined, + { + ...singleNode.node.attrs, + answerRange: onlyNumbers(rangeStudentRef.current.value), + }, + ), + ); + } + }); }; + // SUBMIT + + const isCorrect = !!( + rangeStudentValue <= maxValue && rangeStudentValue >= minValue + ); + return ( <AnswerContainer> - <ValueContainer> - <label htmlFor="minAnswer"> - <ValueInnerContainer> - <span>Min</span> - <input - name="minAnswer" - onChange={onChangeMin} - ref={minRef} - type="text" - value={minValue} - /> - </ValueInnerContainer> - </label> - </ValueContainer> - <ValueContainer> - <label htmlFor="maxAnswer"> - <ValueInnerContainer> - <span>Max</span> - <input - name="maxAnswer" - onChange={onChangeMax} - ref={maxRef} - type="text" - value={maxValue} - /> - </ValueInnerContainer> - </label> - </ValueContainer> + {!testMode && !showFeedBack && ( + <> + <ValueContainer> + <label htmlFor="minAnswer"> + <ValueInnerContainer> + <span>Min</span> + <input + disabled={readOnly} + name="minAnswer" + onChange={onChangeMin} + ref={minRef} + type="text" + value={minValue} + /> + </ValueInnerContainer> + </label> + </ValueContainer> + <ValueContainer> + <label htmlFor="maxAnswer"> + <ValueInnerContainer> + <span>Max</span> + <input + disabled={readOnly} + name="maxAnswer" + onChange={onChangeMax} + ref={maxRef} + type="text" + value={maxValue} + /> + </ValueInnerContainer> + </label> + </ValueContainer> + </> + )} + {testMode && ( + <ValueContainer> + <label htmlFor="exactAnswerStudent"> + <ValueInnerContainer> + <span>Answer</span> + <input + name="exactAnswerStudent" + onChange={onChangeRangeStudent} + ref={rangeStudentRef} + type="text" + value={rangeStudentValue} + /> + </ValueInnerContainer> + </label> + </ValueContainer> + )} + {readOnly && showFeedBack && ( + <ResultContainer> + <span> + Accepted Answer Range: {minValue} - {maxValue} + </span> + <span> + Answer:{' '} + <FinalResult isCorrect={isCorrect}>{rangeStudentValue}</FinalResult> + </span> + </ResultContainer> + )} </AnswerContainer> ); }; +const getNodes = view => { + const allNodes = DocumentHelpers.findBlockNodes(view.state.doc); + const numericalAnswerpContainerNodes = []; + allNodes.forEach(node => { + if (node.node.type.name === 'numerical_answer_container') { + numericalAnswerpContainerNodes.push(node); + } + }); + return numericalAnswerpContainerNodes; +}; + export default RangeAnswerComponent; diff --git a/wax-questions-service/src/NumericalAnswerService/schema/NumericalAnswerContainerNode.js b/wax-questions-service/src/NumericalAnswerService/schema/NumericalAnswerContainerNode.js index a98ec716b4e15bcdefe16103d1288006e0136d81..0ba378786575e6a9cb566504b8f02db237cafd8a 100644 --- a/wax-questions-service/src/NumericalAnswerService/schema/NumericalAnswerContainerNode.js +++ b/wax-questions-service/src/NumericalAnswerService/schema/NumericalAnswerContainerNode.js @@ -4,24 +4,51 @@ const NumericalAnswerContainerNode = { class: { default: 'numerical-answer' }, feedback: { default: '' }, answerType: { default: '' }, + answersExact: { default: [] }, + answerExact: { default: '' }, + answersRange: { default: [] }, + answerRange: { default: '' }, + answersPrecise: { default: [] }, + answerPrecise: { default: '' }, }, group: 'block questions', - atom: true, + isolating: true, content: 'block+', parseDOM: [ { tag: 'div.numerical-answer', getAttrs(dom) { return { + answersExact: JSON.parse(dom.getAttribute('answersExact')), + answerExact: dom.getAttribute('answerExact'), + answersRange: JSON.parse(dom.getAttribute('answersRange')), + answerRange: dom.getAttribute('answerRange'), + answersPrecise: JSON.parse(dom.getAttribute('answersPrecise')), + answerPrecise: dom.getAttribute('answerPrecise'), id: dom.getAttribute('id'), class: dom.getAttribute('class'), feedback: dom.getAttribute('feedback'), + answerType: dom.getAttribute('answerType'), }; }, }, ], toDOM(node) { - return ['div', node.attrs, 0]; + return [ + 'div', + { + answerType: node.attrs.answerType, + answersExact: JSON.stringify(node.attrs.answersExact), + answerExact: node.attrs.answerExact, + answersRange: JSON.stringify(node.attrs.answersRange), + answerRange: node.attrs.answerRange, + answersPrecise: JSON.stringify(node.attrs.answersPrecise), + answerPrecise: node.attrs.answerPrecise, + id: node.attrs.id, + class: node.attrs.class, + feedback: node.attrs.feedback, + }, + ]; }, };