diff --git a/packages/x-markdown/src/plugins/Mermaid/__test__/index.test.tsx b/packages/x-markdown/src/plugins/Mermaid/__test__/index.test.tsx index ef8d09195..7798b465d 100644 --- a/packages/x-markdown/src/plugins/Mermaid/__test__/index.test.tsx +++ b/packages/x-markdown/src/plugins/Mermaid/__test__/index.test.tsx @@ -2,6 +2,13 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import React from 'react'; import Mermaid from '../index'; +const getActionButtonByLabel = (label: string): HTMLElement => { + const icon = screen.getByLabelText(label); + const button = icon.closest('.ant-actions-item'); + if (!button) throw new Error(`Action button with label '${label}' not found.`); + return button as HTMLElement; +}; + // Mock mermaid jest.mock('mermaid', () => ({ initialize: jest.fn(), @@ -135,7 +142,7 @@ describe('Mermaid Plugin', () => { render({mermaidContent}); - const copyButton = screen.getByRole('button', { name: 'copy' }); + const copyButton = getActionButtonByLabel('copy'); fireEvent.click(copyButton); await waitFor(() => { @@ -154,7 +161,7 @@ describe('Mermaid Plugin', () => { render({mermaidContent}); - const copyButton = screen.getByRole('button', { name: 'copy' }); + const copyButton = getActionButtonByLabel('copy'); // 确保点击不会抛出错误 expect(() => fireEvent.click(copyButton)).not.toThrow(); @@ -177,7 +184,7 @@ describe('Mermaid Plugin', () => { render({mermaidContent}); - const copyButton = screen.getByRole('button', { name: 'copy' }); + const copyButton = getActionButtonByLabel('copy'); fireEvent.click(copyButton); await waitFor(() => { @@ -192,23 +199,23 @@ describe('Mermaid Plugin', () => { it('should show zoom controls only in image mode', () => { render({mermaidContent}); - 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.getByLabelText('zoom-in')).toBeInTheDocument(); + expect(screen.getByLabelText('zoom-out')).toBeInTheDocument(); + expect(screen.getByLabelText('undo')).toBeInTheDocument(); + expect(screen.getByLabelText('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.queryByLabelText('zoom-in')).not.toBeInTheDocument(); + expect(screen.queryByLabelText('zoom-out')).not.toBeInTheDocument(); }); it('should handle zoom in/out', () => { render({mermaidContent}); - const zoomInButton = screen.getByRole('button', { name: 'zoom-in' }); - const zoomOutButton = screen.getByRole('button', { name: 'zoom-out' }); + const zoomInButton = getActionButtonByLabel('zoom-in'); + const zoomOutButton = getActionButtonByLabel('zoom-out'); fireEvent.click(zoomInButton); fireEvent.click(zoomOutButton); @@ -217,7 +224,7 @@ describe('Mermaid Plugin', () => { it('should handle reset functionality', () => { render({mermaidContent}); - const resetButton = screen.getByRole('button', { name: 'Reset' }); + const resetButton = getActionButtonByLabel('undo'); fireEvent.click(resetButton); }); }); @@ -500,7 +507,7 @@ describe('Mermaid Plugin', () => { container.querySelector = mockQuerySelector; } - const downloadButton = screen.getByRole('button', { name: 'download' }); + const downloadButton = getActionButtonByLabel('download'); fireEvent.click(downloadButton); // Wait for async operations @@ -542,7 +549,7 @@ describe('Mermaid Plugin', () => { container.querySelector = mockQuerySelector; } - const downloadButton = screen.getByRole('button', { name: 'download' }); + const downloadButton = getActionButtonByLabel('download'); fireEvent.click(downloadButton); // Should not throw and should return early diff --git a/packages/x-markdown/src/plugins/Mermaid/index.tsx b/packages/x-markdown/src/plugins/Mermaid/index.tsx index bb1639b9d..99f98d00d 100644 --- a/packages/x-markdown/src/plugins/Mermaid/index.tsx +++ b/packages/x-markdown/src/plugins/Mermaid/index.tsx @@ -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 { ItemType } 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'; @@ -222,6 +230,46 @@ const Mermaid: React.FC = React.memo((props) => { } }; + // ============================ Action Items ============================ + const baseItems: ItemType[] = [ + { + key: 'copy', + icon: , + label: contextLocale.copy, + onItemClick: handleCopyCode, + }, + ]; + + const imageItems: ItemType[] = [ + ...baseItems, + { + key: 'zoomIn', + icon: , + label: contextLocale.zoomIn, + onItemClick: handleZoomIn, + }, + { + key: 'zoomOut', + icon: , + label: contextLocale.zoomOut, + onItemClick: handleZoomOut, + }, + { + key: 'zoomReset', + icon: , + label: contextLocale.zoomReset, + onItemClick: handleReset, + }, + { + key: 'download', + icon: , + label: contextLocale.download, + onItemClick: handleDownload, + }, + ]; + + const actionItems = renderType === RenderType.Image ? imageItems : baseItems; + const renderHeader = () => { if (header === null) return null; if (header) return header; @@ -244,39 +292,7 @@ const Mermaid: React.FC = React.memo((props) => { value={renderType} onChange={setRenderType} /> - - - - - -