Reuse component shadow-root style nodes across re-renders#62
Reuse component shadow-root style nodes across re-renders#62JosunLP merged 4 commits intodevelopmentfrom
Conversation
Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>
Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR updates the Web Components render path so defineComponent() preserves the component-managed shadow-root <style> element across attribute-driven re-renders, avoiding unnecessary DOM churn and ensuring a stable style node.
Changes:
- Preserve and reuse the component-owned shadow-root
<style>node acrossrender(true)updates by tagging it withdata-bquery-component-style. - Add a regression test that verifies style node identity reuse, ensures only one
<style>remains after update, and confirms rendered content updates.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/component/component.ts |
Reuses an existing style[data-bquery-component-style] node after shadowRoot.innerHTML resets, preventing style element recreation on re-render. |
tests/component.test.ts |
Adds a focused test to validate style node reuse across attribute-triggered re-renders. |
|
@copilot apply changes based on the comments in this thread |
Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>
Applied in 8f0bb2e. The regression test now targets |
There was a problem hiding this comment.
Pull request overview
This PR reduces DOM churn in the component module by preserving the component-owned <style> node across attribute-driven re-renders, instead of recreating it on every render. This improves render efficiency and avoids unnecessary style node replacement.
Changes:
- Preserve and re-prepend an existing shadow-root style node (marked with
data-bquery-component-style) during re-renders. - Add a regression test ensuring the same style element instance is reused and remains singular after an attribute-triggered update.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
src/component/component.ts |
Captures the existing component style node before shadowRoot.innerHTML replacement and reinserts it after render, tagging newly-created style nodes with data-bquery-component-style. |
tests/component.test.ts |
Adds a test verifying the style node instance is reused across attribute-triggered re-renders and that only one style tag exists post-update. |
* Add `bool()` support for boolean attributes in component templates (#55) Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Jonas Pfalzgraf <info@josunlp.de> * Add typed state generics to component definitions (#56) Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Jonas Pfalzgraf <info@josunlp.de> * Pass attribute change metadata to component `updated()` hooks (#58) Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Jonas Pfalzgraf <info@josunlp.de> * Add per-component sanitizer overrides for component render output (#57) * Initial plan * feat: add per-component sanitizer options Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> * docs: clarify component sanitizer baseline Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> * docs: warn about sensitive component sanitizer opts Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> * test: cover sanitizer allowTags overrides Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> * perf: precompute component sanitizer allowlists Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> --------- Signed-off-by: Jonas Pfalzgraf <info@josunlp.de> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> Co-authored-by: Jonas Pfalzgraf <info@josunlp.de> * Pass previous props into component `beforeUpdate` (#59) * Initial plan * Add previous props to beforeUpdate hook Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> * Refine beforeUpdate old props implementation Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> * Fix beforeUpdate typing regressions Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> --------- Signed-off-by: Jonas Pfalzgraf <info@josunlp.de> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> Co-authored-by: Jonas Pfalzgraf <info@josunlp.de> * Add explicit signal subscriptions to `component()` renders (#60) * Initial plan * feat: add component signal subscriptions Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> * chore: document signal subscription tracking Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> * fix: address signal integration review feedback Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> * fix: tighten component signal typing Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> * fix: resolve eslint failure in component signal test Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> * fix: handle reconnect lifecycle and signal effect errors Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> * fix: preserve reconnect signal wiring after hook errors Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> * fix: skip beforeUpdate for signal rerenders Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> --------- Signed-off-by: Jonas Pfalzgraf <info@josunlp.de> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> Co-authored-by: Jonas Pfalzgraf <info@josunlp.de> * Add trusted sanitized fragments for `safeHtml` composition (#61) * Initial plan * feat: add trusted safeHtml fragment support Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> * chore: finalize trusted safeHtml support Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> * docs: address trusted safeHtml review feedback Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> * docs: fix second trusted safeHtml review follow-up Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> * refactor: split trusted html helpers from sanitizer Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> * refactor: tighten trusted html branding Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> * docs: tighten trusted html internals Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> --------- Signed-off-by: Jonas Pfalzgraf <info@josunlp.de> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> Co-authored-by: Jonas Pfalzgraf <info@josunlp.de> * Reuse component shadow-root style nodes across re-renders (#62) * Initial plan * fix: reuse component style element on rerender Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> * chore: finalize component style reuse fix Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> * test: tighten style reuse regression assertions Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> * Add `@bquery/bquery/storybook` template helpers for Storybook stories (#63) * Initial plan * feat: add storybook template helper Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> * docs: clarify storybook when fallback behavior Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> * fix: keep storybook helpers out of full bundle Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> * fix: sanitize storybook helper output Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> * test: tighten storybook helper security coverage Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> * feat: enhance storyHtml with improved boolean attribute handling and sanitization tests * fix: update storyHtml example to reflect correct boolean attribute output * fix: defensive tag delimiter and parser comments per code review Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> * chore: release version 1.6.0 - Update CHANGELOG.md for version 1.6.0 release notes including added features, changes, fixes, and security improvements. - Enhance README.md to document new boolean attribute helpers and Storybook integration. - Update bun.lock to reflect dependency upgrades for Storybook and happy-dom. - Modify definition.md to include new Storybook helpers and component state management features. - Revise components.md to demonstrate new boolean attribute handling and typed state in components. - Update getting-started.md with Storybook authoring examples and improved module descriptions. - Enhance security.md to clarify sanitization practices and introduce trusted fragment handling. - Revise index.md to highlight new Storybook helpers in the feature list. - Update llms.txt to reflect the new version 1.6.0. - Bump version in package.json to 1.6.0 and update devDependencies for Storybook. * Address review feedback for test setup, test type coverage, and component state keys (#65) * Initial plan * Address review feedback for test typing and setup Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> * Finalize review feedback fixes Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> * Export component state key type Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> * Default component state key generic Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> * Tighten component/state update semantics and Storybook attribute sanitization (#66) * Initial plan * fix review follow-up for state updates and storybook sanitization Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> * fix storybook attribute value scanning Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> * fix storybook interpolation boundary parsing Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> * docs align storybook attribute scan jsdoc Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com> --------- Signed-off-by: Jonas Pfalzgraf <info@josunlp.de> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
defineComponent()was rebuilding the shadow-root<style>element on every attribute-driven re-render. That churn discarded the existing style node instead of preserving it across updates.Render path
<style>element after render instead of creating a new onedata-bquery-component-styleso the component only manages its own stylesheetRegression coverage
<style>node instance survives an attribute-triggered re-renderResulting behavior
Screenshot
same node reused: true💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.