-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rich text: formats: allow format to filter value when changing tag name #35516
Changes from all commits
0dd8577
cbcdfd6
8f19d9b
e02f694
80dd602
5443df2
a5ff3bb
97cbbc1
1be2027
78a4905
077d753
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -101,6 +101,21 @@ export function useRichText( { | |
|
||
if ( ! record.current ) { | ||
setRecordFromProps(); | ||
// Sometimes formats are added programmatically and we need to make | ||
// sure it's persisted to the block store / markup. If these formats | ||
// are not applied, they could cause inconsistencies between the data | ||
// in the visual editor and the frontend. Right now, it's only relevant | ||
// to the `core/text-color` format, which is applied at runtime in | ||
// certain circunstances. See the `__unstableFilterAttributeValue` | ||
// function in `packages/format-library/src/text-color/index.js`. | ||
// @todo find a less-hacky way of solving this. | ||
|
||
const hasRelevantInitFormat = | ||
record.current?.formats[ 0 ]?.[ 0 ]?.type === 'core/text-color'; | ||
|
||
if ( hasRelevantInitFormat ) { | ||
handleChangesUponInit( record.current ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @fullofcaffeine Why is this needed? We shouldn't dirty on init. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When we upgrade a span to a mark, it's only visible in the editor. The front-end will still be a span and that's fine. Only when the user changes something in the rich text field will the change be made. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem with the blocks in the editor not being persisted to the block editor's HTML markup. For instance, the programmatically added
Peek.2021-10-22.10-55.mp4At first glance, it all looks good in the editor. However, the second paragraph still has a yellow mark on the frontend. By looking at the code markup in the code editor, you'll see that the transparent background color is not there. If you edit the post*, it will still not sync up the markup, it will still look broken in the frontend. *It does sync up if I edit the same paragraph, but that's less than ideal, as it'd require the user to type in each one of them to make the I'm aware that the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's what it's meant to do. It's not meant to sync up until the user edits the same paragraph. Sure, the old colours that were added will not update to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That makes sense! I think the scale of the issue (i.e in number of paragraphs affected) wasn't as big as I thought then. I think it'd be possible to use the solution without syncing upon init, but instruct users to click each affected paragraph, then 🤔 - it'd require more work from users, though. I completely agree that for the long term this shouldn't be here, thanks for creating the follow-up! |
||
} | ||
} else if ( | ||
selectionStart !== record.current.start || | ||
selectionEnd !== record.current.end | ||
|
@@ -153,6 +168,31 @@ export function useRichText( { | |
forceRender(); | ||
} | ||
|
||
function handleChangesUponInit( newRecord ) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method is a copy of |
||
record.current = newRecord; | ||
|
||
_value.current = toHTMLString( { | ||
value: __unstableBeforeSerialize | ||
? { | ||
...newRecord, | ||
formats: __unstableBeforeSerialize( newRecord ), | ||
} | ||
: newRecord, | ||
multilineTag, | ||
preserveWhiteSpace, | ||
} ); | ||
|
||
const { formats, text } = newRecord; | ||
|
||
registry.batch( () => { | ||
onChange( _value.current, { | ||
__unstableFormats: formats, | ||
__unstableText: text, | ||
} ); | ||
} ); | ||
forceRender(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's possible this is not needed at all, didn't have time to test. |
||
} | ||
|
||
function applyFromProps() { | ||
setRecordFromProps(); | ||
applyRecord( record.current ); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -43,14 +43,6 @@ function createEmptyValue() { | |
}; | ||
} | ||
|
||
function simpleFindKey( object, value ) { | ||
for ( const key in object ) { | ||
if ( object[ key ] === value ) { | ||
return key; | ||
} | ||
} | ||
} | ||
|
||
function toFormat( { type, attributes } ) { | ||
let formatType; | ||
|
||
|
@@ -94,15 +86,33 @@ function toFormat( { type, attributes } ) { | |
|
||
const registeredAttributes = {}; | ||
const unregisteredAttributes = {}; | ||
const _attributes = { ...attributes }; | ||
|
||
for ( const name in attributes ) { | ||
const key = simpleFindKey( formatType.attributes, name ); | ||
for ( const key in formatType.attributes ) { | ||
const name = formatType.attributes[ key ]; | ||
|
||
if ( key ) { | ||
registeredAttributes[ key ] = attributes[ name ]; | ||
} else { | ||
unregisteredAttributes[ name ] = attributes[ name ]; | ||
registeredAttributes[ key ] = _attributes[ name ]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What are There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
if ( formatType.__unstableFilterAttributeValue ) { | ||
registeredAttributes[ | ||
key | ||
] = formatType.__unstableFilterAttributeValue( | ||
key, | ||
registeredAttributes[ key ] | ||
); | ||
} | ||
|
||
// delete the attribute and what's left is considered | ||
// to be unregistered. | ||
delete _attributes[ name ]; | ||
|
||
if ( typeof registeredAttributes[ key ] === 'undefined' ) { | ||
delete registeredAttributes[ key ]; | ||
} | ||
} | ||
|
||
for ( const name in _attributes ) { | ||
unregisteredAttributes[ name ] = attributes[ name ]; | ||
} | ||
|
||
return { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit and curious: why the
join( ':' )
and not just a single string?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's probably keeping with the style used in
./inline.js
. But personally I'd avoid it in such a situation, since I don't know how smart the VM is.