Skip to content

Commit

Permalink
docs: add form example to Storybook (#1673)
Browse files Browse the repository at this point in the history
relates to #1497 

Add form example with several fields to Storybook to showcase the
validation and recommended implementation for forms.
  • Loading branch information
larsrickert authored Jul 31, 2024
1 parent 2da3990 commit c85320b
Show file tree
Hide file tree
Showing 7 changed files with 184 additions and 3 deletions.
4 changes: 4 additions & 0 deletions packages/sit-onyx/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,9 @@ module.exports = {
files: ["src/**/*.spec.ts"],
rules: { "playwright/no-standalone-expect": "off" },
},
{
files: ["src/components/examples/**/*.vue"],
rules: { "vue-scoped-css/enforce-style-type": "off" },
},
],
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { defineStorybookActionsAndVModels } from "@sit-onyx/storybook-utils";
import type { Meta, StoryObj } from "@storybook/vue3";
import OnyxToast from "../../OnyxToast/OnyxToast.vue";
import FormExample from "./FormExample.vue";
import FormExampleSourceCode from "./FormExample.vue?raw";

/**
* This example shows a form with several fields and various validations in combination with the [onyx grid](https://onyx.schwarz/development/grid.html) for responsive layout.
*
* When trying to submit when the form is invalid, the invalid fields will show corresponding error messages with [build-in translations](https://onyx.schwarz/development/i18n.html).
*/
const meta: Meta<typeof FormExample> = {
title: "Examples/Form",
...defineStorybookActionsAndVModels({
component: FormExample,
events: [],
decorators: [
(story) => ({
components: { story, OnyxToast },
template: `<OnyxToast /> <story />`,
}),
],
}),
parameters: {
docs: {
source: {
code: FormExampleSourceCode.replace('from "../../.."', 'from "sit-onyx"'),
},
},
},
};

export default meta;
type Story = StoryObj<typeof FormExample>;

export const Default = { args: {} } satisfies Story;
141 changes: 141 additions & 0 deletions packages/sit-onyx/src/components/examples/FormExample/FormExample.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
<script lang="ts" setup>
import { ref } from "vue";
import {
OnyxButton,
OnyxCheckboxGroup,
OnyxInput,
OnyxSelect,
OnyxStepper,
OnyxTextarea,
useToast,
type CheckboxGroupOption,
type SelectOption,
} from "../../..";
type LegalTerm = "general-terms" | "optional-terms";
type FormState = {
username: string;
email: string;
favoriteFruits?: string[];
age?: number;
description?: string;
terms?: LegalTerm[];
};
const toast = useToast();
const state = ref<Partial<FormState>>({});
const handleSubmit = () => {
// this function is only called if all form validations are correct so
// the type cast to `FormState` is considered safe
const formData = { ...state.value } as FormState;
toast.show({
headline: "Form submitted",
description: JSON.stringify(formData),
color: "success",
});
};
const fruitOptions = ref(
[
"Apple",
"Banana",
"Mango",
"Kiwi",
"Orange",
"Papaya",
"Apricot",
"Lemon",
"Cranberry",
"Avocado",
"Cherry",
"Coconut",
"Lychee",
"Melon",
"Raspberry",
"Strawberry",
].map<SelectOption>((option) => ({ value: option.toLowerCase(), label: option })),
);
const legalTerms: CheckboxGroupOption<LegalTerm>[] = [
{
label: "I agree to the terms and conditions",
value: "general-terms",
required: true,
},
{
label: "Some optional terms",
value: "optional-terms",
},
];
</script>

<template>
<div>
<form class="onyx-grid" @submit.prevent="handleSubmit" @reset="state = {}">
<OnyxInput
v-model="state.username"
class="onyx-grid-span-4"
label="Username"
autocomplete="username"
:minlength="3"
:maxlength="16"
with-counter
required
/>
<OnyxInput
v-model="state.email"
class="onyx-grid-span-4"
label="Email"
type="email"
autocomplete="email"
required
/>

<OnyxSelect
v-model="state.favoriteFruits"
class="onyx-grid-span-4"
label="Favorite fruits"
list-label="List of fruits"
multiple
with-search
required
:options="fruitOptions"
/>

<OnyxStepper v-model="state.age" class="onyx-grid-span-4" label="Age" :min="0" :max="100" />

<OnyxTextarea
v-model="state.description"
class="onyx-grid-span-16"
label="Description"
:maxlength="512"
with-counter
/>

<OnyxCheckboxGroup
v-model="state.terms"
class="onyx-grid-span-16"
headline="Legal terms"
:options="legalTerms"
/>

<div class="onyx-grid-span-16 actions">
<OnyxButton label="Reset" type="reset" color="neutral" />
<OnyxButton label="Submit" type="submit" />
</div>
</form>

<pre>Form state: {{ state }}</pre>
</div>
</template>

<style lang="scss" scoped>
.actions {
display: flex;
justify-content: flex-end;
gap: var(--onyx-grid-gutter);
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const gridElementClasses = computed(() =>
</button>
</template>

<style lang="scss">
<style lang="scss" scoped>
.onyx-grid-playground-element {
height: 100px;
border: 0.125rem dashed var(--onyx-color-base-primary-500);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ const gridConfig = computed(() => {
</div>
</template>

<style lang="scss">
<style lang="scss" scoped>
.onyx-grid-playground-indicator {
display: grid;
color: var(--onyx-color-text-icons-neutral-inverted);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ const handleAddModifier = () => {
</main>
</template>
<style lang="scss">
<style lang="scss" scoped>
.onyx-grid-playground {
color: var(--onyx-color-text-icons-neutral-intense);
Expand Down

0 comments on commit c85320b

Please sign in to comment.