From 6dfa236ed221bd69f857d64ec4bcc6620ab9ed1f Mon Sep 17 00:00:00 2001 From: taninsist Date: Wed, 18 Sep 2024 23:17:28 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat(stepper):=20=E5=AF=B9=E9=BD=90=20mobil?= =?UTF-8?q?e-vue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/_util/formatNumber.ts | 30 + src/stepper/Stepper.tsx | 146 +-- src/stepper/__tests__/index.test.tsx | 120 ++ src/stepper/_example/base.jsx | 6 - src/stepper/_example/base.tsx | 11 + src/stepper/_example/index.jsx | 33 - src/stepper/_example/index.tsx | 32 + src/stepper/_example/min-max.tsx | 31 + src/stepper/_example/pure-stepper.jsx | 10 - src/stepper/_example/size.tsx | 16 + src/stepper/_example/status.jsx | 33 - src/stepper/_example/status.tsx | 10 + src/stepper/_example/style/index.less | 30 +- src/stepper/_example/theme.tsx | 12 + src/stepper/_example/unit-stepper.jsx | 6 - src/stepper/defaultProps.ts | 17 + src/stepper/stepper.en-US.md | 26 + src/stepper/stepper.md | 17 +- src/stepper/style/index.js | 2 +- src/stepper/type.ts | 27 +- test/snap/__snapshots__/csr.test.jsx.snap | 1210 +++++++++++++++++++++ test/snap/__snapshots__/ssr.test.jsx.snap | 12 + 22 files changed, 1649 insertions(+), 188 deletions(-) create mode 100644 src/_util/formatNumber.ts create mode 100644 src/stepper/__tests__/index.test.tsx delete mode 100644 src/stepper/_example/base.jsx create mode 100644 src/stepper/_example/base.tsx delete mode 100644 src/stepper/_example/index.jsx create mode 100644 src/stepper/_example/index.tsx create mode 100644 src/stepper/_example/min-max.tsx delete mode 100644 src/stepper/_example/pure-stepper.jsx create mode 100644 src/stepper/_example/size.tsx delete mode 100644 src/stepper/_example/status.jsx create mode 100644 src/stepper/_example/status.tsx create mode 100644 src/stepper/_example/theme.tsx delete mode 100644 src/stepper/_example/unit-stepper.jsx create mode 100644 src/stepper/defaultProps.ts create mode 100644 src/stepper/stepper.en-US.md diff --git a/src/_util/formatNumber.ts b/src/_util/formatNumber.ts new file mode 100644 index 00000000..76852194 --- /dev/null +++ b/src/_util/formatNumber.ts @@ -0,0 +1,30 @@ +/** + * 格式化数字 + * @param value 传入的数字字符串 + * @param allowDecimal 是否允许小数,默认为 true + * @param allowNegative 是否允许负数,默认为 true + * @returns 返回格式化后的数字字符串 + */ +export const formatNumber = (value: string, allowDecimal = true, allowNegative = true) => { + let resultValue = value; + if (allowDecimal) { + const index = value.indexOf('.'); + if (index !== -1) { + resultValue = `${value.slice(0, index + 1)}${value.slice(index).replace(/\./g, '')}`; + } + } else { + const [splitValue = ''] = value.split('.'); + resultValue = splitValue; + } + + if (allowNegative) { + const index = value.indexOf('-'); + if (index !== -1) { + resultValue = `${value.slice(0, index + 1)}${value.slice(index).replace(/-/g, '')}`; + } + } else { + resultValue = value.replace(/-/g, ''); + } + + return resultValue.replace(/[^\d.-]/g, ''); +}; diff --git a/src/stepper/Stepper.tsx b/src/stepper/Stepper.tsx index 047857d9..df4a93e6 100644 --- a/src/stepper/Stepper.tsx +++ b/src/stepper/Stepper.tsx @@ -1,33 +1,27 @@ -import React, { FC } from 'react'; +import React, { FormEvent, forwardRef, memo } from 'react'; import classNames from 'classnames'; -import identity from 'lodash/identity'; import { AddIcon, RemoveIcon } from 'tdesign-icons-react'; +import useDefaultProps from 'tdesign-mobile-react/hooks/useDefaultProps'; +import useDefault from 'tdesign-mobile-react/_util/useDefault'; +import { formatNumber } from 'tdesign-mobile-react/_util/formatNumber'; import useConfig from '../_util/useConfig'; -import useDefault from '../_util/useDefault'; import { TdStepperProps } from './type'; import withNativeProps, { NativeProps } from '../_util/withNativeProps'; +import { stepperDefaultProps } from './defaultProps'; export interface StepperProps extends TdStepperProps, NativeProps {} -const defaultProps = { - disabled: false, - disableInput: false, - max: 100, - min: 0, - step: 1, - theme: 'normal', - defaultValue: 0, - onBlur: identity, - onChange: identity, - onOverlimit: identity, -}; - -const Stepper: FC = (props) => { +const Stepper = forwardRef((originProps, ref) => { + const props = useDefaultProps(originProps, stepperDefaultProps); const { - disabled, + className, + style, disableInput, + disabled, inputWidth, + integer, max, + size, min, step, theme, @@ -35,101 +29,123 @@ const Stepper: FC = (props) => { defaultValue, onBlur, onChange, + onFocus, onOverlimit, + ...otherProps } = props; + const [currentValue, setCurrentValue] = useDefault(value, defaultValue, onChange); const { classPrefix } = useConfig(); - const name = `${classPrefix}-stepper`; + const baseClass = `${classPrefix}-stepper`; + const inputStyle = inputWidth ? { width: `${props.inputWidth}px` } : {}; const isDisabled = (type: 'minus' | 'plus') => { if (disabled) return true; - if (type === 'minus' && currentValue <= min) { + if (type === 'minus' && Number(currentValue) <= min) { return true; } - if (type === 'plus' && currentValue >= max) { + if (type === 'plus' && Number(currentValue) >= max) { return true; } return false; }; + const getLen = (num: number) => { + const numStr = num.toString(); + return numStr.indexOf('.') === -1 ? 0 : numStr.split('.')[1].length; + }; + + /** + * 精确加法 + */ + const add = (a: number, b: number) => { + const maxLen = Math.max(getLen(a), getLen(b)); + const base = 10 ** maxLen; + return Math.round(a * base + b * base) / base; + }; + const formatValue = (value: number) => - Math.max(Math.min(max, value, Number.MAX_SAFE_INTEGER), min, Number.MIN_SAFE_INTEGER); + Math.max(Math.min(max, value, Number.MAX_SAFE_INTEGER), min, Number.MIN_SAFE_INTEGER).toFixed( + Math.max(getLen(step), getLen(value)), + ); - const updateValue = (value: number) => { - setCurrentValue(formatValue(value)); - onChange(value); + const updateValue = (value: TdStepperProps['value']) => { + setCurrentValue(formatNumber(`${value}`, !integer)); }; - const minusValue = () => { - if (isDisabled('minus')) { - onOverlimit('minus'); + const plusValue = () => { + if (isDisabled('plus')) { + onOverlimit?.('plus'); return; } - updateValue(Number(currentValue) - step); + updateValue(formatValue(add(Number(currentValue), step))); }; - const plusValue = () => { - if (isDisabled('plus')) { - onOverlimit('plus'); + const minusValue = () => { + if (isDisabled('minus')) { + onOverlimit?.('minus'); return; } - updateValue(Number(currentValue) + step); + updateValue(formatValue(add(Number(currentValue), -step))); + }; + + const handleInput = (e: FormEvent) => { + const value = formatNumber((e.target as HTMLInputElement).value, !integer); + setCurrentValue(value); }; - const handleChange = (e: React.FocusEvent) => { - const { value } = e.currentTarget; - if (isNaN(Number(value))) return; - const formattedValue = formatValue(Number(value)); - setCurrentValue(formattedValue); - onChange(formattedValue); + const handleChange = () => { + const formattedValue = formatValue(Number(currentValue)); + updateValue(formattedValue); }; - const handleBlur = (e: React.FocusEvent) => { - const { value } = e.currentTarget; - if (isNaN(Number(value))) return; - const formattedValue = formatValue(Number(value)); - setCurrentValue(formattedValue); - onBlur(formattedValue); + const handleFocus = () => { + onFocus(Number(currentValue)); + }; + + const handleBlur = () => { + onBlur(Number(currentValue)); }; return withNativeProps( props, -
+
- +
= max, + className={classNames(`${baseClass}__plus`, `${baseClass}__plus--${theme}`, `${baseClass}__icon--${size}`, { + [`${baseClass}--${theme}-disabled`]: disabled || Number(currentValue) >= max, })} onClick={plusValue} > - +
, ); -}; +}); -Stepper.defaultProps = defaultProps as StepperProps; +Stepper.defaultProps = stepperDefaultProps as StepperProps; Stepper.displayName = 'Stepper'; -export default Stepper; +export default memo(Stepper); diff --git a/src/stepper/__tests__/index.test.tsx b/src/stepper/__tests__/index.test.tsx new file mode 100644 index 00000000..594d9289 --- /dev/null +++ b/src/stepper/__tests__/index.test.tsx @@ -0,0 +1,120 @@ +import { describe, vi } from '@test/utils'; +import { fireEvent, render } from '@testing-library/react'; +import React from 'react'; +import { Stepper } from '../index'; + +const prefix = 't'; +const baseClass = `.${prefix}-stepper`; + +describe('stepper', () => { + describe('props', () => { + it(': disableInput', () => { + const { container } = render(); + const $stepperItem = container.querySelector('.t-stepper__input') as HTMLElement; + expect($stepperItem.classList.contains(`${baseClass}--disabled`)); + }); + + it(': disabled', () => { + const { container } = render(); + const $stepperItem = container.querySelector('.t-stepper__input') as HTMLElement; + expect($stepperItem.classList.contains(`${baseClass}--disabled`)); + }); + + it(': inputWidth', () => { + const { container } = render(); + const $stepperItem = container.querySelector('.t-stepper__input') as HTMLElement; + expect($stepperItem.style.width).toBe('100px'); + }); + + it(': integer', () => { + const { container } = render(); + const $stepperItem = container.querySelector('.t-stepper__input') as HTMLElement; + expect($stepperItem.getAttribute('value')).toBe('0.5'); + }); + + it(': max', () => { + const { container } = render(); + const $stepperItem = container.querySelector('.t-stepper__plus') as HTMLElement; + expect($stepperItem.classList.contains(`${baseClass}--filled-disabled`)); + }); + + it(': min', () => { + const { container } = render(); + const $stepperItem = container.querySelector(`${baseClass}__minus`) as HTMLElement; + expect($stepperItem.classList.contains(`${baseClass}--filled-disabled`)); + }); + + it(': size', () => { + const size = ['small', 'medium', 'large'] as ('small' | 'medium' | 'large')[]; + size.forEach((s) => { + const { container } = render(); + const $stepperItem = container.querySelector(`${baseClass}`) as HTMLElement; + expect($stepperItem.classList.contains(`${baseClass}--${s}`)); + }); + }); + + it(': step', () => { + const { container } = render(); + const $stepperPlus = container.querySelector('.t-stepper__plus') as HTMLElement; + const $stepperItem = container.querySelector('.t-stepper__input') as HTMLElement; + fireEvent.click($stepperPlus); + expect($stepperItem.getAttribute('value')).toBe('1.0'); + }); + + it(': theme', () => { + const theme = ['normal', 'filled', 'outline'] as ('normal' | 'filled' | 'outline')[]; + theme.forEach((t) => { + const { container } = render(); + const $stepperItem = container.querySelector(`${baseClass}`) as HTMLElement; + expect($stepperItem.classList.contains(`${baseClass}--${t}`)); + }); + }); + + it(': value', () => { + const { container } = render(); + const $stepperItem = container.querySelector(`${baseClass}__input`) as HTMLElement; + expect($stepperItem.getAttribute('value')).toBe('10'); + }); + + it(': defaultValue', () => { + const { container } = render(); + const $stepperItem = container.querySelector(`${baseClass}__input`) as HTMLElement; + expect($stepperItem.getAttribute('value')).toBe('10'); + }); + }); + + describe('event', () => { + it(': blur', async () => { + const onChange = vi.fn(); + const onBlur = vi.fn(); + const { container } = render(); + const $input = container.querySelector(`${baseClass}__input`) as HTMLElement; + fireEvent.blur($input); + expect(onBlur).toHaveBeenCalled(); + }); + + it(': change', async () => { + const onChange = vi.fn(); + const { container } = render(); + const $minusBtn = container.querySelector(`${baseClass}__minus`) as HTMLElement; + fireEvent.click($minusBtn); + expect(onChange).toHaveBeenCalled(); + }); + + it(': overlimit', async () => { + const onOverlimit = vi.fn(); + const { container } = render(); + const $plusBtn = container.querySelector(`${baseClass}__plus`) as HTMLElement; + fireEvent.click($plusBtn); + expect(onOverlimit).toHaveBeenCalled(); + }); + + it(': focus', async () => { + const onFocus = vi.fn(); + const { container } = render(); + const $input = container.querySelector(`${baseClass}__input`) as HTMLElement; + fireEvent.focus($input); + expect(onFocus).toHaveBeenCalled(); + }); + }); +}); diff --git a/src/stepper/_example/base.jsx b/src/stepper/_example/base.jsx deleted file mode 100644 index 598c1511..00000000 --- a/src/stepper/_example/base.jsx +++ /dev/null @@ -1,6 +0,0 @@ -import React from 'react'; -import { Stepper, Cell } from 'tdesign-mobile-react'; - -export default function Base() { - return }>; -} diff --git a/src/stepper/_example/base.tsx b/src/stepper/_example/base.tsx new file mode 100644 index 00000000..ba449abc --- /dev/null +++ b/src/stepper/_example/base.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import { Stepper } from 'tdesign-mobile-react'; + +export default function Base() { + return ( +
+ + +
+ ); +} diff --git a/src/stepper/_example/index.jsx b/src/stepper/_example/index.jsx deleted file mode 100644 index 61d32b95..00000000 --- a/src/stepper/_example/index.jsx +++ /dev/null @@ -1,33 +0,0 @@ -import React from 'react'; -import TDemoBlock from '../../../site/mobile/components/DemoBlock'; -import TDemoHeader from '../../../site/mobile/components/DemoHeader'; -import './style/index.less'; -import Base from './base'; -import UnitStepper from './unit-stepper'; -import PureStepper from './pure-stepper'; -import StatusStepper from './status'; - -export default function StepperDemo() { - return ( -
- - - - - - - - - - - - - - - -
- ); -} diff --git a/src/stepper/_example/index.tsx b/src/stepper/_example/index.tsx new file mode 100644 index 00000000..69737216 --- /dev/null +++ b/src/stepper/_example/index.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import TDemoBlock from '../../../site/mobile/components/DemoBlock'; +import TDemoHeader from '../../../site/mobile/components/DemoHeader'; +import './style/index.less'; +import Base from './base'; +import MinMax from './min-max'; +import Status from './status'; +import Theme from './theme'; +import Size from './size'; + +export default function StepperDemo() { + return ( +
+ + + + + + + + + + + + + + + + +
+ ); +} diff --git a/src/stepper/_example/min-max.tsx b/src/stepper/_example/min-max.tsx new file mode 100644 index 00000000..b6536d15 --- /dev/null +++ b/src/stepper/_example/min-max.tsx @@ -0,0 +1,31 @@ +import React from 'react'; +import { Stepper } from 'tdesign-mobile-react'; + +export default function MinMax() { + const onChange = ($event: number) => { + console.log(`change to ${$event}`); + }; + const onBlur = ($event: number) => { + console.log(`blur to ${$event}`); + }; + const onOverlimit = ($type: string) => { + console.log(`onOverlimit ${$type}`); + }; + + return ( +
+ + + +
+ ); +} diff --git a/src/stepper/_example/pure-stepper.jsx b/src/stepper/_example/pure-stepper.jsx deleted file mode 100644 index 280dd370..00000000 --- a/src/stepper/_example/pure-stepper.jsx +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; -import { Stepper } from 'tdesign-mobile-react'; - -export default function PureStepper() { - return ( -
- -
- ); -} diff --git a/src/stepper/_example/size.tsx b/src/stepper/_example/size.tsx new file mode 100644 index 00000000..4d2d3d3a --- /dev/null +++ b/src/stepper/_example/size.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { Stepper } from 'tdesign-mobile-react'; + +export default function size() { + const handleChange = ($event: number) => { + console.log(`change to ${$event}`); + }; + + return ( +
+ + + +
+ ); +} diff --git a/src/stepper/_example/status.jsx b/src/stepper/_example/status.jsx deleted file mode 100644 index b3ddbda4..00000000 --- a/src/stepper/_example/status.jsx +++ /dev/null @@ -1,33 +0,0 @@ -import React, { useState } from 'react'; -import { Stepper, Cell } from 'tdesign-mobile-react'; - -export default function StatusStepper() { - const [controlValue, setControlValue] = useState(999); - - const onValueChange = (v) => { - setControlValue(v); - }; - - return ( - <> -
}> -
- }> -
-
- } - > -
-
- }> -
-
- - - -
- - ); -} diff --git a/src/stepper/_example/status.tsx b/src/stepper/_example/status.tsx new file mode 100644 index 00000000..c08e9bda --- /dev/null +++ b/src/stepper/_example/status.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import { Stepper } from 'tdesign-mobile-react'; + +export default function StatusStepper() { + return ( +
+ +
+ ); +} diff --git a/src/stepper/_example/style/index.less b/src/stepper/_example/style/index.less index 1d5f2ccf..6e375a33 100644 --- a/src/stepper/_example/style/index.less +++ b/src/stepper/_example/style/index.less @@ -1,26 +1,12 @@ -.pure-stepper-container { - display: flex; - justify-content: center; - align-items: center; - width: 122px; - background: rgba(255, 255, 255, 1); - height: 48px; - opacity: 1; - margin-left: 16px; -} .stepper-container { - margin-bottom: 16px; -} - -.cell-container { - margin-top: 16px; + margin-bottom: 28px; } -.pure-group-container { - display: flex; - padding: 12px 16px; - background-color: #fff; - justify-content: space-between; - margin-top: 16px; -} +.stepper-example { + padding: 16px; + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 24px; + background-color: var(--bg-color-demo, #fff); +} \ No newline at end of file diff --git a/src/stepper/_example/theme.tsx b/src/stepper/_example/theme.tsx new file mode 100644 index 00000000..90f44b8f --- /dev/null +++ b/src/stepper/_example/theme.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import { Stepper } from 'tdesign-mobile-react'; + +export default function theme() { + return ( +
+ + + +
+ ); +} diff --git a/src/stepper/_example/unit-stepper.jsx b/src/stepper/_example/unit-stepper.jsx deleted file mode 100644 index 0d6a4f19..00000000 --- a/src/stepper/_example/unit-stepper.jsx +++ /dev/null @@ -1,6 +0,0 @@ -import React from 'react'; -import { Stepper, Cell } from 'tdesign-mobile-react'; - -export default function UnitStepper() { - return
}>; -} diff --git a/src/stepper/defaultProps.ts b/src/stepper/defaultProps.ts new file mode 100644 index 00000000..a546c5c5 --- /dev/null +++ b/src/stepper/defaultProps.ts @@ -0,0 +1,17 @@ +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import { TdStepperProps } from './type'; + +export const stepperDefaultProps: TdStepperProps = { + disableInput: false, + disabled: undefined, + integer: true, + max: 100, + min: 0, + size: 'medium', + step: 1, + theme: 'normal', + defaultValue: 0, +}; diff --git a/src/stepper/stepper.en-US.md b/src/stepper/stepper.en-US.md new file mode 100644 index 00000000..fa0ee5ce --- /dev/null +++ b/src/stepper/stepper.en-US.md @@ -0,0 +1,26 @@ +:: BASE_DOC :: + +## API + + +### Stepper Props + +name | type | default | description | required +-- | -- | -- | -- | -- +className | String | - | className of component | N +style | Object | - | CSS(Cascading Style Sheets),Typescript:`React.CSSProperties` | N +disableInput | Boolean | false | \- | N +disabled | Boolean | undefined | \- | N +inputWidth | Number | - | \- | N +integer | Boolean | true | \- | N +max | Number | 100 | \- | N +min | Number | 0 | \- | N +size | String | medium | options: small/medium/large。Typescript:`SizeEnum`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N +step | Number | 1 | \- | N +theme | String | normal | stylish。options: normal/filled/outline | N +value | String / Number | 0 | \- | N +defaultValue | String / Number | 0 | uncontrolled property | N +onBlur | Function | | Typescript:`(value: string \| number) => void`
| N +onChange | Function | | Typescript:`(value: string \| number) => void`
| N +onFocus | Function | | Typescript:`(value: string \| number) => void`
| N +onOverlimit | Function | | Typescript:`(type: 'minus' \| 'plus') => void`
| N diff --git a/src/stepper/stepper.md b/src/stepper/stepper.md index fb412742..19904016 100644 --- a/src/stepper/stepper.md +++ b/src/stepper/stepper.md @@ -1,21 +1,26 @@ :: BASE_DOC :: ## API + + ### Stepper Props -名称 | 类型 | 默认值 | 说明 | 必传 +名称 | 类型 | 默认值 | 描述 | 必传 -- | -- | -- | -- | -- className | String | - | 类名 | N style | Object | - | 样式,TS 类型:`React.CSSProperties` | N -disabled | Boolean | false | 禁用全部操作 | N disableInput | Boolean | false | 禁用输入框 | N +disabled | Boolean | undefined | 禁用全部操作 | N inputWidth | Number | - | 输入框宽度 | N +integer | Boolean | true | 是否整型 | N max | Number | 100 | 最大值 | N min | Number | 0 | 最小值 | N +size | String | medium | 组件尺寸。可选项:small/medium/large。TS 类型:`SizeEnum`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-react/blob/develop/src/common.ts) | N step | Number | 1 | 步长 | N -theme | String | normal | 组件风格。可选项:normal/grey | N +theme | String | normal | 组件风格。可选项:normal/filled/outline | N value | String / Number | 0 | 值 | N defaultValue | String / Number | 0 | 值。非受控属性 | N -onBlur | Function | | TS 类型:`(value: string | number) => void`
输入框失去焦点时触发 | N -onChange | Function | | TS 类型:`(value: string | number) => void`
数值发生变更时触发 | N -onOverlimit | Function | | TS 类型:`(type: 'minus' | 'plus') => void`
数值超出限制时触发 | N +onBlur | Function | | TS 类型:`(value: string \| number) => void`
输入框失去焦点时触发 | N +onChange | Function | | TS 类型:`(value: string \| number) => void`
数值发生变更时触发 | N +onFocus | Function | | TS 类型:`(value: string \| number) => void`
输入框聚焦时触发 | N +onOverlimit | Function | | TS 类型:`(type: 'minus' \| 'plus') => void`
数值超出限制时触发 | N diff --git a/src/stepper/style/index.js b/src/stepper/style/index.js index afde5cff..5cda277e 100644 --- a/src/stepper/style/index.js +++ b/src/stepper/style/index.js @@ -1 +1 @@ -import '../../_common/style/mobile/components/stepper/_index.less'; +import '../../_common/style/mobile/components/stepper/v2/_index.less'; diff --git a/src/stepper/type.ts b/src/stepper/type.ts index 6e4b6e00..b6f869a1 100644 --- a/src/stepper/type.ts +++ b/src/stepper/type.ts @@ -4,21 +4,27 @@ * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC * */ +import { SizeEnum } from '../common'; + export interface TdStepperProps { - /** - * 禁用全部操作 - * @default false - */ - disabled?: boolean; /** * 禁用输入框 * @default false */ disableInput?: boolean; + /** + * 禁用全部操作 + */ + disabled?: boolean; /** * 输入框宽度 */ inputWidth?: number; + /** + * 是否整型 + * @default true + */ + integer?: boolean; /** * 最大值 * @default 100 @@ -29,6 +35,11 @@ export interface TdStepperProps { * @default 0 */ min?: number; + /** + * 组件尺寸 + * @default medium + */ + size?: SizeEnum; /** * 步长 * @default 1 @@ -38,7 +49,7 @@ export interface TdStepperProps { * 组件风格 * @default normal */ - theme?: 'normal' | 'grey'; + theme?: 'normal' | 'filled' | 'outline'; /** * 值 * @default 0 @@ -57,6 +68,10 @@ export interface TdStepperProps { * 数值发生变更时触发 */ onChange?: (value: string | number) => void; + /** + * 输入框聚焦时触发 + */ + onFocus?: (value: string | number) => void; /** * 数值超出限制时触发 */ diff --git a/test/snap/__snapshots__/csr.test.jsx.snap b/test/snap/__snapshots__/csr.test.jsx.snap index d857a5d8..279512d0 100644 --- a/test/snap/__snapshots__/csr.test.jsx.snap +++ b/test/snap/__snapshots__/csr.test.jsx.snap @@ -38656,6 +38656,1204 @@ exports[`csr snapshot test > csr test src/skeleton/_example/theme.tsx 1`] = `
`; +exports[`csr snapshot test > csr test src/stepper/_example/base.tsx 1`] = ` +
+
+
+
+ + + +
+ +
+ + + +
+
+
+
+ + + +
+ +
+ + + +
+
+
+
+`; + +exports[`csr snapshot test > csr test src/stepper/_example/index.tsx 1`] = ` +
+
+
+

+ Stepper 步进器 +

+

+ 用于数量的增减 +

+
+
+
+

+ 01 类型 +

+

+ 基础步进器 +

+
+
+
+
+
+ + + +
+ +
+ + + +
+
+
+
+ + + +
+ +
+ + + +
+
+
+
+
+
+
+

+ 02 组件状态 +

+

+ 最大最小状态 +

+
+
+
+
+
+ + + +
+ +
+ + + +
+
+
+
+ + + +
+ +
+ + + +
+
+
+
+ + + +
+ +
+ + + +
+
+
+
+
+
+
+

+ 禁用状态 +

+
+
+
+
+
+ + + +
+ +
+ + + +
+
+
+
+
+
+
+

+ 02 组件样式 +

+

+ 步进器样式 +

+
+
+
+
+
+ + + +
+ +
+ + + +
+
+
+
+ + + +
+ +
+ + + +
+
+
+
+ + + +
+ +
+ + + +
+
+
+
+
+
+
+

+ 步进器尺寸 +

+
+
+
+
+
+ + + +
+ +
+ + + +
+
+
+
+ + + +
+ +
+ + + +
+
+
+
+ + + +
+ +
+ + + +
+
+
+
+
+
+
+`; + +exports[`csr snapshot test > csr test src/stepper/_example/min-max.tsx 1`] = ` +
+
+
+
+ + + +
+ +
+ + + +
+
+
+
+ + + +
+ +
+ + + +
+
+
+
+ + + +
+ +
+ + + +
+
+
+
+`; + +exports[`csr snapshot test > csr test src/stepper/_example/size.tsx 1`] = ` +
+
+
+
+ + + +
+ +
+ + + +
+
+
+
+ + + +
+ +
+ + + +
+
+
+
+ + + +
+ +
+ + + +
+
+
+
+`; + +exports[`csr snapshot test > csr test src/stepper/_example/status.tsx 1`] = ` +
+
+
+
+ + + +
+ +
+ + + +
+
+
+
+`; + +exports[`csr snapshot test > csr test src/stepper/_example/theme.tsx 1`] = ` +
+
+
+
+ + + +
+ +
+ + + +
+
+
+
+ + + +
+ +
+ + + +
+
+
+
+ + + +
+ +
+ + + +
+
+
+
+`; + exports[`csr snapshot test > csr test src/sticky/_example/base.tsx 1`] = `
ssr test src/skeleton/_example/image-group.tsx 1`] exports[`ssr snapshot test > ssr test src/skeleton/_example/theme.tsx 1`] = `"
头像骨架屏
图片骨架屏
文本骨架屏
段落骨架屏
"`; +exports[`ssr snapshot test > ssr test src/stepper/_example/base.tsx 1`] = `"
"`; + +exports[`ssr snapshot test > ssr test src/stepper/_example/index.tsx 1`] = `"

Stepper 步进器

用于数量的增减

01 类型

基础步进器

02 组件状态

最大最小状态

禁用状态

02 组件样式

步进器样式

步进器尺寸

"`; + +exports[`ssr snapshot test > ssr test src/stepper/_example/min-max.tsx 1`] = `"
"`; + +exports[`ssr snapshot test > ssr test src/stepper/_example/size.tsx 1`] = `"
"`; + +exports[`ssr snapshot test > ssr test src/stepper/_example/status.tsx 1`] = `"
"`; + +exports[`ssr snapshot test > ssr test src/stepper/_example/theme.tsx 1`] = `"
"`; + exports[`ssr snapshot test > ssr test src/sticky/_example/base.tsx 1`] = `"
"`; exports[`ssr snapshot test > ssr test src/sticky/_example/container.tsx 1`] = `"
"`; diff --git a/test/snap/__snapshots__/ssr.test.jsx.snap b/test/snap/__snapshots__/ssr.test.jsx.snap index b3ace884..aa4add2f 100644 --- a/test/snap/__snapshots__/ssr.test.jsx.snap +++ b/test/snap/__snapshots__/ssr.test.jsx.snap @@ -318,6 +318,18 @@ exports[`ssr snapshot test > ssr test src/skeleton/_example/image-group.tsx 1`] exports[`ssr snapshot test > ssr test src/skeleton/_example/theme.tsx 1`] = `"
头像骨架屏
图片骨架屏
文本骨架屏
段落骨架屏
"`; +exports[`ssr snapshot test > ssr test src/stepper/_example/base.tsx 1`] = `"
"`; + +exports[`ssr snapshot test > ssr test src/stepper/_example/index.tsx 1`] = `"

Stepper 步进器

用于数量的增减

01 类型

基础步进器

02 组件状态

最大最小状态

禁用状态

02 组件样式

步进器样式

步进器尺寸

"`; + +exports[`ssr snapshot test > ssr test src/stepper/_example/min-max.tsx 1`] = `"
"`; + +exports[`ssr snapshot test > ssr test src/stepper/_example/size.tsx 1`] = `"
"`; + +exports[`ssr snapshot test > ssr test src/stepper/_example/status.tsx 1`] = `"
"`; + +exports[`ssr snapshot test > ssr test src/stepper/_example/theme.tsx 1`] = `"
"`; + exports[`ssr snapshot test > ssr test src/sticky/_example/base.tsx 1`] = `"
"`; exports[`ssr snapshot test > ssr test src/sticky/_example/container.tsx 1`] = `"
"`; From d1a6883c570fa40d4f0cf48a8d3c1ed51309620b Mon Sep 17 00:00:00 2001 From: taninsist Date: Wed, 18 Sep 2024 23:25:23 +0800 Subject: [PATCH 2/3] =?UTF-8?q?fix(stepper):=20=E4=BF=AE=E6=94=B9site?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- site/mobile/mobile.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/mobile/mobile.config.js b/site/mobile/mobile.config.js index a26a8027..03039a05 100644 --- a/site/mobile/mobile.config.js +++ b/site/mobile/mobile.config.js @@ -205,7 +205,7 @@ export default { { title: 'Stepper 步进器', name: 'Stepper', - component: () => import('tdesign-mobile-react/stepper/_example/index.jsx'), + component: () => import('tdesign-mobile-react/stepper/_example/index.tsx'), }, { title: 'PullDownRefresh 下拉刷新', From 209fb0aa9bea35ed8eca69879d79f67a0a02b373 Mon Sep 17 00:00:00 2001 From: anlyyao Date: Thu, 19 Sep 2024 18:12:31 +0800 Subject: [PATCH 3/3] fix: fix cr --- src/stepper/Stepper.tsx | 65 ++++++++++++++--------- src/stepper/_example/min-max.tsx | 7 ++- src/stepper/_example/size.tsx | 13 +++-- test/snap/__snapshots__/csr.test.jsx.snap | 12 ++--- test/snap/__snapshots__/ssr.test.jsx.snap | 4 +- 5 files changed, 60 insertions(+), 41 deletions(-) diff --git a/src/stepper/Stepper.tsx b/src/stepper/Stepper.tsx index df4a93e6..8c3bad5f 100644 --- a/src/stepper/Stepper.tsx +++ b/src/stepper/Stepper.tsx @@ -1,15 +1,15 @@ import React, { FormEvent, forwardRef, memo } from 'react'; import classNames from 'classnames'; import { AddIcon, RemoveIcon } from 'tdesign-icons-react'; -import useDefaultProps from 'tdesign-mobile-react/hooks/useDefaultProps'; -import useDefault from 'tdesign-mobile-react/_util/useDefault'; -import { formatNumber } from 'tdesign-mobile-react/_util/formatNumber'; -import useConfig from '../_util/useConfig'; +import { usePrefixClass } from '../hooks/useClass'; +import useDefaultProps from '../hooks/useDefaultProps'; +import useDefault from '../_util/useDefault'; +import { formatNumber } from '../_util/formatNumber'; +import { StyledProps } from '../common'; import { TdStepperProps } from './type'; -import withNativeProps, { NativeProps } from '../_util/withNativeProps'; import { stepperDefaultProps } from './defaultProps'; -export interface StepperProps extends TdStepperProps, NativeProps {} +export interface StepperProps extends TdStepperProps, StyledProps {} const Stepper = forwardRef((originProps, ref) => { const props = useDefaultProps(originProps, stepperDefaultProps); @@ -31,13 +31,13 @@ const Stepper = forwardRef((originProps, ref) => { onChange, onFocus, onOverlimit, - ...otherProps } = props; const [currentValue, setCurrentValue] = useDefault(value, defaultValue, onChange); - const { classPrefix } = useConfig(); - const baseClass = `${classPrefix}-stepper`; - const inputStyle = inputWidth ? { width: `${props.inputWidth}px` } : {}; + + const stepperClass = usePrefixClass('stepper'); + + const inputStyle = inputWidth ? { width: `${inputWidth}px` } : {}; const isDisabled = (type: 'minus' | 'plus') => { if (disabled) return true; @@ -107,22 +107,31 @@ const Stepper = forwardRef((originProps, ref) => { onBlur(Number(currentValue)); }; - return withNativeProps( - props, -
+ return ( +
- +
((originProps, ref) => { onChange={handleChange} />
= max, - })} + className={classNames( + `${stepperClass}__plus`, + `${stepperClass}__plus--${theme}`, + `${stepperClass}__icon--${size}`, + { + [`${stepperClass}--${theme}-disabled`]: disabled || Number(currentValue) >= max, + }, + )} onClick={plusValue} > - +
-
, +
); }); -Stepper.defaultProps = stepperDefaultProps as StepperProps; Stepper.displayName = 'Stepper'; export default memo(Stepper); diff --git a/src/stepper/_example/min-max.tsx b/src/stepper/_example/min-max.tsx index b6536d15..f9797a1f 100644 --- a/src/stepper/_example/min-max.tsx +++ b/src/stepper/_example/min-max.tsx @@ -1,8 +1,11 @@ -import React from 'react'; +import React, { useState } from 'react'; import { Stepper } from 'tdesign-mobile-react'; export default function MinMax() { + const [value, setValue] = useState(3); + const onChange = ($event: number) => { + setValue($event); console.log(`change to ${$event}`); }; const onBlur = ($event: number) => { @@ -16,7 +19,7 @@ export default function MinMax() {
{ + setValue($event); console.log(`change to ${$event}`); }; return (
- - - + + +
); } diff --git a/test/snap/__snapshots__/csr.test.jsx.snap b/test/snap/__snapshots__/csr.test.jsx.snap index 279512d0..8571e9fa 100644 --- a/test/snap/__snapshots__/csr.test.jsx.snap +++ b/test/snap/__snapshots__/csr.test.jsx.snap @@ -38947,7 +38947,7 @@ exports[`csr snapshot test > csr test src/stepper/_example/index.tsx 1`] = ` class="t-stepper t-stepper--medium" >
csr test src/stepper/_example/index.tsx 1`] = ` class="t-stepper__input t-stepper__input--filled t-stepper__input--medium" inputmode="numeric" type="tel" - value="0" + value="3" />
csr test src/stepper/_example/min-max.tsx 1`] = ` class="t-stepper t-stepper--medium" >
csr test src/stepper/_example/min-max.tsx 1`] = ` class="t-stepper__input t-stepper__input--filled t-stepper__input--medium" inputmode="numeric" type="tel" - value="0" + value="3" />
ssr test src/skeleton/_example/theme.tsx 1`] = `" ssr test src/stepper/_example/base.tsx 1`] = `"
"`; -exports[`ssr snapshot test > ssr test src/stepper/_example/index.tsx 1`] = `"

Stepper 步进器

用于数量的增减

01 类型

基础步进器

02 组件状态

最大最小状态

禁用状态

02 组件样式

步进器样式

步进器尺寸

"`; +exports[`ssr snapshot test > ssr test src/stepper/_example/index.tsx 1`] = `"

Stepper 步进器

用于数量的增减

01 类型

基础步进器

02 组件状态

最大最小状态

禁用状态

02 组件样式

步进器样式

步进器尺寸

"`; -exports[`ssr snapshot test > ssr test src/stepper/_example/min-max.tsx 1`] = `"
"`; +exports[`ssr snapshot test > ssr test src/stepper/_example/min-max.tsx 1`] = `"
"`; exports[`ssr snapshot test > ssr test src/stepper/_example/size.tsx 1`] = `"
"`; diff --git a/test/snap/__snapshots__/ssr.test.jsx.snap b/test/snap/__snapshots__/ssr.test.jsx.snap index aa4add2f..d2d2c00d 100644 --- a/test/snap/__snapshots__/ssr.test.jsx.snap +++ b/test/snap/__snapshots__/ssr.test.jsx.snap @@ -320,9 +320,9 @@ exports[`ssr snapshot test > ssr test src/skeleton/_example/theme.tsx 1`] = `" ssr test src/stepper/_example/base.tsx 1`] = `"
"`; -exports[`ssr snapshot test > ssr test src/stepper/_example/index.tsx 1`] = `"

Stepper 步进器

用于数量的增减

01 类型

基础步进器

02 组件状态

最大最小状态

禁用状态

02 组件样式

步进器样式

步进器尺寸

"`; +exports[`ssr snapshot test > ssr test src/stepper/_example/index.tsx 1`] = `"

Stepper 步进器

用于数量的增减

01 类型

基础步进器

02 组件状态

最大最小状态

禁用状态

02 组件样式

步进器样式

步进器尺寸

"`; -exports[`ssr snapshot test > ssr test src/stepper/_example/min-max.tsx 1`] = `"
"`; +exports[`ssr snapshot test > ssr test src/stepper/_example/min-max.tsx 1`] = `"
"`; exports[`ssr snapshot test > ssr test src/stepper/_example/size.tsx 1`] = `"
"`;