@microsoft/fast-html 1.0.0-alpha.12 → 1.0.0-alpha.13

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 CHANGED
@@ -30,20 +30,14 @@ MyCustomElement.define({
30
30
  shadowOptions: null,
31
31
  });
32
32
 
33
- TemplateElement.options({
34
- "my-custom-element": {
35
- shadowOptions: {
36
- mode: "closed",
37
- }
38
- },
39
- }).define({
33
+ TemplateElement.define({
40
34
  name: "f-template",
41
35
  });
42
36
  ```
43
37
 
44
38
  This will include the `<f-template>` custom element and all logic for interpreting the declarative HTML syntax for a FAST web component as well as the `shadowOptions` for any element an `<f-template>` has been used to define.
45
39
 
46
- It is necessary to set the initial `shadowOptions` of your custom elements to `null` otherwise a shadowRoot will be attached and cause a FOUC (Flash Of Unstyled Content). For more information about how this affects hydration, check out our [document](./RENDERING.md#setting-shadow-options) on rendering DOM from non-browser.
40
+ It is necessary to set the initial `shadowOptions` of your custom elements to `null` otherwise a shadowRoot will be attached and cause a FOUC (Flash Of Unstyled Content). For more information about how this affects hydration, check out our [document](./RENDERING.md#setting-shadow-options) on rendering DOM from a non-browser environment.
47
41
 
48
42
  The template must be wrapped in `<f-template name="[custom-element-name]"><template>[template logic]</template></f-template>` with a `name` attribute for the custom elements name, and the template logic inside.
49
43
 
@@ -59,6 +53,50 @@ Example:
59
53
  </f-template>
60
54
  ```
61
55
 
56
+ #### Non-browser HTML rendering
57
+
58
+ One of the benefits of FAST declarative HTML templates is that the server can be stack agnostic as JavaScript does not need to be interpreted. By default `@microsoft/fast-html` will expect hydratable content and uses comments and datasets for tracking the binding logic. For more information on what that markup should look like, as well as an example of how initial state may be applied, read our [documentation](./RENDERING.md) to understand what markup should be generated for a hydratable experience. For the sake of brevity hydratable markup will be excluded from the README.
59
+
60
+ #### Adding shadowOptions
61
+
62
+ By default `shadowOptions` via the `TemplateElement` will be with `"mode": "open"` once the template has been set. To set each components `shadowOptions` you can pass an `options` object.
63
+
64
+ Example:
65
+
66
+ ```typescript
67
+ TemplateElement.options({
68
+ "my-custom-element": {
69
+ shadowOptions: {
70
+ mode: "closed",
71
+ }
72
+ },
73
+ }).define({
74
+ name: "f-template",
75
+ });
76
+ ```
77
+
78
+ #### Using the RenderableFASTElement
79
+
80
+ The exported abstract class `RenderableFASTElement` is available for automatic addition and removal of `defer-hydration` and `needs-hydration`. If you use `FASTElement` you will need to add `defer-hydration` and `needs-hydration` attributes to your rendered markup and remove them via your component.
81
+
82
+ Example:
83
+ ```typescript
84
+ import { RenderableFASTElement, TemplateElement } from "@microsoft/fast-html";
85
+
86
+ class MyCustomElement extends RenderableFASTElement {
87
+ // component logic
88
+ }
89
+
90
+ MyCustomElement.define({
91
+ name: "my-custom-element",
92
+ shadowOptions: null,
93
+ });
94
+
95
+ TemplateElement.define({
96
+ name: "f-template",
97
+ });
98
+ ```
99
+
62
100
  ### Syntax
63
101
 
64
102
  All bindings use a handlebars-like syntax.
@@ -217,10 +255,6 @@ If your template includes JavaScript specific logic that does not conform to tho
217
255
  - `@microsoft/fast-html/rules/member-expression.yml`
218
256
  - `@microsoft/fast-html/rules/tag-function-to-template-literal.yml`
219
257
 
220
- ### Non-browser HTML rendering
221
-
222
- One of the benefits of FAST declarative HTML templates is that the server can be stack agnostic as JavaScript does not need to be interpreted. FASTElement will expect hydratable content however and uses comments and datasets for tracking the binding logic. For more information on what that markup should look like, as well as an example of how initial state may be applied, read our [documentation](./RENDERING.md) to understand what markup should be generated for a hydratable experience.
223
-
224
258
  ## Acknowledgements
225
259
 
226
260
  This project has been heavily inspired by [Handlebars](https://handlebarsjs.com/) and [Vue.js](https://vuejs.org/).
@@ -0,0 +1,6 @@
1
+ import { FASTElement } from "@microsoft/fast-element";
2
+ export declare abstract class RenderableFASTElement extends FASTElement {
3
+ deferHydration: boolean;
4
+ needsHydration: boolean;
5
+ constructor();
6
+ }
@@ -1 +1,2 @@
1
1
  export { TemplateElement } from "./template.js";
2
+ export { RenderableFASTElement } from "./element.js";
@@ -1,11 +1,13 @@
1
1
  import { FASTElement, ShadowRootOptions } from "@microsoft/fast-element";
2
+ import "@microsoft/fast-element/install-hydratable-view-templates.js";
3
+ export interface ElementOptions {
4
+ shadowOptions?: ShadowRootOptions | undefined;
5
+ }
2
6
  /**
3
7
  * A dictionary of element options the TemplateElement will use to update the registered element
4
8
  */
5
- interface ElementOptions {
6
- [key: string]: {
7
- shadowOptions: ShadowRootOptions | undefined;
8
- };
9
+ export interface ElementOptionsDictionary<ElementOptionsType = ElementOptions> {
10
+ [key: string]: ElementOptionsType;
9
11
  }
10
12
  /**
11
13
  * The <f-template> custom element that will provide view logic to the element
@@ -18,9 +20,16 @@ declare class TemplateElement extends FASTElement {
18
20
  /**
19
21
  * A dictionary of custom element options
20
22
  */
21
- static elementOptions: ElementOptions;
23
+ static elementOptions: ElementOptionsDictionary;
22
24
  private partials;
23
- static options(elementOptions?: ElementOptions): typeof TemplateElement;
25
+ private static defaultElementOptions;
26
+ static options(elementOptions?: ElementOptionsDictionary): typeof TemplateElement;
27
+ constructor();
28
+ /**
29
+ * Set options for a custom element
30
+ * @param name - The name of the custom element to set options for.
31
+ */
32
+ private static setOptions;
24
33
  connectedCallback(): void;
25
34
  /**
26
35
  * Resolve strings and values from an innerHTML string
@@ -1 +1 @@
1
- export { TemplateElement } from "./components/index.js";
1
+ export { RenderableFASTElement, TemplateElement } from "./components/index.js";
@@ -0,0 +1,25 @@
1
+ import { __decorate, __metadata } from "tslib";
2
+ import { attr, FASTElement, Observable } from "@microsoft/fast-element";
3
+ export class RenderableFASTElement extends FASTElement {
4
+ constructor() {
5
+ super();
6
+ this.deferHydration = true;
7
+ this.needsHydration = true;
8
+ this.setAttribute("defer-hydration", "");
9
+ this.setAttribute("needs-hydration", "");
10
+ Observable.defineProperty(this.$fastController.definition, "shadowOptions");
11
+ Observable.getNotifier(this.$fastController.definition).subscribe({
12
+ handleChange: () => {
13
+ this.deferHydration = false;
14
+ },
15
+ }, "shadowOptions");
16
+ }
17
+ }
18
+ __decorate([
19
+ attr({ mode: "boolean", attribute: "defer-hydration" }),
20
+ __metadata("design:type", Boolean)
21
+ ], RenderableFASTElement.prototype, "deferHydration", void 0);
22
+ __decorate([
23
+ attr({ mode: "boolean", attribute: "needs-hydration" }),
24
+ __metadata("design:type", Boolean)
25
+ ], RenderableFASTElement.prototype, "needsHydration", void 0);
@@ -1 +1,2 @@
1
1
  export { TemplateElement } from "./template.js";
2
+ export { RenderableFASTElement } from "./element.js";
@@ -1,5 +1,6 @@
1
1
  import { __awaiter, __decorate, __metadata } from "tslib";
2
- import { attr, DOMAspect, FAST, FASTElement, fastElementRegistry, ViewTemplate, } from "@microsoft/fast-element";
2
+ import { attr, DOMAspect, FAST, FASTElement, fastElementRegistry, HydratableElementController, ViewTemplate, } from "@microsoft/fast-element";
3
+ import "@microsoft/fast-element/install-hydratable-view-templates.js";
3
4
  import { DOMPolicy } from "@microsoft/fast-element/dom-policy.js";
4
5
  import { getAllPartials, getExpressionChain, getNextBehavior, pathResolver, resolveWhen, transformInnerHTML, } from "./utilities.js";
5
6
  function allow(tagName, aspect, aspectName, sink) {
@@ -11,13 +12,34 @@ function allow(tagName, aspect, aspectName, sink) {
11
12
  * The <f-template> custom element that will provide view logic to the element
12
13
  */
13
14
  class TemplateElement extends FASTElement {
15
+ static options(elementOptions = {}) {
16
+ var _a;
17
+ const result = {};
18
+ for (const key in elementOptions) {
19
+ const value = elementOptions[key];
20
+ result[key] = {
21
+ shadowOptions: (_a = value.shadowOptions) !== null && _a !== void 0 ? _a : TemplateElement.defaultElementOptions.shadowOptions,
22
+ };
23
+ }
24
+ this.elementOptions = result;
25
+ HydratableElementController.install();
26
+ return this;
27
+ }
14
28
  constructor() {
15
- super(...arguments);
29
+ super();
16
30
  this.partials = {};
31
+ if (!!TemplateElement.elementOptions) {
32
+ TemplateElement.options();
33
+ }
17
34
  }
18
- static options(elementOptions = {}) {
19
- this.elementOptions = elementOptions;
20
- return this;
35
+ /**
36
+ * Set options for a custom element
37
+ * @param name - The name of the custom element to set options for.
38
+ */
39
+ static setOptions(name) {
40
+ if (!!!TemplateElement.elementOptions[name]) {
41
+ TemplateElement.elementOptions[name] = TemplateElement.defaultElementOptions;
42
+ }
21
43
  }
22
44
  connectedCallback() {
23
45
  super.connectedCallback();
@@ -25,7 +47,10 @@ class TemplateElement extends FASTElement {
25
47
  this.$fastController.definition.registry
26
48
  .whenDefined(this.name)
27
49
  .then((value) => __awaiter(this, void 0, void 0, function* () {
28
- var _a;
50
+ var _a, _b;
51
+ if (this.name && !!!((_a = TemplateElement.elementOptions) === null || _a === void 0 ? void 0 : _a[this.name])) {
52
+ TemplateElement.setOptions(this.name);
53
+ }
29
54
  const registeredFastElement = fastElementRegistry.getByType(value);
30
55
  const template = this.getElementsByTagName("template").item(0);
31
56
  if (template) {
@@ -38,7 +63,7 @@ class TemplateElement extends FASTElement {
38
63
  this.resolveTemplateOrBehavior(strings, values);
39
64
  // set shadow options as defined by the f-template
40
65
  registeredFastElement.shadowOptions =
41
- (_a = TemplateElement.elementOptions[this.name]) === null || _a === void 0 ? void 0 : _a.shadowOptions;
66
+ (_b = TemplateElement.elementOptions[this.name]) === null || _b === void 0 ? void 0 : _b.shadowOptions;
42
67
  }
43
68
  }
44
69
  else {
@@ -182,7 +207,7 @@ class TemplateElement extends FASTElement {
182
207
  (closingParenthesis - openingParenthesis) -
183
208
  1);
184
209
  const arg = bindingHTML.slice(openingParenthesis + 1, closingParenthesis);
185
- const binding = (x, c) => pathResolver(propName, self)(x, c)(...(arg === "e" ? [c.event] : []), ...(arg !== "e" && arg !== ""
210
+ const binding = (x, c) => pathResolver(propName, self)(x, c).bind(x)(...(arg === "e" ? [c.event] : []), ...(arg !== "e" && arg !== ""
186
211
  ? [pathResolver(arg)(x, c)]
187
212
  : []));
188
213
  values.push(binding);
@@ -252,6 +277,11 @@ class TemplateElement extends FASTElement {
252
277
  * A dictionary of custom element options
253
278
  */
254
279
  TemplateElement.elementOptions = {};
280
+ TemplateElement.defaultElementOptions = {
281
+ shadowOptions: {
282
+ mode: "open",
283
+ },
284
+ };
255
285
  __decorate([
256
286
  attr,
257
287
  __metadata("design:type", String)
@@ -1,7 +1,7 @@
1
1
  import { __decorate, __metadata } from "tslib";
2
- import { TemplateElement } from "@microsoft/fast-html";
3
- import { attr, FASTElement } from "@microsoft/fast-element";
4
- class TestElement extends FASTElement {
2
+ import { RenderableFASTElement, TemplateElement } from "@microsoft/fast-html";
3
+ import { attr } from "@microsoft/fast-element";
4
+ class TestElement extends RenderableFASTElement {
5
5
  constructor() {
6
6
  super(...arguments);
7
7
  this.type = "radio";
@@ -15,12 +15,6 @@ TestElement.define({
15
15
  name: "test-element",
16
16
  shadowOptions: null,
17
17
  });
18
- TemplateElement.options({
19
- "test-element": {
20
- shadowOptions: {
21
- mode: "closed",
22
- },
23
- },
24
- }).define({
18
+ TemplateElement.define({
25
19
  name: "f-template",
26
20
  });
@@ -1,7 +1,7 @@
1
1
  import { __decorate, __metadata } from "tslib";
2
- import { TemplateElement } from "@microsoft/fast-html";
3
- import { attr, FASTElement } from "@microsoft/fast-element";
4
- class TestElement extends FASTElement {
2
+ import { RenderableFASTElement, TemplateElement } from "@microsoft/fast-html";
3
+ import { attr } from "@microsoft/fast-element";
4
+ class TestElement extends RenderableFASTElement {
5
5
  constructor() {
6
6
  super(...arguments);
7
7
  this.text = "Hello";
@@ -13,8 +13,9 @@ __decorate([
13
13
  ], TestElement.prototype, "text", void 0);
14
14
  TestElement.define({
15
15
  name: "test-element",
16
+ shadowOptions: null,
16
17
  });
17
- class TestElementUnescaped extends FASTElement {
18
+ class TestElementUnescaped extends RenderableFASTElement {
18
19
  constructor() {
19
20
  super(...arguments);
20
21
  this.html = `<p>Hello world</p>`;
@@ -24,17 +25,6 @@ TestElementUnescaped.define({
24
25
  name: "test-element-unescaped",
25
26
  shadowOptions: null,
26
27
  });
27
- TemplateElement.options({
28
- "test-element": {
29
- shadowOptions: {
30
- mode: "closed",
31
- },
32
- },
33
- "test-element-unescaped": {
34
- shadowOptions: {
35
- mode: "closed",
36
- },
37
- },
38
- }).define({
28
+ TemplateElement.define({
39
29
  name: "f-template",
40
30
  });
@@ -1,7 +1,7 @@
1
1
  import { __decorate, __metadata } from "tslib";
2
- import { TemplateElement } from "@microsoft/fast-html";
3
- import { FASTElement, observable } from "@microsoft/fast-element";
4
- class TestElement extends FASTElement {
2
+ import { RenderableFASTElement, TemplateElement } from "@microsoft/fast-html";
3
+ import { observable } from "@microsoft/fast-element";
4
+ class TestElement extends RenderableFASTElement {
5
5
  constructor() {
6
6
  super(...arguments);
7
7
  this.listItems = [];
@@ -20,12 +20,6 @@ TestElement.define({
20
20
  name: "test-element",
21
21
  shadowOptions: null,
22
22
  });
23
- TemplateElement.options({
24
- "test-element": {
25
- shadowOptions: {
26
- mode: "closed",
27
- },
28
- },
29
- }).define({
23
+ TemplateElement.define({
30
24
  name: "f-template",
31
25
  });
@@ -1,6 +1,5 @@
1
- import { TemplateElement } from "@microsoft/fast-html";
2
- import { FASTElement } from "@microsoft/fast-element";
3
- class TestElement extends FASTElement {
1
+ import { RenderableFASTElement, TemplateElement } from "@microsoft/fast-html";
2
+ class TestElement extends RenderableFASTElement {
4
3
  constructor() {
5
4
  super(...arguments);
6
5
  this.object = {
@@ -12,12 +11,6 @@ TestElement.define({
12
11
  name: "test-element",
13
12
  shadowOptions: null,
14
13
  });
15
- TemplateElement.options({
16
- "test-element": {
17
- shadowOptions: {
18
- mode: "closed",
19
- },
20
- },
21
- }).define({
14
+ TemplateElement.define({
22
15
  name: "f-template",
23
16
  });
@@ -25,4 +25,11 @@ test.describe("f-template", () => __awaiter(void 0, void 0, void 0, function* ()
25
25
  yield customElement.locator("button").nth(2).click();
26
26
  expect(message).toEqual("bar");
27
27
  }));
28
+ test("should properly bind events with `this`", ({ page }) => __awaiter(void 0, void 0, void 0, function* () {
29
+ yield page.goto("/event");
30
+ const customElement = yield page.locator("test-element");
31
+ yield expect(customElement).toHaveJSProperty("foo", "bar");
32
+ yield customElement.locator("button").nth(3).click();
33
+ yield expect(customElement).toHaveJSProperty("foo", "modified-by-click");
34
+ }));
28
35
  }));
@@ -1,7 +1,7 @@
1
1
  import { __decorate, __metadata } from "tslib";
2
- import { TemplateElement } from "@microsoft/fast-html";
3
- import { attr, FASTElement } from "@microsoft/fast-element";
4
- class TestElement extends FASTElement {
2
+ import { RenderableFASTElement, TemplateElement } from "@microsoft/fast-html";
3
+ import { attr } from "@microsoft/fast-element";
4
+ class TestElement extends RenderableFASTElement {
5
5
  constructor() {
6
6
  super(...arguments);
7
7
  this.foo = "";
@@ -15,6 +15,9 @@ class TestElement extends FASTElement {
15
15
  console.log(foo);
16
16
  };
17
17
  }
18
+ handleModifyAttributeClick() {
19
+ this.foo = "modified-by-click";
20
+ }
18
21
  }
19
22
  __decorate([
20
23
  attr,
@@ -24,12 +27,6 @@ TestElement.define({
24
27
  name: "test-element",
25
28
  shadowOptions: null,
26
29
  });
27
- TemplateElement.options({
28
- "test-element": {
29
- shadowOptions: {
30
- mode: "closed",
31
- },
32
- },
33
- }).define({
30
+ TemplateElement.define({
34
31
  name: "f-template",
35
32
  });
@@ -27,12 +27,6 @@ TestElement.define({
27
27
  name: "test-element",
28
28
  shadowOptions: null,
29
29
  });
30
- TemplateElement.options({
31
- "test-element": {
32
- shadowOptions: {
33
- mode: "closed",
34
- },
35
- },
36
- }).define({
30
+ TemplateElement.define({
37
31
  name: "f-template",
38
32
  });
@@ -1,6 +1,5 @@
1
- import { TemplateElement } from "@microsoft/fast-html";
2
- import { FASTElement } from "@microsoft/fast-element";
3
- class TestElement extends FASTElement {
1
+ import { RenderableFASTElement, TemplateElement } from "@microsoft/fast-html";
2
+ class TestElement extends RenderableFASTElement {
4
3
  constructor() {
5
4
  super(...arguments);
6
5
  this.video = null;
@@ -10,12 +9,6 @@ TestElement.define({
10
9
  name: "test-element",
11
10
  shadowOptions: null,
12
11
  });
13
- TemplateElement.options({
14
- "test-element": {
15
- shadowOptions: {
16
- mode: "closed",
17
- },
18
- },
19
- }).define({
12
+ TemplateElement.define({
20
13
  name: "f-template",
21
14
  });
@@ -1,7 +1,7 @@
1
1
  import { __decorate, __metadata } from "tslib";
2
- import { TemplateElement } from "@microsoft/fast-html";
3
- import { FASTElement, observable } from "@microsoft/fast-element";
4
- class TestElement extends FASTElement {
2
+ import { RenderableFASTElement, TemplateElement } from "@microsoft/fast-html";
3
+ import { observable } from "@microsoft/fast-element";
4
+ class TestElement extends RenderableFASTElement {
5
5
  constructor() {
6
6
  super(...arguments);
7
7
  this.list = ["Foo", "Bar"];
@@ -16,7 +16,7 @@ TestElement.define({
16
16
  name: "test-element",
17
17
  shadowOptions: null,
18
18
  });
19
- class TestElementInnerWhen extends FASTElement {
19
+ class TestElementInnerWhen extends RenderableFASTElement {
20
20
  constructor() {
21
21
  super(...arguments);
22
22
  this.list = [
@@ -39,17 +39,6 @@ TestElementInnerWhen.define({
39
39
  name: "test-element-inner-when",
40
40
  shadowOptions: null,
41
41
  });
42
- TemplateElement.options({
43
- "test-element": {
44
- shadowOptions: {
45
- mode: "closed",
46
- },
47
- },
48
- "test-element-inner-when": {
49
- shadowOptions: {
50
- mode: "closed",
51
- },
52
- },
53
- }).define({
42
+ TemplateElement.define({
54
43
  name: "f-template",
55
44
  });
@@ -1,7 +1,7 @@
1
1
  import { __decorate, __metadata } from "tslib";
2
- import { TemplateElement } from "@microsoft/fast-html";
3
- import { FASTElement, observable } from "@microsoft/fast-element";
4
- class TestElement extends FASTElement {
2
+ import { RenderableFASTElement, TemplateElement } from "@microsoft/fast-html";
3
+ import { observable } from "@microsoft/fast-element";
4
+ class TestElement extends RenderableFASTElement {
5
5
  constructor() {
6
6
  super(...arguments);
7
7
  this.slottedNodes = [];
@@ -18,12 +18,6 @@ TestElement.define({
18
18
  name: "test-element",
19
19
  shadowOptions: null,
20
20
  });
21
- TemplateElement.options({
22
- "test-element": {
23
- shadowOptions: {
24
- mode: "closed",
25
- },
26
- },
27
- }).define({
21
+ TemplateElement.define({
28
22
  name: "f-template",
29
23
  });