Wednesday 22 June 2022

Web Tutorial: The Japanese language Trainer App (Part 3/4)

Right now, the display and the keyboard are up. We will not need any more components and utilities. What we will need, are button events that facilitate answers and progress of the Japanese-learning exercise.

In the answers object, add a click event, BtnAnswer_click(). item has to be passed in as an argument.

src/components/Keyboard/Keyboard.js
const answers = keyItems.map((item, index) => (
    <div
        key={'romaji_' + index}
        className={item === '' ? 'Answer Hide' : ( answer === '' ? 'Answer' : 'Answer Greyed')}
        onClick={()=>{BtnAnswer_click(item);}}
    >
        {item}                    
    </div>
    )
);


And here, let's define it.

src/components/Keyboard/Keyboard.js
let setAnswer = props.setAnswer;
let setResult = props.setResult;

const BtnAnswer_click = (ans)=> {

};


const keyItems = GetKeys();


We only want stuff to happen if a button with an actual answer was clicked on. So just in case, we first use an If block to ensure that ans is not an empty string. If so, we set answer to ans.

src/components/Keyboard/Keyboard.js
const BtnAnswer_click = (ans)=> {
    if (ans !== "") {
        setAnswer(ans);
    }

};


We now check if this is the correct answer. We verify that by comparing ans to the romaji property of the current element of charset, pointed to by question. If it's a match, increment result.

src/components/Keyboard/Keyboard.js
const BtnAnswer_click = (ans)=> {
    if (ans !== "") {
        setAnswer(ans);

        if (ans === charset[question].romaji) {
            setResult(result + 1)
        }

    }
};


Try it! Right now, the correct answer is "ko". I'm going to click on it.




We see the result. It is correct! Note that the keyboaad buttons are greyed out now that you have provided an answer.




Try this again by refreshing the app. The correct answer is "zu", but let's select "shi".




We see the result. It is wrong!




Now we are going to provide a click handler for the orange button that goes to the next question. In the Display component, add this function call to the button.

src/components/Display/Display.js
<div className="Remaining">
    <button onClick={ ()=>{BtnNext_click();}} disabled={answer === ""}>
        { remaining - 1 } REMAINING ➤
    </button>
</div>


Let's create this function.

src/components/Display/Display.js
let setQuestion = props.setQuestion;
let setUsedQuestions = props.setUsedQuestions;

const BtnNext_click = (ans)=> {

};


const ManageQuestionList = ()=> {


If remaining is still more than 0, decrement remaining.

src/components/Display/Display.js
const BtnNext_click = (ans)=> {
    if (remaining > 0) {
        setRemaining(remaining - 1);
    }

};


Then reset answer to an empty string, and run ManageQuestionList().

src/components/Display/Display.js
const BtnNext_click = (ans)=> {
    if (remaining > 0) {
        setRemaining(remaining - 1);
    }

    setAnswer("");
    ManageQuestionList();

};


Now if you click on the orange button, the number decrements, and you get a new random character. And the keyboard buttons are enabled!




Hold on, we're not done yet. Add this If block. It checks if started is true and remaining is 0, meaning the user has done all 20 questions.

src/components/Display/Display.js
if (started && remaining > 0 && charset[question] !== undefined) {
    return (
        <>
            <div className="Character">
                { charset[question].char }
            </div>
            <div className={ answer === "" ? "Result Hide" : "Result"}>
                <span className={ charset[question].romaji === answer ? "Correct" : "Wrong" }>
                    { charset[question].romaji === answer ? "✓" : "✗" }
                </span>
                { answer }
            </div>
            <div className="Remaining">
                <button data-testid="BtnRemaining" onClick={ ()=>{BtnNext_click();}} disabled={answer === ""}>
                    { remaining - 1 } REMAINING ➤
                </button>
            </div>
        </>           
    );
}

if (started && remaining === 0) {

}


return (
    <>
        <br />
        <h3>Welcome to J-Trainer!</h3>
        <ol>
            <li>Select your character set.</li>
            <li>Click <button className="btnSmall">BEGIN ➤ </button> to start.</li>
            <li>For every character that is displayed, select the correct pronounciation. Note that some characters may have the same pronounciation as others.</li>
            <li>Click the <button className="btnSmall">REMAINING ➤ </button> button to continue after the results are displayed.</li>
        </ol>
    </>            
);


We will return this. There is a div styled using CSS class Result. There is a h1 tag where we show how many correct answers (result) out of maxRemaining, which is 20. Also, if the percentage of correct answers is more than 50, we style it using Correct and Wrong if otherwise. The naming is a bad practice, but I can't be arsed to change it.

src/components/Display/Display.js
if (started && remaining === 0) {
    return (
        <>
            <div className="Result">
                <h1 className={ (result / maxRemaining * 100) > 50 ? "Correct" : "Wrong" }>
                    { result } / { maxRemaining }
                </h1>
            </div>
        </>           
    );

}


Below that, we have a h3 tag. Again, the styling is based on whether the percentage of correct answers is over 50.

src/components/Display/Display.js
if (started && remaining === 0) {
    return (
        <>
            <div className="Result">
                <h1 className={ (result / maxRemaining * 100) > 50 ? "Correct" : "Wrong" }>
                    { result } / { maxRemaining }
                </h1>
                <h3 className={ (result / maxRemaining * 100) > 50 ? "Correct" : "Wrong" }>
                    
                </h3>

            </div>
        </>           
    );
}


And we have different messages based on whether the percentage is 100, below 50 or otherwise.

src/components/Display/Display.js
if (started && remaining === 0) {
    return (
        <>
            <div className="Result">
                <h1 className={ (result / maxRemaining * 100) > 50 ? "Correct" : "Wrong" }>
                    { result } / { maxRemaining }
                </h1>
                <h3 className={ (result / maxRemaining * 100) > 50 ? "Correct" : "Wrong" }>
                    { (result / maxRemaining * 100) == 100 ? "よかった! Perfect score!" : ((result / maxRemaining * 100) < 50 ? "Practice more..." : "すごいですね!") }
                </h3>
            </div>
        </>           
    );
}


Here are the messages you get for different scenarios.









Next

The app is functioning, and now we will write tests for the interface.

No comments:

Post a Comment