4.6 KiB
4.6 KiB
Implementation Plan: Challenges Page and Challenge Solve Modal 1.05
1. Architectural Reconnaissance
- Codebase style & conventions: TypeScript monorepo with a NestJS backend and Angular 17 standalone frontend. The challenges feature uses OnPush presentational components with inline templates/styles, a root-provided signal store, RxJS HTTP services, and authenticated SSE updates. The backend already returns authoritative per-user
solvedByMevalues, and the store already preserves that isolation across load, submit, SSE, logout, and user-switch boundaries. The remaining defect is accessibility semantics: the visible✓is nested inside a click-only<div>card and does not reliably appear as an independently exposed solved state in the card's accessibility tree. - Data Layer: SQLite through TypeORM. No schema or migration change is required;
solverows and the board API already compute player-specific solve state correctly. - Test Framework & Structure: Root Jest 29 configuration with dedicated suites under
tests/frontendandtests/backend;npm testruns all projects andnpm run test:frontendruns frontend tests. Add one focused, CLI-only source-contract regression test undertests/frontendrather than a browser or visual test. The test should verify both solved and unsolved accessibility bindings are derived directly fromcard.solvedByMeand that the solved glyph remains conditionally rendered. - Required Tools & Dependencies: No new system tools, global CLIs, npm packages, persistent
/dataassets, orsetup.shchanges are needed. Existing Angular, TypeScript, Jest, ts-jest, and jsdom dependencies are sufficient.
2. Impacted Files
- To Modify:
frontend/src/app/features/challenges/challenge-card.component.ts— make each card a semantic keyboard-operable control and expose its player-specific solved status on the card accessibility node while retaining the visible conditional checkmark and solved border.
- To Create:
tests/frontend/challenge-card-accessibility.spec.ts— minimal regression coverage for the card's accessible solved/unsolved state and conditional checkmark wiring.
3. Proposed Changes
- Database / Schema Migration: No change. Preserve the existing
solveuniqueness rules and backendsolvedByMecalculation; the reported scoring, concurrency, and cross-player API behavior already pass. - Backend Logic & APIs: No change. Continue consuming
BoardCard.solvedByMefromGET /api/v1/challenges/board, challenge detail, submit responses, and player-matched SSE updates as the sole source of player-specific state. - Frontend UI Integration:
- Update the challenge card root in
ChallengeCardComponentfrom a generic click target to a native<button type="button">, preserving the existingdata-testid,(click)output, solved CSS class, layout, and visual styling. Normalize button typography/alignment/background as needed so the UI does not regress while gaining native keyboard activation and button semantics. - Bind an accessible name or state description on the root card that includes the challenge name and an explicit
SolvedversusNot solvedphrase derived fromcard.solvedByMe. This puts the state on the card node inspected by accessibility-tree tooling instead of relying on a nested decorative glyph. - Keep the checkmark conditionally rendered only when
card.solvedByMeis true, add a stabledata-testidtied to the challenge id, and mark the glypharia-hidden="true"because the root card now announces the solved state and duplicate speech should be avoided. - Do not infer solved state from solve count, points, global solver data, or another player's SSE event. The template must bind only to
card.solvedByMe, preserving PlayerA/PlayerB isolation already enforced byChallengesStore.
- Update the challenge card root in
4. Test Strategy
- Target Unit Test File:
tests/frontend/challenge-card-accessibility.spec.ts. - Mocking Strategy: No HTTP, backend, database, SSE, filesystem, or UI-browser mocks. Read the component source as an isolated contract test, following the repository's existing lightweight pattern in
tests/frontend/shell-led.spec.ts. Assert that the card is a native button, its accessible label/state distinguishes solved from unsolved usingcard.solvedByMe, and the visible checkmark remains conditionally tied to the same field while being hidden from assistive technology to prevent duplicate announcements. Run from the repository root withnpm run test:frontend(and the fullnpm testverification command during implementation); also run the existing frontend production build because no dedicated lint or typecheck scripts are defined.