Skip to content

Commit b132de6

Browse files
authored
feat: Animate tooltip (#7)
1 parent 7616970 commit b132de6

File tree

7 files changed

+330
-132
lines changed

7 files changed

+330
-132
lines changed

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ yarn add @untemps/svelte-use-tooltip
8383
border-radius: 6px;
8484
padding: 0.5rem;
8585
}
86-
86+
8787
:global(.tooltip::after) {
8888
content: '';
8989
position: absolute;
@@ -100,7 +100,7 @@ yarn add @untemps/svelte-use-tooltip
100100
## API
101101

102102
| Props | Type | Default | Description |
103-
|----------------------|---------|---------|-----------------------------------------------------------------------------------------------------------------|
103+
| -------------------- | ------- | ------- | --------------------------------------------------------------------------------------------------------------- |
104104
| `contentSelector` | string | null | Selector of the content to display in the tooltip. |
105105
| `contentClone` | boolean | null | Flag to clone the content to display in the tooltip. If false, the content is removed from its previous parent. |
106106
| `contentActions` | object | null | Configuration of the tooltip actions (see [Content Actions](#content-actions)). |
@@ -110,7 +110,7 @@ yarn add @untemps/svelte-use-tooltip
110110

111111
### Content Actions
112112

113-
The `contentActions` prop allow handling interactions within the tooltip content.
113+
The `contentActions` prop allow handling interactions within the tooltip content.
114114

115115
Each element inside the content parent may configure its own action since it can be queried using the key-selector.
116116

@@ -146,11 +146,11 @@ One event by element is possible so far as elements are referenced by selector.
146146
```
147147

148148
| Props | Type | Default | Description |
149-
|-------------------|----------|---------|----------------------------------------------------------------------------------------------------------|
149+
| ----------------- | -------- | ------- | -------------------------------------------------------------------------------------------------------- | --- |
150150
| `eventType` | string | null | Type of the event. All available [events](https://developer.mozilla.org/fr/docs/Web/Events) can be used. |
151151
| `callback` | function | null | Function to be used as event handler. |
152152
| `callbackParams` | array | null | List of arguments to pass to the event handler in. |
153-
| `closeOnCallback` | boolean | false | Flag to automatically close the tooltip when the event handler is triggered. | |
153+
| `closeOnCallback` | boolean | false | Flag to automatically close the tooltip when the event handler is triggered. | |
154154

155155
## Development
156156

@@ -169,4 +169,4 @@ Contributions are warmly welcomed:
169169
- Develop the feature AND write the tests (or write the tests AND develop the feature)
170170
- Commit your changes
171171
using [Angular Git Commit Guidelines](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#-git-commit-guidelines)
172-
- Submit a Pull Request
172+
- Submit a Pull Request

dev/src/App.svelte

Lines changed: 100 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -4,55 +4,84 @@
44
let tooltipPosition = 'top'
55
let useCustomTooltipClass = false
66
let isTooltipDisabled = false
7+
let animateTooltip = false
8+
let useCustomAnimationEnterClass = false
9+
let useCustomAnimationLeaveClass = false
710
8-
const _onTooltipClick = (arg, event) => {
11+
const _onTooltipClick = (arg, event) => {
912
console.log(arg)
10-
}
13+
}
1114
</script>
1215

1316
<main>
1417
<div class="container">
15-
<div use:useTooltip={{
16-
position: tooltipPosition,
17-
contentSelector: '.tooltip__button',
18-
contentClone: false,
19-
contentActions: {
20-
'*': {
21-
eventType: 'click',
22-
callback: _onTooltipClick,
23-
callbackParams: ['ok'],
24-
closeOnCallback: true
25-
},
18+
<div
19+
use:useTooltip={{
20+
position: tooltipPosition,
21+
contentSelector: '.tooltip__button',
22+
contentClone: false,
23+
contentActions: {
24+
'*': {
25+
eventType: 'click',
26+
callback: _onTooltipClick,
27+
callbackParams: ['ok'],
28+
closeOnCallback: true,
2629
},
27-
contentClassName: useCustomTooltipClass ? 'tooltip' : null,
28-
disabled: isTooltipDisabled,
29-
}} class="target">Hover me</div>
30-
<span class="tooltip__button">Hi! I'm a fancy tooltip!</span>
30+
},
31+
contentClassName: useCustomTooltipClass ? 'tooltip' : null,
32+
disabled: isTooltipDisabled,
33+
animated: animateTooltip,
34+
animationEnterClassName: useCustomAnimationEnterClass ? 'tooltip-enter' : null,
35+
animationLeaveClassName: useCustomAnimationLeaveClass ? 'tooltip-leave' : null,
36+
}}
37+
class="target"
38+
>
39+
Hover me
40+
</div>
41+
<span class="tooltip__button">Hi! I'm a fancy tooltip!</span>
3142
<form class="settings__form">
3243
<h1>Settings</h1>
33-
<fieldset>
34-
<label>
35-
Use Custom Tooltip Class:
36-
<input type="checkbox" bind:checked={useCustomTooltipClass} />
37-
</label>
38-
</fieldset>
44+
<fieldset>
45+
<label>
46+
Use Custom Tooltip Class:
47+
<input type="checkbox" bind:checked={useCustomTooltipClass} />
48+
</label>
49+
</fieldset>
3950
<fieldset>
4051
<label>
4152
Tooltip Position:
4253
<select bind:value={tooltipPosition}>
43-
<option value="left">Left</option>
44-
<option value="right">Right</option>
45-
<option value="top">Top</option>
46-
<option value="bottom">Bottom</option>
47-
</select>
54+
<option value="left">Left</option>
55+
<option value="right">Right</option>
56+
<option value="top">Top</option>
57+
<option value="bottom">Bottom</option>
58+
</select>
59+
</label>
60+
</fieldset>
61+
<fieldset>
62+
<label>
63+
Animate tooltip:
64+
<input type="checkbox" bind:checked={animateTooltip} />
65+
</label>
66+
</fieldset>
67+
<fieldset>
68+
<label>
69+
Use Custom Animation Enter Class:
70+
<input type="checkbox" bind:checked={useCustomAnimationEnterClass} />
71+
</label>
72+
</fieldset>
73+
<fieldset>
74+
<label>
75+
Use Custom Animation Leave Class:
76+
<input type="checkbox" bind:checked={useCustomAnimationLeaveClass} />
77+
</label>
78+
</fieldset>
79+
<fieldset>
80+
<label>
81+
Disable Tooltip:
82+
<input type="checkbox" bind:checked={isTooltipDisabled} />
4883
</label>
4984
</fieldset>
50-
<fieldset>
51-
<label>
52-
Disable Tooltip:
53-
<input type="checkbox" bind:checked={isTooltipDisabled} />
54-
</label>
55-
</fieldset>
5685
</form>
5786
</div>
5887
</main>
@@ -75,20 +104,20 @@
75104
76105
.target {
77106
width: 10rem;
78-
height: 3rem;
79-
background-color: white;
80-
color: black;
81-
display: flex;
82-
align-items: center;
83-
justify-content: center;
84-
box-shadow: 0 0 5px 0 rgba(0,0,0,0.5);
107+
height: 3rem;
108+
background-color: white;
109+
color: black;
110+
display: flex;
111+
align-items: center;
112+
justify-content: center;
113+
box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.5);
85114
}
86115
87-
.target:hover {
88-
cursor: pointer;
89-
background-color: black;
90-
color: white;
91-
}
116+
.target:hover {
117+
cursor: pointer;
118+
background-color: black;
119+
color: white;
120+
}
92121
93122
.settings__form {
94123
display: flex;
@@ -140,4 +169,29 @@
140169
border-style: solid;
141170
border-color: #ee7008 transparent transparent transparent;
142171
}
172+
173+
:global(.tooltip-enter) {
174+
animation: fadeIn 0.2s linear forwards;
175+
}
176+
177+
:global(.tooltip-leave) {
178+
animation: fadeOut 0.2s linear forwards;
179+
}
180+
181+
@keyframes fadeIn {
182+
from {
183+
opacity: 0;
184+
transform: translateX(50px);
185+
}
186+
to {
187+
opacity: 1;
188+
transform: translateX(0);
189+
}
190+
}
191+
@keyframes fadeOut {
192+
to {
193+
opacity: 0;
194+
transform: translateX(-50px);
195+
}
196+
}
143197
</style>

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@
132132
"build:cjs": "cross-env NODE_ENV=production BABEL_ENV=cjs rollup -c",
133133
"build:es": "cross-env NODE_ENV=production BABEL_ENV=es rollup -c",
134134
"build:umd": "cross-env NODE_ENV=production BABEL_ENV=umd rollup -c",
135-
"prettier": "prettier \"*/**/*.js\" --ignore-path ./.prettierignore --write && git add . && git status"
135+
"prettier": "prettier \"*/**/*.js\" --ignore-path ./.prettierignore --write && git add . && git status",
136+
"prepare": "husky install"
136137
}
137138
}

src/__tests__/useTooltip.test.js

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,27 @@ describe('useTooltip', () => {
4848
action = useTooltip(target, options)
4949
await fireEvent.mouseOver(target) // fireEvent.mouseEnter only works if mouseOver is triggered before
5050
await fireEvent.mouseEnter(target)
51-
expect(template).toBeVisible()
51+
expect(template).toBeInTheDocument()
5252
})
5353

5454
it('Hides tooltip on mouse leave', async () => {
5555
action = useTooltip(target, options)
5656
await fireEvent.mouseOver(target) // fireEvent.mouseEnter only works if mouseOver is triggered before
5757
await fireEvent.mouseEnter(target)
5858
await fireEvent.mouseLeave(target)
59-
expect(template).not.toBeVisible()
59+
expect(template).not.toBeInTheDocument()
60+
await fireEvent.animationEnd(template.parentNode)
61+
expect(template).not.toBeInTheDocument()
62+
})
63+
64+
it('Hides tooltip on mouse leave when animated', async () => {
65+
action = useTooltip(target, { ...options, animated: true })
66+
await fireEvent.mouseOver(target) // fireEvent.mouseEnter only works if mouseOver is triggered before
67+
await fireEvent.mouseEnter(target)
68+
await fireEvent.mouseLeave(target)
69+
expect(template).toBeInTheDocument()
70+
await fireEvent.animationEnd(template.parentNode)
71+
expect(template).not.toBeInTheDocument()
6072
})
6173
})
6274

@@ -70,7 +82,7 @@ describe('useTooltip', () => {
7082
const newTemplate = _createElement('new-template')
7183
action.update({
7284
...options,
73-
contentSelector: '#new-template'
85+
contentSelector: '#new-template',
7486
})
7587
await fireEvent.mouseOver(target) // fireEvent.mouseEnter only works if mouseOver is triggered before
7688
await fireEvent.mouseEnter(target)
@@ -97,9 +109,9 @@ describe('useTooltip', () => {
97109
await fireEvent.mouseEnter(target)
98110
await fireEvent.click(template)
99111
expect(contentAction.callback).toHaveBeenCalledWith(contentAction.callbackParams[0], expect.any(Event))
100-
expect(template).toBeVisible()
112+
expect(template).toBeInTheDocument()
101113
})
102-
114+
103115
it('Closes tooltip after triggering callback', async () => {
104116
action = useTooltip(target, options)
105117
options.contentActions['*'].closeOnCallback = true
@@ -108,7 +120,22 @@ describe('useTooltip', () => {
108120
await fireEvent.mouseEnter(target)
109121
await fireEvent.click(template)
110122
expect(contentAction.callback).toHaveBeenCalledWith(contentAction.callbackParams[0], expect.any(Event))
111-
expect(template).not.toBeVisible()
123+
expect(template).not.toBeInTheDocument()
124+
await fireEvent.animationEnd(template.parentNode)
125+
expect(template).not.toBeInTheDocument()
126+
})
127+
128+
it('Closes tooltip after triggering callback when animated', async () => {
129+
action = useTooltip(target, { ...options, animated: true })
130+
options.contentActions['*'].closeOnCallback = true
131+
const contentAction = options.contentActions['*']
132+
await fireEvent.mouseOver(target) // fireEvent.mouseEnter only works if mouseOver is triggered before
133+
await fireEvent.mouseEnter(target)
134+
await fireEvent.click(template)
135+
expect(contentAction.callback).toHaveBeenCalledWith(contentAction.callbackParams[0], expect.any(Event))
136+
expect(template).toBeInTheDocument()
137+
await fireEvent.animationEnd(template.parentNode)
138+
expect(template).not.toBeInTheDocument()
112139
})
113140

114141
it('Triggers new callback on tooltip click after update', async () => {
@@ -142,14 +169,14 @@ describe('useTooltip', () => {
142169
action = useTooltip(target, options)
143170
await fireEvent.mouseOver(target) // fireEvent.mouseEnter only works if mouseOver is triggered before
144171
await fireEvent.mouseEnter(target)
145-
expect(template.parentNode).toHaveClass('__tooltip__default')
172+
expect(template.parentNode).toHaveClass('__tooltip')
146173
})
147174

148175
it('Sets new tooltip class after update', async () => {
149176
action = useTooltip(target, options)
150177
await fireEvent.mouseOver(target) // fireEvent.mouseEnter only works if mouseOver is triggered before
151178
await fireEvent.mouseEnter(target)
152-
expect(template.parentNode).toHaveClass('__tooltip__default')
179+
expect(template.parentNode).toHaveClass('__tooltip')
153180
action.update({
154181
...options,
155182
contentClassName: 'foo',

src/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export { default as useTooltip } from './useTooltip'
1+
export { default as useTooltip } from './useTooltip'

0 commit comments

Comments
 (0)