fix(audit): chart colours + Charts-tab layout regressions #175
Reference in New Issue
Block a user
Delete Branch "fix/audit-chart-colours-and-layout"
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
Follow-up polish on the audit-log Charts tab shipped in #174. Three small but visible regressions surfaced once real data was loaded:
successcame out teal,deniedcame out orange. With orange = "danger" in most readers' mental model, this is the wrong way around. Added acolorMapinput on<lib-donut-chart>and a curatedsemanticStatusColorsexport so the audit page can mapsuccess → green,denied → orange,failure → red.[hidden]panel on first switch, socanvas.clientWidthis 0 and Plot falls back to its 600/720 default — the resulting fixed-width SVG was wider than the grid column, dragged horizontal overflow up the tree, and (because the grid item defaults tomin-width: auto) wouldn't shrink. The shell layout broke. Fixed by constraining the chart envelope and the grid items so the SVG can scale down to the actual column width.What lands
Bar chart — single fill, lib-owned default
Plot.barYmark no longer encodes colour fromxKey. Thefillparameter is now a literal-string colour, applied uniformly to every bar.fillColorinput on<lib-bar-chart>for the rare case a consumer needs a different shade; defaults toDEFAULT_BAR_FILL = '#1d4ed8'(≈ Tailwind blue-700, picked for AA contrast against both light and dark surfaces, colour-blind-safe).color: { type: 'ordinal', range: palette }scale and thecolorSchemeinput on the bar chart — the categorical palette only made sense whenfill: xKeywas the default behaviour.Donut chart — optional semantic mapping
colorMap?: Readonly<Record<string, string>>input on<lib-donut-chart>. When provided, slice fill resolves tocolorMap[category]first, falling back to the categorical palette for any unmapped category. Lib still owns the palette per ADR-0023; the map only constrains which colour the consumer picks for which category, not what colours exist.semanticStatusColorsexport fromshared-charts— a curated 5-entry map (success / warning / error / info / neutral) tuned for AA contrast and deuteranopia/protanopia distinguishability. Consumers stay inside the lib's palette without authoring their own hex codes.outcomeColorMap = { success: green, failure: red, denied: orange }and passes it to the donut. The donut'sdescription(screen-reader fallback) and the legend chip semantics now line up.Chart envelope — overflow containment
libs/shared/charts/src/lib/_internal/chart-envelope.scss:display: block+min-width: 0on every chart host element (lib-bar-chart,lib-donut-chart,lib-stacked-bar-chart). Angular custom-element hosts default todisplay: inline, which lets the inner<figure>escape parent sizing constraints — the root cause of the body-scrollbar symptom..chart-canvasgetsmin-width: 0and constrains every innerfigure { max-width: 100% }+svg { max-width: 100%; height: auto }. The SVG keeps its viewBox aspect ratio while scaling down to the actual column width.apps/portal-admin/src/app/pages/audit/audit.scss:.chart-tile { min-width: 0; }— grid items default tomin-width: auto, which prevents shrinking below the intrinsic content width. Without it, Plot's fixed-width SVG could still push the grid column past1freven with the lib-side fixes.Notes for the reviewer
#1d4ed8) forDEFAULT_BAR_FILLrather than a brand token? The chart lib is intentionally decoupled fromlibs/shared/tokens— it has no SCSS/CSS-vars dependency and Plot writes the fill as an SVG attribute, not a CSS value, so a CSS variable wouldn't apply anyway. The hex stays inside the lib, marked as the canonical default, with the docstring promising AA contrast + colour-blind safety. If the brand wants to take it over later, swap the constant in one place.semanticStatusColorsis a curated map, not an open extension point. Five entries (success / warning / error / info / neutral) covers the audit module and any future status-bearing donut (user list, integrations health, etc.). Adding a sixth entry needs a small PR + an a11y-contrast check, which is the right friction.aria-label="bar"selector in the new bar-chart spec. Plot tags its bar-mark<g>witharia-label="bar"(and similarly"rule"forPlot.ruleY). Selecting on it scopes the fill-uniqueness check to actual bars, not axes / ticks / labels. If Plot changes that label upstream the spec breaks loudly — preferable to a fragile geometric selector.colorMapeven whendatais empty; the lib'sarcs.forEachshort-circuits on zero arcs so no colour lookup happens.audit.scssis now 7.5 KB, still over the 6 KB component-style warning (untouched from #174) and well under the 8 KB error. The new.chart-tile { min-width: 0 }rule is two lines.Test plan
pnpm nx test shared-charts— 15 specs pass (was 14: +1 donutcolorMaptest, +1 bar single-fill test, -1 obsolete bar palette assumption).pnpm nx test portal-admin— 62 specs pass (unchanged from #174 — semantic mapping is a template-only change, behavioural tests still hold).pnpm nx run-many -t lint test build -p portal-shell,portal-admin,portal-bff,shared-charts— clean. Same three pre-existing lint warnings, no new ones; bundle sizes unchanged within ±0.1 KB.pnpm nx serve portal-admin+pnpm nx serve portal-bff, sign in withPortal.Admin, navigate to/admin/audit:s.total, the green slice issuccess, the orange slice isdenied, the red slice isfailure. Hover each slice —<title>shows<category>: <count>(untouched a11y contract from the lib).(max-width: 800px)breakpoint — grid collapses to one column, charts re-flow without horizontal overflow.What's next
Nothing in this chantier. The audit dashboard is now visually coherent and layout-stable. Two background items to track separately when the CMS / user-list pages land their own dashboards:
audit.htmlintolibs/shared/ui/tabs/once a second consumer appears.