-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Editor: Fix regression introduced in CopyHandler #12543
Conversation
Sadly, it looks like it isn't possible to add e2e tests which would prevent this regression in the future. Such tests would only work when puppeteer operates with a real browser. It won't work in the headless mode which is the most expected way of running those tests. A related issue where this limiation is explained #2147. |
To be clear, the bug is that we'd call |
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.
Another implementation which might be preferable is that the event handlers are bound and unbound in response to the change in condition applying. i.e. we don't bind on mount, but rather when the selectedBlockClientIds.length > 0 && ( hasMultiSelection() || ! documentHasSelection() )
.
For the purposes of resolving the regression, I'm fine with the general approach here. I think the main blocker for me would be the reference of onCopy
passed to addEventListener
/ removeEventListener
needing a stronger guarantee to be the same.
@@ -54,7 +37,7 @@ export default compose( [ | |||
const selectedBlockClientIds = selectedBlockClientId ? [ selectedBlockClientId ] : getMultiSelectedBlockClientIds(); |
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 should have noted this in the previous pull request: But why do we do work here on selecting data, rather than within the callbacks? I don't think we should have any function calls in the upper wrapper aside from dispatch
or select
.
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 is evaluated at the time when a method is called so it doesn't matter from the technical standpoint. There will be some code duplication otherwise. I can move it to methods if you prefer to have it implemented this way.
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 evaluated when the component mounts as well. I'm fine with (in-fact, prefer) the code duplication.
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.
Yep, updated to be placed inside functions, not that much code added to be honest 👍
onCut( dataTransfer ) { | ||
this.onCopy( dataTransfer ); | ||
onCut( event ) { | ||
this.onCopy( event ); |
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 don't trust this
here. I should have noted it in the prior pull request too. Can we just have onCopy
declared as a proper function in the scope, and call upon that here?
function onCopy() {}
return {
onCopy,
onCut() {
onCopy();
// ...
},
};
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, I wasn't sure about it either. It works but might break in the future 👍
onCut( event ) { | ||
this.props.onCut( event.clipboardData ); | ||
event.preventDefault(); | ||
document.removeEventListener( 'copy', this.props.onCopy ); |
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 don't really trust that the reference of onCopy
prop passed in componentDidMount
is the same as the one in componentWillUnmount
. In practice, it probably is, based on the current implementation of withDispatch
. But I don't think it's a guarantee any component should rely on that a prop reference from mount is the same as in unmount.
In other words, an instance's onCopy = ( event ) => this.props.onCopy( event );
, while seemingly redundant, seems a much safer reference to rely upon.
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, in retrospect I think you are right, if there are 2 instances of this component they would reference the same function with this implementation. I will fix it.
Yes, correct 👍 |
0db78ba
to
c9447c3
Compare
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.
Looks good 👍
const selectedBlockClientIds = getSelectedBlockClientId() ? | ||
[ getSelectedBlockClientId() ] : | ||
getMultiSelectedBlockClientIds(); | ||
|
||
removeBlocks( selectedBlockClientIds ); | ||
} |
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.
Just to confirm: We're relying on the event.preventDefault()
to have already occurred in onCopy
?
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, this is what we had before the refactoring, see: https://github.com/WordPress/gutenberg/blob/b2e5ae79fc74f6730e1ded1332e66e6ee732e67d/packages/editor/src/components/copy-handler/index.js
Description
Fixes #12506.
Regression introduced in #11851.
Reported by @afercia:
Initially, I tried to fix it by returning the information whether copy handler override was triggered but it is not possible to return a value from the dispatch handler exposed by
withDispatch
. It seems like a design decision?I ended up with moving
event
handling to the handlers created inwithDispatch
which removes some lines of code. I know that @aduth wanted to avoid that in #11851 as discussed in #11851 (comment). An alternative would be to move event handling to the callback. I'm happy to iterate if you think it'd be cleaner.How has this been tested?
https://vimeo.com/22439234
then edit it e.g. remove a few numbers, copy, paste it: the previous full URL get pastedAll of that should basically work.