Add a reusable donut chart for the AI Governance dashboard

Adds a reusable donut chart for the AI Governance dashboard, matching the two shapes in the GA prototype. No card mounts it yet, so nothing renders in the product.

Why now

The prototype uses two donuts, "Which AI agents developers are using" and "Tool calls by risk". Building the component first keeps the cards that adopt it small, and settles the shapes before the aggregates behind them exist.

What it covers

  • Many slices, with a vertical legend beside the donut. Centre shows a headline number and a caption.
  • Few slices, with a single legend row below, each entry showing its percentage inline.
  • Colour is keyed on the slice name rather than its rank, so filtering does not repaint the slices that remain.
  • Percentages use largest-remainder rounding, so a legend never reads 99% or 101%.
  • The tooltip stays inside the chart instead of floating off the edge of the card.
  • Empty, loading, long-label and dark-mode states.
  • A screen-reader table beside the chart, and the legend is always rendered, so identity is never colour alone.

Mock data for all of the above is in the Storybook story.

Screenshots

Light Dark
Default 01-default 07-default-dark
Legend bottom 03-legend-bottom
Tooltip 06-tooltip-hover 09-tooltip-dark
All nine slices 02-legend-right-all-nine
"Other" folding 04-other-folding
Empty state 05-empty

Differences from the prototype

  • It folds into "Other" after 5 slices by default. maxSlices is configurable.
  • Colours come from the categorical palette, assigned by name, not by rank.
  • There is a 2px gap between slices, per Pajamas.
  • The centre number is slightly smaller than in the prototype.

Colour, and one open question for design

Colour comes from GitLab's shared categorical palette, colorFromDefaultPalette and colorFromDarkPalette in @gitlab/ui/src/utils/charts/theme, the same helpers sectioned_percentage_bar.vue uses. Nothing is hardcoded, and slices without an explicit colour take the dark palette in dark mode rather than reusing the light one.

maxSlices defaults to 5 and folds the remainder into a neutral "Other". The cap allows up to the palette length, but 5 is the number I would keep, for a reason worth recording:

The palette carries 30 colours, but it is five hues cycled through lightness steps. Past the fifth slice you re-enter the same hues darker, and at nine slices two pairs stop being distinguishable: #275600 against #6f3500 measures ΔE 0.7 under deuteranopia, and #006381 against #3f51ae measures 10.5 for normal vision, under the floor of 15. In a nine-slice render the three smallest slices were 4%, 2% and 1%, slivers where colour carries nothing regardless.

So for "which agents are developers using", where the tail is arguably the interesting part, a horizontal bar list is likely the better form. That is a design call, not a blocker for this component.

Recent fixes

  • The spec checks the neutral "Other" colour against its design token instead of a hex value that had drifted.
  • The tooltip is confined to the chart.
  • The centre value and caption use the GitLab chart font.
  • A single slice, such as the empty state, renders as a closed ring with no notch.

Feature flag

Nothing mounts it in this MR, so there is no flag here. The first card to use it is Add 'Which AI agents developers are using' card... (!258337 - merged) (session distribution card), which adds the flag, because an unused flag definition fails rspec:feature-flags.

Related to https://gitlab.com/gitlab-org/gitlab/-/work_items/629071

Edited by Dheeraj Joshi

Merge request reports

Loading
Loading