Skip to content

Pass previous props into component beforeUpdate#59

Merged
JosunLP merged 5 commits intodevelopmentfrom
copilot/fr-5-add-beforeupdate-props
Mar 13, 2026
Merged

Pass previous props into component beforeUpdate#59
JosunLP merged 5 commits intodevelopmentfrom
copilot/fr-5-add-beforeupdate-props

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 13, 2026

beforeUpdate only received the next props, which forced components to read live shadow DOM state to infer what changed. This updates the component lifecycle so hooks can diff newProps against oldProps directly and removes that DOM-coupled pattern from the default component library.

  • Lifecycle API

    • Extends ComponentDefinition.beforeUpdate to receive (newProps, oldProps).
    • Updates the component runtime to snapshot props before update renders and pass the previous snapshot into the hook.
  • Default component optimizations

    • Refactors the default input and textarea components to decide whether to skip a full re-render by comparing previous and next props.
    • Keeps the existing value-only fast path, but no longer depends on querying shadow DOM to reconstruct prior prop values.
  • Behavior coverage

    • Adds tests that verify beforeUpdate receives both prop snapshots.
    • Adds regression coverage for default form controls:
      • value-only updates keep the same live control instance
      • non-value prop changes trigger a full re-render
  • Docs

    • Updates public examples and lifecycle docs to show the new hook signature and diff-based usage.

Example:

component('user-card', {
  props: {
    value: { type: String, default: '' },
    label: { type: String, default: '' },
  },
  beforeUpdate(newProps, oldProps) {
    return newProps.value !== oldProps.value || newProps.label !== oldProps.label;
  },
  render: ({ props }) => html`<div>${props.label}: ${props.value}</div>`,
});

🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

Copilot AI and others added 2 commits March 13, 2026 11:19
Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>
Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>
Copilot AI changed the title [WIP] [FR-5] Add old props to beforeUpdate hook for diffing Pass previous props into component beforeUpdate Mar 13, 2026
Copilot AI requested a review from JosunLP March 13, 2026 11:24
@JosunLP JosunLP marked this pull request as ready for review March 13, 2026 11:27
Copilot AI review requested due to automatic review settings March 13, 2026 11:27
@github-actions github-actions bot added component Changes to the component module docs Changes to the documentation tests Chenges to the tests labels Mar 13, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Extends the beforeUpdate component lifecycle hook to receive both new and previous props, enabling prop diffing without querying shadow DOM. The default input/textarea components are refactored to use this new signature for their render-skip optimization.

Changes:

  • beforeUpdate signature changed from (props) to (newProps, oldProps) with a prop snapshot taken before syncProps()
  • Default input/textarea canSkipRender helpers compare old vs new props directly instead of reading from shadow DOM
  • Tests and docs updated for the new two-argument signature

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/component/types.ts Updated ComponentHookWithProps type to accept two props args
src/component/component.ts Added cloneProps(), overloaded render(), snapshot props before sync
src/component/library.ts Refactored skip-render helpers to compare old/new props; removed getShadowLabelText
tests/component.test.ts Updated existing test, added re-render coverage for non-value prop changes
docs/guide/components.md Updated lifecycle docs for new signature
README.md Updated example for new signature

Signed-off-by: Jonas Pfalzgraf <info@josunlp.de>
@JosunLP JosunLP requested a review from Copilot March 13, 2026 18:40
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends the beforeUpdate lifecycle hook to receive both newProps and oldProps, enabling prop diffing without querying shadow DOM. It refactors the default input/textarea components to use prop-based comparison instead of DOM queries.

Changes:

  • Extends beforeUpdate signature to (newProps, oldProps) across types, runtime, docs, and tests
  • Refactors default input/textarea canSkipRender helpers to compare old vs new props directly
  • Adds test coverage for the new hook signature and re-render behavior on non-value prop changes

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/component/types.ts Adds new hook type aliases for the two-arg signature (duplicates existing ones — bug)
src/component/component.ts Adds cloneProps(), render overloads, and snapshots props before sync
src/component/library.ts Refactors canSkipInputRender/canSkipTextareaRender to use oldProps instead of DOM queries
tests/component.test.ts Updates existing test and adds new test for non-value prop re-renders
docs/guide/components.md Updates lifecycle docs to show new (newProps, oldProps) signature
README.md Updates example to show new hook signature
Comments suppressed due to low confidence (1)

src/component/component.ts:158

  • Bug: previousProps is computed on line 153 but never used. Line 158 still passes { name, oldValue, newValue } (an AttributeChange object) as the second argument to render(). This means beforeUpdate will receive the AttributeChange object as oldProps instead of the actual previous props snapshot. This should be this.render(true, previousProps) (and the AttributeChange can be passed separately if updated() still needs it).
        const previousProps = this.cloneProps();
        this.syncProps();

        if (this.hasMounted) {
          // Component already mounted - trigger update render
          this.render(true, { name, oldValue, newValue });

@JosunLP
Copy link
Copy Markdown
Collaborator

JosunLP commented Mar 13, 2026

@copilot apply changes based on the comments in this thread

Co-authored-by: JosunLP <20913954+JosunLP@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 13, 2026

@copilot apply changes based on the comments in this thread

Applied in d43e350. I removed the duplicate lifecycle hook aliases in src/component/types.ts and fixed render() in src/component/component.ts so update renders receive oldProps separately from AttributeChange. The failing type checks for this branch now pass. No UI changes were involved.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Extends the beforeUpdate lifecycle hook to receive both newProps and oldProps, enabling prop diffing without querying shadow DOM. Default input/textarea components are refactored to use prop-based comparison instead of DOM reads.

Changes:

  • Updated beforeUpdate signature from (props) to (newProps, oldProps) across types, runtime, and default components
  • Refactored canSkipInputRender/canSkipTextareaRender to compare old vs new props directly instead of reading shadow DOM
  • Added tests and updated docs to reflect the new two-argument hook signature

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/component/types.ts Updated ComponentHookWithProps overloads to accept (newProps, oldProps)
src/component/component.ts Added cloneProps(), updated render() overloads to pass previous props snapshot
src/component/library.ts Refactored skip-render helpers to use prop diffing; removed getShadowLabelText
tests/component.test.ts Updated existing test and added new test for non-value prop re-renders
docs/guide/components.md Updated lifecycle docs with new hook signature
README.md Updated example to show (newProps, oldProps) usage

@JosunLP JosunLP merged commit 00f0c59 into development Mar 13, 2026
14 checks passed
@JosunLP JosunLP deleted the copilot/fr-5-add-beforeupdate-props branch March 13, 2026 18:51
JosunLP added a commit that referenced this pull request Mar 15, 2026
* 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>
JosunLP added a commit that referenced this pull request Mar 27, 2026
* 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component Changes to the component module docs Changes to the documentation tests Chenges to the tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants