Skip to content

feat(mv): render variant keys in mono - #8385

Closed
talissoncosta wants to merge 2 commits into
mainfrom
feat/variant-key-monospace
Closed

feat(mv): render variant keys in mono#8385
talissoncosta wants to merge 2 commits into
mainfrom
feat/variant-key-monospace

Conversation

@talissoncosta

@talissoncosta talissoncosta commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Thanks for submitting a PR! Please check the boxes below:

  • 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

Follows @khvn26's suggestion on #8372, that variant keys read as prose when they are actually identifiers.

The premise holds: MultivariateFeatureOption.key is declared as "A stable, human-readable identifier for the variant", is validated with validate_slug, and util/mappers/engine.py:189 carries it into the environment document, so local evaluation SDKs match on it. Body type hides the differences that matter when someone transcribes one into code: 0 against O, l against 1, a stray trailing character.

  • New VariantKey component. Resolves key or its Variant_n fallback and sets it in mono. That fallback was duplicated at every call site.
  • Used at the four surfaces that render a key: the identity override selector, the feature editor label, VariationTable and RolloutSplitEditor.
  • rollout.ts:93 builds a key into a plain label string for a chart legend, so it is untouched.

Uses Bootstrap's .font-monospace, which this build already generates: 3rdParty/_bootstrap.scss:12 imports ~bootstrap/scss/root so --bs-font-monospace is defined, line 71 imports the utilities API, and the map-removals below it only touch color, border-color and shadow, so the font-family key survives.

Deliberately not here

A --font-family-mono token. I built it first and backed it out. There are already two mono stacks in the tree, Bootstrap's $font-family-monospace and a hardcoded copy at styles.scss:74, and they differ over SFMono-Regular. A token copied from either would have been a third. Worth settling separately by pointing $font-family-monospace at one agreed stack, which is also the right moment to close the gap where generate-tokens.mjs emits utilities for colour, radius and shadow but not font-weight, duration or easing.

The rollout summary legend. RolloutSummary.tsx:69 renders a variant key too, from rollout.ts:93. It is not a JSX swap like the other four: RolloutSummaryRow is { label, percentage } with no way to mark a label as a key, the legend mixes keys with a prose Control entry, and the label doubles as the React key and as barSegments[].key. Covering it means changing that type and threading it through getTrafficSegments, which is shared chart plumbing and reviews differently to this.

Copyable. It adds a new interaction in four places, with an icon, a tooltip and a copied state, and @khvn26 parked it as not important for the immediate fix. Cheaper to add to this one component later than to four call sites now.

How did you test this code?

Manually, across all four surfaces: the Edit User Feature modal for a multivariate flag, the feature editor's variant labels, and the two experiments views. Variants with no key set still fall back to Variant_n.

npx tsc --noEmit reports only errors already on main. eslint --fix reports nothing.

@vercel

vercel Bot commented Aug 26, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
flagsmith-frontend-preview Ready Ready Preview Aug 26, 2026 4:09pm
flagsmith-frontend-staging Ready Ready Preview Aug 26, 2026 4:09pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Ignored Ignored Preview Aug 26, 2026 4:09pm

Request Review

@coderabbitai

coderabbitai Bot commented Aug 26, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Added the exported VariantKey component with shared default-key handling. Updated four consumers to render variant keys through this component. Existing values, indexes, CSS classes, and test identifiers remain passed to the rendered element.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: 🔵 Low · up to 6fa10

This change only updates how variant keys are presented. The remaining bounded risk is repository consistency and maintainability: add the required component stylesheet, name the nullable value type, and use the configured import alias. No concrete user or production correctness risk is identified, so it is mergeable with owner follow-up.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

A variant key is an identifier, not prose. It is validated as a slug, and
util/mappers/engine.py carries it into the environment document, so local
evaluation SDKs match on it. Rendering it in body type hides the differences
that matter when someone transcribes it into code: 0 against O, l against 1,
a stray trailing character.

Adds a VariantKey component that resolves the key or its Variant_n fallback
and sets it in mono, then uses it at the four surfaces that show one: the
identity override selector, the feature editor label, and the two experiments
views. The fallback was duplicated at each of them.

Uses Bootstrap's font-monospace, which this build already generates. A new
token would have been a third mono stack in the tree, disagreeing with
Bootstrap's over SFMono-Regular. Settling that is its own change.

Copyable is left for a follow-up.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
tsconfig sets jsx: react-jsx, so the automatic runtime covers it. Matches
VariationTable next door.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 134b5d25-fce9-428e-9ecc-4317acda6dfb

📥 Commits

Reviewing files that changed from the base of the PR and between 2fc223a and 6fa1078.

📒 Files selected for processing (6)
  • frontend/web/components/experiments/RolloutSplitEditor/RolloutSplitEditor.tsx
  • frontend/web/components/experiments/VariationTable/VariationTable.tsx
  • frontend/web/components/mv/VariantKey/VariantKey.tsx
  • frontend/web/components/mv/VariantKey/index.ts
  • frontend/web/components/mv/VariationKeyLabel/VariationKeyLabel.tsx
  • frontend/web/components/mv/VariationOptions.tsx

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.

Comment on lines +5 to +9
interface VariantKeyProps
extends Omit<ComponentPropsWithoutRef<'span'>, 'children'> {
// The variant's own key, absent when the user never set one.
value?: string | null
index: number

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Extract the nullable value type into a named type.

VariantKeyProps.value declares an inline union. Define a named type and use it for the property.

Proposed change
+type VariantKeyValue = string | null
+
 interface VariantKeyProps
   extends Omit<ComponentPropsWithoutRef<'span'>, 'children'> {
-  value?: string | null
+  value?: VariantKeyValue

As per coding guidelines, TypeScript files must use named union types.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
interface VariantKeyProps
extends Omit<ComponentPropsWithoutRef<'span'>, 'children'> {
// The variant's own key, absent when the user never set one.
value?: string | null
index: number
type VariantKeyValue = string | null
interface VariantKeyProps
extends Omit<ComponentPropsWithoutRef<'span'>, 'children'> {
// The variant's own key, absent when the user never set one.
value?: VariantKeyValue
index: number

Source: Coding guidelines

Comment on lines +14 to +23
export const VariantKey: FC<VariantKeyProps> = ({
className,
index,
value,
...rest
}) => (
<span className={classNames('font-monospace', className)} {...rest}>
{value || getDefaultVariantKey(index)}
</span>
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Add the required co-located stylesheet.

The new VariantKey component folder contains no VariantKey.scss file. Add the co-located stylesheet required for new components before merging.

As per coding guidelines, each new component must include a co-located ComponentName.scss file.

Source: Coding guidelines

import Utils from 'common/utils/utils'
import { FlagsmithValue, MultivariateOption } from 'common/types/responses'
import { UnmatchedOverride } from 'common/utils/multivariate'
import { VariantKey } from './VariantKey'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Use the configured component alias.

This frontend consumer uses a relative import. Import VariantKey through components/mv/VariantKey.

Proposed change
-import { VariantKey } from './VariantKey'
+import { VariantKey } from 'components/mv/VariantKey'

As per coding guidelines, frontend files must use common/, components/, or project/ import paths instead of relative imports.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { VariantKey } from './VariantKey'
import { VariantKey } from 'components/mv/VariantKey'

Source: Coding guidelines

@github-actions github-actions Bot added feature New feature or request and removed feature New feature or request labels Aug 26, 2026
@github-actions

github-actions Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Docker builds report

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

@github-actions

github-actions Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor
✅ private-cloud · depot-ubuntu-latest-arm-16 — run #19855 (attempt 1)

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

passed  2 passed

Details

stats  2 tests across 2 suites
duration  1 minute
commit  6fa1078
info  🔄 Run: #19855 (attempt 1)

🗂️ Previous results
✅ private-cloud · depot-ubuntu-latest-16 — run #19855 (attempt 1)

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

passed  1 passed

Details

stats  1 test across 1 suite
duration  37.3 seconds
commit  6fa1078
info  🔄 Run: #19855 (attempt 1)

✅ oss · depot-ubuntu-latest-16 — run #19855 (attempt 1)

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

passed  1 passed

Details

stats  1 test across 1 suite
duration  41.1 seconds
commit  6fa1078
info  🔄 Run: #19855 (attempt 1)

✅ oss · depot-ubuntu-latest-arm-16 — run #19855 (attempt 1)

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

passed  1 passed

Details

stats  1 test across 1 suite
duration  42.2 seconds
commit  6fa1078
info  🔄 Run: #19855 (attempt 1)

@github-actions

Copy link
Copy Markdown
Contributor

Visual Regression

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

@talissoncosta
talissoncosta marked this pull request as draft August 27, 2026 20:13
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.

1 participant