@wordpress/compose 5.0.5 → 5.1.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 +2 -0
- package/LICENSE.md +1 -1
- package/README.md +4 -16
- package/build/higher-order/if-condition/index.js.map +1 -1
- package/build/higher-order/pure/index.js +1 -1
- package/build/higher-order/pure/index.js.map +1 -1
- package/build/higher-order/with-global-events/index.js +5 -2
- package/build/higher-order/with-global-events/index.js.map +1 -1
- package/build/higher-order/with-instance-id/index.js +2 -6
- package/build/higher-order/with-instance-id/index.js.map +1 -1
- package/build/higher-order/with-safe-timeout/index.js.map +1 -1
- package/build/higher-order/with-state/index.js +3 -1
- package/build/higher-order/with-state/index.js.map +1 -1
- package/build/hooks/use-async-list/index.js +4 -3
- package/build/hooks/use-async-list/index.js.map +1 -1
- package/build/hooks/use-constrained-tabbing/index.js +2 -2
- package/build/hooks/use-constrained-tabbing/index.js.map +1 -1
- package/build/hooks/use-copy-on-click/index.js +9 -7
- package/build/hooks/use-copy-on-click/index.js.map +1 -1
- package/build/hooks/use-copy-to-clipboard/index.js +4 -3
- package/build/hooks/use-copy-to-clipboard/index.js.map +1 -1
- package/build/hooks/use-dialog/index.js +2 -2
- package/build/hooks/use-dialog/index.js.map +1 -1
- package/build/hooks/use-disabled/index.js +114 -0
- package/build/hooks/use-disabled/index.js.map +1 -0
- package/build/hooks/use-dragging/index.js +12 -11
- package/build/hooks/use-dragging/index.js.map +1 -1
- package/build/hooks/use-drop-zone/index.js +10 -9
- package/build/hooks/use-drop-zone/index.js.map +1 -1
- package/build/hooks/use-fixed-window-list/index.js +8 -8
- package/build/hooks/use-fixed-window-list/index.js.map +1 -1
- package/build/hooks/use-focus-on-mount/index.js +2 -1
- package/build/hooks/use-focus-on-mount/index.js.map +1 -1
- package/build/hooks/use-instance-id/index.js +2 -1
- package/build/hooks/use-instance-id/index.js.map +1 -1
- package/build/hooks/use-keyboard-shortcut/index.js +17 -15
- package/build/hooks/use-keyboard-shortcut/index.js.map +1 -1
- package/build/hooks/use-preferred-color-scheme/index.android.js +4 -3
- package/build/hooks/use-preferred-color-scheme/index.android.js.map +1 -1
- package/build/hooks/use-ref-effect/index.js +0 -1
- package/build/hooks/use-ref-effect/index.js.map +1 -1
- package/build/hooks/use-resize-observer/index.native.js +4 -3
- package/build/hooks/use-resize-observer/index.native.js.map +1 -1
- package/build/hooks/use-viewport-match/index.js +2 -1
- package/build/hooks/use-viewport-match/index.js.map +1 -1
- package/build/hooks/use-warn-on-change/index.js +5 -2
- package/build/hooks/use-warn-on-change/index.js.map +1 -1
- package/build/index.js +64 -56
- package/build/index.js.map +1 -1
- package/build/index.native.js +46 -46
- package/build/utils/create-higher-order-component/index.js +2 -2
- package/build/utils/create-higher-order-component/index.js.map +1 -1
- package/build-module/higher-order/if-condition/index.js +1 -1
- package/build-module/higher-order/if-condition/index.js.map +1 -1
- package/build-module/higher-order/pure/index.js +4 -1
- package/build-module/higher-order/pure/index.js.map +1 -1
- package/build-module/higher-order/with-global-events/index.js +5 -2
- package/build-module/higher-order/with-global-events/index.js.map +1 -1
- package/build-module/higher-order/with-instance-id/index.js +2 -6
- package/build-module/higher-order/with-instance-id/index.js.map +1 -1
- package/build-module/higher-order/with-safe-timeout/index.js +9 -1
- package/build-module/higher-order/with-safe-timeout/index.js.map +1 -1
- package/build-module/higher-order/with-state/index.js +3 -1
- package/build-module/higher-order/with-state/index.js.map +1 -1
- package/build-module/hooks/use-async-list/index.js +4 -3
- package/build-module/hooks/use-async-list/index.js.map +1 -1
- package/build-module/hooks/use-constrained-tabbing/index.js +2 -2
- package/build-module/hooks/use-constrained-tabbing/index.js.map +1 -1
- package/build-module/hooks/use-copy-on-click/index.js +9 -7
- package/build-module/hooks/use-copy-on-click/index.js.map +1 -1
- package/build-module/hooks/use-copy-to-clipboard/index.js +4 -3
- package/build-module/hooks/use-copy-to-clipboard/index.js.map +1 -1
- package/build-module/hooks/use-dialog/index.js +2 -2
- package/build-module/hooks/use-dialog/index.js.map +1 -1
- package/build-module/hooks/use-disabled/index.js +104 -0
- package/build-module/hooks/use-disabled/index.js.map +1 -0
- package/build-module/hooks/use-dragging/index.js +12 -11
- package/build-module/hooks/use-dragging/index.js.map +1 -1
- package/build-module/hooks/use-drop-zone/index.js +10 -9
- package/build-module/hooks/use-drop-zone/index.js.map +1 -1
- package/build-module/hooks/use-fixed-window-list/index.js +8 -8
- package/build-module/hooks/use-fixed-window-list/index.js.map +1 -1
- package/build-module/hooks/use-focus-on-mount/index.js +2 -1
- package/build-module/hooks/use-focus-on-mount/index.js.map +1 -1
- package/build-module/hooks/use-instance-id/index.js +2 -1
- package/build-module/hooks/use-instance-id/index.js.map +1 -1
- package/build-module/hooks/use-keyboard-shortcut/index.js +17 -15
- package/build-module/hooks/use-keyboard-shortcut/index.js.map +1 -1
- package/build-module/hooks/use-preferred-color-scheme/index.android.js +4 -3
- package/build-module/hooks/use-preferred-color-scheme/index.android.js.map +1 -1
- package/build-module/hooks/use-ref-effect/index.js +0 -1
- package/build-module/hooks/use-ref-effect/index.js.map +1 -1
- package/build-module/hooks/use-resize-observer/index.native.js +4 -3
- package/build-module/hooks/use-resize-observer/index.native.js.map +1 -1
- package/build-module/hooks/use-viewport-match/index.js +2 -1
- package/build-module/hooks/use-viewport-match/index.js.map +1 -1
- package/build-module/hooks/use-warn-on-change/index.js +5 -2
- package/build-module/hooks/use-warn-on-change/index.js.map +1 -1
- package/build-module/index.js +1 -0
- package/build-module/index.js.map +1 -1
- package/build-module/utils/create-higher-order-component/index.js +3 -3
- package/build-module/utils/create-higher-order-component/index.js.map +1 -1
- package/build-types/higher-order/if-condition/index.d.ts +2 -2
- package/build-types/higher-order/if-condition/index.d.ts.map +1 -1
- package/build-types/higher-order/pure/index.d.ts +6 -3
- package/build-types/higher-order/pure/index.d.ts.map +1 -1
- package/build-types/higher-order/with-global-events/index.d.ts.map +1 -1
- package/build-types/higher-order/with-instance-id/index.d.ts +3 -3
- package/build-types/higher-order/with-instance-id/index.d.ts.map +1 -1
- package/build-types/higher-order/with-safe-timeout/index.d.ts +2 -2
- package/build-types/higher-order/with-safe-timeout/index.d.ts.map +1 -1
- package/build-types/higher-order/with-state/index.d.ts.map +1 -1
- package/build-types/hooks/use-copy-on-click/index.d.ts.map +1 -1
- package/build-types/hooks/use-disabled/index.d.ts +23 -0
- package/build-types/hooks/use-disabled/index.d.ts.map +1 -0
- package/build-types/hooks/use-ref-effect/index.d.ts.map +1 -1
- package/build-types/index.d.ts +1 -0
- package/build-types/utils/create-higher-order-component/index.d.ts +4 -6
- package/build-types/utils/create-higher-order-component/index.d.ts.map +1 -1
- package/package.json +12 -9
- package/src/higher-order/if-condition/index.tsx +5 -6
- package/src/higher-order/pure/index.tsx +5 -5
- package/src/higher-order/with-global-events/index.js +1 -0
- package/src/higher-order/with-instance-id/index.tsx +8 -24
- package/src/higher-order/with-safe-timeout/index.tsx +5 -6
- package/src/higher-order/with-state/index.js +1 -0
- package/src/hooks/use-copy-on-click/index.js +1 -2
- package/src/hooks/use-disabled/index.js +113 -0
- package/src/hooks/use-disabled/test/index.js +92 -0
- package/src/hooks/use-ref-effect/index.ts +0 -1
- package/src/index.js +1 -0
- package/src/utils/create-higher-order-component/index.ts +15 -21
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -8,18 +8,16 @@ import type { ComponentType } from 'react';
|
|
|
8
8
|
* of this is the `pure` HOC which does not change the API surface of the component but
|
|
9
9
|
* simply modifies the internals.
|
|
10
10
|
*/
|
|
11
|
-
export declare type HigherOrderComponent<
|
|
12
|
-
export declare type SimpleHigherOrderComponent = <TProps>(Inner: ComponentType<TProps>) => ComponentType<TProps>;
|
|
13
|
-
export declare type PropInjectingHigherOrderComponent<TRemovedProps> = <TProps extends TRemovedProps>(Inner: ComponentType<TProps>) => ComponentType<Omit<TProps, keyof TRemovedProps>>;
|
|
11
|
+
export declare type HigherOrderComponent<HOCProps extends Record<string, any>> = <InnerProps extends HOCProps>(Inner: ComponentType<InnerProps>) => {} extends HOCProps ? ComponentType<InnerProps> : ComponentType<Omit<InnerProps, keyof HOCProps>>;
|
|
14
12
|
/**
|
|
15
13
|
* Given a function mapping a component to an enhanced component and modifier
|
|
16
14
|
* name, returns the enhanced component augmented with a generated displayName.
|
|
17
15
|
*
|
|
18
|
-
* @param
|
|
19
|
-
* @param
|
|
16
|
+
* @param mapComponent Function mapping component to enhanced component.
|
|
17
|
+
* @param modifierName Seed name from which to generated display name.
|
|
20
18
|
*
|
|
21
19
|
* @return Component class with generated display name assigned.
|
|
22
20
|
*/
|
|
23
|
-
declare function createHigherOrderComponent<
|
|
21
|
+
declare function createHigherOrderComponent<HOCProps extends Record<string, any> = {}>(mapComponent: HigherOrderComponent<HOCProps>, modifierName: string): <InnerProps extends HOCProps>(Inner: ComponentType<InnerProps>) => {} extends HOCProps ? ComponentType<InnerProps> : ComponentType<Omit<InnerProps, keyof HOCProps>>;
|
|
24
22
|
export default createHigherOrderComponent;
|
|
25
23
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/create-higher-order-component/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/create-higher-order-component/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAE3C;;;;;;;;GAQG;AACH,oBAAY,oBAAoB,CAAE,QAAQ,SAAS,MAAM,CAAE,MAAM,EAAE,GAAG,CAAE,IAAK,CAC5E,UAAU,SAAS,QAAQ,EAE3B,KAAK,EAAE,aAAa,CAAE,UAAU,CAAE,KAC9B,EAAE,SAAS,QAAQ,GACrB,aAAa,CAAE,UAAU,CAAE,GAC3B,aAAa,CAAE,IAAI,CAAE,UAAU,EAAE,MAAM,QAAQ,CAAE,CAAE,CAAC;AAEvD;;;;;;;;GAQG;AACH,iBAAS,0BAA0B,CAClC,QAAQ,SAAS,MAAM,CAAE,MAAM,EAAE,GAAG,CAAE,GAAG,EAAE,EACzC,YAAY,EAAE,oBAAoB,CAAE,QAAQ,CAAE,EAAE,YAAY,EAAE,MAAM,wKAWtE;AAED,eAAe,0BAA0B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wordpress/compose",
|
|
3
|
-
"version": "5.0
|
|
3
|
+
"version": "5.1.0",
|
|
4
4
|
"description": "WordPress higher-order components (HOCs).",
|
|
5
5
|
"author": "The WordPress Contributors",
|
|
6
6
|
"license": "GPL-2.0-or-later",
|
|
@@ -29,23 +29,26 @@
|
|
|
29
29
|
"types": "build-types",
|
|
30
30
|
"sideEffects": false,
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@babel/runtime": "^7.
|
|
32
|
+
"@babel/runtime": "^7.16.0",
|
|
33
33
|
"@types/lodash": "^4.14.172",
|
|
34
34
|
"@types/mousetrap": "^1.6.8",
|
|
35
|
-
"@wordpress/deprecated": "^3.
|
|
36
|
-
"@wordpress/dom": "^3.
|
|
37
|
-
"@wordpress/element": "^4.0
|
|
38
|
-
"@wordpress/is-shallow-equal": "^4.
|
|
39
|
-
"@wordpress/keycodes": "^3.
|
|
40
|
-
"@wordpress/priority-queue": "^2.
|
|
35
|
+
"@wordpress/deprecated": "^3.3.0",
|
|
36
|
+
"@wordpress/dom": "^3.3.0",
|
|
37
|
+
"@wordpress/element": "^4.1.0",
|
|
38
|
+
"@wordpress/is-shallow-equal": "^4.3.0",
|
|
39
|
+
"@wordpress/keycodes": "^3.3.0",
|
|
40
|
+
"@wordpress/priority-queue": "^2.3.0",
|
|
41
41
|
"clipboard": "^2.0.8",
|
|
42
42
|
"lodash": "^4.17.21",
|
|
43
43
|
"mousetrap": "^1.6.5",
|
|
44
44
|
"react-resize-aware": "^3.1.0",
|
|
45
45
|
"use-memo-one": "^1.1.1"
|
|
46
46
|
},
|
|
47
|
+
"peerDependencies": {
|
|
48
|
+
"react": "^17.0.0"
|
|
49
|
+
},
|
|
47
50
|
"publishConfig": {
|
|
48
51
|
"access": "public"
|
|
49
52
|
},
|
|
50
|
-
"gitHead": "
|
|
53
|
+
"gitHead": "d95ccb9366e249133cdb1d7b25c382446b9ee502"
|
|
51
54
|
}
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* Internal dependencies
|
|
3
3
|
*/
|
|
4
4
|
import createHigherOrderComponent from '../../utils/create-higher-order-component';
|
|
5
|
-
import type { HigherOrderComponent } from '../../utils/create-higher-order-component';
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
7
|
* Higher-order component creator, creating a new component which renders if
|
|
@@ -21,12 +20,12 @@ import type { HigherOrderComponent } from '../../utils/create-higher-order-compo
|
|
|
21
20
|
*
|
|
22
21
|
* @return Higher-order component.
|
|
23
22
|
*/
|
|
24
|
-
const ifCondition = < TProps, >(
|
|
23
|
+
const ifCondition = < TProps extends Record< string, any > >(
|
|
25
24
|
predicate: ( props: TProps ) => boolean
|
|
26
|
-
)
|
|
27
|
-
createHigherOrderComponent(
|
|
28
|
-
( WrappedComponent ) => ( props
|
|
29
|
-
if ( ! predicate( props ) ) {
|
|
25
|
+
) =>
|
|
26
|
+
createHigherOrderComponent< {} >(
|
|
27
|
+
( WrappedComponent ) => ( props ) => {
|
|
28
|
+
if ( ! predicate( props as TProps ) ) {
|
|
30
29
|
return null;
|
|
31
30
|
}
|
|
32
31
|
|
|
@@ -8,20 +8,20 @@ import { Component } from '@wordpress/element';
|
|
|
8
8
|
* Internal dependencies
|
|
9
9
|
*/
|
|
10
10
|
import createHigherOrderComponent from '../../utils/create-higher-order-component';
|
|
11
|
-
import type { SimpleHigherOrderComponent } from '../../utils/create-higher-order-component';
|
|
12
11
|
|
|
13
12
|
/**
|
|
14
13
|
* External dependencies
|
|
15
14
|
*/
|
|
16
|
-
// eslint-disable-next-line no-restricted-imports
|
|
17
15
|
import type { ComponentType, ComponentClass } from 'react';
|
|
18
16
|
|
|
19
17
|
/**
|
|
20
18
|
* Given a component returns the enhanced component augmented with a component
|
|
21
|
-
* only
|
|
19
|
+
* only re-rendering when its props/state change
|
|
22
20
|
*/
|
|
23
|
-
const pure
|
|
24
|
-
< TProps
|
|
21
|
+
const pure = createHigherOrderComponent(
|
|
22
|
+
< TProps extends Record< string, any > >(
|
|
23
|
+
Wrapped: ComponentType< TProps >
|
|
24
|
+
) => {
|
|
25
25
|
if ( Wrapped.prototype instanceof Component ) {
|
|
26
26
|
return class extends ( Wrapped as ComponentClass< TProps > ) {
|
|
27
27
|
shouldComponentUpdate( nextProps: TProps, nextState: any ) {
|
|
@@ -46,6 +46,7 @@ export default function withGlobalEvents( eventTypesToHandlers ) {
|
|
|
46
46
|
alternative: 'useEffect',
|
|
47
47
|
} );
|
|
48
48
|
|
|
49
|
+
// @ts-ignore We don't need to fix the type-related issues because this is deprecated.
|
|
49
50
|
return createHigherOrderComponent( ( WrappedComponent ) => {
|
|
50
51
|
class Wrapper extends Component {
|
|
51
52
|
constructor( /** @type {any} */ props ) {
|
|
@@ -1,37 +1,21 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* External dependencies
|
|
3
|
-
*/
|
|
4
|
-
// eslint-disable-next-line no-restricted-imports
|
|
5
|
-
import type { ComponentType } from 'react';
|
|
6
|
-
|
|
7
1
|
/**
|
|
8
2
|
* Internal dependencies
|
|
9
3
|
*/
|
|
10
4
|
import createHigherOrderComponent from '../../utils/create-higher-order-component';
|
|
11
|
-
import type { PropInjectingHigherOrderComponent } from '../../utils/create-higher-order-component';
|
|
12
5
|
import useInstanceId from '../../hooks/use-instance-id';
|
|
13
6
|
|
|
14
7
|
/**
|
|
15
8
|
* A Higher Order Component used to be provide a unique instance ID by
|
|
16
9
|
* component.
|
|
17
10
|
*/
|
|
18
|
-
const withInstanceId
|
|
11
|
+
const withInstanceId = createHigherOrderComponent< {
|
|
19
12
|
instanceId: string | number;
|
|
20
|
-
} >
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
return
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
<WrappedComponent
|
|
28
|
-
{ ...( props as TProps ) }
|
|
29
|
-
instanceId={ instanceId }
|
|
30
|
-
/>
|
|
31
|
-
);
|
|
32
|
-
};
|
|
33
|
-
},
|
|
34
|
-
'withInstanceId'
|
|
35
|
-
);
|
|
13
|
+
} >( ( WrappedComponent ) => {
|
|
14
|
+
return ( props ) => {
|
|
15
|
+
const instanceId = useInstanceId( WrappedComponent );
|
|
16
|
+
// @ts-ignore
|
|
17
|
+
return <WrappedComponent { ...props } instanceId={ instanceId } />;
|
|
18
|
+
};
|
|
19
|
+
}, 'withInstanceId' );
|
|
36
20
|
|
|
37
21
|
export default withInstanceId;
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* External dependencies
|
|
3
3
|
*/
|
|
4
4
|
import { without } from 'lodash';
|
|
5
|
-
// eslint-disable-next-line no-restricted-imports
|
|
6
5
|
import type { ComponentType } from 'react';
|
|
7
6
|
|
|
8
7
|
/**
|
|
@@ -14,7 +13,6 @@ import { Component } from '@wordpress/element';
|
|
|
14
13
|
* Internal dependencies
|
|
15
14
|
*/
|
|
16
15
|
import createHigherOrderComponent from '../../utils/create-higher-order-component';
|
|
17
|
-
import type { PropInjectingHigherOrderComponent } from '../../utils/create-higher-order-component';
|
|
18
16
|
|
|
19
17
|
/**
|
|
20
18
|
* We cannot use the `Window['setTimeout']` and `Window['clearTimeout']`
|
|
@@ -33,7 +31,7 @@ type TimeoutProps = {
|
|
|
33
31
|
* A higher-order component used to provide and manage delayed function calls
|
|
34
32
|
* that ought to be bound to a component's lifecycle.
|
|
35
33
|
*/
|
|
36
|
-
const withSafeTimeout
|
|
34
|
+
const withSafeTimeout = createHigherOrderComponent< TimeoutProps >(
|
|
37
35
|
< TProps extends TimeoutProps >(
|
|
38
36
|
OriginalComponent: ComponentType< TProps >
|
|
39
37
|
) => {
|
|
@@ -42,7 +40,7 @@ const withSafeTimeout: PropInjectingHigherOrderComponent< TimeoutProps > = creat
|
|
|
42
40
|
> {
|
|
43
41
|
timeouts: number[];
|
|
44
42
|
|
|
45
|
-
constructor( props:
|
|
43
|
+
constructor( props: Omit< TProps, keyof TimeoutProps > ) {
|
|
46
44
|
super( props );
|
|
47
45
|
this.timeouts = [];
|
|
48
46
|
this.setTimeout = this.setTimeout.bind( this );
|
|
@@ -72,8 +70,9 @@ const withSafeTimeout: PropInjectingHigherOrderComponent< TimeoutProps > = creat
|
|
|
72
70
|
...this.props,
|
|
73
71
|
setTimeout: this.setTimeout,
|
|
74
72
|
clearTimeout: this.clearTimeout,
|
|
75
|
-
};
|
|
76
|
-
|
|
73
|
+
} as TProps;
|
|
74
|
+
|
|
75
|
+
return <OriginalComponent { ...props } />;
|
|
77
76
|
}
|
|
78
77
|
};
|
|
79
78
|
},
|
|
@@ -26,8 +26,7 @@ import deprecated from '@wordpress/deprecated';
|
|
|
26
26
|
export default function useCopyOnClick( ref, text, timeout = 4000 ) {
|
|
27
27
|
/* eslint-enable jsdoc/no-undefined-types */
|
|
28
28
|
deprecated( 'wp.compose.useCopyOnClick', {
|
|
29
|
-
since: '
|
|
30
|
-
plugin: 'Gutenberg',
|
|
29
|
+
since: '5.8',
|
|
31
30
|
alternative: 'wp.compose.useCopyToClipboard',
|
|
32
31
|
} );
|
|
33
32
|
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { includes, debounce } from 'lodash';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* WordPress dependencies
|
|
8
|
+
*/
|
|
9
|
+
import { useCallback, useLayoutEffect, useRef } from '@wordpress/element';
|
|
10
|
+
import { focus } from '@wordpress/dom';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Names of control nodes which qualify for disabled behavior.
|
|
14
|
+
*
|
|
15
|
+
* See WHATWG HTML Standard: 4.10.18.5: "Enabling and disabling form controls: the disabled attribute".
|
|
16
|
+
*
|
|
17
|
+
* @see https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#enabling-and-disabling-form-controls:-the-disabled-attribute
|
|
18
|
+
*
|
|
19
|
+
* @type {string[]}
|
|
20
|
+
*/
|
|
21
|
+
const DISABLED_ELIGIBLE_NODE_NAMES = [
|
|
22
|
+
'BUTTON',
|
|
23
|
+
'FIELDSET',
|
|
24
|
+
'INPUT',
|
|
25
|
+
'OPTGROUP',
|
|
26
|
+
'OPTION',
|
|
27
|
+
'SELECT',
|
|
28
|
+
'TEXTAREA',
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* In some circumstances, such as block previews, all focusable DOM elements
|
|
33
|
+
* (input fields, links, buttons, etc.) need to be disabled. This hook adds the
|
|
34
|
+
* behavior to disable nested DOM elements to the returned ref.
|
|
35
|
+
*
|
|
36
|
+
* @return {import('react').RefObject<HTMLElement>} Element Ref.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```js
|
|
40
|
+
* import { __experimentalUseDisabled as useDisabled } from '@wordpress/compose';
|
|
41
|
+
* const DisabledExample = () => {
|
|
42
|
+
* const disabledRef = useDisabled();
|
|
43
|
+
* return (
|
|
44
|
+
* <div ref={ disabledRef }>
|
|
45
|
+
* <a href="#">This link will have tabindex set to -1</a>
|
|
46
|
+
* <input placeholder="This input will have the disabled attribute added to it." type="text" />
|
|
47
|
+
* </div>
|
|
48
|
+
* );
|
|
49
|
+
* };
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export default function useDisabled() {
|
|
53
|
+
/** @type {import('react').RefObject<HTMLElement>} */
|
|
54
|
+
const node = useRef( null );
|
|
55
|
+
|
|
56
|
+
const disable = () => {
|
|
57
|
+
if ( ! node.current ) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
focus.focusable.find( node.current ).forEach( ( focusable ) => {
|
|
62
|
+
if (
|
|
63
|
+
includes( DISABLED_ELIGIBLE_NODE_NAMES, focusable.nodeName )
|
|
64
|
+
) {
|
|
65
|
+
focusable.setAttribute( 'disabled', '' );
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if ( focusable.nodeName === 'A' ) {
|
|
69
|
+
focusable.setAttribute( 'tabindex', '-1' );
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const tabIndex = focusable.getAttribute( 'tabindex' );
|
|
73
|
+
if ( tabIndex !== null && tabIndex !== '-1' ) {
|
|
74
|
+
focusable.removeAttribute( 'tabindex' );
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if ( focusable.hasAttribute( 'contenteditable' ) ) {
|
|
78
|
+
focusable.setAttribute( 'contenteditable', 'false' );
|
|
79
|
+
}
|
|
80
|
+
} );
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// Debounce re-disable since disabling process itself will incur
|
|
84
|
+
// additional mutations which should be ignored.
|
|
85
|
+
const debouncedDisable = useCallback(
|
|
86
|
+
debounce( disable, undefined, { leading: true } ),
|
|
87
|
+
[]
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
useLayoutEffect( () => {
|
|
91
|
+
disable();
|
|
92
|
+
|
|
93
|
+
/** @type {MutationObserver | undefined} */
|
|
94
|
+
let observer;
|
|
95
|
+
if ( node.current ) {
|
|
96
|
+
observer = new window.MutationObserver( debouncedDisable );
|
|
97
|
+
observer.observe( node.current, {
|
|
98
|
+
childList: true,
|
|
99
|
+
attributes: true,
|
|
100
|
+
subtree: true,
|
|
101
|
+
} );
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return () => {
|
|
105
|
+
if ( observer ) {
|
|
106
|
+
observer.disconnect();
|
|
107
|
+
}
|
|
108
|
+
debouncedDisable.cancel();
|
|
109
|
+
};
|
|
110
|
+
}, [] );
|
|
111
|
+
|
|
112
|
+
return node;
|
|
113
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { render, screen, waitFor } from '@testing-library/react';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* WordPress dependencies
|
|
8
|
+
*/
|
|
9
|
+
import { forwardRef } from '@wordpress/element';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Internal dependencies
|
|
13
|
+
*/
|
|
14
|
+
import useDisabled from '../';
|
|
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
|
+
jest.useRealTimers();
|
|
47
|
+
|
|
48
|
+
describe( 'useDisabled', () => {
|
|
49
|
+
const Form = forwardRef( ( { showButton }, ref ) => {
|
|
50
|
+
return (
|
|
51
|
+
<form ref={ ref }>
|
|
52
|
+
<input />
|
|
53
|
+
<a href="https://wordpress.org/">A link</a>
|
|
54
|
+
<p contentEditable={ true } tabIndex="0"></p>
|
|
55
|
+
{ showButton && <button>Button</button> }
|
|
56
|
+
</form>
|
|
57
|
+
);
|
|
58
|
+
} );
|
|
59
|
+
|
|
60
|
+
function DisabledComponent( props ) {
|
|
61
|
+
const disabledRef = useDisabled();
|
|
62
|
+
return <Form ref={ disabledRef } { ...props } />;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
it( 'will disable all fields', () => {
|
|
66
|
+
const { container } = render( <DisabledComponent /> );
|
|
67
|
+
|
|
68
|
+
const input = screen.getByRole( 'textbox' );
|
|
69
|
+
const link = screen.getByRole( 'link' );
|
|
70
|
+
const p = container.querySelector( 'p' );
|
|
71
|
+
|
|
72
|
+
expect( input.hasAttribute( 'disabled' ) ).toBe( true );
|
|
73
|
+
expect( link.getAttribute( 'tabindex' ) ).toBe( '-1' );
|
|
74
|
+
expect( p.getAttribute( 'contenteditable' ) ).toBe( 'false' );
|
|
75
|
+
expect( p.hasAttribute( 'tabindex' ) ).toBe( false );
|
|
76
|
+
expect( p.hasAttribute( 'disabled' ) ).toBe( false );
|
|
77
|
+
} );
|
|
78
|
+
|
|
79
|
+
it( 'will disable an element rendered in an update to the component', async () => {
|
|
80
|
+
const { rerender } = render(
|
|
81
|
+
<DisabledComponent showButton={ false } />
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
expect( screen.queryByText( 'Button' ) ).not.toBeInTheDocument();
|
|
85
|
+
rerender( <DisabledComponent showButton={ true } /> );
|
|
86
|
+
|
|
87
|
+
const button = screen.getByText( 'Button' );
|
|
88
|
+
await waitFor( () => {
|
|
89
|
+
expect( button.hasAttribute( 'disabled' ) ).toBe( true );
|
|
90
|
+
} );
|
|
91
|
+
} );
|
|
92
|
+
} );
|
package/src/index.js
CHANGED
|
@@ -17,6 +17,7 @@ export { default as useConstrainedTabbing } from './hooks/use-constrained-tabbin
|
|
|
17
17
|
export { default as useCopyOnClick } from './hooks/use-copy-on-click';
|
|
18
18
|
export { default as useCopyToClipboard } from './hooks/use-copy-to-clipboard';
|
|
19
19
|
export { default as __experimentalUseDialog } from './hooks/use-dialog';
|
|
20
|
+
export { default as __experimentalUseDisabled } from './hooks/use-disabled';
|
|
20
21
|
export { default as __experimentalUseDragging } from './hooks/use-dragging';
|
|
21
22
|
export { default as useFocusOnMount } from './hooks/use-focus-on-mount';
|
|
22
23
|
export { default as __experimentalUseFocusOutside } from './hooks/use-focus-outside';
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* External dependencies
|
|
3
3
|
*/
|
|
4
4
|
import { camelCase, upperFirst } from 'lodash';
|
|
5
|
-
// eslint-disable-next-line no-restricted-imports
|
|
6
5
|
import type { ComponentType } from 'react';
|
|
7
6
|
|
|
8
7
|
/**
|
|
@@ -14,35 +13,29 @@ import type { ComponentType } from 'react';
|
|
|
14
13
|
* of this is the `pure` HOC which does not change the API surface of the component but
|
|
15
14
|
* simply modifies the internals.
|
|
16
15
|
*/
|
|
17
|
-
export type HigherOrderComponent<
|
|
18
|
-
|
|
19
|
-
) => ComponentType< TOuterProps >;
|
|
20
|
-
|
|
21
|
-
export type SimpleHigherOrderComponent = < TProps >(
|
|
22
|
-
Inner: ComponentType< TProps >
|
|
23
|
-
) => ComponentType< TProps >;
|
|
24
|
-
|
|
25
|
-
export type PropInjectingHigherOrderComponent< TRemovedProps > = <
|
|
26
|
-
TProps extends TRemovedProps
|
|
16
|
+
export type HigherOrderComponent< HOCProps extends Record< string, any > > = <
|
|
17
|
+
InnerProps extends HOCProps
|
|
27
18
|
>(
|
|
28
|
-
Inner: ComponentType<
|
|
29
|
-
) =>
|
|
19
|
+
Inner: ComponentType< InnerProps >
|
|
20
|
+
) => {} extends HOCProps
|
|
21
|
+
? ComponentType< InnerProps >
|
|
22
|
+
: ComponentType< Omit< InnerProps, keyof HOCProps > >;
|
|
30
23
|
|
|
31
24
|
/**
|
|
32
25
|
* Given a function mapping a component to an enhanced component and modifier
|
|
33
26
|
* name, returns the enhanced component augmented with a generated displayName.
|
|
34
27
|
*
|
|
35
|
-
* @param
|
|
36
|
-
* @param
|
|
28
|
+
* @param mapComponent Function mapping component to enhanced component.
|
|
29
|
+
* @param modifierName Seed name from which to generated display name.
|
|
37
30
|
*
|
|
38
31
|
* @return Component class with generated display name assigned.
|
|
39
32
|
*/
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
33
|
+
function createHigherOrderComponent<
|
|
34
|
+
HOCProps extends Record< string, any > = {}
|
|
35
|
+
>( mapComponent: HigherOrderComponent< HOCProps >, modifierName: string ) {
|
|
36
|
+
return < InnerProps extends HOCProps >(
|
|
37
|
+
Inner: ComponentType< InnerProps >
|
|
38
|
+
) => {
|
|
46
39
|
const Outer = mapComponent( Inner );
|
|
47
40
|
const displayName = Inner.displayName || Inner.name || 'Component';
|
|
48
41
|
Outer.displayName = `${ upperFirst(
|
|
@@ -51,4 +44,5 @@ function createHigherOrderComponent< TInnerProps, TOuterProps >(
|
|
|
51
44
|
return Outer;
|
|
52
45
|
};
|
|
53
46
|
}
|
|
47
|
+
|
|
54
48
|
export default createHigherOrderComponent;
|