Describe the Issue
Test #6 has an issue, where you can only set the pet name, when the form is being submitted, not when putting in the pet name with "onChange", which wasn't specified anywhere.
Test #6 has worded it like this: "The second view should contain the provided pet name."
And when using the "onChange" handler inside the "input" element, this should count, which it doesn't.
I have included a comment showing the line of code in the "handleSubmit" event, which shows the line of code which is forced.
And I have included 2 comments showing which piece of code works, but isn't allowed in the input pet-name element.
Affected Page
https://www.freecodecamp.org/learn/front-end-development-libraries-v9/lab-digital-pet-game/lab-digital-pet-game
Your code
index.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Digital Pet Game</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Tektur:wght@400..900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="styles.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.5/babel.min.js"></script>
<script
data-plugins="transform-modules-umd"
type="text/babel"
src="index.tsx"
></script>
</head>
<body>
<div id="root"></div>
<script
data-plugins="transform-modules-umd"
type="text/babel"
data-presets="react"
data-type="module"
>
import { PetGame } from './index.tsx';
ReactDOM.createRoot(document.getElementById('root')).render(<PetGame />);
</script>
</body>
</html>
styles.css:
:root {
/* Backgrounds */
--bg-gradient-start: #dbeafe;
--bg-gradient-mid: #faf5ff;
--bg-gradient-end: #fce7f3;
/* Text */
--text-primary: #1f2937;
--text-muted: #6b7280;
--text-muted-2: #9ca3af;
/* Card / surface */
--card-bg-translucent: rgba(255, 255, 255, 0.6);
--card-bg-solid: rgba(255, 255, 255, 0.8);
/* Accent / brand */
--accent-purple: #8b5cf6;
--accent-purple-dark: #7c3aed;
/* Actions */
--action-feed: #10b981;
--action-feed-dark: #059669;
--action-play: #3b82f6;
--action-play-dark: #2563eb;
--action-sleep: #8b5cf6;
--action-sleep-dark: #7c3aed;
--action-heal: #ec4899;
--action-heal-dark: #db2777;
/* Stats */
--stat-high: #10b981;
--stat-medium: #f59e0b;
--stat-low: #ef4444;
/* UI greys */
--ui-bg-light: #e5e7eb;
--ui-fill: #8b5cf6; /* used for exp fill as accent */
/* Shadows */
--shadow-strong: rgba(0, 0, 0, 0.25);
--shadow-soft: rgba(0, 0, 0, 0.1);
}
html {
/*color-scheme: light dark;*/
font-family: system-ui;
display: flex;
justify-content: center;
font-family:
-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu",
"Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background: linear-gradient(135deg, #dbeafe 0%, #faf5ff 50%, #fce7f3 100%);
min-height: 100vh;
padding: 1rem;
}
header {
text-align: center;
margin-bottom: 2rem;
}
body {
max-width: 400px;
}
.base-container {
background: rgba(255, 255, 255, 0.6);
-webkit-backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
border-radius: 1.5rem;
padding: 2rem;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
.pet-name {
text-align: center;
}
}
.game-container {
margin-bottom: 1.5rem;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
/* Stats Grid */
.stats-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1rem;
margin-bottom: 1.5rem;
}
.stat-bar {
background: rgba(255, 255, 255, 0.8);
-webkit-backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
border-radius: 0.75rem;
padding: 1rem;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
transition: all 0.2s ease;
}
.stat-bar:hover {
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}
.stat-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 0.5rem;
}
.stat-label {
display: flex;
align-items: center;
gap: 0.5rem;
}
.stat-icon {
font-size: 1.25rem;
}
.stat-name {
font-weight: 500;
color: #374151;
}
.stat-value {
font-weight: bold;
color: #1f2937;
}
.stat-progress {
width: 100%;
height: 0.75rem;
background: #e5e7eb;
border-radius: 9999px;
overflow: hidden;
}
.stat-fill {
height: 100%;
transition: width 0.5s ease-out;
border-radius: 9999px;
}
.stat-fill.high {
background: #10b981;
}
.stat-fill.medium {
background: #f59e0b;
}
.stat-fill.low {
background: #ef4444;
}
.pet-buttons {
width: auto;
display: flex;
flex-direction: row;
gap: 1rem;
}
.pet-buttons .pet-button {
min-width: 80px;
background: #c2214a;
color: white;
border-radius: 13%;
border-style: solid;
border-color: #870b04;
box-shadow: rgba(120, 25, 23, 0.2) 2px 2px 4px 2px;
&:active {
transform: scale(0.95);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15) inset;
}
text-align: center;
cursor: pointer;
padding: 0.5rem 1rem;
}
.pet-sprite {
font-size: xxx-large;
text-align: center;
}
.data-management * {
margin: 0 0.5rem 0 0;
}
.info-panel {
text-align: center;
}
.info-panel #hud,
.info-panel .start-questions {
display: flex;
align-items: center;
flex-direction: column;
gap: 1rem;
}
.info-panel #hud p {
margin: 0rem;
}
.info-panel .start-questions div * {
margin: 0 0.5rem 0 0;
}
#pet-fact {
color: var(--text-muted-2);
margin: 0 0 0.5rem 0;
}
#set-name-btn {
display: inline-block;
padding: 14px 28px;
font-size: 1.15rem;
font-weight: 600;
background: linear-gradient(135deg, #6b8cff, #5ec2ff);
color: #fff;
border: none;
border-radius: 10px;
box-shadow: 0 6px 18px rgba(92, 132, 255, 0.25);
cursor: pointer;
transition:
transform 0.08s ease,
box-shadow 0.12s ease,
opacity 0.12s;
}
#set-name-btn:hover {
opacity: 0.98;
}
#set-name-btn:active,
#set-name-btn.pressed {
transform: translateY(2px) scale(0.995);
box-shadow: 0 3px 8px rgba(92, 132, 255, 0.18);
}
#set-name-btn:focus {
outline: 3px solid rgba(94, 190, 255, 0.28);
outline-offset: 2px;
}
index.tsx:
const { useState, useEffect } = React;
enum PetMood{
HAPPY,
EXCITED,
CONTENT,
SAD,
TIRED,
SICK,
HUNGRY
}
const Mood: Record<PetMood, string> = {
[PetMood.HAPPY]: "😺",
[PetMood.EXCITED]: "😸",
[PetMood.CONTENT]: "😼",
[PetMood.SAD]: "😿",
[PetMood.TIRED]: "😽",
[PetMood.SICK]: "🙀",
[PetMood.HUNGRY]: "😾"
}
export const PetGame = () => {
const [petName, setPetName] = useState("");
const [game, setGame] = useState(false);
const [image, setImage] = useState(Mood[PetMood.EXCITED]);
const [hunger, setHunger] = useState(0);
const [happiness, setHappiness] = useState(100);
const [energy, setEnergy] = useState(100);
let interval: number;
useEffect(() => {
if (!game) return;
interval = setInterval(() => {
setHunger(hunger + 10 > 100 ? 100 : hunger + 10);
setHappiness(happiness - 5 < 0 ? 0 : happiness - 5);
}, 20000)
return () => clearInterval(interval);
}, [game, hunger, happiness, energy]);
useEffect(() => {
if (hunger > 70){
setImage(Mood[PetMood.HUNGRY]);
} else if(energy < 30){
setImage(Mood[PetMood.TIRED]);
} else if(happiness < 30){
setImage(Mood[PetMood.SAD]);
} else if(happiness > 80 && energy > 70){
setImage(Mood[PetMood.EXCITED]);
} else if(happiness > 60){
setImage(Mood[PetMood.HAPPY]);
} else{
setImage(Mood[PetMood.CONTENT]);
}
}, [hunger, happiness, energy]);
const handleSubmit = (e:SubmitEvent) => {
e.preventDefault();
setGame(true);
// this is being forced
const input = document.getElementById("pet-name") as HTMLInputElement;
setPetName(input.value);
}
return (
<>
{!game ? (
<div className="base-container info-panel">
<form
className="start-questions"
onSubmit={handleSubmit as any}>
<label htmlFor="pet-name">Choose your pet name:</label>
<input
id="pet-name"
name="pet-name"
type="text"
// this isn't allowed
// onChange={(e) => setPetName(e.target.value)}
required
/>
<button id="set-name-btn" type="submit">Submit</button>
</form>
</div>
) : (
<>
<div className="base-container game-container">
<div className="pet-screen">
<div className="pet-sprite">{image}</div>
<h2 className="pet-name">{petName}</h2>
</div>
<div className="pet-buttons">
<button
id="eat-action"
className="pet-button pet-buttons-left"
onClick={() => {
setHunger(hunger - 10 < 0 ? 0 : hunger - 10);
setEnergy(energy + 5 > 100 ? 100 : energy + 5);
}}
>
EAT
</button>
<button
id="play-action"
className="pet-button pet-buttons-middle"
onClick={() => {
setEnergy(energy - 5 < 0 ? 0 : energy - 5);
setHappiness(happiness + 10 > 100 ? 100 : happiness + 10);
}}
>
PLAY
</button>
<button
id="sleep-action"
className="pet-button pet-buttons-right"
onClick={() => {
setHunger(hunger + 10 > 100 ? 100 : hunger + 10);
setEnergy(energy + 5 > 100 ? 100 : energy + 5);
}}
>
SLEEP
</button>
</div>
</div>
<div className="stats-grid">
<div className="stat-bar stat">
<div className="stat-label">
<div className="stat-icon">🍽️</div>
<div className="stat-name">Hunger</div>
</div>
<div className="stat-value">{hunger}%</div>
<div className="stat-progress">
<div className="stat-fill"></div>
</div>
</div>
<div className="stat-bar stat">
<div className="stat-label">
<div className="stat-icon">😊</div>
<div className="stat-name">Happiness</div>
</div>
<div className="stat-value">{happiness}%</div>
<div className="stat-progress">
<div className="stat-fill"></div>
</div>
</div>
<div className="stat-bar stat">
<div className="stat-label">
<div className="stat-icon">⚡</div>
<div className="stat-name">Energy</div>
</div>
<div className="stat-value">{energy}%</div>
<div className="stat-progress">
<div className="stat-fill"></div>
</div>
</div>
</div>
</>
)}
</>
)
};
Expected behavior
When using the onChange handler in the input element, it would save the pet name and display it correctly, resulting in Test #6 succeeding and not having to worry about it.
Instead Test #6 fails, if you won't don't do it inside the submit event
Screenshots
No response
System
- Device: PC
- OS: Windows 11
- Browser: Firefox
- Version: 153.0.4 (64-Bit)
Additional context
No response
Describe the Issue
Test #6 has an issue, where you can only set the pet name, when the form is being submitted, not when putting in the pet name with "onChange", which wasn't specified anywhere.
Test #6 has worded it like this: "The second view should contain the provided pet name."
And when using the "onChange" handler inside the "input" element, this should count, which it doesn't.
I have included a comment showing the line of code in the "handleSubmit" event, which shows the line of code which is forced.
And I have included 2 comments showing which piece of code works, but isn't allowed in the input pet-name element.
Affected Page
https://www.freecodecamp.org/learn/front-end-development-libraries-v9/lab-digital-pet-game/lab-digital-pet-game
Your code
index.html:
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"/> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Digital Pet Game</title> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Tektur:wght@400..900&display=swap" rel="stylesheet"> <link rel="stylesheet" href="styles.css" /> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.development.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.development.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.26.5/babel.min.js"></script> <script data-plugins="transform-modules-umd" type="text/babel" src="index.tsx" ></script> </head> <body> <div id="root"></div> <script data-plugins="transform-modules-umd" type="text/babel" data-presets="react" data-type="module" > import { PetGame } from './index.tsx'; ReactDOM.createRoot(document.getElementById('root')).render(<PetGame />); </script> </body> </html>styles.css:
:root { /* Backgrounds */ --bg-gradient-start: #dbeafe; --bg-gradient-mid: #faf5ff; --bg-gradient-end: #fce7f3; /* Text */ --text-primary: #1f2937; --text-muted: #6b7280; --text-muted-2: #9ca3af; /* Card / surface */ --card-bg-translucent: rgba(255, 255, 255, 0.6); --card-bg-solid: rgba(255, 255, 255, 0.8); /* Accent / brand */ --accent-purple: #8b5cf6; --accent-purple-dark: #7c3aed; /* Actions */ --action-feed: #10b981; --action-feed-dark: #059669; --action-play: #3b82f6; --action-play-dark: #2563eb; --action-sleep: #8b5cf6; --action-sleep-dark: #7c3aed; --action-heal: #ec4899; --action-heal-dark: #db2777; /* Stats */ --stat-high: #10b981; --stat-medium: #f59e0b; --stat-low: #ef4444; /* UI greys */ --ui-bg-light: #e5e7eb; --ui-fill: #8b5cf6; /* used for exp fill as accent */ /* Shadows */ --shadow-strong: rgba(0, 0, 0, 0.25); --shadow-soft: rgba(0, 0, 0, 0.1); } html { /*color-scheme: light dark;*/ font-family: system-ui; display: flex; justify-content: center; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; background: linear-gradient(135deg, #dbeafe 0%, #faf5ff 50%, #fce7f3 100%); min-height: 100vh; padding: 1rem; } header { text-align: center; margin-bottom: 2rem; } body { max-width: 400px; } .base-container { background: rgba(255, 255, 255, 0.6); -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); border-radius: 1.5rem; padding: 2rem; box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); .pet-name { text-align: center; } } .game-container { margin-bottom: 1.5rem; display: flex; flex-direction: column; justify-content: center; align-items: center; } /* Stats Grid */ .stats-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1rem; margin-bottom: 1.5rem; } .stat-bar { background: rgba(255, 255, 255, 0.8); -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); border-radius: 0.75rem; padding: 1rem; box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1); transition: all 0.2s ease; } .stat-bar:hover { box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); } .stat-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.5rem; } .stat-label { display: flex; align-items: center; gap: 0.5rem; } .stat-icon { font-size: 1.25rem; } .stat-name { font-weight: 500; color: #374151; } .stat-value { font-weight: bold; color: #1f2937; } .stat-progress { width: 100%; height: 0.75rem; background: #e5e7eb; border-radius: 9999px; overflow: hidden; } .stat-fill { height: 100%; transition: width 0.5s ease-out; border-radius: 9999px; } .stat-fill.high { background: #10b981; } .stat-fill.medium { background: #f59e0b; } .stat-fill.low { background: #ef4444; } .pet-buttons { width: auto; display: flex; flex-direction: row; gap: 1rem; } .pet-buttons .pet-button { min-width: 80px; background: #c2214a; color: white; border-radius: 13%; border-style: solid; border-color: #870b04; box-shadow: rgba(120, 25, 23, 0.2) 2px 2px 4px 2px; &:active { transform: scale(0.95); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15) inset; } text-align: center; cursor: pointer; padding: 0.5rem 1rem; } .pet-sprite { font-size: xxx-large; text-align: center; } .data-management * { margin: 0 0.5rem 0 0; } .info-panel { text-align: center; } .info-panel #hud, .info-panel .start-questions { display: flex; align-items: center; flex-direction: column; gap: 1rem; } .info-panel #hud p { margin: 0rem; } .info-panel .start-questions div * { margin: 0 0.5rem 0 0; } #pet-fact { color: var(--text-muted-2); margin: 0 0 0.5rem 0; } #set-name-btn { display: inline-block; padding: 14px 28px; font-size: 1.15rem; font-weight: 600; background: linear-gradient(135deg, #6b8cff, #5ec2ff); color: #fff; border: none; border-radius: 10px; box-shadow: 0 6px 18px rgba(92, 132, 255, 0.25); cursor: pointer; transition: transform 0.08s ease, box-shadow 0.12s ease, opacity 0.12s; } #set-name-btn:hover { opacity: 0.98; } #set-name-btn:active, #set-name-btn.pressed { transform: translateY(2px) scale(0.995); box-shadow: 0 3px 8px rgba(92, 132, 255, 0.18); } #set-name-btn:focus { outline: 3px solid rgba(94, 190, 255, 0.28); outline-offset: 2px; }index.tsx:
const { useState, useEffect } = React; enum PetMood{ HAPPY, EXCITED, CONTENT, SAD, TIRED, SICK, HUNGRY } const Mood: Record<PetMood, string> = { [PetMood.HAPPY]: "😺", [PetMood.EXCITED]: "😸", [PetMood.CONTENT]: "😼", [PetMood.SAD]: "😿", [PetMood.TIRED]: "😽", [PetMood.SICK]: "🙀", [PetMood.HUNGRY]: "😾" } export const PetGame = () => { const [petName, setPetName] = useState(""); const [game, setGame] = useState(false); const [image, setImage] = useState(Mood[PetMood.EXCITED]); const [hunger, setHunger] = useState(0); const [happiness, setHappiness] = useState(100); const [energy, setEnergy] = useState(100); let interval: number; useEffect(() => { if (!game) return; interval = setInterval(() => { setHunger(hunger + 10 > 100 ? 100 : hunger + 10); setHappiness(happiness - 5 < 0 ? 0 : happiness - 5); }, 20000) return () => clearInterval(interval); }, [game, hunger, happiness, energy]); useEffect(() => { if (hunger > 70){ setImage(Mood[PetMood.HUNGRY]); } else if(energy < 30){ setImage(Mood[PetMood.TIRED]); } else if(happiness < 30){ setImage(Mood[PetMood.SAD]); } else if(happiness > 80 && energy > 70){ setImage(Mood[PetMood.EXCITED]); } else if(happiness > 60){ setImage(Mood[PetMood.HAPPY]); } else{ setImage(Mood[PetMood.CONTENT]); } }, [hunger, happiness, energy]); const handleSubmit = (e:SubmitEvent) => { e.preventDefault(); setGame(true); // this is being forced const input = document.getElementById("pet-name") as HTMLInputElement; setPetName(input.value); } return ( <> {!game ? ( <div className="base-container info-panel"> <form className="start-questions" onSubmit={handleSubmit as any}> <label htmlFor="pet-name">Choose your pet name:</label> <input id="pet-name" name="pet-name" type="text" // this isn't allowed // onChange={(e) => setPetName(e.target.value)} required /> <button id="set-name-btn" type="submit">Submit</button> </form> </div> ) : ( <> <div className="base-container game-container"> <div className="pet-screen"> <div className="pet-sprite">{image}</div> <h2 className="pet-name">{petName}</h2> </div> <div className="pet-buttons"> <button id="eat-action" className="pet-button pet-buttons-left" onClick={() => { setHunger(hunger - 10 < 0 ? 0 : hunger - 10); setEnergy(energy + 5 > 100 ? 100 : energy + 5); }} > EAT </button> <button id="play-action" className="pet-button pet-buttons-middle" onClick={() => { setEnergy(energy - 5 < 0 ? 0 : energy - 5); setHappiness(happiness + 10 > 100 ? 100 : happiness + 10); }} > PLAY </button> <button id="sleep-action" className="pet-button pet-buttons-right" onClick={() => { setHunger(hunger + 10 > 100 ? 100 : hunger + 10); setEnergy(energy + 5 > 100 ? 100 : energy + 5); }} > SLEEP </button> </div> </div> <div className="stats-grid"> <div className="stat-bar stat"> <div className="stat-label"> <div className="stat-icon">🍽️</div> <div className="stat-name">Hunger</div> </div> <div className="stat-value">{hunger}%</div> <div className="stat-progress"> <div className="stat-fill"></div> </div> </div> <div className="stat-bar stat"> <div className="stat-label"> <div className="stat-icon">😊</div> <div className="stat-name">Happiness</div> </div> <div className="stat-value">{happiness}%</div> <div className="stat-progress"> <div className="stat-fill"></div> </div> </div> <div className="stat-bar stat"> <div className="stat-label"> <div className="stat-icon">⚡</div> <div className="stat-name">Energy</div> </div> <div className="stat-value">{energy}%</div> <div className="stat-progress"> <div className="stat-fill"></div> </div> </div> </div> </> )} </> ) };Expected behavior
When using the onChange handler in the input element, it would save the pet name and display it correctly, resulting in Test #6 succeeding and not having to worry about it.
Instead Test #6 fails, if you won't don't do it inside the submit event
Screenshots
No response
System
Additional context
No response