From 4118ea4d61b6ce47417d3643ef9d4720eed9f1cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Kov=C3=A1=C5=99?= Date: Thu, 16 May 2024 23:38:42 +0200 Subject: [PATCH 1/3] feat: formAssociated attribute --- src/index.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/index.js b/src/index.js index 815e773..ea157a5 100644 --- a/src/index.js +++ b/src/index.js @@ -64,6 +64,10 @@ export default function register(Component, tagName, propNames, options) { Object.keys(Component.propTypes || {}); PreactElement.observedAttributes = propNames; + if (Component.formAssociated) { + PreactElement.formAssociated = true; + } + // Keep DOM properties and Preact props in sync propNames.forEach((name) => { Object.defineProperty(PreactElement.prototype, name, { From 5a252b3a4548f70c92342206bae33001fdd21c0d Mon Sep 17 00:00:00 2001 From: Ryan Christian Date: Tue, 2 Sep 2025 22:46:44 -0500 Subject: [PATCH 2/3] test: Add test case for class & function components --- src/index.test.jsx | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/index.test.jsx b/src/index.test.jsx index f293941..b1a7592 100644 --- a/src/index.test.jsx +++ b/src/index.test.jsx @@ -1,5 +1,5 @@ import { assert } from '@open-wc/testing'; -import { h, createContext } from 'preact'; +import { h, createContext, Component } from 'preact'; import { useContext } from 'preact/hooks'; import { act } from 'preact/test-utils'; import registerElement from './index'; @@ -279,4 +279,34 @@ describe('web components', () => { root.appendChild(el); assert.isTrue(el.shadowRoot === null); }); + + it('supports the `formAssociated` property', async () => { + class FormAssociatedClass extends Component { + static formAssociated = true; + + render() { + return ; + } + } + registerElement(FormAssociatedClass, 'x-form-associated-class', []); + + function FormAssociatedFunction() { + return ; + } + FormAssociatedFunction.formAssociated = true; + registerElement(FormAssociatedFunction, 'x-form-associated-function', []); + + root.innerHTML = ` +
+ + +
+ `; + + const myForm = document.getElementById('myForm'); + + // The `.elements` property of a form includes all form-associated elements + assert.equal(myForm.elements[0].tagName, 'X-FORM-ASSOCIATED-CLASS'); + assert.equal(myForm.elements[2].tagName, 'X-FORM-ASSOCIATED-FUNCTION'); + }); }); From 83f66152c2163f193b49b3b86e216dbfc76cef94 Mon Sep 17 00:00:00 2001 From: Ryan Christian Date: Wed, 3 Sep 2025 11:09:15 -0500 Subject: [PATCH 3/3] docs: Add mention to ReadMe w/ new "Static Properties" section --- README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.md b/README.md index 426b12b..d4e7dab 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,25 @@ register(TextSelection, 'text-selection', [], { shadow: true }); ``` +### Static Properties + +We support a number of static properties on your component that map to special behaviors of the custom element. These can be set on components like so: + +```js +class MyCustomElement extends Component { + static tagName = 'my-custom-element'; +} + +function MyOtherCustomElement() { ... } +MyOtherCustomElement.tagName = 'my-other-custom-element'; +``` + +- `tagName` + - the custom element's tag name (if not passed as the second argument to `register()`) +- `observedAttributes` + - an array of attribute names to observe (if not passed as the third argument to `register()`) +- `formAssociated` + - a boolean indicating whether the custom element should be form-associated ## Related