Skip to content

Commit

Permalink
docs(all) add initial readmes for components and root
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelwarren1106 committed Dec 30, 2021
1 parent 0c6b18f commit b7c086b
Show file tree
Hide file tree
Showing 3 changed files with 205 additions and 0 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Form Participation
The ability to create custom form elements that extend the behavior and UI/UX of native form elements is an area that has a huge need for standardization. The packages in this monorepo enable web component authors seeking to create custom element inputs a standardized approach to do so.


### Packages

- [`@open-wc/form-control`](./packages/form-control) : A `FormControlMixin` that enables creating a web component that functions like a native form element in a standardized way
- [`@open-wc/form-utils`](./packages/form-utils) Form control related utilities such as implicit submit and form value parsing.

177 changes: 177 additions & 0 deletions packages/form-control/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
# @open-wc/form-control
A standardized mixin for creating custom web component form controls with a standardized validation function pattern.

## Install

```sh
# npm
npm install @open-wc/form-control

# yarn
yarn add @open-wc/form-control
```

## Usage

After importing, create a web component class that extends the mixin, and provide your desired base class as the input to `FormControlMixin`.

> The `FormControlMixin` has been tested with both [LitElement](https://lit.dev/) and `HTMLElement`, so `LitElement` is not required, but all examples in this documentation will show `LitElement` web component syntax and decorators.

```js
// custom web component class that extends FormControlMixin

import { LitElement, html } from 'lit';
import { customElement, query, property } from 'lit/decorators.js'
import { live } from 'lit/directives/live.js';

import { FormControlMixin } from '@open-wc/form-participation';

@customElement('demo-form-control')
class DemoFormControl extends FormControlMixin(LitElement) {
@property({ type: String })
value = '';

render() {
return html`
<label for="input"><slot></slot></label>
<input
id="input"
.value="${live(this.value)}"
@input="${this.#onInput}"
>
`;
}

#onInput({ target }: { target: HTMLInputElement }): void {
this.value = target.value;
}
}
```

Now, the `demo-form-control` custom element will participate as if it was a native element in an HTML form.

```html
<form>
<demo-form-control
name="demo"
value="Hello world"
>Demo form element</demo-form-control>

<button type="submit">Submit</button>
</form>

<script>
const form = document.querySelector('form');
form.addEventListener('submit', event => {
/** Prevent the page from reloading */
event.preventDefault();
/** Get form data object via built-in API */
const data = new FormData(event.target);
console.log('demo-form-control value:', data.get('demo'));
});
</script>
```

### ElementInternals

This library makes use of [ElementInternals](https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals) features. As of the time of writing `ElementInternals` features are fully supported in Chrome, partially supported in Firefox and being strongly considered by Webkit.

In order to make these features work in all browsers you will need to include the [element-internals-polyfill](https://www.npmjs.com/package/element-internals-polyfill). Refer to the `element-internals-polyfill` documentation for installation and usage instructions.

### `Value` & `checked`
Any component that uses the `FormControlMixin` will have a `value` property that the element will apply to the parent form. If the element also has a `checked` property on the prototype (think checkbox or radio button) the element's value will only be applied to the parent form when the `checked` property is truthy (like native checkboxes and radio buttons behave)

## Validation

The `FormControlMixin` includes an API for constraint validations and a set of common validators for validity states like `required`, `minlength`, `maxlength` and `pattern`.

```typescript
import { LitElement, html } from 'lit';
import { customElement, query, property } from 'lit/decorators.js'
import { live } from 'lit/directives/live.js';

import { FormControlMixin } from './path/to/mixin';
import { requiredValidator } from './path/to/mixin/validators';

@customElement('demo-form-control')
class DemoFormControl extends FormControlMixin(LitElement) {
static formControlValidators = [requiredValidator];

@property({ type: Boolean, reflect: true })
required = false;

@property({ type: String })
value = '';

render() {
return html`
<label for="input"><slot></slot></label>
<input
id="input"
.value="${live(this.value)}"
@input="${this.#onInput}"
>
`;
}

#onInput({ target }: { target: HTMLInputElement }): void {
this.value = target.value;
}
}
```

Including the `requiredValidator` adds a validation function attached to the `valueMissing` validity state to the component instance.

> Note, this does require the element's prototype to actually have a `required` property defined.
### Validation Target
Every `FormControlMixin` element will need a public `validationTarget` which must be a focusable DOM element. In the event a control becomes invalid, this item will be focused on form submit for accessibility purposes. Failure to do so will cause an error to throw.

### Validators
This package contains a few standardized validators, though more could be added for various unconsidered use cases. So far, there are validators for:

- **required** (valueMissing) : fails when the element's `value` is falsy while the element's `required` property equals `true`
- **minlength** (rangeUnderflow) : fails if the length of the element's value is less than the defined `minLength`
- **maxlength** (rangeOverflow) : fails if the length of the element's value is greater than the defined `maxLength`
- **programmatic** (customError) : Allows setting a completely custom error state and message as a string.

If you have an idea for another standardized validator, please [Submit an issue](/../../issues) (preferred so that we can discuss) or [Send a PR](/../../pulls) with your ideas.

### Creating a custom validator

It is possible to create a custom validator object using the `Validator` interface:

```typescript
export interface Validator {
attribute?: string;
key?: string;
message: string | ((instance: any, value: any) => string);
callback(instance: HTMLElement, value: any): boolean;
}
```

| Property | Type | Required | Description |
| ---- | ---- | ---- | ---- |
| attribute | `string`| true | If defined, adds the specified attribute to the element's `observedAttributes` and the validator will run when the provided attribute changed |
| key| `string` | - | String name of one of the fields in the `ValidityState` object to override on validator change. If `key` is not set, it is assumed to be `customError`. |
| message | `string \| ((instance: any, value: any) => string)` | true | When set to a string, the `message` will equal the string passed in. If set to a function, the validation message will be the returned value from the callback. The message callback takes two arguments, the element instance and the control's form value (not the element's value property) |
| callback | `(instance: any, value: any) => boolean`| true | When `callback` returns `true`, the validator is considered to be in a valid state. When the callback returns `false` the validator is considered to be in an invalid state. |


#### Example custom validator

So, a validator that would key off an `error` attribute to attach a programatic validation to an input might look like this:

```typescript
export const programaticValidator: Validator = {
attribute: 'error',
message(instance: HTMLElement & { error: string }): string {
return instance.error;
},
callback(instance: HTMLElement & { error: string }): boolean {
return !instance.error;
}
};
```
19 changes: 19 additions & 0 deletions packages/form-utils/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# @open-wc/form-utils
A collection of form control related utilities for working with forms.


## Install

```sh
# npm
npm install @open-wc/form-utils

# yarn
yarn add @open-wc/form-utils
```

### Implicit form submit

### Parse form values

### Parse form object

0 comments on commit b7c086b

Please sign in to comment.