Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions components/popover/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,17 @@ An optional additional class name to apply to the rendered popover.

- Type: `String`
- Required: No

## onClose

A callback invoked when the popover should be closed.

- Type: `Function`
- Required: No

## onClickOutside

A callback invoked when the user clicks outside the opened popover, passing the click event. The popover should be closed in response to this interaction. Defaults to `onClose`.

- Type: `Function`
- Required: No
11 changes: 10 additions & 1 deletion components/popover/detect-outside.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@
* External dependencies
*/
import clickOutside from 'react-click-outside';
import { flowRight } from 'lodash';

/**
* WordPress dependencies
*/
import { Component } from '@wordpress/element';

/**
* Internal dependencies
*/
import withFocusReturn from '../higher-order/with-focus-return';

class PopoverDetectOutside extends Component {
handleClickOutside( event ) {
const { onClickOutside } = this.props;
Expand All @@ -21,4 +27,7 @@ class PopoverDetectOutside extends Component {
}
}

export default clickOutside( PopoverDetectOutside );
export default flowRight( [
withFocusReturn,
clickOutside,
] )( PopoverDetectOutside );
49 changes: 41 additions & 8 deletions components/popover/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,20 @@ import { isEqual, noop } from 'lodash';
* WordPress dependencies
*/
import { createPortal, Component } from '@wordpress/element';
import { focus } from '@wordpress/utils';
import { focus, keycodes } from '@wordpress/utils';

/**
* Internal dependencies
*/
import './style.scss';
import PopoverDetectOutside from './detect-outside';

const { ESCAPE } = keycodes;

/**
* module constants
* Offset by which popover should adjust horizontally to account for tail.
*
* @type {Number}
*/
const ARROW_OFFSET = 20;

Expand All @@ -29,6 +33,7 @@ export class Popover extends Component {
this.bindNode = this.bindNode.bind( this );
this.setOffset = this.setOffset.bind( this );
this.throttledSetOffset = this.throttledSetOffset.bind( this );
this.maybeClose = this.maybeClose.bind( this );

this.nodes = {};

Expand Down Expand Up @@ -173,16 +178,38 @@ export class Popover extends Component {
];
}

maybeClose( event ) {
const { onKeyDown, onClose } = this.props;

// Close on escape
if ( event.keyCode === ESCAPE && onClose ) {
onClose();
}

// Preserve original content prop behavior
if ( onKeyDown ) {
onKeyDown( event );
}
}

bindNode( name ) {
return ( node ) => this.nodes[ name ] = node;
}

render() {
// Disable reason: We generate the `...contentProps` rest as remainder
// of props which aren't explicitly handled by this component.
//
// eslint-disable-next-line no-unused-vars
const { isOpen, onClose, position, children, className, ...contentProps } = this.props;
const {
isOpen,
onClose,
onClickOutside = onClose,
// Disable reason: We generate the `...contentProps` rest as remainder
// of props which aren't explicitly handled by this component.
//
// eslint-disable-next-line no-unused-vars
position,
children,
className,
...contentProps
} = this.props;
const [ yAxis, xAxis ] = this.getPositions();

if ( ! isOpen ) {
Expand All @@ -197,15 +224,20 @@ export class Popover extends Component {
'is-' + xAxis,
);

// Disable reason: We care to capture the _bubbled_ events from inputs
// within popover as inferring close intent.

/* eslint-disable jsx-a11y/no-static-element-interactions */
return (
<span ref={ this.bindNode( 'anchor' ) }>
{ createPortal(
<PopoverDetectOutside onClickOutside={ onClose }>
<PopoverDetectOutside onClickOutside={ onClickOutside }>
<div
ref={ this.bindNode( 'popover' ) }
className={ classes }
tabIndex="0"
{ ...contentProps }
onKeyDown={ this.maybeClose }
>
<div
ref={ this.bindNode( 'content' ) }
Expand All @@ -219,6 +251,7 @@ export class Popover extends Component {
) }
</span>
);
/* eslint-enable jsx-a11y/no-static-element-interactions */
}
}

Expand Down
22 changes: 11 additions & 11 deletions editor/inserter/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,15 @@ class Inserter extends Component {
}

insertBlock( name ) {
if ( name ) {
const {
insertionPoint,
onInsertBlock,
} = this.props;
onInsertBlock(
name,
insertionPoint
);
}
const {
insertionPoint,
onInsertBlock,
} = this.props;

onInsertBlock(
name,
insertionPoint
);

this.close();
}
Expand All @@ -89,7 +88,8 @@ class Inserter extends Component {
<Popover
isOpen={ opened }
position={ position }
onClose={ this.closeOnClickOutside }
onClose={ this.close }
onClickOutside={ this.closeOnClickOutside }
>
<InserterMenu onSelect={ this.insertBlock } />
</Popover>
Expand Down
9 changes: 2 additions & 7 deletions editor/inserter/menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { connect } from 'react-redux';
*/
import { __, _n, sprintf } from '@wordpress/i18n';
import { Component } from '@wordpress/element';
import { withFocusReturn, withInstanceId, withSpokenMessages } from '@wordpress/components';
import { withInstanceId, withSpokenMessages } from '@wordpress/components';
import { keycodes } from '@wordpress/utils';
import { getCategories, getBlockTypes, BlockIcon } from '@wordpress/blocks';

Expand All @@ -20,7 +20,7 @@ import './style.scss';
import { getBlocks, getRecentlyUsedBlocks } from '../selectors';
import { showInsertionPoint, hideInsertionPoint } from '../actions';

const { TAB, ESCAPE, LEFT, UP, RIGHT, DOWN } = keycodes;
const { TAB, LEFT, UP, RIGHT, DOWN } = keycodes;

export const searchBlocks = ( blocks, searchTerm ) => {
const normalizedSearchTerm = searchTerm.toLowerCase().trim();
Expand Down Expand Up @@ -233,11 +233,7 @@ export class InserterMenu extends Component {
keydown.preventDefault();
this.focusNext( this );
break;
case ESCAPE:
keydown.preventDefault();
this.props.onSelect( null );

break;
case LEFT:
if ( this.state.currentFocus === 'search' ) {
return;
Expand Down Expand Up @@ -411,6 +407,5 @@ const connectComponent = connect(
export default flow(
withInstanceId,
withSpokenMessages,
withFocusReturn,
connectComponent
)( InserterMenu );
12 changes: 7 additions & 5 deletions editor/sidebar/post-schedule/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
* External dependencies
*/
import { connect } from 'react-redux';
import clickOutside from 'react-click-outside';
import DatePicker from 'react-datepicker';
import moment from 'moment';
import { flowRight } from 'lodash';
Expand All @@ -26,17 +25,20 @@ import { editPost } from '../../actions';
export class PostSchedule extends Component {
constructor() {
super( ...arguments );

this.toggleDialog = this.toggleDialog.bind( this );
this.closeDialog = this.closeDialog.bind( this );

this.state = {
opened: false,
};
this.toggleDialog = this.toggleDialog.bind( this );
}

toggleDialog() {
this.setState( ( state ) => ( { opened: ! state.opened } ) );
}

handleClickOutside() {
closeDialog() {
this.setState( { opened: false } );
}

Expand Down Expand Up @@ -77,6 +79,7 @@ export class PostSchedule extends Component {
<Popover
position="bottom left"
isOpen={ this.state.opened }
onClose={ this.closeDialog }
className="editor-post-schedule__dialog"
>
<DatePicker
Expand Down Expand Up @@ -120,6 +123,5 @@ const applyWithAPIData = withAPIData( () => {

export default flowRight(
applyConnect,
applyWithAPIData,
clickOutside
applyWithAPIData
)( PostSchedule );
12 changes: 10 additions & 2 deletions editor/sidebar/post-visibility/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export class PostVisibility extends Component {
this.toggleDialog = this.toggleDialog.bind( this );
this.stopPropagation = this.stopPropagation.bind( this );
this.closeOnClickOutside = this.closeOnClickOutside.bind( this );
this.close = this.close.bind( this );
this.setPublic = this.setPublic.bind( this );
this.setPrivate = this.setPrivate.bind( this );
this.setPasswordProtected = this.setPasswordProtected.bind( this );
Expand All @@ -48,8 +49,14 @@ export class PostVisibility extends Component {
}

closeOnClickOutside( event ) {
if ( ! this.buttonNode.contains( event.target ) ) {
this.close();
}
}

close() {
const { opened } = this.state;
if ( opened && ! this.buttonNode.contains( event.target ) ) {
if ( opened ) {
this.toggleDialog();
}
}
Expand Down Expand Up @@ -133,7 +140,8 @@ export class PostVisibility extends Component {
<Popover
position="bottom left"
isOpen={ this.state.opened }
onClose={ this.closeOnClickOutside }
onClickOutside={ this.closeOnClickOutside }
onClose={ this.close }
onClick={ this.stopPropagation }
className="editor-post-visibility__dialog"
>
Expand Down