Skip to content

Commit

Permalink
[Menu][material] Fixes slots and slotProps overriding defaults comple…
Browse files Browse the repository at this point in the history
…tely (#37902)
  • Loading branch information
gitstart authored Jul 27, 2023
1 parent bca4565 commit 83a876d
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 12 deletions.
11 changes: 11 additions & 0 deletions docs/pages/material-ui/api/menu.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,17 @@
}
},
"PopoverClasses": { "type": { "name": "object" } },
"slotProps": {
"type": {
"name": "shape",
"description": "{ paper?: func<br>&#124;&nbsp;object, root?: func<br>&#124;&nbsp;object }"
},
"default": "{}"
},
"slots": {
"type": { "name": "shape", "description": "{ paper?: elementType, root?: elementType }" },
"default": "{}"
},
"sx": {
"type": {
"name": "union",
Expand Down
4 changes: 4 additions & 0 deletions docs/translations/api-docs/menu/menu.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
"PopoverClasses": {
"description": "<code>classes</code> prop applied to the <a href=\"/material-ui/api/popover/\"><code>Popover</code></a> element."
},
"slotProps": {
"description": "The extra props for the slot components. You can override the existing props or add new ones."
},
"slots": { "description": "The components used for each slot inside." },
"sx": {
"description": "The system prop that allows defining system overrides as well as additional CSS styles."
},
Expand Down
25 changes: 23 additions & 2 deletions packages/mui-material/src/Menu/Menu.d.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import * as React from 'react';
import { SxProps } from '@mui/system';
import { MenuOwnerState, SlotComponentProps } from '@mui/base';
import { InternalStandardProps as StandardProps } from '..';
import { PaperProps } from '../Paper';
import Paper, { PaperProps } from '../Paper';
import { PopoverProps } from '../Popover';
import { MenuListProps } from '../MenuList';
import { Theme } from '../styles';
import { TransitionProps } from '../transitions/transition';
import { MenuClasses } from './menuClasses';

export interface MenuProps extends StandardProps<PopoverProps> {
export interface MenuProps
extends StandardProps<Omit<PopoverProps, 'slots' | 'slotProps'>, 'children'> {
/**
* An HTML element, or a function that returns one.
* It's used to set the position of the menu.
Expand Down Expand Up @@ -58,6 +60,25 @@ export interface MenuProps extends StandardProps<PopoverProps> {
* `classes` prop applied to the [`Popover`](/material-ui/api/popover/) element.
*/
PopoverClasses?: PopoverProps['classes'];
/**
* The components used for each slot inside.
*
* @default {}
*/
slots?: {
root?: React.ElementType;
paper?: React.ElementType;
};
/**
* The extra props for the slot components.
* You can override the existing props or add new ones.
*
* @default {}
*/
slotProps?: {
root?: SlotComponentProps<typeof Menu, {}, MenuOwnerState>;
paper?: SlotComponentProps<typeof Paper, {}, {}>;
};
/**
* The system prop that allows defining system overrides as well as additional CSS styles.
*/
Expand Down
60 changes: 50 additions & 10 deletions packages/mui-material/src/Menu/Menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as React from 'react';
import { isFragment } from 'react-is';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import { unstable_composeClasses as composeClasses } from '@mui/base';
import { unstable_composeClasses as composeClasses, useSlotProps } from '@mui/base';
import { HTMLElementType } from '@mui/utils';
import MenuList from '../MenuList';
import Popover, { PopoverPaper } from '../Popover';
Expand Down Expand Up @@ -69,6 +69,7 @@ const Menu = React.forwardRef(function Menu(inProps, ref) {
const {
autoFocus = true,
children,
className,
disableAutoFocusItem = false,
MenuListProps = {},
onClose,
Expand All @@ -78,6 +79,8 @@ const Menu = React.forwardRef(function Menu(inProps, ref) {
transitionDuration = 'auto',
TransitionProps: { onEntering, ...TransitionProps } = {},
variant = 'selectedMenu',
slots = {},
slotProps = {},
...other
} = props;

Expand Down Expand Up @@ -156,6 +159,23 @@ const Menu = React.forwardRef(function Menu(inProps, ref) {
}
});

const PaperSlot = slots.paper ?? MenuPaper;
const paperExternalSlotProps = slotProps.paper ?? PaperProps;

const rootSlotProps = useSlotProps({
elementType: slots.root,
externalSlotProps: slotProps.root,
ownerState,
className: [classes.root, className],
});

const paperSlotProps = useSlotProps({
elementType: PaperSlot,
externalSlotProps: paperExternalSlotProps,
ownerState,
className: classes.paper,
});

return (
<MenuRoot
onClose={onClose}
Expand All @@ -164,17 +184,14 @@ const Menu = React.forwardRef(function Menu(inProps, ref) {
horizontal: isRtl ? 'right' : 'left',
}}
transformOrigin={isRtl ? RTL_ORIGIN : LTR_ORIGIN}
slots={{ paper: MenuPaper }}
slots={{
paper: PaperSlot,
root: slots.root,
}}
slotProps={{
paper: {
...PaperProps,
classes: {
...PaperProps.classes,
root: classes.paper,
},
},
root: rootSlotProps,
paper: paperSlotProps,
}}
className={classes.root}
open={open}
ref={ref}
transitionDuration={transitionDuration}
Expand Down Expand Up @@ -227,6 +244,10 @@ Menu.propTypes /* remove-proptypes */ = {
* Override or extend the styles applied to the component.
*/
classes: PropTypes.object,
/**
* @ignore
*/
className: PropTypes.string,
/**
* When opening the menu will not focus the active item but the `[role="menu"]`
* unless `autoFocus` is also set to `false`. Not using the default means not
Expand Down Expand Up @@ -259,6 +280,25 @@ Menu.propTypes /* remove-proptypes */ = {
* `classes` prop applied to the [`Popover`](/material-ui/api/popover/) element.
*/
PopoverClasses: PropTypes.object,
/**
* The extra props for the slot components.
* You can override the existing props or add new ones.
*
* @default {}
*/
slotProps: PropTypes.shape({
paper: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
}),
/**
* The components used for each slot inside.
*
* @default {}
*/
slots: PropTypes.shape({
paper: PropTypes.elementType,
root: PropTypes.elementType,
}),
/**
* The system prop that allows defining system overrides as well as additional CSS styles.
*/
Expand Down
20 changes: 20 additions & 0 deletions packages/mui-material/src/Menu/Menu.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ describe('<Menu />', () => {
render,
muiName: 'MuiMenu',
refInstanceof: window.HTMLDivElement,
slots: {
root: {
expectedClassName: classes.root,
},
paper: {
expectedClassName: classes.paper,
},
},
testDeepOverrides: { slotName: 'list', slotClassName: classes.list },
testRootOverrides: { slotName: 'root', slotClassName: classes.root },
testVariantProps: { variant: 'menu' },
Expand Down Expand Up @@ -384,4 +392,16 @@ describe('<Menu />', () => {
expect(wrapper.find(MenuPaper)).to.have.length(1);
});
});

describe('slots', () => {
it('should merge slots with existing values', () => {
const wrapper = mount(
<Menu slots={{ root: 'span' }} anchorEl={document.createElement('div')} open>
<div />
</Menu>,
);

expect(wrapper.find(MenuPaper)).to.have.length(1);
});
});
});

0 comments on commit 83a876d

Please sign in to comment.