Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"!server",
"!coverage",
"!scripts/previewEditor/**/*",
"!package.json"
"!package.json",
"!scripts/visual-regression/report-template.html"
]
},
"formatter": {
Expand Down Expand Up @@ -57,7 +58,8 @@
"noArrayIndexKey": "off",
"noConfusingVoidType": "off",
"noThenProperty": "off",
"noTemplateCurlyInString": "off"
"noTemplateCurlyInString": "off",
"noNonNullAssertedOptionalChain": "off"
},
"performance": {
"noDelete": "off",
Expand Down
43 changes: 41 additions & 2 deletions components/sender/__tests__/index.test.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -261,3 +261,42 @@ describe('Sender Component', () => {
});
});
});
describe('maxLength and showCount', () => {
it('should limit input length when maxLength is set', async () => {
const { container } = render(<Sender maxLength={5} />);
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(<Sender maxLength={10} showCount value="text" />);
expect(container.querySelector('.ant-sender-count')?.textContent).toBe('4/10');
});

it('should not display count when showCount is false', () => {
const { container } = render(<Sender maxLength={10} showCount={false} />);
expect(container.querySelector('.ant-sender-count')).toBeNull();
});

it('should update count in real-time', () => {
const { container } = render(<Sender maxLength={20} showCount />);
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(
<Sender maxLength={10} showCount value="initial" onChange={onChange} />,
);
expect(container.querySelector('.ant-sender-count')?.textContent).toBe('7/10');

rerender(<Sender maxLength={10} showCount value="update" onChange={onChange} />);
expect(container.querySelector('.ant-sender-count')?.textContent).toBe('6/10');
});
});
1 change: 1 addition & 0 deletions components/sender/demo/basic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const Demo: React.FC = () => {
/>
<Sender value="Force as loading" loading readOnly />
<Sender disabled value="Set to disabled" />
<Sender maxLength={20} showCount placeholder="最多20字" />
</Flex>
);
};
Expand Down
19 changes: 14 additions & 5 deletions components/sender/index.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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<typeof Input.TextArea>;
Expand Down Expand Up @@ -48,6 +47,8 @@ export interface SenderProps
readOnly?: boolean;
submitType?: SubmitType;
disabled?: boolean;
maxLength?: number;
showCount?: boolean;
onSubmit?: (message: string) => void;
onChange?: (
value: string,
Expand Down Expand Up @@ -131,6 +132,8 @@ const ForwardSender = React.forwardRef<SenderRef, SenderProps>((props, ref) => {
onPaste,
onPasteFile,
autoSize = { maxRows: 8 },
maxLength,
showCount,
...rest
} = props;

Expand All @@ -144,8 +147,8 @@ const ForwardSender = React.forwardRef<SenderRef, SenderProps>((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 =======================
Expand Down Expand Up @@ -360,7 +363,13 @@ const ForwardSender = React.forwardRef<SenderRef, SenderProps>((props, ref) => {
onPaste={onInternalPaste}
variant="borderless"
readOnly={readOnly}
maxLength={maxLength}
/>
{showCount && typeof maxLength === 'number' && (
<div className={`${prefixCls}-count`}>
{innerValue.length}/{maxLength}
</div>
)}
{/* Action List */}
{actionNode && (
<div
Expand Down
12 changes: 12 additions & 0 deletions components/sender/style/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,18 @@ const genSenderStyle: GenerateStyle<SenderToken> = (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',
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -271,4 +271,4 @@
"limit": "350 KiB"
}
]
}
}
184 changes: 93 additions & 91 deletions scripts/visual-regression/report-template.html
Original file line number Diff line number Diff line change
@@ -1,113 +1,115 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Ant Design Visual Diff Report</title>
<link href="https://unpkg.com/antd@latest/dist/reset.css" rel="stylesheet" />
<style>
body {
padding-top: 16px;
padding-bottom: 16px;
}

/* figcaption */
figcaption {
color: #a3a3a3;
text-align: center;
}

figure {
cursor: pointer;
}

/* Table Styles */
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Ant Design Visual Diff Report</title>
<link href="https://unpkg.com/antd@latest/dist/reset.css" rel="stylesheet" />
<style>
body {
padding-top: 16px;
padding-bottom: 16px;
}

/* figcaption */
figcaption {
color: #a3a3a3;
text-align: center;
}

figure {
cursor: pointer;
}

/* Table Styles */
table {
width: 100%;
border-collapse: collapse;
table-layout: fixed;
}

th,
td {
padding: 8px;
text-align: left;
border-bottom: 1px solid #ddd;
vertical-align: top;
}

td img {
max-width: 100%;
}

th,
td {
width: 33%;
}

th {
background-color: #f2f2f2;
}

tr:nth-child(even) {
background-color: #f9f9f9;
}

/* Hover Effect */
tr:hover {
background-color: #e5e5e5;
}

/* Responsive Table */
@media screen and (max-width: 600px) and (min-width: 1px) {
table {
width: 100%;
border-collapse: collapse;
table-layout: fixed;
}

th,
td {
padding: 8px;
text-align: left;
border-bottom: 1px solid #ddd;
vertical-align: top;
}

td img {
max-width: 100%;
border: 0;
}

th,
td {
width: 33%;
display: block;
padding: 6px;
border: none;
}

th {
background-color: #f2f2f2;
text-align: left;
background-color: transparent;
}

tr:nth-child(even) {
background-color: #f9f9f9;
background-color: transparent;
}

/* Hover Effect */
tr:hover {
background-color: #e5e5e5;
tr:nth-child(odd) {
background-color: transparent;
}

/* Responsive Table */
@media screen and (max-width: 600px) and (min-width: 1px) {
table {
border: 0;
}

th,
td {
display: block;
padding: 6px;
border: none;
}

th {
text-align: left;
background-color: transparent;
}

tr:nth-child(even) {
background-color: transparent;
}

tr:nth-child(odd) {
background-color: transparent;
tr:hover {
background-color: transparent;
}
}
</style>
</head>

<body>
{{reportContent}}

<script>
window.addEventListener('click', (e) => {
if (e.target.tagName === 'FIGCAPTION') {
// get previous sibling
const img = e.target.previousElementSibling
if (img.tagName === 'IMG' && img.src) {
window.open(img.src, '_blank')
}
}

tr:hover {
background-color: transparent;
}
if (e.target.tagName === 'IMG' && e.target.src) {
window.open(e.target.src, '_blank')
}
</style>
</head>

<body>
{{reportContent}}

<script>
window.addEventListener('click', function (e) {
if (e.target.tagName === 'FIGCAPTION') {
// get previous sibling
const img = e.target.previousElementSibling;
if (img.tagName === 'IMG' && img.src) {
window.open(img.src, '_blank');
}
}
});
</script>
</body>

if (e.target.tagName === 'IMG' && e.target.src) {
window.open(e.target.src, '_blank');
}
});
</script>
</body>
</html>
</html>
Loading
Loading