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();
});
});
});