From 7ef19633272e871e97b6175388bbc7df1ecb4cff Mon Sep 17 00:00:00 2001 From: Kristin Aoki <42981026+KristinAoki@users.noreply.github.com> Date: Thu, 23 Mar 2023 17:37:13 -0400 Subject: [PATCH] feat: add default advanced setting ui callouts (#285) --- .../__snapshots__/index.test.jsx.snap | 54 ------ .../__snapshots__/index.test.jsx.snap | 24 ++- .../EditProblemView/SettingsWidget/hooks.js | 45 ++++- .../SettingsWidget/hooks.test.js | 58 +++++- .../EditProblemView/SettingsWidget/index.jsx | 14 +- .../SettingsWidget/index.test.jsx | 5 + .../SettingsWidget/messages.js | 5 + .../settingsComponents/ResetCard.jsx | 2 +- .../settingsComponents/ScoringCard.jsx | 75 ++++++-- .../settingsComponents/ScoringCard.test.jsx | 5 +- .../settingsComponents/ShowAnswerCard.jsx | 26 ++- .../ShowAnswerCard.test.jsx | 1 + .../__snapshots__/ResetCard.test.jsx.snap | 4 +- .../__snapshots__/ScoringCard.test.jsx.snap | 174 +++++++++++++----- .../ShowAnswerCard.test.jsx.snap | 4 +- .../containers/ProblemEditor/index.jsx | 5 +- .../containers/ProblemEditor/index.test.jsx | 43 ++++- src/editors/data/constants/requests.js | 2 +- src/editors/data/redux/problem/reducers.js | 22 ++- .../data/redux/problem/reducers.test.js | 16 +- src/editors/data/redux/problem/selectors.js | 1 + .../data/redux/problem/selectors.test.js | 1 + src/editors/data/redux/requests/reducer.js | 1 + .../data/redux/thunkActions/problem.js | 34 +++- .../data/redux/thunkActions/problem.test.js | 72 ++++++-- .../data/redux/thunkActions/requests.js | 8 +- src/editors/data/services/cms/api.js | 2 +- src/editors/data/services/cms/types.js | 5 + 28 files changed, 511 insertions(+), 197 deletions(-) diff --git a/src/editors/containers/ProblemEditor/__snapshots__/index.test.jsx.snap b/src/editors/containers/ProblemEditor/__snapshots__/index.test.jsx.snap index e0418abca..0370230fc 100644 --- a/src/editors/containers/ProblemEditor/__snapshots__/index.test.jsx.snap +++ b/src/editors/containers/ProblemEditor/__snapshots__/index.test.jsx.snap @@ -1,17 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`ProblemEditor snapshots assets loaded, block and studio view not yet loaded, Spinner appears 1`] = ` -
- -
-`; - exports[`ProblemEditor snapshots block failed, message appears 1`] = `
`; -exports[`ProblemEditor snapshots block loaded, studio view and assets not yet loaded, Spinner appears 1`] = ` -
- -
-`; - -exports[`ProblemEditor snapshots renders EditProblemView 1`] = ` -
- -
-`; - -exports[`ProblemEditor snapshots renders SelectTypeModal 1`] = ` - -`; - exports[`ProblemEditor snapshots renders as expected with default behavior 1`] = `
`; - -exports[`ProblemEditor snapshots studio view loaded, block and assets not yet loaded, Spinner appears 1`] = ` -
- -
-`; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/__snapshots__/index.test.jsx.snap b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/__snapshots__/index.test.jsx.snap index 5807a46ed..26d0bb4ec 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/__snapshots__/index.test.jsx.snap +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/__snapshots__/index.test.jsx.snap @@ -14,7 +14,9 @@ exports[`SettingsWidget snapshot snapshot: renders Settings widget for Advanced
- +
- +
- +
- +
- +
- +
useState(val), summary: (val) => useState(val), showAttempts: (val) => useState(val), + attemptDisplayValue: (val) => useState(val), }; export const showAdvancedSettingsCards = () => { @@ -117,19 +118,52 @@ export const resetCardHooks = (updateSettings) => { }; }; -export const scoringCardHooks = (scoring, updateSettings) => { +export const scoringCardHooks = (scoring, updateSettings, defaultValue) => { + const loadedAttemptsNumber = scoring.attempts.number === defaultValue ? `${scoring.attempts.number} (Default)` : scoring.attempts.number; + const [attemptDisplayValue, setAttemptDisplayValue] = module.state.attemptDisplayValue(loadedAttemptsNumber); + const handleUnlimitedChange = (event) => { + const isUnlimited = event.target.checked; + if (isUnlimited) { + setAttemptDisplayValue(''); + updateSettings({ scoring: { ...scoring, attempts: { number: '', unlimited: true } } }); + } else { + setAttemptDisplayValue(`${defaultValue} (Default)`); + updateSettings({ scoring: { ...scoring, attempts: { number: defaultValue, unlimited: false } } }); + } + }; const handleMaxAttemptChange = (event) => { let unlimitedAttempts = false; let attemptNumber = parseInt(event.target.value); + const { value } = event.target; if (_.isNaN(attemptNumber)) { - attemptNumber = ''; - unlimitedAttempts = true; - } else if (attemptNumber < 0) { + if (value === '') { + attemptNumber = defaultValue; + setAttemptDisplayValue(`${defaultValue} (Default)`); + } else { + attemptNumber = ''; + unlimitedAttempts = true; + } + } else if (attemptNumber <= 0) { attemptNumber = 0; + } else if (attemptNumber === defaultValue) { + const attemptNumberStr = value.replace(' (Default)'); + attemptNumber = parseInt(attemptNumberStr); } updateSettings({ scoring: { ...scoring, attempts: { number: attemptNumber, unlimited: unlimitedAttempts } } }); }; + const handleOnChange = (event) => { + let newMaxAttempt = parseInt(event.target.value); + if (newMaxAttempt === defaultValue) { + newMaxAttempt = `${defaultValue} (Default)`; + } else if (_.isNaN(newMaxAttempt)) { + newMaxAttempt = ''; + } else if (newMaxAttempt < 0) { + newMaxAttempt = 0; + } + setAttemptDisplayValue(newMaxAttempt); + }; + const handleWeightChange = (event) => { let weight = parseFloat(event.target.value); if (_.isNaN(weight)) { @@ -139,7 +173,10 @@ export const scoringCardHooks = (scoring, updateSettings) => { }; return { + attemptDisplayValue, + handleUnlimitedChange, handleMaxAttemptChange, + handleOnChange, handleWeightChange, }; }; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.test.js b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.test.js index 48de990df..52ca07fc7 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.test.js +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/hooks.test.js @@ -172,8 +172,21 @@ describe('Problem settings hooks', () => { number: 5, }, }; + const defaultValue = 1; beforeEach(() => { - output = hooks.scoringCardHooks(scoring, updateSettings); + output = hooks.scoringCardHooks(scoring, updateSettings, defaultValue); + }); + test('test handleUnlimitedChange sets attempts.unlimited to true when checked', () => { + output.handleUnlimitedChange({ target: { checked: true } }); + expect(state.setState[state.keys.attemptDisplayValue]).toHaveBeenCalledWith(''); + expect(updateSettings) + .toHaveBeenCalledWith({ scoring: { ...scoring, attempts: { number: '', unlimited: true } } }); + }); + test('test handleUnlimitedChange sets attempts.unlimited to false when unchecked', () => { + output.handleUnlimitedChange({ target: { checked: false } }); + expect(state.setState[state.keys.attemptDisplayValue]).toHaveBeenCalledWith(`${defaultValue} (Default)`); + expect(updateSettings) + .toHaveBeenCalledWith({ scoring: { ...scoring, attempts: { number: defaultValue, unlimited: false } } }); }); test('test handleMaxAttemptChange', () => { const value = 6; @@ -193,11 +206,11 @@ describe('Problem settings hooks', () => { expect(updateSettings) .toHaveBeenCalledWith({ scoring: { ...scoring, attempts: { number: '', unlimited: true } } }); }); - test('test handleMaxAttemptChange set attempts to empty string', () => { - const value = ''; + test('test handleMaxAttemptChange set attempts to default value', () => { + const value = '1 (Default)'; output.handleMaxAttemptChange({ target: { value } }); expect(updateSettings) - .toHaveBeenCalledWith({ scoring: { ...scoring, attempts: { number: '', unlimited: true } } }); + .toHaveBeenCalledWith({ scoring: { ...scoring, attempts: { number: 1, unlimited: false } } }); }); test('test handleMaxAttemptChange set attempts to non-numeric value', () => { const value = 'abc'; @@ -205,12 +218,49 @@ describe('Problem settings hooks', () => { expect(updateSettings) .toHaveBeenCalledWith({ scoring: { ...scoring, attempts: { number: '', unlimited: true } } }); }); + test('test handleMaxAttemptChange set attempts to empty value', () => { + const value = ''; + output.handleMaxAttemptChange({ target: { value } }); + expect(state.setState[state.keys.attemptDisplayValue]).toHaveBeenCalledWith(`${defaultValue} (Default)`); + expect(updateSettings) + .toHaveBeenCalledWith({ scoring: { ...scoring, attempts: { number: 1, unlimited: false } } }); + }); test('test handleMaxAttemptChange set attempts to negative value', () => { const value = -1; output.handleMaxAttemptChange({ target: { value } }); expect(updateSettings) .toHaveBeenCalledWith({ scoring: { ...scoring, attempts: { number: 0, unlimited: false } } }); }); + test('test handleOnChange', () => { + const value = 6; + output.handleOnChange({ target: { value } }); + expect(state.setState[state.keys.attemptDisplayValue]).toHaveBeenCalledWith(value); + }); + test('test handleOnChange set attempts to zero', () => { + const value = 0; + output.handleOnChange({ target: { value } }); + expect(state.setState[state.keys.attemptDisplayValue]).toHaveBeenCalledWith(value); + }); + test('test handleOnChange set attempts to default value from empty string', () => { + const value = ''; + output.handleOnChange({ target: { value } }); + expect(state.setState[state.keys.attemptDisplayValue]).toHaveBeenCalledWith(''); + }); + test('test handleOnChange set attempts to default value', () => { + const value = 1; + output.handleOnChange({ target: { value } }); + expect(state.setState[state.keys.attemptDisplayValue]).toHaveBeenCalledWith('1 (Default)'); + }); + test('test handleOnChange set attempts to non-numeric value', () => { + const value = ''; + output.handleOnChange({ target: { value } }); + expect(state.setState[state.keys.attemptDisplayValue]).toHaveBeenCalledWith(value); + }); + test('test handleOnChange set attempts to negative value', () => { + const value = -1; + output.handleOnChange({ target: { value } }); + expect(state.setState[state.keys.attemptDisplayValue]).toHaveBeenCalledWith(0); + }); test('test handleWeightChange', () => { const value = 2; output.handleWeightChange({ target: { value } }); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.jsx index 2137c090b..7c56120ee 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.jsx @@ -36,6 +36,7 @@ export const SettingsWidget = ({ updateSettings, updateField, updateAnswer, + defaultSettings, }) => { const { isAdvancedCardsVisible, showAdvancedCards } = showAdvancedSettingsCards(); @@ -77,7 +78,11 @@ export const SettingsWidget = ({
)}
- +
@@ -103,6 +108,7 @@ export const SettingsWidget = ({
@@ -159,6 +165,11 @@ SettingsWidget.propTypes = { updateAnswer: PropTypes.func.isRequired, updateField: PropTypes.func.isRequired, updateSettings: PropTypes.func.isRequired, + defaultSettings: PropTypes.shape({ + maxAttempts: PropTypes.number, + showanswer: PropTypes.string, + showReseButton: PropTypes.bool, + }).isRequired, // eslint-disable-next-line settings: PropTypes.any.isRequired, }; @@ -169,6 +180,7 @@ const mapStateToProps = (state) => ({ answers: selectors.problem.answers(state), blockTitle: selectors.app.blockTitle(state), correctAnswerCount: selectors.problem.correctAnswerCount(state), + defaultSettings: selectors.problem.defaultSettings(state), }); export const mapDispatchToProps = { diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.test.jsx index 3b014735f..6cf2b9628 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/index.test.jsx @@ -25,6 +25,11 @@ describe('SettingsWidget', () => { const props = { problemType: ProblemTypeKeys.TEXTINPUT, settings: {}, + defaultSettings: { + maxAttempts: 2, + showanswer: 'finished', + showResetButton: false, + }, }; describe('behavior', () => { diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/messages.js b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/messages.js index 09f3599ab..af54147f6 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/messages.js +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/messages.js @@ -114,6 +114,11 @@ export const messages = { defaultMessage: '{attempts, plural, =1 {# attempt} other {# attempts}}', description: 'Summary text for number of attempts', }, + unlimitedAttemptsCheckboxLabel: { + id: 'authoring.problemeditor.settings.scoring.attempts.unlimitedCheckbox', + defaultMessage: 'Unlimited attempts', + description: 'Label for unlimited attempts checkbox', + }, weightSummary: { id: 'authoring.problemeditor.settings.scoring.weight', defaultMessage: '{weight, plural, =0 {Ungraded} other {# points}}', diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ResetCard.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ResetCard.jsx index efa4d1534..95d00e822 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ResetCard.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ResetCard.jsx @@ -15,7 +15,7 @@ export const ResetCard = ({ intl, }) => { const { setResetTrue, setResetFalse } = resetCardHooks(updateSettings); - const advancedSettingsLink = `${useSelector(selectors.app.studioEndpointUrl)}/settings/advanced/${useSelector(selectors.app.learningContextId)}`; + const advancedSettingsLink = `${useSelector(selectors.app.studioEndpointUrl)}/settings/advanced/${useSelector(selectors.app.learningContextId)}#show_reset_button`; return ( { - const { handleMaxAttemptChange, handleWeightChange } = scoringCardHooks(scoring, updateSettings); + const { + handleUnlimitedChange, + handleMaxAttemptChange, + handleWeightChange, + handleOnChange, + attemptDisplayValue, + } = scoringCardHooks(scoring, updateSettings, defaultValue); - const getScoringSummary = (attempts, unlimited, weight) => { - let summary = unlimited + const getScoringSummary = (weight, attempts, unlimited) => { + let summary = intl.formatMessage(messages.weightSummary, { weight }); + summary += ` ${String.fromCharCode(183)} `; + summary += unlimited ? intl.formatMessage(messages.unlimitedAttemptsSummary) : intl.formatMessage(messages.attemptsSummary, { attempts }); - summary += ` ${String.fromCharCode(183)} `; - summary += intl.formatMessage(messages.weightSummary, { weight }); return summary; }; return ( - - - - - - + + + + + + +
+ +
+
+
+ + +
); }; @@ -62,6 +88,17 @@ ScoringCard.propTypes = { // eslint-disable-next-line scoring: PropTypes.any.isRequired, updateSettings: PropTypes.func.isRequired, + defaultValue: PropTypes.number.isRequired, + // redux + studioEndpointUrl: PropTypes.string.isRequired, + learningContextId: PropTypes.string.isRequired, }; -export default injectIntl(ScoringCard); +export const mapStateToProps = (state) => ({ + studioEndpointUrl: selectors.app.studioEndpointUrl(state), + learningContextId: selectors.app.learningContextId(state), +}); + +export const mapDispatchToProps = {}; + +export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(ScoringCard)); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ScoringCard.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ScoringCard.test.jsx index c729f7e54..14f55b6ad 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ScoringCard.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ScoringCard.test.jsx @@ -16,6 +16,7 @@ describe('ScoringCard', () => { number: 5, }, updateSettings: jest.fn().mockName('args.updateSettings'), + defaultValue: 1, intl: { formatMessage }, }; @@ -27,6 +28,8 @@ describe('ScoringCard', () => { const scoringCardHooksProps = { handleMaxAttemptChange: jest.fn().mockName('scoringCardHooks.handleMaxAttemptChange'), handleWeightChange: jest.fn().mockName('scoringCardHooks.handleWeightChange'), + handleOnChange: jest.fn().mockName('scoringCardHooks.handleOnChange'), + local: 5, }; scoringCardHooks.mockReturnValue(scoringCardHooksProps); @@ -34,7 +37,7 @@ describe('ScoringCard', () => { describe('behavior', () => { it(' calls scoringCardHooks when initialized', () => { shallow(); - expect(scoringCardHooks).toHaveBeenCalledWith(scoring, props.updateSettings); + expect(scoringCardHooks).toHaveBeenCalledWith(scoring, props.updateSettings, props.defaultValue); }); }); diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ShowAnswerCard.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ShowAnswerCard.jsx index 2de5a83d4..ce522372d 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ShowAnswerCard.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ShowAnswerCard.jsx @@ -12,6 +12,7 @@ import { useAnswerSettings } from '../hooks'; export const ShowAnswerCard = ({ showAnswer, updateSettings, + defaultValue, // inject intl, // redux @@ -32,7 +33,7 @@ export const ShowAnswerCard = ({
- +
@@ -42,14 +43,20 @@ export const ShowAnswerCard = ({ value={showAnswer.on} onChange={handleShowAnswerChange} > - {Object.values(ShowAnswerTypesKeys).map((answerType) => ( - - ))} + {Object.values(ShowAnswerTypesKeys).map((answerType) => { + let optionDisplayName = ShowAnswerTypes[answerType]; + if (answerType === defaultValue) { + optionDisplayName = { ...optionDisplayName, defaultMessage: `${optionDisplayName.defaultMessage} (Default)` }; + } + return ( + + ); + })} {showAttempts @@ -84,6 +91,7 @@ ShowAnswerCard.propTypes = { updateSettings: PropTypes.func.isRequired, studioEndpointUrl: PropTypes.string.isRequired, learningContextId: PropTypes.string.isRequired, + defaultValue: PropTypes.string.isRequired, }; ShowAnswerCard.defaultProps = { solutionExplanation: '', diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ShowAnswerCard.test.jsx b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ShowAnswerCard.test.jsx index c4c5652e4..81f58d130 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ShowAnswerCard.test.jsx +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/ShowAnswerCard.test.jsx @@ -27,6 +27,7 @@ describe('ShowAnswerCard', () => { }; const props = { showAnswer, + defaultValue: 'finished', // injected intl: { formatMessage }, // redux diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/ResetCard.test.jsx.snap b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/ResetCard.test.jsx.snap index ca07da6c0..93899ee3a 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/ResetCard.test.jsx.snap +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/ResetCard.test.jsx.snap @@ -23,7 +23,7 @@ exports[`ResetCard snapshot snapshot: renders reset true setting card 1`] = ` className="spacedMessage" > - - - - - - + + + + + + +
+ +
+
+
+ + + `; exports[`ScoringCard snapshot snapshot: scoring setting card max attempts 1`] = ` - - - - - - + + + + + + +
+ +
+
+
+ + +
`; exports[`ScoringCard snapshot snapshot: scoring setting card zero zero weight 1`] = ` - - - - - - + + + + + + +
+ +
+
+
+ + +
`; diff --git a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/ShowAnswerCard.test.jsx.snap b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/ShowAnswerCard.test.jsx.snap index 72c743950..adef7aeac 100644 --- a/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/ShowAnswerCard.test.jsx.snap +++ b/src/editors/containers/ProblemEditor/components/EditProblemView/SettingsWidget/settingsComponents/__snapshots__/ShowAnswerCard.test.jsx.snap @@ -23,7 +23,7 @@ exports[`ShowAnswerCard snapshot snapshot: show answer setting card 1`] = ` className="pb-4" > - Finished + Finished (Default)