Skip to content

Commit

Permalink
refactor!: move avatar-group list-box to light DOM (#3905)
Browse files Browse the repository at this point in the history
  • Loading branch information
web-padawan committed Oct 14, 2022
1 parent d281ec3 commit 8439b22
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 104 deletions.
20 changes: 0 additions & 20 deletions packages/avatar-group/src/vaadin-avatar-group-list-box.js

This file was deleted.

1 change: 0 additions & 1 deletion packages/avatar-group/src/vaadin-avatar-group.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ export interface AvatarGroupItem {
* In addition to `<vaadin-avatar-group>` itself, the following internal
* components are themable:
*
* - `<vaadin-avatar-group-list-box>` - has the same API as [`<vaadin-list-box>`](#/elements/vaadin-list-box).
* - `<vaadin-avatar-group-overlay>` - has the same API as [`<vaadin-overlay>`](#/elements/vaadin-overlay).
*/
declare class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(HTMLElement))) {
Expand Down
103 changes: 76 additions & 27 deletions packages/avatar-group/src/vaadin-avatar-group.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import '@polymer/polymer/lib/elements/dom-repeat.js';
import '@vaadin/avatar/src/vaadin-avatar.js';
import '@vaadin/item/src/vaadin-item.js';
import './vaadin-avatar-group-list-box.js';
import '@vaadin/list-box/src/vaadin-list-box.js';
import './vaadin-avatar-group-overlay.js';
import { calculateSplices } from '@polymer/polymer/lib/utils/array-splice.js';
import { afterNextRender } from '@polymer/polymer/lib/utils/render-status.js';
Expand Down Expand Up @@ -52,7 +52,6 @@ const MINIMUM_DISPLAYED_AVATARS = 2;
* In addition to `<vaadin-avatar-group>` itself, the following internal
* components are themable:
*
* - `<vaadin-avatar-group-list-box>` - has the same API as [`<vaadin-list-box>`](#/elements/vaadin-list-box).
* - `<vaadin-avatar-group-overlay>` - has the same API as [`<vaadin-overlay>`](#/elements/vaadin-overlay).
*
* @extends HTMLElement
Expand Down Expand Up @@ -140,28 +139,7 @@ class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement)
opened="{{_opened}}"
no-vertical-overlap
on-vaadin-overlay-close="_onVaadinOverlayClose"
>
<template>
<vaadin-avatar-group-list-box on-keydown="_onListKeyDown">
<template is="dom-repeat" items="[[__computeExtraItems(items.*, __itemsInView, maxItemsVisible)]]">
<vaadin-item theme="avatar-group-item" role="option">
<vaadin-avatar
name="[[item.name]]"
abbr="[[item.abbr]]"
img="[[item.img]]"
i18n="[[i18n]]"
part="avatar"
theme$="[[_theme]]"
color-index="[[item.colorIndex]]"
tabindex="-1"
aria-hidden="true"
></vaadin-avatar>
[[item.name]]
</vaadin-item>
</template>
</vaadin-avatar-group-list-box>
</template>
</vaadin-avatar-group-overlay>
></vaadin-avatar-group-overlay>
`;
}

Expand Down Expand Up @@ -266,6 +244,13 @@ class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement)
value: null,
},

/** @private */
_overflowItems: {
type: Array,
observer: '__overflowItemsChanged',
computed: '__computeOverflowItems(items.*, __itemsInView, maxItemsVisible)',
},

/** @private */
_opened: {
type: Boolean,
Expand Down Expand Up @@ -293,6 +278,8 @@ class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement)
this._overlayElement = this.shadowRoot.querySelector('vaadin-avatar-group-overlay');
this._overlayElement.positionTarget = this.$.overflow;

this.$.overlay.renderer = this.__overlayRenderer.bind(this);

afterNextRender(this, () => {
this.__setItemsInView();
});
Expand All @@ -318,6 +305,61 @@ class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement)
return action.replace('{user}', user.name || user.abbr || this.i18n.anonymous);
}

/**
* Renders items when they are provided by the `items` property and clears the content otherwise.
* @param {!HTMLElement} root
* @param {!Select} _select
* @private
*/
__overlayRenderer(root) {
let listBox = root.firstElementChild;
if (!listBox) {
listBox = document.createElement('vaadin-list-box');
listBox.addEventListener('keydown', (event) => this._onListKeyDown(event));
root.appendChild(listBox);
}

listBox.textContent = '';

if (!this._overflowItems) {
return;
}

this._overflowItems.forEach((item) => {
listBox.appendChild(this.__createItemElement(item));
});
}

/** @private */
__createItemElement(item) {
const itemElement = document.createElement('vaadin-item');
itemElement.setAttribute('theme', 'avatar-group-item');
itemElement.setAttribute('role', 'option');

const avatar = document.createElement('vaadin-avatar');
itemElement.appendChild(avatar);

avatar.setAttribute('aria-hidden', 'true');
avatar.setAttribute('tabindex', '-1');
avatar.i18n = this.i18n;

if (this._theme) {
avatar.setAttribute('theme', this._theme);
}

avatar.name = item.name;
avatar.abbr = item.abbr;
avatar.img = item.img;
avatar.colorIndex = item.colorIndex;

if (item.name) {
const text = document.createTextNode(item.name);
itemElement.appendChild(text);
}

return itemElement;
}

/** @private */
_onOverflowClick(e) {
e.stopPropagation();
Expand Down Expand Up @@ -368,10 +410,10 @@ class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement)
}

/** @private */
__computeExtraItems(arr, itemsInView, maxItemsVisible) {
__computeOverflowItems(arr, itemsInView, maxItemsVisible) {
const items = arr.base || [];
const limit = this.__getLimit(items.length, itemsInView, maxItemsVisible);
return limit ? items.slice(limit) : items;
return limit ? items.slice(limit) : [];
}

/** @private */
Expand Down Expand Up @@ -483,7 +525,7 @@ class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement)
__openedChanged(opened, wasOpened) {
if (opened) {
if (!this._menuElement) {
this._menuElement = this._overlayElement.content.querySelector('vaadin-avatar-group-list-box');
this._menuElement = this._overlayElement.content.querySelector('vaadin-list-box');
this._menuElement.setAttribute('role', 'listbox');
}

Expand All @@ -499,6 +541,13 @@ class AvatarGroup extends ResizeMixin(ElementMixin(ThemableMixin(PolymerElement)
this.$.overflow.setAttribute('aria-expanded', opened === true);
}

/** @private */
__overflowItemsChanged(items, oldItems) {
if (items || oldItems) {
this.$.overlay.requestContentUpdate();
}
}

/** @private */
__setItemsInView() {
const avatars = this._avatars;
Expand Down
44 changes: 22 additions & 22 deletions packages/avatar-group/test/avatar-group.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ describe('avatar-group', () => {
});

it('should render avatar based on maxItemsVisible, including overflow avatar', () => {
const items = group.shadowRoot.querySelectorAll('vaadin-avatar');
const items = group.$.container.querySelectorAll('vaadin-avatar');
expect(items.length).to.equal(group.maxItemsVisible);
});

Expand All @@ -106,7 +106,7 @@ describe('avatar-group', () => {
it('should show at least two avatars if maxItemsVisible is below 2', async () => {
group.maxItemsVisible = 1;
await nextRender(group);
const items = group.shadowRoot.querySelectorAll('vaadin-avatar');
const items = group.$.container.querySelectorAll('vaadin-avatar');
expect(items.length).to.equal(2);
});

Expand Down Expand Up @@ -154,7 +154,7 @@ describe('avatar-group', () => {
group.items = [];
group.items = items;
await nextRender(group);
const renderedElements = group.shadowRoot.querySelectorAll('vaadin-avatar');
const renderedElements = group.$.container.querySelectorAll('vaadin-avatar');
expect(renderedElements.length).to.equal(maxItemsVisible);
});

Expand All @@ -168,7 +168,7 @@ describe('avatar-group', () => {
group.style.width = '100px';
await onceResized(group);

const items = group.shadowRoot.querySelectorAll('vaadin-avatar');
const items = group.$.container.querySelectorAll('vaadin-avatar');
expect(items.length).to.equal(3);
});

Expand All @@ -195,7 +195,7 @@ describe('avatar-group', () => {
it('should render avatars to fit width on resize', async () => {
group.style.width = '110px';
await onceResized(group);
const items = group.shadowRoot.querySelectorAll('vaadin-avatar');
const items = group.$.container.querySelectorAll('vaadin-avatar');
expect(items.length).to.equal(3);
expect(overflow.abbr).to.equal('+3');
});
Expand All @@ -204,7 +204,7 @@ describe('avatar-group', () => {
group.set('items', group.items.slice(0, 2));
group.style.width = '50px';
await onceResized(group);
const items = group.shadowRoot.querySelectorAll('vaadin-avatar:not([hidden])');
const items = group.$.container.querySelectorAll('vaadin-avatar:not([hidden])');
expect(items.length).to.equal(2);
});

Expand All @@ -223,7 +223,7 @@ describe('avatar-group', () => {

it('should render avatars in the list-box items', (done) => {
overlay.addEventListener('vaadin-overlay-open', () => {
const items = overlay.content.querySelectorAll('[theme="avatar-group-item"]');
const items = overlay.querySelectorAll('[theme="avatar-group-item"]');
expect(items.length).to.equal(3);
done();
});
Expand All @@ -235,7 +235,7 @@ describe('avatar-group', () => {
overlay.addEventListener('vaadin-overlay-open', () => {
group.style.width = '75px';
onceResized(group).then(() => {
const items = overlay.content.querySelectorAll('[theme="avatar-group-item"]');
const items = overlay.querySelectorAll('[theme="avatar-group-item"]');
expect(items.length).to.equal(4);
done();
});
Expand Down Expand Up @@ -299,9 +299,9 @@ describe('avatar-group', () => {

it('should render list-box with items in the overlay', (done) => {
overlay.addEventListener('vaadin-overlay-open', () => {
const list = overlay.content.querySelector('vaadin-avatar-group-list-box');
const list = overlay.querySelector('vaadin-list-box');
expect(list).to.be.ok;
const items = overlay.content.querySelectorAll('[theme="avatar-group-item"]');
const items = overlay.querySelectorAll('[theme="avatar-group-item"]');
expect(items.length).to.equal(3);
done();
});
Expand All @@ -310,7 +310,7 @@ describe('avatar-group', () => {

it('should render avatar names in the list-box items', (done) => {
overlay.addEventListener('vaadin-overlay-open', () => {
const items = overlay.content.querySelectorAll('[theme="avatar-group-item"]');
const items = overlay.querySelectorAll('[theme="avatar-group-item"]');
expect(items[0].textContent.trim()).to.equal(group.items[1].name);
expect(items[1].textContent.trim()).to.equal(group.items[2].name);
expect(items[2].textContent.trim()).to.equal(group.items[3].name);
Expand All @@ -321,7 +321,7 @@ describe('avatar-group', () => {

it('should set tabindex="-1" on the avatars in the items', (done) => {
overlay.addEventListener('vaadin-overlay-open', () => {
const avatars = overlay.content.querySelectorAll('vaadin-avatar');
const avatars = overlay.querySelectorAll('vaadin-avatar');
expect(avatars[0].getAttribute('tabindex')).to.equal('-1');
expect(avatars[1].getAttribute('tabindex')).to.equal('-1');
expect(avatars[2].getAttribute('tabindex')).to.equal('-1');
Expand All @@ -344,7 +344,7 @@ describe('avatar-group', () => {

it('should close overlay on list-box Escape press', (done) => {
overlay.addEventListener('vaadin-overlay-open', () => {
const list = overlay.content.querySelector('vaadin-avatar-group-list-box');
const list = overlay.querySelector('vaadin-list-box');
escKeyDown(list);

afterNextRender(overlay, () => {
Expand All @@ -357,7 +357,7 @@ describe('avatar-group', () => {

it('should close overlay on list-box Tab press', (done) => {
overlay.addEventListener('vaadin-overlay-open', () => {
const list = overlay.content.querySelector('vaadin-avatar-group-list-box');
const list = overlay.querySelector('vaadin-list-box');
tabKeyDown(list);

afterNextRender(overlay, () => {
Expand Down Expand Up @@ -397,7 +397,7 @@ describe('avatar-group', () => {

it('should restore focus-ring attribute on close if closed with keyboard', (done) => {
overlay.addEventListener('vaadin-overlay-open', () => {
const list = overlay.content.querySelector('vaadin-avatar-group-list-box');
const list = overlay.querySelector('vaadin-list-box');
escKeyDown(list);

afterNextRender(overlay, () => {
Expand All @@ -411,7 +411,7 @@ describe('avatar-group', () => {

it('should not restore focus-ring attribute on close if not set', (done) => {
overlay.addEventListener('vaadin-overlay-open', () => {
const items = overlay.content.querySelectorAll('[theme="avatar-group-item"]');
const items = overlay.querySelectorAll('[theme="avatar-group-item"]');
items[0].click();

afterNextRender(overlay, () => {
Expand Down Expand Up @@ -448,7 +448,7 @@ describe('avatar-group', () => {
it('should pass color index to overlay avatars', (done) => {
group.maxItemsVisible = 1;
overlay.addEventListener('vaadin-overlay-open', () => {
const avatars = overlay.content.querySelectorAll('vaadin-avatar');
const avatars = overlay.querySelectorAll('vaadin-avatar');
expect(avatars[0].colorIndex).to.equal(group.items[1].colorIndex);
expect(avatars[1].colorIndex).to.equal(group.items[2].colorIndex);
done();
Expand Down Expand Up @@ -510,7 +510,7 @@ describe('avatar-group', () => {
group.i18n = customI18n;
group.maxItemsVisible = 1;
overlay.addEventListener('vaadin-overlay-open', () => {
const avatars = overlay.content.querySelectorAll('vaadin-avatar');
const avatars = overlay.querySelectorAll('vaadin-avatar');
expect(avatars[0].i18n).to.deep.equal(customI18n);
expect(avatars[1].i18n).to.deep.equal(customI18n);
done();
Expand Down Expand Up @@ -549,7 +549,7 @@ describe('avatar-group', () => {

it('should set role="listbox" on the overlay list-box', (done) => {
overlay.addEventListener('vaadin-overlay-open', () => {
const list = overlay.content.querySelector('vaadin-avatar-group-list-box');
const list = overlay.querySelector('vaadin-list-box');
expect(list.getAttribute('role')).to.equal('listbox');
done();
});
Expand All @@ -558,7 +558,7 @@ describe('avatar-group', () => {

it('should set role="option" on the overlay items', (done) => {
overlay.addEventListener('vaadin-overlay-open', () => {
const items = overlay.content.querySelectorAll('[theme="avatar-group-item"]');
const items = overlay.querySelectorAll('[theme="avatar-group-item"]');
items.forEach((item) => {
expect(item.getAttribute('role')).to.equal('option');
});
Expand All @@ -569,7 +569,7 @@ describe('avatar-group', () => {

it('should not create tooltips for the overlay avatars', (done) => {
overlay.addEventListener('vaadin-overlay-open', () => {
const avatars = overlay.content.querySelectorAll('vaadin-avatar');
const avatars = overlay.querySelectorAll('vaadin-avatar');
avatars.forEach((avatar) => {
expect(avatar.withTooltip).to.be.false;
expect(avatar.querySelector('vaadin-tooltip')).to.be.not.ok;
Expand All @@ -581,7 +581,7 @@ describe('avatar-group', () => {

it('should set aria-hidden="true" on the overlay avatars', (done) => {
overlay.addEventListener('vaadin-overlay-open', () => {
const avatars = overlay.content.querySelectorAll('vaadin-avatar');
const avatars = overlay.querySelectorAll('vaadin-avatar');
avatars.forEach((avatar) => {
expect(avatar.getAttribute('aria-hidden')).to.equal('true');
});
Expand Down
Loading

0 comments on commit 8439b22

Please sign in to comment.