feat add olx solution support (#225)
This adds support for the tag in OLX and maps it to the description in the settings options on the ShowAnswer card. https://2u-internal.atlassian.net/browse/TNL-10397
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -110,3 +110,6 @@ dist
|
||||
|
||||
### local overrides ###
|
||||
module.config.js
|
||||
|
||||
### Code editors ###
|
||||
.vscode
|
||||
@@ -0,0 +1,43 @@
|
||||
import React from 'react';
|
||||
import { Collapsible, Card } from '@edx/paragon';
|
||||
import {
|
||||
bool, string, node,
|
||||
} from 'prop-types';
|
||||
|
||||
const CardSection = ({
|
||||
children, none, isCardCollapsibleOpen, summary,
|
||||
}) => {
|
||||
const show = isCardCollapsibleOpen || summary;
|
||||
if (!show) { return null; }
|
||||
|
||||
return (
|
||||
<Card.Section className="px-4 pb-4 pt-3">
|
||||
<Collapsible.Advanced
|
||||
open={!isCardCollapsibleOpen}
|
||||
>
|
||||
<Collapsible.Body className="collapsible-body">
|
||||
<span className={`small ${none ? 'text-gray-500' : 'text-primary-500'}`}>{summary}</span>
|
||||
</Collapsible.Body>
|
||||
</Collapsible.Advanced>
|
||||
<Collapsible.Advanced
|
||||
open={isCardCollapsibleOpen}
|
||||
>
|
||||
<Collapsible.Body className="collapsible-body">
|
||||
{children}
|
||||
</Collapsible.Body>
|
||||
</Collapsible.Advanced>
|
||||
</Card.Section>
|
||||
);
|
||||
};
|
||||
CardSection.propTypes = {
|
||||
none: bool,
|
||||
children: node.isRequired,
|
||||
summary: string,
|
||||
isCardCollapsibleOpen: bool.isRequired,
|
||||
};
|
||||
CardSection.defaultProps = {
|
||||
none: false,
|
||||
summary: null,
|
||||
};
|
||||
|
||||
export default CardSection;
|
||||
@@ -0,0 +1,12 @@
|
||||
import { shallow } from 'enzyme';
|
||||
import CardSection from './CardSection';
|
||||
|
||||
describe('CardSection', () => {
|
||||
test('open', () => {
|
||||
expect(shallow(<CardSection summary="summary" isCardCollapsibleOpen><h1>Section Text</h1></CardSection>)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('closed', () => {
|
||||
expect(shallow(<CardSection isCardCollapsibleOpen={false}><h1>Section Text</h1></CardSection>)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -1,23 +1,22 @@
|
||||
import React from 'react';
|
||||
import { Collapsible, Icon, Card } from '@edx/paragon';
|
||||
import { KeyboardArrowUp, KeyboardArrowDown } from '@edx/paragon/icons';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
arrayOf, shape, string, node,
|
||||
} from 'prop-types';
|
||||
import { showFullCard } from './hooks';
|
||||
import CardSection from './CardSection';
|
||||
|
||||
export const SettingsOption = ({
|
||||
title,
|
||||
summary,
|
||||
none,
|
||||
children,
|
||||
className,
|
||||
title, className, extraSections, children, summary, ...passThroughProps
|
||||
}) => {
|
||||
const { isCardCollapsed, toggleCardCollapse } = showFullCard();
|
||||
const { isCardCollapsibleOpen, toggleCardCollapse } = showFullCard();
|
||||
|
||||
return (
|
||||
<Card className={`${className} settingsOption border border-light-700 shadow-none`}>
|
||||
<Card.Section className="settingsCardTitleSection">
|
||||
<Card.Section className="settingsCardTitleSection" key={`settingsOption-${title}-header`}>
|
||||
<Collapsible.Advanced
|
||||
open={isCardCollapsed}
|
||||
open={isCardCollapsibleOpen}
|
||||
onToggle={toggleCardCollapse}
|
||||
>
|
||||
<Collapsible.Trigger className="collapsible-trigger d-flex">
|
||||
@@ -31,36 +30,33 @@ export const SettingsOption = ({
|
||||
</Collapsible.Trigger>
|
||||
</Collapsible.Advanced>
|
||||
</Card.Section>
|
||||
<Card.Section className="px-4 pb-4 pt-3">
|
||||
<Collapsible.Advanced
|
||||
open={!isCardCollapsed}
|
||||
>
|
||||
<Collapsible.Body className="collapsible-body">
|
||||
<span className={`small ${none ? 'text-gray-500' : 'text-primary-500'}`}>{summary}</span>
|
||||
</Collapsible.Body>
|
||||
</Collapsible.Advanced>
|
||||
<Collapsible.Advanced
|
||||
open={isCardCollapsed}
|
||||
>
|
||||
<Collapsible.Body className="collapsible-body">
|
||||
{children}
|
||||
</Collapsible.Body>
|
||||
</Collapsible.Advanced>
|
||||
</Card.Section>
|
||||
<CardSection {...passThroughProps} isCardCollapsibleOpen={isCardCollapsibleOpen} summary={summary} key={`settingsOption-${title}-children`}>
|
||||
{children}
|
||||
</CardSection>
|
||||
{extraSections.map((section, index) => (
|
||||
<>
|
||||
{isCardCollapsibleOpen && <hr />}
|
||||
{/* eslint-disable-next-line react/no-array-index-key */}
|
||||
<CardSection {...passThroughProps} isCardCollapsibleOpen={isCardCollapsibleOpen} key={`settingsOption-${title}-${index}`}>
|
||||
{section.children}
|
||||
</CardSection>
|
||||
</>
|
||||
))}
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
SettingsOption.propTypes = {
|
||||
title: PropTypes.string.isRequired,
|
||||
summary: PropTypes.string.isRequired,
|
||||
none: PropTypes.bool,
|
||||
className: PropTypes.string,
|
||||
children: PropTypes.node.isRequired,
|
||||
title: string.isRequired,
|
||||
children: node.isRequired,
|
||||
className: string,
|
||||
summary: string.isRequired,
|
||||
extraSections: arrayOf(shape({
|
||||
children: node,
|
||||
})),
|
||||
};
|
||||
SettingsOption.defaultProps = {
|
||||
none: false,
|
||||
className: '',
|
||||
extraSections: [],
|
||||
};
|
||||
|
||||
export default SettingsOption;
|
||||
|
||||
@@ -3,10 +3,21 @@ import { shallow } from 'enzyme';
|
||||
import SettingsOption from './SettingsOption';
|
||||
|
||||
describe('SettingsOption', () => {
|
||||
describe('render', () => {
|
||||
const testContent = (<h1>My test content</h1>);
|
||||
describe('default with children', () => {
|
||||
const children = (<h1>My test content</h1>);
|
||||
test('snapshot: renders correct', () => {
|
||||
expect(shallow(<SettingsOption title="Settings Option Title" summary="Settings Option Summary">{testContent}</SettingsOption>)).toMatchSnapshot();
|
||||
expect(shallow(<SettingsOption title="Settings Option Title" summary="Settings Option Summary">{children}</SettingsOption>)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
describe('with additional sections', () => {
|
||||
const children = (<h1>First Section</h1>);
|
||||
const sections = [<h1>Second Section</h1>, <h1>Third Section</h1>];
|
||||
test('snapshot: renders correct', () => {
|
||||
expect(shallow(
|
||||
<SettingsOption title="Settings Option Title" summary="Settings Option Summary" extraSections={sections}>
|
||||
{children}
|
||||
</SettingsOption>,
|
||||
)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`CardSection closed 1`] = `""`;
|
||||
|
||||
exports[`CardSection open 1`] = `
|
||||
<Card.Section
|
||||
className="px-4 pb-4 pt-3"
|
||||
>
|
||||
<Advanced
|
||||
open={false}
|
||||
>
|
||||
<Body
|
||||
className="collapsible-body"
|
||||
>
|
||||
<span
|
||||
className="small text-primary-500"
|
||||
>
|
||||
summary
|
||||
</span>
|
||||
</Body>
|
||||
</Advanced>
|
||||
<Advanced
|
||||
open={true}
|
||||
>
|
||||
<Body
|
||||
className="collapsible-body"
|
||||
>
|
||||
<h1>
|
||||
Section Text
|
||||
</h1>
|
||||
</Body>
|
||||
</Advanced>
|
||||
</Card.Section>
|
||||
`;
|
||||
@@ -1,11 +1,12 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`SettingsOption render snapshot: renders correct 1`] = `
|
||||
exports[`SettingsOption default with children snapshot: renders correct 1`] = `
|
||||
<Card
|
||||
className=" settingsOption border border-light-700 shadow-none"
|
||||
>
|
||||
<Card.Section
|
||||
className="settingsCardTitleSection"
|
||||
key="settingsOption-Settings Option Title-header"
|
||||
>
|
||||
<Advanced
|
||||
onToggle={[Function]}
|
||||
@@ -32,33 +33,73 @@ exports[`SettingsOption render snapshot: renders correct 1`] = `
|
||||
</Trigger>
|
||||
</Advanced>
|
||||
</Card.Section>
|
||||
<Card.Section
|
||||
className="px-4 pb-4 pt-3"
|
||||
<CardSection
|
||||
isCardCollapsibleOpen={false}
|
||||
key="settingsOption-Settings Option Title-children"
|
||||
none={false}
|
||||
summary="Settings Option Summary"
|
||||
>
|
||||
<Advanced
|
||||
open={true}
|
||||
>
|
||||
<Body
|
||||
className="collapsible-body"
|
||||
>
|
||||
<span
|
||||
className="small text-primary-500"
|
||||
>
|
||||
Settings Option Summary
|
||||
</span>
|
||||
</Body>
|
||||
</Advanced>
|
||||
<Advanced
|
||||
open={false}
|
||||
>
|
||||
<Body
|
||||
className="collapsible-body"
|
||||
>
|
||||
<h1>
|
||||
My test content
|
||||
</h1>
|
||||
</Body>
|
||||
</Advanced>
|
||||
</Card.Section>
|
||||
<h1>
|
||||
My test content
|
||||
</h1>
|
||||
</CardSection>
|
||||
</Card>
|
||||
`;
|
||||
|
||||
exports[`SettingsOption with additional sections snapshot: renders correct 1`] = `
|
||||
<Card
|
||||
className=" settingsOption border border-light-700 shadow-none"
|
||||
>
|
||||
<Card.Section
|
||||
className="settingsCardTitleSection"
|
||||
key="settingsOption-Settings Option Title-header"
|
||||
>
|
||||
<Advanced
|
||||
onToggle={[Function]}
|
||||
open={false}
|
||||
>
|
||||
<Trigger
|
||||
className="collapsible-trigger d-flex"
|
||||
>
|
||||
<span
|
||||
className="flex-grow-1 text-primary-500 x-small"
|
||||
>
|
||||
Settings Option Title
|
||||
</span>
|
||||
<Visible
|
||||
whenClosed={true}
|
||||
>
|
||||
<Icon />
|
||||
</Visible>
|
||||
<Visible
|
||||
whenOpen={true}
|
||||
>
|
||||
<Icon />
|
||||
</Visible>
|
||||
</Trigger>
|
||||
</Advanced>
|
||||
</Card.Section>
|
||||
<CardSection
|
||||
isCardCollapsibleOpen={false}
|
||||
key="settingsOption-Settings Option Title-children"
|
||||
none={false}
|
||||
summary="Settings Option Summary"
|
||||
>
|
||||
<h1>
|
||||
First Section
|
||||
</h1>
|
||||
</CardSection>
|
||||
<CardSection
|
||||
isCardCollapsibleOpen={false}
|
||||
key="settingsOption-Settings Option Title-0"
|
||||
none={false}
|
||||
summary={null}
|
||||
/>
|
||||
<CardSection
|
||||
isCardCollapsibleOpen={false}
|
||||
key="settingsOption-Settings Option Title-1"
|
||||
none={false}
|
||||
summary={null}
|
||||
/>
|
||||
</Card>
|
||||
`;
|
||||
|
||||
@@ -21,10 +21,10 @@ export const showAdvancedSettingsCards = () => {
|
||||
};
|
||||
|
||||
export const showFullCard = () => {
|
||||
const [isCardCollapsed, setIsCardCollapsed] = module.state.cardCollapsed(false);
|
||||
const [isCardCollapsibleOpen, setIsCardCollapsibleOpen] = module.state.cardCollapsed(false);
|
||||
return {
|
||||
isCardCollapsed,
|
||||
toggleCardCollapse: () => setIsCardCollapsed(!isCardCollapsed),
|
||||
isCardCollapsibleOpen,
|
||||
toggleCardCollapse: () => setIsCardCollapsibleOpen(!isCardCollapsibleOpen),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -147,8 +147,9 @@ export const scoringCardHooks = (scoring, updateSettings) => {
|
||||
};
|
||||
};
|
||||
|
||||
export const showAnswerCardHooks = (showAnswer, updateSettings) => {
|
||||
export const useAnswerSettings = (showAnswer, updateSettings) => {
|
||||
const [showAttempts, setShowAttempts] = module.state.showAttempts(false);
|
||||
|
||||
const numberOfAttemptsChoice = [
|
||||
ShowAnswerTypesKeys.AFTER_SOME_NUMBER_OF_ATTEMPTS,
|
||||
ShowAnswerTypesKeys.AFTER_ALL_ATTEMPTS,
|
||||
@@ -173,9 +174,14 @@ export const showAnswerCardHooks = (showAnswer, updateSettings) => {
|
||||
updateSettings({ showAnswer: { ...showAnswer, afterAttempts: attempts } });
|
||||
};
|
||||
|
||||
const handleExplanationChange = (event) => {
|
||||
updateSettings({ solutionExplanation: event.target.value });
|
||||
};
|
||||
|
||||
return {
|
||||
handleShowAnswerChange,
|
||||
handleAttemptsChange,
|
||||
handleExplanationChange,
|
||||
showAttempts,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -53,7 +53,7 @@ describe('Problem settings hooks', () => {
|
||||
output = hooks.showFullCard();
|
||||
});
|
||||
test('test default state is false', () => {
|
||||
expect(output.isCardCollapsed).toBeFalsy();
|
||||
expect(output.isCardCollapsibleOpen).toBeFalsy();
|
||||
});
|
||||
test('test toggleCardCollapse to true', () => {
|
||||
output.toggleCardCollapse();
|
||||
@@ -220,7 +220,7 @@ describe('Problem settings hooks', () => {
|
||||
afterAttempts: 5,
|
||||
};
|
||||
beforeEach(() => {
|
||||
output = hooks.showAnswerCardHooks(showAnswer, updateSettings);
|
||||
output = hooks.useAnswerSettings(showAnswer, updateSettings);
|
||||
});
|
||||
test('test handleShowAnswerChange', () => {
|
||||
const value = 'always';
|
||||
@@ -232,6 +232,11 @@ describe('Problem settings hooks', () => {
|
||||
output.handleAttemptsChange({ target: { value } });
|
||||
expect(updateSettings).toHaveBeenCalledWith({ showAnswer: { ...showAnswer, afterAttempts: parseInt(value) } });
|
||||
});
|
||||
test('handleExplanationChange should update settings', () => {
|
||||
const value = 'explanation';
|
||||
output.handleExplanationChange({ target: { value } });
|
||||
expect(updateSettings).toHaveBeenCalledWith({ solutionExplanation: value });
|
||||
});
|
||||
});
|
||||
|
||||
describe('Timer card hooks', () => {
|
||||
|
||||
@@ -67,7 +67,11 @@ export const SettingsWidget = ({
|
||||
<Collapsible.Advanced open={isAdvancedCardsVisible}>
|
||||
<Collapsible.Body className="collapsible-body">
|
||||
<div className="my-3">
|
||||
<ShowAnswerCard showAnswer={settings.showAnswer} updateSettings={updateSettings} />
|
||||
<ShowAnswerCard
|
||||
showAnswer={settings.showAnswer}
|
||||
updateSettings={updateSettings}
|
||||
solutionExplanation={settings.solutionExplanation}
|
||||
/>
|
||||
</div>
|
||||
<div className="my-3">
|
||||
<ResetCard showResetButton={settings.showResetButton} updateSettings={updateSettings} />
|
||||
|
||||
@@ -194,6 +194,16 @@ export const messages = {
|
||||
defaultMessage: 'Switch To Advanced Editor',
|
||||
description: 'message to confirm that a user wants to use the advanced editor',
|
||||
},
|
||||
explanationInputLabel: {
|
||||
id: 'authoring.problemeditor.settings.showAnswer.explanation.inputLabel',
|
||||
defaultMessage: 'Explanation',
|
||||
description: 'answer explanation input label',
|
||||
},
|
||||
explanationSettingText: {
|
||||
id: 'authoring.problemeditor.settings.showAnswer.explanation.text',
|
||||
defaultMessage: 'Provide an explanation for the correct answer.',
|
||||
description: 'Solution Explanation text',
|
||||
},
|
||||
};
|
||||
|
||||
export default messages;
|
||||
|
||||
@@ -7,10 +7,11 @@ import SettingsOption from '../SettingsOption';
|
||||
import { ShowAnswerTypes, ShowAnswerTypesKeys } from '../../../../../../data/constants/problem';
|
||||
import { selectors } from '../../../../../../data/redux';
|
||||
import messages from '../messages';
|
||||
import { showAnswerCardHooks } from '../hooks';
|
||||
import { useAnswerSettings } from '../hooks';
|
||||
|
||||
export const ShowAnswerCard = ({
|
||||
showAnswer,
|
||||
solutionExplanation,
|
||||
updateSettings,
|
||||
// inject
|
||||
intl,
|
||||
@@ -21,24 +22,23 @@ export const ShowAnswerCard = ({
|
||||
const {
|
||||
handleShowAnswerChange,
|
||||
handleAttemptsChange,
|
||||
handleExplanationChange,
|
||||
showAttempts,
|
||||
} = showAnswerCardHooks(showAnswer, updateSettings);
|
||||
return (
|
||||
<SettingsOption
|
||||
title={intl.formatMessage(messages.showAnswerSettingsTitle)}
|
||||
summary={intl.formatMessage(ShowAnswerTypes[showAnswer.on])}
|
||||
>
|
||||
<div className="halfSpacedMessage">
|
||||
} = useAnswerSettings(showAnswer, updateSettings);
|
||||
|
||||
const showAnswerSection = (
|
||||
<>
|
||||
<div className="pb-2">
|
||||
<span>
|
||||
<FormattedMessage {...messages.showAnswerSettingText} />
|
||||
</span>
|
||||
</div>
|
||||
<div className="spacedMessage">
|
||||
<div className="pb-4">
|
||||
<Hyperlink destination={`${studioEndpointUrl}/settings/advanced/${learningContextId}`} target="_blank">
|
||||
<FormattedMessage {...messages.advancedSettingsLinkText} />
|
||||
</Hyperlink>
|
||||
</div>
|
||||
<Form.Group>
|
||||
<Form.Group className="pb-0 mb-0">
|
||||
<Form.Control
|
||||
as="select"
|
||||
value={showAnswer.on}
|
||||
@@ -56,7 +56,7 @@ export const ShowAnswerCard = ({
|
||||
</Form.Group>
|
||||
{showAttempts
|
||||
&& (
|
||||
<Form.Group>
|
||||
<Form.Group className="pb-0 mb-0">
|
||||
<Form.Control
|
||||
type="number"
|
||||
value={showAnswer.afterAttempts}
|
||||
@@ -65,6 +65,33 @@ export const ShowAnswerCard = ({
|
||||
/>
|
||||
</Form.Group>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
const explanationSection = (
|
||||
<>
|
||||
<div className="pb-3">
|
||||
<span>
|
||||
<FormattedMessage {...messages.explanationSettingText} />
|
||||
</span>
|
||||
</div>
|
||||
<Form.Group className="pb-0">
|
||||
<Form.Control
|
||||
value={solutionExplanation}
|
||||
onChange={handleExplanationChange}
|
||||
floatingLabel={intl.formatMessage(messages.explanationInputLabel)}
|
||||
/>
|
||||
</Form.Group>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<SettingsOption
|
||||
title={intl.formatMessage(messages.showAnswerSettingsTitle)}
|
||||
summary={intl.formatMessage(ShowAnswerTypes[showAnswer.on])}
|
||||
extraSections={[{ children: explanationSection }]}
|
||||
>
|
||||
{showAnswerSection}
|
||||
</SettingsOption>
|
||||
);
|
||||
};
|
||||
@@ -73,10 +100,14 @@ ShowAnswerCard.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
// eslint-disable-next-line
|
||||
showAnswer: PropTypes.any.isRequired,
|
||||
solutionExplanation: PropTypes.string,
|
||||
updateSettings: PropTypes.func.isRequired,
|
||||
studioEndpointUrl: PropTypes.string.isRequired,
|
||||
learningContextId: PropTypes.string.isRequired,
|
||||
};
|
||||
ShowAnswerCard.defaultProps = {
|
||||
solutionExplanation: '',
|
||||
};
|
||||
|
||||
export const mapStateToProps = (state) => ({
|
||||
studioEndpointUrl: selectors.app.studioEndpointUrl(state),
|
||||
|
||||
@@ -3,10 +3,10 @@ import { shallow } from 'enzyme';
|
||||
import { formatMessage } from '../../../../../../../testUtils';
|
||||
import { selectors } from '../../../../../../data/redux';
|
||||
import { ShowAnswerCard, mapStateToProps, mapDispatchToProps } from './ShowAnswerCard';
|
||||
import { showAnswerCardHooks } from '../hooks';
|
||||
import { useAnswerSettings } from '../hooks';
|
||||
|
||||
jest.mock('../hooks', () => ({
|
||||
showAnswerCardHooks: jest.fn(),
|
||||
useAnswerSettings: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('../../../../../../data/redux', () => ({
|
||||
@@ -34,17 +34,17 @@ describe('ShowAnswerCard', () => {
|
||||
learningContextId: 'sOMEcouRseId',
|
||||
};
|
||||
|
||||
const showAnswerCardHooksProps = {
|
||||
handleShowAnswerChange: jest.fn().mockName('showAnswerCardHooks.handleShowAnswerChange'),
|
||||
handleAttemptsChange: jest.fn().mockName('showAnswerCardHooks.handleAttemptsChange'),
|
||||
const useAnswerSettingsProps = {
|
||||
handleShowAnswerChange: jest.fn().mockName('useAnswerSettings.handleShowAnswerChange'),
|
||||
handleAttemptsChange: jest.fn().mockName('useAnswerSettings.handleAttemptsChange'),
|
||||
};
|
||||
|
||||
showAnswerCardHooks.mockReturnValue(showAnswerCardHooksProps);
|
||||
useAnswerSettings.mockReturnValue(useAnswerSettingsProps);
|
||||
|
||||
describe('behavior', () => {
|
||||
it(' calls showAnswerCardHooks when initialized', () => {
|
||||
it(' calls useAnswerSettings when initialized', () => {
|
||||
shallow(<ShowAnswerCard {...props} />);
|
||||
expect(showAnswerCardHooks).toHaveBeenCalledWith(showAnswer, props.updateSettings);
|
||||
expect(useAnswerSettings).toHaveBeenCalledWith(showAnswer, props.updateSettings);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
exports[`HintsCard snapshot snapshot: renders hints setting card multiple hints 1`] = `
|
||||
<SettingsOption
|
||||
className=""
|
||||
extraSections={Array []}
|
||||
none={false}
|
||||
summary=" {count, plural, =0 {} other {(+# more)}}"
|
||||
title="Hints"
|
||||
@@ -40,6 +41,7 @@ exports[`HintsCard snapshot snapshot: renders hints setting card multiple hints
|
||||
exports[`HintsCard snapshot snapshot: renders hints setting card no hints 1`] = `
|
||||
<SettingsOption
|
||||
className=""
|
||||
extraSections={Array []}
|
||||
none={true}
|
||||
summary="None"
|
||||
title="Hints"
|
||||
@@ -63,6 +65,7 @@ exports[`HintsCard snapshot snapshot: renders hints setting card no hints 1`] =
|
||||
exports[`HintsCard snapshot snapshot: renders hints setting card one hint 1`] = `
|
||||
<SettingsOption
|
||||
className=""
|
||||
extraSections={Array []}
|
||||
none={false}
|
||||
summary="hint1 {count, plural, =0 {} other {(+# more)}}"
|
||||
title="Hints"
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
exports[`MatlabCard snapshot snapshot: renders matlab setting card 1`] = `
|
||||
<SettingsOption
|
||||
className=""
|
||||
extraSections={Array []}
|
||||
none={false}
|
||||
summary="matlab_api_key"
|
||||
title="MATLAB API Key"
|
||||
@@ -48,6 +49,7 @@ exports[`MatlabCard snapshot snapshot: renders matlab setting card 1`] = `
|
||||
exports[`MatlabCard snapshot snapshot: renders matlab setting card no key 1`] = `
|
||||
<SettingsOption
|
||||
className=""
|
||||
extraSections={Array []}
|
||||
none={true}
|
||||
summary="None"
|
||||
title="MATLAB API Key"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
exports[`ResetCard snapshot snapshot: renders reset true setting card 1`] = `
|
||||
<SettingsOption
|
||||
className="resetCard"
|
||||
none={false}
|
||||
extraSections={Array []}
|
||||
summary="False"
|
||||
title="Show reset option"
|
||||
>
|
||||
@@ -65,7 +65,7 @@ exports[`ResetCard snapshot snapshot: renders reset true setting card 1`] = `
|
||||
exports[`ResetCard snapshot snapshot: renders reset true setting card 2`] = `
|
||||
<SettingsOption
|
||||
className="resetCard"
|
||||
none={false}
|
||||
extraSections={Array []}
|
||||
summary="True"
|
||||
title="Show reset option"
|
||||
>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
exports[`ScoringCard snapshot snapshot: scoring setting card 1`] = `
|
||||
<SettingsOption
|
||||
className=""
|
||||
none={false}
|
||||
extraSections={Array []}
|
||||
summary="{attempts, plural, =1 {# attempt} other {# attempts}} · {weight, plural, =0 {Ungraded} other {# points}}"
|
||||
title="Scoring"
|
||||
>
|
||||
@@ -52,7 +52,7 @@ exports[`ScoringCard snapshot snapshot: scoring setting card 1`] = `
|
||||
exports[`ScoringCard snapshot snapshot: scoring setting card max attempts 1`] = `
|
||||
<SettingsOption
|
||||
className=""
|
||||
none={false}
|
||||
extraSections={Array []}
|
||||
summary="Unlimited attempts · {weight, plural, =0 {Ungraded} other {# points}}"
|
||||
title="Scoring"
|
||||
>
|
||||
@@ -101,7 +101,7 @@ exports[`ScoringCard snapshot snapshot: scoring setting card max attempts 1`] =
|
||||
exports[`ScoringCard snapshot snapshot: scoring setting card zero zero weight 1`] = `
|
||||
<SettingsOption
|
||||
className=""
|
||||
none={false}
|
||||
extraSections={Array []}
|
||||
summary="{attempts, plural, =1 {# attempt} other {# attempts}} · {weight, plural, =0 {Ungraded} other {# points}}"
|
||||
title="Scoring"
|
||||
>
|
||||
|
||||
@@ -3,12 +3,38 @@
|
||||
exports[`ShowAnswerCard snapshot snapshot: show answer setting card 1`] = `
|
||||
<SettingsOption
|
||||
className=""
|
||||
none={false}
|
||||
extraSections={
|
||||
Array [
|
||||
Object {
|
||||
"children": <React.Fragment>
|
||||
<div
|
||||
className="pb-3"
|
||||
>
|
||||
<span>
|
||||
<FormattedMessage
|
||||
defaultMessage="Provide an explanation for the correct answer."
|
||||
description="Solution Explanation text"
|
||||
id="authoring.problemeditor.settings.showAnswer.explanation.text"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<Form.Group
|
||||
className="pb-0"
|
||||
>
|
||||
<Form.Control
|
||||
floatingLabel="Explanation"
|
||||
value=""
|
||||
/>
|
||||
</Form.Group>
|
||||
</React.Fragment>,
|
||||
},
|
||||
]
|
||||
}
|
||||
summary="After Some Number of Attempts"
|
||||
title="Show answer"
|
||||
>
|
||||
<div
|
||||
className="halfSpacedMessage"
|
||||
className="pb-2"
|
||||
>
|
||||
<span>
|
||||
<FormattedMessage
|
||||
@@ -19,7 +45,7 @@ exports[`ShowAnswerCard snapshot snapshot: show answer setting card 1`] = `
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className="spacedMessage"
|
||||
className="pb-4"
|
||||
>
|
||||
<Hyperlink
|
||||
destination="SoMEeNDpOinT/settings/advanced/sOMEcouRseId"
|
||||
@@ -32,10 +58,12 @@ exports[`ShowAnswerCard snapshot snapshot: show answer setting card 1`] = `
|
||||
/>
|
||||
</Hyperlink>
|
||||
</div>
|
||||
<Form.Group>
|
||||
<Form.Group
|
||||
className="pb-0 mb-0"
|
||||
>
|
||||
<Form.Control
|
||||
as="select"
|
||||
onChange={[MockFunction showAnswerCardHooks.handleShowAnswerChange]}
|
||||
onChange={[MockFunction useAnswerSettings.handleShowAnswerChange]}
|
||||
value="after_attempts"
|
||||
>
|
||||
<option
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
exports[`TimerCard snapshot snapshot: renders reset true setting card 1`] = `
|
||||
<SettingsOption
|
||||
className=""
|
||||
none={false}
|
||||
extraSections={Array []}
|
||||
summary="5 seconds"
|
||||
title="Time between attempts"
|
||||
>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
exports[`TypeCard snapshot snapshot: renders type setting card 1`] = `
|
||||
<SettingsOption
|
||||
className=""
|
||||
none={false}
|
||||
extraSections={Array []}
|
||||
summary="Text input"
|
||||
title="Type"
|
||||
>
|
||||
|
||||
@@ -8,24 +8,24 @@ import { ProblemTypeKeys } from '../../../data/constants/problem';
|
||||
export const indexToLetterMap = [...Array(26)].map((val, i) => String.fromCharCode(i + 65));
|
||||
|
||||
export const nonQuestionKeys = [
|
||||
'responseparam',
|
||||
'formulaequationinput',
|
||||
'correcthint',
|
||||
'@_answer',
|
||||
'optioninput',
|
||||
'@_type',
|
||||
'additional_answer',
|
||||
'checkboxgroup',
|
||||
'choicegroup',
|
||||
'additional_answer',
|
||||
'stringequalhint',
|
||||
'textline',
|
||||
'@_type',
|
||||
'formulaequationinput',
|
||||
'numericalresponse',
|
||||
'stringresponse',
|
||||
'multiplechoiceresponse',
|
||||
'choiceresponse',
|
||||
'optionresponse',
|
||||
'correcthint',
|
||||
'demandhint',
|
||||
'formulaequationinput',
|
||||
'multiplechoiceresponse',
|
||||
'numericalresponse',
|
||||
'optioninput',
|
||||
'optionresponse',
|
||||
'responseparam',
|
||||
'solution',
|
||||
'stringequalhint',
|
||||
'stringresponse',
|
||||
'textline',
|
||||
];
|
||||
|
||||
export class OLXParser {
|
||||
@@ -327,6 +327,38 @@ export class OLXParser {
|
||||
return hintsObject;
|
||||
}
|
||||
|
||||
#extractTextAndChildren(node) {
|
||||
const children = [];
|
||||
let text = null;
|
||||
|
||||
if (_.isArray(node)) {
|
||||
children.push(...node);
|
||||
} else if (_.isPlainObject(node)) {
|
||||
text = _.get(node, '#text');
|
||||
const nodeWithoutText = _.omit(node, '#text');
|
||||
children.push(...Object.values(nodeWithoutText));
|
||||
}
|
||||
|
||||
return { text, children };
|
||||
}
|
||||
|
||||
getSolutionExplanation() {
|
||||
if (!_.has(this.problem, 'solution')) { return null; }
|
||||
|
||||
const stack = [this.problem.solution];
|
||||
const texts = [];
|
||||
let currentNode;
|
||||
|
||||
while (stack.length) {
|
||||
currentNode = stack.pop();
|
||||
const { text, children } = this.#extractTextAndChildren(currentNode);
|
||||
if (text) { texts.push(text); }
|
||||
stack.push(...children);
|
||||
}
|
||||
|
||||
return texts.reverse().join('\n ');
|
||||
}
|
||||
|
||||
getFeedback(xmlElement) {
|
||||
return _.has(xmlElement, 'correcthint') ? _.get(xmlElement, 'correcthint.#text') : '';
|
||||
}
|
||||
@@ -365,6 +397,8 @@ export class OLXParser {
|
||||
const problemType = this.getProblemType();
|
||||
const hints = this.getHints();
|
||||
const question = this.parseQuestions(problemType);
|
||||
const solutionExplanation = this.getSolutionExplanation();
|
||||
|
||||
switch (problemType) {
|
||||
case ProblemTypeKeys.DROPDOWN:
|
||||
answersObject = this.parseMultipleChoiceAnswers(ProblemTypeKeys.DROPDOWN, 'optioninput', 'option');
|
||||
@@ -400,6 +434,8 @@ export class OLXParser {
|
||||
}
|
||||
const { answers } = answersObject;
|
||||
const settings = { hints };
|
||||
if (solutionExplanation) { settings.solutionExplanation = solutionExplanation; }
|
||||
|
||||
return {
|
||||
question,
|
||||
settings,
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { OLXParser } from './OLXParser';
|
||||
import {
|
||||
checkboxesOLXWithFeedbackAndHintsOLX,
|
||||
getCheckboxesOLXWithFeedbackAndHintsOLX,
|
||||
dropdownOLXWithFeedbackAndHintsOLX,
|
||||
numericInputWithFeedbackAndHintsOLX,
|
||||
numericInputWithFeedbackAndHintsOLXException,
|
||||
textInputWithFeedbackAndHintsOLX,
|
||||
mutlipleChoiceWithFeedbackAndHintsOLX,
|
||||
multipleChoiceWithFeedbackAndHintsOLX,
|
||||
textInputWithFeedbackAndHintsOLXWithMultipleAnswers,
|
||||
advancedProblemOlX,
|
||||
multipleProblemOlX,
|
||||
@@ -31,7 +32,7 @@ describe('Check OLXParser problem type', () => {
|
||||
expect(problemType).toBe(ProblemTypeKeys.DROPDOWN);
|
||||
});
|
||||
test('Test multiple choice with feedback and hints problem type', () => {
|
||||
const olxparser = new OLXParser(mutlipleChoiceWithFeedbackAndHintsOLX.rawOLX);
|
||||
const olxparser = new OLXParser(multipleChoiceWithFeedbackAndHintsOLX.rawOLX);
|
||||
const problemType = olxparser.getProblemType();
|
||||
expect(problemType).toBe(ProblemTypeKeys.SINGLESELECT);
|
||||
});
|
||||
@@ -74,9 +75,9 @@ describe('Check OLXParser hints', () => {
|
||||
expect(hints).toEqual(dropdownOLXWithFeedbackAndHintsOLX.hints);
|
||||
});
|
||||
test('Test multiple choice with feedback and hints problem type', () => {
|
||||
const olxparser = new OLXParser(mutlipleChoiceWithFeedbackAndHintsOLX.rawOLX);
|
||||
const olxparser = new OLXParser(multipleChoiceWithFeedbackAndHintsOLX.rawOLX);
|
||||
const hints = olxparser.getHints();
|
||||
expect(hints).toEqual(mutlipleChoiceWithFeedbackAndHintsOLX.hints);
|
||||
expect(hints).toEqual(multipleChoiceWithFeedbackAndHintsOLX.hints);
|
||||
});
|
||||
test('Test textual problem type', () => {
|
||||
const olxparser = new OLXParser(textInputWithFeedbackAndHintsOLX.rawOLX);
|
||||
@@ -97,9 +98,9 @@ describe('Check OLXParser for answer parsing', () => {
|
||||
expect(answer).toEqual(dropdownOLXWithFeedbackAndHintsOLX.data);
|
||||
});
|
||||
test('Test multiple choice single select', () => {
|
||||
const olxparser = new OLXParser(mutlipleChoiceWithFeedbackAndHintsOLX.rawOLX);
|
||||
const olxparser = new OLXParser(multipleChoiceWithFeedbackAndHintsOLX.rawOLX);
|
||||
const answer = olxparser.parseMultipleChoiceAnswers('multiplechoiceresponse', 'choicegroup', 'choice');
|
||||
expect(answer).toEqual(mutlipleChoiceWithFeedbackAndHintsOLX.data);
|
||||
expect(answer).toEqual(multipleChoiceWithFeedbackAndHintsOLX.data);
|
||||
});
|
||||
test('Test string response answers', () => {
|
||||
const olxparser = new OLXParser(textInputWithFeedbackAndHintsOLX.rawOLX);
|
||||
@@ -135,9 +136,9 @@ describe('Check OLXParser for question parsing', () => {
|
||||
expect(question).toEqual(dropdownOLXWithFeedbackAndHintsOLX.question);
|
||||
});
|
||||
test('Test multiple choice single select question', () => {
|
||||
const olxparser = new OLXParser(mutlipleChoiceWithFeedbackAndHintsOLX.rawOLX);
|
||||
const olxparser = new OLXParser(multipleChoiceWithFeedbackAndHintsOLX.rawOLX);
|
||||
const question = olxparser.parseQuestions('multiplechoiceresponse');
|
||||
expect(question).toEqual(mutlipleChoiceWithFeedbackAndHintsOLX.question);
|
||||
expect(question).toEqual(multipleChoiceWithFeedbackAndHintsOLX.question);
|
||||
});
|
||||
test('Test string response question', () => {
|
||||
const olxparser = new OLXParser(textInputWithFeedbackAndHintsOLX.rawOLX);
|
||||
@@ -161,3 +162,20 @@ describe('Check OLXParser for question parsing', () => {
|
||||
expect(question).toBe(blankQuestionOLX.question);
|
||||
});
|
||||
});
|
||||
|
||||
describe('OLXParser for problem with solution tag', () => {
|
||||
describe('for checkbox questions', () => {
|
||||
test('should parse simple text', () => {
|
||||
const olxparser = new OLXParser(checkboxesOLXWithFeedbackAndHintsOLX.rawOLX);
|
||||
const explanation = olxparser.getSolutionExplanation();
|
||||
expect(explanation).toEqual(checkboxesOLXWithFeedbackAndHintsOLX.solutionExplanation);
|
||||
});
|
||||
test('should parse text in p tags', () => {
|
||||
const { rawOLX } = getCheckboxesOLXWithFeedbackAndHintsOLX({ solution: 'html' });
|
||||
const olxparser = new OLXParser(rawOLX);
|
||||
const explanation = olxparser.getSolutionExplanation();
|
||||
const expected = getCheckboxesOLXWithFeedbackAndHintsOLX({ solution: 'html' }).solutionExplanation;
|
||||
expect(explanation.replace(/\s/g, '')).toBe(expected.replace(/\s/g, ''));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -36,6 +36,18 @@ class ReactStateOLXParser {
|
||||
return demandhint;
|
||||
}
|
||||
|
||||
addSolution() {
|
||||
if (!_.has(this.problemState, 'settings.solutionExplanation')) { return {}; }
|
||||
|
||||
const solutionText = _.get(this.problemState, 'settings.solutionExplanation');
|
||||
const solutionObject = {
|
||||
solution: {
|
||||
'#text': solutionText,
|
||||
},
|
||||
};
|
||||
return solutionObject;
|
||||
}
|
||||
|
||||
addMultiSelectAnswers(option) {
|
||||
const choice = [];
|
||||
let compoundhint = [];
|
||||
@@ -106,6 +118,8 @@ class ReactStateOLXParser {
|
||||
const question = this.addQuestion();
|
||||
const widgetObject = this.addMultiSelectAnswers(option);
|
||||
const demandhint = this.addHints();
|
||||
const solution = this.addSolution();
|
||||
|
||||
const problemObject = {
|
||||
problem: {
|
||||
[problemType]: {
|
||||
@@ -113,6 +127,7 @@ class ReactStateOLXParser {
|
||||
[widget]: widgetObject,
|
||||
},
|
||||
...demandhint,
|
||||
...solution,
|
||||
},
|
||||
};
|
||||
return this.builder.build(problemObject);
|
||||
@@ -122,6 +137,8 @@ class ReactStateOLXParser {
|
||||
const question = this.addQuestion();
|
||||
const demandhint = this.addHints();
|
||||
const answerObject = this.buildTextInputAnswersFeedback();
|
||||
const solution = this.addSolution();
|
||||
|
||||
const problemObject = {
|
||||
problem: {
|
||||
[ProblemTypeKeys.TEXTINPUT]: {
|
||||
@@ -129,6 +146,7 @@ class ReactStateOLXParser {
|
||||
...answerObject,
|
||||
},
|
||||
...demandhint,
|
||||
...solution,
|
||||
},
|
||||
};
|
||||
return this.builder.build(problemObject);
|
||||
@@ -178,11 +196,14 @@ class ReactStateOLXParser {
|
||||
const question = this.addQuestion();
|
||||
const demandhint = this.addHints();
|
||||
const answerObject = this.buildNumericalResponse();
|
||||
const solution = this.addSolution();
|
||||
|
||||
const problemObject = {
|
||||
problem: {
|
||||
...question,
|
||||
[ProblemTypeKeys.NUMERIC]: answerObject,
|
||||
...demandhint,
|
||||
...solution,
|
||||
},
|
||||
};
|
||||
return this.builder.build(problemObject);
|
||||
@@ -255,6 +276,7 @@ class ReactStateOLXParser {
|
||||
buildOLX() {
|
||||
const { problemType } = this.problemState;
|
||||
let problemString = '';
|
||||
|
||||
switch (problemType) {
|
||||
case ProblemTypeKeys.MULTISELECT:
|
||||
problemString = this.buildMultiSelectProblem(ProblemTypeKeys.MULTISELECT, 'checkboxgroup', 'choice');
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
numericInputWithFeedbackAndHintsOLX,
|
||||
numericInputWithFeedbackAndHintsOLXException,
|
||||
textInputWithFeedbackAndHintsOLX,
|
||||
mutlipleChoiceWithFeedbackAndHintsOLX,
|
||||
multipleChoiceWithFeedbackAndHintsOLX,
|
||||
textInputWithFeedbackAndHintsOLXWithMultipleAnswers,
|
||||
} from './mockData/olxTestData';
|
||||
import ReactStateOLXParser from './ReactStateOLXParser';
|
||||
@@ -33,11 +33,11 @@ describe('Check React Sate OLXParser problem', () => {
|
||||
expect(buildOLX).toEqual(textInputWithFeedbackAndHintsOLX.buildOLX);
|
||||
});
|
||||
test('Test multiple choice with feedback and hints problem type', () => {
|
||||
const olxparser = new OLXParser(mutlipleChoiceWithFeedbackAndHintsOLX.rawOLX);
|
||||
const olxparser = new OLXParser(multipleChoiceWithFeedbackAndHintsOLX.rawOLX);
|
||||
const problem = olxparser.getParsedOLXData();
|
||||
const stateParser = new ReactStateOLXParser({ problem });
|
||||
const buildOLX = stateParser.buildOLX();
|
||||
expect(buildOLX).toEqual(mutlipleChoiceWithFeedbackAndHintsOLX.buildOLX);
|
||||
expect(buildOLX).toEqual(multipleChoiceWithFeedbackAndHintsOLX.buildOLX);
|
||||
});
|
||||
test('Test numerical response with feedback and hints problem type', () => {
|
||||
const olxparser = new OLXParser(numericInputWithFeedbackAndHintsOLX.rawOLX);
|
||||
|
||||
@@ -1,38 +1,55 @@
|
||||
export const checkboxesOLXWithFeedbackAndHintsOLX = {
|
||||
export const getCheckboxesOLXWithFeedbackAndHintsOLX = ({ solution = 'simple' }) => ({
|
||||
rawOLX: `<problem>
|
||||
<choiceresponse>
|
||||
<p>You can use this template as a guide to the simple editor markdown and OLX markup to use for checkboxes with hints and feedback problems. Edit this component to replace this template with your own assessment.</p>
|
||||
<label>Add the question text, or prompt, here. This text is required.</label>
|
||||
<description>You can add an optional tip or note related to the prompt like this.</description>
|
||||
<checkboxgroup>
|
||||
<choice correct="true">a correct answer
|
||||
<choicehint selected="true">You can specify optional feedback that appears after the learner selects and submits this answer.</choicehint>
|
||||
<choicehint selected="false">You can specify optional feedback that appears after the learner clears and submits this answer.</choicehint>
|
||||
</choice>
|
||||
<choice correct="false">an incorrect answer</choice>
|
||||
<choice correct="false">an incorrect answer
|
||||
<choicehint selected="true">You can specify optional feedback for none, all, or a subset of the answers.</choicehint>
|
||||
<choicehint selected="false">You can specify optional feedback for selected answers, cleared answers, or both.</choicehint>
|
||||
</choice>
|
||||
<choice correct="true">a correct answer</choice>
|
||||
<compoundhint value="A B D">You can specify optional feedback for a combination of answers which appears after the specified set of answers is submitted.</compoundhint>
|
||||
<compoundhint value="A B C D">You can specify optional feedback for one, several, or all answer combinations.</compoundhint>
|
||||
</checkboxgroup>
|
||||
</choiceresponse>
|
||||
<demandhint>
|
||||
<hint>You can add an optional hint like this. Problems that have a hint include a hint button, and this text appears the first time learners select the button.</hint>
|
||||
<hint>If you add more than one hint, a different hint appears each time learners select the hint button.</hint>
|
||||
</demandhint>
|
||||
</problem>`,
|
||||
hints: [{
|
||||
id: 0,
|
||||
value: 'You can add an optional hint like this. Problems that have a hint include a hint button, and this text appears the first time learners select the button.',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
value: 'If you add more than one hint, a different hint appears each time learners select the hint button.',
|
||||
},
|
||||
<choiceresponse>
|
||||
<p>You can use this template as a guide to the simple editor markdown and OLX markup to use for checkboxes with hints and feedback problems. Edit this component to replace this template with your own assessment.</p>
|
||||
<label>Add the question text, or prompt, here. This text is required.</label>
|
||||
<description>You can add an optional tip or note related to the prompt like this.</description>
|
||||
<checkboxgroup>
|
||||
<choice correct="true">a correct answer
|
||||
<choicehint selected="true">You can specify optional feedback that appears after the learner selects and submits this answer.</choicehint>
|
||||
<choicehint selected="false">You can specify optional feedback that appears after the learner clears and submits this answer.</choicehint>
|
||||
</choice>
|
||||
<choice correct="false">an incorrect answer</choice>
|
||||
<choice correct="false">an incorrect answer
|
||||
<choicehint selected="true">You can specify optional feedback for none, all, or a subset of the answers.</choicehint>
|
||||
<choicehint selected="false">You can specify optional feedback for selected answers, cleared answers, or both.</choicehint>
|
||||
</choice>
|
||||
<choice correct="true">a correct answer</choice>
|
||||
<compoundhint value="A B D">You can specify optional feedback for a combination of answers which appears after the specified set of answers is submitted.</compoundhint>
|
||||
<compoundhint value="A B C D">You can specify optional feedback for one, several, or all answer combinations.</compoundhint>
|
||||
</checkboxgroup>
|
||||
</choiceresponse>
|
||||
<demandhint>
|
||||
<hint>You can add an optional hint like this. Problems that have a hint include a hint button, and this text appears the first time learners select the button.</hint>
|
||||
<hint>If you add more than one hint, a different hint appears each time learners select the hint button.</hint>
|
||||
</demandhint>
|
||||
${solution === 'simple' ? '<solution>This is a detailed explanation of the solution.</solution>' : (
|
||||
`<solution>
|
||||
<div class="detailed-solution">
|
||||
<p>Explanation</p>
|
||||
<p>
|
||||
You can form a voltage divider that evenly divides the input
|
||||
voltage with two identically valued resistors, with the sampled
|
||||
voltage taken in between the two.
|
||||
</p>
|
||||
<p><img src="/static/images/voltage_divider.png" alt=""/></p>
|
||||
</div>
|
||||
</solution>`
|
||||
)}
|
||||
</problem>`,
|
||||
hints: [
|
||||
{
|
||||
id: 0,
|
||||
value: 'You can add an optional hint like this. Problems that have a hint include a hint button, and this text appears the first time learners select the button.',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
value: 'If you add more than one hint, a different hint appears each time learners select the hint button.',
|
||||
},
|
||||
],
|
||||
solutionExplanation: solution === 'simple' ? 'This is a detailed explanation of the solution.' : (
|
||||
'Explanation\n You can form a voltage divider that evenly divides the input voltage with two identically valued resistors, with the sampled voltage taken in between the two.'
|
||||
),
|
||||
data: {
|
||||
answers: [
|
||||
{
|
||||
@@ -107,9 +124,19 @@ an incorrect answer <choicehint selected="true">You can specify optional
|
||||
<hint>You can add an optional hint like this. Problems that have a hint include a hint button, and this text appears the first time learners select the button.</hint>
|
||||
<hint>If you add more than one hint, a different hint appears each time learners select the hint button.</hint>
|
||||
</demandhint>
|
||||
${solution === 'simple' ? '<solution>This is a detailed explanation of the solution.</solution>' : (
|
||||
`<solution>
|
||||
Explanation\n
|
||||
You can form a voltage divider that evenly divides the input
|
||||
voltage with two identically valued resistors, with the sampled
|
||||
voltage taken in between the two.
|
||||
</solution>`
|
||||
)}
|
||||
</problem>
|
||||
`,
|
||||
};
|
||||
});
|
||||
|
||||
export const checkboxesOLXWithFeedbackAndHintsOLX = getCheckboxesOLXWithFeedbackAndHintsOLX({});
|
||||
|
||||
export const dropdownOLXWithFeedbackAndHintsOLX = {
|
||||
rawOLX: `<problem>
|
||||
@@ -184,7 +211,7 @@ an incorrect answer <optionhint>You can specify optional feedback for non
|
||||
`,
|
||||
};
|
||||
|
||||
export const mutlipleChoiceWithFeedbackAndHintsOLX = {
|
||||
export const multipleChoiceWithFeedbackAndHintsOLX = {
|
||||
rawOLX: `<problem>
|
||||
<multiplechoiceresponse>
|
||||
<p>You can use this template as a guide to the simple editor markdown and OLX markup to use for multiple choice with hints and feedback problems. Edit this component to replace this template with your own assessment.</p>
|
||||
|
||||
@@ -29,6 +29,7 @@ const initialState = {
|
||||
afterAttempts: 0,
|
||||
},
|
||||
showResetButton: false,
|
||||
solutionExplanation: '',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user