@wordpress/components 25.4.0 → 25.5.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.
Files changed (119) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/build/border-control/border-control-dropdown/component.js +8 -10
  3. package/build/border-control/border-control-dropdown/component.js.map +1 -1
  4. package/build/color-palette/index.js +2 -2
  5. package/build/color-palette/index.js.map +1 -1
  6. package/build/focal-point-picker/index.native.js +6 -4
  7. package/build/focal-point-picker/index.native.js.map +1 -1
  8. package/build/menu-items-choice/index.js +1 -0
  9. package/build/menu-items-choice/index.js.map +1 -1
  10. package/build/mobile/bottom-sheet/bottom-sheet-navigation/bottom-sheet-navigation-context.native.js +3 -1
  11. package/build/mobile/bottom-sheet/bottom-sheet-navigation/bottom-sheet-navigation-context.native.js.map +1 -1
  12. package/build/mobile/bottom-sheet/bottom-sheet-navigation/navigation-container.native.js +50 -44
  13. package/build/mobile/bottom-sheet/bottom-sheet-navigation/navigation-container.native.js.map +1 -1
  14. package/build/mobile/bottom-sheet/bottom-sheet-navigation/navigation-screen.native.js +13 -20
  15. package/build/mobile/bottom-sheet/bottom-sheet-navigation/navigation-screen.native.js.map +1 -1
  16. package/build/mobile/bottom-sheet/index.native.js +3 -1
  17. package/build/mobile/bottom-sheet/index.native.js.map +1 -1
  18. package/build/mobile/link-picker/link-picker-results.native.js +2 -1
  19. package/build/mobile/link-picker/link-picker-results.native.js.map +1 -1
  20. package/build/mobile/segmented-control/index.native.js +7 -7
  21. package/build/mobile/segmented-control/index.native.js.map +1 -1
  22. package/build/modal/index.js +14 -1
  23. package/build/modal/index.js.map +1 -1
  24. package/build/private-apis.js +4 -1
  25. package/build/private-apis.js.map +1 -1
  26. package/build/progress-bar/index.js +54 -0
  27. package/build/progress-bar/index.js.map +1 -0
  28. package/build/progress-bar/styles.js +69 -0
  29. package/build/progress-bar/styles.js.map +1 -0
  30. package/build/progress-bar/types.js +6 -0
  31. package/build/progress-bar/types.js.map +1 -0
  32. package/build/tab-panel/index.js +91 -58
  33. package/build/tab-panel/index.js.map +1 -1
  34. package/build-module/border-control/border-control-dropdown/component.js +8 -10
  35. package/build-module/border-control/border-control-dropdown/component.js.map +1 -1
  36. package/build-module/color-palette/index.js +2 -2
  37. package/build-module/color-palette/index.js.map +1 -1
  38. package/build-module/focal-point-picker/index.native.js +6 -5
  39. package/build-module/focal-point-picker/index.native.js.map +1 -1
  40. package/build-module/menu-items-choice/index.js +1 -0
  41. package/build-module/menu-items-choice/index.js.map +1 -1
  42. package/build-module/mobile/bottom-sheet/bottom-sheet-navigation/bottom-sheet-navigation-context.native.js +3 -1
  43. package/build-module/mobile/bottom-sheet/bottom-sheet-navigation/bottom-sheet-navigation-context.native.js.map +1 -1
  44. package/build-module/mobile/bottom-sheet/bottom-sheet-navigation/navigation-container.native.js +43 -41
  45. package/build-module/mobile/bottom-sheet/bottom-sheet-navigation/navigation-container.native.js.map +1 -1
  46. package/build-module/mobile/bottom-sheet/bottom-sheet-navigation/navigation-screen.native.js +14 -20
  47. package/build-module/mobile/bottom-sheet/bottom-sheet-navigation/navigation-screen.native.js.map +1 -1
  48. package/build-module/mobile/bottom-sheet/index.native.js +3 -1
  49. package/build-module/mobile/bottom-sheet/index.native.js.map +1 -1
  50. package/build-module/mobile/link-picker/link-picker-results.native.js +2 -1
  51. package/build-module/mobile/link-picker/link-picker-results.native.js.map +1 -1
  52. package/build-module/mobile/segmented-control/index.native.js +7 -7
  53. package/build-module/mobile/segmented-control/index.native.js.map +1 -1
  54. package/build-module/modal/index.js +14 -1
  55. package/build-module/modal/index.js.map +1 -1
  56. package/build-module/private-apis.js +3 -1
  57. package/build-module/private-apis.js.map +1 -1
  58. package/build-module/progress-bar/index.js +41 -0
  59. package/build-module/progress-bar/index.js.map +1 -0
  60. package/build-module/progress-bar/styles.js +61 -0
  61. package/build-module/progress-bar/styles.js.map +1 -0
  62. package/build-module/progress-bar/types.js +2 -0
  63. package/build-module/progress-bar/types.js.map +1 -0
  64. package/build-module/tab-panel/index.js +88 -59
  65. package/build-module/tab-panel/index.js.map +1 -1
  66. package/build-types/border-control/border-control-dropdown/component.d.ts.map +1 -1
  67. package/build-types/menu-items-choice/index.d.ts.map +1 -1
  68. package/build-types/menu-items-choice/types.d.ts +5 -0
  69. package/build-types/menu-items-choice/types.d.ts.map +1 -1
  70. package/build-types/modal/index.d.ts.map +1 -1
  71. package/build-types/private-apis.d.ts.map +1 -1
  72. package/build-types/progress-bar/index.d.ts +5 -0
  73. package/build-types/progress-bar/index.d.ts.map +1 -0
  74. package/build-types/progress-bar/stories/index.d.ts +12 -0
  75. package/build-types/progress-bar/stories/index.d.ts.map +1 -0
  76. package/build-types/progress-bar/styles.d.ts +18 -0
  77. package/build-types/progress-bar/styles.d.ts.map +1 -0
  78. package/build-types/progress-bar/test/index.d.ts +2 -0
  79. package/build-types/progress-bar/test/index.d.ts.map +1 -0
  80. package/build-types/progress-bar/types.d.ts +11 -0
  81. package/build-types/progress-bar/types.d.ts.map +1 -0
  82. package/build-types/tab-panel/index.d.ts.map +1 -1
  83. package/build-types/tab-panel/stories/index.d.ts +1 -0
  84. package/build-types/tab-panel/stories/index.d.ts.map +1 -1
  85. package/build-types/tab-panel/types.d.ts +1 -9
  86. package/build-types/tab-panel/types.d.ts.map +1 -1
  87. package/package.json +20 -20
  88. package/src/border-control/border-control-dropdown/component.tsx +7 -11
  89. package/src/border-control/test/index.js +6 -6
  90. package/src/color-palette/index.tsx +2 -2
  91. package/src/color-palette/test/__snapshots__/index.tsx.snap +1 -1
  92. package/src/color-palette/test/index.tsx +1 -5
  93. package/src/draggable/test/index.native.js +4 -0
  94. package/src/focal-point-picker/index.native.js +6 -5
  95. package/src/menu-item/README.md +7 -0
  96. package/src/menu-items-choice/index.tsx +1 -0
  97. package/src/menu-items-choice/types.ts +5 -0
  98. package/src/mobile/bottom-sheet/bottom-sheet-navigation/bottom-sheet-navigation-context.native.js +1 -1
  99. package/src/mobile/bottom-sheet/bottom-sheet-navigation/navigation-container.native.js +72 -53
  100. package/src/mobile/bottom-sheet/bottom-sheet-navigation/navigation-screen.native.js +15 -21
  101. package/src/mobile/bottom-sheet/bottom-sheet-navigation/test/navigation-container.native.js +165 -119
  102. package/src/mobile/bottom-sheet/index.native.js +2 -0
  103. package/src/mobile/link-picker/link-picker-results.native.js +1 -1
  104. package/src/mobile/link-settings/test/edit.native.js +37 -23
  105. package/src/mobile/segmented-control/index.native.js +11 -11
  106. package/src/modal/index.tsx +16 -0
  107. package/src/modal/test/index.tsx +33 -0
  108. package/src/private-apis.ts +2 -0
  109. package/src/progress-bar/README.md +30 -0
  110. package/src/progress-bar/index.tsx +45 -0
  111. package/src/progress-bar/stories/index.tsx +33 -0
  112. package/src/progress-bar/styles.ts +67 -0
  113. package/src/progress-bar/test/index.tsx +79 -0
  114. package/src/progress-bar/types.ts +11 -0
  115. package/src/tab-panel/index.tsx +121 -84
  116. package/src/tab-panel/stories/index.tsx +6 -0
  117. package/src/tab-panel/test/index.tsx +128 -109
  118. package/src/tab-panel/types.ts +1 -10
  119. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,45 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import type { ForwardedRef } from 'react';
5
+
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import { __ } from '@wordpress/i18n';
10
+ import { forwardRef } from '@wordpress/element';
11
+
12
+ /**
13
+ * Internal dependencies
14
+ */
15
+ import * as ProgressBarStyled from './styles';
16
+ import type { ProgressBarProps } from './types';
17
+ import type { WordPressComponentProps } from '../ui/context';
18
+
19
+ function UnforwardedProgressBar(
20
+ props: WordPressComponentProps< ProgressBarProps, 'progress', false >,
21
+ ref: ForwardedRef< HTMLProgressElement >
22
+ ) {
23
+ const { className, value, ...progressProps } = props;
24
+ const isIndeterminate = ! Number.isFinite( value );
25
+
26
+ return (
27
+ <ProgressBarStyled.Track className={ className }>
28
+ <ProgressBarStyled.Indicator
29
+ isIndeterminate={ isIndeterminate }
30
+ value={ value }
31
+ />
32
+ <ProgressBarStyled.ProgressElement
33
+ max={ 100 }
34
+ value={ value }
35
+ aria-label={ __( 'Loading …' ) }
36
+ ref={ ref }
37
+ { ...progressProps }
38
+ />
39
+ </ProgressBarStyled.Track>
40
+ );
41
+ }
42
+
43
+ export const ProgressBar = forwardRef( UnforwardedProgressBar );
44
+
45
+ export default ProgressBar;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import type { ComponentMeta, ComponentStory } from '@storybook/react';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import { ProgressBar } from '..';
10
+
11
+ const meta: ComponentMeta< typeof ProgressBar > = {
12
+ component: ProgressBar,
13
+ title: 'Components (Experimental)/ProgressBar',
14
+ argTypes: {
15
+ value: { control: { type: 'number', min: 0, max: 100, step: 1 } },
16
+ },
17
+ parameters: {
18
+ controls: {
19
+ expanded: true,
20
+ },
21
+ docs: { source: { state: 'open' } },
22
+ },
23
+ };
24
+ export default meta;
25
+
26
+ const Template: ComponentStory< typeof ProgressBar > = ( { ...args } ) => {
27
+ return <ProgressBar { ...args } />;
28
+ };
29
+
30
+ export const Default: ComponentStory< typeof ProgressBar > = Template.bind(
31
+ {}
32
+ );
33
+ Default.args = {};
@@ -0,0 +1,67 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import styled from '@emotion/styled';
5
+ import { css, keyframes } from '@emotion/react';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import { COLORS, CONFIG } from '../utils';
11
+
12
+ const animateProgressBar = keyframes( {
13
+ '0%': {
14
+ left: '-50%',
15
+ },
16
+ '100%': {
17
+ left: '100%',
18
+ },
19
+ } );
20
+
21
+ // Width of the indicator for the indeterminate progress bar
22
+ export const INDETERMINATE_TRACK_WIDTH = 50;
23
+
24
+ export const Track = styled.div`
25
+ position: relative;
26
+ overflow: hidden;
27
+ width: 100%;
28
+ max-width: 160px;
29
+ height: ${ CONFIG.borderWidthFocus };
30
+ background-color: var(
31
+ --wp-components-color-gray-100,
32
+ ${ COLORS.gray[ 100 ] }
33
+ );
34
+ border-radius: ${ CONFIG.radiusBlockUi };
35
+ `;
36
+
37
+ export const Indicator = styled.div< {
38
+ isIndeterminate: boolean;
39
+ value?: number;
40
+ } >`
41
+ display: inline-block;
42
+ position: absolute;
43
+ top: 0;
44
+ height: 100%;
45
+ border-radius: ${ CONFIG.radiusBlockUi };
46
+ background-color: ${ COLORS.ui.theme };
47
+
48
+ ${ ( { isIndeterminate, value } ) =>
49
+ isIndeterminate
50
+ ? css( {
51
+ animationDuration: '1.5s',
52
+ animationTimingFunction: 'ease-in-out',
53
+ animationIterationCount: 'infinite',
54
+ animationName: animateProgressBar,
55
+ width: `${ INDETERMINATE_TRACK_WIDTH }%`,
56
+ } )
57
+ : css( { width: `${ value }%` } ) };
58
+ `;
59
+
60
+ export const ProgressElement = styled.progress`
61
+ position: absolute;
62
+ top: 0;
63
+ left: 0;
64
+ opacity: 0;
65
+ width: 100%;
66
+ height: 100%;
67
+ `;
@@ -0,0 +1,79 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { render, screen } from '@testing-library/react';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import { ProgressBar } from '..';
10
+ import { INDETERMINATE_TRACK_WIDTH } from '../styles';
11
+
12
+ describe( 'ProgressBar', () => {
13
+ it( 'should render an indeterminate semantic progress bar element', () => {
14
+ render( <ProgressBar /> );
15
+
16
+ const progressBar = screen.getByRole( 'progressbar' );
17
+
18
+ expect( progressBar ).toBeInTheDocument();
19
+ expect( progressBar ).not.toBeVisible();
20
+ expect( progressBar ).not.toHaveValue();
21
+ } );
22
+
23
+ it( 'should render a determinate semantic progress bar element', () => {
24
+ render( <ProgressBar value={ 55 } /> );
25
+
26
+ const progressBar = screen.getByRole( 'progressbar' );
27
+
28
+ expect( progressBar ).toBeInTheDocument();
29
+ expect( progressBar ).not.toBeVisible();
30
+ expect( progressBar ).toHaveValue( 55 );
31
+ } );
32
+
33
+ it( 'should use `INDETERMINATE_TRACK_WIDTH`% as track width for indeterminate progress bar', () => {
34
+ const { container } = render( <ProgressBar /> );
35
+
36
+ /**
37
+ * We're intentionally not using an accessible selector, because
38
+ * the track is an intentionally non-interactive presentation element.
39
+ */
40
+ // eslint-disable-next-line testing-library/no-container, testing-library/no-node-access
41
+ const indicator = container.firstChild?.firstChild;
42
+
43
+ expect( indicator ).toHaveStyle( {
44
+ width: `${ INDETERMINATE_TRACK_WIDTH }%`,
45
+ } );
46
+ } );
47
+
48
+ it( 'should use `value`% as width for determinate progress bar', () => {
49
+ const { container } = render( <ProgressBar value={ 55 } /> );
50
+
51
+ /**
52
+ * We're intentionally not using an accessible selector, because
53
+ * the track is an intentionally non-interactive presentation element.
54
+ */
55
+ // eslint-disable-next-line testing-library/no-container, testing-library/no-node-access
56
+ const indicator = container.firstChild?.firstChild;
57
+
58
+ expect( indicator ).toHaveStyle( {
59
+ width: '55%',
60
+ } );
61
+ } );
62
+
63
+ it( 'should pass any additional props down to the underlying `progress` element', () => {
64
+ const id = 'foo-bar-123';
65
+ const ariaLabel = 'in progress...';
66
+ const style = { opacity: 1 };
67
+
68
+ render(
69
+ <ProgressBar id={ id } aria-label={ ariaLabel } style={ style } />
70
+ );
71
+
72
+ expect( screen.getByRole( 'progressbar' ) ).toHaveAttribute( 'id', id );
73
+ expect( screen.getByRole( 'progressbar' ) ).toHaveAttribute(
74
+ 'aria-label',
75
+ ariaLabel
76
+ );
77
+ expect( screen.getByRole( 'progressbar' ) ).toHaveStyle( style );
78
+ } );
79
+ } );
@@ -0,0 +1,11 @@
1
+ export type ProgressBarProps = {
2
+ /**
3
+ * Value of the progress bar.
4
+ */
5
+ value?: number;
6
+
7
+ /**
8
+ * A CSS class to apply to the progress bar wrapper (track) element.
9
+ */
10
+ className?: string;
11
+ };
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * External dependencies
3
3
  */
4
+ import * as Ariakit from '@ariakit/react';
4
5
  import classnames from 'classnames';
5
6
  import type { ForwardedRef } from 'react';
6
7
 
@@ -9,38 +10,30 @@ import type { ForwardedRef } from 'react';
9
10
  */
10
11
  import {
11
12
  forwardRef,
12
- useState,
13
13
  useEffect,
14
14
  useLayoutEffect,
15
15
  useCallback,
16
16
  } from '@wordpress/element';
17
- import { useInstanceId } from '@wordpress/compose';
17
+ import { useInstanceId, usePrevious } from '@wordpress/compose';
18
18
 
19
19
  /**
20
20
  * Internal dependencies
21
21
  */
22
- import { NavigableMenu } from '../navigable-container';
22
+
23
23
  import Button from '../button';
24
- import type { TabButtonProps, TabPanelProps } from './types';
24
+ import type { TabPanelProps } from './types';
25
25
  import type { WordPressComponentProps } from '../ui/context';
26
26
 
27
- const TabButton = ( {
28
- tabId,
29
- children,
30
- selected,
31
- ...rest
32
- }: TabButtonProps ) => (
33
- <Button
34
- role="tab"
35
- tabIndex={ selected ? undefined : -1 }
36
- aria-selected={ selected }
37
- id={ tabId }
38
- __experimentalIsFocusable
39
- { ...rest }
40
- >
41
- { children }
42
- </Button>
43
- );
27
+ // Separate the actual tab name from the instance ID. This is
28
+ // necessary because Ariakit internally uses the element ID when
29
+ // a new tab is selected, but our implementation looks specifically
30
+ // for the tab name to be passed to the `onSelect` callback.
31
+ const extractTabName = ( id: string | undefined | null ) => {
32
+ if ( typeof id === 'undefined' || id === null ) {
33
+ return;
34
+ }
35
+ return id.match( /^tab-panel-[0-9]*-(.*)/ )?.[ 1 ];
36
+ };
44
37
 
45
38
  /**
46
39
  * TabPanel is an ARIA-compliant tabpanel.
@@ -92,26 +85,65 @@ const UnforwardedTabPanel = (
92
85
  ref: ForwardedRef< any >
93
86
  ) => {
94
87
  const instanceId = useInstanceId( TabPanel, 'tab-panel' );
95
- const [ selected, setSelected ] = useState< string >();
96
88
 
97
- const handleTabSelection = useCallback(
98
- ( tabKey: string ) => {
99
- setSelected( tabKey );
100
- onSelect?.( tabKey );
89
+ const prependInstanceId = useCallback(
90
+ ( tabName: string | undefined ) => {
91
+ if ( typeof tabName === 'undefined' ) {
92
+ return;
93
+ }
94
+ return `${ instanceId }-${ tabName }`;
95
+ },
96
+ [ instanceId ]
97
+ );
98
+
99
+ const tabStore = Ariakit.useTabStore( {
100
+ setSelectedId: ( newTabValue ) => {
101
+ if ( typeof newTabValue === 'undefined' || newTabValue === null ) {
102
+ return;
103
+ }
104
+
105
+ const newTab = tabs.find(
106
+ ( t ) => prependInstanceId( t.name ) === newTabValue
107
+ );
108
+ if ( newTab?.disabled || newTab === selectedTab ) {
109
+ return;
110
+ }
111
+
112
+ const simplifiedTabName = extractTabName( newTabValue );
113
+ if ( typeof simplifiedTabName === 'undefined' ) {
114
+ return;
115
+ }
116
+
117
+ onSelect?.( simplifiedTabName );
118
+ },
119
+ orientation,
120
+ selectOnMove,
121
+ defaultSelectedId: prependInstanceId( initialTabName ),
122
+ } );
123
+
124
+ const selectedTabName = extractTabName( tabStore.useState( 'selectedId' ) );
125
+
126
+ const setTabStoreSelectedId = useCallback(
127
+ ( tabName: string ) => {
128
+ tabStore.setState( 'selectedId', prependInstanceId( tabName ) );
101
129
  },
102
- [ onSelect ]
130
+ [ prependInstanceId, tabStore ]
103
131
  );
104
132
 
105
- // Simulate a click on the newly focused tab, which causes the component
106
- // to show the `tab-panel` associated with the clicked tab.
107
- const activateTabAutomatically = (
108
- _childIndex: number,
109
- child: HTMLElement
110
- ) => {
111
- child.click();
112
- };
113
- const selectedTab = tabs.find( ( { name } ) => name === selected );
114
- const selectedId = `${ instanceId }-${ selectedTab?.name ?? 'none' }`;
133
+ const selectedTab = tabs.find( ( { name } ) => name === selectedTabName );
134
+
135
+ const previousSelectedTabName = usePrevious( selectedTabName );
136
+
137
+ // Ensure `onSelect` is called when the initial tab is selected.
138
+ useEffect( () => {
139
+ if (
140
+ previousSelectedTabName !== selectedTabName &&
141
+ selectedTabName === initialTabName &&
142
+ !! selectedTabName
143
+ ) {
144
+ onSelect?.( selectedTabName );
145
+ }
146
+ }, [ selectedTabName, initialTabName, onSelect, previousSelectedTabName ] );
115
147
 
116
148
  // Handle selecting the initial tab.
117
149
  useLayoutEffect( () => {
@@ -119,25 +151,31 @@ const UnforwardedTabPanel = (
119
151
  if ( selectedTab ) {
120
152
  return;
121
153
  }
122
-
123
154
  const initialTab = tabs.find( ( tab ) => tab.name === initialTabName );
124
-
125
155
  // Wait for the denoted initial tab to be declared before making a
126
156
  // selection. This ensures that if a tab is declared lazily it can
127
157
  // still receive initial selection.
128
158
  if ( initialTabName && ! initialTab ) {
129
159
  return;
130
160
  }
131
-
132
161
  if ( initialTab && ! initialTab.disabled ) {
133
162
  // Select the initial tab if it's not disabled.
134
- handleTabSelection( initialTab.name );
163
+ setTabStoreSelectedId( initialTab.name );
135
164
  } else {
136
- // Fallback to the first enabled tab when the initial is disabled.
165
+ // Fallback to the first enabled tab when the initial tab is
166
+ // disabled or it can't be found.
137
167
  const firstEnabledTab = tabs.find( ( tab ) => ! tab.disabled );
138
- if ( firstEnabledTab ) handleTabSelection( firstEnabledTab.name );
168
+ if ( firstEnabledTab ) {
169
+ setTabStoreSelectedId( firstEnabledTab.name );
170
+ }
139
171
  }
140
- }, [ tabs, selectedTab, initialTabName, handleTabSelection ] );
172
+ }, [
173
+ tabs,
174
+ selectedTab,
175
+ initialTabName,
176
+ instanceId,
177
+ setTabStoreSelectedId,
178
+ ] );
141
179
 
142
180
  // Handle the currently selected tab becoming disabled.
143
181
  useEffect( () => {
@@ -145,59 +183,58 @@ const UnforwardedTabPanel = (
145
183
  if ( ! selectedTab?.disabled ) {
146
184
  return;
147
185
  }
148
-
149
186
  const firstEnabledTab = tabs.find( ( tab ) => ! tab.disabled );
150
-
151
187
  // If the currently selected tab becomes disabled, select the first enabled tab.
152
188
  // (if there is one).
153
189
  if ( firstEnabledTab ) {
154
- handleTabSelection( firstEnabledTab.name );
190
+ setTabStoreSelectedId( firstEnabledTab.name );
155
191
  }
156
- }, [ tabs, selectedTab?.disabled, handleTabSelection ] );
157
-
192
+ }, [ tabs, selectedTab?.disabled, setTabStoreSelectedId, instanceId ] );
158
193
  return (
159
194
  <div className={ className } ref={ ref }>
160
- <NavigableMenu
161
- role="tablist"
162
- orientation={ orientation }
163
- onNavigate={
164
- selectOnMove ? activateTabAutomatically : undefined
165
- }
195
+ <Ariakit.TabList
196
+ store={ tabStore }
166
197
  className="components-tab-panel__tabs"
167
198
  >
168
- { tabs.map( ( tab ) => (
169
- <TabButton
170
- className={ classnames(
171
- 'components-tab-panel__tabs-item',
172
- tab.className,
173
- {
174
- [ activeClass ]: tab.name === selected,
199
+ { tabs.map( ( tab ) => {
200
+ return (
201
+ <Ariakit.Tab
202
+ key={ tab.name }
203
+ id={ prependInstanceId( tab.name ) }
204
+ className={ classnames(
205
+ 'components-tab-panel__tabs-item',
206
+ tab.className,
207
+ {
208
+ [ activeClass ]:
209
+ tab.name === selectedTabName,
210
+ }
211
+ ) }
212
+ disabled={ tab.disabled }
213
+ aria-controls={ `${ prependInstanceId(
214
+ tab.name
215
+ ) }-view` }
216
+ render={
217
+ <Button
218
+ icon={ tab.icon }
219
+ label={ tab.icon && tab.title }
220
+ showTooltip={ !! tab.icon }
221
+ />
175
222
  }
176
- ) }
177
- tabId={ `${ instanceId }-${ tab.name }` }
178
- aria-controls={ `${ instanceId }-${ tab.name }-view` }
179
- selected={ tab.name === selected }
180
- key={ tab.name }
181
- onClick={ () => handleTabSelection( tab.name ) }
182
- disabled={ tab.disabled }
183
- label={ tab.icon && tab.title }
184
- icon={ tab.icon }
185
- showTooltip={ !! tab.icon }
186
- >
187
- { ! tab.icon && tab.title }
188
- </TabButton>
189
- ) ) }
190
- </NavigableMenu>
223
+ >
224
+ { ! tab.icon && tab.title }
225
+ </Ariakit.Tab>
226
+ );
227
+ } ) }
228
+ </Ariakit.TabList>
191
229
  { selectedTab && (
192
- <div
193
- key={ selectedId }
194
- aria-labelledby={ selectedId }
195
- role="tabpanel"
196
- id={ `${ selectedId }-view` }
197
- className="components-tab-panel__tab-content"
230
+ <Ariakit.TabPanel
231
+ id={ `${ prependInstanceId( selectedTab.name ) }-view` }
232
+ store={ tabStore }
233
+ tabId={ prependInstanceId( selectedTab.name ) }
234
+ className={ 'components-tab-panel__tab-content' }
198
235
  >
199
236
  { children( selectedTab ) }
200
- </div>
237
+ </Ariakit.TabPanel>
201
238
  ) }
202
239
  </div>
203
240
  );
@@ -96,3 +96,9 @@ WithTabIconsAndTooltips.args = {
96
96
  },
97
97
  ],
98
98
  };
99
+
100
+ export const ManualActivation = Template.bind( {} );
101
+ ManualActivation.args = {
102
+ ...Default.args,
103
+ selectOnMove: false,
104
+ };