From 2be3906fa73d4670ea0d4a07faca7877fe681273 Mon Sep 17 00:00:00 2001 From: dzucconi Date: Thu, 10 Jun 2021 15:38:48 -0400 Subject: [PATCH] refactor(tooltip): updates tooltip to use positioning engine hook --- .../src/elements/Tooltip/Tooltip.story.tsx | 47 ++++++++++- .../palette/src/elements/Tooltip/Tooltip.tsx | 84 ++++--------------- 2 files changed, 61 insertions(+), 70 deletions(-) diff --git a/packages/palette/src/elements/Tooltip/Tooltip.story.tsx b/packages/palette/src/elements/Tooltip/Tooltip.story.tsx index 7d87c789e..02f8807ad 100644 --- a/packages/palette/src/elements/Tooltip/Tooltip.story.tsx +++ b/packages/palette/src/elements/Tooltip/Tooltip.story.tsx @@ -1,6 +1,7 @@ import React from "react" import { States } from "storybook-states" import { HelpIcon } from "../../svgs" +import { Position, POSITION } from "../../utils/usePosition" import { Text } from "../Text" import { Tooltip, TooltipProps } from "./Tooltip" @@ -15,14 +16,19 @@ export const Default = () => { return ( > states={[ - { placement: "top" }, - { placement: "bottom" }, - { placement: "top", width: 600 }, + { placement: "top-start" }, + { placement: "bottom", width: 600 }, { placement: "bottom", visible: true }, ]} > - + This text has a tooltip @@ -30,6 +36,39 @@ export const Default = () => { ) } +export const Placement = () => { + return ( + > + states={Object.keys(POSITION).map((placement) => ({ + placement: placement as Position, + }))} + > + {(props) => { + return ( + + + {JSON.stringify(props)} + + + ) + }} + + ) +} + export const IconExample = () => { return ( diff --git a/packages/palette/src/elements/Tooltip/Tooltip.tsx b/packages/palette/src/elements/Tooltip/Tooltip.tsx index 7ba6d3742..2471e1a3a 100644 --- a/packages/palette/src/elements/Tooltip/Tooltip.tsx +++ b/packages/palette/src/elements/Tooltip/Tooltip.tsx @@ -1,7 +1,8 @@ -import React, { useEffect, useRef, useState } from "react" +import React, { useRef, useState } from "react" import styled from "styled-components" import { DROP_SHADOW } from "../../helpers" import { useThemeConfig } from "../../Theme" +import { Position, usePosition } from "../../utils/usePosition" import { Box, BoxProps } from "../Box" import { Text, TextVariant } from "../Text" @@ -9,7 +10,7 @@ export interface TooltipProps extends BoxProps, React.HTMLAttributes { content: React.ReactNode - placement?: "top" | "bottom" + placement?: Position size?: "sm" | "lg" width?: number visible?: boolean @@ -23,61 +24,14 @@ export const Tooltip: React.FC = ({ content: _content, size = "lg", width = 230, - placement = "top", + placement = "bottom-end", visible, ...rest }) => { - const innerWrapper = useRef(null) + const tooltipRef = useRef(null) + const anchorRef = useRef(null) const [active, setActive] = useState(false) - const [position, setPosition] = useState({ - left: 0, - right: null, - centered: true, - }) - - const computeTipPosition = () => { - if (!innerWrapper.current) return - - let left = 0 - let right = null - let centered = false - - const inner = innerWrapper.current.getBoundingClientRect() - - left = inner.width / 2 - centered = true - const spillOver = width / 2 - left - - if (spillOver > inner.left) { - centered = false - left = 0 - right = null - } - - if (spillOver > window.innerWidth - inner.right) { - centered = false - left = null - right = 0 - } - - return { - centered, - left, - right, - } - } - - useEffect(() => { - const handler = () => setPosition(computeTipPosition()) - - handler() - window.addEventListener("resize", handler) - - return () => { - window.removeEventListener("resize", handler) - } - }, []) const handleClick = () => { setActive((prevActive) => !prevActive) @@ -98,6 +52,14 @@ export const Tooltip: React.FC = ({ v3: { variant: "xs" as TextVariant }, }) + usePosition({ + anchorRef, + tooltipRef, + position: placement, + offset: 10, + active: visible ?? active, + }) + return ( = ({ onMouseOut={deactivate} onFocus={activate} onBlur={deactivate} - position="relative" - // TODO: Should avoid making assumptions about display display="inline-block" {...rest} > @@ -115,28 +75,20 @@ export const Tooltip: React.FC = ({ p={size === "sm" ? 0.5 : 2} width={width} bg="white100" + ref={tooltipRef as any} + zIndex={1} {...(visible ? // If there's a visible prop being passed; use that { opacity: visible ? 1 : 0 } : // Otherwise use the active state { opacity: active ? 1 : 0 })} - // Positioning - {...{ - bottom: { top: "100%", mt: 0.5 }, - top: { bottom: "100%", mb: 0.5 }, - }[placement]} - left={position.left ?? "auto"} - right={position.right ?? "auto"} - style={{ - transform: position.centered ? "translateX(-50%)" : "none", - }} > {content} - {children} + {children} ) } @@ -157,6 +109,6 @@ const Tip = styled(Box)` transition: opacity 250ms ease-out; text-align: left; box-shadow: ${DROP_SHADOW}; - pointer-events: none; cursor: default; + pointer-events: none; `