-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(ui/toooltip): add Tooltip component
- Loading branch information
1 parent
ff9bbd4
commit 963d6ff
Showing
7 changed files
with
295 additions
and
47 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import React from 'react' | ||
|
||
import { Tooltip } from '../src/Tooltip' | ||
|
||
import utilStyles from './utils.scss' | ||
|
||
export default { | ||
title: 'Components/Tooltip', | ||
component: Tooltip, | ||
} | ||
|
||
export const Default = () => ( | ||
<div className={utilStyles.wrapperCentered}> | ||
<Tooltip id="tooltip-default" content={'Tooltip content'}> | ||
<button aria-labelledby="tooltip-default">Hover me</button> | ||
</Tooltip> | ||
</div> | ||
) | ||
|
||
Default.parameters = { | ||
design: { | ||
type: 'figma', | ||
url: 'https://www.figma.com/file/kaC3jgqMSgqMEgnv7TIse1/%F0%9F%93%90Sign-in-flow?node-id=118%3A2349', | ||
}, | ||
} | ||
|
||
export const TopPlacement = () => ( | ||
<div className={utilStyles.wrapperCentered}> | ||
<Tooltip id="tooltip-placement-top" placement="top" content={'Tooltip content'}> | ||
<button aria-labelledby="tooltip-placement-top">Hover me</button> | ||
</Tooltip> | ||
</div> | ||
) | ||
|
||
export const RightPlacement = () => ( | ||
<div className={utilStyles.wrapperCentered}> | ||
<Tooltip id="tooltip-placement-right" placement="right" content={'Tooltip content'}> | ||
<button aria-labelledby="tooltip-placement-right">Hover me</button> | ||
</Tooltip> | ||
</div> | ||
) | ||
|
||
export const BottomPlacement = () => ( | ||
<div className={utilStyles.wrapperCentered}> | ||
<Tooltip id="tooltip-placement-bottom" placement="bottom" content={'Tooltip content'}> | ||
<button aria-labelledby="tooltip-placement-bottom">Hover me</button> | ||
</Tooltip> | ||
</div> | ||
) | ||
|
||
export const LeftPlacement = () => ( | ||
<div className={utilStyles.wrapperCentered}> | ||
<Tooltip id="tooltip-placement-left" placement="left" content={'Tooltip content'}> | ||
<button aria-labelledby="tooltip-placement-left">Hover me</button> | ||
</Tooltip> | ||
</div> | ||
) | ||
|
||
export const InteractiveContent = () => ( | ||
<div className={utilStyles.wrapperCentered} style={{ minHeight: '250px' }}> | ||
<Tooltip | ||
id="tooltip-interactive-content" | ||
content={ | ||
<> | ||
<p> | ||
Parley come about mutiny swing the lead to go on account run a shot across the bow schooner fathom bounty carouser. Maroon | ||
killick keel driver scourge of the seven seas Jolly Roger hands spyglass Brethren of the Coast booty. Boom rigging gally Plate | ||
Fleet pink dance the hempen jig bilge water measured fer yer chains take a caulk tender. | ||
</p> | ||
|
||
<button>Aye Captain!</button> | ||
</> | ||
} | ||
> | ||
<button aria-labelledby="tooltip-interactive-content">Hover me</button> | ||
</Tooltip> | ||
</div> | ||
) | ||
|
||
export const Disabled = () => ( | ||
<div className={utilStyles.wrapperCentered}> | ||
<Tooltip id="tooltip-disabled" content={'Tooltip content'} disabled> | ||
<button aria-labelledby="tooltip-disabled">Hover me</button> | ||
</Tooltip> | ||
</div> | ||
) |
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 @@ | ||
.wrapperCentered { | ||
display: flex; | ||
flex-direction: column; | ||
align-items: center; | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,112 @@ | ||
@import '../styles/constants/index'; | ||
|
||
.wrapper { | ||
max-width: $tooltipMaxWidth; | ||
word-wrap: break-word; | ||
} | ||
|
||
.overlay { | ||
// TODO: Update colors. Ask Pawel. | ||
color: #fff; | ||
.container { | ||
display: inline; | ||
|
||
:global .rc-tooltip-inner { | ||
background-color: black; | ||
.reference { | ||
display: inline; | ||
} | ||
|
||
&.rc-tooltip-placement-bottom { | ||
:global .rc-tooltip-arrow { | ||
border-bottom-color: black; | ||
.overlay { | ||
position: relative; | ||
max-width: $tooltipMaxWidth; | ||
padding: $grid * 5; | ||
background: $colorNeutralLighter; | ||
border: $tooltipBorderWidth solid $colorNeutralLight; | ||
border-radius: $borderRadius; | ||
font-size: inherit; | ||
color: $colorText; | ||
|
||
&::before, | ||
&::after { | ||
content: ''; | ||
position: absolute; | ||
} | ||
} | ||
&.rc-tooltip-placement-top { | ||
:global .rc-tooltip-arrow { | ||
border-top-color: black; | ||
|
||
&::after { | ||
z-index: 1; | ||
} | ||
|
||
&[data-popper-placement^='top'] { | ||
&::before, | ||
&::after { | ||
border-left: $tooltipArrowSize solid transparent; | ||
border-right: $tooltipArrowSize solid transparent; | ||
top: 100%; | ||
left: 50%; | ||
margin-left: -$tooltipArrowSize; | ||
} | ||
|
||
&::before { | ||
border-top: $tooltipArrowSize solid $colorNeutralLight; | ||
} | ||
|
||
&::after { | ||
border-top: $tooltipArrowSize solid $colorNeutralLighter; | ||
transform: translateY(-2px); | ||
} | ||
} | ||
|
||
&[data-popper-placement^='right'] { | ||
&::before, | ||
&::after { | ||
border-top: $tooltipArrowSize solid transparent; | ||
border-bottom: $tooltipArrowSize solid transparent; | ||
top: 50%; | ||
left: 0%; | ||
margin-top: -$tooltipArrowSize; | ||
} | ||
|
||
&::before { | ||
border-right: $tooltipArrowSize solid $colorNeutralLight; | ||
margin-left: -$tooltipArrowSize; | ||
} | ||
|
||
&::after { | ||
border-right: $tooltipArrowSize solid $colorNeutralLighter; | ||
transform: translateX(-4px); | ||
} | ||
} | ||
|
||
&[data-popper-placement^='bottom'] { | ||
&::before, | ||
&::after { | ||
border-left: $tooltipArrowSize solid transparent; | ||
border-right: $tooltipArrowSize solid transparent; | ||
top: 0; | ||
left: 50%; | ||
margin-left: -$tooltipArrowSize; | ||
} | ||
|
||
&::before { | ||
border-bottom: $tooltipArrowSize solid $colorNeutralLight; | ||
margin-top: -$tooltipArrowSize; | ||
} | ||
|
||
&::after { | ||
border-bottom: $tooltipArrowSize solid $colorNeutralLighter; | ||
transform: translateY(-4px); | ||
} | ||
} | ||
|
||
&[data-popper-placement^='left'] { | ||
&::before, | ||
&::after { | ||
border-top: $tooltipArrowSize solid transparent; | ||
border-bottom: $tooltipArrowSize solid transparent; | ||
top: 50%; | ||
left: 100%; | ||
margin-top: -$tooltipArrowSize; | ||
} | ||
|
||
&::before { | ||
border-left: $tooltipArrowSize solid $colorNeutralLight; | ||
} | ||
|
||
&::after { | ||
border-left: $tooltipArrowSize solid $colorNeutralLighter; | ||
transform: translateX(-2px); | ||
} | ||
} | ||
} | ||
} |
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,37 +1,78 @@ | ||
import React, { FC, ReactElement } from 'react' | ||
import RCTooltip from 'rc-tooltip' | ||
import { TooltipProps as RCTooltipProps } from 'rc-tooltip/lib/Tooltip' | ||
|
||
import 'rc-tooltip/assets/bootstrap.css' | ||
import React, { FC, ReactNode, useCallback, useState } from 'react' | ||
import { usePopper } from 'react-popper' | ||
|
||
import styles from './Tooltip.module.scss' | ||
|
||
export type TooltipProps = RCTooltipProps & { | ||
wrapMaxWidth?: boolean | ||
wrapElement?: boolean | ||
export type TooltipProps = { | ||
content: ReactNode | ||
id?: string | ||
disabled?: boolean | ||
offset?: number | ||
placement?: 'top' | 'right' | 'bottom' | 'left' | ||
} | ||
|
||
export const Tooltip: FC<TooltipProps & { children: ReactElement }> = ({ | ||
overlay, | ||
wrapMaxWidth = true, | ||
wrapElement = false, | ||
children, | ||
...props | ||
}) => { | ||
return overlay ? ( | ||
<RCTooltip | ||
overlay={wrapMaxWidth ? <div className={styles.wrapper}>{overlay}</div> : overlay} | ||
overlayClassName={styles.overlay} | ||
{...props} | ||
> | ||
{wrapElement ? ( | ||
// TODO: remove once https://github.com/react-component/tooltip/issues/18#issuecomment-411476678 has been resolved | ||
<div style={{ display: 'inline-block' }}>{children}</div> | ||
) : ( | ||
children | ||
/** | ||
* ### Purpose | ||
* Useful when you need to display additional information/actionable content. | ||
* A tooltip with this content is displayed when user hovers over a target element. | ||
* | ||
* ### General info | ||
* - Tooltip has four available placements (top, right, bottom and left). | ||
* - Tooltip automatically detects viewport overflow and changes placement to prevent it. | ||
* - Offset (space between a target and tooltip elements) can be also configured via "offset" property. | ||
* - It's recommended set the "id" parameter which will be assigned to the tooltip. This id parameter can be then used as a value of "aria-labelledby" attribute. | ||
* | ||
* ### Usage | ||
* Wrap the target element with Tooltip component and use the "content" property to define what should be displayed inside the tooltip. | ||
*/ | ||
export const Tooltip: FC<TooltipProps> = ({ content, disabled = false, offset = 10, placement = 'top', children, ...props }) => { | ||
const [isShown, setShown] = useState<boolean>(false) | ||
const [hideTimeout, setHideTimeout] = useState<NodeJS.Timeout | null>(null) | ||
const [referenceElement, setReferenceElement] = useState<HTMLElement | null>(null) | ||
const [popperElement, setPopperElement] = useState<HTMLSpanElement | null>(null) | ||
|
||
const { styles: popperStyles, attributes: popperAttributes } = usePopper(referenceElement, popperElement, { | ||
placement, | ||
modifiers: [ | ||
{ | ||
name: 'offset', | ||
options: { | ||
offset: [0, offset], | ||
}, | ||
}, | ||
], | ||
}) | ||
|
||
const onMouseEnter = useCallback(() => { | ||
if (hideTimeout !== null) { | ||
clearTimeout(hideTimeout) | ||
} | ||
|
||
setShown(true) | ||
}, [hideTimeout]) | ||
|
||
const onMouseLeave = useCallback(() => { | ||
setHideTimeout(setTimeout(() => setShown(false), 100)) | ||
}, []) | ||
|
||
return ( | ||
<div onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} className={styles.container}> | ||
<div className={styles.reference} ref={setReferenceElement}> | ||
{children} | ||
</div> | ||
|
||
{isShown && !disabled && ( | ||
<span | ||
role="tooltip" | ||
ref={setPopperElement} | ||
className={styles.overlay} | ||
style={popperStyles.popper} | ||
{...popperAttributes.popper} | ||
{...props} | ||
> | ||
{content} | ||
</span> | ||
)} | ||
</RCTooltip> | ||
) : ( | ||
children | ||
</div> | ||
) | ||
} |
Oops, something went wrong.