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
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { render } from '@testing-library/react';
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import React from 'react';
import XMarkdown from '../../../XMarkdown';
import HighlightCode from '..';
Expand Down Expand Up @@ -177,4 +177,38 @@ plain text
expect(container.querySelector('pre')).toBeInTheDocument();
expect(container.textContent).toContain('plain text');
});

it('should handle copy action click', async () => {
const content = `\`\`\`javascript
console.log("test copy");
\`\`\``;
const mockClipboard = {
writeText: jest.fn().mockResolvedValue(undefined),
};

Object.defineProperty(navigator, 'clipboard', {
writable: true,
value: mockClipboard,
});

render(
<XMarkdown
content={content}
components={{
code: (props: Record<string, any>) => {
const { class: className, children } = props;
const lang = className?.match(/language-(\w+)/)?.[1] || '';
return <HighlightCode lang={lang}>{children}</HighlightCode>;
},
}}
/>,
);

const copyButton = screen.getByRole('img', { name: 'copy' });
fireEvent.click(copyButton);

await waitFor(() => {
expect(mockClipboard.writeText).toHaveBeenCalledWith('console.log("test copy");');
});
});
});
24 changes: 20 additions & 4 deletions packages/x-markdown/src/plugins/HighlightCode/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { CopyOutlined } from '@ant-design/icons';
import useXComponentConfig from '@ant-design/x/es/_util/hooks/use-x-component-config';
import Actions from '@ant-design/x/es/actions';
import type { ActionsProps } from '@ant-design/x/es/actions/interface';
import useLocale from '@ant-design/x/es/locale/useLocale';
import useXProviderContext from '@ant-design/x/es/x-provider/hooks/use-x-provider-context';
import locale_EN from '@ant-design/x/locale/en_US';
import { Button, message, Tooltip } from 'antd';
import { message } from 'antd';
import classnames from 'classnames';
import React from 'react';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
Expand Down Expand Up @@ -74,6 +76,22 @@ const HighlightCode: React.FC<HighlightCodeProps> = (props) => {
}
};

const handleActionsClick: ActionsProps['onClick'] = ({ keyPath }) => {
switch (keyPath[0]) {
case 'copy':
handleCopyCode();
break;
}
};

const actionItems = [
{
key: 'copy',
label: contextLocale.copy,
icon: <CopyOutlined />,
},
];

const renderTitle = () => {
if (header === null) return null;

Expand All @@ -99,9 +117,7 @@ const HighlightCode: React.FC<HighlightCodeProps> = (props) => {
>
{lang}
</span>
<Tooltip title={contextLocale.copy}>
<Button type="text" size="small" icon={<CopyOutlined />} onClick={handleCopyCode} />
</Tooltip>
<Actions items={actionItems} onClick={handleActionsClick} />
</div>
);
};
Expand Down
28 changes: 14 additions & 14 deletions packages/x-markdown/src/plugins/Mermaid/__test__/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ describe('Mermaid Plugin', () => {

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

const copyButton = screen.getByRole('button', { name: 'copy' });
const copyButton = screen.getByRole('img', { name: 'copy' });
fireEvent.click(copyButton);

await waitFor(() => {
Expand All @@ -154,7 +154,7 @@ describe('Mermaid Plugin', () => {

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

const copyButton = screen.getByRole('button', { name: 'copy' });
const copyButton = screen.getByRole('img', { name: 'copy' });

// 确保点击不会抛出错误
expect(() => fireEvent.click(copyButton)).not.toThrow();
Expand All @@ -177,7 +177,7 @@ describe('Mermaid Plugin', () => {

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

const copyButton = screen.getByRole('button', { name: 'copy' });
const copyButton = screen.getByRole('img', { name: 'copy' });
fireEvent.click(copyButton);

await waitFor(() => {
Expand All @@ -192,23 +192,23 @@ describe('Mermaid Plugin', () => {
it('should show zoom controls only in image mode', () => {
render(<Mermaid>{mermaidContent}</Mermaid>);

expect(screen.getByRole('button', { name: 'zoom-in' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'zoom-out' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Reset' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'download' })).toBeInTheDocument();
expect(screen.getByRole('img', { name: 'zoom-in' })).toBeInTheDocument();
expect(screen.getByRole('img', { name: 'zoom-out' })).toBeInTheDocument();
expect(screen.getByRole('img', { name: 'undo' })).toBeInTheDocument();
expect(screen.getByRole('img', { name: 'download' })).toBeInTheDocument();

const codeButton = screen.getByText('Code');
fireEvent.click(codeButton);

expect(screen.queryByRole('button', { name: 'zoom-in' })).not.toBeInTheDocument();
expect(screen.queryByRole('button', { name: 'zoom-out' })).not.toBeInTheDocument();
expect(screen.queryByRole('img', { name: 'zoom-in' })).not.toBeInTheDocument();
expect(screen.queryByRole('img', { name: 'zoom-out' })).not.toBeInTheDocument();
});

it('should handle zoom in/out', () => {
render(<Mermaid>{mermaidContent}</Mermaid>);

const zoomInButton = screen.getByRole('button', { name: 'zoom-in' });
const zoomOutButton = screen.getByRole('button', { name: 'zoom-out' });
const zoomInButton = screen.getByRole('img', { name: 'zoom-in' });
const zoomOutButton = screen.getByRole('img', { name: 'zoom-out' });

fireEvent.click(zoomInButton);
fireEvent.click(zoomOutButton);
Expand All @@ -217,7 +217,7 @@ describe('Mermaid Plugin', () => {
it('should handle reset functionality', () => {
render(<Mermaid>{mermaidContent}</Mermaid>);

const resetButton = screen.getByRole('button', { name: 'Reset' });
const resetButton = screen.getByRole('img', { name: 'undo' });
fireEvent.click(resetButton);
});
});
Expand Down Expand Up @@ -500,7 +500,7 @@ describe('Mermaid Plugin', () => {
container.querySelector = mockQuerySelector;
}

const downloadButton = screen.getByRole('button', { name: 'download' });
const downloadButton = screen.getByRole('img', { name: 'download' });
fireEvent.click(downloadButton);

// Wait for async operations
Expand Down Expand Up @@ -542,7 +542,7 @@ describe('Mermaid Plugin', () => {
container.querySelector = mockQuerySelector;
}

const downloadButton = screen.getByRole('button', { name: 'download' });
const downloadButton = screen.getByRole('img', { name: 'download' });
fireEvent.click(downloadButton);

// Should not throw and should return early
Expand Down
98 changes: 63 additions & 35 deletions packages/x-markdown/src/plugins/Mermaid/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import { CopyOutlined, DownloadOutlined, ZoomInOutlined, ZoomOutOutlined } from '@ant-design/icons';
import {
CopyOutlined,
DownloadOutlined,
UndoOutlined,
ZoomInOutlined,
ZoomOutOutlined,
} from '@ant-design/icons';
import useXComponentConfig from '@ant-design/x/es/_util/hooks/use-x-component-config';
import Actions from '@ant-design/x/es/actions';
import type { ActionsProps } from '@ant-design/x/es/actions/interface';
import useLocale from '@ant-design/x/es/locale/useLocale';
import useXProviderContext from '@ant-design/x/es/x-provider/hooks/use-x-provider-context';
import locale_EN from '@ant-design/x/locale/en_US';
import { Button, message, Segmented, Space, Tooltip } from 'antd';
import { message, Segmented } from 'antd';
import classnames from 'classnames';
import throttle from 'lodash.throttle';
import mermaid from 'mermaid';
Expand Down Expand Up @@ -222,6 +230,58 @@ const Mermaid: React.FC<MermaidProps> = React.memo((props) => {
}
};

const handleActionsClick: ActionsProps['onClick'] = ({ keyPath }) => {
switch (keyPath[0]) {
case 'copy':
handleCopyCode();
break;
case 'zoomOut':
handleZoomOut();
break;
case 'zoomIn':
handleZoomIn();
break;
case 'zoomReset':
handleReset();
break;
case 'download':
handleDownload();
break;
}
};

const actionItems = [
{
key: 'copy',
label: contextLocale.copy,
icon: <CopyOutlined />,
},
{
key: 'zoomIn',
label: contextLocale.zoomIn,
icon: <ZoomInOutlined />,
},
{
key: 'zoomOut',
label: contextLocale.zoomOut,
icon: <ZoomOutOutlined />,
},
{
key: 'zoomReset',
label: contextLocale.zoomReset,
icon: <UndoOutlined />,
},
{
key: 'download',
label: contextLocale.download,
icon: <DownloadOutlined />,
},
];

const activeActions = actionItems.filter(
(item) => item.key === 'copy' || renderType === RenderType.Image,
);

const renderHeader = () => {
if (header === null) return null;
if (header) return header;
Expand All @@ -244,39 +304,7 @@ const Mermaid: React.FC<MermaidProps> = React.memo((props) => {
value={renderType}
onChange={setRenderType}
/>
<Space>
<Tooltip title={contextLocale.copy}>
<Button type="text" size="small" icon={<CopyOutlined />} onClick={handleCopyCode} />
</Tooltip>
{renderType === RenderType.Image ? (
<>
<Tooltip title={contextLocale.zoomOut}>
<Button type="text" size="small" icon={<ZoomInOutlined />} onClick={handleZoomIn} />
</Tooltip>
<Tooltip title={contextLocale.zoomIn}>
<Button
type="text"
size="small"
icon={<ZoomOutOutlined />}
onClick={handleZoomOut}
/>
</Tooltip>
<Tooltip title={contextLocale.zoomReset}>
<Button type="text" size="small" onClick={handleReset}>
{contextLocale.zoomReset}
</Button>
</Tooltip>
<Tooltip title={contextLocale.download}>
<Button
type="text"
size="small"
icon={<DownloadOutlined />}
onClick={handleDownload}
/>
</Tooltip>
</>
) : null}
</Space>
<Actions items={activeActions} onClick={handleActionsClick} />
</div>
);
};
Expand Down
4 changes: 2 additions & 2 deletions packages/x/components/locale/zh_CN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ const localeValues: Required<xLocale & xMarkdownLocale> = {
Mermaid: {
copySuccess: '复制成功',
copy: '复制代码',
zoomIn: '缩小',
zoomOut: '放大',
zoomIn: '放大',
zoomOut: '缩小',
zoomReset: '重置',
download: '下载',
code: '代码',
Expand Down
2 changes: 1 addition & 1 deletion packages/x/docs/x-markdown/demo/_utils/adx-markdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ Ant Design X 是一款AI应用复合工具集,融合了 UI 组件库、流式
npm install @ant-design/x --save # React 版本
\`\`\`

## @ant-design/-sdk
## @ant-design/x-sdk

### 特性

Expand Down
Loading