Skip to content

Commit d124a62

Browse files
authored
refactor: TabPane is a element (#279)
* refactor: TabPane is a element * clean up
1 parent 80af6ad commit d124a62

File tree

11 files changed

+153
-82
lines changed

11 files changed

+153
-82
lines changed

src/TabPanelList/TabPane.tsx

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,36 @@
11
import * as React from 'react';
22
import classNames from 'classnames';
3-
import { Tab } from '../interface';
43

54
export interface TabPaneProps {
6-
prefixCls: string;
7-
id: string;
8-
tab: Tab;
9-
animated: boolean;
10-
active: boolean;
5+
tab?: React.ReactNode;
6+
className?: string;
7+
style?: React.CSSProperties;
8+
disabled?: boolean;
9+
children?: React.ReactNode;
10+
forceRender?: boolean;
11+
closable?: boolean;
12+
closeIcon?: React.ReactNode;
13+
14+
// Pass by TabPaneList
15+
prefixCls?: string;
16+
tabKey?: string;
17+
id?: string;
18+
animated?: boolean;
19+
active?: boolean;
1120
destroyInactiveTabPane?: boolean;
1221
}
1322

1423
export default function TabPane({
1524
prefixCls,
25+
forceRender,
26+
className,
27+
style,
1628
id,
1729
active,
1830
animated,
1931
destroyInactiveTabPane,
20-
tab: { key, children, forceRender, className, style },
32+
tabKey,
33+
children,
2134
}: TabPaneProps) {
2235
const [visited, setVisited] = React.useState(forceRender);
2336

@@ -40,10 +53,10 @@ export default function TabPane({
4053

4154
return (
4255
<div
43-
id={id && `${id}-panel-${key}`}
56+
id={id && `${id}-panel-${tabKey}`}
4457
role="tabpanel"
4558
tabIndex={active ? 0 : -1}
46-
aria-labelledby={id && `${id}-tab-${key}`}
59+
aria-labelledby={id && `${id}-tab-${tabKey}`}
4760
aria-hidden={!active}
4861
style={{ ...mergedStyle, ...style }}
4962
className={classNames(

src/TabPanelList/index.tsx

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import * as React from 'react';
22
import classNames from 'classnames';
3-
import TabPane from './TabPane';
43
import TabContext from '../TabContext';
54
import { TabPosition, AnimatedConfig } from '../interface';
65

@@ -37,17 +36,15 @@ export default function TabPanelList({
3736
}
3837
>
3938
{tabs.map(tab => {
40-
return (
41-
<TabPane
42-
prefixCls={prefixCls}
43-
id={id}
44-
animated={tabPaneAnimated}
45-
destroyInactiveTabPane={destroyInactiveTabPane}
46-
active={tab.key === activeKey}
47-
key={tab.key}
48-
tab={tab}
49-
/>
50-
);
39+
return React.cloneElement(tab.node, {
40+
key: tab.key,
41+
prefixCls,
42+
tabKey: tab.key,
43+
id,
44+
animated: tabPaneAnimated,
45+
active: tab.key === activeKey,
46+
destroyInactiveTabPane,
47+
});
5148
})}
5249
</div>
5350
</div>

src/Tabs.tsx

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,17 @@ import { useEffect, useState } from 'react';
44
import classNames from 'classnames';
55
import toArray from 'rc-util/lib/Children/toArray';
66
import useMergedState from 'rc-util/lib/hooks/useMergedState';
7-
import TabPane, { TabPaneProps } from './sugar/TabPane';
87
import TabNavList from './TabNavList';
98
import TabPanelList from './TabPanelList';
9+
import TabPane, { TabPaneProps } from './TabPanelList/TabPane';
1010
import {
11-
Tab,
1211
TabPosition,
1312
RenderTabBar,
1413
TabsLocale,
1514
EditableConfig,
1615
AnimatedConfig,
1716
OnTabScroll,
17+
Tab,
1818
} from './interface';
1919
import TabContext from './TabContext';
2020
import { isMobile } from './hooks/useTouchMove';
@@ -66,14 +66,20 @@ export interface TabsProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'o
6666
}
6767

6868
function parseTabList(children: React.ReactNode): Tab[] {
69-
return toArray(children).map((node: React.ReactElement<TabPaneProps>) =>
70-
React.isValidElement(node)
71-
? {
72-
key: node.key !== undefined ? String(node.key) : undefined,
69+
return toArray(children)
70+
.map((node: React.ReactElement<TabPaneProps>) => {
71+
if (React.isValidElement(node)) {
72+
const key = node.key !== undefined ? String(node.key) : undefined;
73+
return {
74+
key,
7375
...node.props,
74-
}
75-
: null,
76-
);
76+
node,
77+
};
78+
}
79+
80+
return null;
81+
})
82+
.filter(tab => tab);
7783
}
7884

7985
function Tabs(
@@ -156,7 +162,7 @@ function Tabs(
156162
// Async generate id to avoid ssr mapping failed
157163
useEffect(() => {
158164
if (!id) {
159-
setMergedId(`rc-tabs-${uuid}`);
165+
setMergedId(`rc-tabs-${process.env.NODE_ENV === 'test' ? 'test' : uuid}`);
160166
uuid += 1;
161167
}
162168
}, []);

src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import Tabs, { TabsProps } from './Tabs';
2-
import TabPane from './sugar/TabPane';
2+
import TabPane, { TabPaneProps } from './TabPanelList/TabPane';
33

4-
export { TabPane, TabsProps };
4+
export { TabPane, TabsProps, TabPaneProps };
55

66
export default Tabs;

src/interface.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { TabPaneProps } from './sugar/TabPane';
1+
import { TabPaneProps } from './TabPanelList/TabPane';
22

33
export type TabSizeMap = Map<
44
React.Key,
@@ -17,10 +17,8 @@ export type TabOffsetMap = Map<React.Key, TabOffset>;
1717
export type TabPosition = 'left' | 'right' | 'top' | 'bottom';
1818

1919
export interface Tab extends TabPaneProps {
20-
tab?: React.ReactNode;
21-
children?: React.ReactNode;
22-
forceRender?: boolean;
2320
key: string;
21+
node: React.ReactElement;
2422
}
2523

2624
export type RenderTabBar = (props: any, DefaultTabBar: React.ComponentType) => React.ReactElement;

src/sugar/TabPane.tsx

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

tests/__snapshots__/index.test.tsx.snap

Lines changed: 84 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,32 +16,32 @@ exports[`Tabs.Basic Normal 1`] = `
1616
style="transform: translate(0px, 0px);"
1717
>
1818
<button
19-
aria-controls="rc-tabs-0-panel-light"
19+
aria-controls="rc-tabs-test-panel-light"
2020
aria-selected="false"
2121
class="rc-tabs-tab"
22-
id="rc-tabs-0-tab-light"
22+
id="rc-tabs-test-tab-light"
2323
role="tab"
2424
tabindex="0"
2525
type="button"
2626
>
2727
light
2828
</button>
2929
<button
30-
aria-controls="rc-tabs-0-panel-bamboo"
30+
aria-controls="rc-tabs-test-panel-bamboo"
3131
aria-selected="true"
3232
class="rc-tabs-tab rc-tabs-tab-active"
33-
id="rc-tabs-0-tab-bamboo"
33+
id="rc-tabs-test-tab-bamboo"
3434
role="tab"
3535
tabindex="0"
3636
type="button"
3737
>
3838
bamboo
3939
</button>
4040
<button
41-
aria-controls="rc-tabs-0-panel-cute"
41+
aria-controls="rc-tabs-test-panel-cute"
4242
aria-selected="false"
4343
class="rc-tabs-tab"
44-
id="rc-tabs-0-tab-cute"
44+
id="rc-tabs-test-tab-cute"
4545
role="tab"
4646
tabindex="0"
4747
type="button"
@@ -57,12 +57,12 @@ exports[`Tabs.Basic Normal 1`] = `
5757
class="rc-tabs-nav-operations rc-tabs-nav-operations-hidden"
5858
>
5959
<button
60-
aria-controls="rc-tabs-0-more-popup"
60+
aria-controls="rc-tabs-test-more-popup"
6161
aria-expanded="false"
6262
aria-haspopup="listbox"
6363
aria-hidden="true"
6464
class="rc-tabs-nav-more"
65-
id="rc-tabs-0-more"
65+
id="rc-tabs-test-more"
6666
style="visibility: hidden; order: 1;"
6767
tabindex="-1"
6868
type="button"
@@ -79,28 +79,28 @@ exports[`Tabs.Basic Normal 1`] = `
7979
>
8080
<div
8181
aria-hidden="true"
82-
aria-labelledby="rc-tabs-0-tab-light"
82+
aria-labelledby="rc-tabs-test-tab-light"
8383
class="rc-tabs-tabpane"
84-
id="rc-tabs-0-panel-light"
84+
id="rc-tabs-test-panel-light"
8585
role="tabpanel"
8686
style="display: none;"
8787
tabindex="-1"
8888
/>
8989
<div
9090
aria-hidden="false"
91-
aria-labelledby="rc-tabs-0-tab-bamboo"
91+
aria-labelledby="rc-tabs-test-tab-bamboo"
9292
class="rc-tabs-tabpane rc-tabs-tabpane-active"
93-
id="rc-tabs-0-panel-bamboo"
93+
id="rc-tabs-test-panel-bamboo"
9494
role="tabpanel"
9595
tabindex="0"
9696
>
9797
Bamboo
9898
</div>
9999
<div
100100
aria-hidden="true"
101-
aria-labelledby="rc-tabs-0-tab-cute"
101+
aria-labelledby="rc-tabs-test-tab-cute"
102102
class="rc-tabs-tabpane"
103-
id="rc-tabs-0-panel-cute"
103+
id="rc-tabs-test-panel-cute"
104104
role="tabpanel"
105105
style="display: none;"
106106
tabindex="-1"
@@ -109,3 +109,73 @@ exports[`Tabs.Basic Normal 1`] = `
109109
</div>
110110
</div>
111111
`;
112+
113+
exports[`Tabs.Basic Skip invalidate children 1`] = `
114+
<div
115+
class="rc-tabs rc-tabs-top"
116+
>
117+
<div
118+
class="rc-tabs-nav"
119+
role="tablist"
120+
>
121+
<div
122+
class="rc-tabs-nav-wrap"
123+
>
124+
<div
125+
class="rc-tabs-nav-list"
126+
style="transform: translate(0px, 0px);"
127+
>
128+
<button
129+
aria-controls="rc-tabs-test-panel-light"
130+
aria-selected="true"
131+
class="rc-tabs-tab rc-tabs-tab-active"
132+
id="rc-tabs-test-tab-light"
133+
role="tab"
134+
tabindex="0"
135+
type="button"
136+
>
137+
light
138+
</button>
139+
<div
140+
class="rc-tabs-ink-bar rc-tabs-ink-bar-animated"
141+
/>
142+
</div>
143+
</div>
144+
<div
145+
class="rc-tabs-nav-operations rc-tabs-nav-operations-hidden"
146+
>
147+
<button
148+
aria-controls="rc-tabs-test-more-popup"
149+
aria-expanded="false"
150+
aria-haspopup="listbox"
151+
aria-hidden="true"
152+
class="rc-tabs-nav-more"
153+
id="rc-tabs-test-more"
154+
style="visibility: hidden; order: 1;"
155+
tabindex="-1"
156+
type="button"
157+
>
158+
More
159+
</button>
160+
</div>
161+
</div>
162+
<div
163+
class="rc-tabs-content-holder"
164+
>
165+
<div
166+
class="rc-tabs-content rc-tabs-content-top"
167+
>
168+
<div
169+
aria-hidden="false"
170+
aria-labelledby="rc-tabs-test-tab-light"
171+
class="rc-tabs-tabpane rc-tabs-tabpane-active"
172+
id="rc-tabs-test-panel-light"
173+
role="tabpanel"
174+
tabindex="0"
175+
>
176+
Light
177+
</div>
178+
</div>
179+
</div>
180+
</div>
181+
`;

tests/__snapshots__/overflow.test.tsx.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22

33
exports[`Tabs.Overflow should collapse 1`] = `
44
<button
5-
aria-controls="rc-tabs-0-more-popup"
5+
aria-controls="rc-tabs-test-more-popup"
66
aria-expanded="false"
77
aria-haspopup="listbox"
88
aria-hidden="true"
99
class="rc-tabs-nav-more"
10-
id="rc-tabs-0-more"
10+
id="rc-tabs-test-more"
1111
style=""
1212
tabindex="-1"
1313
type="button"

tests/__snapshots__/suger.test.tsx.snap

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

tests/index.test.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,22 @@ describe('Tabs.Basic', () => {
3030
expect(wrapper.render()).toMatchSnapshot();
3131
});
3232

33+
it('Skip invalidate children', () => {
34+
const wrapper = mount(
35+
getTabs({
36+
children: [
37+
<TabPane tab="light" key="light">
38+
Light
39+
</TabPane>,
40+
'not me',
41+
],
42+
}),
43+
);
44+
wrapper.update();
45+
46+
expect(wrapper.render()).toMatchSnapshot();
47+
});
48+
3349
it('nothing for empty tabs', () => {
3450
mount(getTabs({ children: null }));
3551
});

0 commit comments

Comments
 (0)