diff --git a/packages/eui/.loki/reference/chrome_desktop_Editors_Syntax_EuiCodeBlock_Annotations.png b/packages/eui/.loki/reference/chrome_desktop_Editors_Syntax_EuiCodeBlock_Annotations.png new file mode 100644 index 00000000000..eab653c3752 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Editors_Syntax_EuiCodeBlock_Annotations.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Editors_Syntax_EuiCodeBlock_Highlighted_Lines.png b/packages/eui/.loki/reference/chrome_desktop_Editors_Syntax_EuiCodeBlock_Highlighted_Lines.png new file mode 100644 index 00000000000..f50feea42cc Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Editors_Syntax_EuiCodeBlock_Highlighted_Lines.png differ diff --git a/packages/eui/.loki/reference/chrome_desktop_Editors_Syntax_EuiCodeBlock_Start_Value.png b/packages/eui/.loki/reference/chrome_desktop_Editors_Syntax_EuiCodeBlock_Start_Value.png new file mode 100644 index 00000000000..b12524855be Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Editors_Syntax_EuiCodeBlock_Start_Value.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Editors_Syntax_EuiCodeBlock_Annotations.png b/packages/eui/.loki/reference/chrome_mobile_Editors_Syntax_EuiCodeBlock_Annotations.png new file mode 100644 index 00000000000..6258efa04c4 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Editors_Syntax_EuiCodeBlock_Annotations.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Editors_Syntax_EuiCodeBlock_Highlighted_Lines.png b/packages/eui/.loki/reference/chrome_mobile_Editors_Syntax_EuiCodeBlock_Highlighted_Lines.png new file mode 100644 index 00000000000..a49bed9c0ac Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Editors_Syntax_EuiCodeBlock_Highlighted_Lines.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Editors_Syntax_EuiCodeBlock_Start_Value.png b/packages/eui/.loki/reference/chrome_mobile_Editors_Syntax_EuiCodeBlock_Start_Value.png new file mode 100644 index 00000000000..5872a682a17 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Editors_Syntax_EuiCodeBlock_Start_Value.png differ diff --git a/packages/eui/src/components/code/__snapshots__/code_block.test.tsx.snap b/packages/eui/src/components/code/__snapshots__/code_block.test.tsx.snap index b7f00f4ae15..a4256be341a 100644 --- a/packages/eui/src/components/code/__snapshots__/code_block.test.tsx.snap +++ b/packages/eui/src/components/code/__snapshots__/code_block.test.tsx.snap @@ -1,127 +1,46 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`EuiCodeBlock dynamic content updates DOM when input changes 1`] = ` -
+exports[`EuiCodeBlock Virtualization renders a virtualized code block 1`] = ` +
       
         
-          
-            const
-          
-           value 
-          
-            =
-          
-           
-          
-            'State 1'
-          
-        
-      
-    
-
-
-`; + var some = 'code'; -exports[`EuiCodeBlock dynamic content updates DOM when input changes 2`] = ` -
-
-
-      
+        
         
-          
-            const
-          
-           value 
-          
-            =
-          
-           
-          
-            'State 2'
-          
+          console.log(some);
         
       
     
-
-`; - -exports[`EuiCodeBlock fullscreen displays content in fullscreen mode 1`] = ` -
-
-    
-      
-        
-          const
-        
-         value 
-        
-          =
-        
-         
-        
-          "hello"
-        
-      
-    
-  
`; -exports[`EuiCodeBlock line numbers renders annotated line numbers 1`] = ` -
-
-    
-      
-        
-          
-        
-          var some = 'code';
-
-        
-      
-      
-        
-          
-        
-          console.log(some);
-        
-      
-    
-  
-
-`; - -exports[`EuiCodeBlock line numbers renders highlighted line numbers 1`] = ` -
-
-    
-      
-        
-          
-        
-          var some = 'code';
-
-        
-      
-      
-        
-          
-        
-          console.log(some);
-        
-      
-    
-  
-
-`; - -exports[`EuiCodeBlock line numbers renders line numbers 1`] = ` -
-
-    
-      
-        
-          
-        
-          var some = 'code';
-
-        
-      
-      
-        
-          
-        
-          console.log(some);
-        
-      
-    
-  
-
-`; - -exports[`EuiCodeBlock line numbers renders line numbers with a start value 1`] = ` +exports[`EuiCodeBlock renders a code block 1`] = `
@@ -337,90 +68,6 @@ exports[`EuiCodeBlock line numbers renders line numbers with a start value 1`] = class="euiCodeBlock__code emotion-euiCodeBlock__code" data-code-language="text" data-test-subj="test subject string" - > - - - - - var some = 'code'; - - - - - - - - console.log(some); - - - - -
-`; - -exports[`EuiCodeBlock props fontSize l is rendered 1`] = ` -
-
-    
-      
-        var some = 'code';
-
-      
-      
-        console.log(some);
-      
-    
-  
-
-`; - -exports[`EuiCodeBlock props fontSize m is rendered 1`] = ` -
-
-    
       
 
`; - -exports[`EuiCodeBlock props fontSize s is rendered 1`] = ` -
-
-    
-      
-        var some = 'code';
-
-      
-      
-        console.log(some);
-      
-    
-  
-
-`; - -exports[`EuiCodeBlock props isCopyable is rendered 1`] = ` -
-
-    
-      
-        var some = 'code';
-
-      
-      
-        console.log(some);
-      
-    
-  
-
-
- - - -
-
-
-`; - -exports[`EuiCodeBlock props language is rendered 1`] = ` -
-
-    
-      
-        var some = 'code';
-
-      
-      
-        console.log(some);
-      
-    
-  
-
-`; - -exports[`EuiCodeBlock props overflowHeight is rendered 1`] = ` -
-
-    
-      
-        var some = 'code';
-
-      
-      
-        console.log(some);
-      
-    
-  
-
- -
-
-`; - -exports[`EuiCodeBlock props paddingSize l is rendered 1`] = ` -
-
-    
-      
-        var some = 'code';
-
-      
-      
-        console.log(some);
-      
-    
-  
-
-`; - -exports[`EuiCodeBlock props paddingSize m is rendered 1`] = ` -
-
-    
-      
-        var some = 'code';
-
-      
-      
-        console.log(some);
-      
-    
-  
-
-`; - -exports[`EuiCodeBlock props paddingSize none is rendered 1`] = ` -
-
-    
-      
-        var some = 'code';
-
-      
-      
-        console.log(some);
-      
-    
-  
-
-`; - -exports[`EuiCodeBlock props paddingSize s is rendered 1`] = ` -
-
-    
-      
-        var some = 'code';
-
-      
-      
-        console.log(some);
-      
-    
-  
-
-`; - -exports[`EuiCodeBlock props transparentBackground is rendered 1`] = ` -
-
-    
-      
-        var some = 'code';
-
-      
-      
-        console.log(some);
-      
-    
-  
-
-`; - -exports[`EuiCodeBlock props whiteSpace renders a pre block tag with a css class modifier 1`] = ` -
-
-    
-      
-        var some = 'code';
-
-      
-      
-        console.log(some);
-      
-    
-  
-
-`; - -exports[`EuiCodeBlock renders a code block 1`] = ` -
-
-    
-      
-        var some = 'code';
-
-      
-      
-        console.log(some);
-      
-    
-  
-
-`; - -exports[`EuiCodeBlock virtualization renders a virtualized code block 1`] = ` -
-
-
-      
-        
-          var some = 'code';
-
-        
-        
-          console.log(some);
-        
-      
-    
-
-
- -
-
-`; diff --git a/packages/eui/src/components/code/code_block.stories.tsx b/packages/eui/src/components/code/code_block.stories.tsx index a123e931ec6..7d4d0b7f832 100644 --- a/packages/eui/src/components/code/code_block.stories.tsx +++ b/packages/eui/src/components/code/code_block.stories.tsx @@ -6,9 +6,14 @@ * Side Public License, v 1. */ -import type { Meta, StoryObj } from '@storybook/react'; +import type { Meta, StoryObj, ReactRenderer } from '@storybook/react'; +import type { PlayFunctionContext } from '@storybook/csf'; + +import { within } from '../../../.storybook/test'; +import { LOKI_SELECTORS } from '../../../.storybook/loki'; import { EuiCodeBlock, EuiCodeBlockProps } from './code_block'; +import { expect, userEvent } from '@storybook/test'; const meta: Meta = { title: 'Editors & Syntax/EuiCodeBlock', @@ -32,10 +37,68 @@ const meta: Meta = { export default meta; type Story = StoryObj; +const htmlCode = `

+ +

`; + export const Playground: Story = { args: { - children: `

- -

`, + children: htmlCode, + }, +}; + +export const StartValue: Story = { + args: { + children: htmlCode, + language: 'html', + lineNumbers: { + start: 10, + }, + }, + argTypes: { + lineNumbers: { table: { disable: true } }, + }, +}; + +export const HighlightedLines: Story = { + args: { + children: htmlCode, + language: 'html', + lineNumbers: { + highlight: '1,3', + }, + }, + argTypes: { + lineNumbers: { table: { disable: true } }, + }, +}; + +export const Annotations: Story = { + args: { + children: htmlCode, + language: 'html', + lineNumbers: { + annotations: { + 2: 'Hello world', + }, + }, + }, + argTypes: { + lineNumbers: { table: { disable: true } }, + }, + parameters: { + loki: { chromeSelector: LOKI_SELECTORS.portal }, + }, + play: async ({ canvasElement }: PlayFunctionContext) => { + const canvas = within(canvasElement); + const annotationButton = await canvas.findByRole('button', { + name: 'Click to view a code annotation for line 2', + }); + + userEvent.click(annotationButton); + + const dialog = await canvas.findByRole('dialog'); + + expect(dialog).toHaveTextContent('Hello world'); }, }; diff --git a/packages/eui/src/components/code/code_block.styles.ts b/packages/eui/src/components/code/code_block.styles.ts index 8db1e924b96..895051a72cc 100644 --- a/packages/eui/src/components/code/code_block.styles.ts +++ b/packages/eui/src/components/code/code_block.styles.ts @@ -5,13 +5,6 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ import { css } from '@emotion/react'; import { diff --git a/packages/eui/src/components/code/code_block.test.tsx b/packages/eui/src/components/code/code_block.test.tsx index 64b0e37b8f8..086b2ab053b 100644 --- a/packages/eui/src/components/code/code_block.test.tsx +++ b/packages/eui/src/components/code/code_block.test.tsx @@ -8,10 +8,15 @@ import React from 'react'; import { fireEvent } from '@testing-library/react'; + import { requiredProps } from '../../test/required_props'; import { render } from '../../test/rtl'; -import { EuiCodeBlock, FONT_SIZES, PADDING_SIZES } from './code_block'; +import { + EuiCodeBlock, + EuiCodeBlockFontSize, + EuiCodeBlockPaddingSize, +} from './code_block'; const code = `var some = 'code'; console.log(some);`; @@ -22,28 +27,42 @@ describe('EuiCodeBlock', () => { {code} ); + expect(container).toBeInTheDocument(); expect(container.firstChild).toMatchSnapshot(); }); - describe('props', () => { - describe('transparentBackground', () => { - it('is rendered', () => { - const { container } = render( - {code} - ); + it('updates DOM when the input changes', () => { + const { container, rerender } = render( + + const value = 'State 1' + + ); - expect(container.firstChild).toMatchSnapshot(); - }); - }); + expect(container.querySelector('.euiCodeBlock__line')).toHaveTextContent( + "const value = 'State 1'" + ); - describe('isCopyable', () => { - it('is rendered', () => { - const { container } = render( - {code} - ); + rerender( + + const value = 'State 2' + + ); - expect(container.firstChild).toMatchSnapshot(); - }); + expect(container.querySelector('.euiCodeBlock__line')).toHaveTextContent( + "const value = 'State 2'" + ); + }); + + describe('Props', () => { + it('renders "Copy" on the copy button when no `copyAriaLabel` is passed', () => { + const { getByTestSubject } = render( + {code} + ); + + expect(getByTestSubject('euiCodeBlockCopy')).toHaveAttribute( + 'aria-label', + 'Copy' + ); }); it('renders `copyAriaLabel` on the copy button', () => { @@ -60,83 +79,56 @@ describe('EuiCodeBlock', () => { ); }); - describe('overflowHeight', () => { - it('is rendered', () => { - const { container } = render( - {code} - ); + it('renders a transparent background when `transparentBackground` is `true`', () => { + const { container } = render( + {code} + ); - expect(container.firstChild).toMatchSnapshot(); - }); + expect(container.querySelector('.euiCodeBlock')).toHaveStyleRule( + 'background', + 'transparent' + ); }); - describe('language', () => { - it('is rendered', () => { + test.each<{ paddingSize: EuiCodeBlockPaddingSize; expected: string }>([ + { paddingSize: 'none', expected: '0' }, + { paddingSize: 's', expected: '8px' }, + { paddingSize: 'm', expected: '16px' }, + { paddingSize: 'l', expected: '24px' }, + ])( + 'renders a padding of $expected when `paddingSize` is `$paddingSize`', + ({ paddingSize, expected }) => { const { container } = render( - {code} + {code} ); - expect(container.firstChild).toMatchSnapshot(); - }); - }); - - describe('fontSize', () => { - FONT_SIZES.forEach((fontSize) => { - test(`${fontSize} is rendered`, () => { - const { container } = render( - {code} - ); - - expect(container.firstChild).toMatchSnapshot(); - }); - }); - }); - - describe('paddingSize', () => { - PADDING_SIZES.forEach((paddingSize) => { - test(`${paddingSize} is rendered`, () => { - const { container } = render( - {code} - ); - - expect(container.firstChild).toMatchSnapshot(); - }); - }); - }); + expect(container.querySelector('.euiCodeBlock__pre')).toHaveStyleRule( + 'padding', + expected + ); + } + ); - describe('whiteSpace', () => { - it('renders a pre block tag with a css class modifier', () => { + test.each<{ fontSize: EuiCodeBlockFontSize; expected: string }>([ + { fontSize: 's', expected: '0.8571rem' }, + { fontSize: 'm', expected: '1.0000rem' }, + { fontSize: 'l', expected: '1.1429rem' }, + ])( + 'renders a font size of $expected when `fontSize` is `$fontSize`', + ({ fontSize, expected }) => { const { container } = render( - - {code} - + {code} ); - expect(container.firstChild).toMatchSnapshot(); - }); - }); - }); - - describe('dynamic content', () => { - it('updates DOM when input changes', () => { - const { container, rerender } = render( - - const value = 'State 1' - - ); - expect(container).toMatchSnapshot(); - - rerender( - - const value = 'State 2' - - ); - - expect(container).toMatchSnapshot(); - }); + expect(container.querySelector('.euiCodeBlock')).toHaveStyleRule( + 'font-size', + expected + ); + } + ); }); - describe('fullscreen', () => { + describe('Fullscreen', () => { it('displays content in fullscreen mode', () => { const { getByLabelText, baseElement } = render( { const value = "hello" ); + fireEvent.click(getByLabelText('Expand')); + expect( baseElement.querySelector('.euiCodeBlockFullScreen') - ).toMatchSnapshot(); + ).toBeInTheDocument(); }); - it('closes fullscreen mode when the escape key is pressed', () => { + it('closes fullscreen mode when the Escape key is pressed', () => { const { getByLabelText, baseElement } = render( { const value = "world" ); - fireEvent.click(getByLabelText('Expand')); + fireEvent.click(getByLabelText('Expand')); fireEvent.keyDown( baseElement.querySelector( '.euiCodeBlockFullScreen .euiCodeBlock__pre' )!, { key: 'Escape' } ); + expect( baseElement.querySelector('.euiCodeBlockFullScreen') ).not.toBeInTheDocument(); }); }); - describe('virtualization', () => { + describe('Virtualization', () => { it('renders a virtualized code block', () => { const { container } = render( { {code} ); + + expect( + container.querySelector('[class*="euiCodeBlock__code-isVirtualized"]') + ).toBeInTheDocument(); expect(container.firstChild).toMatchSnapshot(); }); - describe('type checks', () => { - it('requires overflowHeight', () => { - // @ts-expect-error should expect overflowHeight - render(); - }); - - it('only allows whiteSpace of pre', () => { - render( - // @ts-expect-error should only accept "pre" - - ); - // OK - render( - - ); - }); + it('requires overflowHeight', () => { + // @ts-expect-error should expect overflowHeight + render(); + }); + + it('only allows whiteSpace of pre', () => { + render( + // @ts-expect-error should only accept "pre" + + ); + + render( + + ); }); }); - describe('line numbers', () => { + describe('Line numbers', () => { it('renders line numbers', () => { const { container } = render( {code} ); - expect(container.firstChild).toMatchSnapshot(); + + expect( + container.querySelectorAll('.euiCodeBlock__lineNumber').length + ).toBeGreaterThan(0); }); it('renders line numbers with a start value', () => { @@ -230,7 +226,13 @@ describe('EuiCodeBlock', () => { {code} ); - expect(container.firstChild).toMatchSnapshot(); + + const lineNumbers = container.querySelectorAll( + '.euiCodeBlock__lineNumber' + ); + + expect(lineNumbers[0]).toHaveAttribute('data-line-number', '10'); + expect(lineNumbers[1]).toHaveAttribute('data-line-number', '11'); }); it('renders highlighted line numbers', () => { @@ -239,19 +241,27 @@ describe('EuiCodeBlock', () => { {code} ); - expect(container.firstChild).toMatchSnapshot(); + + const highlightedLine = container.querySelector( + '[class*="euiCodeBlock__lineText-isHighlighted"]' + ); + + expect(highlightedLine).toBeInTheDocument(); }); it('renders annotated line numbers', () => { - const { container } = render( + const { getByLabelText } = render( {code} ); - expect(container.firstChild).toMatchSnapshot(); + + expect( + getByLabelText('Click to view a code annotation for line 2') + ).toBeInTheDocument(); }); }); });