Skip to content

Commit

Permalink
Merge pull request #441 from concord-consortium/187021265-allow-empty…
Browse files Browse the repository at this point in the history
…-score-inputs

fix: Allow empty values in score input [PT-187021265]
  • Loading branch information
dougmartin authored Mar 5, 2024
2 parents db7129e + 19b3857 commit b2b0c15
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 58 deletions.
75 changes: 41 additions & 34 deletions css/portal-dashboard/feedback/feedback-settings-modal-button.less
Original file line number Diff line number Diff line change
Expand Up @@ -3,60 +3,67 @@
.feedbackSettingsModalButton {
display: flex;
align-items: center;
gap: 10px;
justify-content: space-between;

&.alignTop {
align-items: start;
}
.modalButtonAndLabel {

.modalButton {
display: flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
cursor: pointer;

&:hover .outerCircle {
background-color: white;
gap: 10px;

&:hover .innerCircle {
background-color: @feedback-green-light3B;
}
&.alignTop {
align-items: start;
}
&:active .outerCircle {
background-color: @feedback-green;

&:active .innerCircle {
background-color: white;
}
}

.outerCircle {
.modalButton {
display: flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
border-radius: 50%;
border: solid 1.5px @cc-charcoal-light1;
background-color: white;
cursor: pointer;

.innerCircle {
width: 12px;
height: 12px;
&:hover .outerCircle {
background-color: white;

&:hover .innerCircle {
background-color: @feedback-green-light3B;
}
}
&:active .outerCircle {
background-color: @feedback-green;

&:active .innerCircle {
background-color: white;
}
}

.outerCircle {
display: flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
border-radius: 50%;
border: solid 1.5px @cc-charcoal-light1;
background-color: white;
cursor: pointer;

.innerCircle {
width: 12px;
height: 12px;
background-color: white;
border-radius: 50%;

&.selected {
background-color: @feedback-green;
&.selected {
background-color: @feedback-green;
}
}
}
}
}

.modalButtonLabel {
font-weight: bold;
.modalButtonLabel {
font-weight: bold;
}
}
}
7 changes: 6 additions & 1 deletion css/portal-dashboard/feedback/feedback-settings-modal.less
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
height: auto;
left: 405px;
top: 135px;
width: 330px;
width: 335px;

.lightboxHeader {
border-radius: 8px 8px 0 0;
Expand Down Expand Up @@ -87,6 +87,11 @@
padding: 5px 10px 6px;
text-align: center;

&.disabled {
cursor: not-allowed;
opacity: 0.35;
}

&:hover {
background: @feedback-green-light4;
}
Expand Down
12 changes: 10 additions & 2 deletions css/portal-dashboard/feedback/score-input.less
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,20 @@
}

input {
width: 45px;
width: 50px;
margin-left: 5px;
padding: 5px 3px;
border: solid 1.5px @cc-charcoal-light1;
border-radius: 4px;
border-radius: 8px;
font-family: lato, arial, helvetica, sans-serif;
font-size: 16px;
text-align: right;

&::-webkit-inner-spin-button {
margin-left: 5px;
}
&::-webkit-outer-spin-button {
margin-left: 5px;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ export const ActivityFeedbackScore: React.FC<IProps> = (props) => {
const scoreType = scoringSettings.scoreType ?? NO_SCORE;
const [ scoreChanged, setScoreChanged ] = useState(false);

const handleScoreChange = (newScore: number) => {
const handleScoreChange = (newScore?: number) => {
setScoreChanged(true);
updateScoreThrottledAndNotLogged(newScore);
};

const updateScore = (score: number, logUpdate?: boolean) => {
const updateScore = (score?: number, logUpdate?: boolean) => {
if (activityId && studentId && updateActivityFeedback) {
if (logUpdate) {
trackEvent("Portal-Dashboard", "AddActivityLevelScore", { label: `${score}`, parameters: { activityId, studentId }});
Expand All @@ -43,10 +43,10 @@ export const ActivityFeedbackScore: React.FC<IProps> = (props) => {
};

// eslint-disable-next-line react-hooks/exhaustive-deps
const updateScoreThrottledAndNotLogged = useCallback(throttle((score: number) => updateScore(score, false), 2000), []);
const updateScoreThrottledAndNotLogged = useCallback(throttle((score?: number) => updateScore(score, false), 2000), []);

// eslint-disable-next-line react-hooks/exhaustive-deps
const updateScoreLogged = useCallback((score: number) => updateScore(score, true), []);
const updateScoreLogged = useCallback((score?: number) => updateScore(score, true), []);

if (!activityStarted || scoreType === NO_SCORE || scoreType === AUTOMATIC_SCORE) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,18 @@ export class FeedbackSettingsModalButton extends PureComponent<IProps> {

render() {
return (
<div className={classNames(css.feedbackSettingsModalButton, {[css.alignTop]: this.props.alignTop})}>
<div className={css.modalButton}>
<button className={css.outerCircle} data-cy="feedback-settings-radio-button" onClick={this.handleOnClick}>
<div className={`${css.innerCircle} ${this.props.selected ? css.selected : ""}`}></div>
</button>
<div className={css.feedbackSettingsModalButton}>
<div className={classNames(css.modalButtonAndLabel, {[css.alignTop]: this.props.alignTop})}>
<div className={css.modalButton}>
<button className={css.outerCircle} data-cy="feedback-settings-radio-button" onClick={this.handleOnClick}>
<div className={`${css.innerCircle} ${this.props.selected ? css.selected : ""}`}></div>
</button>
</div>
<div className={css.modalButtonLabel}>{this.props.label}</div>
</div>
<div>
{this.props.children}
</div>
<div className={css.modalButtonLabel}>{this.props.label}</div>
{this.props.children}
</div>
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { PureComponent } from "react";
import { Modal } from "react-bootstrap";
import { Map } from "immutable";
import classNames from "classnames";
import { FeedbackSettingsModalButton } from "./feedback-settings-modal-button";
import { Rubric } from "./rubric-utils";
import { updateActivityFeedbackSettings } from "../../../actions";
Expand Down Expand Up @@ -93,7 +94,7 @@ class FeedbackSettingsModal extends PureComponent<IProps, IState> {
<div className={css.closeButton} onClick={this.handleCancel} data-cy="feedback-settings-modal-close-button">
Cancel
</div>
<div className={css.closeButton} onClick={this.handleSave} data-cy="feedback-settings-modal-close-button">
<div className={classNames(css.closeButton, {[css.disabled]: this.saveDisabled})} onClick={this.handleSave} data-cy="feedback-settings-modal-close-button">
Save
</div>
</div>
Expand All @@ -120,8 +121,14 @@ class FeedbackSettingsModal extends PureComponent<IProps, IState> {
private handleSave = () => {
const {scoreType, maxScore} = this.state;
const {updateActivityFeedbackSettings, activityId, activityIndex} = this.props;
updateActivityFeedbackSettings(activityId, activityIndex, { scoreType, maxScore });
this.close();
if (!this.saveDisabled) {
const updates: any = { scoreType };
if (maxScore !== undefined) {
updates.maxScore = maxScore;
}
updateActivityFeedbackSettings(activityId, activityIndex, updates);
this.close();
}
}

private close = () => {
Expand All @@ -130,6 +137,11 @@ class FeedbackSettingsModal extends PureComponent<IProps, IState> {
onHide(show);
}
}

private get saveDisabled() {
const { scoreType, maxScore } = this.state;
return scoreType === MANUAL_SCORE && (maxScore === undefined || maxScore < 1);
}
}

function mapStateToProps() {
Expand Down
20 changes: 13 additions & 7 deletions js/components/portal-dashboard/feedback/score-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,22 @@ import classNames from "classnames";
import css from "../../../../css/portal-dashboard/feedback/score-input.less";

interface IProps {
score: number;
score?: number;
minScore: number;
disabled: boolean;
className?: string;
children?: React.ReactNode;
onChange: (score: number) => void;
onBlur?: (score: number) => void;
onChange: (score?: number) => void;
onBlur?: (score?: number) => void;
}

const maxScore = 999;

export class ScoreInput extends PureComponent<IProps> {

render() {
const { score, minScore, disabled, className, children } = this.props;
const { minScore, disabled, className, children } = this.props;
const score = this.props.score ?? "";

return (
<div className={classNames(css.scoreInput, className, {[css.disabled]: disabled})}>
Expand All @@ -36,9 +37,14 @@ export class ScoreInput extends PureComponent<IProps> {

private handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const {minScore, onChange} = this.props;
const score = parseInt(e.target.value, 10);
if ((score >= minScore) && (score <= maxScore)) {
onChange(score);
const value = e.target.value.trim();
if (value.length === 0) {
onChange();
} else {
const score = parseInt(e.target.value, 10);
if ((score >= minScore) && (score <= maxScore)) {
onChange(score);
}
}
}

Expand Down

0 comments on commit b2b0c15

Please sign in to comment.