Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,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 @@ -102,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: { 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). |
| 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
10 changes: 9 additions & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,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 Down Expand Up @@ -102,6 +102,14 @@ npm start
| `label` | React.ReactNode | - | Tab 标签内容。 |
| `style` | React.CSSProperties | - | 面板样式。 |

### MoreProps

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

## 本地开发

```bash
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, {
restTabs: tabs,
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: {
restTabs: 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, { restTabs }) => (
<div data-testid="custom-popup">
<div data-testid="tab-count">{restTabs.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('restTabs');
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