diff --git a/change/@microsoft-fast-element-e173d34b-0061-4c9d-94b5-415929d7dc5e.json b/change/@microsoft-fast-element-e173d34b-0061-4c9d-94b5-415929d7dc5e.json new file mode 100644 index 00000000000..076ceecae26 --- /dev/null +++ b/change/@microsoft-fast-element-e173d34b-0061-4c9d-94b5-415929d7dc5e.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Support element filters for declarative f-children directives.", + "packageName": "@microsoft/fast-element", + "email": "pradeepramolaa@gmail.com", + "dependentChangeType": "none" +} diff --git a/packages/fast-element/docs/declarative/syntax.md b/packages/fast-element/docs/declarative/syntax.md index f2f7fff4b9b..cdfa2058695 100644 --- a/packages/fast-element/docs/declarative/syntax.md +++ b/packages/fast-element/docs/declarative/syntax.md @@ -336,6 +336,8 @@ Attribute directives include: Example: ```html + + ``` - **ref** diff --git a/packages/fast-element/src/declarative/template-bridge.pw.spec.ts b/packages/fast-element/src/declarative/template-bridge.pw.spec.ts index be149fa427b..6a598cb61bf 100644 --- a/packages/fast-element/src/declarative/template-bridge.pw.spec.ts +++ b/packages/fast-element/src/declarative/template-bridge.pw.spec.ts @@ -678,6 +678,49 @@ test.describe("declarativeTemplate", () => { expect(result.shadowText).toContain("reconnected"); }); + test("applies element filters to declarative f-children directives", async ({ + page, + }) => { + await page.goto("/"); + + const result = await page.evaluate(async () => { + // @ts-expect-error: Client module. + const { FASTElement, declarativeTemplate, uniqueElementName } = await import( + "/declarative-main.js" + ); + + const elementName = uniqueElementName(); + + document.body.insertAdjacentHTML( + "beforeend", + ``, + ); + + class TestElement extends FASTElement { + public items = ["Foo", "Bar"]; + public allChildren: Node[] = []; + public filteredChildren: Node[] = []; + } + + await TestElement.define({ + name: elementName, + template: declarativeTemplate(), + }); + + const element = document.createElement(elementName) as TestElement; + document.body.appendChild(element); + await new Promise(resolve => requestAnimationFrame(resolve)); + + return { + allChildrenTags: element.allChildren.map(node => node.nodeName), + filteredChildrenTags: element.filteredChildren.map(node => node.nodeName), + }; + }); + + expect(result.allChildrenTags).toEqual(["LI", "LI"]); + expect(result.filteredChildrenTags).toEqual(["SPAN"]); + }); + test("does not reassign a resolved template for duplicate f-template names", async ({ page, }) => { diff --git a/packages/fast-element/src/declarative/template-parser.ts b/packages/fast-element/src/declarative/template-parser.ts index 4df9c5e8993..7bf75ebdbf5 100644 --- a/packages/fast-element/src/declarative/template-parser.ts +++ b/packages/fast-element/src/declarative/template-parser.ts @@ -45,6 +45,11 @@ interface TemplateResolutionContext { schema: Schema; } +interface NodeDirectiveOptions { + property: string; + filter?: ReturnType; +} + /** * Tracks string segments accumulated during template parsing and maintains * a running concatenation so that `bindingResolver` can receive the full @@ -232,27 +237,12 @@ export class TemplateParser { ): void { switch (name) { case "children": { - externalValues.push(children(propName)); + externalValues.push(children(this.resolveNodeDirectiveOptions(propName))); break; } case "slotted": { - const parts = propName.trim().split(" filter "); - const slottedOption = { - property: parts[0], - }; - - if (parts[1]) { - if (parts[1].startsWith("elements(")) { - let params = parts[1].replace("elements(", ""); - params = params.substring(0, params.lastIndexOf(")")); - Object.assign(slottedOption, { - filter: elements(params || undefined), - }); - } - } - - externalValues.push(slotted(slottedOption)); + externalValues.push(slotted(this.resolveNodeDirectiveOptions(propName))); break; } @@ -264,6 +254,21 @@ export class TemplateParser { } } + private resolveNodeDirectiveOptions(propName: string): NodeDirectiveOptions { + const parts = propName.trim().split(" filter "); + const options: NodeDirectiveOptions = { + property: parts[0], + }; + + if (parts[1]?.startsWith("elements(")) { + let params = parts[1].replace("elements(", ""); + params = params.substring(0, params.lastIndexOf(")")); + options.filter = elements(params || undefined); + } + + return options; + } + /** * Resolve an access binding — shared by content bindings, boolean-attribute * fallback, and default attribute bindings.