Skip to content

Commit cd1a1b5

Browse files
FEC-1010: add positionFixed prop and resize-based reflow to prevent viewport clipping (#3260)
* fix(tooltip): prevent image clipping in tooltip * fix(tooltip): changeset two * fix(tooltip): add resizeobserver stub
1 parent 92ac7de commit cd1a1b5

3 files changed

Lines changed: 32 additions & 0 deletions

File tree

.changeset/tiny-bananas-beam.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@commercetools-uikit/tooltip': patch
3+
---
4+
5+
Prevents image clipping when Tooltip component is hovered.

packages/components/tooltip/src/tooltip.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,12 @@ export type TTooltipProps = {
106106
* Provides a way to fine-tune an appearance of underlying Popper tooltip element. For more information, please check [Popper.js documentation](https://popper.js.org/popper-documentation.html#modifiers).
107107
*/
108108
modifiers?: Modifiers;
109+
/**
110+
* Use CSS `position: fixed` for the popper element instead of `position: absolute`.
111+
* Recommended when the tooltip body is rendered in a portal (e.g. via `TooltipWrapperComponent`)
112+
* to prevent clipping caused by scrolled ancestors.
113+
*/
114+
positionFixed?: boolean;
109115
/**
110116
* Customize the appearance of certain elements of the tooltip.
111117
*/
@@ -185,6 +191,7 @@ const Tooltip = ({
185191
const { reference, popper, popperInstance } = usePopper({
186192
placement: placement,
187193
modifiers: props.modifiers,
194+
positionFixed: props.positionFixed,
188195
});
189196
const [state, setState] = useState<TTooltipState>('closed');
190197

@@ -373,6 +380,21 @@ const Tooltip = ({
373380
};
374381
}, [state, off, isControlled, popperInstance, handleLeave]);
375382

383+
// Recompute position when the tooltip body resizes (e.g. an image that loads
384+
// after the initial position was calculated from a 0-height container).
385+
useEffect(() => {
386+
if (!tooltipIsOpen) return;
387+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
388+
const inst = popperInstance as any;
389+
const popperEl = inst?.popper as HTMLElement | null;
390+
if (!popperEl) return;
391+
const ro = new ResizeObserver(() => {
392+
inst.scheduleUpdate?.();
393+
});
394+
ro.observe(popperEl);
395+
return () => ro.disconnect();
396+
}, [tooltipIsOpen, popperInstance]);
397+
376398
const childrenProps = {
377399
// don't pass event listeners to children
378400
onFocus: null,

test/setup-tests.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ Object.defineProperty(window, 'TextDecoder', {
2929
writable: true,
3030
value: TextDecoder,
3131
});
32+
global.ResizeObserver = class ResizeObserver {
33+
observe() {}
34+
unobserve() {}
35+
disconnect() {}
36+
};
3237

3338
const silenceConsoleWarnings = [];
3439
const notThrowWarnings = [

0 commit comments

Comments
 (0)