From 961b81ed225a1f12067355671cfc0a2302a55b80 Mon Sep 17 00:00:00 2001 From: Alice Pote Date: Thu, 14 Sep 2023 12:50:37 -0400 Subject: [PATCH] docs(component): add documentation for FACE Form-associated custom elements were added to Stencil in https://github.com/ionic-team/stencil/pull/4784 for version 4.5.0. This adds documentation explaining the changes to the component authoring DSL and also breaking down some examples of how the functionality could be used to build out a component. --- cspell-wordlist.txt | 1 + docs/components/component.md | 34 ++++++++ docs/components/form-associated.md | 132 +++++++++++++++++++++++++++++ 3 files changed, 167 insertions(+) create mode 100644 docs/components/form-associated.md diff --git a/cspell-wordlist.txt b/cspell-wordlist.txt index 8e96f59c7..d3aaa9393 100644 --- a/cspell-wordlist.txt +++ b/cspell-wordlist.txt @@ -10,6 +10,7 @@ Deno FOUC POSIX Vetur +WHATWG async autoprefixing contentful diff --git a/docs/components/component.md b/docs/components/component.md index b1c2ee37e..45dd52d15 100644 --- a/docs/components/component.md +++ b/docs/components/component.md @@ -114,6 +114,40 @@ In the example above, the following allows `todo-list` to display the provided a For more information on configuring assets, please see Stencil's [Assets Guide](../guides/assets.md) + +### formAssociated + +**Optional** + +**Type: `boolean`** + +**Default: `false`** + +If `true` the component will be +[form-associated](https://html.spec.whatwg.org/dev/custom-elements.html#form-associated-custom-element), +allowing you to take advantage of the +[`ElementInternals`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/attachInternals) +API to enable your Stencil component to participate in forms. + +A minimal form-associated Stencil component could look like this: + +```tsx +import { Component } from '@stencil/core'; + +@Component({ + tag: 'form-associated', + formAssociated: true +}) +export class FormAssociated { + render() { + return form associated! + } +} +``` + +See the documentation for [form-associated components](./form-associated.md) +for more info and examples. + ### scoped **Optional** diff --git a/docs/components/form-associated.md b/docs/components/form-associated.md new file mode 100644 index 000000000..bee2a3f7c --- /dev/null +++ b/docs/components/form-associated.md @@ -0,0 +1,132 @@ +--- +title: Form-Associated Components +sidebar_label: Form-Associated Components +description: Form-Associated Stencil Components +slug: /form-associated +--- + +# Building Form-Associated Components in Stencil + +As of v4.5.0, Stencil has support for form-associated custom elements. This +allows Stencil components to participate in a rich way in HTML forms, +integrating with native browser features for validation and accessibility while +maintaining encapsulation and control over their styling and presentation. + +:::caution +Browser support for the APIs that this feature depends on is still not +universal[^1] and the Stencil team has no plans at present to support or +incorporate any polyfills for the browser functionality. Before you ship +form-associated Stencil components make sure that the browsers you need to +support have shipped the necessary APIs. +::: + +## Creating a Form-Associated Component + +A form-associated Stencil component is one which sets the new [`formAssociated`](./component.md#formAssociated) +option in the argument to the `@Component` +decorator to `true`, like so: + +```tsx +import { Component } from '@stencil/core'; + +@Component({ + tag: 'my-face', + formAssociated: true, +}) +export class MyFACE { +} +``` + +This element will now be marked as a form-associated custom element via the +[`formAssociated`](https://html.spec.whatwg.org/#custom-elements-face-example) +static property, but by itself this is not terribly useful. + +In order to meaningfully interact with a `
` element that is an ancestor +of our custom element we'll need to get access to an +[`ElementInternals`](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) +object corresponding to our element instance. Stencil provides a decorator, +`@AttachInternals`, which does just this, allowing you to decorate a property on +your component and bind an `ElementInternals` object to that property which you +can then use to interact with the surrounding form. + +:::info +Under the hood the `AttachInternals` decorator makes use of the very similarly +named +[`attachInternals`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/attachInternals) +method on `HTMLElement` to associate your Stencil component with an ancestor +`` element. During compilation, Stencil will generate code that calls +this method at an appropriate point in the component lifecycle for both +[lazy](../output-targets/dist.md) and [custom +elements](../output-targets/custom-elements.md) builds. +::: + +A Stencil component using this API to implement a custom text input could look +like this: + +```tsx title="src/components/custom-text-input.tsx" +import { Component, h, AttachInternals, State } from '@stencil/core'; + +@Component({ + tag: 'custom-text-input', + shadow: true, + formAssociated: true +}) +export class CustomTextInput { + @State() value: string; + + @AttachInternals() internals: ElementInternals; + + handleChange(event) { + this.value = event.target.value; + this.internals.setFormValue(event.target.value); + } + + componentWillLoad() { + this.internals.setFormValue("a default value"); + } + + render() { + return ( + this.handleChange(event)} + /> + ) + } +} +``` + +If this component is rendered within a `` element like so: + + +```html + + +
+``` + +then it will automatically be linked up to the surrounding form. The +`ElementInternals` object found at `this.internals` will have a bunch of +methods on it for interacting with that form and getting key information out of +it. + +In our `` example above we use the +[`setFormValue`](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/setFormValue) +method to set a value in the surrounding form. This will read the `name` +attribute off of the element and use it when setting the value, so the value +typed by a user into the `input` will added to the form under the +`"my-custom-input"` name. + +This example just scratches the surface, and a great deal more is possible with +the `ElementInternals` API, including [setting the element's +validity](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals/setValidity), +reading the validity state of the form, reading other form values, and more. + +## Resources + +- [WHATWG specification for form-associated custom elements](https://html.spec.whatwg.org/dev/custom-elements.html#form-associated-custom-elements) +- [ElementInternals and Form-Associated Custom Elements](https://webkit.org/blog/13711/elementinternals-and-form-associated-custom-elements/) from the WebKit blog + +[^1]: See for up-to-date adoption + estimates.