diff --git a/src/components/code/__snapshots__/code_block.test.tsx.snap b/src/components/code/__snapshots__/code_block.test.tsx.snap index e642da216f7..8c1bd5ff1b5 100644 --- a/src/components/code/__snapshots__/code_block.test.tsx.snap +++ b/src/components/code/__snapshots__/code_block.test.tsx.snap @@ -85,54 +85,41 @@ exports[`EuiCodeBlock fullscreen displays content in fullscreen mode 1`] = `
- - - + + - - + /> + + + -
`; @@ -412,74 +399,68 @@ exports[`EuiCodeBlock props isCopyable is rendered 1`] = `
- - +
- - + - - - - - - - - - + /> + + + + + +
- `; diff --git a/src/components/code/code_block.tsx b/src/components/code/code_block.tsx index b8261e255bb..37cffeed115 100644 --- a/src/components/code/code_block.tsx +++ b/src/components/code/code_block.tsx @@ -27,7 +27,7 @@ import { EuiButtonIcon } from '../button'; import { keysOf, ExclusiveUnion } from '../common'; import { EuiCopy } from '../copy'; import { EuiFocusTrap } from '../focus_trap'; -import { EuiI18n } from '../i18n'; +import { useEuiI18n } from '../i18n'; import { useInnerText } from '../inner_text'; import { useMutationObserver } from '../observer/mutation_observer'; import { useResizeObserver } from '../observer/resize_observer'; @@ -171,7 +171,7 @@ export const EuiCodeBlock: FunctionComponent = ({ [_isVirtualized, data] ); - const { innerTextRef, showCopyButton, CopyButton } = useCopy({ + const { innerTextRef, showCopyButton, textToCopy } = useCopy({ isCopyable, isVirtualized, children, @@ -187,8 +187,8 @@ export const EuiCodeBlock: FunctionComponent = ({ const { showFullScreenButton, onKeyDown, - FullScreenButton, - FullScreenDisplay, + isFullScreen, + toggleFullScreen, } = useFullScreen({ overflowHeight }); // Classes used in both fullscreen and non-fullscreen mode @@ -232,28 +232,46 @@ export const EuiCodeBlock: FunctionComponent = ({ [preClasses, onKeyDown] ); - const optionalStyles: CSSProperties = {}; - - if (overflowHeight) { - const property = - typeof overflowHeight === 'string' ? 'height' : 'maxHeight'; - optionalStyles[property] = overflowHeight; - } + const overflowHeightStyles: CSSProperties = useMemo(() => { + if (overflowHeight) { + const property = + typeof overflowHeight === 'string' ? 'height' : 'maxHeight'; + return { + [property]: overflowHeight, + }; + } + return {}; + }, [overflowHeight]); - const wrapperProps = { - className: classes, - style: optionalStyles, - }; + const wrapperProps = useMemo( + () => ({ + className: classes, + style: overflowHeightStyles, + }), + [classes, overflowHeightStyles] + ); - let codeBlockControls; - if (showCopyButton || showFullScreenButton) { - codeBlockControls = ( -
- - -
- ); - } + const codeBlockControls = useMemo(() => { + if (showCopyButton || showFullScreenButton) { + return ( +
+ {showFullScreenButton && ( + + )} + {showCopyButton && } +
+ ); + } + }, [ + isFullScreen, + toggleFullScreen, + showCopyButton, + showFullScreenButton, + textToCopy, + ]); return (
@@ -268,7 +286,7 @@ export const EuiCodeBlock: FunctionComponent = ({ ) : (
@@ -277,21 +295,23 @@ export const EuiCodeBlock: FunctionComponent = ({
       )}
       {codeBlockControls}
 
-      
-        {isVirtualized ? (
-          
-        ) : (
-          
-            {content}
-          
- )} - {codeBlockControls} -
+ {isFullScreen && ( + + {isVirtualized ? ( + + ) : ( +
+              {content}
+            
+ )} + {codeBlockControls} +
+ )}
); }; @@ -333,6 +353,26 @@ const useOverflowDetection = () => { * Copy logic */ +const CopyButton: FunctionComponent<{ + textToCopy: string; +}> = ({ textToCopy }) => { + const copyButton = useEuiI18n('euiCodeBlock.copyButton', 'Copy'); + return ( +
+ + {(copy) => ( + + )} + +
+ ); +}; + const useCopy = ({ isCopyable, isVirtualized, @@ -351,36 +391,53 @@ const useCopy = ({ const showCopyButton = isCopyable && textToCopy; - const CopyButton = () => { - if (!showCopyButton) return null; - - return ( -
- - {(copyButton: string) => ( - - {(copy) => ( - - )} - - )} - -
- ); - }; - - return { innerTextRef, showCopyButton, CopyButton }; + return { innerTextRef, showCopyButton, textToCopy }; }; /** * Fullscreen logic */ +const FullScreenButton: FunctionComponent<{ + isFullScreen: boolean; + toggleFullScreen: () => void; +}> = ({ isFullScreen, toggleFullScreen }) => { + const [fullscreenCollapse, fullscreenExpand] = useEuiI18n( + ['euiCodeBlock.fullscreenCollapse', 'euiCodeBlock.fullscreenExpand'], + ['Collapse', 'Expand'] + ); + return ( + + ); +}; + +const FullScreenDisplay: FunctionComponent<{ + className: string; +}> = ({ children, className }) => { + // Force fullscreen to use large font and padding. + const fullScreenClasses = classNames( + className, + 'euiCodeBlock--fontLarge', + 'euiCodeBlock--paddingLarge', + 'euiCodeBlock-isFullScreen' + ); + + // Attaches to the body because of EuiOverlayMask's React portal usage. + return ( + + +
{children}
+
+
+ ); +}; + const useFullScreen = ({ overflowHeight, }: { @@ -388,9 +445,9 @@ const useFullScreen = ({ }) => { const [isFullScreen, setIsFullScreen] = useState(false); - const toggleFullScreen = () => { - setIsFullScreen(!isFullScreen); - }; + const toggleFullScreen = useCallback(() => { + setIsFullScreen((isFullScreen) => !isFullScreen); + }, []); const onKeyDown = useCallback((event: KeyboardEvent) => { if (event.key === keys.ESCAPE) { @@ -402,57 +459,10 @@ const useFullScreen = ({ const showFullScreenButton = !!overflowHeight; - const FullScreenButton: React.FC = () => { - if (!showFullScreenButton) return null; - return ( - - {([fullscreenCollapse, fullscreenExpand]: string[]) => ( - - )} - - ); - }; - - const FullScreenDisplay: React.FC<{ className: string }> = ({ - children, - className, - }) => { - if (!isFullScreen) return null; - - // Force fullscreen to use large font and padding. - const fullScreenClasses = classNames( - className, - 'euiCodeBlock--fontLarge', - 'euiCodeBlock--paddingLarge', - 'euiCodeBlock-isFullScreen' - ); - - // Attaches to the body because of EuiOverlayMask's React portal usage. - return ( - - -
{children}
-
-
- ); - }; - return { showFullScreenButton, - FullScreenButton, - FullScreenDisplay, + isFullScreen, + toggleFullScreen, onKeyDown, }; }; diff --git a/upcoming_changelogs/6077.md b/upcoming_changelogs/6077.md new file mode 100644 index 00000000000..9f6b78681df --- /dev/null +++ b/upcoming_changelogs/6077.md @@ -0,0 +1,4 @@ +**Bug fixes** + +- Fixed unintentional subcomponent remounting in `EuiCodeBlock` during rerenders +