Skip to content

Commit 1795acd

Browse files
committed
Fix tooltip clipped by viewport when toolbar is near page bottom
Fixes #1150
1 parent 40e8beb commit 1795acd

4 files changed

Lines changed: 77 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
- Fix pasting table into table cell creating invalid nesting and trailing empty paragraph ([#1314](https://github.com/xdan/jodit/issues/1314))
2121
- Fix font size module misbehavior in pt mode: duplicated unit display, incorrect active state, and wrong px/pt conversion ([#1197](https://github.com/xdan/jodit/issues/1197))
2222
- Fix focus competition between multiple editor instances in Source mode ([#1313](https://github.com/xdan/jodit/issues/1313))
23+
- Fix tooltip clipped by viewport when toolbar is near the bottom of the page ([#1150](https://github.com/xdan/jodit/issues/1150))
2324

2425
## 4.11.7
2526

src/core/ui/button/tooltip/tooltip.less

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,7 @@
5353
.jodit-ui-tooltip.jodit-ui-tooltip_visible_true {
5454
opacity: 1;
5555
}
56+
57+
.jodit-ui-tooltip.jodit-ui-tooltip_above_true {
58+
transform: translate(-50%, calc(var(--padding-default) / -2));
59+
}

src/core/ui/button/tooltip/tooltip.test.js

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,65 @@
263263
});
264264
});
265265

266+
describe('Viewport overflow', () => {
267+
it('Should show tooltip above button when near viewport bottom', () => {
268+
editor.destruct();
269+
270+
// Move the box to the very bottom of the viewport
271+
box.style.position = 'fixed';
272+
box.style.bottom = '0';
273+
box.style.left = '0';
274+
box.style.right = '0';
275+
box.style.minHeight = '60px';
276+
box.style.padding = '0';
277+
278+
editor = getJodit({
279+
...OPTIONS,
280+
toolbarAdaptive: false
281+
});
282+
283+
const boldBtn = getButton('bold', editor);
284+
expect(boldBtn).is.not.null;
285+
286+
// Mock viewport height to simulate overflow
287+
const ow = editor.ownerWindow;
288+
const originalInnerHeight = Object.getOwnPropertyDescriptor(
289+
HTMLElement.prototype,
290+
'clientHeight'
291+
);
292+
const btnRect =
293+
boldBtn.parentElement.getBoundingClientRect();
294+
// Set viewport height so tooltip will overflow below it
295+
Object.defineProperty(ow, 'innerHeight', {
296+
value: Math.round(btnRect.bottom + 5),
297+
configurable: true,
298+
writable: true
299+
});
300+
301+
simulateEvent('mouseenter', boldBtn.parentElement);
302+
timers.delay(100);
303+
304+
const tooltip = getTooltipElm();
305+
expect(tooltip).is.not.null;
306+
expect(tooltip.textContent).equals('Bold');
307+
308+
const tooltipRect = tooltip.getBoundingClientRect();
309+
310+
// Tooltip should be above the button, not below
311+
expect(tooltipRect.bottom).is.below(btnRect.top + 1);
312+
313+
simulateEvent('mouseleave', boldBtn.parentElement);
314+
timers.delay(100);
315+
316+
// Restore
317+
delete ow.innerHeight;
318+
box.style.position = '';
319+
box.style.bottom = '';
320+
box.style.left = '';
321+
box.style.right = '';
322+
});
323+
});
324+
266325
describe('Inside popup', () => {
267326
describe('Hiding popup', () => {
268327
it('should hide it tooltip', () => {

src/core/ui/button/tooltip/tooltip.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,13 +190,26 @@ export class UITooltip extends UIElement {
190190

191191
private __show(getPoint: () => IPoint, content: string): void {
192192
this.setMod('visible', true);
193+
this.setMod('above', false);
193194
this.getElm('content')!.innerHTML = content;
194195
const point = getPoint();
195196

196197
css(this.container, {
197198
left: point.x,
198199
top: point.y
199200
});
201+
202+
const tooltipPos = position(this.container);
203+
const viewHeight = this.j.ow.innerHeight;
204+
205+
// If tooltip overflows below viewport, show it above the target
206+
if (tooltipPos.top + tooltipPos.height > viewHeight) {
207+
const targetPos = position(this.__currentTarget!);
208+
this.setMod('above', true);
209+
css(this.container, {
210+
top: targetPos.top - tooltipPos.height
211+
});
212+
}
200213
}
201214

202215
@autobind

0 commit comments

Comments
 (0)