Phase 2: Capture Suggest-mode edits as an in-memory overlay#77404
Phase 2: Capture Suggest-mode edits as an in-memory overlay#77404adamsilverstein wants to merge 10 commits into
Conversation
|
Size Change: +2.84 kB (+0.04%) Total Size: 7.51 MB 📦 View Changed
|
edf102e to
fa199f5
Compare
f78a787 to
3ca48fe
Compare
8043af8 to
b1af20a
Compare
3ca48fe to
4bdfdda
Compare
6ae1fa6 to
b09c96d
Compare
|
Flaky tests detected in 85c543b. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/28125974011
|
|
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 If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
84b0935 to
1ffa344
Compare
25919c8 to
9a2c057
Compare
1ffa344 to
51e0663
Compare
Add the suggestion-overlay subsystem that powers the `suggest` editor
intent added in the previous step. An `editor.BlockEdit` filter wraps
every block; when the intent is `suggest` it diverts `setAttributes`
into an overlay reducer keyed by `clientId` and passes merged
`{ ...real, ...overlay }` attributes into the block for rendering.
The block-editor store is never touched, so autosave, undo, and RTC
sync stay at the real baseline.
A `SuggestionCommitBar` exposes Submit / Discard controls in the
block toolbar when the selected block has a pending overlay. Submit
creates a note comment carrying a versioned `_wp_suggestion` JSON
payload (`blockName`, `baseRevision`, `operations`) via REST; discard
clears the overlay. The `applySuggestion` / `rejectSuggestion` hooks
are stubbed behind the same provider interface so Phase 3 can plug in
without UI changes, and a future Yjs AttributionManager-backed
provider can drop in with the same shape.
PHP-side registers `_wp_suggestion` and `_wp_suggestion_status`
comment meta with REST exposure. Both require `edit_comment`; no new
routes are added.
Refs #73411
Move the baseline-captured signal from a local ref onto the reducer's entry map so Submit/Discard/orphan-prune cleanly resets it — a second edit after the overlay is cleared now reliably captures a new baseline instead of silently no-op'ing on the stale ref. Split the HOC into an outer Suggest-mode check and an inner SuggestingBlockEdit component. The overlay's context lookup, refs, and merge memo only run when intent === suggest; in Edit/View the wrapper executes a single useSelect and returns the original BlockEdit untouched, removing measurable per-render overhead on timing-sensitive e2e specs running on shards 4/5. Add unit coverage for View-intent pass-through and for re-capturing a baseline after the overlay is cleared.
Adds a store-level interceptor for direct updateBlockAttributes calls (block-switcher variation picker bypasses the BlockEdit setAttributes HOC), replaces the JSON.stringify equality with a recursive structural compare so reordered keys in 'style'/'metadata' don't produce spurious 'changed' detections, and rejects oversized suggestion payloads at the sanitize_callback rather than truncating them into invalid JSON. Also gates the orphan-prune subscription behind hasEntries, makes the PRUNE_ORPHANS action serializable for Redux DevTools, mirrors a payload size cap on the client, and adds tests for the new helpers.
…ceptor The store interceptor reverts any direct `updateBlockAttributes` mutation in Suggest mode so user-driven bypasses (e.g. the block-switcher variation picker) can't sneak past the overlay. It was also reverting the provider's programmatic `metadata.noteId` write that links a freshly- created note comment back to its block, leaving every suggestion-derived note orphaned in the sidebar with both "Original block deleted." and "Target block has been deleted." notices. Treat `metadata.noteId` as a system passthrough: fold it from the live attributes into the snapshot before diffing so the value is invisible to the diff and preserved in the revert payload, and strip it from the overlay-bound payload so it never leaks into the user's pending edits. Other metadata keys still go through the regular diff/revert path. Add unit tests for the new helpers and integration tests that register the actual block-editor store, mount the interceptor in Suggest mode, and confirm a programmatic noteId write persists on the live block — both alone and alongside an unrelated user-style content mutation.
The success snackbar was passing 'snackbar' as the first argument to createNotice, but that argument is the notice status. Use 'success' to match the valid status set; the 'snackbar' display style is still applied via the options.type field.
…sign Document the role and lifecycle of the in-memory overlay, the store-interceptor's snapshot/diff/revert strategy, and the comment-meta storage model so reviewers can understand the architecture without reverse-engineering it from the implementation. Also document the note-permission remap on the REST controller subclass. Adds an eslint suppression entry for store-interceptor.js to match the existing pre-existing react-hooks/refs violations now flagged by the project's lint-suppression machinery.
Add lint suppressions for the recently introduced react-hooks/refs violation in with-suggestion-overlay.js and react-hooks/globals violation in its test, matching the pattern already used elsewhere in the suggestion-mode directory.
9a2c057 to
a23ec2d
Compare
…st-mode-phase-2 # Conflicts: # packages/editor/CHANGELOG.md
Overview
This is one of 5 stacked PRs implementing Suggest mode for the WordPress editor — a Google Docs–style workflow where reviewers can propose changes that the post author can Accept or Reject. Tracked in #73411, with design direction from #73410 and jasmussen's mockups.
This PR (Phase 2)
Builds the in-memory suggestion overlay:
SuggestionOverlayProvider— React context that stores pending attribute changes perclientId, plus a baseline snapshot captured on the first edit. The block-editor store is never written.withSuggestionOverlayHOC — registered as aneditor.BlockEditfilter. The HOC is split into an outeruseSelectwrapper and an innerSuggestingBlockEditcomponent: non-Suggest intents run a singleuseSelectand return the wrappedBlockEdituntouched, so the overlay's context lookup, refs, and merge memo add no measurable overhead on timing-sensitive e2e specs.{ ...realAttributes, ...overlayAttributes }for rendering, so the user sees their in-progress change live. Object-valued attributes (style,metadata) are one-level merged so untouched fields survive.What's NOT in this PR
_wp_suggestioncomment meta and REST plumbing (Phase 3)Phase 2 test plan
wp.data.select('core/editor').getEditedPostContent()and confirm the serialized content does not include your suggested textnpm run test:unit -- packages/editor/src/components/suggestion-mode/test/overlay-context.jsnpm run test:unit -- packages/editor/src/components/suggestion-mode/test/with-suggestion-overlay.js🗺️ PR Stack Navigation
_wp_suggestionmeta, provider, sidebar actions📋 Tracking issue: #73411