Skip to content

Commit e1c3fa3

Browse files
authored
Merge pull request #363 from feritcemkahraman/fix/idempotent-bind-unsubscribe
fix: keep bind subscription cleanup idempotent
2 parents 9d6f120 + 940446f commit e1c3fa3

2 files changed

Lines changed: 39 additions & 1 deletion

File tree

‎src/bind.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export function listenForBind(el: Node = document): Subscription {
4646
return closed
4747
},
4848
unsubscribe() {
49+
if (closed) return
4950
closed = true
5051
observers.delete(el)
5152
observer.disconnect()

‎test/bind.ts‎

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {expect, fixture, html} from '@open-wc/testing'
22
import {fake} from 'sinon'
33
import {controller} from '../src/controller.js'
4-
import {bindShadow} from '../src/bind.js'
4+
import {bindShadow, listenForBind} from '../src/bind.js'
55

66
describe('Actionable', () => {
77
@controller
@@ -28,6 +28,43 @@ describe('Actionable', () => {
2828
<div id="el6" data-action="click:bind-test#foo"></div>`)
2929
})
3030

31+
it('keeps a new subscription cached when an old subscription is unsubscribed again', () => {
32+
const root = document.createElement('div')
33+
const old = listenForBind(root)
34+
old.unsubscribe()
35+
const current = listenForBind(root)
36+
old.unsubscribe()
37+
const reused = listenForBind(root)
38+
try {
39+
expect(old.closed).to.equal(true)
40+
expect(current.closed).to.equal(false)
41+
expect(reused === current).to.equal(true)
42+
} finally {
43+
current.unsubscribe()
44+
reused.unsubscribe()
45+
}
46+
})
47+
48+
it('does not leave an observer binding actions after the current subscription is closed', async () => {
49+
const root = instance.attachShadow({mode: 'open'})
50+
const old = listenForBind(root)
51+
old.unsubscribe()
52+
const current = listenForBind(root)
53+
old.unsubscribe()
54+
const reused = listenForBind(root)
55+
reused.unsubscribe()
56+
try {
57+
const button = document.createElement('button')
58+
button.setAttribute('data-action', 'click:bind-test#foo')
59+
root.append(button)
60+
await Promise.resolve()
61+
button.click()
62+
expect(instance.foo.callCount).to.equal(0)
63+
} finally {
64+
current.unsubscribe()
65+
}
66+
})
67+
3168
it('binds events on elements based on their data-action attribute', () => {
3269
expect(instance.foo).to.have.callCount(0)
3370
instance.querySelector<HTMLElement>('#el1')!.click()

0 commit comments

Comments
 (0)