Skip to content
This repository was archived by the owner on Nov 9, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
},
"extends": ["eslint:recommended", "plugin:react/recommended", "prettier"],
"rules": {
"react/no-find-dom-node": "off",
"react/prop-types": "off"
"react/prop-types": "off",
"no-unused-vars": ["error", { "ignoreRestSiblings": true }]
}
}
15 changes: 7 additions & 8 deletions demo/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,19 +117,19 @@ class ComponentChild extends React.Component {
class App extends React.Component {
state = {
arrow: false,
customClass: ''
customClass: 'hello',
}

toggleArrow = () => {
this.setState(state => ({
arrow: !state.arrow
arrow: !state.arrow,
}))
}

updateCustomClass = (e) => {
this.setState({
customClass: e.target.value
})
componentDidMount() {
setTimeout(() => {
this.setState({ customClass: 'bye' })
}, 500)
}

render() {
Expand Down Expand Up @@ -174,8 +174,7 @@ class App extends React.Component {
</Tippy>

<h1>Other</h1>
<input type="text" placeholder="Enter class" onChange={this.updateCustomClass}/>
<Tippy placement="bottom" className={this.state.customClass}>
<Tippy trigger="click" className={this.state.customClass}>
<button>Custom class</button>
</Tippy>
</main>
Expand Down
25 changes: 8 additions & 17 deletions src/Tippy.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import React, {
cloneElement,
useState,
useRef,
useEffect,
useLayoutEffect,
} from 'react'
import { createPortal } from 'react-dom'
Expand Down Expand Up @@ -32,20 +31,15 @@ function Tippy(props) {
options.trigger = 'manual'
}

useEffect(() => {
useLayoutEffect(() => {
instanceRef.current = tippy(targetRef.current, options)

const { onCreate, isEnabled, isVisible, className } = props
const { onCreate, isEnabled, isVisible } = props

if (onCreate) {
onCreate(instanceRef.current)
}

if (className) {
const { tooltip } = instanceRef.current.popperChildren
updateClassName(tooltip, 'add', props.className)
}

if (isEnabled === false) {
instanceRef.current.disable()
}
Expand Down Expand Up @@ -86,15 +80,12 @@ function Tippy(props) {
})

useLayoutEffect(() => {
if (!isMounted) {
return
}

const { tooltip } = instanceRef.current.popperChildren
updateClassName(tooltip, 'add', props.className)

return () => {
updateClassName(tooltip, 'remove', props.className)
if (props.className) {
const { tooltip } = instanceRef.current.popperChildren
updateClassName(tooltip, 'add', props.className)
return () => {
updateClassName(tooltip, 'remove', props.className)
}
}
}, [props.className])

Expand Down
10 changes: 8 additions & 2 deletions src/utils.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
export function getNativeTippyProps(props) {
// eslint-disable-next-line no-unused-vars
const { children, onCreate, isVisible, isEnabled, className, ...nativeProps } = props
const {
children,
onCreate,
isVisible,
isEnabled,
className,
...nativeProps
} = props
return nativeProps
}

Expand Down
46 changes: 37 additions & 9 deletions test/Tippy.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,27 +52,55 @@ describe('<Tippy />', () => {
expect(instance.popper.querySelector('strong')).not.toBeNull()
})

test('custom class name get added to DOM', () => {
test('props.className: single name is added to tooltip', () => {
const className = 'hello'
const { container } = render(
<Tippy content="tip content" className={className}>
<Tippy content="tooltip" className={className}>
<button />
</Tippy>,
)
const tip = container.querySelector('button')._tippy
expect(tip.popper.querySelector(`.${className}`)).not.toBeNull()
const instance = container.querySelector('button')._tippy
expect(instance.popper.querySelector(`.${className}`)).not.toBeNull()
})

test('custom class name get added to DOM', () => {
test('props.className: multiple names are added to tooltip', () => {
const classNames = 'hello world'
const { container } = render(
<Tippy content="tip content" className={classNames}>
<Tippy content="tooltip" className={classNames}>
<button />
</Tippy>,
)
const instance = container.querySelector('button')._tippy
expect(instance.popper.querySelector('.hello')).not.toBeNull()
expect(instance.popper.querySelector('.world')).not.toBeNull()
})

test('props.className: extra whitespace is ignored', () => {
const className = ' hello world '
const { container } = render(
<Tippy content="tooltip" className={className}>
<button />
</Tippy>,
)
const { tooltip } = container.querySelector('button')._tippy.popperChildren
expect(tooltip.className).toBe('tippy-tooltip dark-theme hello world')
})

test('props.className: updating does not leave stale className behind', () => {
const { container, rerender } = render(
<Tippy content="tooltip" className="one">
<button />
</Tippy>,
)
const { tooltip } = container.querySelector('button')._tippy.popperChildren
expect(tooltip.classList.contains('one')).toBe(true)
rerender(
<Tippy content="tooltip" className="two">
<button />
</Tippy>,
)
const tip = container.querySelector('button')._tippy
expect(tip.popper.querySelector('.hello')).not.toBeNull()
expect(tip.popper.querySelector('.world')).not.toBeNull()
expect(tooltip.classList.contains('one')).toBe(false)
expect(tooltip.classList.contains('two')).toBe(true)
})

test('unmount destroys the tippy instance and allows garbage collection', () => {
Expand Down