Skip to content
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

Writing Flow: Move selection to block's focus handler #5289

Merged
merged 11 commits into from
Mar 7, 2018
Merged

Conversation

aduth
Copy link
Member

@aduth aduth commented Feb 27, 2018

This pull request seeks to resolve conflicts between the WritingFlow's selection transitioning and nested blocks. Prior to these changes, when attempting to press "Down" from the first block in a column, the new block would be added, but focus would be moved back to the first block in the column. This was caused by two related issues:

  • In WritingFlow's handling of vertically moving from one block to the next, we explicitly call to select the parent block. In both nested and unnested contexts, when this occurs as a result of moving into the default block appender, the closestTabbable is the default block appender itself. For non-nested blocks, attempting to select parent block has no effect because there is no "closest block" for the appender. In a nested context, however, the closest block is the wrapper block (Columns), so therefore it becomes selected and focuses its first tabbable which is the first block's wrapper.
    • This is resolved by moving selection handling in response to any focus which occurs within the selected block, not just the current fallback logic for testing against this.node (i.e. clicking at the edge of a block, or a block without its own focusable elements).
  • The previous resolution introduces another issue, which is that we rely on the IgnoreNestedEvents component to flag events as handled so that ancestor blocks don't attempt to handle them. With 8bd012a, since any focus event would cause the block to select itself if not already selected, we need to ensure that focus events from nested blocks are appropriately flagged. However, the previously described behavior of focusing the default block appender occurs outside the block itself, so it will never apply the expected _blockHandled flag.
    • This is resolved by wrapping DefaultBlockAppender with its own IgnoreNestedEvents, capturing and applying the handled flag to events managed in the appender.
    • I really don't like this approach, because it introduces a dependency from the parent BlockListLayout to a child's rendering behavior (the DefaultBlockAppender). I had unsuccessfully explored other options, including a IgnoreNestedEvents.Barrier component sitting at the top of a nested context and flagging every event. Besides being wasteful (not all events are monitored, and some events dispatch very often like mousemove), it introduced more problems of its own where expected events were not being handled correctly (like a block dismissing its hover effects).

Testing instructions:

Verify that there are no regressions in the behavior of selecting a block via focus, both for blocks with their own inputs (paragraph) and those without (image placeholder).

Verify that you can add a default block to the end of a block list, both in a nested and non-nested context, by pressing "Down" from the last non-empty block.

@aduth aduth added the [Feature] Writing Flow Block selection, navigation, splitting, merging, deletion... label Feb 27, 2018
@aduth aduth added this to the 2.3 milestone Feb 28, 2018
Copy link
Contributor

@youknowriad youknowriad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a small issue with these changes.

  • create multiple text blocks
  • Navigate between these blocks using arrows
  • click shift + left/right arrows to select text
  • The toolbar shows up in the wrong block, because the wrong block is selected in the state.

@aduth
Copy link
Member Author

aduth commented Mar 2, 2018

@youknowriad I'm not able to reproduce this, and it doesn't seem like something that ought to be happening, if the block is selecting itself in response to the focus event.

@aduth
Copy link
Member Author

aduth commented Mar 2, 2018

Ah, reproduced in Firefox, but not Chrome.

@youknowriad
Copy link
Contributor

@aduth Here's a gif showing the issue (I'm using firefox)

kapture 2018-03-02 at 15 47 09

@aduth
Copy link
Member Author

aduth commented Mar 2, 2018

Seems related to the Firefox-specific condition that was added in #5112. Removing the condition resolves this issue, but of course regresses on the intended issue addressed in #5112.

@aduth
Copy link
Member Author

aduth commented Mar 2, 2018

Personally I'd find this to be a more important fix than the one addressed in #5112, particularly since the toolbar is still problematic in Safari, another solution is likely needed. I'll continue debugging for a bit longer though...

@aduth aduth force-pushed the try/focus-fixes branch from 07ffc00 to 02a1aa8 Compare March 2, 2018 15:29
@aduth
Copy link
Member Author

aduth commented Mar 2, 2018

I tried to demonstrate the issue with a simplistic CodePen: https://codepen.io/aduth/pen/MQxRME

In master, the block's edit wrapper is focusable, but since the toolbar is outside the edit wrapper and buttons do not emit focus events on click in Firefox and Safari, the focus instead occurs on the parent block's edit wrapper. If we move tabIndex to the top-level wrapper instead, the nested block will flag the event as block-handled in the top-level IgnoreNestedEvents (previously, nothing existed between the button and the parent block's edit wrapper which was focusable).

I'm still trying to understand the original reason it was moved in #2934. Given original attempts at a fix to what ultimately led to the move, I'm wondering if it's even still relevant, since part of the changes proposed here is to remove this target === this.node condition altogether.

@aduth
Copy link
Member Author

aduth commented Mar 2, 2018

Re-testing the original issues described at #2934 (comment), they still appear to work correctly here.

@aduth
Copy link
Member Author

aduth commented Mar 2, 2018

Discovered an issue: Arrowing to an image placeholder block skips it to the following block.

@aduth
Copy link
Member Author

aduth commented Mar 2, 2018

Not so surprisingly, this was a result of our fragile direct selectors being fragile. Fixed in 386ac09.

@aduth
Copy link
Member Author

aduth commented Mar 2, 2018

If these changes are solid, I suspect we might also be able to drop the onMouseDown selection which exists here:

if ( ! this.props.isSelected ) {
this.props.onSelect();
}

I'd explored this once in the past and I think it was needed at the time based on how focus of blocks works. Would be worth exploring, but not necessary here since it's more a simplification of removing unnecessary event handlers.

@mcsf mcsf modified the milestones: 2.3, 2.4 Mar 2, 2018
@aduth
Copy link
Member Author

aduth commented Mar 5, 2018

This was punted as part of the 2.3 release because of the complexity of the changes on a release day, but I'd like to get this in to allow remaining issues to be settled in time for the next release.

I will plan to merge this if there is no other remaining feedback.

aduth added 7 commits March 6, 2018 15:39
See: #2934
See: https://codepen.io/aduth/pen/MQxRME

Something needs to capture the focus event, which is skipped by button press on the block's toolbar, but bubbles up wrongly to the parent's edit wrapper (which has tabIndex) and causes parent block to select itself.
Originally used for down-to-new-paragraph in #3973, removed as of #5271
Corresponding to move of tabIndex from inner edit element to wrapper, we also need to capture focus from wrapper. This way, when focus is applied via WritingFlow, the block appropriately becomes selected. This may also make "onSelect" of movers unnecessary.
@aduth aduth force-pushed the try/focus-fixes branch from f80c123 to 4a85e23 Compare March 6, 2018 21:00
aduth added 4 commits March 6, 2018 16:16
Can now rely on focus behavior captured from wrapper node to reflect selection onto blocks, even those without their own focusable elements
Multi-selection causes focus event to be triggered on the block, but at that point `isSelected` is false, so it will trigger `onSelect` and a subsequent `focusTabbables`, thus breaking the intended multi-selection flow.
Focus will bubble to block wrapper (or in case of Firefox/Safari, where button click itself does not trigger focus, focus on the wrapper itself) to incur self-select.
@aduth
Copy link
Member Author

aduth commented Mar 6, 2018

What started as an exploration of fixing an issue where arrow navigating to an image placeholder would focus but not select a block (4a85e23) resulted in a cascade of commits. In the end, I think it's a more stable implementation overall, but I've come to the conclusion that this is all rather risky code to be touching, so would appreciate another look.

@aduth
Copy link
Member Author

aduth commented Mar 7, 2018

Noticing an issue, but also exists on master:

  1. Add image block, leave as placeholder
  2. Add paragraph block
  3. Start from title, press arrow down
    • Should be focused on image block
  4. Press tab
    • Should be focused on Upload button
  5. Press down

Expected: In subsequent paragraph block
Actual: At end of post in default appender

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] Writing Flow Block selection, navigation, splitting, merging, deletion...
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants