Skip to content

Commit

Permalink
feat: theme property for individual menu items
Browse files Browse the repository at this point in the history
Backported changes for Vaadin 14 from 
vaadin/web-components#2401. Part of 
vaadin/flow-components#880.
  • Loading branch information
tltv committed Oct 27, 2021
1 parent 34654f4 commit 1ea1973
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 3 deletions.
2 changes: 1 addition & 1 deletion src/vaadin-context-menu.html
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
*
* ```javascript
* contextMenu.items = [
* {text: 'Menu Item 1', children:
* {text: 'Menu Item 1', theme: 'primary', children:
* [
* {text: 'Menu Item 1-1', checked: true},
* {text: 'Menu Item 1-2'}
Expand Down
22 changes: 20 additions & 2 deletions src/vaadin-contextmenu-items-mixin.html
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@
* Either a tagName or an element instance. Defaults to "vaadin-context-menu-item".
* @property {boolean} disabled - If true, the item is disabled and cannot be selected
* @property {boolean} checked - If true, the item shows a checkmark next to it
* @property {union: string | string[]} theme - If set, sets the given theme(s) as an
* attribute to the menu item component, overriding any theme set on the context menu.
* @property {MenuItem[]} children - Array of child menu items
*/

Expand All @@ -67,7 +69,7 @@
*
* ```javascript
* contextMenu.items = [
* {text: 'Menu Item 1', children:
* {text: 'Menu Item 1', theme: 'primary', children:
* [
* {text: 'Menu Item 1-1', checked: true},
* {text: 'Menu Item 1-2'}
Expand Down Expand Up @@ -227,7 +229,7 @@
} else if (component.localName === 'hr') {
component.setAttribute('role', 'separator');
}
this.theme && component.setAttribute('theme', this.theme);
this._setMenuItemTheme(component, item, this.theme);

component._item = item;

Expand All @@ -251,6 +253,22 @@
});
}

/** @protected */
_setMenuItemTheme(component, item, hostTheme) {
let theme = hostTheme;

// item theme takes precedence over host theme even if it's empty, as long as it's not undefined or null
if (item.theme != null) {
theme = Array.isArray(item.theme) ? item.theme.join(' ') : item.theme;
}

if (theme) {
component.setAttribute('theme', theme);
} else {
component.removeAttribute('theme');
}
}

/** @private */
__toggleMenuComponentAttribute(component, attribute, on) {
if (on) {
Expand Down
69 changes: 69 additions & 0 deletions test/items.html
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,19 @@
return menu.$.overlay.content.querySelector('vaadin-context-menu');
};

const updateItemsAndReopen = async() => {
rootMenu.items = [...rootMenu.items];
rootMenu.close();
open();
await nextRender(rootMenu);
};

const getMenuItems = (menu = rootMenu) => {
const overlay = menu.$.overlay;
const listBox = overlay.querySelector('vaadin-context-menu-list-box');
return Array.from(listBox.querySelectorAll('vaadin-context-menu-item'));
};

const nextRender = el => new Promise(resolve => {
Polymer.RenderStatus.afterNextRender(el, () => resolve());
});
Expand Down Expand Up @@ -598,6 +611,62 @@
expect(itemsDoNotHaveTheme).to.be.true;
});
});

it('should override the component theme with the item theme', async() => {
rootMenu.items[1].theme = 'bar-1';
rootMenu.items[0].children[0].theme = 'bar-0-0';
await updateItemsAndReopen();

open(menuComponents()[0]);
subMenu = getSubMenu();
await nextRender(rootMenu);

const rootItems = getMenuItems();
const subItems = getMenuItems(subMenu);

expect(rootItems[0].getAttribute('theme')).to.equal('foo');
expect(rootItems[1].getAttribute('theme')).to.equal('bar-1');

expect(subItems[0].getAttribute('theme')).to.equal('bar-0-0');
expect(subItems[1].getAttribute('theme')).to.equal('foo');
});

it('should use the component theme if the item theme is removed', async() => {
rootMenu.items[1].theme = 'bar-1';
await updateItemsAndReopen();

let rootItems = getMenuItems();
expect(rootItems[1].getAttribute('theme')).to.equal('bar-1');

// An empty array should also override the component theme
rootMenu.items[1].theme = [];
await updateItemsAndReopen();

rootItems = getMenuItems();
expect(rootItems[1].hasAttribute('theme')).to.be.false;

// An empty string should also override the component theme
rootMenu.items[1].theme = '';
await updateItemsAndReopen();

rootItems = getMenuItems();
expect(rootItems[1].hasAttribute('theme')).to.be.false;

// If null or undefined, the parent component theme should be used
delete rootMenu.items[1].theme;
await updateItemsAndReopen();

rootItems = getMenuItems();
expect(rootItems[1].getAttribute('theme')).to.equal('foo');
});

it('should support multiple item themes in an array', async() => {
rootMenu.items[1].theme = ['bar-1', 'bar-2', 'bar-3'];
await updateItemsAndReopen();

const rootItems = getMenuItems();
expect(rootItems[1].getAttribute('theme')).to.equal('bar-1 bar-2 bar-3');
});
});
});
</script>
Expand Down

0 comments on commit 1ea1973

Please sign in to comment.