@@ -58,13 +92,16 @@ export function TeamScoreBreakdown({
const remaining = breakdown.totalCount - breakdown.completedCount;
- // Flatten all picks sorted by sport name then pick number
- const allPicks = breakdown.picks
- .slice()
- .toSorted((a, b) => {
- const sportCmp = a.participant.sport.localeCompare(b.participant.sport);
- return sportCmp !== 0 ? sportCmp : a.pickNumber - b.pickNumber;
- });
+ function handleSort(column: SortColumn) {
+ if (sortColumn === column) {
+ setSortDirection(sortDirection === "asc" ? "desc" : "asc");
+ } else {
+ setSortColumn(column);
+ setSortDirection(column === "points" ? "desc" : "asc");
+ }
+ }
+
+ const allPicks = sortPicks(breakdown.picks, sortColumn, sortDirection);
return (
@@ -104,13 +141,29 @@ export function TeamScoreBreakdown({
- Pick #
+
+
+
Sport
Participant
Position
- Points
- actual / projected
+
diff --git a/app/components/standings/__tests__/TeamScoreBreakdown.test.tsx b/app/components/standings/__tests__/TeamScoreBreakdown.test.tsx
index ff19451..63527e2 100644
--- a/app/components/standings/__tests__/TeamScoreBreakdown.test.tsx
+++ b/app/components/standings/__tests__/TeamScoreBreakdown.test.tsx
@@ -1,5 +1,6 @@
import { describe, it, expect } from "vitest";
import { render, screen } from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
import { BrowserRouter } from "react-router";
import { TeamScoreBreakdown } from "../TeamScoreBreakdown";
@@ -376,6 +377,130 @@ describe("TeamScoreBreakdown", () => {
});
});
+ describe("Sorting", () => {
+ function getParticipantNames() {
+ return screen
+ .getAllByRole("row")
+ .slice(1) // skip header row
+ .map((row) => row.querySelectorAll("td")[2]?.textContent ?? "");
+ }
+
+ it("should default sort by pick number ascending", () => {
+ renderWithRouter(
+
+ );
+
+ const names = getParticipantNames();
+ expect(names).toEqual(["Team A", "Driver B", "Team C"]);
+ });
+
+ it("should sort by pick number descending on second click", async () => {
+ const user = userEvent.setup();
+ renderWithRouter(
+
+ );
+
+ const pickHeader = screen.getByRole("button", { name: /pick #/i });
+ await user.click(pickHeader); // toggle to desc
+ const names = getParticipantNames();
+ expect(names).toEqual(["Team C", "Driver B", "Team A"]);
+ });
+
+ it("should sort by points descending on first click of Points header", async () => {
+ const user = userEvent.setup();
+ renderWithRouter(
+
+ );
+
+ // picks: Team A=100, Driver B=50, Team C=0
+ const pointsHeader = screen.getByRole("button", { name: /points/i });
+ await user.click(pointsHeader);
+ const names = getParticipantNames();
+ expect(names).toEqual(["Team A", "Driver B", "Team C"]);
+ });
+
+ it("should sort by points ascending on second click of Points header", async () => {
+ const user = userEvent.setup();
+ renderWithRouter(
+
+ );
+
+ const pointsHeader = screen.getByRole("button", { name: /points/i });
+ await user.click(pointsHeader); // desc
+ await user.click(pointsHeader); // asc
+ const names = getParticipantNames();
+ expect(names).toEqual(["Team C", "Driver B", "Team A"]);
+ });
+
+ it("should use projected points as tiebreaker when actual points are equal", async () => {
+ const user = userEvent.setup();
+ const tieBreakdown = {
+ ...mockBreakdown,
+ picks: [
+ {
+ pickNumber: 1,
+ round: 1,
+ participant: { id: "p1", name: "Alpha", sport: "NFL", sportsSeasonId: "ss-nfl" },
+ finalPosition: null,
+ points: 50,
+ projectedPoints: 10,
+ isComplete: false,
+ isPartialScore: false,
+ },
+ {
+ pickNumber: 2,
+ round: 1,
+ participant: { id: "p2", name: "Beta", sport: "NFL", sportsSeasonId: "ss-nfl" },
+ finalPosition: null,
+ points: 50,
+ projectedPoints: 30,
+ isComplete: false,
+ isPartialScore: false,
+ },
+ ],
+ };
+
+ renderWithRouter(
+
+ );
+
+ const pointsHeader = screen.getByRole("button", { name: /points/i });
+ await user.click(pointsHeader); // sort by points desc → Beta (50/30) before Alpha (50/10)
+ const names = getParticipantNames();
+ expect(names).toEqual(["Beta", "Alpha"]);
+ });
+ });
+
describe("Without Standing Data", () => {
it("should render without standing prop", () => {
renderWithRouter(