Skip to content

Commit

Permalink
Components: Add onFocusOutside alternative to Popover onClickOutside
Browse files Browse the repository at this point in the history
  • Loading branch information
aduth committed Apr 5, 2019
1 parent 6cb0b32 commit 302f25d
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 30 deletions.
1 change: 1 addition & 0 deletions lib/packages-dependencies.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
'wp-a11y',
'wp-api-fetch',
'wp-compose',
'wp-deprecated',
'wp-dom',
'wp-element',
'wp-hooks',
Expand Down
1 change: 1 addition & 0 deletions package-lock.json

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

7 changes: 6 additions & 1 deletion packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
## 7.3.0 (Unreleased)
## Unreleased

### New Features

- Added a new `render` property to `FormFileUpload` component. Allowing users of the component to custom the UI for their needs.
- Added a new `BaseControl.VisualLabel` component.
- Added a new `preview` prop to the `Placeholder` component which allows to display a preview, for example a media preview when the Placeholder is used in media editing contexts.
- Added a new `onFocusOutside` prop to the `Popover` component which serves in place of the existing `onClickOutside` to more accurately reflect all causes for focus transition.

### Bug fixes

- Fix `instanceId` prop passed through to `Button` component via `MenuItems` producing React console error. Fixed by removing the unnecessary use of `withInstanceId` on the `MenuItems` component [#14599](https://github.com/WordPress/gutenberg/pull/14599)

### Deprecations

- The `Popover` component `onClickOutside` prop has been deprecated. Use `onFocusOutside` instead.

## 7.2.0 (2019-03-20)

### Improvements
Expand Down
1 change: 1 addition & 0 deletions packages/components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"@wordpress/a11y": "file:../a11y",
"@wordpress/api-fetch": "file:../api-fetch",
"@wordpress/compose": "file:../compose",
"@wordpress/deprecated": "file:../deprecated",
"@wordpress/dom": "file:../dom",
"@wordpress/element": "file:../element",
"@wordpress/hooks": "file:../hooks",
Expand Down
6 changes: 4 additions & 2 deletions packages/components/src/popover/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,11 @@ A callback invoked when the popover should be closed.
- Type: `Function`
- Required: No

### onClickOutside
### onFocusOutside

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`.
A callback invoked when the focus leaves the opened popover. This should only be provided in advanced use-cases when a Popover should close under specific circumstances; for example, if the new `document.activeElement` is content of or otherwise controlling Popover visibility.

Defaults to `onClose` when not provided.

- Type: `Function`
- Required: No
Expand Down
17 changes: 7 additions & 10 deletions packages/components/src/popover/detect-outside.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,21 @@
/**
* External dependencies
* WordPress dependencies
*/
import clickOutside from 'react-click-outside';
import { Component } from '@wordpress/element';

/**
* WordPress dependencies
* Internal dependencies
*/
import { Component } from '@wordpress/element';
import withFocusOutside from '../higher-order/with-focus-outside';

class PopoverDetectOutside extends Component {
handleClickOutside( event ) {
const { onClickOutside } = this.props;
if ( onClickOutside ) {
onClickOutside( event );
}
handleFocusOutside( event ) {
this.props.onFocusOutside( event );
}

render() {
return this.props.children;
}
}

export default clickOutside( PopoverDetectOutside );
export default withFocusOutside( PopoverDetectOutside );
48 changes: 46 additions & 2 deletions packages/components/src/popover/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Component, createRef } from '@wordpress/element';
import { focus } from '@wordpress/dom';
import { ESCAPE } from '@wordpress/keycodes';
import isShallowEqual from '@wordpress/is-shallow-equal';
import deprecated from '@wordpress/deprecated';

/**
* Internal dependencies
Expand Down Expand Up @@ -40,6 +41,7 @@ class Popover extends Component {
this.getAnchorRect = this.getAnchorRect.bind( this );
this.computePopoverPosition = this.computePopoverPosition.bind( this );
this.maybeClose = this.maybeClose.bind( this );
this.onFocusOutside = this.onFocusOutside.bind( this );
this.throttledRefresh = this.throttledRefresh.bind( this );
this.refresh = this.refresh.bind( this );
this.refreshOnAnchorMove = this.refreshOnAnchorMove.bind( this );
Expand Down Expand Up @@ -94,6 +96,47 @@ class Popover extends Component {
this.toggleAutoRefresh( false );
}

/**
* Shims an onFocusOutside callback to be compatible with a deprecated
* onClickOutside prop function, if provided.
*
* @param {FocusEvent} event Focus event from onFocusOutside.
*/
onFocusOutside( event ) {
const { onClickOutside, onFocusOutside, onClose } = this.props;

// Defer to given `onFocusOutside` if specified. Call `onClose` only if
// both `onFocusOutside` and `onClickOutside` are unspecified. Doing so
// assures backwards-compatibility for prior `onClickOutside` default.
if ( onFocusOutside ) {
onFocusOutside( event );
return;
} else if ( ! onClickOutside ) {
onClose();
return;
}

// Simulate MouseEvent using FocusEvent#relatedTarget as emulated click
// target. MouseEvent constructor is unsupported in Internet Explorer.
let clickEvent;
try {
clickEvent = new window.MouseEvent( 'click' );
} catch ( error ) {
clickEvent = document.createEvent( 'MouseEvent' );
clickEvent.initMouseEvent( 'click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null );
}

Object.defineProperty( clickEvent, 'target', {
get: () => event.relatedTarget,
} );

deprecated( 'Popover onClickOutside prop', {
alternative: 'onFocusOutside',
} );

onClickOutside( clickEvent );
}

toggleAutoRefresh( isActive ) {
window.cancelAnimationFrame( this.rafHandle );

Expand Down Expand Up @@ -254,7 +297,6 @@ class Popover extends Component {
onClose,
children,
className,
onClickOutside = onClose,
noArrow,
// Disable reason: We generate the `...contentProps` rest as remainder
// of props which aren't explicitly handled by this component.
Expand All @@ -265,6 +307,8 @@ class Popover extends Component {
getAnchorRect,
expandOnMobile,
animate = true,
onClickOutside,
onFocusOutside,
/* eslint-enable no-unused-vars */
...contentProps
} = this.props;
Expand Down Expand Up @@ -308,7 +352,7 @@ class Popover extends Component {

/* eslint-disable jsx-a11y/no-static-element-interactions */
let content = (
<PopoverDetectOutside onClickOutside={ onClickOutside }>
<PopoverDetectOutside onFocusOutside={ this.onFocusOutside }>
<Animate
type={ animate && isReadyToAnimate ? 'appear' : null }
options={ { origin: animateYAxis + ' ' + animateXAxis } }
Expand Down
34 changes: 19 additions & 15 deletions packages/components/src/popover/test/__snapshots__/index.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,18 @@ exports[`Popover #render() should pass additional props to portaled element 1`]
tabindex="-1"
>
<div>
<div
class="components-popover is-bottom is-center components-animate__appear is-from-top"
role="tooltip"
style=""
>
<div>
<div
class="components-popover__content"
tabindex="-1"
class="components-popover is-bottom is-center components-animate__appear is-from-top"
role="tooltip"
style=""
>
Hello
<div
class="components-popover__content"
tabindex="-1"
>
Hello
</div>
</div>
</div>
</div>
Expand All @@ -29,15 +31,17 @@ exports[`Popover #render() should render content 1`] = `
tabindex="-1"
>
<div>
<div
class="components-popover is-bottom is-center components-animate__appear is-from-top"
style=""
>
<div>
<div
class="components-popover__content"
tabindex="-1"
class="components-popover is-bottom is-center components-animate__appear is-from-top"
style=""
>
Hello
<div
class="components-popover__content"
tabindex="-1"
>
Hello
</div>
</div>
</div>
</div>
Expand Down

0 comments on commit 302f25d

Please sign in to comment.