mirror of
https://github.com/ant-design/ant-design.git
synced 2024-11-24 02:59:58 +08:00
feat: support icon only in segmented (#35256)
* feat: support icon only with segmented * fix: lint issue * chore: update * test: fix * test: update snapshot
This commit is contained in:
parent
5a6b3ccd98
commit
eee3b50727
@ -644,6 +644,84 @@ Array [
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/segmented/demo/icon-only.md extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-segmented"
|
||||
>
|
||||
<label
|
||||
class="ant-segmented-item ant-segmented-item-selected"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
class="ant-segmented-item-input"
|
||||
type="radio"
|
||||
/>
|
||||
<div
|
||||
class="ant-segmented-item-label"
|
||||
>
|
||||
<span
|
||||
class="ant-segmented-item-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="bars"
|
||||
class="anticon anticon-bars"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="bars"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="0 0 1024 1024"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M912 192H328c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h584c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 284H328c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h584c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 284H328c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h584c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM104 228a56 56 0 10112 0 56 56 0 10-112 0zm0 284a56 56 0 10112 0 56 56 0 10-112 0zm0 284a56 56 0 10112 0 56 56 0 10-112 0z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
<label
|
||||
class="ant-segmented-item"
|
||||
>
|
||||
<input
|
||||
class="ant-segmented-item-input"
|
||||
type="radio"
|
||||
/>
|
||||
<div
|
||||
class="ant-segmented-item-label"
|
||||
>
|
||||
<span
|
||||
class="ant-segmented-item-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="appstore"
|
||||
class="anticon anticon-appstore"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="appstore"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M464 144H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16zm-52 268H212V212h200v200zm452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16zm-52 268H612V212h200v200zM464 544H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16zm-52 268H212V612h200v200zm452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16zm-52 268H612V612h200v200z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/segmented/demo/size.md extend context correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
|
@ -644,6 +644,84 @@ Array [
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/segmented/demo/icon-only.md correctly 1`] = `
|
||||
<div
|
||||
class="ant-segmented"
|
||||
>
|
||||
<label
|
||||
class="ant-segmented-item ant-segmented-item-selected"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
class="ant-segmented-item-input"
|
||||
type="radio"
|
||||
/>
|
||||
<div
|
||||
class="ant-segmented-item-label"
|
||||
>
|
||||
<span
|
||||
class="ant-segmented-item-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="bars"
|
||||
class="anticon anticon-bars"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="bars"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="0 0 1024 1024"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M912 192H328c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h584c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 284H328c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h584c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 284H328c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h584c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM104 228a56 56 0 10112 0 56 56 0 10-112 0zm0 284a56 56 0 10112 0 56 56 0 10-112 0zm0 284a56 56 0 10112 0 56 56 0 10-112 0z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
<label
|
||||
class="ant-segmented-item"
|
||||
>
|
||||
<input
|
||||
class="ant-segmented-item-input"
|
||||
type="radio"
|
||||
/>
|
||||
<div
|
||||
class="ant-segmented-item-label"
|
||||
>
|
||||
<span
|
||||
class="ant-segmented-item-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="appstore"
|
||||
class="anticon anticon-appstore"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="appstore"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M464 144H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16zm-52 268H212V212h200v200zm452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16zm-52 268H612V212h200v200zM464 544H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16zm-52 268H212V612h200v200zm452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16zm-52 268H612V612h200v200z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/segmented/demo/size.md correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
|
@ -631,6 +631,87 @@ exports[`Segmented render segmented: disabled 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Segmented render with icons 1`] = `
|
||||
<div
|
||||
class="ant-segmented"
|
||||
>
|
||||
<label
|
||||
class="ant-segmented-item ant-segmented-item-selected"
|
||||
>
|
||||
<input
|
||||
checked=""
|
||||
class="ant-segmented-item-input"
|
||||
type="radio"
|
||||
/>
|
||||
<div
|
||||
class="ant-segmented-item-label"
|
||||
>
|
||||
<span
|
||||
class="ant-segmented-item-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="bars"
|
||||
class="anticon anticon-bars"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="bars"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="0 0 1024 1024"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M912 192H328c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h584c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 284H328c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h584c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 284H328c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h584c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM104 228a56 56 0 10112 0 56 56 0 10-112 0zm0 284a56 56 0 10112 0 56 56 0 10-112 0zm0 284a56 56 0 10112 0 56 56 0 10-112 0z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
<label
|
||||
class="ant-segmented-item"
|
||||
>
|
||||
<input
|
||||
class="ant-segmented-item-input"
|
||||
type="radio"
|
||||
/>
|
||||
<div
|
||||
class="ant-segmented-item-label"
|
||||
>
|
||||
<span
|
||||
class="ant-segmented-item-icon"
|
||||
>
|
||||
<span
|
||||
aria-label="appstore"
|
||||
class="anticon anticon-appstore"
|
||||
role="img"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
data-icon="appstore"
|
||||
fill="currentColor"
|
||||
focusable="false"
|
||||
height="1em"
|
||||
viewBox="64 64 896 896"
|
||||
width="1em"
|
||||
>
|
||||
<path
|
||||
d="M464 144H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16zm-52 268H212V212h200v200zm452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16zm-52 268H612V212h200v200zM464 544H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16zm-52 268H212V612h200v200zm452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16zm-52 268H612V612h200v200z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
KanbanYes
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Segmented rtl render component should be rendered correctly in RTL direction 1`] = `
|
||||
<div
|
||||
class="ant-segmented ant-segmented-rtl"
|
||||
|
@ -1,5 +1,7 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { AppstoreOutlined, BarsOutlined } from '@ant-design/icons';
|
||||
|
||||
import mountTest from '../../../tests/shared/mountTest';
|
||||
import rtlTest from '../../../tests/shared/rtlTest';
|
||||
import Segmented from '../index';
|
||||
@ -86,10 +88,7 @@ describe('Segmented', () => {
|
||||
it('render segmented with string options', () => {
|
||||
const handleValueChange = jest.fn();
|
||||
const wrapper = mount(
|
||||
<Segmented
|
||||
options={['Daily', 'Weekly', 'Monthly']}
|
||||
onChange={handleValueChange}
|
||||
/>,
|
||||
<Segmented options={['Daily', 'Weekly', 'Monthly']} onChange={handleValueChange} />,
|
||||
);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
|
||||
@ -356,4 +355,25 @@ describe('Segmented', () => {
|
||||
|
||||
expect(wrapper.find(`.${prefixCls}`).at(0).hasClass(`${prefixCls}-lg`)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('render with icons', () => {
|
||||
const wrapper = mount(
|
||||
<Segmented
|
||||
options={[
|
||||
{
|
||||
value: 'List',
|
||||
icon: <BarsOutlined />,
|
||||
},
|
||||
{
|
||||
value: 'Kanban',
|
||||
label: 'KanbanYes',
|
||||
icon: <AppstoreOutlined />,
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
expect(wrapper.render()).toMatchSnapshot();
|
||||
expect(wrapper.find(`span.${prefixCls}-item-icon`).length).toBe(2);
|
||||
expect(wrapper.find(`div.${prefixCls}-item-label`).at(1).contains('KanbanYes')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
order: 10
|
||||
order: 1
|
||||
title:
|
||||
zh-CN: Block 分段选择器
|
||||
en-US: Block Segmented
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
order: 0
|
||||
order: 3
|
||||
title:
|
||||
zh-CN: 受控模式
|
||||
en-US: Controlled mode
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
order: 1
|
||||
order: 4
|
||||
title:
|
||||
zh-CN: 自定义渲染
|
||||
en-US: Custom Render
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
order: 0
|
||||
order: 2
|
||||
title:
|
||||
zh-CN: 不可用
|
||||
en-US: Basic
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
order: 0
|
||||
order: 5
|
||||
title:
|
||||
zh-CN: 动态数据
|
||||
en-US: Dynamic
|
||||
|
34
components/segmented/demo/icon-only.md
Normal file
34
components/segmented/demo/icon-only.md
Normal file
@ -0,0 +1,34 @@
|
||||
---
|
||||
order: 8
|
||||
title:
|
||||
zh-CN: 只设置图标
|
||||
en-US: With Icon only
|
||||
---
|
||||
|
||||
## zh-CN
|
||||
|
||||
在 Segmented Item 选项中只设置 Icon。
|
||||
|
||||
## en-US
|
||||
|
||||
Set `icon` without `label` for Segmented Item.
|
||||
|
||||
```jsx
|
||||
import { Segmented } from 'antd';
|
||||
import { AppstoreOutlined, BarsOutlined } from '@ant-design/icons';
|
||||
|
||||
export default () => (
|
||||
<Segmented
|
||||
options={[
|
||||
{
|
||||
value: 'List',
|
||||
icon: <BarsOutlined />,
|
||||
},
|
||||
{
|
||||
value: 'Kanban',
|
||||
icon: <AppstoreOutlined />,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
```
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
order: 1
|
||||
order: 6
|
||||
title:
|
||||
zh-CN: 三种大小
|
||||
en-US: Three sizes of Segmented
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
order: 0
|
||||
order: 7
|
||||
title:
|
||||
zh-CN: 设置图标
|
||||
en-US: With Icon
|
||||
|
@ -12,12 +12,27 @@ import SizeContext, { SizeType } from '../config-provider/SizeContext';
|
||||
|
||||
export type { SegmentedValue } from 'rc-segmented';
|
||||
|
||||
export interface SegmentedLabeledOption extends RcSegmentedLabeledOption {
|
||||
/** Set icon for Segmented item */
|
||||
icon?: React.ReactNode;
|
||||
interface SegmentedLabeledOptionWithoutIcon extends RcSegmentedLabeledOption {
|
||||
label: RcSegmentedLabeledOption['label'];
|
||||
}
|
||||
|
||||
export interface SegmentedProps extends Omit<RCSegmentedProps, 'size'> {
|
||||
interface SegmentedLabeledOptionWithIcon extends Omit<RcSegmentedLabeledOption, 'label'> {
|
||||
label?: RcSegmentedLabeledOption['label'];
|
||||
/** Set icon for Segmented item */
|
||||
icon: React.ReactNode;
|
||||
}
|
||||
|
||||
function isSegmentedLabeledOptionWithIcon(
|
||||
option: SegmentedRawOption | SegmentedLabeledOptionWithIcon | SegmentedLabeledOptionWithoutIcon,
|
||||
): option is SegmentedLabeledOptionWithIcon {
|
||||
return typeof option === 'object' && !!(option as SegmentedLabeledOptionWithIcon)?.icon;
|
||||
}
|
||||
|
||||
export type SegmentedLabeledOption =
|
||||
| SegmentedLabeledOptionWithIcon
|
||||
| SegmentedLabeledOptionWithoutIcon;
|
||||
|
||||
export interface SegmentedProps extends Omit<RCSegmentedProps, 'size' | 'options'> {
|
||||
options: (SegmentedRawOption | SegmentedLabeledOption)[];
|
||||
/** Option to fit width to its parent's width */
|
||||
block?: boolean;
|
||||
@ -46,14 +61,14 @@ const Segmented = React.forwardRef<HTMLDivElement, SegmentedProps>((props, ref)
|
||||
const extendedOptions = React.useMemo(
|
||||
() =>
|
||||
options.map(option => {
|
||||
if (typeof option === 'object' && option?.icon) {
|
||||
if (isSegmentedLabeledOptionWithIcon(option)) {
|
||||
const { icon, label, ...restOption } = option;
|
||||
return {
|
||||
...restOption,
|
||||
label: (
|
||||
<>
|
||||
<span className={`${prefixCls}-item-icon`}>{icon}</span>
|
||||
<span>{label}</span>
|
||||
{label && <span>{label}</span>}
|
||||
</>
|
||||
),
|
||||
};
|
||||
|
@ -61,8 +61,8 @@
|
||||
}
|
||||
|
||||
// syntactic sugar to add `icon` for Segmented Item
|
||||
&-icon {
|
||||
margin-right: 6px;
|
||||
&-icon + * {
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
&-input {
|
||||
|
Loading…
Reference in New Issue
Block a user