Skip to content

Commit

Permalink
fix: ensure consistent right alignment for nested menus (#228)
Browse files Browse the repository at this point in the history
  • Loading branch information
web-padawan committed Mar 27, 2019
1 parent 579701d commit 17357ce
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 9 deletions.
3 changes: 3 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,8 @@
"globals": {
"Polymer": false,
"Vaadin": false
},
"parserOptions": {
"ecmaVersion": 2017
}
}
3 changes: 2 additions & 1 deletion src/vaadin-context-menu-overlay.html
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@
xMax: overlayRect.right - contentRect.width,
yMax,
left: overlayRect.left,
top: overlayRect.top
top: overlayRect.top,
width: contentRect.width
};
}
}
Expand Down
40 changes: 33 additions & 7 deletions src/vaadin-context-menu.html
Original file line number Diff line number Diff line change
Expand Up @@ -556,21 +556,47 @@
['right-aligned', 'bottom-aligned'].forEach(attr => overlay.removeAttribute(attr));

// Maximum x and y values are imposed by content size and overlay limits.
const {xMax, yMax, left, top} = overlay.getBoundaries();
const {xMax, yMax, left, top, width} = overlay.getBoundaries();
// Reuse saved x and y event values, in order to this method be used async
// in the `vaadin-overlay-change` which guarantees that overlay is ready
const x = this.__x || left;
const y = this.__y || top;

// Select one overlay corner and move to the event x/y position.
// Then set styling attrs for flex-aligning the content appropriatelly.
// Then set styling attrs for flex-aligning the content appropriately.
const wdthVport = document.documentElement.clientWidth;
const hghtVport = document.documentElement.clientHeight;
if (x < wdthVport / 2 || x < xMax) {
style.left = x + 'px';
} else {
style.right = Math.max(0, (wdthVport - x)) + 'px';
overlay.setAttribute('right-aligned', '');

// Align to the parent menu overlay, if any.
const parent = overlay.parentOverlay;
let alignedToParent = false;
if (parent) {
const parentContentRect = parent.$.overlay.getBoundingClientRect();
if (parent.hasAttribute('right-aligned')) {
const parentStyle = getComputedStyle(parent);

const getPadding = (el, direction) => {
return parseFloat(getComputedStyle(el.$.content)['padding' + direction]);
};
const right = parseFloat(parentStyle.right) + parentContentRect.width;
const padding = getPadding(parent, 'Left') + getPadding(overlay, 'Right');

// Preserve right-aligned, if possible.
if ((wdthVport - (right - padding)) > width) {
overlay.setAttribute('right-aligned', '');
style.right = right - padding + 'px';
alignedToParent = true;
}
}
}

if (!alignedToParent) {
if (x < wdthVport / 2 || x < xMax) {
style.left = x + 'px';
} else {
style.right = Math.max(0, (wdthVport - x)) + 'px';
overlay.setAttribute('right-aligned', '');
}
}
if (y < hghtVport / 2 || y < yMax) {
style.top = y + 'px';
Expand Down
61 changes: 60 additions & 1 deletion test/items.html
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@
return menu.$.overlay.content.querySelector('vaadin-context-menu');
};

const nextRender = el => new Promise(resolve => {
Polymer.RenderStatus.afterNextRender(el, () => resolve());
});

afterEach(() => rootMenu.close());

describe('default', () => {
Expand All @@ -56,7 +60,10 @@
{text: 'foo-0', children:
[
{text: 'foo-0-0', checked: true},
{text: 'foo-0-1', disabled: true}
{text: 'foo-0-1', disabled: true},
{text: 'foo-0-2', children: [
{text: 'foo-0-2-0'}
]},
]
},
{text: 'foo-1'}
Expand Down Expand Up @@ -128,6 +135,58 @@
done();
});
});

it('should open the subMenu on the left if root menu is right-aligned', async() => {
subMenu.close();
await nextRender(subMenu);
const rootItem = menuComponents()[0];
const rootItemRect = rootItem.getBoundingClientRect();
const rootOverlay = rootMenu.$.overlay;
rootOverlay.style.removeProperty('left');
rootOverlay.style.right = rootItemRect.width + 'px';
rootOverlay.setAttribute('right-aligned', '');
const padding = parseFloat(getComputedStyle(rootOverlay.$.content).paddingLeft) * 2;
open(rootItem);
await nextRender(subMenu);
expect(subMenu.$.overlay.hasAttribute('right-aligned')).to.be.true;
const rootMenuRect = rootOverlay.$.content.getBoundingClientRect();
const subMenuRect = subMenu.$.overlay.$.content.getBoundingClientRect();
expect(subMenuRect.right).to.be.closeTo(rootMenuRect.left + padding, 1);
});

it('should open the second subMenu on the right again if not enough space', async() => {
let padding;
subMenu.close();
await nextRender(subMenu);
rootMenu.items[0].children[2].text = 'foo-0-2-longer';

const rootItem = menuComponents()[0];
const rootItemRect = rootItem.getBoundingClientRect();
const rootOverlay = rootMenu.$.overlay;
rootOverlay.style.removeProperty('left');
rootOverlay.style.right = rootItemRect.width + 'px';
rootOverlay.setAttribute('right-aligned', '');
padding = parseFloat(getComputedStyle(rootOverlay.$.content).paddingLeft) * 2;

/* first sub-menu right-aligned */
open(rootItem);
await nextRender(subMenu);
expect(subMenu.$.overlay.hasAttribute('right-aligned')).to.be.true;
const rootMenuRect = rootOverlay.$.content.getBoundingClientRect();
const subMenuRect = subMenu.$.overlay.$.content.getBoundingClientRect();
expect(subMenuRect.right).to.be.closeTo(rootMenuRect.left + padding, 1);

/* second sub-menu left-aligned */
const nestedItem = menuComponents(subMenu)[2];
const nestedItemRect = nestedItem.getBoundingClientRect();
padding = parseFloat(getComputedStyle(subMenu.$.overlay.$.content).paddingLeft) * 2;
open(nestedItem);
await nextRender(subMenu);
const subMenu2 = getSubMenu(subMenu);
expect(subMenu2.$.overlay.hasAttribute('right-aligned')).to.be.false;
const subMenu2Rect = subMenu2.$.overlay.$.content.getBoundingClientRect();
expect(subMenu2Rect.left).to.be.closeTo(nestedItemRect.right, 1);
});
}

it('should clean up the old content on reopen', () => {
Expand Down

0 comments on commit 17357ce

Please sign in to comment.