Skip to content

Commit

Permalink
Add trigger 'htmx:sseClose' to sse close event (#31)
Browse files Browse the repository at this point in the history
* Add trigger 'htmx:sseClose' to sse close event

* Add details

* Update documentation

* Update tests

* Remove formatting changes

* Add source to sseClose on nodeReplaced
  • Loading branch information
sondalex authored Jul 12, 2024
1 parent f2cb743 commit c328002
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 1 deletion.
21 changes: 21 additions & 0 deletions src/sse/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,27 @@ This event is dispatched just before the SSE event data is swapped into the DOM.

This event is dispatched after the SSE event data has been swapped into the DOM. The `details` field is a [MessageEvent](https://developer.mozilla.org/en-US/docs/Web/API/EventSource/message_event) - this is the event created by [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) when it receives an SSE message.

#### `htmx:sseClose`

This event is dispatched in three different closing scenario. To control for the scenario the user can control for the evt.detail.sseclose property.

```javascript
document.body.addEventListener('htmx:sseClose', function (e) {
const reason = e.detail.type
switch(reason) {
case "nodeMissing":
// Parent node is missing and therefore connection was closed
...
case "nodeReplaced":
// Parent node replacement caused closing of connection
...
case "message":
// connection was closed due to reception of message sse-close
...
}
})
```

##### Details

* `detail.elt` - The swap target.
Expand Down
15 changes: 14 additions & 1 deletion src/sse/sse.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,12 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
case 'htmx:beforeCleanupElement':
var internalData = api.getInternalData(parent)
// Try to remove remove an EventSource when elements are removed
if (internalData.sseEventSource) {
var source = internalData.sseEventSource
if (source) {
api.triggerEvent(parent, 'htmx:sseClose', {
source,
type: 'nodeReplaced',
})
internalData.sseEventSource.close()
}

Expand Down Expand Up @@ -231,6 +236,10 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
if (closeAttribute) {
// close eventsource when this message is received
source.addEventListener(closeAttribute, function() {
api.triggerEvent(elt, 'htmx:sseClose', {
source,
type: 'message',
})
source.close()
});
}
Expand All @@ -247,6 +256,10 @@ This extension adds support for Server Sent Events to htmx. See /www/extensions
if (!api.bodyContains(elt)) {
var source = api.getInternalData(elt).sseEventSource
if (source != undefined) {
api.triggerEvent(elt, 'htmx:sseClose', {
source,
type: 'nodeMissing',
})
source.close()
// source = null
return true
Expand Down
46 changes: 46 additions & 0 deletions src/sse/test/ext/sse.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ describe('sse extension', function() {

beforeEach(function() {
this.server = makeServer()
this.closeType = ""
this.clock = sinon.useFakeTimers();
var test = this
clearWorkArea()
Expand Down Expand Up @@ -200,9 +201,14 @@ describe('sse extension', function() {
var div = make('<div hx-get="/test" hx-swap="outerHTML" hx-ext="sse" sse-connect="/foo">' +
'<div id="d1" hx-trigger="sse:e1" hx-get="/d1">div1</div>' +
'</div>')
htmx.on(div, "htmx:sseClose", (evt) => {
this.closeType = evt.detail.type
})
this.clock.tick(1)
div.click()

this.server.respond()
this.closeType.should.equal("nodeReplaced")
this.eventSource.readyState.should.equal(EventSource.CLOSED)
})

Expand All @@ -211,9 +217,14 @@ describe('sse extension', function() {
var div = make('<div hx-get="/test" hx-swap="outerHTML" hx-ext="sse" sse-connect="/foo">' +
'<div id="d1" hx-swap="e1" hx-get="/d1">div1</div>' +
'</div>')
htmx.on(div, "htmx:sseClose", (evt) => {
this.closeType = evt.detail.type
})
this.clock.tick(1)
div.click()

this.server.respond()
this.closeType.should.equal("nodeReplaced")
this.eventSource.readyState.should.equal(EventSource.CLOSED)
})

Expand All @@ -223,27 +234,62 @@ describe('sse extension', function() {
'</div>')
this.clock.tick(1)
div.parentElement.removeChild(div)

htmx.on(div, "htmx:sseClose", (evt) => {
this.closeType = evt.detail.type
})

this.eventSource.sendEvent('e1')
this.closeType.should.equal("nodeMissing")
this.eventSource.readyState.should.equal(EventSource.CLOSED)
})

it('is closed after close message from server', function() {
var div = make('<div hx-ext="sse" sse-connect="/foo" sse-close="close">' +
'<div id="d1" sse-swap="e1"></div>' +
'</div>');
htmx.on(div, "htmx:sseClose", (evt) => {
this.closeType = evt.detail.type
})
this.clock.tick(1)
this.eventSource.readyState.should.equal(EventSource.OPEN);
this.eventSource.sendEvent("close");
this.closeType.should.equal("message")
this.eventSource.readyState.should.equal(EventSource.CLOSED);
})

it('is closed after close message from server in nested content', function() {
var div = make('<div hx-ext="sse"><div sse-connect="/foo" sse-close="close">' +
'<div id="d1" sse-swap="e1"></div>' +
'</div></div>');

htmx.on(div, "htmx:sseClose", (evt) => {
this.closeType = evt.detail.type
})
this.clock.tick(1)

this.eventSource.readyState.should.equal(EventSource.OPEN);
this.eventSource.sendEvent("close");
this.closeType.should.equal("message")
this.eventSource.readyState.should.equal(EventSource.CLOSED);
})
it('is closed after close message from server, non-nested', function(){
var div = make(`<p
hx-ext="sse"
sse-connect="/chat"
sse-close="close"
id="message"
sse-swap="message"
></p>`
)
htmx.on(div, "htmx:sseClose", (evt) => {
this.closeType = evt.detail.type;
});
this.clock.tick(1)
this.eventSource.readyState.should.equal(EventSource.OPEN);
this.eventSource.sendEvent('close', '<p></p>')

this.closeType.should.equal('message')
this.eventSource.readyState.should.equal(EventSource.CLOSED);
})

Expand Down

0 comments on commit c328002

Please sign in to comment.