-
Notifications
You must be signed in to change notification settings - Fork 4.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
🪟 🎨 Update Tooltip component to match design library (#14816)
* Add ToolTip storybook * Update ToolTip to scss * gitignore storybook-static dir * Move InfoIcon from ToolTip to icons and remove duplicate * Fix disabled class name in ToolTip * Update ToolTip colors and spacing to match updated design and add light mode * Update ToolTip to show from any side perfectly centered * Add react-tether dependency * Update ToolTip to use react-tether * Add align property to ToolTip * Add link colors to tooltip links and update storybook * Fix font-size in Tooltip * Add minor tweaks to Tooltip component * Remove "help" cursor from state badge Update tooltip corner to 5px Update package-lock with react-tether * Tooltip mode -> Tooltip theme * Add tooltip helper components for learn more url and table * Add tooltip context * Update tooltip stories to demo with new components * REname tooltip index from tsx to ts * Update tooltip to use floating-ui instead of react-tether * Move z-index values to own z-indices file * Update InformationTooltip to use regular tooltip style
- Loading branch information
Showing
22 changed files
with
325 additions
and
98 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
@use "../../scss/colors"; | ||
@use "../../scss/variables"; | ||
@use "../../scss/z-indices"; | ||
|
||
.container { | ||
display: inline; | ||
position: relative; | ||
} | ||
|
||
.tooltip { | ||
font-size: 12px; | ||
line-height: initial; | ||
|
||
padding: variables.$spacing-md; | ||
border-radius: 5px; | ||
max-width: 300px; | ||
z-index: z-indices.$tooltip; | ||
box-shadow: 0px 2px 4px rgba(colors.$dark-blue, 0.12); | ||
background: rgba(colors.$dark-blue, 0.9); | ||
color: colors.$white; | ||
|
||
a { | ||
color: rgba(colors.$white, 0.5); | ||
} | ||
|
||
&.light { | ||
background: rgba(colors.$white, 0.9); | ||
color: colors.$dark-blue; | ||
|
||
a { | ||
color: colors.$blue; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,51 +1,80 @@ | ||
import React from "react"; | ||
import styled from "styled-components"; | ||
|
||
interface ToolTipProps { | ||
control: React.ReactNode; | ||
className?: string; | ||
disabled?: boolean; | ||
cursor?: "pointer" | "help" | "not-allowed"; | ||
} | ||
|
||
const Control = styled.div<{ $cursor?: "pointer" | "help" | "not-allowed"; $showCursor?: boolean }>` | ||
display: inline; | ||
position: relative; | ||
${({ $cursor, $showCursor = true }) => ($showCursor && $cursor ? `cursor: ${$cursor}` : "")}; | ||
`; | ||
|
||
const ToolTipView = styled.div<{ $disabled?: boolean }>` | ||
display: none; | ||
font-size: 14px; | ||
line-height: initial; | ||
position: absolute; | ||
padding: 9px 8px 8px; | ||
box-shadow: 0 24px 38px rgba(53, 53, 66, 0.14), 0 9px 46px rgba(53, 53, 66, 0.12), 0 11px 15px rgba(53, 53, 66, 0.2); | ||
border-radius: 4px; | ||
background: rgba(26, 26, 33, 0.9); | ||
color: ${({ theme }) => theme.whiteColor}; | ||
top: calc(100% + 10px); | ||
left: -50px; | ||
min-width: 100px; | ||
width: max-content; | ||
max-width: 380px; | ||
z-index: 10; | ||
div:hover > &&, | ||
&&:hover { | ||
display: ${({ $disabled }) => ($disabled ? "none" : "block")}; | ||
} | ||
`; | ||
|
||
const ToolTip: React.FC<ToolTipProps> = ({ children, control, className, disabled, cursor }) => { | ||
import { flip, offset, shift, useFloating } from "@floating-ui/react-dom"; | ||
import classNames from "classnames"; | ||
import React, { useState, useEffect } from "react"; | ||
|
||
import { tooltipContext } from "./context"; | ||
import styles from "./ToolTip.module.scss"; | ||
import { ToolTipProps } from "./types"; | ||
|
||
const MOUSE_OUT_TIMEOUT_MS = 50; | ||
|
||
export const ToolTip: React.FC<ToolTipProps> = (props) => { | ||
const { children, control, className, disabled, cursor, theme = "dark", placement = "bottom" } = props; | ||
|
||
const [isMouseOver, setIsMouseOver] = useState(false); | ||
const [isVisible, setIsVisible] = useState(false); | ||
|
||
const { x, y, reference, floating, strategy } = useFloating({ | ||
placement, | ||
middleware: [ | ||
offset(5), // $spacing-sm | ||
flip(), | ||
shift(), | ||
], | ||
}); | ||
|
||
useEffect(() => { | ||
if (isMouseOver) { | ||
setIsVisible(true); | ||
return; | ||
} | ||
|
||
const timeout = window.setTimeout(() => { | ||
setIsVisible(false); | ||
}, MOUSE_OUT_TIMEOUT_MS); | ||
|
||
return () => { | ||
window.clearTimeout(timeout); | ||
}; | ||
}, [isMouseOver]); | ||
|
||
const canShowTooltip = isVisible && !disabled; | ||
|
||
const onMouseOver = () => { | ||
setIsMouseOver(true); | ||
}; | ||
|
||
const onMouseOut = () => { | ||
setIsMouseOver(false); | ||
}; | ||
|
||
return ( | ||
<Control $cursor={cursor} $showCursor={!disabled}> | ||
{control} | ||
<ToolTipView className={className} $disabled={disabled}> | ||
{children} | ||
</ToolTipView> | ||
</Control> | ||
<> | ||
<div | ||
ref={reference} | ||
className={styles.container} | ||
style={disabled ? undefined : { cursor }} | ||
onMouseOver={onMouseOver} | ||
onMouseOut={onMouseOut} | ||
> | ||
{control} | ||
</div> | ||
{canShowTooltip && ( | ||
<div | ||
role="tooltip" | ||
ref={floating} | ||
className={classNames(styles.tooltip, theme === "light" && styles.light, className)} | ||
style={{ | ||
position: strategy, | ||
top: y ?? 0, | ||
left: x ?? 0, | ||
}} | ||
onMouseOver={onMouseOver} | ||
onMouseOut={onMouseOut} | ||
> | ||
<tooltipContext.Provider value={props}>{children}</tooltipContext.Provider> | ||
</div> | ||
)} | ||
</> | ||
); | ||
}; | ||
|
||
export default ToolTip; |
5 changes: 5 additions & 0 deletions
5
airbyte-webapp/src/components/ToolTip/TooltipLearnMoreLink.module.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
@use "../../scss/variables"; | ||
|
||
.container { | ||
margin-top: variables.$spacing-md; | ||
} |
15 changes: 15 additions & 0 deletions
15
airbyte-webapp/src/components/ToolTip/TooltipLearnMoreLink.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { FormattedMessage } from "react-intl"; | ||
|
||
import styles from "./TooltipLearnMoreLink.module.scss"; | ||
|
||
interface TooltipLearnMoreLinkProps { | ||
url: string; | ||
} | ||
|
||
export const TooltipLearnMoreLink: React.VFC<TooltipLearnMoreLinkProps> = ({ url }) => ( | ||
<div className={styles.container}> | ||
<a href={url} target="_blank" rel="noreferrer"> | ||
<FormattedMessage id="ui.learnMore" /> | ||
</a> | ||
</div> | ||
); |
13 changes: 13 additions & 0 deletions
13
airbyte-webapp/src/components/ToolTip/TooltipTable.module.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
@use "../../scss/colors"; | ||
@use "../../scss/variables"; | ||
|
||
.label { | ||
color: rgba(colors.$white, 0.7); | ||
padding-right: variables.$spacing-sm; | ||
} | ||
|
||
.light { | ||
.label { | ||
color: rgba(colors.$dark-blue, 0.7); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { useTooltipContext } from "./context"; | ||
import styles from "./TooltipTable.module.scss"; | ||
|
||
interface TooltipTableProps { | ||
rows: React.ReactNode[][]; | ||
} | ||
|
||
export const TooltipTable: React.VFC<TooltipTableProps> = ({ rows }) => { | ||
const { theme } = useTooltipContext(); | ||
|
||
return rows.length > 0 ? ( | ||
<table className={theme === "light" ? styles.light : undefined}> | ||
<tbody> | ||
{rows?.map((cols) => ( | ||
<tr> | ||
{cols.map((col, index) => ( | ||
<td className={index === 0 ? styles.label : undefined}>{col}</td> | ||
))} | ||
</tr> | ||
))} | ||
</tbody> | ||
</table> | ||
) : null; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { createContext, useContext } from "react"; | ||
|
||
import { TooltipContext } from "./types"; | ||
|
||
export const tooltipContext = createContext<TooltipContext | null>(null); | ||
|
||
export const useTooltipContext = () => { | ||
const ctx = useContext(tooltipContext); | ||
|
||
if (!ctx) { | ||
throw new Error("useTooltipContext should be used within tooltipContext.Provider"); | ||
} | ||
|
||
return ctx; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import { ComponentStory, ComponentMeta } from "@storybook/react"; | ||
|
||
import { ToolTip } from "./ToolTip"; | ||
import { TooltipLearnMoreLink } from "./TooltipLearnMoreLink"; | ||
import { TooltipTable } from "./TooltipTable"; | ||
|
||
export default { | ||
title: "Ui/ToolTip", | ||
component: ToolTip, | ||
argTypes: { | ||
control: { type: { name: "string", required: true } }, | ||
children: { type: { name: "string", required: true } }, | ||
}, | ||
} as ComponentMeta<typeof ToolTip>; | ||
|
||
const Template: ComponentStory<typeof ToolTip> = (args) => ( | ||
<div style={{ display: "flex", height: "100%", alignItems: "center", justifyContent: "center" }}> | ||
<ToolTip {...args} /> | ||
</div> | ||
); | ||
|
||
export const Primary = Template.bind({}); | ||
Primary.args = { | ||
control: "Hover to see Tooltip", | ||
children: ( | ||
<> | ||
Looking for a job?{" "} | ||
<a href="https://www.airbyte.com/careers" target="_blank" rel="noreferrer"> | ||
Apply at Airbyte! | ||
</a> | ||
</> | ||
), | ||
}; | ||
|
||
export const WithLearnMoreUrl = Template.bind({}); | ||
WithLearnMoreUrl.args = { | ||
control: "Hover to see Tooltip with Body", | ||
children: ( | ||
<> | ||
Airbyte is hiring! <TooltipLearnMoreLink url="https://www.airbyte.com/careers" /> | ||
</> | ||
), | ||
}; | ||
|
||
export const WithTable = Template.bind({}); | ||
WithTable.args = { | ||
control: "Hover to see Tooltip with Table", | ||
children: ( | ||
<TooltipTable | ||
rows={[ | ||
["String", "Value"], | ||
["Number", 32768], | ||
["With a longer label", "And here is a longer value"], | ||
]} | ||
/> | ||
), | ||
}; |
Oops, something went wrong.