Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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"
}
2 changes: 2 additions & 0 deletions packages/fast-element/docs/declarative/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,8 @@ Attribute directives include:
Example:
```html
<ul f-children="{listItems}"><f-repeat value="{{item in list}}"><li>{{item}}</li></f-repeat></ul>
<ul f-children="{listItems filter elements()}"><f-repeat value="{{item in list}}"><li>{{item}}</li></f-repeat></ul>
<ul f-children="{listItems filter elements(li)}"><f-repeat value="{{item in list}}"><li>{{item}}</li></f-repeat></ul>
```

- **ref**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
`<f-template name="${elementName}"><template><ul f-children="{allChildren filter elements()}">Text node<f-repeat value="{{item in items}}"><li>{{item}}</li></f-repeat></ul><div f-children="{filteredChildren filter elements(span)}"><span>Included</span><button>Ignored</button>Text node</div></template></f-template>`,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not rely on injecting the f-template via javascript, this should be placed in one of the fixture tests, in this case I would expect this in the one for children which is located in packages/fast-element/test/declarative/fixtures/directives/children. The template bridge is for testing the declarativeTemplate functionality in terms of lifecycle and attachment and DeclarativeTemplateBridge.

);

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,
}) => {
Expand Down
39 changes: 22 additions & 17 deletions packages/fast-element/src/declarative/template-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ interface TemplateResolutionContext {
schema: Schema;
}

interface NodeDirectiveOptions {
property: string;
filter?: ReturnType<typeof elements>;
}

/**
* Tracks string segments accumulated during template parsing and maintains
* a running concatenation so that `bindingResolver` can receive the full
Expand Down Expand Up @@ -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;
}
Expand All @@ -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.
Expand Down