Skip to content

feat(runtime): support the clonable shadow root option - #6837

Open
Arul1998 wants to merge 2 commits into
stenciljs:mainfrom
Arul1998:feat/shadow-clonable
Open

feat(runtime): support the clonable shadow root option#6837
Arul1998 wants to merge 2 commits into
stenciljs:mainfrom
Arul1998:feat/shadow-clonable

Conversation

@Arul1998

Copy link
Copy Markdown
Contributor

Expose the native clonable shadow root option so it can be set via the @Component() decorator, e.g. shadow: { clonable: true }. When enabled, the component's shadow root is attached with { clonable: true }, allowing the shadow root and its contents to be preserved when the host element is deep-cloned (Node.cloneNode(true)). Libraries such as AG Grid rely on this when cloning DOM subtrees that contain shadow components; without it the clone is an empty shell.

This follows the existing delegatesFocus / slotAssignment pipeline end to end:

  • add clonable to the public ShadowRootOptions type
  • add the shadowClonable flag to CMP_FLAGS (1 << 11)
  • emit a clonable static getter from the decorator, parse it onto component compiler meta (shadowClonable), pack it into the runtime flags in formatComponentRuntimeMeta, and add the shadowClonable build conditional
  • set opts.clonable in createShadowRoot behind the BUILD.shadowClonable build flag

Carry clonable through the mock-doc / declarative shadow DOM path so the option is not lost across SSR + hydrate, where the shadow root is reused rather than re-attached: attachShadow records clonable on the mock shadow root and serialization emits the shadowrootclonable boolean attribute, mirroring shadowrootdelegatesfocus.

ShadowRootInit in the current TypeScript DOM lib does not yet include clonable, so the option type is widened locally where it is read. Add clonable and shadowrootclonable to the cspell wordlist.

Tests:

  • parse shadow: { clonable: true } into component meta, incl. scoped / no encapsulation / alongside delegatesFocus (parse-clonable.spec)
  • pack the shadowClonable flag into runtime meta (format-component-runtime-meta.spec)
  • createShadowRoot passes { clonable: true } to attachShadow (shadow-root.spec)
  • DSD serialization emits shadowrootclonable, incl. a toEqualHtml round-trip and a combined delegatesFocus + clonable case (serialize-node.spec)

Fixes #6833

What is the current behavior?

GitHub Issue Number: #6833

Stencil has no way to enable the native clonable option on a component's shadow root. shadow accepts delegatesFocus and slotAssignment, but not clonable, so createShadowRoot always calls attachShadow without it. As a result, when a host element containing a Stencil shadow component is deep-cloned (Node.cloneNode(true)), the shadow root is not preserved and the clone is an empty shell. Libraries such as AG Grid that deep-clone DOM subtrees cannot clone Stencil shadow components. There is no per-component opt-in and no way to carry the option through SSR/hydrate.

What is the new behavior?

clonable can now be set on the shadow options object:

@Component({
  tag: 'my-cmp',
  shadow: { clonable: true },
})
export class MyCmp {}

Expose the native `clonable` shadow root option so it can be set via the
`@Component()` decorator, e.g. `shadow: { clonable: true }`. When enabled, the
component's shadow root is attached with `{ clonable: true }`, allowing the
shadow root and its contents to be preserved when the host element is
deep-cloned (`Node.cloneNode(true)`). Libraries such as AG Grid rely on this
when cloning DOM subtrees that contain shadow components; without it the clone
is an empty shell.

This follows the existing `delegatesFocus` / `slotAssignment` pipeline end to
end:

- add `clonable` to the public `ShadowRootOptions` type
- add the `shadowClonable` flag to `CMP_FLAGS` (1 << 11)
- emit a `clonable` static getter from the decorator, parse it onto component
  compiler meta (`shadowClonable`), pack it into the runtime flags in
  `formatComponentRuntimeMeta`, and add the `shadowClonable` build conditional
- set `opts.clonable` in `createShadowRoot` behind the `BUILD.shadowClonable`
  build flag

Carry `clonable` through the mock-doc / declarative shadow DOM path so the
option is not lost across SSR + hydrate, where the shadow root is reused rather
than re-attached: `attachShadow` records `clonable` on the mock shadow root and
serialization emits the `shadowrootclonable` boolean attribute, mirroring
`shadowrootdelegatesfocus`.

`ShadowRootInit` in the current TypeScript DOM lib does not yet include
`clonable`, so the option type is widened locally where it is read. Add
`clonable` and `shadowrootclonable` to the cspell wordlist.

Tests:
- parse `shadow: { clonable: true }` into component meta, incl. scoped / no
  encapsulation / alongside delegatesFocus (parse-clonable.spec)
- pack the `shadowClonable` flag into runtime meta (format-component-runtime-meta.spec)
- `createShadowRoot` passes `{ clonable: true }` to `attachShadow` (shadow-root.spec)
- DSD serialization emits `shadowrootclonable`, incl. a toEqualHtml round-trip
  and a combined delegatesFocus + clonable case (serialize-node.spec)

Fixes stenciljs#6833
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.

feat: support clonable in the shadow option of @Component

1 participant