@wordpress/compose 8.0.0 → 8.1.1
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 +21 -0
- package/README.md +5 -5
- package/build/higher-order/pure/index.cjs +15 -0
- package/build/higher-order/pure/index.cjs.map +3 -3
- package/build/hooks/use-disabled/index.cjs +2 -2
- package/build/hooks/use-disabled/index.cjs.map +2 -2
- package/build/hooks/use-focus-return/index.cjs.map +1 -1
- package/build/hooks/use-media-query/index.cjs +2 -2
- package/build/hooks/use-media-query/index.cjs.map +2 -2
- package/build/hooks/use-merge-refs/index.cjs +28 -8
- package/build/hooks/use-merge-refs/index.cjs.map +2 -2
- package/build/hooks/use-resize-observer/use-resize-observer.cjs.map +2 -2
- package/build/hooks/use-viewport-match/index.cjs +1 -1
- package/build/hooks/use-viewport-match/index.cjs.map +2 -2
- package/build-module/higher-order/pure/index.mjs +5 -0
- package/build-module/higher-order/pure/index.mjs.map +2 -2
- package/build-module/hooks/use-disabled/index.mjs +2 -2
- package/build-module/hooks/use-disabled/index.mjs.map +2 -2
- package/build-module/hooks/use-focus-return/index.mjs.map +1 -1
- package/build-module/hooks/use-media-query/index.mjs +2 -2
- package/build-module/hooks/use-media-query/index.mjs.map +2 -2
- package/build-module/hooks/use-merge-refs/index.mjs +28 -8
- package/build-module/hooks/use-merge-refs/index.mjs.map +2 -2
- package/build-module/hooks/use-resize-observer/use-resize-observer.mjs.map +2 -2
- package/build-module/hooks/use-viewport-match/index.mjs +1 -1
- package/build-module/hooks/use-viewport-match/index.mjs.map +2 -2
- package/build-types/higher-order/pure/index.d.ts.map +1 -1
- package/build-types/higher-order/with-safe-timeout/index.d.ts +3 -1
- package/build-types/higher-order/with-safe-timeout/index.d.ts.map +1 -1
- package/build-types/hooks/use-media-query/index.d.ts +1 -1
- package/build-types/hooks/use-media-query/index.d.ts.map +1 -1
- package/build-types/hooks/use-merge-refs/index.d.ts +7 -5
- package/build-types/hooks/use-merge-refs/index.d.ts.map +1 -1
- package/build-types/hooks/use-viewport-match/index.d.ts +1 -1
- package/build-types/hooks/use-viewport-match/index.d.ts.map +1 -1
- package/package.json +17 -13
- package/src/higher-order/pure/index.tsx +6 -0
- package/src/higher-order/pure/test/index.js +14 -81
- package/src/hooks/use-disabled/index.ts +3 -3
- package/src/hooks/use-disabled/test/index.js +4 -4
- package/src/hooks/use-drop-zone/README.md +1 -1
- package/src/hooks/use-focus-return/index.js +2 -2
- package/src/hooks/use-media-query/index.ts +10 -3
- package/src/hooks/use-media-query/test/ssr.js +47 -0
- package/src/hooks/use-merge-refs/index.ts +51 -20
- package/src/hooks/use-merge-refs/test/index.js +288 -0
- package/src/hooks/use-resize-observer/use-resize-observer.ts +1 -1
- package/src/hooks/use-viewport-match/index.js +8 -2
- package/src/higher-order/with-network-connectivity/README.md +0 -20
- package/src/higher-order/with-network-connectivity/index.native.js +0 -19
- package/src/higher-order/with-preferred-color-scheme/index.native.js +0 -40
- package/src/hooks/use-constrained-tabbing/index.native.js +0 -14
- package/src/hooks/use-focus-outside/index.native.js +0 -170
- package/src/hooks/use-keyboard-shortcut/index.native.js +0 -2
- package/src/hooks/use-network-connectivity/index.native.js +0 -59
- package/src/hooks/use-network-connectivity/test/index.native.js +0 -87
- package/src/hooks/use-preferred-color-scheme/index.android.js +0 -30
- package/src/hooks/use-preferred-color-scheme/index.ios.js +0 -13
- package/src/hooks/use-preferred-color-scheme-style/index.native.js +0 -33
- package/src/hooks/use-resize-observer/index.native.js +0 -1
- package/src/hooks/use-resize-observer/legacy/index.native.js +0 -59
- package/src/hooks/use-resize-observer/legacy/test/index.native.js +0 -46
- package/src/index.native.js +0 -44
|
@@ -2,13 +2,34 @@
|
|
|
2
2
|
* WordPress dependencies
|
|
3
3
|
*/
|
|
4
4
|
import { useRef, useCallback, useLayoutEffect } from '@wordpress/element';
|
|
5
|
-
import type { Ref, RefCallback } from 'react';
|
|
5
|
+
import type { MutableRefObject, Ref, RefCallback } from 'react';
|
|
6
6
|
|
|
7
|
-
function
|
|
7
|
+
// Returns a cleanup function if the ref callback returned one (React 19 ref
|
|
8
|
+
// callback cleanup pattern), otherwise `undefined`. Object refs never have a
|
|
9
|
+
// cleanup and only set `.current`.
|
|
10
|
+
function assignRef< T >( ref: Ref< T >, value: T ): ( () => void ) | undefined {
|
|
8
11
|
if ( typeof ref === 'function' ) {
|
|
9
|
-
ref( value );
|
|
12
|
+
const returned = ref( value );
|
|
13
|
+
return typeof returned === 'function' ? returned : undefined;
|
|
10
14
|
} else if ( ref && ref.hasOwnProperty( 'current' ) ) {
|
|
11
|
-
ref.current = value;
|
|
15
|
+
( ref as MutableRefObject< T > ).current = value;
|
|
16
|
+
}
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Tear down a ref at the given index: prefer the stored cleanup; otherwise
|
|
21
|
+
// fall back to calling the ref with `null`.
|
|
22
|
+
function detachRef< T >(
|
|
23
|
+
ref: Ref< T >,
|
|
24
|
+
index: number,
|
|
25
|
+
cleanups: Array< ( () => void ) | undefined >
|
|
26
|
+
): void {
|
|
27
|
+
const cleanup = cleanups[ index ];
|
|
28
|
+
if ( cleanup ) {
|
|
29
|
+
cleanups[ index ] = undefined;
|
|
30
|
+
cleanup();
|
|
31
|
+
} else {
|
|
32
|
+
assignRef( ref, null );
|
|
12
33
|
}
|
|
13
34
|
}
|
|
14
35
|
|
|
@@ -28,22 +49,24 @@ function assignRef< T >( ref: Ref< T >, value: T ) {
|
|
|
28
49
|
* old ref callback will be called with `null` and the new ref callback will be
|
|
29
50
|
* called with the same value.
|
|
30
51
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
52
|
+
* Inner ref callbacks may return a cleanup function (React 19's ref callback
|
|
53
|
+
* cleanup pattern). When a ref callback returns a function, that function is
|
|
54
|
+
* invoked at teardown (node change, dependency change, or unmount) **instead
|
|
55
|
+
* of** the callback being called with `null`. Callbacks that do not return a
|
|
56
|
+
* cleanup continue to receive `null` on teardown as before.
|
|
34
57
|
*
|
|
35
58
|
* It's also possible to _disable_ a ref (and its behaviour) by simply not
|
|
36
59
|
* passing the ref.
|
|
37
60
|
*
|
|
38
61
|
* ```jsx
|
|
39
|
-
* const ref =
|
|
62
|
+
* const ref = useCallback( ( node ) => {
|
|
40
63
|
* node.addEventListener( ... );
|
|
41
64
|
* return () => {
|
|
42
65
|
* node.removeEventListener( ... );
|
|
43
66
|
* };
|
|
44
67
|
* }, [ ...dependencies ] );
|
|
45
68
|
* const otherRef = useRef();
|
|
46
|
-
* const mergedRefs useMergeRefs( [
|
|
69
|
+
* const mergedRefs = useMergeRefs( [
|
|
47
70
|
* enabled && ref,
|
|
48
71
|
* otherRef,
|
|
49
72
|
* ] );
|
|
@@ -56,11 +79,15 @@ function assignRef< T >( ref: Ref< T >, value: T ) {
|
|
|
56
79
|
export default function useMergeRefs< T >(
|
|
57
80
|
refs: Ref< T >[]
|
|
58
81
|
): RefCallback< T > {
|
|
59
|
-
const
|
|
82
|
+
const elementRef = useRef< T | null >( null );
|
|
60
83
|
const isAttachedRef = useRef( false );
|
|
61
84
|
const didElementChangeRef = useRef( false );
|
|
62
85
|
const previousRefsRef = useRef< Ref< T >[] >( [] );
|
|
63
86
|
const currentRefsRef = useRef( refs );
|
|
87
|
+
// Position-indexed cleanups returned by inner ref callbacks. A slot is
|
|
88
|
+
// `undefined` when the ref at that position did not return a cleanup (or
|
|
89
|
+
// is an object ref / disabled).
|
|
90
|
+
const cleanupsRef = useRef< Array< ( () => void ) | undefined > >( [] );
|
|
64
91
|
|
|
65
92
|
// Update on render before the ref callback is called, so the ref callback
|
|
66
93
|
// always has access to the current refs.
|
|
@@ -77,8 +104,11 @@ export default function useMergeRefs< T >(
|
|
|
77
104
|
refs.forEach( ( ref, index ) => {
|
|
78
105
|
const previousRef = previousRefsRef.current[ index ];
|
|
79
106
|
if ( ref !== previousRef ) {
|
|
80
|
-
|
|
81
|
-
|
|
107
|
+
detachRef( previousRef, index, cleanupsRef.current );
|
|
108
|
+
cleanupsRef.current[ index ] = assignRef(
|
|
109
|
+
ref,
|
|
110
|
+
elementRef.current as T
|
|
111
|
+
);
|
|
82
112
|
}
|
|
83
113
|
} );
|
|
84
114
|
}
|
|
@@ -97,20 +127,21 @@ export default function useMergeRefs< T >(
|
|
|
97
127
|
return useCallback( ( value: T | null ) => {
|
|
98
128
|
// Update the element so it can be used when calling ref callbacks on a
|
|
99
129
|
// dependency change.
|
|
100
|
-
|
|
130
|
+
elementRef.current = value;
|
|
101
131
|
|
|
102
132
|
didElementChangeRef.current = true;
|
|
103
133
|
isAttachedRef.current = value !== null;
|
|
104
134
|
|
|
105
135
|
// When an element changes, the current ref callback should be called
|
|
106
136
|
// with the new element and the previous one with `null`.
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
137
|
+
if ( value === null ) {
|
|
138
|
+
previousRefsRef.current.forEach( ( ref, index ) => {
|
|
139
|
+
detachRef( ref, index, cleanupsRef.current );
|
|
140
|
+
} );
|
|
141
|
+
} else {
|
|
142
|
+
currentRefsRef.current.forEach( ( ref, index ) => {
|
|
143
|
+
cleanupsRef.current[ index ] = assignRef( ref, value );
|
|
144
|
+
} );
|
|
114
145
|
}
|
|
115
146
|
}, [] );
|
|
116
147
|
}
|
|
@@ -345,3 +345,291 @@ describe( 'useMergeRefs', () => {
|
|
|
345
345
|
] );
|
|
346
346
|
} );
|
|
347
347
|
} );
|
|
348
|
+
|
|
349
|
+
describe( 'useMergeRefs with cleanup-returning ref callbacks', () => {
|
|
350
|
+
// Setup
|
|
351
|
+
// =====
|
|
352
|
+
//
|
|
353
|
+
// Same structure as the suite above, but the ref callbacks return a
|
|
354
|
+
// cleanup function. React 19 (and `useMergeRefs`) invokes the cleanup at
|
|
355
|
+
// teardown instead of calling the callback with `null`. The cleanup pushes
|
|
356
|
+
// the string `'cleanup'` into the same history array, so each call site's
|
|
357
|
+
// full lifecycle is captured as `[ node, 'cleanup', newNode, 'cleanup' ]`.
|
|
358
|
+
// The strict invariant is that `null` never appears in the history of a
|
|
359
|
+
// cleanup-returning ref.
|
|
360
|
+
|
|
361
|
+
function renderCallback( args ) {
|
|
362
|
+
renderCallback.history.push( args );
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
renderCallback.history = [];
|
|
366
|
+
|
|
367
|
+
function MergedRefs( {
|
|
368
|
+
count,
|
|
369
|
+
tagName: TagName = 'ul',
|
|
370
|
+
disable1,
|
|
371
|
+
disable2,
|
|
372
|
+
} ) {
|
|
373
|
+
function refCallback1( value ) {
|
|
374
|
+
refCallback1.history.push( value );
|
|
375
|
+
return () => {
|
|
376
|
+
refCallback1.history.push( 'cleanup' );
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
refCallback1.history = [];
|
|
381
|
+
|
|
382
|
+
function refCallback2( value ) {
|
|
383
|
+
refCallback2.history.push( value );
|
|
384
|
+
return () => {
|
|
385
|
+
refCallback2.history.push( 'cleanup' );
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
refCallback2.history = [];
|
|
390
|
+
|
|
391
|
+
renderCallback( [ refCallback1.history, refCallback2.history ] );
|
|
392
|
+
|
|
393
|
+
const ref1 = useCallback( refCallback1, [] );
|
|
394
|
+
const ref2 = useCallback( refCallback2, [ count ] );
|
|
395
|
+
const mergedRefs = useMergeRefs( [
|
|
396
|
+
! disable1 && ref1,
|
|
397
|
+
! disable2 && ref2,
|
|
398
|
+
] );
|
|
399
|
+
|
|
400
|
+
return <TagName ref={ mergedRefs } />;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
afterEach( () => {
|
|
404
|
+
renderCallback.history = [];
|
|
405
|
+
} );
|
|
406
|
+
|
|
407
|
+
it( 'should invoke cleanup on unmount instead of calling ref with null', () => {
|
|
408
|
+
const { unmount } = render( <MergedRefs /> );
|
|
409
|
+
|
|
410
|
+
const originalElement = screen.getByRole( 'list' );
|
|
411
|
+
|
|
412
|
+
expect( renderCallback.history ).toEqual( [
|
|
413
|
+
[ [ originalElement ], [ originalElement ] ],
|
|
414
|
+
] );
|
|
415
|
+
|
|
416
|
+
unmount();
|
|
417
|
+
|
|
418
|
+
// Cleanups run; refs are NOT called with `null`.
|
|
419
|
+
expect( renderCallback.history ).toEqual( [
|
|
420
|
+
[
|
|
421
|
+
[ originalElement, 'cleanup' ],
|
|
422
|
+
[ originalElement, 'cleanup' ],
|
|
423
|
+
],
|
|
424
|
+
] );
|
|
425
|
+
} );
|
|
426
|
+
|
|
427
|
+
it( 'should invoke cleanup on node change instead of calling ref with null', () => {
|
|
428
|
+
const { rerender, unmount } = render( <MergedRefs /> );
|
|
429
|
+
|
|
430
|
+
const originalElement = screen.getByRole( 'list' );
|
|
431
|
+
|
|
432
|
+
rerender( <MergedRefs tagName="button" /> );
|
|
433
|
+
|
|
434
|
+
const newElement = screen.getByRole( 'button' );
|
|
435
|
+
|
|
436
|
+
// Node change triggers the outer callback first with `null` (which
|
|
437
|
+
// runs the cleanups) and then with the new node (which sets up new
|
|
438
|
+
// cleanups).
|
|
439
|
+
expect( renderCallback.history ).toEqual( [
|
|
440
|
+
[
|
|
441
|
+
[ originalElement, 'cleanup', newElement ],
|
|
442
|
+
[ originalElement, 'cleanup', newElement ],
|
|
443
|
+
],
|
|
444
|
+
[ [], [] ],
|
|
445
|
+
] );
|
|
446
|
+
|
|
447
|
+
unmount();
|
|
448
|
+
|
|
449
|
+
expect( renderCallback.history ).toEqual( [
|
|
450
|
+
[
|
|
451
|
+
[ originalElement, 'cleanup', newElement, 'cleanup' ],
|
|
452
|
+
[ originalElement, 'cleanup', newElement, 'cleanup' ],
|
|
453
|
+
],
|
|
454
|
+
[ [], [] ],
|
|
455
|
+
] );
|
|
456
|
+
} );
|
|
457
|
+
|
|
458
|
+
it( 'should invoke cleanup on dependency change instead of calling ref with null', () => {
|
|
459
|
+
const { rerender, unmount } = render( <MergedRefs count={ 1 } /> );
|
|
460
|
+
|
|
461
|
+
const originalElement = screen.getByRole( 'list' );
|
|
462
|
+
|
|
463
|
+
expect( renderCallback.history ).toEqual( [
|
|
464
|
+
[ [ originalElement ], [ originalElement ] ],
|
|
465
|
+
] );
|
|
466
|
+
|
|
467
|
+
rerender( <MergedRefs count={ 2 } /> );
|
|
468
|
+
|
|
469
|
+
// Only ref2's deps changed. Its previous cleanup runs and the new ref
|
|
470
|
+
// callback is called with the still-attached node. ref1 is untouched.
|
|
471
|
+
expect( renderCallback.history ).toEqual( [
|
|
472
|
+
[ [ originalElement ], [ originalElement, 'cleanup' ] ],
|
|
473
|
+
[ [], [ originalElement ] ],
|
|
474
|
+
] );
|
|
475
|
+
|
|
476
|
+
unmount();
|
|
477
|
+
|
|
478
|
+
expect( renderCallback.history ).toEqual( [
|
|
479
|
+
[
|
|
480
|
+
[ originalElement, 'cleanup' ],
|
|
481
|
+
[ originalElement, 'cleanup' ],
|
|
482
|
+
],
|
|
483
|
+
[ [], [ originalElement, 'cleanup' ] ],
|
|
484
|
+
] );
|
|
485
|
+
} );
|
|
486
|
+
|
|
487
|
+
it( 'should invoke cleanup on simultaneous node and dependency change', () => {
|
|
488
|
+
const { rerender, unmount } = render( <MergedRefs count={ 1 } /> );
|
|
489
|
+
|
|
490
|
+
const originalElement = screen.getByRole( 'list' );
|
|
491
|
+
|
|
492
|
+
rerender( <MergedRefs count={ 2 } tagName="button" /> );
|
|
493
|
+
|
|
494
|
+
const newElement = screen.getByRole( 'button' );
|
|
495
|
+
|
|
496
|
+
// Outer callback's null/new-node pair runs both cleanups and sets
|
|
497
|
+
// up the new ones (ref1 reused, ref2 has a new identity).
|
|
498
|
+
expect( renderCallback.history ).toEqual( [
|
|
499
|
+
[
|
|
500
|
+
[ originalElement, 'cleanup', newElement ],
|
|
501
|
+
[ originalElement, 'cleanup' ],
|
|
502
|
+
],
|
|
503
|
+
[ [], [ newElement ] ],
|
|
504
|
+
] );
|
|
505
|
+
|
|
506
|
+
unmount();
|
|
507
|
+
|
|
508
|
+
expect( renderCallback.history ).toEqual( [
|
|
509
|
+
[
|
|
510
|
+
[ originalElement, 'cleanup', newElement, 'cleanup' ],
|
|
511
|
+
[ originalElement, 'cleanup' ],
|
|
512
|
+
],
|
|
513
|
+
[ [], [ newElement, 'cleanup' ] ],
|
|
514
|
+
] );
|
|
515
|
+
} );
|
|
516
|
+
|
|
517
|
+
it( 'should invoke cleanup when a ref becomes disabled', () => {
|
|
518
|
+
const { rerender, unmount } = render( <MergedRefs /> );
|
|
519
|
+
|
|
520
|
+
const originalElement = screen.getByRole( 'list' );
|
|
521
|
+
|
|
522
|
+
rerender( <MergedRefs disable1 /> );
|
|
523
|
+
|
|
524
|
+
// ref1 is disabled: its stored cleanup must fire. ref2 is unchanged.
|
|
525
|
+
expect( renderCallback.history ).toEqual( [
|
|
526
|
+
[ [ originalElement, 'cleanup' ], [ originalElement ] ],
|
|
527
|
+
[ [], [] ],
|
|
528
|
+
] );
|
|
529
|
+
|
|
530
|
+
unmount();
|
|
531
|
+
|
|
532
|
+
expect( renderCallback.history ).toEqual( [
|
|
533
|
+
[
|
|
534
|
+
[ originalElement, 'cleanup' ],
|
|
535
|
+
[ originalElement, 'cleanup' ],
|
|
536
|
+
],
|
|
537
|
+
[ [], [] ],
|
|
538
|
+
] );
|
|
539
|
+
} );
|
|
540
|
+
|
|
541
|
+
it( 'should support mixing a cleanup-returning ref with a void-returning ref', () => {
|
|
542
|
+
function MergedRefsMixed() {
|
|
543
|
+
function refCleanup( value ) {
|
|
544
|
+
refCleanup.history.push( value );
|
|
545
|
+
return () => {
|
|
546
|
+
refCleanup.history.push( 'cleanup' );
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
refCleanup.history = [];
|
|
551
|
+
|
|
552
|
+
function refVoid( value ) {
|
|
553
|
+
refVoid.history.push( value );
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
refVoid.history = [];
|
|
557
|
+
|
|
558
|
+
renderCallback( [ refCleanup.history, refVoid.history ] );
|
|
559
|
+
|
|
560
|
+
const r1 = useCallback( refCleanup, [] );
|
|
561
|
+
const r2 = useCallback( refVoid, [] );
|
|
562
|
+
const merged = useMergeRefs( [ r1, r2 ] );
|
|
563
|
+
|
|
564
|
+
return <ul ref={ merged } />;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
const { unmount } = render( <MergedRefsMixed /> );
|
|
568
|
+
|
|
569
|
+
const el = screen.getByRole( 'list' );
|
|
570
|
+
|
|
571
|
+
expect( renderCallback.history ).toEqual( [ [ [ el ], [ el ] ] ] );
|
|
572
|
+
|
|
573
|
+
unmount();
|
|
574
|
+
|
|
575
|
+
// Cleanup ref sees the cleanup; void ref still sees null. Independent.
|
|
576
|
+
expect( renderCallback.history ).toEqual( [
|
|
577
|
+
[
|
|
578
|
+
[ el, 'cleanup' ],
|
|
579
|
+
[ el, null ],
|
|
580
|
+
],
|
|
581
|
+
] );
|
|
582
|
+
} );
|
|
583
|
+
|
|
584
|
+
it( 'should support mixing an object ref with a cleanup-returning ref', () => {
|
|
585
|
+
const objectRef = { current: null };
|
|
586
|
+
|
|
587
|
+
function MergedRefsMixed( { tagName: TagName = 'ul' } ) {
|
|
588
|
+
function refCleanup( value ) {
|
|
589
|
+
refCleanup.history.push( value );
|
|
590
|
+
return () => {
|
|
591
|
+
refCleanup.history.push( 'cleanup' );
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
refCleanup.history = [];
|
|
596
|
+
|
|
597
|
+
renderCallback( [ refCleanup.history ] );
|
|
598
|
+
|
|
599
|
+
const r = useCallback( refCleanup, [] );
|
|
600
|
+
const merged = useMergeRefs( [ objectRef, r ] );
|
|
601
|
+
|
|
602
|
+
return <TagName ref={ merged } />;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
const { rerender, unmount } = render( <MergedRefsMixed /> );
|
|
606
|
+
|
|
607
|
+
const originalElement = screen.getByRole( 'list' );
|
|
608
|
+
|
|
609
|
+
// Object ref's .current is set; cleanup ref is called with the node.
|
|
610
|
+
expect( objectRef.current ).toBe( originalElement );
|
|
611
|
+
expect( renderCallback.history ).toEqual( [ [ [ originalElement ] ] ] );
|
|
612
|
+
|
|
613
|
+
rerender( <MergedRefsMixed tagName="button" /> );
|
|
614
|
+
|
|
615
|
+
const newElement = screen.getByRole( 'button' );
|
|
616
|
+
|
|
617
|
+
// Node change: object ref retargets, callback ref's cleanup fires
|
|
618
|
+
// and the callback is re-invoked with the new node.
|
|
619
|
+
expect( objectRef.current ).toBe( newElement );
|
|
620
|
+
expect( renderCallback.history ).toEqual( [
|
|
621
|
+
[ [ originalElement, 'cleanup', newElement ] ],
|
|
622
|
+
[ [] ],
|
|
623
|
+
] );
|
|
624
|
+
|
|
625
|
+
unmount();
|
|
626
|
+
|
|
627
|
+
// Object ref nulled via the fallback assignRef path; callback ref
|
|
628
|
+
// sees its cleanup, never null.
|
|
629
|
+
expect( objectRef.current ).toBe( null );
|
|
630
|
+
expect( renderCallback.history ).toEqual( [
|
|
631
|
+
[ [ originalElement, 'cleanup', newElement, 'cleanup' ] ],
|
|
632
|
+
[ [] ],
|
|
633
|
+
] );
|
|
634
|
+
} );
|
|
635
|
+
} );
|
|
@@ -18,7 +18,7 @@ export function useResizeObserver< T extends HTMLElement >(
|
|
|
18
18
|
): ( element?: T | null ) => void {
|
|
19
19
|
const callbackEvent = useEvent( callback );
|
|
20
20
|
|
|
21
|
-
const observedElementRef = useRef< T >( null );
|
|
21
|
+
const observedElementRef = useRef< T | null >( null );
|
|
22
22
|
const resizeObserverRef = useRef< ResizeObserver >( undefined );
|
|
23
23
|
return useEvent( ( element?: T | null ) => {
|
|
24
24
|
if ( element === observedElementRef.current ) {
|
|
@@ -64,7 +64,7 @@ ViewportMatchWidthContext.displayName = 'ViewportMatchWidthContext';
|
|
|
64
64
|
*
|
|
65
65
|
* @param {WPBreakpoint} breakpoint Breakpoint size name.
|
|
66
66
|
* @param {WPViewportOperator} [operator=">="] Viewport operator.
|
|
67
|
-
* @param {Window}
|
|
67
|
+
* @param {Window|undefined} [view=window] Window instance in which to perform viewport matching.
|
|
68
68
|
*
|
|
69
69
|
* @example
|
|
70
70
|
*
|
|
@@ -75,7 +75,13 @@ ViewportMatchWidthContext.displayName = 'ViewportMatchWidthContext';
|
|
|
75
75
|
*
|
|
76
76
|
* @return {boolean} Whether viewport matches query.
|
|
77
77
|
*/
|
|
78
|
-
const useViewportMatch = (
|
|
78
|
+
const useViewportMatch = (
|
|
79
|
+
breakpoint,
|
|
80
|
+
operator = '>=',
|
|
81
|
+
// Resolve the default lazily so SSR (where `window` is undeclared) does not
|
|
82
|
+
// throw a ReferenceError when this default expression is evaluated.
|
|
83
|
+
view = typeof window !== 'undefined' ? window : undefined
|
|
84
|
+
) => {
|
|
79
85
|
const simulatedWidth = useContext( ViewportMatchWidthContext );
|
|
80
86
|
const mediaQuery =
|
|
81
87
|
! simulatedWidth &&
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
# withNetworkConnectivity
|
|
2
|
-
|
|
3
|
-
`withNetworkConnectivity` provides a true/false mobile connectivity status based on the `useNetworkConnectivity` hook.
|
|
4
|
-
|
|
5
|
-
## Usage
|
|
6
|
-
|
|
7
|
-
```jsx
|
|
8
|
-
/**
|
|
9
|
-
* WordPress dependencies
|
|
10
|
-
*/
|
|
11
|
-
import { withNetworkConnectivity } from '@wordpress/compose';
|
|
12
|
-
|
|
13
|
-
export class MyComponent extends Component {
|
|
14
|
-
if ( this.props.isConnected !== true ) {
|
|
15
|
-
console.log( 'You are currently offline.' )
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export default withNetworkConnectivity( MyComponent )
|
|
20
|
-
```
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Internal dependencies
|
|
3
|
-
*/
|
|
4
|
-
import { createHigherOrderComponent } from '../../utils/create-higher-order-component';
|
|
5
|
-
import useNetworkConnectivity from '../../hooks/use-network-connectivity';
|
|
6
|
-
|
|
7
|
-
const withNetworkConnectivity = createHigherOrderComponent(
|
|
8
|
-
( WrappedComponent ) => {
|
|
9
|
-
return ( props ) => {
|
|
10
|
-
const { isConnected } = useNetworkConnectivity();
|
|
11
|
-
return (
|
|
12
|
-
<WrappedComponent { ...props } isConnected={ isConnected } />
|
|
13
|
-
);
|
|
14
|
-
};
|
|
15
|
-
},
|
|
16
|
-
'withNetworkConnectivity'
|
|
17
|
-
);
|
|
18
|
-
|
|
19
|
-
export default withNetworkConnectivity;
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Internal dependencies
|
|
3
|
-
*/
|
|
4
|
-
import { createHigherOrderComponent } from '../../utils/create-higher-order-component';
|
|
5
|
-
import usePreferredColorScheme from '../../hooks/use-preferred-color-scheme';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* WordPress dependencies
|
|
9
|
-
*/
|
|
10
|
-
import { useCallback } from '@wordpress/element';
|
|
11
|
-
|
|
12
|
-
const withPreferredColorScheme = createHigherOrderComponent(
|
|
13
|
-
( WrappedComponent ) => ( props ) => {
|
|
14
|
-
const colorScheme = usePreferredColorScheme();
|
|
15
|
-
const isDarkMode = colorScheme === 'dark';
|
|
16
|
-
|
|
17
|
-
const getStyles = useCallback(
|
|
18
|
-
( lightStyles, darkStyles ) => {
|
|
19
|
-
const finalDarkStyles = {
|
|
20
|
-
...lightStyles,
|
|
21
|
-
...darkStyles,
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
return isDarkMode ? finalDarkStyles : lightStyles;
|
|
25
|
-
},
|
|
26
|
-
[ isDarkMode ]
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
return (
|
|
30
|
-
<WrappedComponent
|
|
31
|
-
preferredColorScheme={ colorScheme }
|
|
32
|
-
getStylesFromColorScheme={ getStyles }
|
|
33
|
-
{ ...props }
|
|
34
|
-
/>
|
|
35
|
-
);
|
|
36
|
-
},
|
|
37
|
-
'withPreferredColorScheme'
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
export default withPreferredColorScheme;
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WordPress dependencies
|
|
3
|
-
*/
|
|
4
|
-
import { useRef } from '@wordpress/element';
|
|
5
|
-
|
|
6
|
-
function useConstrainedTabbing() {
|
|
7
|
-
const ref = useRef();
|
|
8
|
-
|
|
9
|
-
// Do nothing on mobile as tabbing is not a mobile behavior.
|
|
10
|
-
|
|
11
|
-
return ref;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export default useConstrainedTabbing;
|