@wordpress/compose 5.16.1-next.4d3b314fd5.0 → 5.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/README.md +3 -0
- package/build/higher-order/with-global-events/listener.js +7 -7
- package/build/higher-order/with-global-events/listener.js.map +1 -1
- package/build/higher-order/with-safe-timeout/index.js +1 -7
- package/build/higher-order/with-safe-timeout/index.js.map +1 -1
- package/build/hooks/use-disabled/index.js +10 -104
- package/build/hooks/use-disabled/index.js.map +1 -1
- package/build/hooks/use-focus-outside/index.js +2 -8
- package/build/hooks/use-focus-outside/index.js.map +1 -1
- package/build/hooks/use-focus-outside/index.native.js +2 -8
- package/build/hooks/use-focus-outside/index.native.js.map +1 -1
- package/build/hooks/use-keyboard-shortcut/index.js +2 -3
- package/build/hooks/use-keyboard-shortcut/index.js.map +1 -1
- package/build/hooks/use-merge-refs/index.js +4 -2
- package/build/hooks/use-merge-refs/index.js.map +1 -1
- package/build/hooks/use-resize-observer/index.native.js +1 -0
- package/build/hooks/use-resize-observer/index.native.js.map +1 -1
- package/build-module/higher-order/with-global-events/listener.js +7 -6
- package/build-module/higher-order/with-global-events/listener.js.map +1 -1
- package/build-module/higher-order/with-safe-timeout/index.js +1 -6
- package/build-module/higher-order/with-safe-timeout/index.js.map +1 -1
- package/build-module/hooks/use-disabled/index.js +10 -102
- package/build-module/hooks/use-disabled/index.js.map +1 -1
- package/build-module/hooks/use-focus-outside/index.js +2 -7
- package/build-module/hooks/use-focus-outside/index.js.map +1 -1
- package/build-module/hooks/use-focus-outside/index.native.js +2 -7
- package/build-module/hooks/use-focus-outside/index.native.js.map +1 -1
- package/build-module/hooks/use-keyboard-shortcut/index.js +2 -2
- package/build-module/hooks/use-keyboard-shortcut/index.js.map +1 -1
- package/build-module/hooks/use-merge-refs/index.js +4 -2
- package/build-module/hooks/use-merge-refs/index.js.map +1 -1
- package/build-module/hooks/use-resize-observer/index.native.js +1 -0
- package/build-module/hooks/use-resize-observer/index.native.js.map +1 -1
- package/build-types/higher-order/with-global-events/listener.d.ts.map +1 -1
- package/build-types/higher-order/with-safe-timeout/index.d.ts.map +1 -1
- package/build-types/hooks/use-disabled/index.d.ts +4 -1
- package/build-types/hooks/use-disabled/index.d.ts.map +1 -1
- package/build-types/hooks/use-focus-outside/index.d.ts.map +1 -1
- package/build-types/hooks/use-keyboard-shortcut/index.d.ts.map +1 -1
- package/build-types/hooks/use-merge-refs/index.d.ts.map +1 -1
- package/package.json +8 -10
- package/src/higher-order/with-global-events/listener.js +6 -8
- package/src/higher-order/with-global-events/test/index.js +18 -29
- package/src/higher-order/with-safe-timeout/index.tsx +1 -6
- package/src/higher-order/with-state/test/index.js +13 -26
- package/src/hooks/use-disabled/index.ts +81 -0
- package/src/hooks/use-disabled/test/index.js +4 -36
- package/src/hooks/use-focus-outside/index.js +2 -8
- package/src/hooks/use-focus-outside/index.native.js +2 -8
- package/src/hooks/use-focus-outside/test/index.js +44 -45
- package/src/hooks/use-instance-id/test/index.js +9 -16
- package/src/hooks/use-keyboard-shortcut/index.js +4 -2
- package/src/hooks/use-media-query/test/index.js +33 -53
- package/src/hooks/use-merge-refs/index.js +6 -1
- package/src/hooks/use-merge-refs/test/index.js +44 -0
- package/src/hooks/use-resize-observer/index.native.js +5 -1
- package/src/hooks/use-resize-observer/test/index.native.js +18 -22
- package/src/hooks/use-viewport-match/test/index.js +78 -78
- package/tsconfig.tsbuildinfo +1 -1
- package/src/hooks/use-disabled/index.js +0 -203
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/higher-order/with-safe-timeout/index.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/higher-order/with-safe-timeout/index.tsx"],"names":[],"mappings":"AAKA;;GAEG;AACH,OAAO,EAEN,iBAAiB,EACjB,oBAAoB,EACpB,MAAM,2CAA2C,CAAC;AAEnD;;;;;;;GAOG;AACH,UAAU,YAAY;IACrB,UAAU,EAAE,CAAE,EAAE,EAAE,MAAM,IAAI,EAAE,KAAK,EAAE,MAAM,KAAM,MAAM,CAAC;IACxD,YAAY,EAAE,CAAE,EAAE,EAAE,MAAM,KAAM,IAAI,CAAC;CACrC;AAED;;;GAGG;AACH,QAAA,MAAM,eAAe;;kBAMR,MAAM,EAAE;;uBAaF,MAAM,IAAI,SAAS,MAAM;yBASvB,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;CAkB1B,CAAC;AAEF,eAAe,eAAe,CAAC"}
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* (input fields, links, buttons, etc.) need to be disabled. This hook adds the
|
|
4
4
|
* behavior to disable nested DOM elements to the returned ref.
|
|
5
5
|
*
|
|
6
|
+
* If you can, prefer the use of the inert HTML attribute.
|
|
7
|
+
*
|
|
6
8
|
* @param {Object} config Configuration object.
|
|
7
9
|
* @param {boolean=} config.isDisabled Whether the element should be disabled.
|
|
8
10
|
* @return {import('react').RefCallback<HTMLElement>} Element Ref.
|
|
@@ -10,6 +12,7 @@
|
|
|
10
12
|
* @example
|
|
11
13
|
* ```js
|
|
12
14
|
* import { useDisabled } from '@wordpress/compose';
|
|
15
|
+
*
|
|
13
16
|
* const DisabledExample = () => {
|
|
14
17
|
* const disabledRef = useDisabled();
|
|
15
18
|
* return (
|
|
@@ -23,5 +26,5 @@
|
|
|
23
26
|
*/
|
|
24
27
|
export default function useDisabled({ isDisabled: isDisabledProp, }?: {
|
|
25
28
|
isDisabled?: boolean | undefined;
|
|
26
|
-
}):
|
|
29
|
+
}): (instance: Node | null) => void;
|
|
27
30
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/use-disabled/index.
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/use-disabled/index.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,CAAC,OAAO,UAAU,WAAW,CAAE,EACpC,UAAU,EAAE,cAAsB,GAClC;;CAAK,mCA8CL"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/use-focus-outside/index.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/use-focus-outside/index.js"],"names":[],"mappings":"AAgDA;;GAEG;AAEH;;;GAGG;AAEH;;;GAGG;AAEH;;GAEG;AAEH;;;;;;;;GAQG;AAEH;;;;;;;;;;GAUG;AACH,wDAPW,aAAa,GAGZ,uBAAuB,CAqGlC;oCA1KY,iBAAiB,GAAG,eAAe,GAAG,gBAAgB;6BAmCtD,OAAO,OAAO,EAAE,cAAc;oCAKhC,cAAc;;;;;wBAKX,aAAa;;8BAId,OAAO,OAAO,EAAE,gBAAgB,CAAC,wBAAwB,GAAG,SAAS,CAAC;;;;;aAKrE,aAAa;;;;YACb,aAAa;;;;iBACb,aAAa;;;;eACb,aAAa;;;;kBACb,aAAa;;;;gBACb,aAAa"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/use-keyboard-shortcut/index.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/use-keyboard-shortcut/index.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAYA;;;;;;;;;GASG;AAGH;;;;;;;;GAQG;AACH,gDAJW,MAAM,EAAE,GAAC,MAAM,gBACX,OAAO,WAAW,EAAE,qBAAqB,SAAS,MAAM,KAAK,IAAI,mDACrE,wBAAwB,QAyElC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/use-merge-refs/index.js"],"names":[],"mappings":"AA4BA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/use-merge-refs/index.js"],"names":[],"mappings":"AA4BA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,wIA2DC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wordpress/compose",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.18.0",
|
|
4
4
|
"description": "WordPress higher-order components (HOCs).",
|
|
5
5
|
"author": "The WordPress Contributors",
|
|
6
6
|
"license": "GPL-2.0-or-later",
|
|
@@ -30,17 +30,15 @@
|
|
|
30
30
|
"sideEffects": false,
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@babel/runtime": "^7.16.0",
|
|
33
|
-
"@types/lodash": "^4.14.172",
|
|
34
33
|
"@types/mousetrap": "^1.6.8",
|
|
35
|
-
"@wordpress/deprecated": "^3.
|
|
36
|
-
"@wordpress/dom": "^3.
|
|
37
|
-
"@wordpress/element": "^4.
|
|
38
|
-
"@wordpress/is-shallow-equal": "^4.
|
|
39
|
-
"@wordpress/keycodes": "^3.
|
|
40
|
-
"@wordpress/priority-queue": "^2.
|
|
34
|
+
"@wordpress/deprecated": "^3.20.0",
|
|
35
|
+
"@wordpress/dom": "^3.20.0",
|
|
36
|
+
"@wordpress/element": "^4.18.0",
|
|
37
|
+
"@wordpress/is-shallow-equal": "^4.20.0",
|
|
38
|
+
"@wordpress/keycodes": "^3.20.0",
|
|
39
|
+
"@wordpress/priority-queue": "^2.20.0",
|
|
41
40
|
"change-case": "^4.1.2",
|
|
42
41
|
"clipboard": "^2.0.8",
|
|
43
|
-
"lodash": "^4.17.21",
|
|
44
42
|
"mousetrap": "^1.6.5",
|
|
45
43
|
"use-memo-one": "^1.1.1"
|
|
46
44
|
},
|
|
@@ -50,5 +48,5 @@
|
|
|
50
48
|
"publishConfig": {
|
|
51
49
|
"access": "public"
|
|
52
50
|
},
|
|
53
|
-
"gitHead": "
|
|
51
|
+
"gitHead": "a2ff0e6471c88436dad0287beb88d1729aa6f5dd"
|
|
54
52
|
}
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* External dependencies
|
|
3
|
-
*/
|
|
4
|
-
import { without } from 'lodash';
|
|
5
|
-
|
|
6
1
|
/**
|
|
7
2
|
* Class responsible for orchestrating event handling on the global window,
|
|
8
3
|
* binding a single event to be shared across all handling instances, and
|
|
@@ -27,9 +22,12 @@ class Listener {
|
|
|
27
22
|
}
|
|
28
23
|
|
|
29
24
|
remove( /** @type {any} */ eventType, /** @type {any} */ instance ) {
|
|
30
|
-
this.listeners[ eventType ]
|
|
31
|
-
|
|
32
|
-
|
|
25
|
+
if ( ! this.listeners[ eventType ] ) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
this.listeners[ eventType ] = this.listeners[ eventType ].filter(
|
|
30
|
+
( /** @type {any} */ listener ) => listener !== instance
|
|
33
31
|
);
|
|
34
32
|
|
|
35
33
|
if ( ! this.listeners[ eventType ].length ) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* External dependencies
|
|
3
3
|
*/
|
|
4
|
-
import
|
|
4
|
+
import { render, screen } from '@testing-library/react';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* WordPress dependencies
|
|
@@ -30,8 +30,6 @@ jest.mock( '../listener', () => {
|
|
|
30
30
|
} );
|
|
31
31
|
|
|
32
32
|
describe( 'withGlobalEvents', () => {
|
|
33
|
-
let wrapper;
|
|
34
|
-
|
|
35
33
|
class OriginalComponent extends Component {
|
|
36
34
|
handleResize( event ) {
|
|
37
35
|
this.props.onResize( event );
|
|
@@ -50,51 +48,42 @@ describe( 'withGlobalEvents', () => {
|
|
|
50
48
|
jest.clearAllMocks();
|
|
51
49
|
} );
|
|
52
50
|
|
|
53
|
-
|
|
54
|
-
if ( wrapper ) {
|
|
55
|
-
wrapper.unmount();
|
|
56
|
-
wrapper = null;
|
|
57
|
-
}
|
|
58
|
-
} );
|
|
59
|
-
|
|
60
|
-
function mountEnhancedComponent( props = {} ) {
|
|
51
|
+
it( 'renders with original component', () => {
|
|
61
52
|
const EnhancedComponent = withGlobalEvents( {
|
|
62
53
|
resize: 'handleResize',
|
|
63
54
|
} )( OriginalComponent );
|
|
64
55
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
wrapper = TestRenderer.create(
|
|
68
|
-
<EnhancedComponent { ...props }>Hello</EnhancedComponent>
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
it( 'renders with original component', () => {
|
|
73
|
-
mountEnhancedComponent();
|
|
56
|
+
render( <EnhancedComponent ref={ () => {} }>Hello</EnhancedComponent> );
|
|
74
57
|
|
|
75
58
|
expect( console ).toHaveWarned();
|
|
76
|
-
expect(
|
|
77
|
-
'Hello'
|
|
78
|
-
);
|
|
59
|
+
expect( screen.getByText( 'Hello' ) ).toBeVisible();
|
|
79
60
|
} );
|
|
80
61
|
|
|
81
62
|
it( 'binds events from passed object', () => {
|
|
82
|
-
|
|
63
|
+
const EnhancedComponent = withGlobalEvents( {
|
|
64
|
+
resize: 'handleResize',
|
|
65
|
+
} )( OriginalComponent );
|
|
83
66
|
|
|
84
|
-
|
|
85
|
-
const hocInstance =
|
|
86
|
-
wrapper.root.findByType( OriginalComponent ).parent.instance;
|
|
67
|
+
render( <EnhancedComponent ref={ () => {} }>Hello</EnhancedComponent> );
|
|
87
68
|
|
|
88
69
|
expect( Listener._instance.add ).toHaveBeenCalledWith(
|
|
89
70
|
'resize',
|
|
90
|
-
|
|
71
|
+
// If not `undefined`, then we consider handlers were properly bound to the wrapper component.
|
|
72
|
+
expect.any( Object )
|
|
91
73
|
);
|
|
92
74
|
} );
|
|
93
75
|
|
|
94
76
|
it( 'handles events', () => {
|
|
77
|
+
const EnhancedComponent = withGlobalEvents( {
|
|
78
|
+
resize: 'handleResize',
|
|
79
|
+
} )( OriginalComponent );
|
|
95
80
|
const onResize = jest.fn();
|
|
96
81
|
|
|
97
|
-
|
|
82
|
+
render(
|
|
83
|
+
<EnhancedComponent ref={ () => {} } onResize={ onResize }>
|
|
84
|
+
Hello
|
|
85
|
+
</EnhancedComponent>
|
|
86
|
+
);
|
|
98
87
|
|
|
99
88
|
const event = { type: 'resize' };
|
|
100
89
|
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* External dependencies
|
|
3
|
-
*/
|
|
4
|
-
import { without } from 'lodash';
|
|
5
|
-
|
|
6
1
|
/**
|
|
7
2
|
* WordPress dependencies
|
|
8
3
|
*/
|
|
@@ -64,7 +59,7 @@ const withSafeTimeout = createHigherOrderComponent(
|
|
|
64
59
|
|
|
65
60
|
clearTimeout( id: number ) {
|
|
66
61
|
clearTimeout( id );
|
|
67
|
-
this.timeouts
|
|
62
|
+
this.timeouts.filter( ( timeoutId ) => timeoutId !== id );
|
|
68
63
|
}
|
|
69
64
|
|
|
70
65
|
render() {
|
|
@@ -1,31 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* External dependencies
|
|
3
3
|
*/
|
|
4
|
-
import
|
|
4
|
+
import { render, screen } from '@testing-library/react';
|
|
5
|
+
import userEvent from '@testing-library/user-event';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Internal dependencies
|
|
8
9
|
*/
|
|
9
10
|
import withState from '../';
|
|
10
11
|
|
|
11
|
-
/**
|
|
12
|
-
* WordPress dependencies
|
|
13
|
-
*/
|
|
14
|
-
import { Component } from '@wordpress/element';
|
|
15
|
-
|
|
16
|
-
// This is needed because TestUtils does not accept a stateless component.
|
|
17
|
-
// anything run through a HOC ends up as a stateless component.
|
|
18
|
-
const getTestComponent = ( WrappedComponent ) => {
|
|
19
|
-
class TestComponent extends Component {
|
|
20
|
-
render() {
|
|
21
|
-
return <WrappedComponent { ...this.props } />;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
return <TestComponent />;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
12
|
describe( 'withState', () => {
|
|
28
|
-
it( 'should pass initial state and allow updates', () => {
|
|
13
|
+
it( 'should pass initial state and allow updates', async () => {
|
|
14
|
+
const user = userEvent.setup( {
|
|
15
|
+
advanceTimers: jest.advanceTimersByTime,
|
|
16
|
+
} );
|
|
29
17
|
const EnhancedComponent = withState( {
|
|
30
18
|
count: 0,
|
|
31
19
|
} )( ( { count, setState } ) => (
|
|
@@ -38,16 +26,15 @@ describe( 'withState', () => {
|
|
|
38
26
|
</button>
|
|
39
27
|
) );
|
|
40
28
|
|
|
41
|
-
|
|
42
|
-
getTestComponent( EnhancedComponent )
|
|
43
|
-
);
|
|
29
|
+
render( <EnhancedComponent /> );
|
|
44
30
|
|
|
45
|
-
const
|
|
46
|
-
TestUtils.findRenderedDOMComponentWithTag( wrapper, 'button' );
|
|
31
|
+
const button = screen.getByRole( 'button' );
|
|
47
32
|
|
|
48
33
|
expect( console ).toHaveWarned();
|
|
49
|
-
expect(
|
|
50
|
-
|
|
51
|
-
|
|
34
|
+
expect( button ).toHaveTextContent( '0' );
|
|
35
|
+
|
|
36
|
+
await user.click( button );
|
|
37
|
+
|
|
38
|
+
expect( button ).toHaveTextContent( '1' );
|
|
52
39
|
} );
|
|
53
40
|
} );
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { debounce } from '../../utils/debounce';
|
|
5
|
+
import useRefEffect from '../use-ref-effect';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* In some circumstances, such as block previews, all focusable DOM elements
|
|
9
|
+
* (input fields, links, buttons, etc.) need to be disabled. This hook adds the
|
|
10
|
+
* behavior to disable nested DOM elements to the returned ref.
|
|
11
|
+
*
|
|
12
|
+
* If you can, prefer the use of the inert HTML attribute.
|
|
13
|
+
*
|
|
14
|
+
* @param {Object} config Configuration object.
|
|
15
|
+
* @param {boolean=} config.isDisabled Whether the element should be disabled.
|
|
16
|
+
* @return {import('react').RefCallback<HTMLElement>} Element Ref.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```js
|
|
20
|
+
* import { useDisabled } from '@wordpress/compose';
|
|
21
|
+
*
|
|
22
|
+
* const DisabledExample = () => {
|
|
23
|
+
* const disabledRef = useDisabled();
|
|
24
|
+
* return (
|
|
25
|
+
* <div ref={ disabledRef }>
|
|
26
|
+
* <a href="#">This link will have tabindex set to -1</a>
|
|
27
|
+
* <input placeholder="This input will have the disabled attribute added to it." type="text" />
|
|
28
|
+
* </div>
|
|
29
|
+
* );
|
|
30
|
+
* };
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export default function useDisabled( {
|
|
34
|
+
isDisabled: isDisabledProp = false,
|
|
35
|
+
} = {} ) {
|
|
36
|
+
return useRefEffect(
|
|
37
|
+
( node ) => {
|
|
38
|
+
if ( isDisabledProp ) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** A variable keeping track of the previous updates in order to restore them. */
|
|
43
|
+
const updates: Function[] = [];
|
|
44
|
+
const disable = () => {
|
|
45
|
+
node.childNodes.forEach( ( child ) => {
|
|
46
|
+
if ( ! ( child instanceof HTMLElement ) ) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if ( ! child.getAttribute( 'inert' ) ) {
|
|
50
|
+
child.setAttribute( 'inert', 'true' );
|
|
51
|
+
updates.push( () => {
|
|
52
|
+
child.removeAttribute( 'inert' );
|
|
53
|
+
} );
|
|
54
|
+
}
|
|
55
|
+
} );
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// Debounce re-disable since disabling process itself will incur
|
|
59
|
+
// additional mutations which should be ignored.
|
|
60
|
+
const debouncedDisable = debounce( disable, 0, {
|
|
61
|
+
leading: true,
|
|
62
|
+
} );
|
|
63
|
+
disable();
|
|
64
|
+
|
|
65
|
+
/** @type {MutationObserver | undefined} */
|
|
66
|
+
const observer = new window.MutationObserver( debouncedDisable );
|
|
67
|
+
observer.observe( node, {
|
|
68
|
+
childList: true,
|
|
69
|
+
} );
|
|
70
|
+
|
|
71
|
+
return () => {
|
|
72
|
+
if ( observer ) {
|
|
73
|
+
observer.disconnect();
|
|
74
|
+
}
|
|
75
|
+
debouncedDisable.cancel();
|
|
76
|
+
updates.forEach( ( update ) => update() );
|
|
77
|
+
};
|
|
78
|
+
},
|
|
79
|
+
[ isDisabledProp ]
|
|
80
|
+
);
|
|
81
|
+
}
|
|
@@ -13,36 +13,6 @@ import { forwardRef } from '@wordpress/element';
|
|
|
13
13
|
*/
|
|
14
14
|
import useDisabled from '../';
|
|
15
15
|
|
|
16
|
-
jest.mock( '@wordpress/dom', () => {
|
|
17
|
-
const focus = jest.requireActual( '../../../../../dom/src' ).focus;
|
|
18
|
-
|
|
19
|
-
return {
|
|
20
|
-
focus: {
|
|
21
|
-
...focus,
|
|
22
|
-
focusable: {
|
|
23
|
-
...focus.focusable,
|
|
24
|
-
find( context ) {
|
|
25
|
-
// In JSDOM, all elements have zero'd widths and height.
|
|
26
|
-
// This is a metric for focusable's `isVisible`, so find
|
|
27
|
-
// and apply an arbitrary non-zero width.
|
|
28
|
-
Array.from( context.querySelectorAll( '*' ) ).forEach(
|
|
29
|
-
( element ) => {
|
|
30
|
-
Object.defineProperties( element, {
|
|
31
|
-
offsetWidth: {
|
|
32
|
-
get: () => 1,
|
|
33
|
-
configurable: true,
|
|
34
|
-
},
|
|
35
|
-
} );
|
|
36
|
-
}
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
return focus.focusable.find( ...arguments );
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
},
|
|
43
|
-
};
|
|
44
|
-
} );
|
|
45
|
-
|
|
46
16
|
jest.useRealTimers();
|
|
47
17
|
|
|
48
18
|
describe( 'useDisabled', () => {
|
|
@@ -69,11 +39,9 @@ describe( 'useDisabled', () => {
|
|
|
69
39
|
const link = screen.getByRole( 'link' );
|
|
70
40
|
const p = container.querySelector( 'p' );
|
|
71
41
|
|
|
72
|
-
expect( input.
|
|
73
|
-
expect( link.
|
|
74
|
-
expect( p.
|
|
75
|
-
expect( p.hasAttribute( 'tabindex' ) ).toBe( false );
|
|
76
|
-
expect( p.hasAttribute( 'disabled' ) ).toBe( false );
|
|
42
|
+
expect( input ).toHaveAttribute( 'inert', 'true' );
|
|
43
|
+
expect( link ).toHaveAttribute( 'inert', 'true' );
|
|
44
|
+
expect( p ).toHaveAttribute( 'inert', 'true' );
|
|
77
45
|
} );
|
|
78
46
|
|
|
79
47
|
it( 'will disable an element rendered in an update to the component', async () => {
|
|
@@ -86,7 +54,7 @@ describe( 'useDisabled', () => {
|
|
|
86
54
|
|
|
87
55
|
const button = screen.getByText( 'Button' );
|
|
88
56
|
await waitFor( () => {
|
|
89
|
-
expect( button.
|
|
57
|
+
expect( button ).toHaveAttribute( 'inert', 'true' );
|
|
90
58
|
} );
|
|
91
59
|
} );
|
|
92
60
|
} );
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* External dependencies
|
|
3
|
-
*/
|
|
4
|
-
import { includes } from 'lodash';
|
|
5
|
-
|
|
6
1
|
/**
|
|
7
2
|
* WordPress dependencies
|
|
8
3
|
*/
|
|
@@ -42,8 +37,7 @@ function isFocusNormalizedButton( eventTarget ) {
|
|
|
42
37
|
return true;
|
|
43
38
|
|
|
44
39
|
case 'INPUT':
|
|
45
|
-
return includes(
|
|
46
|
-
INPUT_BUTTON_TYPES,
|
|
40
|
+
return INPUT_BUTTON_TYPES.includes(
|
|
47
41
|
/** @type {HTMLInputElement} */ ( eventTarget ).type
|
|
48
42
|
);
|
|
49
43
|
}
|
|
@@ -136,7 +130,7 @@ export default function useFocusOutside( onFocusOutside ) {
|
|
|
136
130
|
*/
|
|
137
131
|
const normalizeButtonFocus = useCallback( ( event ) => {
|
|
138
132
|
const { type, target } = event;
|
|
139
|
-
const isInteractionEnd =
|
|
133
|
+
const isInteractionEnd = [ 'mouseup', 'touchend' ].includes( type );
|
|
140
134
|
|
|
141
135
|
if ( isInteractionEnd ) {
|
|
142
136
|
preventBlurCheck.current = false;
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* External dependencies
|
|
3
|
-
*/
|
|
4
|
-
import { includes } from 'lodash';
|
|
5
|
-
|
|
6
1
|
/**
|
|
7
2
|
* WordPress dependencies
|
|
8
3
|
*/
|
|
@@ -39,8 +34,7 @@ function isFocusNormalizedButton( eventTarget ) {
|
|
|
39
34
|
return true;
|
|
40
35
|
|
|
41
36
|
case 'INPUT':
|
|
42
|
-
return includes(
|
|
43
|
-
INPUT_BUTTON_TYPES,
|
|
37
|
+
return INPUT_BUTTON_TYPES.includes(
|
|
44
38
|
/** @type {HTMLInputElement} */ ( eventTarget ).type
|
|
45
39
|
);
|
|
46
40
|
}
|
|
@@ -133,7 +127,7 @@ export default function useFocusOutside( onFocusOutside ) {
|
|
|
133
127
|
*/
|
|
134
128
|
const normalizeButtonFocus = useCallback( ( event ) => {
|
|
135
129
|
const { type, target } = event;
|
|
136
|
-
const isInteractionEnd =
|
|
130
|
+
const isInteractionEnd = [ 'mouseup', 'touchend' ].includes( type );
|
|
137
131
|
|
|
138
132
|
if ( isInteractionEnd ) {
|
|
139
133
|
preventBlurCheck.current = false;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* External dependencies
|
|
3
3
|
*/
|
|
4
|
-
import
|
|
4
|
+
import { render, screen } from '@testing-library/react';
|
|
5
|
+
import userEvent from '@testing-library/user-event';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* WordPress dependencies
|
|
@@ -12,38 +13,24 @@ import { Component } from '@wordpress/element';
|
|
|
12
13
|
* Internal dependencies
|
|
13
14
|
*/
|
|
14
15
|
import useFocusOutside from '../';
|
|
15
|
-
import ReactDOM from 'react-dom';
|
|
16
16
|
|
|
17
|
-
let
|
|
17
|
+
let onFocusOutside;
|
|
18
18
|
|
|
19
19
|
describe( 'useFocusOutside', () => {
|
|
20
20
|
let origHasFocus;
|
|
21
21
|
|
|
22
22
|
const FocusOutsideComponent = ( { onFocusOutside: callback } ) => (
|
|
23
23
|
<div { ...useFocusOutside( callback ) }>
|
|
24
|
-
<input />
|
|
24
|
+
<input type="text" />
|
|
25
25
|
<input type="button" />
|
|
26
26
|
</div>
|
|
27
27
|
);
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
class TestComponent extends Component {
|
|
33
|
-
render() {
|
|
34
|
-
return <WrappedComponent { ...props } />;
|
|
35
|
-
}
|
|
29
|
+
class TestComponent extends Component {
|
|
30
|
+
render() {
|
|
31
|
+
return <FocusOutsideComponent { ...this.props } />;
|
|
36
32
|
}
|
|
37
|
-
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
const simulateEvent = ( event, index = 0 ) => {
|
|
41
|
-
const element = TestUtils.scryRenderedDOMComponentsWithTag(
|
|
42
|
-
wrapper,
|
|
43
|
-
'input'
|
|
44
|
-
);
|
|
45
|
-
TestUtils.Simulate[ event ]( element[ index ] );
|
|
46
|
-
};
|
|
33
|
+
}
|
|
47
34
|
|
|
48
35
|
beforeEach( () => {
|
|
49
36
|
// Mock document.hasFocus() to always be true for testing
|
|
@@ -52,9 +39,6 @@ describe( 'useFocusOutside', () => {
|
|
|
52
39
|
document.hasFocus = () => true;
|
|
53
40
|
|
|
54
41
|
onFocusOutside = jest.fn();
|
|
55
|
-
wrapper = TestUtils.renderIntoDocument(
|
|
56
|
-
getTestComponent( FocusOutsideComponent, { onFocusOutside } )
|
|
57
|
-
);
|
|
58
42
|
} );
|
|
59
43
|
|
|
60
44
|
afterEach( () => {
|
|
@@ -62,24 +46,31 @@ describe( 'useFocusOutside', () => {
|
|
|
62
46
|
} );
|
|
63
47
|
|
|
64
48
|
it( 'should not call handler if focus shifts to element within component', () => {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
49
|
+
render( <TestComponent onFocusOutside={ onFocusOutside } /> );
|
|
50
|
+
|
|
51
|
+
const input = screen.getByRole( 'textbox' );
|
|
52
|
+
const button = screen.getByRole( 'button' );
|
|
53
|
+
|
|
54
|
+
input.focus();
|
|
55
|
+
input.blur();
|
|
56
|
+
button.focus();
|
|
68
57
|
|
|
69
58
|
jest.runAllTimers();
|
|
70
59
|
|
|
71
60
|
expect( onFocusOutside ).not.toHaveBeenCalled();
|
|
72
61
|
} );
|
|
73
62
|
|
|
74
|
-
it( 'should not call handler if focus transitions via click to button', () => {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
63
|
+
it( 'should not call handler if focus transitions via click to button', async () => {
|
|
64
|
+
const user = userEvent.setup( {
|
|
65
|
+
advanceTimers: jest.advanceTimersByTime,
|
|
66
|
+
} );
|
|
67
|
+
render( <TestComponent onFocusOutside={ onFocusOutside } /> );
|
|
68
|
+
|
|
69
|
+
const input = screen.getByRole( 'textbox' );
|
|
70
|
+
const button = screen.getByRole( 'button' );
|
|
78
71
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
// the normalization behavior tested here.
|
|
82
|
-
simulateEvent( 'mouseUp', 1 );
|
|
72
|
+
input.focus();
|
|
73
|
+
await user.click( button );
|
|
83
74
|
|
|
84
75
|
jest.runAllTimers();
|
|
85
76
|
|
|
@@ -87,8 +78,11 @@ describe( 'useFocusOutside', () => {
|
|
|
87
78
|
} );
|
|
88
79
|
|
|
89
80
|
it( 'should call handler if focus doesn’t shift to element within component', () => {
|
|
90
|
-
|
|
91
|
-
|
|
81
|
+
render( <TestComponent onFocusOutside={ onFocusOutside } /> );
|
|
82
|
+
|
|
83
|
+
const input = screen.getByRole( 'textbox' );
|
|
84
|
+
input.focus();
|
|
85
|
+
input.blur();
|
|
92
86
|
|
|
93
87
|
jest.runAllTimers();
|
|
94
88
|
|
|
@@ -96,12 +90,15 @@ describe( 'useFocusOutside', () => {
|
|
|
96
90
|
} );
|
|
97
91
|
|
|
98
92
|
it( 'should not call handler if focus shifts outside the component when the document does not have focus', () => {
|
|
93
|
+
render( <TestComponent onFocusOutside={ onFocusOutside } /> );
|
|
94
|
+
|
|
99
95
|
// Force document.hasFocus() to return false to simulate the window/document losing focus
|
|
100
96
|
// See https://developer.mozilla.org/en-US/docs/Web/API/Document/hasFocus.
|
|
101
97
|
document.hasFocus = () => false;
|
|
102
98
|
|
|
103
|
-
|
|
104
|
-
|
|
99
|
+
const input = screen.getByRole( 'textbox' );
|
|
100
|
+
input.focus();
|
|
101
|
+
input.blur();
|
|
105
102
|
|
|
106
103
|
jest.runAllTimers();
|
|
107
104
|
|
|
@@ -109,14 +106,16 @@ describe( 'useFocusOutside', () => {
|
|
|
109
106
|
} );
|
|
110
107
|
|
|
111
108
|
it( 'should cancel check when unmounting while queued', () => {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
ReactDOM.unmountComponentAtNode(
|
|
116
|
-
// eslint-disable-next-line react/no-find-dom-node
|
|
117
|
-
ReactDOM.findDOMNode( wrapper ).parentNode
|
|
109
|
+
const { rerender } = render(
|
|
110
|
+
<TestComponent onFocusOutside={ onFocusOutside } />
|
|
118
111
|
);
|
|
119
112
|
|
|
113
|
+
const input = screen.getByRole( 'textbox' );
|
|
114
|
+
input.focus();
|
|
115
|
+
input.blur();
|
|
116
|
+
|
|
117
|
+
rerender( <div /> );
|
|
118
|
+
|
|
120
119
|
jest.runAllTimers();
|
|
121
120
|
|
|
122
121
|
expect( onFocusOutside ).not.toHaveBeenCalled();
|