Skip to content
This repository was archived by the owner on Nov 9, 2024. It is now read-only.

Commit 313ff15

Browse files
benkeenatomiks
authored andcommitted
Added className prop support (#56)
* added className prop support * code review feedback; incomplete * whitespace separator fix * Update index.d.ts * Remove failing test for now
1 parent eb45eab commit 313ff15

File tree

5 files changed

+70
-5
lines changed

5 files changed

+70
-5
lines changed

demo/index.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,14 +117,21 @@ class ComponentChild extends React.Component {
117117
class App extends React.Component {
118118
state = {
119119
arrow: false,
120+
customClass: ''
120121
}
121122

122123
toggleArrow = () => {
123124
this.setState(state => ({
124-
arrow: !state.arrow,
125+
arrow: !state.arrow
125126
}))
126127
}
127128

129+
updateCustomClass = (e) => {
130+
this.setState({
131+
customClass: e.target.value
132+
})
133+
}
134+
128135
render() {
129136
return (
130137
<main className="container">
@@ -165,6 +172,12 @@ class App extends React.Component {
165172
</Tippy>
166173
</Tippy>
167174
</Tippy>
175+
176+
<h1>Other</h1>
177+
<input type="text" placeholder="Enter class" onChange={this.updateCustomClass}/>
178+
<Tippy placement="bottom" className={this.state.customClass}>
179+
<button>Custom class</button>
180+
</Tippy>
168181
</main>
169182
)
170183
}

index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export interface TippyProps extends Omit<Props, 'content'> {
99
onCreate?: (tip: Instance) => void
1010
isVisible?: boolean
1111
isEnabled?: boolean
12+
className?: string
1213
}
1314

1415
declare const Tippy: React.ForwardRefExoticComponent<TippyProps>

src/Tippy.js

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import React, {
44
useState,
55
useRef,
66
useEffect,
7+
useLayoutEffect,
78
} from 'react'
89
import { createPortal } from 'react-dom'
910
import PropTypes from 'prop-types'
@@ -13,6 +14,7 @@ import {
1314
hasOwnProperty,
1415
ssrSafeCreateDiv,
1516
preserveRef,
17+
updateClassName,
1618
} from './utils'
1719

1820
function Tippy(props) {
@@ -33,12 +35,17 @@ function Tippy(props) {
3335
useEffect(() => {
3436
instanceRef.current = tippy(targetRef.current, options)
3537

36-
const { onCreate, isEnabled, isVisible } = props
38+
const { onCreate, isEnabled, isVisible, className } = props
3739

3840
if (onCreate) {
3941
onCreate(instanceRef.current)
4042
}
4143

44+
if (className) {
45+
const { tooltip } = instanceRef.current.popperChildren
46+
updateClassName(tooltip, 'add', props.className)
47+
}
48+
4249
if (isEnabled === false) {
4350
instanceRef.current.disable()
4451
}
@@ -55,7 +62,7 @@ function Tippy(props) {
5562
}
5663
}, [])
5764

58-
useEffect(() => {
65+
useLayoutEffect(() => {
5966
if (!isMounted) {
6067
return
6168
}
@@ -70,7 +77,6 @@ function Tippy(props) {
7077
if (isEnabled === false) {
7178
instanceRef.current.disable()
7279
}
73-
7480
if (isVisible === true) {
7581
instanceRef.current.show()
7682
}
@@ -79,6 +85,19 @@ function Tippy(props) {
7985
}
8086
})
8187

88+
useLayoutEffect(() => {
89+
if (!isMounted) {
90+
return
91+
}
92+
93+
const { tooltip } = instanceRef.current.popperChildren
94+
updateClassName(tooltip, 'add', props.className)
95+
96+
return () => {
97+
updateClassName(tooltip, 'remove', props.className)
98+
}
99+
}, [props.className])
100+
82101
return (
83102
<>
84103
{cloneElement(props.children, {
@@ -99,6 +118,7 @@ Tippy.propTypes = {
99118
onCreate: PropTypes.func,
100119
isVisible: PropTypes.bool,
101120
isEnabled: PropTypes.bool,
121+
className: PropTypes.string,
102122
}
103123

104124
Tippy.defaultProps = {

src/utils.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export function getNativeTippyProps(props) {
22
// eslint-disable-next-line no-unused-vars
3-
const { children, onCreate, isVisible, isEnabled, ...nativeProps } = props
3+
const { children, onCreate, isVisible, isEnabled, className, ...nativeProps } = props
44
return nativeProps
55
}
66

@@ -22,3 +22,11 @@ export function preserveRef(ref, node) {
2222
export function ssrSafeCreateDiv() {
2323
return typeof document !== 'undefined' && document.createElement('div')
2424
}
25+
26+
export function updateClassName(tooltip, action, classNames) {
27+
classNames.split(/\s+/).forEach(name => {
28+
if (name) {
29+
tooltip.classList[action](name)
30+
}
31+
})
32+
}

test/Tippy.test.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,29 @@ describe('<Tippy />', () => {
5353
expect(instance.popper.querySelector('strong')).not.toBeNull()
5454
})
5555

56+
test('custom class name get added to DOM', () => {
57+
const className = 'hello'
58+
const { container } = render(
59+
<Tippy content="tip content" className={className}>
60+
<button />
61+
</Tippy>,
62+
)
63+
const tip = container.querySelector('button')._tippy
64+
expect(tip.popper.querySelector(`.${className}`)).not.toBeNull()
65+
})
66+
67+
test('custom class name get added to DOM', () => {
68+
const classNames = 'hello world'
69+
const { container } = render(
70+
<Tippy content="tip content" className={classNames}>
71+
<button />
72+
</Tippy>,
73+
)
74+
const tip = container.querySelector('button')._tippy
75+
expect(tip.popper.querySelector('.hello')).not.toBeNull()
76+
expect(tip.popper.querySelector('.world')).not.toBeNull()
77+
})
78+
5679
test('unmount destroys the tippy instance and allows garbage collection', () => {
5780
const { container, unmount } = render(
5881
<Tippy content="tooltip">

0 commit comments

Comments
 (0)