Skip to content

feat: Feature Analytics label grouping (#6067)#7215

Open
talissoncosta wants to merge 10 commits intofix/storybook-dark-modefrom
feat/feature-analytics-6067
Open

feat: Feature Analytics label grouping (#6067)#7215
talissoncosta wants to merge 10 commits intofix/storybook-dark-modefrom
feat/feature-analytics-6067

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

Closes #6067

Feature Analytics label grouping

When the API returns labelled evaluation buckets (e.g. different SDKs), the chart now stacks bars by label value with different colours, and shows a MultiSelect filter to show/hide specific labels. Falls back to environment-based grouping when no labels are present.

New infrastructure

Component What it does
BarChart (web/components/charts/BarChart.tsx) Reusable bar chart wrapping Recharts. Stacked/non-stacked modes, staggered entrance animation, zero custom CSS.
ChartTooltip (web/components/charts/ChartTooltip.tsx) Custom tooltip with per-series breakdown and total. Uses Bootstrap utilities + ColorSwatch.
ColorSwatch (web/components/ColorSwatch.tsx) Reusable colour indicator (sm 8px, md 12px, lg 16px).
getCSSVar (common/utils/getCSSVar.ts) Reads resolved CSS custom property values at runtime for JS chart libraries. Respects dark mode.
Chart color tokens --color-chart-1 through --color-chart-10 with light/dark variants. CHART_COLOURS array export.
analyticsUtils.ts Extracted data utilities (hasLabelledData, aggregateByLabels, buildEnvColorMap).

Storybook stories

  • Components/BarChart — WithLabelledBuckets (interactive filter), WithoutLabels, SingleSeries
  • Components/MultiSelect — Default, WithLabel, WithColors, PreSelected, Disabled, Inline, Small

How did you test this code?

In Storybook

  1. npm run storybook
  2. Components/BarChart — all 3 stories render with fake data, filter works
  3. Components/MultiSelect — all 7 variants render in light + dark mode

In the app

  1. ENV=local npm run dev
  2. Navigate to a feature flag → Usage tab → chart renders with environment colours
  3. If labelled data exists, bars stack by SDK with filter dropdown

Automated

  • npx eslint — 0 errors
  • npm run typecheck — 0 new errors

Stack: 2/3 — depends on PR #7209 (Storybook fixes). PR #7214 (react-select tokens) is independent.

🤖 Generated with Claude Code

talissoncosta and others added 5 commits April 10, 2026 15:10
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>
@vercel
Copy link
Copy Markdown

vercel bot commented Apr 10, 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 10, 2026 6:46pm
flagsmith-frontend-staging Ready Ready Preview, Comment Apr 10, 2026 6:46pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Ignored Ignored Preview Apr 10, 2026 6:46pm

Request Review

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>
@github-actions github-actions bot added feature New feature or request and removed feature New feature or request labels Apr 10, 2026
@talissoncosta talissoncosta marked this pull request as ready for review April 10, 2026 18:49
@talissoncosta talissoncosta requested a review from a team as a code owner April 10, 2026 18:49
@talissoncosta talissoncosta requested review from kyle-ssg and removed request for a team April 10, 2026 18:49
Copy link
Copy Markdown

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Code review skipped — your organization's overage spend limit has been reached.

Code review is billed via overage credits. To resume reviews, an organization admin can raise the monthly limit at claude.ai/admin-settings/claude-code.

Once credits are available, reopen this pull request to trigger a review.

@github-actions github-actions bot added feature New feature or request and removed feature New feature or request labels Apr 10, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 10, 2026

Docker builds report

Image Build Status Security report
ghcr.io/flagsmith/flagsmith-api-test:pr-7215 Finished ✅ Skipped
ghcr.io/flagsmith/flagsmith-e2e:pr-7215 Finished ✅ Skipped
ghcr.io/flagsmith/flagsmith-api:pr-7215 Finished ✅ Results
ghcr.io/flagsmith/flagsmith:pr-7215 Finished ✅ Results
ghcr.io/flagsmith/flagsmith-private-cloud:pr-7215 Finished ✅ Results
ghcr.io/flagsmith/flagsmith-frontend:pr-7215 Finished ✅ Results

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 10, 2026

Playwright Test Results (oss - depot-ubuntu-latest-16)

passed  11 passed

Details

stats  11 tests across 8 suites
duration  0.9 seconds
commit  aaa56af
info  🔄 Run: #15983 (attempt 1)

Playwright Test Results (oss - depot-ubuntu-latest-arm-16)

passed  11 passed

Details

stats  11 tests across 8 suites
duration  51.5 seconds
commit  aaa56af
info  🔄 Run: #15983 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-arm-16)

passed  17 passed

Details

stats  17 tests across 14 suites
duration  1 minute, 2 seconds
commit  aaa56af
info  🔄 Run: #15983 (attempt 1)

Playwright Test Results (private-cloud - depot-ubuntu-latest-16)

passed  1 passed

Details

stats  1 test across 1 suite
duration  56.2 seconds
commit  aaa56af
info  🔄 Run: #15983 (attempt 1)

@github-actions
Copy link
Copy Markdown
Contributor

Visual Regression

16 screenshots compared. See report for details.
View full report

Copy link
Copy Markdown
Contributor Author

@talissoncosta talissoncosta left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


/**
* Reads the computed value of CSS custom properties from the document root.
*
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recharts' fill prop needs hex strings, not CSS var refs. getComputedStyle resolves to the current theme value automatically (dark mode respected).

}

// Collect raw entries with labels intact for label-based grouping.
// chartData aggregates by environment (existing behaviour),
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

chartData = existing environment aggregation. rawEntries = new, preserves per-entry labels so FeatureAnalytics can group by SDK without calling hooks inside a loop (was a rules-of-hooks violation).

import React, { FC } from 'react'
import {
Bar,
BarChart as RawBarChart,
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alias to avoid shadowing — our component is named BarChart which would collide with the Recharts import.

height={80}
angle={-90}
textAnchor='end'
tick={{ dx: -4, fill: '#656D7B', fontSize: 11 }}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#656D7B is --slate-500. Recharts tick/axis props need hex strings (same limitation as fill). Consistent with existing Recharts usage. Could use axis colour tokens in a follow-up.

dataKey={label}
stackId={stacked ? 'series' : undefined}
fill={colorMap.get(label) || '#656D7B'}
animationBegin={index * 80}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Staggered bar entrance — each series starts 80ms after the previous. Uses Recharts built-in animation, not the motion library.

className={classNames('d-inline-block flex-shrink-0 rounded-xs', className)}
style={{
backgroundColor: color,
height: SIZE_MAP[size],
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

backgroundColor is dynamic (per instance). height/width have no Bootstrap utility for 8/12/16px. borderRadius uses the rounded-xs token class.

rawData.forEach((entry) => {
const date = entry.day
// Priority: user_agent (SDK name) > client_application_name > 'Unknown'
const labelValue =
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Priority from backend: user_agent (SDK name, most useful) > client_application_name (fallback) > Unknown (empty/missing labels).

--red-200: #f9cbc9;
--red-300: #f5a5a2;
--red-400: #f57c78;
--red-50: #fef2f1;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not duplication — first block is :root (light, 500/600 shades), this block is .dark (lighter 300/400 shades for contrast on dark backgrounds). Same pattern as all other semantic tokens.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request front-end Issue related to the React Front End Dashboard

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement dashboard functionality for displaying SDK metrics in Feature Analytics charts

1 participant