Skip to content

Commit

Permalink
feat: support showRestInDropdown in tabs (#2289)
Browse files Browse the repository at this point in the history
* feat: support showRestInDropdown in tabs
* test: add cypress test
  • Loading branch information
YannLynn authored Jun 17, 2024
1 parent a0a3687 commit 97c5371
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 32 deletions.
1 change: 1 addition & 0 deletions content/navigation/tabs/index-en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,7 @@ lazyRender | Lazy rendering, only when the panel is activated will it be rendere
more | Render a portion of the Tab into a drop-down menu ** >= 2.59.0** | number \| {count:number,render:()=>ReactNode,dropdownProps:DropDownProps} | - |
renderTabBar | Used for secondary packaging tab bar | (tabBarProps: object, defaultTabBar: React.ComponentType) => ReactNode | None |
preventScroll | Indicates whether the browser should scroll the document to display the newly focused element, acting on the focus method inside the component, excluding the component passed in by the user | boolean | | |
showRestInDropdown | Whether to display the collapsed Tab in the drop-down menu (only effective when collapsible is true) **>= 2.61.0** | boolean | true |
size | Size, providing three types of `large`, `medium`, and `small`, **>=1.11.0, currently only supports linear Tabs** | string | `large` |
style | style object | CSSProperties | None |
tabBarExtraContent | Used to extend the content of the tab bar | ReactNode | None |
Expand Down
1 change: 1 addition & 0 deletions content/navigation/tabs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,7 @@ lazyRender | 懒渲染,仅当面板激活过才被渲染在 DOM 树中, **>=1.
more | 将一部分 Tab 渲染到下拉菜单中 ** >= 2.59.0** | number \| {count:number,render:()=>ReactNode,dropdownProps:DropDownProps} | - |
renderTabBar | 用于二次封装标签栏 | (tabBarProps: object, defaultTabBar: React.ComponentType) => ReactNode | 无 |
preventScroll | 指示浏览器是否应滚动文档以显示新聚焦的元素,作用于组件内的 focus 方法 | boolean | | |
showRestInDropdown | 是否将收起的 Tab 展示在下拉菜单中(仅当 collapsible 为 true 时生效) **>= 2.61.0** | boolean | true |
size | 大小,提供 `large``medium``small` 三种类型,**>=1.11.0,目前仅支持线性 Tabs** | string | `large` |
style | 样式对象 | CSSProperties | 无 |
tabBarExtraContent | 用于扩展标签栏的内容 | ReactNode | 无 |
Expand Down
7 changes: 7 additions & 0 deletions cypress/e2e/tabs.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,11 @@ describe('tabs', () => {
cy.get('.semi-button-disabled').eq(0).should('exist');
cy.get('.semi-tabs-bar-arrow .semi-button-primary').eq(0).should('exist');
});

it('showRestInDropdown', () => {
cy.visit('http://127.0.0.1:6006/iframe.html?id=tabs--show-rest-in-dropdown-demo&args=&viewMode=story');

cy.get('.semi-button').eq(1).trigger('mouseover');
cy.get('.semi-dropdown-content .semi-dropdown-item').should('not.exist');
});
});
67 changes: 37 additions & 30 deletions packages/semi-ui/tabs/TabBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ class TabBar extends React.Component<TabBarProps, TabBarState> {
uuid: getUuidv4(),
});
}

componentDidUpdate(prevProps) {
if (prevProps.activeKey !== this.props.activeKey) {
if (this.props.collapsible) {
this.scrollActiveTabItemIntoView()
this.scrollActiveTabItemIntoView();
}
}
}
Expand Down Expand Up @@ -106,7 +106,7 @@ class TabBar extends React.Component<TabBarProps, TabBarState> {
renderTabItem = (panel: PlainTab): ReactNode => {
const { size, type, deleteTabItem, handleKeyDown, tabPosition } = this.props;
const isSelected = this._isActive(panel.itemKey);

return (
<TabItem
{...pick(panel, ['disabled', 'icon', 'itemKey', 'tab', 'closable'])}
Expand All @@ -129,7 +129,7 @@ class TabBar extends React.Component<TabBarProps, TabBarState> {

scrollActiveTabItemIntoView = (logicalPosition?: ScrollLogicalPosition) => {
const key = this._getItemKey(this.props.activeKey);
this.scrollTabItemIntoViewByKey(key, logicalPosition)
this.scrollTabItemIntoViewByKey(key, logicalPosition);
}

renderTabComponents = (list: Array<PlainTab>): Array<ReactNode> => list.map(panel => this.renderTabItem(panel));
Expand All @@ -140,15 +140,15 @@ class TabBar extends React.Component<TabBarProps, TabBarState> {
return;
}
const key = this._getItemKey(lastItem.itemKey);
this.scrollTabItemIntoViewByKey(key)
this.scrollTabItemIntoViewByKey(key);
};

renderCollapse = (items: Array<OverflowItem>, icon: ReactNode, pos: 'start' | 'end'): ReactNode => {
const arrowCls = cls({
[`${cssClasses.TABS_BAR}-arrow-${pos}`]: pos,
[`${cssClasses.TABS_BAR}-arrow`]: true,
});

if (isEmpty(items)) {
return (
<div role="presentation" className={arrowCls}>
Expand All @@ -160,7 +160,7 @@ class TabBar extends React.Component<TabBarProps, TabBarState> {
</div>
);
}
const { dropdownClassName, dropdownStyle } = this.props;
const { dropdownClassName, dropdownStyle, showRestInDropdown } = this.props;
const { rePosKey } = this.state;
const disabled = !items.length;
const menu = (
Expand All @@ -182,37 +182,44 @@ class TabBar extends React.Component<TabBarProps, TabBarState> {
</Dropdown.Menu>
);

const button = (
<div role="presentation" className={arrowCls} onClick={(e): void => this.handleArrowClick(items, pos)}>
<Button
disabled={disabled}
icon={icon}
theme="borderless"
/>
</div>
);

const dropdownCls = cls(dropdownClassName, {
[`${cssClasses.TABS_BAR}-dropdown`]: true,
});

return (
<Dropdown
className={dropdownCls}
clickToHide
clickTriggerToHide
key={`${rePosKey}-${pos}`}
position={pos === 'start' ? 'bottomLeft' : 'bottomRight'}
render={disabled ? null : menu}
showTick
style={dropdownStyle}
trigger={'hover'}
disableFocusListener // prevent the panel from popping up again after clicking
>
<div role="presentation" className={arrowCls} onClick={(e): void => this.handleArrowClick(items, pos)}>
<Button
disabled={disabled}
icon={icon}
// size="small"
theme="borderless"
/>
</div>
</Dropdown>
<>
{showRestInDropdown ? (
<Dropdown
className={dropdownCls}
clickToHide
clickTriggerToHide
key={`${rePosKey}-${pos}`}
position={pos === 'start' ? 'bottomLeft' : 'bottomRight'}
render={disabled ? null : menu}
showTick
style={dropdownStyle}
trigger={'hover'}
disableFocusListener // prevent the panel from popping up again after clicking
>
{button}
</Dropdown>
) : (button)}
</>
);
};

renderOverflow = (items: any[]): Array<ReactNode> => items.map((item, ind) => {
const icon = ind === 0 ? <IconChevronLeft/> : <IconChevronRight/>;
const icon = ind === 0 ? <IconChevronLeft /> : <IconChevronRight />;
const pos = ind === 0 ? 'start' : 'end';
return this.renderCollapse(item, icon, pos);
});
Expand Down Expand Up @@ -246,7 +253,7 @@ class TabBar extends React.Component<TabBarProps, TabBarState> {
{(locale: Locale['Tabs'], localeCode: Locale['code']) => (
<div className={`${cssClasses.TABS_BAR}-more-trigger-content`}>
<div>{locale.more}</div>
<IconChevronDown className={`${cssClasses.TABS_BAR}-more-trigger-content-icon`}/>
<IconChevronDown className={`${cssClasses.TABS_BAR}-more-trigger-content-icon`} />
</div>
)}
</LocaleConsumer>
Expand Down
22 changes: 21 additions & 1 deletion packages/semi-ui/tabs/_story/tabs.stories.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -1038,4 +1038,24 @@ export const Fix2239 = () => {

Fix2239.story = {
name: 'Fix 2239',
};
};

export const ShowRestInDropdownDemo = () => {
return (
<Tabs
style={{
width: '60%',
margin: '20px',
}}
type="card"
collapsible
showRestInDropdown={false}
>
{[...Array(30).keys()].map(i => (
<TabPane tab={`Tab-${i}`} itemKey={`Tab-${i}`} key={`${i}`}>
Content of card tab {i}
</TabPane>
))}
</Tabs>
)
}
6 changes: 5 additions & 1 deletion packages/semi-ui/tabs/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class Tabs extends BaseComponent<TabsProps, TabsState> {
onChange: PropTypes.func,
onTabClick: PropTypes.func,
renderTabBar: PropTypes.func,
showRestInDropdown: PropTypes.bool,
size: PropTypes.oneOf(strings.SIZE),
style: PropTypes.object,
tabBarClassName: PropTypes.string,
Expand All @@ -66,7 +67,8 @@ class Tabs extends BaseComponent<TabsProps, TabsState> {
tabPaneMotion: true,
tabPosition: 'top',
type: 'line',
onTabClose: () => undefined
onTabClose: () => undefined,
showRestInDropdown: true
});

contentRef: RefObject<HTMLDivElement>;
Expand Down Expand Up @@ -246,6 +248,7 @@ class Tabs extends BaseComponent<TabsProps, TabsState> {
keepDOM,
lazyRender,
renderTabBar,
showRestInDropdown,
size,
style,
tabBarClassName,
Expand Down Expand Up @@ -274,6 +277,7 @@ class Tabs extends BaseComponent<TabsProps, TabsState> {
collapsible,
list: panes,
onTabClick: this.onTabClick,
showRestInDropdown,
size,
style: tabBarStyle,
tabBarExtraContent,
Expand Down
2 changes: 2 additions & 0 deletions packages/semi-ui/tabs/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export interface TabsProps {
onChange?: (activeKey: string) => void;
onTabClick?: (activeKey: string, e: MouseEvent<Element>) => void;
renderTabBar?: (tabBarProps: TabBarProps, defaultTabBar: typeof TabBar) => ReactNode;
showRestInDropdown?: boolean;
size?: TabSize;
style?: CSSProperties;
tabBarClassName?: string;
Expand All @@ -48,6 +49,7 @@ export interface TabBarProps {
collapsible?: boolean;
list?: Array<PlainTab>;
onTabClick?: (activeKey: string, event: MouseEvent<Element>) => void;
showRestInDropdown?: boolean;
size?: TabSize;
style?: CSSProperties;
tabBarExtraContent?: ReactNode;
Expand Down

0 comments on commit 97c5371

Please sign in to comment.