diff --git a/docs/data/api/tabs-root.json b/docs/data/api/tabs-root.json index e3d5d1f5c8..0a6fd74aaa 100644 --- a/docs/data/api/tabs-root.json +++ b/docs/data/api/tabs-root.json @@ -2,10 +2,6 @@ "props": { "className": { "type": { "name": "union", "description": "func
| string" } }, "defaultValue": { "type": { "name": "any" }, "default": "0" }, - "direction": { - "type": { "name": "enum", "description": "'ltr'
| 'rtl'" }, - "default": "'ltr'" - }, "onValueChange": { "type": { "name": "func" } }, "orientation": { "type": { "name": "enum", "description": "'horizontal'
| 'vertical'" }, diff --git a/docs/data/components/tabs/tabs.mdx b/docs/data/components/tabs/tabs.mdx index d24bd1efa7..7704c427f5 100644 --- a/docs/data/components/tabs/tabs.mdx +++ b/docs/data/components/tabs/tabs.mdx @@ -154,6 +154,18 @@ Base UI Tabs follow the [Tabs WAI-ARIA design pattern](https://www.w3.org/WAI/A | Down Arrow | Moves focus to the next tab (when `orientation="vertical"`) and activates it if `activateOnFocus` is set. | | Space, Enter | Activates the focused tab. | +## RTL + +Place horizontal tabs inside an HTML element or component with the HTML `dir="rtl"` attribute to reverse arrow key navigation for right-to-left languages: + +```jsx + + + {/* RTL keyboard behavior*/} + + +``` + ### Labeling To make the Tabs component suite accessible to assistive technology, label the `` element with `aria-label`. diff --git a/docs/data/translations/api-docs/tabs-root/tabs-root.json b/docs/data/translations/api-docs/tabs-root/tabs-root.json index 27020d8d4f..d6bf5a38ed 100644 --- a/docs/data/translations/api-docs/tabs-root/tabs-root.json +++ b/docs/data/translations/api-docs/tabs-root/tabs-root.json @@ -7,7 +7,6 @@ "defaultValue": { "description": "The default value. Use when the component is not controlled. When the value is null, no Tab will be selected." }, - "direction": { "description": "The direction of the text." }, "onValueChange": { "description": "Callback invoked when new value is being set." }, "orientation": { "description": "The component orientation (layout flow direction)." }, "render": { "description": "A function to customize rendering of the component." }, diff --git a/docs/reference/generated/tabs-root.json b/docs/reference/generated/tabs-root.json index 33998d6c74..b953ac03c1 100644 --- a/docs/reference/generated/tabs-root.json +++ b/docs/reference/generated/tabs-root.json @@ -11,11 +11,6 @@ "default": "0", "description": "The default value. Use when the component is not controlled.\nWhen the value is `null`, no Tab will be selected." }, - "direction": { - "type": "'ltr' | 'rtl'", - "default": "'ltr'", - "description": "The direction of the text." - }, "onValueChange": { "type": "(value, event) => void", "description": "Callback invoked when new value is being set." diff --git a/packages/react/src/tabs/root/TabsRoot.test.tsx b/packages/react/src/tabs/root/TabsRoot.test.tsx index 823173819b..3daea2ebc0 100644 --- a/packages/react/src/tabs/root/TabsRoot.test.tsx +++ b/packages/react/src/tabs/root/TabsRoot.test.tsx @@ -286,19 +286,20 @@ describe('', () => { const handleChange = spy(); const handleKeyDown = spy(); const { getAllByRole } = await render( - - - - - - - , +
+ + + + + + + +
, ); const [firstTab, , lastTab] = getAllByRole('tab'); await act(async () => { @@ -318,19 +319,20 @@ describe('', () => { const handleChange = spy(); const handleKeyDown = spy(); const { getAllByRole } = await render( - - - - - - - , +
+ + + + + + + +
, ); const [firstTab, secondTab] = getAllByRole('tab'); await act(async () => { @@ -352,19 +354,20 @@ describe('', () => { const handleChange = spy(); const handleKeyDown = spy(); const { getAllByRole } = await render( - - - - - - - , +
+ + + + + + + +
, ); const [firstTab, , lastTab] = getAllByRole('tab'); await act(async () => { @@ -385,19 +388,20 @@ describe('', () => { const handleChange = spy(); const handleKeyDown = spy(); const { getAllByRole } = await render( - - - - - - - , +
+ + + + + + + +
, ); const [firstTab, secondTab] = getAllByRole('tab'); await act(async () => { @@ -418,18 +422,19 @@ describe('', () => { it('skips over disabled tabs', async () => { const handleKeyDown = spy(); const { getAllByRole } = await render( - - - - - - - , +
+ + + + + + + +
, ); const [firstTab, , lastTab] = getAllByRole('tab'); await act(async () => { @@ -451,19 +456,20 @@ describe('', () => { const handleChange = spy(); const handleKeyDown = spy(); const { getAllByRole } = await render( - - - - - - - , +
+ + + + + + + +
, ); const [firstTab, , lastTab] = getAllByRole('tab'); await act(async () => { @@ -483,19 +489,20 @@ describe('', () => { const handleChange = spy(); const handleKeyDown = spy(); const { getAllByRole } = await render( - - - - - - - , +
+ + + + + + + +
, ); const [, secondTab, lastTab] = getAllByRole('tab'); await act(async () => { @@ -517,19 +524,20 @@ describe('', () => { const handleChange = spy(); const handleKeyDown = spy(); const { getAllByRole } = await render( - - - - - - - , +
+ + + + + + + +
, ); const [firstTab, , lastTab] = getAllByRole('tab'); await act(async () => { @@ -550,19 +558,20 @@ describe('', () => { const handleChange = spy(); const handleKeyDown = spy(); const { getAllByRole } = await render( - - - - - - - , +
+ + + + + + + +
, ); const [, secondTab, lastTab] = getAllByRole('tab'); await act(async () => { @@ -583,18 +592,19 @@ describe('', () => { it('skips over disabled tabs', async () => { const handleKeyDown = spy(); const { getAllByRole } = await render( - - - - - - - , +
+ + + + + + + +
, ); const [firstTab, , lastTab] = getAllByRole('tab'); await act(async () => { diff --git a/packages/react/src/tabs/root/TabsRoot.tsx b/packages/react/src/tabs/root/TabsRoot.tsx index f9bcf00f11..18983cb370 100644 --- a/packages/react/src/tabs/root/TabsRoot.tsx +++ b/packages/react/src/tabs/root/TabsRoot.tsx @@ -26,7 +26,6 @@ const TabsRoot = React.forwardRef(function TabsRoot( const { className, defaultValue = 0, - direction: directionProp = 'ltr', onValueChange: onValueChangeProp, orientation = 'horizontal', render, @@ -35,8 +34,6 @@ const TabsRoot = React.forwardRef(function TabsRoot( } = props; const { - getRootProps, - direction, getTabElementBySelectedValue, getTabIdByPanelValueOrIndex, getTabPanelIdByTabValueOrIndex, @@ -50,12 +47,10 @@ const TabsRoot = React.forwardRef(function TabsRoot( value: valueProp, defaultValue, onValueChange: onValueChangeProp, - direction: directionProp, }); const tabsContextValue: TabsRootContext = React.useMemo( () => ({ - direction, getTabElementBySelectedValue, getTabIdByPanelValueOrIndex, getTabPanelIdByTabValueOrIndex, @@ -66,7 +61,6 @@ const TabsRoot = React.forwardRef(function TabsRoot( value, }), [ - direction, getTabElementBySelectedValue, getTabIdByPanelValueOrIndex, getTabPanelIdByTabValueOrIndex, @@ -80,12 +74,10 @@ const TabsRoot = React.forwardRef(function TabsRoot( const state: TabsRoot.State = { orientation, - direction, tabActivationDirection, }; const { renderElement } = useComponentRenderer({ - propGetter: getRootProps, render: render ?? 'div', className, state, @@ -104,14 +96,12 @@ const TabsRoot = React.forwardRef(function TabsRoot( }); export type TabsOrientation = 'horizontal' | 'vertical'; -export type TabsDirection = 'ltr' | 'rtl'; export type TabActivationDirection = 'left' | 'right' | 'up' | 'down' | 'none'; export type TabValue = any | null; namespace TabsRoot { export type State = { orientation: TabsOrientation; - direction: TabsDirection; tabActivationDirection: TabActivationDirection; }; @@ -132,11 +122,6 @@ namespace TabsRoot { * @default 'horizontal' */ orientation?: TabsOrientation; - /** - * The direction of the text. - * @default 'ltr' - */ - direction?: TabsDirection; /** * Callback invoked when new value is being set. */ @@ -165,11 +150,6 @@ TabsRoot.propTypes /* remove-proptypes */ = { * @default 0 */ defaultValue: PropTypes.any, - /** - * The direction of the text. - * @default 'ltr' - */ - direction: PropTypes.oneOf(['ltr', 'rtl']), /** * Callback invoked when new value is being set. */ diff --git a/packages/react/src/tabs/root/TabsRootContext.ts b/packages/react/src/tabs/root/TabsRootContext.ts index 9fe68c4458..8e567a38c3 100644 --- a/packages/react/src/tabs/root/TabsRootContext.ts +++ b/packages/react/src/tabs/root/TabsRootContext.ts @@ -20,10 +20,6 @@ export interface TabsRootContext { * The component orientation (layout flow direction). */ orientation: 'horizontal' | 'vertical'; - /** - * The direction of the tabs. - */ - direction: 'ltr' | 'rtl'; /** * Gets the element of the Tab with the given value. * @param {any | undefined} value Value to find the tab for. diff --git a/packages/react/src/tabs/root/styleHooks.ts b/packages/react/src/tabs/root/styleHooks.ts index 4929d153ae..87481b41d2 100644 --- a/packages/react/src/tabs/root/styleHooks.ts +++ b/packages/react/src/tabs/root/styleHooks.ts @@ -2,7 +2,6 @@ import type { TabsRoot } from './TabsRoot'; import type { CustomStyleHookMapping } from '../../utils/getStyleHookProps'; export const tabsStyleHookMapping: CustomStyleHookMapping = { - direction: () => null, tabActivationDirection: (dir) => ({ 'data-activation-direction': dir, }), diff --git a/packages/react/src/tabs/root/useTabsRoot.ts b/packages/react/src/tabs/root/useTabsRoot.ts index 2938c5f1f1..18ce3dc3ea 100644 --- a/packages/react/src/tabs/root/useTabsRoot.ts +++ b/packages/react/src/tabs/root/useTabsRoot.ts @@ -1,7 +1,5 @@ 'use client'; import * as React from 'react'; -import { mergeReactProps } from '../../utils/mergeReactProps'; -import { GenericHTMLProps } from '../../utils/types'; import { useControlled } from '../../utils/useControlled'; import type { CompositeMetadata } from '../../composite/list/CompositeList'; import type { TabPanelMetadata } from '../tab-panel/useTabPanel'; @@ -9,12 +7,7 @@ import type { TabMetadata } from '../tab/useTab'; import type { TabActivationDirection, TabValue } from './TabsRoot'; function useTabsRoot(parameters: useTabsRoot.Parameters): useTabsRoot.ReturnValue { - const { - value: valueProp, - defaultValue, - onValueChange: onValueChangeProp, - direction = 'ltr', - } = parameters; + const { value: valueProp, defaultValue, onValueChange: onValueChangeProp } = parameters; const tabPanelRefs = React.useRef<(HTMLElement | null)[]>([]); @@ -122,17 +115,7 @@ function useTabsRoot(parameters: useTabsRoot.Parameters): useTabsRoot.ReturnValu [tabMap], ); - const getRootProps: useTabsRoot.ReturnValue['getRootProps'] = React.useCallback( - (otherProps = {}) => - mergeReactProps<'div'>(otherProps, { - dir: direction, - }), - [direction], - ); - return { - getRootProps, - direction, getTabElementBySelectedValue, getTabIdByPanelValueOrIndex, getTabPanelIdByTabValueOrIndex, @@ -157,11 +140,6 @@ namespace useTabsRoot { * The default value. Use when the component is not controlled. */ defaultValue?: TabValue; - /** - * The direction of the text. - * @default 'ltr' - */ - direction?: 'ltr' | 'rtl'; /** * Callback invoked when new value is being set. */ @@ -169,16 +147,6 @@ namespace useTabsRoot { } export interface ReturnValue { - /** - * Resolver for the Root component's props. - * @param externalProps additional props for Tabs.Root - * @returns props that should be spread on Tabs.Root - */ - getRootProps: (externalProps?: GenericHTMLProps) => GenericHTMLProps; - /** - * The direction of the text. - */ - direction: 'ltr' | 'rtl'; /** * Gets the element of the Tab with the given value. * @param {any | undefined} value Value to find the tab for. diff --git a/packages/react/src/tabs/tab-indicator/TabIndicator.tsx b/packages/react/src/tabs/tab-indicator/TabIndicator.tsx index f1c13b2f84..de24e2da6a 100644 --- a/packages/react/src/tabs/tab-indicator/TabIndicator.tsx +++ b/packages/react/src/tabs/tab-indicator/TabIndicator.tsx @@ -4,7 +4,7 @@ import PropTypes from 'prop-types'; import { useComponentRenderer } from '../../utils/useComponentRenderer'; import { useOnMount } from '../../utils/useOnMount'; import type { BaseUIComponentProps } from '../../utils/types'; -import type { TabsDirection, TabsOrientation, TabsRoot } from '../root/TabsRoot'; +import type { TabsOrientation, TabsRoot } from '../root/TabsRoot'; import { useTabsRootContext } from '../root/TabsRootContext'; import { tabsStyleHookMapping } from '../root/styleHooks'; import { useTabsListContext } from '../tabs-list/TabsListContext'; @@ -27,7 +27,7 @@ const TabIndicator = React.forwardRef( function TabIndicator(props, forwardedRef) { const { className, render, renderBeforeHydration = false, ...other } = props; - const { direction, getTabElementBySelectedValue, orientation, tabActivationDirection, value } = + const { getTabElementBySelectedValue, orientation, tabActivationDirection, value } = useTabsRootContext(); const { tabsListRef } = useTabsListContext(); @@ -46,12 +46,11 @@ const TabIndicator = React.forwardRef( const state: TabIndicator.State = React.useMemo( () => ({ - direction, orientation, selectedTabPosition, tabActivationDirection, }), - [direction, orientation, selectedTabPosition, tabActivationDirection], + [orientation, selectedTabPosition, tabActivationDirection], ); const { renderElement } = useComponentRenderer({ @@ -94,7 +93,6 @@ namespace TabIndicator { export interface State extends TabsRoot.State { selectedTabPosition: ActiveTabPosition | null; orientation: TabsOrientation; - direction: TabsDirection; } export interface Props extends BaseUIComponentProps<'span', TabIndicator.State> { diff --git a/packages/react/src/tabs/tab-panel/TabPanel.test.tsx b/packages/react/src/tabs/tab-panel/TabPanel.test.tsx index 4746039bdb..8aa9546152 100644 --- a/packages/react/src/tabs/tab-panel/TabPanel.test.tsx +++ b/packages/react/src/tabs/tab-panel/TabPanel.test.tsx @@ -13,7 +13,6 @@ describe('', () => { getTabElementBySelectedValue: () => null, getTabIdByPanelValueOrIndex: () => '', getTabPanelIdByTabValueOrIndex: () => '', - direction: 'ltr', orientation: 'horizontal', tabActivationDirection: 'none', }; diff --git a/packages/react/src/tabs/tab-panel/TabPanel.tsx b/packages/react/src/tabs/tab-panel/TabPanel.tsx index a18de9deef..6c43b34e2a 100644 --- a/packages/react/src/tabs/tab-panel/TabPanel.tsx +++ b/packages/react/src/tabs/tab-panel/TabPanel.tsx @@ -28,7 +28,6 @@ const TabPanel = React.forwardRef(function TabPanel( value: selectedValue, getTabIdByPanelValueOrIndex, orientation, - direction, tabActivationDirection, } = useTabsRootContext(); @@ -41,12 +40,11 @@ const TabPanel = React.forwardRef(function TabPanel( const state: TabPanel.State = React.useMemo( () => ({ - direction, hidden, orientation, tabActivationDirection, }), - [direction, hidden, orientation, tabActivationDirection], + [hidden, orientation, tabActivationDirection], ); const { renderElement } = useComponentRenderer({ diff --git a/packages/react/src/tabs/tab/Tab.test.tsx b/packages/react/src/tabs/tab/Tab.test.tsx index a0df40aadc..c52060addc 100644 --- a/packages/react/src/tabs/tab/Tab.test.tsx +++ b/packages/react/src/tabs/tab/Tab.test.tsx @@ -32,7 +32,6 @@ describe('', () => { getTabIdByPanelValueOrIndex: () => '', getTabPanelIdByTabValueOrIndex: () => '', orientation: 'horizontal', - direction: 'ltr', tabActivationDirection: 'none', }; diff --git a/packages/react/src/tabs/tabs-list/TabsList.test.tsx b/packages/react/src/tabs/tabs-list/TabsList.test.tsx index 17ce391fec..3ca0a1988e 100644 --- a/packages/react/src/tabs/tabs-list/TabsList.test.tsx +++ b/packages/react/src/tabs/tabs-list/TabsList.test.tsx @@ -20,7 +20,6 @@ describe('', () => { getTabIdByPanelValueOrIndex: () => '', getTabPanelIdByTabValueOrIndex: () => '', orientation: 'horizontal', - direction: 'ltr', tabActivationDirection: 'none', }} > diff --git a/packages/react/src/tabs/tabs-list/TabsList.tsx b/packages/react/src/tabs/tabs-list/TabsList.tsx index 28ca823047..4d405cd364 100644 --- a/packages/react/src/tabs/tabs-list/TabsList.tsx +++ b/packages/react/src/tabs/tabs-list/TabsList.tsx @@ -28,7 +28,6 @@ const TabsList = React.forwardRef(function TabsList( const { activateOnFocus = true, className, loop = true, render, ...other } = props; const { - direction = 'ltr', getTabElementBySelectedValue, onValueChange, orientation = 'horizontal', @@ -53,11 +52,10 @@ const TabsList = React.forwardRef(function TabsList( const state: TabsList.State = React.useMemo( () => ({ - direction, orientation, tabActivationDirection, }), - [direction, orientation, tabActivationDirection], + [orientation, tabActivationDirection], ); const { renderElement } = useComponentRenderer({