Skip to content

Commit 5a54ed0

Browse files
authored
feat(Tabs): support popupRender in more dropdown
1 parent bd2d0f9 commit 5a54ed0

6 files changed

Lines changed: 79 additions & 9 deletions

File tree

README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ Then open `http://localhost:8000`.
7474
| `indicator` | `{ size?: GetIndicatorSize; align?: 'start' \| 'center' \| 'end' }` | - | Indicator size and alignment. |
7575
| `items` | Tab[] | [] | Tab items. |
7676
| `locale` | TabsLocale | - | Accessibility locale text. |
77-
| `more` | MoreProps | - | Overflow dropdown config. |
77+
| `more` | MoreProps | - | more dropdown config, see [MoreProps](#moreprops) for full API. Additionally supports `popupRender` for custom popup content. |
7878
| `onChange` | `(activeKey: string) => void` | - | Triggered when active tab changes. |
7979
| `onTabClick` | `(activeKey, event) => void` | - | Triggered when a tab is clicked. |
8080
| `onTabScroll` | `({ direction }) => void` | - | Triggered when tab navigation scrolls. |
@@ -102,6 +102,14 @@ Then open `http://localhost:8000`.
102102
| `label` | React.ReactNode | - | Tab label. |
103103
| `style` | React.CSSProperties | - | Panel style. |
104104

105+
### MoreProps
106+
107+
| Name | Type | Default | Description |
108+
| --- | --- | --- | --- |
109+
| `icon` | ReactNode | - | The icon shown in the more trigger. |
110+
| `popupRender` | `(menu: ReactElement, info: { restTabs: Tab[], onClose: () => void }) => ReactElement` | - | Customize the dropdown popup content. The `info` object provides `restTabs` (all overflowed tabs) and `onClose` (function to close the dropdown). |
111+
| Other dropdown props | from DropdownProps | - | All other [rc-dropdown](https://github.com/react-component/dropdown) props such as `trigger`, `overlayClassName`, `visible`, etc. are also supported. |
112+
105113
## Development
106114

107115
```bash

README.zh-CN.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ npm start
7474
| `indicator` | `{ size?: GetIndicatorSize; align?: 'start' \| 'center' \| 'end' }` | - | 指示器尺寸和对齐方式。 |
7575
| `items` | Tab[] | [] | 选项卡项目。 |
7676
| `locale` | TabsLocale | - | 无障碍本地化文本。 |
77-
| `more` | MoreProps | - | 溢出下拉菜单配置。 |
77+
| `more` | MoreProps | - | 溢出下拉菜单配置,详情见 [MoreProps](#moreprops)。支持 `popupRender` 自定义弹层内容|
7878
| `onChange` | `(activeKey: string) => void` | - | 当活动选项卡更改时触发。 |
7979
| `onTabClick` | `(activeKey, event) => void` | - | 单击选项卡时触发。 |
8080
| `onTabScroll` | `({ direction }) => void` | - | 当选项卡导航滚动时触发。 |
@@ -102,6 +102,14 @@ npm start
102102
| `label` | React.ReactNode | - | Tab 标签内容。 |
103103
| `style` | React.CSSProperties | - | 面板样式。 |
104104

105+
### MoreProps
106+
107+
| 名称 | 类型 | 默认值 | 说明 |
108+
| --- | --- | --- | --- |
109+
| `icon` | ReactNode | - | 更多按钮的图标。 |
110+
| `popupRender` | `(menu: ReactElement, info: { restTabs: Tab[], onClose: () => void }) => ReactElement` | - | 自定义下拉弹层内容。`info` 对象提供 `restTabs`(所有溢出标签)和 `onClose`(关闭下拉菜单的函数)。 |
111+
| 其他下拉属性 | 来自 DropdownProps | - | 其他 [rc-dropdown](https://github.com/react-component/dropdown) 属性如 `trigger``overlayClassName``visible` 等也都支持。 |
112+
105113
## 本地开发
106114

107115
```bash

src/TabNavList/OperationNode.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ const OperationNode = React.forwardRef<HTMLDivElement, OperationNodeProps>((prop
5757
const [open, setOpen] = useState(false);
5858
const [selectedKey, setSelectedKey] = useState<string>(null);
5959

60-
const { icon: moreIcon = 'More' } = moreProps;
60+
const { icon: moreIcon = 'More', popupRender } = moreProps;
6161

6262
const popupId = `${id}-more-popup`;
6363
const dropdownPrefix = `${prefixCls}-dropdown`;
@@ -119,6 +119,13 @@ const OperationNode = React.forwardRef<HTMLDivElement, OperationNodeProps>((prop
119119
</Menu>
120120
);
121121

122+
const overlay = popupRender
123+
? popupRender(menu, {
124+
restTabs: tabs,
125+
onClose: () => setOpen(false),
126+
})
127+
: menu;
128+
122129
function selectOffset(offset: -1 | 1) {
123130
const enabledTabs = tabs.filter(tab => !tab.disabled);
124131
let selectedIndex = enabledTabs.findIndex(tab => tab.key === selectedKey) || 0;
@@ -196,7 +203,7 @@ const OperationNode = React.forwardRef<HTMLDivElement, OperationNodeProps>((prop
196203
const moreNode: React.ReactNode = mobile ? null : (
197204
<Dropdown
198205
prefixCls={dropdownPrefix}
199-
overlay={menu}
206+
overlay={overlay}
200207
visible={tabs.length ? open : false}
201208
onVisibleChange={setOpen}
202209
overlayClassName={overlayClassName}

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export type {
55
AnimatedConfig,
66
EditableConfig,
77
MoreProps,
8+
PopupRender,
89
Tab,
910
TabBarExtraContent,
1011
} from './interface';

src/interface.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,23 @@ export type TriggerProps = {
88
trigger?: 'hover' | 'click';
99
};
1010
export type moreIcon = React.ReactNode;
11+
12+
export type Tab = Omit<TabPaneProps, 'tab'> & {
13+
key: string;
14+
label: React.ReactNode;
15+
};
16+
17+
export type PopupRender = (
18+
menu: React.ReactElement,
19+
info: {
20+
restTabs: Tab[];
21+
onClose: () => void;
22+
},
23+
) => React.ReactElement;
24+
1125
export type MoreProps = {
1226
icon?: moreIcon;
27+
popupRender?: PopupRender;
1328
} & Omit<DropdownProps, 'children'>;
1429

1530
export type SizeInfo = [width: number, height: number];
@@ -31,11 +46,6 @@ export type TabOffsetMap = Map<React.Key, TabOffset>;
3146

3247
export type TabPosition = 'left' | 'right' | 'top' | 'bottom';
3348

34-
export interface Tab extends Omit<TabPaneProps, 'tab'> {
35-
key: string;
36-
label: React.ReactNode;
37-
}
38-
3949
type RenderTabBarProps = {
4050
id: string;
4151
activeKey: string;

tests/overflow.test.tsx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -671,4 +671,40 @@ describe('Tabs.Overflow', () => {
671671
unmount();
672672
jest.useRealTimers();
673673
});
674+
675+
it('should pass correct params and support custom popup content', () => {
676+
jest.useFakeTimers();
677+
const popupRender = jest.fn((menu, { restTabs }) => (
678+
<div data-testid="custom-popup">
679+
<div data-testid="tab-count">{restTabs.length}</div>
680+
{menu}
681+
</div>
682+
));
683+
const { container } = render(getTabs({ more: { popupRender } }));
684+
685+
triggerResize(container);
686+
act(() => {
687+
jest.runAllTimers();
688+
});
689+
fireEvent.mouseEnter(container.querySelector('.rc-tabs-nav-more'));
690+
act(() => {
691+
jest.runAllTimers();
692+
});
693+
694+
expect(popupRender).toHaveBeenCalled();
695+
const callArgs = popupRender.mock.calls[0];
696+
expect(callArgs[1]).toHaveProperty('restTabs');
697+
expect(callArgs[1]).toHaveProperty('onClose');
698+
expect(document.querySelector('[data-testid="custom-popup"]')).toBeTruthy();
699+
700+
const onClose = callArgs[1].onClose;
701+
act(() => {
702+
onClose();
703+
jest.runAllTimers();
704+
});
705+
const dropdownPopup = document.querySelector('.rc-tabs-dropdown');
706+
expect(dropdownPopup?.classList.contains('rc-tabs-dropdown-hidden')).toBeTruthy();
707+
708+
jest.useRealTimers();
709+
});
674710
});

0 commit comments

Comments
 (0)