Tooltip preventOverflow option
Ste35 opened this issue · comments
Hi,
I'd like to report a possible bug. When a tooltip is near the edge of the document (or near the edge of it's parent element with overflow:hidden
style) and there isn't the auto
value in placement
option, the tooltip overflow the visible part of the viewport resulting in being partially "cropped". According to popper docs this can be avoided using the preventOverflow
option that (if I'm not wrong) should be enabled by default, but it doesn't seem to work.
I also tried to manually add the modifiers via the popperOptions
property without any success...
It seems to work fine with only bootstrap:
<button
type="button"
class="btn btn-outline-secondary m-2"
data-bs-toggle="tooltip"
data-bs-placement="bottom"
data-bs-title="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin porttitor semper sapien ac posuere. Ut mattis hendrerit massa in laoreet"
data-bs-trigger="click"
>
Tooltip on top
</button>
<script>const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]')
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl))</script>
Is there a way to keep the placement (e.g. bottom
) and avoid the overflow?
Link to minimally-working StackBlitz that reproduces the issue:
Versions of Angular, ng-bootstrap and Bootstrap:
Angular: 17
ng-bootstrap: 16
The report issue is also happen to dropdown and popover. From the code I can see preventOverflow has been added as default.
ng-bootstrap
utilize popper lite, which popper lite doesn't include preventOverflow, where you have to import in to make it work. Which ng-bootstrap
did.
ng-bootstrap/src/util/positioning.ts
Lines 88 to 173 in b5d5feb
bootstrap
also has preventOverflow
, but they use popper instead of popper lite.
Personally, this has been struggling me for a while as well.
I have found out the issue. The following code cause preventOverflow
to not functional. The goal of the code was suppose to provide additional option and to be merged with popper's preventOverflow modifier. But it actually setup fn
.
ng-bootstrap/src/util/positioning.ts
Lines 165 to 170 in b5d5feb
If we compare the option bootstrap has added and ng-bootstrap's code.
// bootstrap
{
name: 'preventOverflow',
options: {
boundary: this._config.boundary
}
}
// ng-bootstrap
{
enabled: true,
name: 'preventOverflow',
phase: 'main',
fn: function () {},
}
If we either remove ng-bootstrap's modifier or swap with bootstrap's modifier. In both case works.
Following is popperOptions function I use to debug ng-bootstrap. The logic is to check how many preventOverflow
has been added. If more than one, remove last one. @Ste35 you can give it a try.
popperOptions(options: Partial<Options>): Partial<Options> {
const target = options.modifiers?.filter(m => m.name === 'preventOverflow');
if (!target || target.length <= 1) {
return options;
}
const last = target.at(-1);
if (options.modifiers && last) {
options.modifiers = options.modifiers.filter(m => m !== last);
}
console.log('popper options', options);
return options;
}
@maxokorokov The second preventOverflow that ng-bootstrap added, I feels not necessary. Bootstrap use it to setup boundary, where ng-bootstrap setup nothing. We can potentially remove it.