| Version | Tag | Status | API |
|---|---|---|---|
| v1 | <oc-form-group-v1> |
Stable, allowed for generation | FormGroupV1 |
Overview (v1)
Source: ./src/components/form-group/v1/Overview.mdx
Form group
The form group component groups multiple form elements in a container and provides a label it. It supports flexible layout options, validation messages, hint messages, and ARIA labels for accessibility.
Default variation
Story Default:
<oc-form-group-v1 class="${className}" oc-aria-label="Form Group" orientation="horizontal" flex-behavior="grow" style="--gap: var(--oc-base-dimension-8)"
><oc-text-field-v1>Text Field 1</oc-text-field-v1><oc-text-field-v1>Text Field 2</oc-text-field-v1></oc-form-group-v1
>${css}
Configuration
The form group component is configurable, allowing you to tailor its features and appearance to your specific needs. To explore all the available options and adjust the component, use the component configurator and see the changes affect the component in real-time.
Usage guidelines
Before integrating the form group component into your project, make sure you have correctly installed the OTTO components package. Look through the variations page for examples of possible component variations. Here, you can discover both common and specific variations that address different use cases.
Info
See the Form group UX documentation for detailed user experience guidelines.
Accessibility
The form group component comes with a set of built-in accessibility features to ensure a seamless experience for all users.
Use oc-aria-label
To make the form group recognizable for screen readers, use the oc-aria-label attribute to provide clear and descriptive context information.
See the general accessibility documentation for guidance on using oc-aria-label, including how it works with link and masked link behavior.
Aria Description
The content of the form group can have additional information by using the hint attribute.
This helps users understand the purpose of the form group.
Validation
The form group can have a validation state by using the validation-message attribute.
The validation messages should be clear and concise.
They are automatically associated with the form elements and set the validation state of those elements.
Configuration (v1)
Source: ./src/components/form-group/v1/Configuration.mdx
Form group configuration
Configure the Form group component with the controls below and see the changes live in the preview canvas. Click the Show code button within the preview canvas to see the source code for the current component configuration.
Story Default:
<oc-form-group-v1 class="${className}" oc-aria-label="Form Group" orientation="horizontal" flex-behavior="grow" style="--gap: var(--oc-base-dimension-8)"
><oc-text-field-v1>Text Field 1</oc-text-field-v1><oc-text-field-v1>Text Field 2</oc-text-field-v1></oc-form-group-v1
>${css}
Interactive configurator (Storybook controls); every option is listed in the API section of this file.
API v1
Source: ./src/components/form-group/v1/FormGroupV1.API.g.mdx
Form Group v1 API
API: <oc-form-group-v1> (FormGroupV1)
The Form Group component groups multiple form elements in a container and provides a label it. It supports flexible layout options, validation messages, hint messages, and ARIA labels for accessibility.
Attributes / properties
| Attribute | Type | Default | Required | Description |
|---|---|---|---|---|
orientation |
"horizontal" | "vertical" |
undefined |
no | Defines the orientation of the form group, determining the layout of its child elements. |
hint |
string |
undefined |
no | Sets and displays a hint message for the form group, providing details for all child elements. |
validation-message |
string |
undefined |
no | Sets and displays an error message for the form group, marking all child elements as invalid. |
flex-behavior |
"shrink" | "grow" |
undefined |
no | Determines whether the form group should shrink or grow its child elements. If set to grow, the child elements will expand to fill the available space.If set to shrink, the child elements will reduce in size to fit.This setting works best with a horizontal orientation. |
gap |
string |
undefined |
no | Sets the gap between child elements in the form group. Also, can be set by using the --gap CSS variable. |
oc-aria-label |
string |
no | Sets the ARIA label of the form group. This label is used by screen readers and should describe the overall content or purpose of the form group. |
Slots
| Slot | Required | Description |
|---|---|---|
default |
yes | Sets the content of the form group. Example:<oc-text-field-v1>Text Field 1</oc-text-field-v1> <oc-text-field-v1>Text Field 2</oc-text-field-v1> |
Events
| Event | Detail type | Description |
|---|---|---|
oc-property-change |
OcFormGroupV1Events["oc-property-change"] |
Whenever a property value changes, this event triggers. Use this event to track all property changes within the component. Refer to the Events documentation for more information. |
oc-mount |
{ component: string; } |
Fired when the component is mounted to the DOM. The event is fired when the onMount hook of the component is called by the runtime. |
oc-unmount |
{ component: string; } |
Fired when the component is unmounted from the DOM. The event is fired when the function returned by the onMount hook of the component is called by the runtime. |
CSS custom properties
| Custom property | Default | Description |
|---|---|---|
--gap |
Sets the gap between child elements in the form group. | |
--column-gap |
Sets the gap between columns in the form group. | |
--row-gap |
Sets the gap between rows in the form group. |
Variations (v1)
Source: ./src/components/form-group/v1/Variations.mdx
Variations
Listed below are the most common variations of the form group component as well as specific component variations for different use cases.
You can explore all available options using the component configurator, adjust the component to your needs, and see the changes live in a preview canvas.
Default
Story: components-form-group-variations--default · tags: components
The default configuration of the form group uses two text field components.
Args: --gap=var(--oc-base-dimension-8)
<oc-form-group-v1 class="${className}" oc-aria-label="Form Group" orientation="horizontal" flex-behavior="grow" style="--gap: var(--oc-base-dimension-8)"
><oc-text-field-v1>Text Field 1</oc-text-field-v1><oc-text-field-v1>Text Field 2</oc-text-field-v1></oc-form-group-v1
>${css}
Story source (TypeScript, verbatim from Storybook)
{
name: "Default",
args: {
"--gap": "var(--oc-base-dimension-8)"
},
render(args) {
const {
defaultSlot,
...props
} = args;
const {
css,
className
} = cssVariablesExample(args);
return html`<oc-form-group-v1 class="${className}" ${spread(props)}
>${unsafeHTML(defaultSlot)}</oc-form-group-v1
>${css}`;
}
}
With hint
Story: components-form-group-variations--hint · tags: components
Variation with the hint attribute set, providing additional information about the form group.
Args: hint=Example hint., --gap=var(--oc-base-dimension-8)
<oc-form-group-v1 oc-aria-label="Form Group" orientation="horizontal" flex-behavior="grow" hint="Example hint." style="--gap: var(--oc-base-dimension-8)">
<oc-text-field-v1>Text Field 1</oc-text-field-v1><oc-text-field-v1>Text Field 2</oc-text-field-v1>
</oc-form-group-v1>
Story source (TypeScript, verbatim from Storybook)
{
name: "With hint",
args: {
hint: "Example hint.",
"--gap": "var(--oc-base-dimension-8)"
},
render: Default.render
}
With validation message
Story: components-form-group-variations--validation · tags: components
Variation with the validation-message attribute set, providing form validation feedback to the user.
Args: validation-message=Invalid input., --gap=var(--oc-base-dimension-8)
<oc-form-group-v1 oc-aria-label="Form Group" orientation="horizontal" flex-behavior="grow" validation-message="Invalid input." style="--gap: var(--oc-base-dimension-8)">
<oc-text-field-v1>Text Field 1</oc-text-field-v1><oc-text-field-v1>Text Field 2</oc-text-field-v1>
</oc-form-group-v1>
Story source (TypeScript, verbatim from Storybook)
{
name: "With validation message",
args: {
"validation-message": "Invalid input.",
"--gap": "var(--oc-base-dimension-8)"
},
render: Default.render
}
Without layout
Story: components-form-group-variations--without-layout · tags: components
Variation with undefined orientation and flex-behavior attributes, showing the form group without layout.
Args: orientation=null, flex-behavior=null, --gap=var(--oc-base-dimension-8)
<oc-form-group-v1 oc-aria-label="Form Group" style="--gap: var(--oc-base-dimension-8)">
<oc-text-field-v1>Text Field 1</oc-text-field-v1><oc-text-field-v1>Text Field 2</oc-text-field-v1>
</oc-form-group-v1>
Story source (TypeScript, verbatim from Storybook)
{
name: "Without layout",
args: {
orientation: undefined,
"flex-behavior": undefined,
"--gap": "var(--oc-base-dimension-8)"
},
render: Default.render
}
Demo: Text Field
Story: components-form-group-variations--demo-text-field · tags: components
A demo showcasing how to group multiple text fields in a form group with horizontal orientation and grow flex-behavior.
Args: defaultSlot=(see snippet), hint=Example hint., oc-aria-label=Text Field, --gap=var(--oc-base-dimension-8)
<oc-form-group-v1 oc-aria-label="Text Field" orientation="horizontal" flex-behavior="grow" hint="Example hint." style="--gap: var(--oc-base-dimension-8)">
<oc-text-field-v1 style="flex: 8 0 auto">Straße</oc-text-field-v1>
<oc-text-field-v1 style="flex: 1 0 4rem">Hausnummer</oc-text-field-v1>
</oc-form-group-v1>
Demo: Text Area
Story: components-form-group-variations--demo-text-area · tags: components
A demo showcasing how to group multiple text areas in a form group with horizontal orientation and grow flex-behavior.
Args: defaultSlot=<oc-text-area-v1>Content 1</oc-text-area-v1> <oc-text-area-v1>Content 2</oc-text-area-v1>, hint=Example hint., oc-aria-label=Text Area, --gap=var(--oc-base-dimension-8)
<oc-form-group-v1 oc-aria-label="Text Area" orientation="horizontal" flex-behavior="grow" hint="Example hint." style="--gap: var(--oc-base-dimension-8)">
<oc-text-area-v1>Content 1</oc-text-area-v1>
<oc-text-area-v1>Content 2</oc-text-area-v1>
</oc-form-group-v1>
Demo: Checkbox
Story: components-form-group-variations--demo-checkbox · tags: components
A demo showcasing how to group multiple checkboxes in a form group with horizontal orientation and shrink flex-behavior.
Args: defaultSlot=(see snippet), flex-behavior=shrink, hint=Example hint., oc-aria-label=Checkbox
<oc-form-group-v1 oc-aria-label="Checkbox" orientation="horizontal" flex-behavior="shrink" hint="Example hint.">
<oc-checkbox-v1 name="options" value="option1">Option 1</oc-checkbox-v1>
<oc-checkbox-v1 name="options" value="option2">Option 2</oc-checkbox-v1>
<oc-checkbox-v1 name="options" value="option3">Option 3</oc-checkbox-v1>
<oc-checkbox-v1 name="options" value="option4">Option 4</oc-checkbox-v1>
<oc-checkbox-v1 name="options" value="option5">Option 5</oc-checkbox-v1>
<oc-checkbox-v2>
<input type="checkbox" name="options" value="option6" aria-label="oc-auto"/>
<label slot="label">Option 6</label>
</oc-checkbox-v2>
<oc-checkbox-v2>
<input type="checkbox" name="options" value="option7" aria-label="oc-auto"/>
<label slot="label">Option 7</label>
</oc-checkbox-v2>
<oc-checkbox-v2>
<input type="checkbox" name="options" value="option8" aria-label="oc-auto"/>
<label slot="label">Option 8</label>
</oc-checkbox-v2>
<oc-checkbox-v2>
<input type="checkbox" name="options" value="option9" aria-label="oc-auto"/>
<label slot="label">Option 9</label>
</oc-checkbox-v2>
<oc-checkbox-v2>
<input type="checkbox" name="options" value="option10" aria-label="oc-auto"/>
<label slot="label">Option 10</label>
</oc-checkbox-v2>
</oc-form-group-v1>
Demo: Radio Button
Story: components-form-group-variations--demo-radio-button · tags: components
A demo showcasing how to group multiple radio buttons in a form group with horizontal orientation and grow flex-behavior.
Args: defaultSlot=(see snippet), flex-behavior=grow, hint=Example hint., oc-aria-label=Radio Button
<oc-form-group-v1 oc-aria-label="Radio Button" orientation="horizontal" flex-behavior="grow" hint="Example hint.">
<oc-radio-button-v1 name="options" value="option1">Option 1</oc-radio-button-v1>
<oc-radio-button-v1 name="options" value="option2">Option 2</oc-radio-button-v1>
<oc-radio-button-v1 name="options" value="option3">Option 3</oc-radio-button-v1>
<oc-radio-button-v1 name="options" value="option4">Option 4</oc-radio-button-v1>
<oc-radio-button-v1 name="options" value="option5">Option 5</oc-radio-button-v1>
<oc-radio-button-v1 name="options" value="option6">Option 6</oc-radio-button-v1>
<oc-radio-button-v2>
<input type="radio" name="options" value="option6" aria-label="oc-auto"/>
<label slot="label">Option 6</label>
</oc-radio-button-v2>
<oc-radio-button-v2>
<input type="radio" name="options" value="option7" aria-label="oc-auto"/>
<label slot="label">Option 7</label>
</oc-radio-button-v2>
<oc-radio-button-v2>
<input type="radio" name="options" value="option8" aria-label="oc-auto"/>
<label slot="label">Option 8</label>
</oc-radio-button-v2>
<oc-radio-button-v2>
<input type="radio" name="options" value="option9" aria-label="oc-auto"/>
<label slot="label">Option 9</label>
</oc-radio-button-v2>
<oc-radio-button-v2>
<input type="radio" name="options" value="option10" aria-label="oc-auto"/>
<label slot="label">Option 10</label>
</oc-radio-button-v2>
</oc-form-group-v1>
Demo: Dropdown
Story: components-form-group-variations--demo-dropdown · tags: components
A demo showcasing how to group multiple dropdowns in a form group with horizontal orientation and grow flex-behavior.
Args: defaultSlot=(see snippet), hint=Example hint., oc-aria-label=Dropdown, --gap=var(--oc-base-dimension-8)
<oc-form-group-v1 oc-aria-label="Dropdown" orientation="horizontal" flex-behavior="grow" hint="Example hint." style="--gap: var(--oc-base-dimension-8)">
<oc-dropdown-v1 label='Options 1'><option>Option 1</option><option>Option 2</option></oc-dropdown-v1>
<oc-dropdown-v1 label='Options 2'><option>Option 1</option><option>Option 2</option></oc-dropdown-v1>
</oc-form-group-v1>
Demo: Form
Story: components-form-group-variations--form-demo · tags: components
A demo form showcasing all form elements with different hints.
<form novalidate>
<oc-text-field-v1 class="oc-mt-150" hint="Example hint." name="field"
>text field
</oc-text-field-v1>
<oc-text-area-v1 class="oc-mt-150" hint="Example hint." name="area"
>text area</oc-text-area-v1
>
<oc-dropdown-v1
class="oc-mt-150"
hint="Example hint."
label="Options"
name="dropdown"
options='[{"label":"Option 1","value":"1"},{"label":"Option 2","value":"2"},{"label":"Option 3","value":"3"}]'
>dropdown
</oc-dropdown-v1>
<oc-form-group-v1
class="demo-class oc-mt-150"
hint="Text fields hint."
orientation="horizontal"
flex-behavior="grow"
>
<oc-text-field-v1 name="field1">text field 1</oc-text-field-v1>
<oc-text-field-v1 name="field2">text field 2</oc-text-field-v1>
</oc-form-group-v1>
<oc-form-group-v1
class="demo-class oc-mt-150"
hint="Text areas hint."
orientation="horizontal"
flex-behavior="grow"
>
<oc-text-area-v1 name="area1">text area 1</oc-text-area-v1>
<oc-text-area-v1 name="area2">text area 2</oc-text-area-v1>
</oc-form-group-v1>
<oc-form-group-v1
class="demo-class oc-mt-150"
hint="Dropdowns hint."
orientation="horizontal"
flex-behavior="grow"
>
<oc-dropdown-v1
label="Options"
name="dropdown1"
options='[{"label":"Option 1","value":"1"},{"label":"Option 2","value":"2"},{"label":"Option 3","value":"3"}]'
>dropdown 1
</oc-dropdown-v1>
<oc-dropdown-v1
label="Options"
name="dropdown2"
options='[{"label":"Option 1","value":"1"},{"label":"Option 2","value":"2"},{"label":"Option 3","value":"3"}]'
>dropdown 2
</oc-dropdown-v1>
</oc-form-group-v1>
<oc-form-group-v1
class="oc-mt-150"
hint="Checkboxes hint."
orientation="horizontal"
flex-behavior="shrink"
>
<oc-checkbox-v1 name="checkbox" value="checked1">checkbox 1</oc-checkbox-v1>
<oc-checkbox-v1 name="checkbox" value="checked2">checkbox 2</oc-checkbox-v1>
</oc-form-group-v1>
<oc-form-group-v1
class="oc-mt-150"
hint="Radio buttons hint."
orientation="horizontal"
flex-behavior="shrink"
>
<oc-radio-button-v1 name="radio" value="checked1">radio button 1</oc-radio-button-v1>
<oc-radio-button-v1 name="radio" value="checked2">radio button 2</oc-radio-button-v1>
</oc-form-group-v1>
<div class="oc-mt-150">
<oc-button-v1 type="reset" fit-content variant="secondary">Reset</oc-button-v1>
<oc-button-v1 fit-content id="mark-invalid">Mark Invalid</oc-button-v1>
<oc-button-v1 type="submit" fit-content>Submit</oc-button-v1>
</div>
</form>
<style>
.demo-class {
--gap: var(--oc-base-dimension-8);
}
</style>
<script>
(() => {
const [textField] = document.getElementsByTagName("oc-text-field-v1");
const [textArea] = document.getElementsByTagName("oc-text-area-v1");
const [dropdown] = document.getElementsByTagName("oc-dropdown-v1");
const formGroups = document.getElementsByTagName("oc-form-group-v1");
const [form] = document.getElementsByTagName("form");
const [reset, markInvalid] = document.getElementsByTagName("oc-button-v1");
markInvalid.addEventListener("click", () => {
textField.validationMessage = "This field is invalid";
textArea.validationMessage = "This field is invalid";
dropdown.validationMessage = "This field is invalid";
for (const formGroup of formGroups) {
formGroup.validationMessage = "These fields are invalid";
}
});
reset.addEventListener("click", () => {
textField.validationMessage = undefined;
textArea.validationMessage = undefined;
dropdown.validationMessage = undefined;
for (const formGroup of formGroups) {
formGroup.validationMessage = undefined;
}
});
form.addEventListener("submit", (ev) => {
let formData = new FormData(form);
let data = "";
formData.forEach((value, key) => (data += key + "=" + value + "\\n"));
console.log(data);
alert("sending:\\n" + data);
ev.preventDefault();
});
})();
</script>
Story source (TypeScript, verbatim from Storybook)
{
parameters: {
controls: {
disabled: true
}
},
name: "Demo: Form",
render() {
return html`
<form novalidate>
<oc-text-field-v1 class="oc-mt-150" hint="Example hint." name="field"
>text field
</oc-text-field-v1>
<oc-text-area-v1 class="oc-mt-150" hint="Example hint." name="area"
>text area</oc-text-area-v1
>
<oc-dropdown-v1
class="oc-mt-150"
hint="Example hint."
label="Options"
name="dropdown"
options='[{"label":"Option 1","value":"1"},{"label":"Option 2","value":"2"},{"label":"Option 3","value":"3"}]'
>dropdown
</oc-dropdown-v1>
<oc-form-group-v1
class="demo-class oc-mt-150"
hint="Text fields hint."
orientation="horizontal"
flex-behavior="grow"
>
<oc-text-field-v1 name="field1">text field 1</oc-text-field-v1>
<oc-text-field-v1 name="field2">text field 2</oc-text-field-v1>
</oc-form-group-v1>
<oc-form-group-v1
class="demo-class oc-mt-150"
hint="Text areas hint."
orientation="horizontal"
flex-behavior="grow"
>
<oc-text-area-v1 name="area1">text area 1</oc-text-area-v1>
<oc-text-area-v1 name="area2">text area 2</oc-text-area-v1>
</oc-form-group-v1>
<oc-form-group-v1
class="demo-class oc-mt-150"
hint="Dropdowns hint."
orientation="horizontal"
flex-behavior="grow"
>
<oc-dropdown-v1
label="Options"
name="dropdown1"
options='[{"label":"Option 1","value":"1"},{"label":"Option 2","value":"2"},{"label":"Option 3","value":"3"}]'
>dropdown 1
</oc-dropdown-v1>
<oc-dropdown-v1
label="Options"
name="dropdown2"
options='[{"label":"Option 1","value":"1"},{"label":"Option 2","value":"2"},{"label":"Option 3","value":"3"}]'
>dropdown 2
</oc-dropdown-v1>
</oc-form-group-v1>
<oc-form-group-v1
class="oc-mt-150"
hint="Checkboxes hint."
orientation="horizontal"
flex-behavior="shrink"
>
<oc-checkbox-v1 name="checkbox" value="checked1">checkbox 1</oc-checkbox-v1>
<oc-checkbox-v1 name="checkbox" value="checked2">checkbox 2</oc-checkbox-v1>
</oc-form-group-v1>
<oc-form-group-v1
class="oc-mt-150"
hint="Radio buttons hint."
orientation="horizontal"
flex-behavior="shrink"
>
<oc-radio-button-v1 name="radio" value="checked1">radio button 1</oc-radio-button-v1>
<oc-radio-button-v1 name="radio" value="checked2">radio button 2</oc-radio-button-v1>
</oc-form-group-v1>
<div class="oc-mt-150">
<oc-button-v1 type="reset" fit-content variant="secondary">Reset</oc-button-v1>
<oc-button-v1 fit-content id="mark-invalid">Mark Invalid</oc-button-v1>
<oc-button-v1 type="submit" fit-content>Submit</oc-button-v1>
</div>
</form>
<style>
.demo-class {
--gap: var(--oc-base-dimension-8);
}
</style>
<script>
(() => {
const [textField] = document.getElementsByTagName("oc-text-field-v1");
const [textArea] = document.getElementsByTagName("oc-text-area-v1");
const [dropdown] = document.getElementsByTagName("oc-dropdown-v1");
const formGroups = document.getElementsByTagName("oc-form-group-v1");
const [form] = document.getElementsByTagName("form");
const [reset, markInvalid] = document.getElementsByTagName("oc-button-v1");
markInvalid.addEventListener("click", () => {
textField.validationMessage = "This field is invalid";
textArea.validationMessage = "This field is invalid";
dropdown.validationMessage = "This field is invalid";
for (const formGroup of formGroups) {
formGroup.validationMessage = "These fields are invalid";
}
});
reset.addEventListener("click", () => {
textField.validationMessage = undefined;
textArea.validationMessage = undefined;
dropdown.validationMessage = undefined;
for (const formGroup of formGroups) {
formGroup.validationMessage = undefined;
}
});
form.addEventListener("submit", (ev) => {
let formData = new FormData(form);
let data = "";
formData.forEach((value, key) => (data += key + "=" + value + "\\n"));
console.log(data);
alert("sending:\\n" + data);
ev.preventDefault();
});
})();
</script>
`;
}
}
Interaction tests (FormGroupV1.interactions.stories.ts)
Interaction test stories (automated tests, not usage patterns).
Should Pass Details To Children
Story: components-form-group-interaction-tests--should-pass-details-to-children · tags: play-fn
<oc-form-group-v1
class="oc-mt-150"
hint="my hint"
orientation="horizontal"
flex-behavior="grow"
>
<oc-text-field-v1 name="field1">text field 1</oc-text-field-v1>
<oc-text-field-v1 name="field2">text field 2</oc-text-field-v1>
</oc-form-group-v1>
<oc-form-group-v1
class="oc-mt-150"
hint="my hint"
orientation="horizontal"
flex-behavior="grow"
>
<oc-text-area-v1 name="area1">text area 1</oc-text-area-v1>
<oc-text-area-v1 name="area2">text area 2</oc-text-area-v1>
</oc-form-group-v1>
<oc-form-group-v1
class="oc-mt-150"
hint="my hint"
orientation="horizontal"
flex-behavior="grow"
>
<oc-dropdown-v1
label="Options"
name="dropdown1"
options='[{"label":"Option 1","value":"1"},{"label":"Option 2","value":"2"},{"label":"Option 3","value":"3"}]'
>dropdown 1
</oc-dropdown-v1>
<oc-dropdown-v1
label="Options"
name="dropdown2"
options='[{"label":"Option 1","value":"1"},{"label":"Option 2","value":"2"},{"label":"Option 3","value":"3"}]'
>dropdown 2
</oc-dropdown-v1>
</oc-form-group-v1>
<oc-form-group-v1
class="oc-mt-150"
hint="my hint"
orientation="horizontal"
flex-behavior="shrink"
>
<oc-checkbox-v1 name="checkbox" value="checked1">checkbox 1</oc-checkbox-v1>
<oc-checkbox-v1 name="checkbox" value="checked2">checkbox 2</oc-checkbox-v1>
</oc-form-group-v1>
<oc-form-group-v1
class="oc-mt-150"
hint="my hint"
orientation="horizontal"
flex-behavior="shrink"
>
<oc-radio-button-v1 name="radio" value="checked1">radio button 1</oc-radio-button-v1>
<oc-radio-button-v1 name="radio" value="checked2">radio button 2</oc-radio-button-v1>
</oc-form-group-v1>
Story source (TypeScript, verbatim from Storybook)
{
render: () => {
return html`
<oc-form-group-v1
class="oc-mt-150"
hint="my hint"
orientation="horizontal"
flex-behavior="grow"
>
<oc-text-field-v1 name="field1">text field 1</oc-text-field-v1>
<oc-text-field-v1 name="field2">text field 2</oc-text-field-v1>
</oc-form-group-v1>
<oc-form-group-v1
class="oc-mt-150"
hint="my hint"
orientation="horizontal"
flex-behavior="grow"
>
<oc-text-area-v1 name="area1">text area 1</oc-text-area-v1>
<oc-text-area-v1 name="area2">text area 2</oc-text-area-v1>
</oc-form-group-v1>
<oc-form-group-v1
class="oc-mt-150"
hint="my hint"
orientation="horizontal"
flex-behavior="grow"
>
<oc-dropdown-v1
label="Options"
name="dropdown1"
options='[{"label":"Option 1","value":"1"},{"label":"Option 2","value":"2"},{"label":"Option 3","value":"3"}]'
>dropdown 1
</oc-dropdown-v1>
<oc-dropdown-v1
label="Options"
name="dropdown2"
options='[{"label":"Option 1","value":"1"},{"label":"Option 2","value":"2"},{"label":"Option 3","value":"3"}]'
>dropdown 2
</oc-dropdown-v1>
</oc-form-group-v1>
<oc-form-group-v1
class="oc-mt-150"
hint="my hint"
orientation="horizontal"
flex-behavior="shrink"
>
<oc-checkbox-v1 name="checkbox" value="checked1">checkbox 1</oc-checkbox-v1>
<oc-checkbox-v1 name="checkbox" value="checked2">checkbox 2</oc-checkbox-v1>
</oc-form-group-v1>
<oc-form-group-v1
class="oc-mt-150"
hint="my hint"
orientation="horizontal"
flex-behavior="shrink"
>
<oc-radio-button-v1 name="radio" value="checked1">radio button 1</oc-radio-button-v1>
<oc-radio-button-v1 name="radio" value="checked2">radio button 2</oc-radio-button-v1>
</oc-form-group-v1>
`;
},
play: async ({
canvasElement
}) => {
const textFields = Array.from(canvasElement.getElementsByTagName("oc-text-field-v1"));
const textAreas = Array.from(canvasElement.getElementsByTagName("oc-text-area-v1"));
const dropdowns = Array.from(canvasElement.getElementsByTagName("oc-dropdown-v1"));
const checkboxes = Array.from(canvasElement.getElementsByTagName("oc-checkbox-v1"));
const radioButtons = Array.from(canvasElement.getElementsByTagName("oc-radio-button-v1"));
await waitFor(() => [...textFields, ...textAreas, ...dropdowns].flatMap(element => [
// @ts-expect-error -- internally defined property
expect(element.hideDetails, "should not show details").toBeTruthy(), expect(element.hint, "should have a hint").toBe("my hint")]));
await waitFor(() => [...textFields, ...textAreas, ...dropdowns, ...checkboxes, ...radioButtons].map(element =>
// @ts-expect-error -- internally defined property
expect(element.validationMessage, "should not have validation message").toBeUndefined()));
Array.from(canvasElement.getElementsByTagName("oc-form-group-v1")).forEach(formGroup => {
formGroup.setAttribute("validation-message", "This is a validation message");
formGroup.setAttribute("hint", "This is a hint");
});
await tick();
await waitFor(() => [...textFields, ...textAreas, ...dropdowns].flatMap(element => [
// @ts-expect-error -- internally defined property
expect(element.hideDetails, "still should not show details").toBeTruthy(), expect(element.hint, "should have new hint").toBe("This is a hint")]));
await waitFor(() => [...textFields, ...textAreas, ...dropdowns, ...checkboxes, ...radioButtons].map(element =>
// @ts-expect-error -- internally defined property
expect(element.validationMessage, "should have correct validation message").toBe("This is a validation message")));
}
}
Should Handle Async Data
Story: components-form-group-interaction-tests--should-handle-async-data · tags: play-fn
<oc-form-group-v1 orientation="horizontal" flex-behavior="grow">
<oc-text-field-v1 name="field1">text field 1</oc-text-field-v1>
</oc-form-group-v1>
Story source (TypeScript, verbatim from Storybook)
{
render: () => {
return html`
<oc-form-group-v1 orientation="horizontal" flex-behavior="grow">
<oc-text-field-v1 name="field1">text field 1</oc-text-field-v1>
</oc-form-group-v1>
`;
},
play: async ({
canvasElement
}) => {
const formGroup = canvasElement.getElementsByTagName("oc-form-group-v1")[0];
const textField1 = canvasElement.getElementsByTagName("oc-text-field-v1")[0];
formGroup.hint = "This is a hint";
await tick();
await waitFor(() => [
// @ts-expect-error -- internally defined property
expect(textField1.hideDetails, "textField1.hideDetail").toBeTruthy(), expect(textField1.validationMessage, "textField1.validationMessage").toBeUndefined(), expect(textField1.hint, "textField1.hint").toBe("This is a hint")]);
await tick();
const textField2 = document.createElement("oc-text-field-v1");
textField2.innerText = "text field 2";
formGroup.appendChild(textField2);
await waitFor(() => [
// @ts-expect-error -- internally defined property
expect(textField2.hideDetails, "textField2.hideDetails").toBeTruthy(), expect(textField2.validationMessage, "textField2.validationMessage").toBeUndefined(), expect(textField2.hint, "textField2.hint").toBe("This is a hint")]);
formGroup.validationMessage = "This is a validation message";
await tick();
const textField3 = document.createElement("oc-text-field-v1");
textField3.innerText = "text field 3";
formGroup.appendChild(textField3);
await waitFor(() => [
// @ts-expect-error -- internally defined property
expect(textField1.hideDetails, "textField1.hideDetails").toBeTruthy(), expect(textField1.validationMessage, "textField1.validationMessage").toBe("This is a validation message"), expect(textField1.hint, "textField1.hint").toBe("This is a hint"),
// @ts-expect-error -- internally defined property
expect(textField2.hideDetails, "textField2.hideDetails").toBeTruthy(), expect(textField2.validationMessage, "textField2.validationMessage").toBe("This is a validation message"), expect(textField2.hint, "textField2.hint").toBe("This is a hint"),
// @ts-expect-error -- internally defined property
expect(textField3.hideDetails, "textField3.hideDetails").toBeTruthy(), expect(textField3.validationMessage, "textField3.validationMessage").toBe("This is a validation message"), expect(textField3.hint, "textField3.hint").toBe("This is a hint")]);
}
}