Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions frontend/common/hooks/useChartColors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { useMemo } from 'react'
import { CHART_COLOURS } from 'common/theme/tokens'

/**
* 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).

* Why not import values directly from tokens.ts?
* tokens.ts exports CSS var() references (e.g. 'var(--color-chart-1, #0aaddf)'),
* not raw hex strings. Recharts' `fill` prop needs actual colour values like
* '#0aaddf', not var() references. getComputedStyle resolves the var to its
* current value, which also means dark mode is respected automatically —
* the same token resolves to a different hex in light vs dark.
*/
function resolveColors(varNames: readonly string[]): string[] {
if (typeof document === 'undefined') return varNames.map(() => '')
const style = getComputedStyle(document.documentElement)
return varNames.map((name) => style.getPropertyValue(name).trim())
}

/**
* Returns the resolved chart color palette for the current theme.
*
* @example
* const colors = useChartColors()
* colors[0] // '#0aaddf' in light, '#45bce0' in dark
*/
export function useChartColors(): string[] {
return useMemo(() => resolveColors(CHART_COLOURS), [])
}

/**
* Builds a color map from a list of series names to chart colors.
* Wraps around when there are more series than available colors.
*
* @example
* const colorMap = useChartColorMap(['js-sdk', 'python-sdk'])
* colorMap.get('js-sdk') // '#0aaddf'
*/
export function useChartColorMap(series: string[]): Map<string, string> {
const colors = useChartColors()
return useMemo(() => {
const map = new Map<string, string>()
series.forEach((name, index) => {
map.set(name, colors[index % colors.length])
})
return map
}, [series, colors])
}
12 changes: 10 additions & 2 deletions frontend/common/services/useFeatureAnalytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,27 @@ export const featureAnalyticsService = service
preBuiltData.push(dayObj)
}

// 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).

// rawEntries preserves per-SDK labels for stacked charts (#6067).
const rawEntries: Res['environmentAnalytics'] = []

responses.forEach((response, i) => {
const environment_id = query.environment_ids[i]

response.data?.forEach((entry) => {
rawEntries.push(entry)
const date = moment(entry.day).format('Do MMM')
const dayEntry = preBuiltData.find((d) => d.day === date)
if (dayEntry) {
dayEntry[environment_id] = entry.count // Set count for specific environment ID
dayEntry[environment_id] = entry.count
}
})
})
return {
data: error ? [] : preBuiltData,
data: error
? { chartData: [], rawEntries: [] }
: { chartData: preBuiltData, rawEntries },
error,
}
},
Expand Down
12 changes: 12 additions & 0 deletions frontend/common/theme/tokens.json
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,18 @@
"info": { "cssVar": "--color-icon-info", "light": "#0aaddf", "dark": "#0aaddf" }
}
},
"chart": {
"1": { "cssVar": "--color-chart-1", "light": "#0aaddf", "dark": "#45bce0", "description": "First series in charts. Blue." },
"2": { "cssVar": "--color-chart-2", "light": "#ef4d56", "dark": "#f57c78", "description": "Second series. Red." },
"3": { "cssVar": "--color-chart-3", "light": "#27ab95", "dark": "#56ccad", "description": "Third series. Green." },
"4": { "cssVar": "--color-chart-4", "light": "#ff9f43", "dark": "#ffc08a", "description": "Fourth series. Orange." },
"5": { "cssVar": "--color-chart-5", "light": "#7a4dfc", "dark": "#906af6", "description": "Fifth series. Purple." },
"6": { "cssVar": "--color-chart-6", "light": "#0b8bb2", "dark": "#7ecde2", "description": "Sixth series. Blue dark." },
"7": { "cssVar": "--color-chart-7", "light": "#e61b26", "dark": "#f5a5a2", "description": "Seventh series. Red dark." },
"8": { "cssVar": "--color-chart-8", "light": "#13787b", "dark": "#87d4c4", "description": "Eighth series. Green dark." },
"9": { "cssVar": "--color-chart-9", "light": "#fa810c", "dark": "#ffd7b5", "description": "Ninth series. Orange dark." },
"10": { "cssVar": "--color-chart-10", "light": "#6837fc", "dark": "#b794ff", "description": "Tenth series. Purple dark." }
},
"radius": {
"none": { "cssVar": "--radius-none", "value": "0px", "description": "Sharp corners. Tables, dividers." },
"xs": { "cssVar": "--radius-xs", "value": "2px", "description": "Barely rounded. Badges, tags." },
Expand Down
14 changes: 14 additions & 0 deletions frontend/common/theme/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,20 @@ export const easing: Record<string, TokenEntry> = {
},
}

// Chart colours — use with useChartColors() hook for runtime resolution
export const CHART_COLOURS = [
'--color-chart-1',
'--color-chart-2',
'--color-chart-3',
'--color-chart-4',
'--color-chart-5',
'--color-chart-6',
'--color-chart-7',
'--color-chart-8',
'--color-chart-9',
'--color-chart-10',
] as const

export type TokenCategory = keyof typeof tokens
export type TokenName<C extends TokenCategory> = keyof (typeof tokens)[C]
export type RadiusScale = keyof typeof radius
Expand Down
18 changes: 13 additions & 5 deletions frontend/common/types/responses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1211,14 +1211,22 @@ export type Res = {
releasePipeline: SingleReleasePipeline
pipelineStages: PagedResponse<PipelineStage>
featureCodeReferences: FeatureCodeReferences[]
featureAnalytics: ({
day: string
} & {
[environmentId: string]: number
})[]
featureAnalytics: {
chartData: ({
day: string
} & {
[environmentId: string]: number
})[]
rawEntries: Res['environmentAnalytics']
}
environmentAnalytics: {
day: string
count: number
labels?: {
user_agent?: string | null
client_application_name?: string | null
client_application_version?: string | null
} | null
}[]
featureList: {
results: ProjectFlag[]
Expand Down
102 changes: 102 additions & 0 deletions frontend/documentation/TokenReference.generated.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,108 @@ export const AllTokens: StoryObj = {
</tr>
</tbody>
</table>
<h3>Chart colours</h3>
<table className='docs-table'>
<thead>
<tr>
<th>Token</th>
<th>Value</th>
<th>Usage</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>--color-chart-1</code>
</td>
<td>
<code>#0aaddf</code>
</td>
<td>First series in charts. Blue.</td>
</tr>
<tr>
<td>
<code>--color-chart-2</code>
</td>
<td>
<code>#ef4d56</code>
</td>
<td>Second series. Red.</td>
</tr>
<tr>
<td>
<code>--color-chart-3</code>
</td>
<td>
<code>#27ab95</code>
</td>
<td>Third series. Green.</td>
</tr>
<tr>
<td>
<code>--color-chart-4</code>
</td>
<td>
<code>#ff9f43</code>
</td>
<td>Fourth series. Orange.</td>
</tr>
<tr>
<td>
<code>--color-chart-5</code>
</td>
<td>
<code>#7a4dfc</code>
</td>
<td>Fifth series. Purple.</td>
</tr>
<tr>
<td>
<code>--color-chart-6</code>
</td>
<td>
<code>#0b8bb2</code>
</td>
<td>Sixth series. Blue dark.</td>
</tr>
<tr>
<td>
<code>--color-chart-7</code>
</td>
<td>
<code>#e61b26</code>
</td>
<td>Seventh series. Red dark.</td>
</tr>
<tr>
<td>
<code>--color-chart-8</code>
</td>
<td>
<code>#13787b</code>
</td>
<td>Eighth series. Green dark.</td>
</tr>
<tr>
<td>
<code>--color-chart-9</code>
</td>
<td>
<code>#fa810c</code>
</td>
<td>Ninth series. Orange dark.</td>
</tr>
<tr>
<td>
<code>--color-chart-10</code>
</td>
<td>
<code>#6837fc</code>
</td>
<td>Tenth series. Purple dark.</td>
</tr>
</tbody>
</table>
<h3>Radius</h3>
<table className='docs-table'>
<thead>
Expand Down
Loading
Loading