Skip to content

Commit

Permalink
Merge pull request #20 from bigskysoftware/multiple-trigger-parsing
Browse files Browse the repository at this point in the history
Handle SSE triggers with hx-trigger's multiple triggers syntax
  • Loading branch information
Renerick committed Apr 28, 2024
2 parents 3dc8b44 + 9d11aa1 commit cac304a
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 25 deletions.
43 changes: 19 additions & 24 deletions src/sse/sse.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
var sseEventNames = sseSwapAttr.split(',')

for (var i = 0; i < sseEventNames.length; i++) {
var sseEventName = sseEventNames[i].trim()
var listener = function(event) {
const sseEventName = sseEventNames[i].trim()
const listener = function(event) {
// If the source is missing then close SSE
if (maybeCloseSSESource(sourceElement)) {
return
Expand Down Expand Up @@ -137,33 +137,28 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
var internalData = api.getInternalData(sourceElement)
var source = internalData.sseEventSource

var sseEventName = api.getAttributeValue(elt, 'hx-trigger')
if (sseEventName == null) {
return
}

// Only process hx-triggers for events with the "sse:" prefix
if (sseEventName.slice(0, 4) != 'sse:') {
return
}

var listener = function(event) {
if (maybeCloseSSESource(sourceElement)) {
var triggerSpecs = api.getTriggerSpecs(elt)
triggerSpecs.forEach(function(ts) {
if (ts.trigger.slice(0, 4) !== 'sse:') {
return
}

if (!api.bodyContains(elt)) {
source.removeEventListener(sseEventName, listener)
var listener = function (event) {
if (maybeCloseSSESource(sourceElement)) {
return
}
if (!api.bodyContains(elt)) {
source.removeEventListener(ts.trigger.slice(4), listener)
}
// Trigger events to be handled by the rest of htmx
htmx.trigger(elt, ts.trigger, event)
htmx.trigger(elt, 'htmx:sseMessage', event)
}

// Trigger events to be handled by the rest of htmx
htmx.trigger(elt, sseEventName, event)
htmx.trigger(elt, 'htmx:sseMessage', event)
}

// Register the new listener
api.getInternalData(elt).sseEventListener = listener
source.addEventListener(sseEventName.slice(4), listener)
// Register the new listener
api.getInternalData(elt).sseEventListener = listener
source.addEventListener(ts.trigger.slice(4), listener)
})
}
}

Expand Down
63 changes: 62 additions & 1 deletion src/sse/test/ext/sse.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,36 @@ describe('sse extension', function() {
byId('d2').innerHTML.should.equal('div2 updated')
})

it('supports hx-trigger\'s multiple triggers syntax', function() {
this.server.respondWith('GET', '/d1', 'div1 updated')
this.server.respondWith('GET', '/d2', 'div2 updated')
this.server.respondWith('GET', '/d3', 'div3 updated')

var div = make('<div hx-ext="sse" sse-connect="/foo">' +
'<div id="d1" hx-trigger="click, whatever from:body, sse:e1" hx-get="/d1">div1</div>' +
'<div id="d2" hx-trigger="keyup, sse:e2, someTrigger" hx-get="/d2">div2</div>' +
'<div id="d3" hx-trigger="sse:e3, anotherTrigger" hx-get="/d3">div3</div>' +
'</div>')

this.eventSource.sendEvent('e1')
this.server.respond()
byId('d1').innerHTML.should.equal('div1 updated')
byId('d2').innerHTML.should.equal('div2')
byId('d3').innerHTML.should.equal('div3')

this.eventSource.sendEvent('e2')
this.server.respond()
byId('d1').innerHTML.should.equal('div1 updated')
byId('d2').innerHTML.should.equal('div2 updated')
byId('d3').innerHTML.should.equal('div3')

this.eventSource.sendEvent('e3')
this.server.respond()
byId('d1').innerHTML.should.equal('div1 updated')
byId('d2').innerHTML.should.equal('div2 updated')
byId('d3').innerHTML.should.equal('div3 updated')
})

it('does not trigger events that arent named', function() {
this.server.respondWith('GET', '/d1', 'div1 updated')

Expand Down Expand Up @@ -187,12 +217,43 @@ describe('sse extension', function() {

it('is not listening for events after hx-swap element removed', function() {
var div = make('<div hx-ext="sse" sse-connect="/foo">' +
'<div id="d1" hx-swap="outerHTML" sse-swap="e1">div1</div>' +
'<div id="d1" hx-swap="innerHTML" sse-swap="e1, e2">div1</div>' +
'<div id="d2" hx-swap="innerHTML" sse-swap="e2">div1</div>' +
'</div>')
this.eventSource._listeners.e1.should.be.lengthOf(1)
this.eventSource._listeners.e2.should.be.lengthOf(2)
div.removeChild(byId('d1'))
this.eventSource.sendEvent('e1', 'Test')
this.eventSource.sendEvent('e2', 'Test')
this.eventSource._listeners.e1.should.be.empty
this.eventSource._listeners.e2.should.be.lengthOf(1)
div.removeChild(byId('d2'))
this.eventSource.sendEvent('e1', 'Test')
this.eventSource.sendEvent('e2', 'Test')
this.eventSource._listeners.e1.should.be.empty
this.eventSource._listeners.e2.should.be.empty
})

it('is not listening for events after hx-trigger element removed', function() {
this.server.respondWith('GET', '/test', function(xhr) {
xhr.respond(200, {})
})
var div = make('<div hx-ext="sse" sse-connect="/foo">' +
'<div id="d1" hx-get="/test" hx-target="this" hx-swap="innerHTML" hx-trigger="sse:e1, sse:e2">div1</div>' +
'<div id="d2" hx-get="/test" hx-target="this" hx-swap="innerHTML" hx-trigger="sse:e2">div1</div>' +
'</div>')
this.eventSource._listeners.e1.should.be.lengthOf(1)
this.eventSource._listeners.e2.should.be.lengthOf(2)
div.removeChild(byId('d1'))
this.eventSource.sendEvent('e1', 'Test')
this.eventSource.sendEvent('e2', 'Test')
this.eventSource._listeners.e1.should.be.empty
this.eventSource._listeners.e2.should.be.lengthOf(1)
div.removeChild(byId('d2'))
this.eventSource.sendEvent('e1', 'Test')
this.eventSource.sendEvent('e2', 'Test')
this.eventSource._listeners.e1.should.be.empty
this.eventSource._listeners.e2.should.be.empty
})

// sse and hx-trigger handlers are distinct
Expand Down

0 comments on commit cac304a

Please sign in to comment.