@wordpress/compose 8.7.2-next.v.202609031004.0 → 8.8.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 +5 -1
- package/build/hooks/use-resize-observer/use-resize-observer.cjs +9 -0
- package/build/hooks/use-resize-observer/use-resize-observer.cjs.map +2 -2
- package/build-module/hooks/use-resize-observer/use-resize-observer.mjs +10 -1
- package/build-module/hooks/use-resize-observer/use-resize-observer.mjs.map +2 -2
- package/build-types/hooks/use-resize-observer/use-resize-observer.d.ts.map +1 -1
- package/package.json +12 -12
- package/src/higher-order/pure/test/index.jsdom.test.jsx +1 -0
- package/src/higher-order/with-global-events/test/index.jsdom.test.jsx +18 -15
- package/src/higher-order/with-global-events/test/listener.jsdom.test.js +13 -4
- package/src/higher-order/with-instance-id/test/index.jsdom.test.jsx +1 -0
- package/src/higher-order/with-state/test/index.jsdom.test.jsx +1 -0
- package/src/hooks/use-copy-on-click/test/index.jsdom.test.tsx +18 -17
- package/src/hooks/use-copy-to-clipboard/test/index.jsdom.test.tsx +12 -11
- package/src/hooks/use-dialog/test/index.jsdom.test.tsx +14 -13
- package/src/hooks/use-disabled/test/index.jsdom.test.jsx +1 -0
- package/src/hooks/use-drop-zone/test/index.jsdom.test.jsx +1 -0
- package/src/hooks/use-focus-outside/test/index.jsdom.test.jsx +7 -8
- package/src/hooks/use-instance-id/test/index.jsdom.test.jsx +1 -0
- package/src/hooks/use-merge-refs/test/index.jsdom.test.jsx +1 -0
- package/src/hooks/use-observable-value/test/index.jsdom.test.jsx +2 -1
- package/src/hooks/use-resize-observer/test/index.jsdom.test.ts +37 -0
- package/src/hooks/use-resize-observer/use-resize-observer.ts +13 -1
- package/src/hooks/use-state-with-history/test/index.jsdom.test.jsx +1 -0
- package/src/hooks/use-viewport-match/test/index.jsdom.test.jsx +2 -3
- package/src/utils/subscribe-delegated-listener/test/index.jsdom.test.js +17 -16
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
-
## 8.
|
|
5
|
+
## 8.8.0 (2026-09-10)
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
- `useResizeObserver`: Disconnect the observer when the hook unmounts so queued callbacks cannot run after teardown ([#82687](https://github.com/WordPress/gutenberg/pull/82687)).
|
|
6
10
|
|
|
7
11
|
### Internal
|
|
8
12
|
|
|
@@ -39,6 +39,15 @@ function useResizeObserver(callback, resizeObserverOptions = {}) {
|
|
|
39
39
|
const callbackEvent = (0, import_use_event.default)(callback);
|
|
40
40
|
const observedElementRef = (0, import_element.useRef)(null);
|
|
41
41
|
const resizeObserverRef = (0, import_element.useRef)(void 0);
|
|
42
|
+
(0, import_element.useEffect)(() => {
|
|
43
|
+
if (observedElementRef.current) {
|
|
44
|
+
resizeObserverRef.current?.observe(
|
|
45
|
+
observedElementRef.current,
|
|
46
|
+
resizeObserverOptions
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
return () => resizeObserverRef.current?.disconnect();
|
|
50
|
+
}, []);
|
|
42
51
|
return (0, import_use_event.default)((element) => {
|
|
43
52
|
if (element === observedElementRef.current) {
|
|
44
53
|
return;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/hooks/use-resize-observer/use-resize-observer.ts"],
|
|
4
|
-
"sourcesContent": ["import { useRef } from '@wordpress/element';\nimport useEvent from '../use-event';\n\n// This is the current implementation of `useResizeObserver`.\n//\n// The legacy implementation is still supported for backwards compatibility.\n// This is achieved by overloading the exported function with both signatures,\n// and detecting which API is being used at runtime.\nexport function useResizeObserver< T extends HTMLElement >(\n\tcallback: ResizeObserverCallback,\n\tresizeObserverOptions: ResizeObserverOptions = {}\n): ( element?: T | null ) => void {\n\tconst callbackEvent = useEvent( callback );\n\n\tconst observedElementRef = useRef< T | null >( null );\n\tconst resizeObserverRef = useRef< ResizeObserver >( undefined );\n\treturn useEvent( ( element?: T | null ) => {\n\t\tif ( element === observedElementRef.current ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Set up `ResizeObserver`.\n\t\tresizeObserverRef.current ??= new ResizeObserver( callbackEvent );\n\t\tconst { current: resizeObserver } = resizeObserverRef;\n\n\t\t// Unobserve previous element.\n\t\tif ( observedElementRef.current ) {\n\t\t\tresizeObserver.unobserve( observedElementRef.current );\n\t\t}\n\n\t\t// Observe new element.\n\t\tobservedElementRef.current = element ?? null;\n\t\tif ( element ) {\n\t\t\tresizeObserver.observe( element, resizeObserverOptions );\n\t\t}\n\t} );\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,
|
|
4
|
+
"sourcesContent": ["import { useEffect, useRef } from '@wordpress/element';\nimport useEvent from '../use-event';\n\n// This is the current implementation of `useResizeObserver`.\n//\n// The legacy implementation is still supported for backwards compatibility.\n// This is achieved by overloading the exported function with both signatures,\n// and detecting which API is being used at runtime.\nexport function useResizeObserver< T extends HTMLElement >(\n\tcallback: ResizeObserverCallback,\n\tresizeObserverOptions: ResizeObserverOptions = {}\n): ( element?: T | null ) => void {\n\tconst callbackEvent = useEvent( callback );\n\n\tconst observedElementRef = useRef< T | null >( null );\n\tconst resizeObserverRef = useRef< ResizeObserver >( undefined );\n\tuseEffect( () => {\n\t\tif ( observedElementRef.current ) {\n\t\t\tresizeObserverRef.current?.observe(\n\t\t\t\tobservedElementRef.current,\n\t\t\t\tresizeObserverOptions\n\t\t\t);\n\t\t}\n\t\treturn () => resizeObserverRef.current?.disconnect();\n\t\t// This effect owns the observer lifecycle. The returned ref handles\n\t\t// element and option changes without recreating the observer.\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\n\t}, [] );\n\treturn useEvent( ( element?: T | null ) => {\n\t\tif ( element === observedElementRef.current ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Set up `ResizeObserver`.\n\t\tresizeObserverRef.current ??= new ResizeObserver( callbackEvent );\n\t\tconst { current: resizeObserver } = resizeObserverRef;\n\n\t\t// Unobserve previous element.\n\t\tif ( observedElementRef.current ) {\n\t\t\tresizeObserver.unobserve( observedElementRef.current );\n\t\t}\n\n\t\t// Observe new element.\n\t\tobservedElementRef.current = element ?? null;\n\t\tif ( element ) {\n\t\t\tresizeObserver.observe( element, resizeObserverOptions );\n\t\t}\n\t} );\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAAkC;AAClC,uBAAqB;AAOd,SAAS,kBACf,UACA,wBAA+C,CAAC,GACf;AACjC,QAAM,oBAAgB,iBAAAA,SAAU,QAAS;AAEzC,QAAM,yBAAqB,uBAAoB,IAAK;AACpD,QAAM,wBAAoB,uBAA0B,MAAU;AAC9D,gCAAW,MAAM;AAChB,QAAK,mBAAmB,SAAU;AACjC,wBAAkB,SAAS;AAAA,QAC1B,mBAAmB;AAAA,QACnB;AAAA,MACD;AAAA,IACD;AACA,WAAO,MAAM,kBAAkB,SAAS,WAAW;AAAA,EAIpD,GAAG,CAAC,CAAE;AACN,aAAO,iBAAAA,SAAU,CAAE,YAAwB;AAC1C,QAAK,YAAY,mBAAmB,SAAU;AAC7C;AAAA,IACD;AAGA,sBAAkB,YAAY,IAAI,eAAgB,aAAc;AAChE,UAAM,EAAE,SAAS,eAAe,IAAI;AAGpC,QAAK,mBAAmB,SAAU;AACjC,qBAAe,UAAW,mBAAmB,OAAQ;AAAA,IACtD;AAGA,uBAAmB,UAAU,WAAW;AACxC,QAAK,SAAU;AACd,qBAAe,QAAS,SAAS,qBAAsB;AAAA,IACxD;AAAA,EACD,CAAE;AACH;",
|
|
6
6
|
"names": ["useEvent"]
|
|
7
7
|
}
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
// packages/compose/src/hooks/use-resize-observer/use-resize-observer.ts
|
|
2
|
-
import { useRef } from "@wordpress/element";
|
|
2
|
+
import { useEffect, useRef } from "@wordpress/element";
|
|
3
3
|
import useEvent from "../use-event/index.mjs";
|
|
4
4
|
function useResizeObserver(callback, resizeObserverOptions = {}) {
|
|
5
5
|
const callbackEvent = useEvent(callback);
|
|
6
6
|
const observedElementRef = useRef(null);
|
|
7
7
|
const resizeObserverRef = useRef(void 0);
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
if (observedElementRef.current) {
|
|
10
|
+
resizeObserverRef.current?.observe(
|
|
11
|
+
observedElementRef.current,
|
|
12
|
+
resizeObserverOptions
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
return () => resizeObserverRef.current?.disconnect();
|
|
16
|
+
}, []);
|
|
8
17
|
return useEvent((element) => {
|
|
9
18
|
if (element === observedElementRef.current) {
|
|
10
19
|
return;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/hooks/use-resize-observer/use-resize-observer.ts"],
|
|
4
|
-
"sourcesContent": ["import { useRef } from '@wordpress/element';\nimport useEvent from '../use-event';\n\n// This is the current implementation of `useResizeObserver`.\n//\n// The legacy implementation is still supported for backwards compatibility.\n// This is achieved by overloading the exported function with both signatures,\n// and detecting which API is being used at runtime.\nexport function useResizeObserver< T extends HTMLElement >(\n\tcallback: ResizeObserverCallback,\n\tresizeObserverOptions: ResizeObserverOptions = {}\n): ( element?: T | null ) => void {\n\tconst callbackEvent = useEvent( callback );\n\n\tconst observedElementRef = useRef< T | null >( null );\n\tconst resizeObserverRef = useRef< ResizeObserver >( undefined );\n\treturn useEvent( ( element?: T | null ) => {\n\t\tif ( element === observedElementRef.current ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Set up `ResizeObserver`.\n\t\tresizeObserverRef.current ??= new ResizeObserver( callbackEvent );\n\t\tconst { current: resizeObserver } = resizeObserverRef;\n\n\t\t// Unobserve previous element.\n\t\tif ( observedElementRef.current ) {\n\t\t\tresizeObserver.unobserve( observedElementRef.current );\n\t\t}\n\n\t\t// Observe new element.\n\t\tobservedElementRef.current = element ?? null;\n\t\tif ( element ) {\n\t\t\tresizeObserver.observe( element, resizeObserverOptions );\n\t\t}\n\t} );\n}\n"],
|
|
5
|
-
"mappings": ";AAAA,SAAS,cAAc;
|
|
4
|
+
"sourcesContent": ["import { useEffect, useRef } from '@wordpress/element';\nimport useEvent from '../use-event';\n\n// This is the current implementation of `useResizeObserver`.\n//\n// The legacy implementation is still supported for backwards compatibility.\n// This is achieved by overloading the exported function with both signatures,\n// and detecting which API is being used at runtime.\nexport function useResizeObserver< T extends HTMLElement >(\n\tcallback: ResizeObserverCallback,\n\tresizeObserverOptions: ResizeObserverOptions = {}\n): ( element?: T | null ) => void {\n\tconst callbackEvent = useEvent( callback );\n\n\tconst observedElementRef = useRef< T | null >( null );\n\tconst resizeObserverRef = useRef< ResizeObserver >( undefined );\n\tuseEffect( () => {\n\t\tif ( observedElementRef.current ) {\n\t\t\tresizeObserverRef.current?.observe(\n\t\t\t\tobservedElementRef.current,\n\t\t\t\tresizeObserverOptions\n\t\t\t);\n\t\t}\n\t\treturn () => resizeObserverRef.current?.disconnect();\n\t\t// This effect owns the observer lifecycle. The returned ref handles\n\t\t// element and option changes without recreating the observer.\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\n\t}, [] );\n\treturn useEvent( ( element?: T | null ) => {\n\t\tif ( element === observedElementRef.current ) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Set up `ResizeObserver`.\n\t\tresizeObserverRef.current ??= new ResizeObserver( callbackEvent );\n\t\tconst { current: resizeObserver } = resizeObserverRef;\n\n\t\t// Unobserve previous element.\n\t\tif ( observedElementRef.current ) {\n\t\t\tresizeObserver.unobserve( observedElementRef.current );\n\t\t}\n\n\t\t// Observe new element.\n\t\tobservedElementRef.current = element ?? null;\n\t\tif ( element ) {\n\t\t\tresizeObserver.observe( element, resizeObserverOptions );\n\t\t}\n\t} );\n}\n"],
|
|
5
|
+
"mappings": ";AAAA,SAAS,WAAW,cAAc;AAClC,OAAO,cAAc;AAOd,SAAS,kBACf,UACA,wBAA+C,CAAC,GACf;AACjC,QAAM,gBAAgB,SAAU,QAAS;AAEzC,QAAM,qBAAqB,OAAoB,IAAK;AACpD,QAAM,oBAAoB,OAA0B,MAAU;AAC9D,YAAW,MAAM;AAChB,QAAK,mBAAmB,SAAU;AACjC,wBAAkB,SAAS;AAAA,QAC1B,mBAAmB;AAAA,QACnB;AAAA,MACD;AAAA,IACD;AACA,WAAO,MAAM,kBAAkB,SAAS,WAAW;AAAA,EAIpD,GAAG,CAAC,CAAE;AACN,SAAO,SAAU,CAAE,YAAwB;AAC1C,QAAK,YAAY,mBAAmB,SAAU;AAC7C;AAAA,IACD;AAGA,sBAAkB,YAAY,IAAI,eAAgB,aAAc;AAChE,UAAM,EAAE,SAAS,eAAe,IAAI;AAGpC,QAAK,mBAAmB,SAAU;AACjC,qBAAe,UAAW,mBAAmB,OAAQ;AAAA,IACtD;AAGA,uBAAmB,UAAU,WAAW;AACxC,QAAK,SAAU;AACd,qBAAe,QAAS,SAAS,qBAAsB;AAAA,IACxD;AAAA,EACD,CAAE;AACH;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-resize-observer.d.ts","sourceRoot":"","sources":["../../../src/hooks/use-resize-observer/use-resize-observer.ts"],"names":[],"mappings":"AAQA,wBAAgB,iBAAiB,CAAE,CAAC,SAAS,WAAW,EACvD,QAAQ,EAAE,sBAAsB,EAChC,qBAAqB,GAAE,qBAA0B,GAC/C,CAAE,OAAO,CAAC,EAAE,CAAC,GAAG,IAAI,KAAM,IAAI,
|
|
1
|
+
{"version":3,"file":"use-resize-observer.d.ts","sourceRoot":"","sources":["../../../src/hooks/use-resize-observer/use-resize-observer.ts"],"names":[],"mappings":"AAQA,wBAAgB,iBAAiB,CAAE,CAAC,SAAS,WAAW,EACvD,QAAQ,EAAE,sBAAsB,EAChC,qBAAqB,GAAE,qBAA0B,GAC/C,CAAE,OAAO,CAAC,EAAE,CAAC,GAAG,IAAI,KAAM,IAAI,CAqChC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wordpress/compose",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.8.0",
|
|
4
4
|
"description": "WordPress higher-order components (HOCs).",
|
|
5
5
|
"author": "The WordPress Contributors",
|
|
6
6
|
"license": "GPL-2.0-or-later",
|
|
@@ -46,26 +46,26 @@
|
|
|
46
46
|
"sideEffects": false,
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@types/mousetrap": "^1.6.8",
|
|
49
|
-
"@wordpress/deprecated": "^4.
|
|
50
|
-
"@wordpress/dom": "^4.
|
|
51
|
-
"@wordpress/element": "^8.
|
|
52
|
-
"@wordpress/is-shallow-equal": "^5.
|
|
53
|
-
"@wordpress/keycodes": "^4.
|
|
54
|
-
"@wordpress/priority-queue": "^3.
|
|
55
|
-
"@wordpress/private-apis": "^1.
|
|
56
|
-
"@wordpress/undo-manager": "^1.
|
|
49
|
+
"@wordpress/deprecated": "^4.55.0",
|
|
50
|
+
"@wordpress/dom": "^4.55.0",
|
|
51
|
+
"@wordpress/element": "^8.7.0",
|
|
52
|
+
"@wordpress/is-shallow-equal": "^5.55.0",
|
|
53
|
+
"@wordpress/keycodes": "^4.55.0",
|
|
54
|
+
"@wordpress/priority-queue": "^3.55.0",
|
|
55
|
+
"@wordpress/private-apis": "^1.55.0",
|
|
56
|
+
"@wordpress/undo-manager": "^1.55.0",
|
|
57
57
|
"change-case": "^4.1.2",
|
|
58
58
|
"mousetrap": "^1.6.5",
|
|
59
59
|
"use-memo-one": "^1.1.1"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@testing-library/dom": "^10.4.1",
|
|
63
|
-
"@testing-library/react": "^16.3.
|
|
63
|
+
"@testing-library/react": "^16.3.3",
|
|
64
64
|
"@testing-library/user-event": "^14.6.1",
|
|
65
65
|
"@types/jest": "^30.0.0",
|
|
66
66
|
"mock-match-media": "^0.4.2",
|
|
67
67
|
"react-dom": "^18.3.1",
|
|
68
|
-
"vitest": "^
|
|
68
|
+
"vitest": "^5.0.0"
|
|
69
69
|
},
|
|
70
70
|
"peerDependencies": {
|
|
71
71
|
"@types/react": "^18 || ^19",
|
|
@@ -79,5 +79,5 @@
|
|
|
79
79
|
"publishConfig": {
|
|
80
80
|
"access": "public"
|
|
81
81
|
},
|
|
82
|
-
"gitHead": "
|
|
82
|
+
"gitHead": "485f42ae8a1c58ceea18371a507fd4acfa86fbd8"
|
|
83
83
|
}
|
|
@@ -1,20 +1,23 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
1
2
|
import { render, screen } from '@testing-library/react';
|
|
2
3
|
import { Component } from '@wordpress/element';
|
|
3
4
|
import withGlobalEvents from '../';
|
|
4
5
|
import Listener from '../listener';
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
const ActualListener =
|
|
7
|
+
vi.mock( import( '../listener' ), async ( importOriginal ) => {
|
|
8
|
+
const { default: ActualListener } = await importOriginal();
|
|
8
9
|
|
|
9
|
-
return
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
return {
|
|
11
|
+
default: class extends ActualListener {
|
|
12
|
+
constructor() {
|
|
13
|
+
super( ...arguments );
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
this.constructor._instance = this;
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
vi.spyOn( this, 'add' );
|
|
18
|
+
vi.spyOn( this, 'remove' );
|
|
19
|
+
}
|
|
20
|
+
},
|
|
18
21
|
};
|
|
19
22
|
} );
|
|
20
23
|
|
|
@@ -30,12 +33,12 @@ describe( 'withGlobalEvents', () => {
|
|
|
30
33
|
}
|
|
31
34
|
}
|
|
32
35
|
|
|
33
|
-
beforeAll( () => {
|
|
34
|
-
jest.spyOn( OriginalComponent.prototype, 'handleResize' );
|
|
35
|
-
} );
|
|
36
|
-
|
|
37
36
|
beforeEach( () => {
|
|
38
|
-
|
|
37
|
+
vi.spyOn( OriginalComponent.prototype, 'handleResize' );
|
|
38
|
+
if ( Listener._instance ) {
|
|
39
|
+
vi.spyOn( Listener._instance, 'add' );
|
|
40
|
+
vi.spyOn( Listener._instance, 'remove' );
|
|
41
|
+
}
|
|
39
42
|
} );
|
|
40
43
|
|
|
41
44
|
it( 'renders with original component', () => {
|
|
@@ -67,7 +70,7 @@ describe( 'withGlobalEvents', () => {
|
|
|
67
70
|
const EnhancedComponent = withGlobalEvents( {
|
|
68
71
|
resize: 'handleResize',
|
|
69
72
|
} )( OriginalComponent );
|
|
70
|
-
const onResize =
|
|
73
|
+
const onResize = vi.fn();
|
|
71
74
|
|
|
72
75
|
render(
|
|
73
76
|
<EnhancedComponent ref={ () => {} } onResize={ onResize }>
|
|
@@ -1,19 +1,28 @@
|
|
|
1
|
+
import {
|
|
2
|
+
afterAll,
|
|
3
|
+
beforeAll,
|
|
4
|
+
beforeEach,
|
|
5
|
+
describe,
|
|
6
|
+
expect,
|
|
7
|
+
it,
|
|
8
|
+
vi,
|
|
9
|
+
} from 'vitest';
|
|
1
10
|
import Listener from '../listener';
|
|
2
11
|
|
|
3
12
|
describe( 'Listener', () => {
|
|
4
|
-
const createHandler = () => ( { handleEvent:
|
|
13
|
+
const createHandler = () => ( { handleEvent: vi.fn() } );
|
|
5
14
|
|
|
6
15
|
let listener, _addEventListener, _removeEventListener;
|
|
7
16
|
beforeAll( () => {
|
|
8
17
|
_addEventListener = global.window.addEventListener;
|
|
9
18
|
_removeEventListener = global.window.removeEventListener;
|
|
10
|
-
global.window.addEventListener =
|
|
11
|
-
global.window.removeEventListener =
|
|
19
|
+
global.window.addEventListener = vi.fn();
|
|
20
|
+
global.window.removeEventListener = vi.fn();
|
|
12
21
|
} );
|
|
13
22
|
|
|
14
23
|
beforeEach( () => {
|
|
15
24
|
listener = new Listener();
|
|
16
|
-
|
|
25
|
+
vi.clearAllMocks();
|
|
17
26
|
} );
|
|
18
27
|
|
|
19
28
|
afterAll( () => {
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
1
2
|
import { render, screen, act } from '@testing-library/react';
|
|
2
3
|
import userEvent from '@testing-library/user-event';
|
|
3
4
|
import deprecated from '@wordpress/deprecated';
|
|
4
5
|
import { useRef } from '@wordpress/element';
|
|
5
6
|
import useCopyOnClick from '../';
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
vi.mock( import( '@wordpress/deprecated' ) );
|
|
8
9
|
|
|
9
10
|
interface TestComponentProps {
|
|
10
11
|
text: string | ( () => string );
|
|
@@ -12,6 +13,10 @@ interface TestComponentProps {
|
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
describe( 'useCopyOnClick', () => {
|
|
16
|
+
afterEach( () => {
|
|
17
|
+
vi.useRealTimers();
|
|
18
|
+
} );
|
|
19
|
+
|
|
15
20
|
const TestComponent = ( { text, timeout = 4000 }: TestComponentProps ) => {
|
|
16
21
|
const ref = useRef< HTMLButtonElement >( null );
|
|
17
22
|
const hasCopied = useCopyOnClick( ref, text, timeout );
|
|
@@ -23,7 +28,7 @@ describe( 'useCopyOnClick', () => {
|
|
|
23
28
|
};
|
|
24
29
|
|
|
25
30
|
it( 'should call deprecated when the hook is used', () => {
|
|
26
|
-
|
|
31
|
+
vi.mocked( deprecated ).mockClear();
|
|
27
32
|
render( <TestComponent text="test text" /> );
|
|
28
33
|
|
|
29
34
|
expect( deprecated ).toHaveBeenCalledWith(
|
|
@@ -39,7 +44,7 @@ describe( 'useCopyOnClick', () => {
|
|
|
39
44
|
const user = userEvent.setup();
|
|
40
45
|
render( <TestComponent text="test text" /> );
|
|
41
46
|
|
|
42
|
-
const writeTextMock =
|
|
47
|
+
const writeTextMock = vi
|
|
43
48
|
.spyOn( navigator.clipboard, 'writeText' )
|
|
44
49
|
.mockResolvedValue();
|
|
45
50
|
|
|
@@ -53,7 +58,7 @@ describe( 'useCopyOnClick', () => {
|
|
|
53
58
|
const user = userEvent.setup();
|
|
54
59
|
render( <TestComponent text="test text" /> );
|
|
55
60
|
|
|
56
|
-
|
|
61
|
+
vi.spyOn( navigator.clipboard, 'writeText' ).mockResolvedValue();
|
|
57
62
|
|
|
58
63
|
expect( screen.getByRole( 'button' ) ).toHaveTextContent( 'Copy' );
|
|
59
64
|
|
|
@@ -67,36 +72,32 @@ describe( 'useCopyOnClick', () => {
|
|
|
67
72
|
} );
|
|
68
73
|
|
|
69
74
|
it( 'should reset hasCopied after timeout', async () => {
|
|
70
|
-
|
|
75
|
+
vi.useFakeTimers( { shouldAdvanceTime: true } );
|
|
71
76
|
const user = userEvent.setup( {
|
|
72
|
-
advanceTimers:
|
|
77
|
+
advanceTimers: ( delay ) => vi.advanceTimersByTime( delay ),
|
|
73
78
|
} );
|
|
74
79
|
render( <TestComponent text="test text" timeout={ 1000 } /> );
|
|
75
80
|
|
|
76
|
-
|
|
81
|
+
vi.spyOn( navigator.clipboard, 'writeText' ).mockResolvedValue();
|
|
77
82
|
|
|
78
83
|
await user.click( screen.getByRole( 'button' ) );
|
|
79
84
|
|
|
80
85
|
await act( async () => {
|
|
81
|
-
|
|
82
|
-
jest.advanceTimersByTime( 0 );
|
|
83
|
-
await p;
|
|
86
|
+
await vi.advanceTimersByTimeAsync( 0 );
|
|
84
87
|
} );
|
|
85
88
|
expect( screen.getByRole( 'button' ) ).toHaveTextContent( 'Copied!' );
|
|
86
89
|
|
|
87
|
-
act( () => {
|
|
88
|
-
|
|
90
|
+
await act( async () => {
|
|
91
|
+
await vi.advanceTimersByTimeAsync( 1000 );
|
|
89
92
|
} );
|
|
90
93
|
expect( screen.getByRole( 'button' ) ).toHaveTextContent( 'Copy' );
|
|
91
|
-
|
|
92
|
-
jest.useRealTimers();
|
|
93
94
|
} );
|
|
94
95
|
|
|
95
96
|
it( 'should not set hasCopied when copy fails', async () => {
|
|
96
97
|
const user = userEvent.setup();
|
|
97
98
|
render( <TestComponent text="test text" /> );
|
|
98
99
|
|
|
99
|
-
|
|
100
|
+
vi.spyOn( navigator.clipboard, 'writeText' ).mockRejectedValue(
|
|
100
101
|
new Error()
|
|
101
102
|
);
|
|
102
103
|
|
|
@@ -110,7 +111,7 @@ describe( 'useCopyOnClick', () => {
|
|
|
110
111
|
} );
|
|
111
112
|
|
|
112
113
|
it( 'should not update hasCopied after unmount', async () => {
|
|
113
|
-
const renderSpy =
|
|
114
|
+
const renderSpy = vi.fn();
|
|
114
115
|
|
|
115
116
|
const SpyComponent = ( { text }: { text: string } ) => {
|
|
116
117
|
const ref = useRef< HTMLButtonElement >( null );
|
|
@@ -127,7 +128,7 @@ describe( 'useCopyOnClick', () => {
|
|
|
127
128
|
const delayedPromise = new Promise< void >( ( resolve ) => {
|
|
128
129
|
resolvePromise = resolve;
|
|
129
130
|
} );
|
|
130
|
-
|
|
131
|
+
vi.spyOn( navigator.clipboard, 'writeText' ).mockReturnValue(
|
|
131
132
|
delayedPromise as Promise< void >
|
|
132
133
|
);
|
|
133
134
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
1
2
|
import { render, screen, act } from '@testing-library/react';
|
|
2
3
|
import userEvent from '@testing-library/user-event';
|
|
3
4
|
import useCopyToClipboard, { copyToClipboard, restoreFocus } from '../';
|
|
@@ -17,7 +18,7 @@ describe( 'useCopyToClipboard', () => {
|
|
|
17
18
|
const user = userEvent.setup();
|
|
18
19
|
render( <TestComponent text="test text" /> );
|
|
19
20
|
|
|
20
|
-
const writeTextMock =
|
|
21
|
+
const writeTextMock = vi
|
|
21
22
|
.spyOn( navigator.clipboard, 'writeText' )
|
|
22
23
|
.mockResolvedValue();
|
|
23
24
|
|
|
@@ -29,7 +30,7 @@ describe( 'useCopyToClipboard', () => {
|
|
|
29
30
|
|
|
30
31
|
it( 'should call onSuccess when copy succeeds', async () => {
|
|
31
32
|
const user = userEvent.setup();
|
|
32
|
-
const onSuccess =
|
|
33
|
+
const onSuccess = vi.fn();
|
|
33
34
|
render( <TestComponent text="test text" onSuccess={ onSuccess } /> );
|
|
34
35
|
|
|
35
36
|
await user.click( screen.getByRole( 'button' ) );
|
|
@@ -39,7 +40,7 @@ describe( 'useCopyToClipboard', () => {
|
|
|
39
40
|
|
|
40
41
|
it( 'should call onSuccess when copy empty text', async () => {
|
|
41
42
|
const user = userEvent.setup();
|
|
42
|
-
const onSuccess =
|
|
43
|
+
const onSuccess = vi.fn();
|
|
43
44
|
render( <TestComponent text="" onSuccess={ onSuccess } /> );
|
|
44
45
|
|
|
45
46
|
await user.click( screen.getByRole( 'button' ) );
|
|
@@ -49,10 +50,10 @@ describe( 'useCopyToClipboard', () => {
|
|
|
49
50
|
|
|
50
51
|
it( 'should not call onSuccess when copy fails', async () => {
|
|
51
52
|
const user = userEvent.setup();
|
|
52
|
-
const onSuccess =
|
|
53
|
+
const onSuccess = vi.fn();
|
|
53
54
|
render( <TestComponent text="test text" onSuccess={ onSuccess } /> );
|
|
54
55
|
|
|
55
|
-
|
|
56
|
+
vi.spyOn( navigator.clipboard, 'writeText' ).mockRejectedValue(
|
|
56
57
|
new Error()
|
|
57
58
|
);
|
|
58
59
|
|
|
@@ -66,12 +67,12 @@ describe( 'useCopyToClipboard', () => {
|
|
|
66
67
|
const delayedPromise = new Promise< void >( ( resolve ) => {
|
|
67
68
|
resolvePromise = resolve;
|
|
68
69
|
} );
|
|
69
|
-
|
|
70
|
+
vi.spyOn( navigator.clipboard, 'writeText' ).mockReturnValue(
|
|
70
71
|
delayedPromise
|
|
71
72
|
);
|
|
72
73
|
|
|
73
74
|
const user = userEvent.setup();
|
|
74
|
-
const onSuccess =
|
|
75
|
+
const onSuccess = vi.fn();
|
|
75
76
|
const { unmount } = render(
|
|
76
77
|
<TestComponent text="test" onSuccess={ onSuccess } />
|
|
77
78
|
);
|
|
@@ -92,7 +93,7 @@ describe( 'useCopyToClipboard', () => {
|
|
|
92
93
|
const delayedPromise = new Promise< void >( ( resolve ) => {
|
|
93
94
|
resolvePromise = resolve;
|
|
94
95
|
} );
|
|
95
|
-
|
|
96
|
+
vi.spyOn( navigator.clipboard, 'writeText' ).mockReturnValue(
|
|
96
97
|
delayedPromise
|
|
97
98
|
);
|
|
98
99
|
|
|
@@ -100,7 +101,7 @@ describe( 'useCopyToClipboard', () => {
|
|
|
100
101
|
const { unmount } = render( <TestComponent text="test" /> );
|
|
101
102
|
|
|
102
103
|
const button = screen.getByRole( 'button' );
|
|
103
|
-
const focusSpy =
|
|
104
|
+
const focusSpy = vi.spyOn( button, 'focus' );
|
|
104
105
|
|
|
105
106
|
await user.click( button );
|
|
106
107
|
unmount();
|
|
@@ -129,7 +130,7 @@ describe( 'copyToClipboard', () => {
|
|
|
129
130
|
|
|
130
131
|
// JSDOM does not implement execCommand; add a mock for the fallback path.
|
|
131
132
|
// See: https://github.com/jsdom/jsdom/issues/1742
|
|
132
|
-
const execCommandMock =
|
|
133
|
+
const execCommandMock = vi.fn().mockReturnValue( true );
|
|
133
134
|
Object.defineProperty( document, 'execCommand', {
|
|
134
135
|
value: execCommandMock,
|
|
135
136
|
configurable: true,
|
|
@@ -154,7 +155,7 @@ describe( 'restoreFocus', () => {
|
|
|
154
155
|
it( 'should focus the trigger element', () => {
|
|
155
156
|
const trigger = document.createElement( 'button' );
|
|
156
157
|
document.body.appendChild( trigger );
|
|
157
|
-
const focusMock =
|
|
158
|
+
const focusMock = vi.spyOn( trigger, 'focus' );
|
|
158
159
|
|
|
159
160
|
restoreFocus( trigger );
|
|
160
161
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
1
2
|
import { render, screen } from '@testing-library/react';
|
|
2
3
|
import userEvent from '@testing-library/user-event';
|
|
3
4
|
import { createPortal } from 'react-dom';
|
|
@@ -20,7 +21,7 @@ function Dialog( { onClose }: { onClose?: () => void } ) {
|
|
|
20
21
|
describe( 'useDialog', () => {
|
|
21
22
|
it( 'should call onClose when Escape is pressed', async () => {
|
|
22
23
|
const user = userEvent.setup();
|
|
23
|
-
const onClose =
|
|
24
|
+
const onClose = vi.fn();
|
|
24
25
|
|
|
25
26
|
render( <Dialog onClose={ onClose } /> );
|
|
26
27
|
|
|
@@ -32,7 +33,7 @@ describe( 'useDialog', () => {
|
|
|
32
33
|
|
|
33
34
|
it( 'should not call onClose when Escape is pressed if defaultPrevented', async () => {
|
|
34
35
|
const user = userEvent.setup();
|
|
35
|
-
const onClose =
|
|
36
|
+
const onClose = vi.fn();
|
|
36
37
|
|
|
37
38
|
function DialogWithChild() {
|
|
38
39
|
const [ ref, props ] = useDialog( {
|
|
@@ -69,8 +70,8 @@ describe( 'useDialog', () => {
|
|
|
69
70
|
|
|
70
71
|
it( 'should stop Escape event from propagating to parent elements', async () => {
|
|
71
72
|
const user = userEvent.setup();
|
|
72
|
-
const onClose =
|
|
73
|
-
const parentKeyDownHandler =
|
|
73
|
+
const onClose = vi.fn();
|
|
74
|
+
const parentKeyDownHandler = vi.fn();
|
|
74
75
|
|
|
75
76
|
render(
|
|
76
77
|
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
|
|
@@ -88,7 +89,7 @@ describe( 'useDialog', () => {
|
|
|
88
89
|
|
|
89
90
|
it( 'should let Escape propagate when there is no onClose handler', async () => {
|
|
90
91
|
const user = userEvent.setup();
|
|
91
|
-
const parentKeyDownHandler =
|
|
92
|
+
const parentKeyDownHandler = vi.fn();
|
|
92
93
|
|
|
93
94
|
render(
|
|
94
95
|
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
|
|
@@ -105,8 +106,8 @@ describe( 'useDialog', () => {
|
|
|
105
106
|
|
|
106
107
|
it( 'should not close when a portaled descendant handles Escape and stops propagation', async () => {
|
|
107
108
|
const user = userEvent.setup();
|
|
108
|
-
const onClose =
|
|
109
|
-
const descendantHandled =
|
|
109
|
+
const onClose = vi.fn();
|
|
110
|
+
const descendantHandled = vi.fn();
|
|
110
111
|
|
|
111
112
|
const portalTarget = document.createElement( 'div' );
|
|
112
113
|
document.body.appendChild( portalTarget );
|
|
@@ -153,8 +154,8 @@ describe( 'useDialog', () => {
|
|
|
153
154
|
|
|
154
155
|
it( 'should close only the innermost dialog when nested', async () => {
|
|
155
156
|
const user = userEvent.setup();
|
|
156
|
-
const outerOnClose =
|
|
157
|
-
const innerOnClose =
|
|
157
|
+
const outerOnClose = vi.fn();
|
|
158
|
+
const innerOnClose = vi.fn();
|
|
158
159
|
|
|
159
160
|
function NestedDialogs() {
|
|
160
161
|
const [ outerRef, outerProps ] = useDialog( {
|
|
@@ -196,8 +197,8 @@ describe( 'useDialog', () => {
|
|
|
196
197
|
|
|
197
198
|
it( 'should call the consumer-provided onKeyDown alongside close-on-Escape', async () => {
|
|
198
199
|
const user = userEvent.setup();
|
|
199
|
-
const onClose =
|
|
200
|
-
const consumerOnKeyDown =
|
|
200
|
+
const onClose = vi.fn();
|
|
201
|
+
const consumerOnKeyDown = vi.fn();
|
|
201
202
|
|
|
202
203
|
function DialogWithConsumerOnKeyDown() {
|
|
203
204
|
const [ ref, props ] = useDialog( {
|
|
@@ -227,7 +228,7 @@ describe( 'useDialog', () => {
|
|
|
227
228
|
|
|
228
229
|
it( 'should let the consumer-provided onKeyDown opt out of close-on-Escape via preventDefault', async () => {
|
|
229
230
|
const user = userEvent.setup();
|
|
230
|
-
const onClose =
|
|
231
|
+
const onClose = vi.fn();
|
|
231
232
|
|
|
232
233
|
function DialogOptingOut() {
|
|
233
234
|
const [ ref, props ] = useDialog( {
|
|
@@ -255,7 +256,7 @@ describe( 'useDialog', () => {
|
|
|
255
256
|
|
|
256
257
|
it( 'should not call onClose for non-Escape keys', async () => {
|
|
257
258
|
const user = userEvent.setup();
|
|
258
|
-
const onClose =
|
|
259
|
+
const onClose = vi.fn();
|
|
259
260
|
|
|
260
261
|
render( <Dialog onClose={ onClose } /> );
|
|
261
262
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
1
2
|
import { render, screen } from '@testing-library/react';
|
|
2
3
|
import userEvent from '@testing-library/user-event';
|
|
3
4
|
import useFocusOutside from '../';
|
|
@@ -19,7 +20,7 @@ const FocusOutsideComponent = ( { onFocusOutside: callback } ) => (
|
|
|
19
20
|
|
|
20
21
|
describe( 'useFocusOutside', () => {
|
|
21
22
|
it( 'should not call handler if focus shifts to element within component', async () => {
|
|
22
|
-
const mockOnFocusOutside =
|
|
23
|
+
const mockOnFocusOutside = vi.fn();
|
|
23
24
|
const user = userEvent.setup();
|
|
24
25
|
|
|
25
26
|
render(
|
|
@@ -40,7 +41,7 @@ describe( 'useFocusOutside', () => {
|
|
|
40
41
|
} );
|
|
41
42
|
|
|
42
43
|
it( 'should not call handler if focus transitions via click to button', async () => {
|
|
43
|
-
const mockOnFocusOutside =
|
|
44
|
+
const mockOnFocusOutside = vi.fn();
|
|
44
45
|
const user = userEvent.setup();
|
|
45
46
|
|
|
46
47
|
render(
|
|
@@ -57,7 +58,7 @@ describe( 'useFocusOutside', () => {
|
|
|
57
58
|
} );
|
|
58
59
|
|
|
59
60
|
it( 'should call handler if focus shifts to element outside component', async () => {
|
|
60
|
-
const mockOnFocusOutside =
|
|
61
|
+
const mockOnFocusOutside = vi.fn();
|
|
61
62
|
const user = userEvent.setup();
|
|
62
63
|
|
|
63
64
|
render(
|
|
@@ -82,10 +83,10 @@ describe( 'useFocusOutside', () => {
|
|
|
82
83
|
it( 'should not call handler if focus shifts outside the component when the document does not have focus', async () => {
|
|
83
84
|
// Force document.hasFocus() to return false to simulate the window/document losing focus
|
|
84
85
|
// See https://developer.mozilla.org/en-US/docs/Web/API/Document/hasFocus.
|
|
85
|
-
const mockedDocumentHasFocus =
|
|
86
|
+
const mockedDocumentHasFocus = vi
|
|
86
87
|
.spyOn( document, 'hasFocus' )
|
|
87
88
|
.mockImplementation( () => false );
|
|
88
|
-
const mockOnFocusOutside =
|
|
89
|
+
const mockOnFocusOutside = vi.fn();
|
|
89
90
|
const user = userEvent.setup();
|
|
90
91
|
|
|
91
92
|
render(
|
|
@@ -114,9 +115,7 @@ describe( 'useFocusOutside', () => {
|
|
|
114
115
|
const promise = new Promise( ( resolve ) => {
|
|
115
116
|
resolvePromise = resolve;
|
|
116
117
|
} );
|
|
117
|
-
const mockOnFocusOutside =
|
|
118
|
-
.fn()
|
|
119
|
-
.mockImplementation( resolvePromise );
|
|
118
|
+
const mockOnFocusOutside = vi.fn().mockImplementation( resolvePromise );
|
|
120
119
|
const user = userEvent.setup();
|
|
121
120
|
|
|
122
121
|
const { unmount } = render(
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { describe, expect, test, vi } from 'vitest';
|
|
1
2
|
import { render, screen, act } from '@testing-library/react';
|
|
2
3
|
import { observableMap } from '../../../utils/observable-map';
|
|
3
4
|
import useObservableValue from '..';
|
|
@@ -7,7 +8,7 @@ describe( 'useObservableValue', () => {
|
|
|
7
8
|
const map = observableMap();
|
|
8
9
|
map.set( 'a', 1 );
|
|
9
10
|
|
|
10
|
-
const MapUI =
|
|
11
|
+
const MapUI = vi.fn( () => {
|
|
11
12
|
const value = useObservableValue( map, 'a' );
|
|
12
13
|
return <div>value is { value }</div>;
|
|
13
14
|
} );
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { act, renderHook } from '@testing-library/react';
|
|
3
|
+
import useResizeObserver from '..';
|
|
4
|
+
|
|
5
|
+
const disconnect = vi.fn();
|
|
6
|
+
|
|
7
|
+
class MockResizeObserver {
|
|
8
|
+
observe() {}
|
|
9
|
+
unobserve() {}
|
|
10
|
+
disconnect = disconnect;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const originalResizeObserver = globalThis.ResizeObserver;
|
|
14
|
+
|
|
15
|
+
beforeAll( () => {
|
|
16
|
+
globalThis.ResizeObserver =
|
|
17
|
+
MockResizeObserver as unknown as typeof ResizeObserver;
|
|
18
|
+
} );
|
|
19
|
+
|
|
20
|
+
afterAll( () => {
|
|
21
|
+
globalThis.ResizeObserver = originalResizeObserver;
|
|
22
|
+
} );
|
|
23
|
+
|
|
24
|
+
describe( 'useResizeObserver', () => {
|
|
25
|
+
it( 'disconnects the observer on unmount', () => {
|
|
26
|
+
const { result, unmount } = renderHook( () =>
|
|
27
|
+
useResizeObserver( vi.fn() )
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
act( () => result.current( document.createElement( 'div' ) ) );
|
|
31
|
+
expect( disconnect ).not.toHaveBeenCalled();
|
|
32
|
+
|
|
33
|
+
unmount();
|
|
34
|
+
|
|
35
|
+
expect( disconnect ).toHaveBeenCalledOnce();
|
|
36
|
+
} );
|
|
37
|
+
} );
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useRef } from '@wordpress/element';
|
|
1
|
+
import { useEffect, useRef } from '@wordpress/element';
|
|
2
2
|
import useEvent from '../use-event';
|
|
3
3
|
|
|
4
4
|
// This is the current implementation of `useResizeObserver`.
|
|
@@ -14,6 +14,18 @@ export function useResizeObserver< T extends HTMLElement >(
|
|
|
14
14
|
|
|
15
15
|
const observedElementRef = useRef< T | null >( null );
|
|
16
16
|
const resizeObserverRef = useRef< ResizeObserver >( undefined );
|
|
17
|
+
useEffect( () => {
|
|
18
|
+
if ( observedElementRef.current ) {
|
|
19
|
+
resizeObserverRef.current?.observe(
|
|
20
|
+
observedElementRef.current,
|
|
21
|
+
resizeObserverOptions
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
return () => resizeObserverRef.current?.disconnect();
|
|
25
|
+
// This effect owns the observer lifecycle. The returned ref handles
|
|
26
|
+
// element and option changes without recreating the observer.
|
|
27
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
28
|
+
}, [] );
|
|
17
29
|
return useEvent( ( element?: T | null ) => {
|
|
18
30
|
if ( element === observedElementRef.current ) {
|
|
19
31
|
return;
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
1
2
|
import { render } from '@testing-library/react';
|
|
2
3
|
import useViewportMatch from '../';
|
|
3
|
-
jest.mock( '../../use-media-query', () => {
|
|
4
|
-
return jest.fn();
|
|
5
|
-
} );
|
|
6
4
|
import useMediaQueryMock from '../../use-media-query';
|
|
5
|
+
vi.mock( import( '../../use-media-query' ), () => ( { default: vi.fn() } ) );
|
|
7
6
|
|
|
8
7
|
describe( 'useViewportMatch', () => {
|
|
9
8
|
afterEach( () => {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
|
1
2
|
import subscribeDelegatedListener from '..';
|
|
2
3
|
|
|
3
4
|
describe( 'subscribeDelegatedListener', () => {
|
|
@@ -27,14 +28,14 @@ describe( 'subscribeDelegatedListener', () => {
|
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
test( 'invokes element subscriber when event fires on that element', () => {
|
|
30
|
-
const cb =
|
|
31
|
+
const cb = vi.fn();
|
|
31
32
|
subscribeDelegatedListener( target, 'click', cb );
|
|
32
33
|
fire( target );
|
|
33
34
|
expect( cb ).toHaveBeenCalledTimes( 1 );
|
|
34
35
|
} );
|
|
35
36
|
|
|
36
37
|
test( 'invokes element subscriber when event fires on a descendant', () => {
|
|
37
|
-
const cb =
|
|
38
|
+
const cb = vi.fn();
|
|
38
39
|
const child = document.createElement( 'b' );
|
|
39
40
|
target.appendChild( child );
|
|
40
41
|
subscribeDelegatedListener( target, 'click', cb );
|
|
@@ -43,7 +44,7 @@ describe( 'subscribeDelegatedListener', () => {
|
|
|
43
44
|
} );
|
|
44
45
|
|
|
45
46
|
test( 'does not invoke element subscriber when event fires outside its subtree', () => {
|
|
46
|
-
const cb =
|
|
47
|
+
const cb = vi.fn();
|
|
47
48
|
const sibling = document.createElement( 'div' );
|
|
48
49
|
document.body.appendChild( sibling );
|
|
49
50
|
subscribeDelegatedListener( target, 'click', cb );
|
|
@@ -83,8 +84,8 @@ describe( 'subscribeDelegatedListener', () => {
|
|
|
83
84
|
} );
|
|
84
85
|
|
|
85
86
|
test( 'capture and bubble registries are independent', () => {
|
|
86
|
-
const captureCb =
|
|
87
|
-
const bubbleCb =
|
|
87
|
+
const captureCb = vi.fn();
|
|
88
|
+
const bubbleCb = vi.fn();
|
|
88
89
|
subscribeDelegatedListener( target, 'click', captureCb, true );
|
|
89
90
|
subscribeDelegatedListener( target, 'click', bubbleCb, false );
|
|
90
91
|
fire( target );
|
|
@@ -93,15 +94,15 @@ describe( 'subscribeDelegatedListener', () => {
|
|
|
93
94
|
} );
|
|
94
95
|
|
|
95
96
|
test( 'document subscriber always fires for events in the document', () => {
|
|
96
|
-
const cb =
|
|
97
|
+
const cb = vi.fn();
|
|
97
98
|
subscribeDelegatedListener( document, 'click', cb );
|
|
98
99
|
fire( target );
|
|
99
100
|
expect( cb ).toHaveBeenCalledTimes( 1 );
|
|
100
101
|
} );
|
|
101
102
|
|
|
102
103
|
test( 'window subscriber fans out independently of element subscribers', () => {
|
|
103
|
-
const winCb =
|
|
104
|
-
const elCb =
|
|
104
|
+
const winCb = vi.fn();
|
|
105
|
+
const elCb = vi.fn();
|
|
105
106
|
subscribeDelegatedListener( window, 'resize', winCb );
|
|
106
107
|
subscribeDelegatedListener( target, 'resize', elCb );
|
|
107
108
|
window.dispatchEvent( new Event( 'resize' ) );
|
|
@@ -111,8 +112,8 @@ describe( 'subscribeDelegatedListener', () => {
|
|
|
111
112
|
} );
|
|
112
113
|
|
|
113
114
|
test( 'multiple callbacks for the same element all fire', () => {
|
|
114
|
-
const a =
|
|
115
|
-
const b =
|
|
115
|
+
const a = vi.fn();
|
|
116
|
+
const b = vi.fn();
|
|
116
117
|
subscribeDelegatedListener( target, 'click', a );
|
|
117
118
|
subscribeDelegatedListener( target, 'click', b );
|
|
118
119
|
fire( target );
|
|
@@ -121,7 +122,7 @@ describe( 'subscribeDelegatedListener', () => {
|
|
|
121
122
|
} );
|
|
122
123
|
|
|
123
124
|
test( 'unsubscribe stops further dispatch', () => {
|
|
124
|
-
const cb =
|
|
125
|
+
const cb = vi.fn();
|
|
125
126
|
const unsub = subscribeDelegatedListener( target, 'click', cb );
|
|
126
127
|
fire( target );
|
|
127
128
|
expect( cb ).toHaveBeenCalledTimes( 1 );
|
|
@@ -131,12 +132,12 @@ describe( 'subscribeDelegatedListener', () => {
|
|
|
131
132
|
} );
|
|
132
133
|
|
|
133
134
|
test( 'attaches one native listener per (root, eventType, phase) regardless of subscriber count', () => {
|
|
134
|
-
const spy =
|
|
135
|
+
const spy = vi.spyOn( document, 'addEventListener' );
|
|
135
136
|
// First subscribe attaches the native listener; subsequent ones
|
|
136
137
|
// for the same key share it.
|
|
137
|
-
subscribeDelegatedListener( target, 'mousemove',
|
|
138
|
-
subscribeDelegatedListener( root, 'mousemove',
|
|
139
|
-
subscribeDelegatedListener( document, 'mousemove',
|
|
138
|
+
subscribeDelegatedListener( target, 'mousemove', vi.fn() );
|
|
139
|
+
subscribeDelegatedListener( root, 'mousemove', vi.fn() );
|
|
140
|
+
subscribeDelegatedListener( document, 'mousemove', vi.fn() );
|
|
140
141
|
const nativeMousemoves = spy.mock.calls.filter(
|
|
141
142
|
( [ type, , opts ] ) => type === 'mousemove' && ! opts // bubble-phase only
|
|
142
143
|
).length;
|
|
@@ -154,7 +155,7 @@ describe( 'subscribeDelegatedListener', () => {
|
|
|
154
155
|
const iframeTarget = iframeDoc.createElement( 'span' );
|
|
155
156
|
iframeDoc.body.appendChild( iframeTarget );
|
|
156
157
|
|
|
157
|
-
const cb =
|
|
158
|
+
const cb = vi.fn();
|
|
158
159
|
expect( () =>
|
|
159
160
|
subscribeDelegatedListener( iframeTarget, 'click', cb )
|
|
160
161
|
).not.toThrow();
|