Skip to content

Commit

Permalink
Remove findDOMNode usage from the NavigableToolbar component
Browse files Browse the repository at this point in the history
  • Loading branch information
youknowriad committed Nov 2, 2018
1 parent 8322628 commit dd22b38
Show file tree
Hide file tree
Showing 25 changed files with 472 additions and 371 deletions.
130 changes: 74 additions & 56 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/block-library/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
},
"devDependencies": {
"deep-freeze": "^0.0.1",
"enzyme": "^3.3.0",
"enzyme": "^3.7.0",
"react-test-renderer": "^16.4.1"
},
"publishConfig": {
Expand Down
2 changes: 1 addition & 1 deletion packages/components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
},
"devDependencies": {
"@wordpress/token-list": "file:../token-list",
"enzyme": "^3.3.0",
"enzyme": "^3.7.0",
"react-test-renderer": "^16.4.1"
},
"publishConfig": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ exports[`MenuGroup should match snapshot 1`] = `
>
My group
</div>
<NavigableMenu
<ForwardRef(NavigableMenu)
aria-labelledby="components-menu-group-label-1"
orientation="vertical"
>
<p>
My item
</p>
</NavigableMenu>
</ForwardRef(NavigableMenu)>
</div>
`;
132 changes: 132 additions & 0 deletions packages/components/src/navigable-container/container.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@

/**
* External Dependencies
*/
import { omit, noop, isFunction } from 'lodash';

/**
* WordPress Dependencies
*/
import { Component, forwardRef } from '@wordpress/element';
import { focus } from '@wordpress/dom';

function cycleValue( value, total, offset ) {
const nextValue = value + offset;
if ( nextValue < 0 ) {
return total + nextValue;
} else if ( nextValue >= total ) {
return nextValue - total;
}

return nextValue;
}

class NavigableContainer extends Component {
constructor() {
super( ...arguments );
this.onKeyDown = this.onKeyDown.bind( this );
this.bindContainer = this.bindContainer.bind( this );

this.getFocusableContext = this.getFocusableContext.bind( this );
this.getFocusableIndex = this.getFocusableIndex.bind( this );
}

bindContainer( ref ) {
const { forwardedRef } = this.props;
this.container = ref;

if ( isFunction( forwardedRef ) ) {
forwardedRef( ref );
} else if ( forwardedRef && 'current' in forwardedRef ) {
forwardedRef.current = ref;
}
}

getFocusableContext( target ) {
const { onlyBrowserTabstops } = this.props;
const finder = onlyBrowserTabstops ? focus.tabbable : focus.focusable;
const focusables = finder.find( this.container );

const index = this.getFocusableIndex( focusables, target );
if ( index > -1 && target ) {
return { index, target, focusables };
}
return null;
}

getFocusableIndex( focusables, target ) {
const directIndex = focusables.indexOf( target );
if ( directIndex !== -1 ) {
return directIndex;
}
}

onKeyDown( event ) {
if ( this.props.onKeyDown ) {
this.props.onKeyDown( event );
}

const { getFocusableContext } = this;
const { cycle = true, eventToOffset, onNavigate = noop, stopNavigationEvents } = this.props;

const offset = eventToOffset( event );

// eventToOffset returns undefined if the event is not handled by the component
if ( offset !== undefined && stopNavigationEvents ) {
// Prevents arrow key handlers bound to the document directly interfering
event.nativeEvent.stopImmediatePropagation();

// When navigating a collection of items, prevent scroll containers
// from scrolling.
if ( event.target.getAttribute( 'role' ) === 'menuitem' ) {
event.preventDefault();
}

event.stopPropagation();
}

if ( ! offset ) {
return;
}

const context = getFocusableContext( document.activeElement );
if ( ! context ) {
return;
}

const { index, focusables } = context;
const nextIndex = cycle ? cycleValue( index, focusables.length, offset ) : index + offset;
if ( nextIndex >= 0 && nextIndex < focusables.length ) {
focusables[ nextIndex ].focus();
onNavigate( nextIndex, focusables[ nextIndex ] );
}
}

render() {
const { children, ...props } = this.props;
// Disable reason: Assumed role is applied by parent via props spread.
/* eslint-disable jsx-a11y/no-static-element-interactions */
return (
<div ref={ this.bindContainer }
{ ...omit( props, [
'stopNavigationEvents',
'eventToOffset',
'onNavigate',
'cycle',
'onlyBrowserTabstops',
'forwardedRef',
] ) }
onKeyDown={ this.onKeyDown }
onFocus={ this.onFocus }>
{ children }
</div>
);
}
}

const forwardedNavigableContainer = ( props, ref ) => {
return <NavigableContainer { ...props } forwardedRef={ ref } />;
};
forwardedNavigableContainer.displayName = 'NavigableContainer';

export default forwardRef( forwardedNavigableContainer );
Loading

0 comments on commit dd22b38

Please sign in to comment.