-
Notifications
You must be signed in to change notification settings - Fork 2.2k
feat(widgets): custom tooltip system for widget buttons #10435
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
4a13cc3
83e103a
1b184e8
334933b
f6d5f88
63a8a4e
b1828c0
752d0d5
ba2eb05
1318337
f879189
c09b97c
ed96783
8c3a379
3f0e274
6011051
4c165b4
88f2ad9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,138 @@ | ||
| # Widget Tooltips | ||
|
|
||
| <img src="https://img.shields.io/badge/from-v9.5-green.svg?style=flat-square" alt="from v9.5" /> | ||
|
|
||
| Built-in button widgets ship with styled tooltips that appear on hover and keyboard focus. These replace the slow native browser title tooltips with themed, customizable alternatives. | ||
|
|
||
| ## Customizing Built-in Tooltips | ||
|
|
||
| ### `label` vs `tooltip` | ||
|
|
||
| The `label` prop sets the button's accessible name (`aria-label`) and is always a plain string. By default, the tooltip displays the label text. The `tooltip` prop overrides only the visual tooltip — use it for rich HTML content, a different display string, or `false` to hide the tooltip while preserving accessibility. | ||
|
|
||
| ### Overriding tooltip text | ||
|
|
||
| Each button widget accepts a tooltip prop that overrides the default label: | ||
|
|
||
| ```ts | ||
| new ZoomWidget({ zoomInTooltip: 'Zoom In (Ctrl+Plus)' }) | ||
| new FullscreenWidget({ enterTooltip: 'Go Fullscreen (F)' }) | ||
| ``` | ||
|
|
||
| ### Overriding with custom HTML | ||
|
|
||
| For rich content (e.g. keyboard shortcut badges), pass an `HTMLElement`: | ||
|
|
||
| ```ts | ||
| const tip = document.createElement('span'); | ||
| tip.innerHTML = 'Zoom In <kbd>⌘+</kbd>'; | ||
| new ZoomWidget({ zoomInTooltip: tip }) | ||
| ``` | ||
|
|
||
| ### Disabling tooltips | ||
|
|
||
| Pass `false` to suppress the tooltip for a specific button: | ||
|
|
||
| ```ts | ||
| new ZoomWidget({ zoomInTooltip: false }) | ||
| new CompassWidget({ tooltip: false }) | ||
| ``` | ||
|
|
||
| ### Styling tooltips | ||
|
|
||
| Tooltip appearance inherits the widget theme via CSS variables. You can customize them globally or per-widget: | ||
|
|
||
| ```css | ||
| .deck-widget { | ||
| --tooltip-max-width: 300px; | ||
| --tooltip-z-index: 2000; | ||
| } | ||
| ``` | ||
|
|
||
| | Name | Type | Default | | ||
| | ---- | ---- | ------- | | ||
| | `--tooltip-max-width` | [Dimension](https://developer.mozilla.org/en-US/docs/Web/CSS/dimension) | `240px` | | ||
| | `--tooltip-z-index` | Number | `1000` | | ||
|
|
||
| Tooltips also inherit the following [menu variables](./styling.md#menu): `--menu-background`, `--menu-shadow`, `--menu-backdrop-filter`, `--menu-text`. | ||
|
|
||
| ## Writing Tooltips in Custom Widgets | ||
|
|
||
| ### Using Preact (with \_Tooltip component) | ||
|
|
||
| If your custom widget uses Preact for rendering, import the `_Tooltip` component: | ||
|
|
||
| ```tsx | ||
| import {_Tooltip as Tooltip} from '@deck.gl/widgets'; | ||
| import {render} from 'preact'; | ||
|
|
||
| class MyWidget extends Widget { | ||
| className = 'my-widget'; | ||
| placement = 'top-left'; | ||
|
|
||
| onRenderHTML(rootElement) { | ||
| render( | ||
| <Tooltip content="My tooltip text"> | ||
| <button aria-label="My Action" onClick={...}> | ||
| Do thing | ||
| </button> | ||
| </Tooltip>, | ||
| rootElement | ||
| ); | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| #### TooltipProps | ||
|
|
||
| | Prop | Type | Default | Description | | ||
| | ---- | ---- | ------- | ----------- | | ||
| | `content` | `string \| ComponentChildren` | — | Tooltip content to display | | ||
| | `placement` | `Placement` | `'right'` | Position relative to the trigger (uses [@floating-ui/dom](https://floating-ui.com/docs/computePosition#placement) placement values) | | ||
| | `children` | `ComponentChildren` | — | The trigger element | | ||
|
|
||
| ### Without Preact (CSS class + your own logic) | ||
|
|
||
| For custom widgets using vanilla JS, React, or any other framework, implement your own show/hide behavior and apply the `.deck-widget-tooltip` CSS class to get consistent theming: | ||
|
|
||
| ```ts | ||
| class MyWidget extends Widget { | ||
| className = 'my-widget'; | ||
| placement = 'top-left'; | ||
|
|
||
| onRenderHTML(rootElement) { | ||
| const btn = document.createElement('button'); | ||
| btn.setAttribute('aria-label', 'My Action'); | ||
| btn.setAttribute('aria-describedby', 'my-tooltip'); | ||
|
|
||
| const tooltip = document.createElement('div'); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a nit: This example is rather confusing to me and makes the tooltip system read as more complex than it is. My knee jerk reaction, is why would we suggest users to do all this? It makes it seems very complicated. For the folks who don't want to import preact and configure JSX in their project for just one custom widget, can't we just export the a plain JS function that renders the internal Preact into an HTML element and this also handles the positioning so we don't have to ask the user to do all this and install the external library etc. Separately, since we clearly care about Accessibility, for me it would make more sense to have a section that describes Accessibility first (perhaps in every widget page). Then the example we are giving here would seem less random, as it is clearly implementing the explicit accessibility spec above, rather than forcing the user to try to understand what those HTML element stanzas are for - are they standard DOM fields or some deck convention etc.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Idea: since we clearly care about Accessibility, for me it would make more sense to have a section that describes Accessibility support/considerations first (perhaps even in every widget page). There are clearly a number of things that need to be done, setting roles and aria-... attributes etc. and not every developer is up to date on that. Then the non-Preact example we are giving would seem less random to, as it would clearly be implementing the explicit accessibility spec above, rather than forcing the user to try to understand what those HTML element stanzas are for - are they standard DOM fields or some deck.gl/widgets convention etc. |
||
| tooltip.id = 'my-tooltip'; | ||
| tooltip.className = 'deck-widget-tooltip'; | ||
| tooltip.setAttribute('role', 'tooltip'); | ||
| tooltip.textContent = 'My Action'; | ||
| tooltip.hidden = true; | ||
|
|
||
| btn.addEventListener('pointerenter', () => { tooltip.hidden = false; }); | ||
| btn.addEventListener('pointerleave', () => { tooltip.hidden = true; }); | ||
|
|
||
| rootElement.replaceChildren(btn, tooltip); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Replacing all children seems a bit heavy handed? |
||
| } | ||
| } | ||
| ``` | ||
|
|
||
| The `.deck-widget-tooltip` class provides themed background, shadow, text color, font, border-radius, and max-width — matching the rest of the widget UI. You are responsible for: | ||
|
|
||
| - Positioning (consider [@floating-ui/dom](https://floating-ui.com/docs/getting-started) or CSS anchor positioning) | ||
| - Show/hide behavior (pointer events, focus, keyboard dismiss) | ||
|
|
||
| ## Accessibility | ||
|
|
||
| Built-in widget tooltips follow these accessibility practices: | ||
|
|
||
| - Buttons use `aria-label` for screen reader announcements | ||
| - Tooltip elements have `role="tooltip"` | ||
| - Tooltips appear on keyboard focus | ||
| - Pressing `Escape` dismisses the tooltip | ||
|
|
||
| Custom widget authors should follow the same patterns. | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.