Skip to content

Fix setAttribute() after mount not updating reflected props (closes #98)#117

Open
DmitrySharabin wants to merge 1 commit into
props-api-polishfrom
fix-issue-98
Open

Fix setAttribute() after mount not updating reflected props (closes #98)#117
DmitrySharabin wants to merge 1 commit into
props-api-polishfrom
fix-issue-98

Conversation

@DmitrySharabin
Copy link
Copy Markdown
Member

Port of #109 from main. Same root cause, same fix; tests are already in place.

Per the HTML spec, customElements.define only reads Class.observedAttributes if attributeChangedCallback is non-null on the prototype at registration time. Pre-this-PR, first_constructor_static wired ACB at first instance construction — after registration — so the browser snapshotted a null ACB and never read observedAttributes. setAttribute after mount silently dropped its update.

Fix: make ACB a regular entry in the props plugin's provides so addPlugins lands it on NudeElement.prototype before any subclass calls customElements.define. The eager static observedAttributes getter on providesStatic runs defineProps() lazily. Subclasses chain via super.attributeChangedCallback(...) — same convention as connectedCallback.

Bonus: also flips "removeAttribute collapses a reflected prop to its default" green — same root cause.

Stacked on #116.

Test plan

🤖 Generated with Claude Code

…98)

Port of #109 from `main`. Per the HTML spec, `customElements.define`
only reads `Class.observedAttributes` if `attributeChangedCallback` is
non-null on the prototype at registration time. The previous
`first_constructor_static` hook wired ACB at *first instance
construction*, which fires after registration — so the browser
snapshotted a `null` ACB and never read `observedAttributes`.
`setAttribute` after mount silently dropped its update.

Fix: make ACB a regular entry in the props plugin's `provides`, so
`addPlugins` lands it on `NudeElement.prototype` before any subclass
calls `customElements.define`. Subclasses chain via super, matching
the convention used for `connectedCallback` / `disconnectedCallback`.

The eager static `observedAttributes` getter on `providesStatic` now
runs `defineProps()` lazily — that's the registration-time path. The
`setup` hook is gated by `!Object.hasOwn(this, observedAttributes)`
so it doesn't re-install when the static getter has already done so.

Bonus: this also flips "removeAttribute collapses a reflected prop to
its default" green — same root cause.

Baseline: 91 → 93 pass / 7 → 5 fail / 4 skip.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant