The tea shop is asking customers which tea to stock next. Build Poll, the default export: a question, one button per option, and the results once you've voted.
It takes question, a string, and options, an array of { id, label, votes } where votes is how many people voted before you. Both are optional, with a tea question and three teas as defaults. Don't change the options array or its objects.
Show the question as an <h2>, then the options as a list: one <li> per option, in order, each with a button whose text is the option's label. Below the list, show the total number of votes: 9 votes, or 1 vote when there's exactly one.
Clicking an option votes for it. Its button gets aria-pressed="true" (the others have aria-pressed="false"), your vote is added to its count, and the total goes up by one. Once you've voted, each <li> also shows the option's share of all the votes as a whole percentage, like 50%, with a bar as wide as that share (the tests check the text, not the bar). Before you vote, no percentages show.
You get one vote. Clicking your choice again changes nothing. Clicking another option moves your vote there. Round each share with Math.round, so the shares may not add up to exactly 100.
<Poll
question="Which tea should we stock next?"
options={[
{ id: "matcha", label: "Matcha", votes: 4 },
{ id: "hojicha", label: "Hojicha", votes: 3 },
{ id: "genmaicha", label: "Genmaicha", votes: 2 },
]}
/>
// after voting for Matcha:
<li><button aria-pressed="true">Matcha</button> 50% <div style="width: 50%"></div></li>
<li><button aria-pressed="false">Hojicha</button> 30% ...</li>
<li><button aria-pressed="false">Genmaicha</button> 20% ...</li>
<p>10 votes</p>
const options = [
{ id: "matcha", label: "Matcha", votes: 4 },
{ id: "hojicha", label: "Hojicha", votes: 3 },
{ id: "genmaicha", label: "Genmaicha", votes: 2 },
];
render(<App question="Which tea should we stock next?" options={options} />);
expect(screen.getByRole("heading", { level: 2, name: "Which tea should we stock next?" })).toBeInTheDocument();
const items = screen.getAllByRole("listitem");
expect(items.length).toBe(3);
expect(within(items[0]).getByRole("button", { name: "Matcha" })).toHaveAttribute("aria-pressed", "false");
expect(within(items[2]).getByRole("button", { name: "Genmaicha" })).toHaveAttribute("aria-pressed", "false");
expect(screen.getByText("9 votes")).toBeInTheDocument();
expect(screen.queryByText(/%/)).not.toBeInTheDocument();
const options = [
{ id: "matcha", label: "Matcha", votes: 4 },
{ id: "hojicha", label: "Hojicha", votes: 3 },
{ id: "genmaicha", label: "Genmaicha", votes: 2 },
];
render(<App options={options} />);
await user.click(screen.getByRole("button", { name: "Matcha" }));
const items = screen.getAllByRole("listitem");
expect(items[0]).toHaveTextContent("50%");
expect(items[1]).toHaveTextContent("30%");
expect(items[2]).toHaveTextContent("20%");
expect(screen.getByText("10 votes")).toBeInTheDocument();
const options = [
{ id: "matcha", label: "Matcha", votes: 4 },
{ id: "hojicha", label: "Hojicha", votes: 3 },
];
render(<App options={options} />);
await user.click(screen.getByRole("button", { name: "Hojicha" }));
expect(screen.getByRole("button", { name: "Hojicha" })).toHaveAttribute("aria-pressed", "true");
expect(screen.getByRole("button", { name: "Matcha" })).toHaveAttribute("aria-pressed", "false");
const options = [
{ id: "matcha", label: "Matcha", votes: 4 },
{ id: "hojicha", label: "Hojicha", votes: 3 },
{ id: "genmaicha", label: "Genmaicha", votes: 2 },
];
render(<App options={options} />);
await user.click(screen.getByRole("button", { name: "Matcha" }));
await user.click(screen.getByRole("button", { name: "Matcha" }));
expect(screen.getByText("10 votes")).toBeInTheDocument();
expect(screen.getAllByRole("listitem")[0]).toHaveTextContent("50%");
expect(screen.getByRole("button", { name: "Matcha" })).toHaveAttribute("aria-pressed", "true");
const options = [
{ id: "matcha", label: "Matcha", votes: 4 },
{ id: "hojicha", label: "Hojicha", votes: 3 },
{ id: "genmaicha", label: "Genmaicha", votes: 2 },
];
render(<App options={options} />);
await user.click(screen.getByRole("button", { name: "Matcha" }));
await user.click(screen.getByRole("button", { name: "Hojicha" }));
const items = screen.getAllByRole("listitem");
expect(items[0]).toHaveTextContent("40%");
expect(items[1]).toHaveTextContent("40%");
expect(items[2]).toHaveTextContent("20%");
expect(screen.getByText("10 votes")).toBeInTheDocument();
expect(screen.getByRole("button", { name: "Hojicha" })).toHaveAttribute("aria-pressed", "true");
expect(screen.getByRole("button", { name: "Matcha" })).toHaveAttribute("aria-pressed", "false");
const options = [
{ id: "matcha", label: "Matcha", votes: 1 },
{ id: "hojicha", label: "Hojicha", votes: 1 },
{ id: "genmaicha", label: "Genmaicha", votes: 0 },
];
render(<App options={options} />);
await user.click(screen.getByRole("button", { name: "Matcha" }));
const items = screen.getAllByRole("listitem");
expect(items[0]).toHaveTextContent("67%");
expect(items[1]).toHaveTextContent("33%");
expect(items[2]).toHaveTextContent(/(^|[^0-9])0%/);
expect(screen.getByText("3 votes")).toBeInTheDocument();
const options = [
{ id: "matcha", label: "Matcha", votes: 0 },
{ id: "hojicha", label: "Hojicha", votes: 0 },
];
render(<App options={options} />);
expect(screen.getByText("0 votes")).toBeInTheDocument();
await user.click(screen.getByRole("button", { name: "Hojicha" }));
expect(screen.getByText("1 vote")).toBeInTheDocument();
expect(screen.getAllByRole("listitem")[1]).toHaveTextContent("100%");
const options = [
{ id: "matcha", label: "Matcha", votes: 4 },
{ id: "hojicha", label: "Hojicha", votes: 3 },
];
const before = JSON.stringify(options);
render(<App options={options} />);
await user.click(screen.getByRole("button", { name: "Matcha" }));
await user.click(screen.getByRole("button", { name: "Hojicha" }));
expect(JSON.stringify(options)).toBe(before);
expect(screen.getByText("8 votes")).toBeInTheDocument();
Run tests runs every test on your code, here in your browser. The problem is solved when they all pass.
Ctrl + Enter runs the tests (⌘ + Enter on a Mac).
I'm here to help with Poll widget. I give hints, never the solution, so you get to solve it yourself. Ask for a hint, select code to ask about it, or press Ask Tuto why on a failing test.
Your first 3 answers need no account. for free to keep asking.