Skip to content

Commit 3248893

Browse files
committed
Block: Cancel deselect via synthetic events
More reliable when using virtual event bubbling (e.g. portals), but as workaround to noted issue of document-level event binding, need to stop propagation. See: facebook/react#285
1 parent 74e58ea commit 3248893

File tree

1 file changed

+19
-10
lines changed

1 file changed

+19
-10
lines changed

editor/components/block-list/block.js

+19-10
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,6 @@ export class BlockListBlock extends Component {
184184
// Remove and cancel deselect focus handlers
185185
document.removeEventListener( 'focus', this.triggerDeselect, true );
186186
document.removeEventListener( 'deselect', this.debouncedDeselect );
187-
this.wrapperNode.removeEventListener( 'focusin', this.cancelDeselect );
188187
this.debouncedDeselect.cancel();
189188
}
190189

@@ -216,12 +215,6 @@ export class BlockListBlock extends Component {
216215
const bindFn = isListening ? 'addEventListener' : 'removeEventListener';
217216
document[ bindFn ]( 'focus', this.triggerDeselect, true );
218217
document[ bindFn ]( 'deselect', this.debouncedDeselect );
219-
220-
// Bind to the DOM reference of the rendered wrapper node, since React
221-
// synthetic event binding and focus normalization conflicts with our
222-
// own document event binding and therefore canceling occurs before the
223-
// deselect event is triggered (out of expected order).
224-
this.wrapperNode[ bindFn ]( 'focusin', this.cancelDeselect );
225218
}
226219

227220
setAttributes( attributes ) {
@@ -258,9 +251,14 @@ export class BlockListBlock extends Component {
258251
*
259252
* @see https://developer.mozilla.org/en-US/docs/Web/Events/focus
260253
*
261-
* @param {FocusEvent} event Focus event
254+
* @param {FocusEvent} event Focus event
262255
*/
263256
triggerDeselect( event ) {
257+
// Only deselect when focusing outside current node
258+
if ( this.wrapperNode.contains( event.target ) ) {
259+
return;
260+
}
261+
264262
const deselectEvent = new window.Event( 'deselect', {
265263
bubbles: true,
266264
cancelable: true,
@@ -271,10 +269,20 @@ export class BlockListBlock extends Component {
271269

272270
/**
273271
* Cancels the debounced deselect. Debouncing allows focus within the block
274-
* wrapper to prevent deselect from occurring.
272+
* wrapper to prevent deselect from occurring. Stops propagation to work
273+
* around unreliable ordering of document-level event handlers.
274+
*
275+
* @param {Event} event Focus event
275276
*/
276-
cancelDeselect() {
277+
cancelDeselect( event ) {
277278
this.debouncedDeselect.cancel();
279+
280+
// Stop propagation for synthetic events, necessary because the order
281+
// of the document-level focus deselect handler occurs after React's
282+
// internal event hub's capture.
283+
//
284+
// See: https://github.com/facebook/react/issues/285
285+
event.nativeEvent.stopImmediatePropagation();
278286
}
279287

280288
/**
@@ -471,6 +479,7 @@ export class BlockListBlock extends Component {
471479
data-type={ block.name }
472480
onTouchStart={ this.onTouchStart }
473481
onClick={ this.onClick }
482+
onFocus={ this.cancelDeselect }
474483
{ ...wrapperProps }
475484
>
476485
<BlockDropZone index={ order } />

0 commit comments

Comments
 (0)