Skip to content

Commit

Permalink
Ensure TooltipIcon description can be updated (#6099)
Browse files Browse the repository at this point in the history
  • Loading branch information
philippjfr committed Dec 20, 2023
1 parent 1fe663e commit 1f4275e
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 39 deletions.
58 changes: 23 additions & 35 deletions panel/models/tooltip_icon.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,29 @@
import { Control, ControlView } from '@bokehjs/models/widgets/control'
import { Tooltip, TooltipView } from '@bokehjs/models/ui/tooltip'
import { Tooltip } from '@bokehjs/models/ui/tooltip'
import { UIElement } from "@bokehjs/models/ui/ui_element"
import { LayoutDOM, LayoutDOMView } from "@bokehjs/models/layouts/layout_dom"

import type {IterViews} from '@bokehjs/core/build_views'
import {build_view} from '@bokehjs/core/build_views'
import type {StyleSheetLike} from '@bokehjs/core/dom'
import {div, label} from '@bokehjs/core/dom'
import * as p from '@bokehjs/core/properties'

import inputs_css, * as inputs from '@bokehjs/styles/widgets/inputs.css'
import icons_css from '@bokehjs/styles/icons.css'

export class TooltipIconView extends ControlView {
export class TooltipIconView extends LayoutDOMView {
declare model: TooltipIcon

protected description: TooltipView

protected desc_el: HTMLElement

public *controls() {}

override *children(): IterViews {
yield* super.children()
yield this.description
}

override async lazy_initialize(): Promise<void> {
await super.lazy_initialize()

const { description } = this.model
this.description = await build_view(description, { parent: this })
get child_models(): UIElement[] {
if (this.model.description == null)
return []
return [this.model.description]
}

override remove(): void {
this.description?.remove()
super.remove()
override connect_signals(): void {
super.connect_signals()
const {description} = this.model.properties
this.on_change(description, () => this.update_children())
}

override stylesheets(): StyleSheetLike[] {
Expand All @@ -46,37 +36,37 @@ export class TooltipIconView extends ControlView {
const icon_el = div({ class: inputs.icon })
this.desc_el = div({ class: inputs.description }, icon_el)

const { desc_el, description } = this
description.model.target = desc_el
this.model.description.target = this.desc_el

let persistent = false

const toggle = (visible: boolean) => {
description.model.setv({
this.model.description.setv({
visible,
closable: persistent,
})
icon_el.classList.toggle(inputs.opaque, visible && persistent)
}

this.on_change(description.model.properties.visible, () => {
const { visible } = description.model
this.on_change(this.model.description.properties.visible, () => {
const { visible } = this.model.description
if (!visible) {
persistent = false
}
toggle(visible)
})
desc_el.addEventListener('mouseenter', () => {
this.desc_el.addEventListener('mouseenter', () => {
toggle(true)
})
desc_el.addEventListener('mouseleave', () => {
this.desc_el.addEventListener('mouseleave', () => {
if (!persistent) toggle(false)
})
document.addEventListener('mousedown', (event) => {
const path = event.composedPath()
if (path.includes(description.el)) {
const tooltip_view = this._child_views.get(this.model.description)
if (tooltip_view !== undefined && path.includes(tooltip_view.el)) {
return
} else if (path.includes(desc_el)) {
} else if (path.includes(this.desc_el)) {
persistent = !persistent
toggle(persistent)
} else {
Expand All @@ -92,21 +82,19 @@ export class TooltipIconView extends ControlView {
// Label to get highlight when icon is hovered
this.shadow_el.appendChild(label(this.desc_el))
}

change_input(): void {}
}

export namespace TooltipIcon {
export type Attrs = p.AttrsOf<Props>

export type Props = Control.Props & {
export type Props = LayoutDOM.Props & {
description: p.Property<Tooltip>
}
}

export interface TooltipIcon extends TooltipIcon.Attrs {}

export class TooltipIcon extends Control {
export class TooltipIcon extends LayoutDOM {
declare properties: TooltipIcon.Props
declare __view_type__: TooltipIconView
static __module__ = 'panel.models.widgets'
Expand Down
33 changes: 32 additions & 1 deletion panel/tests/ui/widgets/test_indicators.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from bokeh.models import Tooltip
from playwright.sync_api import expect

from panel.tests.util import serve_component
from panel.tests.util import serve_component, wait_until
from panel.widgets import TooltipIcon

pytestmark = pytest.mark.ui
Expand Down Expand Up @@ -49,3 +49,34 @@ def test_plaintext_tooltip(page, value):
page.click("body")
tooltip = page.locator(".bk-tooltip-content")
expect(tooltip).to_have_count(0)


def test_tooltip_text_updates(page):
tooltip_icon = TooltipIcon(value="Test")

serve_component(page, tooltip_icon)

icon = page.locator(".bk-icon")
expect(icon).to_have_count(1)
tooltip = page.locator(".bk-tooltip-content")
expect(tooltip).to_have_count(0)

# Hovering over the icon should show the tooltip
page.hover(".bk-icon")
tooltip = page.locator(".bk-tooltip-content")
expect(tooltip).to_have_count(1)
expect(tooltip).to_have_text("Test")

tooltip_icon.value = "Updated"

def hover():
page.hover(".bk-icon")
visible = page.locator(".bk-tooltip-content").count() == 1
page.hover("body")
return visible
wait_until(hover, page)

page.hover(".bk-icon")
tooltip = page.locator(".bk-tooltip-content")
expect(tooltip).to_have_count(1)
expect(tooltip).to_have_text("Updated")
7 changes: 4 additions & 3 deletions panel/widgets/indicators.py
Original file line number Diff line number Diff line change
Expand Up @@ -1354,18 +1354,19 @@ class TooltipIcon(Widget):
>>> pn.widgets.TooltipIcon(value="This is a simple tooltip by using a string")
"""

value = param.ClassSelector(default="Description", class_=(str, Tooltip), doc="""
The description in the tooltip.""")

align = Align(default='center', doc="""
Whether the object should be aligned with the start, end or
center of its container. If set as a tuple it will declare
(vertical, horizontal) alignment.""")

_widget_type = _BkTooltipIcon
value = param.ClassSelector(default="Description", class_=(str, Tooltip), doc="""
The description in the tooltip.""")

_rename: ClassVar[Mapping[str, str | None]] = {'name': None, 'value': 'description'}

_widget_type = _BkTooltipIcon


__all__ = [
"BooleanIndicator",
Expand Down

0 comments on commit 1f4275e

Please sign in to comment.