From 247ba404031f48d55a5650549d927452af49a60f Mon Sep 17 00:00:00 2001 From: Jamie Henson Date: Tue, 19 Nov 2024 15:49:58 +0000 Subject: [PATCH] feat: port Tooltip to use Radix primitive --- package.json | 1 + .../__snapshots__/Table.stories.tsx.snap | 375 +++++++++--------- src/core/Tooltip.tsx | 203 ++-------- src/core/Tooltip/Tooltip.stories.tsx | 51 +-- .../__snapshots__/Tooltip.stories.tsx.snap | 102 +---- tailwind.config.js | 6 +- yarn.lock | 120 ++++++ 7 files changed, 367 insertions(+), 491 deletions(-) diff --git a/package.json b/package.json index a01434443..74c9b781f 100644 --- a/package.json +++ b/package.json @@ -77,6 +77,7 @@ }, "dependencies": { "@radix-ui/react-accordion": "^1.2.1", + "@radix-ui/react-tooltip": "^1.1.4", "addsearch-js-client": "^0.8.11", "array-flat-polyfill": "^1.0.1", "clsx": "^2.1.1", diff --git a/src/core/Table/__snapshots__/Table.stories.tsx.snap b/src/core/Table/__snapshots__/Table.stories.tsx.snap index 55b1b6366..d8482102b 100644 --- a/src/core/Table/__snapshots__/Table.stories.tsx.snap +++ b/src/core/Table/__snapshots__/Table.stories.tsx.snap @@ -63,19 +63,18 @@ exports[`JS Components/Table PricingPage smoke-test 1`] = ` > Label 1 -
- -
+ + + - - + + + - - + + + - - + + + - - + + + - - + + + - - + + + - - + + + - - + + + - - + + + - - + + + - - + + + - - + + + - - + + + - - + + + ) => { - const [open, setOpen] = useState(false); - const [fadeOut, setFadeOut] = useState(false); - const [position, setPosition] = useState({ x: 0, y: 0, orientation: "top" }); - const offset = 8; - const reference = useRef(null); - const floating = useRef(null); - const fadeOutTimeoutRef = useRef(null); - - useEffect(() => { - if (open) { - const floatingRect = floating.current?.getBoundingClientRect(); - const referenceRect = reference.current?.getBoundingClientRect(); - const viewportWidth = window.innerWidth; - const viewportHeight = window.innerHeight; - let orientation = "top"; - - if (floatingRect && referenceRect) { - let x = - referenceRect.left + - referenceRect.width / 2 - - floatingRect.width / 2 + - window.scrollX; - let y = - referenceRect.top - floatingRect.height - offset + window.scrollY; - - // Adjust if tooltip goes off the right edge - if (x + floatingRect.width > viewportWidth + window.scrollX) { - x = viewportWidth + window.scrollX - floatingRect.width - offset; - orientation = "left"; - } - - // Adjust if tooltip goes off the left edge - if (x < window.scrollX) { - x = window.scrollX + offset; - orientation = "right"; - } - - // Adjust if tooltip goes off the top edge - if (y < window.scrollY) { - y = referenceRect.bottom + offset + window.scrollY; - orientation = "bottom"; - } - - // Adjust if tooltip goes off the bottom edge - if (y + floatingRect.height > viewportHeight + window.scrollY) { - y = referenceRect.top - floatingRect.height - offset + window.scrollY; - } - - setPosition({ x, y, orientation }); - } - } else { - setPosition({ x: 0, y: 0, orientation: "top" }); - } - - return () => { - if (fadeOutTimeoutRef.current !== null) { - clearTimeout(fadeOutTimeoutRef.current); - } - }; - }, [open]); - - const initiateFadeOut = () => { - setFadeOut(true); - fadeOutTimeoutRef.current = setTimeout(() => { - setOpen(false); - setFadeOut(false); - }, 250); - }; - - const cursorTowardsTooltip = ( - event: MouseEvent, - ref: RefObject, - ) => { - if (!ref.current) { - return false; - } - - const { clientX, clientY } = event; - const { x, y, width, height } = ref.current.getBoundingClientRect(); - const { orientation } = position; - - switch (orientation) { - case "top": - return clientX >= x && clientX <= x + width && clientY < y; - case "left": - return clientY >= y && clientY <= y + height && clientX < x; - case "right": - return clientY >= y && clientY <= y + height && clientX > x + width; - case "bottom": - return clientX >= x && clientX <= x + width && clientY > y + height; - default: - return false; - } - }; - - const fadeOutIfNotWithinTrigger = (event: MouseEvent) => { - if (!reference.current) return; - - const { clientX, clientY } = event; - const { x, y, width, height } = reference.current.getBoundingClientRect(); - const withinBounds = - clientX >= x && - clientX <= x + width && - clientY >= y && - clientY <= y + height; - - if (!withinBounds) { - initiateFadeOut(); - } - }; - return ( -
- - - {open - ? createPortal( + + + +
+ +
+
+ +
- setTimeout(() => fadeOutIfNotWithinTrigger(event), 250) - } - style={{ - top: position.y, - left: position.x, - zIndex: 1000, - boxShadow: "4px 4px 15px rgba(0, 0, 0, 0.2)", - }} - {...tooltipProps} - className={`bg-neutral-1000 dark:bg-neutral-300 text-neutral-200 dark:text-neutral-1000 ui-text-p3 font-medium p-16 ${interactive ? "" : "pointer-events-none"} rounded-lg absolute ${ - tooltipProps?.className ?? "" - } ${fadeOut ? "animate-[tooltipExit_0.25s_ease-in-out]" : "animate-[tooltipEntry_0.25s_ease-in-out]"}`} + className={clsx( + "max-w-[240px] w-auto bg-neutral-1000 dark:bg-neutral-300 text-neutral-200 dark:text-neutral-1000 ui-text-p3 font-medium p-16 rounded-lg absolute", + { "pointer-events-none": !interactive }, + tooltipProps?.className, + )} > -
{children}
-
, - document.body, - ) - : null} -
+ {children} + + + + + ); }; diff --git a/src/core/Tooltip/Tooltip.stories.tsx b/src/core/Tooltip/Tooltip.stories.tsx index 86a64d8dd..974aebefd 100644 --- a/src/core/Tooltip/Tooltip.stories.tsx +++ b/src/core/Tooltip/Tooltip.stories.tsx @@ -13,57 +13,30 @@ export default { export const Central = { render: (args) => ( -
-
dark
-
light
-
- {args.children} -
-
- {args.children} -
+
+ {args.children}
), }; export const LeftBound = { render: (args) => ( -
-
dark
-
light
-
- {args.children} -
-
- {args.children} -
+
+ {args.children}
), }; export const Interactive = { render: () => ( -
-
dark
-
light
-
- - Here's some super stuff with a{" "} - - super interactive link - {" "} - in it - -
-
- - Here's some super stuff with a{" "} - - super interactive link - {" "} - in it - -
+
+ + Here's some super stuff with a{" "} + + super interactive link + {" "} + in it +
), parameters: { diff --git a/src/core/Tooltip/__snapshots__/Tooltip.stories.tsx.snap b/src/core/Tooltip/__snapshots__/Tooltip.stories.tsx.snap index 6c15e7f08..d5fd26aa8 100644 --- a/src/core/Tooltip/__snapshots__/Tooltip.stories.tsx.snap +++ b/src/core/Tooltip/__snapshots__/Tooltip.stories.tsx.snap @@ -1,19 +1,10 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`JS Components/Tooltip Central smoke-test 1`] = ` -
-
- dark -
-
- light -
-
+
+
-
-
-
- -
-
+
`; exports[`JS Components/Tooltip Interactive smoke-test 1`] = ` -
-
- dark -
-
- light -
-
+
+ -
-
-
-
-
-
+
`; exports[`JS Components/Tooltip LeftBound smoke-test 1`] = ` -
-
- dark -
-
- light -
-
+
+ -
-
-
-
-
-
+
`; diff --git a/tailwind.config.js b/tailwind.config.js index 8a662989d..b4868a220 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -309,11 +309,11 @@ module.exports = { 8: "8", }, keyframes: { - tooltipEntry: { + "tooltip-entry": { "0%": { transform: "translateY(8px)", opacity: 0 }, "100%": { transform: "translateY(0)", opacity: 1 }, }, - tooltipExit: { + "tooltip-exit": { "0%": { opacity: 1 }, "100%": { opacity: 0 }, }, @@ -329,6 +329,8 @@ module.exports = { animation: { "accordion-down": "accordion-down 0.2s ease-out", "accordion-up": "accordion-up 0.2s ease-out", + "tooltip-entry": "tooltip-entry 0.2s cubic-bezier(0.16, 1, 0.3, 1)", + "tooltip-exit": "tooltip-exit 0.2s cubic-bezier(0.16, 1, 0.3, 1)", }, }, listStyleType: { diff --git a/yarn.lock b/yarn.lock index 2b2b53c0c..49e38fa3a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -867,6 +867,33 @@ resolved "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz" integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q== +"@floating-ui/core@^1.6.0": + version "1.6.8" + resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.6.8.tgz#aa43561be075815879305965020f492cdb43da12" + integrity sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA== + dependencies: + "@floating-ui/utils" "^0.2.8" + +"@floating-ui/dom@^1.0.0": + version "1.6.12" + resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.6.12.tgz#6333dcb5a8ead3b2bf82f33d6bc410e95f54e556" + integrity sha512-NP83c0HjokcGVEMeoStg317VD9W7eDlGK7457dMBANbKA6GJZdc7rjujdgqzTaz93jkGgc5P/jeWbaCHnMNc+w== + dependencies: + "@floating-ui/core" "^1.6.0" + "@floating-ui/utils" "^0.2.8" + +"@floating-ui/react-dom@^2.0.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.1.2.tgz#a1349bbf6a0e5cb5ded55d023766f20a4d439a31" + integrity sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A== + dependencies: + "@floating-ui/dom" "^1.0.0" + +"@floating-ui/utils@^0.2.8": + version "0.2.8" + resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.8.tgz#21a907684723bbbaa5f0974cf7730bd797eb8e62" + integrity sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig== + "@hapi/hoek@^9.0.0", "@hapi/hoek@^9.3.0": version "9.3.0" resolved "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz" @@ -1324,6 +1351,13 @@ "@radix-ui/react-primitive" "2.0.0" "@radix-ui/react-use-controllable-state" "1.1.0" +"@radix-ui/react-arrow@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz#744f388182d360b86285217e43b6c63633f39e7a" + integrity sha512-FmlW1rCg7hBpEBwFbjHwCW6AmWLQM6g/v0Sn8XbP9NvmSZ2San1FpQeyPtufzOMSIx7Y4dzjlHoifhp+7NkZhw== + dependencies: + "@radix-ui/react-primitive" "2.0.0" + "@radix-ui/react-collapsible@1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@radix-ui/react-collapsible/-/react-collapsible-1.1.1.tgz#1382cc9ec48f8b473c14f3779d317f0cdf6da5e9" @@ -1368,6 +1402,17 @@ resolved "https://registry.yarnpkg.com/@radix-ui/react-direction/-/react-direction-1.1.0.tgz#a7d39855f4d077adc2a1922f9c353c5977a09cdc" integrity sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg== +"@radix-ui/react-dismissable-layer@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.1.tgz#cbdcb739c5403382bdde5f9243042ba643883396" + integrity sha512-QSxg29lfr/xcev6kSz7MAlmDnzbP1eI/Dwn3Tp1ip0KT5CUELsxkekFEMVBEoykI3oV39hKT4TKZzBNMbcTZYQ== + dependencies: + "@radix-ui/primitive" "1.1.0" + "@radix-ui/react-compose-refs" "1.1.0" + "@radix-ui/react-primitive" "2.0.0" + "@radix-ui/react-use-callback-ref" "1.1.0" + "@radix-ui/react-use-escape-keydown" "1.1.0" + "@radix-ui/react-id@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-1.1.0.tgz#de47339656594ad722eb87f94a6b25f9cffae0ed" @@ -1375,6 +1420,30 @@ dependencies: "@radix-ui/react-use-layout-effect" "1.1.0" +"@radix-ui/react-popper@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-1.2.0.tgz#a3e500193d144fe2d8f5d5e60e393d64111f2a7a" + integrity sha512-ZnRMshKF43aBxVWPWvbj21+7TQCvhuULWJ4gNIKYpRlQt5xGRhLx66tMp8pya2UkGHTSlhpXwmjqltDYHhw7Vg== + dependencies: + "@floating-ui/react-dom" "^2.0.0" + "@radix-ui/react-arrow" "1.1.0" + "@radix-ui/react-compose-refs" "1.1.0" + "@radix-ui/react-context" "1.1.0" + "@radix-ui/react-primitive" "2.0.0" + "@radix-ui/react-use-callback-ref" "1.1.0" + "@radix-ui/react-use-layout-effect" "1.1.0" + "@radix-ui/react-use-rect" "1.1.0" + "@radix-ui/react-use-size" "1.1.0" + "@radix-ui/rect" "1.1.0" + +"@radix-ui/react-portal@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.1.2.tgz#51eb46dae7505074b306ebcb985bf65cc547d74e" + integrity sha512-WeDYLGPxJb/5EGBoedyJbT0MpoULmwnIPMJMSldkuiMsBAv7N1cRdsTWZWht9vpPOiN3qyiGAtbK2is47/uMFg== + dependencies: + "@radix-ui/react-primitive" "2.0.0" + "@radix-ui/react-use-layout-effect" "1.1.0" + "@radix-ui/react-presence@1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.1.1.tgz#98aba423dba5e0c687a782c0669dcd99de17f9b1" @@ -1397,6 +1466,24 @@ dependencies: "@radix-ui/react-compose-refs" "1.1.0" +"@radix-ui/react-tooltip@^1.1.4": + version "1.1.4" + resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-1.1.4.tgz#152d8485859b80d395d6b3229f676fef3cec56b3" + integrity sha512-QpObUH/ZlpaO4YgHSaYzrLO2VuO+ZBFFgGzjMUPwtiYnAzzNNDPJeEGRrT7qNOrWm/Jr08M1vlp+vTHtnSQ0Uw== + dependencies: + "@radix-ui/primitive" "1.1.0" + "@radix-ui/react-compose-refs" "1.1.0" + "@radix-ui/react-context" "1.1.1" + "@radix-ui/react-dismissable-layer" "1.1.1" + "@radix-ui/react-id" "1.1.0" + "@radix-ui/react-popper" "1.2.0" + "@radix-ui/react-portal" "1.1.2" + "@radix-ui/react-presence" "1.1.1" + "@radix-ui/react-primitive" "2.0.0" + "@radix-ui/react-slot" "1.1.0" + "@radix-ui/react-use-controllable-state" "1.1.0" + "@radix-ui/react-visually-hidden" "1.1.0" + "@radix-ui/react-use-callback-ref@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz#bce938ca413675bc937944b0d01ef6f4a6dc5bf1" @@ -1409,11 +1496,44 @@ dependencies: "@radix-ui/react-use-callback-ref" "1.1.0" +"@radix-ui/react-use-escape-keydown@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz#31a5b87c3b726504b74e05dac1edce7437b98754" + integrity sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw== + dependencies: + "@radix-ui/react-use-callback-ref" "1.1.0" + "@radix-ui/react-use-layout-effect@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz#3c2c8ce04827b26a39e442ff4888d9212268bd27" integrity sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w== +"@radix-ui/react-use-rect@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz#13b25b913bd3e3987cc9b073a1a164bb1cf47b88" + integrity sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ== + dependencies: + "@radix-ui/rect" "1.1.0" + +"@radix-ui/react-use-size@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz#b4dba7fbd3882ee09e8d2a44a3eed3a7e555246b" + integrity sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw== + dependencies: + "@radix-ui/react-use-layout-effect" "1.1.0" + +"@radix-ui/react-visually-hidden@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.0.tgz#ad47a8572580f7034b3807c8e6740cd41038a5a2" + integrity sha512-N8MDZqtgCgG5S3aV60INAB475osJousYpZ4cTJ2cFbMpdHS5Y6loLTH8LPtkj2QN0x93J30HT/M3qJXM0+lyeQ== + dependencies: + "@radix-ui/react-primitive" "2.0.0" + +"@radix-ui/rect@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/rect/-/rect-1.1.0.tgz#f817d1d3265ac5415dadc67edab30ae196696438" + integrity sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg== + "@resvg/resvg-js-android-arm-eabi@2.6.2": version "2.6.2" resolved "https://registry.yarnpkg.com/@resvg/resvg-js-android-arm-eabi/-/resvg-js-android-arm-eabi-2.6.2.tgz#e761e0b688127db64879f455178c92468a9aeabe"