Skip to content

Commit 74d9ae2

Browse files
michaelwarren1106calebdwilliams
authored andcommitted
fix: custom states will delay if removed in constructor
1 parent 6154e8d commit 74d9ae2

File tree

2 files changed

+50
-7
lines changed

2 files changed

+50
-7
lines changed

Diff for: src/CustomStateSet.ts

+20-3
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,27 @@ export class CustomStateSet extends Set<CustomState> {
6060
delete(state: CustomState) {
6161
const result = super.delete(state);
6262
const ref = customStateMap.get(this);
63-
ref.toggleAttribute(`state${state}`, false);
64-
if (ref.part) {
65-
ref.part.remove(`state${state}`);
63+
64+
/**
65+
* Only toggle the state/attr immediately if the ref is connected to the DOM;
66+
* otherwise, wait a tick because the element is likely being constructed
67+
* by document.createElement and would throw otherwise.
68+
*/
69+
if (ref.isConnected) {
70+
ref.toggleAttribute(`state${state}`, false);
71+
if (ref.part) {
72+
ref.part.remove(`state${state}`);
73+
}
74+
} else {
75+
setTimeout(() => {
76+
ref.toggleAttribute(`state${state}`, false);
77+
if (ref.part) {
78+
ref.part.remove(`state${state}`);
79+
}
80+
});
6681
}
82+
83+
6784
return result;
6885
}
6986
}

Diff for: test/CustomStateSet.test.ts

+30-4
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { fixture, html, expect, fixtureCleanup, aTimeout } from '@open-wc/testin
44
import { ICustomElement } from '../dist';
55
import { CustomStateSet } from '../src/CustomStateSet';
66

7-
class CustomElementStateInConstructor extends HTMLElement {
7+
class CustomElementAddStateInConstructor extends HTMLElement {
88
internals = this.attachInternals();
99
constructor() {
1010
super();
@@ -13,8 +13,20 @@ class CustomElementStateInConstructor extends HTMLElement {
1313
}
1414
}
1515

16-
const tagName = 'custom-element-state-in-constructor'
17-
customElements.define(tagName, CustomElementStateInConstructor);
16+
const addStateTagName = 'custom-element-add-state-in-constructor';
17+
customElements.define(addStateTagName, CustomElementAddStateInConstructor);
18+
19+
class CustomElementDeleteStateInConstructor extends HTMLElement {
20+
internals = this.attachInternals();
21+
constructor() {
22+
super();
23+
24+
this.internals.states.delete('--foo');
25+
}
26+
}
27+
28+
const deleteStateTagName = 'custom-element-delete-state-in-constructor';
29+
customElements.define(deleteStateTagName, CustomElementDeleteStateInConstructor);
1830

1931
describe('CustomStateSet polyfill', () => {
2032
let el: HTMLElement;
@@ -74,7 +86,7 @@ describe('CustomStateSet polyfill', () => {
7486
it('will use a timeout if a state is added in a constructor', async () => {
7587
let el;
7688
expect(() => {
77-
el = document.createElement(tagName);
89+
el = document.createElement(addStateTagName);
7890
}).not.to.throw();
7991

8092
if (window.CustomStateSet.isPolyfilled) {
@@ -84,4 +96,18 @@ describe('CustomStateSet polyfill', () => {
8496
expect(el.matches(`:--foo`)).to.be.true;
8597
}
8698
});
99+
100+
it('will use a timeout if a state is deleted in a constructor', async () => {
101+
let el;
102+
expect(() => {
103+
el = document.createElement(deleteStateTagName);
104+
}).not.to.throw();
105+
106+
if (window.CustomStateSet.isPolyfilled) {
107+
await aTimeout(100);
108+
expect(el.matches('[state--foo]')).to.be.false;
109+
} else {
110+
expect(el.matches(`:--foo`)).to.be.false;
111+
}
112+
});
87113
});

0 commit comments

Comments
 (0)