Skip to content
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
**Bug fixes**

- Fixed invalid color entry passed to `EuiBadge` color prop ([#4481](https://github.com/elastic/eui/pull/4481))
- Fixed `EuiCodeBlock` focus-state if content overflows ([#4463]https://github.com/elastic/eui/pull/4463)

## [`31.5.0`](https://github.com/elastic/eui/tree/v31.5.0)

Expand Down
4 changes: 4 additions & 0 deletions src/components/code/__snapshots__/_code_block.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ exports[`EuiCodeBlockImpl block highlights javascript code, adding "js" class 1`
>
<pre
class="euiCodeBlock__pre euiCodeBlock__pre--whiteSpacePreWrap"
tabindex="-1"
>
<code
class="euiCodeBlock__code js hljs javascript"
Expand All @@ -20,6 +21,7 @@ exports[`EuiCodeBlockImpl block renders a pre block tag 1`] = `
>
<pre
class="euiCodeBlock__pre euiCodeBlock__pre--whiteSpacePreWrap"
tabindex="-1"
>
<code
aria-label="aria-label"
Expand All @@ -39,6 +41,7 @@ exports[`EuiCodeBlockImpl block renders a pre block tag with a css class modifie
>
<pre
class="euiCodeBlock__pre euiCodeBlock__pre--whiteSpacePre"
tabindex="-1"
>
<code
aria-label="aria-label"
Expand All @@ -58,6 +61,7 @@ exports[`EuiCodeBlockImpl block renders with transparent background 1`] = `
>
<pre
class="euiCodeBlock__pre euiCodeBlock__pre--whiteSpacePreWrap"
tabindex="-1"
>
<code
class="euiCodeBlock__code"
Expand Down
16 changes: 14 additions & 2 deletions src/components/code/__snapshots__/code_block.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
exports[`EuiCodeBlock dynamic content updates DOM when input changes 1`] = `
"<div>
<div class=\\"euiCodeBlock euiCodeBlock--fontSmall euiCodeBlock--paddingLarge\\">
<pre class=\\"euiCodeBlock__pre euiCodeBlock__pre--whiteSpacePreWrap\\">
<pre class=\\"euiCodeBlock__pre euiCodeBlock__pre--whiteSpacePreWrap\\" tabindex=\\"-1\\">
<code class=\\"euiCodeBlock__code javascript hljs\\">
<span class=\\"hljs-keyword\\">const</span>value =
<span class=\\"hljs-string\\">'State 1'</span>
Expand All @@ -16,7 +16,7 @@ exports[`EuiCodeBlock dynamic content updates DOM when input changes 1`] = `
exports[`EuiCodeBlock dynamic content updates DOM when input changes 2`] = `
"<div>
<div class=\\"euiCodeBlock euiCodeBlock--fontSmall euiCodeBlock--paddingLarge\\">
<pre class=\\"euiCodeBlock__pre euiCodeBlock__pre--whiteSpacePreWrap\\">
<pre class=\\"euiCodeBlock__pre euiCodeBlock__pre--whiteSpacePreWrap\\" tabindex=\\"-1\\">
<code class=\\"euiCodeBlock__code javascript hljs\\">
<span class=\\"hljs-keyword\\">const</span>value =
<span class=\\"hljs-string\\">'State 2'</span>
Expand All @@ -32,6 +32,7 @@ exports[`EuiCodeBlock props fontSize l is rendered 1`] = `
>
<pre
class="euiCodeBlock__pre euiCodeBlock__pre--whiteSpacePreWrap"
tabindex="-1"
>
<code
class="euiCodeBlock__code"
Expand All @@ -49,6 +50,7 @@ exports[`EuiCodeBlock props fontSize m is rendered 1`] = `
>
<pre
class="euiCodeBlock__pre euiCodeBlock__pre--whiteSpacePreWrap"
tabindex="-1"
>
<code
class="euiCodeBlock__code"
Expand All @@ -66,6 +68,7 @@ exports[`EuiCodeBlock props fontSize s is rendered 1`] = `
>
<pre
class="euiCodeBlock__pre euiCodeBlock__pre--whiteSpacePreWrap"
tabindex="-1"
>
<code
class="euiCodeBlock__code"
Expand All @@ -83,6 +86,7 @@ exports[`EuiCodeBlock props isCopyable is rendered 1`] = `
>
<pre
class="euiCodeBlock__pre euiCodeBlock__pre--whiteSpacePreWrap"
tabindex="-1"
>
<code
class="euiCodeBlock__code"
Expand Down Expand Up @@ -123,6 +127,7 @@ exports[`EuiCodeBlock props language is rendered 1`] = `
>
<pre
class="euiCodeBlock__pre euiCodeBlock__pre--whiteSpacePreWrap"
tabindex="-1"
>
<code
class="euiCodeBlock__code html hljs xml"
Expand All @@ -142,6 +147,7 @@ exports[`EuiCodeBlock props overflowHeight is rendered 1`] = `
<pre
class="euiCodeBlock__pre euiCodeBlock__pre--whiteSpacePreWrap"
style="max-height: 200px;"
tabindex="-1"
>
<code
class="euiCodeBlock__code"
Expand Down Expand Up @@ -174,6 +180,7 @@ exports[`EuiCodeBlock props paddingSize l is rendered 1`] = `
>
<pre
class="euiCodeBlock__pre euiCodeBlock__pre--whiteSpacePreWrap"
tabindex="-1"
>
<code
class="euiCodeBlock__code"
Expand All @@ -191,6 +198,7 @@ exports[`EuiCodeBlock props paddingSize m is rendered 1`] = `
>
<pre
class="euiCodeBlock__pre euiCodeBlock__pre--whiteSpacePreWrap"
tabindex="-1"
>
<code
class="euiCodeBlock__code"
Expand All @@ -208,6 +216,7 @@ exports[`EuiCodeBlock props paddingSize none is rendered 1`] = `
>
<pre
class="euiCodeBlock__pre euiCodeBlock__pre--whiteSpacePreWrap"
tabindex="-1"
>
<code
class="euiCodeBlock__code"
Expand All @@ -225,6 +234,7 @@ exports[`EuiCodeBlock props paddingSize s is rendered 1`] = `
>
<pre
class="euiCodeBlock__pre euiCodeBlock__pre--whiteSpacePreWrap"
tabindex="-1"
>
<code
class="euiCodeBlock__code"
Expand All @@ -242,6 +252,7 @@ exports[`EuiCodeBlock props transparentBackground is rendered 1`] = `
>
<pre
class="euiCodeBlock__pre euiCodeBlock__pre--whiteSpacePreWrap"
tabindex="-1"
>
<code
class="euiCodeBlock__code"
Expand All @@ -259,6 +270,7 @@ exports[`EuiCodeBlock renders a code block 1`] = `
>
<pre
class="euiCodeBlock__pre euiCodeBlock__pre--whiteSpacePreWrap"
tabindex="-1"
>
<code
aria-label="aria-label"
Expand Down
90 changes: 53 additions & 37 deletions src/components/code/_code_block.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,30 +17,27 @@
* under the License.
*/

import classNames from 'classnames';
import hljs from 'highlight.js';
import React, {
CSSProperties,
FunctionComponent,
KeyboardEvent,
CSSProperties,
useEffect,
useRef,
useState,
} from 'react';
import { createPortal } from 'react-dom';
import classNames from 'classnames';
import hljs from 'highlight.js';

import { EuiCopy } from '../copy';

import { keys, useCombinedRefs } from '../../services';
import { EuiButtonIcon } from '../button';

import { EuiOverlayMask } from '../overlay_mask';

import { keysOf } from '../common';
import { EuiCopy } from '../copy';
import { EuiFocusTrap } from '../focus_trap';

import { keys } from '../../services';
import { EuiI18n } from '../i18n';
import { EuiInnerText } from '../inner_text';
import { keysOf } from '../common';
import { useInnerText } from '../inner_text';
import { useMutationObserver } from '../observer/mutation_observer';
import { useResizeObserver } from '../observer/resize_observer';
import { EuiOverlayMask } from '../overlay_mask';
import { FontSize, PaddingSize } from './code_block';

const fontSizeToClassNameMap = {
Expand Down Expand Up @@ -110,10 +107,35 @@ export const EuiCodeBlockImpl: FunctionComponent<Props> = ({
const [isPortalTargetReady, setIsPortalTargetReady] = useState(false);
const codeTarget = useRef<HTMLDivElement | null>(null);
const code = useRef<HTMLElement | null>(null);
const [wrapperRef, setWrapperRef] = useState<Element | null>(null);
const [innerTextRef, innerText] = useInnerText('');
const [tabIndex, setTabIndex] = useState<-1 | 0>(-1);
const combinedRef = useCombinedRefs<HTMLPreElement>([
innerTextRef,
setWrapperRef,
]);
const { width, height } = useResizeObserver(wrapperRef);
const [codeFullScreen, setCodeFullScreen] = useState<HTMLElement | null>(
null
);

const doesOverflow = () => {
if (!wrapperRef) return;

const { clientWidth, clientHeight, scrollWidth, scrollHeight } = wrapperRef;
const doesOverflow =
scrollHeight > clientHeight || scrollWidth > clientWidth;

setTabIndex(doesOverflow ? 0 : -1);
};

useMutationObserver(wrapperRef, doesOverflow, {
subtree: true,
childList: true,
});

useEffect(doesOverflow, [width, height, wrapperRef]);

useEffect(() => {
codeTarget.current = document.createElement('div');
setIsPortalTargetReady(true);
Expand Down Expand Up @@ -292,11 +314,10 @@ export const EuiCodeBlockImpl: FunctionComponent<Props> = ({
<EuiOverlayMask>
<EuiFocusTrap clickOutsideDisables={true}>
<div className={fullScreenClasses}>
<pre className={preClasses}>
<pre className={preClasses} tabIndex={0}>
<code
ref={setCodeFullScreen}
className={codeClasses}
tabIndex={0}
onKeyDown={onKeyDown}
/>
</pre>
Expand All @@ -311,31 +332,26 @@ export const EuiCodeBlockImpl: FunctionComponent<Props> = ({
return fullScreenDisplay;
};

const codeBlockControls = getCodeBlockControls(innerText);
return isPortalTargetReady ? (
<>
{createPortal(children, codeTarget.current!)}
<EuiInnerText fallback="">
{(innerTextRef, innerText) => {
const codeBlockControls = getCodeBlockControls(innerText);
return (
<div {...wrapperProps}>
<pre
ref={innerTextRef}
style={optionalStyles}
className={preClasses}>
{codeSnippet}
</pre>

{/*
If the below fullScreen code renders, it actually attaches to the body because of
EuiOverlayMask's React portal usage.
*/}
{codeBlockControls}
{getFullScreenDisplay(codeBlockControls)}
</div>
);
}}
</EuiInnerText>
<div {...wrapperProps}>
<pre
ref={combinedRef}
style={optionalStyles}
className={preClasses}
tabIndex={tabIndex}>
{codeSnippet}
</pre>

{/*
If the below fullScreen code renders, it actually attaches to the body because of
EuiOverlayMask's React portal usage.
*/}
{codeBlockControls}
{getFullScreenDisplay(codeBlockControls)}
</div>
</>
) : null;
};