From 7287c1ea1e732eb7777183148a389d0a7a3f8410 Mon Sep 17 00:00:00 2001 From: Ricky Smith Date: Tue, 7 Nov 2023 12:31:31 +0100 Subject: [PATCH] Refactor tooltip theming Copying the styling and `overridesResolver` from MUIs TooltipPopper is required because when overriding the Tooltips Popper we cannot extend the existing Popper. Overriding the Popper is required to implement our variant-specific styling, as we cannot set the styles from the Root element, as the Popper is not rendered as a child of the Tooltip in the DOM. --- packages/admin/admin/src/common/Tooltip.tsx | 232 +++++++++++++++----- 1 file changed, 172 insertions(+), 60 deletions(-) diff --git a/packages/admin/admin/src/common/Tooltip.tsx b/packages/admin/admin/src/common/Tooltip.tsx index 2d8a1111b1..ecdf9150af 100644 --- a/packages/admin/admin/src/common/Tooltip.tsx +++ b/packages/admin/admin/src/common/Tooltip.tsx @@ -1,58 +1,166 @@ -import { ClickAwayListener, ComponentsOverrides, Theme, Tooltip as MuiTooltip, tooltipClasses, TooltipProps as MuiTooltipProps } from "@mui/material"; -import { createStyles, WithStyles, withStyles } from "@mui/styles"; +import { + ClickAwayListener, + ComponentsOverrides, + Popper as MuiPopper, + Theme, + Tooltip as MuiTooltip, + tooltipClasses, + TooltipClassKey as MuiTooltipClassKey, + TooltipProps as MuiTooltipProps, +} from "@mui/material"; +import { css, styled, SxProps, useThemeProps } from "@mui/material/styles"; import React, { cloneElement } from "react"; export interface TooltipProps extends MuiTooltipProps { trigger?: "hover" | "focus" | "click"; variant?: Variant; + sx?: SxProps; } + type Variant = "light" | "dark" | "neutral" | "primary"; -export type TooltipClassKey = Variant; +export type TooltipClassKey = "root" | Variant | MuiTooltipClassKey; -const styles = (theme: Theme) => - createStyles({ - light: { - [`& .${tooltipClasses.arrow}`]: { - color: theme.palette.common.white, - }, - [`& .${tooltipClasses.tooltip}`]: { - backgroundColor: theme.palette.common.white, - color: theme.palette.common.black, - boxShadow: theme.shadows[1], - }, - }, - dark: { - [`& .${tooltipClasses.arrow}`]: { - color: theme.palette.grey[900], - }, - [`& .${tooltipClasses.tooltip}`]: { - backgroundColor: theme.palette.grey[900], - color: theme.palette.common.white, - boxShadow: theme.shadows[1], - }, - }, - neutral: { - [`& .${tooltipClasses.arrow}`]: { - color: theme.palette.grey[100], - }, - [`& .${tooltipClasses.tooltip}`]: { - backgroundColor: theme.palette.grey[100], - color: theme.palette.common.black, - }, - }, - primary: { - [`& .${tooltipClasses.arrow}`]: { - color: theme.palette.primary.light, - }, - [`& .${tooltipClasses.tooltip}`]: { - backgroundColor: theme.palette.primary.light, - color: theme.palette.common.black, - }, - }, - }); +type TooltipPopperProps = { + variant: Variant; + ownerState: MuiTooltipProps; +}; + +const TooltipRoot = styled(MuiTooltip, { + name: "CometAdminTooltip", + slot: "Root", + overridesResolver(_, styles) { + return [styles.root]; + }, +})(); + +const TooltipPopper = styled(MuiPopper, { + name: "CometAdminTooltip", + slot: "Popper", + overridesResolver({ variant, ownerState }: TooltipPopperProps, styles) { + return [ + styles.popper, + styles[variant], + // Copied the following from MUIs default TooltipPopper: https://github.com/mui/material-ui/blob/a13c0c026692aafc303756998a78f1d6c2dd707d/packages/mui-material/src/Tooltip/Tooltip.js#L48 + !ownerState.disableInteractive && styles.popperInteractive, + ownerState.arrow && styles.popperArrow, + !ownerState.open && styles.popperClose, + ]; + }, +})( + ({ theme, variant, ownerState }) => css` + ${variant === "light" && + css` + .${tooltipClasses.arrow} { + color: ${theme.palette.common.white}; + } + .${tooltipClasses.tooltip} { + background-color: ${theme.palette.common.white}; + color: ${theme.palette.common.black}; + box-shadow: ${theme.shadows[1]}; + } + `} + + ${variant === "dark" && + css` + .${tooltipClasses.arrow} { + color: ${theme.palette.grey[900]}; + } + .${tooltipClasses.tooltip} { + background-color: ${theme.palette.grey[900]}; + color: ${theme.palette.common.white}; + box-shadow: ${theme.shadows[1]}; + } + `} + + ${variant === "neutral" && + css` + .${tooltipClasses.arrow} { + color: ${theme.palette.grey[100]}; + } + .${tooltipClasses.tooltip} { + background-color: ${theme.palette.grey[100]}; + color: ${theme.palette.common.black}; + } + `} + + ${variant === "primary" && + css` + .${tooltipClasses.arrow} { + color: ${theme.palette.primary.light}; + } + .${tooltipClasses.tooltip} { + background-color: ${theme.palette.primary.light}; + color: ${theme.palette.common.black}; + } + `}; + + // Copied the following from MUIs default TooltipPopper: https://github.com/mui/material-ui/blob/a13c0c026692aafc303756998a78f1d6c2dd707d/packages/mui-material/src/Tooltip/Tooltip.js#L55 + z-index: ${theme.zIndex.tooltip}; + pointer-events: none; + ${!ownerState.disableInteractive && + css` + pointer-events: auto; + `}; + ${!ownerState.open && + css` + pointer-events: none; + `}; + ${ownerState.arrow && + css` + &[data-popper-placement*="bottom"] .${tooltipClasses.arrow} { + top: 0; + margin-top: -0.71em; + &::before { + transform-origin: 0 100%; + } + } + &[data-popper-placement*="top"] .${tooltipClasses.arrow} { + bottom: 0; + margin-bottom: -0.71em; + &::before { + transform-origin: 100% 0; + } + } + &[data-popper-placement*="right"] .${tooltipClasses.arrow} { + ${!ownerState.isRtl + ? css` + left: 0; + margin-left: -0.71em; + ` + : css` + right: 0; + margin-right: -0.71em; + `} + height: 1em; + width: 0.71em; + &::before { + transform-origin: 100% 100%; + } + } + &[data-popper-placement*="left"] .${tooltipClasses.arrow} { + ${!ownerState.isRtl + ? css` + right: 0; + margin-right: -0.71em; + ` + : css` + left: 0; + margin-left: -0.71em; + `} + height: 1em; + width: 0.71em; + &::before { + transform-origin: 0 0; + } + } + `}; + `, +); + +export const Tooltip = (inProps: TooltipProps): JSX.Element => { + const { trigger = "hover", variant = "dark", children, sx, ...props } = useThemeProps({ props: inProps, name: "CometAdminTooltip" }); -const Tooltip = ({ trigger = "hover", variant = "dark", children, classes, ...props }: TooltipProps & WithStyles): JSX.Element => { const [open, setOpen] = React.useState(false); const handleTooltipClose = () => { @@ -64,36 +172,40 @@ const Tooltip = ({ trigger = "hover", variant = "dark", children, classes, ...pr setOpen(!open); }; + const commonTooltipProps = { + sx, + variant, + slots: { + popper: TooltipPopper, + }, + slotProps: { + popper: { + variant, + }, + }, + ...props, + }; + return trigger === "click" ? ( - {cloneElement(children, { onClick: toggleTooltip })} - + ) : ( - + {children} - + ); }; -const TooltipWithStyles = withStyles(styles, { name: "CometAdminTooltip" })(Tooltip); - -export { TooltipWithStyles as Tooltip }; - declare module "@mui/material/styles" { interface ComponentsPropsList { CometAdminTooltip: Partial;