diff --git a/docs_build/dev/reports/PR_26174_ALFA_010-game-hub-source-idea-child-table-polish-branch-validation.txt b/docs_build/dev/reports/PR_26174_ALFA_010-game-hub-source-idea-child-table-polish-branch-validation.txt new file mode 100644 index 000000000..bc01c2101 --- /dev/null +++ b/docs_build/dev/reports/PR_26174_ALFA_010-game-hub-source-idea-child-table-polish-branch-validation.txt @@ -0,0 +1,6 @@ +Branch Validation: PASS + +PASS - Current branch: pr/26174-ALFA-010-game-hub-source-idea-child-table-polish. +PASS - Stack base: pr/26174-ALFA-009-game-hub-parent-child-table-layout. +PASS - Changes are scoped to Source Idea child table rendering, targeted tests, and required reports. +PASS - No merge to main performed. diff --git a/docs_build/dev/reports/PR_26174_ALFA_010-game-hub-source-idea-child-table-polish-manual-validation-notes.txt b/docs_build/dev/reports/PR_26174_ALFA_010-game-hub-source-idea-child-table-polish-manual-validation-notes.txt new file mode 100644 index 000000000..161a3d220 --- /dev/null +++ b/docs_build/dev/reports/PR_26174_ALFA_010-game-hub-source-idea-child-table-polish-manual-validation-notes.txt @@ -0,0 +1,6 @@ +Manual Validation Notes: PASS + +PASS - Confirmed the Idea Board-created Lantern Reef project expands in Game Hub. +PASS - Confirmed Source Idea child table shows Idea, Pitch, and Note 1 rows. +PASS - Confirmed Source Idea child table has no input, textarea, select, or button controls. +PASS - Confirmed source details still come from the existing Game Hub project record contract. diff --git a/docs_build/dev/reports/PR_26174_ALFA_010-game-hub-source-idea-child-table-polish-requirement-checklist.txt b/docs_build/dev/reports/PR_26174_ALFA_010-game-hub-source-idea-child-table-polish-requirement-checklist.txt new file mode 100644 index 000000000..185081aa4 --- /dev/null +++ b/docs_build/dev/reports/PR_26174_ALFA_010-game-hub-source-idea-child-table-polish-requirement-checklist.txt @@ -0,0 +1,9 @@ +Requirement Checklist: PASS + +PASS - Source Idea appears as a child table under the game parent row. +PASS - Source Idea includes source idea details. +PASS - Source Idea includes notes as read-only child/context rows where data supports it. +PASS - Source Idea is read-only. +PASS - No edit/delete controls for source idea context. +PASS - Creator-facing labels are clear and non-technical. +PASS - Existing API/service contract is preserved. diff --git a/docs_build/dev/reports/PR_26174_ALFA_010-game-hub-source-idea-child-table-polish-validation-lane.txt b/docs_build/dev/reports/PR_26174_ALFA_010-game-hub-source-idea-child-table-polish-validation-lane.txt new file mode 100644 index 000000000..b2926560a --- /dev/null +++ b/docs_build/dev/reports/PR_26174_ALFA_010-game-hub-source-idea-child-table-polish-validation-lane.txt @@ -0,0 +1,3 @@ +Validation Lane: PASS + +PASS - npx playwright test tests/playwright/tools/IdeaBoardTableNotes.spec.mjs -g "Idea Board uses accordion table ideas and notes" diff --git a/docs_build/dev/reports/PR_26174_ALFA_010-game-hub-source-idea-child-table-polish.md b/docs_build/dev/reports/PR_26174_ALFA_010-game-hub-source-idea-child-table-polish.md new file mode 100644 index 000000000..7a430f990 --- /dev/null +++ b/docs_build/dev/reports/PR_26174_ALFA_010-game-hub-source-idea-child-table-polish.md @@ -0,0 +1,16 @@ +# PR_26174_ALFA_010-game-hub-source-idea-child-table-polish + +## Purpose + +Make Source Idea a dedicated child table under the expanded game row. + +## Summary + +- Added a Source Idea child table to expanded Game Hub parent rows. +- Displays source idea, pitch, and read-only source note rows when data is present. +- Kept source idea context free of edit/delete controls. +- Preserved the existing Local API/service contract and source idea normalization. + +## Validation + +PASS - `npx playwright test tests/playwright/tools/IdeaBoardTableNotes.spec.mjs -g "Idea Board uses accordion table ideas and notes"` diff --git a/docs_build/dev/reports/codex_changed_files.txt b/docs_build/dev/reports/codex_changed_files.txt index c4c8e6cf9..6353fc8be 100644 --- a/docs_build/dev/reports/codex_changed_files.txt +++ b/docs_build/dev/reports/codex_changed_files.txt @@ -1,9 +1,9 @@ toolbox/game-hub/game-hub.js -tests/playwright/tools/GameHubMockRepository.spec.mjs +tests/playwright/tools/IdeaBoardTableNotes.spec.mjs docs_build/dev/reports/codex_review.diff docs_build/dev/reports/codex_changed_files.txt -docs_build/dev/reports/PR_26174_ALFA_009-game-hub-parent-child-table-layout.md -docs_build/dev/reports/PR_26174_ALFA_009-game-hub-parent-child-table-layout-branch-validation.txt -docs_build/dev/reports/PR_26174_ALFA_009-game-hub-parent-child-table-layout-requirement-checklist.txt -docs_build/dev/reports/PR_26174_ALFA_009-game-hub-parent-child-table-layout-validation-lane.txt -docs_build/dev/reports/PR_26174_ALFA_009-game-hub-parent-child-table-layout-manual-validation-notes.txt +docs_build/dev/reports/PR_26174_ALFA_010-game-hub-source-idea-child-table-polish.md +docs_build/dev/reports/PR_26174_ALFA_010-game-hub-source-idea-child-table-polish-branch-validation.txt +docs_build/dev/reports/PR_26174_ALFA_010-game-hub-source-idea-child-table-polish-requirement-checklist.txt +docs_build/dev/reports/PR_26174_ALFA_010-game-hub-source-idea-child-table-polish-validation-lane.txt +docs_build/dev/reports/PR_26174_ALFA_010-game-hub-source-idea-child-table-polish-manual-validation-notes.txt diff --git a/docs_build/dev/reports/codex_review.diff b/docs_build/dev/reports/codex_review.diff index 8b90ad570..d241bee0b 100644 --- a/docs_build/dev/reports/codex_review.diff +++ b/docs_build/dev/reports/codex_review.diff @@ -1,195 +1,116 @@ -diff --git a/tests/playwright/tools/GameHubMockRepository.spec.mjs b/tests/playwright/tools/GameHubMockRepository.spec.mjs -index e11631269..695b0a609 100644 ---- a/tests/playwright/tools/GameHubMockRepository.spec.mjs -+++ b/tests/playwright/tools/GameHubMockRepository.spec.mjs -@@ -264,10 +264,32 @@ test("Game Hub creates, opens, and deletes mock games", async ({ page }) => { - await expect(page.locator("[data-game-list]")).toContainText("Gravity Demo"); - await expect(page.locator("[data-game-list]")).toContainText("Collision Demo"); - await expect(page.locator("[data-game-list]")).toContainText("Camera Follow Demo"); -+ await expect(page.locator("[data-game-parent-table='open-games']")).toHaveAttribute("aria-label", "Open Games"); -+ await expect(page.locator("[data-game-parent-table='open-games'] caption")).toHaveText("Open Games"); -+ await expect(page.locator("[data-game-parent-table='open-games'] thead th")).toHaveText([ -+ "Game", -+ "Purpose", -+ "Status", -+ "Owner", -+ "Actions", +diff --git a/tests/playwright/tools/IdeaBoardTableNotes.spec.mjs b/tests/playwright/tools/IdeaBoardTableNotes.spec.mjs +index 722595ae9..f861f1f44 100644 +--- a/tests/playwright/tools/IdeaBoardTableNotes.spec.mjs ++++ b/tests/playwright/tools/IdeaBoardTableNotes.spec.mjs +@@ -392,6 +392,18 @@ test("Idea Board uses accordion table ideas and notes", async ({ page }) => { + await expect(page.locator("[data-source-idea-notes]")).toContainText("Use dusk tide changes as the first Game Hub planning note."); + await expect(page.locator("[data-source-idea-section] :is(input, textarea, select, button)")).toHaveCount(0); + await expect(page.getByRole("button", { name: "Delete Open Game" })).toHaveCount(0); ++ const activeGameRow = page.locator("[data-game-row][data-game-active='true']"); ++ await expect(activeGameRow).toContainText("Lantern Reef"); ++ await activeGameRow.locator("[data-game-toggle]").click(); ++ const sourceIdeaChildTable = page.locator("[data-game-expanded-row] [data-game-child-table='source-idea']"); ++ await expect(sourceIdeaChildTable.locator("caption")).toHaveText("Source Idea"); ++ await expect(sourceIdeaChildTable.locator("thead th")).toHaveText(["Context", "Details"]); ++ await expect(sourceIdeaChildTable.locator("tbody tr")).toHaveText([ ++ "IdeaLantern Reef", ++ "PitchGuide light through a reef that rearranges at dusk.", ++ "Note 1Use dusk tide changes as the first Game Hub planning note.", + ]); - const demoGameRow = page.locator("[data-game-row='demo-game']"); - await expect(demoGameRow.locator("> .status")).toHaveCount(0); -+ await expect(demoGameRow.locator("[data-game-toggle='demo-game']")).toHaveAttribute("aria-expanded", "false"); - await expect(demoGameRow.getByRole("button", { name: "Open Demo Game (Active)" })).toHaveClass(/primary/); - await expect(demoGameRow.getByRole("button", { name: "Open Demo Game (Active)" })).toHaveAttribute("aria-current", "true"); -+ await demoGameRow.locator("[data-game-toggle='demo-game']").click(); -+ await expect(demoGameRow.locator("[data-game-toggle='demo-game']")).toHaveAttribute("aria-expanded", "true"); -+ await expect(page.locator("[data-game-row='demo-game'] + [data-game-expanded-row='demo-game']")).toHaveCount(1); -+ await expect(page.locator("[data-game-expanded-row='demo-game'] [data-game-child-table='summary']")).toContainText("Game Summary"); -+ await expect(page.locator("[data-game-expanded-row='demo-game'] [data-game-child-table='summary'] tbody tr")).toHaveText([ -+ "ProjectDemo Game", -+ "PurposeGame", -+ "StatusUnder Construction", -+ "OwnerUser 1", -+ ]); -+ await demoGameRow.locator("[data-game-toggle='demo-game']").click(); -+ await expect(page.locator("[data-game-expanded-row='demo-game']")).toHaveCount(0); - - await page.getByLabel("Game Name").fill("Launch Test Game"); - await page.getByRole("button", { name: "Create Game" }).click(); ++ await expect(sourceIdeaChildTable.locator(":is(input, textarea, select, button)")).toHaveCount(0); + await page.reload({ waitUntil: "networkidle" }); + await expect(page.locator("[data-active-game-name]")).toHaveText("Lantern Reef"); + await expect(page.locator("[data-game-list]")).toContainText("Lantern Reef"); diff --git a/toolbox/game-hub/game-hub.js b/toolbox/game-hub/game-hub.js -index 2d6f321e4..121e82562 100644 +index 121e82562..67f56ca41 100644 --- a/toolbox/game-hub/game-hub.js +++ b/toolbox/game-hub/game-hub.js -@@ -39,6 +39,10 @@ const elements = { - tableCounts: document.querySelector("[data-game-table-counts]"), - }; - -+const state = { -+ expandedGameId: "", -+}; -+ - function setText(element, value) { - if (element && typeof element.forEach === "function" && !element.nodeType) { - element.forEach((item) => { -@@ -225,6 +229,90 @@ function createGameListStatus(message, state) { - return emptyState; +@@ -271,6 +271,54 @@ function renderGameSummaryChildTable(parent, game) { + parent.append(wrapper); } -+function createCell(value, tagName = "td") { -+ const cell = document.createElement(tagName); -+ cell.textContent = value; -+ return cell; ++function gameSourceIdeaDetails(game) { ++ const sourceIdea = isRecord(game?.sourceIdea) ? game.sourceIdea : null; ++ const name = String(sourceIdea?.idea || "").trim(); ++ const pitch = String(sourceIdea?.pitch || "").trim(); ++ const notes = Array.isArray(sourceIdea?.notes) ++ ? sourceIdea.notes.map((note) => String(note || "").trim()).filter(Boolean) ++ : []; ++ return { ++ name, ++ notes, ++ pitch, ++ }; +} + -+function createGameToggleButton(game, expanded) { -+ const button = document.createElement("button"); -+ button.className = expanded ? "btn btn--compact primary" : "btn btn--compact"; -+ button.type = "button"; -+ button.dataset.gameToggle = game.id; -+ button.setAttribute("aria-expanded", String(expanded)); -+ button.setAttribute("aria-controls", `game-child-${game.id}`); -+ button.textContent = game.name; -+ return button; -+} -+ -+function renderGameSummaryChildTable(parent, game) { ++function renderSourceIdeaChildTable(parent, game) { ++ const sourceIdea = gameSourceIdeaDetails(game); + const wrapper = document.createElement("div"); + wrapper.className = "table-wrapper"; + const table = document.createElement("table"); + table.className = "data-table data-table--fixed"; -+ table.dataset.gameChildTable = "summary"; -+ table.setAttribute("aria-label", `${game.name} game summary`); -+ table.innerHTML = "