Skip to content

Commit fc7e41a

Browse files
Merge pull request #1048 from MoveRoad/feat/add-border-option
Add "border" option including arrow
2 parents 2ce8c44 + cd0a869 commit fc7e41a

File tree

7 files changed

+59
-5
lines changed

7 files changed

+59
-5
lines changed

docs/docs/options.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,13 +114,14 @@ import { Tooltip } from 'react-tooltip';
114114
| `closeOnEsc` | `boolean` | no | `false` | `true` `false` | Pressing escape key will close the tooltip |
115115
| `closeOnScroll` | `boolean` | no | `false` | `true` `false` | Scrolling will close the tooltip (for this to work, scroll element must be either the root html tag, the tooltip parent, or the anchor parent) |
116116
| `closeOnEsc` | `boolean` | no | `false` | `true` `false` | Resizing the window will close the tooltip |
117-
| `style` | `CSSProperties` | no | | a React inline style | Add inline styles directly to the tooltip |
117+
| `style` | `CSSProperties` | no | | a CSS style object | Add inline styles directly to the tooltip |
118118
| `position` | `{ x: number; y: number }` | no | | any `number` value for both `x` and `y` | Override the tooltip position on the DOM |
119119
| `isOpen` | `boolean` | no | handled by internal state | `true` `false` | The tooltip can be controlled or uncontrolled, this attribute can be used to handle show and hide tooltip outside tooltip (can be used **without** `setIsOpen`) |
120120
| `setIsOpen` | `function` | no | | | The tooltip can be controlled or uncontrolled, this attribute can be used to handle show and hide tooltip outside tooltip |
121121
| `afterShow` | `function` | no | | | A function to be called after the tooltip is shown |
122122
| `afterHide` | `function` | no | | | A function to be called after the tooltip is hidden |
123123
| `middlewares` | `Middleware[]` | no | | array of valid `floating-ui` middlewares | Allows for advanced customization. Check the [`floating-ui` docs](https://floating-ui.com/docs/middleware) for more information |
124+
| `border` | `CSSProperties['border']` | no | | a CSS border style | Change the style of the tooltip border (including the arrow) |
124125

125126
### Envs
126127

src/components/Tooltip/Tooltip.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ const Tooltip = ({
4444
setIsOpen,
4545
activeAnchor,
4646
setActiveAnchor,
47+
border,
4748
}: ITooltip) => {
4849
const tooltipRef = useRef<HTMLElement>(null)
4950
const tooltipArrowRef = useRef<HTMLElement>(null)
@@ -237,6 +238,7 @@ const Tooltip = ({
237238
tooltipArrowReference: tooltipArrowRef.current,
238239
strategy: positionStrategy,
239240
middlewares,
241+
border,
240242
}).then((computedStylesData) => {
241243
if (Object.keys(computedStylesData.tooltipStyles).length) {
242244
setInlineStyles(computedStylesData.tooltipStyles)
@@ -503,6 +505,7 @@ const Tooltip = ({
503505
tooltipArrowReference: tooltipArrowRef.current,
504506
strategy: positionStrategy,
505507
middlewares,
508+
border,
506509
}).then((computedStylesData) => {
507510
if (!mounted.current) {
508511
// invalidate computed positions after remount

src/components/Tooltip/TooltipTypes.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,5 @@ export interface ITooltip {
8989
afterHide?: () => void
9090
activeAnchor: HTMLElement | null
9191
setActiveAnchor: (anchor: HTMLElement | null) => void
92+
border?: CSSProperties['border']
9293
}

src/components/TooltipController/TooltipController.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ const TooltipController = ({
4444
style,
4545
position,
4646
isOpen,
47+
border,
4748
setIsOpen,
4849
afterShow,
4950
afterHide,
@@ -235,6 +236,20 @@ const TooltipController = ({
235236
}
236237
}, [anchorRefs, providerActiveAnchor, activeAnchor, anchorId, anchorSelect])
237238

239+
useEffect(() => {
240+
if (process.env.NODE_ENV === 'production') {
241+
return
242+
}
243+
if (style?.border) {
244+
// eslint-disable-next-line no-console
245+
console.warn('[react-tooltip] Do not set `style.border`. Use `border` prop instead.')
246+
}
247+
if (border && !CSS.supports('border', `${border}`)) {
248+
// eslint-disable-next-line no-console
249+
console.warn(`[react-tooltip] "${border}" is not a valid \`border\`.`)
250+
}
251+
}, [])
252+
238253
/**
239254
* content priority: children < render or content < html
240255
* children should be lower priority so that it can be used as the "default" content
@@ -283,6 +298,7 @@ const TooltipController = ({
283298
style,
284299
position,
285300
isOpen,
301+
border,
286302
setIsOpen,
287303
afterShow,
288304
afterHide,

src/components/TooltipController/TooltipControllerTypes.d.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ export interface ITooltipController {
6060
style?: CSSProperties
6161
position?: IPosition
6262
isOpen?: boolean
63+
/**
64+
* @description see https://developer.mozilla.org/en-US/docs/Web/CSS/border.
65+
*
66+
* Adding a border with width > 3px, or with `em/cm/rem/...` instead of `px`
67+
* might break the tooltip arrow positioning.
68+
*/
69+
border?: CSSProperties['border']
6370
setIsOpen?: (value: boolean) => void
6471
afterShow?: () => void
6572
afterHide?: () => void
@@ -69,8 +76,8 @@ declare module 'react' {
6976
interface HTMLAttributes<T> extends AriaAttributes, DOMAttributes<T> {
7077
'data-tooltip-id'?: string
7178
'data-tooltip-place'?: PlacesType
72-
'data-tooltip-content'?: string
73-
'data-tooltip-html'?: string
79+
'data-tooltip-content'?: string | null
80+
'data-tooltip-html'?: string | null
7481
'data-tooltip-variant'?: VariantType
7582
'data-tooltip-offset'?: number
7683
'data-tooltip-wrapper'?: WrapperType

src/utils/compute-positions-types.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { CSSProperties } from 'react'
12
import type { Middleware } from '../components/Tooltip/TooltipTypes'
23

34
export interface IComputePositions {
@@ -20,4 +21,5 @@ export interface IComputePositions {
2021
offset?: number
2122
strategy?: 'absolute' | 'fixed'
2223
middlewares?: Middleware[]
24+
border?: CSSProperties['border']
2325
}

src/utils/compute-positions.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export const computeTooltipPosition = async ({
99
offset: offsetValue = 10,
1010
strategy = 'absolute',
1111
middlewares = [offset(Number(offsetValue)), flip(), shift({ padding: 5 })],
12+
border,
1213
}: IComputePositions) => {
1314
if (!elementReference) {
1415
// elementReference can be null or undefined and we will not compute the position
@@ -31,7 +32,7 @@ export const computeTooltipPosition = async ({
3132
strategy,
3233
middleware,
3334
}).then(({ x, y, placement, middlewareData }) => {
34-
const styles = { left: `${x}px`, top: `${y}px` }
35+
const styles = { left: `${x}px`, top: `${y}px`, border }
3536

3637
const { x: arrowX, y: arrowY } = middlewareData.arrow ?? { x: 0, y: 0 }
3738

@@ -43,12 +44,35 @@ export const computeTooltipPosition = async ({
4344
left: 'right',
4445
}[placement.split('-')[0]] ?? 'bottom'
4546

47+
const borderSide =
48+
border &&
49+
{
50+
top: { borderBottom: border, borderRight: border },
51+
right: { borderBottom: border, borderLeft: border },
52+
bottom: { borderTop: border, borderLeft: border },
53+
left: { borderTop: border, borderRight: border },
54+
}[placement.split('-')[0]]
55+
56+
let borderWidth = 0
57+
if (border) {
58+
const match = `${border}`.match(/(\d+)px/)
59+
if (match?.[1]) {
60+
borderWidth = Number(match[1])
61+
} else {
62+
/**
63+
* this means `border` was set without `width`, or non-px value
64+
*/
65+
borderWidth = 1
66+
}
67+
}
68+
4669
const arrowStyle = {
4770
left: arrowX != null ? `${arrowX}px` : '',
4871
top: arrowY != null ? `${arrowY}px` : '',
4972
right: '',
5073
bottom: '',
51-
[staticSide]: '-4px',
74+
...borderSide,
75+
[staticSide]: `-${4 + borderWidth}px`,
5276
}
5377

5478
return { tooltipStyles: styles, tooltipArrowStyles: arrowStyle, place: placement }

0 commit comments

Comments
 (0)