From 64318379852708a9e3d5b8c8874d20df0ad6b724 Mon Sep 17 00:00:00 2001 From: pingtougea <1465164225@qq.com> Date: Tue, 11 Nov 2025 18:47:03 +0800 Subject: [PATCH 1/3] feat(Sender): implement maxLength and showCount support --- components/sender/__tests__/index.test.tsx | 40 ++++++++++++++++++++++ components/sender/demo/basic.tsx | 1 + components/sender/index.tsx | 8 +++++ components/sender/style/index.ts | 12 +++++++ package.json | 5 ++- 5 files changed, 65 insertions(+), 1 deletion(-) diff --git a/components/sender/__tests__/index.test.tsx b/components/sender/__tests__/index.test.tsx index dbc8ca60c..80fd2570f 100644 --- a/components/sender/__tests__/index.test.tsx +++ b/components/sender/__tests__/index.test.tsx @@ -4,6 +4,7 @@ import { fireEvent, render } from '@testing-library/react'; import mountTest from '../../../tests/shared/mountTest'; import rtlTest from '../../../tests/shared/rtlTest'; import Sender from '../index'; +import { values } from 'lodash'; describe('Sender Component', () => { mountTest(() => ); @@ -261,3 +262,42 @@ describe('Sender Component', () => { }); }); }); +describe('maxLength and showCount', () => { + it('should limit input length when maxLength is set', () => { + const { container } = render(); + const textarea = container.querySelector('textarea')!; + fireEvent.change(textarea, { target: { value: 'abcdefgh' } }); + expect(textarea).toHaveValue('abcde'); + }); + + it('should display count when showCount and maxLength are set', () => { + const { container } = render(); + expect(container.querySelector('.ant-sender-count')?.textContent).toBe('4/10'); + }); + + it('should not display count when showCount is false', () => { + const { container } = render(); + expect(container.querySelector('.ant-sender-count')).toBeNull(); + }); + + it('should update count in real-time', () => { + const { container } = render(); + const textarea = container.querySelector('textarea')!; + fireEvent.change(textarea, { target: { value: 'hello' } }); + expect(container.querySelector('.ant-sender-count')?.textContent).toBe('5 / 20'); + + fireEvent.change(textarea, { target: { value: 'hello world' } }); + expect(container.querySelector('.ant-sender-count')?.textContent).toBe('11 / 20'); + }); + + it('should work with controlled component', () => { + const onChange = jest.fn(); + const { container, rerender } = render( + , + ); + expect(container.querySelector('.ant-sender-count')?.textContent).toBe('7 / 10'); + + rerender(); + expect(container.querySelector('.ant-sender-count')?.textContent).toBe('7 / 10'); + }); +}); diff --git a/components/sender/demo/basic.tsx b/components/sender/demo/basic.tsx index cf2f639d2..0b3f7bacc 100644 --- a/components/sender/demo/basic.tsx +++ b/components/sender/demo/basic.tsx @@ -42,6 +42,7 @@ const Demo: React.FC = () => { /> + ); }; diff --git a/components/sender/index.tsx b/components/sender/index.tsx index 470fbc124..b7bdabd6f 100644 --- a/components/sender/index.tsx +++ b/components/sender/index.tsx @@ -48,6 +48,8 @@ export interface SenderProps readOnly?: boolean; submitType?: SubmitType; disabled?: boolean; + maxLength?: number; + showCount?: boolean; onSubmit?: (message: string) => void; onChange?: ( value: string, @@ -360,7 +362,13 @@ const ForwardSender = React.forwardRef((props, ref) => { onPaste={onInternalPaste} variant="borderless" readOnly={readOnly} + maxLength={props.maxLength} /> + {props.showCount && typeof props.maxLength === 'number' && ( +
+ {innerValue.length}/{props.maxLength} +
+ )} {/* Action List */} {actionNode && (
= (token) => { boxSizing: 'border-box', alignItems: 'flex-end', }, + // ============================ Count ============================= + [`${componentCls}-count`]: { + flex: 'none', + fontSize: token.fontSizeSM, + color: token.colorTextDescription, + lineHeight: token.lineHeight, + paddingInlineStart: 0, + paddingInlineEnd: token.paddingSM, + alignSelf: 'flex-end', + userSelect: 'none', + pointerEvents: 'none', + }, // ============================ Prefix ============================= [`${componentCls}-prefix`]: { flex: 'none', diff --git a/package.json b/package.json index 6c90535be..a5945dc30 100644 --- a/package.json +++ b/package.json @@ -270,5 +270,8 @@ "path": "./dist/antdx.min.js", "limit": "350 KiB" } - ] + ], + "volta": { + "node": "18.20.8" + } } From 84cee377fbfe4ce68d42520746a387ca013e36ec Mon Sep 17 00:00:00 2001 From: pingtougea <1465164225@qq.com> Date: Tue, 11 Nov 2025 23:25:03 +0800 Subject: [PATCH 2/3] =?UTF-8?q?refactor:=20=E4=BF=AE=E5=A4=8DBiome?= =?UTF-8?q?=E8=AF=AD=E6=B3=95=E9=94=99=E8=AF=AF=20+=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E8=A7=84=E8=8C=83=20-=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E5=8F=AF=E9=80=89=E9=93=BE=E5=90=8E=E7=9A=84=E9=9D=9E=E7=A9=BA?= =?UTF-8?q?=E6=96=AD=E8=A8=80=EF=BC=8C=E7=AC=A6=E5=90=88lint=E8=A7=84?= =?UTF-8?q?=E5=88=99=20-=20=E7=BB=9F=E4=B8=80=E5=B1=9E=E6=80=A7=E8=AE=BF?= =?UTF-8?q?=E9=97=AE=E6=96=B9=E5=BC=8F=E4=B8=8E=E8=AE=A1=E6=95=B0=E5=99=A8?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=20-=20=E7=A7=BB=E9=99=A4=E6=9C=AA=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E5=AF=BC=E5=85=A5=E5=92=8C=E5=86=97=E4=BD=99volta?= =?UTF-8?q?=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/sender/__tests__/index.test.tsx | 17 ++++++++--------- components/sender/index.tsx | 17 +++++++++-------- package.json | 7 ++----- 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/components/sender/__tests__/index.test.tsx b/components/sender/__tests__/index.test.tsx index 80fd2570f..fb6c40b29 100644 --- a/components/sender/__tests__/index.test.tsx +++ b/components/sender/__tests__/index.test.tsx @@ -1,10 +1,9 @@ -import React from 'react'; - import { fireEvent, render } from '@testing-library/react'; +import userEvent from '@texting-library/user-event'; +import React from 'react'; import mountTest from '../../../tests/shared/mountTest'; import rtlTest from '../../../tests/shared/rtlTest'; import Sender from '../index'; -import { values } from 'lodash'; describe('Sender Component', () => { mountTest(() => ); @@ -263,10 +262,10 @@ describe('Sender Component', () => { }); }); describe('maxLength and showCount', () => { - it('should limit input length when maxLength is set', () => { + it('should limit input length when maxLength is set', async () => { const { container } = render(); const textarea = container.querySelector('textarea')!; - fireEvent.change(textarea, { target: { value: 'abcdefgh' } }); + await userEvent.type(textarea, 'abcdefgh'); expect(textarea).toHaveValue('abcde'); }); @@ -284,10 +283,10 @@ describe('maxLength and showCount', () => { const { container } = render(); const textarea = container.querySelector('textarea')!; fireEvent.change(textarea, { target: { value: 'hello' } }); - expect(container.querySelector('.ant-sender-count')?.textContent).toBe('5 / 20'); + expect(container.querySelector('.ant-sender-count')?.textContent).toBe('5/20'); fireEvent.change(textarea, { target: { value: 'hello world' } }); - expect(container.querySelector('.ant-sender-count')?.textContent).toBe('11 / 20'); + expect(container.querySelector('.ant-sender-count')?.textContent).toBe('11/20'); }); it('should work with controlled component', () => { @@ -295,9 +294,9 @@ describe('maxLength and showCount', () => { const { container, rerender } = render( , ); - expect(container.querySelector('.ant-sender-count')?.textContent).toBe('7 / 10'); + expect(container.querySelector('.ant-sender-count')?.textContent).toBe('7/10'); rerender(); - expect(container.querySelector('.ant-sender-count')?.textContent).toBe('7 / 10'); + expect(container.querySelector('.ant-sender-count')?.textContent).toBe('6/10'); }); }); diff --git a/components/sender/index.tsx b/components/sender/index.tsx index b7bdabd6f..8b6e631f0 100644 --- a/components/sender/index.tsx +++ b/components/sender/index.tsx @@ -1,3 +1,4 @@ +import type { InputRef as AntdInputRef, ButtonProps, GetProps } from 'antd'; import { Flex, Input } from 'antd'; import classnames from 'classnames'; import { useMergedState } from 'rc-util'; @@ -7,17 +8,15 @@ import React from 'react'; import useProxyImperativeHandle from '../_util/hooks/use-proxy-imperative-handle'; import useXComponentConfig from '../_util/hooks/use-x-component-config'; import { useXProviderContext } from '../x-provider'; -import SenderHeader, { SendHeaderContext } from './SenderHeader'; import { ActionButtonContext } from './components/ActionButton'; import ClearButton from './components/ClearButton'; import LoadingButton from './components/LoadingButton'; import SendButton from './components/SendButton'; import SpeechButton from './components/SpeechButton'; +import SenderHeader, { SendHeaderContext } from './SenderHeader'; import useStyle from './style'; import useSpeech, { type AllowSpeech } from './useSpeech'; -import type { InputRef as AntdInputRef, ButtonProps, GetProps } from 'antd'; - export type SubmitType = 'enter' | 'shiftEnter' | false; type TextareaProps = GetProps; @@ -133,6 +132,8 @@ const ForwardSender = React.forwardRef((props, ref) => { onPaste, onPasteFile, autoSize = { maxRows: 8 }, + maxLength, + showCount, ...rest } = props; @@ -146,8 +147,8 @@ const ForwardSender = React.forwardRef((props, ref) => { useProxyImperativeHandle(ref, () => ({ nativeElement: containerRef.current!, - focus: inputRef.current?.focus!, - blur: inputRef.current?.blur!, + focus: inputRef.current?.focus, + blur: inputRef.current?.blur, })); // ======================= Component Config ======================= @@ -362,11 +363,11 @@ const ForwardSender = React.forwardRef((props, ref) => { onPaste={onInternalPaste} variant="borderless" readOnly={readOnly} - maxLength={props.maxLength} + maxLength={maxLength} /> - {props.showCount && typeof props.maxLength === 'number' && ( + {showCount && typeof maxLength === 'number' && (
- {innerValue.length}/{props.maxLength} + {innerValue.length}/{maxLength}
)} {/* Action List */} diff --git a/package.json b/package.json index a5945dc30..575cd4995 100644 --- a/package.json +++ b/package.json @@ -270,8 +270,5 @@ "path": "./dist/antdx.min.js", "limit": "350 KiB" } - ], - "volta": { - "node": "18.20.8" - } -} + ] +} \ No newline at end of file From 8ec5ab973cacef55f330368be276d708c34a2985 Mon Sep 17 00:00:00 2001 From: pingtougea <1465164225@qq.com> Date: Wed, 12 Nov 2025 15:04:34 +0800 Subject: [PATCH 3/3] fix(lint): resolve biome check errors --- biome.json | 6 +- .../visual-regression/report-template.html | 184 +++++++++--------- tests/index.html | 30 +-- 3 files changed, 113 insertions(+), 107 deletions(-) diff --git a/biome.json b/biome.json index 5c022992d..fb388ab92 100644 --- a/biome.json +++ b/biome.json @@ -13,7 +13,8 @@ "!server", "!coverage", "!scripts/previewEditor/**/*", - "!package.json" + "!package.json", + "!scripts/visual-regression/report-template.html" ] }, "formatter": { @@ -57,7 +58,8 @@ "noArrayIndexKey": "off", "noConfusingVoidType": "off", "noThenProperty": "off", - "noTemplateCurlyInString": "off" + "noTemplateCurlyInString": "off", + "noNonNullAssertedOptionalChain": "off" }, "performance": { "noDelete": "off", diff --git a/scripts/visual-regression/report-template.html b/scripts/visual-regression/report-template.html index 22619e1f0..422f5d268 100644 --- a/scripts/visual-regression/report-template.html +++ b/scripts/visual-regression/report-template.html @@ -1,113 +1,115 @@ - - - - Ant Design Visual Diff Report - - + + + + {{reportContent}} + + + - if (e.target.tagName === 'IMG' && e.target.src) { - window.open(e.target.src, '_blank'); - } - }); - - - + \ No newline at end of file diff --git a/tests/index.html b/tests/index.html index 6f64fb77e..437181be4 100644 --- a/tests/index.html +++ b/tests/index.html @@ -1,17 +1,19 @@ - - - - Amazing Antd - - - -
- - + + + + Amazing Antd + + + + +
+ + + \ No newline at end of file