Skip to content

Commit aaa4a58

Browse files
committed
fix(ui-motion,ui-alerts): capture child DOM via elementRef in Transition to avoid React 'ref is not a prop' warning
1 parent 446e820 commit aaa4a58

5 files changed

Lines changed: 60 additions & 15 deletions

File tree

packages/ui-alerts/src/Alert/v1/index.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ class Alert extends Component<AlertProps, AlertState> {
9999

100100
handleRef = (el: Element | null) => {
101101
this.ref = el
102+
const { elementRef } = this.props
103+
if (typeof elementRef === 'function') {
104+
elementRef(el)
105+
}
102106
}
103107

104108
handleTimeout = () => {

packages/ui-alerts/src/Alert/v1/props.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@ type AlertOwnProps = {
109109
* An icon, or function that returns an icon. Setting it will override the variant's icon.
110110
*/
111111
renderCustomIcon?: Renderable
112+
113+
/**
114+
* provides a reference to the underlying html root element
115+
*/
116+
elementRef?: (element: Element | null) => void
112117
}
113118

114119
type PropKeys = keyof AlertOwnProps
@@ -137,7 +142,8 @@ const allowedProps: AllowedPropKeys = [
137142
'transition',
138143
'open',
139144
'hasShadow',
140-
'renderCustomIcon'
145+
'renderCustomIcon',
146+
'elementRef'
141147
]
142148

143149
type AlertState = {

packages/ui-alerts/src/Alert/v2/index.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ class Alert extends Component<AlertProps, AlertState> {
9898

9999
handleRef = (el: Element | null) => {
100100
this.ref = el
101+
const { elementRef } = this.props
102+
if (typeof elementRef === 'function') {
103+
elementRef(el)
104+
}
101105
}
102106

103107
handleTimeout = () => {

packages/ui-alerts/src/Alert/v2/props.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,11 @@ type AlertOwnProps = {
110110
* An icon, or function that returns an icon. Setting it will override the variant's icon.
111111
*/
112112
renderCustomIcon?: Renderable
113+
114+
/**
115+
* provides a reference to the underlying html root element
116+
*/
117+
elementRef?: (element: Element | null) => void
113118
}
114119

115120
type PropKeys = keyof AlertOwnProps
@@ -138,7 +143,8 @@ const allowedProps: AllowedPropKeys = [
138143
'transition',
139144
'open',
140145
'hasShadow',
141-
'renderCustomIcon'
146+
'renderCustomIcon',
147+
'elementRef'
142148
]
143149

144150
type AlertState = {

packages/ui-motion/src/Transition/BaseTransition/index.ts

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,14 @@
2222
* SOFTWARE.
2323
*/
2424

25-
import { Component, ReactElement } from 'react'
25+
import { Component, ReactElement, ReactInstance } from 'react'
2626

2727
import { getClassList, findDOMNode } from '@instructure/ui-dom-utils'
2828
import {
2929
ensureSingleChild,
3030
safeCloneElement
3131
} from '@instructure/ui-react-utils'
32+
import { createChainedFunction } from '@instructure/ui-utils'
3233

3334
import { allowedProps } from './props.js'
3435
import type {
@@ -334,19 +335,43 @@ class BaseTransition extends Component<
334335
}
335336

336337
renderChildren() {
337-
return this.props.children
338-
? safeCloneElement(
339-
ensureSingleChild(this.props.children) as ReactElement,
340-
{
341-
'aria-hidden': !this.props.in ? true : undefined,
342-
ref: (el: React.ReactInstance | null) => {
343-
const ref = (findDOMNode(el) as Element) || null
344-
345-
this.handleRef(ref)
346-
}
338+
if (!this.props.children) {
339+
return null
340+
}
341+
342+
const child = ensureSingleChild(this.props.children) as ReactElement
343+
344+
const elementOnlyRef = (el: ReactInstance | Element | null) => {
345+
if (el instanceof Element) {
346+
this.handleRef(el)
347+
}
348+
}
349+
350+
// `typeof type === 'object'` => forwardRef wrapper (withStyle-decorated InstUI components)
351+
const refProps =
352+
typeof child.type === 'object'
353+
? {
354+
// chain so the child's own elementRef still fires instead of being overwritten
355+
elementRef: createChainedFunction(
356+
(child.props as { elementRef?: (el: Element | null) => void })
357+
?.elementRef,
358+
this.handleRef
359+
),
360+
// fallback for forwardRef children that expose their node via `ref`, not elementRef
361+
ref: elementOnlyRef
347362
}
348-
)
349-
: null
363+
: {
364+
// for host el / plain class|fn: findDOMNode is the fallback
365+
ref: (el: ReactInstance | Element | null) =>
366+
this.handleRef(
367+
el instanceof Element ? el : (findDOMNode(el) as Element) ?? null
368+
)
369+
}
370+
371+
return safeCloneElement(child, {
372+
'aria-hidden': !this.props.in ? true : undefined,
373+
...refProps
374+
})
350375
}
351376

352377
render() {

0 commit comments

Comments
 (0)