From 1a1af7748b7e4164ccb09e47d919af0c67218475 Mon Sep 17 00:00:00 2001 From: wryyyds7 Date: Thu, 16 Jul 2026 11:00:09 +0800 Subject: [PATCH 1/3] feat(CodeHighlighter): support showLineNumber, wrapLongLines, showCopyButton props Add explicit props to CodeHighlighter for common configurations that previously required passing through highlightProps: - showLineNumber: control line number display (default: false) - wrapLongLines: control long line wrapping (default: false) - showCopyButton: control copy button visibility in default header (default: true) Closes #1951 --- .../code-highlighter/CodeHighlighter.tsx | 7 +- .../code-highlighter/__tests__/index.test.tsx | 162 ++++++++++++++++++ .../code-highlighter/demo/configurations.tsx | 42 +++++ .../code-highlighter/index.en-US.md | 4 + .../code-highlighter/index.zh-CN.md | 4 + .../components/code-highlighter/interface.ts | 18 ++ 6 files changed, 236 insertions(+), 1 deletion(-) create mode 100644 packages/x/components/code-highlighter/demo/configurations.tsx diff --git a/packages/x/components/code-highlighter/CodeHighlighter.tsx b/packages/x/components/code-highlighter/CodeHighlighter.tsx index 4438147ac7..745637e9fc 100644 --- a/packages/x/components/code-highlighter/CodeHighlighter.tsx +++ b/packages/x/components/code-highlighter/CodeHighlighter.tsx @@ -71,6 +71,9 @@ const CodeHighlighter = React.forwardRef(( style = {}, highlightProps, prismLightMode = true, + showLineNumber = false, + wrapLongLines = false, + showCopyButton = true, ...restProps } = props; @@ -141,7 +144,7 @@ const CodeHighlighter = React.forwardRef(( > {lang} - + {showCopyButton && } ); } @@ -160,6 +163,8 @@ const CodeHighlighter = React.forwardRef(( { }); }); + describe('showLineNumber', () => { + it('should render line numbers when showLineNumber is true', async () => { + const { container } = render( + + {`console.log("test");`} + , + ); + await waitFor(() => { + expect(container.querySelector('pre')).toBeInTheDocument(); + }); + // react-syntax-highlighter renders line numbers with class "react-syntax-highlighter-line-number" + expect( + container.querySelector('.react-syntax-highlighter-line-number'), + ).toBeInTheDocument(); + }); + + it('should not render line numbers by default', async () => { + const { container } = render( + {`console.log("test");`}, + ); + await waitFor(() => { + expect(container.querySelector('pre')).toBeInTheDocument(); + }); + expect( + container.querySelector('.react-syntax-highlighter-line-number'), + ).not.toBeInTheDocument(); + }); + + it('should not render line numbers when showLineNumber is false', async () => { + const { container } = render( + + {`console.log("test");`} + , + ); + await waitFor(() => { + expect(container.querySelector('pre')).toBeInTheDocument(); + }); + expect( + container.querySelector('.react-syntax-highlighter-line-number'), + ).not.toBeInTheDocument(); + }); + }); + + describe('wrapLongLines', () => { + it('should apply wrapLongLines style when enabled', async () => { + const { container } = render( + + {`console.log("test");`} + , + ); + await waitFor(() => { + expect(container.querySelector('pre')).toBeInTheDocument(); + }); + // react-syntax-highlighter sets whiteSpace: 'pre-wrap' on the code element + const codeElement = container.querySelector('code'); + expect(codeElement).toBeInTheDocument(); + expect(codeElement?.style.whiteSpace).toBe('pre-wrap'); + }); + + it('should not apply wrapLongLines style by default', async () => { + const { container } = render( + {`console.log("test");`}, + ); + await waitFor(() => { + expect(container.querySelector('pre')).toBeInTheDocument(); + }); + const codeElement = container.querySelector('code'); + expect(codeElement).toBeInTheDocument(); + expect(codeElement?.style.whiteSpace).not.toBe('pre-wrap'); + }); + }); + + describe('showCopyButton', () => { + it('should render copy button by default', async () => { + const { container } = render( + {`console.log("test");`}, + ); + await waitFor(() => { + expect(container.querySelector('.ant-codeHighlighter-header')).toBeInTheDocument(); + }); + // The default header should contain a copy button (Actions.Copy renders a button) + const buttons = container.querySelectorAll('.ant-codeHighlighter-header button'); + expect(buttons.length).toBeGreaterThan(0); + }); + + it('should not render copy button when showCopyButton is false', async () => { + const { container } = render( + + {`console.log("test");`} + , + ); + await waitFor(() => { + expect(container.querySelector('.ant-codeHighlighter-header')).toBeInTheDocument(); + }); + // Header should still show the language name + expect(container.querySelector('.ant-codeHighlighter-header')?.textContent).toContain( + 'javascript', + ); + // But no copy button (no buttons in header) + const buttons = container.querySelectorAll('.ant-codeHighlighter-header button'); + expect(buttons.length).toBe(0); + }); + + it('should render copy button when showCopyButton is true', async () => { + const { container } = render( + + {`console.log("test");`} + , + ); + await waitFor(() => { + expect(container.querySelector('.ant-codeHighlighter-header')).toBeInTheDocument(); + }); + const buttons = container.querySelectorAll('.ant-codeHighlighter-header button'); + expect(buttons.length).toBeGreaterThan(0); + }); + + it('should not affect custom header', async () => { + const { container } = render( + custom} + > + {`console.log("test");`} + , + ); + await waitFor(() => { + expect(container.querySelector('.myCustomHeader')).toBeInTheDocument(); + }); + // Custom header should be rendered as-is + expect(container.querySelector('.myCustomHeader')?.textContent).toContain('custom'); + }); + }); + + describe('combined props', () => { + it('should work with showLineNumber, wrapLongLines, and showCopyButton together', async () => { + const { container } = render( + + {`console.log("test");`} + , + ); + await waitFor(() => { + expect(container.querySelector('pre')).toBeInTheDocument(); + }); + // Line numbers should be present + expect( + container.querySelector('.react-syntax-highlighter-line-number'), + ).toBeInTheDocument(); + // Wrap should be applied + const codeElement = container.querySelector('code'); + expect(codeElement?.style.whiteSpace).toBe('pre-wrap'); + // No copy button + const buttons = container.querySelectorAll('.ant-codeHighlighter-header button'); + expect(buttons.length).toBe(0); + }); + }); + describe('displayName', () => { it('should have displayName in development', () => { const originalEnv = process.env.NODE_ENV; diff --git a/packages/x/components/code-highlighter/demo/configurations.tsx b/packages/x/components/code-highlighter/demo/configurations.tsx new file mode 100644 index 0000000000..4f7e022118 --- /dev/null +++ b/packages/x/components/code-highlighter/demo/configurations.tsx @@ -0,0 +1,42 @@ +import { CodeHighlighter } from '@ant-design/x'; +import React from 'react'; + +const App: React.FC = () => { + const code = `function fibonacci(n) { + if (n <= 1) return n; + let a = 0, b = 1; + for (let i = 2; i <= n; i++) { + [a, b] = [b, a + b]; + } + return b; +} + +const result = fibonacci(10); +console.log(result); // 55`; + + return ( +
+

Show Line Number

+ + {code} + + +

Wrap Long Lines

+ + {`const config = { apiKey: "sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", endpoint: "https://api.example.com/v1/chat/completions", model: "gpt-4" };`} + + +

Hide Copy Button

+ + {code} + + +

All Combined

+ + {code} + +
+ ); +}; + +export default App; \ No newline at end of file diff --git a/packages/x/components/code-highlighter/index.en-US.md b/packages/x/components/code-highlighter/index.en-US.md index f48ef8f27e..62bb74ebb6 100644 --- a/packages/x/components/code-highlighter/index.en-US.md +++ b/packages/x/components/code-highlighter/index.en-US.md @@ -22,6 +22,7 @@ The CodeHighlighter component is used in scenarios where you need to display cod Basic Custom Header +Configurations With XMarkdown ## API @@ -39,6 +40,9 @@ For common properties, refer to: [Common Properties](/docs/react/common-props). | classNames | Style class names | `string` | - | | highlightProps | Code highlighting configuration | [`highlightProps`](https://github.com/react-syntax-highlighter/react-syntax-highlighter?tab=readme-ov-file#props) | - | | prismLightMode | Whether to use Prism light mode to automatically load language support based on lang prop for smaller bundle size | `boolean` | `true` | +| showLineNumber | Whether to show line numbers | `boolean` | `false` | +| wrapLongLines | Whether to wrap long lines | `boolean` | `false` | +| showCopyButton | Whether to show copy button (only effective with default header) | `boolean` | `true` | ### CodeHighlighterRef diff --git a/packages/x/components/code-highlighter/index.zh-CN.md b/packages/x/components/code-highlighter/index.zh-CN.md index 5c138cc9d9..9060d62db9 100644 --- a/packages/x/components/code-highlighter/index.zh-CN.md +++ b/packages/x/components/code-highlighter/index.zh-CN.md @@ -23,6 +23,7 @@ CodeHighlighter 组件用于需要展示带有语法高亮的代码片段的场 基本 自定义 Header +配置项 配合 XMarkdown ## API @@ -38,6 +39,9 @@ CodeHighlighter 组件用于需要展示带有语法高亮的代码片段的场 | header | 头部内容,为 `false` 时不显示头部 | `React.ReactNode \| (() => React.ReactNode \| false) \| false` | - | | highlightProps | 代码高亮配置,透传给 react-syntax-highlighter | [`SyntaxHighlighterProps`](https://github.com/react-syntax-highlighter/react-syntax-highlighter?tab=readme-ov-file#props) | - | | prismLightMode | 是否使用 Prism 轻量模式,根据 `lang` 自动按需加载语言支持以减少打包体积 | `boolean` | `true` | +| showLineNumber | 是否显示行号 | `boolean` | `false` | +| wrapLongLines | 是否自动换行(长行换行) | `boolean` | `false` | +| showCopyButton | 是否显示复制按钮(仅在默认 header 下生效) | `boolean` | `true` | ### CodeHighlighterRef diff --git a/packages/x/components/code-highlighter/interface.ts b/packages/x/components/code-highlighter/interface.ts index fc0fb7d685..13221319a4 100644 --- a/packages/x/components/code-highlighter/interface.ts +++ b/packages/x/components/code-highlighter/interface.ts @@ -61,4 +61,22 @@ export interface CodeHighlighterProps * ``` */ prismLightMode?: boolean; + /** + * @desc 是否显示行号 + * @descEN Whether to show line numbers + * @default false + */ + showLineNumber?: boolean; + /** + * @desc 是否自动换行(长行换行) + * @descEN Whether to wrap long lines + * @default false + */ + wrapLongLines?: boolean; + /** + * @desc 是否显示复制按钮(仅在默认 header 下生效) + * @descEN Whether to show copy button (only effective with default header) + * @default true + */ + showCopyButton?: boolean; } From 1c8f9ebecd9a79ac3f6a93825be6be73b6f6bc03 Mon Sep 17 00:00:00 2001 From: wryyyds7 Date: Thu, 16 Jul 2026 11:16:22 +0800 Subject: [PATCH 2/3] refactor(CodeHighlighter): rename showLineNumber to showLineNumbers & fix copy text trailing newline - Rename showLineNumber to showLineNumbers to match react-syntax-highlighter naming convention (address review feedback) - Strip trailing newline from Actions.Copy text to match rendered content (address review feedback) --- .../code-highlighter/CodeHighlighter.tsx | 6 +++--- .../code-highlighter/__tests__/index.test.tsx | 16 ++++++++-------- .../code-highlighter/demo/configurations.tsx | 4 ++-- .../x/components/code-highlighter/index.en-US.md | 2 +- .../x/components/code-highlighter/index.zh-CN.md | 2 +- .../x/components/code-highlighter/interface.ts | 2 +- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/x/components/code-highlighter/CodeHighlighter.tsx b/packages/x/components/code-highlighter/CodeHighlighter.tsx index 745637e9fc..448a92b0d2 100644 --- a/packages/x/components/code-highlighter/CodeHighlighter.tsx +++ b/packages/x/components/code-highlighter/CodeHighlighter.tsx @@ -71,7 +71,7 @@ const CodeHighlighter = React.forwardRef(( style = {}, highlightProps, prismLightMode = true, - showLineNumber = false, + showLineNumbers = false, wrapLongLines = false, showCopyButton = true, ...restProps @@ -144,7 +144,7 @@ const CodeHighlighter = React.forwardRef(( > {lang} - {showCopyButton && } + {showCopyButton && } ); } @@ -163,7 +163,7 @@ const CodeHighlighter = React.forwardRef(( { const { container } = render( {`console.log("test");`} , @@ -512,10 +512,10 @@ describe('CodeHighlighter', () => { }); }); - describe('showLineNumber', () => { - it('should render line numbers when showLineNumber is true', async () => { + describe('showLineNumbers', () => { + it('should render line numbers when showLineNumbers is true', async () => { const { container } = render( - + {`console.log("test");`} , ); @@ -540,9 +540,9 @@ describe('CodeHighlighter', () => { ).not.toBeInTheDocument(); }); - it('should not render line numbers when showLineNumber is false', async () => { + it('should not render line numbers when showLineNumbers is false', async () => { const { container } = render( - + {`console.log("test");`} , ); @@ -647,11 +647,11 @@ describe('CodeHighlighter', () => { }); describe('combined props', () => { - it('should work with showLineNumber, wrapLongLines, and showCopyButton together', async () => { + it('should work with showLineNumbers, wrapLongLines, and showCopyButton together', async () => { const { container } = render( diff --git a/packages/x/components/code-highlighter/demo/configurations.tsx b/packages/x/components/code-highlighter/demo/configurations.tsx index 4f7e022118..60fe91810c 100644 --- a/packages/x/components/code-highlighter/demo/configurations.tsx +++ b/packages/x/components/code-highlighter/demo/configurations.tsx @@ -17,7 +17,7 @@ console.log(result); // 55`; return (

Show Line Number

- + {code} @@ -32,7 +32,7 @@ console.log(result); // 55`;

All Combined

- + {code}
diff --git a/packages/x/components/code-highlighter/index.en-US.md b/packages/x/components/code-highlighter/index.en-US.md index 62bb74ebb6..8098c01634 100644 --- a/packages/x/components/code-highlighter/index.en-US.md +++ b/packages/x/components/code-highlighter/index.en-US.md @@ -40,7 +40,7 @@ For common properties, refer to: [Common Properties](/docs/react/common-props). | classNames | Style class names | `string` | - | | highlightProps | Code highlighting configuration | [`highlightProps`](https://github.com/react-syntax-highlighter/react-syntax-highlighter?tab=readme-ov-file#props) | - | | prismLightMode | Whether to use Prism light mode to automatically load language support based on lang prop for smaller bundle size | `boolean` | `true` | -| showLineNumber | Whether to show line numbers | `boolean` | `false` | +| showLineNumbers | Whether to show line numbers | `boolean` | `false` | | wrapLongLines | Whether to wrap long lines | `boolean` | `false` | | showCopyButton | Whether to show copy button (only effective with default header) | `boolean` | `true` | diff --git a/packages/x/components/code-highlighter/index.zh-CN.md b/packages/x/components/code-highlighter/index.zh-CN.md index 9060d62db9..470d5c1c08 100644 --- a/packages/x/components/code-highlighter/index.zh-CN.md +++ b/packages/x/components/code-highlighter/index.zh-CN.md @@ -39,7 +39,7 @@ CodeHighlighter 组件用于需要展示带有语法高亮的代码片段的场 | header | 头部内容,为 `false` 时不显示头部 | `React.ReactNode \| (() => React.ReactNode \| false) \| false` | - | | highlightProps | 代码高亮配置,透传给 react-syntax-highlighter | [`SyntaxHighlighterProps`](https://github.com/react-syntax-highlighter/react-syntax-highlighter?tab=readme-ov-file#props) | - | | prismLightMode | 是否使用 Prism 轻量模式,根据 `lang` 自动按需加载语言支持以减少打包体积 | `boolean` | `true` | -| showLineNumber | 是否显示行号 | `boolean` | `false` | +| showLineNumbers | 是否显示行号 | `boolean` | `false` | | wrapLongLines | 是否自动换行(长行换行) | `boolean` | `false` | | showCopyButton | 是否显示复制按钮(仅在默认 header 下生效) | `boolean` | `true` | diff --git a/packages/x/components/code-highlighter/interface.ts b/packages/x/components/code-highlighter/interface.ts index 13221319a4..e0eb49bb68 100644 --- a/packages/x/components/code-highlighter/interface.ts +++ b/packages/x/components/code-highlighter/interface.ts @@ -66,7 +66,7 @@ export interface CodeHighlighterProps * @descEN Whether to show line numbers * @default false */ - showLineNumber?: boolean; + showLineNumbers?: boolean; /** * @desc 是否自动换行(长行换行) * @descEN Whether to wrap long lines From bc01a7a8021bd7d3d15664bd18447bce89d38a9e Mon Sep 17 00:00:00 2001 From: wryyyds7 Date: Fri, 24 Jul 2026 09:50:58 +0800 Subject: [PATCH 3/3] test(suggestion): add snapshot for scroll demo Add missing snapshot for suggestion/demo/scroll.tsx which was introduced by merge of fix/suggestion-max-height branch but the snapshot was not updated, causing CI test failure. --- .../__tests__/__snapshots__/demo.test.ts.snap | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/packages/x/components/suggestion/__tests__/__snapshots__/demo.test.ts.snap b/packages/x/components/suggestion/__tests__/__snapshots__/demo.test.ts.snap index 35d80e304c..1071dcf990 100644 --- a/packages/x/components/suggestion/__tests__/__snapshots__/demo.test.ts.snap +++ b/packages/x/components/suggestion/__tests__/__snapshots__/demo.test.ts.snap @@ -112,6 +112,62 @@ exports[`renders components/suggestion/demo/block.tsx correctly 1`] = ` `; +exports[`renders components/suggestion/demo/scroll.tsx correctly 1`] = ` +
+
+
+