@microsoft/fast-html 1.0.0-alpha.6 → 1.0.0-alpha.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -7
- package/dist/dts/components/utilities.d.ts +9 -0
- package/dist/esm/components/template.js +19 -5
- package/dist/esm/components/utilities.js +81 -7
- package/dist/esm/components/utilities.spec.js +40 -17
- package/dist/esm/fixtures/binding/binding.spec.js +6 -0
- package/dist/esm/fixtures/binding/main.js +9 -0
- package/dist/esm/fixtures/children/children.spec.js +4 -0
- package/dist/esm/fixtures/repeat/repeat.spec.js +4 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -47,7 +47,15 @@ Example:
|
|
|
47
47
|
|
|
48
48
|
### Syntax
|
|
49
49
|
|
|
50
|
-
All bindings use a handlebars syntax.
|
|
50
|
+
All bindings use a handlebars-like syntax.
|
|
51
|
+
|
|
52
|
+
Some bindings are only relevant to the browser, such as for click handlers or other pieces of dynamic interaction. As such, their bindings use single curly braces `{}`, this is to prevent an intial SSR (Server Side Rendering) or other build time rendering technologies from needing to interpret them.
|
|
53
|
+
|
|
54
|
+
If a binding is relevant to both client and the back end rendering engine, it will use `{{}}` or `{{{}}}` depending on what type of data injection is being done.
|
|
55
|
+
|
|
56
|
+
Browser-only bindings:
|
|
57
|
+
- Event bindings
|
|
58
|
+
- Attribute directives
|
|
51
59
|
|
|
52
60
|
#### Content binding
|
|
53
61
|
|
|
@@ -60,45 +68,47 @@ All bindings use a handlebars syntax.
|
|
|
60
68
|
Event bindings must include the `()` as well as being preceeded by `@` in keeping with `@microsoft/fast-element` tagged template `html` syntax.
|
|
61
69
|
|
|
62
70
|
```html
|
|
63
|
-
<button @click="{
|
|
71
|
+
<button @click="{handleClick()}"></button>
|
|
64
72
|
```
|
|
65
73
|
|
|
66
74
|
In addition you may include an event or attribute or observable, events are denoted with `e` as a reserved letter.
|
|
67
75
|
|
|
68
76
|
Event:
|
|
69
77
|
```html
|
|
70
|
-
<button @click="{
|
|
78
|
+
<button @click="{handleClick(e)}"></button>
|
|
71
79
|
```
|
|
72
80
|
|
|
73
81
|
Attribute/Observable:
|
|
74
82
|
```html
|
|
75
|
-
<button @click="{
|
|
83
|
+
<button @click="{handleClick(foo)}"></button>
|
|
76
84
|
```
|
|
77
85
|
|
|
78
86
|
#### Directives
|
|
79
87
|
|
|
80
88
|
Directives are assumed to be either an attribute directive or a directive that also serves a template. Both are prepended by `f-`. The logic of these directives and what their use cases are is explained in the [FAST html documentation](https://fast.design/docs/getting-started/html-directives).
|
|
81
89
|
|
|
90
|
+
Attribute directives are part of [client side binding](#syntax) and therefore use the `{}` syntax.
|
|
91
|
+
|
|
82
92
|
Attribute directives include:
|
|
83
93
|
- **slotted**
|
|
84
94
|
|
|
85
95
|
Example:
|
|
86
96
|
```html
|
|
87
|
-
<slot f-slotted="{
|
|
97
|
+
<slot f-slotted="{slottedNodes}"></slot>
|
|
88
98
|
```
|
|
89
99
|
|
|
90
100
|
- **children**
|
|
91
101
|
|
|
92
102
|
Example:
|
|
93
103
|
```html
|
|
94
|
-
<ul f-children="{
|
|
104
|
+
<ul f-children="{listItems}"><f-repeat value="{{item in list}}"><li>{{item}}</li></f-repeat></ul>
|
|
95
105
|
```
|
|
96
106
|
|
|
97
107
|
- **ref**
|
|
98
108
|
|
|
99
109
|
Example:
|
|
100
110
|
```html
|
|
101
|
-
<video f-ref="{
|
|
111
|
+
<video f-ref="{video}"></video>
|
|
102
112
|
```
|
|
103
113
|
|
|
104
114
|
Template directives include:
|
|
@@ -152,6 +162,15 @@ Where the right operand can be either a reference to a value (string e.g. `{{foo
|
|
|
152
162
|
<f-apply partial="test" value="{{items}}"></f-apply>
|
|
153
163
|
```
|
|
154
164
|
|
|
165
|
+
#### Unescaped HTML
|
|
166
|
+
|
|
167
|
+
You can add unescaped HTML using triple braces, this will create an additional `div` element as the HTML needs an element to bind to. Where possible it is advisable to not use unescaped HTML and instead use other binding techniques as well as partials.
|
|
168
|
+
|
|
169
|
+
Example:
|
|
170
|
+
```html
|
|
171
|
+
{{{html}}}
|
|
172
|
+
```
|
|
173
|
+
|
|
155
174
|
### Writing Components
|
|
156
175
|
|
|
157
176
|
When writing components with the intention of using the declarative HTML syntax, it is imperative that components are written with styling and rendering of the component to be less reliant on any JavaScript state management. An example of this is relying on `elementInterals` state to style a component.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
type BehaviorType = "dataBinding" | "templateDirective";
|
|
2
2
|
type TemplateDirective = "when" | "repeat" | "apply";
|
|
3
3
|
export type AttributeDirective = "children" | "slotted" | "ref";
|
|
4
|
+
type DataBindingBindingType = "client" | "default" | "unescaped";
|
|
4
5
|
interface BehaviorConfig {
|
|
5
6
|
type: BehaviorType;
|
|
6
7
|
}
|
|
@@ -18,6 +19,7 @@ export interface AttributeDirectiveBindingBehaviorConfig extends BaseDataBinding
|
|
|
18
19
|
export type DataBindingBehaviorConfig = ContentDataBindingBehaviorConfig | AttributeDataBindingBehaviorConfig | AttributeDirectiveBindingBehaviorConfig;
|
|
19
20
|
export interface BaseDataBindingBehaviorConfig extends BehaviorConfig {
|
|
20
21
|
type: "dataBinding";
|
|
22
|
+
bindingType: DataBindingBindingType;
|
|
21
23
|
openingStartIndex: number;
|
|
22
24
|
openingEndIndex: number;
|
|
23
25
|
closingStartIndex: number;
|
|
@@ -99,4 +101,11 @@ interface OperatorConfig {
|
|
|
99
101
|
* @returns Operator
|
|
100
102
|
*/
|
|
101
103
|
export declare function getOperator(value: string): OperatorConfig;
|
|
104
|
+
/**
|
|
105
|
+
* This is the transform utility for rationalizing declarative HTML syntax
|
|
106
|
+
* with bindings in the ViewTemplate
|
|
107
|
+
* @param innerHTML The innerHTML to transform
|
|
108
|
+
* @param index The index to start the current slice of HTML to evaluate
|
|
109
|
+
*/
|
|
110
|
+
export declare function transformInnerHTML(innerHTML: string, index?: number): string;
|
|
102
111
|
export {};
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { __awaiter, __decorate, __metadata } from "tslib";
|
|
2
|
-
import { attr, FAST, FASTElement, fastElementRegistry, ViewTemplate, } from "@microsoft/fast-element";
|
|
2
|
+
import { attr, DOMAspect, FAST, FASTElement, fastElementRegistry, ViewTemplate, } from "@microsoft/fast-element";
|
|
3
3
|
import { DOMPolicy } from "@microsoft/fast-element/dom-policy.js";
|
|
4
|
-
import { getAllPartials, getNextBehavior, getOperator, pathResolver, } from "./utilities.js";
|
|
4
|
+
import { getAllPartials, getNextBehavior, getOperator, pathResolver, transformInnerHTML, } from "./utilities.js";
|
|
5
|
+
function allow(tagName, aspect, aspectName, sink) {
|
|
6
|
+
return (target, name, value, ...rest) => {
|
|
7
|
+
sink(target, name, value, ...rest);
|
|
8
|
+
};
|
|
9
|
+
}
|
|
5
10
|
/**
|
|
6
11
|
* The <f-template> custom element that will provide view logic to the element
|
|
7
12
|
*/
|
|
@@ -19,8 +24,9 @@ class TemplateElement extends FASTElement {
|
|
|
19
24
|
const registeredFastElement = fastElementRegistry.getByType(value);
|
|
20
25
|
const template = this.getElementsByTagName("template").item(0);
|
|
21
26
|
if (template) {
|
|
22
|
-
yield
|
|
23
|
-
|
|
27
|
+
const innerHTML = yield transformInnerHTML(this.innerHTML);
|
|
28
|
+
yield this.resolveAllPartials(innerHTML);
|
|
29
|
+
const { strings, values } = yield this.resolveStringsAndValues(innerHTML);
|
|
24
30
|
if (registeredFastElement) {
|
|
25
31
|
// all new elements will get the updated template
|
|
26
32
|
registeredFastElement.template =
|
|
@@ -56,7 +62,15 @@ class TemplateElement extends FASTElement {
|
|
|
56
62
|
* @param values - The interpreted values.
|
|
57
63
|
*/
|
|
58
64
|
resolveTemplateOrBehavior(strings, values) {
|
|
59
|
-
return ViewTemplate.create(strings, values, DOMPolicy.create(
|
|
65
|
+
return ViewTemplate.create(strings, values, DOMPolicy.create({
|
|
66
|
+
guards: {
|
|
67
|
+
aspects: {
|
|
68
|
+
[DOMAspect.property]: {
|
|
69
|
+
innerHTML: allow,
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
}));
|
|
60
74
|
}
|
|
61
75
|
/**
|
|
62
76
|
* Resolve a template directive
|
|
@@ -1,9 +1,17 @@
|
|
|
1
|
-
const
|
|
2
|
-
const
|
|
1
|
+
const openClientSideBinding = "{";
|
|
2
|
+
const closeClientSideBinding = "}";
|
|
3
|
+
const openContentBinding = "{{";
|
|
4
|
+
const closeContentBinding = "}}";
|
|
5
|
+
const openUnescapedBinding = "{{{";
|
|
6
|
+
const closeUnescapedBinding = "}}}";
|
|
3
7
|
const openTagStart = "<f-";
|
|
4
8
|
const tagEnd = ">";
|
|
5
9
|
const closeTagStart = "</f-";
|
|
6
10
|
const attributeDirectivePrefix = "f-";
|
|
11
|
+
const startInnerHTMLDiv = `<div :innerHTML="{{`;
|
|
12
|
+
const startInnerHTMLDivLength = startInnerHTMLDiv.length;
|
|
13
|
+
const endInnerHTMLDiv = `}}"></div>`;
|
|
14
|
+
const endInnerHTMLDivLength = endInnerHTMLDiv.length;
|
|
7
15
|
/**
|
|
8
16
|
* Get the index of the next matching tag
|
|
9
17
|
* @param openingTagStartSlice - The slice starting from the opening tag
|
|
@@ -131,20 +139,57 @@ function getAttributeDirectiveDataBindingConfig(innerHTML, config) {
|
|
|
131
139
|
function getContentDataBindingConfig(config) {
|
|
132
140
|
return Object.assign(Object.assign({}, config), { subtype: "content" });
|
|
133
141
|
}
|
|
142
|
+
function getIndexAndBindingTypeOfNextDataBindingBehavior(innerHTML) {
|
|
143
|
+
// {{{}}} binding
|
|
144
|
+
const openingUnescapedStartIndex = innerHTML.indexOf(openUnescapedBinding);
|
|
145
|
+
const closingUnescapedStartIndex = innerHTML.indexOf(closeUnescapedBinding);
|
|
146
|
+
// {{}} binding
|
|
147
|
+
const openingContentStartIndex = innerHTML.indexOf(openContentBinding);
|
|
148
|
+
const closingContentStartIndex = innerHTML.indexOf(closeContentBinding);
|
|
149
|
+
// {} binding
|
|
150
|
+
const openingClientStartIndex = innerHTML.indexOf(openClientSideBinding);
|
|
151
|
+
const closingClientStartIndex = innerHTML.indexOf(closeClientSideBinding);
|
|
152
|
+
if (openingUnescapedStartIndex !== -1 &&
|
|
153
|
+
openingUnescapedStartIndex <= openingContentStartIndex &&
|
|
154
|
+
openingUnescapedStartIndex <= openingClientStartIndex) {
|
|
155
|
+
// is unescaped {{{}}}
|
|
156
|
+
return {
|
|
157
|
+
openingStartIndex: openingUnescapedStartIndex,
|
|
158
|
+
closingStartIndex: closingUnescapedStartIndex,
|
|
159
|
+
bindingType: "unescaped",
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
else if (openingContentStartIndex !== -1 &&
|
|
163
|
+
openingContentStartIndex <= openingClientStartIndex) {
|
|
164
|
+
// is default {{}}
|
|
165
|
+
return {
|
|
166
|
+
openingStartIndex: openingContentStartIndex,
|
|
167
|
+
closingStartIndex: closingContentStartIndex,
|
|
168
|
+
bindingType: "default",
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
// is client {}
|
|
172
|
+
return {
|
|
173
|
+
openingStartIndex: openingClientStartIndex,
|
|
174
|
+
closingStartIndex: closingClientStartIndex,
|
|
175
|
+
bindingType: "client",
|
|
176
|
+
};
|
|
177
|
+
}
|
|
134
178
|
/**
|
|
135
179
|
* Get the next data binding
|
|
136
180
|
* @param innerHTML - The innerHTML string to evaluate
|
|
137
181
|
* @returns DataBindingBehaviorConfig - A configuration object
|
|
138
182
|
*/
|
|
139
183
|
function getNextDataBindingBehavior(innerHTML) {
|
|
140
|
-
const openingStartIndex = innerHTML
|
|
141
|
-
const
|
|
184
|
+
const { openingStartIndex, closingStartIndex, bindingType } = getIndexAndBindingTypeOfNextDataBindingBehavior(innerHTML);
|
|
185
|
+
const bindingLength = bindingType === "client" ? 1 : bindingType === "default" ? 2 : 3;
|
|
142
186
|
const partialConfig = {
|
|
143
187
|
type: "dataBinding",
|
|
188
|
+
bindingType,
|
|
144
189
|
openingStartIndex,
|
|
145
|
-
openingEndIndex: openingStartIndex +
|
|
190
|
+
openingEndIndex: openingStartIndex + bindingLength,
|
|
146
191
|
closingStartIndex,
|
|
147
|
-
closingEndIndex: closingStartIndex +
|
|
192
|
+
closingEndIndex: closingStartIndex + bindingLength,
|
|
148
193
|
};
|
|
149
194
|
return isAttributeDirective(innerHTML, openingStartIndex)
|
|
150
195
|
? getAttributeDirectiveDataBindingConfig(innerHTML, partialConfig)
|
|
@@ -158,7 +203,7 @@ function getNextDataBindingBehavior(innerHTML) {
|
|
|
158
203
|
* @returns DataBindingBehaviorConfig | DirectiveBehaviorConfig | null - A configuration object or null
|
|
159
204
|
*/
|
|
160
205
|
export function getNextBehavior(innerHTML) {
|
|
161
|
-
const dataBindingOpen = innerHTML.indexOf(
|
|
206
|
+
const dataBindingOpen = innerHTML.indexOf(openClientSideBinding); // client side binding will capture all bindings starting with "{"
|
|
162
207
|
const directiveBindingOpen = innerHTML.indexOf(openTagStart);
|
|
163
208
|
if (dataBindingOpen === -1 && directiveBindingOpen === -1) {
|
|
164
209
|
return null;
|
|
@@ -294,3 +339,32 @@ export function getOperator(value) {
|
|
|
294
339
|
rightIsValue: null,
|
|
295
340
|
};
|
|
296
341
|
}
|
|
342
|
+
/**
|
|
343
|
+
* This is the transform utility for rationalizing declarative HTML syntax
|
|
344
|
+
* with bindings in the ViewTemplate
|
|
345
|
+
* @param innerHTML The innerHTML to transform
|
|
346
|
+
* @param index The index to start the current slice of HTML to evaluate
|
|
347
|
+
*/
|
|
348
|
+
export function transformInnerHTML(innerHTML, index = 0) {
|
|
349
|
+
const sliceToEvaluate = innerHTML.slice(index);
|
|
350
|
+
const nextBinding = getNextBehavior(sliceToEvaluate);
|
|
351
|
+
let transformedInnerHTML = innerHTML;
|
|
352
|
+
if (nextBinding && nextBinding.type === "dataBinding") {
|
|
353
|
+
if (nextBinding.bindingType === "unescaped") {
|
|
354
|
+
transformedInnerHTML = `${innerHTML.slice(0, index)}${sliceToEvaluate.slice(0, nextBinding.openingStartIndex)}${startInnerHTMLDiv}${sliceToEvaluate.slice(nextBinding.openingStartIndex + 3, nextBinding.closingStartIndex)}${endInnerHTMLDiv}${sliceToEvaluate.slice(nextBinding.closingStartIndex + 3)}`;
|
|
355
|
+
return transformInnerHTML(transformedInnerHTML, index +
|
|
356
|
+
startInnerHTMLDivLength +
|
|
357
|
+
endInnerHTMLDivLength +
|
|
358
|
+
nextBinding.closingStartIndex -
|
|
359
|
+
3);
|
|
360
|
+
}
|
|
361
|
+
else if (nextBinding.bindingType === "client") {
|
|
362
|
+
return transformInnerHTML(transformedInnerHTML, index + nextBinding.closingEndIndex);
|
|
363
|
+
}
|
|
364
|
+
return transformInnerHTML(transformedInnerHTML, index + nextBinding.closingStartIndex - 2);
|
|
365
|
+
}
|
|
366
|
+
else if (nextBinding) {
|
|
367
|
+
return transformInnerHTML(transformedInnerHTML, index + nextBinding.closingTagEndIndex);
|
|
368
|
+
}
|
|
369
|
+
return transformedInnerHTML;
|
|
370
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { __awaiter } from "tslib";
|
|
2
2
|
import { expect, test } from "@playwright/test";
|
|
3
|
-
import { getNextBehavior, getAllPartials, getIndexOfNextMatchingTag, pathResolver } from "./utilities.js";
|
|
3
|
+
import { getNextBehavior, getAllPartials, getIndexOfNextMatchingTag, pathResolver, transformInnerHTML } from "./utilities.js";
|
|
4
4
|
test.describe("utilities", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
5
5
|
test.describe("content", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
6
6
|
test("get the next content binding", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -8,6 +8,7 @@ test.describe("utilities", () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
8
8
|
const templateResult = getNextBehavior(innerHTML);
|
|
9
9
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.type).toEqual("dataBinding");
|
|
10
10
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.subtype).toEqual("content");
|
|
11
|
+
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.bindingType).toEqual("default");
|
|
11
12
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.openingStartIndex).toEqual(0);
|
|
12
13
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.openingEndIndex).toEqual(2);
|
|
13
14
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.closingStartIndex).toEqual(6);
|
|
@@ -21,21 +22,23 @@ test.describe("utilities", () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
21
22
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.type).toEqual("dataBinding");
|
|
22
23
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.subtype).toEqual("attribute");
|
|
23
24
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.aspect).toEqual(null);
|
|
25
|
+
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.bindingType).toEqual("default");
|
|
24
26
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.openingStartIndex).toEqual(13);
|
|
25
27
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.openingEndIndex).toEqual(15);
|
|
26
28
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.closingStartIndex).toEqual(19);
|
|
27
29
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.closingEndIndex).toEqual(21);
|
|
28
30
|
}));
|
|
29
31
|
test("get the next attribute event binding", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
30
|
-
const innerHTML = "<input @click=\"{
|
|
32
|
+
const innerHTML = "<input @click=\"{handleClick()}\">";
|
|
31
33
|
const templateResult = getNextBehavior(innerHTML);
|
|
32
34
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.type).toEqual("dataBinding");
|
|
33
35
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.subtype).toEqual("attribute");
|
|
34
36
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.aspect).toEqual("@");
|
|
37
|
+
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.bindingType).toEqual("client");
|
|
35
38
|
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.openingStartIndex).toEqual(15);
|
|
36
|
-
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.openingEndIndex).toEqual(
|
|
37
|
-
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.closingStartIndex).toEqual(
|
|
38
|
-
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.closingEndIndex).toEqual(
|
|
39
|
+
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.openingEndIndex).toEqual(16);
|
|
40
|
+
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.closingStartIndex).toEqual(29);
|
|
41
|
+
expect(templateResult === null || templateResult === void 0 ? void 0 : templateResult.closingEndIndex).toEqual(30);
|
|
39
42
|
}));
|
|
40
43
|
}));
|
|
41
44
|
test.describe("templates", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -69,37 +72,40 @@ test.describe("utilities", () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
69
72
|
}));
|
|
70
73
|
test.describe("attributes", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
71
74
|
test("children directive", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
72
|
-
const innerHTML = "<ul f-children=\"{
|
|
75
|
+
const innerHTML = "<ul f-children=\"{list}\"></ul>";
|
|
73
76
|
const result = getNextBehavior(innerHTML);
|
|
74
77
|
expect(result === null || result === void 0 ? void 0 : result.type).toEqual("dataBinding");
|
|
75
78
|
expect(result === null || result === void 0 ? void 0 : result.subtype).toEqual("attributeDirective");
|
|
76
79
|
expect(result === null || result === void 0 ? void 0 : result.name).toEqual("children");
|
|
80
|
+
expect(result === null || result === void 0 ? void 0 : result.bindingType).toEqual("client");
|
|
77
81
|
expect(result === null || result === void 0 ? void 0 : result.openingStartIndex).toEqual(16);
|
|
78
|
-
expect(result === null || result === void 0 ? void 0 : result.openingEndIndex).toEqual(
|
|
79
|
-
expect(result === null || result === void 0 ? void 0 : result.closingStartIndex).toEqual(
|
|
80
|
-
expect(result === null || result === void 0 ? void 0 : result.closingEndIndex).toEqual(
|
|
82
|
+
expect(result === null || result === void 0 ? void 0 : result.openingEndIndex).toEqual(17);
|
|
83
|
+
expect(result === null || result === void 0 ? void 0 : result.closingStartIndex).toEqual(21);
|
|
84
|
+
expect(result === null || result === void 0 ? void 0 : result.closingEndIndex).toEqual(22);
|
|
81
85
|
}));
|
|
82
86
|
test("slotted directive", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
83
|
-
const innerHTML = "<slot f-slotted=\"{
|
|
87
|
+
const innerHTML = "<slot f-slotted=\"{slottedNodes}\"></slot>";
|
|
84
88
|
const result = getNextBehavior(innerHTML);
|
|
85
89
|
expect(result === null || result === void 0 ? void 0 : result.type).toEqual("dataBinding");
|
|
86
90
|
expect(result === null || result === void 0 ? void 0 : result.subtype).toEqual("attributeDirective");
|
|
87
91
|
expect(result === null || result === void 0 ? void 0 : result.name).toEqual("slotted");
|
|
92
|
+
expect(result === null || result === void 0 ? void 0 : result.bindingType).toEqual("client");
|
|
88
93
|
expect(result === null || result === void 0 ? void 0 : result.openingStartIndex).toEqual(17);
|
|
89
|
-
expect(result === null || result === void 0 ? void 0 : result.openingEndIndex).toEqual(
|
|
90
|
-
expect(result === null || result === void 0 ? void 0 : result.closingStartIndex).toEqual(
|
|
91
|
-
expect(result === null || result === void 0 ? void 0 : result.closingEndIndex).toEqual(
|
|
94
|
+
expect(result === null || result === void 0 ? void 0 : result.openingEndIndex).toEqual(18);
|
|
95
|
+
expect(result === null || result === void 0 ? void 0 : result.closingStartIndex).toEqual(30);
|
|
96
|
+
expect(result === null || result === void 0 ? void 0 : result.closingEndIndex).toEqual(31);
|
|
92
97
|
}));
|
|
93
98
|
test("ref directive", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
94
|
-
const innerHTML = "<video f-ref=\"{
|
|
99
|
+
const innerHTML = "<video f-ref=\"{video}\"></video>";
|
|
95
100
|
const result = getNextBehavior(innerHTML);
|
|
96
101
|
expect(result === null || result === void 0 ? void 0 : result.type).toEqual("dataBinding");
|
|
97
102
|
expect(result === null || result === void 0 ? void 0 : result.subtype).toEqual("attributeDirective");
|
|
98
103
|
expect(result === null || result === void 0 ? void 0 : result.name).toEqual("ref");
|
|
104
|
+
expect(result === null || result === void 0 ? void 0 : result.bindingType).toEqual("client");
|
|
99
105
|
expect(result === null || result === void 0 ? void 0 : result.openingStartIndex).toEqual(14);
|
|
100
|
-
expect(result === null || result === void 0 ? void 0 : result.openingEndIndex).toEqual(
|
|
101
|
-
expect(result === null || result === void 0 ? void 0 : result.closingStartIndex).toEqual(
|
|
102
|
-
expect(result === null || result === void 0 ? void 0 : result.closingEndIndex).toEqual(
|
|
106
|
+
expect(result === null || result === void 0 ? void 0 : result.openingEndIndex).toEqual(15);
|
|
107
|
+
expect(result === null || result === void 0 ? void 0 : result.closingStartIndex).toEqual(20);
|
|
108
|
+
expect(result === null || result === void 0 ? void 0 : result.closingEndIndex).toEqual(21);
|
|
103
109
|
}));
|
|
104
110
|
}));
|
|
105
111
|
test.describe("partials", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -160,4 +166,21 @@ test.describe("utilities", () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
160
166
|
expect(pathResolver("../foo")({}, { parent: { foo: "bar" } })).toEqual("bar");
|
|
161
167
|
}));
|
|
162
168
|
}));
|
|
169
|
+
test.describe("transformInnerHTML", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
170
|
+
test("should resolve a single unescaped data binding", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
171
|
+
expect(transformInnerHTML(`{{{html}}}`)).toEqual(`<div :innerHTML="{{html}}"></div>`);
|
|
172
|
+
}));
|
|
173
|
+
test("should resolve multiple unescaped data bindings", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
174
|
+
expect(transformInnerHTML(`{{{foo}}}{{{bar}}}`)).toEqual(`<div :innerHTML="{{foo}}"></div><div :innerHTML="{{bar}}"></div>`);
|
|
175
|
+
}));
|
|
176
|
+
test("should resolve a unescaped data bindings in a mix of other data content bindings", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
177
|
+
expect(transformInnerHTML(`{{text1}}{{{foo}}}{{text2}}{{{bar}}}{{text3}}`)).toEqual(`{{text1}}<div :innerHTML="{{foo}}"></div>{{text2}}<div :innerHTML="{{bar}}"></div>{{text3}}`);
|
|
178
|
+
}));
|
|
179
|
+
test("should resolve a unescaped data bindings in a mix of other data attribute bindings and nesting", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
180
|
+
expect(transformInnerHTML(`<div data-foo="{{text1}}">{{{foo}}}</div><div data-bar="{{text2}}"></div>{{{bar}}}<div data-bat="{{text3}}"></div>`)).toEqual(`<div data-foo="{{text1}}"><div :innerHTML="{{foo}}"></div></div><div data-bar="{{text2}}"></div><div :innerHTML="{{bar}}"></div><div data-bat="{{text3}}"></div>`);
|
|
181
|
+
}));
|
|
182
|
+
test("should resolve a non-data and non-attribute bindings", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
183
|
+
expect(transformInnerHTML(`<button @click="{handleNoArgsClick()}">No arguments</button>`)).toEqual(`<button @click="{handleNoArgsClick()}">No arguments</button>`);
|
|
184
|
+
}));
|
|
185
|
+
}));
|
|
163
186
|
}));
|
|
@@ -14,4 +14,10 @@ test.describe("f-template", () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
14
14
|
yield expect(yield customElement.getAttribute("text")).toEqual("Hello pluto");
|
|
15
15
|
yield expect(customElement).toHaveText("Hello pluto");
|
|
16
16
|
}));
|
|
17
|
+
test("create an unescaped binding", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
|
|
18
|
+
yield page.goto("/binding");
|
|
19
|
+
const customElement = yield page.locator("test-element-unescaped");
|
|
20
|
+
yield expect(yield customElement.locator("p").count()).toEqual(1);
|
|
21
|
+
yield expect(customElement).toHaveText("Hello world");
|
|
22
|
+
}));
|
|
17
23
|
}));
|
|
@@ -14,6 +14,15 @@ __decorate([
|
|
|
14
14
|
TestElement.define({
|
|
15
15
|
name: "test-element",
|
|
16
16
|
});
|
|
17
|
+
class TestElementUnescaped extends FASTElement {
|
|
18
|
+
constructor() {
|
|
19
|
+
super(...arguments);
|
|
20
|
+
this.html = `<p>Hello world</p>`;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
TestElementUnescaped.define({
|
|
24
|
+
name: "test-element-unescaped",
|
|
25
|
+
});
|
|
17
26
|
TemplateElement.define({
|
|
18
27
|
name: "f-template",
|
|
19
28
|
});
|
|
@@ -20,6 +20,10 @@ test.describe("f-template", () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
20
20
|
"C"
|
|
21
21
|
];
|
|
22
22
|
});
|
|
23
|
+
const timeout = new Promise(function (resolve) {
|
|
24
|
+
setTimeout(resolve, 100);
|
|
25
|
+
});
|
|
26
|
+
yield timeout;
|
|
23
27
|
const listItemCount2 = yield page.evaluate(() => {
|
|
24
28
|
var _a;
|
|
25
29
|
const customElement = document.getElementsByTagName("test-element");
|
|
@@ -16,7 +16,10 @@ test.describe("f-template", () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
16
16
|
"C"
|
|
17
17
|
];
|
|
18
18
|
});
|
|
19
|
-
|
|
19
|
+
const timeout = new Promise(function (resolve) {
|
|
20
|
+
setTimeout(resolve, 100);
|
|
21
|
+
});
|
|
22
|
+
yield timeout;
|
|
20
23
|
customElementListItems = yield customElement.locator("li");
|
|
21
24
|
expect(yield customElementListItems.count()).toEqual(3);
|
|
22
25
|
expect(yield customElementListItems.nth(0).textContent()).toEqual("A - Bat");
|