fix: normalize data store attributes to plain strings in JS bridge#429
Draft
fix: normalize data store attributes to plain strings in JS bridge#429
Conversation
974d1bb to
abb6056
Compare
The WordPress data store's `getEditedPostContent()` may return
`{raw, rendered}` objects instead of plain strings because it uses
`getEditedEntityRecord` (which preserves the object shape) rather than
`getRawEntityRecord` (which extracts `.raw`).
Add `normalizeAttribute()` to always extract the raw string before
returning values to the native host via `getTitleAndContent()`.
Also remove the redundant `getContent()` bridge method and its iOS
public API — `getTitleAndContent()` is the single accessor for editor
state.
abb6056 to
9c68308
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes a bug where
editor.getTitleAndContent()could return JavaScript objects instead of plain strings to the native host app, corrupting the content in the host app.Root Cause
WordPress's
getEditedPostContent()selector usesgetEditedEntityRecord()internally, which preserves the raw entity record shape. When a post is loaded, title and content are stored as{ raw: "..." }objects in the entity record. Most selectors (likegetEditedPostAttribute('title')) go throughgetRawEntityRecord(), which extracts.rawautomatically. ButgetEditedPostContent()does not — it hits a fallback path (else if (record.content) { return record.content; }) that returns the{ raw }wrapper object as-is.This means:
getEditedPostAttribute('title')→ already returns a plain string (goes throughgetRawEntityRecord+getPostRawValue)getEditedPostContent()→ may return{ raw: "..." }object (goes throughgetEditedEntityRecord, which does not unwrap)The
{ raw }object would then cross the JS-to-native bridge and arrive as a dictionary/map where the native side expected a string.What We Explored
1. Making native types match the
{raw, rendered}shapeWe initially updated iOS (
EditorTitlestruct) and Android (TitleValuedata class) to parse{raw, rendered}objects for both title and content. This worked mechanically, but:renderedis useless client-side: It's only available from the server's initial REST API response. Once the user edits a field, the data store replaces the object with a plain string, losingrendered. The server-side rendering pipeline (render_block(),the_titlefilters) cannot be reproduced in the client.renderedwas identical torawfor content:getEditedPostContent()returns serialized block markup — there's no separate rendered form.{raw, rendered}shape only appeared in the exact states that triggered the bug — making it doubly useless to expose to the host app.2. Hoisting the
{raw, rendered}wrapper into native input typesWe considered having the native
EditorConfigurationaccept{raw, rendered}objects (matching the WP REST API shape) instead of plain strings, so the JS bridge wouldn't need to wrap/unwrap. But:{raw, rendered}with a plain string after any edit — so the output type would still be inconsistent regardless of input.3. Using
getEditedPostAttribute('content')instead ofgetEditedPostContent()getEditedPostAttributegoes throughgetRawEntityRecordwhich extracts.raw. But for'content'specifically, it delegates togetEditedPostContent()— same code path, same bug.Fix
Add a
normalizeAttribute()function at the JS bridge boundary that extracts.rawfrom objects or passes strings through unchanged. Applied togetTitleAndContent()for both title and content.Also removed the redundant
getContent()bridge method (and its iOS public API) —getTitleAndContent()is now the single accessor for reading editor state.Changes
src/components/editor/use-host-bridge.js: AddnormalizeAttribute(), apply togetTitleAndContent(), removegetContent()src/components/editor/test/use-host-bridge.test.jsx: Add unit tests for normalization with proper mockse2e/get-title-and-content.spec.js: New E2E test covering initial load (the bug case), post-edit, and empty statee2e/editor-page.js: AddgetTitleAndContent()helper to page objectios/.../EditorViewController.swift: RemovegetContent()public APIios/.../EditorViewControllerDelegate.swift: Update doc referenceTest Plan
get-title-and-content.spec.jsthat verifies plain strings are returned before edits (the bug case), after title edits, after content edits, and with empty initial state