Blocks: Add automatic handling of focus for RichText component#6419
Blocks: Add automatic handling of focus for RichText component#6419
Conversation
57280ac to
0e50419
Compare
0e50419 to
c5a10a8
Compare
There was a problem hiding this comment.
Should we add a deprecation message about the isSelected prop in RichText components?
There was a problem hiding this comment.
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.
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.
youknowriad
left a comment
There was a problem hiding this comment.
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. |
| return { | ||
| isBlockSelected: isSelected, | ||
| isSelected: context.isSelected && context.focusedElement === ownProps.instanceId, | ||
| onSetup: () => { |
There was a problem hiding this comment.
Nit: Why arrow function rather than function property shorthand?
onSetup() {
// ...
}There was a problem hiding this comment.
Good question, I don’t have an answer 😜 I’ll update.
| return { | ||
| isBlockSelected: isSelected, | ||
| isSelected: context.isSelected && context.focusedElement === ownProps.instanceId, | ||
| onSetup: () => { |
There was a problem hiding this comment.
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.
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.
But I don't understand why we need to set the focused element merely by fact the RichText has initialized.
| return { | ||
| isBlockSelected: isSelected, | ||
| isSelected: context.isSelected && context.focusedElement === ownProps.instanceId, | ||
| onSetup: () => { |
There was a problem hiding this comment.
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.
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.
"If you can't beat them, join them" ? 😆
There was a problem hiding this comment.
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. |
6f4a136 to
c9f68d3
Compare
|
@aduth I think I found a way to simplify this implementation using |
| } | ||
| // When explicitly set as selected or has onFocus prop provided, | ||
| // use value stored in the context instead. | ||
| if ( ownProps.isSelected === true || ownProps.onFocus ) { |
| edit: withState( { | ||
| editable: 'content', | ||
| } )( ( { attributes, setAttributes, isSelected, className, editable, setState } ) => { | ||
| edit: ( { attributes, setAttributes, isSelected, className } ) => { |
There was a problem hiding this comment.
Nit: Fewer characters as function property shorthand:
edit: ( { attributes, setAttributes, isSelected, className } ) => {edit( { attributes, setAttributes, isSelected, className } ) {c9f68d3 to
8fb655e
Compare
Description
Closes #6213.
Follow up for #5029 where we introduced new context API from React 16.3 to apply
isSelectedprop to a few components used withBlockEdit.This PR explores this idea further to make it possible to enforce that there can only be one
RichTextcomponent 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
BlockEditscoped solution to aovid dealing with the global store. I didn't see an immediate need to usewp.datato 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:
PullquoteQuoteText columnsScreenshots
Types of changes
Refactoring, no changes in the UX or UI.
Next steps
We use a similar technique that uses
withStateto 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: