From 43672c8165f46d2417c73d80e1edc345651f74fe Mon Sep 17 00:00:00 2001 From: Weining Li Date: Fri, 22 Jan 2021 12:48:35 -0500 Subject: [PATCH 1/4] + option to prevent submitting with incomplete questions --- src/components/creator/creator-app.js | 11 +- .../creator-incomplete-attempt-modal.js | 39 +++++ src/components/player/player-app.js | 11 +- src/components/player/question-select.js | 10 +- src/components/player/token-drawer.js | 140 ------------------ src/components/player/warning-modal.js | 23 ++- src/creator-store.js | 15 +- src/creator.scss | 58 ++++---- src/demo.json | 1 + src/player-store.js | 14 +- 10 files changed, 128 insertions(+), 194 deletions(-) create mode 100644 src/components/creator/creator-incomplete-attempt-modal.js delete mode 100644 src/components/player/token-drawer.js diff --git a/src/components/creator/creator-app.js b/src/components/creator/creator-app.js index a02a476..5da2902 100644 --- a/src/components/creator/creator-app.js +++ b/src/components/creator/creator-app.js @@ -9,6 +9,7 @@ import CreatorTutorial from './creator-tutorial'; import CreatorHintsModal from './creator-hints-modal'; import CreatorFakeoutModal from './creator-fakeout-modal' import CreatorBankModal from './creator-bank-modal' +import CreatorIncompleteAttemptModal from './creator-incomplete-attempt-modal' import CreatorErrorModal from './creator-error-modal' const CreatorApp = (props) => { @@ -127,6 +128,7 @@ const CreatorApp = (props) => { options: { legend: global.state.legend, enableQuestionBank: global.state.enableQuestionBank, + allowIncompleteAttempt: global.state.allowIncompleteAttempt, numAsk: global.state.numAsk } } @@ -163,6 +165,10 @@ const CreatorApp = (props) => { dispatch({type: 'toggle_bank_modal', payload: {}}) } + const toggleIncompleteAttempt = () => { + dispatch({type: 'toggle_incomplete_attempt_modal', payload: {}}) + } + const toggleHintModal = () => { dispatch({type: 'toggle_hint_modal'}) } @@ -184,14 +190,17 @@ const CreatorApp = (props) => { enableQuestionBank={global.state.enableQuestionBank} numAsk={global.state.numAsk} questionCount={global.state.items.length}> + - +
+
diff --git a/src/components/creator/creator-incomplete-attempt-modal.js b/src/components/creator/creator-incomplete-attempt-modal.js new file mode 100644 index 0000000..30a18dc --- /dev/null +++ b/src/components/creator/creator-incomplete-attempt-modal.js @@ -0,0 +1,39 @@ +import React, {useContext} from 'react' +import { store } from '../../creator-store' + +const CreatorIncompleteAttempModal = (props) => { + + const global = useContext(store) + const dispatch = global.dispatch + + const handleToggleIncompleteAttempt = (event) => { + dispatch({type:'toggle_incomplete_attempt', payload: event.target.value == 'true' ? true : false}) + } + + const dismiss = () => { + dispatch({type: 'toggle_incomplete_attempt_modal'}) + } + + return ( +
+
+

Incomplete Attempts

+

Allow students to submit a widget without having answered every question?

+ + + + Yes + + + + + No + + +
+
+
+ ) +} + +export default CreatorIncompleteAttempModal diff --git a/src/components/player/player-app.js b/src/components/player/player-app.js index 985d56b..b0d78bb 100644 --- a/src/components/player/player-app.js +++ b/src/components/player/player-app.js @@ -40,12 +40,15 @@ const PlayerApp = (props) => { const emptyQuestionCheck = () => { let isEmpty = false + let i = 0; for (let item of global.state.items) { - if (item.sorted.length <= 0) { + if (item.sorted.length <= 0) { + dispatch({type: 'select_question', payload: i}) isEmpty = true break } + i++ } return isEmpty } @@ -81,7 +84,9 @@ const PlayerApp = (props) => { return(
- +
{global.state.title} @@ -94,7 +99,7 @@ const PlayerApp = (props) => {

{questionText}

0 && + global.state.items[global.state.currentIndex]?.attemptsUsed > 0 && global.state.items[global.state.currentIndex]?.attemptsUsed < global.state.items[global.state.currentIndex]?.attempts && global.state.items[global.state.currentIndex]?.responseState != 'correct' && global.state.items[global.state.currentIndex]?.responseState != 'incorrect-no-attempts' && diff --git a/src/components/player/question-select.js b/src/components/player/question-select.js index f61c5ae..c4d1520 100644 --- a/src/components/player/question-select.js +++ b/src/components/player/question-select.js @@ -5,7 +5,7 @@ const QuestionSelect = (props) => { const global = useContext(store) const dispatch = global.dispatch - + const [state, setState] = useState({paginateMin: 0, paginateMax: 8, visibleQuestions: []}) const currentIndex = global.state.currentIndex @@ -14,7 +14,7 @@ const QuestionSelect = (props) => { let questionList = global.state.items.map((item, index) => { return }) - + // if the list of questions gets too long, we have to start computing the subset to display if (questionList.length > 10) { if (currentIndex < state.paginateMin) { @@ -23,7 +23,7 @@ const QuestionSelect = (props) => { else if (currentIndex > state.paginateMax) { setState(state => ({...state, paginateMin: currentIndex - 8, paginateMax: currentIndex})) } - + setState(state => ({...state, visibleQuestions: [ ...questionList.slice(state.paginateMin, currentIndex), ...questionList.slice(currentIndex, state.paginateMax + 1) @@ -37,7 +37,7 @@ const QuestionSelect = (props) => { const selectQuestion = (index) => { dispatch({type: 'select_question', payload: index}) } - + return (
@@ -47,4 +47,4 @@ const QuestionSelect = (props) => { ) } -export default QuestionSelect \ No newline at end of file +export default QuestionSelect diff --git a/src/components/player/token-drawer.js b/src/components/player/token-drawer.js deleted file mode 100644 index 968a49f..0000000 --- a/src/components/player/token-drawer.js +++ /dev/null @@ -1,140 +0,0 @@ -import React, {useContext} from 'react' -import Token from './token' -import { store } from '../../player-store' - -const TokenDrawer = (props) => { - - const global = useContext(store) - const dispatch = global.dispatch - - const paginate = () => { - dispatch({type: 'paginate_question_forward'}) - } - - const handleCheckAnswer = () => { - let item = global.state.items[global.state.currentIndex] - - // attempt limit already reached, assume call is invalid - if (props.responseState == 'incorrect-no-attempts') return - - let response = verify(item) - let state = 'none' - - if (!response) { - if ((props.attemptLimit - 1) > props.attemptsUsed) { - state = 'incorrect-attempts-remaining' - } - else { - state = 'incorrect-no-attempts' - } - } - else { - state = 'correct' - } - - dispatch({type: 'attempt_submit', payload: { - questionIndex: global.state.currentIndex, - response: state - }}) - } - - const verify = (item) => { - - if (item.sorted.length != item.correctPhrase.length) { - return false - } - - for (let i = 0; i < item.sorted.length; i++) { - - if (item.displayPref == 'word') { - if (item.sorted[i].value != item.correctPhrase[i].value || item.sorted[i].legend != item.sorted[i].legend) return false - } - else if (item.displayPref == 'legend') { - if (item.sorted[i].legend != item.correctPhrase[i].legend) return false - } - } - return true - } - - let tokenList = props.phrase?.map((token, index) => { - return = props.attemptLimit)}> - - }) - - let isLastQuestion = global.state.currentIndex == global.state.items.length - 1 - - let currentResponseText = '' - - let remaining = props.attemptLimit - props.attemptsUsed - - switch (props.responseState) { - case 'ready': - - if (isLastQuestion && remaining > 0) { - currentResponseText = You have {remaining} attempt{remaining > 1 ? 's' : ''} remaining. Select Check Answer to check your answer, or select Submit at the top-right for scoring. - } - else if (isLastQuestion) { - currentResponseText = When you're ready, select Submit at the top-right for scoring or go back and review your answers. - } - else { - currentResponseText = You have {remaining} attempt{remaining > 1 ? 's' : ''} remaining. Select Check Answer to check your answer, or select Next Question to continue. - } - break - case 'pending': - currentResponseText = PENDING - break - case 'incorrect-attempts-remaining': - currentResponseText = That's not quite right. You have {remaining} attempt{remaining > 1 ? 's' : ''} remaining. - break - case 'incorrect-no-attempts': - if (isLastQuestion) { - currentResponseText = That's not quite right. You've exhausted your attempts for this question. When you're ready, select Submit at the top-right for scoring or go back and review your answers. - } - else { - currentResponseText = That's not quite right. You've exhausted your attempts for this question. Select Next Question to continue. - } - break - case 'correct': - if (isLastQuestion) { - currentResponseText = Nice work! You aced it. When you're ready, select Submit at the top-right for scoring or go back and review your answers. - } - else - { - currentResponseText = Nice work! You aced it. Select Next Question to continue. - } - break - case 'none': - default: - currentResponseText = NONE - break - } - - return( -
- {tokenList} -
-
- {currentResponseText} -
-
- - -
-
-
- ) -} - -export default TokenDrawer diff --git a/src/components/player/warning-modal.js b/src/components/player/warning-modal.js index 729b2aa..4432c97 100644 --- a/src/components/player/warning-modal.js +++ b/src/components/player/warning-modal.js @@ -2,7 +2,7 @@ import React, {useContext} from 'react' import { store } from '../../player-store' const WarningModal = (props) => { - + console.log(props) const global = useContext(store) const dispatch = global.dispatch @@ -14,11 +14,22 @@ const WarningModal = (props) => {
You still have unfinished questions. -

Are you sure you want to submit?

-
- - -
+ {props.allowIncompleteAttempt ? +
+

Please answer all questions before submitting.

+
+ +
+
+ : +
+

Are you sure you want to submit?

+
+ + +
+
+ }
diff --git a/src/creator-store.js b/src/creator-store.js index 4522cae..0c27c72 100644 --- a/src/creator-store.js +++ b/src/creator-store.js @@ -10,6 +10,7 @@ const init = { showHintModal: false, showFakeoutModal: false, showBankModal: false, + showIncompleteAttemptModal: false, showErrorModal: false, errors: [], selectedTokenIndex: -1, @@ -33,6 +34,7 @@ const init = { ], numAsk: 1, enableQuestionBank: false, + allowIncompleteAttempt: true, showLegend: false, legendColorPickerTarget: -1, onboarding: true, @@ -63,7 +65,8 @@ const importFromQset = (qset) => { items: items, legend: qset.options.legend, numAsk: qset.options.numAsk, - enableQuestionBank: qset.options.enableQuestionBank + enableQuestionBank: qset.options.enableQuestionBank, + allowIncompleteAttempt: qset.options.allowIncompleteAttempt } } @@ -134,7 +137,7 @@ const questionItemReducer = (items, action) => { ...item, attempts: parseInt(action.payload.pref) } - + } else return item }) @@ -288,7 +291,7 @@ const StateProvider = ( { children } ) => { return {...state, requireInit: false} case 'init-existing': let imported = importFromQset(action.payload.qset) - return {...state, title: action.payload.title, items: imported.items, legend: imported.legend, numAsk: imported.numAsk, enableQuestionBank: imported.enableQuestionBank, requireInit: false, onboarding: false, showTokenTutorial: false} + return {...state, title: action.payload.title, items: imported.items, legend: imported.legend, numAsk: imported.numAsk, enableQuestionBank: imported.enableQuestionBank, allowIncompleteAttempt: imported.allowIncompleteAttempt, requireInit: false, onboarding: false, showTokenTutorial: false} case 'dismiss_tutorial': return {...state, showTutorial: false} case 'toggle_token_tutorial': @@ -309,7 +312,7 @@ const StateProvider = ( { children } ) => { case 'phrase_input_to_token': return {...state, items: questionItemReducer(state.items, action), showTokenTutorial: false} case 'phrase_token_to_input': - return {...state, items: questionItemReducer(state.items, action), selectedTokenIndex: action.payload.phraseIndex == state.selectedTokenIndex ? -1 : state.selectedTokenIndex } + return {...state, items: questionItemReducer(state.items, action), selectedTokenIndex: action.payload.phraseIndex == state.selectedTokenIndex ? -1 : state.selectedTokenIndex } case 'fakeout_token_to_input': return {...state, items: questionItemReducer(state.items, action), selectedFakeoutIndex: action.payload.fakeoutIndex == state.selectedFakeoutIndex ? -1 : state.selectedFakeoutIndex } case 'phrase_token_type_select': @@ -341,12 +344,16 @@ const StateProvider = ( { children } ) => { return {...state, showFakeoutModal: !state.showFakeoutModal} case 'toggle_bank_modal': return {...state, showBankModal: !state.showBankModal} + case 'toggle_incomplete_attempt_modal': + return {...state, showIncompleteAttemptModal: !state.showIncompleteAttemptModal} case 'toggle_error_modal': return {...state, errors: action.payload.error, showErrorModal: !state.showErrorModal} case 'update_num_ask': return {...state, numAsk: action.payload} case 'toggle_ask_limit': return {...state, enableQuestionBank: action.payload} + case 'toggle_incomplete_attempt': + return {...state, allowIncompleteAttempt: action.payload} default: throw new Error('Base reducer: this action type was not defined') } diff --git a/src/creator.scss b/src/creator.scss index 5dd36e8..41c6160 100644 --- a/src/creator.scss +++ b/src/creator.scss @@ -32,14 +32,14 @@ div.startupTooltip { &.show { display: block; } - + } .creator-header { position: fixed; top: 0; z-index: 9; - + input { width: 400px; margin-top: 5px; @@ -75,7 +75,7 @@ div.startupTooltip { font-weight: bold; } - .toggleLegend, .toggleBank { + .toggleLegend, .toggleBank, .toggleIncompleteAttempt { float: right; padding: 8px 28px; outline: none; @@ -86,7 +86,7 @@ div.startupTooltip { border: solid 1px $colorAccentHover; font-weight: bold; - &.toggleBank { + &.toggleBank, &.toggleIncompleteAttempt { margin-right: 10px; } @@ -122,7 +122,7 @@ div.startupTooltip { margin: 0 0 15px 0; border-width: 0 0 2px 0; border-color: $colorInput; - + font-size: 1.1em; &:focus { @@ -314,12 +314,12 @@ div.startupTooltip { display: block; position: relative; min-height: 24px; - + padding: 0 10px 0 30px; margin-bottom: 5px; - + font-size: 0.8em; - + input { position: absolute; top: 0px; @@ -328,10 +328,10 @@ div.startupTooltip { height: 20px; opacity: 0; z-index: 10; - + cursor: pointer; } - + .radio-overlay { position: absolute; display: inline-block; @@ -339,13 +339,13 @@ div.startupTooltip { height: 20px; top: 0px; left: 0px; - + margin-right: 10px; - + background: #fff; border: solid 1px #000; border-radius: 12px; - + &.selected { background: $colorAccent; border-color: #000; @@ -387,7 +387,7 @@ div.startupTooltip { margin-right: 10px; margin-bottom: 10px; padding: 5px 10px; - + } } } @@ -431,7 +431,7 @@ div.startupTooltip { &:hover { background: $colorAccentHover; } - + header { display: inline-block; margin: 12px 10px 12px 0; @@ -447,7 +447,7 @@ div.startupTooltip { float: right; } } - + } } @@ -660,55 +660,55 @@ section.fakeout-builder { .legend-item { margin-bottom: 15px; - + .item-color { display: inline-block; width:25px; height: 25px; border-style: solid; border-radius: 2px; - + margin-right: 10px; - + vertical-align: middle; - + &.selected { border: solid 2px #fff; } } - + input { display: inline-block; background: none; outline: none; border: none; border-bottom: solid 2px #ffffff; - + margin: 0; padding: 0; vertical-align: middle; - + font-size: 1.1em; - + color: #ffffff; - + } - + .remove-item { display: inline-block; outline: none; border: none; vertical-align: middle; - + margin-left: 10px; font-size: 1.1em; - + &:hover { background: $colorAccentHover; } } } - } + } .legend-color-picker-wrapper { position: absolute; diff --git a/src/demo.json b/src/demo.json index c3b4125..81fc3d1 100644 --- a/src/demo.json +++ b/src/demo.json @@ -293,6 +293,7 @@ } ], "enableQuestionBank": false, + "allowIncompleteAttempt": true, "numAsk": 1 } } diff --git a/src/player-store.js b/src/player-store.js index 528266c..80e6165 100644 --- a/src/player-store.js +++ b/src/player-store.js @@ -10,6 +10,7 @@ const init = { showWarning: false, numAsk: 1, enableQuestionBank: "no", + allowIncompleteAttempt: "no", questionsAsked: [] } @@ -44,7 +45,7 @@ const importFromQset = (qset) => { let fakes = item.options.fakes.map((token) => { return {...token, status: 'unsorted', fakeout: true, id: createTokenKey()}}) let reals = item.answers[0].options.phrase.map((token) => { return {...token, status: 'unsorted', fakeout: false, id: createTokenKey()} }) - + return { question: item.questions[0].text, answer: item.answers[0].text, @@ -63,6 +64,7 @@ const importFromQset = (qset) => { legend: qset.options.legend, numAsk: qset.options.numAsk, enableQuestionBank: qset.options.enableQuestionBank, + allowIncompleteAttempt: qset.options.allowIncompleteAttempt, questionsAsked: [] } } @@ -84,9 +86,9 @@ const calcResponseState = (item) => { state = 'pending' } else state = 'ready' - } + } break - + case 'pending': if (item.fakeout.length == 0 && item.phrase.length == 0) { state = 'ready' @@ -142,7 +144,7 @@ const tokenSortedPhraseReducer = (list, action) => { ...list.slice(0, action.payload.tokenIndex), ...list.slice(action.payload.tokenIndex + 1) ] - + return sorted.map((token) => ({ ...token, reqPositionUpdate: true @@ -179,13 +181,13 @@ const tokenSortedPhraseReducer = (list, action) => { }, ...list.slice(action.payload.targetIndex) ] - + return sorted.map((token) => ({ ...token, reqPositionUpdate: true }) ) - } + } case 'response_token_rearrange': let target = action.payload.targetIndex if (action.payload.originIndex < target) target-- From 414aca8c9bc04ddbb9a2d500e33bb511bbbe9acb Mon Sep 17 00:00:00 2001 From: Weining Li Date: Fri, 22 Jan 2021 14:30:12 -0500 Subject: [PATCH 2/4] oops misssed a file? --- src/components/player/token-drawer.js | 140 ++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 src/components/player/token-drawer.js diff --git a/src/components/player/token-drawer.js b/src/components/player/token-drawer.js new file mode 100644 index 0000000..968a49f --- /dev/null +++ b/src/components/player/token-drawer.js @@ -0,0 +1,140 @@ +import React, {useContext} from 'react' +import Token from './token' +import { store } from '../../player-store' + +const TokenDrawer = (props) => { + + const global = useContext(store) + const dispatch = global.dispatch + + const paginate = () => { + dispatch({type: 'paginate_question_forward'}) + } + + const handleCheckAnswer = () => { + let item = global.state.items[global.state.currentIndex] + + // attempt limit already reached, assume call is invalid + if (props.responseState == 'incorrect-no-attempts') return + + let response = verify(item) + let state = 'none' + + if (!response) { + if ((props.attemptLimit - 1) > props.attemptsUsed) { + state = 'incorrect-attempts-remaining' + } + else { + state = 'incorrect-no-attempts' + } + } + else { + state = 'correct' + } + + dispatch({type: 'attempt_submit', payload: { + questionIndex: global.state.currentIndex, + response: state + }}) + } + + const verify = (item) => { + + if (item.sorted.length != item.correctPhrase.length) { + return false + } + + for (let i = 0; i < item.sorted.length; i++) { + + if (item.displayPref == 'word') { + if (item.sorted[i].value != item.correctPhrase[i].value || item.sorted[i].legend != item.sorted[i].legend) return false + } + else if (item.displayPref == 'legend') { + if (item.sorted[i].legend != item.correctPhrase[i].legend) return false + } + } + return true + } + + let tokenList = props.phrase?.map((token, index) => { + return = props.attemptLimit)}> + + }) + + let isLastQuestion = global.state.currentIndex == global.state.items.length - 1 + + let currentResponseText = '' + + let remaining = props.attemptLimit - props.attemptsUsed + + switch (props.responseState) { + case 'ready': + + if (isLastQuestion && remaining > 0) { + currentResponseText = You have {remaining} attempt{remaining > 1 ? 's' : ''} remaining. Select Check Answer to check your answer, or select Submit at the top-right for scoring. + } + else if (isLastQuestion) { + currentResponseText = When you're ready, select Submit at the top-right for scoring or go back and review your answers. + } + else { + currentResponseText = You have {remaining} attempt{remaining > 1 ? 's' : ''} remaining. Select Check Answer to check your answer, or select Next Question to continue. + } + break + case 'pending': + currentResponseText = PENDING + break + case 'incorrect-attempts-remaining': + currentResponseText = That's not quite right. You have {remaining} attempt{remaining > 1 ? 's' : ''} remaining. + break + case 'incorrect-no-attempts': + if (isLastQuestion) { + currentResponseText = That's not quite right. You've exhausted your attempts for this question. When you're ready, select Submit at the top-right for scoring or go back and review your answers. + } + else { + currentResponseText = That's not quite right. You've exhausted your attempts for this question. Select Next Question to continue. + } + break + case 'correct': + if (isLastQuestion) { + currentResponseText = Nice work! You aced it. When you're ready, select Submit at the top-right for scoring or go back and review your answers. + } + else + { + currentResponseText = Nice work! You aced it. Select Next Question to continue. + } + break + case 'none': + default: + currentResponseText = NONE + break + } + + return( +
+ {tokenList} +
+
+ {currentResponseText} +
+
+ + +
+
+
+ ) +} + +export default TokenDrawer From a185dd5a9dea7c741849706aad0055434224e704 Mon Sep 17 00:00:00 2001 From: Corey Peterson Date: Wed, 26 May 2021 14:36:32 -0400 Subject: [PATCH 3/4] Refactored incompleteAttempt to submissionSettings. Polish. Added backwards compatibility. --- src/components/creator/creator-app.js | 14 +++--- .../creator-incomplete-attempt-modal.js | 39 ---------------- .../creator-submission-settings-modal.js | 45 +++++++++++++++++++ src/components/player/player-app.js | 2 +- src/components/player/warning-modal.js | 3 +- src/creator-store.js | 16 +++---- src/creator.scss | 4 +- src/demo.json | 2 +- src/player-store.js | 6 +-- 9 files changed, 68 insertions(+), 63 deletions(-) delete mode 100644 src/components/creator/creator-incomplete-attempt-modal.js create mode 100644 src/components/creator/creator-submission-settings-modal.js diff --git a/src/components/creator/creator-app.js b/src/components/creator/creator-app.js index 5da2902..9300ebd 100644 --- a/src/components/creator/creator-app.js +++ b/src/components/creator/creator-app.js @@ -9,7 +9,7 @@ import CreatorTutorial from './creator-tutorial'; import CreatorHintsModal from './creator-hints-modal'; import CreatorFakeoutModal from './creator-fakeout-modal' import CreatorBankModal from './creator-bank-modal' -import CreatorIncompleteAttemptModal from './creator-incomplete-attempt-modal' +import CreatorSubmissionSettingsModal from './creator-submission-settings-modal' import CreatorErrorModal from './creator-error-modal' const CreatorApp = (props) => { @@ -128,7 +128,7 @@ const CreatorApp = (props) => { options: { legend: global.state.legend, enableQuestionBank: global.state.enableQuestionBank, - allowIncompleteAttempt: global.state.allowIncompleteAttempt, + requireAllQuestions: global.state.requireAllQuestions, numAsk: global.state.numAsk } } @@ -165,8 +165,8 @@ const CreatorApp = (props) => { dispatch({type: 'toggle_bank_modal', payload: {}}) } - const toggleIncompleteAttempt = () => { - dispatch({type: 'toggle_incomplete_attempt_modal', payload: {}}) + const toggleSubmissionSettings = () => { + dispatch({type: 'toggle_submission_settings_modal', payload: {}}) } const toggleHintModal = () => { @@ -190,8 +190,8 @@ const CreatorApp = (props) => { enableQuestionBank={global.state.enableQuestionBank} numAsk={global.state.numAsk} questionCount={global.state.items.length}> - + @@ -200,7 +200,7 @@ const CreatorApp = (props) => { - +
diff --git a/src/components/creator/creator-incomplete-attempt-modal.js b/src/components/creator/creator-incomplete-attempt-modal.js deleted file mode 100644 index 30a18dc..0000000 --- a/src/components/creator/creator-incomplete-attempt-modal.js +++ /dev/null @@ -1,39 +0,0 @@ -import React, {useContext} from 'react' -import { store } from '../../creator-store' - -const CreatorIncompleteAttempModal = (props) => { - - const global = useContext(store) - const dispatch = global.dispatch - - const handleToggleIncompleteAttempt = (event) => { - dispatch({type:'toggle_incomplete_attempt', payload: event.target.value == 'true' ? true : false}) - } - - const dismiss = () => { - dispatch({type: 'toggle_incomplete_attempt_modal'}) - } - - return ( -
-
-

Incomplete Attempts

-

Allow students to submit a widget without having answered every question?

- - - - Yes - - - - - No - - -
-
-
- ) -} - -export default CreatorIncompleteAttempModal diff --git a/src/components/creator/creator-submission-settings-modal.js b/src/components/creator/creator-submission-settings-modal.js new file mode 100644 index 0000000..ea1629f --- /dev/null +++ b/src/components/creator/creator-submission-settings-modal.js @@ -0,0 +1,45 @@ +import React, {useContext} from 'react' +import { store } from '../../creator-store' + +const CreatorSubmissionSettingsModal = (props) => { + + const global = useContext(store) + const dispatch = global.dispatch + + const handleToggleSubmissionSettings = (event) => { + dispatch({type:'toggle_require_all_questions', payload: event.target.value == 'true' ? true : false}) + } + + const dismiss = () => { + dispatch({type: 'toggle_submission_settings_modal'}) + } + + return ( +
+
+

Submission Settings

+ + Require all questions? + + + + Yes + + + + + No + + + + If enabled, students must respond to all questions before submission by sorting at least one token per question. Empty responses are not allowed. + + + +
+
+
+ ) +} + +export default CreatorSubmissionSettingsModal diff --git a/src/components/player/player-app.js b/src/components/player/player-app.js index b0d78bb..9efbd8b 100644 --- a/src/components/player/player-app.js +++ b/src/components/player/player-app.js @@ -86,7 +86,7 @@ const PlayerApp = (props) => {
+ requireAllQuestions={props.qset.options.requireAllQuestions}>
{global.state.title} diff --git a/src/components/player/warning-modal.js b/src/components/player/warning-modal.js index 4432c97..02ed1a6 100644 --- a/src/components/player/warning-modal.js +++ b/src/components/player/warning-modal.js @@ -2,7 +2,6 @@ import React, {useContext} from 'react' import { store } from '../../player-store' const WarningModal = (props) => { - console.log(props) const global = useContext(store) const dispatch = global.dispatch @@ -14,7 +13,7 @@ const WarningModal = (props) => {
You still have unfinished questions. - {props.allowIncompleteAttempt ? + {props.requireAllQuestions ?

Please answer all questions before submitting.

diff --git a/src/creator-store.js b/src/creator-store.js index 0c27c72..688f396 100644 --- a/src/creator-store.js +++ b/src/creator-store.js @@ -10,7 +10,7 @@ const init = { showHintModal: false, showFakeoutModal: false, showBankModal: false, - showIncompleteAttemptModal: false, + showSubmissionSettingsModal: false, showErrorModal: false, errors: [], selectedTokenIndex: -1, @@ -34,7 +34,7 @@ const init = { ], numAsk: 1, enableQuestionBank: false, - allowIncompleteAttempt: true, + requireAllQuestions: true, showLegend: false, legendColorPickerTarget: -1, onboarding: true, @@ -66,7 +66,7 @@ const importFromQset = (qset) => { legend: qset.options.legend, numAsk: qset.options.numAsk, enableQuestionBank: qset.options.enableQuestionBank, - allowIncompleteAttempt: qset.options.allowIncompleteAttempt + requireAllQuestions: qset.options.requireAllQuestions ? qset.options.requireAllQuestions : false // this value will not exist for older qsets } } @@ -291,7 +291,7 @@ const StateProvider = ( { children } ) => { return {...state, requireInit: false} case 'init-existing': let imported = importFromQset(action.payload.qset) - return {...state, title: action.payload.title, items: imported.items, legend: imported.legend, numAsk: imported.numAsk, enableQuestionBank: imported.enableQuestionBank, allowIncompleteAttempt: imported.allowIncompleteAttempt, requireInit: false, onboarding: false, showTokenTutorial: false} + return {...state, title: action.payload.title, items: imported.items, legend: imported.legend, numAsk: imported.numAsk, enableQuestionBank: imported.enableQuestionBank, requireAllQuestions: imported.requireAllQuestions, requireInit: false, onboarding: false, showTokenTutorial: false} case 'dismiss_tutorial': return {...state, showTutorial: false} case 'toggle_token_tutorial': @@ -344,16 +344,16 @@ const StateProvider = ( { children } ) => { return {...state, showFakeoutModal: !state.showFakeoutModal} case 'toggle_bank_modal': return {...state, showBankModal: !state.showBankModal} - case 'toggle_incomplete_attempt_modal': - return {...state, showIncompleteAttemptModal: !state.showIncompleteAttemptModal} + case 'toggle_submission_settings_modal': + return {...state, showSubmissionSettingsModal: !state.showSubmissionSettingsModal} case 'toggle_error_modal': return {...state, errors: action.payload.error, showErrorModal: !state.showErrorModal} case 'update_num_ask': return {...state, numAsk: action.payload} case 'toggle_ask_limit': return {...state, enableQuestionBank: action.payload} - case 'toggle_incomplete_attempt': - return {...state, allowIncompleteAttempt: action.payload} + case 'toggle_require_all_questions': + return {...state, requireAllQuestions: action.payload} default: throw new Error('Base reducer: this action type was not defined') } diff --git a/src/creator.scss b/src/creator.scss index b5db2d1..7fe9edc 100644 --- a/src/creator.scss +++ b/src/creator.scss @@ -75,7 +75,7 @@ div.startupTooltip { .toggleLegend, .toggleBank, - .toggleIncompleteAttempt { + .toggleSubmissionSettings { float: right; padding: 8px 28px; outline: none; @@ -86,7 +86,7 @@ div.startupTooltip { border: solid 1px $colorAccentHover; font-weight: bold; - &.toggleBank, &.toggleIncompleteAttempt { + &.toggleBank, &.toggleSubmissionSettings { margin-right: 10px; } diff --git a/src/demo.json b/src/demo.json index 81fc3d1..9d347be 100644 --- a/src/demo.json +++ b/src/demo.json @@ -293,7 +293,7 @@ } ], "enableQuestionBank": false, - "allowIncompleteAttempt": true, + "requireAllQuestions": true, "numAsk": 1 } } diff --git a/src/player-store.js b/src/player-store.js index 21c7f69..ba71557 100644 --- a/src/player-store.js +++ b/src/player-store.js @@ -9,8 +9,8 @@ const init = { showTutorial: true, showWarning: false, numAsk: 1, - enableQuestionBank: "no", - allowIncompleteAttempt: "no", + enableQuestionBank: false, + requireAllQuestions: false, questionsAsked: [] } @@ -64,7 +64,7 @@ const importFromQset = (qset) => { legend: qset.options.legend, numAsk: qset.options.numAsk, enableQuestionBank: qset.options.enableQuestionBank, - allowIncompleteAttempt: qset.options.allowIncompleteAttempt, + requireAllQuestions: qset.options.requireAllQuestions ? qset.options.requireAllQuestions : false, questionsAsked: [] } } From ebf5f03cb426e33189df960b409a0b84b9687df1 Mon Sep 17 00:00:00 2001 From: Corey Peterson Date: Wed, 26 May 2021 14:40:15 -0400 Subject: [PATCH 4/4] requireAllQuestions default changed to false --- src/creator-store.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/creator-store.js b/src/creator-store.js index 688f396..0ea78ef 100644 --- a/src/creator-store.js +++ b/src/creator-store.js @@ -34,7 +34,7 @@ const init = { ], numAsk: 1, enableQuestionBank: false, - requireAllQuestions: true, + requireAllQuestions: false, showLegend: false, legendColorPickerTarget: -1, onboarding: true,