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
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
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,36 @@ test.describe("f-template", async () => {
await hydrationCompleted;

const element = page.locator("test-element");
const listItems = element.locator("li");
const listItems = element.locator("[data-testid='list-items'] li");

await expect(listItems).toHaveCount(2);

await expect(listItems).toHaveText(["Foo", "Bar"]);

const filteredNodeNames = await element.evaluate(
(
node: HTMLElement & {
allChildren: Node[];
filteredChildren: Node[];
},
) => ({
allChildren: node.allChildren.map(child => child.nodeName),
filteredChildren: node.filteredChildren.map(child => child.nodeName),
}),
);

expect(filteredNodeNames).toEqual({
allChildren: ["LI", "LI"],
filteredChildren: ["SPAN"],
});

await element.evaluate((node: HTMLElement & { list: Array<string> }) => {
node.list = ["A", "B", "C"];
});

await expect(listItems).toHaveCount(3);

await expect(listItems).toHaveText(["A", "B", "C"]);
await expect(element).toHaveJSProperty("allChildren.length", 3);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,15 @@
<title></title>
</head>
<body>
<test-element><template shadowrootmode="open" shadowroot="open"><ul data-fe="1"><!--fe:b--><!--fe:r--><li><!--fe:b-->Foo<!--fe:/b--></li><!--fe:/r--><!--fe:r--><li><!--fe:b-->Bar<!--fe:/b--></li><!--fe:/r--><!--fe:/b--></ul></template></test-element>
<test-element><template shadowrootmode="open" shadowroot="open"><ul data-testid="list-items" data-fe="1"><!--fe:b--><!--fe:r--><li><!--fe:b-->Foo<!--fe:/b--></li><!--fe:/r--><!--fe:r--><li><!--fe:b-->Bar<!--fe:/b--></li><!--fe:/r--><!--fe:/b--></ul>
<ul data-fe="1">Text node<!--fe:b--><!--fe:r--><li><!--fe:b-->Foo<!--fe:/b--></li><!--fe:/r--><!--fe:r--><li><!--fe:b-->Bar<!--fe:/b--></li><!--fe:/r--><!--fe:/b--></ul>
<div data-fe="1"><span>Included</span><button type="button">Ignored</button>Text node</div></template></test-element>
<f-template name="test-element">
<template><ul f-children="{listItems}"><f-repeat value="{{item in list}}"><li>{{item}}</li></f-repeat></ul></template>
<template>
<ul data-testid="list-items" f-children="{listItems}"><f-repeat value="{{item in list}}"><li>{{item}}</li></f-repeat></ul>
<ul f-children="{allChildren filter elements()}">Text node<f-repeat value="{{item in list}}"><li>{{item}}</li></f-repeat></ul>
<div f-children="{filteredChildren filter elements(span)}"><span>Included</span><button type="button">Ignored</button>Text node</div>
</template>
</f-template>

<script type="module" src="./main.ts"></script>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ class TestElement extends FASTElement {
@observable
listItems: Node[] = [];

@observable
allChildren: Node[] = [];

@observable
filteredChildren: Node[] = [];

@observable
list: Array<string> = ["Foo", "Bar"];
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
<f-template name="test-element">
<template><ul f-children="{listItems}"><f-repeat value="{{item in list}}"><li>{{item}}</li></f-repeat></ul></template>
<template>
<ul data-testid="list-items" f-children="{listItems}"><f-repeat value="{{item in list}}"><li>{{item}}</li></f-repeat></ul>
<ul f-children="{allChildren filter elements()}">Text node<f-repeat value="{{item in list}}"><li>{{item}}</li></f-repeat></ul>
<div f-children="{filteredChildren filter elements(span)}"><span>Included</span><button type="button">Ignored</button>Text node</div>
</template>
</f-template>