From c6b9e8a436b7b52e5df02182b663167bb34cad8a Mon Sep 17 00:00:00 2001
From: Jane Chu <7559015+janechu@users.noreply.github.com>
Date: Wed, 15 Jan 2025 15:29:17 -0800
Subject: [PATCH] Add documentation explaining some architectural concepts
(#7033)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
# Pull Request
## 📖 Description
This PR adds documentation on the internals of `FASTElement` and it's lifecycle events. This is meant to serve as a general overview for contributors looking to understand the architecture of FAST custom elements.
## ✅ Checklist
### General
- [ ] I have included a change request file using `$ npm run change`
- [ ] I have added tests for my changes.
- [ ] I have tested my changes.
- [x] I have updated the project documentation to reflect my changes.
- [x] I have read the [CONTRIBUTING](https://github.com/microsoft/fast/blob/master/CONTRIBUTING.md) documentation and followed the [standards](https://github.com/microsoft/fast/blob/master/CODE_OF_CONDUCT.md#our-standards) for this project.
---
...-e44aecad-1602-46a6-a317-92f22a19d964.json | 7 +++
.../fast-element/ARCHITECTURE_FASTELEMENT.md | 63 +++++++++++++++++++
...CHITECTURE_HTML_TAGGED_TEMPLATE_LITERAL.md | 34 ++++++++++
.../fast-element/ARCHITECTURE_INTRO.md | 10 +++
.../fast-element/ARCHITECTURE_OVERVIEW.md | 52 +++++++++++++++
.../fast-element/ARCHITECTURE_UPDATES.md | 11 ++++
6 files changed, 177 insertions(+)
create mode 100644 change/@microsoft-fast-element-e44aecad-1602-46a6-a317-92f22a19d964.json
create mode 100644 packages/web-components/fast-element/ARCHITECTURE_FASTELEMENT.md
create mode 100644 packages/web-components/fast-element/ARCHITECTURE_HTML_TAGGED_TEMPLATE_LITERAL.md
create mode 100644 packages/web-components/fast-element/ARCHITECTURE_INTRO.md
create mode 100644 packages/web-components/fast-element/ARCHITECTURE_OVERVIEW.md
create mode 100644 packages/web-components/fast-element/ARCHITECTURE_UPDATES.md
diff --git a/change/@microsoft-fast-element-e44aecad-1602-46a6-a317-92f22a19d964.json b/change/@microsoft-fast-element-e44aecad-1602-46a6-a317-92f22a19d964.json
new file mode 100644
index 00000000000..1486986567e
--- /dev/null
+++ b/change/@microsoft-fast-element-e44aecad-1602-46a6-a317-92f22a19d964.json
@@ -0,0 +1,7 @@
+{
+ "type": "none",
+ "comment": "Add documentation explaining some architectural concepts",
+ "packageName": "@microsoft/fast-element",
+ "email": "7559015+janechu@users.noreply.github.com",
+ "dependentChangeType": "none"
+}
diff --git a/packages/web-components/fast-element/ARCHITECTURE_FASTELEMENT.md b/packages/web-components/fast-element/ARCHITECTURE_FASTELEMENT.md
new file mode 100644
index 00000000000..e227474a2bf
--- /dev/null
+++ b/packages/web-components/fast-element/ARCHITECTURE_FASTELEMENT.md
@@ -0,0 +1,63 @@
+# FASTElement
+
+The `FASTElement` is our extension of the `HTMLElement`. As such it leverages the lifecycles but also requires additional setup before before the class `constructor` or the `connectedCallback` method is executed which are explained in the [overview](./ARCHITECTURE_OVERVIEW.md). This document explains specifically what `FASTElement` leverages inside the `HTMLElement`s lifecycle hooks.
+
+For an explanation into `HTMLElement` lifecycle hooks refer to the [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks).
+
+## A Custom Element is Detected by the Browser
+
+Because the `FASTElement` is an extension of the `HTMLElement`, it makes use of the same lifecycle hooks and extends them with `$fastController`. It also initializes using another class `ElementController`, the methods this are called during the custom elements native `constructor`, `connectedCallback`, `disconnectedCallback`, and `attributeChangedCallback` lifecycle hooks.
+
+### 🔄 **Lifecycle**: Initialization
+
+```mermaid
+flowchart TD
+ A[A FASTElement
web component is added to the DOM
] --> B
+ B[FASTElement
initializes the ElementController.forCustomElement
passing the Custom Element instance] --> C
+ B --> F[Observables applied to the FASTElement
are updated on the FAST global without values]
+ C{Is an ElementController
available?}
+ C --> |yes|E
+ C --> |no|D
+ D[Initialize a new ElementController
referred to as setting an element controller strategy]
+ D --> F
+ E[ElementController
captures the Custom Element instance and the definition and attaches them to the $fastController
which is then attached to the instance]
+```
+
+### 🔄 **Lifecycle**: Component is connected
+
+```mermaid
+flowchart TD
+ A[browser HTMLElement
's connectedCallback
is called] --> B
+ B[this.$fastController.connect
in FASTElement is called] --> C
+ B --> D
+ B --> E
+ B --> F
+ C[bind observables by capturing the Custom Elements properties and setting the values from the bound observables on the Custom Element]
+ D[connect behaviors by call the connectedCallback
on all behaviors]
+ E[render template, execute an ElementViewTemplate
's render method]
+ F[add styles either as an appended StyleElement
node or an adoptedStylesheet
which may include and attach behaviors]
+```
+
+#### Render Template
+
+The rendering of the template on the `ElementController` is by the `renderTemplate` method which is called during the `ElementController.connect` method which is triggered by `HTMLElement`s `connectedCallback` lifecycle.
+
+The `renderTemplate` identifies the Custom Element, and the shadow root associated with the Custom Element. This then places a rendering of the template (an `ElementView`) onto the internal `view` of the controller. When creating the `ElementView`/`HTMLView` using the `ViewTemplate.render`, the `Compile.compile()` method identifies a `DocumentFragment` either by using an existing `` tag, or creating one to wrap the contents of the shadow root. A new `CompilationContext` is created and the `compileAttributes` function is called, this results in the replacement of the placeholder attributes initally set-up during the pre-render step with their values if a value has been assigned. The factories with the associated nodes identified are then passed to the context. The view then binds all behaviors to the source element. The `CompilationContext.createView` is executed with the `DocumentFragment` as the root, and returns an `HTMLView`. This `HTMLView` includes an `appendTo` method to attach the fragment to the host element, which it then does. It should be noted that the compiled HTML is a `string`, which when set on the `DocumentFragment` as `innerHTML`, this allows the browser to dictate the creation of HTML nodes.
+
+### 🔄 **Lifecycle**: Component is disconnected
+
+When a component is disconnected, a cleanup step is created to remove associated behaviors.
+
+### 🔄 **Lifecycle**: Attribute has been changed
+
+Attributes have an `AttributeDefinition` which allows for converters, attachment of the `Changed` aspect of the `@attr` decorator among other capabilities.
+
+```mermaid
+flowchart TD
+ A[The browser HTMLElement
's attributeChangedCallback
is called] --> B
+ B[this.$fastController.onAttributeChangedCallback
in FASTElement
is called] --> C
+ C[calls the attribute definitions onAttributeChangedCallback
method with the updated value] --> D
+ D[An Updates.enqueue
is called which places the update in the task queue which is then executed, these are performed async unless otherwise specified]
+```
+
+These changes are observed and similar to the way `Observables` work, they utilize an `Accessor` pattern which has a `getValue` and `setValue` in which DOM updates are applied.
\ No newline at end of file
diff --git a/packages/web-components/fast-element/ARCHITECTURE_HTML_TAGGED_TEMPLATE_LITERAL.md b/packages/web-components/fast-element/ARCHITECTURE_HTML_TAGGED_TEMPLATE_LITERAL.md
new file mode 100644
index 00000000000..8c9c447b5a9
--- /dev/null
+++ b/packages/web-components/fast-element/ARCHITECTURE_HTML_TAGGED_TEMPLATE_LITERAL.md
@@ -0,0 +1,34 @@
+# `html` tagged template literal
+
+The `html` export from `@microsoft/fast-element` is used to create the template logic for the custom element.
+
+## Pre-processing the `html` tagged template literal contents
+
+Before the template can be used it goes through a step to convert it into a `ViewTemplate` which it does via the `ViewTemplate.create()` method. This is then used during the `compose` step, before `FASTElement` is instantiated.
+
+During the `Compiler.compile()` method(triggered by `ViewTemplate.create()` method), the following happens for each string:
+- Factories with unique IDs are created for each tag template literal argument (or `TemplateValue`) which matches with the corresponding string
+- A binding is created from the `TemplateValue`
+
+A resulting string using a `createHTML()` function is produced using the `HTMLDirective`s executed for each factory. The behavior is augmented by the previous string from the `html` tag template which determines the aspect if one exists, these aspects are the `@`, `:`, or other binding aspect attached to attributes.
+
+The `createHTML()` function utilizes a `Markup` attribute which is assigned to a factory's unique ID. The strings are concatenated and passed to a new `ViewTemplate` with all the factories (empty until one is assigned) that act as a dictionary with the unique IDs as the key to look up each factory once it has been created. The string this creates is injected into a `` as `innerHTML`, which allows the browser to create the nodes and placeholder factory IDs, with the only `DOM` node that is explicitly created being the wrapping `` element.
+
+## Directives
+
+The `HTMLBindingDirective` applies bindings to items identified as various `TemplateValue`s within the `html` tagged template. The `createHTML` step uses the factory associated with the binding to create strings in the markup using the factory's ID.
+
+```mermaid
+flowchart TD
+ A[A new HTMLBindingDirective
is created with a data binding which has a policy, options, createObserver
, and an evaluate method assigned to the passed arrow function such as x => x.foo
]
+ B[oneTime
binding passes the corresponding tag template argument, an arrow function]
+ C[oneWay
binding passes a copy of the corresponding tag template argument, an arrow function]
+ D[An already specified binding such as a repeat
or when
directive is passed]
+ A --> B
+ A --> C
+ A --> D
+ E[When a createObserver
is called, an Observable.binding
is created passing the arrow function to be evaluated and the subscriber to be notified]
+ C --> E
+ F[When a createObserver
is called, the instance of the one time binding is returned which includes a bind method returning the arrow function executed against the controller source and context]
+ B --> F
+```
\ No newline at end of file
diff --git a/packages/web-components/fast-element/ARCHITECTURE_INTRO.md b/packages/web-components/fast-element/ARCHITECTURE_INTRO.md
new file mode 100644
index 00000000000..b260a28ef46
--- /dev/null
+++ b/packages/web-components/fast-element/ARCHITECTURE_INTRO.md
@@ -0,0 +1,10 @@
+# Introduction
+
+This document (and the linked documents) explains how the exports and side effects of `@microsoft/fast-element` are used to create custom elements.
+
+## Glossary
+
+- [Overview](./ARCHITECTURE_OVERVIEW.md): How the `@microsoft/fast-element` should be used by a developer and what code is executed during the first render.
+- [`FASTElement`](./ARCHITECTURE_FASTELEMENT.md): How the `FASTElement` is architected.
+- [`html` tagged template literal](./ARCHITECTURE_HTML_TAGGED_TEMPLATE_LITERAL.md): How the `html` tagged template literal takes and converts the contents into a `VIEWTemplate`.
+- [`Updates` queue](./ARCHITECTURE_UPDATES.md): How updates to attributes and observables are processed.
\ No newline at end of file
diff --git a/packages/web-components/fast-element/ARCHITECTURE_OVERVIEW.md b/packages/web-components/fast-element/ARCHITECTURE_OVERVIEW.md
new file mode 100644
index 00000000000..68e609acbd8
--- /dev/null
+++ b/packages/web-components/fast-element/ARCHITECTURE_OVERVIEW.md
@@ -0,0 +1,52 @@
+# General FAST usage
+
+`FASTElement` is an extension of `HTMLElement` which makes use of Custom Element APIs native to the browser. It also supplies the following methods:
+
+- The `compose` method combines the Custom Element name, template, style, and other options to create the definition for the Custom Element.
+- The `define` method makes use of the native Custom Element [`define()`](https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/define) to register the Custom Element with a Custom Element Registry.
+- The `from` method allows the use of Customized Built-in elements, which extend from native elements such as `HTMLButtonElement`.
+
+### Creating a Custom Element from FASTElement
+
+A basic developer flow for defining a custom element looks like this:
+
+```mermaid
+flowchart TD
+ A[Create a FASTElement
web component by extending the FASTElement
class] --> F
+ F[Compose with FASTElement.compose
to include template and styles] --> G[Define the component in the browser with FASTElement.define
]
+```
+
+Let's take a look at the compose step to see what the FAST architecture is doing at this stage.
+
+### Composing a Custom Element
+
+The `FASTElement.compose()` function creates a new `FASTElementDefinition`, which includes all metadata needed for the element (such as templates, attributes, and styles). The element definition is registered with the global `FAST` for re-use, and the `FASTElementDefinition` is returned. The resulting object can be retrieved via `ElementController.definition`.
+
+## A Custom Element in JavaScript is sent to the Browser
+
+Let's step back from defining the Custom Element and consider what is happening when we import from the `@microsoft/fast-element` package.
+
+First, a global `FAST` property will be created if one does not already exist, typically in browser on the `window`.
+
+Additionally, when Custom Elements are included in a script a few things might happen even before a Custom Element gets detected by the browser. First, there are initial side effects caused by the use of decorators. These include the `attr` and `observable` decorators made available by the `@microsoft/fast-element` package.
+
+Here is a basic flow of what code is executed and when during initial load of a script that contains a FAST defined Custom Element:
+
+```mermaid
+flowchart TD
+ A@{ shape: circle, label: "The browser loads the JavaScript file containing FAST and FAST Custom Element definitions" }
+ B@{ shape: rect, label: "The FAST global is added to the window" }
+ A --> B
+ C@{ shape: rect, label: "A Custom Element executes the compose
step"}
+ B --> C
+ D@{ shape: procs, label: "- Any defined observable decorators are added to the FAST global
- An attribute decorator locates the associated Custom Elements constructor which it uses to push itself to the Custom Elements attribute collection
"}
+ C --> D
+ F@{ shape: rect, label: "An HTML tagged template literal is executed for the FAST Custom Element and applied to the definition" }
+ D --> F
+ G@{ shape: rect, label: "The define
step is hit and the customElement
is registered with the Custom Element Registry" }
+ F --> G
+ H@{ shape: rect, label: "A Custom Element is detected on the page and the browser initializes it" }
+ I@{ shape: dbl-circ, label: "The lifecycle steps for FASTElement
are executed" }
+ G --> H
+ H --> I
+```
\ No newline at end of file
diff --git a/packages/web-components/fast-element/ARCHITECTURE_UPDATES.md b/packages/web-components/fast-element/ARCHITECTURE_UPDATES.md
new file mode 100644
index 00000000000..6d2502d3d18
--- /dev/null
+++ b/packages/web-components/fast-element/ARCHITECTURE_UPDATES.md
@@ -0,0 +1,11 @@
+# `Updates`
+
+The `Updates` API is part of the `FAST` global. The updates that are queued include updates to attributes, observables, and observed arrays.
+
+## Attributes
+
+An attribute change may be queued when the value of the attribute is updated. This change is triggered by `HTMLElement` lifecycle hook `attributeChangedCallback`. The `Updates` queue is used to try to reflect the attribute with the updated value onto the element using `DOM` APIs.
+
+## Observables
+
+The `Reflect` API is used to override `defineProperty` in order to observe a property on an Custom Element, this overrides the getter and setter, which allows subscribers to be notified of changes. Any changes which `set` the value are passed to the `Updates` queue.
\ No newline at end of file