AniUI Academy
easy+150 XP

Build a star rating

Render five stars a person can rate something with, 1 through 5.

Hovering previews the rating (that star and every one before it lights up) without committing anything. Clicking commits the rating.

Requirements

  • Each star is a `<button>` with `aria-label="Rate 1 star"` for the first star and `aria-label="Rate {n} stars"` for the rest.
  • Each star has `data-filled="true"` or `"false"` reflecting the committed rating, and `data-preview="true"` or `"false"` reflecting whichever star is currently hovered (and every star before it) — independent of the committed rating.
  • An element with `data-testid="rating-value"` shows the committed rating as plain text ("0" before anything is clicked).
  • Hovering must never change the committed rating shown in rating-value.
  • Re-rating (clicking a different star after one is already committed) must correctly update which stars are filled — both up and down.

Your workspace

import { useState } from "react";

export default function App() {
  const [rating, setRating] = useState(0);
  const [hovered, setHovered] = useState(null);

  return (
    <div style={{ fontFamily: "sans-serif", padding: 16 }}>
      <div style={{ display: "flex", gap: 4 }}>
        {[1, 2, 3, 4, 5].map((n) => {
          // TODO: `filled` should reflect the committed rating, and
          // `preview` should reflect the hovered star (and everything
          // before it), independent of the committed rating.
          const filled = false;
          const preview = false;

          return (
            <button
              key={n}
              aria-label={`Rate ${n} star${n === 1 ? "" : "s"}`}
              data-filled={filled}
              data-preview={preview}
              onMouseEnter={() => setHovered(n)}
              onClick={() => setRating(n)}
              style={{
                fontSize: 24,
                border: "none",
                background: "none",
                cursor: "pointer",
                color: filled || preview ? "#f5a623" : "#ccc",
              }}
            >
              {"\u2605"}
            </button>
          );
        })}
      </div>
      <p>
        Rating: <span data-testid="rating-value">{rating}</span>
      </p>
    </div>
  );
}

Ready to check it?

5 tests run against your rendered component, right here in your browser. Sign in to claim the XP when you pass.

Stuck? The react course covers the ideas this problem is built on.