diff --git a/change/@microsoft-fast-element-00081d16-c6c4-4ceb-9f3e-83e02b28b92a.json b/change/@microsoft-fast-element-00081d16-c6c4-4ceb-9f3e-83e02b28b92a.json new file mode 100644 index 00000000000..aa5a589929b --- /dev/null +++ b/change/@microsoft-fast-element-00081d16-c6c4-4ceb-9f3e-83e02b28b92a.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "Adds volatile binding support for JavaScript optional chaining syntax", + "packageName": "@microsoft/fast-element", + "email": "nicholasrice@users.noreply.github.com", + "dependentChangeType": "prerelease" +} diff --git a/packages/web-components/fast-element/src/observation/observable.spec.ts b/packages/web-components/fast-element/src/observation/observable.spec.ts index fd7dc328794..0c308a2c6e1 100644 --- a/packages/web-components/fast-element/src/observation/observable.spec.ts +++ b/packages/web-components/fast-element/src/observation/observable.spec.ts @@ -1,7 +1,7 @@ import { expect } from "chai"; import { Updates } from "./update-queue.js"; import { PropertyChangeNotifier, SubscriberSet } from "./notifier.js"; -import { ExecutionContext, Observable, observable, volatile } from "./observable.js"; +import { ExecutionContext, Expression, Observable, observable, volatile } from "./observable.js"; import { Fake } from "../testing/fakes.js"; describe("The Observable", () => { @@ -627,4 +627,34 @@ describe("The Observable", () => { expect(model.child2ChangedCalled).to.be.true; }); }); + + context("isVolatileBinding", () => { + it("should return true when expression uses ternary operator", () => { + const expression = (a) => a !== undefined ? a : undefined; + + expect(Observable.isVolatileBinding(expression)).to.equal(true) + }); + it("should return true when expression uses 'if' condition", () => { + const expression = (a) => { if (a !== undefined) { return a }}; + + expect(Observable.isVolatileBinding(expression)).to.equal(true) + }); + it("should return true when expression uses '&&' operator", () => { + const expression = (a) => { a && true}; + + expect(Observable.isVolatileBinding(expression)).to.equal(true) + }); + it("should return true when expression uses '||' operator", () => { + const expression = (a) => { a || true}; + + expect(Observable.isVolatileBinding(expression)).to.equal(true) + }); + it("should return true when when expression uses JavaScript optional chaining", () => { + // Avoid TS Compiling Optional property syntax away into ternary + // by using Function constructor + const expression = Function("(a) => a?.b") as Expression; + + expect(Observable.isVolatileBinding(expression)).to.equal(true) + }) + }) }); diff --git a/packages/web-components/fast-element/src/observation/observable.ts b/packages/web-components/fast-element/src/observation/observable.ts index b57ae47fb1e..965980bf83e 100644 --- a/packages/web-components/fast-element/src/observation/observable.ts +++ b/packages/web-components/fast-element/src/observation/observable.ts @@ -172,7 +172,7 @@ export interface ExpressionNotifier */ export const Observable = FAST.getById(KernelServiceId.observable, () => { const queueUpdate = Updates.enqueue; - const volatileRegex = /(:|&&|\|\||if)/; + const volatileRegex = /(:|&&|\|\||if|\?\.)/; const notifierLookup = new WeakMap(); let watcher: ExpressionNotifierImplementation | undefined = void 0; let createArrayObserver = (array: any[]): Notifier => {