From 7908ef98ec5acf0f58834dc7504ae58501d620b8 Mon Sep 17 00:00:00 2001 From: AK <144495202+AKnassa@users.noreply.github.com> Date: Sun, 12 Jul 2026 02:05:28 -0400 Subject: [PATCH 1/2] fix: clear pre-upgrade observable property shadowing on element upgrade --- ...-9c4b1e27-6a3f-4d55-b0a1-7e2c8f13d4ab.json | 7 + .../components/element-controller.pw.spec.ts | 461 ++++++++++++------ .../src/components/element-controller.ts | 43 +- 3 files changed, 340 insertions(+), 171 deletions(-) create mode 100644 change/@microsoft-fast-element-9c4b1e27-6a3f-4d55-b0a1-7e2c8f13d4ab.json diff --git a/change/@microsoft-fast-element-9c4b1e27-6a3f-4d55-b0a1-7e2c8f13d4ab.json b/change/@microsoft-fast-element-9c4b1e27-6a3f-4d55-b0a1-7e2c8f13d4ab.json new file mode 100644 index 00000000000..0cc3fd48162 --- /dev/null +++ b/change/@microsoft-fast-element-9c4b1e27-6a3f-4d55-b0a1-7e2c8f13d4ab.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Fix observable properties set to undefined before element upgrade permanently shadowing the observable accessor.", + "packageName": "@microsoft/fast-element", + "email": "144495202+AKnassa@users.noreply.github.com", + "dependentChangeType": "none" +} diff --git a/packages/fast-element/src/components/element-controller.pw.spec.ts b/packages/fast-element/src/components/element-controller.pw.spec.ts index 31d13f6df1d..a2fc996082f 100644 --- a/packages/fast-element/src/components/element-controller.pw.spec.ts +++ b/packages/fast-element/src/components/element-controller.pw.spec.ts @@ -23,7 +23,7 @@ test.describe("The ElementController", () => { FASTElementDefinition.compose( class ControllerTest extends FASTElement { static definition = { name }; - } + }, ).define(); const element = document.createElement(name); @@ -51,7 +51,7 @@ test.describe("The ElementController", () => { FASTElementDefinition.compose( class ControllerTest extends FASTElement { static definition = { name, shadowOptions: { mode: "open" } }; - } + }, ).define(); const element = document.createElement(name); @@ -81,7 +81,7 @@ test.describe("The ElementController", () => { FASTElementDefinition.compose( class ControllerTest extends FASTElement { static definition = { name, shadowOptions: null }; - } + }, ).define(); const element = document.createElement(name); @@ -111,7 +111,7 @@ test.describe("The ElementController", () => { FASTElementDefinition.compose( class ControllerTest extends FASTElement { static definition = { name, shadowOptions: { mode: "closed" } }; - } + }, ).define(); const element = document.createElement(name); @@ -139,7 +139,7 @@ test.describe("The ElementController", () => { FASTElementDefinition.compose( class ControllerTest extends FASTElement { static definition = { name }; - } + }, ).define(); const element = document.createElement(name); @@ -177,7 +177,7 @@ test.describe("The ElementController", () => { FASTElementDefinition.compose( class ControllerTest extends FASTElement { static definition = { name }; - } + }, ).define(); const element = document.createElement(name); @@ -218,7 +218,7 @@ test.describe("The ElementController", () => { FASTElementDefinition.compose( class ControllerTest extends FASTElement { static definition = { name, shadowOptions: null }; - } + }, ).define(); const element = document.createElement(name); @@ -264,7 +264,7 @@ test.describe("The ElementController", () => { ${templateA} `, }; - } + }, ).define(); const element = document.createElement(name); @@ -276,7 +276,7 @@ test.describe("The ElementController", () => { return { beforeConnect, afterConnect }; }, - templateA + templateA, ); expect(beforeConnect).toBe(""); @@ -313,7 +313,7 @@ test.describe("The ElementController", () => { ${templateA} `, }; - } + }, ).define(); const element = document.createElement(name); @@ -325,7 +325,7 @@ test.describe("The ElementController", () => { return { beforeConnect, afterConnect }; }, - templateA + templateA, ); expect(beforeConnect).toBe(""); @@ -361,7 +361,7 @@ test.describe("The ElementController", () => { ${templateA} `, }; - } + }, ).define(); const element = document.createElement(name); @@ -376,7 +376,7 @@ test.describe("The ElementController", () => { return { beforeConnect, afterConnect }; }, - { templateA, templateB } + { templateA, templateB }, ); expect(beforeConnect).toBe(""); @@ -413,7 +413,7 @@ test.describe("The ElementController", () => { ${templateA} `, }; - } + }, ).define(); const element = document.createElement(name); @@ -428,7 +428,7 @@ test.describe("The ElementController", () => { return { beforeConnect, afterConnect }; }, - { templateA, templateB } + { templateA, templateB }, ); expect(beforeConnect).toBe(""); @@ -466,7 +466,7 @@ test.describe("The ElementController", () => { ${templateA} `; } - } + }, ).define(); const element = document.createElement(name); @@ -478,7 +478,7 @@ test.describe("The ElementController", () => { return { beforeConnect, afterConnect }; }, - templateA + templateA, ); expect(beforeConnect).toBe(""); @@ -516,7 +516,7 @@ test.describe("The ElementController", () => { ${templateA} `; } - } + }, ).define(); const element = document.createElement(name); @@ -528,7 +528,7 @@ test.describe("The ElementController", () => { return { beforeConnect, afterConnect }; }, - templateA + templateA, ); expect(beforeConnect).toBe(""); @@ -566,7 +566,7 @@ test.describe("The ElementController", () => { ${templateA} `; } - } + }, ).define(); const element = document.createElement(name); @@ -581,7 +581,7 @@ test.describe("The ElementController", () => { return { beforeConnect, afterConnect }; }, - { templateA, templateB } + { templateA, templateB }, ); expect(beforeConnect).toBe(""); @@ -619,7 +619,7 @@ test.describe("The ElementController", () => { ${templateA} `; } - } + }, ).define(); const element = document.createElement(name); @@ -634,7 +634,7 @@ test.describe("The ElementController", () => { return { beforeConnect, afterConnect }; }, - { templateA, templateB } + { templateA, templateB }, ); expect(beforeConnect).toBe(""); @@ -667,7 +667,7 @@ test.describe("The ElementController", () => { FASTElementDefinition.compose( class ControllerTest extends FASTElement { static definition = { name }; - } + }, ).define(); const element = document.createElement(name); @@ -724,7 +724,7 @@ test.describe("The ElementController", () => { FASTElementDefinition.compose( class ControllerTest extends FASTElement { static definition = { name, styles: stylesA }; - } + }, ).define(); const element = document.createElement(name); @@ -782,7 +782,7 @@ test.describe("The ElementController", () => { FASTElementDefinition.compose( class ControllerTest extends FASTElement { static definition = { name, styles: stylesA }; - } + }, ).define(); const element = document.createElement(name); @@ -803,7 +803,7 @@ test.describe("The ElementController", () => { cssText, }; }, - { cssA, cssB } + { cssA, cssB }, ); if (supportsAdoptedStyleSheets) { @@ -846,7 +846,7 @@ test.describe("The ElementController", () => { resolveStyles() { return stylesA; } - } + }, ).define(); const element = document.createElement(name); @@ -907,7 +907,7 @@ test.describe("The ElementController", () => { resolveStyles() { return stylesA; } - } + }, ).define(); const element = document.createElement(name); @@ -928,7 +928,7 @@ test.describe("The ElementController", () => { cssText, }; }, - { cssA, cssB } + { cssA, cssB }, ); if (supportsAdoptedStyleSheets) { @@ -970,7 +970,7 @@ test.describe("The ElementController", () => { ${templateA} `, }; - } + }, ).define(); const element = document.createElement(name); @@ -986,7 +986,7 @@ test.describe("The ElementController", () => { return { beforeConnect, afterConnect, afterChange }; }, - { templateA, templateB } + { templateA, templateB }, ); expect(beforeConnect).toBe(""); @@ -1026,7 +1026,7 @@ test.describe("The ElementController", () => { ${templateA} `, }; - } + }, ).define(); const element = document.createElement(name); @@ -1042,7 +1042,7 @@ test.describe("The ElementController", () => { return { beforeConnect, afterConnect, afterChange }; }, - { templateA, templateB } + { templateA, templateB }, ); expect(beforeConnect).toBe(""); @@ -1092,7 +1092,7 @@ test.describe("The ElementController", () => { FASTElementDefinition.compose( class ControllerTest extends FASTElement { static definition = { name, styles: stylesA }; - } + }, ).define(); const element = document.createElement(name); @@ -1118,7 +1118,7 @@ test.describe("The ElementController", () => { lengthAfterChange, }; }, - { cssA, cssB } + { cssA, cssB }, ); if (supportsAdoptedStyleSheets) { @@ -1147,7 +1147,7 @@ test.describe("The ElementController", () => { FASTElementDefinition.compose( class ControllerTest extends FASTElement { static definition = { name }; - } + }, ).define(); const element = document.createElement(name); @@ -1178,7 +1178,7 @@ test.describe("The ElementController", () => { FASTElementDefinition.compose( class ControllerTest extends FASTElement { static definition = { name }; - } + }, ).define(); const element = document.createElement(name); @@ -1199,7 +1199,7 @@ test.describe("The ElementController", () => { const afterRemove = attached; return { initialAttached, afterAppend, afterRemove }; - } + }, ); expect(initialAttached).toBe(false); @@ -1223,7 +1223,7 @@ test.describe("The ElementController", () => { FASTElementDefinition.compose( class ControllerTest extends FASTElement { static definition = { name }; - } + }, ).define(); const element = document.createElement(name); @@ -1259,7 +1259,7 @@ test.describe("The ElementController", () => { FASTElementDefinition.compose( class ControllerTest extends FASTElement { static definition = { name }; - } + }, ).define(); const element = document.createElement(name); @@ -1295,7 +1295,7 @@ test.describe("The ElementController", () => { FASTElementDefinition.compose( class ControllerTest extends FASTElement { static definition = { name }; - } + }, ).define(); const element = document.createElement(name); @@ -1341,7 +1341,7 @@ test.describe("The ElementController", () => { ${templateA} `, }; - } + }, ).define(); const element = document.createElement(name); @@ -1358,7 +1358,7 @@ test.describe("The ElementController", () => { return { beforeAdd, afterAdd, afterRemove }; }, - templateA + templateA, ); expect(beforeAdd).toBe(false); @@ -1382,7 +1382,7 @@ test.describe("The ElementController", () => { FASTElementDefinition.compose( class ControllerTest extends FASTElement { static definition = { name }; - } + }, ).define(); const element = document.createElement(name); @@ -1418,16 +1418,40 @@ test.describe("The ElementController", () => { FASTElementDefinition.compose( class ControllerTest extends FASTElement { static definition = { name }; - } + }, ).define(); const element = document.createElement(name) as any; const controller = ElementController.forCustomElement(element); const behaviors = [ - { bound: false, connectedCallback() { this.bound = true; }, disconnectedCallback() { this.bound = false; } }, - { bound: false, connectedCallback() { this.bound = true; }, disconnectedCallback() { this.bound = false; } }, - { bound: false, connectedCallback() { this.bound = true; }, disconnectedCallback() { this.bound = false; } }, + { + bound: false, + connectedCallback() { + this.bound = true; + }, + disconnectedCallback() { + this.bound = false; + }, + }, + { + bound: false, + connectedCallback() { + this.bound = true; + }, + disconnectedCallback() { + this.bound = false; + }, + }, + { + bound: false, + connectedCallback() { + this.bound = true; + }, + disconnectedCallback() { + this.bound = false; + }, + }, ]; behaviors.forEach(x => controller.addBehavior(x)); @@ -1462,7 +1486,7 @@ test.describe("The ElementController", () => { FASTElementDefinition.compose( class ControllerTest extends FASTElement { static definition = { name }; - } + }, ).define(); const element = document.createElement(name) as any; @@ -1506,7 +1530,7 @@ test.describe("The ElementController", () => { FASTElementDefinition.compose( class ControllerTest extends FASTElement { static definition = { name }; - } + }, ).define(); const element = document.createElement(name) as any; @@ -1554,7 +1578,7 @@ test.describe("The ElementController", () => { FASTElementDefinition.compose( class ControllerTest extends FASTElement { static definition = { name }; - } + }, ).define(); const element = document.createElement(name) as any; @@ -1562,8 +1586,12 @@ test.describe("The ElementController", () => { const behavior = { bound: false, - connectedCallback() { this.bound = true; }, - disconnectedCallback() { this.bound = false; }, + connectedCallback() { + this.bound = true; + }, + disconnectedCallback() { + this.bound = false; + }, }; document.body.appendChild(element); @@ -1608,7 +1636,7 @@ test.describe("The ElementController", () => { FASTElementDefinition.compose( class ControllerTest extends FASTElement { static definition = { name }; - } + }, ).define(); const element = document.createElement(name) as any; @@ -1616,8 +1644,12 @@ test.describe("The ElementController", () => { const behavior = { bound: false, - connectedCallback() { this.bound = true; }, - disconnectedCallback() { this.bound = false; }, + connectedCallback() { + this.bound = true; + }, + disconnectedCallback() { + this.bound = false; + }, }; document.body.appendChild(element); @@ -1657,7 +1689,7 @@ test.describe("The ElementController", () => { FASTElementDefinition.compose( class ControllerTest extends FASTElement { static definition = { name }; - } + }, ).define(); const element = document.createElement(name) as any; @@ -1666,8 +1698,12 @@ test.describe("The ElementController", () => { let connectedCalled = false; let disconnectedCalled = false; const behavior = { - connectedCallback() { connectedCalled = true; }, - disconnectedCallback() { disconnectedCalled = true; }, + connectedCallback() { + connectedCalled = true; + }, + disconnectedCallback() { + disconnectedCalled = true; + }, }; controller.addStyles(css``.withBehaviors(behavior)); @@ -1678,8 +1714,11 @@ test.describe("The ElementController", () => { controller.disconnect(); const disconnected = disconnectedCalled; - return { connectedCalled: connected, disconnectedCalled: disconnected }; - } + return { + connectedCalled: connected, + disconnectedCalled: disconnected, + }; + }, ); expect(connectedCalled).toBe(true); @@ -1707,8 +1746,12 @@ test.describe("The ElementController", () => { let connectedCalled = false; let disconnectedCalled = false; const behavior = { - connectedCallback() { connectedCalled = true; }, - disconnectedCallback() { disconnectedCalled = true; }, + connectedCallback() { + connectedCalled = true; + }, + disconnectedCallback() { + disconnectedCalled = true; + }, }; FASTElementDefinition.compose( @@ -1717,7 +1760,7 @@ test.describe("The ElementController", () => { name, styles: css``.withBehaviors(behavior), }; - } + }, ).define(); const element = document.createElement(name) as any; @@ -1729,8 +1772,11 @@ test.describe("The ElementController", () => { controller.disconnect(); const disconnected = disconnectedCalled; - return { connectedCalled: connected, disconnectedCalled: disconnected }; - } + return { + connectedCalled: connected, + disconnectedCalled: disconnected, + }; + }, ); expect(connectedCalled).toBe(true); @@ -1767,8 +1813,12 @@ test.describe("The ElementController", () => { let connectCount = 0; let disconnectCount = 0; const behavior = { - connectedCallback() { connectCount++; }, - disconnectedCallback() { disconnectCount++; }, + connectedCallback() { + connectCount++; + }, + disconnectedCallback() { + disconnectCount++; + }, }; FASTElementDefinition.compose( @@ -1777,7 +1827,7 @@ test.describe("The ElementController", () => { name, styles: css``.withBehaviors(behavior), }; - } + }, ).define(); const element = document.createElement(name) as any; @@ -1827,7 +1877,7 @@ test.describe("The ElementController", () => { FASTElementDefinition.compose( class ControllerTest extends FASTElement { static definition = { name }; - } + }, ).define(); const element = document.createElement(name) as any; @@ -1836,8 +1886,12 @@ test.describe("The ElementController", () => { let addedCalled = false; let removedCalled = false; const behavior = { - addedCallback() { addedCalled = true; }, - removedCallback() { removedCalled = true; }, + addedCallback() { + addedCalled = true; + }, + removedCallback() { + removedCalled = true; + }, }; const styles = css``.withBehaviors(behavior); @@ -1861,12 +1915,8 @@ test.describe("The ElementController", () => { const innerHTML = await page.evaluate(async () => { // @ts-expect-error: Client module. - const { - FASTElement, - FASTElementDefinition, - html, - uniqueElementName, - } = await import("/main.js"); + const { FASTElement, FASTElementDefinition, html, uniqueElementName } = + await import("/main.js"); const name = uniqueElementName(); const element = document.createElement(name); @@ -1881,7 +1931,7 @@ test.describe("The ElementController", () => { name, template: html`Test 2`, }; - } + }, ).define(); const result = root.innerHTML; @@ -1893,6 +1943,121 @@ test.describe("The ElementController", () => { }); }); + test.describe("when observables are set before upgrade", () => { + test("should make a property reactive when it was set to undefined before upgrade", async ({ + page, + }) => { + await page.goto("/"); + + const result = await page.evaluate(async () => { + // @ts-expect-error: Client module. + const { + FASTElement, + FASTElementDefinition, + Observable, + Updates, + html, + uniqueElementName, + } = await import("/main.js"); + + const name = uniqueElementName(); + const element = document.createElement(name) as any; + document.body.appendChild(element); + + // The binding engine writes the parent's uninitialized property + // to the not-yet-upgraded child. + element.value = undefined; + + const shadowedBeforeUpgrade = Object.prototype.hasOwnProperty.call( + element, + "value", + ); + + class Test extends FASTElement { + static definition = { + name, + template: html`${(x: any) => x.value}`, + }; + } + + Observable.defineProperty(Test.prototype, "value"); + FASTElementDefinition.compose(Test).define(); + + await Updates.next(); + + element.value = "updated"; + + await Updates.next(); + + const text = element.shadowRoot?.textContent ?? ""; + document.body.removeChild(element); + + return { + shadowedBeforeUpgrade, + shadowedAfterUpgrade: Object.prototype.hasOwnProperty.call( + element, + "value", + ), + text, + }; + }); + + expect(result.shadowedBeforeUpgrade).toBe(true); + expect(result.shadowedAfterUpgrade).toBe(false); + expect(result.text).toBe("updated"); + }); + + test("should replay a property that was set to a defined value before upgrade", async ({ + page, + }) => { + await page.goto("/"); + + const result = await page.evaluate(async () => { + // @ts-expect-error: Client module. + const { + FASTElement, + FASTElementDefinition, + Observable, + Updates, + html, + uniqueElementName, + } = await import("/main.js"); + + const name = uniqueElementName(); + const element = document.createElement(name) as any; + document.body.appendChild(element); + + element.value = "initial"; + + class Test extends FASTElement { + static definition = { + name, + template: html`${(x: any) => x.value}`, + }; + } + + Observable.defineProperty(Test.prototype, "value"); + FASTElementDefinition.compose(Test).define(); + + await Updates.next(); + + const initialText = element.shadowRoot?.textContent ?? ""; + + element.value = "updated"; + + await Updates.next(); + + const updatedText = element.shadowRoot?.textContent ?? ""; + document.body.removeChild(element); + + return { initialText, updatedText }; + }); + + expect(result.initialText).toBe("initial"); + expect(result.updatedText).toBe("updated"); + }); + }); + test("should ensure proper invocation order of state, rendering, and behaviors during connection and disconnection", async ({ page, }) => { @@ -1940,7 +2105,7 @@ test.describe("The ElementController", () => { FASTElementDefinition.compose( class extends Test { static definition = { name, template }; - } + }, ).define(); const element = document.createElement(name) as any; @@ -1948,12 +2113,10 @@ test.describe("The ElementController", () => { Observable.getNotifier(controller).subscribe( { handleChange() { - orderLog.push( - `isConnected set ${controller.isConnected}` - ); + orderLog.push(`isConnected set ${controller.isConnected}`); }, }, - "isConnected" + "isConnected", ); controller.addBehavior({ connectedCallback() { @@ -1995,41 +2158,39 @@ test.describe("The HydratableElementController", () => { }) => { await page.goto("/"); - const { hasDeferHydration, hasNeedsHydration } = await page.evaluate( - async () => { - // @ts-expect-error: Client module. - const { - FASTElement, - FASTElementDefinition, - ElementController, - HydratableElementController, - needsHydrationAttribute, - deferHydrationAttribute, - uniqueElementName, - } = await import("/main.js"); + const { hasDeferHydration, hasNeedsHydration } = await page.evaluate(async () => { + // @ts-expect-error: Client module. + const { + FASTElement, + FASTElementDefinition, + ElementController, + HydratableElementController, + needsHydrationAttribute, + deferHydrationAttribute, + uniqueElementName, + } = await import("/main.js"); - HydratableElementController.install(); + HydratableElementController.install(); - const name = uniqueElementName(); - FASTElementDefinition.compose( - class ControllerTest extends FASTElement { - static definition = { name }; - } - ).define(); + const name = uniqueElementName(); + FASTElementDefinition.compose( + class ControllerTest extends FASTElement { + static definition = { name }; + }, + ).define(); - const element = document.createElement(name) as any; - element.setAttribute("needs-hydration", ""); - ElementController.forCustomElement(element); + const element = document.createElement(name) as any; + element.setAttribute("needs-hydration", ""); + ElementController.forCustomElement(element); - const result = { - hasDeferHydration: element.hasAttribute(deferHydrationAttribute), - hasNeedsHydration: element.hasAttribute(needsHydrationAttribute), - }; + const result = { + hasDeferHydration: element.hasAttribute(deferHydrationAttribute), + hasNeedsHydration: element.hasAttribute(needsHydrationAttribute), + }; - ElementController.setStrategy(ElementController); - return result; - } - ); + ElementController.setStrategy(ElementController); + return result; + }); expect(hasDeferHydration).toBe(false); expect(hasNeedsHydration).toBe(true); @@ -2040,48 +2201,46 @@ test.describe("The HydratableElementController", () => { }) => { await page.goto("/"); - const { hasDeferHydration, hasNeedsHydration } = await page.evaluate( - async () => { - // @ts-expect-error: Client module. - const { - FASTElement, - FASTElementDefinition, - ElementController, - HydratableElementController, - needsHydrationAttribute, - deferHydrationAttribute, - uniqueElementName, - } = await import("/main.js"); + const { hasDeferHydration, hasNeedsHydration } = await page.evaluate(async () => { + // @ts-expect-error: Client module. + const { + FASTElement, + FASTElementDefinition, + ElementController, + HydratableElementController, + needsHydrationAttribute, + deferHydrationAttribute, + uniqueElementName, + } = await import("/main.js"); - ElementController.setStrategy(HydratableElementController); + ElementController.setStrategy(HydratableElementController); - const name = uniqueElementName(); - FASTElementDefinition.compose( - class ControllerTest extends FASTElement { - static definition = { - name, - shadowOptions: null, - template: undefined, - templateOptions: "defer-and-hydrate", - }; - } - ).define(); + const name = uniqueElementName(); + FASTElementDefinition.compose( + class ControllerTest extends FASTElement { + static definition = { + name, + shadowOptions: null, + template: undefined, + templateOptions: "defer-and-hydrate", + }; + }, + ).define(); - const element = document.createElement(name) as any; - const controller = ElementController.forCustomElement(element); - controller.connect(); + const element = document.createElement(name) as any; + const controller = ElementController.forCustomElement(element); + controller.connect(); - controller.shadowOptions = { mode: "open" }; + controller.shadowOptions = { mode: "open" }; - const result = { - hasDeferHydration: element.hasAttribute(deferHydrationAttribute), - hasNeedsHydration: element.hasAttribute(needsHydrationAttribute), - }; + const result = { + hasDeferHydration: element.hasAttribute(deferHydrationAttribute), + hasNeedsHydration: element.hasAttribute(needsHydrationAttribute), + }; - ElementController.setStrategy(ElementController); - return result; - } - ); + ElementController.setStrategy(ElementController); + return result; + }); expect(hasDeferHydration).toBe(true); expect(hasNeedsHydration).toBe(true); diff --git a/packages/fast-element/src/components/element-controller.ts b/packages/fast-element/src/components/element-controller.ts index 1a07b178d87..6db3027ad68 100644 --- a/packages/fast-element/src/components/element-controller.ts +++ b/packages/fast-element/src/components/element-controller.ts @@ -309,8 +309,11 @@ export class ElementController const propertyName = accessors[i].name as keyof TElement; const value = (element as any)[propertyName]; - if (value !== void 0) { + if (Object.prototype.hasOwnProperty.call(element, propertyName)) { delete element[propertyName]; + } + + if (value !== void 0) { boundObservables[propertyName] = value; } } @@ -409,7 +412,7 @@ export class ElementController * @param styles - the styles to remove. */ public removeStyles( - styles: ElementStyles | HTMLStyleElement | null | undefined + styles: ElementStyles | HTMLStyleElement | null | undefined, ): void { if (!styles) { return; @@ -542,7 +545,7 @@ export class ElementController public onAttributeChangedCallback( name: string, oldValue: string | null, - newValue: string | null + newValue: string | null, ): void { const attrDef = this.definition.attributeLookup[name]; @@ -562,11 +565,11 @@ export class ElementController public emit( type: string, detail?: any, - options?: Omit + options?: Omit, ): void | boolean { if (this.stage === Stages.connected) { return this.source.dispatchEvent( - new CustomEvent(type, { detail, ...defaultEventOptions, ...options }) + new CustomEvent(type, { detail, ...defaultEventOptions, ...options }), ); } @@ -619,7 +622,7 @@ export class ElementController */ public static forCustomElement( element: HTMLElement, - override: boolean = false + override: boolean = false, ): ElementController { const controller: ElementController = (element as any).$fastController; @@ -640,7 +643,7 @@ export class ElementController (element as FASTElement).$fastController.connect(); }, }, - "template" + "template", ); Observable.getNotifier(definition).subscribe( @@ -650,12 +653,12 @@ export class ElementController (element as FASTElement).$fastController.connect(); }, }, - "shadowOptions" + "shadowOptions", ); return ((element as any).$fastController = new elementControllerStrategy( element, - definition + definition, )); } @@ -766,7 +769,7 @@ export class StyleElementStrategy implements StyleStrategy { public removeStylesFrom(target: StyleTarget): void { target = usableStyleTarget(normalizeStyleTarget(target)); const styles: NodeListOf = target.querySelectorAll( - `.${this.styleClass}` + `.${this.styleClass}`, ); for (let i = 0, ii = styles.length; i < ii; ++i) { @@ -780,10 +783,10 @@ let addAdoptedStyleSheets = (target: Required, sheets: CSSStyleShee }; let removeAdoptedStyleSheets = ( target: Required, - sheets: CSSStyleSheet[] + sheets: CSSStyleSheet[], ) => { target.adoptedStyleSheets = target.adoptedStyleSheets!.filter( - (x: CSSStyleSheet) => sheets.indexOf(x) === -1 + (x: CSSStyleSheet) => sheets.indexOf(x) === -1, ); }; if (ElementStyles.supportsAdoptedStyleSheets) { @@ -827,7 +830,7 @@ export const needsHydrationAttribute = "needs-hydration"; * @public */ export interface HydrationControllerCallbacks< - TElement extends HTMLElement = HTMLElement + TElement extends HTMLElement = HTMLElement, > { /** * Called once when the first element enters the hydration pipeline. @@ -860,7 +863,7 @@ export interface HydrationControllerCallbacks< * @beta */ export class HydratableElementController< - TElement extends HTMLElement = HTMLElement + TElement extends HTMLElement = HTMLElement, > extends ElementController { /** * Controls whether the controller will hydrate during the connect() method. @@ -869,7 +872,7 @@ export class HydratableElementController< */ protected needsHydration?: boolean; private static hydrationObserver = new UnobservableMutationObserver( - HydratableElementController.hydrationObserverHandler + HydratableElementController.hydrationObserverHandler, ); /** @@ -952,7 +955,7 @@ export class HydratableElementController< if (deadline.didTimeout) { HydratableElementController.idleCallbackId = requestIdleCallback( HydratableElementController.checkHydrationComplete, - { timeout: 50 } + { timeout: 50 }, ); return; } @@ -1019,7 +1022,7 @@ export class HydratableElementController< try { HydratableElementController.lifecycleCallbacks.elementWillHydrate?.( - this.source + this.source, ); } catch { // A lifecycle callback must never prevent hydration. @@ -1054,7 +1057,7 @@ export class HydratableElementController< (this as Mutable).view = this.template.hydrate( firstChild, lastChild, - element + element, ); this.view?.bind(this.source); } else { @@ -1092,7 +1095,7 @@ export class HydratableElementController< try { HydratableElementController.lifecycleCallbacks.elementDidHydrate?.( - this.source + this.source, ); } catch { // A lifecycle callback must never prevent hydration. @@ -1114,7 +1117,7 @@ export class HydratableElementController< HydratableElementController.idleCallbackId = requestIdleCallback( HydratableElementController.checkHydrationComplete, - { timeout: 50 } + { timeout: 50 }, ); } } From 78a5ac7557fbde1fcfbb822060231d47511e8a03 Mon Sep 17 00:00:00 2001 From: AK <144495202+AKnassa@users.noreply.github.com> Date: Sun, 12 Jul 2026 03:16:16 -0400 Subject: [PATCH 2/2] chore: update generated export size reports --- packages/fast-element/SIZES.md | 4 ++-- sites/website/src/docs/2.x/resources/export-sizes.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/fast-element/SIZES.md b/packages/fast-element/SIZES.md index 65afceadf47..eb8692a9cc3 100644 --- a/packages/fast-element/SIZES.md +++ b/packages/fast-element/SIZES.md @@ -4,8 +4,8 @@ Bundle sizes for `@microsoft/fast-element` exports. | Export | Minified | Gzip | Brotli | |--------|----------|------|--------| -| CDN Rollup Bundle | 67.67 KB | 20.04 KB | 17.99 KB | -| FASTElement | 28.10 KB | 8.72 KB | 7.86 KB | +| CDN Rollup Bundle | 67.71 KB | 20.06 KB | 18.01 KB | +| FASTElement | 28.14 KB | 8.74 KB | 7.88 KB | | Updates | 3.61 KB | 1.48 KB | 1.26 KB | | Observable | 8.12 KB | 2.99 KB | 2.66 KB | | observable | 8.16 KB | 3.00 KB | 2.68 KB | diff --git a/sites/website/src/docs/2.x/resources/export-sizes.md b/sites/website/src/docs/2.x/resources/export-sizes.md index ce92cae5790..973a6ec3875 100644 --- a/sites/website/src/docs/2.x/resources/export-sizes.md +++ b/sites/website/src/docs/2.x/resources/export-sizes.md @@ -19,8 +19,8 @@ Bundle sizes for `@microsoft/fast-element` exports. | Export | Minified | Gzip | Brotli | |--------|----------|------|--------| -| CDN Rollup Bundle | 67.67 KB | 20.04 KB | 17.99 KB | -| FASTElement | 28.10 KB | 8.72 KB | 7.86 KB | +| CDN Rollup Bundle | 67.71 KB | 20.06 KB | 18.01 KB | +| FASTElement | 28.14 KB | 8.74 KB | 7.88 KB | | Updates | 3.61 KB | 1.48 KB | 1.26 KB | | Observable | 8.12 KB | 2.99 KB | 2.66 KB | | observable | 8.16 KB | 3.00 KB | 2.68 KB |