From 5d35a48e513e4c76d16fecbdea6d626f33db8fb4 Mon Sep 17 00:00:00 2001 From: Delta Team Date: Wed, 24 Jun 2026 13:17:33 -0400 Subject: [PATCH] PR_26175_ALFA_017: consolidate Alfa Game Hub interaction runtime changes --- ...lfa-game-hub-interactions-consolidation.md | 67 ++ .../dev/reports/codex_changed_files.txt | 12 +- docs_build/dev/reports/codex_review.diff | 644 +----------------- .../tools/GameHubMockRepository.spec.mjs | 23 +- toolbox/game-hub/game-hub.js | 19 +- 5 files changed, 93 insertions(+), 672 deletions(-) create mode 100644 docs_build/dev/reports/PR_26175_ALFA_017-alfa-game-hub-interactions-consolidation.md diff --git a/docs_build/dev/reports/PR_26175_ALFA_017-alfa-game-hub-interactions-consolidation.md b/docs_build/dev/reports/PR_26175_ALFA_017-alfa-game-hub-interactions-consolidation.md new file mode 100644 index 000000000..7b8961565 --- /dev/null +++ b/docs_build/dev/reports/PR_26175_ALFA_017-alfa-game-hub-interactions-consolidation.md @@ -0,0 +1,67 @@ +# PR_26175_ALFA_017 - Alfa Game Hub Interactions Consolidation + +## Executive Summary + +PASS - Consolidated the current-main-safe Batch C Game Hub interaction behavior from GitHub PRs #107 through #113. + +This PR carries forward the remaining runtime-safe Game Hub child-row behavior: every expanded game parent row now exposes the same two child rows, Source Idea first and Readiness Output second, even when the game does not yet have source idea details. Existing current-main behavior for selected-game button state, guest save redirects, table-row add/edit flows, and removed standalone panels was preserved. + +## Runtime Files Changed + +| File | Change | +| --- | --- | +| `toolbox/game-hub/game-hub.js` | Always renders Source Idea and Readiness Output child rows for expanded game rows; aligns `aria-controls` with both child row IDs. | +| `tests/playwright/tools/GameHubMockRepository.spec.mjs` | Updates focused Game Hub expectations for ordinary games to require the stable two-child-row structure and Source Idea fallback content. | + +## Source PR Coverage + +| Source PR | Batch C Area | Current-Main Resolution | +| --- | --- | --- | +| #107 | Game row child rows | Carried forward the stable Source Idea + Readiness Output child-row contract. | +| #108 | Parent table centered in main panel / columns | Already present on current main without reintroducing obsolete Owner/Role/Next Tool columns. | +| #109 | Actions/setup cleanup | Already present; Open Game Journey and Game Setup controls remain absent. | +| #110 | Row add/edit selected state | Already present; add/edit rows remain table-native. | +| #111 | Guest save redirect | Already present; save actions continue redirecting guests to `account/sign-in.html`. | +| #112 | Selected-game button state | Already present; row/cell selected markers remain absent. | +| #113 | Selected button and Game Crew label cleanup | Selected button styling already present; Game Crew label is no longer relevant on current main. | + +## Requirement Checklist + +| Requirement | Status | Notes | +| --- | --- | --- | +| Start from `main` | PASS | Initial gate passed before branch creation. | +| Hard stop if branch/worktree/sync invalid | PASS | `main`, clean worktree, local/origin sync `0 0` confirmed before branch creation. | +| Read all Project Instructions | PASS | Active Project Instructions and history snapshots were reviewed before edits. | +| Implement current-main-safe Batch C runtime changes | PASS | Implemented the remaining stable Source Idea + Readiness Output child-row behavior. | +| Do not create report-only PR | PASS | Runtime and Playwright test files changed. | +| Do not change status bar work | PASS | Status bar diff check was empty. | +| Do not reintroduce removed standalone panels | PASS | No panel markup was restored. | +| Do not use browser-owned product data as source of truth | PASS | Change uses existing repository/API-driven Game Hub state only. | +| Do not install Chromium | PASS | Chromium was not installed. | +| Required reports created | PASS | `codex_review.diff`, `codex_changed_files.txt`, and this report are included. | +| Repo-structured ZIP under `tmp/` | PASS | ZIP created after report generation. | + +## Validation Lane + +| Command | Status | Result | +| --- | --- | --- | +| `node --check toolbox/game-hub/game-hub.js` | PASS | JavaScript syntax valid. | +| `node --check tests/playwright/tools/GameHubMockRepository.spec.mjs` | PASS | Test file syntax valid. | +| `node --check tests/playwright/tools/IdeaBoardTableNotes.spec.mjs` | PASS | Targeted adjacent test syntax valid. | +| `node --check tests/playwright/tools/GameJourneyTool.spec.mjs` | PASS | Targeted adjacent test syntax valid. | +| `git diff --check -- toolbox/game-hub/game-hub.js tests/playwright/tools/GameHubMockRepository.spec.mjs` | PASS | Exit code 0; Git emitted a non-blocking CRLF working-copy warning for the test file. | +| `git diff -- assets/theme-v2/css/status.css assets/theme-v2/js/toolbox-status-bar.js tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs` | PASS | Empty diff; status bar work untouched. | +| `npx playwright test tests/playwright/tools/IdeaBoardTableNotes.spec.mjs tests/playwright/tools/GameJourneyTool.spec.mjs tests/playwright/tools/GameHubMockRepository.spec.mjs` | BLOCKED | Timed out with no diagnostics before the narrower browser check. | +| `npx playwright test tests/playwright/tools/GameHubMockRepository.spec.mjs -g "Game Hub creates, opens, and deletes mock games|Game Hub validates game parent rows and child tables|Game Hub readiness child rows update from mock game state" --workers=1 --reporter=line --timeout=30000` | BLOCKED | Browser executable missing at `C:\Users\davidq\AppData\Local\ms-playwright\chromium-1217\chrome-win64\chrome.exe`. Chromium was not installed per instruction. | + +## Manual Validation Notes + +- Reviewed source PR metadata and file patches for #107, #108, #109, #110, #111, #112, and #113. +- Compared the final Batch C branch state against current main and preserved current-main additions, including selected-game change notifications and required Add Game validation. +- Confirmed the implementation does not modify `toolbox-status-bar.js`, `status.css`, or `ToolboxSelectedGameStatusBar.spec.mjs`. +- Confirmed no runtime JSON contract changes and no browser-owned product data source was introduced. +- Browser validation remains blocked until the local Playwright Chromium executable is available. + +## Branch Validation + +PASS - Work began from clean, synced `main`; implementation was made on `PR_26175_ALFA_017-alfa-game-hub-interactions-consolidation`. diff --git a/docs_build/dev/reports/codex_changed_files.txt b/docs_build/dev/reports/codex_changed_files.txt index 1c6777b35..39527f045 100644 --- a/docs_build/dev/reports/codex_changed_files.txt +++ b/docs_build/dev/reports/codex_changed_files.txt @@ -1,9 +1,5 @@ -docs_build/dev/BUILD_PR.md -assets/theme-v2/js/toolbox-status-bar.js -assets/theme-v2/css/status.css -tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs -docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_report.md -docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_validation-lane.md -docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_requirements-checklist.md -docs_build/dev/reports/codex_review.diff +docs_build/dev/reports/PR_26175_ALFA_017-alfa-game-hub-interactions-consolidation.md 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 diff --git a/docs_build/dev/reports/codex_review.diff b/docs_build/dev/reports/codex_review.diff index 548c6e30e..0155c2d0c 100644 --- a/docs_build/dev/reports/codex_review.diff +++ b/docs_build/dev/reports/codex_review.diff @@ -1,643 +1 @@ -diff --git a/assets/theme-v2/css/status.css b/assets/theme-v2/css/status.css -index 6acb4555b..4ca9cb266 100644 ---- a/assets/theme-v2/css/status.css -+++ b/assets/theme-v2/css/status.css -@@ -38,6 +38,7 @@ - .toolbox-status-bar { - --toolbox-status-bar-height: var(--space-52); - --toolbox-status-game-max: 220px; -+ --toolbox-status-progress-max: 34vw; - width: 100%; - min-block-size: var(--toolbox-status-bar-height); - border-block: var(--border-standard); -@@ -50,7 +51,7 @@ - margin: var(--space-0) auto; - padding: var(--space-10) var(--space-0); - display: grid; -- grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr); -+ grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr) minmax(var(--space-0), max-content); - gap: var(--space-16); - align-items: center - } -@@ -90,6 +91,19 @@ - overflow-wrap: anywhere - } - -+.toolbox-status-bar__progress { -+ min-width: var(--space-0); -+ max-width: var(--toolbox-status-progress-max); -+ margin: var(--space-0); -+ color: var(--muted); -+ font-size: var(--font-size-sm); -+ line-height: var(--line-height-tight); -+ overflow: hidden; -+ text-align: right; -+ text-overflow: ellipsis; -+ white-space: nowrap -+} -+ - .toolbox-status-bar[data-selected-game-state="active"] { - border-color: color-mix(in srgb, var(--green) 52%, var(--line)) - } -@@ -249,16 +263,21 @@ body.tool-focus-mode .tool-center-panel { - @media (max-width: 720px) { - .toolbox-status-bar__inner { - width: var(--container-width); -- grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr); -+ grid-template-columns: minmax(var(--space-0), max-content) minmax(var(--space-0), 1fr) minmax(var(--space-0), max-content); - gap: var(--space-10); - text-align: center - } - - .toolbox-status-bar__game-name { -- max-width: 34vw -+ max-width: 24vw - } - -- .toolbox-status-bar__message { -+ .toolbox-status-bar__message, -+ .toolbox-status-bar__progress { - font-size: var(--font-size-sm) - } -+ -+ .toolbox-status-bar__progress { -+ max-width: 30vw -+ } - } -diff --git a/assets/theme-v2/js/toolbox-status-bar.js b/assets/theme-v2/js/toolbox-status-bar.js -index 24c935311..6adc17290 100644 ---- a/assets/theme-v2/js/toolbox-status-bar.js -+++ b/assets/theme-v2/js/toolbox-status-bar.js -@@ -1,7 +1,39 @@ -+import { readGameJourneyCompletionMetrics } from "/src/api/game-journey-completion-api-client.js"; - import { createServerRepositoryClient } from "/src/api/server-api-client.js"; -+import { getToolBySlug } from "/src/shared/toolbox/tool-metadata-inventory.js"; - - const EXCLUDED_SELECTED_GAME_TOOLS = new Set(["idea-board"]); - const STATUS_BAR_SELECTOR = "[data-toolbox-status-bar]"; -+const TOOL_PROGRESS_BUCKET_BY_SLUG = Object.freeze({ -+ "achievements": "Progression", -+ "assets": "Graphics", -+ "audio": "Audio", -+ "audio-effects": "Audio", -+ "characters": "Objects", -+ "colors": "Graphics", -+ "community": "Share", -+ "controls": "Controls", -+ "events": "Rules", -+ "game-configuration": "Create", -+ "game-design": "Design", -+ "game-hub": "Create", -+ "game-testing": "Play Test", -+ "hitboxes": "Objects", -+ "idea-board": "Idea", -+ "input-mapping-v2": "Controls", -+ "marketplace": "Share", -+ "messages": "Interface", -+ "music": "Audio", -+ "objects": "Objects", -+ "publish": "Publish", -+ "ratings": "Share", -+ "sprites": "Graphics", -+ "tags": "Progression", -+ "text-to-speech": "Audio", -+ "videos": "Graphics", -+ "voices": "Audio", -+ "worlds": "Worlds", -+}); - - let repository = null; - let messageObserver = null; -@@ -101,7 +133,10 @@ function createStatusBar() { - message.setAttribute("role", "status"); - center.append(message); - -- inner.append(game, center); -+ const progress = createText("p", "toolbox-status-bar__progress", "toolboxStatusProgress"); -+ progress.setAttribute("aria-label", "Tool and journey progress"); -+ -+ inner.append(game, center, progress); - bar.append(inner); - return bar; - } -@@ -233,6 +268,107 @@ function classifyToolContext(messageText, state, required) { - return { kind: "action" }; - } - -+function normalizeTextKey(value) { -+ return String(value || "") -+ .toLowerCase() -+ .replace(/[^a-z0-9]+/g, ""); -+} -+ -+function formatToolSlug(slug) { -+ return String(slug || "Tool") -+ .split(/[-_\s]+/) -+ .filter(Boolean) -+ .map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`) -+ .join(" ") || "Tool"; -+} -+ -+function currentToolContext() { -+ const slug = toolSlugFromPath(mountOptions.pagePath); -+ const tool = getToolBySlug(slug); -+ return { -+ label: tool?.shortLabel || tool?.displayName || tool?.name || formatToolSlug(slug), -+ slug, -+ }; -+} -+ -+function normalizeProgressRecord(record) { -+ const total = Math.max(0, Number(record?.plannedCount) || 0); -+ const complete = Math.max(0, Math.min(Number(record?.completedCount) || 0, total)); -+ const percent = Number.isFinite(Number(record?.percentComplete)) -+ ? Math.max(0, Math.min(Number(record.percentComplete), 100)) -+ : total > 0 -+ ? Math.round((complete / total) * 100) -+ : 0; -+ return { -+ complete, -+ percent, -+ total, -+ }; -+} -+ -+function formatProgressRecord(label, record) { -+ const progress = normalizeProgressRecord(record); -+ return `${label} ${progress.complete}/${progress.total} (${progress.percent}%)`; -+} -+ -+function findMetricForCurrentTool(snapshot, toolContext) { -+ if (toolContext.slug === "game-journey") { -+ return snapshot; -+ } -+ -+ const records = Array.isArray(snapshot?.records) ? snapshot.records : []; -+ const explicitBucketName = TOOL_PROGRESS_BUCKET_BY_SLUG[toolContext.slug]; -+ const explicitBucket = normalizeTextKey(explicitBucketName); -+ const toolLabel = normalizeTextKey(toolContext.label); -+ const toolSlug = normalizeTextKey(toolContext.slug); -+ -+ return records.find((metric) => { -+ const bucketName = normalizeTextKey(metric?.bucketName); -+ return bucketName && ( -+ bucketName === explicitBucket || -+ bucketName === toolLabel || -+ bucketName === toolSlug -+ ); -+ }) || null; -+} -+ -+function resolveProgressContext() { -+ const toolContext = currentToolContext(); -+ const snapshot = readGameJourneyCompletionMetrics(); -+ const currentMetric = findMetricForCurrentTool(snapshot, toolContext); -+ const journeyText = formatProgressRecord("Journey", snapshot); -+ -+ if (!currentMetric) { -+ return { -+ state: "unmapped", -+ text: `${toolContext.label} progress unavailable | ${journeyText}`, -+ }; -+ } -+ -+ return { -+ state: "active", -+ text: `${formatProgressRecord(toolContext.label, currentMetric)} | ${journeyText}`, -+ }; -+} -+ -+function renderProgressContext(bar) { -+ const progress = bar.querySelector("[data-toolbox-status-progress]"); -+ if (!progress) { -+ return; -+ } -+ -+ try { -+ const context = resolveProgressContext(); -+ bar.dataset.toolboxProgressState = context.state; -+ progress.textContent = context.text; -+ progress.removeAttribute("title"); -+ } catch (error) { -+ bar.dataset.toolboxProgressState = "error"; -+ progress.textContent = "Progress unavailable"; -+ progress.removeAttribute("title"); -+ } -+} -+ - function renderSelectedGame(bar, selectedGame, state, messageText) { - const required = pageRequiresSelectedGame(); - const name = bar.querySelector("[data-toolbox-selected-game-name]"); -@@ -247,6 +383,7 @@ function renderSelectedGame(bar, selectedGame, state, messageText) { - bar.dataset.selectedGameState = state; - bar.dataset.selectedGameRequired = String(required); - bar.dataset.toolboxStatusContextKind = context.kind; -+ renderProgressContext(bar); - - if (selectedGame) { - name.textContent = selectedGame.name; -diff --git a/docs_build/dev/BUILD_PR.md b/docs_build/dev/BUILD_PR.md -index 0778a1f1e..f4bc1e8e4 100644 ---- a/docs_build/dev/BUILD_PR.md -+++ b/docs_build/dev/BUILD_PR.md -@@ -1,50 +1,54 @@ --# PR_26175_ALFA_009-status-bar-single-row-rebuild -+# PR_26175_ALFA_011-status-bar-journey-progress-context - - ## Purpose --Rebuild the shared toolbox status bar on current `main` so it is a single-row creator context bar. -+Add right-anchored progress context to the shared toolbox status bar using the existing Game Journey completion metrics/API pipeline. - - ## Source Of Truth --This `BUILD_PR.md` is the source of truth for `PR_26175_ALFA_009-status-bar-single-row-rebuild`. -+This `BUILD_PR.md` is the source of truth for `PR_26175_ALFA_011-status-bar-journey-progress-context`. - - ## Exact Scope --- Display only the selected Game Hub game name on the left side of the toolbox status bar. --- Display only the current status message in the center of the toolbox status bar. --- Remove visible status bar labels: `Selected Game Name`, `Selected Game Purpose`, `Save State`, `Tool Action`, `Warning`, and `Error`. --- Remove selected-game purpose from the visible status bar. --- Preserve normal status bar placement above the footer. --- Remove extra status bar/footer spacing so the shared footer top padding resolves to `0px`. --- Preserve fullscreen/tool display mode bottom anchoring. --- Ensure fullscreen center scrollable content stops above the fixed status bar. --- Ensure fullscreen tool content is not hidden behind the status bar. --- Preserve Idea Board selected-game filtering exclusion. --- Preserve Game Hub as selected-game owner through the existing repository contract. -+- Preserve the ALFA_009 single-row toolbox status bar behavior: -+ - left side displays only the selected Game Hub game name. -+ - center displays only the current status message. -+- Add right-anchored progress text in this format: -+ - `{CurrentTool} {complete}/{total} ({percent}%) | Journey {complete}/{total} ({percent}%)` -+- Use existing Game Journey completion metrics/API pipeline for Journey totals. -+- Derive current-tool progress from the existing completion metrics record that matches the current toolbox tool/section. -+- Do not add new storage. -+- Do not use browser-owned authoritative progress data. -+- Preserve fullscreen bottom anchoring and existing fullscreen content bottom reserve. -+- Preserve normal placement above the footer. - - Use shared Theme V2 CSS/classes only. --- Update targeted Playwright coverage for the single-row status bar, footer spacing, and fullscreen bottom reserve. -+- Update targeted Playwright coverage for the right-anchored progress text and existing left/center behavior. - - ## Exact Targets - - `docs_build/dev/BUILD_PR.md` - - `assets/theme-v2/js/toolbox-status-bar.js` - - `assets/theme-v2/css/status.css` --- `assets/theme-v2/css/layout.css` - - `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs` --- `docs_build/dev/reports/PR_26175_ALFA_009-status-bar-single-row-rebuild_report.md` --- `docs_build/dev/reports/PR_26175_ALFA_009-status-bar-single-row-rebuild_validation-lane.md` --- `docs_build/dev/reports/PR_26175_ALFA_009-status-bar-single-row-rebuild_requirements-checklist.md` -+- `docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_report.md` -+- `docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_validation-lane.md` -+- `docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_requirements-checklist.md` - - `docs_build/dev/reports/codex_review.diff` - - `docs_build/dev/reports/codex_changed_files.txt` - -+## Evidence Sources -+- `assets/js/shared/game-journey-api-client.js` -+- `src/api/game-journey-completion-api-client.js` -+- `src/dev-runtime/server/local-api-router.mjs` -+- `src/dev-runtime/persistence/game-journey-completion-metrics-store.mjs` -+ - ## Out Of Scope --- No merge of PR #120. --- No reuse of stale ALFA_003 branch. -+- No Game Journey API/service/repository contract changes. -+- No new persistence/storage. -+- No browser-owned product data as source of truth. -+- No silent fallback data. - - No environment/server details in the status bar. - - No selected game purpose in the visible status bar. - - No visible status category labels in the status bar. --- No status action links in the visible status bar. - - No large banners. - - No modal messages or modal-style status messages. - - No row highlights. --- No API/service/repository contract changes. --- No browser-owned product data as source of truth. - - No inline styles, style blocks, or page-local CSS. - - No engine core changes. - - No `start_of_day` folder changes. -@@ -59,12 +63,12 @@ npx playwright test tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs - Also verify changed source does not introduce inline styles or style blocks: - - ```powershell --rg -n "<[s]tyle|[s]tyle=" assets/theme-v2/js/toolbox-status-bar.js assets/theme-v2/css/status.css assets/theme-v2/css/layout.css tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs -+rg -n "<[s]tyle|[s]tyle=" assets/theme-v2/js/toolbox-status-bar.js assets/theme-v2/css/status.css tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs - ``` - - ## Artifact - Create repo-structured delta ZIP: - - ```text --tmp/PR_26175_ALFA_009-status-bar-single-row-rebuild_delta.zip -+tmp/PR_26175_ALFA_011-status-bar-journey-progress-context_delta.zip - ``` -diff --git a/docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_report.md b/docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_report.md -new file mode 100644 -index 000000000..f737efdd4 ---- /dev/null -+++ b/docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_report.md -@@ -0,0 +1,38 @@ -+# PR_26175_ALFA_011-status-bar-journey-progress-context Report -+ -+## Status -+PASS -+ -+Added right-anchored progress context to the shared toolbox status bar while preserving the ALFA_009 left and center behavior. -+ -+## Changes -+- `assets/theme-v2/js/toolbox-status-bar.js` -+ - Imports the existing Game Journey completion metrics API client at line 1. -+ - Adds current-tool-to-Journey-bucket matching at lines 7-35. -+ - Adds the right progress node at line 136. -+ - Formats progress as `{CurrentTool} {complete}/{total} ({percent}%) | Journey {complete}/{total} ({percent}%)` at lines 309-350. -+ - Renders visible unavailable progress state without raw environment/server details at lines 354-370. -+- `assets/theme-v2/css/status.css` -+ - Adds a three-column shared status bar grid at line 54. -+ - Adds right-aligned single-line progress styling at lines 94-106. -+ - Preserves existing fullscreen anchoring and bottom reserve rules at lines 119-150. -+- `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs` -+ - Routes `/api/game-journey/completion-metrics` through the existing server API response shape at lines 76-83. -+ - Verifies Game Design progress at lines 239 and 253. -+ - Verifies the requested Objects example at lines 275 and 279. -+ - Verifies right anchoring at lines 260 and 280. -+ - Verifies Game Hub, missing-game, Idea Board, and fullscreen behavior at lines 304, 328, 353, 371, and 398. -+ -+## Contract Notes -+- No storage was added. -+- No API/service/repository contract was changed. -+- Progress data is read from the existing Game Journey completion metrics API pipeline. -+- Browser state is not used as authoritative progress data. -+- Existing selected-game ownership and left/center status bar behavior are preserved. -+ -+## Validation -+- PASS: `npx playwright test tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs --workers=1` -+- PASS: `rg -n "<[s]tyle|[s]tyle=" assets/theme-v2/js/toolbox-status-bar.js assets/theme-v2/css/status.css tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs` -+ -+## Artifact -+- `tmp/PR_26175_ALFA_011-status-bar-journey-progress-context_delta.zip` -diff --git a/docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_requirements-checklist.md b/docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_requirements-checklist.md -new file mode 100644 -index 000000000..94c18da59 ---- /dev/null -+++ b/docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_requirements-checklist.md -@@ -0,0 +1,17 @@ -+# PR_26175_ALFA_011 Requirements Checklist -+ -+| Requirement | Status | Evidence | -+| --- | --- | --- | -+| Implement right-anchored progress in the shared toolbox status bar. | PASS | `assets/theme-v2/js/toolbox-status-bar.js:136`, `assets/theme-v2/css/status.css:94`. | -+| Use format `{CurrentTool} {complete}/{total} ({percent}%) | Journey {complete}/{total} ({percent}%)`. | PASS | `assets/theme-v2/js/toolbox-status-bar.js:309` and `:350`; tests at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:239`, `:275`. | -+| Use existing Game Journey completion metrics/API pipeline. | PASS | `assets/theme-v2/js/toolbox-status-bar.js:1`, `:337`; tests route `/api/game-journey/completion-metrics` at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:76`. | -+| Do not add new storage. | PASS | No persistence files changed. | -+| Do not use browser-owned authoritative progress data. | PASS | Progress is read from `readGameJourneyCompletionMetrics()` and not local/session storage. | -+| Keep existing left status bar behavior from ALFA_009. | PASS | Selected game assertions remain at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:237`, `:302`, `:391`. | -+| Keep existing center status bar behavior from ALFA_009. | PASS | Message assertions remain at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:238`, `:303`, `:370`, `:397`. | -+| Preserve fullscreen bottom anchoring. | PASS | Existing CSS rules remain at `assets/theme-v2/css/status.css:119`; test coverage at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:315`. | -+| Preserve normal placement above footer. | PASS | Existing status bar placement test remains at `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs:224`. | -+| No inline styles, style blocks, or page-local CSS. | PASS | Style scan returned no matches. | -+| Use Theme V2 shared CSS/classes. | PASS | Styling changes are limited to `assets/theme-v2/css/status.css`. | -+| Update targeted Playwright coverage. | PASS | `tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs` now validates right progress text and anchoring. | -+| Produce required reports and repo-structured ZIP. | PASS | PR reports added and ZIP target is `tmp/PR_26175_ALFA_011-status-bar-journey-progress-context_delta.zip`. | -diff --git a/docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_validation-lane.md b/docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_validation-lane.md -new file mode 100644 -index 000000000..daa775695 ---- /dev/null -+++ b/docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_validation-lane.md -@@ -0,0 +1,38 @@ -+# PR_26175_ALFA_011 Validation Lane -+ -+## Playwright -+Command: -+ -+```powershell -+npx playwright test tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs --workers=1 -+``` -+ -+Result: -+ -+```text -+Running 7 tests using 1 worker -+7 passed -+``` -+ -+Covered: -+- Existing selected Game Hub game appears on the left. -+- Existing center status message behavior is preserved. -+- Right progress text renders in the required format. -+- Objects example renders as `Objects 12/25 (48%) | Journey 12/125 (10%)`. -+- Game Hub save state keeps center status behavior and right progress. -+- Fullscreen/tool display mode keeps the fixed bottom status bar and content reserve. -+- Game Hub owner selection updates global status context. -+- Missing-game prompt remains creator-safe. -+- Idea Board remains excluded from selected-game filtering. -+ -+## Style And Scope -+Command: -+ -+```powershell -+rg -n "<[s]tyle|[s]tyle=" assets/theme-v2/js/toolbox-status-bar.js assets/theme-v2/css/status.css tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs -+``` -+ -+Result: PASS. The command returned no matches. -+ -+## Generated Files -+Playwright updated generated coverage reports during validation. They were restored to `HEAD` because they are outside this PR's exact target list. -diff --git a/docs_build/dev/reports/codex_changed_files.txt b/docs_build/dev/reports/codex_changed_files.txt -index 4aa64ab71..1c6777b35 100644 ---- a/docs_build/dev/reports/codex_changed_files.txt -+++ b/docs_build/dev/reports/codex_changed_files.txt -@@ -1,17 +1,9 @@ --docs_build/dev/ProjectInstructions/PROJECT_INSTRUCTIONS.md --docs_build/dev/ProjectInstructions/TEAM_START_COMMANDS.md --docs_build/dev/ProjectInstructions/addendums/multi_team.md --docs_build/dev/ProjectInstructions/addendums/pr_workflow.md --docs_build/dev/ProjectInstructions/addendums/team_release_readiness.md --docs_build/dev/ProjectInstructions/addendums/team_start_and_release.md --docs_build/dev/ProjectInstructions/backlog/BACKLOG_MASTER.md --docs_build/dev/ProjectInstructions/team_assignments/ACTIVE_TEAM_REGISTRY.md --docs_build/dev/ProjectInstructions/team_assignments/TEAM_ASSIGNMENTS.md --docs_build/dev/ProjectInstructions/team_assignments/team_ownership.md --docs_build/dev/reports/PR_26175_OWNER_046-pr-targeted-review-packets.md --docs_build/dev/reports/PR_REVIEW_003.md --docs_build/dev/reports/PR_REVIEW_050.md --docs_build/dev/reports/PR_REVIEW_051.md --docs_build/dev/reports/PR_REVIEW_118.md --docs_build/dev/reports/codex_changed_files.txt -+docs_build/dev/BUILD_PR.md -+assets/theme-v2/js/toolbox-status-bar.js -+assets/theme-v2/css/status.css -+tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs -+docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_report.md -+docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_validation-lane.md -+docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_requirements-checklist.md - docs_build/dev/reports/codex_review.diff -+docs_build/dev/reports/codex_changed_files.txt -diff --git a/tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs b/tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs -index 1a40d61c5..e4a940bad 100644 ---- a/tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs -+++ b/tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs -@@ -73,6 +73,15 @@ async function openRepoPage(page, pathName, options = {}) { - }); - }); - } -+ await page.route("**/api/game-journey/completion-metrics", async (route) => { -+ await route.fulfill({ -+ contentType: "application/json", -+ body: JSON.stringify({ -+ data: options.completionMetrics || completionMetricsFixture(), -+ ok: true, -+ }), -+ }); -+ }); - if (options.session) { - const userKey = options.session.userKey || MOCK_DB_KEYS.users.user1; - await page.route("**/api/session/current", async (route) => { -@@ -113,6 +122,44 @@ function creatorSession() { - }; - } - -+function completionMetric(bucketKey, bucketName, completedCount, plannedCount) { -+ return { -+ active: true, -+ bucketKey, -+ bucketName, -+ completedCount, -+ percentComplete: plannedCount > 0 ? Math.round((completedCount / plannedCount) * 100) : 0, -+ plannedCount, -+ }; -+} -+ -+function completionMetricsFixture() { -+ return { -+ api: "Local API", -+ completedCount: 12, -+ databaseEngine: "Postgres", -+ percentComplete: 10, -+ plannedCount: 125, -+ records: [ -+ completionMetric("001-idea", "Idea", 1, 4), -+ completionMetric("002-create", "Create", 3, 5), -+ completionMetric("003-design", "Design", 2, 5), -+ completionMetric("004-graphics", "Graphics", 5, 10), -+ completionMetric("005-audio", "Audio", 0, 4), -+ completionMetric("006-objects", "Objects", 12, 25), -+ completionMetric("007-worlds", "Worlds", 0, 5), -+ completionMetric("008-interface", "Interface", 1, 5), -+ completionMetric("009-controls", "Controls", 1, 4), -+ completionMetric("010-rules", "Rules", 4, 5), -+ completionMetric("011-progression", "Progression", 0, 4), -+ completionMetric("012-play-test", "Play Test", 0, 5), -+ completionMetric("013-publish", "Publish", 0, 5), -+ completionMetric("014-share", "Share", 0, 5), -+ ], -+ serviceContract: "Web UI -> Local API/Service Contract -> Postgres", -+ }; -+} -+ - const REMOVED_STATUS_BAR_LABELS = [ - "Selected Game Name", - "Selected Game Purpose", -@@ -148,6 +195,7 @@ async function statusBarSnapshot(page) { - const centerPanel = document.querySelector(".tool-center-panel"); - const gameName = bar.querySelector("[data-toolbox-selected-game-name]"); - const message = bar.querySelector("[data-toolbox-status-message]"); -+ const progress = bar.querySelector("[data-toolbox-status-progress]"); - const position = getComputedStyle(bar).position; - const barBox = bar.getBoundingClientRect(); - const footerBox = footer?.getBoundingClientRect(); -@@ -165,6 +213,9 @@ async function statusBarSnapshot(page) { - messageBox: boxSnapshot(message), - messageText: message?.textContent.replace(/\s+/g, " ").trim() || "", - position, -+ progressBox: boxSnapshot(progress), -+ progressState: bar.dataset.toolboxProgressState || "", -+ progressText: progress?.textContent.replace(/\s+/g, " ").trim() || "", - topBeforeFooter: footerBox ? barBox.bottom <= footerBox.top + 1 : false, - }; - }); -@@ -185,6 +236,7 @@ test("shared toolbox status bar shows selected Game Hub game above the footer", - await expect(statusBar.locator("[data-toolbox-status-action]")).toHaveCount(0); - await expect(statusBar.locator("[data-toolbox-selected-game]")).not.toContainText("Under Construction"); - await expect(statusBar.locator("[data-toolbox-status-message]")).toContainText("Game Design mock repository ready."); -+ await expect(statusBar.locator("[data-toolbox-status-progress]")).toHaveText("Game Design 2/5 (40%) | Journey 12/125 (10%)"); - await expect(page.locator("body")).toHaveAttribute("data-toolbox-selected-game-id", "demo-game"); - await expect(page.locator("body")).toHaveAttribute("data-toolbox-selected-game-filter", "active"); - -@@ -197,9 +249,35 @@ test("shared toolbox status bar shows selected Game Hub game above the footer", - expect(snapshot.dataset.selectedGameRequired).toBe("true"); - expect(snapshot.gameText).toBe("Demo Game"); - expect(snapshot.messageText).toContain("Game Design mock repository ready."); -+ expect(snapshot.progressState).toBe("active"); -+ expect(snapshot.progressText).toBe("Game Design 2/5 (40%) | Journey 12/125 (10%)"); - expect(Math.max(snapshot.gameBox.top, snapshot.messageBox.top)).toBeLessThanOrEqual( - Math.min(snapshot.gameBox.bottom, snapshot.messageBox.bottom), - ); -+ expect(Math.max(snapshot.gameBox.top, snapshot.messageBox.top, snapshot.progressBox.top)).toBeLessThanOrEqual( -+ Math.min(snapshot.gameBox.bottom, snapshot.messageBox.bottom, snapshot.progressBox.bottom), -+ ); -+ expect(snapshot.progressBox.right).toBeGreaterThan(snapshot.messageBox.right); -+ -+ expectNoPageFailures(failures); -+ } finally { -+ await failures.server.close(); -+ } -+}); -+ -+test("shared toolbox status bar shows right-anchored current tool and journey progress", async ({ page }) => { -+ const failures = await openRepoPage(page, "/toolbox/objects/index.html"); -+ -+ try { -+ const statusBar = page.locator("[data-toolbox-status-bar]"); -+ await expect(statusBar).toBeVisible(); -+ await expect(statusBar.locator("[data-toolbox-selected-game-name]")).toHaveText("Demo Game"); -+ await expect(statusBar.locator("[data-toolbox-status-progress]")).toHaveText("Objects 12/25 (48%) | Journey 12/125 (10%)"); -+ -+ const snapshot = await statusBarSnapshot(page); -+ expect(snapshot.progressState).toBe("active"); -+ expect(snapshot.progressText).toBe("Objects 12/25 (48%) | Journey 12/125 (10%)"); -+ expect(snapshot.progressBox.right).toBeGreaterThan(snapshot.messageBox.right); - - expectNoPageFailures(failures); - } finally { -@@ -223,6 +301,7 @@ test("shared toolbox status bar center reports save state after Game Hub saves", - await expect(statusBar.locator("[data-toolbox-status-context-type]")).toHaveCount(0); - await expect(statusBar.locator("[data-toolbox-status-message]")).toHaveText("Created and opened Status Bar Save."); - await expect(statusBar.locator("[data-toolbox-selected-game-name]")).toHaveText("Status Bar Save"); -+ await expect(statusBar.locator("[data-toolbox-status-progress]")).toHaveText("Game Hub 3/5 (60%) | Journey 12/125 (10%)"); - await expect(statusBar.locator("[data-toolbox-selected-game-purpose]")).toHaveCount(0); - await expect(statusBar).not.toContainText("Learning Game"); - await expect(statusBar).not.toContainText("Environment"); -@@ -246,6 +325,7 @@ test("shared toolbox status bar anchors to the bottom in tool display mode", asy - expect(Math.abs(snapshot.bottomGap)).toBeLessThanOrEqual(2); - expect(snapshot.gameText).toBe("Demo Game"); - expect(snapshot.messageText).toContain("Game Design mock repository ready."); -+ expect(snapshot.progressText).toBe("Game Design 2/5 (40%) | Journey 12/125 (10%)"); - expect(snapshot.centerPanelBox.bottom).toBeLessThanOrEqual(snapshot.barBox.top + 1); - - expectNoPageFailures(failures); -@@ -270,6 +350,7 @@ test("Game Hub owner selection updates the global toolbox status bar", async ({ - await expect(page.locator("body")).toHaveAttribute("data-toolbox-selected-game-filter", "active"); - await expect(statusBar.locator("[data-toolbox-status-context-type]")).toHaveCount(0); - await expect(statusBar.locator("[data-toolbox-status-message]")).toContainText("Sign in to create or update Game Hub projects."); -+ await expect(statusBar.locator("[data-toolbox-status-progress]")).toHaveText("Game Hub 3/5 (60%) | Journey 12/125 (10%)"); - - expectNoPageFailures(failures); - } finally { -@@ -287,6 +368,7 @@ test("non-Idea Board toolbox pages show a creator-safe prompt when no Game Hub g - await expect(statusBar.locator("[data-toolbox-selected-game-purpose]")).toHaveCount(0); - await expect(statusBar.locator("[data-toolbox-status-context-type]")).toHaveCount(0); - await expect(statusBar.locator("[data-toolbox-status-message]")).toHaveText("Select or create a game in Game Hub before using this toolbox page."); -+ await expect(statusBar.locator("[data-toolbox-status-progress]")).toHaveText("Game Design 2/5 (40%) | Journey 12/125 (10%)"); - await expect(statusBar.locator("[data-toolbox-status-action]")).toHaveCount(0); - await expect(page.locator("body")).toHaveAttribute("data-toolbox-selected-game-filter", "missing"); - await expect(page.locator("body")).not.toHaveAttribute("data-toolbox-selected-game-id", /.+/); -@@ -313,6 +395,7 @@ test("Idea Board is excluded from selected-game filtering and does not show the - await expect(statusBar.locator("[data-toolbox-status-context-type]")).toHaveCount(0); - await expect(statusBar.locator("[data-toolbox-status-message]")).toContainText("Ready to shape ideas and notes."); - await expect(statusBar.locator("[data-toolbox-status-message]")).not.toContainText("Select or create a game"); -+ await expect(statusBar.locator("[data-toolbox-status-progress]")).toHaveText("Idea Board 1/4 (25%) | Journey 12/125 (10%)"); - await expect(page.locator("body")).toHaveAttribute("data-toolbox-selected-game-filter", "optional"); - await expect(page.locator("body")).not.toHaveClass(/toolbox-selected-game-missing/); +diff --git a/docs_build/dev/reports/PR_26175_ALFA_017-alfa-game-hub-interactions-consolidation.md b/docs_build/dev/reports/PR_26175_ALFA_017-alfa-game-hub-interactions-consolidation.mdnew file mode 100644index 000000000..7b8961565--- /dev/null+++ b/docs_build/dev/reports/PR_26175_ALFA_017-alfa-game-hub-interactions-consolidation.md@@ -0,0 +1,67 @@+# PR_26175_ALFA_017 - Alfa Game Hub Interactions Consolidation++## Executive Summary++PASS - Consolidated the current-main-safe Batch C Game Hub interaction behavior from GitHub PRs #107 through #113.++This PR carries forward the remaining runtime-safe Game Hub child-row behavior: every expanded game parent row now exposes the same two child rows, Source Idea first and Readiness Output second, even when the game does not yet have source idea details. Existing current-main behavior for selected-game button state, guest save redirects, table-row add/edit flows, and removed standalone panels was preserved.++## Runtime Files Changed++| File | Change |+| --- | --- |+| `toolbox/game-hub/game-hub.js` | Always renders Source Idea and Readiness Output child rows for expanded game rows; aligns `aria-controls` with both child row IDs. |+| `tests/playwright/tools/GameHubMockRepository.spec.mjs` | Updates focused Game Hub expectations for ordinary games to require the stable two-child-row structure and Source Idea fallback content. |++## Source PR Coverage++| Source PR | Batch C Area | Current-Main Resolution |+| --- | --- | --- |+| #107 | Game row child rows | Carried forward the stable Source Idea + Readiness Output child-row contract. |+| #108 | Parent table centered in main panel / columns | Already present on current main without reintroducing obsolete Owner/Role/Next Tool columns. |+| #109 | Actions/setup cleanup | Already present; Open Game Journey and Game Setup controls remain absent. |+| #110 | Row add/edit selected state | Already present; add/edit rows remain table-native. |+| #111 | Guest save redirect | Already present; save actions continue redirecting guests to `account/sign-in.html`. |+| #112 | Selected-game button state | Already present; row/cell selected markers remain absent. |+| #113 | Selected button and Game Crew label cleanup | Selected button styling already present; Game Crew label is no longer relevant on current main. |++## Requirement Checklist++| Requirement | Status | Notes |+| --- | --- | --- |+| Start from `main` | PASS | Initial gate passed before branch creation. |+| Hard stop if branch/worktree/sync invalid | PASS | `main`, clean worktree, local/origin sync `0 0` confirmed before branch creation. |+| Read all Project Instructions | PASS | Active Project Instructions and history snapshots were reviewed before edits. |+| Implement current-main-safe Batch C runtime changes | PASS | Implemented the remaining stable Source Idea + Readiness Output child-row behavior. |+| Do not create report-only PR | PASS | Runtime and Playwright test files changed. |+| Do not change status bar work | PASS | Status bar diff check was empty. |+| Do not reintroduce removed standalone panels | PASS | No panel markup was restored. |+| Do not use browser-owned product data as source of truth | PASS | Change uses existing repository/API-driven Game Hub state only. |+| Do not install Chromium | PASS | Chromium was not installed. |+| Required reports created | PASS | `codex_review.diff`, `codex_changed_files.txt`, and this report are included. |+| Repo-structured ZIP under `tmp/` | PASS | ZIP created after report generation. |++## Validation Lane++| Command | Status | Result |+| --- | --- | --- |+| `node --check toolbox/game-hub/game-hub.js` | PASS | JavaScript syntax valid. |+| `node --check tests/playwright/tools/GameHubMockRepository.spec.mjs` | PASS | Test file syntax valid. |+| `node --check tests/playwright/tools/IdeaBoardTableNotes.spec.mjs` | PASS | Targeted adjacent test syntax valid. |+| `node --check tests/playwright/tools/GameJourneyTool.spec.mjs` | PASS | Targeted adjacent test syntax valid. |+| `git diff --check -- toolbox/game-hub/game-hub.js tests/playwright/tools/GameHubMockRepository.spec.mjs` | PASS | Exit code 0; Git emitted a non-blocking CRLF working-copy warning for the test file. |+| `git diff -- assets/theme-v2/css/status.css assets/theme-v2/js/toolbox-status-bar.js tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs` | PASS | Empty diff; status bar work untouched. |+| `npx playwright test tests/playwright/tools/IdeaBoardTableNotes.spec.mjs tests/playwright/tools/GameJourneyTool.spec.mjs tests/playwright/tools/GameHubMockRepository.spec.mjs` | BLOCKED | Timed out with no diagnostics before the narrower browser check. |+| `npx playwright test tests/playwright/tools/GameHubMockRepository.spec.mjs -g "Game Hub creates, opens, and deletes mock games|Game Hub validates game parent rows and child tables|Game Hub readiness child rows update from mock game state" --workers=1 --reporter=line --timeout=30000` | BLOCKED | Browser executable missing at `C:\Users\davidq\AppData\Local\ms-playwright\chromium-1217\chrome-win64\chrome.exe`. Chromium was not installed per instruction. |++## Manual Validation Notes++- Reviewed source PR metadata and file patches for #107, #108, #109, #110, #111, #112, and #113.+- Compared the final Batch C branch state against current main and preserved current-main additions, including selected-game change notifications and required Add Game validation.+- Confirmed the implementation does not modify `toolbox-status-bar.js`, `status.css`, or `ToolboxSelectedGameStatusBar.spec.mjs`.+- Confirmed no runtime JSON contract changes and no browser-owned product data source was introduced.+- Browser validation remains blocked until the local Playwright Chromium executable is available.++## Branch Validation++PASS - Work began from clean, synced `main`; implementation was made on `PR_26175_ALFA_017-alfa-game-hub-interactions-consolidation`.diff --git a/docs_build/dev/reports/codex_changed_files.txt b/docs_build/dev/reports/codex_changed_files.txtindex 1c6777b35..39527f045 100644--- a/docs_build/dev/reports/codex_changed_files.txt+++ b/docs_build/dev/reports/codex_changed_files.txt@@ -1,9 +1,5 @@-docs_build/dev/BUILD_PR.md-assets/theme-v2/js/toolbox-status-bar.js-assets/theme-v2/css/status.css-tests/playwright/tools/ToolboxSelectedGameStatusBar.spec.mjs-docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_report.md-docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_validation-lane.md-docs_build/dev/reports/PR_26175_ALFA_011-status-bar-journey-progress-context_requirements-checklist.md-docs_build/dev/reports/codex_review.diff+docs_build/dev/reports/PR_26175_ALFA_017-alfa-game-hub-interactions-consolidation.md 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.jsdiff --git a/tests/playwright/tools/GameHubMockRepository.spec.mjs b/tests/playwright/tools/GameHubMockRepository.spec.mjsindex 29e11c37a..356235a03 100644--- a/tests/playwright/tools/GameHubMockRepository.spec.mjs+++ b/tests/playwright/tools/GameHubMockRepository.spec.mjs@@ -354,12 +354,19 @@ test("Game Hub creates, opens, and deletes mock games", async ({ page }) => { await demoGameRow.locator("[data-game-toggle='demo-game']").click(); await expect(demoGameRow.locator("[data-game-toggle='demo-game']")).toHaveAttribute("aria-expanded", "true"); const demoChildRows = page.locator("[data-game-expanded-row='demo-game']");- await expect(demoChildRows).toHaveCount(1);- await expect(demoChildRows.nth(0)).toHaveAttribute("data-game-child-row", "readiness-output");+ await expect(demoChildRows).toHaveCount(2);+ await expect(demoChildRows.nth(0)).toHaveAttribute("data-game-child-row", "source-idea");+ await expect(demoChildRows.nth(1)).toHaveAttribute("data-game-child-row", "readiness-output"); await expect(page.locator("[data-game-expanded-row='demo-game'] [data-game-child-table='summary']")).toHaveCount(0);- await expect(page.locator("[data-game-expanded-row='demo-game'] [data-game-child-table]")).toHaveCount(1);- await expect(page.locator("[data-game-expanded-row='demo-game'] [data-game-child-table='source-idea']")).toHaveCount(0);- const readinessOutputTable = demoChildRows.nth(0).locator("[data-game-child-table='readiness-output']");+ await expect(page.locator("[data-game-expanded-row='demo-game'] [data-game-child-table]")).toHaveCount(2);+ const demoSourceIdeaTable = demoChildRows.nth(0).locator("[data-game-child-table='source-idea']");+ await expect(demoSourceIdeaTable.locator("caption")).toHaveText("Source Idea");+ await expect(demoSourceIdeaTable.locator("tbody tr")).toHaveText([+ "IdeaNo source idea yet",+ "PitchCreate a project from Idea Board to see source details.",+ "Note 1No source notes.",+ ]);+ const readinessOutputTable = demoChildRows.nth(1).locator("[data-game-child-table='readiness-output']"); await expect(readinessOutputTable.locator("caption")).toHaveText("Readiness Output"); await expect(readinessOutputTable.locator("thead th")).toHaveText(["Output", "Status"]); await expect(readinessOutputTable.locator("tbody tr")).toHaveText([@@ -441,8 +448,10 @@ test("Game Hub creates, opens, and deletes mock games", async ({ page }) => { 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(1);- await expect(page.locator("[data-game-expanded-row='launch-test-game-1']")).toHaveAttribute("data-game-child-row", "readiness-output");+ const launchChildRows = page.locator("[data-game-expanded-row='launch-test-game-1']");+ await expect(launchChildRows).toHaveCount(2);+ await expect(launchChildRows.nth(0)).toHaveAttribute("data-game-child-row", "source-idea");+ await expect(launchChildRows.nth(1)).toHaveAttribute("data-game-child-row", "readiness-output"); 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']")).toHaveClass("btn btn--compact primary"); await expect(page.locator("[data-game-row='launch-test-game-1']").getByRole("button", { name: "Edit Launch Test Game" })).not.toHaveClass(/primary/);diff --git a/toolbox/game-hub/game-hub.js b/toolbox/game-hub/game-hub.jsindex 50bbe46f1..9e144bedf 100644--- a/toolbox/game-hub/game-hub.js+++ b/toolbox/game-hub/game-hub.js@@ -287,12 +287,7 @@ function createGameToggleButton(game, expanded, active) { button.setAttribute("aria-current", "true"); } button.setAttribute("aria-expanded", String(expanded));- const controlledRows = [];- if (hasSourceIdeaDetails(game)) {- controlledRows.push(`game-child-source-idea-${game.id}`);- }- controlledRows.push(`game-child-readiness-output-${game.id}`);- button.setAttribute("aria-controls", controlledRows.join(" "));+ button.setAttribute("aria-controls", `game-child-source-idea-${game.id} game-child-readiness-output-${game.id}`); button.textContent = game.name; return button; }@@ -397,22 +392,18 @@ function renderReadinessOutputChildTable(parent, game, progress, active) { } function renderExpandedGameRow(tbody, game, progress, active) {- const childRows = [];- if (hasSourceIdeaDetails(game)) {- childRows.push({+ [+ { id: `game-child-source-idea-${game.id}`, render: (parent) => renderSourceIdeaChildTable(parent, game), type: "source-idea",- });- }- childRows.push(+ }, { id: `game-child-readiness-output-${game.id}`, render: (parent) => renderReadinessOutputChildTable(parent, game, progress, active), type: "readiness-output", },- );- childRows.forEach(({ id, render, type }) => {+ ].forEach(({ id, render, type }) => { const row = document.createElement("tr"); row.dataset.gameExpandedRow = game.id; row.dataset.gameChildRow = type; \ No newline at end of file diff --git a/tests/playwright/tools/GameHubMockRepository.spec.mjs b/tests/playwright/tools/GameHubMockRepository.spec.mjs index 29e11c37a..356235a03 100644 --- a/tests/playwright/tools/GameHubMockRepository.spec.mjs +++ b/tests/playwright/tools/GameHubMockRepository.spec.mjs @@ -354,12 +354,19 @@ test("Game Hub creates, opens, and deletes mock games", async ({ page }) => { await demoGameRow.locator("[data-game-toggle='demo-game']").click(); await expect(demoGameRow.locator("[data-game-toggle='demo-game']")).toHaveAttribute("aria-expanded", "true"); const demoChildRows = page.locator("[data-game-expanded-row='demo-game']"); - await expect(demoChildRows).toHaveCount(1); - await expect(demoChildRows.nth(0)).toHaveAttribute("data-game-child-row", "readiness-output"); + await expect(demoChildRows).toHaveCount(2); + await expect(demoChildRows.nth(0)).toHaveAttribute("data-game-child-row", "source-idea"); + await expect(demoChildRows.nth(1)).toHaveAttribute("data-game-child-row", "readiness-output"); await expect(page.locator("[data-game-expanded-row='demo-game'] [data-game-child-table='summary']")).toHaveCount(0); - await expect(page.locator("[data-game-expanded-row='demo-game'] [data-game-child-table]")).toHaveCount(1); - await expect(page.locator("[data-game-expanded-row='demo-game'] [data-game-child-table='source-idea']")).toHaveCount(0); - const readinessOutputTable = demoChildRows.nth(0).locator("[data-game-child-table='readiness-output']"); + await expect(page.locator("[data-game-expanded-row='demo-game'] [data-game-child-table]")).toHaveCount(2); + const demoSourceIdeaTable = demoChildRows.nth(0).locator("[data-game-child-table='source-idea']"); + await expect(demoSourceIdeaTable.locator("caption")).toHaveText("Source Idea"); + await expect(demoSourceIdeaTable.locator("tbody tr")).toHaveText([ + "IdeaNo source idea yet", + "PitchCreate a project from Idea Board to see source details.", + "Note 1No source notes.", + ]); + const readinessOutputTable = demoChildRows.nth(1).locator("[data-game-child-table='readiness-output']"); await expect(readinessOutputTable.locator("caption")).toHaveText("Readiness Output"); await expect(readinessOutputTable.locator("thead th")).toHaveText(["Output", "Status"]); await expect(readinessOutputTable.locator("tbody tr")).toHaveText([ @@ -441,8 +448,10 @@ test("Game Hub creates, opens, and deletes mock games", async ({ page }) => { 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(1); - await expect(page.locator("[data-game-expanded-row='launch-test-game-1']")).toHaveAttribute("data-game-child-row", "readiness-output"); + const launchChildRows = page.locator("[data-game-expanded-row='launch-test-game-1']"); + await expect(launchChildRows).toHaveCount(2); + await expect(launchChildRows.nth(0)).toHaveAttribute("data-game-child-row", "source-idea"); + await expect(launchChildRows.nth(1)).toHaveAttribute("data-game-child-row", "readiness-output"); 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']")).toHaveClass("btn btn--compact primary"); await expect(page.locator("[data-game-row='launch-test-game-1']").getByRole("button", { name: "Edit Launch Test Game" })).not.toHaveClass(/primary/); diff --git a/toolbox/game-hub/game-hub.js b/toolbox/game-hub/game-hub.js index 50bbe46f1..9e144bedf 100644 --- a/toolbox/game-hub/game-hub.js +++ b/toolbox/game-hub/game-hub.js @@ -287,12 +287,7 @@ function createGameToggleButton(game, expanded, active) { button.setAttribute("aria-current", "true"); } button.setAttribute("aria-expanded", String(expanded)); - const controlledRows = []; - if (hasSourceIdeaDetails(game)) { - controlledRows.push(`game-child-source-idea-${game.id}`); - } - controlledRows.push(`game-child-readiness-output-${game.id}`); - button.setAttribute("aria-controls", controlledRows.join(" ")); + button.setAttribute("aria-controls", `game-child-source-idea-${game.id} game-child-readiness-output-${game.id}`); button.textContent = game.name; return button; } @@ -397,22 +392,18 @@ function renderReadinessOutputChildTable(parent, game, progress, active) { } function renderExpandedGameRow(tbody, game, progress, active) { - const childRows = []; - if (hasSourceIdeaDetails(game)) { - childRows.push({ + [ + { id: `game-child-source-idea-${game.id}`, render: (parent) => renderSourceIdeaChildTable(parent, game), type: "source-idea", - }); - } - childRows.push( + }, { id: `game-child-readiness-output-${game.id}`, render: (parent) => renderReadinessOutputChildTable(parent, game, progress, active), type: "readiness-output", }, - ); - childRows.forEach(({ id, render, type }) => { + ].forEach(({ id, render, type }) => { const row = document.createElement("tr"); row.dataset.gameExpandedRow = game.id; row.dataset.gameChildRow = type;