Skip to content

Commit

Permalink
feat(Forms): add new experimental standalone form components with dec…
Browse files Browse the repository at this point in the history
…oupled `vee-validate` integration (#176)

* feat(Forms): (experimental) add new standalone `Input` and `InputField` components

* chore(Experimental/Forms/Input): remove unnecessary `validationMode` prop & fix logic to show error message

* chore(Experimental/Forms/Input): add input entry point

* chore(deps): upgrade deps

* chore(deps): upgrade deps

* docs: update logo

* chore(Experimental/Forms/Input): add input types story

* feat(Experimental/Forms/Input): add add new `FormField` component

* feat(Experimental/Forms/Input): support variety html input field

* feat(Experimental/Forms): add new `TextareaInput` and `TextareaInputField` components

* chore(Experimental/Forms): revalidate field on blur

* feat(Experimental/Forms): add new select components

* feat(Experimental/Forms): add new `Checkbox` components

* chore(Experimental/Forms): add checkbox entry point

* feat(Experimental/Forms): add new `Radio` components

* feat(Experimental/Switch): add new uncontrolled `Switch` component

* chore: fix missing imports

* fix(Select): fix placement and margin

* feat(Forms): add new `FileInput` components

* chore(Forms): add new entry point for `FileInput` components

* chore: fix types

* chore: export `SelectOptions`

* chore(Forms/FileInput): move some props to the base component

* chore(Forms/FileInput): fix error message not displayed

* chore(Forms/FileInput): add `button` variant story

* chore(Experiental/Forms/Input): fix `v-model` binding

* chore(Experiental/Forms): update entry point and add forms playground

* feat(Experiental/Forms/FileInput): add support for drag and drop files

* chore(Experiental/Forms): add missing `error` prop and fix `value` type

* chore(Experiental/Forms): fix import

* chore(Experiental/Forms/Select): fix model binding

* chore(Experiental/Forms/Input): fix exports

* chore(Experiental/Forms/Switch): fix model binding and fix output size

* chore(Experiental/Forms): fix output size

* chore(Experiental/Forms): add forms validation playground

* feat(Experiental/Forms/FileInput): add `error` state on `FileInputDefaultActivator`

* chore(Select): fix deps

* chore(Forms): fix exports

* chore: update lock file

* chore: fix external deps and types

* feat(Badge): use different `VBadgeContent` component to prevent compilation error

* docs: add standalone form components docs
  • Loading branch information
gravitano authored Jul 27, 2023
1 parent b27e52a commit 1a54522
Show file tree
Hide file tree
Showing 104 changed files with 4,701 additions and 306 deletions.
21 changes: 13 additions & 8 deletions .storybook/main.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// const { mergeConfig } = require('vite');
// const vuePlugin = require('@vitejs/plugin-vue')

module.exports = {
stories: [
'../packages/*/src/**/*.stories.@(js|jsx|ts|tsx|mdx)',
Expand All @@ -12,13 +15,15 @@ module.exports = {
storyStoreV7: true,
},
// viteFinal: (config) => {
// return {
// ...config,
// build: {
// ...config.build,
// sourcemap: false,
// // target: ['es2020'],
// },
// };
// return mergeConfig(config, {
// plugins: [
// vuePlugin({
// script: {
// defineModel: true,
// propsDestructure: true,
// },
// }),
// ]
// });
// },
};
6 changes: 5 additions & 1 deletion docs/.vitepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default defineConfig({
search: {
provider: 'local',
},
logo: 'https://gits.id/wp-content/uploads/2022/06/Logo-Main-1.png',
logo: '/morpheme-logo.png',
nav: [
{text: 'Guide', link: '/guide/getting-started'},
{text: 'Storybook', link: 'https://gits-ui.web.app'},
Expand Down Expand Up @@ -246,6 +246,10 @@ export default defineConfig({
text: 'TextArea',
link: '/components/textarea',
},
{
text: 'Standalone Components',
link: '/components/standalone-form-components',
},
],
},
{
Expand Down
264 changes: 264 additions & 0 deletions docs/components/standalone-form-components.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
# Standalone Form Components

## Introduction

The Standalone Form Components provide enhanced flexibility and compatibility with various form validation libraries. Starting from version `1.0.0-beta.11`, these components are decoupled from `vee-validate`, enabling seamless integration with different validation strategies and offering a more predictable behavior.

One of the key advantages of standalone form components is their compatibility with `v-model`, making data binding effortless and streamlined. Additionally, for users who still prefer using `vee-validate`, there are custom components available, marked by the `Field` suffix (e.g., `InputField`), which integrate with `vee-validate` and retain its capabilities.

By adopting the Standalone Form Components, developers can harness the full potential of the form components, allowing for a tailored form handling process that caters to diverse development preferences and validation libraries.

## Adoption Strategy

To ensure a smooth transition and avoid breaking changes, all new standalone form components will not replace the current components. Instead, you can use them by importing the required components from the @morpheme/forms package, except for the Switch component, which should be imported from the @morpheme/switch package.

## Standalone Form Component List

Here is a list of the available standalone form components:

- `Input`
- `InputField`
- `FormField`
- `Radio`
- `Checkbox`
- `Select`
- `SelectField`
- `SelectOptions`
- `FileInput`
- `FileInputField`
- `FileInputButtonActivator`
- `FileInputDefaultActivator`
- `FileInputItem`
- `FileInputItems`
- `Switch`
- `TextareaInput`
- `TextareaInputField`

## Basic Usage

The standalone form components are designed to work seamlessly with `v-model`, allowing easy data binding. Below is an example of basic usage:

```vue
<script setup lang="ts">
import {reactive} from 'vue';
import {Input, TextareaInput, Select, Radio, Checkbox, FileInput} from '@morpheme/forms';
import {Switch} from '@morpheme/switch';
import {VBtn} from '@morpheme/button';
const form = reactive({
name: '',
description: '',
category: '',
active: false,
radio: '',
checkboxSingle: false,
checkboxMultiple: [],
file: undefined,
avatar: undefined,
});
</script>
<template>
<form>
<Input v-model="form.name" label="Name" wrapper-class="mb-4" />
<TextareaInput
v-model="form.description"
label="Description"
wrapper-class="mb-4"
/>
<Select
v-model="form.category"
label="Category"
wrapper-class="mb-4"
:options="[
{
label: 'Category 1',
value: '1',
},
{
label: 'Category 2',
value: '2',
},
]"
/>
<Switch v-model="form.active" label="Active" wrapper-class="mb-4" />
<Radio id="radio1" v-model="form.radio" label="Radio 1" value="1" />
<Radio
id="radio2"
v-model="form.radio"
label="Radio 2"
value="2"
wrapper-class="mb-4"
/>
<Checkbox
id="checkbox-single"
v-model="form.checkboxSingle"
label="Checkbox 1"
value="check1"
wrapper-class="mb-4"
/>
<Checkbox
id="checkbox1"
v-model="form.checkboxMultiple"
label="Checkbox 1"
value="check1"
/>
<Checkbox
id="checkbox2"
v-model="form.checkboxMultiple"
label="Checkbox 2"
value="check2"
wrapper-class="mb-4"
/>
<FileInput v-model="form.file" label="File" />
<FileInput v-model="form.avatar" label="Avatar" variant="button" />
<div class="mt-4">
<VBtn color="primary">Submit</VBtn>
</div>
</form>
</template>
```

## Form validation with `vee-validate`

For users who prefer using `vee-validate`, the standalone form components offer custom versions indicated by the `Field` suffix (e.g., `InputField`). Below is an example of using `vee-validate`'s version of form components:

```vue
<script setup lang="ts">
import {
InputField,
TextareaInputField,
SelectField,
Radio,
Checkbox,
CheckboxField,
FileInputField,
} from '@morpheme/forms';
import VSwitch from '@morpheme/switch';
import {VBtn} from '@morpheme/button';
import {useField, useForm} from 'vee-validate';
import {array, boolean, object, string} from 'yup';
const {handleSubmit, values, errors, resetForm} = useForm({
validationSchema: object({
name: string().required(),
description: string().required(),
category: string().required(),
active: boolean().oneOf([true]).required(),
gender: string().required(),
'checkbox-single': boolean().oneOf([true]).required(),
hobbies: array().min(1).required(),
file: array().min(1).required(),
avatar: array().min(1).required(),
}),
});
const {value: gender} = useField<string>('gender', undefined, {
initialValue: '',
});
const {value: hobbies} = useField<any[]>('hobbies', undefined, {
initialValue: [],
});
const onSubmit = handleSubmit((values) => {
console.log(values);
});
</script>
<template>
<form @submit="onSubmit">
<InputField name="name" label="Name" wrapper-class="mb-4" />
<TextareaInputField
name="description"
label="Description"
wrapper-class="mb-4"
/>
<SelectField
name="category"
label="Category"
wrapper-class="mb-4"
:options="[
{
label: 'Category 1',
value: '1',
},
{
label: 'Category 2',
value: '2',
},
]"
/>
<VSwitch name="active" label="Active" wrapper-class="mb-4" />
<div class="mb-4">
<div class="v-input-label">Gender</div>
<Radio
v-model="gender"
id="radio1"
name="gender"
label="Male"
value="male"
/>
<Radio
v-model="gender"
id="radio2"
name="gender"
label="Female"
value="female"
/>
<div class="v-input-error">{{ errors.gender }}</div>
</div>
<div class="mb-4">
<CheckboxField
id="checkbox-single"
name="checkbox-single"
label="Checkbox 1"
/>
</div>
<div class="mb-4">
<div class="v-input-label">Hobby</div>
<Checkbox
v-model="hobbies"
id="checkbox1"
name="hobbies"
label="Coding"
value="coding"
:error="!!errors['hobbies']"
/>
<Checkbox
v-model="hobbies"
id="checkbox2"
name="hobbies"
label="Sport"
value="Sport"
:error="!!errors['hobbies']"
/>
<div class="v-input-error">{{ errors['hobbies'] }}</div>
</div>
<FileInputField name="file" label="File" wrapper-class="mb-4" />
<FileInputField name="avatar" label="Avatar" variant="button" />
<div class="mt-4">
<VBtn type="submit" color="primary">Submit</VBtn>
<VBtn @click="resetForm" class="ml-2">Reset</VBtn>
</div>
<div class="mt-4">
<pre>{{ {values, errors} }}</pre>
</div>
</form>
</template>
```

## Additional Examples

For more examples and usage scenarios, please check out the Storybook under the `Experimental` group.

By leveraging the Standalone Form Components and choosing the appropriate validation library, you can enhance your form handling process and create a better user experience tailored to your project's needs.
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ hero:
text: Get Started
link: /guide/getting-started
- theme: alt
text: View on Storybook
text: View Storybook
link: https://gits-ui.web.app
features:
- icon: ⛰️
Expand Down
Binary file added docs/public/morpheme-design-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/public/morpheme-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@
"docs:serve": "vitepress serve docs"
},
"dependencies": {
"@headlessui/vue": "^1.7.13",
"@vueuse/core": "^10.1.2",
"@headlessui/vue": "^1.7.14",
"@vueuse/core": "^10.2.1",
"floating-vue": "^2.0.0-beta.20",
"pagino": "^1.4.1",
"vee-validate": "^4.9.5",
"pagino": "^1.4.2",
"vee-validate": "^4.10.8",
"vue": "^3.3.4",
"vue-router": "^4.2.1",
"vue-router": "^4.2.4",
"vuex": "^4.1.0",
"yup": "^1.1.1"
"yup": "^1.2.0"
},
"devDependencies": {
"@commitlint/cli": "^17.0.3",
Expand Down
2 changes: 1 addition & 1 deletion packages/app-shell/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"@vue/test-utils": "^2.0.0-rc.17",
"c8": "^7.11.3",
"tailwindcss": "^3.3.1",
"vee-validate": "^4.5.9",
"vee-validate": "^4.10.8",
"vite": "^4.3.8",
"vitest": "^0.12.4"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/badge/src/VBadge.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {Story, Meta} from '@storybook/vue3';
import {defaultRounded, defaultColors} from '@morpheme/theme/defaultTheme';
import {List as VList, ListItem as VListItem} from '@morpheme/list'
import VBadgeGroup from './VBadgeGroup.vue';
import VBadgeContent from './VBadgeContent.vue';
import {VBadgeContent} from './badge-content';

const colors = [
...defaultColors,
Expand Down
8 changes: 8 additions & 0 deletions packages/badge/src/badge-content.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import {defineComponent, h} from 'vue';

export const VBadgeContent = defineComponent({
name: 'VBadgeContent',
setup(_props, {slots}) {
return () => h('div', {class: 'badge-content'}, slots.default?.());
},
});
3 changes: 2 additions & 1 deletion packages/badge/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export { default } from "./VBadge.vue";
export { default as VBadge } from "./VBadge.vue";
export { default as VBadgeGroup } from "./VBadgeGroup.vue";
export { default as VBadgeContent } from "./VBadgeContent.vue";
// export { default as VBadgeContent } from "./VBadgeContent.vue";
export * from "./api";
export * from "./types";
export * from "./badge-content";
2 changes: 1 addition & 1 deletion packages/banner/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"@vue/test-utils": "^2.0.0-rc.17",
"c8": "^7.11.3",
"tailwindcss": "^3.3.1",
"vee-validate": "^4.5.9",
"vee-validate": "^4.10.8",
"vite": "^4.3.8",
"vitest": "^0.12.4"
},
Expand Down
Loading

0 comments on commit 1a54522

Please sign in to comment.