react-tooltip 5.1.0 → 5.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/dist/react-tooltip.cjs.js +1 -1
  2. package/dist/react-tooltip.cjs.min.js +1 -1
  3. package/dist/react-tooltip.d.ts +2 -3
  4. package/dist/react-tooltip.esm.js +1 -1
  5. package/dist/react-tooltip.esm.min.js +1 -1
  6. package/dist/react-tooltip.umd.js +1 -1
  7. package/dist/react-tooltip.umd.min.js +1 -1
  8. package/package.json +1 -1
  9. package/src/App.tsx +119 -0
  10. package/src/components/Tooltip/Tooltip.tsx +226 -0
  11. package/src/components/Tooltip/TooltipTypes.d.ts +47 -0
  12. package/src/components/Tooltip/index.ts +1 -0
  13. package/src/components/Tooltip/styles.module.css +62 -0
  14. package/src/components/TooltipContent/TooltipContent.tsx +8 -0
  15. package/src/components/TooltipContent/TooltipContentTypes.d.ts +3 -0
  16. package/src/components/TooltipContent/index.ts +1 -0
  17. package/src/components/TooltipController/TooltipController.tsx +187 -0
  18. package/src/components/TooltipController/TooltipControllerTypes.d.ts +46 -0
  19. package/src/components/TooltipController/index.ts +1 -0
  20. package/src/components/TooltipProvider/TooltipProvider.tsx +110 -0
  21. package/src/components/TooltipProvider/TooltipProviderTypes.d.ts +33 -0
  22. package/src/components/TooltipProvider/TooltipWrapper.tsx +48 -0
  23. package/src/components/TooltipProvider/index.ts +2 -0
  24. package/src/index-dev.tsx +16 -0
  25. package/src/index.tsx +4 -0
  26. package/src/styles.module.css +5 -0
  27. package/src/test/__snapshots__/index.spec.js.snap +102 -0
  28. package/src/test/index.spec.js +143 -0
  29. package/src/tokens.css +8 -0
  30. package/src/utils/compute-positions-types.d.ts +8 -0
  31. package/src/utils/compute-positions.ts +65 -0
  32. package/src/utils/debounce.ts +27 -0
@@ -0,0 +1,33 @@
1
+ import type { ReactNode, RefObject } from 'react'
2
+ import type { ITooltipController } from 'components/TooltipController/TooltipControllerTypes'
3
+
4
+ export type AnchorRef = RefObject<HTMLElement>
5
+
6
+ export interface TooltipContextData {
7
+ anchorRefs: Set<AnchorRef>
8
+ activeAnchor: AnchorRef
9
+ attach: (...refs: AnchorRef[]) => void
10
+ detach: (...refs: AnchorRef[]) => void
11
+ setActiveAnchor: (ref: AnchorRef) => void
12
+ }
13
+
14
+ export type TooltipContextDataWrapper = TooltipContextData & {
15
+ // This means the context is a callable object
16
+ (tooltipId?: string): TooltipContextData
17
+ }
18
+
19
+ export interface ITooltipWrapper {
20
+ tooltipId?: string
21
+ children: ReactNode
22
+
23
+ place?: ITooltipController['place']
24
+ content?: ITooltipController['content']
25
+ html?: ITooltipController['html']
26
+ variant?: ITooltipController['variant']
27
+ offset?: ITooltipController['offset']
28
+ wrapper?: ITooltipController['wrapper']
29
+ events?: ITooltipController['events']
30
+ positionStrategy?: ITooltipController['positionStrategy']
31
+ delayShow?: ITooltipController['delayShow']
32
+ delayHide?: ITooltipController['delayHide']
33
+ }
@@ -0,0 +1,48 @@
1
+ import { useEffect, useRef } from 'react'
2
+ import { useTooltip } from './TooltipProvider'
3
+ import type { ITooltipWrapper } from './TooltipProviderTypes'
4
+
5
+ const TooltipWrapper = ({
6
+ tooltipId,
7
+ children,
8
+ place,
9
+ content,
10
+ html,
11
+ variant,
12
+ offset,
13
+ wrapper,
14
+ events,
15
+ positionStrategy,
16
+ delayShow,
17
+ delayHide,
18
+ }: ITooltipWrapper) => {
19
+ const { attach, detach } = useTooltip()(tooltipId)
20
+ const anchorRef = useRef<HTMLElement | null>(null)
21
+
22
+ useEffect(() => {
23
+ attach(anchorRef)
24
+ return () => {
25
+ detach(anchorRef)
26
+ }
27
+ }, [])
28
+
29
+ return (
30
+ <span
31
+ ref={anchorRef}
32
+ data-tooltip-place={place}
33
+ data-tooltip-content={content}
34
+ data-tooltip-html={html}
35
+ data-tooltip-variant={variant}
36
+ data-tooltip-offset={offset}
37
+ data-tooltip-wrapper={wrapper}
38
+ data-tooltip-events={events}
39
+ data-tooltip-position-strategy={positionStrategy}
40
+ data-tooltip-delay-show={delayShow}
41
+ data-tooltip-delay-hide={delayHide}
42
+ >
43
+ {children}
44
+ </span>
45
+ )
46
+ }
47
+
48
+ export default TooltipWrapper
@@ -0,0 +1,2 @@
1
+ export { default as TooltipProvider, useTooltip } from './TooltipProvider'
2
+ export { default as TooltipWrapper } from './TooltipWrapper'
@@ -0,0 +1,16 @@
1
+ import { StrictMode, version } from 'react'
2
+ import { createRoot } from 'react-dom/client'
3
+ import './tokens.css'
4
+ import App from './App'
5
+
6
+ // eslint-disable-next-line no-console
7
+ console.log('Parent folder loaded react version: ', version)
8
+
9
+ const container = document.getElementById('app')
10
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
11
+ const root = createRoot(container!)
12
+ root.render(
13
+ <StrictMode>
14
+ <App />
15
+ </StrictMode>,
16
+ )
package/src/index.tsx ADDED
@@ -0,0 +1,4 @@
1
+ import './tokens.css'
2
+
3
+ export { TooltipController as Tooltip } from './components/TooltipController'
4
+ export { TooltipProvider, TooltipWrapper } from './components/TooltipProvider'
@@ -0,0 +1,5 @@
1
+ .main {
2
+ margin: 0 auto;
3
+ width: 100%;
4
+ height: 100vh;
5
+ }
@@ -0,0 +1,102 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`tooltip props basic tooltip component 1`] = `
4
+ [
5
+ <span
6
+ id="basic-example"
7
+ >
8
+ Lorem Ipsum
9
+ </span>,
10
+ <div
11
+ className=""
12
+ role="tooltip"
13
+ style={{}}
14
+ >
15
+ Hello World!
16
+ <div
17
+ className=""
18
+ style={{}}
19
+ />
20
+ </div>,
21
+ ]
22
+ `;
23
+
24
+ exports[`tooltip props tooltip component - getContent 1`] = `
25
+ [
26
+ <span
27
+ id="basic-example-get-content"
28
+ >
29
+ Lorem Ipsum
30
+ </span>,
31
+ <div
32
+ className=""
33
+ role="tooltip"
34
+ style={{}}
35
+ >
36
+ Hello World!
37
+ <div
38
+ className=""
39
+ style={{}}
40
+ />
41
+ </div>,
42
+ ]
43
+ `;
44
+
45
+ exports[`tooltip props tooltip component - html 1`] = `
46
+ [
47
+ <span
48
+ id="basic-example-html"
49
+ >
50
+ Lorem Ipsum
51
+ </span>,
52
+ <div
53
+ className=""
54
+ role="tooltip"
55
+ style={{}}
56
+ >
57
+ <span
58
+ dangerouslySetInnerHTML={
59
+ {
60
+ "__html": "Hello World!",
61
+ }
62
+ }
63
+ />
64
+ <div
65
+ className=""
66
+ style={{}}
67
+ />
68
+ </div>,
69
+ ]
70
+ `;
71
+
72
+ exports[`tooltip props tooltip component - without anchorId 1`] = `
73
+ [
74
+ <span>
75
+ Lorem Ipsum
76
+ </span>,
77
+ <div
78
+ className=""
79
+ role="tooltip"
80
+ style={{}}
81
+ >
82
+ Hello World!
83
+ <div
84
+ className=""
85
+ style={{}}
86
+ />
87
+ </div>,
88
+ ]
89
+ `;
90
+
91
+ exports[`tooltip props tooltip component - without element reference 1`] = `
92
+ <div
93
+ className=""
94
+ role="tooltip"
95
+ style={{}}
96
+ >
97
+ <div
98
+ className=""
99
+ style={{}}
100
+ />
101
+ </div>
102
+ `;
@@ -0,0 +1,143 @@
1
+ import renderer from 'react-test-renderer'
2
+ import debounce from 'utils/debounce'
3
+ import { computeTooltipPosition } from 'utils/compute-positions'
4
+ import { TooltipController as Tooltip } from '../components/TooltipController'
5
+
6
+ // Tell Jest to mock all timeout functions
7
+ jest.useFakeTimers()
8
+
9
+ // eslint-disable-next-line react/prop-types
10
+ const TooltipProps = ({ id, ...tooltipParams }) => (
11
+ <>
12
+ <span id={id}>Lorem Ipsum</span>
13
+ <Tooltip anchorId={id} {...tooltipParams} />
14
+ </>
15
+ )
16
+
17
+ describe('tooltip props', () => {
18
+ test('tooltip component - without anchorId', () => {
19
+ const component = renderer.create(<TooltipProps content="Hello World!" />)
20
+ const tree = component.toJSON()
21
+ expect(tree).toMatchSnapshot()
22
+ })
23
+
24
+ test('tooltip component - without element reference', () => {
25
+ const component = renderer.create(<Tooltip />)
26
+ const tree = component.toJSON()
27
+ expect(tree).toMatchSnapshot()
28
+ })
29
+
30
+ test('basic tooltip component', () => {
31
+ const component = renderer.create(<TooltipProps id="basic-example" content="Hello World!" />)
32
+ const tree = component.toJSON()
33
+ expect(tree).toMatchSnapshot()
34
+ })
35
+
36
+ test('tooltip component - html', () => {
37
+ const component = renderer.create(
38
+ <TooltipProps id="basic-example-html" html="Hello World!" variant="info" place="top" />,
39
+ )
40
+ const tree = component.toJSON()
41
+ expect(tree).toMatchSnapshot()
42
+ })
43
+
44
+ test('tooltip component - getContent', () => {
45
+ const component = renderer.create(
46
+ <TooltipProps
47
+ id="basic-example-get-content"
48
+ content="Hello World!"
49
+ getContent={(value) => `${value} Manipuled!`}
50
+ variant="info"
51
+ place="top"
52
+ />,
53
+ )
54
+ const tree = component.toJSON()
55
+ expect(tree).toMatchSnapshot()
56
+ })
57
+ })
58
+
59
+ describe('compute positions', () => {
60
+ test('empty reference elements', async () => {
61
+ const value = await computeTooltipPosition({
62
+ elementReference: null,
63
+ tooltipReference: null,
64
+ tooltipArrowReference: null,
65
+ })
66
+
67
+ expect(value).toEqual({ tooltipStyles: {}, tooltipArrowStyles: {} })
68
+ })
69
+
70
+ test('empty tooltip reference element', async () => {
71
+ const element = document.createElement('div')
72
+ const value = await computeTooltipPosition({
73
+ elementReference: element,
74
+ tooltipReference: null,
75
+ tooltipArrowReference: null,
76
+ })
77
+
78
+ expect(value).toEqual({ tooltipStyles: {}, tooltipArrowStyles: {} })
79
+ })
80
+
81
+ test('empty tooltip arrow reference element', async () => {
82
+ const element = document.createElement('div')
83
+ const elementTooltip = document.createElement('div')
84
+ const value = await computeTooltipPosition({
85
+ elementReference: element,
86
+ tooltipReference: elementTooltip,
87
+ tooltipArrowReference: null,
88
+ })
89
+
90
+ expect(value).toEqual({
91
+ tooltipArrowStyles: {},
92
+ tooltipStyles: {
93
+ left: '5px',
94
+ top: '10px',
95
+ },
96
+ })
97
+ })
98
+
99
+ test('all reference elements', async () => {
100
+ const element = document.createElement('div')
101
+ const elementTooltip = document.createElement('div')
102
+ const elementTooltipArrow = document.createElement('div')
103
+ const value = await computeTooltipPosition({
104
+ elementReference: element,
105
+ tooltipReference: elementTooltip,
106
+ tooltipArrowReference: elementTooltipArrow,
107
+ })
108
+
109
+ expect(value).toEqual({
110
+ tooltipArrowStyles: {
111
+ bottom: '-4px',
112
+ left: '0px',
113
+ right: '',
114
+ top: '',
115
+ },
116
+ tooltipStyles: {
117
+ left: '5px',
118
+ top: '-10px',
119
+ },
120
+ })
121
+ })
122
+ })
123
+
124
+ describe('debounce', () => {
125
+ let func
126
+ let debouncedFunc
127
+
128
+ beforeEach((timeout = 1000) => {
129
+ func = jest.fn()
130
+ debouncedFunc = debounce(func, timeout)
131
+ })
132
+
133
+ test('execute just once', () => {
134
+ for (let i = 0; i < 100; i += 1) {
135
+ debouncedFunc()
136
+ }
137
+
138
+ // Fast-forward time
139
+ jest.runAllTimers()
140
+
141
+ expect(func).toBeCalledTimes(1)
142
+ })
143
+ })
package/src/tokens.css ADDED
@@ -0,0 +1,8 @@
1
+ :root {
2
+ --rt-color-white: #fff;
3
+ --rt-color-dark: #222;
4
+ --rt-color-success: #8dc572;
5
+ --rt-color-error: #be6464;
6
+ --rt-color-warning: #f0ad4e;
7
+ --rt-color-info: #337ab7;
8
+ }
@@ -0,0 +1,8 @@
1
+ export interface IComputePositions {
2
+ elementReference?: Element | HTMLElement | null
3
+ tooltipReference?: Element | HTMLElement | null
4
+ tooltipArrowReference?: Element | HTMLElement | null
5
+ place?: 'top' | 'right' | 'bottom' | 'left'
6
+ offset?: number
7
+ strategy?: 'absolute' | 'fixed'
8
+ }
@@ -0,0 +1,65 @@
1
+ import { computePosition, offset, flip, shift, arrow } from '@floating-ui/dom'
2
+ import type { IComputePositions } from './compute-positions-types'
3
+
4
+ export const computeTooltipPosition = async ({
5
+ elementReference = null,
6
+ tooltipReference = null,
7
+ tooltipArrowReference = null,
8
+ place = 'top',
9
+ offset: offsetValue = 10,
10
+ strategy = 'absolute',
11
+ }: IComputePositions) => {
12
+ if (!elementReference) {
13
+ // elementReference can be null or undefined and we will not compute the position
14
+ // eslint-disable-next-line no-console
15
+ // console.error('The reference element for tooltip was not defined: ', elementReference)
16
+ return { tooltipStyles: {}, tooltipArrowStyles: {} }
17
+ }
18
+
19
+ if (tooltipReference === null) {
20
+ return { tooltipStyles: {}, tooltipArrowStyles: {} }
21
+ }
22
+
23
+ const middleware = [offset(Number(offsetValue)), flip(), shift({ padding: 5 })]
24
+
25
+ if (tooltipArrowReference) {
26
+ middleware.push(arrow({ element: tooltipArrowReference as HTMLElement }))
27
+ return computePosition(elementReference as HTMLElement, tooltipReference as HTMLElement, {
28
+ placement: place,
29
+ strategy,
30
+ middleware,
31
+ }).then(({ x, y, placement, middlewareData }) => {
32
+ const styles = { left: `${x}px`, top: `${y}px` }
33
+
34
+ const { x: arrowX, y: arrowY } = middlewareData.arrow ?? { x: 0, y: 0 }
35
+
36
+ const staticSide =
37
+ {
38
+ top: 'bottom',
39
+ right: 'left',
40
+ bottom: 'top',
41
+ left: 'right',
42
+ }[placement.split('-')[0]] ?? 'bottom'
43
+
44
+ const arrowStyle = {
45
+ left: arrowX != null ? `${arrowX}px` : '',
46
+ top: arrowY != null ? `${arrowY}px` : '',
47
+ right: '',
48
+ bottom: '',
49
+ [staticSide]: '-4px',
50
+ }
51
+
52
+ return { tooltipStyles: styles, tooltipArrowStyles: arrowStyle }
53
+ })
54
+ }
55
+
56
+ return computePosition(elementReference as HTMLElement, tooltipReference as HTMLElement, {
57
+ placement: 'bottom',
58
+ strategy,
59
+ middleware,
60
+ }).then(({ x, y }) => {
61
+ const styles = { left: `${x}px`, top: `${y}px` }
62
+
63
+ return { tooltipStyles: styles, tooltipArrowStyles: {} }
64
+ })
65
+ }
@@ -0,0 +1,27 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ /**
3
+ * This function debounce the received function
4
+ * @param { function } func Function to be debounced
5
+ * @param { number } wait Time to wait before execut the function
6
+ * @param { boolean } immediate Param to define if the function will be executed immediately
7
+ */
8
+ const debounce = (func: (...args: any[]) => void, wait?: number, immediate?: true) => {
9
+ let timeout: NodeJS.Timeout | null = null
10
+
11
+ return function debounced(this: typeof func, ...args: any[]) {
12
+ const later = () => {
13
+ timeout = null
14
+ if (!immediate) {
15
+ func.apply(this, args)
16
+ }
17
+ }
18
+
19
+ if (timeout) {
20
+ clearTimeout(timeout)
21
+ }
22
+
23
+ timeout = setTimeout(later, wait)
24
+ }
25
+ }
26
+
27
+ export default debounce