diff --git a/panel/models/tooltip_icon.ts b/panel/models/tooltip_icon.ts index 664beb5315..db639cd6af 100644 --- a/panel/models/tooltip_icon.ts +++ b/panel/models/tooltip_icon.ts @@ -1,8 +1,7 @@ -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' @@ -10,30 +9,21 @@ 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 { - 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[] { @@ -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 { @@ -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 - export type Props = Control.Props & { + export type Props = LayoutDOM.Props & { description: p.Property } } 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' diff --git a/panel/tests/ui/widgets/test_indicators.py b/panel/tests/ui/widgets/test_indicators.py index 995b30b76a..49e1a49ef4 100644 --- a/panel/tests/ui/widgets/test_indicators.py +++ b/panel/tests/ui/widgets/test_indicators.py @@ -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 @@ -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") diff --git a/panel/widgets/indicators.py b/panel/widgets/indicators.py index 306fa54e9d..5d795d09cf 100644 --- a/panel/widgets/indicators.py +++ b/panel/widgets/indicators.py @@ -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",