[LWC] Menus are misaligned in Lightning Modal
When a Bryntum widget is rendered inside of the lightning-modal
component, the menus are shifted by several pixels to the right from the expected position. This is due to the CSS class slds-modal__container
on the modal's body element defining transform: translate(0, 0)
rule.
To replicate the issue, create three Lightning web components: bryntumGrid
, modalWithGrid
, and modalPreview
as follows.
bryntumGrid
HTML:
<template>
<div
id="b"
data-role="bryntum-host"
class="slds-grid slds-grid_vertical"
style="height: 100%"
lwc:dom="manual"
></div>
</template>
JS:
import { LightningElement } from 'lwc';
import { loadScript, loadStyle } from 'lightning/platformResourceLoader';
import RESOURCE_URL from '@salesforce/resourceUrl/bryntum';
const getBundle = () => {
/* global bryntum */
try {
return bryntum?.grid;
} catch (e) {
return undefined;
}
};
const loadBundle = async (cmp) => {
if (!getBundle()) {
await Promise.all([
loadStyle(cmp, `${RESOURCE_URL}/grid.stockholm.css`),
loadScript(cmp, `${RESOURCE_URL}/grid.lwc.module.js`),
]);
}
return getBundle();
};
export default class BryntumGrid extends LightningElement {
gridId = null;
async renderedCallback() {
this.createGrid(await loadBundle(this));
}
disconnectedCallback() {
const { gridId } = this;
if (gridId) {
const { Widget } = getBundle();
const grid = Widget.getById(gridId);
grid.destroy();
this.gridId = null;
}
}
createGrid({ Grid }) {
const { appendTo } = this;
const grid = new Grid({
appendTo,
columns: [
{
type: 'rownumber',
},
{
text: 'Name',
flex: 1,
},
],
});
this.gridId = grid.id;
}
get appendTo() {
// An element with lwc:dom="manual"
return this.template.querySelector('[data-role="bryntum-host"]');
}
}
modalWithGrid
HTML:
<template>
<lightning-modal-header
label="Grid in Modal"
></lightning-modal-header>
<lightning-modal-body>
<c-bryntum-grid
class="slds-card slds-card_boundary"
style="display: block; height: 50vh"
></c-bryntum-grid>
</lightning-modal-body>
</template>
JS:
import LightningModal from 'lightning/modal';
export default class ModalWithGrid extends LightningModal {}
modalPreview
HTML:
<template>
<lightning-button
variant="brand"
label="Show the Modal Dialog"
onclick={handleButtonClick}
></lightning-button>
</template>
JS:
import { LightningElement } from 'lwc';
import ModalWithGrid from 'c/modalWithGrid';
export default class ModalPreview extends LightningElement {
async handleButtonClick() {
await ModalWithGrid.open({
size: 'large',
});
}
}
Observed result
Add the modalPreview
component to an App page or a Tab, open it and click the button. Once the modal dialog opens and the grid loads, right-click on a column header. The menu is displayed at a distance from the mouse pointer. Now hover over an expandable menu option. The submenu will display at the same distance away from the main menu.
The fix
A simple override of the Rectangle.alignTo()
method resolves this issue:
class RectangleOverride {
static get target() {
return {
class: Rectangle,
};
}
alignTo(spec) {
const result = this._overridden.alignTo.call(this, spec);
const { source, constrainTo } = spec;
if (constrainTo === window || constrainTo === document) {
const rootEl = DomHelper.getRootElement(source.element);
const floatRoot = rootEl.querySelector('.b-float-root');
if (floatRoot) {
const { x } = Rectangle.from(floatRoot);
// Compensate for the position shift
// caused by slds-modal__container's
// CSS transform: translate(0, 0);
result.translate(-x, 0);
}
}
return result;
}
}
Override.apply(RectangleOverride);
Expected result
Follow the same steps as before to launch the modal dialog and open the header context menu.