fix(svelte): Fix tooltip in hovercards (#64443)

Fixes srch-744

It seems that the code for testing whether the target element is part of
the layout didn't work in hovercards because it (possibly?) runs before
the hovercard is rendered.
Moving the logic to `onMount` + `await tick()` seem to work, although
that might still be a coincidence.

## Test plan

Hovering over the 'precise' badge shows the corresponding tooltip.
This commit is contained in:
Felix Kling 2024-08-13 15:39:35 +02:00 committed by GitHub
parent df91f98feb
commit 3e734549c7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 35 additions and 10 deletions

View File

@ -5,7 +5,9 @@
</script>
<script lang="ts">
import { popover, portal, uniqueID } from './dom'
import { onMount, tick } from 'svelte'
import { type PopoverOptions, popover, portal, uniqueID } from './dom'
/**
* The content of the tooltip.
@ -41,8 +43,24 @@
shift: {
padding: 4,
},
onSize(element, { availableWidth, availableHeight }) {
Object.assign(element.style, {
maxWidth: `min(var(--tooltip-max-width), ${availableWidth}px)`,
maxHeight: `${availableHeight}px`,
})
},
} satisfies PopoverOptions
$: if (target && tooltip) {
target.setAttribute('aria-label', tooltip)
}
$: {
onMount(async () => {
// We need to wait for the element to be rendered before we can check whether it
// is part of the layout.
// (this fixes and issue where the tooltip would not show up in hovercards)
await tick()
let node = wrapper?.firstElementChild
// Use `getClientRects` to check if the element is part of the layout.
// For example, an element with `display: contents` will not be part of the layout.
@ -54,10 +72,7 @@
if (node) {
target = node
}
}
$: if (target && tooltip) {
target.setAttribute('aria-label', tooltip)
}
})
</script>
<!-- TODO: close tooltip on escape -->

View File

@ -126,7 +126,7 @@ export const onClickOutside: Action<
}
}
interface PopoverOptions {
export interface PopoverOptions {
/**
* The placement of the popover relative to the reference element.
*/
@ -146,6 +146,11 @@ interface PopoverOptions {
* The middleware is always enabled.
*/
flip?: FlipOptions
/**
* A callback to set the available width of the popover. The default behavior is
* to set the elements `maxWidth` and `maxHeight` style properties.
*/
onSize?: (element: HTMLElement, size: { availableWidth: number; availableHeight: number }) => void
}
/**
@ -166,10 +171,15 @@ export const popover: Action<HTMLElement, { reference: Element; options: Popover
shift(options.shift),
flip(options.flip),
size({
apply({ availableWidth, availableHeight }) {
apply(dimensions) {
if (options.onSize) {
options.onSize(popover, dimensions)
return
}
Object.assign(popover.style, {
maxWidth: `${availableWidth}px`,
maxHeight: `${availableHeight}px`,
maxWidth: `${dimensions.availableWidth}px`,
maxHeight: `${dimensions.availableHeight}px`,
})
},
})