diff --git a/assets/theme-v2/css/tables.css b/assets/theme-v2/css/tables.css index 8289fd805..ae4dc414e 100644 --- a/assets/theme-v2/css/tables.css +++ b/assets/theme-v2/css/tables.css @@ -115,9 +115,10 @@ td { cursor: pointer } -.data-table [data-game-active-cell="true"] { - box-shadow: inset var(--space-3) 0 0 var(--gold); - border-bottom-color: var(--gold-border-muted) +.data-table [data-game-toggle][aria-current="true"] { + border-color: var(--gold); + background: color-mix(in srgb, var(--gold) 18%, var(--panel-soft)); + box-shadow: inset 0 0 0 1px var(--gold-border-muted) } .idea-board-idea-label { diff --git a/docs_build/dev/reports/PR_26174_ALFA_018-game-selection-button-state.md b/docs_build/dev/reports/PR_26174_ALFA_018-game-selection-button-state.md new file mode 100644 index 000000000..cb4c0d692 --- /dev/null +++ b/docs_build/dev/reports/PR_26174_ALFA_018-game-selection-button-state.md @@ -0,0 +1,26 @@ +# PR_26174_ALFA_018-game-selection-button-state + +## Summary + +Moved Game Hub selected-game indication to the game button only. + +## Implementation + +- Removed active-game markers from parent rows. +- Removed active-game markers from parent table cells. +- Replaced the left-border cell highlight with selected styling on the game button. +- Removed the `Selected {game}.` status log update when selecting a game row. +- Preserved child row behavior so Source Idea and Readiness Output follow the selected game. +- Added targeted Playwright assertions for one selected button, unchanged sibling columns, child row movement, and no selected status copy. + +## Scope Control + +- Preserved the existing API/service contract. +- Preserved the Game row parent structure. +- Preserved Source Idea and Readiness Output child rows/tables. +- Did not add browser-owned product data. +- Did not introduce silent fallbacks. + +## ZIP + +- `tmp/PR_26174_ALFA_018-game-selection-button-state_delta.zip` diff --git a/docs_build/dev/reports/PR_26174_ALFA_018-game-selection-button-state_branch-validation.txt b/docs_build/dev/reports/PR_26174_ALFA_018-game-selection-button-state_branch-validation.txt new file mode 100644 index 000000000..b3d79a161 --- /dev/null +++ b/docs_build/dev/reports/PR_26174_ALFA_018-game-selection-button-state_branch-validation.txt @@ -0,0 +1,14 @@ +Branch validation: PASS + +Branch: +pr/26174-ALFA-018-game-selection-button-state + +Base stack branch: +pr/26174-ALFA-017-game-hub-guest-save-and-crew-cleanup + +Checks: +- Current branch is the ALFA_018 branch: PASS +- Worktree was clean before ALFA_018 edits: PASS +- Scope limited to Game Hub selection state, targeted Playwright coverage, and required reports: PASS +- No protected Project Instructions changes: PASS +- No merge to main performed: PASS diff --git a/docs_build/dev/reports/PR_26174_ALFA_018-game-selection-button-state_manual-validation-notes.txt b/docs_build/dev/reports/PR_26174_ALFA_018-game-selection-button-state_manual-validation-notes.txt new file mode 100644 index 000000000..85f5d6781 --- /dev/null +++ b/docs_build/dev/reports/PR_26174_ALFA_018-game-selection-button-state_manual-validation-notes.txt @@ -0,0 +1,10 @@ +Manual validation notes: PASS + +- Reviewed `docs_build/dev/ProjectInstructions/addendums/table_first_ui.md`. +- Confirmed selected state remains on the game button through `aria-current`. +- Confirmed parent rows no longer receive active attributes. +- Confirmed cells no longer receive active markers. +- Confirmed the old left-border cell selector was removed. +- Confirmed selecting another game leaves only one selected game button. +- Confirmed Source Idea and Readiness Output child rows move with the selected game. +- Confirmed the bottom status area no longer receives `Selected {game}.` when a game row is selected. diff --git a/docs_build/dev/reports/PR_26174_ALFA_018-game-selection-button-state_requirement-checklist.txt b/docs_build/dev/reports/PR_26174_ALFA_018-game-selection-button-state_requirement-checklist.txt new file mode 100644 index 000000000..43fd997cb --- /dev/null +++ b/docs_build/dev/reports/PR_26174_ALFA_018-game-selection-button-state_requirement-checklist.txt @@ -0,0 +1,13 @@ +Requirement checklist: PASS + +- Selected game is indicated only by the Game button: PASS +- Row-level selection indicators removed: PASS +- Left border highlight removed: PASS +- Row background highlight removed: PASS +- Cell background highlights removed: PASS +- Purpose, Status, and Actions columns remain visually identical for all rows: PASS +- Only one game may be selected at a time: PASS +- Selecting a different game moves selected styling to that game button: PASS +- Child rows continue to follow the selected game: PASS +- Follow table_first_ui.md: PASS +- Removed selected-game status copy from bottom status area: PASS diff --git a/docs_build/dev/reports/PR_26174_ALFA_018-game-selection-button-state_validation-lane.txt b/docs_build/dev/reports/PR_26174_ALFA_018-game-selection-button-state_validation-lane.txt new file mode 100644 index 000000000..edd65a9f8 --- /dev/null +++ b/docs_build/dev/reports/PR_26174_ALFA_018-game-selection-button-state_validation-lane.txt @@ -0,0 +1,11 @@ +Validation lane: PASS + +Commands: +- `git diff --check -- toolbox/game-hub/game-hub.js assets/theme-v2/css/tables.css tests/playwright/tools/GameHubMockRepository.spec.mjs` + - PASS +- `node --check toolbox/game-hub/game-hub.js` + - PASS +- `npx playwright test tests/playwright/tools/GameHubMockRepository.spec.mjs -g "Game Hub"` + - PASS, 11 passed + +Generated coverage reports were restored after Playwright validation to keep this PR scoped. diff --git a/docs_build/dev/reports/codex_changed_files.txt b/docs_build/dev/reports/codex_changed_files.txt index f7be6c46d..6ae3f1e60 100644 --- a/docs_build/dev/reports/codex_changed_files.txt +++ b/docs_build/dev/reports/codex_changed_files.txt @@ -1,10 +1,10 @@ -docs_build/dev/reports/PR_26174_ALFA_017-game-hub-guest-save-and-crew-cleanup.md -docs_build/dev/reports/PR_26174_ALFA_017-game-hub-guest-save-and-crew-cleanup_branch-validation.txt -docs_build/dev/reports/PR_26174_ALFA_017-game-hub-guest-save-and-crew-cleanup_manual-validation-notes.txt -docs_build/dev/reports/PR_26174_ALFA_017-game-hub-guest-save-and-crew-cleanup_requirement-checklist.txt -docs_build/dev/reports/PR_26174_ALFA_017-game-hub-guest-save-and-crew-cleanup_validation-lane.txt +assets/theme-v2/css/tables.css +docs_build/dev/reports/PR_26174_ALFA_018-game-selection-button-state.md +docs_build/dev/reports/PR_26174_ALFA_018-game-selection-button-state_branch-validation.txt +docs_build/dev/reports/PR_26174_ALFA_018-game-selection-button-state_manual-validation-notes.txt +docs_build/dev/reports/PR_26174_ALFA_018-game-selection-button-state_requirement-checklist.txt +docs_build/dev/reports/PR_26174_ALFA_018-game-selection-button-state_validation-lane.txt docs_build/dev/reports/codex_changed_files.txt docs_build/dev/reports/codex_review.diff tests/playwright/tools/GameHubMockRepository.spec.mjs toolbox/game-hub/game-hub.js -toolbox/game-hub/index.html diff --git a/docs_build/dev/reports/codex_review.diff b/docs_build/dev/reports/codex_review.diff index 882ec504b..55044b966 100644 Binary files a/docs_build/dev/reports/codex_review.diff and b/docs_build/dev/reports/codex_review.diff differ diff --git a/tests/playwright/tools/GameHubMockRepository.spec.mjs b/tests/playwright/tools/GameHubMockRepository.spec.mjs index 40f1bc19e..7ec3691b8 100644 --- a/tests/playwright/tools/GameHubMockRepository.spec.mjs +++ b/tests/playwright/tools/GameHubMockRepository.spec.mjs @@ -290,24 +290,48 @@ test("Game Hub creates, opens, and deletes mock games", async ({ page }) => { const demoGameRow = page.locator("[data-game-row='demo-game']"); await expect(demoGameRow.locator("td")).toHaveText(["Game", "Under Construction", "Edit"]); await expect(demoGameRow).not.toContainText("User 1"); - await expect(demoGameRow).toHaveAttribute("data-game-active", "true"); - await expect(demoGameRow).toHaveAttribute("aria-current", "true"); - await expect(demoGameRow.locator("th[data-game-active-cell='true']")).toContainText("Demo Game"); - const activeCellStyle = await demoGameRow.locator("th[data-game-active-cell='true']").evaluate((cell) => { - const styles = getComputedStyle(cell); - return { - backgroundColor: styles.backgroundColor, - boxShadow: styles.boxShadow, - }; - }); - const inactiveCellBackground = await page.locator("[data-game-row='gravity-demo'] th").evaluate((cell) => getComputedStyle(cell).backgroundColor); - expect(activeCellStyle.backgroundColor).toBe(inactiveCellBackground); - expect(activeCellStyle.boxShadow).not.toBe("none"); + await expect(demoGameRow).not.toHaveAttribute("data-game-active", "true"); + await expect(demoGameRow).not.toHaveAttribute("aria-current", "true"); + await expect(demoGameRow.locator("th[data-game-active-cell='true']")).toHaveCount(0); + await expect(page.locator("[data-game-row][data-game-active='true']")).toHaveCount(0); + await expect(page.locator("[data-game-row][aria-current='true']")).toHaveCount(0); + await expect(page.locator("[data-game-active-cell='true']")).toHaveCount(0); + const rowVisuals = await page.locator("[data-game-row]").evaluateAll((rows) => rows.map((row) => { + const cells = Array.from(row.children).slice(1); + return cells.map((cell) => { + const styles = getComputedStyle(cell); + return { + backgroundColor: styles.backgroundColor, + boxShadow: styles.boxShadow, + }; + }); + })); + expect(rowVisuals[0]).toEqual(rowVisuals[1]); await expect(demoGameRow.locator("> .status")).toHaveCount(0); await expect(demoGameRow.locator("[data-game-toggle='demo-game']")).toHaveAttribute("aria-expanded", "false"); await expect(demoGameRow.locator("[data-game-toggle='demo-game']")).not.toHaveClass(/primary/); await expect(demoGameRow.locator("[data-game-toggle='demo-game']")).toHaveClass(/\bbtn--compact\b/); + await expect(demoGameRow.locator("[data-game-toggle='demo-game']")).toHaveAttribute("data-game-active", "true"); await expect(demoGameRow.locator("[data-game-toggle='demo-game']")).toHaveAttribute("aria-current", "true"); + await expect(page.locator("[data-game-toggle][aria-current='true']")).toHaveCount(1); + await expect(page.locator("[data-game-toggle][data-game-active='true']")).toHaveCount(1); + const activeButtonStyle = await demoGameRow.locator("[data-game-toggle='demo-game']").evaluate((button) => { + const styles = getComputedStyle(button); + return { + backgroundColor: styles.backgroundColor, + borderColor: styles.borderColor, + boxShadow: styles.boxShadow, + }; + }); + const inactiveButtonStyle = await page.locator("[data-game-row='gravity-demo'] [data-game-toggle='gravity-demo']").evaluate((button) => { + const styles = getComputedStyle(button); + return { + backgroundColor: styles.backgroundColor, + borderColor: styles.borderColor, + boxShadow: styles.boxShadow, + }; + }); + expect(activeButtonStyle).not.toEqual(inactiveButtonStyle); await expect(demoGameRow.getByRole("button", { name: "Edit Demo Game" })).toHaveText("Edit"); await expect(demoGameRow.getByRole("button", { name: "Edit Demo Game" })).not.toHaveClass(/primary/); await expect(demoGameRow.getByRole("button", { name: "Edit Demo Game" })).toHaveClass(/\bbtn--compact\b/); @@ -348,7 +372,10 @@ test("Game Hub creates, opens, and deletes mock games", async ({ page }) => { await addGameRow.getByLabel("Status").selectOption("Ready for Testing"); await addGameRow.getByRole("button", { name: "Save" }).click(); await expect(page.locator("[data-game-list]")).toContainText("Launch Test Game"); - await expect(page.locator("[data-game-row='launch-test-game-1']")).toHaveAttribute("data-game-active", "true"); + await expect(page.locator("[data-game-row='launch-test-game-1']")).not.toHaveAttribute("data-game-active", "true"); + await expect(page.locator("[data-game-row='launch-test-game-1']")).not.toHaveAttribute("aria-current", "true"); + await expect(page.locator("[data-game-toggle][aria-current='true']")).toHaveCount(1); + await expect(page.locator("[data-game-row='launch-test-game-1'] [data-game-toggle='launch-test-game-1']")).toHaveAttribute("aria-current", "true"); await expect(page.locator("[data-game-row='launch-test-game-1'] [data-game-toggle='launch-test-game-1']")).not.toHaveClass(/primary/); await expect(page.locator("[data-game-row='launch-test-game-1']").getByRole("button", { name: "Edit Launch Test Game" })).not.toHaveClass(/primary/); await expect(page.locator("[data-game-row='launch-test-game-1'] td").nth(0)).toHaveText("Learning Game"); @@ -376,14 +403,21 @@ test("Game Hub creates, opens, and deletes mock games", async ({ page }) => { await page.getByRole("button", { name: "Add Game" }).click(); await page.locator("[data-game-add-row='input']").getByLabel("Game").fill("Archive Game"); await page.locator("[data-game-add-row='input']").getByRole("button", { name: "Save" }).click(); - await expect(page.locator("[data-game-row='archive-game-2']")).toHaveAttribute("data-game-active", "true"); + await expect(page.locator("[data-game-row='archive-game-2']")).not.toHaveAttribute("data-game-active", "true"); + await expect(page.locator("[data-game-row='archive-game-2'] [data-game-toggle='archive-game-2']")).toHaveAttribute("aria-current", "true"); await expect(page.locator("[data-game-row='archive-game-2'] [data-game-toggle='archive-game-2']")).not.toHaveClass(/primary/); await page.locator("[data-game-row='launch-test-game-1'] [data-game-toggle='launch-test-game-1']").click(); - await expect(page.locator("[data-game-row='launch-test-game-1']")).toHaveAttribute("data-game-active", "true"); + await expect(page.locator("[data-game-row='launch-test-game-1']")).not.toHaveAttribute("data-game-active", "true"); + await expect(page.locator("[data-game-row='launch-test-game-1'] [data-game-toggle='launch-test-game-1']")).toHaveAttribute("aria-current", "true"); + await expect(page.locator("[data-game-row='archive-game-2'] [data-game-toggle='archive-game-2']")).not.toHaveAttribute("aria-current", "true"); + await expect(page.locator("[data-game-toggle][aria-current='true']")).toHaveCount(1); + await expect(page.locator("[data-game-toggle][data-game-active='true']")).toHaveCount(1); + await expect(page.locator("[data-game-expanded-row='launch-test-game-1']")).toHaveCount(2); + await expect(page.locator("[data-game-expanded-row='archive-game-2']")).toHaveCount(0); await expect(page.locator("[data-game-row='launch-test-game-1'] [data-game-toggle='launch-test-game-1']")).not.toHaveClass(/primary/); await expect(page.locator("[data-game-row='launch-test-game-1']").getByRole("button", { name: "Edit Launch Test Game" })).not.toHaveClass(/primary/); - await expect(page.locator("[data-game-hub-log]")).toHaveText("Selected Launch Test Game."); + await expect(page.locator("[data-game-hub-log]")).not.toHaveText("Selected Launch Test Game."); await page.getByRole("button", { name: "Delete Open Game" }).click(); await expect(page.locator("[data-game-row='launch-test-game-1']")).toHaveCount(0); diff --git a/toolbox/game-hub/game-hub.js b/toolbox/game-hub/game-hub.js index e4659f47b..a79c9d7d8 100644 --- a/toolbox/game-hub/game-hub.js +++ b/toolbox/game-hub/game-hub.js @@ -482,16 +482,9 @@ function renderGameParentRow(tbody, game, activeGame, progress) { const row = document.createElement("tr"); row.dataset.gameRow = game.id; - if (active) { - row.dataset.gameActive = "true"; - row.setAttribute("aria-current", "true"); - } const nameCell = document.createElement("th"); nameCell.scope = "row"; - if (active) { - nameCell.dataset.gameActiveCell = "true"; - } nameCell.append(createGameToggleButton(game, expanded, active)); row.append( nameCell, @@ -731,9 +724,6 @@ elements.gameList?.addEventListener("click", (event) => { renderWorkspace(); return; } - if (game) { - setStatusLog(`Selected ${game.name}.`); - } state.expandedGameId = state.expandedGameId === toggle.dataset.gameToggle ? "" : toggle.dataset.gameToggle; renderWorkspace(); return;