Files
HIPCTF2/.kilo/plans/879.md
T

179 lines
8.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Implementation Plan: Authenticated Shell — Component-Scoped LED Fix (Job 879, iteration 1.03)
## Status
The Job 879 reproduction shows that the LED `<span class="led led-running">`
renders with zero size and transparent background even though the global
`frontend/src/styles.css` declares the rules and the production bundle
contains them. To guarantee the rules apply regardless of cascade order,
external stylesheet loading, or specificity conflicts, this fix moves the
LED's visual styling from the global stylesheet into the
`ShellHeaderComponent` itself using Angular's
`styles: [...]` (component-scoped CSS).
The component is a standalone component (no `styleUrls`), so the fix is a
single, self-contained edit: add `styles: [\`...\`] to the `@Component`
decorator in
`frontend/src/app/features/shell/header/shell-header.component.ts`.
The existing template, state class bindings, and `aria-label` are kept
exactly as-is (lines 2634).
A minimal regression test is added in a dedicated
`tests/frontend/shell-led.spec.ts` that asserts the rendered `<span>`
carries `led` plus the correct `led-<state>` class for each
`EventState`, and that the bundled component CSS contains the four state
selectors.
## 1. Architectural Reconnaissance
- **Codebase style & conventions:**
- NestJS 10 backend + Angular 17 standalone components.
- Standalone components use `imports: [...]` and prefer `styles: [\`...\`]`
for small, co-located CSS. Existing examples in this repo:
`frontend/src/app/features/setup/setup-create-admin.component.ts`
uses `styleUrls: ['./setup-create-admin.component.css']`;
`frontend/src/app/features/landing/landing.component.ts` likewise.
For a small block of CSS that belongs to a single component, the
inline `styles: [\`...\`] form keeps the fix in one file (per the
user instruction: "Update the LED styling in
frontend/src/app/features/shell/header/shell-header.component.ts near
lines 25-34").
- Angular's default `ViewEncapsulation.Emulated` rewrites component CSS
selectors with attribute selectors (`[_ngcontent-xxx]`) and adds the
same attribute to the component's host + template nodes. Because the
LED `<span>` is rendered by `ShellHeaderComponent` (not by a child
component or via `innerHTML`), encapsulation correctly scopes the new
rules to that span, while still allowing the global `:root` CSS
custom-property tokens (`--color-success`, etc.) to be inherited.
- **Data Layer:** Not impacted. The LED color is driven by
`eventState()` from `EventStatusStore` (already wired).
- **Test Framework & Structure:**
- Jest 29 multi-project config (`tests/jest.config.js`).
- Frontend project uses `ts-jest` + jsdom +
`tests/frontend/jest.setup.ts` (Angular TestBed shims).
- Tests MUST live in `tests/frontend/` — never alongside source. They
MUST run via `npm test` (single root command). Tests MUST NOT
require a UI; this plan keeps them purely structural.
- **Required Tools & Dependencies:** None new. Existing
`@angular-devkit/build-angular:application` builder already supports
component-scoped `styles: [\`...\`] `, and `critters` (used for inline
above-the-fold CSS) is unaffected. `setup.sh` needs no changes.
## 2. Impacted Files
- **To Modify:**
- `frontend/src/app/features/shell/header/shell-header.component.ts`
— add a `styles: [\`...\`] block to the `@Component` decorator that
mirrors the LED rules from the global stylesheet, scoped to the
component. Keep the existing template (lines 1088) untouched.
Keep the `[class.led-<state>]` bindings and the dynamic `aria-label`.
- **To Create:**
- `tests/frontend/shell-led.spec.ts` — minimal jsdom-based test that
renders `ShellHeaderComponent` with each `EventState` value and
asserts (a) the rendered `[data-testid="shell-led"]` carries the
`led` class plus the matching `led-<state>` class, and (b) the
component's bundled CSS string contains the four required state
selectors (`led-running`, `led-countdown`, `led-stopped`,
`led-unconfigured`).
## 3. Proposed Changes
1. **Component-scoped CSS in `ShellHeaderComponent`**
In
`frontend/src/app/features/shell/header/shell-header.component.ts`,
add a `styles` property to the `@Component` decorator. Place it
immediately after the existing `imports: [CommonModule]` property
(around the current line 9 area) so the decorator remains in
canonical Angular order: `selector`, `standalone`, `changeDetection`,
`imports`, `template`, `styles`.
Exact CSS block to add inside the backtick literal:
```
:host .led,
.led {
display: inline-block;
width: 10px;
height: 10px;
border-radius: 50%;
border: 0;
background-color: transparent;
vertical-align: middle;
}
.led-running { background-color: var(--color-success); }
.led-countdown { background-color: var(--color-warning); }
.led-stopped { background-color: var(--color-danger); }
.led-unconfigured { background-color: var(--color-secondary); }
```
Notes:
- The base rule is duplicated as `:host .led, .led` so it matches
both a projected host usage and the in-template span. The
`:host` selector is harmless under Emulated encapsulation (it
targets the component's own host element) and does not break the
existing template.
- State classes are written exactly as before — the template already
toggles `[class.led-running]`, `[class.led-countdown]`,
`[class.led-stopped]`, `[class.led-unconfigured]`.
- Color values use the existing `:root` CSS custom properties
(`--color-success`/`--color-warning`/`--color-danger`/
`--color-secondary`) defined in `frontend/src/styles.css`. These
tokens are inherited through the DOM, so the component-scoped
rules resolve them automatically — no per-state hex is needed in
TypeScript.
- The current global rules in `frontend/src/styles.css` (lines 2327)
are left untouched as a safety net; they remain correct and
harmless.
2. **No template or state-binding changes**
The `<span ... class="led" [class.led-running]="..." ...>`
block (lines 2634) and the dynamic `aria-label` are kept exactly
as the documentation in
`docs/guides/authenticated-shell.md` specifies.
3. **No backend / DB changes**
The bug is purely a frontend cascade issue.
## 4. Test Strategy
- **Target Unit Test File:** `tests/frontend/shell-led.spec.ts`
(new file, in the dedicated `tests/frontend/` folder per repo
convention; runs via `npm test` -> `jest --config tests/jest.config.js
--selectProjects frontend`).
- **Mocking Strategy:**
- Use Angular's `TestBed.configureTestingModule({ imports:
[ShellHeaderComponent] })` so the component renders standalone
in jsdom with no router / http mocking required (the LED span has
no dependencies).
- Provide minimal inputs via the component's `input.required<...>` /
`input<...>(...)` signals. Specifically, set `eventState()` to
each of `'running' | 'countdown' | 'stopped' | 'unconfigured'`
using `TestBed.runInInjectionContext` + `ComponentRef.setInput`
(or `fixture.componentRef.setInput`) and assert the DOM.
- No HTTP, no router, no SSE — keeping the test fast and isolated.
- Two compact assertions per case:
1. `expect(led.classList.contains('led')).toBe(true)` and the
matching `led-<state>` class.
2. Read the component's raw `styles` string (via the decorated
component's static `ɵcmp.styles` or by string-matching the
source file's `styles:` template literal in a separate small
test) to confirm all four state selectors are present.
Concretely: a single test that imports the component file as
text (via `fs.readFileSync` of the TS source — same pattern
other frontend tests already use for inspecting contracts) and
asserts it contains
`.led-running`, `.led-countdown`, `.led-stopped`,
`.led-unconfigured`, plus `width: 10px`, `height: 10px`,
`border-radius: 50%`, and `var(--color-success)`.
- **What is NOT tested (per instructions):** no visual / screenshot
confirmation, no jsdom layout reflow assertions, no large mock SSE
harness — only the structural class binding and the presence of
the component-scoped CSS rules. This matches the
"minimal, highly-focused tests" instruction.