Skip to content

Commit

Permalink
fix: fix React findDOMNode deprecation warning (#338)
Browse files Browse the repository at this point in the history
close #334
  • Loading branch information
LouisBarranqueiro committed Jan 8, 2021
1 parent bdb6cc5 commit cf9e52f
Show file tree
Hide file tree
Showing 16 changed files with 826 additions and 1,390 deletions.
8 changes: 5 additions & 3 deletions demo/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ setUpNotifications({
})

const App = hot(() => (
<Provider store={store}>
<Demo />
</Provider>
<React.StrictMode>
<Provider store={store}>
<Demo />
</Provider>
</React.StrictMode>
))

render(<App />, document.getElementById('root'))
20 changes: 10 additions & 10 deletions src/components/FadeTransition.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
import React, {ReactNode} from 'react'
import React, {RefObject} from 'react'
import {Transition} from 'react-transition-group'
import {TransitionProps} from 'react-transition-group/Transition'

type Props = {
children: ReactNode
duration?: number
[index: string]: any
}
} & Omit<TransitionProps<HTMLElement>, 'addEndListener'>

const FadeTransition = (props: Props) => {
const duration = props.duration || 300
const {children, ...otherProps} = props
const {children, nodeRef, ...otherProps} = props
const getNode = () => (nodeRef as RefObject<HTMLElement>).current as HTMLElement
// eslint-disable-next-line no-undef
const animationProps: KeyframeAnimationOptions = {
fill: 'forwards',
duration,
}
const onEnter = (node: HTMLElement) => {
node.animate([{opacity: 0}, {opacity: 1}], animationProps)
const onEnter = () => {
getNode().animate([{opacity: 0}, {opacity: 1}], animationProps)
}
const onExit = (node: HTMLElement) => {
node.animate(
const onExit = () => {
getNode().animate(
[
{
maxHeight: '150px',
Expand All @@ -35,7 +35,7 @@ const FadeTransition = (props: Props) => {
}

return (
<Transition onEnter={onEnter} onExit={onExit} timeout={duration + 100} {...otherProps}>
<Transition nodeRef={nodeRef} onEnter={onEnter} onExit={onExit} timeout={duration + 100} {...otherProps}>
{children}
</Transition>
)
Expand Down
28 changes: 17 additions & 11 deletions src/components/GrowTransition.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
import React, {ReactNode} from 'react'
import React, {RefObject} from 'react'
import {Transition} from 'react-transition-group'
import {TransitionProps} from 'react-transition-group/Transition'

type Props = {
children: ReactNode
duration?: number
[index: string]: any
}
} & Omit<TransitionProps<HTMLElement>, 'addEndListener'>

const GrowTransition = (props: Props) => {
const colapseAnimationDuration = 250
const duration = props.duration || 300
const {children, ...otherProps} = props
const {children, nodeRef, ...otherProps} = props
const getNode = () => (nodeRef as RefObject<HTMLElement>).current as HTMLElement
// eslint-disable-next-line no-undef
const animationProps: KeyframeAnimationOptions = {
fill: 'forwards',
duration,
}
const onEnter = (node: HTMLElement) => {
node.animate(
const onEnter = () => {
getNode().animate(
[
{
transform: 'scale(0.6)',
Expand All @@ -31,22 +31,28 @@ const GrowTransition = (props: Props) => {
animationProps
)
}
const onExit = (node: HTMLElement) => {
const onExit = () => {
const hideAnimationDuration = duration
node.animate([{transform: 'scale(0.6)', opacity: 0}], animationProps)
getNode().animate([{transform: 'scale(0.6)', opacity: 0}], animationProps)
setTimeout(() => {
// `150px`: A value higher than the height a notification can have
// to create a smooth animation for displayed notifications
// when a notification is removed from a container.
node.animate([{maxHeight: '150px'}, {margin: 0, maxHeight: 0}], {
getNode().animate([{maxHeight: '150px'}, {margin: 0, maxHeight: 0}], {
fill: 'forwards',
duration: hideAnimationDuration,
})
}, hideAnimationDuration)
}

return (
<Transition onEnter={onEnter} onExit={onExit} timeout={duration + colapseAnimationDuration} {...otherProps}>
<Transition
nodeRef={nodeRef}
onEnter={onEnter}
onExit={onExit}
timeout={duration + colapseAnimationDuration}
{...otherProps}
>
{children}
</Transition>
)
Expand Down
39 changes: 28 additions & 11 deletions src/components/NotificationContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
import React, {ReactNode, useEffect, useState} from 'react'
import React, {useEffect, useRef, useState} from 'react'
import {Timer} from '../utils'
import {Notification as NotificationType} from '../reducers/notifications/types'
import {DismissNotification} from './NotificationsSystem'
import {useComponentsContext} from '../hooks/useComponentsContext'
import SlideTransition from './SlideTransition'
import {useTheme} from '../hooks/useTheme'
import NotificationComponent from './Notification'
import {TransitionProps} from 'react-transition-group/Transition'

type Props = {
notification: NotificationType
dismissNotification: DismissNotification
children?: ReactNode
}
} & Omit<TransitionProps, 'addEndListener'>

const NotificationContainer = (props: Props) => {
const {notification, dismissNotification} = props
const {notification, dismissNotification, ...transitionProps} = props
const {dismissAfter, onAdd, onDismiss} = notification
const components = useComponentsContext()
const theme = useTheme()
const Transition = components.Transition || SlideTransition
const Notification = components.Notification || NotificationComponent
const [timer, setTimer] = useState<Timer | null>(null)
const nodeRef = useRef(null)

useEffect(() => {
if (onAdd) {
Expand All @@ -37,13 +46,21 @@ const NotificationContainer = (props: Props) => {
}, [dismissAfter])

return (
<div
data-testid="timed-notification"
onMouseEnter={timer ? () => timer.pause() : undefined}
onMouseLeave={timer ? () => timer.resume() : undefined}
>
{props.children}
</div>
<Transition notification={notification} nodeRef={nodeRef} {...transitionProps}>
<div
ref={nodeRef}
data-testid="timed-notification"
onMouseEnter={timer ? () => timer.pause() : undefined}
onMouseLeave={timer ? () => timer.resume() : undefined}
>
<Notification
notification={notification}
theme={theme}
dismissNotification={() => dismissNotification(notification.id)}
components={components}
/>
</div>
</Transition>
)
}

Expand Down
29 changes: 7 additions & 22 deletions src/components/NotificationsContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import React from 'react'
import {TransitionGroup} from 'react-transition-group'
import {Notification as NotificationType, Position} from '../reducers/notifications/types'
import {classnames, POSITIONS} from '../constants'
import {useComponentsContext} from '../hooks/useComponentsContext'
import {useTheme} from '../hooks/useTheme'
import SlideTransition from './SlideTransition'
import NotificationContainer from './NotificationContainer'
import NotificationComponent from './Notification'
import {DismissNotification} from './NotificationsSystem'
import {TransitionGroup} from 'react-transition-group'

type Props = {
notifications: NotificationType[]
Expand All @@ -18,12 +15,9 @@ type Props = {

const NotificationsContainer = (props: Props) => {
const {position, dismissNotification, singleContainer} = props
const components = useComponentsContext()
const theme = useTheme()
const styles = theme ? theme.container(position, singleContainer) : {}
const classname = classnames.container(position, singleContainer)
const Transition = components.Transition || SlideTransition
const Notification = components.Notification || NotificationComponent
let {notifications} = props

// when notifications are displayed at the bottom,
Expand All @@ -34,22 +28,13 @@ const NotificationsContainer = (props: Props) => {

return (
<div style={styles} className={classname.join(' ')}>
<TransitionGroup>
<TransitionGroup component={null}>
{notifications.map((notification) => (
<Transition key={notification.id} notification={notification}>
<NotificationContainer
key={notification.id}
notification={notification}
dismissNotification={dismissNotification}
>
<Notification
notification={notification}
dismissNotification={dismissNotification}
theme={theme}
components={components}
/>
</NotificationContainer>
</Transition>
<NotificationContainer
key={notification.id}
notification={notification}
dismissNotification={dismissNotification}
/>
))}
</TransitionGroup>
</div>
Expand Down
28 changes: 17 additions & 11 deletions src/components/SlideTransition.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import {Transition} from 'react-transition-group'
import React, {ReactNode} from 'react'
import React, {RefObject} from 'react'
import {POSITIONS} from '../constants'
import {Notification} from '../reducers/notifications/types'
import {TransitionProps} from 'react-transition-group/Transition'

type Props = {
notification: Notification
children: ReactNode
duration?: number
[index: string]: any
}
} & Omit<TransitionProps<HTMLElement>, 'addEndListener'>

const SlideTransition = (props: Props) => {
const duration = props.duration || 300
const colapseAnimationDuration = 250
const {children, notification, ...otherProps} = props
const {children, notification, nodeRef, ...otherProps} = props
const getNode = () => (nodeRef as RefObject<HTMLElement>).current as HTMLElement
const transformDirection = ([POSITIONS.topCenter, POSITIONS.bottomCenter] as string[]).includes(
notification.position
)
Expand All @@ -30,31 +30,37 @@ const SlideTransition = (props: Props) => {
fill: 'forwards',
duration,
}
const onEnter = (node: HTMLElement) => {
node.animate(
const onEnter = () => {
getNode().animate(
[
{transform: `${transformDirection}(${transformValue})`, opacity: 0},
{transform: `${transformDirection}(0)`, opacity: 1},
],
animationProps
)
}
const onExit = (node: HTMLElement) => {
const onExit = () => {
const hideAnimationDuration = duration
node.animate([{transform: `${transformDirection}(${transformValue})`, opacity: 0}], animationProps)
getNode().animate([{transform: `${transformDirection}(${transformValue})`, opacity: 0}], animationProps)
setTimeout(() => {
// `150px`: A value higher than the height a notification can have
// to create a smooth animation for displayed notifications
// when a notification is removed from a container.
node.animate([{maxHeight: '150px'}, {margin: 0, maxHeight: 0}], {
getNode().animate([{maxHeight: '150px'}, {margin: 0, maxHeight: 0}], {
...animationProps,
duration: colapseAnimationDuration,
})
}, hideAnimationDuration)
}

return (
<Transition onEnter={onEnter} onExit={onExit} timeout={duration + colapseAnimationDuration} {...otherProps}>
<Transition
nodeRef={nodeRef}
onEnter={onEnter}
onExit={onExit}
timeout={duration + colapseAnimationDuration}
{...otherProps}
>
{children}
</Transition>
)
Expand Down
36 changes: 33 additions & 3 deletions src/components/tests/FadeTransition.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react'
import React, {RefObject} from 'react'
import {render} from '@testing-library/react'

import {POSITIONS, STATUSES} from '../../constants'
Expand All @@ -13,11 +13,41 @@ describe('<FadeTransition/>', () => {
status: STATUSES.none,
buttons: [],
}
const {container} = render(
<FadeTransition notification={baseNotification}>
const nodeRef = {
current: {
animate: jest.fn(),
},
}
// Assert enter animation
const {container, rerender} = render(
<FadeTransition
mountOnEnter
appear
in
notification={baseNotification}
nodeRef={(nodeRef as unknown) as RefObject<HTMLElement>}
>
<div>notification</div>
</FadeTransition>
)
expect(container.innerHTML).toMatchSnapshot()
expect(nodeRef.current.animate.mock.calls).toMatchSnapshot()
jest.resetAllMocks()

// Assert exit animation
rerender(
<FadeTransition
mountOnEnter
appear
in={false}
notification={baseNotification}
nodeRef={(nodeRef as unknown) as RefObject<HTMLElement>}
>
<div>notification</div>
</FadeTransition>
)
expect(container.innerHTML).toMatchSnapshot()
expect(nodeRef.current.animate.mock.calls).toMatchSnapshot()
jest.resetAllMocks()
})
})

0 comments on commit cf9e52f

Please sign in to comment.