-
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
Blocks: Add automatic handling of focus for RichText component #6419
Conversation
57280ac
to
0e50419
Compare
0e50419
to
c5a10a8
Compare
withInstanceId, | ||
withBlockEditContext( ( context, ownProps ) => { | ||
// When explicitly set as not selected, do nothing. | ||
if ( ownProps.isSelected === false ) { |
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.
Should we add a deprecation message about the isSelected
prop in RichText components?
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.
We might, but as soon as we remove all occurrences in core blocks. Otherwise, we would flood the console with the deprecation messages. We should also make sure there aren't any cases when this possibility would still make sense.
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.
I love having focus handled automatically by default. I'd like to be able to optionally control it myself by setting that prop using my own logic.
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.
I like it but there might be a small bug on initial mount.
Try this:
- Insert a paragraph block
- Using the autocompleter switch to the text columns block
- Type and move the mouse
- Notice the toolbar is not shown even if the text columns has the focus.
@youknowriad, great catch. I think I found a solution which solves the issue you described: f0edd3c. Let me know if you have some ideas how to improve this code. Maybe there is a way to avoid introducing another method in the BlockEdit context, which doesn't require passing down boolean param. |
f0edd3c
to
e80d22f
Compare
Rebased with master. |
blocks/rich-text/index.js
Outdated
return { | ||
isBlockSelected: isSelected, | ||
isSelected: context.isSelected && context.focusedElement === ownProps.instanceId, | ||
onSetup: () => { |
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: Why arrow function rather than function property shorthand?
onSetup() {
// ...
}
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.
Good question, I don’t have an answer 😜 I’ll update.
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.
Updated with 6f4a136
blocks/rich-text/index.js
Outdated
return { | ||
isBlockSelected: isSelected, | ||
isSelected: context.isSelected && context.focusedElement === ownProps.instanceId, | ||
onSetup: () => { |
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.
When does onSetup
get called? When the user first focuses into the field? I would have thought it'd be at each's initialization, though it doesn't seem like we should want to set the focused element at all until the user actually focuses within (i.e. only onFocus
).
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.
OnSetup gets called when RichText gets mounted (initial render of the editor or when new block gets added). OnFocus is triggered only when a block gets focus but not on the initial render. That’s why we need to maintain two methods.
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.
But I don't understand why we need to set the focused element merely by fact the RichText has initialized.
blocks/rich-text/index.js
Outdated
return { | ||
isBlockSelected: isSelected, | ||
isSelected: context.isSelected && context.focusedElement === ownProps.instanceId, | ||
onSetup: () => { |
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.
Is this going to incur a lot of unnecessary renders due to the new function references being passed each time (bypassing any pure component behavior)?
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.
There are also multiple props present that change frequently causing rerenders. I tried to add some optimizations to avoid creating new references, but it wasn’t helpful at all. I didn’t notice any improvements
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.
"If you can't beat them, join them" ? 😆
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 would be great to take a closer look at all updates triggered by RichText and better understand how and why it works this way.
e80d22f
to
6f4a136
Compare
I rebased with master again and also added a small change in 6f4a136. |
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.
Nice work.
6f4a136
to
c9f68d3
Compare
@aduth I think I found a way to simplify this implementation using |
blocks/rich-text/index.js
Outdated
} | ||
// When explicitly set as selected or has onFocus prop provided, | ||
// use value stored in the context instead. | ||
if ( ownProps.isSelected === true || ownProps.onFocus ) { |
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.
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.
Yes, it makes sense 👍
core-blocks/pullquote/index.js
Outdated
edit: withState( { | ||
editable: 'content', | ||
} )( ( { attributes, setAttributes, isSelected, className, editable, setState } ) => { | ||
edit: ( { attributes, setAttributes, isSelected, className } ) => { |
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: Fewer characters as function property shorthand:
edit: ( { attributes, setAttributes, isSelected, className } ) => {
edit( { attributes, setAttributes, isSelected, className } ) {
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.
Will do on Monday 👍
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.
Also applied to Quote
c9f68d3
to
8fb655e
Compare
Description
Closes #6213.
Follow up for #5029 where we introduced new context API from React 16.3 to apply
isSelected
prop to a few components used withBlockEdit
.This PR explores this idea further to make it possible to enforce that there can only be one
RichText
component focused at the same time. Historically it was manually enforced by using local state which stored which component is focused and prevented others to be selected at the same time.I proposed the following
@jorgefilipecosta also proposed together with @jayshenk the following:
I started small by using
BlockEdit
scoped solution to aovid dealing with the global store. I didn't see an immediate need to usewp.data
to solve the issue we had here. We might want to revisit this later, but at the moment it seems like using the context API limits the number of operation React would have to perform where the focused element changes. I might be wrong here though.How has this been tested?
Made sure it works without any changes for all blocks and for those updated in particular:
Pullquote
Quote
Text columns
Screenshots
Types of changes
Refactoring, no changes in the UX or UI.
Next steps
We use a similar technique that uses
withState
to change focus for Gallery and Image blocks, but they are more complex so I excluded them from this PR. However we might look into exposing a general purpose helper that would allow to wrap components and inject this behavior to them.Checklist: