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

Using NavigableContainer to make a TabPanel for Inserter #3281

Merged
merged 54 commits into from
Nov 14, 2017

Conversation

ephox-mogran
Copy link
Contributor

@ephox-mogran ephox-mogran commented Nov 1, 2017

Description

Exploring #1823

Created some component for navigation and a11y. Have used them in the InserterMenu as a POC.

How Has This Been Tested?

It's a work in progress. I'll write tests as it's stabilising.

Screenshots (jpeg or gifs if applicable):

Types of changes

New components.

Checklist:

  • My code is tested.
  • My code follows the WordPress code style.
  • My code follows has proper inline documentation.

@ephox-mogran
Copy link
Contributor Author

ephox-mogran commented Nov 1, 2017

Basically, there are two bits of feedback that I'd like when people have time:

  1. Are you happy with the architecture of TabPanel and NavigableContainer? Does it look generally useful?

  2. Are you happy with the keyboard flow through the Inserter Menu (excluding pressing Left and Right in the search bar which is currently being interfered with by WritingFlow)? Is that how you would like tabs to work?

The navigation for the InserterMenu is as follows:

Focus starts in search bar.
Pressing tab will shift to the Tabbar and focus the highlighted tab.
Pressing arrow keys will change tabs
Pressing tab from the Tabbar will jump to the first block category
Pressing arrow keys inside the block category will navigate inside that category (in a grid)
Pressing tab from the first block category will jump to the second block category is there is one, or search bar if there isn't
Pressing tab will keep moving through the block categories
Pressing tab on the last block category will jump back to the search bar
Pressing shift+tab at any point will go in the opposite direction

While open, tab and shift+tab will not exit the inserter menu until it is closed with Escape.

Note, the use of stopImmediatePropagation is due to a change where WritingFlow is now binding to the document directly. This is causing many keydowns to shift blocks unexpectedly, unless stopImmediatePropagation is used.

@jasmussen
Copy link
Contributor

I like this a lot. Seems to work well. But whether it's okay to do this — require arrow keys to switch tabs — I don't know. @afercia if you have time? Thanks.

Visually the tabs now have rounded corners, but okay to not fix in this branch as I need to revisit focus styles separately as part of #3039

screen shot 2017-11-01 at 08 20 09

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.

I'm having a hard time reviewing this PR, Do you think we could split it into two PRs, one for the tabs components and one for the NavigableContainer.

Also, having a README explaining all the possible props and properties of a "mode" would help the review.

@ephox-mogran
Copy link
Contributor Author

Yeah, I can add some readme details and tests. Sorry, it's probably not in a review ready state yet. I should have realised.

@youknowriad
Copy link
Contributor

Don't worry, it's pretty solid already. Just need some clarity ;)

@ephox-mogran ephox-mogran mentioned this pull request Nov 2, 2017
3 tasks
@ephox-mogran ephox-mogran changed the base branch from master to try/general-navigable-component November 2, 2017 04:42
@ephox-mogran
Copy link
Contributor Author

I've noticed a regression. It isn't focusing the disabled blocks properly. I'll investigate.

@ephox-mogran
Copy link
Contributor Author

The original code has this:

do {
 -			nextIndex += increment;
 -			// Return the name of the next block type.
 -			blockType = blockTypes[ nextIndex ];
 -			if ( blockType && ! this.isDisabledBlock( blockType ) ) {
 -				return blockType.name;
 -			}
 -		} while ( blockType );

So it looks like it was designed to skip over disabled options. This is consistent with the fact that the focus outlines don't appear for disabled elements. Is it reasonable to just make all navigation skip disabled elements then?

@ephox-mogran
Copy link
Contributor Author

ephox-mogran commented Nov 13, 2017

Seems like the issue is that when focusable was written, it didn't expect buttons to have a tabindex and be disabled. However, giving a button a tabindex of -1 allows a roving tabindex, which is good for accessibility. Therefore, we can't just OR all of those selectors together any more.

EDIT: Alternatively, we can ensure that we never give a tabindex to anything that is disabled.

@ephox-mogran
Copy link
Contributor Author

OK. I've tried to update it in line with the PR comments, and also support disabled blocks. I noticed that I had the wrong prop when checking if something should be disabled, so I've fixed that too. @youknowriad @mcsf let me know what else needs to be changed.

@ephox-mogran
Copy link
Contributor Author

ephox-mogran commented Nov 14, 2017

Another issue: sometimes pressing tab from the search bar doesn't go into the Search blocks. It looks to be deterministic ... probably something about the roving tab index.

EDIT: boolean logic error when trying to preserve a previous roving tabindex / selection that is still valid. Should be fixed now.

}
}
const TabButton = ( { tabId, onClick, children, selected, ...rest } ) => {
return <button role="tab"
Copy link
Contributor

Choose a reason for hiding this comment

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

Minor: might want to use parens around the element here:

return (
  <element />
);

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.

I think we're in a good shape here. I found it weird to not be able to navigate the whole list of blocks with arrow keys, I need to tab between categories but I'll defer to @afercia for the right approach here.

There's a small eslint error :)

As a user, pressing the wrong group of arrows (e.g. down instead of
right or tab) shouldn't take the focus out of the inserter. This is most
easily experienced when using VisualEditorSiblingInserter, the inserter
sitting in between any two blocks.
@mcsf
Copy link
Contributor

mcsf commented Nov 14, 2017

Aside from #3281 (comment), the other odd thing that stuck out as a user was Escape not closing the inserter if focus is in the search input.

I don't see it as a blocker, though.

@ephox-mogran
Copy link
Contributor Author

Interesting. Is that a regression? Or just something we need to implement?

@mcsf
Copy link
Contributor

mcsf commented Nov 14, 2017

Is that a regression? Or just something we need to implement?

I can't remember. Again, not a blocker, but something to be aware of. If you want to fix it here and the fix is trivial, go for it.

{ blockTypes.map( this.renderItem, this ) }
</NavigableMenu>;
return (
<NavigableMenu
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the cleanup. Eventually, I hope to get to the point where you don't have to do this every time :)

Copy link
Contributor

Choose a reason for hiding this comment

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

No worries :), it's almost therapeutic. We just need the robots to do more for us (more linting rules + autoformatters).

@ephox-mogran
Copy link
Contributor Author

Aside from #3281 (comment), the other odd thing that stuck out as a user was Escape not closing the inserter if focus is in the search input.

I can't actually replicate this. I'll merge this one, and we can create a new issue if it happens again.

@ephox-mogran ephox-mogran merged commit 4312160 into master Nov 14, 2017
@ephox-mogran ephox-mogran deleted the try/tab-component branch November 14, 2017 22:58
@mcsf
Copy link
Contributor

mcsf commented Nov 14, 2017

I can't actually replicate this. I'll merge this one, and we can create a new issue if it happens again.

Oh, you know what, this was almost for sure my Vimium browser extension. I've had it intermittently off for a11y testing. Problem solved never existed. :)

tabs,
} = this.props;

const selectedTab = tabs.find( ( { name } ) => name === selected );
Copy link
Member

@aduth aduth Nov 15, 2017

Choose a reason for hiding this comment

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

Array#find is not polyfilled and is not supported by IE11. This results in crashes in IE11.

if ( ! isEqual( this.props.blockTypes, nextProps.blockTypes ) ) {
this.activeBlocks = deriveActiveBlocks( nextProps.blockTypes );
// Try and preserve any still valid selected state.
const current = this.activeBlocks.find( block => block.name === this.state.current );
Copy link
Member

Choose a reason for hiding this comment

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

Array#find is not polyfilled and is not supported by IE11. This results in crashes in IE11.

Copy link
Contributor

Choose a reason for hiding this comment

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

Should we provide a runtime warning in dev env?

Copy link
Member

Choose a reason for hiding this comment

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

Per #746 (comment), I think eventually we'll just want to pull in the polyfill, ideally once Babel 7.x lands.

/**
* Internal dependencies
*/
import { default as withInstanceId } from '../higher-order/with-instance-id';
Copy link
Member

Choose a reason for hiding this comment

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

You don't need { default as ... } syntax here. This can be simplified to:

import withInstanceId from '../higher-order/with-instance-id';

The default export is inferred when assigning outside the curly braces.

>
{ blocks.map( ( block ) => this.getBlockItem( block ) ) }
</div>
<InserterGroup blockTypes={ blockTypesInfo } labelledBy={ labelledBy }
Copy link
Member

Choose a reason for hiding this comment

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

This convention for applying props is quite difficult to read. I'd suggest either keeping it on a single line, to a certain character limit (80-100), then otherwise consistently applying props one-per-line:

<InserterGroup
	blockTypes={ blockTypesInfo }
	labelledBy={ labelledBy }
	bindReferenceNode={ this.bindReferenceNode }
	selectBlock={ this.selectBlock }
/>

Copy link
Member

Choose a reason for hiding this comment

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

Also, we lost the showInsertionPoint, hideInsertionPoint props in this refactor. See #3613.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants