feat[Segmented]: add tooltip to segmented option item (#54273)

* feat: add tooltip to segmented option item

* feat[segmented]: add demo, and tooltip can be configured as an object

* docs: change tooltip type

* test: update snap

* feat: change  itemRender of SegmentedProps

* feat: tooltipProps remove children

* feat: delete ?.

* feat: add if to tooltipProps

* feat: not inline return

* docs: custom

* Update components/segmented/__tests__/index.test.tsx

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: thinkasany <480968828@qq.com>

* Update components/segmented/demo/custom.tsx

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: thinkasany <480968828@qq.com>

* Update components/segmented/demo/custom.tsx

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: thinkasany <480968828@qq.com>

* docs: link tooltip api

* docs: lint tooltip us

* test: update snap

---------

Signed-off-by: thinkasany <480968828@qq.com>
Co-authored-by: 刘欢 <lh01217311@antgroup.com>
Co-authored-by: thinkasany <480968828@qq.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
EmilyyyLiu
2025-07-04 11:01:21 +08:00
committed by GitHub
parent 92c115e5a2
commit b2df7b3a06
9 changed files with 134 additions and 22 deletions

View File

@@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`renders components/segmented/demo/basic.tsx extend context correctly 1`] = `
<div
@@ -488,6 +488,7 @@ exports[`renders components/segmented/demo/custom.tsx extend context correctly 1
class="ant-segmented-group"
>
<label
aria-describedby="test-id"
class="ant-segmented-item ant-segmented-item-selected"
>
<input
@@ -517,7 +518,28 @@ exports[`renders components/segmented/demo/custom.tsx extend context correctly 1
</div>
</div>
</label>
<div
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-tooltip-gold css-var-test-id ant-tooltip-placement-top"
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
>
<div
class="ant-tooltip-arrow"
style="position: absolute; bottom: 0px; left: 0px;"
/>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-inner"
id="test-id"
role="tooltip"
>
hello user1
</div>
</div>
</div>
<label
aria-describedby="test-id"
class="ant-segmented-item"
>
<input
@@ -550,7 +572,28 @@ exports[`renders components/segmented/demo/custom.tsx extend context correctly 1
</div>
</div>
</label>
<div
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-tooltip-pink css-var-test-id ant-tooltip-placement-top"
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
>
<div
class="ant-tooltip-arrow"
style="position: absolute; bottom: 0px; left: 0px;"
/>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-inner"
id="test-id"
role="tooltip"
>
hello user2
</div>
</div>
</div>
<label
aria-describedby="test-id"
class="ant-segmented-item"
>
<input
@@ -596,6 +639,26 @@ exports[`renders components/segmented/demo/custom.tsx extend context correctly 1
</div>
</div>
</label>
<div
class="ant-tooltip ant-zoom-big-fast-appear ant-zoom-big-fast-appear-prepare ant-zoom-big-fast ant-tooltip-geekblue css-var-test-id ant-tooltip-placement-top"
style="--arrow-x: 0px; --arrow-y: 0px; left: -1000vw; top: -1000vh; box-sizing: border-box;"
>
<div
class="ant-tooltip-arrow"
style="position: absolute; bottom: 0px; left: 0px;"
/>
<div
class="ant-tooltip-content"
>
<div
class="ant-tooltip-inner"
id="test-id"
role="tooltip"
>
hello user3
</div>
</div>
</div>
</div>
</div>
<div

View File

@@ -1,4 +1,4 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`renders components/segmented/demo/basic.tsx correctly 1`] = `
<div
@@ -478,6 +478,7 @@ exports[`renders components/segmented/demo/custom.tsx correctly 1`] = `
class="ant-segmented-group"
>
<label
aria-describedby="test-id"
class="ant-segmented-item ant-segmented-item-selected"
>
<input
@@ -508,6 +509,7 @@ exports[`renders components/segmented/demo/custom.tsx correctly 1`] = `
</div>
</label>
<label
aria-describedby="test-id"
class="ant-segmented-item"
>
<input
@@ -541,6 +543,7 @@ exports[`renders components/segmented/demo/custom.tsx correctly 1`] = `
</div>
</label>
<label
aria-describedby="test-id"
class="ant-segmented-item"
>
<input

View File

@@ -3,7 +3,7 @@ import { AppstoreOutlined, BarsOutlined } from '@ant-design/icons';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { fireEvent, render } from '../../../tests/utils';
import { fireEvent, render, waitFor } from '../../../tests/utils';
import type { SegmentedValue } from '../index';
import Segmented from '../index';
@@ -414,4 +414,31 @@ describe('Segmented', () => {
expect(container.querySelector<HTMLDivElement>('.ant-segmented-vertical')).not.toBeNull();
});
});
describe('toolTip for optionItem ', () => {
it('Configuring tooltip in the options should display the corresponding information', async () => {
const { container } = render(
<Segmented
orientation="vertical"
options={[
{ label: 'Daily', value: 'Daily', tooltip: 'hello Daily' },
'Weekly',
{ label: 'Monthly', value: 'Monthly', tooltip: 'hello Monthly' },
]}
/>,
);
const itemList = container.querySelectorAll('.ant-segmented-item');
fireEvent.mouseEnter(itemList[0]);
fireEvent.mouseEnter(itemList[1]);
fireEvent.mouseEnter(itemList[2]);
await waitFor(() => {
const tooltipList = document.querySelectorAll('.ant-tooltip');
expect(tooltipList).toHaveLength(2);
const tooltipInnerList = document.querySelectorAll('.ant-tooltip-inner');
expect(tooltipInnerList).toHaveLength(2);
expect(tooltipInnerList[0]?.textContent).toBe('hello Daily');
expect(tooltipInnerList[1]?.textContent).toBe('hello Monthly');
});
});
});
});

View File

@@ -1,7 +1,7 @@
## zh-CN
使用 ReactNode 自定义渲染每一个 Segmented Item。
自定义渲染每一个 Segmented Item。
## en-US
Custom each Segmented Item by ReactNode.
Custom each Segmented Item.

View File

@@ -14,6 +14,7 @@ const App: React.FC = () => (
</div>
),
value: 'user1',
tooltip: { title: 'hello user1', color: 'gold' },
},
{
label: (
@@ -23,6 +24,7 @@ const App: React.FC = () => (
</div>
),
value: 'user2',
tooltip: { title: 'hello user2', color: 'pink' },
},
{
label: (
@@ -32,6 +34,7 @@ const App: React.FC = () => (
</div>
),
value: 'user3',
tooltip: { title: 'hello user3', color: 'geekblue' },
},
]}
/>

View File

@@ -57,13 +57,14 @@ Common props ref[Common props](/docs/react/common-props)
### SegmentedItemType
| Property | Description | Type | Default | Version |
| --------- | -------------------------------- | ---------------- | ------- | ------- |
| label | Display text for Segmented item | ReactNode | - | |
| value | Value for Segmented item | string \| number | - | |
| icon | Display icon for Segmented item | ReactNode | - | |
| disabled | Disabled state of segmented item | boolean | false | |
| className | The additional css class | string | - | |
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| disabled | Disabled state of segmented item | boolean | false | |
| className | The additional css class | string | - | |
| icon | Display icon for Segmented item | ReactNode | - | |
| label | Display text for Segmented item | ReactNode | - | |
| tooltip | tooltip for Segmented item | string \| [TooltipProps](../tooltip/index.en-US.md#api) | - | |
| value | Value for Segmented item | string \| number | - | |
## Semantic DOM

View File

@@ -7,6 +7,7 @@ import type {
} from '@rc-component/segmented';
import RcSegmented from '@rc-component/segmented';
import useId from '@rc-component/util/lib/hooks/useId';
import { Tooltip, TooltipProps } from 'antd';
import classNames from 'classnames';
import useOrientation from '../_util/hooks/useOrientation';
@@ -18,9 +19,11 @@ import useStyle from './style';
export type { SegmentedValue } from '@rc-component/segmented';
export type SemanticName = 'root' | 'icon' | 'label' | 'item';
interface SegmentedLabeledOptionWithoutIcon<ValueType = RcSegmentedValue>
extends RcSegmentedLabeledOption<ValueType> {
label: RcSegmentedLabeledOption['label'];
tooltip?: string | Omit<TooltipProps, 'children'>;
}
interface SegmentedLabeledOptionWithIcon<ValueType = RcSegmentedValue>
@@ -28,6 +31,7 @@ interface SegmentedLabeledOptionWithIcon<ValueType = RcSegmentedValue>
label?: RcSegmentedLabeledOption['label'];
/** Set icon for Segmented item */
icon: React.ReactNode;
tooltip?: string | Omit<TooltipProps, 'children'>;
}
function isSegmentedLabeledOptionWithIcon(
@@ -43,7 +47,7 @@ export type SegmentedLabeledOption<ValueType = RcSegmentedValue> =
export type SegmentedOptions<T = SegmentedRawOption> = (T | SegmentedLabeledOption<T>)[];
export interface SegmentedProps<ValueType = RcSegmentedValue>
extends Omit<RCSegmentedProps<ValueType>, 'size' | 'options'> {
extends Omit<RCSegmentedProps<ValueType>, 'size' | 'options' | 'itemRender'> {
rootClassName?: string;
options: SegmentedOptions<ValueType>;
/** Option to fit width to its parent's width */
@@ -59,7 +63,6 @@ export interface SegmentedProps<ValueType = RcSegmentedValue>
const InternalSegmented = React.forwardRef<HTMLDivElement, SegmentedProps>((props, ref) => {
const defaultName = useId();
const {
prefixCls: customizePrefixCls,
className,
@@ -151,6 +154,16 @@ const InternalSegmented = React.forwardRef<HTMLDivElement, SegmentedProps>((prop
...style,
};
const itemRender = (node: React.ReactNode, { item }: { item: SegmentedLabeledOption }) => {
if (!item.tooltip) {
return node;
}
const tooltipProps: TooltipProps =
typeof item.tooltip === 'object' ? item.tooltip : { title: item.tooltip };
return <Tooltip {...tooltipProps}>{node}</Tooltip>;
};
return (
<RcSegmented
{...restProps}
@@ -165,6 +178,7 @@ const InternalSegmented = React.forwardRef<HTMLDivElement, SegmentedProps>((prop
item: { ...contextStyles.item, ...styles?.item },
label: { ...contextStyles.label, ...styles?.label },
}}
itemRender={itemRender}
options={extendedOptions}
ref={ref}
prefixCls={prefixCls}

View File

@@ -60,13 +60,14 @@ demo:
### SegmentedItemType
| 属性 | 描述 | 类型 | 默认值 | 版本 |
| --------- | ---------------- | ---------------- | ------ | ---- |
| label | 分段项的显示文本 | ReactNode | - | |
| value | 分段项的值 | string \| number | - | |
| icon | 分段项的显示图标 | ReactNode | - | |
| disabled | 分段项的禁用状态 | boolean | false | |
| className | 自定义类名 | string | - | |
| 属性 | 描述 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| className | 自定义类名 | string | - | |
| disabled | 分段项的禁用状态 | boolean | false | |
| icon | 分段项的显示图标 | ReactNode | - | |
| label | 分段项的显示文本 | ReactNode | - | |
| tooltip | 分段项的工具提示 | string \| [TooltipProps](../tooltip/index.zh-CN.md#api) | - | |
| value | 分段项的值 | string \| number | - | |
## Semantic DOM

View File

@@ -133,7 +133,7 @@
"@rc-component/progress": "~1.0.1",
"@rc-component/qrcode": "~1.0.0",
"@rc-component/resize-observer": "^1.0.0",
"@rc-component/segmented": "~1.1.0",
"@rc-component/segmented": "~1.2.1",
"@rc-component/select": "~1.1.2",
"@rc-component/steps": "~1.2.1",
"@rc-component/switch": "~1.0.0",