Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions __tests__/controllers/webchat_controller_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1582,6 +1582,39 @@ describe('WebchatController', () => {
expect(addEventListenerSpy).toHaveBeenCalledWith('keydown', controller.closePopoverOnEscape, true)
})

it('isolates message scrolling from host smooth scroll handlers', () => {
const addEventListenerSpy = jest.spyOn(mockMessagesContainer, 'addEventListener')

setHasTeaserTarget(false)
controller.connect()

expect(mockMessagesContainer.style.overscrollBehavior).toBe('contain')
expect(mockMessagesContainer.style.webkitOverflowScrolling).toBe('touch')
expect(mockMessagesContainer.style.touchAction).toBe('pan-y')
expect(mockMessagesContainer.hasAttribute('data-lenis-prevent')).toBe(true)
expect(mockMessagesContainer.hasAttribute('data-lenis-prevent-wheel')).toBe(true)
expect(mockMessagesContainer.hasAttribute('data-lenis-prevent-touch')).toBe(true)
expect(addEventListenerSpy).toHaveBeenCalledWith(
'wheel',
controller.stopHostScrollPropagation,
expect.objectContaining({ capture: true, passive: true }),
)
expect(addEventListenerSpy).toHaveBeenCalledWith(
'touchmove',
controller.stopHostScrollPropagation,
expect.objectContaining({ capture: true, passive: true }),
)
})

it('stops host scroll events from bubbling out of the message scroller', () => {
const event = new WheelEvent('wheel')
const stopPropagation = jest.spyOn(event, 'stopPropagation')

controller.stopHostScrollPropagation(event)

expect(stopPropagation).toHaveBeenCalled()
})

it('removes the capture-phase Escape listener on disconnect', () => {
const removeEventListenerSpy = jest.spyOn(window, 'removeEventListener')

Expand All @@ -1593,6 +1626,26 @@ describe('WebchatController', () => {
expect(removeEventListenerSpy).toHaveBeenCalledWith('keydown', controller.closePopoverOnEscape, true)
})

it('removes message scroll isolation listeners on disconnect', () => {
const removeEventListenerSpy = jest.spyOn(mockMessagesContainer, 'removeEventListener')

setHasTeaserTarget(false)
controller.floatingUICleanup = jest.fn()
controller.connect()
controller.disconnect()

expect(removeEventListenerSpy).toHaveBeenCalledWith(
'wheel',
controller.stopHostScrollPropagation,
expect.objectContaining({ capture: true, passive: true }),
)
expect(removeEventListenerSpy).toHaveBeenCalledWith(
'touchmove',
controller.stopHostScrollPropagation,
expect.objectContaining({ capture: true, passive: true }),
)
})

it('does not set up teaser positioning without a teaser target', () => {
setHasTeaserTarget(false)

Expand Down
2 changes: 1 addition & 1 deletion dist/hellotext.js

Large diffs are not rendered by default.

24 changes: 24 additions & 0 deletions lib/controllers/webchat_controller.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ const MESSAGE_TIMESTAMP_FORMAT_OPTIONS = {
hour: 'numeric',
minute: '2-digit'
};
const SCROLL_ISOLATION_EVENT_OPTIONS = {
capture: true,
passive: true
};
let _default = /*#__PURE__*/function (_Controller) {
_inherits(_default, _Controller);
var _super = _createSuper(_default);
Expand Down Expand Up @@ -82,7 +86,10 @@ let _default = /*#__PURE__*/function (_Controller) {
this.webChatChannel.onMessage(this.onMessageReceived);
this.webChatChannel.onTypingStart(this.onTypingStart);
this.webChatChannel.onReaction(this.onMessageReaction);
this.setupMessagesContainerScrollIsolation();
this.messagesContainerTarget.addEventListener('scroll', this.onScroll);
this.messagesContainerTarget.addEventListener('wheel', this.stopHostScrollPropagation, SCROLL_ISOLATION_EVENT_OPTIONS);
this.messagesContainerTarget.addEventListener('touchmove', this.stopHostScrollPropagation, SCROLL_ISOLATION_EVENT_OPTIONS);
if (this.shouldOpenOnMount) {
this.openValue = true;
}
Expand All @@ -101,6 +108,8 @@ let _default = /*#__PURE__*/function (_Controller) {
this.teardownOpeningSequence();
this.broadcastChannel.removeEventListener('message', this.onOutboundMessageSent);
this.messagesContainerTarget.removeEventListener('scroll', this.onScroll);
this.messagesContainerTarget.removeEventListener('wheel', this.stopHostScrollPropagation, SCROLL_ISOLATION_EVENT_OPTIONS);
this.messagesContainerTarget.removeEventListener('touchmove', this.stopHostScrollPropagation, SCROLL_ISOLATION_EVENT_OPTIONS);
window.removeEventListener('keydown', this.closePopoverOnEscape, true);

// Clean up typing indicator timeouts
Expand All @@ -109,6 +118,21 @@ let _default = /*#__PURE__*/function (_Controller) {
this.floatingUICleanup();
_get(_getPrototypeOf(_default.prototype), "disconnect", this).call(this);
}
}, {
key: "setupMessagesContainerScrollIsolation",
value: function setupMessagesContainerScrollIsolation() {
this.messagesContainerTarget.style.overscrollBehavior = 'contain';
this.messagesContainerTarget.style.webkitOverflowScrolling = 'touch';
this.messagesContainerTarget.style.touchAction = 'pan-y';
this.messagesContainerTarget.setAttribute('data-lenis-prevent', '');
this.messagesContainerTarget.setAttribute('data-lenis-prevent-wheel', '');
this.messagesContainerTarget.setAttribute('data-lenis-prevent-touch', '');
}
}, {
key: "stopHostScrollPropagation",
value: function stopHostScrollPropagation(event) {
event.stopPropagation();
}
}, {
key: "onTypingStart",
value: function onTypingStart() {
Expand Down
24 changes: 24 additions & 0 deletions lib/controllers/webchat_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ var MESSAGE_TIMESTAMP_FORMAT_OPTIONS = {
hour: 'numeric',
minute: '2-digit'
};
var SCROLL_ISOLATION_EVENT_OPTIONS = {
capture: true,
passive: true
};
var _default = /*#__PURE__*/function (_Controller) {
_inherits(_default, _Controller);
var _super = _createSuper(_default);
Expand Down Expand Up @@ -80,7 +84,10 @@ var _default = /*#__PURE__*/function (_Controller) {
this.webChatChannel.onMessage(this.onMessageReceived);
this.webChatChannel.onTypingStart(this.onTypingStart);
this.webChatChannel.onReaction(this.onMessageReaction);
this.setupMessagesContainerScrollIsolation();
this.messagesContainerTarget.addEventListener('scroll', this.onScroll);
this.messagesContainerTarget.addEventListener('wheel', this.stopHostScrollPropagation, SCROLL_ISOLATION_EVENT_OPTIONS);
this.messagesContainerTarget.addEventListener('touchmove', this.stopHostScrollPropagation, SCROLL_ISOLATION_EVENT_OPTIONS);
if (this.shouldOpenOnMount) {
this.openValue = true;
}
Expand All @@ -99,6 +106,8 @@ var _default = /*#__PURE__*/function (_Controller) {
this.teardownOpeningSequence();
this.broadcastChannel.removeEventListener('message', this.onOutboundMessageSent);
this.messagesContainerTarget.removeEventListener('scroll', this.onScroll);
this.messagesContainerTarget.removeEventListener('wheel', this.stopHostScrollPropagation, SCROLL_ISOLATION_EVENT_OPTIONS);
this.messagesContainerTarget.removeEventListener('touchmove', this.stopHostScrollPropagation, SCROLL_ISOLATION_EVENT_OPTIONS);
window.removeEventListener('keydown', this.closePopoverOnEscape, true);

// Clean up typing indicator timeouts
Expand All @@ -107,6 +116,21 @@ var _default = /*#__PURE__*/function (_Controller) {
this.floatingUICleanup();
_get(_getPrototypeOf(_default.prototype), "disconnect", this).call(this);
}
}, {
key: "setupMessagesContainerScrollIsolation",
value: function setupMessagesContainerScrollIsolation() {
this.messagesContainerTarget.style.overscrollBehavior = 'contain';
this.messagesContainerTarget.style.webkitOverflowScrolling = 'touch';
this.messagesContainerTarget.style.touchAction = 'pan-y';
this.messagesContainerTarget.setAttribute('data-lenis-prevent', '');
this.messagesContainerTarget.setAttribute('data-lenis-prevent-wheel', '');
this.messagesContainerTarget.setAttribute('data-lenis-prevent-touch', '');
}
}, {
key: "stopHostScrollPropagation",
value: function stopHostScrollPropagation(event) {
event.stopPropagation();
}
}, {
key: "onTypingStart",
value: function onTypingStart() {
Expand Down
36 changes: 36 additions & 0 deletions src/controllers/webchat_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const MESSAGE_TIMESTAMP_FORMAT_OPTIONS = {
hour: 'numeric',
minute: '2-digit',
}
const SCROLL_ISOLATION_EVENT_OPTIONS = { capture: true, passive: true }

export default class extends Controller {
static messageTimestampFormatters = {}
Expand Down Expand Up @@ -122,7 +123,18 @@ export default class extends Controller {

this.webChatChannel.onReaction(this.onMessageReaction)

this.setupMessagesContainerScrollIsolation()
this.messagesContainerTarget.addEventListener('scroll', this.onScroll)
this.messagesContainerTarget.addEventListener(
'wheel',
this.stopHostScrollPropagation,
SCROLL_ISOLATION_EVENT_OPTIONS,
)
this.messagesContainerTarget.addEventListener(
'touchmove',
this.stopHostScrollPropagation,
SCROLL_ISOLATION_EVENT_OPTIONS,
)

if (this.shouldOpenOnMount) {
this.openValue = true
Expand All @@ -144,6 +156,16 @@ export default class extends Controller {

this.broadcastChannel.removeEventListener('message', this.onOutboundMessageSent)
this.messagesContainerTarget.removeEventListener('scroll', this.onScroll)
this.messagesContainerTarget.removeEventListener(
'wheel',
this.stopHostScrollPropagation,
SCROLL_ISOLATION_EVENT_OPTIONS,
)
this.messagesContainerTarget.removeEventListener(
'touchmove',
this.stopHostScrollPropagation,
SCROLL_ISOLATION_EVENT_OPTIONS,
)
window.removeEventListener('keydown', this.closePopoverOnEscape, true)

// Clean up typing indicator timeouts
Expand All @@ -155,6 +177,20 @@ export default class extends Controller {
super.disconnect()
}

setupMessagesContainerScrollIsolation() {
this.messagesContainerTarget.style.overscrollBehavior = 'contain'
this.messagesContainerTarget.style.webkitOverflowScrolling = 'touch'
this.messagesContainerTarget.style.touchAction = 'pan-y'

this.messagesContainerTarget.setAttribute('data-lenis-prevent', '')
this.messagesContainerTarget.setAttribute('data-lenis-prevent-wheel', '')
this.messagesContainerTarget.setAttribute('data-lenis-prevent-touch', '')
}

stopHostScrollPropagation(event) {
event.stopPropagation()
}

onTypingStart() {
if (this.typingIndicatorVisible) {
return this.resetTypingIndicatorTimer()
Expand Down