Chore: Ensure all react-transition-group animations use nodeRef (#88604)

* ensure all react-transition-group animations pass nodeRef

* clone children instead of wrapping in div

* remove div wrapper

* revert back to div
This commit is contained in:
Ashley Harrison 2024-06-05 15:01:50 +01:00 committed by GitHub
parent a2f7e208fd
commit 2297687ae1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 41 additions and 16 deletions

View File

@ -1,5 +1,5 @@
import { css } from '@emotion/css';
import React from 'react';
import React, { useRef } from 'react';
import { CSSTransition } from 'react-transition-group';
import { GrafanaTheme2 } from '@grafana/data';
@ -7,7 +7,7 @@ import { GrafanaTheme2 } from '@grafana/data';
import { useStyles2 } from '../../themes';
type Props = {
children: React.ReactNode;
children: React.ReactElement;
visible: boolean;
duration?: number;
};
@ -15,10 +15,18 @@ type Props = {
export function FadeTransition(props: Props) {
const { visible, children, duration = 250 } = props;
const styles = useStyles2(getStyles, duration);
const transitionRef = useRef(null);
return (
<CSSTransition in={visible} mountOnEnter={true} unmountOnExit={true} timeout={duration} classNames={styles}>
{children}
<CSSTransition
in={visible}
mountOnEnter={true}
unmountOnExit={true}
timeout={duration}
classNames={styles}
nodeRef={transitionRef}
>
{React.cloneElement(children, { ref: transitionRef })}
</CSSTransition>
);
}

View File

@ -1,5 +1,5 @@
import { css } from '@emotion/css';
import React from 'react';
import React, { useRef } from 'react';
import { CSSTransition } from 'react-transition-group';
import { GrafanaTheme2 } from '@grafana/data';
@ -7,7 +7,7 @@ import { GrafanaTheme2 } from '@grafana/data';
import { useStyles2 } from '../../themes';
type Props = {
children: React.ReactNode;
children: React.ReactElement;
visible: boolean;
size: number;
@ -18,10 +18,18 @@ type Props = {
export function SlideOutTransition(props: Props) {
const { visible, children, duration = 250, horizontal, size } = props;
const styles = useStyles2(getStyles, duration, horizontal ? 'width' : 'height', size);
const transitionRef = useRef(null);
return (
<CSSTransition in={visible} mountOnEnter={true} unmountOnExit={true} timeout={duration} classNames={styles}>
{children}
<CSSTransition
in={visible}
mountOnEnter={true}
unmountOnExit={true}
timeout={duration}
classNames={styles}
nodeRef={transitionRef}
>
{React.cloneElement(children, { ref: transitionRef })}
</CSSTransition>
);
}

View File

@ -1,4 +1,4 @@
import React, { CSSProperties } from 'react';
import React, { CSSProperties, useRef } from 'react';
import Transition, { ExitHandler } from 'react-transition-group/Transition';
interface Props {
@ -10,6 +10,7 @@ interface Props {
}
export const FadeIn = (props: Props) => {
const transitionRef = useRef(null);
const defaultStyle: CSSProperties = {
transition: `opacity ${props.duration}ms linear`,
opacity: 0,
@ -28,9 +29,11 @@ export const FadeIn = (props: Props) => {
timeout={props.duration}
unmountOnExit={props.unmountOnExit || false}
onExited={props.onExited}
nodeRef={transitionRef}
>
{(state) => (
<div
ref={transitionRef}
style={{
...defaultStyle,
...transitionStyles[state],

View File

@ -1,4 +1,4 @@
import React, { CSSProperties, FC } from 'react';
import React, { CSSProperties, FC, useRef } from 'react';
import Transition from 'react-transition-group/Transition';
interface Style {
@ -24,6 +24,7 @@ export interface Props {
}
export const SlideDown: FC<Props> = ({ children, in: inProp, maxHeight = defaultMaxHeight, style = defaultStyle }) => {
const transitionRef = useRef(null);
// There are 4 main states a Transition can be in:
// ENTERING, ENTERED, EXITING, EXITED
// https://reactcommunity.or[g/react-transition-group/
@ -35,13 +36,14 @@ export const SlideDown: FC<Props> = ({ children, in: inProp, maxHeight = default
};
return (
<Transition in={inProp} timeout={defaultDuration}>
<Transition in={inProp} timeout={defaultDuration} nodeRef={transitionRef}>
{(state) => (
<div
style={{
...style,
...transitionStyles[state],
}}
ref={transitionRef}
>
{children}
</div>

View File

@ -1,5 +1,5 @@
import { css } from '@emotion/css';
import React from 'react';
import React, { useRef } from 'react';
import { CSSTransition } from 'react-transition-group';
import { Tooltip, ButtonGroup, ToolbarButton } from '@grafana/ui';
@ -15,6 +15,7 @@ type LiveTailButtonProps = {
};
export function LiveTailButton(props: LiveTailButtonProps) {
const transitionRef = useRef(null);
const { start, pause, resume, isLive, isPaused, stop, splitted } = props;
const buttonVariant = isLive && !isPaused ? 'active' : 'canvas';
const onClickMain = isLive ? (isPaused ? resume : pause) : start;
@ -46,9 +47,10 @@ export function LiveTailButton(props: LiveTailButtonProps) {
exit: styles.stopButtonExit,
exitActive: styles.stopButtonExitActive,
}}
nodeRef={transitionRef}
>
<Tooltip content={<>Stop and exit the live stream</>} placement="bottom">
<ToolbarButton variant={buttonVariant} onClick={stop} icon="square-shape" />
<ToolbarButton ref={transitionRef} variant={buttonVariant} onClick={stop} icon="square-shape" />
</Tooltip>
</CSSTransition>
</ButtonGroup>

View File

@ -1,6 +1,6 @@
import { css } from '@emotion/css';
import memoizeOne from 'memoize-one';
import React from 'react';
import React, { useRef } from 'react';
import { CSSTransition } from 'react-transition-group';
const transitionDuration = 500;
@ -39,7 +39,7 @@ const getStyles = memoizeOne(() => {
});
type Props = {
children: React.ReactNode;
children: React.ReactElement;
visible: boolean;
};
@ -49,6 +49,7 @@ type Props = {
*/
export function LogsCrossFadeTransition(props: Props) {
const { visible, children } = props;
const transitionRef = useRef(null);
const styles = getStyles();
return (
<CSSTransition
@ -62,8 +63,9 @@ export function LogsCrossFadeTransition(props: Props) {
exit: styles.logsExit,
exitActive: styles.logsExitActive,
}}
nodeRef={transitionRef}
>
{children}
<div ref={transitionRef}>{children}</div>
</CSSTransition>
);
}