From 0d06a1bc10fca39f5467a3a1057f167a843113bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dudak?= Date: Mon, 7 Aug 2023 16:41:20 +0200 Subject: [PATCH 1/2] [POC] Slots using render props --- docs/pages/experiments/base/render-props.tsx | 88 +++++++++++++++++++ .../mui-base/src/MenuButton/MenuButton.tsx | 17 +++- .../src/MenuButton/MenuButton.types.ts | 11 +++ .../src/useMenuButton/useMenuButton.types.ts | 2 +- 4 files changed, 113 insertions(+), 5 deletions(-) create mode 100644 docs/pages/experiments/base/render-props.tsx diff --git a/docs/pages/experiments/base/render-props.tsx b/docs/pages/experiments/base/render-props.tsx new file mode 100644 index 00000000000000..75d90b7ce52fe4 --- /dev/null +++ b/docs/pages/experiments/base/render-props.tsx @@ -0,0 +1,88 @@ +import * as React from 'react'; +import { Menu } from '@mui/base/Menu'; +import { MenuItem } from '@mui/base/MenuItem'; +import { MenuButton, MenuButtonRootSlotProps } from '@mui/base/MenuButton'; +import { Dropdown } from '@mui/base/Dropdown'; +import IconButton from '@mui/joy/IconButton'; +import MenuIcon from '@mui/icons-material/Menu'; +import { CssVarsProvider } from '@mui/joy'; + +function WithRenderProp() { + return ( + + ( + + + + )} + /> + + Profile + My account + Logout + + + ); +} + +function WithSlotsAndSlotProps() { + return ( + + + + + + Profile + My account + Logout + + + ); +} + +function MenuIconButton(props: MenuButtonRootSlotProps) { + return ( + + {props.children} + + ); +} + +function WithSlots() { + return ( + + + + + + Profile + My account + Logout + + + ); +} + +export default function RenderProps() { + return ( + + + + + + ); +} diff --git a/packages/mui-base/src/MenuButton/MenuButton.tsx b/packages/mui-base/src/MenuButton/MenuButton.tsx index 9ae79be6d66fb6..a1469818cfe678 100644 --- a/packages/mui-base/src/MenuButton/MenuButton.tsx +++ b/packages/mui-base/src/MenuButton/MenuButton.tsx @@ -1,8 +1,8 @@ 'use client'; import * as React from 'react'; import PropTypes from 'prop-types'; -import { MenuButtonOwnerState, MenuButtonProps } from './MenuButton.types'; -import { useSlotProps } from '../utils'; +import { MenuButtonOwnerState, MenuButtonProps, MenuButtonRootSlotProps } from './MenuButton.types'; +import { useSlotProps, WithOptionalOwnerState } from '../utils'; import { useMenuButton } from '../useMenuButton'; import { unstable_composeClasses as composeClasses } from '../composeClasses'; import { useClassNamesOverride } from '../utils/ClassNameConfigurator'; @@ -39,6 +39,7 @@ const MenuButton = React.forwardRef(function MenuButton( slots = {}, slotProps = {}, focusableWhenDisabled = false, + renderRoot, ...other } = props; @@ -59,7 +60,7 @@ const MenuButton = React.forwardRef(function MenuButton( const classes = useUtilityClasses(ownerState); const Root = slots.root || 'button'; - const rootProps = useSlotProps({ + const rootProps: WithOptionalOwnerState = useSlotProps({ elementType: Root, getSlotProps: getRootProps, externalForwardedProps: other, @@ -67,12 +68,20 @@ const MenuButton = React.forwardRef(function MenuButton( additionalProps: { ref: forwardedRef, type: 'button', + children, }, ownerState, className: classes.root, }); - return {children}; + // eslint-disable-next-line @typescript-eslint/naming-convention + const { ownerState: _, ...otherRootProps } = rootProps; + + return renderRoot ? ( + renderRoot(otherRootProps, ownerState) + ) : ( + {children} + ); }); MenuButton.propTypes /* remove-proptypes */ = { diff --git a/packages/mui-base/src/MenuButton/MenuButton.types.ts b/packages/mui-base/src/MenuButton/MenuButton.types.ts index 5d1d951824cc39..509605099c2689 100644 --- a/packages/mui-base/src/MenuButton/MenuButton.types.ts +++ b/packages/mui-base/src/MenuButton/MenuButton.types.ts @@ -1,3 +1,4 @@ +import { UseMenuButtonRootSlotProps } from '../useMenuButton'; import { SlotComponentProps } from '../utils/types'; export interface MenuButtonRootSlotPropsOverrides {} @@ -35,6 +36,11 @@ export interface MenuButtonProps { slotProps?: { root?: SlotComponentProps<'button', MenuButtonRootSlotPropsOverrides, MenuButtonOwnerState>; }; + + renderRoot?: ( + props: Omit, + ownerState: MenuButtonOwnerState, + ) => React.JSX.Element; } export interface MenuButtonSlots { @@ -50,3 +56,8 @@ export type MenuButtonOwnerState = MenuButtonProps & { focusableWhenDisabled: boolean; open: boolean; }; + +export type MenuButtonRootSlotProps = UseMenuButtonRootSlotProps & { + ownerState: MenuButtonOwnerState; + children?: React.ReactNode; +}; diff --git a/packages/mui-base/src/useMenuButton/useMenuButton.types.ts b/packages/mui-base/src/useMenuButton/useMenuButton.types.ts index 6549b290740d92..3d6d989dd9908b 100644 --- a/packages/mui-base/src/useMenuButton/useMenuButton.types.ts +++ b/packages/mui-base/src/useMenuButton/useMenuButton.types.ts @@ -17,7 +17,7 @@ export interface UseMenuButtonParameters { rootRef?: React.Ref; } -interface UseMenuButtonRootSlotProps { +export interface UseMenuButtonRootSlotProps { 'aria-haspopup': 'menu'; 'aria-expanded': boolean; 'aria-controls': string; From e067d9dbf1ea45877a7afa05121d2da386fad22e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dudak?= Date: Fri, 6 Oct 2023 13:31:15 +0200 Subject: [PATCH 2/2] Forward ref in the MenuIconButton component --- docs/pages/experiments/base/render-props.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/pages/experiments/base/render-props.tsx b/docs/pages/experiments/base/render-props.tsx index 75d90b7ce52fe4..dd30358e6a6cfd 100644 --- a/docs/pages/experiments/base/render-props.tsx +++ b/docs/pages/experiments/base/render-props.tsx @@ -50,13 +50,16 @@ function WithSlotsAndSlotProps() { ); } -function MenuIconButton(props: MenuButtonRootSlotProps) { +const MenuIconButton = React.forwardRef(function MenuIconButton( + props: MenuButtonRootSlotProps, + ref: React.ForwardedRef, +) { return ( - + {props.children} ); -} +}); function WithSlots() { return (