@wordpress/components 23.3.0 → 23.3.2
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 +22 -2
- package/build/color-palette/index.js +7 -4
- package/build/color-palette/index.js.map +1 -1
- package/build/color-palette/utils.js +12 -4
- package/build/color-palette/utils.js.map +1 -1
- package/build/custom-select-control/index.js +7 -0
- package/build/custom-select-control/index.js.map +1 -1
- package/build/index.js +16 -10
- package/build/index.js.map +1 -1
- package/build/navigator/context.js +5 -1
- package/build/navigator/context.js.map +1 -1
- package/build/navigator/index.js +8 -0
- package/build/navigator/index.js.map +1 -1
- package/build/navigator/navigator-back-button/hook.js +11 -3
- package/build/navigator/navigator-back-button/hook.js.map +1 -1
- package/build/navigator/navigator-provider/component.js +119 -11
- package/build/navigator/navigator-provider/component.js.map +1 -1
- package/build/navigator/navigator-screen/component.js +18 -7
- package/build/navigator/navigator-screen/component.js.map +1 -1
- package/build/navigator/navigator-to-parent-button/component.js +75 -0
- package/build/navigator/navigator-to-parent-button/component.js.map +1 -0
- package/build/navigator/navigator-to-parent-button/index.js +16 -0
- package/build/navigator/navigator-to-parent-button/index.js.map +1 -0
- package/build/navigator/use-navigator.js +6 -2
- package/build/navigator/use-navigator.js.map +1 -1
- package/build/navigator/utils/router.js +57 -0
- package/build/navigator/utils/router.js.map +1 -0
- package/build/private-apis.js +35 -0
- package/build/private-apis.js.map +1 -0
- package/build/select-control/index.js +1 -1
- package/build/select-control/index.js.map +1 -1
- package/build/select-control/styles/select-control-styles.js +38 -25
- package/build/select-control/styles/select-control-styles.js.map +1 -1
- package/build/tools-panel/tools-panel/hook.js +4 -4
- package/build/tools-panel/tools-panel/hook.js.map +1 -1
- package/build/tools-panel/tools-panel-item/hook.js +20 -11
- package/build/tools-panel/tools-panel-item/hook.js.map +1 -1
- package/build-module/color-palette/index.js +8 -5
- package/build-module/color-palette/index.js.map +1 -1
- package/build-module/color-palette/utils.js +12 -4
- package/build-module/color-palette/utils.js.map +1 -1
- package/build-module/custom-select-control/index.js +5 -0
- package/build-module/custom-select-control/index.js.map +1 -1
- package/build-module/index.js +5 -4
- package/build-module/index.js.map +1 -1
- package/build-module/navigator/context.js +5 -1
- package/build-module/navigator/context.js.map +1 -1
- package/build-module/navigator/index.js +1 -0
- package/build-module/navigator/index.js.map +1 -1
- package/build-module/navigator/navigator-back-button/hook.js +11 -3
- package/build-module/navigator/navigator-back-button/hook.js.map +1 -1
- package/build-module/navigator/navigator-provider/component.js +117 -12
- package/build-module/navigator/navigator-provider/component.js.map +1 -1
- package/build-module/navigator/navigator-screen/component.js +20 -9
- package/build-module/navigator/navigator-screen/component.js.map +1 -1
- package/build-module/navigator/navigator-to-parent-button/component.js +61 -0
- package/build-module/navigator/navigator-to-parent-button/component.js.map +1 -0
- package/build-module/navigator/navigator-to-parent-button/index.js +2 -0
- package/build-module/navigator/navigator-to-parent-button/index.js.map +1 -0
- package/build-module/navigator/use-navigator.js +6 -2
- package/build-module/navigator/use-navigator.js.map +1 -1
- package/build-module/navigator/utils/router.js +51 -0
- package/build-module/navigator/utils/router.js.map +1 -0
- package/build-module/private-apis.js +20 -0
- package/build-module/private-apis.js.map +1 -0
- package/build-module/select-control/index.js +1 -1
- package/build-module/select-control/index.js.map +1 -1
- package/build-module/select-control/styles/select-control-styles.js +38 -25
- package/build-module/select-control/styles/select-control-styles.js.map +1 -1
- package/build-module/tools-panel/tools-panel/hook.js +4 -4
- package/build-module/tools-panel/tools-panel/hook.js.map +1 -1
- package/build-module/tools-panel/tools-panel-item/hook.js +20 -11
- package/build-module/tools-panel/tools-panel-item/hook.js.map +1 -1
- package/build-style/style-rtl.css +0 -11
- package/build-style/style.css +0 -11
- package/build-types/base-control/hooks.d.ts +1 -1
- package/build-types/base-field/hook.d.ts +2 -2
- package/build-types/border-box-control/border-box-control/hook.d.ts +2 -2
- package/build-types/border-box-control/border-box-control-linked-button/hook.d.ts +2 -2
- package/build-types/border-box-control/border-box-control-split-controls/hook.d.ts +2 -2
- package/build-types/border-box-control/border-box-control-visualizer/hook.d.ts +2 -2
- package/build-types/border-control/border-control/hook.d.ts +2 -2
- package/build-types/border-control/border-control-dropdown/hook.d.ts +2 -2
- package/build-types/border-control/border-control-style-picker/hook.d.ts +2 -2
- package/build-types/button/deprecated.d.ts +2 -2
- package/build-types/button/types.d.ts +3 -1
- package/build-types/button/types.d.ts.map +1 -1
- package/build-types/card/card/hook.d.ts +2 -2
- package/build-types/card/card-body/hook.d.ts +2 -2
- package/build-types/card/card-divider/hook.d.ts +2 -2
- package/build-types/card/card-footer/hook.d.ts +2 -2
- package/build-types/card/card-header/hook.d.ts +2 -2
- package/build-types/card/card-media/hook.d.ts +2 -2
- package/build-types/color-palette/index.d.ts.map +1 -1
- package/build-types/color-palette/styles.d.ts +1 -1
- package/build-types/color-palette/utils.d.ts +8 -5
- package/build-types/color-palette/utils.d.ts.map +1 -1
- package/build-types/color-picker/styles.d.ts +7 -7
- package/build-types/custom-select-control/index.d.ts +1 -0
- package/build-types/custom-select-control/index.d.ts.map +1 -1
- package/build-types/date-time/date/styles.d.ts +3 -3
- package/build-types/date-time/date-time/styles.d.ts +3 -3
- package/build-types/date-time/time/styles.d.ts +8 -8
- package/build-types/elevation/hook.d.ts +2 -2
- package/build-types/external-link/styles/external-link-styles.d.ts +1 -1
- package/build-types/flex/flex/hook.d.ts +2 -2
- package/build-types/flex/flex-block/hook.d.ts +2 -2
- package/build-types/flex/flex-item/hook.d.ts +2 -2
- package/build-types/focal-point-picker/styles/focal-point-picker-style.d.ts +2 -2
- package/build-types/form-token-field/styles.d.ts +1 -1
- package/build-types/grid/hook.d.ts +2 -2
- package/build-types/h-stack/component.d.ts +1 -1
- package/build-types/h-stack/hook.d.ts +2 -2
- package/build-types/heading/hook.d.ts +2 -2
- package/build-types/input-control/styles/input-control-styles.d.ts +2 -2
- package/build-types/item-group/item/hook.d.ts +2 -2
- package/build-types/item-group/item-group/hook.d.ts +2 -2
- package/build-types/navigator/context.d.ts.map +1 -1
- package/build-types/navigator/index.d.ts +1 -0
- package/build-types/navigator/index.d.ts.map +1 -1
- package/build-types/navigator/navigator-back-button/component.d.ts +22 -3
- package/build-types/navigator/navigator-back-button/component.d.ts.map +1 -1
- package/build-types/navigator/navigator-back-button/hook.d.ts +24 -6
- package/build-types/navigator/navigator-back-button/hook.d.ts.map +1 -1
- package/build-types/navigator/navigator-button/component.d.ts +22 -3
- package/build-types/navigator/navigator-button/component.d.ts.map +1 -1
- package/build-types/navigator/navigator-button/hook.d.ts +22 -4
- package/build-types/navigator/navigator-button/hook.d.ts.map +1 -1
- package/build-types/navigator/navigator-provider/component.d.ts.map +1 -1
- package/build-types/navigator/navigator-screen/component.d.ts.map +1 -1
- package/build-types/navigator/navigator-to-parent-button/component.d.ts +27 -0
- package/build-types/navigator/navigator-to-parent-button/component.d.ts.map +1 -0
- package/build-types/navigator/navigator-to-parent-button/index.d.ts +2 -0
- package/build-types/navigator/navigator-to-parent-button/index.d.ts.map +1 -0
- package/build-types/navigator/stories/index.d.ts +1 -0
- package/build-types/navigator/stories/index.d.ts.map +1 -1
- package/build-types/navigator/test/router.d.ts +2 -0
- package/build-types/navigator/test/router.d.ts.map +1 -0
- package/build-types/navigator/types.d.ts +25 -9
- package/build-types/navigator/types.d.ts.map +1 -1
- package/build-types/navigator/use-navigator.d.ts.map +1 -1
- package/build-types/navigator/utils/router.d.ts +10 -0
- package/build-types/navigator/utils/router.d.ts.map +1 -0
- package/build-types/number-control/index.d.ts +2 -2
- package/build-types/number-control/stories/index.d.ts +2 -2
- package/build-types/popover/index.d.ts +1 -1
- package/build-types/popover/stories/e2e/index.d.ts +1 -1
- package/build-types/private-apis.d.ts +4 -0
- package/build-types/private-apis.d.ts.map +1 -0
- package/build-types/range-control/index.d.ts +2 -2
- package/build-types/range-control/styles/range-control-styles.d.ts +2 -2
- package/build-types/resizable-box/index.d.ts +1 -1
- package/build-types/resizable-box/resize-tooltip/index.d.ts +1 -1
- package/build-types/resizable-box/stories/index.d.ts +2 -2
- package/build-types/scrollable/hook.d.ts +2 -2
- package/build-types/search-control/index.d.ts +1 -1
- package/build-types/search-control/stories/index.d.ts +2 -2
- package/build-types/select-control/index.d.ts.map +1 -1
- package/build-types/select-control/styles/select-control-styles.d.ts +1 -1
- package/build-types/select-control/styles/select-control-styles.d.ts.map +1 -1
- package/build-types/select-control/types.d.ts +3 -1
- package/build-types/select-control/types.d.ts.map +1 -1
- package/build-types/spacer/hook.d.ts +2 -2
- package/build-types/spinner/index.d.ts +1 -1
- package/build-types/surface/hook.d.ts +2 -2
- package/build-types/text/hook.d.ts +2 -2
- package/build-types/text-control/index.d.ts +3 -3
- package/build-types/tools-panel/tools-panel/hook.d.ts +2 -2
- package/build-types/tools-panel/tools-panel/hook.d.ts.map +1 -1
- package/build-types/tools-panel/tools-panel-header/hook.d.ts +2 -2
- package/build-types/tools-panel/tools-panel-item/hook.d.ts +2 -2
- package/build-types/tools-panel/tools-panel-item/hook.d.ts.map +1 -1
- package/build-types/truncate/hook.d.ts +2 -2
- package/build-types/ui/control-group/hook.d.ts +2 -2
- package/build-types/ui/control-label/hook.d.ts +2 -2
- package/build-types/ui/form-group/form-group.d.ts +2 -2
- package/build-types/ui/form-group/use-form-group.d.ts +2 -2
- package/build-types/unit-control/index.d.ts +1 -1
- package/build-types/unit-control/styles/unit-control-styles.d.ts +2 -2
- package/build-types/v-stack/component.d.ts +2 -2
- package/build-types/v-stack/hook.d.ts +2 -2
- package/build-types/v-stack/stories/index.d.ts +2 -2
- package/package.json +20 -18
- package/src/button/types.ts +5 -2
- package/src/color-palette/index.tsx +13 -5
- package/src/color-palette/test/utils.ts +17 -1
- package/src/color-palette/utils.ts +12 -7
- package/src/custom-select-control/index.js +9 -0
- package/src/custom-select-control/stories/index.js +1 -1
- package/src/custom-select-control/test/index.js +2 -2
- package/src/dimension-control/test/__snapshots__/index.test.js.snap +4 -4
- package/src/index.js +5 -2
- package/src/navigator/context.ts +4 -0
- package/src/navigator/index.ts +1 -0
- package/src/navigator/navigator-back-button/README.md +1 -17
- package/src/navigator/navigator-back-button/hook.ts +10 -5
- package/src/navigator/navigator-button/README.md +1 -1
- package/src/navigator/navigator-provider/README.md +25 -4
- package/src/navigator/navigator-provider/component.tsx +170 -14
- package/src/navigator/navigator-screen/component.tsx +22 -11
- package/src/navigator/navigator-to-parent-button/README.md +15 -0
- package/src/navigator/navigator-to-parent-button/component.tsx +65 -0
- package/src/navigator/navigator-to-parent-button/index.ts +1 -0
- package/src/navigator/stories/index.tsx +93 -3
- package/src/navigator/test/index.tsx +267 -23
- package/src/navigator/test/router.ts +122 -0
- package/src/navigator/types.ts +31 -12
- package/src/navigator/use-navigator.ts +4 -1
- package/src/navigator/utils/router.ts +49 -0
- package/src/private-apis.js +22 -0
- package/src/select-control/README.md +3 -1
- package/src/select-control/index.tsx +3 -1
- package/src/select-control/style.scss +0 -10
- package/src/select-control/styles/select-control-styles.ts +36 -22
- package/src/select-control/types.ts +3 -1
- package/src/tools-panel/test/index.js +65 -0
- package/src/tools-panel/tools-panel/hook.ts +4 -5
- package/src/tools-panel/tools-panel-item/hook.ts +24 -14
- package/tsconfig.json +5 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { patternMatch, findParent } from '../utils/router';
|
|
5
|
+
|
|
6
|
+
describe( 'patternMatch', () => {
|
|
7
|
+
it( 'should return undefined if not pattern is matched', () => {
|
|
8
|
+
const result = patternMatch( '/test', [ { id: 'route', path: '/' } ] );
|
|
9
|
+
expect( result ).toBeUndefined();
|
|
10
|
+
} );
|
|
11
|
+
|
|
12
|
+
it( 'should match a pattern with no params', () => {
|
|
13
|
+
const result = patternMatch( '/test', [
|
|
14
|
+
{ id: 'route', path: '/test' },
|
|
15
|
+
] );
|
|
16
|
+
expect( result ).toEqual( { id: 'route', params: {} } );
|
|
17
|
+
} );
|
|
18
|
+
|
|
19
|
+
it( 'should match a pattern with params', () => {
|
|
20
|
+
const result = patternMatch( '/test/123', [
|
|
21
|
+
{ id: 'route', path: '/test/:id' },
|
|
22
|
+
] );
|
|
23
|
+
expect( result ).toEqual( { id: 'route', params: { id: '123' } } );
|
|
24
|
+
} );
|
|
25
|
+
|
|
26
|
+
it( 'should match the first pattern in case of ambiguity', () => {
|
|
27
|
+
const result = patternMatch( '/test/123', [
|
|
28
|
+
{ id: 'route1', path: '/test/:id' },
|
|
29
|
+
{ id: 'route2', path: '/test/123' },
|
|
30
|
+
] );
|
|
31
|
+
expect( result ).toEqual( { id: 'route1', params: { id: '123' } } );
|
|
32
|
+
} );
|
|
33
|
+
|
|
34
|
+
it( 'should match a pattern with optional params', () => {
|
|
35
|
+
const result = patternMatch( '/test', [
|
|
36
|
+
{ id: 'route', path: '/test/:id?' },
|
|
37
|
+
] );
|
|
38
|
+
expect( result ).toEqual( { id: 'route', params: {} } );
|
|
39
|
+
} );
|
|
40
|
+
|
|
41
|
+
it( 'should return an array of matches for the same param', () => {
|
|
42
|
+
const result = patternMatch( '/some/basic/route', [
|
|
43
|
+
{ id: 'route', path: '/:test+' },
|
|
44
|
+
] );
|
|
45
|
+
expect( result ).toEqual( {
|
|
46
|
+
id: 'route',
|
|
47
|
+
params: { test: [ 'some', 'basic', 'route' ] },
|
|
48
|
+
} );
|
|
49
|
+
} );
|
|
50
|
+
} );
|
|
51
|
+
|
|
52
|
+
describe( 'findParent', () => {
|
|
53
|
+
it( 'should return undefined if no parent is found', () => {
|
|
54
|
+
const result = findParent( '/test', [
|
|
55
|
+
{ id: 'route', path: '/test' },
|
|
56
|
+
] );
|
|
57
|
+
expect( result ).toBeUndefined();
|
|
58
|
+
} );
|
|
59
|
+
|
|
60
|
+
it( 'should return the parent path', () => {
|
|
61
|
+
const result = findParent( '/test', [
|
|
62
|
+
{ id: 'route1', path: '/test' },
|
|
63
|
+
{ id: 'route2', path: '/' },
|
|
64
|
+
] );
|
|
65
|
+
expect( result ).toEqual( '/' );
|
|
66
|
+
} );
|
|
67
|
+
|
|
68
|
+
it( 'should return to another parent path', () => {
|
|
69
|
+
const result = findParent( '/test/123', [
|
|
70
|
+
{ id: 'route1', path: '/test/:id' },
|
|
71
|
+
{ id: 'route2', path: '/test' },
|
|
72
|
+
] );
|
|
73
|
+
expect( result ).toEqual( '/test' );
|
|
74
|
+
} );
|
|
75
|
+
|
|
76
|
+
it( 'should return the parent path with params', () => {
|
|
77
|
+
const result = findParent( '/test/123/456', [
|
|
78
|
+
{ id: 'route1', path: '/test/:id/:subId' },
|
|
79
|
+
{ id: 'route2', path: '/test/:id' },
|
|
80
|
+
] );
|
|
81
|
+
expect( result ).toEqual( '/test/123' );
|
|
82
|
+
} );
|
|
83
|
+
|
|
84
|
+
it( 'should return the parent path with optional params', () => {
|
|
85
|
+
const result = findParent( '/test/123', [
|
|
86
|
+
{ id: 'route', path: '/test/:id?' },
|
|
87
|
+
] );
|
|
88
|
+
expect( result ).toEqual( '/test' );
|
|
89
|
+
} );
|
|
90
|
+
|
|
91
|
+
it( 'should return the grand parent if no parent found', () => {
|
|
92
|
+
const result = findParent( '/test/123/456', [
|
|
93
|
+
{ id: 'route1', path: '/test/:id/:subId' },
|
|
94
|
+
{ id: 'route2', path: '/test' },
|
|
95
|
+
] );
|
|
96
|
+
expect( result ).toEqual( '/test' );
|
|
97
|
+
} );
|
|
98
|
+
|
|
99
|
+
it( 'should return the root when no grand parent found', () => {
|
|
100
|
+
const result = findParent( '/test/nested/path', [
|
|
101
|
+
{ id: 'route1', path: '/other-path' },
|
|
102
|
+
{ id: 'route2', path: '/yet-another-path' },
|
|
103
|
+
{ id: 'root', path: '/' },
|
|
104
|
+
] );
|
|
105
|
+
expect( result ).toEqual( '/' );
|
|
106
|
+
} );
|
|
107
|
+
|
|
108
|
+
it( 'should return undefined when no potential parent found', () => {
|
|
109
|
+
const result = findParent( '/test/nested/path', [
|
|
110
|
+
{ id: 'route1', path: '/other-path' },
|
|
111
|
+
{ id: 'route2', path: '/yet-another-path' },
|
|
112
|
+
] );
|
|
113
|
+
expect( result ).toBeUndefined();
|
|
114
|
+
} );
|
|
115
|
+
|
|
116
|
+
it( 'should return undefined for non supported paths', () => {
|
|
117
|
+
const result = findParent( 'this-is-a-path', [
|
|
118
|
+
{ id: 'route', path: '/' },
|
|
119
|
+
] );
|
|
120
|
+
expect( result ).toBeUndefined();
|
|
121
|
+
} );
|
|
122
|
+
} );
|
package/src/navigator/types.ts
CHANGED
|
@@ -3,25 +3,38 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { ReactNode } from 'react';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Internal dependencies
|
|
8
|
+
*/
|
|
9
|
+
import type { ButtonAsButtonProps } from '../button/types';
|
|
10
|
+
|
|
11
|
+
export type MatchParams = Record< string, string | string[] >;
|
|
12
|
+
|
|
6
13
|
type NavigateOptions = {
|
|
7
14
|
focusTargetSelector?: string;
|
|
15
|
+
isBack?: boolean;
|
|
8
16
|
};
|
|
9
17
|
|
|
10
18
|
export type NavigatorLocation = NavigateOptions & {
|
|
11
19
|
isInitial?: boolean;
|
|
12
|
-
isBack?: boolean;
|
|
13
20
|
path?: string;
|
|
14
21
|
hasRestoredFocus?: boolean;
|
|
15
22
|
};
|
|
16
23
|
|
|
17
|
-
|
|
24
|
+
// Returned by the `useNavigator` hook.
|
|
25
|
+
export type Navigator = {
|
|
18
26
|
location: NavigatorLocation;
|
|
27
|
+
params: MatchParams;
|
|
19
28
|
goTo: ( path: string, options?: NavigateOptions ) => void;
|
|
20
29
|
goBack: () => void;
|
|
30
|
+
goToParent: () => void;
|
|
21
31
|
};
|
|
22
32
|
|
|
23
|
-
|
|
24
|
-
|
|
33
|
+
export type NavigatorContext = Navigator & {
|
|
34
|
+
addScreen: ( screen: Screen ) => void;
|
|
35
|
+
removeScreen: ( screen: Screen ) => void;
|
|
36
|
+
match?: string;
|
|
37
|
+
};
|
|
25
38
|
|
|
26
39
|
export type NavigatorProviderProps = {
|
|
27
40
|
/**
|
|
@@ -45,18 +58,19 @@ export type NavigatorScreenProps = {
|
|
|
45
58
|
children: ReactNode;
|
|
46
59
|
};
|
|
47
60
|
|
|
48
|
-
type
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
variant?: 'primary' | 'secondary' | 'tertiary' | 'link';
|
|
52
|
-
};
|
|
53
|
-
export type NavigatorBackButtonProps = Omit< ButtonProps, 'href' > & {
|
|
61
|
+
export type NavigatorBackButtonProps = ButtonAsButtonProps;
|
|
62
|
+
|
|
63
|
+
export type NavigatorBackButtonHookProps = NavigatorBackButtonProps & {
|
|
54
64
|
/**
|
|
55
|
-
*
|
|
65
|
+
* Whether we should navigate to the parent screen.
|
|
66
|
+
*
|
|
67
|
+
* @default 'false'
|
|
56
68
|
*/
|
|
57
|
-
|
|
69
|
+
goToParent?: boolean;
|
|
58
70
|
};
|
|
59
71
|
|
|
72
|
+
export type NavigatorToParentButtonProps = NavigatorBackButtonProps;
|
|
73
|
+
|
|
60
74
|
export type NavigatorButtonProps = NavigatorBackButtonProps & {
|
|
61
75
|
/**
|
|
62
76
|
* The path of the screen to navigate to. The value of this prop needs to be
|
|
@@ -71,3 +85,8 @@ export type NavigatorButtonProps = NavigatorBackButtonProps & {
|
|
|
71
85
|
*/
|
|
72
86
|
attributeName?: string;
|
|
73
87
|
};
|
|
88
|
+
|
|
89
|
+
export type Screen = {
|
|
90
|
+
id: string;
|
|
91
|
+
path: string;
|
|
92
|
+
};
|
|
@@ -13,12 +13,15 @@ import type { Navigator } from './types';
|
|
|
13
13
|
* Retrieves a `navigator` instance.
|
|
14
14
|
*/
|
|
15
15
|
function useNavigator(): Navigator {
|
|
16
|
-
const { location, goTo, goBack } =
|
|
16
|
+
const { location, params, goTo, goBack, goToParent } =
|
|
17
|
+
useContext( NavigatorContext );
|
|
17
18
|
|
|
18
19
|
return {
|
|
19
20
|
location,
|
|
20
21
|
goTo,
|
|
21
22
|
goBack,
|
|
23
|
+
goToParent,
|
|
24
|
+
params,
|
|
22
25
|
};
|
|
23
26
|
}
|
|
24
27
|
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { match } from 'path-to-regexp';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Internal dependencies
|
|
8
|
+
*/
|
|
9
|
+
import type { Screen, MatchParams } from '../types';
|
|
10
|
+
|
|
11
|
+
function matchPath( path: string, pattern: string ) {
|
|
12
|
+
const matchingFunction = match< MatchParams >( pattern, {
|
|
13
|
+
decode: decodeURIComponent,
|
|
14
|
+
} );
|
|
15
|
+
return matchingFunction( path );
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function patternMatch( path: string, screens: Screen[] ) {
|
|
19
|
+
for ( const screen of screens ) {
|
|
20
|
+
const matched = matchPath( path, screen.path );
|
|
21
|
+
if ( matched ) {
|
|
22
|
+
return { params: matched.params, id: screen.id };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function findParent( path: string, screens: Screen[] ) {
|
|
30
|
+
if ( ! path.startsWith( '/' ) ) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
const pathParts = path.split( '/' );
|
|
34
|
+
let parentPath;
|
|
35
|
+
while ( pathParts.length > 1 && parentPath === undefined ) {
|
|
36
|
+
pathParts.pop();
|
|
37
|
+
const potentialParentPath =
|
|
38
|
+
pathParts.join( '/' ) === '' ? '/' : pathParts.join( '/' );
|
|
39
|
+
if (
|
|
40
|
+
screens.find( ( screen ) => {
|
|
41
|
+
return matchPath( potentialParentPath, screen.path ) !== false;
|
|
42
|
+
} )
|
|
43
|
+
) {
|
|
44
|
+
parentPath = potentialParentPath;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return parentPath;
|
|
49
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { __dangerousOptInToUnstableAPIsOnlyForCoreModules } from '@wordpress/private-apis';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Internal dependencies
|
|
8
|
+
*/
|
|
9
|
+
import { default as CustomSelectControl } from './custom-select-control';
|
|
10
|
+
import { positionToPlacement as __experimentalPopoverLegacyPositionToPlacement } from './popover/utils';
|
|
11
|
+
|
|
12
|
+
export const { lock, unlock } =
|
|
13
|
+
__dangerousOptInToUnstableAPIsOnlyForCoreModules(
|
|
14
|
+
'I know using unstable features means my plugin or theme will inevitably break on the next WordPress release.',
|
|
15
|
+
'@wordpress/components'
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
export const privateApis = {};
|
|
19
|
+
lock( privateApis, {
|
|
20
|
+
CustomSelectControl,
|
|
21
|
+
__experimentalPopoverLegacyPositionToPlacement,
|
|
22
|
+
} );
|
|
@@ -187,7 +187,9 @@ If this property is added, a help text will be generated using help property as
|
|
|
187
187
|
|
|
188
188
|
#### multiple
|
|
189
189
|
|
|
190
|
-
If this property is added, multiple values can be selected. The value passed should be an array.
|
|
190
|
+
If this property is added, multiple values can be selected. The `value` passed should be an array.
|
|
191
|
+
|
|
192
|
+
In most cases, it is preferable to use the `FormTokenField` or `CheckboxControl` components instead.
|
|
191
193
|
|
|
192
194
|
- Type: `Boolean`
|
|
193
195
|
- Required: No
|
|
@@ -101,7 +101,9 @@ function UnforwardedSelectControl(
|
|
|
101
101
|
isFocused={ isFocused }
|
|
102
102
|
label={ label }
|
|
103
103
|
size={ size }
|
|
104
|
-
suffix={
|
|
104
|
+
suffix={
|
|
105
|
+
suffix || ( ! multiple && <SelectControlChevronDown /> )
|
|
106
|
+
}
|
|
105
107
|
prefix={ prefix }
|
|
106
108
|
labelPosition={ labelPosition }
|
|
107
109
|
__next36pxDefaultSize={ __next36pxDefaultSize }
|
|
@@ -1,16 +1,6 @@
|
|
|
1
1
|
.components-select-control__input {
|
|
2
|
-
background: $white;
|
|
3
|
-
height: 36px;
|
|
4
|
-
line-height: 36px;
|
|
5
|
-
margin: 1px;
|
|
6
2
|
outline: 0;
|
|
7
|
-
width: 100%;
|
|
8
3
|
-webkit-tap-highlight-color: rgba(0, 0, 0, 0) !important;
|
|
9
|
-
|
|
10
|
-
@include break-medium() {
|
|
11
|
-
height: 28px;
|
|
12
|
-
line-height: 28px;
|
|
13
|
-
}
|
|
14
4
|
}
|
|
15
5
|
|
|
16
6
|
@media (max-width: #{ ($break-medium) }) {
|
|
@@ -13,7 +13,10 @@ import type { SelectControlProps } from '../types';
|
|
|
13
13
|
import InputControlSuffixWrapper from '../../input-control/input-suffix-wrapper';
|
|
14
14
|
|
|
15
15
|
interface SelectProps
|
|
16
|
-
extends Pick<
|
|
16
|
+
extends Pick<
|
|
17
|
+
SelectControlProps,
|
|
18
|
+
'__next36pxDefaultSize' | 'disabled' | 'multiple'
|
|
19
|
+
> {
|
|
17
20
|
// Using `selectSize` instead of `size` to avoid a type conflict with the
|
|
18
21
|
// `size` HTML attribute of the `select` element.
|
|
19
22
|
selectSize?: SelectControlProps[ 'size' ];
|
|
@@ -50,8 +53,15 @@ const fontSizeStyles = ( { selectSize = 'default' }: SelectProps ) => {
|
|
|
50
53
|
|
|
51
54
|
const sizeStyles = ( {
|
|
52
55
|
__next36pxDefaultSize,
|
|
56
|
+
multiple,
|
|
53
57
|
selectSize = 'default',
|
|
54
58
|
}: SelectProps ) => {
|
|
59
|
+
if ( multiple ) {
|
|
60
|
+
// When `multiple`, just use the native browser styles
|
|
61
|
+
// without setting explicit height.
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
55
65
|
const sizes = {
|
|
56
66
|
default: {
|
|
57
67
|
height: 36,
|
|
@@ -91,33 +101,37 @@ export const chevronIconSize = 18;
|
|
|
91
101
|
|
|
92
102
|
const sizePaddings = ( {
|
|
93
103
|
__next36pxDefaultSize,
|
|
104
|
+
multiple,
|
|
94
105
|
selectSize = 'default',
|
|
95
106
|
}: SelectProps ) => {
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
paddingLeft: 16,
|
|
101
|
-
paddingRight: 16 + iconWidth,
|
|
102
|
-
},
|
|
103
|
-
small: {
|
|
104
|
-
paddingLeft: 8,
|
|
105
|
-
paddingRight: 8 + iconWidth,
|
|
106
|
-
},
|
|
107
|
-
'__unstable-large': {
|
|
108
|
-
paddingLeft: 16,
|
|
109
|
-
paddingRight: 16 + iconWidth,
|
|
110
|
-
},
|
|
107
|
+
const padding = {
|
|
108
|
+
default: 16,
|
|
109
|
+
small: 8,
|
|
110
|
+
'__unstable-large': 16,
|
|
111
111
|
};
|
|
112
112
|
|
|
113
113
|
if ( ! __next36pxDefaultSize ) {
|
|
114
|
-
|
|
115
|
-
paddingLeft: 8,
|
|
116
|
-
paddingRight: 8 + iconWidth,
|
|
117
|
-
};
|
|
114
|
+
padding.default = 8;
|
|
118
115
|
}
|
|
119
116
|
|
|
120
|
-
|
|
117
|
+
const selectedPadding = padding[ selectSize ] || padding.default;
|
|
118
|
+
|
|
119
|
+
return rtl( {
|
|
120
|
+
paddingLeft: selectedPadding,
|
|
121
|
+
paddingRight: selectedPadding + chevronIconSize,
|
|
122
|
+
...( multiple
|
|
123
|
+
? {
|
|
124
|
+
paddingTop: selectedPadding,
|
|
125
|
+
paddingBottom: selectedPadding,
|
|
126
|
+
}
|
|
127
|
+
: {} ),
|
|
128
|
+
} );
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const overflowStyles = ( { multiple }: SelectProps ) => {
|
|
132
|
+
return {
|
|
133
|
+
overflow: multiple ? 'auto' : 'hidden',
|
|
134
|
+
};
|
|
121
135
|
};
|
|
122
136
|
|
|
123
137
|
// TODO: Resolve need to use &&& to increase specificity
|
|
@@ -137,7 +151,6 @@ export const Select = styled.select< SelectProps >`
|
|
|
137
151
|
width: 100%;
|
|
138
152
|
max-width: none;
|
|
139
153
|
cursor: pointer;
|
|
140
|
-
overflow: hidden;
|
|
141
154
|
white-space: nowrap;
|
|
142
155
|
text-overflow: ellipsis;
|
|
143
156
|
|
|
@@ -145,6 +158,7 @@ export const Select = styled.select< SelectProps >`
|
|
|
145
158
|
${ fontSizeStyles };
|
|
146
159
|
${ sizeStyles };
|
|
147
160
|
${ sizePaddings };
|
|
161
|
+
${ overflowStyles }
|
|
148
162
|
}
|
|
149
163
|
`;
|
|
150
164
|
|
|
@@ -23,7 +23,9 @@ export interface SelectControlProps
|
|
|
23
23
|
>,
|
|
24
24
|
Pick< BaseControlProps, 'help' | '__nextHasNoMarginBottom' > {
|
|
25
25
|
/**
|
|
26
|
-
* If this property is added, multiple values can be selected. The value passed should be an array.
|
|
26
|
+
* If this property is added, multiple values can be selected. The `value` passed should be an array.
|
|
27
|
+
*
|
|
28
|
+
* In most cases, it is preferable to use the `FormTokenField` or `CheckboxControl` components instead.
|
|
27
29
|
*
|
|
28
30
|
* @default false
|
|
29
31
|
*/
|
|
@@ -316,6 +316,71 @@ describe( 'ToolsPanel', () => {
|
|
|
316
316
|
expect( announcement ).toHaveAttribute( 'aria-live', 'assertive' );
|
|
317
317
|
} );
|
|
318
318
|
|
|
319
|
+
it( 'should render optional panel item when value is updated externally and panel has an ID', async () => {
|
|
320
|
+
const ToolsPanelOptional = ( { toolsPanelItemValue } ) => {
|
|
321
|
+
const itemProps = {
|
|
322
|
+
attributes: { value: toolsPanelItemValue },
|
|
323
|
+
hasValue: () => !! toolsPanelItemValue,
|
|
324
|
+
label: 'Alt',
|
|
325
|
+
onDeselect: jest.fn(),
|
|
326
|
+
onSelect: jest.fn(),
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
return (
|
|
330
|
+
<ToolsPanel { ...defaultProps }>
|
|
331
|
+
<ToolsPanelItem { ...itemProps }>
|
|
332
|
+
<div>Optional control</div>
|
|
333
|
+
</ToolsPanelItem>
|
|
334
|
+
</ToolsPanel>
|
|
335
|
+
);
|
|
336
|
+
};
|
|
337
|
+
const { rerender } = render( <ToolsPanelOptional /> );
|
|
338
|
+
|
|
339
|
+
const control = screen.queryByText( 'Optional control' );
|
|
340
|
+
|
|
341
|
+
expect( control ).not.toBeInTheDocument();
|
|
342
|
+
|
|
343
|
+
rerender( <ToolsPanelOptional toolsPanelItemValue={ 100 } /> );
|
|
344
|
+
|
|
345
|
+
const controlRerendered = screen.getByText( 'Optional control' );
|
|
346
|
+
|
|
347
|
+
expect( controlRerendered ).toBeInTheDocument();
|
|
348
|
+
} );
|
|
349
|
+
|
|
350
|
+
it( 'should render optional item when value is updated externally and panelId is null', async () => {
|
|
351
|
+
// This test partially covers: https://github.com/WordPress/gutenberg/issues/47368
|
|
352
|
+
const ToolsPanelOptional = ( { toolsPanelItemValue } ) => {
|
|
353
|
+
const itemProps = {
|
|
354
|
+
attributes: { value: toolsPanelItemValue },
|
|
355
|
+
hasValue: () => !! toolsPanelItemValue,
|
|
356
|
+
label: 'Alt',
|
|
357
|
+
onDeselect: jest.fn(),
|
|
358
|
+
onSelect: jest.fn(),
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
// The null panelId below simulates the panel prop when there
|
|
362
|
+
// are multiple blocks selected.
|
|
363
|
+
return (
|
|
364
|
+
<ToolsPanel { ...defaultProps } panelId={ null }>
|
|
365
|
+
<ToolsPanelItem { ...itemProps }>
|
|
366
|
+
<div>Optional control</div>
|
|
367
|
+
</ToolsPanelItem>
|
|
368
|
+
</ToolsPanel>
|
|
369
|
+
);
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
const { rerender } = render( <ToolsPanelOptional /> );
|
|
373
|
+
const control = screen.queryByText( 'Optional control' );
|
|
374
|
+
|
|
375
|
+
expect( control ).not.toBeInTheDocument();
|
|
376
|
+
|
|
377
|
+
rerender( <ToolsPanelOptional toolsPanelItemValue={ 99 } /> );
|
|
378
|
+
|
|
379
|
+
const controlRerendered = screen.getByText( 'Optional control' );
|
|
380
|
+
|
|
381
|
+
expect( controlRerendered ).toBeInTheDocument();
|
|
382
|
+
} );
|
|
383
|
+
|
|
319
384
|
it( 'should continue to render shown by default item after it is toggled off via menu item', async () => {
|
|
320
385
|
render(
|
|
321
386
|
<ToolsPanel { ...defaultProps }>
|
|
@@ -35,12 +35,11 @@ const generateMenuItems = ( {
|
|
|
35
35
|
panelItems.forEach( ( { hasValue, isShownByDefault, label } ) => {
|
|
36
36
|
const group = isShownByDefault ? 'default' : 'optional';
|
|
37
37
|
|
|
38
|
-
// If a menu item for this label already
|
|
39
|
-
//
|
|
40
|
-
// lose
|
|
38
|
+
// If a menu item for this label has already been flagged as customized
|
|
39
|
+
// (for default controls), or toggled on (for optional controls), do not
|
|
40
|
+
// overwrite its value as those controls would lose that state.
|
|
41
41
|
const existingItemValue = currentMenuItems?.[ group ]?.[ label ];
|
|
42
|
-
const value =
|
|
43
|
-
existingItemValue !== undefined ? existingItemValue : hasValue();
|
|
42
|
+
const value = existingItemValue ? existingItemValue : hasValue();
|
|
44
43
|
|
|
45
44
|
menuItems[ group ][ label ] = shouldReset ? false : value;
|
|
46
45
|
} );
|
|
@@ -86,30 +86,40 @@ export function useToolsPanelItem(
|
|
|
86
86
|
deregisterPanelItem,
|
|
87
87
|
] );
|
|
88
88
|
|
|
89
|
+
// Note: `label` is used as a key when building menu item state in
|
|
90
|
+
// `ToolsPanel`.
|
|
91
|
+
const menuGroup = isShownByDefault ? 'default' : 'optional';
|
|
92
|
+
const isMenuItemChecked = menuItems?.[ menuGroup ]?.[ label ];
|
|
93
|
+
const wasMenuItemChecked = usePrevious( isMenuItemChecked );
|
|
94
|
+
const isRegistered = menuItems?.[ menuGroup ]?.[ label ] !== undefined;
|
|
95
|
+
|
|
89
96
|
const isValueSet = hasValue();
|
|
90
97
|
const wasValueSet = usePrevious( isValueSet );
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
// panel when
|
|
98
|
+
const newValueSet = isValueSet && ! wasValueSet;
|
|
99
|
+
|
|
100
|
+
// Notify the panel when an item's value has been set.
|
|
101
|
+
//
|
|
102
|
+
// 1. For default controls, this is so "reset" appears beside its menu item.
|
|
103
|
+
// 2. For optional controls, when the panel ID is `null`, it allows the
|
|
104
|
+
// panel to ensure the item is toggled on for display in the menu, given the
|
|
105
|
+
// value has been set external to the control.
|
|
94
106
|
useEffect( () => {
|
|
95
|
-
if (
|
|
96
|
-
|
|
107
|
+
if ( ! newValueSet ) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if ( isShownByDefault || currentPanelId === null ) {
|
|
112
|
+
flagItemCustomization( label, menuGroup );
|
|
97
113
|
}
|
|
98
114
|
}, [
|
|
99
|
-
|
|
100
|
-
|
|
115
|
+
currentPanelId,
|
|
116
|
+
newValueSet,
|
|
101
117
|
isShownByDefault,
|
|
118
|
+
menuGroup,
|
|
102
119
|
label,
|
|
103
120
|
flagItemCustomization,
|
|
104
121
|
] );
|
|
105
122
|
|
|
106
|
-
// Note: `label` is used as a key when building menu item state in
|
|
107
|
-
// `ToolsPanel`.
|
|
108
|
-
const menuGroup = isShownByDefault ? 'default' : 'optional';
|
|
109
|
-
const isMenuItemChecked = menuItems?.[ menuGroup ]?.[ label ];
|
|
110
|
-
const wasMenuItemChecked = usePrevious( isMenuItemChecked );
|
|
111
|
-
const isRegistered = menuItems?.[ menuGroup ]?.[ label ] !== undefined;
|
|
112
|
-
|
|
113
123
|
// Determine if the panel item's corresponding menu is being toggled and
|
|
114
124
|
// trigger appropriate callback if it is.
|
|
115
125
|
useEffect( () => {
|
package/tsconfig.json
CHANGED
|
@@ -24,11 +24,15 @@
|
|
|
24
24
|
{ "path": "../deprecated" },
|
|
25
25
|
{ "path": "../dom" },
|
|
26
26
|
{ "path": "../element" },
|
|
27
|
-
{ "path": "../html
|
|
27
|
+
{ "path": "../escape-html" },
|
|
28
28
|
{ "path": "../hooks" },
|
|
29
|
+
{ "path": "../html-entities" },
|
|
30
|
+
{ "path": "../i18n" },
|
|
29
31
|
{ "path": "../icons" },
|
|
30
32
|
{ "path": "../is-shallow-equal" },
|
|
33
|
+
{ "path": "../keycodes" },
|
|
31
34
|
{ "path": "../primitives" },
|
|
35
|
+
{ "path": "../private-apis" },
|
|
32
36
|
{ "path": "../react-i18n" },
|
|
33
37
|
{ "path": "../warning" }
|
|
34
38
|
],
|