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/components/sender/__tests__/index.test.tsx b/components/sender/__tests__/index.test.tsx index dbc8ca60c..fb6c40b29 100644 --- a/components/sender/__tests__/index.test.tsx +++ b/components/sender/__tests__/index.test.tsx @@ -1,6 +1,6 @@ -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'; @@ -261,3 +261,42 @@ describe('Sender Component', () => { }); }); }); +describe('maxLength and showCount', () => { + it('should limit input length when maxLength is set', async () => { + const { container } = render(); + const textarea = container.querySelector('textarea')!; + await userEvent.type(textarea, '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('6/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..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; @@ -48,6 +47,8 @@ export interface SenderProps readOnly?: boolean; submitType?: SubmitType; disabled?: boolean; + maxLength?: number; + showCount?: boolean; onSubmit?: (message: string) => void; onChange?: ( value: string, @@ -131,6 +132,8 @@ const ForwardSender = React.forwardRef((props, ref) => { onPaste, onPasteFile, autoSize = { maxRows: 8 }, + maxLength, + showCount, ...rest } = props; @@ -144,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 ======================= @@ -360,7 +363,13 @@ const ForwardSender = React.forwardRef((props, ref) => { onPaste={onInternalPaste} variant="borderless" readOnly={readOnly} + maxLength={maxLength} /> + {showCount && typeof maxLength === 'number' && ( +
+ {innerValue.length}/{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..575cd4995 100644 --- a/package.json +++ b/package.json @@ -271,4 +271,4 @@ "limit": "350 KiB" } ] -} +} \ No newline at end of file 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