Skip to content

Commit

Permalink
fix(ui-motion,ui-portal): fix ref for Transition and Portal
Browse files Browse the repository at this point in the history
  • Loading branch information
ke1k0 committed Apr 7, 2022
1 parent e71453c commit b847d9e
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 23 deletions.
28 changes: 21 additions & 7 deletions packages/ui-motion/src/Transition/BaseTransition/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@
* SOFTWARE.
*/

import React, { createRef, ReactElement } from 'react'
import React, { ReactElement } from 'react'

import { getClassList } from '@instructure/ui-dom-utils'
import { getClassList, findDOMNode } from '@instructure/ui-dom-utils'
import {
ensureSingleChild,
safeCloneElement
Expand Down Expand Up @@ -77,6 +77,18 @@ class BaseTransition extends React.Component<
transitioning: false
}

ref: Element | null = null

handleRef = (el: Element | null) => {
const { elementRef } = this.props

this.ref = el

if (typeof elementRef === 'function') {
elementRef(el)
}
}

componentDidMount() {
this.startTransition(this.props.in, this.props.transitionOnMount)
}
Expand Down Expand Up @@ -139,7 +151,7 @@ class BaseTransition extends React.Component<

const { onTransition } = this.props

const classList = getClassList(this)
const classList = getClassList(this.ref)

const transitionClassName = this.getTransitionClassName(toState)
const prevTransitionClassName = this.getTransitionClassName(fromState)
Expand Down Expand Up @@ -180,7 +192,7 @@ class BaseTransition extends React.Component<
this.setState({ transitioning: false }, () => {
if (this._unmounted) return

const classList = getClassList(this)
const classList = getClassList(this.ref)

;(Object.values(STATES) as BaseTransitionStatesType[]).forEach(
(state) => {
Expand Down Expand Up @@ -314,15 +326,17 @@ class BaseTransition extends React.Component<
}
}

ref = createRef()

renderChildren() {
return this.props.children
? safeCloneElement(
ensureSingleChild(this.props.children) as ReactElement,
{
'aria-hidden': !this.props.in ? true : undefined,
ref: this.ref
ref: (el: React.ReactInstance | null) => {
const ref = (findDOMNode(el) as Element) || null

this.handleRef(ref)
}
}
)
: null
Expand Down
11 changes: 9 additions & 2 deletions packages/ui-motion/src/Transition/BaseTransition/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ type TransitionCommonProps = {
* A single element to animate in and out
*/
children?: React.ReactNode

/**
* provides a reference to the underlying html root element
*/
elementRef?: (element: Element | null) => void
}

type OwnProps = {
Expand Down Expand Up @@ -171,7 +176,8 @@ const transitionCommonPropTypes: PropValidators<CommonPropKeys> = {
onExit: PropTypes.func,
onExiting: PropTypes.func,
onExited: PropTypes.func,
children: PropTypes.node
children: PropTypes.node,
elementRef: PropTypes.func
}

const propTypes: PropValidators<PropKeys> = {
Expand Down Expand Up @@ -208,7 +214,8 @@ const allowedProps: AllowedPropKeys = [
'onExiting',
'onExited',
'children',
'className'
'className',
'elementRef'
]

type BaseTransitionState = {
Expand Down
22 changes: 20 additions & 2 deletions packages/ui-motion/src/Transition/__tests__/Transition.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
* SOFTWARE.
*/

import React from 'react'
import React, { Component } from 'react'
import {
expect,
mount,
Expand All @@ -45,6 +45,12 @@ const getClass = (
return classNames[phase]
}

class ExampleComponent extends Component {
render() {
return <div>Example</div>
}
}

describe('<Transition />', async () => {
const types: TransitionType[] = [
'fade',
Expand All @@ -56,7 +62,7 @@ describe('<Transition />', async () => {
]

const expectTypeClass = function (type: TransitionType) {
it(`should correctly apply classes for '${type}'`, async () => {
it(`should correctly apply classes for '${type}' with html element`, async () => {
const subject = await mount(
<Transition type={type} in={true}>
<div>hello</div>
Expand All @@ -67,6 +73,18 @@ describe('<Transition />', async () => {

expect(transition.hasClass(getClass(type, 'entered'))).to.be.true()
})

it(`should correctly apply classes for '${type}' with Component`, async () => {
const subject = await mount(
<Transition type={type} in={true}>
<ExampleComponent />
</Transition>
)

const transition = within(subject.getDOMNode())

expect(transition.hasClass(getClass(type, 'entered'))).to.be.true()
})
}

types.forEach((type) => {
Expand Down
13 changes: 13 additions & 0 deletions packages/ui-motion/src/Transition/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,18 @@ class Transition extends Component<TransitionProps> {

static states = BaseTransition.states

ref: Element | null = null

handleRef = (el: Element | null) => {
const { elementRef } = this.props

this.ref = el

if (typeof elementRef === 'function') {
elementRef(el)
}
}

componentDidMount() {
this.props.makeStyles?.()
}
Expand Down Expand Up @@ -117,6 +129,7 @@ class Transition extends Component<TransitionProps> {
enteringClassName={styles!.classNames.entering}
onEntered={this.handleEntered}
onExited={this.handleExited}
elementRef={this.handleRef}
>
{children}
</BaseTransition>
Expand Down
3 changes: 2 additions & 1 deletion packages/ui-motion/src/Transition/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ const allowedProps: AllowedPropKeys = [
'onEntered',
'onExit',
'onExiting',
'onExited'
'onExited',
'elementRef'
]

export type { TransitionProps, TransitionType, TransitionStyle }
Expand Down
28 changes: 17 additions & 11 deletions packages/ui-portal/src/Portal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,19 @@ class Portal extends Component<PortalProps, PortalState> {

DOMNode: PortalNode = null

ref: PortalNode = null

handleRef = (el: PortalNode) => {
const { elementRef } = this.props

this.ref = el
this.DOMNode = el

if (typeof elementRef === 'function') {
elementRef(el)
}
}

componentDidMount() {
if (!canUseDOM) {
return
Expand Down Expand Up @@ -127,11 +140,8 @@ class Portal extends Component<PortalProps, PortalState> {
typeof this.DOMNode.parentNode.removeChild === 'function'
) {
this.DOMNode.parentNode.removeChild(this.DOMNode)
this.DOMNode = null

if (typeof this.props.elementRef === 'function') {
this.props.elementRef(this.DOMNode)
}
this.handleRef(null)
}
}

Expand Down Expand Up @@ -160,15 +170,11 @@ class Portal extends Component<PortalProps, PortalState> {
node.setAttribute(name, attributes[name] as string)
})

if (typeof elementRef === 'function') {
elementRef(node)
}

this.DOMNode = node
this.handleRef(node)
}

// Append node to container if it isn't already
if (this.DOMNode.parentNode !== this.state.mountNode) {
if (this.DOMNode && this.DOMNode.parentNode !== this.state.mountNode) {
if (insertAt === 'bottom') {
this.state.mountNode.appendChild(this.DOMNode)
} else {
Expand All @@ -179,7 +185,7 @@ class Portal extends Component<PortalProps, PortalState> {
}
}

return this.DOMNode
return this.DOMNode!
}

findMountNode(props: PortalProps) {
Expand Down

0 comments on commit b847d9e

Please sign in to comment.