react-tooltip 5.0.0-beta.2 → 5.1.0-beta.0
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.
- package/CONTRIBUTION.md +38 -0
- package/README.md +9 -4
- package/dist/react-tooltip.cjs.js +15 -5
- package/dist/react-tooltip.cjs.min.js +1 -1
- package/dist/react-tooltip.css +11 -13
- package/dist/react-tooltip.d.ts +17 -3
- package/dist/react-tooltip.esm.js +15 -5
- package/dist/react-tooltip.esm.min.js +2 -2
- package/dist/react-tooltip.min.css +1 -1
- package/dist/react-tooltip.umd.js +15 -5
- package/dist/react-tooltip.umd.min.js +1 -1
- package/package.json +2 -4
- package/rollup.config.types.js +6 -2
- package/src/App.tsx +79 -0
- package/src/components/Tooltip/Tooltip.tsx +191 -0
- package/src/components/Tooltip/TooltipTypes.d.ts +35 -0
- package/src/components/Tooltip/index.ts +1 -0
- package/src/components/Tooltip/styles.module.css +62 -0
- package/src/components/TooltipContent/TooltipContent.tsx +8 -0
- package/src/components/TooltipContent/TooltipContentTypes.d.ts +3 -0
- package/src/components/TooltipContent/index.ts +1 -0
- package/src/components/TooltipController/TooltipController.tsx +203 -0
- package/src/components/TooltipController/TooltipControllerTypes.d.ts +32 -0
- package/src/components/TooltipController/constants.ts +11 -0
- package/src/components/TooltipController/index.ts +1 -0
- package/src/index-dev.tsx +15 -0
- package/src/index.tsx +3 -0
- package/src/styles.module.css +5 -0
- package/src/test/__snapshots__/index.spec.js.snap +107 -0
- package/src/test/index.spec.js +142 -0
- package/src/tokens.css +8 -0
- package/src/utils/compute-positions-types.d.ts +8 -0
- package/src/utils/compute-positions.ts +66 -0
- package/src/utils/debounce.ts +32 -0
- package/.editorconfig +0 -25
- package/coverage/clover.xml +0 -610
- package/coverage/coverage-final.json +0 -11
- package/coverage/lcov-report/base.css +0 -224
- package/coverage/lcov-report/block-navigation.js +0 -87
- package/coverage/lcov-report/components/Tooltip/Tooltip.tsx.html +0 -646
- package/coverage/lcov-report/components/Tooltip/index.html +0 -146
- package/coverage/lcov-report/components/Tooltip/index.ts.html +0 -88
- package/coverage/lcov-report/components/Tooltip/styles.module.css.html +0 -277
- package/coverage/lcov-report/components/TooltipContent/TooltipContent.tsx.html +0 -109
- package/coverage/lcov-report/components/TooltipContent/index.html +0 -131
- package/coverage/lcov-report/components/TooltipContent/index.ts.html +0 -88
- package/coverage/lcov-report/components/TooltipController/TooltipController.tsx.html +0 -664
- package/coverage/lcov-report/components/TooltipController/constants.ts.html +0 -118
- package/coverage/lcov-report/components/TooltipController/index.html +0 -146
- package/coverage/lcov-report/components/TooltipController/index.ts.html +0 -88
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +0 -161
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -2
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -196
- package/coverage/lcov-report/sum.js.html +0 -97
- package/coverage/lcov-report/sum.ts.html +0 -100
- package/coverage/lcov-report/test/index.html +0 -116
- package/coverage/lcov-report/test/sum.ts.html +0 -100
- package/coverage/lcov-report/utils/compute-positions.ts.html +0 -277
- package/coverage/lcov-report/utils/debounce.ts.html +0 -181
- package/coverage/lcov-report/utils/index.html +0 -131
- package/coverage/lcov.info +0 -729
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react'
|
|
2
|
+
import { Tooltip } from 'components/Tooltip'
|
|
3
|
+
import type {
|
|
4
|
+
EventsType,
|
|
5
|
+
PositionStrategy,
|
|
6
|
+
PlacesType,
|
|
7
|
+
VariantType,
|
|
8
|
+
WrapperType,
|
|
9
|
+
} from 'components/Tooltip/TooltipTypes'
|
|
10
|
+
import { dataAttributesKeys } from './constants'
|
|
11
|
+
import type { ITooltipController } from './TooltipControllerTypes'
|
|
12
|
+
|
|
13
|
+
const TooltipController = ({
|
|
14
|
+
id,
|
|
15
|
+
anchorId,
|
|
16
|
+
content,
|
|
17
|
+
html,
|
|
18
|
+
className,
|
|
19
|
+
classNameArrow,
|
|
20
|
+
variant = 'dark',
|
|
21
|
+
place = 'top',
|
|
22
|
+
offset = 10,
|
|
23
|
+
wrapper = 'div',
|
|
24
|
+
children = null,
|
|
25
|
+
events = ['hover'],
|
|
26
|
+
positionStrategy = 'absolute',
|
|
27
|
+
delayShow = 0,
|
|
28
|
+
delayHide = 0,
|
|
29
|
+
style,
|
|
30
|
+
getContent,
|
|
31
|
+
isOpen,
|
|
32
|
+
setIsOpen,
|
|
33
|
+
}: ITooltipController) => {
|
|
34
|
+
const [tooltipContent, setTooltipContent] = useState(content || html)
|
|
35
|
+
const [tooltipPlace, setTooltipPlace] = useState(place)
|
|
36
|
+
const [tooltipVariant, setTooltipVariant] = useState(variant)
|
|
37
|
+
const [tooltipOffset, setTooltipOffset] = useState(offset)
|
|
38
|
+
const [tooltipDelayShow, setTooltipDelayShow] = useState(delayShow)
|
|
39
|
+
const [tooltipDelayHide, setTooltipDelayHide] = useState(delayHide)
|
|
40
|
+
const [tooltipWrapper, setTooltipWrapper] = useState<WrapperType>(wrapper)
|
|
41
|
+
const [tooltipEvents, setTooltipEvents] = useState<EventsType[]>(events)
|
|
42
|
+
const [tooltipPositionStrategy, setTooltipPositionStrategy] =
|
|
43
|
+
useState<PositionStrategy>(positionStrategy)
|
|
44
|
+
const [isHtmlContent, setIsHtmlContent] = useState<boolean>(Boolean(html))
|
|
45
|
+
|
|
46
|
+
const getDataAttributesFromAnchorElement = (elementReference: HTMLElement) => {
|
|
47
|
+
const dataAttributes = elementReference?.getAttributeNames().reduce((acc, name) => {
|
|
48
|
+
if (name.includes('data-tooltip-')) {
|
|
49
|
+
;(acc as any)[name] = elementReference?.getAttribute(name)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return acc
|
|
53
|
+
}, {})
|
|
54
|
+
|
|
55
|
+
return dataAttributes
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const applyAllDataAttributesFromAnchorElement = (dataAttributes: {
|
|
59
|
+
[key: string]: string | number | boolean
|
|
60
|
+
}) => {
|
|
61
|
+
const keys = Object.keys(dataAttributes)
|
|
62
|
+
let formatedKey = null
|
|
63
|
+
|
|
64
|
+
const handleDataAttributes = {
|
|
65
|
+
place: (value: PlacesType) => {
|
|
66
|
+
setTooltipPlace(value)
|
|
67
|
+
},
|
|
68
|
+
content: (value: string) => {
|
|
69
|
+
setIsHtmlContent(true)
|
|
70
|
+
if (getContent) {
|
|
71
|
+
setTooltipContent(getContent(value))
|
|
72
|
+
} else {
|
|
73
|
+
setTooltipContent(value)
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
html: (value: string) => {
|
|
77
|
+
setIsHtmlContent(true)
|
|
78
|
+
if (getContent) {
|
|
79
|
+
setTooltipContent(getContent(value))
|
|
80
|
+
} else {
|
|
81
|
+
setTooltipContent(value)
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
variant: (value: VariantType) => {
|
|
85
|
+
setTooltipVariant(value)
|
|
86
|
+
},
|
|
87
|
+
offset: (value: number) => {
|
|
88
|
+
setTooltipOffset(value)
|
|
89
|
+
},
|
|
90
|
+
wrapper: (value: WrapperType) => {
|
|
91
|
+
setTooltipWrapper(value)
|
|
92
|
+
},
|
|
93
|
+
events: (value: string) => {
|
|
94
|
+
const parsedEvents = value.split(' ')
|
|
95
|
+
setTooltipEvents(parsedEvents as EventsType[])
|
|
96
|
+
},
|
|
97
|
+
positionStrategy: (value: PositionStrategy) => {
|
|
98
|
+
setTooltipPositionStrategy(value)
|
|
99
|
+
},
|
|
100
|
+
'delay-show': (value: number) => {
|
|
101
|
+
setTooltipDelayShow(Number(value))
|
|
102
|
+
},
|
|
103
|
+
'delay-hide': (value: number) => {
|
|
104
|
+
setTooltipDelayHide(Number(value))
|
|
105
|
+
},
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
keys.forEach((key) => {
|
|
109
|
+
formatedKey = key.replace('data-tooltip-', '')
|
|
110
|
+
|
|
111
|
+
if (dataAttributesKeys.includes(formatedKey)) {
|
|
112
|
+
// @ts-ignore
|
|
113
|
+
handleDataAttributes[formatedKey](dataAttributes[key])
|
|
114
|
+
}
|
|
115
|
+
})
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const getElementSpecificAttributeKeyAndValueParsed = ({
|
|
119
|
+
element,
|
|
120
|
+
attributeName,
|
|
121
|
+
}: {
|
|
122
|
+
element: HTMLElement
|
|
123
|
+
attributeName: string
|
|
124
|
+
}) => {
|
|
125
|
+
return { [attributeName]: element.getAttribute(attributeName) }
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
useEffect(() => {
|
|
129
|
+
if (!anchorId) {
|
|
130
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
131
|
+
return () => {}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const elementReference = document.querySelector(`#${anchorId}`)
|
|
135
|
+
|
|
136
|
+
if (!elementReference) {
|
|
137
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
138
|
+
return () => {}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (content && getContent) {
|
|
142
|
+
setTooltipContent(getContent(content))
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// do not check for subtree and childrens, we only want to know attribute changes
|
|
146
|
+
// to stay watching `data-attributes` from anchor element
|
|
147
|
+
const observerConfig = { attributes: true, childList: false, subtree: false }
|
|
148
|
+
|
|
149
|
+
const observerCallback = (mutationList: any) => {
|
|
150
|
+
mutationList.forEach((mutation: any) => {
|
|
151
|
+
if (mutation.type === 'attributes') {
|
|
152
|
+
const attributeKeyAndValue = getElementSpecificAttributeKeyAndValueParsed({
|
|
153
|
+
element: elementReference as HTMLElement,
|
|
154
|
+
attributeName: mutation.attributeName,
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
applyAllDataAttributesFromAnchorElement(
|
|
158
|
+
attributeKeyAndValue as { [key: string]: string | number | boolean },
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
})
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Create an observer instance linked to the callback function
|
|
165
|
+
const observer = new MutationObserver(observerCallback)
|
|
166
|
+
|
|
167
|
+
// Start observing the target node for configured mutations
|
|
168
|
+
observer.observe(elementReference, observerConfig)
|
|
169
|
+
|
|
170
|
+
const dataAttributes = getDataAttributesFromAnchorElement(elementReference as HTMLElement)
|
|
171
|
+
|
|
172
|
+
applyAllDataAttributesFromAnchorElement(dataAttributes)
|
|
173
|
+
|
|
174
|
+
return () => {
|
|
175
|
+
// Remove the observer when the tooltip is destroyed
|
|
176
|
+
observer.disconnect()
|
|
177
|
+
}
|
|
178
|
+
}, [anchorId])
|
|
179
|
+
|
|
180
|
+
const props = {
|
|
181
|
+
id,
|
|
182
|
+
anchorId,
|
|
183
|
+
className,
|
|
184
|
+
classNameArrow,
|
|
185
|
+
content: tooltipContent,
|
|
186
|
+
isHtmlContent,
|
|
187
|
+
place: tooltipPlace,
|
|
188
|
+
variant: tooltipVariant,
|
|
189
|
+
offset: tooltipOffset,
|
|
190
|
+
wrapper: tooltipWrapper,
|
|
191
|
+
events: tooltipEvents,
|
|
192
|
+
positionStrategy: tooltipPositionStrategy,
|
|
193
|
+
delayShow: tooltipDelayShow,
|
|
194
|
+
delayHide: tooltipDelayHide,
|
|
195
|
+
style,
|
|
196
|
+
isOpen,
|
|
197
|
+
setIsOpen,
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return children ? <Tooltip {...props}>{children}</Tooltip> : <Tooltip {...props} />
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export default TooltipController
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { CSSProperties } from 'react'
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
PlacesType,
|
|
5
|
+
VariantType,
|
|
6
|
+
WrapperType,
|
|
7
|
+
ChildrenType,
|
|
8
|
+
EventsType,
|
|
9
|
+
PositionStrategy,
|
|
10
|
+
} from 'components/Tooltip/TooltipTypes'
|
|
11
|
+
|
|
12
|
+
export interface ITooltipController {
|
|
13
|
+
className?: string
|
|
14
|
+
classNameArrow?: string
|
|
15
|
+
content?: string | number
|
|
16
|
+
html?: string
|
|
17
|
+
place?: PlacesType
|
|
18
|
+
offset?: number
|
|
19
|
+
id?: string
|
|
20
|
+
variant?: VariantType
|
|
21
|
+
anchorId?: string
|
|
22
|
+
wrapper?: WrapperType
|
|
23
|
+
children?: ChildrenType
|
|
24
|
+
events?: EventsType[]
|
|
25
|
+
positionStrategy?: PositionStrategy
|
|
26
|
+
delayShow?: number
|
|
27
|
+
delayHide?: number
|
|
28
|
+
getContent?: (value) => string
|
|
29
|
+
style?: CSSProperties
|
|
30
|
+
isOpen?: boolean
|
|
31
|
+
setIsOpen?: (value: boolean) => void
|
|
32
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const dataAttributesKeys = [
|
|
2
|
+
'place', // place of tooltip
|
|
3
|
+
'content', // content of tooltip
|
|
4
|
+
'html', // html content of tooltip
|
|
5
|
+
'variant', // variant of tooltip
|
|
6
|
+
'offset', // offset of tooltip
|
|
7
|
+
'wrapper', // wrapper of tooltip
|
|
8
|
+
'events', // events of tooltip
|
|
9
|
+
'delay-show', // delay to show tooltip
|
|
10
|
+
'delay-hide', // delay to hide tooltip
|
|
11
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as TooltipController } from './TooltipController'
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { StrictMode, version } from 'react'
|
|
2
|
+
import { createRoot } from 'react-dom/client'
|
|
3
|
+
import './tokens.css'
|
|
4
|
+
import App from './App'
|
|
5
|
+
|
|
6
|
+
console.log('Parent folder loaded react version: ', version)
|
|
7
|
+
|
|
8
|
+
const container = document.getElementById('app')
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
10
|
+
const root = createRoot(container!)
|
|
11
|
+
root.render(
|
|
12
|
+
<StrictMode>
|
|
13
|
+
<App />
|
|
14
|
+
</StrictMode>,
|
|
15
|
+
)
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
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
|
+
id=":r2:"
|
|
13
|
+
role="tooltip"
|
|
14
|
+
style={{}}
|
|
15
|
+
>
|
|
16
|
+
Hello World!
|
|
17
|
+
<div
|
|
18
|
+
className=""
|
|
19
|
+
style={{}}
|
|
20
|
+
/>
|
|
21
|
+
</div>,
|
|
22
|
+
]
|
|
23
|
+
`;
|
|
24
|
+
|
|
25
|
+
exports[`tooltip props tooltip component - getContent 1`] = `
|
|
26
|
+
[
|
|
27
|
+
<span
|
|
28
|
+
id="basic-example-get-content"
|
|
29
|
+
>
|
|
30
|
+
Lorem Ipsum
|
|
31
|
+
</span>,
|
|
32
|
+
<div
|
|
33
|
+
className=""
|
|
34
|
+
id=":r4:"
|
|
35
|
+
role="tooltip"
|
|
36
|
+
style={{}}
|
|
37
|
+
>
|
|
38
|
+
Hello World!
|
|
39
|
+
<div
|
|
40
|
+
className=""
|
|
41
|
+
style={{}}
|
|
42
|
+
/>
|
|
43
|
+
</div>,
|
|
44
|
+
]
|
|
45
|
+
`;
|
|
46
|
+
|
|
47
|
+
exports[`tooltip props tooltip component - html 1`] = `
|
|
48
|
+
[
|
|
49
|
+
<span
|
|
50
|
+
id="basic-example-html"
|
|
51
|
+
>
|
|
52
|
+
Lorem Ipsum
|
|
53
|
+
</span>,
|
|
54
|
+
<div
|
|
55
|
+
className=""
|
|
56
|
+
id=":r3:"
|
|
57
|
+
role="tooltip"
|
|
58
|
+
style={{}}
|
|
59
|
+
>
|
|
60
|
+
<span
|
|
61
|
+
dangerouslySetInnerHTML={
|
|
62
|
+
{
|
|
63
|
+
"__html": "Hello World!",
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/>
|
|
67
|
+
<div
|
|
68
|
+
className=""
|
|
69
|
+
style={{}}
|
|
70
|
+
/>
|
|
71
|
+
</div>,
|
|
72
|
+
]
|
|
73
|
+
`;
|
|
74
|
+
|
|
75
|
+
exports[`tooltip props tooltip component - without anchorId 1`] = `
|
|
76
|
+
[
|
|
77
|
+
<span>
|
|
78
|
+
Lorem Ipsum
|
|
79
|
+
</span>,
|
|
80
|
+
<div
|
|
81
|
+
className=""
|
|
82
|
+
id=":r0:"
|
|
83
|
+
role="tooltip"
|
|
84
|
+
style={{}}
|
|
85
|
+
>
|
|
86
|
+
Hello World!
|
|
87
|
+
<div
|
|
88
|
+
className=""
|
|
89
|
+
style={{}}
|
|
90
|
+
/>
|
|
91
|
+
</div>,
|
|
92
|
+
]
|
|
93
|
+
`;
|
|
94
|
+
|
|
95
|
+
exports[`tooltip props tooltip component - without element reference 1`] = `
|
|
96
|
+
<div
|
|
97
|
+
className=""
|
|
98
|
+
id=":r1:"
|
|
99
|
+
role="tooltip"
|
|
100
|
+
style={{}}
|
|
101
|
+
>
|
|
102
|
+
<div
|
|
103
|
+
className=""
|
|
104
|
+
style={{}}
|
|
105
|
+
/>
|
|
106
|
+
</div>
|
|
107
|
+
`;
|
|
@@ -0,0 +1,142 @@
|
|
|
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
|
+
const TooltipProps = ({ id, ...tooltipParams }) => (
|
|
10
|
+
<>
|
|
11
|
+
<span id={id}>Lorem Ipsum</span>
|
|
12
|
+
<Tooltip anchorId={id} {...tooltipParams} />
|
|
13
|
+
</>
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
describe('tooltip props', () => {
|
|
17
|
+
test('tooltip component - without anchorId', () => {
|
|
18
|
+
const component = renderer.create(<TooltipProps content="Hello World!" />)
|
|
19
|
+
const tree = component.toJSON()
|
|
20
|
+
expect(tree).toMatchSnapshot()
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
test('tooltip component - without element reference', () => {
|
|
24
|
+
const component = renderer.create(<Tooltip />)
|
|
25
|
+
const tree = component.toJSON()
|
|
26
|
+
expect(tree).toMatchSnapshot()
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
test('basic tooltip component', () => {
|
|
30
|
+
const component = renderer.create(<TooltipProps id="basic-example" content="Hello World!" />)
|
|
31
|
+
const tree = component.toJSON()
|
|
32
|
+
expect(tree).toMatchSnapshot()
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('tooltip component - html', () => {
|
|
36
|
+
const component = renderer.create(
|
|
37
|
+
<TooltipProps id="basic-example-html" html="Hello World!" variant="info" place="top" />,
|
|
38
|
+
)
|
|
39
|
+
const tree = component.toJSON()
|
|
40
|
+
expect(tree).toMatchSnapshot()
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test('tooltip component - getContent', () => {
|
|
44
|
+
const component = renderer.create(
|
|
45
|
+
<TooltipProps
|
|
46
|
+
id="basic-example-get-content"
|
|
47
|
+
content="Hello World!"
|
|
48
|
+
getContent={(value) => `${value} Manipuled!`}
|
|
49
|
+
variant="info"
|
|
50
|
+
place="top"
|
|
51
|
+
/>,
|
|
52
|
+
)
|
|
53
|
+
const tree = component.toJSON()
|
|
54
|
+
expect(tree).toMatchSnapshot()
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
describe('compute positions', () => {
|
|
59
|
+
test('empty reference elements', async () => {
|
|
60
|
+
const value = await computeToolTipPosition({
|
|
61
|
+
elementReference: null,
|
|
62
|
+
tooltipReference: null,
|
|
63
|
+
tooltipArrowReference: null,
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
expect(value).toEqual({ tooltipStyles: {}, tooltipArrowStyles: {} })
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test('empty tooltip reference element', async () => {
|
|
70
|
+
const element = document.createElement('div')
|
|
71
|
+
const value = await computeToolTipPosition({
|
|
72
|
+
elementReference: element,
|
|
73
|
+
tooltipReference: null,
|
|
74
|
+
tooltipArrowReference: null,
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
expect(value).toEqual({ tooltipStyles: {}, tooltipArrowStyles: {} })
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
test('empty tooltip arrow reference element', async () => {
|
|
81
|
+
const element = document.createElement('div')
|
|
82
|
+
const elementTooltip = document.createElement('div')
|
|
83
|
+
const value = await computeToolTipPosition({
|
|
84
|
+
elementReference: element,
|
|
85
|
+
tooltipReference: elementTooltip,
|
|
86
|
+
tooltipArrowReference: null,
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
expect(value).toEqual({
|
|
90
|
+
tooltipArrowStyles: {},
|
|
91
|
+
tooltipStyles: {
|
|
92
|
+
left: '5px',
|
|
93
|
+
top: '10px',
|
|
94
|
+
},
|
|
95
|
+
})
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
test('all reference elements', async () => {
|
|
99
|
+
const element = document.createElement('div')
|
|
100
|
+
const elementTooltip = document.createElement('div')
|
|
101
|
+
const elementTooltipArrow = document.createElement('div')
|
|
102
|
+
const value = await computeToolTipPosition({
|
|
103
|
+
elementReference: element,
|
|
104
|
+
tooltipReference: elementTooltip,
|
|
105
|
+
tooltipArrowReference: elementTooltipArrow,
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
expect(value).toEqual({
|
|
109
|
+
tooltipArrowStyles: {
|
|
110
|
+
bottom: '-4px',
|
|
111
|
+
left: '0px',
|
|
112
|
+
right: '',
|
|
113
|
+
top: '',
|
|
114
|
+
},
|
|
115
|
+
tooltipStyles: {
|
|
116
|
+
left: '5px',
|
|
117
|
+
top: '-10px',
|
|
118
|
+
},
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
describe('debounce', () => {
|
|
124
|
+
let func
|
|
125
|
+
let debouncedFunc
|
|
126
|
+
|
|
127
|
+
beforeEach((timeout = 1000) => {
|
|
128
|
+
func = jest.fn()
|
|
129
|
+
debouncedFunc = debounce(func, timeout)
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
test('execute just once', () => {
|
|
133
|
+
for (let i = 0; i < 100; i += 1) {
|
|
134
|
+
debouncedFunc()
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Fast-forward time
|
|
138
|
+
jest.runAllTimers()
|
|
139
|
+
|
|
140
|
+
expect(func).toBeCalledTimes(1)
|
|
141
|
+
})
|
|
142
|
+
})
|
package/src/tokens.css
ADDED
|
@@ -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,66 @@
|
|
|
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
|
+
// @ts-ignore
|
|
35
|
+
const { x: arrowX, y: arrowY } = middlewareData.arrow
|
|
36
|
+
|
|
37
|
+
const staticSide = {
|
|
38
|
+
top: 'bottom',
|
|
39
|
+
right: 'left',
|
|
40
|
+
bottom: 'top',
|
|
41
|
+
left: 'right',
|
|
42
|
+
}[placement.split('-')[0]]
|
|
43
|
+
|
|
44
|
+
const arrowStyle = {
|
|
45
|
+
left: arrowX != null ? `${arrowX}px` : '',
|
|
46
|
+
top: arrowY != null ? `${arrowY}px` : '',
|
|
47
|
+
right: '',
|
|
48
|
+
bottom: '',
|
|
49
|
+
// @ts-ignore
|
|
50
|
+
[staticSide]: '-4px',
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return { tooltipStyles: styles, tooltipArrowStyles: arrowStyle }
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return computePosition(elementReference as HTMLElement, tooltipReference as HTMLElement, {
|
|
58
|
+
placement: 'bottom',
|
|
59
|
+
strategy,
|
|
60
|
+
middleware,
|
|
61
|
+
}).then(({ x, y }) => {
|
|
62
|
+
const styles = { left: `${x}px`, top: `${y}px` }
|
|
63
|
+
|
|
64
|
+
return { tooltipStyles: styles, tooltipArrowStyles: {} }
|
|
65
|
+
})
|
|
66
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/* eslint-disable func-names */
|
|
2
|
+
/* eslint-disable prefer-rest-params */
|
|
3
|
+
/* eslint-disable @typescript-eslint/no-this-alias */
|
|
4
|
+
/**
|
|
5
|
+
* This function debounce the received function
|
|
6
|
+
* @param { function } func Function to be debounced
|
|
7
|
+
* @param { number } wait Time to wait before execut the function
|
|
8
|
+
* @param { boolean } immediate Param to define if the function will be executed immediately
|
|
9
|
+
*/
|
|
10
|
+
const debounce = (func: { (): void; (): void; apply?: any }, wait?: number, immediate?: true) => {
|
|
11
|
+
let timeout: string | number | NodeJS.Timeout | null | undefined
|
|
12
|
+
|
|
13
|
+
return function () {
|
|
14
|
+
// @ts-ignore
|
|
15
|
+
const context = this
|
|
16
|
+
const args = arguments
|
|
17
|
+
|
|
18
|
+
const later = () => {
|
|
19
|
+
timeout = null
|
|
20
|
+
if (!immediate) {
|
|
21
|
+
func.apply(context, args)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// @ts-ignore
|
|
26
|
+
clearTimeout(timeout)
|
|
27
|
+
|
|
28
|
+
timeout = setTimeout(later, wait)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export default debounce
|
package/.editorconfig
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
# editorconfig.org
|
|
3
|
-
root = true
|
|
4
|
-
|
|
5
|
-
[*]
|
|
6
|
-
charset = utf-8
|
|
7
|
-
indent_style = space
|
|
8
|
-
indent_size = 2
|
|
9
|
-
end_of_line = lf
|
|
10
|
-
trim_trailing_whitespace = true
|
|
11
|
-
insert_final_newline = true
|
|
12
|
-
max_line_length = 100
|
|
13
|
-
|
|
14
|
-
[*.md]
|
|
15
|
-
max_line_length = 0
|
|
16
|
-
trim_trailing_whitespace = false
|
|
17
|
-
|
|
18
|
-
[{*.json, *.svg}]
|
|
19
|
-
indent_style = space
|
|
20
|
-
indent_size = 2
|
|
21
|
-
|
|
22
|
-
# Matches the exact package.json, or *rc
|
|
23
|
-
[{package.json,*.yml,*rc}]
|
|
24
|
-
indent_style = space
|
|
25
|
-
indent_size = 2
|