Skip to content

Blocks V3: keep undo and redo working for block fields - #578

Open
faisalahammad wants to merge 3 commits into
WordPress:trunkfrom
faisalahammad:fix/577-blocks-v3-undo-redo
Open

faisalahammad wants to merge 3 commits into
WordPress:trunkfrom
faisalahammad:fix/577-blocks-v3-undo-redo

Conversation

@faisalahammad

Copy link
Copy Markdown
Contributor

Undo and redo did not work for Blocks V3 fields. Ctrl+Z reverted the block's data attribute, but the V3 form never follows data. About 200 ms later the form wrote its own values back, which undid the undo and cleared the redo stack. The history then got stuck at the SCF block, so edits made before it in other blocks could not be undone either.

Depends on #571. This branch is stacked on it, so the first two commits here are from #571. The change for this issue is the last commit, 83b2e80. I will rebase once #571 is merged.

What changed

  • block-edit.js
    • SCF now tells a form edit apart from a data change made outside the form (undo, redo or a script). On an outside change it no longer writes the form back. It fetches the form and preview for the current data and re-mounts BlockForm with a key. Focus and caret go back to the field that had them, unless focus has moved somewhere else.
    • New isSameBlockData() compares stored data with the form by value. Field names map to keys through the _name entries, or through the block's fields when those entries are missing.
    • An edit typed while the new form is loading wins, and the reload is dropped.
  • block-form.js
    • New isReloaded prop. A re-mounted form only counts edits made in it, so the Ctrl+Z that caused the reload is not taken as an edit, and field setup in the new form is not written back.
    • Runs the unmount action when the form goes away, so fields like WYSIWYG clean up. V2 already did this.
  • _acf-fields.js
    • _acf_changed is written with { undoIgnore: true }. Before, it added an empty undo step, so the first click on the header Undo button did nothing.

How to test

  1. Activate the test block from tests/e2e/plugins/scf-test-v3-block.php. Add a paragraph and an "SCF V3 Block" with the title Hello to a post. Save and reload.
  2. Select the block, type XYZ at the end of the Title field in the sidebar, then wait a second.
  3. Press Ctrl+Z. Before: the field shows Hello for a moment, then HelloXYZ comes back and redo does nothing. After: the field, the preview and data stay at Hello, and focus stays in the field.
  4. Press Ctrl+Shift+Z. HelloXYZ comes back. Type !, and the new edit is kept.
  5. Type in the paragraph, then in the SCF field, then press Ctrl+Z three times. Both edits are undone in order and redo brings them back.
  6. Click the SCF block without editing it, then type in the paragraph. Ctrl+Z still undoes the paragraph edits.
  7. Run wp.data.dispatch( 'core/block-editor' ).updateBlockAttributes() on the block with a new data value. The sidebar field and the preview show the new value, and the value is not overwritten.
  8. Regression: normal edits in text, textarea and true/false fields still save. Opening a post without editing still leaves it clean (Blocks V3: opening a post marks it as changed, though nothing was edited #556).

Jest tests added in block-edit.test.js and block-form.test.js. The new undo tests fail without this change. Full suite: 980 passed.

Known limits:

  • Each undo or redo that reaches an SCF block reloads that block's form from the server, so the field updates after a short delay.
  • Pressing Ctrl+Z within 200 ms of the last keystroke can still lose redo, because that keystroke is not stored yet.
  • Focus is not restored inside WYSIWYG or select2 fields.
  • Blocks V2 is not changed. I will open a separate issue for it.

Closes #577

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
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.
- When the block data changes from outside the form (undo, redo or
  a script), load a new form for that data and re-mount it, instead
  of writing the old form values back.
- Compare stored data with the form by value, mapping field names to
  keys through the "_name" entries or the block's fields.
- Keep an edit typed while the new form is loading, and put focus back
  in the field that had it.
- Only count edits made in a re-mounted form, and run the "unmount"
  action when the form goes away so fields like WYSIWYG clean up.
- Write _acf_changed with undoIgnore so it does not add an empty undo
  step.

The V3 form never followed the data attribute. After Ctrl+Z it wrote
its own values back about 200 ms later, which undid the undo and
cleared the redo stack.

Fixes WordPress#577
@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: @wojtekpiskorz.

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.

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: undo and redo don't work for SCF block fields, and get stuck at SCF blocks

1 participant