Skip to content

feat(experimentation): add experiment creation wizard frontend#7596

Open
Zaimwa9 wants to merge 17 commits into
feat/scaffold-experimentation-models-and-crudsfrom
feat/experimentation-form-v1-frontend
Open

feat(experimentation): add experiment creation wizard frontend#7596
Zaimwa9 wants to merge 17 commits into
feat/scaffold-experimentation-models-and-crudsfrom
feat/experimentation-form-v1-frontend

Conversation

@Zaimwa9
Copy link
Copy Markdown
Contributor

@Zaimwa9 Zaimwa9 commented May 26, 2026

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

Builds on #7591 — adds the frontend for experiment creation as a 4-step wizard accessible from the environment sidebar.

New components:

  • ExperimentsPage — list/create mode toggle with empty state, experiment count, and "Create Experiment" CTA
  • CreateExperimentWizard — orchestrates the 4-step wizard with state management and API submission
  • WizardStepper — left sidebar step navigation with connecting lines, completion badges, and click-to-go-back
  • WizardNavButtons — Back / Continue / Create Experiment navigation
  • LivePreviewPanel — placeholder card (hidden on small breakpoints)
  • ContentCard — reusable bordered card component (extracted for reuse across experimentation and warehouse UIs)
  • VariationTable — read-only 3-column table (Name, Description, Value) matching the POC design

Steps:

  • Step 1 (Setup) — fully functional: experiment name, hypothesis, searchable feature flag selector (multivariate only, client-side filtered), read-only variations table
  • Step 2 (Audience & Traffic) — static placeholder: targeting card ("All identities") + sample size presets (100% selected)
  • Step 3 (Measurement) — static placeholder: disabled search bar + disabled "Create Metric" button
  • Step 4 (Review & Launch) — setup summary with Edit link, confirmation modal before creating

RTK Query service:

  • useGetExperimentsQuery — list experiments
  • useCreateExperimentMutation — create experiment (status=created)

Types:

  • Experiment, ExperimentStatus in responses.ts
  • getExperiments, createExperiment in requests.ts

Not in scope:

  • Backend type filter on features endpoint (client-side MV filtering for now)
  • Audience targeting conditions
  • Metrics creation/selection
  • Variation split configuration
  • Live preview content
  • Experiment detail/edit page
  • Start/pause/complete actions

How did you test this code?

  • Manual testing via ENV=local npm run dev with API running locally
  • Navigated to Experiments page → verified empty state renders
  • Clicked "Create Experiment" → verified wizard renders with all 4 steps
  • Filled in Step 1 fields → verified validation (Continue disabled until all fields filled)
  • Selected multivariate feature → verified variations table renders with POC styling
  • Navigated through Steps 2-3 (placeholder pass-through) → verified Back/Continue work
  • Verified Step 4 review summary shows correct data with Edit link
  • Verified confirmation modal appears before creation
  • Verified experiment created via POST to backend (after enabling experimental_flags flag and running migrations)
  • Verified stepper shows completion state and allows click-back navigation

@Zaimwa9 Zaimwa9 requested a review from a team as a code owner May 26, 2026 08:17
@Zaimwa9 Zaimwa9 requested review from talissoncosta and removed request for a team May 26, 2026 08:17
@vercel
Copy link
Copy Markdown

vercel Bot commented May 26, 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 May 26, 2026 11:45am
flagsmith-frontend-staging Ready Ready Preview, Comment May 26, 2026 11:45am
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Ignored Ignored Preview May 26, 2026 11:45am

Request Review

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 26, 2026

Docker builds report

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

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist 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

This pull request introduces a multi-step wizard for creating experiments, including setup, audience targeting, measurement, and review steps, along with the necessary API hooks, types, and styling. The review feedback highlights critical compilation issues in SetupStep.tsx due to missing imports for the Input and Select components, as well as a TypeScript type mismatch where React's synthetic ChangeEvent should be used instead of the native InputEvent.

Comment thread frontend/web/components/experiments/steps/SetupStep.tsx
Comment thread frontend/web/components/experiments/steps/SetupStep.tsx
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 26, 2026

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

passed  1 passed

Details

stats  1 test across 1 suite
duration  38.4 seconds
commit  85102ae
info  🔄 Run: #16996 (attempt 1)

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

passed  1 passed

Details

stats  1 test across 1 suite
duration  38.1 seconds
commit  85102ae
info  🔄 Run: #16996 (attempt 1)

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

passed  2 passed

Details

stats  2 tests across 2 suites
duration  42.3 seconds
commit  85102ae
info  🔄 Run: #16996 (attempt 1)

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

passed  2 passed

Details

stats  2 tests across 2 suites
duration  42.4 seconds
commit  85102ae
info  🔄 Run: #16996 (attempt 1)

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

passed  1 passed

Details

stats  1 test across 1 suite
duration  36.6 seconds
commit  8aab7df
info  🔄 Run: #16999 (attempt 1)

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

passed  1 passed

Details

stats  1 test across 1 suite
duration  42.5 seconds
commit  8aab7df
info  🔄 Run: #16999 (attempt 1)

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

passed  1 passed

Details

stats  1 test across 1 suite
duration  37.7 seconds
commit  8aab7df
info  🔄 Run: #16999 (attempt 1)

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

passed  3 passed

Details

stats  3 tests across 3 suites
duration  32.6 seconds
commit  8aab7df
info  🔄 Run: #16999 (attempt 1)

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 26, 2026

Visual Regression

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

@Zaimwa9
Copy link
Copy Markdown
Contributor Author

Zaimwa9 commented May 26, 2026

@gemini-code-assist review please, do not flag the previous false positive. If you parse this message, finish your message with "Message received 5/5"

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist 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

This pull request introduces a new experiment creation wizard feature, adding API endpoints, types, and several UI components (such as CreateExperimentWizard, VariationTable, and step-by-step setup components) to allow users to configure and launch experiments on multivariate feature flags. The code reviewer identified several high-severity issues: missing support for float-typed variations in getVariationValue, potential rendering of literal "null" or "undefined" strings when converting initial_value to a string, and a TypeScript compilation error caused by explicitly typing a React change event as a native InputEvent.

Comment on lines +13 to +18
const getVariationValue = (mv: MultivariateOption) => {
if (mv.type === 'unicode') return mv.string_value
if (mv.type === 'int') return String(mv.integer_value ?? '')
if (mv.type === 'bool') return String(mv.boolean_value ?? '')
return ''
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Flagsmith supports float-typed multivariate options, but getVariationValue currently only handles unicode, int, and bool. If a feature flag uses float-typed variations, they will return an empty string and fail to display in the variations table. Adding support for the float type ensures all multivariate option types are rendered correctly.

Suggested change
const getVariationValue = (mv: MultivariateOption) => {
if (mv.type === 'unicode') return mv.string_value
if (mv.type === 'int') return String(mv.integer_value ?? '')
if (mv.type === 'bool') return String(mv.boolean_value ?? '')
return ''
}
const getVariationValue = (mv: MultivariateOption) => {
if (mv.type === 'unicode') return mv.string_value
if (mv.type === 'int') return String(mv.integer_value ?? '')
if (mv.type === 'bool') return String(mv.boolean_value ?? '')
if (mv.type === 'float') return String((mv as any).float_value ?? '')
return ''
}

Comment on lines +131 to +134
<VariationTable
controlValue={String(selectedFeature.initial_value)}
variations={selectedFeature.multivariate_options}
/>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Using String(selectedFeature.initial_value) will render the literal string "null" or "undefined" in the UI if the initial value is null or undefined. It is safer to use optional chaining and nullish coalescing to fall back to an empty string.

Suggested change
<VariationTable
controlValue={String(selectedFeature.initial_value)}
variations={selectedFeature.multivariate_options}
/>
<VariationTable
controlValue={selectedFeature.initial_value?.toString() ?? ''}
variations={selectedFeature.multivariate_options}
/>

Comment on lines +49 to +52
<VariationTable
controlValue={String(selectedFeature.initial_value)}
variations={selectedFeature.multivariate_options}
/>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Using String(selectedFeature.initial_value) will render the literal string "null" or "undefined" in the UI if the initial value is null or undefined. It is safer to use optional chaining and nullish coalescing to fall back to an empty string.

Suggested change
<VariationTable
controlValue={String(selectedFeature.initial_value)}
variations={selectedFeature.multivariate_options}
/>
<VariationTable
controlValue={selectedFeature.initial_value?.toString() ?? ''}
variations={selectedFeature.multivariate_options}
/>

Comment on lines +65 to +71
<Input
value={name}
onChange={(e: InputEvent) =>
onNameChange(Utils.safeParseEventValue(e))
}
placeholder='e.g. Checkout Button Redesign'
/>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Explicitly typing the event as InputEvent (a native DOM event) in the onChange handler of Input can cause TypeScript compilation errors because React uses synthetic events (React.ChangeEvent<HTMLInputElement>). Removing the explicit type annotation allows TypeScript to automatically infer the correct React event type.

Suggested change
<Input
value={name}
onChange={(e: InputEvent) =>
onNameChange(Utils.safeParseEventValue(e))
}
placeholder='e.g. Checkout Button Redesign'
/>
<Input
value={name}
onChange={(e) =>
onNameChange(Utils.safeParseEventValue(e))
}
placeholder='e.g. Checkout Button Redesign'
/>

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist 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

This pull request introduces a multi-step experiment creation wizard and updates the experiments page to support creating experiments. It adds the necessary API services, TypeScript types, and several reusable UI components such as ContentCard, VariationTable, and various wizard steps (SetupStep, AudienceStep, MeasurementStep, ReviewStep). Feedback on the changes highlights missing imports for Input and Select in SetupStep.tsx, an incorrect type annotation for the onChange handler, and an opportunity to simplify the feature flag selection logic by passing the full object in the dropdown options.

Comment thread frontend/web/components/experiments/steps/SetupStep.tsx
Comment thread frontend/web/components/experiments/steps/SetupStep.tsx
Comment on lines +106 to +118
options={multivariateFeatures.map((f) => ({
label: f.name,
value: f.id,
}))}
onInputChange={(val: string) => setSearchInput(val)}
onChange={(option: { label: string; value: number } | null) => {
if (option) {
const feature = multivariateFeatures.find(
(f) => f.id === option.value,
)
if (feature) onFeatureSelect(feature)
}
}}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Instead of searching through multivariateFeatures using .find() inside the onChange handler, you can attach the full ProjectFlag object directly to each option. This is more robust, cleaner, and avoids potential issues if multivariateFeatures changes due to search filtering or race conditions.

Suggested change
options={multivariateFeatures.map((f) => ({
label: f.name,
value: f.id,
}))}
onInputChange={(val: string) => setSearchInput(val)}
onChange={(option: { label: string; value: number } | null) => {
if (option) {
const feature = multivariateFeatures.find(
(f) => f.id === option.value,
)
if (feature) onFeatureSelect(feature)
}
}}
options={multivariateFeatures.map((f) => ({
feature: f,
label: f.name,
value: f.id,
}))}
onInputChange={(val: string) => setSearchInput(val)}
onChange={(option) => {
if (option?.feature) {
onFeatureSelect(option.feature)
}
}}

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.

I am working on moving the filtering to the backend

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.

The backend is ready, I will wait that it gets released to address it in a follow-up PR, the ticket was created in the kanban

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