Skip to content

Commit c12f5b7

Browse files
authored
Merge pull request #243 from github/fix-providable-bugs
Fix providable bugs
2 parents 9f1fff8 + 57c988c commit c12f5b7

2 files changed

Lines changed: 103 additions & 11 deletions

File tree

src/providable.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,10 @@ const [consume, getConsume, initConsume] = createMark<CustomElement>(
5858
(value: unknown, dispose?: () => void) => {
5959
if (!disposes.has(instance)) disposes.set(instance, new Map())
6060
const instanceDisposes = disposes.get(instance)!
61-
if (instanceDisposes.has(name)) instanceDisposes.get(name)!()
61+
if (instanceDisposes.has(name)) {
62+
const oldDispose = instanceDisposes.get(name)!
63+
if (oldDispose !== dispose) oldDispose()
64+
}
6265
if (dispose) instanceDisposes.set(name, dispose)
6366
currentValue = value
6467
access.set?.call(instance, currentValue)
@@ -83,19 +86,24 @@ export const providable = createAbility(
8386
constructor(...args: any[]) {
8487
super(...args)
8588
initProvide(this)
86-
if (getProvide(this).size) {
89+
const provides = getProvide(this)
90+
if (provides.size) {
91+
if (!contexts.has(this)) contexts.set(this, new Map())
92+
const instanceContexts = contexts.get(this)!
8793
this.addEventListener('context-request', event => {
8894
if (!isContextEvent(event)) return
8995
const name = event.context.name
96+
if (!provides.has(name)) return
9097
const value = this[name]
91-
const callback = event.callback
98+
const dispose = () => instanceContexts.get(name)?.delete(callback)
99+
const eventCallback = event.callback
100+
const callback = (newValue: unknown) => eventCallback(newValue, dispose)
92101
if (event.multiple) {
93-
if (!contexts.has(this)) contexts.set(this, new Map())
94-
const instanceContexts = contexts.get(this)!
95102
if (!instanceContexts.has(name)) instanceContexts.set(name, new Set())
96103
instanceContexts.get(name)!.add(callback)
97104
}
98-
callback(value, () => contexts.get(this)?.get(name)?.delete(callback))
105+
event.stopPropagation()
106+
callback(value)
99107
})
100108
}
101109
}

test/providable.ts

Lines changed: 89 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@ describe('Providable', () => {
1616
}
1717
window.customElements.define('providable-provider-test', ProvidableProviderTest)
1818

19+
@providable
20+
class ProvidableSomeProviderTest extends HTMLElement {
21+
@provide foo = 'greetings'
22+
bar = 'universe'
23+
baz = 18
24+
@provide qux = 42
25+
}
26+
window.customElements.define('providable-some-provider-test', ProvidableSomeProviderTest)
27+
1928
@providable
2029
class ProvidableConsumerTest extends HTMLElement {
2130
@consume foo = 'goodbye'
@@ -39,9 +48,15 @@ describe('Providable', () => {
3948

4049
describe('consumer without provider', () => {
4150
let instance: ProvidableConsumerTest
51+
let events = fake()
4252
beforeEach(async () => {
53+
events = fake()
54+
document.body.addEventListener('context-request', events)
4355
instance = await fixture(html`<providable-consumer-test />`)
4456
})
57+
afterEach(() => {
58+
document.body.removeEventListener('context-request', events)
59+
})
4560

4661
it('uses the given values', () => {
4762
expect(instance).to.have.property('foo', 'goodbye')
@@ -59,11 +74,6 @@ describe('Providable', () => {
5974
})
6075

6176
it('emits the `context-request` event when connected, for each field', async () => {
62-
instance = document.createElement('providable-consumer-test') as ProvidableConsumerTest
63-
const events = fake()
64-
instance.addEventListener('context-request', events)
65-
await fixture(instance)
66-
6777
expect(events).to.have.callCount(5)
6878
const fooEvent = events.getCall(0).args[0]
6979
expect(fooEvent).to.be.instanceof(ContextEvent)
@@ -100,6 +110,34 @@ describe('Providable', () => {
100110
expect(quxEvent).to.have.property('multiple', true)
101111
expect(quxEvent).to.have.property('bubbles', true)
102112
})
113+
114+
it('changes value based on callback new value', async () => {
115+
expect(events).to.have.callCount(5)
116+
const fooCallback = events.getCall(0).args[0].callback
117+
fooCallback('hello')
118+
expect(instance).to.have.property('foo', 'hello')
119+
fooCallback('goodbye')
120+
expect(instance).to.have.property('foo', 'goodbye')
121+
})
122+
123+
it('disposes of past callbacks when given new ones', async () => {
124+
const dispose1 = fake()
125+
const dispose2 = fake()
126+
expect(events).to.have.callCount(5)
127+
const fooCallback = events.getCall(0).args[0].callback
128+
fooCallback('hello', dispose1)
129+
expect(dispose1).to.have.callCount(0)
130+
expect(dispose2).to.have.callCount(0)
131+
fooCallback('goodbye', dispose1)
132+
expect(dispose1).to.have.callCount(0)
133+
expect(dispose2).to.have.callCount(0)
134+
fooCallback('greetings', dispose2)
135+
expect(dispose1).to.have.callCount(1)
136+
expect(dispose2).to.have.callCount(0)
137+
fooCallback('hola', dispose1)
138+
expect(dispose1).to.have.callCount(1)
139+
expect(dispose2).to.have.callCount(1)
140+
})
103141
})
104142

105143
describe('provider', () => {
@@ -190,6 +228,52 @@ describe('Providable', () => {
190228
provider.qux = 17
191229
expect(consumer).to.have.property('qux', 17)
192230
expect(consumer).to.have.property('count', 2)
231+
provider.qux = 18
232+
expect(consumer).to.have.property('qux', 18)
233+
expect(consumer).to.have.property('count', 3)
234+
})
235+
})
236+
237+
describe('consumer with nested provider parents', () => {
238+
let provider: ProvidableProviderTest
239+
let someProvider: ProvidableSomeProviderTest
240+
let consumer: ProvidableConsumerTest
241+
beforeEach(async () => {
242+
provider = await fixture(html`<providable-provider-test>
243+
<main>
244+
<article>
245+
<providable-some-provider-test>
246+
<section>
247+
<div>
248+
<providable-consumer-test></providable-consumer-test>
249+
</div>
250+
</section>
251+
</providable-some-provider-test>
252+
</article>
253+
</main>
254+
</providable-provider-test>`)
255+
someProvider = provider.querySelector<ProvidableSomeProviderTest>('providable-some-provider-test')!
256+
consumer = provider.querySelector<ProvidableConsumerTest>('providable-consumer-test')!
257+
})
258+
259+
it('only recieves provider responses from first matching provider', () => {
260+
expect(consumer).to.have.property('foo', 'greetings')
261+
expect(consumer).to.have.property('bar', 'world')
262+
expect(consumer).to.have.property('baz', 3)
263+
expect(consumer).to.have.property(sym).eql({provided: true})
264+
expect(consumer).to.have.property('qux').eql(42)
265+
expect(consumer).to.have.property('count').eql(1)
266+
})
267+
268+
it('only updates on appropriate provider changing values', () => {
269+
expect(consumer).to.have.property('qux').eql(42)
270+
expect(consumer).to.have.property('count').eql(1)
271+
provider.qux = 12
272+
expect(consumer).to.have.property('qux').eql(42)
273+
expect(consumer).to.have.property('count').eql(1)
274+
someProvider.qux = 88
275+
expect(consumer).to.have.property('qux').eql(88)
276+
expect(consumer).to.have.property('count').eql(2)
193277
})
194278
})
195279

0 commit comments

Comments
 (0)