Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Portal] Migrate to unstyled #24890

Merged
merged 8 commits into from
Feb 15, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions docs/pages/api-docs/portal-unstyled.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import * as React from 'react';
import ApiPage from 'docs/src/modules/components/ApiPage';
import mapApiPageTranslations from 'docs/src/modules/utils/mapApiPageTranslations';
import jsonPageContent from './portal-unstyled.json';

export default function Page(props) {
const { descriptions, pageContent } = props;
return <ApiPage descriptions={descriptions} pageContent={pageContent} />;
}

Page.getInitialProps = () => {
const req = require.context(
'docs/translations/api-docs/portal-unstyled',
false,
/portal-unstyled.*.json$/,
);
const descriptions = mapApiPageTranslations(req);

return {
descriptions,
pageContent: jsonPageContent,
};
};
15 changes: 15 additions & 0 deletions docs/pages/api-docs/portal-unstyled.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"props": {
"children": { "type": { "name": "node" } },
"container": { "type": { "name": "union", "description": "HTML element<br>&#124;&nbsp;func" } },
"disablePortal": { "type": { "name": "bool" } }
},
"name": "PortalUnstyled",
"styles": { "classes": [], "globalClasses": {}, "name": null },
"spread": false,
"filename": "/packages/material-ui-unstyled/src/PortalUnstyled/PortalUnstyled.js",
"inheritance": null,
"demos": "",
"styledComponent": true,
"cssComponent": false
}
2 changes: 1 addition & 1 deletion docs/pages/api-docs/portal.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"disablePortal": { "type": { "name": "bool" } }
},
"name": "Portal",
"styles": { "classes": [], "globalClasses": {}, "name": null },
"styles": { "classes": [], "globalClasses": {}, "name": "MuiPortal" },
"spread": false,
"filename": "/packages/material-ui/src/Portal/Portal.js",
"inheritance": null,
Expand Down
1 change: 1 addition & 0 deletions docs/src/pagesApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ module.exports = [
{ pathname: '/api-docs/popover' },
{ pathname: '/api-docs/popper' },
{ pathname: '/api-docs/portal' },
{ pathname: '/api-docs/portal-unstyled' },
{ pathname: '/api-docs/radio' },
{ pathname: '/api-docs/radio-group' },
{ pathname: '/api-docs/rating' },
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"componentDescription": "Portals provide a first-class way to render children into a DOM node\nthat exists outside the DOM hierarchy of the parent component.",
"propDescriptions": {
"children": "The children to render into the <code>container</code>.",
"container": "An HTML element or function that returns one. The <code>container</code> will have the portal children appended to it.<br>By default, it uses the body of the top-level document object, so it&#39;s simply <code>document.body</code> most of the time.",
"disablePortal": "The <code>children</code> will be under the DOM hierarchy of the parent component."
},
"classDescriptions": {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"componentDescription": "Portals provide a first-class way to render children into a DOM node\nthat exists outside the DOM hierarchy of the parent component.",
"propDescriptions": {
"children": "The children to render into the <code>container</code>.",
"container": "An HTML element or function that returns one. The <code>container</code> will have the portal children appended to it.<br>By default, it uses the body of the top-level document object, so it&#39;s simply <code>document.body</code> most of the time.",
"disablePortal": "The <code>children</code> will be under the DOM hierarchy of the parent component."
},
"classDescriptions": {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"componentDescription": "Portals provide a first-class way to render children into a DOM node\nthat exists outside the DOM hierarchy of the parent component.",
povilass marked this conversation as resolved.
Show resolved Hide resolved
"propDescriptions": {
"children": "The children to render into the <code>container</code>.",
"container": "An HTML element or function that returns one. The <code>container</code> will have the portal children appended to it.<br>By default, it uses the body of the top-level document object, so it&#39;s simply <code>document.body</code> most of the time.",
"disablePortal": "The <code>children</code> will be under the DOM hierarchy of the parent component."
},
"classDescriptions": {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"componentDescription": "Portals provide a first-class way to render children into a DOM node\nthat exists outside the DOM hierarchy of the parent component.",
"propDescriptions": {
"children": "The children to render into the <code>container</code>.",
"container": "An HTML element or function that returns one. The <code>container</code> will have the portal children appended to it.<br>By default, it uses the body of the top-level document object, so it&#39;s simply <code>document.body</code> most of the time.",
"disablePortal": "The <code>children</code> will be under the DOM hierarchy of the parent component."
},
"classDescriptions": {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"componentDescription": "Portals provide a first-class way to render children into a DOM node\nthat exists outside the DOM hierarchy of the parent component.",
"propDescriptions": {
"children": "The children to render into the <code>container</code>.",
"container": "An HTML element or function that returns one. The <code>container</code> will have the portal children appended to it.<br>By default, it uses the body of the top-level document object, so it&#39;s simply <code>document.body</code> most of the time.",
"disablePortal": "The <code>children</code> will be under the DOM hierarchy of the parent component."
},
"classDescriptions": {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"componentDescription": "Portals provide a first-class way to render children into a DOM node\nthat exists outside the DOM hierarchy of the parent component.",
"propDescriptions": {
"children": "The children to render into the <code>container</code>.",
"container": "An HTML element or function that returns one. The <code>container</code> will have the portal children appended to it.<br>By default, it uses the body of the top-level document object, so it&#39;s simply <code>document.body</code> most of the time.",
"disablePortal": "The <code>children</code> will be under the DOM hierarchy of the parent component."
},
"classDescriptions": {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"componentDescription": "Portals provide a first-class way to render children into a DOM node\nthat exists outside the DOM hierarchy of the parent component.",
"propDescriptions": {
"children": "The children to render into the <code>container</code>.",
"container": "An HTML element or function that returns one. The <code>container</code> will have the portal children appended to it.<br>By default, it uses the body of the top-level document object, so it&#39;s simply <code>document.body</code> most of the time.",
"disablePortal": "The <code>children</code> will be under the DOM hierarchy of the parent component."
},
"classDescriptions": {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"componentDescription": "Portals provide a first-class way to render children into a DOM node\nthat exists outside the DOM hierarchy of the parent component.",
"propDescriptions": {
"children": "The children to render into the <code>container</code>.",
"container": "An HTML element or function that returns one. The <code>container</code> will have the portal children appended to it.<br>By default, it uses the body of the top-level document object, so it&#39;s simply <code>document.body</code> most of the time.",
"disablePortal": "The <code>children</code> will be under the DOM hierarchy of the parent component."
},
"classDescriptions": {}
}
2 changes: 1 addition & 1 deletion packages/material-ui-unstyled/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"build:types": "node ../../scripts/buildTypes",
"prebuild": "rimraf build",
"release": "yarn build && npm publish build --tag next",
"test": "cd ../../ && cross-env NODE_ENV=test mocha 'packages/material-ui-lab/**/*.test.{js,ts,tsx}'",
"test": "cd ../../ && cross-env NODE_ENV=test mocha 'packages/material-ui-unstyled/**/*.test.{js,ts,tsx}'",
"typescript": "tslint -p tsconfig.json \"{src,test}/**/*.{spec,d}.{ts,tsx}\" && tsc -p tsconfig.json"
},
"peerDependencies": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import * as React from 'react';

export type PortalUnstyledProps<P = {}> = P & {
/**
* The children to render into the `container`.
*/
children?: React.ReactNode;
/**
* An HTML element or function that returns one.
* The `container` will have the portal children appended to it.
*
* By default, it uses the body of the top-level document object,
* so it's simply `document.body` most of the time.
*/
container?: Element | (() => Element | null) | null;
/**
* The `children` will be under the DOM hierarchy of the parent component.
* @default false
*/
disablePortal?: boolean;
};

/**
* Portals provide a first-class way to render children into a DOM node
* that exists outside the DOM hierarchy of the parent component.
*
* Demos:
*
* - [Portal](https://material-ui.com/components/portal/)
*
* API:
*
* - [PortalUnstyled API](https://material-ui.com/api/portal-unstyled/)
*/
export default function PortalUnstyled(props: PortalUnstyledProps): JSX.Element;
86 changes: 86 additions & 0 deletions packages/material-ui-unstyled/src/PortalUnstyled/PortalUnstyled.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import {
exactProp,
HTMLElementType,
unstable_useEnhancedEffect as useEnhancedEffect,
unstable_useForkRef as useForkRef,
unstable_setRef as setRef,
} from '@material-ui/utils';

function getContainer(container) {
return typeof container === 'function' ? container() : container;
}

/**
* Portals provide a first-class way to render children into a DOM node
* that exists outside the DOM hierarchy of the parent component.
*/
const PortalUnstyled = React.forwardRef(function PortalUnstyled(props, ref) {
const { children, container, disablePortal = false } = props;
const [mountNode, setMountNode] = React.useState(null);
const handleRef = useForkRef(React.isValidElement(children) ? children.ref : null, ref);

useEnhancedEffect(() => {
if (!disablePortal) {
setMountNode(getContainer(container) || document.body);
}
}, [container, disablePortal]);

useEnhancedEffect(() => {
if (mountNode && !disablePortal) {
setRef(ref, mountNode);
return () => {
setRef(ref, null);
};
}

return undefined;
}, [ref, mountNode, disablePortal]);

if (disablePortal) {
if (React.isValidElement(children)) {
return React.cloneElement(children, {
ref: handleRef,
});
}
return children;
}

return mountNode ? ReactDOM.createPortal(children, mountNode) : mountNode;
});

PortalUnstyled.propTypes = {
// ----------------------------- Warning --------------------------------
// | These PropTypes are generated from the TypeScript type definitions |
// | To update them edit the d.ts file and run "yarn proptypes" |
// ----------------------------------------------------------------------
/**
* The children to render into the `container`.
*/
children: PropTypes.node,
/**
* An HTML element or function that returns one.
* The `container` will have the portal children appended to it.
*
* By default, it uses the body of the top-level document object,
* so it's simply `document.body` most of the time.
*/
container: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([
HTMLElementType,
PropTypes.func,
]),
/**
* The `children` will be under the DOM hierarchy of the parent component.
* @default false
*/
disablePortal: PropTypes.bool,
};

if (process.env.NODE_ENV !== 'production') {
// eslint-disable-next-line
PortalUnstyled['propTypes' + ''] = exactProp(PortalUnstyled.propTypes);
}

export default PortalUnstyled;
Loading