This commit is contained in:
edison 2025-06-19 09:53:21 +08:00 committed by GitHub
commit b4d0d24b2e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 46 additions and 0 deletions

View File

@ -17,6 +17,7 @@ import {
render, render,
renderSlot, renderSlot,
useHost, useHost,
useHostInternals,
useShadowRoot, useShadowRoot,
} from '../src' } from '../src'
@ -1399,6 +1400,21 @@ describe('defineCustomElement', () => {
const style = el.shadowRoot?.querySelector('style')! const style = el.shadowRoot?.querySelector('style')!
expect(style.textContent).toBe(`div { color: red; }`) expect(style.textContent).toBe(`div { color: red; }`)
}) })
// wait for jsdom to fix https://github.com/jsdom/jsdom/issues/3732
test.todo('useHostInternals', async () => {
const Foo = defineCustomElement({
setup() {
const internals = useHostInternals()!
internals.ariaLive = 'polite'
return () => h('div', 'hello')
},
})
customElements.define('my-el-use-host-internals', Foo)
container.innerHTML = `<my-el-use-host-internals>`
const el = container.childNodes[0] as VueElement
expect(el._internals?.ariaLive).toBe('polite')
})
}) })
describe('expose', () => { describe('expose', () => {
@ -1712,6 +1728,20 @@ describe('defineCustomElement', () => {
expect(e.shadowRoot!.innerHTML).toBe(`false,boolean`) expect(e.shadowRoot!.innerHTML).toBe(`false,boolean`)
}) })
test('support attachInternals method', () => {
const E = defineCustomElement({
formAssociated: true,
render() {
return h('div', 'hello')
},
})
customElements.define('my-el-attach-internals', E)
container.innerHTML = `<my-el-attach-internals></my-el-attach-internals>`
const e = container.childNodes[0] as VueElement
expect(e.shadowRoot!.innerHTML).toBe(`<div>hello</div>`)
expect(e._internals).toBeTruthy()
})
test('hyphenated attr removal', async () => { test('hyphenated attr removal', async () => {
const E = defineCustomElement({ const E = defineCustomElement({
props: { props: {

View File

@ -176,6 +176,8 @@ export function defineCustomElement(
if (isPlainObject(Comp)) extend(Comp, extraOptions) if (isPlainObject(Comp)) extend(Comp, extraOptions)
class VueCustomElement extends VueElement { class VueCustomElement extends VueElement {
static def = Comp static def = Comp
static formAssociated = !!options.formAssociated
constructor(initialProps?: Record<string, any>) { constructor(initialProps?: Record<string, any>) {
super(Comp, initialProps, _createApp) super(Comp, initialProps, _createApp)
} }
@ -204,6 +206,7 @@ export class VueElement
implements ComponentCustomElementInterface implements ComponentCustomElementInterface
{ {
_isVueCE = true _isVueCE = true
_internals: ElementInternals | null = null
/** /**
* @internal * @internal
*/ */
@ -253,6 +256,9 @@ export class VueElement
private _createApp: CreateAppFunction<Element> = createApp, private _createApp: CreateAppFunction<Element> = createApp,
) { ) {
super() super()
if (this.attachInternals) this._internals = this.attachInternals()
if (this.shadowRoot && _createApp !== createApp) { if (this.shadowRoot && _createApp !== createApp) {
this._root = this.shadowRoot this._root = this.shadowRoot
} else { } else {
@ -716,3 +722,12 @@ export function useShadowRoot(): ShadowRoot | null {
const el = __DEV__ ? useHost('useShadowRoot') : useHost() const el = __DEV__ ? useHost('useShadowRoot') : useHost()
return el && el.shadowRoot return el && el.shadowRoot
} }
/**
* Retrieve the ElementInternals of the current custom element. Only usable in setup()
* of a `defineCustomElement` component.
*/
export function useHostInternals(): ElementInternals | null {
const el = __DEV__ ? useHost('useHostInternals') : useHost()
return el && el._internals
}

View File

@ -256,6 +256,7 @@ export {
defineSSRCustomElement, defineSSRCustomElement,
useShadowRoot, useShadowRoot,
useHost, useHost,
useHostInternals,
VueElement, VueElement,
type VueElementConstructor, type VueElementConstructor,
type CustomElementOptions, type CustomElementOptions,