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

[Popover] Migrate to emotion #25197

Merged
merged 11 commits into from
Mar 7, 2021
Merged
3 changes: 2 additions & 1 deletion docs/pages/api-docs/popover.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"type": { "name": "shape", "description": "{ component?: element type }" },
"default": "{}"
},
"sx": { "type": { "name": "object" } },
"transformOrigin": {
"type": {
"name": "shape",
Expand All @@ -55,6 +56,6 @@
"filename": "/packages/material-ui/src/Popover/Popover.js",
"inheritance": { "component": "Modal", "pathname": "/api/modal/" },
"demos": "<ul><li><a href=\"/components/menus/\">Menus</a></li>\n<li><a href=\"/components/popover/\">Popover</a></li></ul>",
"styledComponent": false,
"styledComponent": true,
"cssComponent": false
}
1 change: 1 addition & 0 deletions docs/translations/api-docs/popover/popover.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"onClose": "Callback fired when the component requests to be closed. The <code>reason</code> parameter can optionally be used to control the response to <code>onClose</code>.",
"open": "If <code>true</code>, the component is shown.",
"PaperProps": "Props applied to the <a href=\"/api/paper/\"><code>Paper</code></a> element.",
"sx": "The system prop that allows defining system overrides as well as additional CSS styles. See the <a href=\"/system/basics/#the-sx-prop\">`sx` page</a> for more details.",
"transformOrigin": "This is the point on the popover which will attach to the anchor&#39;s origin.<br>Options: vertical: [top, center, bottom, x(px)]; horizontal: [left, center, right, x(px)].",
"TransitionComponent": "The component used for the transition. <a href=\"/components/transitions/#transitioncomponent-prop\">Follow this guide</a> to learn more about the requirements for this component.",
"transitionDuration": "Set to &#39;auto&#39; to automatically calculate transition time based on height.",
Expand Down
6 changes: 6 additions & 0 deletions packages/material-ui/src/Popover/Popover.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import * as React from 'react';
import { SxProps } from '@material-ui/system';
import { InternalStandardProps as StandardProps } from '..';
import { PaperProps } from '../Paper';
import { ModalProps } from '../Modal';
import { Theme } from '../styles';
import { TransitionHandlerProps, TransitionProps } from '../transitions/transition';

export interface PopoverOrigin {
Expand Down Expand Up @@ -103,6 +105,10 @@ export interface PopoverProps
* @default {}
*/
PaperProps?: Partial<PaperProps>;
/**
* The system prop that allows defining system overrides as well as additional CSS styles.
*/
sx?: SxProps<Theme>;
/**
* This is the point on the popover which
* will attach to the anchor's origin.
Expand Down
115 changes: 84 additions & 31 deletions packages/material-ui/src/Popover/Popover.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import { unstable_composeClasses as composeClasses } from '@material-ui/unstyled';
import {
chainPropTypes,
deepmerge,
elementTypeAcceptingRef,
refType,
HTMLElementType,
} from '@material-ui/utils';
import clsx from 'clsx';
import experimentalStyled from '../styles/experimentalStyled';
import useThemeProps from '../styles/useThemeProps';
import debounce from '../utils/debounce';
import ownerDocument from '../utils/ownerDocument';
import ownerWindow from '../utils/ownerWindow';
import withStyles from '../styles/withStyles';
import Modal from '../Modal';
import Grow from '../Grow';
import Modal from '../Modal';
import Paper from '../Paper';
import popoverClasses, { getPopoverUtilityClass } from './popoverClasses';

export function getOffsetTop(rect, vertical) {
let offset = 0;
Expand Down Expand Up @@ -65,26 +69,56 @@ function getAnchorEl(anchorEl) {
return typeof anchorEl === 'function' ? anchorEl() : anchorEl;
}

export const styles = {
/* Styles applied to the root element. */
root: {},
/* Styles applied to the Paper component. */
paper: {
position: 'absolute',
overflowY: 'auto',
overflowX: 'hidden',
// So we see the popover when it's empty.
// It's most likely on issue on userland.
minWidth: 16,
minHeight: 16,
maxWidth: 'calc(100% - 32px)',
maxHeight: 'calc(100% - 32px)',
// We disable the focus ring for mouse, touch and keyboard users.
outline: 0,
},
const overridesResolver = (props, styles) => {
return deepmerge(styles.root || {}, {
[`& .${popoverClasses.paper}`]: styles.paper,
});
};

const useUtilityClasses = (styleProps) => {
const { classes } = styleProps;

const slots = {
root: ['root'],
paper: ['paper'],
};

return composeClasses(slots, getPopoverUtilityClass, classes);
};

const Popover = React.forwardRef(function Popover(props, ref) {
const PopoverRoot = experimentalStyled(
Modal,
{},
{
name: 'MuiPopover',
slot: 'Root',
overridesResolver,
},
)({});

const PopoverPaper = experimentalStyled(
Paper,
{},
{
name: 'MuiPopover',
slot: 'Paper',
},
)({
position: 'absolute',
overflowY: 'auto',
overflowX: 'hidden',
// So we see the popover when it's empty.
// It's most likely on issue on userland.
minWidth: 16,
minHeight: 16,
maxWidth: 'calc(100% - 32px)',
maxHeight: 'calc(100% - 32px)',
// We disable the focus ring for mouse, touch and keyboard users.
outline: 0,
});

const Popover = React.forwardRef(function Popover(inProps, ref) {
const props = useThemeProps({ props: inProps, name: 'MuiPopover' });
const {
action,
anchorEl,
Expand All @@ -95,7 +129,6 @@ const Popover = React.forwardRef(function Popover(props, ref) {
anchorPosition,
anchorReference = 'anchorEl',
children,
classes,
className,
container: containerProp,
elevation = 8,
Expand All @@ -114,6 +147,21 @@ const Popover = React.forwardRef(function Popover(props, ref) {
} = props;
const paperRef = React.useRef();

const styleProps = {
...props,
anchorOrigin,
anchorReference,
elevation,
marginThreshold,
PaperProps,
transformOrigin,
TransitionComponent,
transitionDuration: transitionDurationProp,
TransitionProps,
};

const classes = useUtilityClasses(styleProps);

// Returns the top/left offset of the position
// to attach to on the anchor element (or body if none is provided)
const getAnchorOffset = React.useCallback(
Expand Down Expand Up @@ -379,31 +427,32 @@ const Popover = React.forwardRef(function Popover(props, ref) {
containerProp || (anchorEl ? ownerDocument(getAnchorEl(anchorEl)).body : undefined);

return (
<Modal
<PopoverRoot
BackdropProps={{ invisible: true }}
className={clsx(classes.root, className)}
container={container}
open={open}
ref={ref}
BackdropProps={{ invisible: true }}
className={clsx(classes.root, className)}
styleProps={styleProps}
{...other}
>
<TransitionComponent
appear
in={open}
timeout={transitionDuration}
onEntering={handleEntering}
timeout={transitionDuration}
{...TransitionProps}
>
<Paper
<PopoverPaper
className={clsx(classes.paper, PaperProps.className)}
elevation={elevation}
ref={paperRef}
{...PaperProps}
className={clsx(classes.paper, PaperProps.className)}
>
{children}
</Paper>
</PopoverPaper>
</TransitionComponent>
</Modal>
</PopoverRoot>
);
});

Expand Down Expand Up @@ -548,6 +597,10 @@ Popover.propTypes = {
PaperProps: PropTypes /* @typescript-to-proptypes-ignore */.shape({
component: elementTypeAcceptingRef,
}),
/**
* The system prop that allows defining system overrides as well as additional CSS styles.
*/
sx: PropTypes.object,
/**
* This is the point on the popover which
* will attach to the anchor's origin.
Expand Down Expand Up @@ -595,4 +648,4 @@ Popover.propTypes = {
TransitionProps: PropTypes.object,
};

export default withStyles(styles, { name: 'MuiPopover' })(Popover);
export default Popover;
33 changes: 19 additions & 14 deletions packages/material-ui/src/Popover/Popover.test.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import * as React from 'react';
import { expect } from 'chai';
import { spy, stub, useFakeTimers } from 'sinon';
import { findOutermostIntrinsic, getClasses, createMount, describeConformance } from 'test/utils';
import {
findOutermostIntrinsic,
createMount,
createClientRender,
describeConformanceV5,
} from 'test/utils';
import PropTypes from 'prop-types';
import Grow from '../Grow';
import Modal from '../Modal';
import Paper from '../Paper';
import Popover, { getOffsetLeft, getOffsetTop } from './Popover';
import classes from './popoverClasses';
import useForkRef from '../utils/useForkRef';

const mockedAnchorEl = () => {
Expand Down Expand Up @@ -49,27 +55,26 @@ const FakePaper = React.forwardRef(function FakeWidthPaper(props, ref) {
describe('<Popover />', () => {
// StrictModeViolation: Not using act(), prefer using createClientRender from test/utils
const mount = createMount({ strict: false });
let classes;
const render = createClientRender();
const defaultProps = {
open: false,
anchorEl: () => document.createElement('svg'),
};

before(() => {
classes = getClasses(
<Popover {...defaultProps}>
<div />
</Popover>,
);
});

describeConformance(<Popover {...defaultProps} open />, () => ({
describeConformanceV5(<Popover {...defaultProps} open />, () => ({
classes,
inheritComponent: Modal,
render,
mount,
muiName: 'MuiPopover',
refInstanceof: window.HTMLDivElement,
testDeepOverrides: { slotName: 'paper', slotClassName: classes.paper },
skip: [
'componentProp',
'componentsProp',
'themeDefaultProps',
'themeStyleOverrides',
'themeVariants',
// react-transition-group issue
'reactTestRenderer',
],
Expand All @@ -83,7 +88,7 @@ describe('<Popover />', () => {
</Popover>,
);
const root = wrapper.find('ForwardRef(Popover) > [data-root-node]').first();
expect(root.type()).to.equal(Modal);
expect(root.find(Modal).exists()).to.equal(true);
expect(root.props().BackdropProps.invisible).to.equal(true);
});

Expand Down Expand Up @@ -433,7 +438,7 @@ describe('<Popover />', () => {
it('should warn if anchorEl is not valid', () => {
expect(() => {
PropTypes.checkPropTypes(
Popover.Naked.propTypes,
Popover.propTypes,
{ classes: {}, open: true },
'prop',
'MockedPopover',
Expand All @@ -444,7 +449,7 @@ describe('<Popover />', () => {
it('warns if a component for the Paper is used that cant hold a ref', () => {
expect(() => {
PropTypes.checkPropTypes(
Popover.Naked.propTypes,
Popover.propTypes,
{ ...defaultProps, classes: {}, PaperProps: { component: () => <div />, elevation: 4 } },
'prop',
'MockedPopover',
Expand Down
3 changes: 3 additions & 0 deletions packages/material-ui/src/Popover/index.js
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
export { default } from './Popover';

export { default as popoverClasses } from './popoverClasses';
export * from './popoverClasses';
7 changes: 7 additions & 0 deletions packages/material-ui/src/Popover/popoverClasses.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { PopoverClassKey } from './Popover';

declare const popoverClasses: Record<PopoverClassKey, string>;

export function getPopoverUtilityClass(slot: string): string;

export default popoverClasses;
9 changes: 9 additions & 0 deletions packages/material-ui/src/Popover/popoverClasses.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { generateUtilityClass, generateUtilityClasses } from '@material-ui/unstyled';

export function getPopoverUtilityClass(slot) {
return generateUtilityClass('MuiPopover', slot);
}

const popoverClasses = generateUtilityClasses('MuiPopover', ['root', 'paper']);

export default popoverClasses;