-
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
Make ClipboardButton inside a block work correctly in Safari #7106
Conversation
@mirka: thanks for a great report and patch! It's unfortunate that this flew under the radar. This does need a rebase, though. Among other things, I see that a part of this patch has already been merged via #7622 (commit). From quick testing it seems the Do you have some time to circle back to this? 🙏 |
@mcsf Done! Tested in Chrome, Firefox, and Safari. |
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.
Because the copy handler does not stop propagation, the changes here introduce an issue in that copying within the editor area will be handled once by the div
wrapper, and a second time by the document
.
One option may be a matter of just adding event.stopPropagation
to avoid letting the document handle the event. (Though, it may in-fact require stopImmediatePropagation
since, as far as I recall, React events are actually bound to the document).
Which speaks to another point: One of the root causes here, as you noted, is the awkward conflict between binding to the DOM directly, vs. letting it be handled by React. I think as you've reimplemented this toward a wrapping component is how it ought to have been implemented in the first place. It's unfortunate then to hear that this doesn't work correctly for copying multi-selections in Safari. Have you been able to track down any resources on why this may occur? Is it because of the type of element that the copy is being performed on?
@aduth I tried removing the multi-selection workaround, and luckily it looks like it isn't needed any more. (Seven month old PR 😅) Thanks for the prompt. |
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.
@mirka: thanks for circling back so quickly! This looks a lot better.
Seems in very good shape, nothing awkward coming up in testing, event flow seems clear.
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 should consider how to describe this for the CHANGELOGs of each package.
https://github.com/WordPress/gutenberg/blob/master/packages/README.md#maintaining-changelogs
I worry this would be considered a breaking change, if one had come to rely on the fact that the component would bind to the document.
@aduth Good point! I updated the changelogs for the affected packages. |
An update in passing, just to break the silence: I raised the breaking change as a concern since, well... we can't just be doing breaking changes now that this component is available as part of a stable WordPress release. It's admittedly not so likely that someone would be using this component, but it doesn't immediately exempt it from the rule. I'm not sure what to do here, then, other than add some compatibility layer for the prior usage. The fact that it was previously buggy behavior could add some credence to the idea that it allowed to go through as-is. Related: https://make.wordpress.org/core/2018/11/14/technical-organisation-post-5-0/ Deprecations / breaking changes have also been a topic of discussion for recent core JavaScript meetings. https://make.wordpress.org/core/2019/01/08/javascript-chat-summary-january-7th/ |
This sounds like a solid reason to get it in. The longer it waits, the more risk of harm. @aduth what's needed at this point for a decision to move forward? And thanks again @mirka for the patch! |
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 agree, it won't help matters to let this one linger.
As one metric, I searched and found no occurrences of CopyHandler
in any plugins in the WordPress.org plugin repository, aside from Gutenberg itself:
https://wpdirectory.net/search/01D6911R475FRT87RVDAQ79ZYQ
I expected it would probably not find much usage outside our own, so in combination with the NPM major version bump, I think it would be reasonable to merge this one as-is.
The alternative would be to handle the backwards-compatibility by keeping the current logic (with document
handlers), but only run when there are no children passed to the component (with deprecated
logging). The downside is that this would effectively live on as dead code. With that in mind and of the previous note of unuse, I don't consider it blocking.
This does need a rebase, however. |
Let's rebase and merge as-is 👍 |
Thanks for the fix, and for your patience! |
* Make ClipboardButton inside a block work in Safari * Update changelogs * Block Editor: Update "Next" to "Unreleased" per guidelines https://github.com/WordPress/gutenberg/blob/master/packages/README.md#maintaining-changelogs
* RichText: improve format boundary style (#14519) * RichText: improve format boundary style * rgb => rgba * Paste: check plain text for gutenberg content (#14536) * Make ClipboardButton inside a block work correctly in Safari (#7106) * Make ClipboardButton inside a block work in Safari * Update changelogs * Block Editor: Update "Next" to "Unreleased" per guidelines https://github.com/WordPress/gutenberg/blob/master/packages/README.md#maintaining-changelogs * Input Interaction: always expand single line selection vertically (#14487) * Input Interaction: always expand single line selection vertically * Add e2e test * Use MenuItem instead of IconButton (#14569) * Remove id, infoid, label and aria-describedby from MenuItem (#14423) * Preformatted: save line breaks as characters (#14653) * Preformatted: save line breaks as characters * Update e2e test * Remove negative toolbar position rules from full-aligned blocks. (#14669) * Fix issue with double scrollbar in Fullscreen Mode (#14677) This PR fixes an issue where the sidebar would have two scrollbars when in fullscreen mode. * Fix WordPress embed block resolution (#14658) * Retry failing embeds with trailing slash (#14705) * Fix embedding Twitter URLs with a trailing slash (Closes #12664) * Fix race condition for WordPress URLs that end in slashes, add test * API Fetch: Fix error on empty OPTIONS preload data (#14714) * Input Interaction: better horizontal edge detection (#14462) * Input Interaction: better horizontal edge detection * Correct BR ranges * Add e2e test * Increase buffer for Firefox * Clean up * Merge isEdge logic * Fix typo * Address feedback * Build docs * Fix memize option key typo (#14750) * RichText: unify active formats, 'selectedFormat' and 'placeholderFormat' (#14411) * RichText: unify active formats, 'selectedFormat' and 'placeholderFormat' * Add extra e2e test * Only should boundary style when focused * Update docs * Try to trigger tests with Travis * Restore Travis config
Description
This PR addresses some quirks in Safari that can cause a
ClipboardButton
inside a block to not work correctly.I encountered this problem while implementing the Copy URL functionality in the File block (#6805).
Test directions
ClipboardButton
component to some blockIn Safari, start a new post (
/wp-admin/post-new.php
), and add the blockClick on the Copy button
Expected behavior: “foo” is copied to clipboard
Actual behavior (before this fix): A serialized string of the block is copied to clipboard
Other things to test
Technical notes
This is a strange combination of multiple Safari quirks and how they interact with
CopyHandler
.In Safari, the hidden
textarea
created by clipboard.js is not thedocument.activeElement
at the moment when the copy event fires, causingCopyHandler
to misinterpret the event asdocumentHasSelection() === false
. This causes a serialized block string to be copied instead of the intended text.The workaround is to listen to copy events on ClipboardButton, and explicitly
e.target.focus()
on the textarea.There is an inconsistency in event order between the following two cases, presumably because of mount order.
While this behavior is the same across browsers, in Safari it causes a problem in the latter case because the
e.target.focus()
workaround in 1 will not work unless the copy handler on ClipboardButton fires first.The workaround is to attach the event listeners directly on CopyHandler in React, instead of doing
document.addEventListener()
. This ensures a consistent event order.While the two workarounds above are enough to fix the copy bug, there is another quirk in Safari where multiblock copy events can only be caught at the document level. Therefore, we need to also keep the document-level event listeners to have multiblock copy work in Safari.
Is this workaround worth the trouble?
I’m not entirely sure. Maybe we wouldn’t need this if we rewrote clipboard.js. But that seems like a lot of trouble, too.