Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
046a033
feat: focus
kimteayon Dec 10, 2025
257997d
feat: focus
kimteayon Dec 10, 2025
bc9d70d
refactor: slot
kimteayon Dec 10, 2025
5e7014e
feat: sursor
kimteayon Dec 11, 2025
08758fb
feat: sursor
kimteayon Dec 11, 2025
0b4e6dd
feat: slot
kimteayon Dec 12, 2025
d8f676f
feat: slot
kimteayon Dec 12, 2025
df573b8
feat: slot
kimteayon Dec 12, 2025
33abf68
chore: welcome workflow
kimteayon Dec 12, 2025
b3ca89a
chore: welcome workflow
kimteayon Dec 12, 2025
93097ef
test: sender
kimteayon Dec 13, 2025
d79ba37
test: sender
kimteayon Dec 13, 2025
3fe36fe
feat: slotconfig
kimteayon Dec 13, 2025
52a68b1
feat: slotconfig
kimteayon Dec 13, 2025
8ecd69b
feat: slotconfig
kimteayon Dec 13, 2025
70d0b46
test: sender
kimteayon Dec 14, 2025
ba2765b
test: sender
kimteayon Dec 14, 2025
828fd3c
test: sender
kimteayon Dec 14, 2025
9adeb5b
test: sender
kimteayon Dec 14, 2025
94667c3
test: sender
kimteayon Dec 14, 2025
e5fc173
test: sender
kimteayon Dec 14, 2025
f99b392
feat: sender
kimteayon Dec 15, 2025
47cd9ff
Merge branch 'main' into slot-sender-refactor
kimteayon Dec 15, 2025
6610c41
feat: sender
kimteayon Dec 15, 2025
70f68e4
Merge branch 'slot-sender-refactor' of https://github.com/ant-design/…
kimteayon Dec 15, 2025
7843d90
feat: sender
kimteayon Dec 15, 2025
d27223a
feat: sender
kimteayon Dec 15, 2025
f625eff
feat: sender
kimteayon Dec 15, 2025
b369a06
feat: sender
kimteayon Dec 15, 2025
fe71a9f
feat: sender
kimteayon Dec 15, 2025
4b85a9b
fix: ts
kimteayon Dec 15, 2025
0168ea7
feat: sender
kimteayon Dec 15, 2025
110f1d8
feat: sender
kimteayon Dec 15, 2025
0936fe2
feat: sender
kimteayon Dec 15, 2025
241ff98
feat: sender
kimteayon Dec 15, 2025
2c84fc2
fix:l;int
kimteayon Dec 16, 2025
9ceca96
Merge branch 'main' of https://github.com/ant-design/x into slot-send…
kimteayon Dec 16, 2025
b958585
feat: install
kimteayon Dec 16, 2025
02e873b
Merge branch 'main' into slot-sender-refactor
kimteayon Dec 16, 2025
d170904
test: 测试用例
kimteayon Dec 17, 2025
673a372
Merge branch 'slot-sender-refactor' of https://github.com/ant-design/…
kimteayon Dec 17, 2025
0ab17ab
Merge branch 'main' into slot-sender-refactor
kimteayon Dec 17, 2025
cdc109b
test: sender
kimteayon Dec 17, 2025
b932817
Merge branch 'slot-sender-refactor' of https://github.com/ant-design/…
kimteayon Dec 17, 2025
15bd04e
test: sender
kimteayon Dec 17, 2025
9eeda12
test: sender
kimteayon Dec 17, 2025
ae4d14c
test: 测试用例
kimteayon Dec 17, 2025
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"@codecov/webpack-plugin": "^1.4.0",
"@madccc/duplicate-package-checker-webpack-plugin": "^1.0.0",
"@size-limit/file": "^11.1.5",
"antd": "^6.1.0",
"antd": "^6.1.1",
"circular-dependency-plugin": "^5.2.2",
"dekko": "^0.2.1",
"father": "^4.6.10",
Expand Down
2 changes: 1 addition & 1 deletion packages/x-markdown/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
"@types/react-dom": "^19.0.2",
"@types/react-syntax-highlighter": "^15.5.13",
"@umijs/mako": "^0.11.10",
"antd": "^6.1.0",
"antd": "^6.1.1",
"glob": "^11.0.0",
"react": "^19.0.0",
"react-dom": "^19.0.0"
Expand Down
2 changes: 1 addition & 1 deletion packages/x/.dumi/theme/builtins/Sandpack/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ const Sandpack: React.FC<React.PropsWithChildren<SandpackProps>> = ({
dependencies: {
react: '^19.0.2',
'react-dom': '^19.0.2',
antd: '^6.1.0',
antd: '^6.1.1',
'@ant-design/x': '^2.0.0',
'@ant-design/x-markdown': '^2.0.0',
'@ant-design/x-sdk': '^2.0.0',
Expand Down
4 changes: 1 addition & 3 deletions packages/x/.dumi/theme/slots/Header/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -194,9 +194,7 @@ const HeaderNavigation: React.FC<HeaderNavigationProps> = (props) => {
{locale[item.key as keyof typeof locale]}
</Link>
))}
{isZhCN && origin !== zhHrefOrigin && (
<a href={`${zhHrefOrigin}/index-cn`}>{locale['zhUrl']}</a>
)}
{isZhCN && origin !== zhHrefOrigin && <a href={`${zhHrefOrigin}/index-cn`}>{locale.zhUrl}</a>}
</nav>
);
};
Expand Down
6 changes: 1 addition & 5 deletions packages/x/components/attachments/FileList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -154,11 +154,7 @@ export default function FileList(props: FileListProps) {
extension={
<SilentUploader visible={showExtension} upload={upload}>
<Button
className={clsx(
classNames.upload,
contextClassNames.upload,
`${listCls}-upload-btn`,
)}
className={clsx(classNames.upload, contextClassNames.upload, `${listCls}-upload-btn`)}
style={{ ...styles.upload, ...contextStyles.upload }}
type="dashed"
>
Expand Down
162 changes: 162 additions & 0 deletions packages/x/components/attachments/__tests__/util.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import { isImageFileType, previewImage } from '../util';

describe('attachments util', () => {
describe('isImageFileType', () => {
it('should return true for image types', () => {
expect(isImageFileType('image/jpeg')).toBe(true);
expect(isImageFileType('image/png')).toBe(true);
expect(isImageFileType('image/gif')).toBe(true);
expect(isImageFileType('image/svg+xml')).toBe(true);
expect(isImageFileType('image/webp')).toBe(true);
});

it('should return false for non-image types', () => {
expect(isImageFileType('application/pdf')).toBe(false);
expect(isImageFileType('text/plain')).toBe(false);
expect(isImageFileType('video/mp4')).toBe(false);
expect(isImageFileType('audio/mp3')).toBe(false);
expect(isImageFileType('')).toBe(false);
});

it('should handle case sensitivity', () => {
expect(isImageFileType('IMAGE/JPEG')).toBe(false);
expect(isImageFileType('Image/Png')).toBe(false);
});
});

describe('previewImage', () => {
beforeEach(() => {
// Mock DOM APIs
Object.defineProperty(global, 'Image', {
value: class MockImage {
onload: (() => void) | null = null;
onerror: (() => void) | null = null;
crossOrigin = '';
src = '';
width = 0;
height = 0;

constructor() {
setTimeout(() => {
this.width = 400;
this.height = 300;
this.onload?.();
}, 0);
}
},
writable: true,
});

// Mock canvas
const mockContext = {
drawImage: jest.fn(),
} as any;
global.HTMLCanvasElement.prototype.getContext = jest.fn(() => mockContext);
global.HTMLCanvasElement.prototype.toDataURL = jest.fn(() => 'data:image/jpeg;base64,mocked');

// Mock URL APIs
global.URL.createObjectURL = jest.fn(() => 'blob:mocked-url');
global.URL.revokeObjectURL = jest.fn();

// Mock FileReader
const MockFileReader = jest.fn().mockImplementation(() => ({
readAsDataURL: jest.fn(),
readAsText: jest.fn(),
readAsArrayBuffer: jest.fn(),
onload: null,
onerror: null,
result: null,
EMPTY: 0,
LOADING: 1,
DONE: 2,
}));
global.FileReader = MockFileReader as any;

// Mock document.createElement
global.document.createElement = jest.fn((tagName: string) => {
if (tagName === 'canvas') {
return {
width: 0,
height: 0,
getContext: jest.fn(() => ({
drawImage: jest.fn(),
})),
toDataURL: jest.fn(() => 'data:image/jpeg;base64,mocked'),
style: {},
} as unknown as HTMLCanvasElement;
}
return {} as HTMLElement;
});

// Mock document.body properly
Object.defineProperty(global.document, 'body', {
value: {
appendChild: jest.fn(),
removeChild: jest.fn(),
},
writable: true,
});
});

afterEach(() => {
jest.clearAllMocks();
});

it('should return empty string for non-image files', async () => {
const file = new File(['content'], 'test.txt', { type: 'text/plain' });
const result = await previewImage(file);
expect(result).toBe('');
});

it('should return empty string for file without type', async () => {
const file = new File(['content'], 'test.jpg');
const result = await previewImage(file);
expect(result).toBe('');
});

it('should handle SVG files with FileReader', async () => {
const file = new File(['<svg></svg>'], 'test.svg', { type: 'image/svg+xml' });

// Mock FileReader behavior
const mockFileReaderInstance = {
readAsDataURL: jest.fn(),
result: 'data:image/svg+xml;base64,PHN2Zz4=',
onload: null as any,
};
const MockFileReader = jest.fn().mockImplementation(() => mockFileReaderInstance);
global.FileReader = MockFileReader as any;

const promise = previewImage(file);

// Trigger FileReader onload
await new Promise((resolve) => setTimeout(resolve, 0));
if (mockFileReaderInstance.onload) {
mockFileReaderInstance.onload({} as ProgressEvent<FileReader>);
}

await promise;

expect(MockFileReader).toHaveBeenCalled();
expect(mockFileReaderInstance.readAsDataURL).toHaveBeenCalledWith(file);
});

it('should handle regular image files with createObjectURL', async () => {
const file = new File(['image content'], 'test.jpg', { type: 'image/jpeg' });

const result = await previewImage(file);

expect(URL.createObjectURL).toHaveBeenCalledWith(file);
expect(result).toBe('data:image/jpeg;base64,mocked');
});

it('should handle null file', async () => {
const result = await previewImage(null as any);
expect(result).toBe('');
});

it('should handle undefined file', async () => {
const result = await previewImage(undefined as any);
expect(result).toBe('');
});
});
});
6 changes: 1 addition & 5 deletions packages/x/components/code-highlighter/CodeHighlighter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,7 @@ const CodeHighlighter = React.forwardRef<HTMLDivElement, CodeHighlighterProps>((

return (
<div
className={clsx(
`${prefixCls}-header`,
contextConfig.classNames?.header,
classNames.header,
)}
className={clsx(`${prefixCls}-header`, contextConfig.classNames?.header, classNames.header)}
style={{ ...contextConfig.styles?.header, ...styles.header }}
>
<span
Expand Down
14 changes: 3 additions & 11 deletions packages/x/components/file-card/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,9 @@ const List: React.FC<FileCardListProps> = (props) => {
const [pingEnd, setPingEnd] = React.useState(false);

const { root: classNameRoot, card: classNameCard, ...classNameOther } = classNames;
const mergedCls = clsx(
compCls,
rootClassName,
className,
classNameRoot,
hashId,
cssVarCls,
{
[`${prefixCls}-rtl`]: direction === 'rtl',
},
);
const mergedCls = clsx(compCls, rootClassName, className, classNameRoot, hashId, cssVarCls, {
[`${prefixCls}-rtl`]: direction === 'rtl',
});

const checkPing = () => {
const containerEle = containerRef.current;
Expand Down
9 changes: 3 additions & 6 deletions packages/x/components/mermaid/Mermaid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import mermaid from 'mermaid';
import React, { useEffect, useRef, useState } from 'react';
import SyntaxHighlighter from 'react-syntax-highlighter';
import useXComponentConfig from '../_util/hooks/use-x-component-config';
import warning from '../_util/warning';
import Actions from '../actions';
import type { ItemType } from '../actions/interface';
import locale_EN from '../locale/en_US';
Expand Down Expand Up @@ -101,7 +102,7 @@ const Mermaid: React.FC<MermaidProps> = React.memo((props) => {
const { svg } = await mermaid.render(id, newText, containerRef.current);
containerRef.current.innerHTML = svg;
} catch (error) {
console.warn(`Mermaid render failed: ${error}`);
warning(false, 'Mermaid', `Render failed: ${error}`);
}
}, 100);

Expand Down Expand Up @@ -311,11 +312,7 @@ const Mermaid: React.FC<MermaidProps> = React.memo((props) => {
/>
{renderType === RenderType.Code ? (
<div
className={clsx(
`${prefixCls}-code`,
contextConfig.classNames?.code,
classNames?.code,
)}
className={clsx(`${prefixCls}-code`, contextConfig.classNames?.code, classNames?.code)}
style={{ ...contextConfig.styles?.code, ...styles.code }}
>
<SyntaxHighlighter
Expand Down
10 changes: 6 additions & 4 deletions packages/x/components/mermaid/__tests__/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,14 @@ describe('Mermaid Component', () => {

it('should handle invalid mermaid syntax', async () => {
mockParse.mockResolvedValue(false);
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
const consoleSpy = jest.spyOn(console, 'error').mockImplementation();

render(<Mermaid>invalid syntax</Mermaid>);

await waitFor(() => {
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Mermaid render failed'));
expect(consoleSpy).toHaveBeenCalledWith(
expect.stringContaining('[antdx: Mermaid] Render failed'),
);
});

consoleSpy.mockRestore();
Expand Down Expand Up @@ -418,13 +420,13 @@ describe('Mermaid Component', () => {
describe('Error Handling', () => {
it('should handle mermaid render errors', async () => {
mockRender.mockRejectedValue(new Error('Render error'));
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
const consoleSpy = jest.spyOn(console, 'error').mockImplementation();

render(<Mermaid>{mermaidContent}</Mermaid>);

await waitFor(() => {
expect(consoleSpy).toHaveBeenCalledWith(
expect.stringContaining('Mermaid render failed: Error: Render error'),
expect.stringContaining('[antdx: Mermaid] Render failed: Error: Render error'),
);
});

Expand Down
9 changes: 6 additions & 3 deletions packages/x/components/notification/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useState } from 'react';
import warning from '../_util/warning';
import type { useNotificationType, XNotificationOpenArgs } from './interface';

let uuid = 0;
Expand All @@ -8,9 +9,11 @@ class XNotification {
static permissible: boolean;
constructor() {
XNotification.permissible = !!globalThis?.Notification;
if (!XNotification.permissible) {
console.warn('Notification API is not supported in this environment.');
}
warning(
XNotification.permissible,
'XNotification',
'Notification API is not supported in this environment.',
);
}

public get permission(): NotificationPermission {
Expand Down
Loading
Loading