Skip to content

Use toggle UI, auto-save, and inline DataForm settings for experiments#376

Merged
dkotter merged 46 commits intodevelopfrom
feature/settings-toggle-autosave
Apr 9, 2026
Merged

Use toggle UI, auto-save, and inline DataForm settings for experiments#376
dkotter merged 46 commits intodevelopfrom
feature/settings-toggle-autosave

Conversation

@jorgefilipecosta
Copy link
Copy Markdown
Member

@jorgefilipecosta jorgefilipecosta commented Apr 7, 2026

Summary

  • Replace checkbox controls with toggle switches on the AI settings page
  • Auto-save settings on each toggle change instead of requiring a manual "Save Changes" button click
  • Show contextual snackbar notices (e.g. "AI enabled.", "Content Classification disabled.")
  • Allow experiments to declare custom settings fields via get_settings_fields() on Abstract_Feature, rendered as inline DataForms below the experiment toggle when enabled
  • Migrate Content Classification's custom settings (taxonomy strategy, max suggestions) from server-rendered HTML to DataForm fields with show_in_rest support
  • Custom experiment settings use a separate Save button that only appears when changes are pending

Test plan

  • Visit the AI settings page and verify toggles render instead of checkboxes
  • Toggle a setting and verify it auto-saves with a contextual snackbar message
  • Disable the global AI toggle and verify feature toggles become disabled
  • Enable Content Classification and verify the strategy and max suggestions fields appear inline below the toggle
  • Disable Content Classification and verify the sub-settings disappear
  • Change the taxonomy strategy, verify the Save button appears, click it, and confirm the snackbar and value persistence on reload
  • Verify /wp/v2/settings/ REST API returns wpai_feature_content-classification_field_strategy and wpai_feature_content-classification_field_max_suggestions
  • Run E2E tests: npm run test:e2e -- --grep "Plugin settings" and npm run test:e2e -- --grep "Content Classification"
Open WordPress Playground Preview

Use the DataForm 'toggle' field type and ToggleControl component
for a more intuitive on/off UI on the AI settings page.
Remove manual save button clicks from test helpers since settings
now auto-save on toggle.
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 7, 2026

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: jorgefilipecosta <jorgefilipecosta@git.wordpress.org>
Co-authored-by: dkotter <dkotter@git.wordpress.org>
Co-authored-by: jeffpaul <jeffpaul@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 7, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 63.81%. Comparing base (ffd1eb4) to head (e0bddc2).
⚠️ Report is 58 commits behind head on develop.

Additional details and impacted files
@@              Coverage Diff              @@
##             develop     #376      +/-   ##
=============================================
+ Coverage      62.63%   63.81%   +1.17%     
- Complexity       680      684       +4     
=============================================
  Files             49       49              
  Lines           3501     3529      +28     
=============================================
+ Hits            2193     2252      +59     
+ Misses          1308     1277      -31     
Flag Coverage Δ
unit 63.81% <100.00%> (+1.17%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

dkotter
dkotter previously approved these changes Apr 7, 2026
Copy link
Copy Markdown
Collaborator

@dkotter dkotter left a comment

Choose a reason for hiding this comment

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

Tested and works well for me, noting this also seems to solve the problem reported in #352, which is great.

There are E2E failures here with Content Classification, I'm assuming because the individual settings it needs no longer display, so I think fine to ignore that until we have a PR up to fix that.

That said, I do wonder how this will scale for features that need their own settings (like Content Classification). Is the plan that anytime you change an individual setting we save immediately, similar to how these toggles work? Seems like in that scenario having a save button still may be best

Copy link
Copy Markdown
Member

@jeffpaul jeffpaul left a comment

Choose a reason for hiding this comment

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

Tests real nicely, the toggles and autosave are great to avoid the extra button clicks, thanks! Will await @dkotter review before approval+merge.

@jeffpaul
Copy link
Copy Markdown
Member

jeffpaul commented Apr 7, 2026

lol Darin 🥇, JP 🥈

@jeffpaul
Copy link
Copy Markdown
Member

jeffpaul commented Apr 7, 2026

As a reference, here's what the Content Classification settings bits look like:

Screenshot 2026-03-17 at 4 39 37 PM

ToggleControl associates its label differently than CheckboxControl
in the accessibility tree, so getByRole('checkbox', { name }) fails.
Use getByLabel which works reliably with both control types.
Allow experiments to declare custom settings field definitions
via get_settings_fields() for DataForm rendering. Add
get_settings_fields_metadata() to resolve short field IDs to
full WordPress option names for REST API consumption.
Replace render_settings_fields() HTML with get_settings_fields()
field definitions. Add show_in_rest with proper schemas to
register_settings() so custom fields are accessible via the
WordPress REST API and core-data store.
Pass each feature's custom settings field definitions to the
settings page script data so the frontend can render DataForms
for experiment-specific settings.
Add InlineFeatureSettings component that renders a DataForm with
custom fields below the experiment toggle when enabled. Uses local
state with a Save button that only appears when changes are pending.
The sub-settings appear inline within the same card as the toggle.
Navigate to the new settings page URL and use label-based
selectors with snackbar confirmation instead of the old form
submission approach.
@jorgefilipecosta
Copy link
Copy Markdown
Member Author

Hi @jeffpaul, @dkotter,
I included a change in this PR to add additional settings field:
Screenshot 2026-04-08 at 18 08 55

When there are changes it renders a save button for the setting with inner changes:
image

Enabling disabling is still immediate.

@jorgefilipecosta jorgefilipecosta changed the title Use toggle UI and auto-save for settings page Use toggle UI, auto-save, and inline DataForm settings for experiments Apr 8, 2026
Cover get_settings_fields(), get_settings_fields_metadata(), and
settingsFields in bootstrap feature metadata output. Includes tests
for Content_Classification field declarations and show_in_rest.
The enableExperiment helper uses page.getByLabel() which matches
against the toggle's display label, not the slug. Updated the
constant to use 'Content Classification' matching all other tests.
@jeffpaul jeffpaul requested a review from dkotter April 8, 2026 17:57
Move the settings data lookup to a ref so the callback identity
stays stable across saves, avoiding unnecessary DataForm
re-renders caused by the data dependency changing on every
settings update.
Reset localEdits when the feature id changes so stale keys from a
previous field definition do not persist into the new one.
Replace coordinated mutable state (savingRef, dataRef, localEdits,
editComponentsRef) with core-data's built-in dirty tracking. Toggle
auto-save now uses __experimentalSaveSpecifiedEntityEdits to persist
only the toggled key, so rapid clicks are never dropped and inline
drafts are never flushed. InlineFeatureSettings writes directly to
the entity record store and saves only its own field IDs.

Update E2E helpers to assert snackbar text instead of test ID.
@jeffpaul
Copy link
Copy Markdown
Member

jeffpaul commented Apr 8, 2026

Content Classification settings work as expected, though the horizontal line and ALL CAPS for sub-experiment sections feels off from a UI perspective. Any chance we can remove the separator, switch to Title Case, and left align the sub-experiment settings with the experiment title & description?

The inline settings Save button was clickable while saving because it
only had isBusy without disabled. Also, isSavingEntityRecord('root',
'site') fired for any site entity save including toggle auto-saves,
causing false spinner activation.

Replace the store-level isSaving with local useState so the button
only reflects the inline settings save, and add disabled={isSaving}
to prevent concurrent requests.
…a11y

Output DataForm-compatible field definitions from PHP (use 'text' type,
nest min/max under isValid) so the frontend can pass them through without
transformation. Remove parseSettingsField and the type-mapping useMemo.

Replace the module-level Map component cache with a single stable
FeatureToggleWithSettings component that looks up its feature via an
immutable FEATURES_BY_SETTING Map.

Add aria-label to the inline Save button so screen readers can
distinguish between multiple feature settings forms.
@jeffpaul jeffpaul mentioned this pull request Apr 8, 2026
31 tasks
…dent

- Remove border-top separator between toggle and sub-settings
- Override Emotion text-transform: uppercase on BaseControl and
  InputControl labels with !important
- Indent sub-settings with padding-left to align under toggle label
- Constrain form width with max-width to prevent overly wide inputs
@jorgefilipecosta
Copy link
Copy Markdown
Member Author

Content Classification settings work as expected, though the horizontal line and ALL CAPS for sub-experiment sections feels off from a UI perspective. Any chance we can remove the separator, switch to Title Case, and left align the sub-experiment settings with the experiment title & description?

Hi @jeffpaul, I tried to address your feedback:
image

jeffpaul
jeffpaul previously approved these changes Apr 8, 2026
Extract MIN_SUGGESTIONS and MAX_SUGGESTIONS class constants in
Content_Classification to eliminate duplicated magic numbers across
register_settings, get_settings_fields, and sanitize_max_suggestions.

Move the DisabledToggle no-op onChange handler to a module-level
constant to avoid allocating a new function on every render.
Copy link
Copy Markdown
Collaborator

@dkotter dkotter left a comment

Choose a reason for hiding this comment

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

There may still be work being done here but just noting there's a failing eslint check and E2E tests are also still failing

@jorgefilipecosta
Copy link
Copy Markdown
Member Author

There may still be work being done here but just noting there's a failing eslint check and E2E tests are also still failing

Hi @dkotter it was from in progress work, everything is green now.

@dkotter dkotter merged commit aa6409b into develop Apr 9, 2026
19 of 20 checks passed
@dkotter dkotter deleted the feature/settings-toggle-autosave branch April 9, 2026 13:33
@github-project-automation github-project-automation bot moved this from Needs review to Done in WordPress AI Planning & Roadmap Apr 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

3 participants