Skip to content

refactor: Consolidate charts with BarChart component#7223

Draft
talissoncosta wants to merge 18 commits intofeat/feature-analytics-6067from
refactor/consolidate-charts-with-barchart
Draft

refactor: Consolidate charts with BarChart component#7223
talissoncosta wants to merge 18 commits intofeat/feature-analytics-6067from
refactor/consolidate-charts-with-barchart

Conversation

@talissoncosta
Copy link
Copy Markdown
Contributor

  • I have read the Contributing Guide.
  • I have added information to docs/ if required so people know about the feature.
  • I have filled in the "Changes" section below.
  • I have filled in the "How did you test this code" section below.

Changes

Follow-up to #7215 — migrates the two raw-recharts consumers in the org-level Usage page to the shared <BarChart /> component, retiring the legacy recharts SCSS overrides.

What's migrated

Component Path What
OrganisationUsage (chart section) web/components/organisation-settings/usage/OrganisationUsage.container.tsx 4-metric stack with custom display labels (environment_document → "Environment Document"), selection-driven series, pre-formatted day axis
SingleSDKLabelsChart web/components/organisation-settings/usage/components/SingleSDKLabelsChart.tsx Per-SDK stack with palette colour map from parent + MultiSelect filter
OrganisationUsageMetrics (parent) web/components/organisation-settings/usage/OrganisationUsageMetrics.container.tsx Pre-formats day to 'D MMM' (matches BarChart's day-as-display-string contract); userAgentColorMap switched from MapRecord<string, string> for consistency with the BarChart prop

BarChart API additions

Both consumers needed two props the existing BarChart didn't expose:

  • barSize?: number — fixed bar width in pixels (OrganisationUsage uses 14px to fit four series per day comfortably)
  • verticalGrid?: boolean (default true) — toggles CartesianGrid's vertical lines (legacy charts hide them)

seriesLabels (added in #7215) handles the display-name mapping for OrganisationUsage (environment_document → "Environment Document").

Cleanup

  • Delete web/styles/3rdParty/_recharts.scss entirely — its rules existed solely to style the two legacy charts' tooltips. With both consumers migrated, no recharts global-className consumer remains; the new BarChart's ChartTooltip uses Bootstrap utilities + semantic token classes directly. Drop the @import too.
  • SingleSDKLabelsChart loses unused props (metricKey, colours) that the migration made redundant.

Net diff

+101 / -312 — six files changed, one deleted.

Type strictness

OrganisationUsage's colorMap and seriesLabels are typed against Record<MetricDataKey, string> where MetricDataKey is derived from the METRICS array ((typeof METRICS)[number]['dataKey']). Adding a new metric to METRICS forces both maps to be updated — TS errors on missing keys.

How did you test this code?

In the app

  1. ENV=local npm run dev
  2. Navigate to Organisation → Usage → 4-metric stacked chart renders, tooltip shows display names ("Flags", "Environment Document", etc.) with formatted dates
  3. Toggle metric checkboxes → series add/remove correctly
  4. Switch to SDK view (requires sdk_usage_charts flag enabled) → per-SDK stack renders, MultiSelect filter narrows visible series, tooltip shows SDK names

Automated

  • npx eslint — 0 errors on touched files
  • npm run typecheck — 0 new errors (pre-existing any cluster in useFeatureAnalytics.ts unchanged; pre-existing errors in OrganisationUsageSideBar/OrganisationUsagePage are unrelated)

Stack: 2/2 — depends on #7215 (Feature Analytics label grouping) for the BarChart component, seriesLabels prop, and chart colour tokens. Will rebase onto main when #7215 lands.

🤖 Generated with Claude Code

talissoncosta and others added 18 commits April 13, 2026 10:07
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Zero custom CSS — tooltip uses Bootstrap utilities + semantic tokens.
ChartTooltip extracted to own file for single responsibility.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Data utilities extracted to analyticsUtils.ts for testability.

Closes #6067

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Chart tokens were sorted alphabetically (1, 10, 2, 3...) instead of
numerically (1, 2, 3...10). Use localeCompare with numeric option.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Update featureAnalytics service queryFn to return rawEntries alongside
  chartData, so FeatureAnalytics doesn't need to call hooks in a loop
- Remove useGetEnvironmentAnalyticsQuery calls inside .map() (React
  rules-of-hooks violation)
- Rename analyticsUtils.ts to utils.ts
- Add 14 unit tests for hasLabelledData, aggregateByLabels, buildEnvColorMap
- Fix natural sort order in token generator

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add useChartColors() and useChartColorMap() hooks (common/hooks/)
- Remove getCSSVars/CHART_COLOURS imports from utils.ts — colors
  are now passed as a parameter from the hook
- Remove buildEnvColorMap — replaced by useChartColorMap hook
- Update tests to pass mock colors directly (no jest.mock needed)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace getCSSVars/CHART_COLOURS with useChartColorMap hook
- Replace inline color/fontSize/margin styles with Bootstrap utilities

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove common/utils/getCSSVar.ts — only had one consumer
- Inline resolveColors() into useChartColors hook with JSDoc explaining
  why we read from DOM (Recharts needs hex strings, not var() refs)
- Add comment on rawEntries in service queryFn explaining the dual
  return (chartData for environments, rawEntries for labels)
- Add comment on label priority in utils.ts
- Regenerate tokens.ts with updated comment

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Create web/styles/3rdParty/_recharts.scss as proper home for recharts
  global styles (alongside _react-select.scss, _react-datepicker.scss)
- Switch axis tick + line colours to var(--color-text-secondary) via
  recharts' built-in classNames — dark mode handled automatically, no
  hex strings threaded through props
- Drop hardcoded '#656D7B' from BarChart.tsx (4 axis instances + 1 bar
  fallback)
- Delete dead .xAxis/.yAxis rules from _tooltips.scss (no consumers)
- TODO on legacy .recharts-tooltip + .dark block — removes when
  SingleSDKLabelsChart and OrganisationUsage migrate to <BarChart />

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tColors

Generator now emits flat camelCase constants for every semantic token
(e.g. colorChart1, colorTextSecondary, radiusMd) as CSS value strings —
'var(--token, #hex-fallback)'. Pass directly to recharts props, inline
styles, or anywhere a CSS value is accepted; var() resolves at render
and theme toggle updates colours via the CSS cascade.

Charts:
- BarChart uses colorTextSecondary directly for axis tick + line — no
  CSS classname plumbing in _recharts.scss
- buildChartColorMap pure function (web/components/charts/) replaces
  useChartColors / useChartColorMap hooks — no DOM read, no
  theme-toggle staleness bug
- _recharts.scss reduced to legacy rules + TODO for the two remaining
  raw-recharts consumers (SingleSDKLabelsChart, OrganisationUsage)

Cleanup:
- Delete common/hooks/useChartColors.ts (both hooks)
- Delete common/theme/index.ts barrel — codebase convention is direct
  file imports
- Drop nested tokens object + 4 unused type exports from tokens.ts;
  update the one MDX docs example to use a flat constant

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix inverted pluralisation in empty-state message ("environment"
  takes an 's' when there are multiple selected, not the other way
  around)
- Type ChartTooltip against recharts' Payload<ValueType, NameType>
  instead of `el: any` — use `el.color` (typed) in place of `el.fill`
  (untyped), add numericValue helper to keep the reduce typed
- Add direct unit tests for buildChartColorMap (basic mapping,
  wraparound, empty array) — previously covered only transitively
  via aggregateByLabels tests

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ull labels

Three bugs caused labelled analytics to look wrong in real data:

1. hasLabelledData was too permissive — passed `{ user_agent: null }`
   as "labelled" because the key exists. Every entry then fell through
   to the 'Unknown' fallback in aggregation, collapsing all data into
   a single series. Tightened to require at least one entry with a
   non-empty `user_agent` or `client_application_name` value.

2. chartData from aggregateByLabels had no date ordering guarantee —
   dates appeared in whatever order the raw entries landed (visible
   in prod as `2026-04-03` rendered AFTER `2026-04-12`). Fixed by
   having aggregateByLabels seed its output from a caller-supplied
   day axis (chronologically pre-built by useFeatureAnalytics for
   the env-path chart).

3. Labelled chart only included days with events, so the x-axis was
   sparse — whereas the env path pre-builds all 30 days. Fixed by
   reusing the env-path day axis, giving both paths the same complete
   date range and the same 'Do MMM' display format.

hasData check updated — labelledChartData now always has one bucket
per day (including empty ones), so `.length > 0` no longer indicates
presence of data. Switched to "any day has a non-zero count for a
label series".

Tests: 20 passing (17 utils + 3 buildChartColorMap), includes new
cases for null-label-values, caller-provided day order, and out-of-
range entries being dropped.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The new BarChart introduced two regressions for the env-grouped path:

1. Legend showed raw env IDs (e.g. "22", "1848") because the new
   BarChart unconditionally renders recharts' <Legend /> using each
   series' dataKey. The env tags at the top already serve as a
   colour legend, so a second one was redundant and broken.
   - Add `showLegend?: boolean` to BarChart (default false)
   - FeatureAnalytics only enables it for the labelled path (where
     the filter above the chart doesn't carry colour per series)
   - Stories keep `showLegend` on (no filter UI above them)

2. Bar colours didn't match the env tag chip colours. The new code
   used `buildChartColorMap(environmentIds)` — which indexes into
   our chart palette by selection order. Old prod used
   `Utils.getTagColour(indexInProjectEnvList)` — the same function
   that colours the env chip, indexed by the env's position in the
   project's env list. Restored that behaviour:
   - Fetch envs via `useGetEnvironmentsQuery`
   - Sort selected envs by their project-list position (stable)
   - Build envColorMap with `Color(Utils.getTagColour(idx)).alpha(0.75)`
   - Pass sortedEnvIds as the chart series

Labelled path keeps the new `CHART_COLOURS` palette (intentional — SDK
names aren't in the tag-colour palette, and this is a net-new feature).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two remaining Feature Analytics tooltip regressions:

1. Raw dataKeys surfaced in the tooltip — env mode showed "22: 0" /
   "1848: 0" instead of "Production: 0" / "Staging: 0". Added a
   `seriesLabels?: Record<string, string>` prop to BarChart, threaded
   through to ChartTooltip (and recharts' <Legend formatter> when the
   optional legend is enabled). FeatureAnalytics builds an env-id →
   env-name map from useGetEnvironmentsQuery and passes it for the
   env path; labelled path leaves it undefined (dataKeys are already
   SDK names).

2. Tooltip label text used `text-secondary` which in the theme's
   tooltip-on-surface context rendered with poor contrast (muted
   yellow-ish on white). Switched label + value to `text-default`
   with a semibold weight on the value — keeps the label/value
   hierarchy while matching the tooltip header's contrast.

Incidental type fixes (both introduced earlier this session):
- ChartTooltip.formatNumber now accepts recharts' ValueType (was
  string | number | undefined — ValueType is broader and can be an
  array)
- FeatureAnalytics passes `projectId: Number(projectId)` to
  useGetEnvironmentsQuery — the Req type expects number, old code
  was silently typechecking as string against a loose upstream
  signature

The pre-existing `any` cluster in useFeatureAnalytics.ts is unchanged
(out of scope — separate follow-up).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…to Record

Three targeted cleanups after auditing the PR for overengineering:

1. Extract useEnvChartProps({ projectId, environmentIds }) — returns
   { series, colorMap, seriesLabels } for env-grouped charts. Bundles
   the env-list query, project-position-based sorting, tag-colour
   mapping (Utils.getTagColour + alpha), and env-name lookup into one
   hook. Real reuse ahead: the planned legacy chart migration
   (SingleSDKLabelsChart, OrganisationUsage) needs the same env
   colouring to match their tag chips — this hook is the single
   source of that logic. FeatureAnalytics.tsx drops ~30 lines of
   derivation in the process.

2. Remove BarChart's `stacked` and `height` props. Both defaulted
   sensibly (stacked=true, height=400) and no consumer ever overrode
   them (grep'd every <BarChart /> callsite, including the legacy
   chart migration targets). YAGNI — cheaper to add them back later
   than to carry unused API surface now.

3. Switch every `colorMap: Map<string, string>` to
   `Record<string, string>` — BarChart, MultiSelect, aggregateByLabels,
   buildChartColorMap, plus all tests and stories. Previous mix of
   Map (for colours) and Record (for seriesLabels) wasn't principled,
   just historical. Record is more idiomatic React, simpler to
   inspect in devtools, and callsites are slightly cleaner (`m[k]`
   vs `m.get(k)`). Tree-shakeable, no behavioural change.

Labelled-mode derivations (aggregateByLabels call, labelOptions,
filteredLabels) stay inline as component-local useMemos — extracting
them into a second hook would be packaging for packaging's sake, with
no reuse today.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…rts SCSS

Replaces the two raw-recharts consumers with the shared <BarChart />
component introduced in #7215. Both pages keep their behaviour and
visual contract; the JSX collapses substantially.

BarChart API additions to support these consumers:
- `barSize?: number` — fixed bar width in pixels (OrganisationUsage
  uses 14px to fit four series per day comfortably)
- `verticalGrid?: boolean` (default true) — toggles CartesianGrid's
  vertical lines (legacy charts hide them)

Migrations:
- OrganisationUsage.container.tsx (4 metric series stacked, custom
  display labels via seriesLabels, selection-driven series filter,
  pre-formatted day axis)
- SingleSDKLabelsChart.tsx (per-SDK stacked, palette colour map
  passed in from parent, MultiSelect-driven SDK filter)
- OrganisationUsageMetrics.container.tsx — pre-formats day to
  'D MMM' (matches the new chart-side day-as-display-string contract),
  switches userAgentColorMap from Map to Record (consistent with
  BarChart's prop after #7215)

Cleanup:
- Delete web/styles/3rdParty/_recharts.scss entirely — its rules
  existed solely to style the two legacy charts' tooltips. With both
  consumers migrated, no consumer of recharts global classNames
  remains; the new BarChart's ChartTooltip uses Bootstrap utilities +
  semantic token classes directly. Drop the @import too.
- SingleSDKLabelsChart loses unused props (`metricKey`, `colours`)
  that the migration made redundant.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel bot commented Apr 13, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
flagsmith-frontend-preview Ready Ready Preview, Comment Apr 13, 2026 7:32pm
flagsmith-frontend-staging Ready Ready Preview, Comment Apr 13, 2026 7:32pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Ignored Ignored Preview Apr 13, 2026 7:32pm

Request Review

@github-actions github-actions bot added front-end Issue related to the React Front End Dashboard refactor labels Apr 13, 2026
@talissoncosta talissoncosta force-pushed the feat/feature-analytics-6067 branch from 8483792 to 45ee267 Compare April 14, 2026 16:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

front-end Issue related to the React Front End Dashboard refactor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant