Skip to content
Merged
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

<p align="center">English | <a href="./README.zh-CN.md">简体中文</a></p>


## Highlights

- Supports top, bottom, left, and right tab positions with RTL layouts.
Expand Down Expand Up @@ -75,7 +74,7 @@ Then open `http://localhost:8000`.
| `indicator` | `{ size?: GetIndicatorSize; align?: 'start' \| 'center' \| 'end' }` | - | Indicator size and alignment. |
| `items` | Tab[] | [] | Tab items. |
| `locale` | TabsLocale | - | Accessibility locale text. |
| `more` | MoreProps | - | Overflow dropdown config. |
| `more` | MoreProps | - | more dropdown config, see [MoreProps](#moreprops) for full API. Additionally supports `popupRender` for custom popup content. |
| `onChange` | `(activeKey: string) => void` | - | Triggered when active tab changes. |
| `onTabClick` | `(activeKey, event) => void` | - | Triggered when a tab is clicked. |
| `onTabScroll` | `({ direction }) => void` | - | Triggered when tab navigation scrolls. |
Expand Down Expand Up @@ -103,6 +102,14 @@ Then open `http://localhost:8000`.
| `label` | React.ReactNode | - | Tab label. |
| `style` | React.CSSProperties | - | Panel style. |

### MoreProps

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `icon` | ReactNode | - | The icon shown in the more trigger. |
| `popupRender` | `(menu: ReactElement, info: { tabs: Tab[], onClose: () => void }) => ReactElement` | - | Customize the dropdown popup content. The `info` object provides `tabs` (all overflowed tabs) and `onClose` (function to close the dropdown). |
| 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. |

## Development

```bash
Expand Down
31 changes: 19 additions & 12 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

<p align="center"><a href="./README.md">English</a> | 简体中文</p>


## 特性

- 支持 RTL 布局的顶部、底部、左侧和右侧选项卡位置。
Expand Down Expand Up @@ -75,7 +74,7 @@ npm start
| `indicator` | `{ size?: GetIndicatorSize; align?: 'start' \| 'center' \| 'end' }` | - | 指示器尺寸和对齐方式。 |
| `items` | Tab[] | [] | 选项卡项目。 |
| `locale` | TabsLocale | - | 无障碍本地化文本。 |
| `more` | MoreProps | - | 溢出下拉菜单配置。 |
| `more` | MoreProps | - | 溢出下拉菜单配置,详情见 [MoreProps](#moreprops)。支持 `popupRender` 自定义弹层内容。 |
| `onChange` | `(activeKey: string) => void` | - | 当活动选项卡更改时触发。 |
| `onTabClick` | `(activeKey, event) => void` | - | 单击选项卡时触发。 |
| `onTabScroll` | `({ direction }) => void` | - | 当选项卡导航滚动时触发。 |
Expand All @@ -90,18 +89,26 @@ npm start

### Tab

| 名称 | 类型 | 默认值 | 说明 |
| ----------------- | ------------------- | ------ | ---------------------------------- |
| `children` | React.ReactNode | - | 选项卡面板内容。 |
| `className` | string | - | 面板 className。 |
| `closable` | boolean | - | 是否可以在可编辑模式下关闭选项卡。 |
| `closeIcon` | React.ReactNode | - | 自定义关闭图标。 |
| `destroyOnHidden` | boolean | false | 销毁非活动面板。 |
| `disabled` | boolean | false | 禁用该选项卡。 |
| `forceRender` | boolean | false | 在面板变为活动状态之前渲染面板。 |
| `key` | string | - | 需要唯一的 Tab 键。 |
| `label` | React.ReactNode | - | Tab 标签内容。 |
| `style` | React.CSSProperties | - | 面板样式。 |

### MoreProps

| 名称 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `children` | React.ReactNode | - | 选项卡面板内容。 |
| `className` | string | - | 面板 className。 |
| `closable` | boolean | - | 是否可以在可编辑模式下关闭选项卡。 |
| `closeIcon` | React.ReactNode | - | 自定义关闭图标。 |
| `destroyOnHidden` | boolean | false | 销毁非活动面板。 |
| `disabled` | boolean | false | 禁用该选项卡。 |
| `forceRender` | boolean | false | 在面板变为活动状态之前渲染面板。 |
| `key` | string | - | 需要唯一的 Tab 键。 |
| `label` | React.ReactNode | - | Tab 标签内容。 |
| `style` | React.CSSProperties | - | 面板样式。 |
| `icon` | ReactNode | - | 更多按钮的图标。 |
| `popupRender` | `(menu: ReactElement, info: { tabs: Tab[], onClose: () => void }) => ReactElement` | - | 自定义下拉弹层内容。`info` 对象提供 `tabs`(所有溢出标签)和 `onClose`(关闭下拉菜单的函数)。 |
| 其他下拉属性 | 来自 DropdownProps | - | 其他 [rc-dropdown](https://github.com/react-component/dropdown) 属性如 `trigger`、`overlayClassName`、`visible` 等也都支持。 |

## 本地开发

Expand Down
11 changes: 9 additions & 2 deletions src/TabNavList/OperationNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ const OperationNode = React.forwardRef<HTMLDivElement, OperationNodeProps>((prop
const [open, setOpen] = useState(false);
const [selectedKey, setSelectedKey] = useState<string>(null);

const { icon: moreIcon = 'More' } = moreProps;
const { icon: moreIcon = 'More', popupRender } = moreProps;

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

const overlay = popupRender
? popupRender(menu, {
tabs,
Comment thread
EmilyyyLiu marked this conversation as resolved.
Outdated
onClose: () => setOpen(false),
})
: menu;

function selectOffset(offset: -1 | 1) {
const enabledTabs = tabs.filter(tab => !tab.disabled);
let selectedIndex = enabledTabs.findIndex(tab => tab.key === selectedKey) || 0;
Expand Down Expand Up @@ -196,7 +203,7 @@ const OperationNode = React.forwardRef<HTMLDivElement, OperationNodeProps>((prop
const moreNode: React.ReactNode = mobile ? null : (
<Dropdown
prefixCls={dropdownPrefix}
overlay={menu}
overlay={overlay}
visible={tabs.length ? open : false}
onVisibleChange={setOpen}
overlayClassName={overlayClassName}
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export type {
AnimatedConfig,
EditableConfig,
MoreProps,
PopupRender,
Tab,
TabBarExtraContent,
} from './interface';
Expand Down
20 changes: 15 additions & 5 deletions src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,23 @@ export type TriggerProps = {
trigger?: 'hover' | 'click';
};
export type moreIcon = React.ReactNode;

export type Tab = Omit<TabPaneProps, 'tab'> & {
key: string;
label: React.ReactNode;
};

export type PopupRender = (
menu: React.ReactElement,
info: {
tabs: Tab[];
onClose: () => void;
},
) => React.ReactElement;

export type MoreProps = {
icon?: moreIcon;
popupRender?: PopupRender;
} & Omit<DropdownProps, 'children'>;

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

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

export interface Tab extends Omit<TabPaneProps, 'tab'> {
key: string;
label: React.ReactNode;
}

type RenderTabBarProps = {
id: string;
activeKey: string;
Expand Down
36 changes: 36 additions & 0 deletions tests/overflow.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -671,4 +671,40 @@ describe('Tabs.Overflow', () => {
unmount();
jest.useRealTimers();
});

it('should pass correct params and support custom popup content', () => {
jest.useFakeTimers();
const popupRender = jest.fn((menu, { tabs }) => (
<div data-testid="custom-popup">
<div data-testid="tab-count">{tabs.length}</div>
{menu}
</div>
));
const { container } = render(getTabs({ more: { popupRender } }));

triggerResize(container);
act(() => {
jest.runAllTimers();
});
fireEvent.mouseEnter(container.querySelector('.rc-tabs-nav-more'));
act(() => {
jest.runAllTimers();
});

expect(popupRender).toHaveBeenCalled();
const callArgs = popupRender.mock.calls[0];
expect(callArgs[1]).toHaveProperty('tabs');
expect(callArgs[1]).toHaveProperty('onClose');
expect(document.querySelector('[data-testid="custom-popup"]')).toBeTruthy();

const onClose = callArgs[1].onClose;
act(() => {
onClose();
jest.runAllTimers();
});
const dropdownPopup = document.querySelector('.rc-tabs-dropdown');
expect(dropdownPopup?.classList.contains('rc-tabs-dropdown-hidden')).toBeTruthy();

jest.useRealTimers();
});
});
Loading