Skip to content

Commit

Permalink
feat(components): add Tooltip
Browse files Browse the repository at this point in the history
  • Loading branch information
maximeperrault authored and louptheron committed Jan 28, 2025
1 parent 27b7f6c commit bfa2597
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 0 deletions.
85 changes: 85 additions & 0 deletions src/components/Tooltip/Tooltip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { useNewWindow } from '@hooks/useNewWindow'
import { THEME } from '@theme'
import { useId, useRef, useState, type FunctionComponent, type ReactNode } from 'react'
import { createPortal } from 'react-dom'
import styled from 'styled-components'

import { Icon as IconUi } from '../..'

import type { IconProps } from '@types_/definitions'

type TooltipProps = {
Icon?: FunctionComponent<IconProps>
children: ReactNode
className?: string
color?: string
isSideWindow?: boolean
}

/**
*
* @param isSideWindow set it to `true` when used on SideWindow
*/
export function Tooltip({
children,
className,
color = THEME.color.slateGray,
Icon = IconUi.Info,
isSideWindow = false
}: TooltipProps) {
// eslint-disable-next-line no-null/no-null
const ref = useRef<HTMLDivElement>(null)
const refLeftPosition = ref.current?.getBoundingClientRect().left ?? 0
const refTopPosition = ref.current?.getBoundingClientRect().top ?? 0

const [isVisible, setIsVisible] = useState<boolean>(false)
const id = useId()

const { newWindowContainerRef } = useNewWindow()

return (
<>
<Wrapper ref={ref} className={className}>
<Icon
aria-describedby={id}
color={color}
onBlur={() => setIsVisible(false)}
onFocus={() => setIsVisible(true)}
onMouseLeave={() => setIsVisible(false)}
onMouseOver={() => setIsVisible(true)}
style={{ cursor: 'pointer' }}
tabIndex={0}
/>
</Wrapper>

{isVisible &&
createPortal(
<StyledTooltip $left={refLeftPosition} $top={refTopPosition} id={id} role="tooltip">
{children}
</StyledTooltip>,
isSideWindow ? newWindowContainerRef.current : (document.body as HTMLElement)
)}
</>
)
}

const StyledTooltip = styled.p<{ $left: number; $top: number }>`
background: ${p => p.theme.color.cultured};
border: ${p => p.theme.color.lightGray} 1px solid;
box-shadow: 0px 3px 6px ${p => p.theme.color.slateGray};
font-size: 11px;
font-weight: normal;
padding: 4px 8px;
position: fixed;
left: calc(${p => p.$left}px + 24px);
top: ${p => p.$top}px;
max-width: 310px;
pointer-events: none;
z-index: 2;
`

const Wrapper = styled.div`
> span:hover {
color: ${p => p.theme.color.blueYonder};
}
`
35 changes: 35 additions & 0 deletions stories/components/Tooltip.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// TODO Migrate this story to the new Storybook structure. Example: stories/components/Banner.stories.tsx.
/* eslint-disable react-hooks/rules-of-hooks */

import { Icon } from '@constants'
import { THEME } from '@theme'
import { Tooltip } from 'components/Tooltip/Tooltip'

import { generateStoryDecorator } from '../../.storybook/utils/generateStoryDecorator'

import type { Meta } from '@storybook/react'

/* eslint-disable sort-keys-fix/sort-keys-fix */
const meta: Meta<{}> = {
title: 'Components/Tooltip',
component: Tooltip,

argTypes: {},

decorators: [generateStoryDecorator()]
}
/* eslint-enable sort-keys-fix/sort-keys-fix */

export default meta

export function _Tooltip() {
return (
<div style={{ height: '500px' }}>
<Tooltip>Hi ! I&apos;m a tooltip</Tooltip>
<Tooltip Icon={Icon.AttentionFilled}>Hi ! I&apos;m a tooltip without another icon</Tooltip>
<Tooltip color={THEME.color.maximumRed} Icon={Icon.Fishery}>
Hi ! I&apos;m a tooltip without another icon and another color
</Tooltip>
</div>
)
}

0 comments on commit bfa2597

Please sign in to comment.