Follow-up to PR #30 (Storybook) and PR #32 (Playwright fix). Splits HealthDashboard.vue into:
HealthDashboardView.vue (new, dumb / pure / presentational) — accepts data, pending, error as props
HealthDashboard.vue (refactored into a smart wrapper) — does the useFetch('/api/healthz') and forwards state to View
Why
PR #30 left a documentation note on HealthDashboard.stories.ts saying state-based stories needed a future prop refactor. PR #32 added a regression test mocking /api/healthz to 502, but the component itself still couldn't be Storybook-previewed in Loading/Error states because the data fetching was hardcoded internally.
This splits per SRP (data layer ↔ presentation layer), enabling:
4 Storybook stories on the View (Healthy / Loading / ErrorState / HealthyHighUptime)
Unit-testing each rendering branch without mocking the Nuxt fetch layer
Reusing the View in different contexts (e.g. embedded in admin dashboards, status pages)
What's preserved
pages/index.vue still imports <HealthDashboard /> (unchanged).
All existing data-testid attributes preserved (health-dashboard, health-info, health-status).
Playwright tests from PR #32 continue to pass without modification.
What's added
New testids on the View for fine-grained targeting: health-loading, health-error.
shims-vue.d.ts: Vue 3 file module declaration with permissive DefineComponent<any,any,any> typing. Required because the strict TS + Storybook combo otherwise generates false-positive TS2353 errors on prop typing through .vue imports.
Storybook shows 4 stories on HealthDashboardView (manual)
Playwright e2e (existing) still passes (manual)
🤖 Co-Authored-By: Claude Opus 4.7 (1M context)
## Summary
Follow-up to PR #30 (Storybook) and PR #32 (Playwright fix). Splits `HealthDashboard.vue` into:
1. **`HealthDashboardView.vue`** (new, **dumb / pure / presentational**) — accepts `data`, `pending`, `error` as props
2. **`HealthDashboard.vue`** (refactored into a **smart wrapper**) — does the `useFetch('/api/healthz')` and forwards state to View
## Why
PR #30 left a documentation note on `HealthDashboard.stories.ts` saying state-based stories needed a future prop refactor. PR #32 added a regression test mocking /api/healthz to 502, but the component itself still couldn't be Storybook-previewed in Loading/Error states because the data fetching was hardcoded internally.
This splits per **SRP** (data layer ↔ presentation layer), enabling:
- 4 Storybook stories on the View (Healthy / Loading / ErrorState / HealthyHighUptime)
- Unit-testing each rendering branch without mocking the Nuxt fetch layer
- Reusing the View in different contexts (e.g. embedded in admin dashboards, status pages)
## What's preserved
- `pages/index.vue` still imports `<HealthDashboard />` (unchanged).
- All existing `data-testid` attributes preserved (`health-dashboard`, `health-info`, `health-status`).
- Playwright tests from PR #32 continue to pass without modification.
## What's added
- New testids on the View for fine-grained targeting: `health-loading`, `health-error`.
- `shims-vue.d.ts`: Vue 3 file module declaration with permissive `DefineComponent<any,any,any>` typing. Required because the strict TS + Storybook combo otherwise generates false-positive TS2353 errors on prop typing through .vue imports.
## Architecture notes (per code-reviewer skill SOLID/DDD section)
- **SRP** : data fetching and rendering separated into 2 files
- **OCP** : the View can be reused with different data sources (mock, real, polled, websocket)
- **DDD modular** : the wrapper is the "adapter" boundary between Nuxt's runtime and the pure UI; matches the Hexagonal architecture pattern
- **Cognitive load** : each file < 30 lines, naming explicit (`HealthDashboard` = composite, `HealthDashboardView` = view-only)
## Test plan
- [x] `npx tsc --noEmit` passes
- [ ] CI passes
- [ ] Storybook shows 4 stories on HealthDashboardView (manual)
- [ ] Playwright e2e (existing) still passes (manual)
🤖 Co-Authored-By: Claude Opus 4.7 (1M context)
User feedback (PR #32 commit, T13 follow-up): HealthDashboard.stories.ts could not
demonstrate Loading or Error states because the component used useFetch internally
and didn't accept props. Same limitation made unit-testing the rendering branches
impossible without mocking the Nuxt fetch layer.
Split into 2 files (SRP / DDD modular per code-reviewer skill):
- HealthDashboardView.vue (NEW): pure presentational, accepts data/pending/error
as props. Adds explicit data-testid="health-loading" + "health-error" so e2e and
unit tests can target each branch.
- HealthDashboard.vue (REFACTORED): now a thin smart wrapper that calls
useFetch('/api/healthz') and forwards data/pending/error to HealthDashboardView.
Stories:
- HealthDashboardView.stories.ts (NEW): 4 stories — Healthy, Loading, ErrorState,
HealthyHighUptime. Reviewers can now see all branches without running the backend.
- HealthDashboard.stories.ts: still has the Default story for the wrapper (smoke).
Tooling:
- shims-vue.d.ts: Vue file module declaration with permissive any-typing for the
DefineComponent. Required because Vue 3 strict TS + Storybook propagates prop
types poorly through .vue imports otherwise (false-positive TS2353 errors).
Backwards compatibility:
- pages/index.vue still imports <HealthDashboard /> (unchanged).
- All existing data-testid attributes preserved (health-dashboard, health-info,
health-status). The new health-loading and health-error testids are additive.
- The Playwright tests from PR #32 continue to pass without modification.
🤖 Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Follow-up to PR #30 (Storybook) and PR #32 (Playwright fix). Splits
HealthDashboard.vueinto:HealthDashboardView.vue(new, dumb / pure / presentational) — acceptsdata,pending,erroras propsHealthDashboard.vue(refactored into a smart wrapper) — does theuseFetch('/api/healthz')and forwards state to ViewWhy
PR #30 left a documentation note on
HealthDashboard.stories.tssaying state-based stories needed a future prop refactor. PR #32 added a regression test mocking /api/healthz to 502, but the component itself still couldn't be Storybook-previewed in Loading/Error states because the data fetching was hardcoded internally.This splits per SRP (data layer ↔ presentation layer), enabling:
What's preserved
pages/index.vuestill imports<HealthDashboard />(unchanged).data-testidattributes preserved (health-dashboard,health-info,health-status).What's added
health-loading,health-error.shims-vue.d.ts: Vue 3 file module declaration with permissiveDefineComponent<any,any,any>typing. Required because the strict TS + Storybook combo otherwise generates false-positive TS2353 errors on prop typing through .vue imports.Architecture notes (per code-reviewer skill SOLID/DDD section)
HealthDashboard= composite,HealthDashboardView= view-only)Test plan
npx tsc --noEmitpasses🤖 Co-Authored-By: Claude Opus 4.7 (1M context)