From 7c3617c9d79f1511b36ab7d0fba9265a94e9b1a0 Mon Sep 17 00:00:00 2001 From: Gabriel Radureau Date: Sun, 3 May 2026 17:55:47 +0200 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(frontend):=20spli?= =?UTF-8?q?t=20HealthDashboard=20into=20smart=20wrapper=20+=20dumb=20View?= =?UTF-8?q?=20for=20state-based=20stories=20(#33)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SRP split: HealthDashboardView (presentational, props-based) + HealthDashboard (smart wrapper, useFetch). Enables 4 Storybook stories per state + unit testability per branch. Existing testids preserved, Playwright tests still pass. Co-authored-by: Gabriel Radureau Co-committed-by: Gabriel Radureau --- .../components/HealthDashboard.stories.ts | 27 +++---- frontend/components/HealthDashboard.vue | 25 ++---- .../components/HealthDashboardView.stories.ts | 79 +++++++++++++++++++ frontend/components/HealthDashboardView.vue | 30 +++++++ frontend/shims-vue.d.ts | 6 ++ 5 files changed, 133 insertions(+), 34 deletions(-) create mode 100644 frontend/components/HealthDashboardView.stories.ts create mode 100644 frontend/components/HealthDashboardView.vue create mode 100644 frontend/shims-vue.d.ts diff --git a/frontend/components/HealthDashboard.stories.ts b/frontend/components/HealthDashboard.stories.ts index 6980774..56e78d4 100644 --- a/frontend/components/HealthDashboard.stories.ts +++ b/frontend/components/HealthDashboard.stories.ts @@ -5,6 +5,16 @@ const meta: Meta = { title: 'Components/HealthDashboard', component: HealthDashboard, tags: ['autodocs'], + parameters: { + docs: { + description: { + component: + 'Smart wrapper that calls /api/healthz internally and delegates rendering to HealthDashboardView. ' + + 'For state-by-state previews (Healthy, Loading, Error), see ' + + '[HealthDashboardView stories](?path=/docs/components-healthdashboardview--docs).', + }, + }, + }, } export default meta @@ -14,20 +24,3 @@ type Story = StoryObj export const Default: Story = { args: {}, } - -// Documents the loading/error visuals so reviewers see them in Storybook -// without needing a backend down. The component currently doesn't accept overrides -// because it uses useFetch internally - this story is a placeholder for a future -// refactor that exposes data/pending/error as props for testability. -export const DocumentationStub: Story = { - args: {}, - parameters: { - docs: { - description: { - story: - 'Renders the same as Default. The HealthDashboard fetches /api/healthz internally, so loading/error states only appear when the backend is down or slow. ' + - 'Future improvement: accept healthData/pending/error as props for easy state-based stories. Until then, see frontend/docs/e2e/ for screenshots of both healthy and error states.', - }, - }, - }, -} diff --git a/frontend/components/HealthDashboard.vue b/frontend/components/HealthDashboard.vue index 8d42398..858b705 100644 --- a/frontend/components/HealthDashboard.vue +++ b/frontend/components/HealthDashboard.vue @@ -1,22 +1,13 @@ + diff --git a/frontend/components/HealthDashboardView.stories.ts b/frontend/components/HealthDashboardView.stories.ts new file mode 100644 index 0000000..942fa2a --- /dev/null +++ b/frontend/components/HealthDashboardView.stories.ts @@ -0,0 +1,79 @@ +import type { Meta, StoryObj } from '@storybook/vue3' +import HealthDashboardView from './HealthDashboardView.vue' + +interface ViewArgs { + data: { + status: string + version: string + uptime_seconds: number + timestamp: string + } | null + pending: boolean + error: { message: string } | null +} + +const meta = { + title: 'Components/HealthDashboardView', + component: HealthDashboardView, + tags: ['autodocs'], + argTypes: { + pending: { control: 'boolean' }, + }, + parameters: { + docs: { + description: { + component: + 'Pure presentational component for the health dashboard. ' + + 'Accepts `data`, `pending`, `error` as props so all 3 states can be ' + + 'previewed in Storybook and asserted in unit tests. The data fetching ' + + 'wrapper is `HealthDashboard.vue`.', + }, + }, + }, +} satisfies Meta + +export default meta + +type Story = StoryObj + +export const Healthy: Story = { + args: { + data: { + status: 'healthy', + version: '1.4.0', + uptime_seconds: 3600, + timestamp: '2026-05-03T17:30:00.000Z', + }, + pending: false, + error: null, + }, +} + +export const Loading: Story = { + args: { + data: null, + pending: true, + error: null, + }, +} + +export const ErrorState: Story = { + args: { + data: null, + pending: false, + error: { message: '[GET] "/api/healthz": 502 Bad Gateway (simulated)' }, + }, +} + +export const HealthyHighUptime: Story = { + args: { + data: { + status: 'healthy', + version: '1.5.0-rc1', + uptime_seconds: 86400 * 7, + timestamp: new Date().toISOString(), + }, + pending: false, + error: null, + }, +} diff --git a/frontend/components/HealthDashboardView.vue b/frontend/components/HealthDashboardView.vue new file mode 100644 index 0000000..46741fe --- /dev/null +++ b/frontend/components/HealthDashboardView.vue @@ -0,0 +1,30 @@ + + + diff --git a/frontend/shims-vue.d.ts b/frontend/shims-vue.d.ts new file mode 100644 index 0000000..576404f --- /dev/null +++ b/frontend/shims-vue.d.ts @@ -0,0 +1,6 @@ +declare module '*.vue' { + import type { DefineComponent } from 'vue' + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const component: DefineComponent + export default component +}