-
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
Buttons: Preserve styling from adjacent button blocks when inserting a new button block #37905
Changes from 1 commit
85aff5e
d9204b4
ed10faa
090aee8
44538f6
8d04641
a32fa27
392b83e
f21abdf
af31fee
8f7dc06
401358f
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 |
---|---|---|
|
@@ -255,15 +255,21 @@ export default compose( [ | |
return; | ||
} | ||
|
||
function getAdjacentBlockAttributes() { | ||
function getAdjacentBlockAttributes( attributesToCopy ) { | ||
const { getBlock, getPreviousBlockClientId } = select( | ||
blockEditorStore | ||
); | ||
|
||
if ( ! clientId && ! rootClientId ) { | ||
if ( | ||
! attributesToCopy || | ||
( ! clientId && ! rootClientId ) | ||
) { | ||
return {}; | ||
} | ||
|
||
const result = {}; | ||
let adjacentAttributes = {}; | ||
|
||
// If there is no clientId, then attempt to get attributes | ||
// from the last block within innerBlocks of the root block. | ||
if ( ! clientId ) { | ||
|
@@ -279,24 +285,35 @@ export default compose( [ | |
directInsertBlock && | ||
directInsertBlock?.name === lastInnerBlock.name | ||
) { | ||
return { ...lastInnerBlock.attributes }; | ||
adjacentAttributes = lastInnerBlock.attributes; | ||
} | ||
} | ||
return {}; | ||
} else { | ||
// Otherwise, attempt to get attributes from the | ||
// previous block relative to the current clientId. | ||
const currentBlock = getBlock( clientId ); | ||
const previousBlock = getBlock( | ||
getPreviousBlockClientId( clientId ) | ||
); | ||
|
||
if ( currentBlock?.name === previousBlock?.name ) { | ||
adjacentAttributes = | ||
previousBlock?.attributes || {}; | ||
} | ||
} | ||
|
||
// Attempt to get attributes from the previous block | ||
// relative to the current clientId. | ||
const currentBlock = getBlock( clientId ); | ||
const previousBlock = getBlock( | ||
getPreviousBlockClientId( clientId ) | ||
); | ||
|
||
if ( currentBlock?.name === previousBlock?.name ) { | ||
return { ...( previousBlock?.attributes || {} ) }; | ||
// Copy over only those attributes flagged to be copied. | ||
for ( const attribute in attributesToCopy ) { | ||
if ( | ||
attributesToCopy[ attribute ] && | ||
adjacentAttributes.hasOwnProperty( attribute ) | ||
) { | ||
result[ attribute ] = | ||
adjacentAttributes[ attribute ]; | ||
} | ||
} | ||
|
||
return {}; | ||
return result; | ||
} | ||
|
||
function getInsertionIndex() { | ||
|
@@ -331,25 +348,11 @@ export default compose( [ | |
let blockToInsert; | ||
|
||
// Attempt to augment the directInsertBlock with attributes from an adjacent block. | ||
// This ensures that styling from existing nearby blocks are preserved in the | ||
// newly inserted block. To support intentionally clearing out certain attributes, | ||
// the attributes of the directInsertBlock override those of the adjacent block. | ||
// This ensures styling from nearby blocks is preserved in the newly inserted block. | ||
// See: https://github.com/WordPress/gutenberg/issues/37904 | ||
if ( directInsertBlock ) { | ||
const newAttributes = {}; | ||
const adjacentBlockAttributes = getAdjacentBlockAttributes(); | ||
|
||
Object.keys( adjacentBlockAttributes ).forEach( | ||
( attributeName ) => { | ||
if ( | ||
directInsertBlock.attributesToCopy?.[ | ||
attributeName | ||
] | ||
) { | ||
newAttributes[ attributeName ] = | ||
adjacentBlockAttributes[ attributeName ]; | ||
} | ||
} | ||
const newAttributes = getAdjacentBlockAttributes( | ||
directInsertBlock.attributesToCopy | ||
); | ||
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. Hmm, this will cycle through all the adjacent block attributes even if we don't mean to copy any of them. I'm thinking it would be better to return early if there are no attributes to be copied. A couple ways we could do this:
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. Great points! I've moved the logic up to go inside Let me know if you think it needs further tidying (or refactoring to another file now that it's a bit longer!) |
||
|
||
blockToInsert = createBlock( directInsertBlock.name, { | ||
|
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.
Can we remove this and just check:
if ( adjacentAttributes.hasOwnProperty( attribute ) )
?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.
Thanks for re-testing @stacimc, another good question! I wasn't too sure about this one — the
attributesToCopy
is currently an object where each attribute is set to a boolean (true
in all the cases in the Buttons block). So, if someone were to theoretically set one of them tofalse
it might be odd if it still copied the attribute.I initially had it an object so that look up was easy when looping over the adjacent block's attributes, so an object look up was preferable to an array. Now that it's switched the other way (we're looping over the
attributesToCopy
) the object structure doesn't seem to be quite as necessary. I kinda like the parallel, though, with having bothattributes
andattributesToCopy
being objects.So, a couple of follow-up questions! 😀
Is it better for
attributesToCopy
to be an object or an array? And if we keep it as an object, should it be possible for someone to set one of the values tofalse
to switch off copying of that attribute (there's no real use case for this, but I think it determines whether or not we should remove this line 🤔)Apologies if I'm overthinking this!
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.
Oh of course,
attributesToCopy
is an object now 🤦♀️ You're right, this is a bit tricky! ProvidedattributesToCopy
remains an object, I'd leave it as you have it. I don't think anyone ought to explicitly set a value tofalse
within that object, but I think you're right to safeguard against it.To me it does feel slightly less clear when reading -- maybe just a naming thing, but I wouldn't expect a parameter named
attributesToCopy
to contain data for attributes that shouldn't be copied. I may be the one overthinking it here, though 😄 I don't feel strongly about changing it and I don't think it should block the PR, so up to you!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.
Making
attributesToCopy
an array would make it less verbose to add those attributes, and semantically makes more sense because we wouldn't ever expect them to be set tofalse
(there's no point adding an attribute to copy if we don't want it copied, and there's no way of toggling those attributes after they're set).I agree it's not a blocking issue for this PR though; we can always address it in a follow up.
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.
Thanks @tellthemachines, that's a great point. I've updated it to be an array in 401358f (and updated the
typedef
so that it's an array of strings), and that looks much clearer to me. Cheers!I'm out for the next few hours, but will 🤞 merge in this PR tonight once the tests pass, unless there are any objections 🙂