Skip to content

Commit

Permalink
Make tabbable codeblocks if content overflows (#4463)
Browse files Browse the repository at this point in the history
Co-authored-by: Chandler Prall <chandler.prall@gmail.com>
  • Loading branch information
Michail Yasonik and chandlerprall authored Feb 9, 2021
1 parent 19874e6 commit 56c6d68
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 39 deletions.
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;
};

0 comments on commit 56c6d68

Please sign in to comment.