feat(shared-charts): foundations + bar / donut / stacked-bar components #171
Reference in New Issue
Block a user
Delete Branch "feat/shared-charts-foundations"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Implementation of ADR-0023 — the foundations of the workspace's chart library. PR 2 of the chantier:
libs/shared/charts/foundations +<lib-bar-chart>,<lib-donut-chart>,<lib-stacked-bar-chart>./auditpage — daily-volume bar + outcome-breakdown donut + event-type-over-time stacked bar.What lands
Workspace deps
All four (+ matching
@types/*) land in the workspace rootdevDependencies. Tree-shaken at build time per ADR-0023's bundle plan.New lib
libs/shared/charts/A11y contract baked in for v1
Per ADR-0023's six commitments, every chart component produces (and unit-tests for):
<figure role="img" aria-labelledby aria-describedby>wrapping the SVG.<title>+<desc>as the first two children — injected post-render viainjectSvgTitleDescbecause Plot doesn't emit them itself.findChartSvghandles both Plot output shapes (bare SVG, or<figure>wrapping a legend + SVG forlegend: trueconfigs).<details>disclosure with a<table>rendering every data point — the keyboard-navigable / screen-reader-friendly fallback for non-visual users._internal/palette.tsonly — Viridis / Cividis for sequential, ColorBrewer Set2 for categorical. Both colour-blind-safe.:where(.dark)flips in_internal/chart-envelope.scss.prefers-reduced-motion→data-no-transitionsmarker on the SVG, CSS strips animations + transitions.A custom ESLint rule in
libs/shared/charts/eslint.config.mjsbans direct imports ofd3-scale-chromaticoutside_internal/palette.tsso a future contributor can't bypass the colour-blind-safe contract.Component contract
Every
<lib-*-chart>exposes the same Signal-based shape per ADR-0023:Re-renders triggered by Angular's
effect()on input changes; the previous SVG isreplaceChildren-d out so there's no DOM accumulation across data updates.Notes for the reviewer
_internal/chart-envelope.scssis the consolidation; each chart's.scssis now 4-20 LOC of chart-specific tweaks.<lib-donut-chart>raw D3 rather than Plot? Plot's design philosophy explicitly excludes pie/donut marks ("a bar chart is almost always more legible"). The audit-log outcome breakdown reads naturally as a donut (the centre carries the total). Rawd3-shapeis the lower-level fallback ADR-0023 reserves precisely for this kind of case; the component's API is identical to the Plot-backed siblings.<title>? Belt-and-suspenders. The top-level SVG<title>reads the caption; per-slice<title>reads "category: value" on hover (the SVG-native tooltip convention) for keyboard / screen-reader users who land on a specific slice.pnpm.overridesadjustment ford3-*transitives? None of the new deps brought a vulnerability in this install. Thepnpm audit --audit-level=moderategate from #161 stays green./audit-page integration — data aggregation from the current page (AdminAuditPagerows already loaded), the actual<lib-*-chart>placements above the existing table, the i18n strings for the captions / descriptions / aria-labels. No mock SAMPLE data shipped in this PR — every test uses local fixtures so the lib stays decoupled from any specific consumer.Test plan
pnpm nx test shared-charts— 13 specs pass across the three components.pnpm nx lint shared-charts— clean, including the customno-restricted-importsguard ond3-scale-chromatic.pnpm nx build shared-charts— clean (TS strict + ng-packagr).pnpm nx run-many -t lint test build --projects=shared-charts,portal-shell,portal-admin— 12/12 tasks green.pnpm nx build portal-shell --configuration=production— i18n-strict prod build clean. The lib ships no i18n marks (axis labels are caller-supplied per ADR-0023's i18n posture), so no xlf entry change here./audit) —<figure>+ caption visible, SVG<title>exposed by VoiceOver / NVDA,<details>fallback expands to a table, dark-mode toggle flips axis text + Cividis palette,prefers-reduced-motionstrips Plot's fade-in.What's next
PR 3 wires the three components onto the
/auditpage: aggregatesAdminAuditPage.itemsclient-side into the three required shapes (daily totals, outcome counts, daily-by-event-type pivots), places them above the existing filter form, ships the i18n strings for the captions and descriptions inmessages.fr.xlf. Bundle impact on the lazyauditchunk gets verified there (the ~65 KB gzip plan from the ADR).Implementation of ADR-0023 (PR 1). The next chantier wires these components into the audit-log page; this PR delivers the lib + the three starter chart types + the a11y contract. Deps: * d3 + @types/d3 (top-level) * d3-shape + @types/d3-shape (donut's pie/arc generators) * d3-scale-chromatic + @types/d3-scale-chromatic (palettes) * @observablehq/plot (bar + stacked-bar) Lib `libs/shared/charts/`: * `_internal/palette.ts` — single source of palettes. Sequential (Viridis / Cividis for dark mode) + categorical (ColorBrewer Set2). Colour-blind-safe per the ColorBrewer review. * `_internal/a11y.ts` — chartId generator, SVG <title>/<desc> injector, `findChartSvg` for Plot's legend-wrapped output, prefers-reduced-motion guard, theme resolver. * `_internal/chart-envelope.scss` — shared figure/caption/ fallback rules consumed via `@use` by each chart's .scss. Extracted at the third consumer per CLAUDE.md's "three similar things" rule. * `_internal/chart-types.ts` — common `ChartBaseInputs<T>` interface every component extends. Three Plot-or-D3-backed components: * `<lib-bar-chart>` — Plot.barY, categorical-by-default palette. * `<lib-donut-chart>` — raw d3-shape (pie + arc); Plot has no donut mark so we reach for D3 directly. Each slice carries its own <title> child for SVG-tooltip accessibility. * `<lib-stacked-bar-chart>` — Plot.barY with `fill` set to the series key (auto-stacked), legend on. Each component honours ADR-0023's six commitments: 1. <figure role="img" aria-labelledby aria-describedby> 2. SVG <title> + <desc> as first two children (injected post- render because Plot doesn't emit them) 3. <details> tabular fallback rendering every data point 4. Palette from the canonical list (no ad-hoc colours) 5. AA-contrast text on axis ticks (theme-aware grayscale via the .chart-canvas svg text rule) 6. prefers-reduced-motion → data-no-transitions marker on the SVG, CSS strips animations. A custom ESLint rule in `libs/shared/charts/eslint.config.mjs` bans direct imports of `d3-scale-chromatic` outside `_internal/palette.ts`, so a future contributor can't bypass the colour-blind-safe contract. Tests: 13 specs across the three components. Each component covers the figure ARIA contract, the SVG title/desc injection, the tabular fallback, and data-reactivity. Donut spec additionally asserts per-slice <title> labels for screen readers. Verification: * `pnpm nx lint test build --projects=shared-charts,portal-shell,portal-admin` — 12/12 tasks green. * `pnpm nx build portal-shell --configuration=production` — i18n-strict prod build clean (the lib ships no i18n marks since axis labels are caller-supplied).