Skip to content

Commit

Permalink
feat: checkbox input (#70)
Browse files Browse the repository at this point in the history
  • Loading branch information
joshuagraber committed May 7, 2024
1 parent f3ecc13 commit 621064b
Show file tree
Hide file tree
Showing 14 changed files with 201 additions and 179 deletions.
6 changes: 6 additions & 0 deletions docs/components.md
Expand Up @@ -319,6 +319,12 @@ export default {
value: 999
},
},
{
id: 'ice-cream',
name: 'iceCream',
label: 'Do you like ice cream?',
type: 'checkbox',
defaultChecked: true,
}
]
}
Expand Down
27 changes: 17 additions & 10 deletions src/components/Form/PdapForm.vue
Expand Up @@ -24,15 +24,14 @@
"
:value="values[field.name]"
@change="updateForm(field.name, $event)"
@input="updateForm(field.name, $event)"
/>
<slot />
</form>
</template>
<script setup lang="ts">
// Globals
import { computed, reactive, ref, watchEffect } from 'vue';
import { computed, ref, watchEffect } from 'vue';
import { useVuelidate } from '@vuelidate/core';
// Components
Expand Down Expand Up @@ -63,7 +62,7 @@ const data = computed(() =>
})
);
let values = reactive<Record<string, string>>(
const values = ref<Record<string, string>>(
data.value.reduce((acc, input) => {
switch (input.type) {
case PdapInputTypes.CHECKBOX:
Expand Down Expand Up @@ -101,8 +100,15 @@ export type VuelidateInstance = typeof v$.value;
const errorMessage = ref(props.error);
// Handlers
function updateForm(fieldName: string, value: string) {
values[fieldName] = value;
function updateForm(fieldName: string, event: Event) {
const target = event.target as HTMLInputElement;
const update =
target.type === PdapInputTypes.CHECKBOX &&
typeof target.checked === 'boolean'
? target.checked.toString()
: target.value;
values.value[fieldName] = update;
change();
}
Expand Down Expand Up @@ -133,24 +139,25 @@ watchEffect(() => {
});
function change() {
emit('change', { ...values });
emit('change', { ...values.value });
}
async function submit(event: 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 });
emit('submit', { ...values.value });
// Reset vuelidate and form
v$.value.$reset();
const form = <HTMLFormElement>event.target;
form.reset();
// Wipe values state ('' for text inputs, as-was for everything else)
values = Object.entries(values).reduce((acc, [key, value]) => {
return { ...acc, [key]: ['true', 'false'].includes(value) ? value : '' };
// Wipe values state
values.value = Object.entries(values).reduce((acc, [key]) => {
return { ...acc, [key]: '' };
}, {});
return;
}
}
Expand Down
70 changes: 18 additions & 52 deletions src/components/Form/__snapshots__/form.spec.ts.snap
Expand Up @@ -3,49 +3,32 @@
exports[`Form component > Renders component in form error state 1`] = `
<form class="pdap-form" id="test" name="test">
<div class="pdap-form-error-message">This form is incorrect</div>
<div class="pdap-grid-container pdap-input">
<!-- Text -->
<input id="test-1" name="testOne" placeholder="Enter a value here" type="text"><!-- Password -->
<!--v-if-->
<div class="pdap-input pdap-input-text">
<input id="test-1" name="testOne" placeholder="Enter a value here" type="text">
<!--v-if-->
<label class="pdap-input-label" for="test-1">First test input</label>
</div>
<div class="pdap-grid-container pdap-input">
<!-- Text -->
<input id="test-email" name="email" placeholder="Email" type="text"><!-- Password -->
<!--v-if-->
<div class="pdap-input pdap-input-text">
<input id="test-email" name="email" placeholder="Email" type="text">
<!--v-if-->
<label class="pdap-input-label" for="test-email">Email test input</label>
</div>
<div class="pdap-grid-container pdap-input">
<!-- Text -->
<!--v-if-->
<!-- Password -->
<div class="pdap-input pdap-input-password">
<input id="test-password" name="password" placeholder="Password" type="password">
<!--v-if-->
<label class="pdap-input-label" for="test-password">Password test input</label>
</div>
<div class="pdap-grid-container pdap-input">
<!-- Text -->
<input id="test-2" name="testTwo" placeholder="Enter another value here" type="text"><!-- Password -->
<!--v-if-->
<div class="pdap-input pdap-input-text">
<input id="test-2" name="testTwo" placeholder="Enter another value here" type="text">
<!--v-if-->
<label class="pdap-input-label" for="test-2">Second test input</label>
</div>
<div class="pdap-grid-container pdap-input">
<!-- Text -->
<!--v-if-->
<!-- Password -->
<!-- Checkbox -->
<div class="pdap-input pdap-input-checkbox pdap-input-checkbox-checked">
<input checked class="pdap-input-checkbox" id="checkbox-default-checked" name="checkboxDefaultChecked" type="checkbox" value="true">
<!--v-if-->
<label class="pdap-input-label" for="checkbox-default-checked">Keep this box checked?</label>
</div>
<div class="pdap-grid-container pdap-input">
<!-- Text -->
<!--v-if-->
<!-- Password -->
<!-- Checkbox -->
<div class="pdap-input pdap-input-checkbox">
<input class="pdap-input-checkbox" id="checkbox-default-unchecked" name="checkboxDefaultUnchecked" type="checkbox" value="false">
<!--v-if-->
<label class="pdap-input-label" for="checkbox-default-unchecked">Check this box?</label>
Expand All @@ -57,49 +40,32 @@ exports[`Form component > Renders component in form error state 1`] = `
exports[`Form component > Renders component in static state 1`] = `
<form class="pdap-form" id="test" name="test">
<!--v-if-->
<div class="pdap-grid-container pdap-input">
<!-- Text -->
<input id="test-1" name="testOne" placeholder="Enter a value here" type="text"><!-- Password -->
<!--v-if-->
<div class="pdap-input pdap-input-text">
<input id="test-1" name="testOne" placeholder="Enter a value here" type="text">
<!--v-if-->
<label class="pdap-input-label" for="test-1">First test input</label>
</div>
<div class="pdap-grid-container pdap-input">
<!-- Text -->
<input id="test-email" name="email" placeholder="Email" type="text"><!-- Password -->
<!--v-if-->
<div class="pdap-input pdap-input-text">
<input id="test-email" name="email" placeholder="Email" type="text">
<!--v-if-->
<label class="pdap-input-label" for="test-email">Email test input</label>
</div>
<div class="pdap-grid-container pdap-input">
<!-- Text -->
<!--v-if-->
<!-- Password -->
<div class="pdap-input pdap-input-password">
<input id="test-password" name="password" placeholder="Password" type="password">
<!--v-if-->
<label class="pdap-input-label" for="test-password">Password test input</label>
</div>
<div class="pdap-grid-container pdap-input">
<!-- Text -->
<input id="test-2" name="testTwo" placeholder="Enter another value here" type="text"><!-- Password -->
<!--v-if-->
<div class="pdap-input pdap-input-text">
<input id="test-2" name="testTwo" placeholder="Enter another value here" type="text">
<!--v-if-->
<label class="pdap-input-label" for="test-2">Second test input</label>
</div>
<div class="pdap-grid-container pdap-input">
<!-- Text -->
<!--v-if-->
<!-- Password -->
<!-- Checkbox -->
<div class="pdap-input pdap-input-checkbox pdap-input-checkbox-checked">
<input checked class="pdap-input-checkbox" id="checkbox-default-checked" name="checkboxDefaultChecked" type="checkbox" value="true">
<!--v-if-->
<label class="pdap-input-label" for="checkbox-default-checked">Keep this box checked?</label>
</div>
<div class="pdap-grid-container pdap-input">
<!-- Text -->
<!--v-if-->
<!-- Password -->
<!-- Checkbox -->
<div class="pdap-input pdap-input-checkbox">
<input class="pdap-input-checkbox" id="checkbox-default-unchecked" name="checkboxDefaultUnchecked" type="checkbox" value="false">
<!--v-if-->
<label class="pdap-input-label" for="checkbox-default-unchecked">Check this box?</label>
Expand Down
4 changes: 2 additions & 2 deletions src/components/Form/form.spec.ts
Expand Up @@ -215,10 +215,10 @@ describe('Form component', () => {
);
expect(
(inputCheckboxDefaultChecked.element as HTMLInputElement).value
).toBe('false');
).toBe('true');
expect(
(inputCheckboxDefaultUnchecked.element as HTMLInputElement).value
).toBe('true');
).toBe('false');
});

test('Submits with correct values', async () => {
Expand Down
7 changes: 3 additions & 4 deletions src/components/Form/types.ts
@@ -1,4 +1,4 @@
import { PdapInputBaseProps } from '../Input/types';
import { PdapInputProps } from '../Input/types';

export type PdapLengthRules = 'maxLength' | 'minLength';

Expand All @@ -21,10 +21,9 @@ export interface PdapFormValidators {
password: PdapFormValidator<boolean>;
}

export interface PdapFormInputProps
extends Exclude<PdapInputBaseProps, 'error'> {
export type PdapFormInputProps = PdapInputProps & {
validators?: Partial<PdapFormValidators>;
}
};
export type PdapFormSchema = PdapFormInputProps[];
export type PdapFormData = Record<string, unknown>;

Expand Down
27 changes: 27 additions & 0 deletions src/components/Input/Checkbox/InputCheckbox.vue
@@ -0,0 +1,27 @@
<template>
<input
:id="id"
:name="name"
:checked="value === 'true' ?? undefined"
:value="value"
class="pdap-input-checkbox"
type="checkbox"
v-bind="$attrs"
@change="change"
/>
</template>
<script setup lang="ts">
import { PdapInputCheckboxProps } from '../types';
defineProps<PdapInputCheckboxProps>();
// Emits
const emit = defineEmits<{
(event: 'change', val: Event): void;
}>();
const change = (event: Event) => {
emit('change', event);
};
</script>
95 changes: 35 additions & 60 deletions src/components/Input/PdapInput.vue
@@ -1,46 +1,22 @@
<template>
<div
:class="{
'pdap-grid-container': true,
'pdap-input': true,
[`pdap-input-${type}`]: true,
[`pdap-input-${type}-checked`]:
type === PdapInputTypes.CHECKBOX && value === 'true',
'pdap-input-error': Number(error?.length) >= 1,
}"
>
<!-- Text -->
<input
v-if="type === PdapInputTypes.TEXT"
:id="id"
type="text"
:name="name"
:placeholder="placeholder"
:value="value"
v-bind="$attrs"
@input="input"
<PdapInputCheckbox
v-if="type === PdapInputTypes.CHECKBOX"
class="pdap-input-checkbox"
v-bind="{ ...$attrs, ...(props as PdapInputCheckboxProps) }"
/>
<!-- Password -->
<input
v-if="type === PdapInputTypes.PASSWORD"
:id="id"
type="password"
:name="name"
:placeholder="placeholder"
:value="value"
v-bind="$attrs"
@input="input"
/>
<!-- Checkbox -->
<input
v-else-if="type === PdapInputTypes.CHECKBOX"
:id="id"
class="pdap-input-checkbox"
:name="name"
:checked="checked"
type="checkbox"
:value="value"
v-bind="$attrs"
@change="change"
<PdapInputText
v-else
v-bind="{ ...$attrs, ...($props as PdapInputTextProps) }"
/>
<div v-if="error" :id="errorMessageId" class="pdap-input-error-message">
Expand All @@ -52,27 +28,17 @@
</template>
<script setup lang="ts">
import { computed, ref } from 'vue';
import { PdapInputBaseProps, PdapInputTypes } from './types';
const props = withDefaults(defineProps<PdapInputBaseProps>(), {});
// Emits
const emit = defineEmits<{
(event: 'change', val: string): void;
(event: 'input', val: string): void;
}>();
const change = () => {
checked.value = !checked.value;
emit('change', checked.value.toString());
};
const input = (event: Event) => {
emit('input', (<HTMLInputElement>event.target).value);
};
const checked = ref(props.defaultChecked);
import { computed } from 'vue';
import {
PdapInputProps,
PdapInputTypes,
PdapInputCheckboxProps,
PdapInputTextProps,
} from './types';
import PdapInputText from './Text/InputText.vue';
import PdapInputCheckbox from './Checkbox/InputCheckbox.vue';
const props = withDefaults(defineProps<PdapInputProps>(), {});
const errorMessageId = computed(() => `pdap-${props.name}-input-error`);
</script>
Expand All @@ -86,10 +52,6 @@ const errorMessageId = computed(() => `pdap-${props.name}-input-error`);
@apply h-[max-content] gap-4 leading-normal mb-3 w-full flex flex-col;
}
.pdap-grid-container.pdap-input {
@apply p-0 gap-0;
}
.pdap-input input {
@apply dark:bg-neutral-950 border border-neutral-500 border-solid px-3 py-2 text-[rgba(0,0,0)];
}
Expand Down Expand Up @@ -134,12 +96,25 @@ const errorMessageId = computed(() => `pdap-${props.name}-input-error`);
}
/* Input - checkbox */
.pdap-input-checkbox {
@apply border-2 border-transparent items-center gap-4 flex-row py-1 px-2 w-auto;
}
.pdap-input-checkbox-checked {
@apply border-2 border-brand-gold border-solid rounded-md;
}
.pdap-input input[type='checkbox'] {
@apply h-6 w-6 accent-brand-gold;
}
.pdap-input input[type='checkbox'] ~ label {
@apply pl-0;
@apply pl-0 w-full max-w-full;
}
.pdap-input input[type='checkbox'],
.pdap-input input[type='checkbox'] ~ label {
@apply cursor-pointer;
}
}
</style>

0 comments on commit 621064b

Please sign in to comment.