Skip to content

Commit

Permalink
feat: form v2 and inputs v2 (#105)
Browse files Browse the repository at this point in the history
resolves #102
  • Loading branch information
joshuagraber authored Oct 8, 2024
1 parent a4491b0 commit c362b2e
Show file tree
Hide file tree
Showing 23 changed files with 851 additions and 6 deletions.
2 changes: 1 addition & 1 deletion commitlint.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export default {
extends: ['@commitlint/config-conventional'],
rules: {
'footer-max-length': [2, 'never']
'footer-max-length': [0]
}
};
3 changes: 3 additions & 0 deletions src/components/Form/PdapForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,9 @@ async function submit(e: Event) {
* The `Form` component is powerful. All you need to do is pass a few props, and the component will generate inputs and render them in the UI, complete with customizable form validation and both form-level and input-level error states.
*
*
* @deprecated use FormV2 with the PdapInputCheckbox, ...Text, and ...Password components instead
*
*
* ## Props
* @prop {string | undefined | null} error Error state. Only a non-falsy string results in a form-level error being displayed
* @prop {string} id Passed through to the `form` element as its `id`
Expand Down
99 changes: 99 additions & 0 deletions src/components/FormV2/PdapFormV2.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<template>
<form :id="id" :name="name" class="pdap-form" @submit.prevent="submit">
<slot v-if="$slots.error" name="error" />
<div
v-else-if="typeof errorMessage === 'string'"
class="pdap-form-error-message"
>
{{ errorMessage }}
</div>

<slot />
</form>
</template>
<script setup lang="ts">
// Globals
import { provide, ref, watchEffect } from 'vue';
import { useVuelidate } from '@vuelidate/core';
// Types
import { PdapFormPropsV2 } from './types';
import { provideKey, makeRules } from './util';
// Props
const { defaultValues, error, schema } = withDefaults(
defineProps<PdapFormPropsV2>(),
{
error: null,
}
);
// Emits
const emit = defineEmits(['submit']);
// Constants
const errorMessage = ref(error);
const values = ref(defaultValues ?? {});
const rules = makeRules(schema);
const v$ = useVuelidate(rules, values, { $autoDirty: false, $lazy: true });
// Provide
provide(provideKey, {
setValues,
values,
rules,
v$,
});
// Expose
defineExpose({
setValues,
});
// Helpers
function setValues(val: Partial<typeof values.value>) {
values.value = { ...values.value, ...val };
}
function resetForm() {
v$.value.$reset();
values.value = Object.entries(values.value).reduce((acc, [key]) => {
return {
...acc,
[key]:
typeof values.value[key] === 'string'
? ''
: Boolean(defaultValues?.[key]),
};
}, {});
}
async function submit(e: Event) {
// Check form submission
const isValidSubmission = await v$.value.$validate();
if (isValidSubmission) {
// Emit submit event (spread to new object to create new object, this allows us to reset `values` without messing with the data returned)
emit('submit', { ...values.value }, e);
resetForm();
}
}
// Effects
// Effect - Updates form error state based on input error state and/or props
watchEffect(() => {
if (error) errorMessage.value = error;
else if (errorMessage.value && v$.value.$errors.length === 0)
errorMessage.value = null;
else if (!errorMessage.value && v$.value.$errors.length > 0)
errorMessage.value = 'Please update this form to correct the errors';
});
</script>

<style>
@tailwind components;
@layer components {
.pdap-form-error-message {
@apply items-center justify-start basis-full flex-shrink flex bg-red-300 text-red-800 mb-3 p-2 text-sm;
}
}
</style>
139 changes: 139 additions & 0 deletions src/components/FormV2/__snapshots__/formv2.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`PdapFormV2 > calls submit event with form values on valid submission 1`] = `
<form class="pdap-form" id="test" name="test">
<!--v-if-->
<div class="pdap-input">
<!--v-if-->
<input id="name" name="name" placeholder="Name" type="text">
<label for="name">Name</label>
</div>
<div class="pdap-input">
<!--v-if-->
<input id="email" name="email" placeholder="Email" type="text">
<label for="email">Email</label>
</div>
<div class="pdap-input pdap-input-password">
<!--v-if-->
<div class="pdap-input-password-wrapper">
<input id="password" name="password" placeholder="Password" type="password">
<button aria-label="Show text" class="pdap-input-password-toggle" type="button">
<svg aria-hidden="true" class="svg-inline--fa fa-eye" data-icon="eye" data-prefix="fas" focusable="false" role="img" viewBox="0 0 576 512" xmlns="http://www.w3.org/2000/svg">
<path class="" d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM144 256a144 144 0 1 1 288 0 144 144 0 1 1 -288 0zm144-64c0 35.3-28.7 64-64 64c-7.1 0-13.9-1.2-20.3-3.3c-5.5-1.8-11.9 1.6-11.7 7.4c.3 6.9 1.3 13.8 3.2 20.7c13.7 51.2 66.4 81.6 117.6 67.9s81.6-66.4 67.9-117.6c-11.1-41.5-47.8-69.4-88.6-71.1c-5.8-.2-9.2 6.1-7.4 11.7c2.1 6.4 3.3 13.2 3.3 20.3z" fill="currentColor" />
</svg>
</button>
</div>
<label for="password">Password</label>
</div>
<div class="pdap-input pdap-input-checkbox">
<!--v-if-->
<input checked id="ice-cream" name="ice-cream" type="checkbox" value="true">
<label for="ice-cream">Ice Cream</label>
</div>
</form>
`;
exports[`PdapFormV2 > renders default error message when form has errors 1`] = `
<form class="pdap-form" id="test" name="test">
<div class="pdap-form-error-message">Please update this form to correct the errors</div>
<div class="pdap-input pdap-input-error">
<div class="pdap-input-error-message">Value is required</div>
<input id="name" name="name" placeholder="Name" type="text">
<label for="name">Name</label>
</div>
<div class="pdap-input pdap-input-error">
<div class="pdap-input-error-message">Value is required</div>
<input id="email" name="email" placeholder="Email" type="text">
<label for="email">Email</label>
</div>
<div class="pdap-input pdap-input-password">
<!--v-if-->
<div class="pdap-input-password-wrapper">
<input id="password" name="password" placeholder="Password" type="password">
<button aria-label="Show text" class="pdap-input-password-toggle" type="button">
<svg aria-hidden="true" class="svg-inline--fa fa-eye" data-icon="eye" data-prefix="fas" focusable="false" role="img" viewBox="0 0 576 512" xmlns="http://www.w3.org/2000/svg">
<path class="" d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM144 256a144 144 0 1 1 288 0 144 144 0 1 1 -288 0zm144-64c0 35.3-28.7 64-64 64c-7.1 0-13.9-1.2-20.3-3.3c-5.5-1.8-11.9 1.6-11.7 7.4c.3 6.9 1.3 13.8 3.2 20.7c13.7 51.2 66.4 81.6 117.6 67.9s81.6-66.4 67.9-117.6c-11.1-41.5-47.8-69.4-88.6-71.1c-5.8-.2-9.2 6.1-7.4 11.7c2.1 6.4 3.3 13.2 3.3 20.3z" fill="currentColor" />
</svg>
</button>
</div>
<label for="password">Password</label>
</div>
<div class="pdap-input pdap-input-checkbox">
<!--v-if-->
<input id="ice-cream" name="ice-cream" type="checkbox" value="false">
<label for="ice-cream">Ice Cream</label>
</div>
</form>
`;
exports[`PdapFormV2 > renders error message slot when provided 1`] = `
<form class="pdap-form" id="test" name="test">
<div>Custom Error Message</div>
</form>
`;
exports[`PdapFormV2 > renders error message when errorMessage prop is provided 1`] = `
<form class="pdap-form" id="test" name="test">
<div class="pdap-form-error-message">Form Error</div>
<div class="pdap-input">
<!--v-if-->
<input id="name" name="name" placeholder="Name" type="text">
<label for="name">Name</label>
</div>
<div class="pdap-input">
<!--v-if-->
<input id="email" name="email" placeholder="Email" type="text">
<label for="email">Email</label>
</div>
<div class="pdap-input pdap-input-password">
<!--v-if-->
<div class="pdap-input-password-wrapper">
<input id="password" name="password" placeholder="Password" type="password">
<button aria-label="Show text" class="pdap-input-password-toggle" type="button">
<svg aria-hidden="true" class="svg-inline--fa fa-eye" data-icon="eye" data-prefix="fas" focusable="false" role="img" viewBox="0 0 576 512" xmlns="http://www.w3.org/2000/svg">
<path class="" d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM144 256a144 144 0 1 1 288 0 144 144 0 1 1 -288 0zm144-64c0 35.3-28.7 64-64 64c-7.1 0-13.9-1.2-20.3-3.3c-5.5-1.8-11.9 1.6-11.7 7.4c.3 6.9 1.3 13.8 3.2 20.7c13.7 51.2 66.4 81.6 117.6 67.9s81.6-66.4 67.9-117.6c-11.1-41.5-47.8-69.4-88.6-71.1c-5.8-.2-9.2 6.1-7.4 11.7c2.1 6.4 3.3 13.2 3.3 20.3z" fill="currentColor" />
</svg>
</button>
</div>
<label for="password">Password</label>
</div>
<div class="pdap-input pdap-input-checkbox">
<!--v-if-->
<input id="ice-cream" name="ice-cream" type="checkbox" value="false">
<label for="ice-cream">Ice Cream</label>
</div>
</form>
`;
exports[`PdapFormV2 > renders the form element 1`] = `
<form class="pdap-form" id="test" name="test">
<!--v-if-->
<div class="pdap-input">
<!--v-if-->
<input id="name" name="name" placeholder="Name" type="text">
<label for="name">Name</label>
</div>
<div class="pdap-input">
<!--v-if-->
<input id="email" name="email" placeholder="Email" type="text">
<label for="email">Email</label>
</div>
<div class="pdap-input pdap-input-password">
<!--v-if-->
<div class="pdap-input-password-wrapper">
<input id="password" name="password" placeholder="Password" type="password">
<button aria-label="Show text" class="pdap-input-password-toggle" type="button">
<svg aria-hidden="true" class="svg-inline--fa fa-eye" data-icon="eye" data-prefix="fas" focusable="false" role="img" viewBox="0 0 576 512" xmlns="http://www.w3.org/2000/svg">
<path class="" d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM144 256a144 144 0 1 1 288 0 144 144 0 1 1 -288 0zm144-64c0 35.3-28.7 64-64 64c-7.1 0-13.9-1.2-20.3-3.3c-5.5-1.8-11.9 1.6-11.7 7.4c.3 6.9 1.3 13.8 3.2 20.7c13.7 51.2 66.4 81.6 117.6 67.9s81.6-66.4 67.9-117.6c-11.1-41.5-47.8-69.4-88.6-71.1c-5.8-.2-9.2 6.1-7.4 11.7c2.1 6.4 3.3 13.2 3.3 20.3z" fill="currentColor" />
</svg>
</button>
</div>
<label for="password">Password</label>
</div>
<div class="pdap-input pdap-input-checkbox">
<!--v-if-->
<input id="ice-cream" name="ice-cream" type="checkbox" value="false">
<label for="ice-cream">Ice Cream</label>
</div>
</form>
`;
Loading

0 comments on commit c362b2e

Please sign in to comment.