@wordpress/components 32.6.0 → 33.0.1-next.v.202604201441.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 +15 -0
- package/build/autocomplete/get-autocomplete-match.cjs +11 -2
- package/build/autocomplete/get-autocomplete-match.cjs.map +2 -2
- package/build/autocomplete/index.cjs +42 -11
- package/build/autocomplete/index.cjs.map +2 -2
- package/build/external-link/index.cjs +1 -1
- package/build/external-link/index.cjs.map +2 -2
- package/build/menu/styles.cjs +15 -15
- package/build/menu/styles.cjs.map +2 -2
- package/build/navigable-container/container.cjs +72 -110
- package/build/navigable-container/container.cjs.map +2 -2
- package/build-module/autocomplete/get-autocomplete-match.mjs +11 -2
- package/build-module/autocomplete/get-autocomplete-match.mjs.map +2 -2
- package/build-module/autocomplete/index.mjs +42 -11
- package/build-module/autocomplete/index.mjs.map +2 -2
- package/build-module/external-link/index.mjs +1 -1
- package/build-module/external-link/index.mjs.map +2 -2
- package/build-module/menu/styles.mjs +15 -15
- package/build-module/menu/styles.mjs.map +2 -2
- package/build-module/navigable-container/container.mjs +73 -111
- package/build-module/navigable-container/container.mjs.map +2 -2
- package/build-types/autocomplete/get-autocomplete-match.d.ts +10 -1
- package/build-types/autocomplete/get-autocomplete-match.d.ts.map +1 -1
- package/build-types/autocomplete/index.d.ts.map +1 -1
- package/build-types/base-control/stories/index.story.d.ts.map +1 -1
- package/build-types/button/stories/index.story.d.ts.map +1 -1
- package/build-types/checkbox-control/stories/index.story.d.ts.map +1 -1
- package/build-types/color-indicator/stories/index.story.d.ts.map +1 -1
- package/build-types/color-palette/stories/index.story.d.ts.map +1 -1
- package/build-types/color-picker/stories/index.story.d.ts.map +1 -1
- package/build-types/combobox-control/stories/index.story.d.ts.map +1 -1
- package/build-types/composite/stories/index.story.d.ts.map +1 -1
- package/build-types/custom-select-control/stories/index.story.d.ts.map +1 -1
- package/build-types/disabled/stories/index.story.d.ts.map +1 -1
- package/build-types/drop-zone/stories/index.story.d.ts.map +1 -1
- package/build-types/dropdown/stories/index.story.d.ts.map +1 -1
- package/build-types/external-link/index.d.ts.map +1 -1
- package/build-types/external-link/stories/index.story.d.ts.map +1 -1
- package/build-types/form-file-upload/stories/index.story.d.ts.map +1 -1
- package/build-types/form-toggle/stories/index.story.d.ts.map +1 -1
- package/build-types/form-token-field/stories/index.story.d.ts.map +1 -1
- package/build-types/gradient-picker/stories/index.story.d.ts.map +1 -1
- package/build-types/icon/stories/index.story.d.ts.map +1 -1
- package/build-types/keyboard-shortcuts/stories/index.story.d.ts.map +1 -1
- package/build-types/menu-group/stories/index.story.d.ts.map +1 -1
- package/build-types/menu-item/stories/index.story.d.ts.map +1 -1
- package/build-types/menu-items-choice/stories/index.story.d.ts.map +1 -1
- package/build-types/modal/stories/index.story.d.ts.map +1 -1
- package/build-types/navigable-container/container.d.ts +3 -8
- package/build-types/navigable-container/container.d.ts.map +1 -1
- package/build-types/navigable-container/types.d.ts +1 -5
- package/build-types/navigable-container/types.d.ts.map +1 -1
- package/build-types/navigation/stories/utils/more-examples.d.ts.map +1 -1
- package/build-types/navigator/stories/index.story.d.ts.map +1 -1
- package/build-types/notice/stories/index.story.d.ts.map +1 -1
- package/build-types/panel/stories/index.story.d.ts.map +1 -1
- package/build-types/popover/stories/index.story.d.ts.map +1 -1
- package/build-types/progress-bar/stories/index.story.d.ts.map +1 -1
- package/build-types/radio-control/stories/index.story.d.ts.map +1 -1
- package/build-types/range-control/stories/index.story.d.ts.map +1 -1
- package/build-types/resizable-box/stories/index.story.d.ts.map +1 -1
- package/build-types/sandbox/stories/index.story.d.ts.map +1 -1
- package/build-types/scroll-lock/stories/index.story.d.ts.map +1 -1
- package/build-types/search-control/stories/index.story.d.ts.map +1 -1
- package/build-types/select-control/stories/index.story.d.ts.map +1 -1
- package/build-types/shortcut/stories/index.story.d.ts.map +1 -1
- package/build-types/slot-fill/stories/index.story.d.ts.map +1 -1
- package/build-types/snackbar/stories/index.story.d.ts.map +1 -1
- package/build-types/spinner/stories/index.story.d.ts.map +1 -1
- package/build-types/text-control/stories/index.story.d.ts.map +1 -1
- package/build-types/text-highlight/stories/index.story.d.ts.map +1 -1
- package/build-types/textarea-control/stories/index.story.d.ts.map +1 -1
- package/build-types/toggle-control/stories/index.story.d.ts.map +1 -1
- package/build-types/tooltip/stories/index.story.d.ts.map +1 -1
- package/build-types/tree-select/stories/index.story.d.ts.map +1 -1
- package/build-types/visually-hidden/stories/index.story.d.ts.map +1 -1
- package/package.json +21 -21
- package/src/autocomplete/get-autocomplete-match.ts +25 -4
- package/src/autocomplete/index.tsx +69 -21
- package/src/autocomplete/test/get-autocomplete-match.ts +97 -75
- package/src/base-control/stories/index.story.tsx +1 -0
- package/src/button/stories/index.story.tsx +1 -0
- package/src/checkbox-control/stories/index.story.tsx +1 -0
- package/src/color-indicator/stories/index.story.tsx +1 -0
- package/src/color-palette/stories/index.story.tsx +1 -0
- package/src/color-picker/stories/index.story.tsx +1 -0
- package/src/combobox-control/stories/index.story.tsx +1 -0
- package/src/composite/stories/index.story.tsx +1 -0
- package/src/confirm-dialog/stories/index.story.tsx +1 -1
- package/src/custom-select-control/stories/index.story.tsx +1 -0
- package/src/disabled/stories/index.story.tsx +1 -0
- package/src/drop-zone/stories/index.story.tsx +1 -0
- package/src/dropdown/stories/index.story.tsx +1 -0
- package/src/external-link/index.tsx +1 -6
- package/src/external-link/stories/index.story.tsx +1 -0
- package/src/form-file-upload/stories/index.story.tsx +1 -0
- package/src/form-toggle/stories/index.story.tsx +1 -0
- package/src/form-token-field/stories/index.story.tsx +1 -0
- package/src/gradient-picker/stories/index.story.tsx +1 -0
- package/src/icon/stories/index.story.tsx +1 -0
- package/src/input-control/stories/index.story.tsx +1 -1
- package/src/item-group/stories/index.story.tsx +1 -1
- package/src/keyboard-shortcuts/stories/index.story.tsx +1 -0
- package/src/menu/styles.ts +1 -1
- package/src/menu-group/stories/index.story.tsx +1 -0
- package/src/menu-item/stories/index.story.tsx +1 -0
- package/src/menu-items-choice/stories/index.story.tsx +1 -0
- package/src/mobile/link-settings/index.native.js +1 -1
- package/src/modal/stories/index.story.tsx +1 -0
- package/src/navigable-container/container.tsx +120 -141
- package/src/navigable-container/test/navigable-menu.tsx +24 -0
- package/src/navigable-container/types.ts +1 -5
- package/src/navigation/stories/utils/more-examples.tsx +2 -1
- package/src/navigator/stories/index.story.tsx +1 -0
- package/src/notice/stories/index.story.tsx +1 -0
- package/src/number-control/stories/index.story.tsx +1 -1
- package/src/panel/stories/index.story.tsx +1 -0
- package/src/popover/stories/index.story.tsx +1 -0
- package/src/progress-bar/stories/index.story.tsx +1 -0
- package/src/radio-control/stories/index.story.tsx +1 -0
- package/src/range-control/stories/index.story.tsx +1 -0
- package/src/resizable-box/stories/index.story.tsx +1 -0
- package/src/sandbox/stories/index.story.tsx +1 -0
- package/src/scroll-lock/stories/index.story.tsx +1 -0
- package/src/search-control/stories/index.story.tsx +1 -0
- package/src/select-control/stories/index.story.tsx +1 -0
- package/src/shortcut/stories/index.story.tsx +1 -0
- package/src/slot-fill/stories/index.story.tsx +1 -0
- package/src/snackbar/stories/index.story.tsx +1 -0
- package/src/spinner/stories/index.story.tsx +1 -0
- package/src/text-control/stories/index.story.tsx +1 -0
- package/src/text-highlight/stories/index.story.tsx +1 -0
- package/src/textarea-control/stories/index.story.tsx +1 -0
- package/src/toggle-control/stories/index.story.tsx +1 -0
- package/src/toggle-group-control/stories/index.story.tsx +1 -1
- package/src/tooltip/stories/index.story.tsx +1 -0
- package/src/tree-grid/stories/index.story.tsx +1 -1
- package/src/tree-select/stories/index.story.tsx +1 -0
- package/src/truncate/stories/index.story.tsx +1 -1
- package/src/unit-control/stories/index.story.tsx +1 -1
- package/src/visually-hidden/stories/index.story.tsx +1 -0
- package/build/card/context.cjs +0 -36
- package/build/card/context.cjs.map +0 -7
- package/build-module/card/context.mjs +0 -10
- package/build-module/card/context.mjs.map +0 -7
- package/build-types/card/context.d.ts +0 -3
- package/build-types/card/context.d.ts.map +0 -1
- package/src/card/context.ts +0 -9
|
@@ -16,6 +16,7 @@ import { VStack } from '../../v-stack';
|
|
|
16
16
|
import { HStack } from '../../h-stack';
|
|
17
17
|
|
|
18
18
|
const meta: Meta< typeof CheckboxControl > = {
|
|
19
|
+
tags: [ 'manifest' ],
|
|
19
20
|
component: CheckboxControl,
|
|
20
21
|
title: 'Components/Selection & Input/Common/CheckboxControl',
|
|
21
22
|
id: 'components-checkboxcontrol',
|
|
@@ -9,6 +9,7 @@ import type { Meta, StoryFn } from '@storybook/react-vite';
|
|
|
9
9
|
import ColorIndicator from '..';
|
|
10
10
|
|
|
11
11
|
const meta: Meta< typeof ColorIndicator > = {
|
|
12
|
+
tags: [ 'manifest' ],
|
|
12
13
|
component: ColorIndicator,
|
|
13
14
|
title: 'Components/Selection & Input/Color/ColorIndicator',
|
|
14
15
|
id: 'components-colorindicator',
|
|
@@ -14,6 +14,7 @@ import { useState } from '@wordpress/element';
|
|
|
14
14
|
import ColorPalette from '..';
|
|
15
15
|
|
|
16
16
|
const meta: Meta< typeof ColorPalette > = {
|
|
17
|
+
tags: [ 'manifest' ],
|
|
17
18
|
title: 'Components/Selection & Input/Color/ColorPalette',
|
|
18
19
|
id: 'components-colorpalette',
|
|
19
20
|
component: ColorPalette,
|
|
@@ -10,6 +10,7 @@ import { fn } from 'storybook/test';
|
|
|
10
10
|
import { ColorPicker } from '../component';
|
|
11
11
|
|
|
12
12
|
const meta: Meta< typeof ColorPicker > = {
|
|
13
|
+
tags: [ 'manifest' ],
|
|
13
14
|
component: ColorPicker,
|
|
14
15
|
title: 'Components/Selection & Input/Color/ColorPicker',
|
|
15
16
|
id: 'components-colorpicker',
|
|
@@ -15,6 +15,7 @@ import { useState } from '@wordpress/element';
|
|
|
15
15
|
import CustomSelectControl from '..';
|
|
16
16
|
|
|
17
17
|
const meta: Meta< typeof CustomSelectControl > = {
|
|
18
|
+
tags: [ 'manifest' ],
|
|
18
19
|
title: 'Components/Selection & Input/Common/CustomSelectControl',
|
|
19
20
|
component: CustomSelectControl,
|
|
20
21
|
id: 'components-customselectcontrol',
|
|
@@ -18,6 +18,7 @@ import TextareaControl from '../../textarea-control/';
|
|
|
18
18
|
import { VStack } from '../../v-stack/';
|
|
19
19
|
|
|
20
20
|
const meta: Meta< typeof Disabled > = {
|
|
21
|
+
tags: [ 'manifest' ],
|
|
21
22
|
title: 'Components/Utilities/Disabled',
|
|
22
23
|
id: 'components-disabled',
|
|
23
24
|
component: Disabled,
|
|
@@ -14,6 +14,7 @@ import MenuItem from '../../menu-item';
|
|
|
14
14
|
import { DropdownContentWrapper } from '../dropdown-content-wrapper';
|
|
15
15
|
|
|
16
16
|
const meta: Meta< typeof Dropdown > = {
|
|
17
|
+
tags: [ 'manifest' ],
|
|
17
18
|
title: 'Components/Overlays/Dropdown',
|
|
18
19
|
id: 'components-dropdown',
|
|
19
20
|
component: Dropdown,
|
|
@@ -26,12 +26,7 @@ function UnforwardedExternalLink(
|
|
|
26
26
|
const { href, children, className, rel = '', ...additionalProps } = props;
|
|
27
27
|
const optimizedRel = [
|
|
28
28
|
...new Set(
|
|
29
|
-
[
|
|
30
|
-
...rel.split( ' ' ),
|
|
31
|
-
'external',
|
|
32
|
-
'noreferrer',
|
|
33
|
-
'noopener',
|
|
34
|
-
].filter( Boolean )
|
|
29
|
+
[ ...rel.split( ' ' ), 'external', 'noopener' ].filter( Boolean )
|
|
35
30
|
),
|
|
36
31
|
].join( ' ' );
|
|
37
32
|
const classes = clsx( 'components-external-link', className );
|
|
@@ -9,6 +9,7 @@ import type { Meta, StoryFn } from '@storybook/react-vite';
|
|
|
9
9
|
import ExternalLink from '..';
|
|
10
10
|
|
|
11
11
|
const meta: Meta< typeof ExternalLink > = {
|
|
12
|
+
tags: [ 'manifest' ],
|
|
12
13
|
component: ExternalLink,
|
|
13
14
|
title: 'Components/Navigation/ExternalLink',
|
|
14
15
|
id: 'components-externallink',
|
|
@@ -14,6 +14,7 @@ import { upload as uploadIcon } from '@wordpress/icons';
|
|
|
14
14
|
import FormFileUpload from '..';
|
|
15
15
|
|
|
16
16
|
const meta: Meta< typeof FormFileUpload > = {
|
|
17
|
+
tags: [ 'manifest' ],
|
|
17
18
|
title: 'Components/Selection & Input/File Upload/FormFileUpload',
|
|
18
19
|
id: 'components-formfileupload',
|
|
19
20
|
component: FormFileUpload,
|
|
@@ -14,6 +14,7 @@ import { useState } from '@wordpress/element';
|
|
|
14
14
|
import FormTokenField from '../';
|
|
15
15
|
|
|
16
16
|
const meta: Meta< typeof FormTokenField > = {
|
|
17
|
+
tags: [ 'manifest' ],
|
|
17
18
|
component: FormTokenField,
|
|
18
19
|
title: 'Components/Selection & Input/Common/FormTokenField',
|
|
19
20
|
id: 'components-formtokenfield',
|
|
@@ -15,6 +15,7 @@ import { useState } from '@wordpress/element';
|
|
|
15
15
|
import GradientPicker from '..';
|
|
16
16
|
|
|
17
17
|
const meta: Meta< typeof GradientPicker > = {
|
|
18
|
+
tags: [ 'manifest' ],
|
|
18
19
|
title: 'Components/Selection & Input/Color/GradientPicker',
|
|
19
20
|
id: 'components-gradientpicker',
|
|
20
21
|
component: GradientPicker,
|
|
@@ -31,7 +31,7 @@ const meta: Meta< typeof InputControl > = {
|
|
|
31
31
|
type: { control: { type: 'text' } },
|
|
32
32
|
value: { control: { disable: true } },
|
|
33
33
|
},
|
|
34
|
-
tags: [ 'status-experimental' ],
|
|
34
|
+
tags: [ 'status-experimental', 'manifest' ],
|
|
35
35
|
args: {
|
|
36
36
|
onChange: fn(),
|
|
37
37
|
onValidate: fn(),
|
|
@@ -19,7 +19,7 @@ const meta: Meta< typeof ItemGroup > = {
|
|
|
19
19
|
as: { control: false },
|
|
20
20
|
children: { control: false },
|
|
21
21
|
},
|
|
22
|
-
tags: [ 'status-experimental' ],
|
|
22
|
+
tags: [ 'status-experimental', 'manifest' ],
|
|
23
23
|
parameters: {
|
|
24
24
|
controls: { expanded: true },
|
|
25
25
|
docs: { canvas: { sourceState: 'shown' } },
|
|
@@ -9,6 +9,7 @@ import type { Meta, StoryFn } from '@storybook/react-vite';
|
|
|
9
9
|
import KeyboardShortcuts from '..';
|
|
10
10
|
|
|
11
11
|
const meta: Meta< typeof KeyboardShortcuts > = {
|
|
12
|
+
tags: [ 'manifest' ],
|
|
12
13
|
component: KeyboardShortcuts,
|
|
13
14
|
title: 'Components/Utilities/KeyboardShortcuts',
|
|
14
15
|
id: 'components-keyboardshortcuts',
|
package/src/menu/styles.ts
CHANGED
|
@@ -114,6 +114,7 @@ export const Menu = styled( Ariakit.Menu )< Pick< ContextProps, 'variant' > >`
|
|
|
114
114
|
|
|
115
115
|
const baseItem = css`
|
|
116
116
|
all: unset;
|
|
117
|
+
cursor: pointer;
|
|
117
118
|
|
|
118
119
|
position: relative;
|
|
119
120
|
min-height: ${ space( 8 ) };
|
|
@@ -157,7 +158,6 @@ const baseItem = css`
|
|
|
157
158
|
|
|
158
159
|
&[aria-disabled='true'] {
|
|
159
160
|
color: ${ COLORS.ui.textDisabled };
|
|
160
|
-
cursor: not-allowed;
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
/* Active item (including hover) */
|
|
@@ -16,6 +16,7 @@ import MenuItem from '../../menu-item';
|
|
|
16
16
|
import MenuItemsChoice from '../../menu-items-choice';
|
|
17
17
|
|
|
18
18
|
const meta: Meta< typeof MenuGroup > = {
|
|
19
|
+
tags: [ 'manifest' ],
|
|
19
20
|
title: 'Components/Actions/MenuGroup',
|
|
20
21
|
component: MenuGroup,
|
|
21
22
|
id: 'components-menugroup',
|
|
@@ -15,6 +15,7 @@ import MenuItemsChoice from '..';
|
|
|
15
15
|
import MenuGroup from '../../menu-group';
|
|
16
16
|
|
|
17
17
|
const meta: Meta< typeof MenuItemsChoice > = {
|
|
18
|
+
tags: [ 'manifest' ],
|
|
18
19
|
component: MenuItemsChoice,
|
|
19
20
|
title: 'Components/Actions/MenuItemsChoice',
|
|
20
21
|
id: 'components-menuitemschoice',
|
|
@@ -6,7 +6,8 @@ import type { ForwardedRef } from 'react';
|
|
|
6
6
|
/**
|
|
7
7
|
* WordPress dependencies
|
|
8
8
|
*/
|
|
9
|
-
import {
|
|
9
|
+
import { forwardRef, useRef, useEffect, useCallback } from '@wordpress/element';
|
|
10
|
+
import { useMergeRefs } from '@wordpress/compose';
|
|
10
11
|
import { focus } from '@wordpress/dom';
|
|
11
12
|
|
|
12
13
|
/**
|
|
@@ -28,163 +29,141 @@ function cycleValue( value: number, total: number, offset: number ) {
|
|
|
28
29
|
return nextValue;
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
this.container.removeEventListener( 'keydown', this.onKeyDown );
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
bindContainer( ref: HTMLDivElement ) {
|
|
65
|
-
const { forwardedRef } = this.props;
|
|
66
|
-
this.container = ref;
|
|
32
|
+
function UnforwardedNavigableContainer(
|
|
33
|
+
{
|
|
34
|
+
children,
|
|
35
|
+
stopNavigationEvents,
|
|
36
|
+
eventToOffset,
|
|
37
|
+
onNavigate = noop,
|
|
38
|
+
onKeyDown,
|
|
39
|
+
cycle = true,
|
|
40
|
+
onlyBrowserTabstops,
|
|
41
|
+
...restProps
|
|
42
|
+
}: NavigableContainerProps,
|
|
43
|
+
ref: ForwardedRef< HTMLDivElement >
|
|
44
|
+
) {
|
|
45
|
+
const containerRef = useRef< HTMLDivElement | null >( null );
|
|
46
|
+
|
|
47
|
+
const getFocusableIndex = useCallback(
|
|
48
|
+
( focusables: Element[], target: Element ) => {
|
|
49
|
+
return focusables.indexOf( target );
|
|
50
|
+
},
|
|
51
|
+
[]
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const getFocusableContext = useCallback(
|
|
55
|
+
( target: Element ) => {
|
|
56
|
+
if ( ! containerRef.current ) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
67
59
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
}
|
|
60
|
+
const finder = onlyBrowserTabstops
|
|
61
|
+
? focus.tabbable
|
|
62
|
+
: focus.focusable;
|
|
63
|
+
const focusables = finder.find( containerRef.current );
|
|
74
64
|
|
|
75
|
-
|
|
76
|
-
|
|
65
|
+
const index = getFocusableIndex( focusables, target );
|
|
66
|
+
if ( index > -1 && target ) {
|
|
67
|
+
return { index, target, focusables };
|
|
68
|
+
}
|
|
77
69
|
return null;
|
|
78
|
-
}
|
|
70
|
+
},
|
|
71
|
+
[ onlyBrowserTabstops, getFocusableIndex ]
|
|
72
|
+
);
|
|
79
73
|
|
|
80
|
-
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const index = this.getFocusableIndex( focusables, target );
|
|
85
|
-
if ( index > -1 && target ) {
|
|
86
|
-
return { index, target, focusables };
|
|
74
|
+
useEffect( () => {
|
|
75
|
+
const container = containerRef.current;
|
|
76
|
+
if ( ! container ) {
|
|
77
|
+
return;
|
|
87
78
|
}
|
|
88
|
-
return null;
|
|
89
|
-
}
|
|
90
79
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
onKeyDown( event: KeyboardEvent ) {
|
|
96
|
-
if ( this.props.onKeyDown ) {
|
|
97
|
-
this.props.onKeyDown( event );
|
|
98
|
-
}
|
|
80
|
+
function handleKeyDown( event: KeyboardEvent ) {
|
|
81
|
+
if ( onKeyDown ) {
|
|
82
|
+
onKeyDown( event );
|
|
83
|
+
}
|
|
99
84
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
event.target as HTMLDivElement | null
|
|
121
|
-
)?.getAttribute( 'role' );
|
|
122
|
-
const targetHasMenuItemRole =
|
|
123
|
-
!! targetRole && MENU_ITEM_ROLES.includes( targetRole );
|
|
124
|
-
|
|
125
|
-
if ( targetHasMenuItemRole ) {
|
|
126
|
-
event.preventDefault();
|
|
85
|
+
const offset = eventToOffset( event );
|
|
86
|
+
|
|
87
|
+
// eventToOffset returns undefined if the event is not handled by the component.
|
|
88
|
+
if ( offset !== undefined && stopNavigationEvents ) {
|
|
89
|
+
// Prevents arrow key handlers bound to the document directly interfering.
|
|
90
|
+
event.stopImmediatePropagation();
|
|
91
|
+
|
|
92
|
+
// When navigating a collection of items, prevent scroll containers
|
|
93
|
+
// from scrolling. The preventDefault also prevents Voiceover from
|
|
94
|
+
// 'handling' the event, as voiceover will try to use arrow keys
|
|
95
|
+
// for highlighting text.
|
|
96
|
+
const targetRole = (
|
|
97
|
+
event.target as HTMLDivElement | null
|
|
98
|
+
)?.getAttribute( 'role' );
|
|
99
|
+
const targetHasMenuItemRole =
|
|
100
|
+
!! targetRole && MENU_ITEM_ROLES.includes( targetRole );
|
|
101
|
+
|
|
102
|
+
if ( targetHasMenuItemRole ) {
|
|
103
|
+
event.preventDefault();
|
|
104
|
+
}
|
|
127
105
|
}
|
|
128
|
-
}
|
|
129
106
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
107
|
+
if ( ! offset ) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
133
110
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
111
|
+
const activeElement = ( event.target as HTMLElement | null )
|
|
112
|
+
?.ownerDocument?.activeElement;
|
|
113
|
+
if ( ! activeElement ) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
139
116
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
117
|
+
const context = getFocusableContext( activeElement );
|
|
118
|
+
if ( ! context ) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
144
121
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
122
|
+
const { index, focusables } = context;
|
|
123
|
+
const nextIndex = cycle
|
|
124
|
+
? cycleValue( index, focusables.length, offset )
|
|
125
|
+
: index + offset;
|
|
149
126
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
127
|
+
if ( nextIndex >= 0 && nextIndex < focusables.length ) {
|
|
128
|
+
focusables[ nextIndex ].focus();
|
|
129
|
+
onNavigate( nextIndex, focusables[ nextIndex ] as HTMLElement );
|
|
153
130
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
131
|
+
// `preventDefault()` on tab to avoid having the browser move the focus
|
|
132
|
+
// after this component has already moved it.
|
|
133
|
+
if ( event.code === 'Tab' ) {
|
|
134
|
+
event.preventDefault();
|
|
135
|
+
}
|
|
158
136
|
}
|
|
159
137
|
}
|
|
160
|
-
}
|
|
161
138
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
139
|
+
// We use DOM event listeners instead of React event listeners
|
|
140
|
+
// because we want to catch events from the underlying DOM tree.
|
|
141
|
+
// The React Tree can be different from the DOM tree when using
|
|
142
|
+
// portals. Block Toolbars for instance are rendered in a separate
|
|
143
|
+
// React Trees.
|
|
144
|
+
container.addEventListener( 'keydown', handleKeyDown );
|
|
145
|
+
return () => {
|
|
146
|
+
container.removeEventListener( 'keydown', handleKeyDown );
|
|
147
|
+
};
|
|
148
|
+
}, [
|
|
149
|
+
onKeyDown,
|
|
150
|
+
eventToOffset,
|
|
151
|
+
stopNavigationEvents,
|
|
152
|
+
cycle,
|
|
153
|
+
onNavigate,
|
|
154
|
+
getFocusableContext,
|
|
155
|
+
] );
|
|
156
|
+
|
|
157
|
+
const mergedRef = useMergeRefs( [ containerRef, ref ] );
|
|
158
|
+
|
|
159
|
+
return (
|
|
160
|
+
<div ref={ mergedRef } { ...restProps }>
|
|
161
|
+
{ children }
|
|
162
|
+
</div>
|
|
163
|
+
);
|
|
180
164
|
}
|
|
181
165
|
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
ref: ForwardedRef< HTMLDivElement >
|
|
185
|
-
) => {
|
|
186
|
-
return <NavigableContainer { ...props } forwardedRef={ ref } />;
|
|
187
|
-
};
|
|
188
|
-
forwardedNavigableContainer.displayName = 'NavigableContainer';
|
|
166
|
+
const NavigableContainer = forwardRef( UnforwardedNavigableContainer );
|
|
167
|
+
NavigableContainer.displayName = 'NavigableContainer';
|
|
189
168
|
|
|
190
|
-
export default
|
|
169
|
+
export default NavigableContainer;
|
|
@@ -215,6 +215,30 @@ describe( 'NavigableMenu', () => {
|
|
|
215
215
|
expect( externalWrapperOnKeyDownSpy ).toHaveBeenCalledTimes( 2 );
|
|
216
216
|
} );
|
|
217
217
|
|
|
218
|
+
it( 'should keep forwarded callback refs stable across rerenders', () => {
|
|
219
|
+
const refSpy = jest.fn();
|
|
220
|
+
|
|
221
|
+
const { rerender } = render(
|
|
222
|
+
<NavigableMenu ref={ refSpy }>
|
|
223
|
+
<button>Item 1</button>
|
|
224
|
+
</NavigableMenu>
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
expect( refSpy ).toHaveBeenCalledTimes( 1 );
|
|
228
|
+
expect( refSpy ).toHaveBeenCalledWith( expect.any( HTMLElement ) );
|
|
229
|
+
|
|
230
|
+
rerender(
|
|
231
|
+
<NavigableMenu ref={ refSpy }>
|
|
232
|
+
<button>Item 1</button>
|
|
233
|
+
</NavigableMenu>
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
// With a stable merged ref (useMergeRefs), the callback ref should
|
|
237
|
+
// not be called again on rerender. Previously, an inline ref callback
|
|
238
|
+
// would cause React to detach (null) and reattach on every render.
|
|
239
|
+
expect( refSpy ).toHaveBeenCalledTimes( 1 );
|
|
240
|
+
} );
|
|
241
|
+
|
|
218
242
|
it( 'skips its internal logic when the tab key is pressed', async () => {
|
|
219
243
|
const user = userEvent.setup();
|
|
220
244
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* External dependencies
|
|
3
3
|
*/
|
|
4
|
-
import type {
|
|
4
|
+
import type { ReactNode } from 'react';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Internal dependencies
|
|
@@ -35,10 +35,6 @@ export type NavigableContainerProps = WordPressComponentProps<
|
|
|
35
35
|
* Gets an offset, given an event.
|
|
36
36
|
*/
|
|
37
37
|
eventToOffset: ( event: KeyboardEvent ) => -1 | 0 | 1 | undefined;
|
|
38
|
-
/**
|
|
39
|
-
* The forwarded ref.
|
|
40
|
-
*/
|
|
41
|
-
forwardedRef?: ForwardedRef< any >;
|
|
42
38
|
/**
|
|
43
39
|
* Whether to only consider browser tab stops.
|
|
44
40
|
*
|
|
@@ -82,11 +82,12 @@ export const MoreExamplesStory: StoryFn< typeof Navigation > = ( {
|
|
|
82
82
|
title="WordPress.org"
|
|
83
83
|
/>
|
|
84
84
|
<NavigationItem item="item-5">
|
|
85
|
+
{ /* eslint-disable-next-line react/jsx-no-target-blank */ }
|
|
85
86
|
<a
|
|
86
87
|
className="navigation-story__wordpress-icon"
|
|
87
88
|
href="https://wordpress.org/"
|
|
88
89
|
target="_blank"
|
|
89
|
-
rel="
|
|
90
|
+
rel="noopener"
|
|
90
91
|
>
|
|
91
92
|
<Icon icon={ wordpress } />
|
|
92
93
|
<em>Custom Content</em>
|