Skip to content

Only save global settings the user has entered - #17

Draft
roborourke wants to merge 3 commits into
mainfrom
claude/resolve-open-issue-up4945
Draft

Only save global settings the user has entered#17
roborourke wants to merge 3 commits into
mainfrom
claude/resolve-open-issue-up4945

Conversation

@roborourke

@roborourke roborourke commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Fixes #15.

The bug

The Set as global defaults button built its payload from all three block attributes every time:

saveSite( {
	hubspot_embed_portal_id: newPortalId,
	hubspot_embed_region: newRegion,
	hubspot_embed_business_unit_id: newBusinessUnitId || 0,
} );

A blank field in that panel means "inherit the global default shown as the placeholder", but it was still sent — and it broke in two different ways depending on the field:

  • Business Unit ID was coerced to 0 by || 0, overwriting a previously stored value with an empty one. This is the wipe described in the issue.
  • Portal ID and Region were sent as null. WP_REST_Settings_Controller only deletes an option on null when it has no stored value; once it holds one, it bails out with rest_invalid_stored_value and a 500 — and because it returns on the first offending option, nothing in the request is saved, including the value the user was actually trying to set. (CI confirmed this: my first attempt at the tests used null to reset the options and got The hubspot_embed_portal_id property has an invalid stored value, and cannot be updated to null.)

Region had a third problem: its block attribute defaults to eu1, so promoting any global on a site whose region was na1 sent eu1 over the top of it.

The fix

Track which of the global fields the user actually edited, and build the saveSite payload from only those that hold a value:

  • Each global can be saved on its own; the others are not sent at all, so they keep their stored values and the request no longer aborts.
  • A blank field is never submitted, so it cannot overwrite a saved global — the placeholder keeps showing the inherited value.
  • The button is hidden unless there is something to promote (including after typing into a field and clearing it again).
  • Only the overrides that were actually promoted get cleared from the block attributes.

Also fixed the two editor notices, which checked the global portal ID alone and told the user to "enter a Portal ID" even when the block supplied its own — they now fall back the same way render.php does.

Testing

tests/global-settings.spec.js covers the reported sequence and the region case:

  • Save a Portal ID alone, then a Business Unit ID alone — both globals survive, and the Portal ID field falls back to showing the saved value as its placeholder.
  • Promoting a Portal ID on a site with region = na1 leaves the region alone.
  • The save button only appears once a global value is entered.

npm run lint:js and npm run build pass locally. The Playwright suite needs Playground to download WordPress, which is blocked in my sandbox, so these run in CI only.

Open WordPress Playground Preview

Fixes #15. The "Set as global defaults" button always sent all three
settings, using the block attributes as the source of values. A blank
field means "inherit the global default shown as the placeholder", but it
resolved to null (or 0 for the business unit ID), and the settings REST
endpoint deletes an option when it receives null. Saving a business unit
ID therefore wiped a previously saved portal ID, and saving a portal ID
reset a non-default region back to eu1.

Track which of the global fields the user actually edited and build the
payload from only those that hold a value, so each global can be saved on
its own without disturbing the others. The button is now hidden unless
there is something to promote, and only the overrides that were promoted
are cleared from the block attributes.

Also fall back to the block-level portal ID in the two editor notices,
which previously looked at the global value alone and told the user to
enter a portal ID even when the block supplied one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DWkBq1aQNvFyvGdXpJUa39
@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown

Playwright E2E Test Results (PHP 8.4, WP latest)

passed  17 passed
flaky  2 flaky

Details

stats  19 tests across 5 suites
duration  3 minutes, 51 seconds
commit  b671390

Flaky tests

chromium › form-render.spec.js › HubSpot Form — frontend render › should render form container with correct data attributes
chromium › persist-success.spec.js › HubSpot Form — persist success › should pre-swap the container on repeat visit, stripping the first-submission group

Writing null to a registered setting only deletes the option when it has
no stored value; once it holds one, the settings endpoint bails out with
rest_invalid_stored_value and a 500. The spec's beforeEach/afterEach hit
that and failed all three tests before reaching their assertions.

Use 0 for the two ID options instead. It is always a valid value, and both
the plugin and the editor already treat it as unset.

Correct the comment in edit.js to describe the same behaviour accurately:
sending a blank field as null aborted the whole save rather than deleting
the option, while the `|| 0` coercion overwrote a stored global.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DWkBq1aQNvFyvGdXpJUa39
github-actions Bot added a commit that referenced this pull request Aug 11, 2026
github-actions Bot added a commit that referenced this pull request Aug 11, 2026
The regression test drove both saves through the sidebar, so it depended
on the inspector surviving the first one. It does not: the Global Settings
panel's initialOpen is `! defaultPortalId`, which flips once a global
Portal ID is stored, collapsing the panel and unmounting the Portal ID
field. The test then failed with "element(s) not found".

Seed the pre-existing Portal ID through the REST API instead and drive
only the Business Unit ID through the UI. That needs no post-save DOM
interaction, and is a closer reproduction of the report, where the Portal
ID was saved in an earlier session.

Split the portal-only save into its own test, and cover the two cases that
were previously asserted mid-save as stable states: a saved global shows
as the field's placeholder, and a block-level Portal ID satisfies the
editor notice when no global is set.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DWkBq1aQNvFyvGdXpJUa39
github-actions Bot added a commit that referenced this pull request Aug 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Updating one global setting unsets another if not also entered

2 participants