diff --git a/docs/src/components/ComponentDoc/ComponentDoc.js b/docs/src/components/ComponentDoc/ComponentDoc.js
index 34bad666db..4fcf1e0817 100644
--- a/docs/src/components/ComponentDoc/ComponentDoc.js
+++ b/docs/src/components/ComponentDoc/ComponentDoc.js
@@ -73,12 +73,14 @@ class ComponentDoc extends Component {
subheader={_.join(componentInfo.docblock.description, ' ')}
/>
-
+ {componentInfo.repoPath && (
+
+ )}
diff --git a/docs/src/examples/addons/Ref/Types/RefExampleRef.js b/docs/src/examples/addons/Ref/Types/RefExampleRef.js
new file mode 100644
index 0000000000..e6370f8158
--- /dev/null
+++ b/docs/src/examples/addons/Ref/Types/RefExampleRef.js
@@ -0,0 +1,67 @@
+import React from 'react'
+import { Grid, Table, Ref, Segment } from 'semantic-ui-react'
+
+function RefExampleRef() {
+ const objectRef = React.useRef(null)
+ const [functionalRef, setFunctionalRef] = React.useState(null)
+ const [isMounted, setIsMounted] = React.useState(false)
+
+ React.useEffect(() => {
+ setIsMounted(true)
+ return () => setIsMounted(false)
+ }, [])
+
+ return (
+
+
+
+ [
+ ]An example node with functional ref
+
+ [
+ ]
+ An example node with ref via React.useRef()
+
+
+
+
+
+ {isMounted && (
+
+
+
+ Type
+
+ nodeName
+
+
+ textContent
+
+
+
+
+
+
+
+ Functional ref via React.useState()
hook
+
+ {functionalRef.nodeName}
+ {functionalRef.textContent}
+
+
+
+
+ From React.useRef()
hook
+
+ {objectRef.current.nodeName}
+ {objectRef.current.textContent}
+
+
+
+ )}
+
+
+ )
+}
+
+export default RefExampleRef
diff --git a/docs/src/examples/addons/Ref/Types/RefForwardingExample.js b/docs/src/examples/addons/Ref/Types/RefForwardingExample.js
new file mode 100644
index 0000000000..1e8ba6953d
--- /dev/null
+++ b/docs/src/examples/addons/Ref/Types/RefForwardingExample.js
@@ -0,0 +1,54 @@
+import React from 'react'
+import { Grid, Ref, Segment } from 'semantic-ui-react'
+
+const ExampleButton = React.forwardRef((props, ref) => (
+
+
+
+))
+
+function RefForwardingExample() {
+ const forwardedRef = React.useRef(null)
+ const [isMounted, setIsMounted] = React.useState(false)
+
+ React.useEffect(() => {
+ setIsMounted(true)
+ return () => setIsMounted(false)
+ }, [])
+
+ return (
+
+
+
+
+ A button below uses React.forwardRef()
API.
+
+
+ [
+ ]A button
+
+
+
+
+
+ {isMounted && (
+
+
+ {JSON.stringify(
+ {
+ nodeName: forwardedRef.current.nodeName,
+ nodeType: forwardedRef.current.nodeType,
+ textContent: forwardedRef.current.textContent,
+ },
+ null,
+ 2,
+ )}
+
+
+ )}
+
+
+ )
+}
+
+export default RefForwardingExample
diff --git a/docs/src/examples/addons/Ref/Types/index.js b/docs/src/examples/addons/Ref/Types/index.js
new file mode 100644
index 0000000000..6431b2eefa
--- /dev/null
+++ b/docs/src/examples/addons/Ref/Types/index.js
@@ -0,0 +1,30 @@
+import React from 'react'
+
+import ComponentExample from 'docs/src/components/ComponentDoc/ComponentExample'
+import ExampleSection from 'docs/src/components/ComponentDoc/ExampleSection'
+
+const RefTypesExamples = () => (
+
+
+ A component exposes the innerRef
prop that always returns
+ the DOM node of both functional and class component children.
+
+ }
+ examplePath='addons/Ref/Types/RefExampleRef'
+ />
+
+ React.forwardRef()
API is also handled by this component.
+
+ }
+ examplePath='addons/Ref/Types/RefForwardingExample'
+ />
+
+)
+
+export default RefTypesExamples
diff --git a/docs/src/examples/addons/Ref/index.js b/docs/src/examples/addons/Ref/index.js
new file mode 100644
index 0000000000..fad5922cc1
--- /dev/null
+++ b/docs/src/examples/addons/Ref/index.js
@@ -0,0 +1,28 @@
+import React from 'react'
+import { Message } from 'semantic-ui-react'
+
+import Types from './Types'
+
+const RefExamples = () => (
+ <>
+
+
+ Currently, it's recommended to use Ref
component to get
+ refs to HTML elements from Semantic UI React components as not all
+ components support native ref forwarding via{' '}
+ React.forwardRef()
.
+
+
+ As it uses deprecated ReactDOM.findDOMNode()
you may
+ receive warnings in React's StrictMode. We are working on it in{' '}
+
+ Semantic-Org/Semantic-UI-React#3819
+
+ .
+
+
+
+ >
+)
+
+export default RefExamples
diff --git a/docs/static/utils/getComponentMenu.js b/docs/static/utils/getComponentMenu.js
index 0ebd169c78..c91d27d1c5 100644
--- a/docs/static/utils/getComponentMenu.js
+++ b/docs/static/utils/getComponentMenu.js
@@ -1,5 +1,17 @@
+import _ from 'lodash'
import componentMenu from '../../src/componentMenu'
-const getComponentMenu = () => componentMenu
+const getComponentMenu = () =>
+ _.sortBy(
+ [
+ ...componentMenu,
+ {
+ displayName: 'Ref',
+ type: 'addon',
+ external: true,
+ },
+ ],
+ 'displayName',
+ )
export default getComponentMenu
diff --git a/src/modules/Popup/lib/createReferenceProxy.js b/src/modules/Popup/lib/createReferenceProxy.js
index 2d61e3395c..8595ca6d94 100644
--- a/src/modules/Popup/lib/createReferenceProxy.js
+++ b/src/modules/Popup/lib/createReferenceProxy.js
@@ -1,4 +1,4 @@
-import { isRefObject, toRefObject } from '@fluentui/react-component-ref'
+import { isRefObject } from '@fluentui/react-component-ref'
import _ from 'lodash'
class ReferenceProxy {
@@ -31,7 +31,7 @@ class ReferenceProxy {
* @see https://popper.js.org/popper-documentation.html#referenceObject
*/
const createReferenceProxy = _.memoize(
- (reference) => new ReferenceProxy(isRefObject(reference) ? reference : toRefObject(reference)),
+ (reference) => new ReferenceProxy(isRefObject(reference) ? reference : { current: reference }),
)
export default createReferenceProxy
diff --git a/static.routes.js b/static.routes.js
index 5093932991..372d31a738 100644
--- a/static.routes.js
+++ b/static.routes.js
@@ -36,27 +36,82 @@ export default async () => {
})),
// Routes for components, i.e. /element/button
- ..._.map(getComponentMenu(), (baseInfo) => ({
- path: getComponentPathname(baseInfo),
+ ..._.map(
+ _.filter(getComponentMenu(), (baseInfo) => !baseInfo.external),
+ (baseInfo) => ({
+ path: getComponentPathname(baseInfo),
+ component: 'docs/src/components/ComponentDoc',
+ priority: 0.8,
+ getData: async () => {
+ const componentsInfo = getComponentGroupInfo(baseInfo.displayName)
+ const sidebarSections = getSidebarSections(baseInfo.displayName)
+
+ return {
+ componentsInfo,
+ exampleSources,
+ sidebarSections,
+ displayName: baseInfo.displayName,
+ deprecated: !!_.find(
+ _.get(componentsInfo[baseInfo.displayName], 'docblock.tags'),
+ (tag) => tag.title === 'deprecated',
+ ),
+ seeTags: getInfoForSeeTags(componentsInfo[baseInfo.displayName]),
+ }
+ },
+ }),
+ ),
+
+ {
+ path: `/addons/ref/`,
component: 'docs/src/components/ComponentDoc',
priority: 0.8,
getData: async () => {
- const componentsInfo = getComponentGroupInfo(baseInfo.displayName)
- const sidebarSections = getSidebarSections(baseInfo.displayName)
+ const componentsInfo = {
+ Ref: {
+ displayName: 'Ref',
+ props: [
+ {
+ description: ['Called when a child component will be mounted or updated.'],
+ name: 'innerRef',
+ type: 'func',
+ required: true,
+ tags: [
+ {
+ title: 'param',
+ description: 'Referred node.',
+ type: {
+ type: 'NameExpression',
+ name: 'HTMLElement',
+ },
+ name: 'node',
+ },
+ ],
+ },
+ ],
+ type: 'addon',
+ isParent: true,
+ subcomponents: [],
+ docblock: {
+ tags: [],
+ description: [
+ 'This component exposes the `innerRef` prop that supports functional and React.createRef()/React.useRef() API and returns the DOM node of both functional and class component children.',
+ ],
+ },
+ examplesExist: true,
+ },
+ }
+ const sidebarSections = getSidebarSections('Ref')
return {
componentsInfo,
exampleSources,
sidebarSections,
- displayName: baseInfo.displayName,
- deprecated: !!_.find(
- _.get(componentsInfo[baseInfo.displayName], 'docblock.tags'),
- (tag) => tag.title === 'deprecated',
- ),
- seeTags: getInfoForSeeTags(componentsInfo[baseInfo.displayName]),
+ displayName: 'Ref',
+ deprecated: false,
+ seeTags: [],
}
},
- })),
+ },
// Routes for layouts, i.e. /layouts/theming
..._.map(await getLayoutPaths(), ({ routeName, componentFilename }) => ({
diff --git a/test/specs/modules/Popup/lib/createReferenceProxy-test.js b/test/specs/modules/Popup/lib/createReferenceProxy-test.js
new file mode 100644
index 0000000000..0c08ab994a
--- /dev/null
+++ b/test/specs/modules/Popup/lib/createReferenceProxy-test.js
@@ -0,0 +1,19 @@
+import React from 'react'
+import createReferenceProxy from 'src/modules/Popup/lib/createReferenceProxy'
+
+describe('createReferenceProxy', () => {
+ it('handles nodes', () => {
+ const node = document.createElement('div')
+ const proxy = createReferenceProxy(node)
+
+ expect(proxy.getBoundingClientRect()).to.include({ height: 0, width: 0 })
+ })
+
+ it('handles ref objects', () => {
+ const ref = React.createRef()
+ const proxy = createReferenceProxy(ref)
+
+ ref.current = document.createElement('div')
+ expect(proxy.getBoundingClientRect()).to.include({ height: 0, width: 0 })
+ })
+})