diff --git a/src/components/chat/chat-message.ts b/src/components/chat/chat-message.ts index 5a3399dc2..dfd0c3477 100644 --- a/src/components/chat/chat-message.ts +++ b/src/components/chat/chat-message.ts @@ -272,7 +272,6 @@ export default class IgcChatMessageComponent extends HydrationMixin( ) { return html` { expect(host.internals.labels).to.be.null; }); + + describe('of a form associated host', () => { + let tag: string; + let tagName: ReturnType; + + type Fixture = LitElement & { internals: ElementInternalsController }; + + before(() => { + tag = defineCE( + class extends LitElement { + public static formAssociated = true; + public internals = addInternalsController(this); + } + ); + tagName = unsafeStatic(tag); + }); + + afterEach(() => sinon.restore()); + + it('does not create the live list when no label can name the host', async () => { + const root = await fixture( + html`
<${tagName}>
` + ); + const host = root.querySelector(tag)!; + const read = sinon.spy(ElementInternals.prototype, 'labels', ['get']); + + expect(host.internals.labels).to.be.null; + expect(read.get.called).to.be.false; + }); + + it('returns the label that names the host id', async () => { + const root = await fixture( + html`
+ <${tagName} id="named"> +
` + ); + const host = root.querySelector('#named')!; + + expect(host.internals.labels).to.eql([root.querySelector('label')]); + }); + + it('returns an ancestor label', async () => { + const label = await fixture( + html`` + ); + const host = label.querySelector(tag)!; + + expect(host.internals.labels).to.eql([label]); + }); + }); }); }); diff --git a/src/internals/controllers/internals.ts b/src/internals/controllers/internals.ts index 9eea6337b..fbf14e7c0 100644 --- a/src/internals/controllers/internals.ts +++ b/src/internals/controllers/internals.ts @@ -1,7 +1,8 @@ -import type { - LitElement, - ReactiveController, - ReactiveControllerHost, +import { + isServer, + type LitElement, + type ReactiveController, + type ReactiveControllerHost, } from 'lit'; import { sameItems } from '../utils/arrays.js'; import { setOrRemoveAttribute } from '../utils/dom.js'; @@ -106,9 +107,17 @@ class ElementInternalsController implements ReactiveController { return this._labels; } - const host = this._host.constructor as { formAssociated?: boolean }; - // The getter throws for an element that is not form associated. - const list = host.formAssociated ? this._internals.labels : null; + const host = this._host; + // The getter throws unless the host is form associated. Without an id or an + // ancestor `