Skip to content

Commit

Permalink
[Tabs][base] Drop component prop (mui#36770)
Browse files Browse the repository at this point in the history
  • Loading branch information
sai6855 authored and binh1298 committed May 17, 2023
1 parent 011946b commit 6d2bf2e
Show file tree
Hide file tree
Showing 7 changed files with 40 additions and 31 deletions.
30 changes: 22 additions & 8 deletions docs/data/base/components/tabs/tabs.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,23 +84,37 @@ The Tab components are each composed of a root slot with no interior slots:
</div>
```

### Slot props
### Custom structure

:::info
The following props are available on all non-utility Base components.
See [Usage](/base/getting-started/usage/) for full details.
:::

Use the `component` prop to override the root slot with a custom element:
Use the `slots` prop to override the root or any other interior slot:

```jsx
<Tab component="span" />
<Tab slots={{ root: 'span' }} />
```

If you provide a non-interactive element such as a `<span>`, the Tab components will automatically add the necessary accessibility attributes.

:::info
The `slots` prop is available on all non-utility Base components.
See [Overriding component structure](/base/guides/overriding-component-structure/) for full details.
:::

## Customization

### Usage with TypeScript

In TypeScript, you can specify the custom component type used in the `slots.root` as a generic parameter of the unstyled component. This way, you can safely provide the custom root's props directly on the component:

```tsx
<Tab<typeof CustomComponent> slots={{ root: CustomComponent }} customProp />
```

The same applies for props specific to custom primitive elements:

```tsx
<Tab<'button'> slots={{ root: 'button' }} onClick={() => {}} />
```

### Third-party routing library

A common use case for tabs is to implement client-side navigation that doesn't require an HTTP round-trip to the server.
Expand Down
1 change: 0 additions & 1 deletion docs/pages/base/api/tabs.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"props": {
"children": { "type": { "name": "node" } },
"component": { "type": { "name": "elementType" } },
"defaultValue": { "type": { "name": "union", "description": "number<br>&#124;&nbsp;string" } },
"direction": {
"type": { "name": "enum", "description": "'ltr'<br>&#124;&nbsp;'rtl'" },
Expand Down
1 change: 0 additions & 1 deletion docs/translations/api-docs-base/tabs/tabs.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
"componentDescription": "",
"propDescriptions": {
"children": "The content of the component.",
"component": "The component used for the root node. Either a string to use a HTML element or a component.",
"defaultValue": "The default value. Use when the component is not controlled.",
"direction": "The direction of the text.",
"onChange": "Callback invoked when new value is being set.",
Expand Down
18 changes: 11 additions & 7 deletions packages/mui-base/src/Tabs/Tabs.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,23 @@ const polymorphicComponentTest = () => {
{/* @ts-expect-error */}
<Tabs invalidProp={0} />

<Tabs component="a" href="#" />
<Tabs<'a'> slots={{ root: 'a' }} href="#" />

<Tabs component={CustomComponent} stringProp="test" numberProp={0} />
{/* @ts-expect-error */}
<Tabs component={CustomComponent} />
<Tabs<typeof CustomComponent>
slots={{ root: CustomComponent }}
stringProp="test"
numberProp={0}
/>
{/* @ts-expect-error required props not specified */}
<Tabs<typeof CustomComponent> slots={{ root: CustomComponent }} />

<Tabs
component="button"
<Tabs<'button'>
slots={{ root: 'button' }}
onClick={(e: React.MouseEvent<HTMLButtonElement>) => e.currentTarget.checkValidity()}
/>

<Tabs<'button'>
component="button"
slots={{ root: 'button' }}
ref={(elem) => {
expectType<HTMLButtonElement | null, typeof elem>(elem);
}}
Expand Down
1 change: 1 addition & 0 deletions packages/mui-base/src/Tabs/Tabs.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ describe('<Tabs />', () => {
expectedClassName: classes.root,
},
},
skip: ['componentProp'],
}));

it('can be named via `aria-label`', () => {
Expand Down
13 changes: 3 additions & 10 deletions packages/mui-base/src/Tabs/Tabs.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import { OverridableComponent } from '@mui/types';
import { useSlotProps, WithOptionalOwnerState } from '../utils';
import { PolymorphicComponent, useSlotProps, WithOptionalOwnerState } from '../utils';
import composeClasses from '../composeClasses';
import { getTabsUtilityClass } from './tabsClasses';
import { TabsOwnerState, TabsProps, TabsRootSlotProps, TabsTypeMap } from './Tabs.types';
Expand Down Expand Up @@ -39,7 +38,6 @@ const Tabs = React.forwardRef(function Tabs<RootComponentType extends React.Elem
defaultValue,
orientation = 'horizontal',
direction = 'ltr',
component,
onChange,
selectionFollowsFocus,
slotProps = {},
Expand All @@ -57,7 +55,7 @@ const Tabs = React.forwardRef(function Tabs<RootComponentType extends React.Elem

const classes = useUtilityClasses(ownerState);

const TabsRoot: React.ElementType = component ?? slots.root ?? 'div';
const TabsRoot: React.ElementType = slots.root ?? 'div';
const tabsRootProps: WithOptionalOwnerState<TabsRootSlotProps> = useSlotProps({
elementType: TabsRoot,
externalSlotProps: slotProps.root,
Expand All @@ -74,7 +72,7 @@ const Tabs = React.forwardRef(function Tabs<RootComponentType extends React.Elem
<TabsProvider value={contextValue}>{children}</TabsProvider>
</TabsRoot>
);
}) as OverridableComponent<TabsTypeMap>;
}) as PolymorphicComponent<TabsTypeMap>;

Tabs.propTypes /* remove-proptypes */ = {
// ----------------------------- Warning --------------------------------
Expand All @@ -85,11 +83,6 @@ Tabs.propTypes /* remove-proptypes */ = {
* The content of the component.
*/
children: PropTypes.node,
/**
* The component used for the root node.
* Either a string to use a HTML element or a component.
*/
component: PropTypes.elementType,
/**
* The default value. Use when the component is not controlled.
*/
Expand Down
7 changes: 3 additions & 4 deletions packages/mui-base/src/Tabs/Tabs.types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from 'react';
import { OverrideProps, Simplify } from '@mui/types';
import { Simplify } from '@mui/types';
import { SlotComponentProps } from '../utils';
import { PolymorphicProps } from '../utils/PolymorphicComponent';

export interface TabsRootSlotPropsOverrides {}

Expand Down Expand Up @@ -75,9 +76,7 @@ export interface TabsTypeMap<

export type TabsProps<
RootComponentType extends React.ElementType = TabsTypeMap['defaultComponent'],
> = OverrideProps<TabsTypeMap<{}, RootComponentType>, RootComponentType> & {
component?: RootComponentType;
};
> = PolymorphicProps<TabsTypeMap<{}, RootComponentType>, RootComponentType>;

export type TabsOwnerState = Simplify<
TabsOwnProps & {
Expand Down

0 comments on commit 6d2bf2e

Please sign in to comment.