Merged in chore/sw-3145-move-tooltip (pull request #2514)

chore(SW-3145): Move Tooltip to design-system

* Move Tooltip to design-system


Approved-by: Joakim Jäderberg
This commit is contained in:
Anton Gunnarsson
2025-07-03 13:00:12 +00:00
parent daf765f3d5
commit 0ee6e8800d
6 changed files with 33 additions and 34 deletions

View File

@@ -0,0 +1,75 @@
import { type PropsWithChildren, useState } from 'react'
import { tooltipVariants } from './variants'
import styles from './tooltip.module.css'
import Caption from '../Caption'
type TooltipPosition = 'left' | 'right' | 'top' | 'bottom'
type VerticalArrow = 'top' | 'bottom' | 'center'
type HorizontalArrow = 'left' | 'right' | 'center'
type ValidArrowMap = {
left: VerticalArrow
right: VerticalArrow
top: HorizontalArrow
bottom: HorizontalArrow
}
type ValidArrow<P extends TooltipPosition> = P extends keyof ValidArrowMap
? ValidArrowMap[P]
: never
interface TooltipProps<P extends TooltipPosition = TooltipPosition> {
heading?: string
text?: string
position: P
arrow: ValidArrow<P>
isTouchable?: boolean
}
export function Tooltip<P extends TooltipPosition>({
heading,
text,
position,
arrow,
children,
isTouchable = false,
}: PropsWithChildren<TooltipProps<P>>) {
const className = tooltipVariants({ position, arrow })
const [isActive, setIsActive] = useState(false)
function handleToggle() {
setIsActive((prevState) => !prevState)
}
function handleKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault()
handleToggle()
}
}
return (
<div
className={styles.tooltipContainer}
role="tooltip"
aria-label={text}
tabIndex={0}
onClick={isTouchable ? undefined : handleToggle}
onTouchStart={isTouchable ? handleToggle : undefined}
onKeyDown={handleKeyDown}
data-active={isActive}
>
<div className={className}>
{heading && (
<Caption type="bold" color="white">
{heading}
</Caption>
)}
{text && <Caption color="white">{text}</Caption>}
</div>
{children}
</div>
)
}

View File

@@ -0,0 +1,167 @@
.tooltipContainer {
position: relative;
display: flex;
}
.tooltip {
padding: var(--Spacing-x1);
background-color: var(--Surface-UI-Fill-Intense);
border: 0.5px solid var(--Border-Interactive-Focus);
border-radius: var(--Corner-radius-md);
color: var(--Base-Text-Inverted);
position: absolute;
visibility: hidden;
z-index: 1000;
opacity: 0;
transition: opacity 0.3s;
max-width: 200px;
min-width: 150px;
height: fit-content;
}
.tooltipContainer:hover .tooltip {
visibility: visible;
opacity: 1;
}
.left {
right: 100%;
}
.right {
left: 100%;
}
.top {
bottom: calc(100% + 8px);
}
.bottom {
top: calc(100% + 8px);
}
.bottom.arrowRight {
right: 0;
}
.tooltip::before {
content: '';
position: absolute;
border-style: solid;
}
.bottom.arrowLeft::before {
top: -8px;
left: 16px;
border-width: 0 7px 8px 7px;
border-color: transparent transparent var(--Border-Interactive-Focus)
transparent;
}
.bottom.arrowCenter::before {
top: -8px;
left: 50%;
transform: translateX(-50%);
border-width: 0 7px 8px 7px;
border-color: transparent transparent var(--Border-Interactive-Focus)
transparent;
}
.bottom.arrowRight::before {
top: -8px;
right: 16px;
border-width: 0 7px 8px 7px;
border-color: transparent transparent var(--Border-Interactive-Focus)
transparent;
}
.top.arrowLeft::before {
bottom: -8px;
left: 16px;
border-width: 8px 7px 0 7px;
border-color: var(--Border-Interactive-Focus) transparent transparent
transparent;
}
.top.arrowCenter::before {
bottom: -8px;
left: 50%;
transform: translateX(-50%);
border-width: 8px 7px 0 7px;
border-color: var(--Border-Interactive-Focus) transparent transparent
transparent;
}
.top.arrowRight::before {
bottom: -8px;
right: 16px;
border-width: 8px 7px 0 7px;
border-color: var(--Border-Interactive-Focus) transparent transparent
transparent;
}
.left.arrowTop::before {
top: 16px;
right: -8px;
transform: translateY(-50%);
border-width: 7px 0 7px 8px;
border-color: transparent transparent transparent
var(--Border-Interactive-Focus);
}
.left.arrowCenter::before {
top: 50%;
right: -8px;
transform: translateY(-50%);
border-width: 7px 0 7px 8px;
border-color: transparent transparent transparent
var(--Border-Interactive-Focus);
}
.left.arrowBottom::before {
bottom: 16px;
right: -8px;
transform: translateY(50%);
border-width: 7px 0 7px 8px;
border-color: transparent transparent transparent
var(--Border-Interactive-Focus);
}
.right.arrowTop::before {
top: 16px;
left: -8px;
transform: translateY(-50%);
border-width: 7px 8px 7px 0;
border-color: transparent var(--Border-Interactive-Focus) transparent
transparent;
}
.right.arrowCenter::before {
top: 50%;
left: -8px;
transform: translateY(-50%);
border-width: 7px 8px 7px 0;
border-color: transparent var(--Border-Interactive-Focus) transparent
transparent;
}
.right.arrowBottom::before {
bottom: 16px;
left: -8px;
transform: translateY(50%);
border-width: 7px 8px 7px 0;
border-color: transparent var(--Border-Interactive-Focus) transparent
transparent;
}
@media screen and (max-width: 767px) {
.tooltipContainer[data-active='true'] .tooltip {
visibility: visible;
opacity: 1;
}
.tooltipContainer[data-active='false'] .tooltip {
visibility: hidden;
opacity: 0;
}
}

View File

@@ -0,0 +1,21 @@
import { cva } from 'class-variance-authority'
import styles from './tooltip.module.css'
export const tooltipVariants = cva(styles.tooltip, {
variants: {
position: {
left: styles.left,
right: styles.right,
top: styles.top,
bottom: styles.bottom,
},
arrow: {
left: styles.arrowLeft,
right: styles.arrowRight,
center: styles.arrowCenter,
top: styles.arrowTop,
bottom: styles.arrowBottom,
},
},
})