Skip to content

Commit b799b7d

Browse files
committed
Alternate value-required approach
1 parent f120754 commit b799b7d

File tree

26 files changed

+145
-265
lines changed

26 files changed

+145
-265
lines changed

docs/pages/api-docs/tab-context.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ You can learn more about the difference by [reading this guide](/guides/minimizi
2727
| Name | Type | Default | Description |
2828
|:-----|:-----|:--------|:------------|
2929
| <span class="prop-name">children</span> | <span class="prop-type">node</span> | | The content of the component. |
30-
| <span class="prop-name required">value&nbsp;*</span> | <span class="prop-type">any</span> | | The value of the currently selected `Tab`. If you don't want any selected `Tab`, you can set this property to `false`. |
30+
| <span class="prop-name required">value&nbsp;*</span> | <span class="prop-type">string</span> | | The value of the currently selected `Tab`. |
3131

3232
The component cannot hold a ref.
3333

docs/pages/api-docs/tab-list.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,12 @@ You can learn more about the difference by [reading this guide](/guides/minimizi
2828
|:-----|:-----|:--------|:------------|
2929
| <span class="prop-name">children</span> | <span class="prop-type">Array&lt;element&gt;</span> | | |
3030

31-
The component cannot hold a ref.
31+
The `ref` is forwarded to the root element.
3232

33-
Any other props supplied will be provided to the root element (native element).
33+
Any other props supplied will be provided to the root element ([Tabs](/api/tabs/)).
34+
35+
## Inheritance
36+
37+
The props of the [Tabs](/api/tabs/) component are also available.
38+
You can take advantage of this behavior to [target nested components](/guides/api/#spread).
3439

docs/pages/api-docs/tab-panel.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,9 @@ The `MuiTabPanel` name can be used for providing [default props](/customization/
2828

2929
| Name | Type | Default | Description |
3030
|:-----|:-----|:--------|:------------|
31-
| <span class="prop-name required">activeValue&nbsp;*</span> | <span class="prop-type">any</span> | | |
32-
| <span class="prop-name">children</span> | <span class="prop-type">node</span> | | |
31+
| <span class="prop-name">children</span> | <span class="prop-type">node</span> | | The content of the component. |
3332
| <span class="prop-name">classes</span> | <span class="prop-type">object</span> | | Override or extend the styles applied to the component. See [CSS API](#css) below for more details. |
34-
| <span class="prop-name required">value&nbsp;*</span> | <span class="prop-type">any</span> | | |
33+
| <span class="prop-name required">value&nbsp;*</span> | <span class="prop-type">string</span> | | The `value` of the corresponding `Tab`. Must use the index of the `Tab` when no `value` was passed to `Tab`. |
3534

3635
The `ref` is forwarded to the root element.
3736

docs/pages/api-docs/tab-panels.js

Lines changed: 0 additions & 15 deletions
This file was deleted.

docs/pages/api-docs/tab-panels.md

Lines changed: 0 additions & 34 deletions
This file was deleted.

docs/src/pages/components/tabs/LabTabs.js

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import Tab from '@material-ui/core/Tab';
55
import TabContext from '@material-ui/lab/TabContext';
66
import TabList from '@material-ui/lab/TabList';
77
import TabPanel from '@material-ui/lab/TabPanel';
8-
import TabPanels from '@material-ui/lab/TabPanels';
98

109
const useStyles = makeStyles((theme) => ({
1110
root: {
@@ -16,7 +15,7 @@ const useStyles = makeStyles((theme) => ({
1615

1716
export default function LabTabs() {
1817
const classes = useStyles();
19-
const [value, setValue] = React.useState(0);
18+
const [value, setValue] = React.useState('1');
2019

2120
const handleChange = (event, newValue) => {
2221
setValue(newValue);
@@ -27,16 +26,14 @@ export default function LabTabs() {
2726
<TabContext value={value}>
2827
<AppBar position="static">
2928
<TabList onChange={handleChange} aria-label="simple tabs example">
30-
<Tab label="Item One" />
31-
<Tab label="Item Two" />
32-
<Tab label="Item Three" />
29+
<Tab label="Item One" value="1" />
30+
<Tab label="Item Two" value="2" />
31+
<Tab label="Item Three" value="3" />
3332
</TabList>
3433
</AppBar>
35-
<TabPanels>
36-
<TabPanel>Item One</TabPanel>
37-
<TabPanel>Item Two</TabPanel>
38-
<TabPanel>Item Three</TabPanel>
39-
</TabPanels>
34+
<TabPanel value="1">Item One</TabPanel>
35+
<TabPanel value="2">Item Two</TabPanel>
36+
<TabPanel value="3">Item Three</TabPanel>
4037
</TabContext>
4138
</div>
4239
);

docs/src/pages/components/tabs/LabTabs.tsx

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import Tab from '@material-ui/core/Tab';
55
import TabContext from '@material-ui/lab/TabContext';
66
import TabList from '@material-ui/lab/TabList';
77
import TabPanel from '@material-ui/lab/TabPanel';
8-
import TabPanels from '@material-ui/lab/TabPanels';
98

109
const useStyles = makeStyles((theme: Theme) => ({
1110
root: {
@@ -16,9 +15,9 @@ const useStyles = makeStyles((theme: Theme) => ({
1615

1716
export default function LabTabs() {
1817
const classes = useStyles();
19-
const [value, setValue] = React.useState(0);
18+
const [value, setValue] = React.useState('1');
2019

21-
const handleChange = (event: React.ChangeEvent<{}>, newValue: number) => {
20+
const handleChange = (event: React.ChangeEvent<{}>, newValue: string) => {
2221
setValue(newValue);
2322
};
2423

@@ -27,16 +26,14 @@ export default function LabTabs() {
2726
<TabContext value={value}>
2827
<AppBar position="static">
2928
<TabList onChange={handleChange} aria-label="simple tabs example">
30-
<Tab label="Item One" />
31-
<Tab label="Item Two" />
32-
<Tab label="Item Three" />
29+
<Tab label="Item One" value="1" />
30+
<Tab label="Item Two" value="2" />
31+
<Tab label="Item Three" value="3" />
3332
</TabList>
3433
</AppBar>
35-
<TabPanels>
36-
<TabPanel>Item One</TabPanel>
37-
<TabPanel>Item Two</TabPanel>
38-
<TabPanel>Item Three</TabPanel>
39-
</TabPanels>
34+
<TabPanel value="1">Item One</TabPanel>
35+
<TabPanel value="2">Item Two</TabPanel>
36+
<TabPanel value="3">Item Three</TabPanel>
4037
</TabContext>
4138
</div>
4239
);
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
import * as React from 'react';
22

33
export interface TabContextValue {
4-
panelPrefix: string;
5-
tabPrefix: string;
6-
value: unknown;
4+
idPrefix: string;
5+
value: string;
76
}
87

98
export interface TabContextProps {
109
children?: React.ReactNode;
1110
/**
1211
* The value of the currently selected `Tab`.
13-
* If you don't want any selected `Tab`, you can set this property to `false`.
1412
*/
15-
value: unknown;
13+
value: string;
1614
}
1715
/**
1816
*
@@ -22,3 +20,5 @@ export interface TabContextProps {
2220
*/
2321
export default function TabContext(props: TabContextProps): JSX.Element;
2422
export function useTabContext(): TabContextValue | null;
23+
export function getPanelId(context: TabContextValue, tabValue: string): string;
24+
export function getTabId(context: TabContextValue, tabValue: string): string;

packages/material-ui-lab/src/TabContext/TabContext.js

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import * as React from 'react';
22
import * as PropTypes from 'prop-types';
33

4+
/**
5+
* @type {React.Context<{ idPrefix: string; value: string } | null>}
6+
*/
47
const Context = React.createContext(null);
58
if (process.env.NODE_ENV !== 'production') {
69
Context.displayName = 'TabContext';
@@ -9,19 +12,18 @@ if (process.env.NODE_ENV !== 'production') {
912
function useUniquePrefix() {
1013
const [id, setId] = React.useState(null);
1114
React.useEffect(() => {
12-
setId(`mui-${Math.round(Math.random() * 1e5)}`);
15+
setId(`mui-p-${Math.round(Math.random() * 1e5)}`);
1316
}, []);
1417
return id;
1518
}
1619

1720
export default function TabContext(props) {
1821
const { children, value } = props;
19-
const panelPrefix = useUniquePrefix();
20-
const tabPrefix = useUniquePrefix();
22+
const idPrefix = useUniquePrefix();
2123

2224
const context = React.useMemo(() => {
23-
return { tabPrefix, panelPrefix, value };
24-
}, [panelPrefix, tabPrefix, value]);
25+
return { idPrefix, value };
26+
}, [idPrefix, value]);
2527

2628
return <Context.Provider value={context}>{children}</Context.Provider>;
2729
}
@@ -37,11 +39,29 @@ TabContext.propTypes = {
3739
children: PropTypes.node,
3840
/**
3941
* The value of the currently selected `Tab`.
40-
* If you don't want any selected `Tab`, you can set this property to `false`.
4142
*/
42-
value: PropTypes.any.isRequired,
43+
value: PropTypes.string.isRequired,
4344
};
4445

46+
/**
47+
* @returns {unknown}
48+
*/
4549
export function useTabContext() {
4650
return React.useContext(Context);
4751
}
52+
53+
export function getPanelId(context, value) {
54+
const { idPrefix } = context;
55+
if (idPrefix === null) {
56+
return null;
57+
}
58+
return `${context.idPrefix}-P-${value}`;
59+
}
60+
61+
export function getTabId(context, value) {
62+
const { idPrefix } = context;
63+
if (idPrefix === null) {
64+
return null;
65+
}
66+
return `${context.idPrefix}-T-${value}`;
67+
}

packages/material-ui-lab/src/TabContext/TabContext.test.js

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as React from 'react';
22
import ReactDOMServer from 'react-dom/server';
33
import { expect } from 'chai';
44
import { createClientRender } from 'test/utils/createClientRender';
5-
import TabContext, { useTabContext } from './TabContext';
5+
import TabContext, { getPanelId, getTabId, useTabContext } from './TabContext';
66

77
describe('<TabContext />', () => {
88
const render = createClientRender();
@@ -19,21 +19,21 @@ describe('<TabContext />', () => {
1919
expect(value).to.equal(null);
2020
});
2121

22-
it('provides tab and panel prefixes for IDREFs and the active value', () => {
23-
function Tabs() {
24-
const { panelPrefix, tabPrefix, value } = useTabContext();
22+
it('provides an id prefix for IDREFs and the active value', () => {
23+
function Tabs({ value }) {
24+
const context = useTabContext();
2525
return (
2626
<React.Fragment>
27-
<div data-testid="active-value" data-value={value} />
28-
<div role="tab" id={tabPrefix} />
29-
<div role="tabpanel" id={panelPrefix} />
27+
<div data-testid="active-value" data-value={context.value} />
28+
<div role="tab" id={getTabId(context, value)} />
29+
<div role="tabpanel" id={getPanelId(context, value)} />
3030
</React.Fragment>
3131
);
3232
}
3333

3434
const { getByRole, getByTestId } = render(
35-
<TabContext value={0}>
36-
<Tabs />
35+
<TabContext value="0">
36+
<Tabs value="0" />
3737
</TabContext>,
3838
);
3939

@@ -46,20 +46,20 @@ describe('<TabContext />', () => {
4646
});
4747

4848
it('provides undefined tab and panel prefixes and the active value when ssr', () => {
49-
function Tabs() {
50-
const { panelPrefix, tabPrefix, value } = useTabContext();
49+
function Tabs({ value }) {
50+
const context = useTabContext();
5151
return (
5252
<React.Fragment>
53-
<div data-testid="active-value" data-value={value} />
54-
<div role="tab" id={tabPrefix} />
55-
<div role="tabpanel" id={panelPrefix} />
53+
<div data-testid="active-value" data-value={context.value} />
54+
<div role="tab" id={getTabId(context, value)} />
55+
<div role="tabpanel" id={getPanelId(context, value)} />
5656
</React.Fragment>
5757
);
5858
}
5959

6060
const markup = ReactDOMServer.renderToStaticMarkup(
61-
<TabContext value={0}>
62-
<Tabs />
61+
<TabContext value="0">
62+
<Tabs value="0" />
6363
</TabContext>,
6464
);
6565

@@ -69,18 +69,18 @@ describe('<TabContext />', () => {
6969
});
7070

7171
it('hydrates tab and tabpanel prefixes', () => {
72-
function Tabs() {
73-
const { panelPrefix, tabPrefix } = useTabContext();
72+
function Tabs({ value }) {
73+
const context = useTabContext();
7474
return (
7575
<React.Fragment>
76-
<div role="tab" id={tabPrefix} />
77-
<div role="tabpanel" id={panelPrefix} />
76+
<div role="tab" id={getTabId(context, value)} />
77+
<div role="tabpanel" id={getPanelId(context, value)} />
7878
</React.Fragment>
7979
);
8080
}
8181
const reactElement = (
82-
<TabContext value={0}>
83-
<Tabs />
82+
<TabContext value="0">
83+
<Tabs value="0" />
8484
</TabContext>
8585
);
8686
const markup = ReactDOMServer.renderToString(reactElement);
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
export { default, useTabContext } from './TabContext';
1+
export { default } from './TabContext';
2+
export * from './TabContext';
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
export { default, useTabContext } from './TabContext';
1+
export { default } from './TabContext';
2+
export * from './TabContext';

packages/material-ui-lab/src/TabList/TabList.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export interface TabListTypeMap<
1616
* API:
1717
*
1818
* - [TabList API](https://material-ui.com/api/tab-list/)
19+
* - inherits [Tabs API](https://material-ui.com/api/tabs/)
1920
*/
2021
declare const TabList: OverridableComponent<TabListTypeMap>;
2122

0 commit comments

Comments
 (0)