Skip to content

Commit

Permalink
Merge pull request #2351 from pie-framework/feat/PD-3843
Browse files Browse the repository at this point in the history
feat(multiple-choice): add min/max selection possibility PD-3843 PD-4518
  • Loading branch information
CarlaCostea authored Dec 20, 2024
2 parents c54c641 + 0048652 commit 1bf85f1
Show file tree
Hide file tree
Showing 9 changed files with 356 additions and 290 deletions.
2 changes: 2 additions & 0 deletions packages/multiple-choice/controller/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ export async function model(question, session, env, updateSession) {
extraCSSRules: normalizedQuestion.extraCSSRules,
fontSizeFactor: normalizedQuestion.fontSizeFactor,
isSelectionButtonBelow: normalizedQuestion.isSelectionButtonBelow,
minSelections: normalizedQuestion.minSelections,
maxSelections: normalizedQuestion.maxSelections,
};

const { role, mode } = env || {};
Expand Down
2 changes: 1 addition & 1 deletion packages/multiple-choice/docs/demo/generate.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ exports.model = (id, element) => ({
.blue {
color: blue !important;
}
`
`,
},
prompt: '',
promptEnabled: true,
Expand Down
10 changes: 10 additions & 0 deletions packages/multiple-choice/docs/pie-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,16 @@
"type": "boolean",
"title": "lockChoiceOrder"
},
"minSelections": {
"description": "Indicates that minimum selections that should be made (only for choice mode 'checkbox')",
"type": "number",
"title": "minSelections"
},
"maxSelections": {
"description": "Indicates that maximum selections that should be made (only for choice mode 'checkbox')",
"type": "number",
"title": "maxSelections"
},
"partialScoring": {
"description": "Indicates that the item should use partial scoring",
"type": "boolean",
Expand Down
8 changes: 8 additions & 0 deletions packages/multiple-choice/docs/pie-schema.json.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,14 @@ Indicates the order of choices presented to user
if true, answer choices will be presented in the order they are defined in the model
If false, answer choices may be presented in a random order (depending upon the value of the lockChoiceOrder environment variable)

# `minSelections` (number)

Indicates that minimum selections that should be made (only for choice mode 'checkbox')

# `maxSelections` (number)

Indicates that maximum selections that should be made (only for choice mode 'checkbox')

# `partialScoring` (boolean)

Indicates that the item should use partial scoring
Expand Down
51 changes: 22 additions & 29 deletions packages/multiple-choice/src/choice-input.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ const styleSheet = (theme) => ({
flex: 1,
'& label': {
color: color.text(),
'& > span':{
'& > span': {
fontSize: 'inherit',
}
},
},
},
horizontalLayout: {
Expand All @@ -36,45 +36,42 @@ const styleSheet = (theme) => ({
},
belowLayout: {
'& > label': {
alignItems: 'flex-start'
}
alignItems: 'flex-start',
},
},
belowLayoutCenter: {
justifyContent: 'center',
'& > label': {
alignItems: 'center'
}
alignItems: 'center',
},
},
belowSelectionComponent: {
display: 'flex',
alignItems: 'center',
'& > span': {
paddingLeft: 0
}
}
paddingLeft: 0,
},
},
});

const formStyleSheet = {
label: {
color: `${color.text()} !important`, //'var(--choice-input-color, black)'
backgroundColor: color.background(),
letterSpacing: 'normal'
letterSpacing: 'normal',
},
disabled: {
// apply to all children
'& *': {
cursor: 'not-allowed !important',
}
}
},
},
};

export const StyledFormControlLabel = withStyles(formStyleSheet, {
name: 'FormControlLabel',
})((props) => (
<FormControlLabel
{...props}
classes={{ label: props.classes.label, disabled: props.classes.disabled }}
/>
<FormControlLabel {...props} classes={{ label: props.classes.label, disabled: props.classes.disabled }} />
));

const colorStyle = (varName, fallback) => ({
Expand Down Expand Up @@ -177,8 +174,7 @@ export class ChoiceInput extends React.Component {
hideTick: PropTypes.bool,
isEvaluateMode: PropTypes.bool,
choicesLayout: PropTypes.oneOf(['vertical', 'grid', 'horizontal']),
updateSession: PropTypes.func,
isSelectionButtonBelow: PropTypes.bool
isSelectionButtonBelow: PropTypes.bool,
};

static defaultProps = {
Expand All @@ -196,10 +192,6 @@ export class ChoiceInput extends React.Component {

onToggleChoice(event) {
this.props.onChange(event);
this.props.updateSession({
value: this.props.value,
selected: !this.props.checked,
});
}

generateChoiceId() {
Expand All @@ -223,7 +215,7 @@ export class ChoiceInput extends React.Component {
choicesLayout,
value,
checked,
isSelectionButtonBelow
isSelectionButtonBelow,
} = this.props;

const Tag = choiceMode === 'checkbox' ? StyledCheckbox : StyledRadio;
Expand All @@ -233,7 +225,6 @@ export class ChoiceInput extends React.Component {
[classes.horizontalLayout]: choicesLayout === 'horizontal',
[classes.belowLayout]: isSelectionButtonBelow && choicesLayout !== 'grid',
[classes.belowLayoutCenter]: isSelectionButtonBelow && choicesLayout === 'grid',

});

const choicelabel = (
Expand All @@ -252,7 +243,7 @@ export class ChoiceInput extends React.Component {
return (
<div className={classNames(className, 'corespring-' + classSuffix, 'choice-input')}>
<div className={classes.row}>
{!hideTick && isEvaluateMode && <FeedbackTick correctness={correctness}/>}
{!hideTick && isEvaluateMode && <FeedbackTick correctness={correctness} />}
<div className={classNames(holderClassNames, 'checkbox-holder')}>
{isSelectionButtonBelow ? (
<StyledFormControlLabel
Expand Down Expand Up @@ -290,12 +281,14 @@ export class ChoiceInput extends React.Component {
value={value}
id={this.choiceId}
onChange={this.onToggleChoice}
/>}
/>)}
/>
}
/>
)}
</div>
</div>
{rationale && <PreviewPrompt className="rationale" defaultClassName="rationale" prompt={rationale}/>}
<Feedback feedback={feedback} correctness={correctness}/>
{rationale && <PreviewPrompt className="rationale" defaultClassName="rationale" prompt={rationale} />}
<Feedback feedback={feedback} correctness={correctness} />
</div>
);
}
Expand Down
12 changes: 4 additions & 8 deletions packages/multiple-choice/src/choice.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ import classNames from 'classnames';
import ChoiceInput from './choice-input';

export class Choice extends React.Component {
static propTypes = {
updateSession: PropTypes.func,
};
static propTypes = {};

onChange = (choice) => {
const { disabled, onChoiceChanged } = this.props;
Expand All @@ -33,8 +31,7 @@ export class Choice extends React.Component {
classes,
choicesLayout,
gridColumns,
updateSession,
isSelectionButtonBelow
isSelectionButtonBelow,
} = this.props;
const choiceClass = 'choice' + (index === choicesLength - 1 ? ' last' : '');

Expand All @@ -51,10 +48,9 @@ export class Choice extends React.Component {
index,
choicesLayout,
gridColumns,
updateSession,
onChange: this.onChange,
isEvaluateMode,
isSelectionButtonBelow
isSelectionButtonBelow,
};

const names = classNames(classes.choice, {
Expand Down Expand Up @@ -85,7 +81,7 @@ Choice.propTypes = {
displayKey: PropTypes.string,
choicesLayout: PropTypes.oneOf(['vertical', 'grid', 'horizontal']),
gridColumns: PropTypes.string,
isSelectionButtonBelow: PropTypes.bool
isSelectionButtonBelow: PropTypes.bool,
};

export default withStyles((theme) => ({
Expand Down
26 changes: 22 additions & 4 deletions packages/multiple-choice/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,26 @@ import { updateSessionValue } from './session-updater';

const log = debug('pie-ui:multiple-choice');

export const isComplete = (session) => !!(session && session.value && session.value.length);
export const isComplete = (session, model) => {
if (!session || !session.value) {
return false;
}

const { choiceMode, minSelections, maxSelections } = model || {};
const selections = session.value.length || 0;

if (choiceMode === 'radio') {
return !!selections;
}

if (selections < minSelections || selections > maxSelections) {
return false;
}

return true;

// !!(session && session.value && session.value.length);
};

export default class MultipleChoice extends HTMLElement {
constructor() {
Expand All @@ -32,7 +51,6 @@ export default class MultipleChoice extends HTMLElement {
this._model.choiceMode === 'radio' ? 'Multiple Choice Question' : 'Multiple Correct Answer Question',
);
this.setAttribute('role', 'region');

this.setLangAttribute();

ReactDOM.render(element, this, () => {
Expand All @@ -52,7 +70,7 @@ export default class MultipleChoice extends HTMLElement {
bubbles: true,
composed: true,
detail: {
complete: isComplete(this._session),
complete: isComplete(this._session, this._model),
component: this.tagName.toLowerCase(),
},
});
Expand All @@ -67,7 +85,7 @@ export default class MultipleChoice extends HTMLElement {
bubbles: true,
composed: true,
detail: {
complete: isComplete(this._session),
complete: isComplete(this._session, this._model),
component: this.tagName.toLowerCase(),
hasModel: this._model !== undefined,
},
Expand Down
Loading

0 comments on commit 1bf85f1

Please sign in to comment.