diff --git a/src/vaadin-message-list.js b/src/vaadin-message-list.js index 25cb28a..e0dee6d 100644 --- a/src/vaadin-message-list.js +++ b/src/vaadin-message-list.js @@ -4,6 +4,7 @@ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ */ import { PolymerElement, html } from '@polymer/polymer/polymer-element.js'; +import { microTask } from '@polymer/polymer/lib/utils/async.js'; import '@polymer/polymer/lib/elements/dom-repeat.js'; import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js'; import { ElementMixin } from '@vaadin/vaadin-element-mixin/vaadin-element-mixin.js'; @@ -59,7 +60,8 @@ class MessageListElement extends ElementMixin(ThemableMixin(PolymerElement)) { type: Array, value: function () { return []; - } + }, + observer: '_itemsChanged' } }; } @@ -99,6 +101,21 @@ class MessageListElement extends ElementMixin(ThemableMixin(PolymerElement)) { this.setAttribute('tabindex', '0'); } + _itemsChanged(newVal, oldVal) { + if ( + (!oldVal || newVal.length > oldVal.length) && // there are new items + this.scrollHeight < this.clientHeight + this.scrollTop + 50 // bottom of list + ) { + microTask.run(() => this._scrollToLastMessage()); + } + } + + _scrollToLastMessage() { + if (this.items.length > 0) { + this.scrollTop = this.scrollHeight - this.clientHeight; + } + } + static get is() { return 'vaadin-message-list'; } diff --git a/test/message-list.test.js b/test/message-list.test.js index fd7ebf6..0aeae97 100644 --- a/test/message-list.test.js +++ b/test/message-list.test.js @@ -29,6 +29,26 @@ describe('message-list', () => { userImg: '/test/visual/avatars/avatar.jpg', userColorIndex: 1 }, + { + text: 'A message in the stream of messages', + time: '9:35 AM', + user: { + name: 'Joan Doe', + abbr: 'JD', + img: '/test/visual/avatars/avatar.jpg', + colorIndex: 1 + } + }, + { + text: 'A message in the stream of messages', + time: '9:36 AM', + user: { + name: 'Joan Doe', + abbr: 'JD', + img: '/test/visual/avatars/avatar.jpg', + colorIndex: 1 + } + }, { text: 'Call upon the times of glory', time: '2:34 PM', @@ -55,7 +75,7 @@ describe('message-list', () => { it('message list should have two messages', () => { const items = messageList.shadowRoot.querySelectorAll('vaadin-message'); - expect(items.length).to.equal(2); + expect(items.length).to.equal(4); }); it('message properties should be correctly set', () => { @@ -74,5 +94,43 @@ describe('message-list', () => { messageList.scrollBy(0, 1000); expect(messageList.scrollTop).to.be.at.least(1); }); + + it('message list should scroll to bottom on new messages', async () => { + messageList.style.height = '100px'; + messageList.scrollBy(0, 1000); + const scrollTopBeforeMessage = messageList.scrollTop; + messageList.items = [ + ...messageList.items, + { + text: 'A new message arrives!', + time: '2:35 PM', + user: { + name: 'Steve Mops', + abbr: 'SM', + colorIndex: 2 + } + } + ]; + await nextRender(messageList); + expect(messageList.scrollTop).to.be.at.least(scrollTopBeforeMessage + 1); + }); + + it('message list should not scroll if not at the bottom', async () => { + messageList.style.height = '100px'; + messageList.items = [ + ...messageList.items, + { + text: 'A new message arrives!', + time: '2:35 PM', + user: { + name: 'Steve Mops', + abbr: 'SM', + colorIndex: 2 + } + } + ]; + await nextRender(messageList); + expect(messageList.scrollTop).to.be.equal(0); + }); }); });