Skip to content

Commit

Permalink
WIP flatten TabsProvider
Browse files Browse the repository at this point in the history
  • Loading branch information
mj12albert committed Nov 12, 2024
1 parent 6907335 commit f718823
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 153 deletions.
108 changes: 0 additions & 108 deletions packages/mui-base/src/Tabs/Root/TabsProvider.tsx

This file was deleted.

57 changes: 48 additions & 9 deletions packages/mui-base/src/Tabs/Root/TabsRoot.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
'use client';
import * as React from 'react';
import PropTypes from 'prop-types';
import { useTabsRoot } from './useTabsRoot';
import { tabsStyleHookMapping } from './styleHooks';
import { TabsProvider } from './TabsProvider';
import { useComponentRenderer } from '../../utils/useComponentRenderer';
import type { BaseUIComponentProps } from '../../utils/types';
import { CompoundComponentContext } from '../../useCompound';
import { useTabsRoot } from './useTabsRoot';
import { TabsRootContext } from './TabsRootContext';
import { tabsStyleHookMapping } from './styleHooks';

/**
*
Expand All @@ -24,22 +25,54 @@ const TabsRoot = React.forwardRef(function TabsRoot(
const {
className,
defaultValue,
direction = 'ltr',
direction: directionProp = 'ltr',
onValueChange,
orientation = 'horizontal',
render,
value,
value: valueProp,
...other
} = props;

const { contextValue, getRootProps, tabActivationDirection } = useTabsRoot({
const {
getRootProps,
compoundContext,
direction,
getTabId,
getTabPanelId,
onSelected,
registerTabIdLookup,
tabActivationDirection,
value,
} = useTabsRoot({
value: valueProp,
defaultValue,
onValueChange,
orientation,
direction,
direction: directionProp,
});

const tabsContextValue = React.useMemo(
() => ({
direction,
getTabId,
getTabPanelId,
onSelected,
orientation,
registerTabIdLookup,
tabActivationDirection,
value,
}),
[
direction,
getTabId,
getTabPanelId,
onSelected,
orientation,
registerTabIdLookup,
tabActivationDirection,
value,
],
);

const ownerState: TabsRoot.OwnerState = {
orientation,
direction,
Expand All @@ -56,7 +89,13 @@ const TabsRoot = React.forwardRef(function TabsRoot(
customStyleHookMapping: tabsStyleHookMapping,
});

return <TabsProvider value={contextValue}>{renderElement()}</TabsProvider>;
return (
<CompoundComponentContext.Provider value={compoundContext}>
<TabsRootContext.Provider value={tabsContextValue}>
{renderElement()}
</TabsRootContext.Provider>
</CompoundComponentContext.Provider>
);
});

export type TabsOrientation = 'horizontal' | 'vertical';
Expand Down
2 changes: 1 addition & 1 deletion packages/mui-base/src/Tabs/Root/TabsRootContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export interface TabsRootContext {
/**
* The currently selected tab's value.
*/
value?: any | null;
value: any | null;
/**
* Callback for setting new value.
*/
Expand Down
78 changes: 49 additions & 29 deletions packages/mui-base/src/Tabs/Root/useTabsRoot.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
'use client';
import * as React from 'react';
import type { TabActivationDirection } from './TabsRoot';
import type { TabsProviderValue, TabPanelMetadata } from './TabsProvider';
import { useCompoundParent } from '../../useCompound';
import { CompoundComponentContextValue, useCompoundParent } from '../../useCompound';
import { mergeReactProps } from '../../utils/mergeReactProps';
import { useControlled } from '../../utils/useControlled';

interface TabPanelMetadata {
id: string | undefined;
ref: React.RefObject<HTMLElement>;
}

export interface TabMetadata {
disabled: boolean;
id: string | undefined;
Expand All @@ -15,13 +19,7 @@ export interface TabMetadata {
type IdLookupFunction = (id: any) => string | undefined;

function useTabsRoot(parameters: useTabsRoot.Parameters): useTabsRoot.ReturnValue {
const {
value: valueProp,
defaultValue,
onValueChange,
orientation = 'horizontal',
direction = 'ltr',
} = parameters;
const { value: valueProp, defaultValue, onValueChange, direction = 'ltr' } = parameters;

const [value, setValue] = useControlled({
controlled: valueProp,
Expand Down Expand Up @@ -77,19 +75,15 @@ function useTabsRoot(parameters: useTabsRoot.Parameters): useTabsRoot.ReturnValu
);

return {
contextValue: {
direction,
getTabId,
getTabPanelId,
onSelected,
orientation,
registerTabIdLookup,
value,
tabActivationDirection,
...compoundComponentContextValue,
},
compoundContext: compoundComponentContextValue,
getRootProps,
direction,
getTabId,
getTabPanelId,
onSelected,
registerTabIdLookup,
tabActivationDirection,
value,
};
}

Expand All @@ -104,11 +98,6 @@ namespace useTabsRoot {
* The default value. Use when the component is not controlled.
*/
defaultValue?: any | null;
/**
* The component orientation (layout flow direction).
* @default 'horizontal'
*/
orientation?: 'horizontal' | 'vertical';
/**
* The direction of the text.
* @default 'ltr'
Expand All @@ -121,14 +110,45 @@ namespace useTabsRoot {
}

export interface ReturnValue {
/**
* Returns the values to be passed to the tabs provider.
*/
contextValue: TabsProviderValue;
getRootProps: (
externalProps?: React.ComponentPropsWithRef<'div'>,
) => React.ComponentPropsWithRef<'div'>;
compoundContext: CompoundComponentContextValue<any, TabPanelMetadata>;
/**
* The direction of the text.
*/
direction: 'ltr' | 'rtl';
/**
* Gets the id of the tab with the given value.
* @param value Value to find the tab for.
*/
getTabId: (value: any) => string | undefined;
/**
* Gets the id of the tab panel with the given value.
* @param value Value to find the tab panel for.
*/
getTabPanelId: (value: any) => string | undefined;

/**
* Callback for setting new value.
*/
onSelected: (
event: React.SyntheticEvent | null,
value: any | null,
activationDirection: TabActivationDirection,
) => void;
/**
* Registers a function that returns the id of the tab with the given value.
*/
registerTabIdLookup: (lookupFunction: (id: any) => string | undefined) => void;
/**
* The position of the active tab relative to the previously active tab.
*/
tabActivationDirection: TabActivationDirection;
/**
* The currently selected tab's value.
*/
value: any | null;
}
}

Expand Down
22 changes: 16 additions & 6 deletions packages/mui-base/src/Tabs/TabPanel/TabPanel.test.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,38 @@
import * as React from 'react';
import { Tabs } from '@base_ui/react/Tabs';
import { createRenderer, describeConformance } from '#test-utils';
import { TabsProvider, TabsProviderValue } from '../Root/TabsProvider';
import { CompoundComponentContext } from '../../useCompound';
import { TabsRootContext } from '../Root/TabsRootContext';

describe('<Tabs.Panel />', () => {
const { render } = createRenderer();

const tabsProviderDefaultValue: TabsProviderValue = {
const compoundContextValue = {
getItemIndex: () => 0,
registerItem: () => ({ id: 0, deregister: () => {} }),
totalSubitemCount: 1,
};

const tabsContextDefaultValue: TabsRootContext = {
value: '1',
onSelected: () => {},
registerTabIdLookup() {},
getTabId: () => '',
getTabPanelId: () => '',
getItemIndex: () => 0,
registerItem: () => ({ id: 0, deregister: () => {} }),
totalSubitemCount: 1,
direction: 'ltr',
orientation: 'horizontal',
tabActivationDirection: 'none',
};

describeConformance(<Tabs.Panel value="1" />, () => ({
render: (node) => {
return render(<TabsProvider value={tabsProviderDefaultValue}>{node}</TabsProvider>);
return render(
<CompoundComponentContext.Provider value={compoundContextValue}>
<TabsRootContext.Provider value={tabsContextDefaultValue}>
{node}
</TabsRootContext.Provider>
</CompoundComponentContext.Provider>,
);
},
refInstanceof: window.HTMLDivElement,
}));
Expand Down
4 changes: 4 additions & 0 deletions packages/mui-base/src/Tabs/TabsList/useTabsList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ function useTabsList(parameters: useTabsList.Parameters): useTabsList.ReturnValu
TabMetadata
>();

// get id attribute of a Tab by tab value
// for binding aria attributes
// this function needs to be registered to the context
const tabIdLookup = React.useCallback(
(tabValue: any) => {
return subitems.get(tabValue)?.id;
Expand All @@ -39,6 +42,7 @@ function useTabsList(parameters: useTabsList.Parameters): useTabsList.ReturnValu

const subitemKeys = React.useMemo(() => Array.from(subitems.keys()), [subitems]);

// get the element/node of a tab by tab value
const getTabElement = React.useCallback(
(tabValue: any) => {
if (tabValue == null) {
Expand Down

0 comments on commit f718823

Please sign in to comment.