diff --git a/src/components/insights/__tests__/sub-page-shell.test.tsx b/src/components/insights/__tests__/sub-page-shell.test.tsx index 4873a42e7..e586b2329 100644 --- a/src/components/insights/__tests__/sub-page-shell.test.tsx +++ b/src/components/insights/__tests__/sub-page-shell.test.tsx @@ -47,7 +47,30 @@ describe("", () => { , ); // The shell uses a `

` for the description; no `

` appears when - // the description prop is absent. + // neither a description nor an explainer metric is supplied. expect(html).not.toMatch(/]*class="text-muted-foreground/); }); + + it("renders the explainer body inline beneath the heading", () => { + const html = render( + + + , + ); + // The inline caption reuses the same body string the `?` popover reads. + expect(html).toMatch(/data-slot="metric-explainer-inline"/); + expect(html).toContain( + "Blood pressure is the force your blood exerts on the artery walls", + ); + }); + + it("keeps the explainer trigger glyph when explainerMetric is set", () => { + const html = render( + + + , + ); + // The `?` glyph stays available alongside the inline caption. + expect(html).toMatch(/data-slot="metric-explainer-trigger"/); + }); }); diff --git a/src/components/insights/sub-page-shell.tsx b/src/components/insights/sub-page-shell.tsx index cc2c84566..b26575fe0 100644 --- a/src/components/insights/sub-page-shell.tsx +++ b/src/components/insights/sub-page-shell.tsx @@ -5,6 +5,7 @@ import { useEffect, useRef, type ReactNode } from "react"; import { Badge } from "@/components/ui/badge"; import { MetricExplainer } from "@/components/insights/metric-explainer"; import { useScrollResetOnRoute } from "@/hooks/use-scroll-reset-on-route"; +import { useTranslations } from "@/lib/i18n/context"; import { cn } from "@/lib/utils"; /** @@ -41,6 +42,13 @@ export interface SubPageShellProps { * The value is the metric key feeding * `insights.subPage.explainer.{Title,Body}`. Omitted on the * mother page; every routed metric sub-page passes its key. + * + * v1.8.4 — the same explainer body also renders inline as a muted + * caption directly beneath the heading, so the one- to two-sentence + * definition is visible without opening the popover. The `?` glyph + * stays as the always-available pointer for the same copy. Both the + * inline caption and the popover read the identical + * `insights.subPage.explainer.Body` string — no duplicated copy. */ explainerMetric?: string; /** @@ -69,6 +77,7 @@ export function SubPageShell({ focusOnMount = false, children, }: SubPageShellProps) { + const { t } = useTranslations(); // a11y: focus the heading on mount so a tab-strip navigation actually // moves screen-reader focus into the sub-page body. v1.4.27 MB7 / // CF-35 made the focus call opt-in via `focusOnMount` — the @@ -118,6 +127,18 @@ export function SubPageShell({ ) : null} + {explainerMetric ? ( + // v1.8.4 — surface the explainer definition inline, reusing the + // exact body string the `?` popover reads. Muted caption styling + // matches the description paragraph below so the under-heading + // band reads as one calm block. +

+ {t(`insights.subPage.explainer.${explainerMetric}Body`)} +

+ ) : null} {description ? (

{description}

) : null}