Skip to content

Blocks V3: stop marking the post as changed on load - #571

Open
faisalahammad wants to merge 2 commits into
WordPress:trunkfrom
faisalahammad:fix/556-blocks-v3-dirty-on-load
Open

faisalahammad wants to merge 2 commits into
WordPress:trunkfrom
faisalahammad:fix/556-blocks-v3-dirty-on-load

Conversation

@faisalahammad

Copy link
Copy Markdown

Opening a post or template with a Blocks V3 block marked it as changed, with no edit. This came from two updates SCF made to every block on load:

  1. hasAcfError was set on every mount, as a persistent change, even when it was already falsy.
  2. The block form's first serialization was written back into data. Stored data is keyed by field name (with _name → field key) and the form serializes by field key, so the string compare never matched and the same values were written again after the first click anywhere in the editor.

What changed

  • block-edit.js
    • hasAcfError is only set when it changes. That update is marked not persistent and sends only { hasAcfError }, so the next real edit is not folded into it (isUpdatingSameBlockAttribute).
    • generateAttributesHash() hashes a copy. Before, it deleted hasAcfError and added _acf_context on the store's own attributes object.
    • The form's serialized state is kept when the form mounts. Until the first real write, a serialization equal to that state does not call setAttributes, it only refreshes the preview. This compares the form with itself, so no name to key mapping or line break handling is needed, and it works for repeaters and groups too.
    • Small serializeBlockForm() helper, used by the three places that serialized the form.
  • block-form.js
    • New optional onUntouchedChange prop. A form change before any interaction (fields setting up) is reported once, so the kept state follows it.
    • Native input / change events now count as interaction, so an edit like a context menu paste is never taken as setup.

How to test

  1. Register a V3 block with a few fields (text, textarea, WYSIWYG, select, true/false). The block in tests/e2e/plugins/scf-test-v3-block.php works.
  2. Add it to a page, fill the fields, save.
  3. Reload the editor and do nothing. Run wp.data.select( 'core/editor' ).isEditedPostDirty() in the console. Before: true. After: false.
  4. Click the canvas, select the block, open and close the Expanded Editor, click into a field without typing. Still false, and leaving the page shows no "Leave site?" prompt.
  5. Edit any field (typing, WYSIWYG, select, toggle, inline editing, clearing an inline value). The post becomes dirty and the value saves.
  6. Site Editor: a template part holding the block no longer shows "Review 1 change" after opening it.

Jest tests added in block-edit.test.js and block-form.test.js. The new BlockEdit tests fail on trunk and pass with this change. Full suite: 967 passed.

Closes #556

Use of AI Tools

Claude Code was used to help write the fix and tests. I reviewed and tested the changes before submitting.

- Set hasAcfError only when it changes, as a non persistent update
  that sends only that key.
- Hash a copy of the attributes so the store object is not changed.
- Skip writing form data back while the form still has its loaded
  values, compared with the form's own first serialization.
- Count native input and change events in the block form as user
  interaction.

Stored block data is keyed by field name and the form serializes by
field key, so the first form serialization always looked like a change
and was written back, marking the post as dirty with no edit.

Fixes WordPress#556
@github-actions

Copy link
Copy Markdown

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.

Unlinked Accounts

The following contributors have not linked their GitHub and WordPress.org accounts: @zerodigitaluk.

Contributors, please read how to link your accounts to ensure your work is properly credited in WordPress releases.

Core Committers: Use this line as a base for the props when committing in SVN:

Props faisalahammad.

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

A new block has no stored data, so when the form still had its loaded
values the fetch sent no field data and the server treated it as a
first preview, skipping validation. Required fields on new blocks then
no longer blocked publishing.

Send the form values with the fetch, without storing them, so the
server validates them like before.
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.

Blocks V3: opening a post marks it as changed, though nothing was edited

1 participant