diff --git a/components/steps/__tests__/__snapshots__/demo-extend.test.ts.snap b/components/steps/__tests__/__snapshots__/demo-extend.test.ts.snap
index 68cf002590..e55a4ae37b 100644
--- a/components/steps/__tests__/__snapshots__/demo-extend.test.ts.snap
+++ b/components/steps/__tests__/__snapshots__/demo-extend.test.ts.snap
@@ -6282,6 +6282,281 @@ Array [
exports[`renders components/steps/demo/steps-in-steps.tsx extend context correctly 2`] = `[]`;
+exports[`renders components/steps/demo/style-class.tsx extend context correctly 1`] = `
+
+
+
+
+
+
+
+
+ This is a content.
+
+
+
+
+
+
+
+
+ 2
+
+
+
+
+
+ This is a content.
+
+
+
+
+
+
+
+
+ 3
+
+
+
+
+
+ This is a content.
+
+
+
+
+
+
+
+
+
+
+
+
+ This is a content.
+
+
+
+
+
+
+
+
+ 2
+
+
+
+
+
+ This is a content.
+
+
+
+
+
+
+
+
+ 3
+
+
+
+
+
+ This is a content.
+
+
+
+
+
+
+`;
+
+exports[`renders components/steps/demo/style-class.tsx extend context correctly 2`] = `[]`;
+
exports[`renders components/steps/demo/title-placement.tsx extend context correctly 1`] = `
Array [
+
+
+
+
+
+
+
+ This is a content.
+
+
+
+
+
+
+
+
+ 2
+
+
+
+
+
+ This is a content.
+
+
+
+
+
+
+
+
+ 3
+
+
+
+
+
+ This is a content.
+
+
+
+
+
+
+
+
+
+
+
+
+ This is a content.
+
+
+
+
+
+
+
+
+ 2
+
+
+
+
+
+ This is a content.
+
+
+
+
+
+
+
+
+ 3
+
+
+
+
+
+ This is a content.
+
+
+
+
+
+
+`;
+
exports[`renders components/steps/demo/title-placement.tsx correctly 1`] = `
Array [
{
expect(element).toHaveStyle(style);
});
});
+
+ it('semantic structure with function classNames and styles', () => {
+ const classNamesFn: StepsProps['classNames'] = (info) => {
+ if (info.props.type === 'navigation') {
+ return { root: 'custom-navigation-root' };
+ }
+ return { root: 'custom-default-root' };
+ };
+
+ const stylesFn: StepsProps['styles'] = (info) => {
+ if (info.props.current === 1) {
+ return { root: { backgroundColor: 'rgb(255, 0, 0)' } };
+ }
+ return { root: { backgroundColor: 'rgb(0, 255, 0)' } };
+ };
+
+ const { container } = render(
+ renderSteps({
+ type: 'navigation',
+ current: 1,
+ classNames: classNamesFn,
+ styles: stylesFn,
+ }),
+ );
+
+ const rootElement = container.querySelector('.custom-navigation-root');
+ expect(rootElement).toBeTruthy();
+ expect(rootElement).toHaveClass('ant-steps');
+ expect(rootElement).toHaveStyle({ backgroundColor: 'rgb(255, 0, 0)' });
+ });
});
diff --git a/components/steps/demo/style-class.md b/components/steps/demo/style-class.md
new file mode 100644
index 0000000000..00ec95685b
--- /dev/null
+++ b/components/steps/demo/style-class.md
@@ -0,0 +1,7 @@
+## zh-CN
+
+通过 `classNames` 和 `styles` 传入对象/函数可以自定义 Steps 的[语义化结构](#semantic-dom)样式。
+
+## en-US
+
+You can customize the [semantic dom](#semantic-dom) style of Steps by passing objects/functions through `classNames` and `styles`.
diff --git a/components/steps/demo/style-class.tsx b/components/steps/demo/style-class.tsx
new file mode 100644
index 0000000000..fec73ca2fc
--- /dev/null
+++ b/components/steps/demo/style-class.tsx
@@ -0,0 +1,60 @@
+import React from 'react';
+import { Flex, Steps } from 'antd';
+import type { StepsProps } from 'antd';
+import { createStyles } from 'antd-style';
+
+const useStyles = createStyles(({ token }) => ({
+ root: {
+ border: `2px dashed ${token.colorBorder}`,
+ borderRadius: token.borderRadius,
+ padding: token.padding,
+ },
+}));
+
+const stylesObject: StepsProps['styles'] = {
+ itemIcon: { borderRadius: '30%' },
+ itemContent: { fontStyle: 'italic' },
+};
+
+const stylesFn: StepsProps['styles'] = (info) => {
+ if (info.props.type === 'navigation') {
+ return {
+ root: { borderColor: '#1890ff' },
+ };
+ }
+ return {};
+};
+
+const App: React.FC = () => {
+ const { styles } = useStyles();
+
+ const sharedProps: StepsProps = {
+ items: [
+ {
+ title: 'Finished',
+ content: 'This is a content.',
+ },
+ {
+ title: 'In Progress',
+ content: 'This is a content.',
+ },
+ {
+ title: 'Waiting',
+ content: 'This is a content.',
+ },
+ ],
+ current: 1,
+ classNames: {
+ root: styles.root,
+ },
+ };
+
+ return (
+
+
+
+
+ );
+};
+
+export default App;
diff --git a/components/steps/index.en-US.md b/components/steps/index.en-US.md
index 289d4fc56b..3af2b446e1 100644
--- a/components/steps/index.en-US.md
+++ b/components/steps/index.en-US.md
@@ -30,6 +30,7 @@ When a given task is complicated or has a certain sequence in the series of subt
Steps inside Steps
Inline Steps
Inline Style Combination
+Custom semantic dom styling
Variant Debug
Component Token
@@ -43,7 +44,7 @@ The whole of the step bar.
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
-| classNames | Semantic DOM class | [Record](#semantic-dom) | - | |
+| classNames | Customize class for each semantic structure inside the component. Supports object or function. | Record<[SemanticDOM](#semantic-dom), string> \| (info: { props })=> Record<[SemanticDOM](#semantic-dom), string> | - | |
| current | To set the current step, counting from 0. You can overwrite this state by using `status` of `Step` | number | 0 | |
| ~~direction~~ | To specify the direction of the step bar, `horizontal` or `vertical` | string | `horizontal` | |
| iconRender | Custom render icon, please use `items.icon` first | (oriNode, info: { index, active, item }) => ReactNode | - | |
@@ -55,7 +56,7 @@ The whole of the step bar.
| responsive | Change to vertical direction when screen width smaller than `532px` | boolean | true | |
| size | To specify the size of the step bar, `default` and `small` are currently supported | string | `default` | |
| status | To specify the status of current step, can be set to one of the following values: `wait` `process` `finish` `error` | string | `process` | |
-| styles | Semantic DOM style | [Record](#semantic-dom) | - | |
+| styles | Customize inline style for each semantic structure inside the component. Supports object or function. | Record<[SemanticDOM](#semantic-dom), CSSProperties> \| (info: { props })=> Record<[SemanticDOM](#semantic-dom), CSSProperties> | - | |
| titlePlacement | Place title and content with `horizontal` or `vertical` direction | string | `horizontal` | |
| type | Type of steps, can be set to one of the following values: `default` `dot` `inline` `navigation` `panel` | string | `default` | |
| variant | Config style variant | `filled` \| `outlined` | `filled` | |
diff --git a/components/steps/index.tsx b/components/steps/index.tsx
index 4d4314540c..691a9e4f48 100644
--- a/components/steps/index.tsx
+++ b/components/steps/index.tsx
@@ -6,6 +6,7 @@ import type { StepsProps as RcStepsProps } from '@rc-component/steps/lib/Steps';
import cls from 'classnames';
import useMergeSemantic from '../_util/hooks/useMergeSemantic';
+import type { SemanticClassNamesType, SemanticStylesType } from '../_util/hooks/useMergeSemantic';
import type { GetProp } from '../_util/type';
import { devUseWarning } from '../_util/warning';
import Wave from '../_util/wave';
@@ -26,6 +27,21 @@ export type IconRenderType = (
info: Pick,
) => React.ReactNode;
+export type StepsSemanticName =
+ | 'root'
+ | 'item'
+ | 'itemWrapper'
+ | 'itemIcon'
+ | 'itemSection'
+ | 'itemHeader'
+ | 'itemTitle'
+ | 'itemSubtitle'
+ | 'itemContent'
+ | 'itemRail';
+
+export type StepsClassNamesType = SemanticClassNamesType;
+export type StepsStylesType = SemanticStylesType;
+
interface StepItem {
className?: string;
style?: React.CSSProperties;
@@ -55,14 +71,12 @@ export type ProgressDotRender = (
},
) => React.ReactNode;
-export interface StepsProps {
+export interface BaseStepsProps {
// Style
- prefixCls?: string;
className?: string;
- style?: React.CSSProperties;
rootClassName?: string;
- classNames?: RcStepsProps['classNames'];
- styles?: RcStepsProps['styles'];
+ classNames?: StepsClassNamesType;
+ styles?: StepsStylesType;
variant?: 'filled' | 'outlined';
size?: 'default' | 'small';
@@ -97,6 +111,11 @@ export interface StepsProps {
onChange?: (current: number) => void;
}
+export interface StepsProps extends BaseStepsProps {
+ prefixCls?: string;
+ style?: React.CSSProperties;
+}
+
const waveEffectClassNames: StepsProps['classNames'] = {
itemIcon: TARGET_CLS,
};
@@ -171,12 +190,6 @@ const Steps = (props: StepsProps) => {
// ============================= Item =============================
const mergedItems = React.useMemo(() => (items || []).filter(Boolean), [items]);
- // ============================ Styles ============================
- const [mergedClassNames, mergedStyles] = useMergeSemantic(
- [waveEffectClassNames, contextClassNames, classNames],
- [contextStyles, styles],
- );
-
// ============================ Layout ============================
const { xs } = useBreakpoint(responsive);
@@ -225,6 +238,29 @@ const Steps = (props: StepsProps) => {
// ========================== Percentage ==========================
const mergedPercent = isInline ? undefined : percent;
+ // =========== Merged Props for Semantic ===========
+ const mergedProps: StepsProps = {
+ ...props,
+ variant,
+ size: mergedSize,
+ type: mergedType,
+ orientation: mergedOrientation,
+ titlePlacement: mergedTitlePlacement,
+ current,
+ percent: mergedPercent,
+ responsive,
+ offset,
+ };
+
+ // ============================ Styles ============================
+ const [mergedClassNames, mergedStyles] = useMergeSemantic<
+ StepsClassNamesType,
+ StepsStylesType,
+ StepsProps
+ >([waveEffectClassNames, contextClassNames, classNames], [contextStyles, styles], undefined, {
+ props: mergedProps,
+ });
+
// ============================= Icon =============================
const internalIconRender: RcStepsProps['iconRender'] = (_, info) => {
const {
diff --git a/components/steps/index.zh-CN.md b/components/steps/index.zh-CN.md
index a4394d6633..d597f2e7d0 100644
--- a/components/steps/index.zh-CN.md
+++ b/components/steps/index.zh-CN.md
@@ -33,6 +33,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*cFsBQLA0b7UAAA
内联样式组合
变体 Debug
组件 Token
+自定义各种语义结构的样式和类
## API
@@ -44,7 +45,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*cFsBQLA0b7UAAA
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
-| classNames | 语义化结构 className | [Record](#semantic-dom) | - | |
+| classNames | 用于自定义组件内部各语义化结构的 class,支持对象或函数 | Record<[SemanticDOM](#semantic-dom), string> \| (info: { props })=> Record<[SemanticDOM](#semantic-dom), string> | - | |
| current | 指定当前步骤,从 0 开始记数。在子 Step 元素中,可以通过 `status` 属性覆盖状态 | number | 0 | |
| ~~direction~~ | 指定步骤条方向。目前支持水平(`horizontal`)和竖直(`vertical`)两种方向 | string | `horizontal` | |
| iconRender | 自定义渲染图标,请优先使用 `items.icon` | (oriNode, info: { index, active, item }) => ReactNode | - | |
@@ -56,7 +57,7 @@ coverDark: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*cFsBQLA0b7UAAA
| responsive | 当屏幕宽度小于 `532px` 时自动变为垂直模式 | boolean | true | |
| size | 指定大小,目前支持普通(`default`)和迷你(`small`) | string | `default` | |
| status | 指定当前步骤的状态,可选 `wait` `process` `finish` `error` | string | `process` | |
-| styles | 语义化结构 style | [Record](#semantic-dom) | - | |
+| styles | 用于自定义组件内部各语义化结构的行内 style,支持对象或函数 | Record<[SemanticDOM](#semantic-dom), CSSProperties> \| (info: { props })=> Record<[SemanticDOM](#semantic-dom), CSSProperties> | - | |
| titlePlacement | 指定标签放置位置,默认水平放图标右侧,可选 `vertical` 放图标下方 | string | `horizontal` | |
| type | 步骤条类型,可选 `default` `dot` `inline` `navigation` `panel` | string | `default` | |
| variant | 设置样式变体 | `filled` \| `outlined` | `filled` | |