Level 2 Team Project
Emoji Personality Test App

Overview
This guide walks you through building the Emoji Personality Test App using Vite with the React (JavaScript) template. By the end, you’ll understand how each part works and how to complete the handleClick
function to move emojis between columns, track the count, and reveal results.
Step 1. Initialize the Project
Create a new Vite app:
npm create vite@latest emoji-personality-test -- --template react
Install dependencies:
cd emoji-personality-test npm install npm install nanoid
Open your editor and confirm the generated files:
package.json
,vite.config.js
src/
anddata/
andcomponents/
directory. If not created, create them now.
Step 2. Project Structure
emoji-personality-test/
├─ src/
│ ├ AppEmojiPersonality.jsx # Main application logic
│ ├ style.css # App-wide styles
│ ├ data/
│ │ └ emojis.js # Array of emoji strings
│ └ components/
│ ├ EmojiLists.jsx # Displays Liked vs Unselected lists
│ └ ResultsModal.jsx # Modal for final results
└─ public/
└ index.html # HTML entry point
Step 3. Data Source (data/emojis.js
)
data/emojis.js
)Contains a default export: an array of hundreds of emoji strings.
Used by
getRandomEmojis()
to pick three at random each round.
// emojis.js
export default [
"💘", "💝", "💖", /* … hundreds more … */ "😀", "😃", "😄"
]
4. Styling (style.css
)
style.css
)Key classes:
.wrapper
: centers and sizes the app container.overall-emojis-container
: lays out the three buttons horizontally.overall-emoji-lists-container
: holds the two list columns.individual-emoji-list-container
: styles each column.results-modal-container
: full-screen overlay for results
No changes are needed here unless you want to customize visuals.
Adjusting Emoji Size in Lists
If your emoji list items overflow, reduce their font sizes in style.css
:
/* Liked emojis (larger) */
.individual-emoji-list-container:first-of-type li {
font-size: 30px; /* was 45px */
}
/* Unselected emojis (smaller) */
.individual-emoji-list-container:last-of-type li {
font-size: 20px; /* was 25px */
line-height: 40px; /* adjust to match size */
}
Step 5. Main Logic (AppEmojiPersonality.jsx
)
AppEmojiPersonality.jsx
)The following is the complete AppEmojiPersonality.jsx
file with comments highlighting each part:
import React from 'react'
import ResultsModal from './components/ResultModal'
import EmojiLists from './components/EmojiLists'
import emojis from './data/emojis'
import { nanoid } from 'nanoid'
import './style.css'
export default function App() {
// State to track emojis liked by the user
const [likedEmojis, setLikedEmojis] = React.useState([])
// State to track emojis the user didn't click
const [passedEmojis, setPassedEmojis] = React.useState([])
// Current set of three emojis to display
const [currentEmojis, setCurrentEmojis] = React.useState(getRandomEmojis)
// Control whether the results modal is shown
const [showResults, setShowResults] = React.useState(false)
// Control whether the final results are ready (after animation)
const [resultsReady, setResultsReady] = React.useState(false)
/**
* handleClick — invoked when an emoji button is clicked.
* 1. Add clicked emoji to likedEmojis.
* 2. Add the other two emojis to passedEmojis.
* 3. Refresh currentEmojis via getRandomEmojis().
*/
function handleClick(event) {
// Determine which emoji was clicked
const clicked = event.target.innerText
// 1️⃣ Add the clicked emoji to the end of likedEmojis array
setLikedEmojis((prev) => [...prev, clicked])
// 2️⃣ Collect the other two emojis and add them to passedEmojis
const others = currentEmojis.filter((e) => e !== clicked)
setPassedEmojis((prev) => [...prev, ...others])
// 3️⃣ Generate three new random emojis for the next round
setCurrentEmojis(getRandomEmojis)
}
/**
* getRandomEmojis — returns an array of three random emojis
* picked from the data/emojis.js array.
*/
function getRandomEmojis() {
function chooseRandomEmoji() {
return emojis[Math.floor(Math.random() * emojis.length)]
}
return new Array(3).fill('').map(() => chooseRandomEmoji())
}
/** Show the results modal */
function getResults() {
setShowResults(true)
}
/** Reset all state for a new test */
function reset() {
setLikedEmojis([])
setPassedEmojis([])
setShowResults(false)
setResultsReady(false)
}
// When showResults becomes true, start a timer to trigger the "resultsReady" state
React.useEffect(() => {
if (showResults) {
setTimeout(() => {
setResultsReady(true)
}, 2000)
}
}, [showResults])
// Utility to generate list items with unique keys
function generateListItems(element) {
return <li key={nanoid()}>{element}</li>
}
return (
<div className="wrapper">
{/* Counter showing how many emojis have been liked out of 10 */}
<div className="results-counter">{likedEmojis.length} / 10</div>
{/* Modal component for displaying final results */}
<ResultsModal
showResults={showResults}
getResults={getResults}
resultsReady={resultsReady}
reset={reset}
generateListItems={generateListItems}
likedEmojis={likedEmojis}
/>
<h1>Emoji Personality Test</h1>
{/* Show three emoji buttons until 10 selections, then show Get Results */}
{likedEmojis.length < 10 ? (
<div className="overall-emojis-container">
<button onClick={handleClick}>{currentEmojis[0]}</button>
<button onClick={handleClick}>{currentEmojis[1]}</button>
<button onClick={handleClick}>{currentEmojis[2]}</button>
</div>
) : (
!showResults && (
<button className="get-results-button" onClick={getResults}>
Get Results
</button>
)
)}
{/* Lists showing liked vs. passed emojis */}
<EmojiLists
likedEmojis={likedEmojis}
passedEmojis={passedEmojis}
generateListItems={generateListItems}
/>
</div>
)
}
Main Logic (Step 5)
State Initialization: Five
useState
hooks manage distinct parts of the UI:likedEmojis
andpassedEmojis
collect user choices over time.currentEmojis
holds the three emojis displayed each round, initialized by callinggetRandomEmojis
.showResults
andresultsReady
control the flow and timing of the results modal.
Emojis Selection Flow (
handleClick
):Identify Clicked Emoji: using
event.target.innerText
.Update Liked List: appends the clicked emoji to the existing array (
setLikedEmojis(prev => [...prev, clicked])
).Update Passed List: filters out the clicked emoji and appends the remaining two (
setPassedEmojis(prev => [...prev, ...others])
).Next Round: refreshes
currentEmojis
by re-invokinggetRandomEmojis
, triggering a re-render with new choices.
Randomization (
getRandomEmojis
):Chooses three emojis independently by randomly indexing into the imported
emojis
array.Ensures every round offers fresh, unpredictable options.
Results Cycle:
Trigger: After 10 selections (reflected by
likedEmojis.length
), the Get Results button becomes visible.Timing: Clicking it sets
showResults
true, and an effect hook starts a 2 s timer before markingresultsReady
— enabling a simple "loading" animation via CSS.
Utility Helpers:
generateListItems
ensures each list item has a uniquekey
, preventing React warnings and enabling proper reconciliation.
Step 6. Display Components
components/EmojiLists.jsx
components/EmojiLists.jsx
components/ResultsModal.jsx
components/ResultsModal.jsx
// EmojiLists.jsx
export default function EmojiLists({
likedEmojis,
passedEmojis,
generateListItems,
}) {
return (
<div className="overall-emoji-lists-container">
<div className="individual-emoji-list-container">
<h3>Liked Emojis</h3>
<ul>{likedEmojis.map(generateListItems)}</ul>
</div>
<div className="individual-emoji-list-container">
<h3>Unselected Emojis</h3>
<ul>{passedEmojis.map(generateListItems)}</ul>
</div>
</div>
)
}
Explanation for Display Components (Step 6)
Props-Driven Rendering:
Both
EmojiLists
andResultsModal
receive state and helper functions via props — keeping them stateless and reusable.
Conditional UI (ResultsModal):
Returns
null
untilshowResults
is true.Inside, it toggles between two states: a Get Results button (waiting on user click) and the final emoji list (once
resultsReady
is true), plus a Try Again button to reset.
Flex Layout:
<div className="overall-emoji-lists-container">
uses CSS flexboxes to lay out columns side by side.Each
<ul>
wraps emojis with appropriate font sizes for visual hierarchy.
Separation of Concerns:
Core logic resides in the main
App
component; display-only components simply render based on props, simplifying testing and maintenance.
Display Components (Section 6)
Props-Driven Rendering:
Both
EmojiLists
andResultsModal
receive state and helper functions via props — keeping them stateless and reusable.
Conditional UI (ResultsModal):
Returns
null
untilshowResults
is true.Inside, it toggles between two states: a Get Results button (waiting on user click) and the final emoji list (once
resultsReady
is true), plus a Try Again button to reset.
Flex Layout:
<div className="overall-emoji-lists-container">
uses CSS flexboxes to lay out columns side by side.Each
<ul>
wraps emojis with appropriate font sizes for visual hierarchy.
Separation of Concerns:
Core logic resides in the main
App
component; display-only components simply render based on props, simplifying testing and maintenance.
Step 7. Run & Test
Start the dev server:
npm run dev
Interact with the three emojis.
Observe:
Emojis move into the correct columns.
Counter increments from 0 → 10.
At 10, click Get Results, then Try Again to reset.
Output:

Last updated