-
Notifications
You must be signed in to change notification settings - Fork 4
feat: migrate Tooltip component #801
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 9 commits
d4bb6a4
81680a5
38ec1f3
a221f58
67aafd3
8a432a2
4fd8724
7a621fb
686fe82
7222ca4
12307fb
379c375
07b0d00
7aa5bab
966f690
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
/* stylelint-disable selector-pseudo-class-no-unknown */ | ||
|
||
.tooltip { | ||
@apply border border-solid shadow-2 rounded-lg; | ||
@apply motion-reduce:transition-none; | ||
} | ||
|
||
/* Enables opacity fade animation */ | ||
.tooltip[data-state='hidden'] { | ||
@apply opacity-0; | ||
} | ||
|
||
.tooltip :global(.tippy-content) { | ||
@apply px-4 py-2; | ||
} | ||
|
||
/* Color Variants */ | ||
.variantLight { | ||
@apply border-neutral-300 text-neutral-700 bg-neutral-100; | ||
--arrow-color: var(--eds-color-neutral-300); | ||
} | ||
|
||
.variantDark { | ||
@apply border-neutral-700 text-white bg-neutral-700; | ||
--arrow-color: var(--eds-color-neutral-700); | ||
} | ||
|
||
/* Add Arrows */ | ||
.tooltip :global(.tippy-arrow) { | ||
@apply absolute w-4 h-4; | ||
} | ||
|
||
.tooltip :global(.tippy-arrow::before) { | ||
@apply absolute border-solid border-transparent border-8; | ||
|
||
content: ''; | ||
} | ||
|
||
.tooltip[data-placement^='top'] :global(.tippy-arrow) { | ||
@apply left-0; | ||
|
||
transform: translate3d(73px, 0, 0); | ||
} | ||
|
||
.tooltip[data-placement^='bottom'] :global(.tippy-arrow) { | ||
@apply top-0 left-0; | ||
|
||
transform: translate3d(73px, 0, 0); | ||
} | ||
|
||
.tooltip[data-placement^='left'] :global(.tippy-arrow) { | ||
@apply top-0 right-0; | ||
|
||
transform: translate3d(0, 19px, 0); | ||
} | ||
|
||
.tooltip[data-placement^='right'] :global(.tippy-arrow) { | ||
@apply top-0 left-0; | ||
|
||
transform: translate3d(0, 25px, 0); | ||
} | ||
|
||
.tooltip[data-placement^='top'] :global(.tippy-arrow::before) { | ||
@apply left-0; | ||
|
||
border-top-color: var(--arrow-color); | ||
border-bottom-width: 0; | ||
} | ||
|
||
.tooltip[data-placement^='bottom'] :global(.tippy-arrow::before) { | ||
@apply left-0; | ||
|
||
border-bottom-color: var(--arrow-color); | ||
border-top-width: 0; | ||
top: -7px; | ||
} | ||
|
||
.tooltip[data-placement^='left'] :global(.tippy-arrow::before) { | ||
border-left-color: var(--arrow-color); | ||
border-right-width: 0; | ||
right: -7px; | ||
} | ||
|
||
.tooltip[data-placement^='right'] :global(.tippy-arrow::before) { | ||
border-right-color: var(--arrow-color); | ||
border-left-width: 0; | ||
left: -7px; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { generateSnapshots } from "@chanzuckerberg/story-utils"; | ||
import * as TooltipStoryFile from "./Tooltip.stories"; | ||
|
||
describe("<Tooltip />", () => { | ||
generateSnapshots(TooltipStoryFile, { | ||
getElement: (wrapper) => { | ||
return wrapper.baseElement; | ||
}, | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
import type { Meta, StoryObj } from "@storybook/react"; | ||
import * as React from "react"; | ||
import Button from "../Button"; | ||
import Tooltip from "./Tooltip"; | ||
|
||
const defaultArgs = { | ||
content: ( | ||
<span> | ||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.{" "} | ||
<b>Donec a erat eu augue consequat eleifend non vel sem.</b> Praesent | ||
efficitur mauris ac leo semper accumsan. | ||
</span> | ||
), | ||
children: <Button className="mx-32 my-32">Tooltip trigger</Button>, | ||
placement: "right", | ||
visible: true, | ||
}; | ||
|
||
export default { | ||
title: "Tooltip", | ||
component: Tooltip, | ||
args: defaultArgs, | ||
argTypes: { | ||
variant: { | ||
control: { | ||
type: "select", | ||
options: ["light", "dark"], | ||
}, | ||
}, | ||
placement: { | ||
control: { | ||
type: "radio", | ||
options: ["left", "right", "top", "bottom"], | ||
}, | ||
}, | ||
}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you don't need these in this repo. Try taking them out and see if you still see the controls on the first story on the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ohh you are right, removed |
||
} as Meta<Args>; | ||
|
||
type Args = React.ComponentProps<typeof Tooltip>; | ||
|
||
export const LightVariant: StoryObj<Args> = {}; | ||
|
||
export const DarkVariant: StoryObj<Args> = { | ||
...LightVariant, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What does using the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not much, oops, removed from all the stories |
||
args: { | ||
variant: "dark", | ||
}, | ||
}; | ||
|
||
export const LeftPlacement: StoryObj<Args> = { | ||
...LightVariant, | ||
args: { | ||
placement: "left", | ||
children: <Button className="ml-96 my-32">Tooltip trigger</Button>, | ||
}, | ||
}; | ||
|
||
export const TopPlacement: StoryObj<Args> = { | ||
...LightVariant, | ||
args: { | ||
placement: "top", | ||
children: <Button className="mt-32 ml-32">Tooltip trigger</Button>, | ||
}, | ||
}; | ||
|
||
export const BottomPlacement: StoryObj<Args> = { | ||
...LightVariant, | ||
args: { | ||
placement: "bottom", | ||
children: <Button className="mb-32 ml-32">Tooltip trigger</Button>, | ||
}, | ||
}; | ||
|
||
export const LongText: StoryObj<Args> = { | ||
...LightVariant, | ||
args: { | ||
content: ( | ||
<span> | ||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.{" "} | ||
<b>Donec a erat eu augue consequat eleifend non vel sem.</b> Praesent | ||
efficitur mauris ac leo semper accumsan. Donec posuere semper fermentum. | ||
Vivamus venenatis laoreet venenatis. Sed consectetur, dolor sed | ||
tristique vehicula, sapien nulla convallis odio, et tempus urna mi eu | ||
leo. Phasellus a venenatis sapien. Cras massa lectus, sollicitudin id | ||
nulla id, laoreet facilisis est. | ||
</span> | ||
), | ||
}, | ||
}; | ||
|
||
export const LongButtonText: StoryObj<Args> = { | ||
...LightVariant, | ||
args: { | ||
children: ( | ||
<Button className="my-20"> | ||
Tooltip trigger with longer text to test placement | ||
</Button> | ||
), | ||
}, | ||
}; | ||
|
||
export const Interactive = { | ||
...LightVariant, | ||
args: { | ||
visible: undefined, | ||
}, | ||
parameters: { | ||
chromatic: { | ||
disableSnapshot: true, | ||
}, | ||
}, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import Tippy from "@tippyjs/react"; | ||
import clsx from "clsx"; | ||
import * as React from "react"; | ||
import styles from "./Tooltip.module.css"; | ||
|
||
// Full list of Tippy props: https://atomiks.github.io/tippyjs/v6/all-props/ | ||
type TooltipProps = { | ||
/** | ||
* The trigger element the tooltip appears next to. | ||
*/ | ||
children?: React.ReactElement; | ||
/** | ||
* The trigger element the tooltip appears next to. | ||
* | ||
* Use this instead of `children` if the trigger element is being | ||
* stored in state or a ref. Most cases will use `children` and not | ||
* `reference`. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice prop doc! Did the idea/reference/wording for storing an element in For my own knowledge, I'm curious as to the intended use case. Ref makes sense, but storing an element in state isn't usually necessary (and according to my own mental pattern matching would still work with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Think @dierat deserves all the praise for the work, I'm just migrating to EDS haha. Any thought's on Andrew's question? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't remember exactly where "state" came from here; I'm fine with dropping it. This is where we're currently using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes sense. I say lets drop reference to |
||
*/ | ||
reference?: React.RefObject<Element> | Element; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was |
||
/** | ||
* The content of the tooltip bubble. | ||
*/ | ||
content?: React.ReactNode; | ||
/** | ||
* Where the tooltip should be placed in relation to the element it's attached to. | ||
* | ||
* Tippy also supports 'top-start', 'top-end', 'right-start', 'right-end', etc, | ||
* but our CSS currently only supports the 4 main sides. | ||
*/ | ||
placement?: "top" | "right" | "bottom" | "left"; | ||
/** | ||
* Whether the tooltip is always visible or always invisible. | ||
* | ||
* This is most often left undefined so the Tooltip component | ||
* controls if/when the bubble appears (on hover, click, focus, etc). | ||
*/ | ||
visible?: boolean; | ||
/** | ||
* Custom classname for additional styles. | ||
* | ||
* These styles will only affect the tooltip bubble. | ||
*/ | ||
className?: string; | ||
/** | ||
* Whether the tooltip has a light or dark background. | ||
*/ | ||
variant?: "light" | "dark"; | ||
/** | ||
* How long to delay the Tooltip showing and hiding, in milliseconds. | ||
* | ||
* If a single number is provided, it will be applied to showing and hiding. | ||
* If an array with 2 numbers is provided, the first will apply to showing and | ||
* the second will be applied to hiding. | ||
* https://atomiks.github.io/tippyjs/v6/all-props/#delay | ||
*/ | ||
delay?: number | [number | null, number | null]; | ||
}; | ||
|
||
/** | ||
* A styled tooltip built on Tippy.js. | ||
* | ||
* https://atomiks.github.io/tippyjs/ | ||
* https://github.com/atomiks/tippyjs-react | ||
*/ | ||
export default function Tooltip({ | ||
variant = "light", | ||
placement = "top", | ||
className, | ||
...rest | ||
}: TooltipProps) { | ||
return ( | ||
<Tippy | ||
{...rest} | ||
className={clsx( | ||
styles.tooltip, | ||
className, | ||
variant === "light" && styles.variantLight, | ||
variant === "dark" && styles.variantDark, | ||
)} | ||
duration={200} | ||
placement={placement} | ||
/> | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better way via stylelintrc? Or would this be the only file with
:global
use?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is fine. 🤷
My guess is that we'll eventually end up doing it again as we start building on top of other components that are not from Headless UI and require us to dig in and target a specific element that's not exposed to us. But I don't have any specific examples; it's just a feeling.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good, I think we can add to stylelint when we have to get across that bridge