From e0de939a6505d15b48877d28b27466a785ded35d Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Mon, 17 Aug 2020 10:55:50 +0200 Subject: [PATCH 1/3] docs(Ref): restore docs for a component --- .../components/ComponentDoc/ComponentDoc.js | 14 ++-- .../addons/Ref/Types/RefExampleRef.js | 67 ++++++++++++++++ .../addons/Ref/Types/RefForwardingExample.js | 54 +++++++++++++ docs/src/examples/addons/Ref/Types/index.js | 30 ++++++++ docs/src/examples/addons/Ref/index.js | 28 +++++++ docs/static/utils/getComponentMenu.js | 14 +++- src/modules/Popup/lib/createReferenceProxy.js | 4 +- static.routes.js | 77 ++++++++++++++++--- 8 files changed, 268 insertions(+), 20 deletions(-) create mode 100644 docs/src/examples/addons/Ref/Types/RefExampleRef.js create mode 100644 docs/src/examples/addons/Ref/Types/RefForwardingExample.js create mode 100644 docs/src/examples/addons/Ref/Types/index.js create mode 100644 docs/src/examples/addons/Ref/index.js 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..d4965477ba 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 createRef() 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 }) => ({ From 49ac11304b427b55a048926c26075d0d23a5f2f6 Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Mon, 17 Aug 2020 12:22:42 +0200 Subject: [PATCH 2/3] fix description --- static.routes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static.routes.js b/static.routes.js index d4965477ba..372d31a738 100644 --- a/static.routes.js +++ b/static.routes.js @@ -94,7 +94,7 @@ export default async () => { docblock: { tags: [], description: [ - 'This component exposes the `innerRef` prop that supports functional and createRef() API and returns the DOM node of both functional and class component children.', + '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, From 088ae105fcfbdc45344b03b95bc60bf773f29137 Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Mon, 17 Aug 2020 12:33:36 +0200 Subject: [PATCH 3/3] add a UT --- .../Popup/lib/createReferenceProxy-test.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 test/specs/modules/Popup/lib/createReferenceProxy-test.js 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 }) + }) +})