Source

components/Music/MusicPerformanceHeader/ExerciseGenerator/ExerciseGenerator.js

// NPM module imports
import React, { Component } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { withRouter } from "react-router-dom";

// Component Imports
import RectangularButton from "../../../Buttons/RectangularButton/RectangularButton";
import SmallTextInput from "../../../FormInputs/TextInputs/SmallTextInput/SmallTextInput";

// File imports
import * as rectButtonColorOptions from "../../../Buttons/RectangularButton/rectangularButtonColorOptions";
import * as buttonTypes from "../../../Buttons/buttonTypes";
import * as textInputTypes from "../../../FormInputs/TextInputs/textInputTypes";
import { exerciseRequested } from "../../../../store/actions";

// Image imports
import closeIconWhite from "../../../../assets/icons/close-icon-white.svg";

// Style imports
import styles from "./ExerciseGenerator.module.scss";

/**
 * Renders the ExerciseGenerator component
 * @extends {Component}
 * @component
 * @category Music
 * @author Dan Levy <danlevy124@gmail.com>
 */
class ExerciseGenerator extends Component {
    /**
     * ExerciseGenerator component state
     * @property {string} startMeasureValue - The entered start measure
     * @property {string} endMeasureValue - The entered end measure
     */
    state = {
        startMeasureValue: "1",
        endMeasureValue: "1",
    };

    /**
     * Submits the exercise generation request.
     * Shows the exercise.
     * @function
     */
    generateExerciseSubmitHandler = (event) => {
        event.preventDefault();

        // Tells Redux to request the exercise
        this.props.generateExercise(
            this.state.startMeasureValue,
            this.state.endMeasureValue
        );

        // Switches to the exercise view
        this.props.showExercise();
    };

    /**
     * Updates the measure value in state
     * @param event - The event that triggered this function to be called
     * @function
     */
    measureValueChangedHandler = (event) => {
        // Gets the text input value
        if (event.target.name === "start-measure") {
            // Updates the start measure in state
            this.setState({
                startMeasureValue: event.target.value,
            });
        } else if (event.target.name === "end-measure") {
            // Updates the end measure in state
            this.setState({
                endMeasureValue: event.target.value,
            });
        }
    };

    /**
     * Renders the ExerciseGenerator component
     */
    render() {
        // Returns the JSX to render
        return (
            <div className={styles.exerciseGenerator}>
                <div className={styles.exerciseGeneratorHeader}>
                    {/* Empty div on the left side (used for centering content) */}
                    <div></div>

                    {/* Heading */}
                    <h1 className={styles.exerciseGeneratorHeaderHeading}>
                        Generate an Exercise
                    </h1>

                    {/* Close button */}
                    <button
                        className={styles.exerciseGeneratorHeaderCloseButton}
                        type="button"
                        onClick={this.props.onGenerateExerciseClose}
                    >
                        <img
                            className={
                                styles.exerciseGeneratorHeaderCloseButtonImg
                            }
                            src={closeIconWhite}
                            alt="Close Button"
                        />
                    </button>
                </div>

                {/* Exercise generation form */}
                <form
                    className={styles.exerciseGeneratorForm}
                    onSubmit={this.generateExerciseSubmitHandler}
                >
                    <div className={styles.exerciseGeneratorFormMeasureInputs}>
                        {/* Start measure input */}
                        <div
                            className={styles.exerciseGeneratorFormMeasureInput}
                        >
                            <SmallTextInput
                                inputType={textInputTypes.NUMBER}
                                inputWidth="50px"
                                inputName="start-measure"
                                value={this.state.startMeasureValue}
                                labelText="Start Measure"
                                isRequired={true}
                                onChange={this.measureValueChangedHandler}
                                minVal={"1"}
                                maxVal={this.state.endMeasureValue}
                            />
                        </div>

                        {/* End measure input */}
                        <div
                            className={styles.exerciseGeneratorFormMeasureInput}
                        >
                            <SmallTextInput
                                inputType={textInputTypes.NUMBER}
                                inputWidth="50px"
                                inputName="end-measure"
                                value={this.state.endMeasureValue}
                                labelText="End Measure"
                                isRequired={true}
                                onChange={this.measureValueChangedHandler}
                                minVal={this.state.startMeasureValue}
                                maxVal={this.props.numberOfMeasures}
                            />
                        </div>
                    </div>

                    {/* Generate exercise button */}
                    <RectangularButton
                        type={buttonTypes.SUBMIT}
                        value="generate-exercise"
                        text="Go!"
                        backgroundColor={rectButtonColorOptions.ORANGE}
                    />
                </form>
            </div>
        );
    }
}

// Prop types for the ExerciseGenerator component
ExerciseGenerator.propTyes = {
    /**
     * The number of measures in the current piece of sheet music
     */
    numberOfMeasures: PropTypes.string.isRequired,

    /**
     * Closes the exercise generator
     */
    onGenerateExerciseClose: PropTypes.func.isRequired,

    /**
     * Tells Redux to request the exercise
     */
    generateExercise: PropTypes.func.isRequired,

    /**
     * Switches to the exercise view
     */
    showExercise: PropTypes.func.isRequired,
};

/**
 * Passes certain Redux actions to the ExerciseGenerator component as props.
 * This function is used only by the react-redux connect function.
 * @memberof ExerciseGenerator
 * @param {function} dispatch - The react-redux dispatch function
 * @returns {object} Redux actions used in the ExerciseGenerator component
 */
const mapDispatchToProps = (dispatch) => {
    return {
        generateExercise: (startMeasure, endMeasure) =>
            dispatch(exerciseRequested(startMeasure, endMeasure)),
    };
};

export default withRouter(connect(null, mapDispatchToProps)(ExerciseGenerator));