From 660c8ce22204444d5d01a3abd4f3a9e964023c1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Sun, 27 Apr 2025 16:40:42 +0800 Subject: [PATCH 01/22] chore: bump react --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 07e9f09..b62ac94 100644 --- a/package.json +++ b/package.json @@ -80,8 +80,8 @@ "querystring": "^0.2.0", "rc-dialog": "8.x", "rc-test": "^7.0.9", - "react": "^19.0.0", - "react-dom": "^19.0.0", + "react": "^18.0.0", + "react-dom": "^18.0.0", "typescript": "^5.0.0" }, "peerDependencies": { From 917262f1fdf70c2e408510d6cee2d6f742aa0dfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Sun, 27 Apr 2025 19:06:56 +0800 Subject: [PATCH 02/22] chore: init --- src/Step.tsx | 33 +++++------ src/Steps.tsx | 151 ++++++++++++++++++++++++++++---------------------- 2 files changed, 98 insertions(+), 86 deletions(-) diff --git a/src/Step.tsx b/src/Step.tsx index 02cc9d9..e651f84 100644 --- a/src/Step.tsx +++ b/src/Step.tsx @@ -3,18 +3,22 @@ import * as React from 'react'; import classNames from 'classnames'; import KeyCode from '@rc-component/util/lib/KeyCode'; import type { Status, Icons } from './interface'; -import type { StepIconRender, ProgressDotRender } from './Steps'; +import type { ProgressDotRender, StepItem, StepsProps } from './Steps'; function isString(str: any): str is string { return typeof str === 'string'; } export interface StepProps { + // style prefixCls?: string; className?: string; style?: React.CSSProperties; - wrapperStyle?: React.CSSProperties; - iconPrefix?: string; + classNames: StepsProps['classNames']; + styles: StepsProps['styles']; + + // data + data: StepItem; active?: boolean; disabled?: boolean; stepIndex?: number; @@ -23,39 +27,36 @@ export interface StepProps { title?: React.ReactNode; subTitle?: React.ReactNode; description?: React.ReactNode; - tailContent?: React.ReactNode; + + // render + iconRender?: StepsProps['iconRender']; + icon?: React.ReactNode; - icons?: Icons; onClick?: React.MouseEventHandler; onStepClick?: (index: number) => void; progressDot?: ProgressDotRender | boolean; - stepIcon?: StepIconRender; render?: (stepItem: React.ReactElement) => React.ReactNode; } -const Step: React.FC = (props) => { +export default function Step(props: StepProps) { const { className, prefixCls, style, active, status, - iconPrefix, icon, - wrapperStyle, stepNumber, disabled, description, title, subTitle, progressDot, - stepIcon, - tailContent, - icons, stepIndex, onStepClick, onClick, render, + data, ...restProps } = props; @@ -153,7 +154,7 @@ const Step: React.FC = (props) => { let stepNode: React.ReactNode = (
-
{tailContent}
+
{renderIconNode()}
@@ -178,10 +179,4 @@ const Step: React.FC = (props) => { } return stepNode; -}; - -if (process.env.NODE_ENV !== 'production') { - Step.displayName = 'rc-step'; } - -export default Step; diff --git a/src/Steps.tsx b/src/Steps.tsx index 41f6bc6..ed506ca 100644 --- a/src/Steps.tsx +++ b/src/Steps.tsx @@ -1,15 +1,30 @@ /* eslint react/no-did-mount-set-state: 0, react/prop-types: 0 */ -import classNames from 'classnames'; +import cls from 'classnames'; import React from 'react'; import type { Icons, Status } from './interface'; import type { StepProps } from './Step'; import Step from './Step'; +export type SemanticName = 'root'; + +export type StepItem = { + /** @deprecated Please use `content` instead. */ + description?: React.ReactNode; + content?: React.ReactNode; + disabled?: boolean; + icon?: React.ReactNode; + status?: Status; + subTitle?: React.ReactNode; + title?: React.ReactNode; +}; + export type StepIconRender = (info: { index: number; status: Status; title: React.ReactNode; + // @deprecated Please use `content` instead. description: React.ReactNode; + content: React.ReactNode; node: React.ReactNode; }) => React.ReactNode; @@ -19,119 +34,129 @@ export type ProgressDotRender = ( index: number; status: Status; title: React.ReactNode; + // @deprecated Please use `content` instead. description: React.ReactNode; + content: React.ReactNode; }, ) => React.ReactNode; export interface StepsProps { + // style prefixCls?: string; style?: React.CSSProperties; className?: string; - children?: React.ReactNode; - direction?: 'horizontal' | 'vertical'; - type?: 'default' | 'navigation' | 'inline'; + classNames?: Partial>; + styles?: Partial>; + + // layout + orientation?: 'horizontal' | 'vertical'; labelPlacement?: 'horizontal' | 'vertical'; - iconPrefix?: string; + progressDot?: ProgressDotRender | boolean; + + // data status?: Status; - size?: 'default' | 'small'; current?: number; - progressDot?: ProgressDotRender | boolean; - stepIcon?: StepIconRender; initial?: number; - icons?: Icons; - items?: StepProps[]; - itemRender?: (item: StepProps, stepItem: React.ReactElement) => React.ReactNode; + items?: StepItem[]; onChange?: (current: number) => void; + + // render + iconRender?: ( + item: StepItem, + info: { + status: Status; + }, + ) => React.ReactNode; + itemRender?: (item: StepItem, stepItem: React.ReactElement) => React.ReactNode; } -const Steps: React.FC & { - Step: typeof Step; -} = (props) => { +export default function Steps(props: StepsProps) { const { + // style prefixCls = 'rc-steps', - style = {}, + style, className, - children, - direction = 'horizontal', - type = 'default', + classNames, + styles, + + // layout + orientation = 'horizontal', labelPlacement = 'horizontal', - iconPrefix = 'rc', + progressDot, + + // data status = 'process', - size, current = 0, - progressDot = false, - stepIcon, initial = 0, - icons, onChange, + items, + + // render + iconRender, itemRender, - items = [], + ...restProps } = props; - const isNav = type === 'navigation'; - const isInline = type === 'inline'; - - // inline type requires fixed progressDot direction size. - const mergedProgressDot = isInline || progressDot; - const mergedDirection = isInline ? 'horizontal' : direction; - const mergedSize = isInline ? undefined : size; - - const adjustedLabelPlacement = mergedProgressDot ? 'vertical' : labelPlacement; - const classString = classNames(prefixCls, `${prefixCls}-${mergedDirection}`, className, { - [`${prefixCls}-${mergedSize}`]: mergedSize, - [`${prefixCls}-label-${adjustedLabelPlacement}`]: mergedDirection === 'horizontal', - [`${prefixCls}-dot`]: !!mergedProgressDot, - [`${prefixCls}-navigation`]: isNav, - [`${prefixCls}-inline`]: isInline, + // ============================= layout ============================= + const [mergedOrientation, mergeLabelPlacement] = React.useMemo(() => { + const nextOrientation = orientation === 'vertical' ? 'vertical' : 'horizontal'; + const nextLabelPlacement = progressDot ? 'vertical' : labelPlacement; + + return [nextOrientation, nextLabelPlacement] as const; + }, [orientation, progressDot, labelPlacement]); + + // ============================= styles ============================= + const classString = cls(prefixCls, `${prefixCls}-${mergedOrientation}`, className, { + [`${prefixCls}-label-${mergeLabelPlacement}`]: mergedOrientation === 'horizontal', + [`${prefixCls}-dot`]: !!progressDot, }); + // ============================== Data ============================== + const mergedItems = React.useMemo(() => (items || []).filter(Boolean), [items]); + + // ============================= events ============================= const onStepClick = (next: number) => { if (onChange && current !== next) { onChange(next); } }; + // ============================= render ============================= const renderStep = (item: StepProps, index: number) => { - const mergedItem: StepProps = { ...item }; + const prevItem = mergedItems[index - 1]; + + const data: StepProps = { ...item }; const stepNumber = initial + index; // fix tail color if (status === 'error' && index === current - 1) { - mergedItem.className = `${prefixCls}-next-error`; + data.className = `${prefixCls}-next-error`; } - if (!mergedItem.status) { + if (!data.status) { if (stepNumber === current) { - mergedItem.status = status; + data.status = status; } else if (stepNumber < current) { - mergedItem.status = 'finish'; + data.status = 'finish'; } else { - mergedItem.status = 'wait'; + data.status = 'wait'; } } - if (isInline) { - mergedItem.icon = undefined; - mergedItem.subTitle = undefined; - } - - if (!mergedItem.render && itemRender) { - mergedItem.render = (stepItem) => itemRender(mergedItem, stepItem); + if (!data.render && itemRender) { + data.render = (stepItem) => itemRender(data, stepItem); } return ( ); @@ -139,15 +164,7 @@ const Steps: React.FC & { return (
- {items.filter(Boolean).map(renderStep)} + {mergedItems.map(renderStep)}
); -}; - -Steps.Step = Step; - -if (process.env.NODE_ENV !== 'production') { - Steps.displayName = 'rc-steps'; } - -export default Steps; From 2181d7e160576c70febd844c4f2a053299ba799e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Sun, 27 Apr 2025 21:38:06 +0800 Subject: [PATCH 03/22] chore: classNames --- src/Step.tsx | 88 ++++++++++++++++++++++----------------- src/Steps.tsx | 112 +++++++++++++++++++++++++++++++++++--------------- 2 files changed, 131 insertions(+), 69 deletions(-) diff --git a/src/Step.tsx b/src/Step.tsx index e651f84..1a754bc 100644 --- a/src/Step.tsx +++ b/src/Step.tsx @@ -1,6 +1,6 @@ /* eslint react/prop-types: 0 */ import * as React from 'react'; -import classNames from 'classnames'; +import cls from 'classnames'; import KeyCode from '@rc-component/util/lib/KeyCode'; import type { Status, Icons } from './interface'; import type { ProgressDotRender, StepItem, StepsProps } from './Steps'; @@ -19,49 +19,64 @@ export interface StepProps { // data data: StepItem; + status: Status; + prevStatus?: Status; + active?: boolean; disabled?: boolean; - stepIndex?: number; - stepNumber?: number; - status?: Status; - title?: React.ReactNode; - subTitle?: React.ReactNode; - description?: React.ReactNode; + index: number; + + // stepIndex?: number; + // stepNumber?: number; + // title?: React.ReactNode; + // subTitle?: React.ReactNode; + // description?: React.ReactNode; // render iconRender?: StepsProps['iconRender']; - icon?: React.ReactNode; - onClick?: React.MouseEventHandler; - onStepClick?: (index: number) => void; progressDot?: ProgressDotRender | boolean; render?: (stepItem: React.ReactElement) => React.ReactNode; + + // Event + onClick?: (index: number) => void; } export default function Step(props: StepProps) { const { - className, + // style prefixCls, + className, style, - active, + classNames, + styles, + + // data + data, status, - icon, - stepNumber, + prevStatus, + + active, disabled, - description, - title, - subTitle, + index, + + // render + icon, progressDot, - stepIndex, - onStepClick, - onClick, render, - data, + + // events + onClick, + ...restProps } = props; + // ========================== Data ========================== + const { onClick: onItemClick, title, subTitle, content, description } = data; + const mergedContent = content ?? description; + // ========================= Click ========================== - const clickable = !!onStepClick && !disabled; + const clickable = !!(onItemClick || onItemClick) && !disabled; const accessibilityProps: { role?: string; @@ -69,17 +84,19 @@ export default function Step(props: StepProps) { onClick?: React.MouseEventHandler; onKeyDown?: React.KeyboardEventHandler; } = {}; + if (clickable) { accessibilityProps.role = 'button'; accessibilityProps.tabIndex = 0; accessibilityProps.onClick = (e) => { - onClick?.(e); - onStepClick(stepIndex); + onItemClick?.(e); + onClick(index); }; + accessibilityProps.onKeyDown = (e) => { const { which } = e; if (which === KeyCode.ENTER || which === KeyCode.SPACE) { - onStepClick(stepIndex); + onClick(index); } }; } @@ -87,7 +104,7 @@ export default function Step(props: StepProps) { // ========================= Render ========================= const renderIconNode = () => { let iconNode: React.ReactNode; - const iconClassName = classNames(`${prefixCls}-icon`, `${iconPrefix}icon`, { + const iconClassName = cls(`${prefixCls}-icon`, `${iconPrefix}icon`, { [`${iconPrefix}icon-${icon}`]: icon && isString(icon), [`${iconPrefix}icon-check`]: !icon && status === 'finish' && ((icons && !icons.finish) || !icons), @@ -105,6 +122,7 @@ export default function Step(props: StepProps) { status, title, description, + content: mergedContent, })} ); @@ -125,10 +143,11 @@ export default function Step(props: StepProps) { if (stepIcon) { iconNode = stepIcon({ - index: stepNumber - 1, + index, status, title, description, + content: mergedContent, node: iconNode, }); } @@ -138,16 +157,11 @@ export default function Step(props: StepProps) { const mergedStatus = status || 'wait'; - const classString = classNames( - `${prefixCls}-item`, - `${prefixCls}-item-${mergedStatus}`, - className, - { - [`${prefixCls}-item-custom`]: icon, - [`${prefixCls}-item-active`]: active, - [`${prefixCls}-item-disabled`]: disabled === true, - }, - ); + const classString = cls(`${prefixCls}-item`, `${prefixCls}-item-${mergedStatus}`, className, { + [`${prefixCls}-item-custom`]: icon, + [`${prefixCls}-item-active`]: active, + [`${prefixCls}-item-disabled`]: disabled === true, + }); const stepItemStyle: React.CSSProperties = { ...style }; diff --git a/src/Steps.tsx b/src/Steps.tsx index ed506ca..ece99a1 100644 --- a/src/Steps.tsx +++ b/src/Steps.tsx @@ -16,6 +16,8 @@ export type StepItem = { status?: Status; subTitle?: React.ReactNode; title?: React.ReactNode; + + onClick?: React.MouseEventHandler; }; export type StepIconRender = (info: { @@ -47,6 +49,7 @@ export interface StepsProps { className?: string; classNames?: Partial>; styles?: Partial>; + rootClassName?: string; // layout orientation?: 'horizontal' | 'vertical'; @@ -78,6 +81,7 @@ export default function Steps(props: StepsProps) { className, classNames, styles, + rootClassName, // layout orientation = 'horizontal', @@ -107,13 +111,38 @@ export default function Steps(props: StepsProps) { }, [orientation, progressDot, labelPlacement]); // ============================= styles ============================= - const classString = cls(prefixCls, `${prefixCls}-${mergedOrientation}`, className, { - [`${prefixCls}-label-${mergeLabelPlacement}`]: mergedOrientation === 'horizontal', - [`${prefixCls}-dot`]: !!progressDot, - }); + const classString = cls( + prefixCls, + `${prefixCls}-${mergedOrientation}`, + { + [`${prefixCls}-label-${mergeLabelPlacement}`]: mergedOrientation === 'horizontal', + [`${prefixCls}-dot`]: !!progressDot, + }, + rootClassName, + className, + classNames.root, + ); // ============================== Data ============================== const mergedItems = React.useMemo(() => (items || []).filter(Boolean), [items]); + const statuses = React.useMemo( + () => + mergedItems.map(({ status: itemStatus }, index) => { + const stepNumber = initial + index; + + if (!itemStatus) { + if (stepNumber === current) { + return status; + } else if (stepNumber < current) { + return 'finish'; + } + return 'wait'; + } + + return itemStatus; + }), + [mergedItems, status, current, initial], + ); // ============================= events ============================= const onStepClick = (next: number) => { @@ -123,47 +152,66 @@ export default function Steps(props: StepsProps) { }; // ============================= render ============================= - const renderStep = (item: StepProps, index: number) => { - const prevItem = mergedItems[index - 1]; + const renderStep = (item: StepItem, index: number) => { + const stepIndex = initial + index; - const data: StepProps = { ...item }; - const stepNumber = initial + index; // fix tail color - if (status === 'error' && index === current - 1) { - data.className = `${prefixCls}-next-error`; - } - - if (!data.status) { - if (stepNumber === current) { - data.status = status; - } else if (stepNumber < current) { - data.status = 'finish'; - } else { - data.status = 'wait'; - } - } - - if (!data.render && itemRender) { - data.render = (stepItem) => itemRender(data, stepItem); - } + // if (status === 'error' && index === current - 1) { + // data.className = `${prefixCls}-next-error`; + // } + + // const { status: currentStatus = status } = item; + // const { status: prevStatus = currentStatus } = prevItem || {}; + + // if (!data.status) { + // if (stepNumber === current) { + // data.status = status; + // } else if (stepNumber < current) { + // data.status = 'finish'; + // } else { + // data.status = 'wait'; + // } + // } + + const prevStatus = statuses[index - 1]; + const itemStatus = statuses[index]; + + // if (!data.render && itemRender) { + // data.render = (stepItem) => itemRender(data, stepItem); + // } return ( ); }; return ( -
+
{mergedItems.map(renderStep)}
); From 335cd68279feb27b2b01f103c80e860edb67f3d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Sun, 27 Apr 2025 22:15:03 +0800 Subject: [PATCH 04/22] chore: item semantic part --- src/Step.tsx | 124 ++++++++++++++++++-------------------------------- src/Steps.tsx | 19 ++++---- 2 files changed, 56 insertions(+), 87 deletions(-) diff --git a/src/Step.tsx b/src/Step.tsx index 1a754bc..faba17b 100644 --- a/src/Step.tsx +++ b/src/Step.tsx @@ -12,18 +12,14 @@ function isString(str: any): str is string { export interface StepProps { // style prefixCls?: string; - className?: string; - style?: React.CSSProperties; classNames: StepsProps['classNames']; styles: StepsProps['styles']; // data data: StepItem; - status: Status; prevStatus?: Status; active?: boolean; - disabled?: boolean; index: number; // stepIndex?: number; @@ -36,7 +32,7 @@ export interface StepProps { iconRender?: StepsProps['iconRender']; icon?: React.ReactNode; progressDot?: ProgressDotRender | boolean; - render?: (stepItem: React.ReactElement) => React.ReactNode; + itemRender?: (stepItem: React.ReactElement) => React.ReactNode; // Event onClick?: (index: number) => void; @@ -46,33 +42,40 @@ export default function Step(props: StepProps) { const { // style prefixCls, - className, - style, classNames, styles, // data data, - status, prevStatus, active, - disabled, index, // render - icon, progressDot, - render, + itemRender, + iconRender, // events onClick, - - ...restProps } = props; // ========================== Data ========================== - const { onClick: onItemClick, title, subTitle, content, description } = data; + const { + onClick: onItemClick, + title, + subTitle, + content, + description, + disabled, + icon, + status, + + className, + style, + ...restItemProps + } = data; const mergedContent = content ?? description; // ========================= Click ========================== @@ -102,74 +105,37 @@ export default function Step(props: StepProps) { } // ========================= Render ========================= - const renderIconNode = () => { - let iconNode: React.ReactNode; - const iconClassName = cls(`${prefixCls}-icon`, `${iconPrefix}icon`, { - [`${iconPrefix}icon-${icon}`]: icon && isString(icon), - [`${iconPrefix}icon-check`]: - !icon && status === 'finish' && ((icons && !icons.finish) || !icons), - [`${iconPrefix}icon-cross`]: - !icon && status === 'error' && ((icons && !icons.error) || !icons), - }); - const iconDot = ; - // `progressDot` enjoy the highest priority - if (progressDot) { - if (typeof progressDot === 'function') { - iconNode = ( - - {progressDot(iconDot, { - index: stepNumber - 1, - status, - title, - description, - content: mergedContent, - })} - - ); - } else { - iconNode = {iconDot}; - } - } else if (icon && !isString(icon)) { - iconNode = {icon}; - } else if (icons && icons.finish && status === 'finish') { - iconNode = {icons.finish}; - } else if (icons && icons.error && status === 'error') { - iconNode = {icons.error}; - } else if (icon || status === 'finish' || status === 'error') { - iconNode = ; - } else { - iconNode = {stepNumber}; - } - - if (stepIcon) { - iconNode = stepIcon({ - index, - status, - title, - description, - content: mergedContent, - node: iconNode, - }); - } - - return iconNode; - }; - const mergedStatus = status || 'wait'; - const classString = cls(`${prefixCls}-item`, `${prefixCls}-item-${mergedStatus}`, className, { - [`${prefixCls}-item-custom`]: icon, - [`${prefixCls}-item-active`]: active, - [`${prefixCls}-item-disabled`]: disabled === true, - }); - - const stepItemStyle: React.CSSProperties = { ...style }; + const classString = cls( + `${prefixCls}-item`, + `${prefixCls}-item-${mergedStatus}`, + { + [`${prefixCls}-item-custom`]: icon, + [`${prefixCls}-item-active`]: active, + [`${prefixCls}-item-disabled`]: disabled === true, + }, + className, + classNames.item, + ); let stepNode: React.ReactNode = ( -
-
+
+
-
{renderIconNode()}
+
+ {iconRender(data, { + index, + active, + })} +
{title} @@ -188,8 +154,8 @@ export default function Step(props: StepProps) {
); - if (render) { - stepNode = (render(stepNode) || null) as React.ReactElement; + if (itemRender) { + stepNode = (itemRender(stepNode) || null) as React.ReactElement; } return stepNode; diff --git a/src/Steps.tsx b/src/Steps.tsx index ece99a1..8a4037d 100644 --- a/src/Steps.tsx +++ b/src/Steps.tsx @@ -5,7 +5,7 @@ import type { Icons, Status } from './interface'; import type { StepProps } from './Step'; import Step from './Step'; -export type SemanticName = 'root'; +export type SemanticName = 'root' | 'item' | 'itemTitle' | 'itemContent' | 'itemIcon'; export type StepItem = { /** @deprecated Please use `content` instead. */ @@ -16,9 +16,7 @@ export type StepItem = { status?: Status; subTitle?: React.ReactNode; title?: React.ReactNode; - - onClick?: React.MouseEventHandler; -}; +} & Pick, 'onClick' | 'className' | 'style'>; export type StepIconRender = (info: { index: number; @@ -67,7 +65,8 @@ export interface StepsProps { iconRender?: ( item: StepItem, info: { - status: Status; + index: number; + active: boolean; }, ) => React.ReactNode; itemRender?: (item: StepItem, stepItem: React.ReactElement) => React.ReactNode; @@ -176,6 +175,11 @@ export default function Steps(props: StepsProps) { const prevStatus = statuses[index - 1]; const itemStatus = statuses[index]; + const data = { + ...item, + status: itemStatus, + }; + // if (!data.render && itemRender) { // data.render = (stepItem) => itemRender(data, stepItem); // } @@ -187,17 +191,16 @@ export default function Steps(props: StepsProps) { classNames={classNames} styles={styles} // Data - data={item} + data={data} status={itemStatus} prevStatus={prevStatus} active={stepIndex === current} index={stepIndex} - // stepNumber={stepNumber + 1} - // stepIndex={stepNumber} // Render key={stepIndex} progressDot={progressDot} iconRender={iconRender} + itemRender={itemRender} onClick={onChange && onStepClick} /> ); From 107634a2be97610ebf1f5998f55e31e4cf1306ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Mon, 28 Apr 2025 10:29:25 +0800 Subject: [PATCH 05/22] chore: render icon --- src/Step.tsx | 46 ++++++++++++++++++++++++++-------------------- src/Steps.tsx | 23 +++++++++++------------ 2 files changed, 37 insertions(+), 32 deletions(-) diff --git a/src/Step.tsx b/src/Step.tsx index faba17b..34aaefc 100644 --- a/src/Step.tsx +++ b/src/Step.tsx @@ -17,7 +17,7 @@ export interface StepProps { // data data: StepItem; - prevStatus?: Status; + nextStatus?: Status; active?: boolean; index: number; @@ -32,7 +32,7 @@ export interface StepProps { iconRender?: StepsProps['iconRender']; icon?: React.ReactNode; progressDot?: ProgressDotRender | boolean; - itemRender?: (stepItem: React.ReactElement) => React.ReactNode; + itemRender?: StepsProps['itemRender']; // Event onClick?: (index: number) => void; @@ -47,7 +47,7 @@ export default function Step(props: StepProps) { // data data, - prevStatus, + nextStatus, active, index, @@ -61,6 +61,8 @@ export default function Step(props: StepProps) { onClick, } = props; + const itemCls = `${prefixCls}-item`; + // ========================== Data ========================== const { onClick: onItemClick, @@ -76,8 +78,15 @@ export default function Step(props: StepProps) { style, ...restItemProps } = data; + const mergedContent = content ?? description; + const renderInfo = { + item: data, + index, + active, + }; + // ========================= Click ========================== const clickable = !!(onItemClick || onItemClick) && !disabled; @@ -108,12 +117,12 @@ export default function Step(props: StepProps) { const mergedStatus = status || 'wait'; const classString = cls( - `${prefixCls}-item`, - `${prefixCls}-item-${mergedStatus}`, + itemCls, + `${itemCls}-${mergedStatus}`, { - [`${prefixCls}-item-custom`]: icon, - [`${prefixCls}-item-active`]: active, - [`${prefixCls}-item-disabled`]: disabled === true, + [`${itemCls}-custom`]: icon, + [`${itemCls}-active`]: active, + [`${itemCls}-disabled`]: disabled === true, }, className, classNames.item, @@ -128,34 +137,31 @@ export default function Step(props: StepProps) { ...style, }} > -
-
-
- {iconRender(data, { - index, - active, - })} +
+
+
+ {iconRender(renderInfo)}
-
-
+
+
{title} {subTitle && (
{subTitle}
)}
- {description &&
{description}
} + {mergedContent &&
{mergedContent}
}
); if (itemRender) { - stepNode = (itemRender(stepNode) || null) as React.ReactElement; + stepNode = (itemRender(stepNode, renderInfo) || null) as React.ReactElement; } return stepNode; diff --git a/src/Steps.tsx b/src/Steps.tsx index 8a4037d..ed781de 100644 --- a/src/Steps.tsx +++ b/src/Steps.tsx @@ -5,7 +5,7 @@ import type { Icons, Status } from './interface'; import type { StepProps } from './Step'; import Step from './Step'; -export type SemanticName = 'root' | 'item' | 'itemTitle' | 'itemContent' | 'itemIcon'; +export type SemanticName = 'root' | 'item' | 'itemTitle' | 'itemContent' | 'itemIcon' | 'itemRail'; export type StepItem = { /** @deprecated Please use `content` instead. */ @@ -40,6 +40,12 @@ export type ProgressDotRender = ( }, ) => React.ReactNode; +export type RenderInfo = { + index: number; + active: boolean; + item: StepItem; +}; + export interface StepsProps { // style prefixCls?: string; @@ -62,14 +68,8 @@ export interface StepsProps { onChange?: (current: number) => void; // render - iconRender?: ( - item: StepItem, - info: { - index: number; - active: boolean; - }, - ) => React.ReactNode; - itemRender?: (item: StepItem, stepItem: React.ReactElement) => React.ReactNode; + iconRender?: (info: RenderInfo) => React.ReactNode; + itemRender?: (originNode: React.ReactElement, info: RenderInfo) => React.ReactNode; } export default function Steps(props: StepsProps) { @@ -172,8 +172,8 @@ export default function Steps(props: StepsProps) { // } // } - const prevStatus = statuses[index - 1]; const itemStatus = statuses[index]; + const nextStatus = statuses[index + 1]; const data = { ...item, @@ -192,8 +192,7 @@ export default function Steps(props: StepsProps) { styles={styles} // Data data={data} - status={itemStatus} - prevStatus={prevStatus} + nextStatus={nextStatus} active={stepIndex === current} index={stepIndex} // Render From 3911fe150781ceb4abf941f3926d6fb87da25840 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Mon, 28 Apr 2025 11:14:15 +0800 Subject: [PATCH 06/22] chore: next status --- src/Rail.tsx | 23 +++++++++++++ src/Step.tsx | 23 ++++--------- src/Steps.tsx | 90 +++++++++++++++--------------------------------- src/interface.ts | 5 --- 4 files changed, 56 insertions(+), 85 deletions(-) create mode 100644 src/Rail.tsx diff --git a/src/Rail.tsx b/src/Rail.tsx new file mode 100644 index 0000000..85503c3 --- /dev/null +++ b/src/Rail.tsx @@ -0,0 +1,23 @@ +import * as React from 'react'; +import cls from 'classnames'; +import type { Status, StepsProps } from './Steps'; + +export interface RailProps { + prefixCls: string; + classNames: StepsProps['classNames']; + styles: StepsProps['styles']; + status: Status; +} + +export default function Rail(props: RailProps) { + const { prefixCls, classNames, styles, status } = props; + const railCls = `${prefixCls}-rail`; + + // ============================= render ============================= + return ( +
+ ); +} diff --git a/src/Step.tsx b/src/Step.tsx index 34aaefc..e283995 100644 --- a/src/Step.tsx +++ b/src/Step.tsx @@ -2,12 +2,7 @@ import * as React from 'react'; import cls from 'classnames'; import KeyCode from '@rc-component/util/lib/KeyCode'; -import type { Status, Icons } from './interface'; -import type { ProgressDotRender, StepItem, StepsProps } from './Steps'; - -function isString(str: any): str is string { - return typeof str === 'string'; -} +import type { StepItem, StepsProps } from './Steps'; export interface StepProps { // style @@ -17,7 +12,6 @@ export interface StepProps { // data data: StepItem; - nextStatus?: Status; active?: boolean; index: number; @@ -31,7 +25,6 @@ export interface StepProps { // render iconRender?: StepsProps['iconRender']; icon?: React.ReactNode; - progressDot?: ProgressDotRender | boolean; itemRender?: StepsProps['itemRender']; // Event @@ -47,13 +40,11 @@ export default function Step(props: StepProps) { // data data, - nextStatus, active, index, // render - progressDot, itemRender, iconRender, @@ -139,22 +130,20 @@ export default function Step(props: StepProps) { >
-
- {iconRender(renderInfo)} -
-
-
+
{iconRender?.(renderInfo)}
+
+
{title} {subTitle && (
{subTitle}
)}
- {mergedContent &&
{mergedContent}
} + {mergedContent &&
{mergedContent}
}
diff --git a/src/Steps.tsx b/src/Steps.tsx index ed781de..e9e9ceb 100644 --- a/src/Steps.tsx +++ b/src/Steps.tsx @@ -1,9 +1,10 @@ /* eslint react/no-did-mount-set-state: 0, react/prop-types: 0 */ import cls from 'classnames'; import React from 'react'; -import type { Icons, Status } from './interface'; -import type { StepProps } from './Step'; import Step from './Step'; +import Rail from './Rail'; + +export type Status = 'error' | 'process' | 'finish' | 'wait'; export type SemanticName = 'root' | 'item' | 'itemTitle' | 'itemContent' | 'itemIcon' | 'itemRail'; @@ -28,18 +29,6 @@ export type StepIconRender = (info: { node: React.ReactNode; }) => React.ReactNode; -export type ProgressDotRender = ( - iconDot: React.ReactNode, - info: { - index: number; - status: Status; - title: React.ReactNode; - // @deprecated Please use `content` instead. - description: React.ReactNode; - content: React.ReactNode; - }, -) => React.ReactNode; - export type RenderInfo = { index: number; active: boolean; @@ -58,7 +47,6 @@ export interface StepsProps { // layout orientation?: 'horizontal' | 'vertical'; labelPlacement?: 'horizontal' | 'vertical'; - progressDot?: ProgressDotRender | boolean; // data status?: Status; @@ -78,14 +66,13 @@ export default function Steps(props: StepsProps) { prefixCls = 'rc-steps', style, className, - classNames, - styles, + classNames = {}, + styles = {}, rootClassName, // layout orientation = 'horizontal', labelPlacement = 'horizontal', - progressDot, // data status = 'process', @@ -102,21 +89,14 @@ export default function Steps(props: StepsProps) { } = props; // ============================= layout ============================= - const [mergedOrientation, mergeLabelPlacement] = React.useMemo(() => { - const nextOrientation = orientation === 'vertical' ? 'vertical' : 'horizontal'; - const nextLabelPlacement = progressDot ? 'vertical' : labelPlacement; - - return [nextOrientation, nextLabelPlacement] as const; - }, [orientation, progressDot, labelPlacement]); + const mergedOrientation = orientation === 'vertical' ? 'vertical' : 'horizontal'; + const mergeLabelPlacement = labelPlacement === 'vertical' ? 'vertical' : 'horizontal'; // ============================= styles ============================= const classString = cls( prefixCls, `${prefixCls}-${mergedOrientation}`, - { - [`${prefixCls}-label-${mergeLabelPlacement}`]: mergedOrientation === 'horizontal', - [`${prefixCls}-dot`]: !!progressDot, - }, + `${prefixCls}-label-${mergeLabelPlacement}`, rootClassName, className, classNames.root, @@ -159,49 +139,33 @@ export default function Steps(props: StepsProps) { // data.className = `${prefixCls}-next-error`; // } - // const { status: currentStatus = status } = item; - // const { status: prevStatus = currentStatus } = prevItem || {}; - - // if (!data.status) { - // if (stepNumber === current) { - // data.status = status; - // } else if (stepNumber < current) { - // data.status = 'finish'; - // } else { - // data.status = 'wait'; - // } - // } - const itemStatus = statuses[index]; - const nextStatus = statuses[index + 1]; const data = { ...item, status: itemStatus, }; - // if (!data.render && itemRender) { - // data.render = (stepItem) => itemRender(data, stepItem); - // } - return ( - + + {index !== 0 && ( + + )} + + ); }; diff --git a/src/interface.ts b/src/interface.ts index 54bce1e..8b13789 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -1,6 +1 @@ -export type Status = 'error' | 'process' | 'finish' | 'wait'; -export interface Icons { - finish: React.ReactNode; - error: React.ReactNode; -} From 117515d9398135110e8ac4cab2813e800f30103a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Mon, 28 Apr 2025 14:38:29 +0800 Subject: [PATCH 07/22] chore: line shify --- assets/index.less | 135 ++++++++++++++++++------------------ assets/inline.less | 33 +-------- assets/label-placement.less | 6 +- assets/nav.less | 11 --- assets/progress-dot.less | 10 --- assets/small.less | 11 +-- assets/vertical.less | 20 +----- src/Step.tsx | 48 ++++++++----- src/Steps.tsx | 18 ++--- 9 files changed, 110 insertions(+), 182 deletions(-) diff --git a/assets/index.less b/assets/index.less index 4f8ebd8..79bea8a 100644 --- a/assets/index.less +++ b/assets/index.less @@ -17,32 +17,18 @@ display: inline-block; vertical-align: top; flex: 1; - overflow: hidden; - - &-container[role='button'] { - cursor: pointer; - transition: opacity .3s; - - &:hover { - opacity: 0.7; - } - } + // overflow: hidden; &:last-child { flex: none; } - &:last-child &-tail, &:last-child &-title:after { display: none; } - &-container { - display: inline-block; - } - &-icon, - &-content { + &-section { display: inline-block; vertical-align: top; } @@ -55,8 +41,9 @@ text-align: center; border-radius: 26px; font-size: 14px; - margin-right: 8px; - transition: background-color .3s, border-color .3s; + transition: + background-color 0.3s, + border-color 0.3s; > .@{stepsPrefixClass}-icon { line-height: 1; @@ -72,49 +59,20 @@ } } - &-tail { - position: absolute; - left: 0; - width: 100%; - top: 12px; - padding: 0 10px; - &:after { - content: ''; - display: inline-block; - background: @wait-tail-color; - height: 1px; - border-radius: 1px; - width: 100%; - transition: background .3s; - } - } - &-content { + &-section { margin-top: 3px; } &-title { font-size: 14px; - margin-bottom: 4px; color: #666; font-weight: bold; display: inline-block; - padding-right: 10px; position: relative; - &:after { - content: ''; - height: 1px; - width: 1000px; - background: @wait-tail-color; - display: block; - position: absolute; - top: 0.55em; - left: 100%; - } } &-subtitle { font-size: 12px; display: inline-block; color: #999; - margin-left: 8px; } &-description { font-size: 12px; @@ -142,9 +100,6 @@ &:last-child { margin-right: 0; } - &-tail { - display: none; - } &-description { max-width: @stepDescriptionMaxWidth; } @@ -152,10 +107,10 @@ } .step-item-status(@status) { - @icon-color: "@{status}-icon-color"; - @title-color: "@{status}-title-color"; - @description-color: "@{status}-description-color"; - @tail-color: "@{status}-tail-color"; + @icon-color: '@{status}-icon-color'; + @title-color: '@{status}-title-color'; + @description-color: '@{status}-description-color'; + @tail-color: '@{status}-tail-color'; &-@{status} &-icon { border-color: @@icon-color; background-color: #fff; @@ -168,22 +123,68 @@ } &-@{status} &-title { color: @@title-color; - &:after { - background-color: @@tail-color; - } } &-@{status} &-description { color: @@description-color; } - &-@{status} &-tail:after { - background-color: @@tail-color; +} + +// @import 'custom-icon'; +// @import 'small'; +// @import 'vertical'; +// @import 'label-placement'; +// @import 'progress-dot'; +// @import 'nav'; +// @import 'inline'; + +// ======================= Horizontal ======================= +.verticalFlex() { + display: flex; + flex-direction: column; + align-items: center; +} + +.@{stepsPrefixClass} { + .@{stepsPrefixClass}-item { + &-header { + display: flex; + gap: 8px; + align-items: center; + } } } -@import 'custom-icon'; -@import 'small'; -@import 'vertical'; -@import 'label-placement'; -@import 'progress-dot'; -@import 'nav'; -@import 'inline'; +.@{stepsPrefixClass}-horizontal { + .@{stepsPrefixClass}-item { + flex: 1; + position: relative; + + &-rail { + height: 1px; + background: @process-tail-color; + } + } + + // Label Vertical + &.@{stepsPrefixClass}-label-vertical { + .@{stepsPrefixClass}-item { + .verticalFlex(); + padding-inline: 8px; + + &-section { + .verticalFlex(); + } + + &-rail { + position: absolute; + top: 13px; + left: calc(50% + 13px); + width: 100%; + } + } + } +} + +// ======================== Vertical ======================== +.@{stepsPrefixClass}-vertical { +} diff --git a/assets/inline.less b/assets/inline.less index 1bbe6a6..519e990 100644 --- a/assets/inline.less +++ b/assets/inline.less @@ -7,18 +7,6 @@ .@{stepsPrefixClass}-item { flex: none; - &-container { - padding: 9px 4px 0; - margin: 0 2px; - border-radius: 4px; - cursor: pointer; - &:hover { - background: rgba(0, 0, 0, 0.04); - } - &[role='button']:hover { - opacity: 1; - } - } &-icon { width: 6px; @@ -32,7 +20,7 @@ } } - &-content { + &-section { width: auto; margin-top: 7px; } @@ -47,26 +35,7 @@ display: none; } - &-tail { - margin-left: 0; - top: 11px; - &:after { - height: 1px; - } - } - &:first-child .@{stepsPrefixClass}-item-tail{ - width: 50%; - margin-left: 50%; - } - &:last-child .@{stepsPrefixClass}-item-tail{ - display: block; - width: 50%; - } - &-finish { - .@{stepsPrefixClass}-item-tail:after { - background-color: @process-tail-color; - } .@{stepsPrefixClass}-item-icon .@{stepsPrefixClass}-icon .@{stepsPrefixClass}-icon-dot { background-color: @process-tail-color; } diff --git a/assets/label-placement.less b/assets/label-placement.less index 76f07c9..6de6495 100644 --- a/assets/label-placement.less +++ b/assets/label-placement.less @@ -3,11 +3,7 @@ .@{stepsPrefixClass}-label-vertical { .@{stepsPrefixClass}-item { overflow: visible; - &-tail { - padding: 0px 24px; - margin-left: 48px; - } - &-content { + &-section { display: block; text-align: center; margin-top: 8px; diff --git a/assets/nav.less b/assets/nav.less index d7fc9f4..b5a257c 100644 --- a/assets/nav.less +++ b/assets/nav.less @@ -13,11 +13,6 @@ text-align: center; overflow: visible; - &-container { - text-align: left; - padding-bottom: 8px; - outline: none; - } &-title { max-width: @stepNavContentMaxWidth; @@ -53,11 +48,5 @@ margin-left: -8px; } - &-active { - .@{stepsPrefixClass}-item-container { - padding-bottom: 5px; - border-bottom: 3px solid @primary-color; - } - } } } diff --git a/assets/progress-dot.less b/assets/progress-dot.less index f4a6a7b..1c3dc20 100644 --- a/assets/progress-dot.less +++ b/assets/progress-dot.less @@ -2,16 +2,6 @@ .@{stepsPrefixClass}-dot { .@{stepsPrefixClass}-item { - &-tail { - width: 100%; - top: 1px; - margin: 0 0 0 @stepDescriptionMaxWidth / 2; - padding: 0; - - &:after { - height: 3px; - } - } &-icon { padding-right: 0; width: 5px; diff --git a/assets/small.less b/assets/small.less index d5b4661..95e281f 100644 --- a/assets/small.less +++ b/assets/small.less @@ -16,7 +16,7 @@ top: -1px; } } - .@{stepsPrefixClass}-item-content { + .@{stepsPrefixClass}-item-section { margin-top: 0; } .@{stepsPrefixClass}-item-title { @@ -29,15 +29,6 @@ font-size: 12px; color: #999; } - .@{stepsPrefixClass}-item-tail { - top: 8px; - padding: 0 8px; - &:after { - height: 1px; - border-radius: 1px; - width: 100%; - } - } .@{stepsPrefixClass}-item-custom .@{stepsPrefixClass}-item-icon { width: inherit; diff --git a/assets/vertical.less b/assets/vertical.less index 6d669b8..0c2e3ec 100644 --- a/assets/vertical.less +++ b/assets/vertical.less @@ -11,7 +11,7 @@ margin-right: 16px; } } - &-content { + &-section { min-height: 48px; overflow: hidden; display: block; @@ -25,27 +25,9 @@ &-description { padding-bottom: 12px; } - &-tail { - position: absolute; - left: 13px; - top: 0; - height: 100%; - width: 1px; - padding: 30px 0 4px 0; - &:after { - height: 100%; - width: 1px; - } - } } &.@{stepsPrefixClass}-small { - .@{stepsPrefixClass}-item-tail { - position: absolute; - left: 9px; - top: 0; - padding: 22px 0 4px 0; - } .@{stepsPrefixClass}-item-title { line-height: 18px; } diff --git a/src/Step.tsx b/src/Step.tsx index e283995..f866977 100644 --- a/src/Step.tsx +++ b/src/Step.tsx @@ -2,7 +2,8 @@ import * as React from 'react'; import cls from 'classnames'; import KeyCode from '@rc-component/util/lib/KeyCode'; -import type { StepItem, StepsProps } from './Steps'; +import type { Status, StepItem, StepsProps } from './Steps'; +import Rail from './Rail'; export interface StepProps { // style @@ -12,9 +13,10 @@ export interface StepProps { // data data: StepItem; - + nextStatus?: Status; active?: boolean; index: number; + last: boolean; // stepIndex?: number; // stepNumber?: number; @@ -40,7 +42,8 @@ export default function Step(props: StepProps) { // data data, - + last, + nextStatus, active, index, @@ -122,29 +125,36 @@ export default function Step(props: StepProps) { let stepNode: React.ReactNode = (
-
-
-
{iconRender?.(renderInfo)}
-
-
- {title} - {subTitle && ( -
- {subTitle} -
- )} -
- {mergedContent &&
{mergedContent}
} +
{iconRender?.(renderInfo)}
+
+
+
{title}
+ {subTitle && ( +
+ {subTitle} +
+ )} + + {!last && ( + + )}
+ {mergedContent &&
{mergedContent}
}
); diff --git a/src/Steps.tsx b/src/Steps.tsx index e9e9ceb..f12f4b4 100644 --- a/src/Steps.tsx +++ b/src/Steps.tsx @@ -89,8 +89,10 @@ export default function Steps(props: StepsProps) { } = props; // ============================= layout ============================= - const mergedOrientation = orientation === 'vertical' ? 'vertical' : 'horizontal'; - const mergeLabelPlacement = labelPlacement === 'vertical' ? 'vertical' : 'horizontal'; + const isVertical = orientation === 'vertical'; + const mergedOrientation = isVertical ? 'vertical' : 'horizontal'; + const mergeLabelPlacement = + !isVertical && labelPlacement === 'vertical' ? 'vertical' : 'horizontal'; // ============================= styles ============================= const classString = cls( @@ -134,12 +136,8 @@ export default function Steps(props: StepsProps) { const renderStep = (item: StepItem, index: number) => { const stepIndex = initial + index; - // fix tail color - // if (status === 'error' && index === current - 1) { - // data.className = `${prefixCls}-next-error`; - // } - const itemStatus = statuses[index]; + const nextStatus = statuses[index + 1]; const data = { ...item, @@ -148,9 +146,9 @@ export default function Steps(props: StepsProps) { return ( - {index !== 0 && ( + {/* {index !== 0 && ( - )} + )} */} Date: Mon, 28 Apr 2025 14:56:08 +0800 Subject: [PATCH 08/22] chore: style ellipsis --- assets/index.less | 39 +++++++++++++++++++++++++++++++++++---- src/Step.tsx | 2 +- src/Steps.tsx | 39 +++++++++++++++++---------------------- 3 files changed, 53 insertions(+), 27 deletions(-) diff --git a/assets/index.less b/assets/index.less index 79bea8a..3a3284b 100644 --- a/assets/index.less +++ b/assets/index.less @@ -34,6 +34,7 @@ } &-icon { + flex: none; border: 1px solid @wait-icon-color; width: 26px; height: 26px; @@ -96,10 +97,6 @@ .@{stepsPrefixClass}-horizontal:not(.@{stepsPrefixClass}-label-vertical) { .@{stepsPrefixClass}-item { - margin-right: 10px; - &:last-child { - margin-right: 0; - } &-description { max-width: @stepDescriptionMaxWidth; } @@ -146,11 +143,24 @@ .@{stepsPrefixClass} { .@{stepsPrefixClass}-item { + &-section { + min-width: 0; + } + &-header { display: flex; gap: 8px; align-items: center; } + + // Ellipsis + &-title, + &-subtitle, + &-description { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } } } @@ -158,6 +168,7 @@ .@{stepsPrefixClass}-item { flex: 1; position: relative; + min-width: 0; &-rail { height: 1px; @@ -183,6 +194,26 @@ } } } + + // Label Horizontal + &.@{stepsPrefixClass}-label-horizontal { + .@{stepsPrefixClass}-item { + display: flex; + + &:last-child { + flex: none; + } + + &-section { + flex: 1; + } + + &-rail { + flex: 1; + min-width: 0; + } + } + } } // ======================== Vertical ======================== diff --git a/src/Step.tsx b/src/Step.tsx index f866977..bfceb9c 100644 --- a/src/Step.tsx +++ b/src/Step.tsx @@ -150,7 +150,7 @@ export default function Step(props: StepProps) { prefixCls={itemCls} classNames={classNames} styles={styles} - status={mergedStatus} + status={nextStatus} /> )}
diff --git a/src/Steps.tsx b/src/Steps.tsx index f12f4b4..b61f3d6 100644 --- a/src/Steps.tsx +++ b/src/Steps.tsx @@ -2,7 +2,6 @@ import cls from 'classnames'; import React from 'react'; import Step from './Step'; -import Rail from './Rail'; export type Status = 'error' | 'process' | 'finish' | 'wait'; @@ -145,27 +144,23 @@ export default function Steps(props: StepsProps) { }; return ( - - {/* {index !== 0 && ( - - )} */} - - + ); }; From fe93120ca325181bb52ee8557963c2f082507a87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Mon, 28 Apr 2025 15:22:36 +0800 Subject: [PATCH 09/22] chore: bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b62ac94..791ef26 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@rc-component/steps", - "version": "0.0.0", + "version": "0.0.0-alpha.0", "description": "steps ui component for react", "keywords": [ "react", From 10ec568afb8e2bdfc7c5af9034ac306962398acd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Wed, 30 Apr 2025 15:14:59 +0800 Subject: [PATCH 10/22] chore: fix click logic --- src/Step.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Step.tsx b/src/Step.tsx index bfceb9c..80161c5 100644 --- a/src/Step.tsx +++ b/src/Step.tsx @@ -82,7 +82,7 @@ export default function Step(props: StepProps) { }; // ========================= Click ========================== - const clickable = !!(onItemClick || onItemClick) && !disabled; + const clickable = !!(onClick || onItemClick) && !disabled; const accessibilityProps: { role?: string; From 59b6cf0a37a04b1bb10f68929b4dcceac9921087 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Wed, 30 Apr 2025 15:18:02 +0800 Subject: [PATCH 11/22] chore: bump version to 0.0.0-alpha.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 791ef26..09ab880 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@rc-component/steps", - "version": "0.0.0-alpha.0", + "version": "0.0.0-alpha.1", "description": "steps ui component for react", "keywords": [ "react", From 8f47e5855816f52a1475a2494a2a85c75f7e5468 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Wed, 7 May 2025 16:37:24 +0800 Subject: [PATCH 12/22] chore: more layout --- src/Step.tsx | 47 ++++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/src/Step.tsx b/src/Step.tsx index 80161c5..de133d1 100644 --- a/src/Step.tsx +++ b/src/Step.tsx @@ -125,36 +125,37 @@ export default function Step(props: StepProps) { let stepNode: React.ReactNode = (
-
{iconRender?.(renderInfo)}
-
-
-
{title}
- {subTitle && ( -
- {subTitle} -
- )} - - {!last && ( - - )} +
+
{iconRender?.(renderInfo)}
+
+
+
{title}
+ {subTitle && ( +
+ {subTitle} +
+ )} + + {!last && ( + + )} +
+ {mergedContent &&
{mergedContent}
}
- {mergedContent &&
{mergedContent}
}
); From 1d903d28b3b67bf88a1177a168c42c6d148c8647 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Wed, 7 May 2025 16:38:32 +0800 Subject: [PATCH 13/22] chore: bump version to 0.0.0-alpha.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 09ab880..34c7ec0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@rc-component/steps", - "version": "0.0.0-alpha.1", + "version": "0.0.0-alpha.2", "description": "steps ui component for react", "keywords": [ "react", From 701e7505660bc0a96f0fd64eef6df3edf002e10f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Thu, 8 May 2025 18:12:43 +0800 Subject: [PATCH 14/22] chore: add itemWrapperRender --- src/Step.tsx | 53 ++++++++++++++++++++++++++------------------------- src/Steps.tsx | 3 +++ 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/src/Step.tsx b/src/Step.tsx index de133d1..e59a5f7 100644 --- a/src/Step.tsx +++ b/src/Step.tsx @@ -28,6 +28,7 @@ export interface StepProps { iconRender?: StepsProps['iconRender']; icon?: React.ReactNode; itemRender?: StepsProps['itemRender']; + itemWrapperRender?: StepsProps['itemWrapperRender']; // Event onClick?: (index: number) => void; @@ -50,6 +51,7 @@ export default function Step(props: StepProps) { // render itemRender, iconRender, + itemWrapperRender, // events onClick, @@ -122,6 +124,30 @@ export default function Step(props: StepProps) { classNames.item, ); + const wrapperNode = ( +
+
{iconRender?.(renderInfo)}
+
+
+
{title}
+ {subTitle && ( +
+ {subTitle} +
+ )} + + {!last && ( + + )} +
+ {mergedContent &&
{mergedContent}
} +
+
+ ); + let stepNode: React.ReactNode = (
-
-
{iconRender?.(renderInfo)}
-
-
-
{title}
- {subTitle && ( -
- {subTitle} -
- )} - - {!last && ( - - )} -
- {mergedContent &&
{mergedContent}
} -
-
+ {itemWrapperRender ? itemWrapperRender(wrapperNode) : wrapperNode}
); diff --git a/src/Steps.tsx b/src/Steps.tsx index b61f3d6..e7dea27 100644 --- a/src/Steps.tsx +++ b/src/Steps.tsx @@ -57,6 +57,7 @@ export interface StepsProps { // render iconRender?: (info: RenderInfo) => React.ReactNode; itemRender?: (originNode: React.ReactElement, info: RenderInfo) => React.ReactNode; + itemWrapperRender?: (originNode: React.ReactElement) => React.ReactNode; } export default function Steps(props: StepsProps) { @@ -83,6 +84,7 @@ export default function Steps(props: StepsProps) { // render iconRender, itemRender, + itemWrapperRender, ...restProps } = props; @@ -159,6 +161,7 @@ export default function Steps(props: StepsProps) { // Render iconRender={iconRender} itemRender={itemRender} + itemWrapperRender={itemWrapperRender} onClick={onChange && onStepClick} /> ); From 709f517585040672840ab0d1004082d593c60c77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Thu, 8 May 2025 18:13:22 +0800 Subject: [PATCH 15/22] chore: bump version to 0.0.0-alpha.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 34c7ec0..21bd4c1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@rc-component/steps", - "version": "0.0.0-alpha.2", + "version": "0.0.0-alpha.3", "description": "steps ui component for react", "keywords": [ "react", From bc6e3c2f17bb5858f4d82f826cb5586d1624fd2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Fri, 9 May 2025 16:31:00 +0800 Subject: [PATCH 16/22] chore: update render logic --- src/Step.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Step.tsx b/src/Step.tsx index e59a5f7..e4a6ed6 100644 --- a/src/Step.tsx +++ b/src/Step.tsx @@ -78,7 +78,10 @@ export default function Step(props: StepProps) { const mergedContent = content ?? description; const renderInfo = { - item: data, + item: { + ...data, + content: mergedContent, + }, index, active, }; From 76c5882731386a986b76d42ba47b28df52202e41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Fri, 9 May 2025 16:32:09 +0800 Subject: [PATCH 17/22] chore: bump version to 0.0.0-alpha.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 21bd4c1..e6078b6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@rc-component/steps", - "version": "0.0.0-alpha.3", + "version": "0.0.0-alpha.4", "description": "steps ui component for react", "keywords": [ "react", From 1d331024b3f4a773aa3c287f32d80b2f91c8688f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Fri, 9 May 2025 17:09:19 +0800 Subject: [PATCH 18/22] chore: link with semantic content --- src/Step.tsx | 30 +++++++++++++++++++++++------- src/Steps.tsx | 12 +++++++++++- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/src/Step.tsx b/src/Step.tsx index e4a6ed6..1280229 100644 --- a/src/Step.tsx +++ b/src/Step.tsx @@ -128,15 +128,24 @@ export default function Step(props: StepProps) { ); const wrapperNode = ( -
-
{iconRender?.(renderInfo)}
-
-
-
{title}
+
+
+ {iconRender?.(renderInfo)} +
+
+
+
+ {title} +
{subTitle && (
{subTitle}
@@ -146,7 +155,14 @@ export default function Step(props: StepProps) { )}
- {mergedContent &&
{mergedContent}
} + {mergedContent && ( +
+ {mergedContent} +
+ )}
); diff --git a/src/Steps.tsx b/src/Steps.tsx index e7dea27..756f55e 100644 --- a/src/Steps.tsx +++ b/src/Steps.tsx @@ -5,7 +5,17 @@ import Step from './Step'; export type Status = 'error' | 'process' | 'finish' | 'wait'; -export type SemanticName = 'root' | 'item' | 'itemTitle' | 'itemContent' | 'itemIcon' | 'itemRail'; +export type SemanticName = + | 'root' + | 'item' + | 'itemWrapper' + | 'itemHeader' + | 'itemTitle' + | 'itemSubtitle' + | 'itemSection' + | 'itemContent' + | 'itemIcon' + | 'itemRail'; export type StepItem = { /** @deprecated Please use `content` instead. */ From 34398921ad98e333e9b9f6dce0b389258a4d564c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Fri, 9 May 2025 17:11:40 +0800 Subject: [PATCH 19/22] chore: bump version to 0.0.0-alpha.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e6078b6..d5fb883 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@rc-component/steps", - "version": "0.0.0-alpha.4", + "version": "0.0.0-alpha.5", "description": "steps ui component for react", "keywords": [ "react", From 4f3ac5d1f30e5b39f590ea84a677437da3a9795a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Fri, 9 May 2025 23:03:18 +0800 Subject: [PATCH 20/22] test: add test case --- src/Step.tsx | 2 +- src/index.ts | 3 +- tests/__snapshots__/index.test.tsx.snap | 1753 +++++++---------------- tests/index.test.tsx | 184 +-- tests/semantic.test.tsx | 76 + 5 files changed, 640 insertions(+), 1378 deletions(-) create mode 100644 tests/semantic.test.tsx diff --git a/src/Step.tsx b/src/Step.tsx index 1280229..a4f611b 100644 --- a/src/Step.tsx +++ b/src/Step.tsx @@ -157,7 +157,7 @@ export default function Step(props: StepProps) {
{mergedContent && (
{mergedContent} diff --git a/src/index.ts b/src/index.ts index 738dae3..8c5e175 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,6 @@ -import Steps from './Steps'; +import Steps, { type StepsProps } from './Steps'; import Step from './Step'; export { Step }; +export type { StepsProps }; export default Steps; diff --git a/tests/__snapshots__/index.test.tsx.snap b/tests/__snapshots__/index.test.tsx.snap index b44aebb..f663d7a 100644 --- a/tests/__snapshots__/index.test.tsx.snap +++ b/tests/__snapshots__/index.test.tsx.snap @@ -8,27 +8,25 @@ exports[`Steps render renders correctly 1`] = ` class="rc-steps-item rc-steps-item-process rc-steps-item-active" >
-
- - 1 - -
+ />
- 已完成 +
+ 已完成 +
+
@@ -37,27 +35,25 @@ exports[`Steps render renders correctly 1`] = ` class="rc-steps-item rc-steps-item-wait" >
-
- - 2 - -
+ />
- 进行中 +
+ 进行中 +
+
@@ -66,27 +62,25 @@ exports[`Steps render renders correctly 1`] = ` class="rc-steps-item rc-steps-item-wait" >
-
- - 3 - -
+ />
- 待运行 +
+ 待运行 +
+
@@ -95,27 +89,22 @@ exports[`Steps render renders correctly 1`] = ` class="rc-steps-item rc-steps-item-wait" >
-
- - 4 - -
+ />
- 待运行 +
+ 待运行 +
@@ -131,25 +120,25 @@ exports[`Steps render renders current correctly 1`] = ` class="rc-steps-item rc-steps-item-finish" >
-
- -
+ />
- 已完成 +
+ 已完成 +
+
@@ -158,25 +147,25 @@ exports[`Steps render renders current correctly 1`] = ` class="rc-steps-item rc-steps-item-finish" >
-
- -
+ />
- 进行中 +
+ 进行中 +
+
@@ -185,27 +174,25 @@ exports[`Steps render renders current correctly 1`] = ` class="rc-steps-item rc-steps-item-process rc-steps-item-active" >
-
- - 3 - -
+ />
- 待运行 +
+ 待运行 +
+
@@ -214,27 +201,22 @@ exports[`Steps render renders current correctly 1`] = ` class="rc-steps-item rc-steps-item-wait" >
-
- - 4 - -
+ />
- 待运行 +
+ 待运行 +
@@ -250,27 +232,25 @@ exports[`Steps render renders labelPlacement correctly 1`] = ` class="rc-steps-item rc-steps-item-process rc-steps-item-active" >
-
- - 1 - -
+ />
- 已完成 +
+ 已完成 +
+
@@ -279,27 +259,25 @@ exports[`Steps render renders labelPlacement correctly 1`] = ` class="rc-steps-item rc-steps-item-wait" >
-
- - 2 - -
+ />
- 进行中 +
+ 进行中 +
+
@@ -308,27 +286,25 @@ exports[`Steps render renders labelPlacement correctly 1`] = ` class="rc-steps-item rc-steps-item-wait" >
-
- - 3 - -
+ />
- 待运行 +
+ 待运行 +
+
@@ -337,27 +313,22 @@ exports[`Steps render renders labelPlacement correctly 1`] = ` class="rc-steps-item rc-steps-item-wait" >
-
- - 4 - -
+ />
- 待运行 +
+ 待运行 +
@@ -367,35 +338,31 @@ exports[`Steps render renders labelPlacement correctly 1`] = ` exports[`Steps render renders progressDot correctly 1`] = `
-
- - - -
+ />
- 已完成 +
+ 已完成 +
+
@@ -404,29 +371,25 @@ exports[`Steps render renders progressDot correctly 1`] = ` class="rc-steps-item rc-steps-item-wait" >
-
- - - -
+ />
- 进行中 +
+ 进行中 +
+
@@ -435,29 +398,25 @@ exports[`Steps render renders progressDot correctly 1`] = ` class="rc-steps-item rc-steps-item-wait" >
-
- - - -
+ />
- 待运行 +
+ 待运行 +
+
@@ -466,29 +425,22 @@ exports[`Steps render renders progressDot correctly 1`] = ` class="rc-steps-item rc-steps-item-wait" >
-
- - - -
+ />
- 待运行 +
+ 待运行 +
@@ -498,35 +450,31 @@ exports[`Steps render renders progressDot correctly 1`] = ` exports[`Steps render renders progressDot function correctly 1`] = `
-
- - - a - - -
+ />
- 已完成 +
+ 已完成 +
+
@@ -535,29 +483,25 @@ exports[`Steps render renders progressDot function correctly 1`] = ` class="rc-steps-item rc-steps-item-wait" >
-
- - - a - - -
+ />
- 进行中 +
+ 进行中 +
+
@@ -566,29 +510,25 @@ exports[`Steps render renders progressDot function correctly 1`] = ` class="rc-steps-item rc-steps-item-wait" >
-
- - - a - - -
+ />
- 待运行 +
+ 待运行 +
+
@@ -597,29 +537,22 @@ exports[`Steps render renders progressDot function correctly 1`] = ` class="rc-steps-item rc-steps-item-wait" >
-
- - - a - - -
+ />
- 待运行 +
+ 待运行 +
@@ -635,52 +568,52 @@ exports[`Steps render renders status correctly 1`] = ` class="rc-steps-item rc-steps-item-finish" >
-
- -
+ />
- 已完成 +
+ 已完成 +
+
-
- -
+ />
- 进行中 +
+ 进行中 +
+
@@ -689,25 +622,25 @@ exports[`Steps render renders status correctly 1`] = ` class="rc-steps-item rc-steps-item-error rc-steps-item-active" >
-
- -
+ />
- 待运行 +
+ 待运行 +
+
@@ -716,27 +649,22 @@ exports[`Steps render renders status correctly 1`] = ` class="rc-steps-item rc-steps-item-wait" >
-
- - 4 - -
+ />
- 待运行 +
+ 待运行 +
@@ -752,27 +680,25 @@ exports[`Steps render renders step with description 1`] = ` class="rc-steps-item rc-steps-item-process rc-steps-item-active" >
- - 1 - -
-
- 已完成 +
+ 已完成 +
+
-
- - 2 - -
+ />
- 进行中 +
+ 进行中 +
+
-
- - 3 - -
+ />
- 待运行 +
+ 待运行 +
+
-
- - 4 - -
+ />
- 待运行 +
+ 待运行 +
-
- - 1 - -
+ />
- 已完成 +
+ 已完成 +
+
-
- - 2 - -
+ />
- 进行中 +
+ 进行中 +
+
-
- - 3 - -
+ />
- 待运行 +
+ 待运行 +
+
-
- -
+ />
- 待运行 +
+ 待运行 +
`; -exports[`Steps render renders step with tailContent 1`] = ` +exports[`Steps render renders stepIcon function correctly 1`] = `
@@ -1036,34 +944,25 @@ exports[`Steps render renders step with tailContent 1`] = ` class="rc-steps-item rc-steps-item-process rc-steps-item-active" >
-
- text -
- - 1 - -
+ />
- 已完成 -
-
- xx +
+ 已完成 +
+
@@ -1072,36 +971,25 @@ exports[`Steps render renders step with tailContent 1`] = ` class="rc-steps-item rc-steps-item-wait" >
-
-
- content -
-
- - 2 - -
+ />
- 进行中 -
-
- xx +
+ 进行中 +
+
@@ -1110,34 +998,25 @@ exports[`Steps render renders step with tailContent 1`] = ` class="rc-steps-item rc-steps-item-wait" >
-
- 3 -
- - 3 - -
+ />
- 待运行 -
-
- xx +
+ 待运行 +
+
@@ -1146,34 +1025,22 @@ exports[`Steps render renders step with tailContent 1`] = ` class="rc-steps-item rc-steps-item-wait" >
-
- text -
- - 4 - -
+ />
- 待运行 -
-
- xx +
+ 待运行 +
@@ -1181,121 +1048,112 @@ exports[`Steps render renders step with tailContent 1`] = `
`; -exports[`Steps render renders step with type inline 1`] = ` +exports[`Steps render renders vertical correctly 1`] = `
-
- - - -
+ />
- Step 1 -
-
- This is a description. +
+ 已完成 +
+
-
- - - -
+ />
- Step 2 -
-
- This is a description. +
+ 进行中 +
+
- - + 待运行 +
+
- +
+
+
+
+
+
- Step 3 -
-
- This is a description. +
+ 待运行 +
@@ -1303,464 +1161,103 @@ exports[`Steps render renders step with type inline 1`] = `
`; -exports[`Steps render renders step with type navigation 1`] = ` +exports[`Steps render renders with falsy children 1`] = `
-
- -
+ />
- Step 1
- 剩余 00:00:05 超长隐藏 + 已完成
+
- This is a description. + xx
-
- - 2 - -
+ />
- Step 2 +
+ 进行中 +
+
+ 剩余 00:00:07 +
+
- This is a description. + xx
-
- - 3 - -
+ />
- Step 3 -
-
- This is a description. -
-
-
-
-
-`; - -exports[`Steps render renders stepIcon function correctly 1`] = ` -
-
-
-
-
- - a - -
-
-
- 已完成 -
-
-
-
-
-
-
-
- - a - -
-
-
- 进行中 -
-
-
-
-
-
-
-
- - a - -
-
-
- 待运行 -
-
-
-
-
-
-
-
- - a - -
-
-
- 待运行 -
-
-
-
-
-`; - -exports[`Steps render renders vertical correctly 1`] = ` -
-
-
-
-
- - 1 - -
-
-
- 已完成 -
-
-
-
-
-
-
-
- - 2 - -
-
-
- 进行中 -
-
-
-
-
-
-
-
- - 3 - -
-
-
- 待运行 -
-
-
-
-
-
-
-
- - 4 - -
-
-
- 待运行 -
-
-
-
-
-`; - -exports[`Steps render renders with falsy children 1`] = ` -
-
-
-
-
- - 1 - -
-
-
- 已完成 -
-
- xx -
-
-
-
-
-
-
-
- - 2 - -
-
-
- 进行中
- 剩余 00:00:07 + 待运行
-
-
- xx -
-
-
-
-
-
-
-
- - 3 - -
-
-
- 待运行 +
-
- -
-
-
- 待运行 -
-
- xx -
-
-
-
-
-`; - -exports[`Steps render should render svg finishIcon and errorIcon correctly 1`] = ` -
-
-
-
- - - - - -
-
- Finished -
-
- This is a description -
-
-
-
-
-
-
-
- - - - - - -
-
-
- In Process -
-
- This is a description -
-
-
-
-
-
-
-
- - 3 - -
-
-
- Waiting + 待运行 +
- This is a description + xx
@@ -1945,29 +1307,25 @@ exports[`Steps should render customIcon correctly 1`] = ` class="rc-steps-item rc-steps-item-finish rc-steps-item-custom" >
-
- - - -
+ />
- 步骤1 +
+ 步骤1 +
+
@@ -1976,25 +1334,25 @@ exports[`Steps should render customIcon correctly 1`] = ` class="rc-steps-item rc-steps-item-process rc-steps-item-custom rc-steps-item-active" >
-
- -
+ />
- 步骤2 +
+ 步骤2 +
+
@@ -2003,25 +1361,22 @@ exports[`Steps should render customIcon correctly 1`] = ` class="rc-steps-item rc-steps-item-wait rc-steps-item-custom" >
-
- -
+ />
- 步骤3 +
+ 步骤3 +
diff --git a/tests/index.test.tsx b/tests/index.test.tsx index 413c859..7c57ade 100644 --- a/tests/index.test.tsx +++ b/tests/index.test.tsx @@ -1,10 +1,11 @@ import React from 'react'; -import { render, fireEvent, createEvent } from '@testing-library/react'; +import { render, fireEvent } from '@testing-library/react'; import Steps from '../src'; describe('Steps', () => { describe('render', () => { - let description = 'xx'; + const description = 'xx'; + const setSteps = (props) => ( { ); expect(container.firstChild).toMatchSnapshot(); }); - - it('renders step with tailContent', () => { - const { container } = render( - content
, - }, - { - title: '待运行', - description, - tailContent: 3, - }, - { - title: '待运行', - description, - tailContent: 'text', - }, - ]} - />, - ); - expect(container.firstChild).toMatchSnapshot(); - }); - - it('renders step with type navigation', () => { - description = 'This is a description.'; - const { container } = render( - {}} - items={[ - { - title: 'Step 1', - subTitle: '剩余 00:00:05 超长隐藏', - description, - }, - { - title: 'Step 2', - description, - }, - { - title: 'Step 3', - description, - disabled: true, - }, - ]} - />, - ); - expect(container.firstChild).toMatchSnapshot(); - }); - - it('renders step with type inline', () => { - description = 'This is a description.'; - const { container } = render( - {}} - items={[ - { - title: 'Step 1', - description, - }, - { - title: 'Step 2', - description, - }, - { - title: 'Step 3', - description, - disabled: true, - }, - ]} - itemRender={(item, stepItem) => React.cloneElement(stepItem, { title: item.description })} - />, - ); - expect(container.firstChild).toMatchSnapshot(); - }); - - function getFinishIcon() { - const path = - 'M923 283.6c-13.4-31.1-32.6-58.9-56.9-82.8-24.3-23.8-52.' + - '5-42.4-84-55.5-32.5-13.5-66.9-20.3-102.4-20.3-49.3 0-97.4 13.5-139' + - '.2 39-10 6.1-19.5 12.8-28.5 20.1-9-7.3-18.5-14-28.5-20.1-41.8-25.5' + - '-89.9-39-139.2-39-35.5 0-69.9 6.8-102.4 20.3-31.4 13-59.7 31.7-84 ' + - '55.5-24.4 23.9-43.5 51.7-56.9 82.8-13.9 32.3-21 66.6-21 101.9 0 33' + - '.3 6.8 68 20.3 103.3 11.3 29.5 27.5 60.1 48.2 91 32.8 48.9 77.9 99' + - '.9 133.9 151.6 92.8 85.7 184.7 144.9 188.6 147.3l23.7 15.2c10.5 6.' + - '7 24 6.7 34.5 0l23.7-15.2c3.9-2.5 95.7-61.6 188.6-147.3 56-51.7 10' + - '1.1-102.7 133.9-151.6 20.7-30.9 37-61.5 48.2-91 13.5-35.3 20.3-70 ' + - '20.3-103.3 0.1-35.3-7-69.6-20.9-101.9z'; - return ( - - - - ); - } - - function getErrorIcon() { - const path1 = - 'M512 0C229.2 0 0 229.2 0 512s229.2 512 512 512 512-229' + - '.2 512-512S794.8 0 512 0zm311.1 823.1c-40.4 40.4-87.5 72.2-139.9 9' + - '4.3C629 940.4 571.4 952 512 952s-117-11.6-171.2-34.5c-52.4-22.2-99' + - '.4-53.9-139.9-94.3-40.4-40.4-72.2-87.5-94.3-139.9C83.6 629 72 571.' + - '4 72 512s11.6-117 34.5-171.2c22.2-52.4 53.9-99.4 94.3-139.9 40.4-4' + - '0.4 87.5-72.2 139.9-94.3C395 83.6 452.6 72 512 72s117 11.6 171.2 3' + - '4.5c52.4 22.2 99.4 53.9 139.9 94.3 40.4 40.4 72.2 87.5 94.3 139.9C' + - '940.4 395 952 452.6 952 512s-11.6 117-34.5 171.2c-22.2 52.4-53.9 9' + - '9.5-94.4 139.9z'; - const path2 = - 'M640.3 765.5c-19.9 0-36-16.1-36-36 0-50.9-41.4-92.3-92' + - '.3-92.3s-92.3 41.4-92.3 92.3c0 19.9-16.1 36-36 36s-36-16.1-36-36c0' + - '-90.6 73.7-164.3 164.3-164.3s164.3 73.7 164.3 164.3c0 19.9-16.1 36' + - '-36 36zM194.2 382.4a60 60 0 1 0 120 0 60 60 0 1 0-120 0zM709.5 382' + - '.4a60 60 0 1 0 120 0 60 60 0 1 0-120 0z'; - return ( - - - - - ); - } - it('should render svg finishIcon and errorIcon correctly', () => { - const icons = { - finish: getFinishIcon(), - error: getErrorIcon(), - }; - const { container } = render( - , - ); - expect(container.firstChild).toMatchSnapshot(); - }); }); it('should render customIcon correctly', () => { @@ -380,7 +210,7 @@ describe('Steps', () => { ]} />, ); - const items = container.querySelectorAll('.rc-steps-item-container'); + const items = container.querySelectorAll('.rc-steps-item-wrapper'); fireEvent.click(items[1]); expect(onChange).toBeCalledWith(1); }); @@ -403,7 +233,7 @@ describe('Steps', () => { , ); - const step = container.querySelectorAll('.rc-steps-item-container')[1]; + const step = container.querySelectorAll('.rc-steps-item-wrapper')[1]; fireEvent.click(step); rerender(); expect(container.querySelectorAll('.rc-steps-item')[1].classList).toContain( @@ -435,7 +265,7 @@ describe('Steps', () => { />, ); - const btn = container.querySelectorAll('.rc-steps-item-container')[0]; + const btn = container.querySelectorAll('.rc-steps-item-wrapper')[0]; fireEvent.click(btn); expect(onClick).toHaveBeenCalled(); }); @@ -446,7 +276,7 @@ describe('Steps', () => { , ); - const items = container.querySelectorAll('.rc-steps-item-container'); + const items = container.querySelectorAll('.rc-steps-item-wrapper'); fireEvent.click(items[2]); expect(onChange).not.toBeCalled(); }); diff --git a/tests/semantic.test.tsx b/tests/semantic.test.tsx new file mode 100644 index 0000000..d84b735 --- /dev/null +++ b/tests/semantic.test.tsx @@ -0,0 +1,76 @@ +import React from 'react'; +import { render, fireEvent } from '@testing-library/react'; +import Steps, { type StepsProps } from '../src'; +import type { SemanticName } from '../src/Steps'; + +describe('Steps.Semantic', () => { + const renderSteps = (props: Partial) => ( + ({ + title: `Step ${index + 1}`, + subTitle: `SubTitle ${index + 1}`, + description: `Description ${index + 1}`, + }))} + {...props} + /> + ); + + it('semantic structure', () => { + const classNames: Record = { + root: 'custom-root', + item: 'custom-item', + itemWrapper: 'custom-item-wrapper', + itemIcon: 'custom-item-icon', + itemSection: 'custom-item-section', + itemHeader: 'custom-item-header', + itemTitle: 'custom-item-title', + itemSubtitle: 'custom-item-subtitle', + itemContent: 'custom-item-content', + itemRail: 'custom-item-rail', + }; + + const classNamesTargets: Record = { + root: 'rc-steps', + item: 'rc-steps-item', + itemWrapper: 'rc-steps-item-wrapper', + itemIcon: 'rc-steps-item-icon', + itemSection: 'rc-steps-item-section', + itemHeader: 'rc-steps-item-header', + itemTitle: 'rc-steps-item-title', + itemSubtitle: 'rc-steps-item-subtitle', + itemContent: 'rc-steps-item-content', + itemRail: 'rc-steps-item-rail', + }; + + const styles: Record> = { + root: { color: 'red' }, + item: { color: 'blue' }, + itemWrapper: { color: 'green' }, + itemIcon: { color: 'yellow' }, + itemSection: { color: 'purple' }, + itemHeader: { color: 'orange' }, + itemTitle: { color: 'pink' }, + itemSubtitle: { color: 'cyan' }, + itemContent: { color: 'magenta' }, + itemRail: { color: 'lime' }, + }; + + const { container } = render( + renderSteps({ + classNames, + styles, + }), + ); + + Object.keys(classNames).forEach((key) => { + const className = classNames[key as SemanticName]; + const oriClassName = classNamesTargets[key as SemanticName]; + const style = styles[key as SemanticName]; + + const element = container.querySelector(`.${className}`); + expect(element).toBeTruthy(); + expect(element).toHaveClass(oriClassName); + expect(element).toHaveStyle(style); + }); + }); +}); From 81f1fe841fffea20bd0b07d8d542458704896d4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Fri, 9 May 2025 23:11:05 +0800 Subject: [PATCH 21/22] test: add test case --- tests/__snapshots__/index.test.tsx.snap | 24 +++++++-------- tests/index.test.tsx | 39 +++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 12 deletions(-) diff --git a/tests/__snapshots__/index.test.tsx.snap b/tests/__snapshots__/index.test.tsx.snap index f663d7a..c7ac8a0 100644 --- a/tests/__snapshots__/index.test.tsx.snap +++ b/tests/__snapshots__/index.test.tsx.snap @@ -701,7 +701,7 @@ exports[`Steps render renders step with description 1`] = ` />
xx
@@ -733,7 +733,7 @@ exports[`Steps render renders step with description 1`] = ` />
xx
@@ -765,7 +765,7 @@ exports[`Steps render renders step with description 1`] = ` />
xx
@@ -794,7 +794,7 @@ exports[`Steps render renders step with description 1`] = `
xx
@@ -833,7 +833,7 @@ exports[`Steps render renders step with description and status 1`] = ` />
xx
@@ -865,7 +865,7 @@ exports[`Steps render renders step with description and status 1`] = ` />
xx
@@ -897,7 +897,7 @@ exports[`Steps render renders step with description and status 1`] = ` />
xx
@@ -926,7 +926,7 @@ exports[`Steps render renders step with description and status 1`] = `
xx
@@ -1190,7 +1190,7 @@ exports[`Steps render renders with falsy children 1`] = ` />
xx
@@ -1228,7 +1228,7 @@ exports[`Steps render renders with falsy children 1`] = ` />
xx
@@ -1260,7 +1260,7 @@ exports[`Steps render renders with falsy children 1`] = ` />
xx
@@ -1289,7 +1289,7 @@ exports[`Steps render renders with falsy children 1`] = `
xx
diff --git a/tests/index.test.tsx b/tests/index.test.tsx index 7c57ade..f0a2d03 100644 --- a/tests/index.test.tsx +++ b/tests/index.test.tsx @@ -305,4 +305,43 @@ describe('Steps', () => { expect(onChange).toHaveBeenCalledWith(1); }); + + it('itemRender', () => { + const { container } = render( + { + return
{oriNode}
; + }} + />, + ); + + expect(container.querySelector('.bamboo')).toBeTruthy(); + expect(container.querySelectorAll('.bamboo')).toHaveLength(1); + expect(container.querySelector('.rc-steps-item')).toBeTruthy(); + }); + + it('itemWrapperRender', () => { + const { container } = render( + { + return
{oriNode}
; + }} + />, + ); + + expect(container.querySelector('.bamboo')).toBeTruthy(); + expect(container.querySelectorAll('.bamboo')).toHaveLength(1); + expect(container.querySelector('.rc-steps-item')).toBeTruthy(); + expect(container.querySelector('.rc-steps-item > .bamboo')).toBeTruthy(); + }); }); From e44ed1e3d18edcf09ae21445da4247ff08d27dc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Fri, 9 May 2025 23:19:28 +0800 Subject: [PATCH 22/22] chore: fix def --- src/Step.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Step.tsx b/src/Step.tsx index a4f611b..966ce6d 100644 --- a/src/Step.tsx +++ b/src/Step.tsx @@ -31,7 +31,7 @@ export interface StepProps { itemWrapperRender?: StepsProps['itemWrapperRender']; // Event - onClick?: (index: number) => void; + onClick: (index: number) => void; } export default function Step(props: StepProps) {