Skip to content

Commit

Permalink
feat(FE:FSADT1-772): updating radio to use new component
Browse files Browse the repository at this point in the history
  • Loading branch information
Paulo Gomes da Cruz Junior authored and DerekRoberts committed May 23, 2023
1 parent 54a218e commit fef4ff2
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 13 deletions.
1 change: 1 addition & 0 deletions frontend/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,6 @@ declare module '@vue/runtime-core' {
BRow: typeof import('bootstrap-vue-3')['BRow']
BTab: typeof import('bootstrap-vue-3')['BTab']
BTabs: typeof import('bootstrap-vue-3')['BTabs']
RadioInputComponent: typeof import('./src/components/forms/RadioInputComponent.vue')['default']
}
}
64 changes: 64 additions & 0 deletions frontend/src/components/forms/RadioInputComponent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<template>
<b-form-group label="Choose one these options:" v-slot="{ ariaDescribedby}">
<b-form-radio v-for="(option, index) in modelValue" :key="index"
v-model="selectedValue"
:id="id+'_'+option.value"
:value="option.value"
:name="option.text"
:aria-describedby="ariaDescribedby"
@change="validateInput"
>{{ option.text }}</b-form-radio>
</b-form-group>
</template>

<script setup lang="ts">
import { ref, watch } from "vue";
import { CodeDescrType, isEmpty } from "@/core/CommonTypes";
const props = defineProps({
id: { type: String, required: true },
modelValue: {
type: Array as () => CodeDescrType[],
required: true,
},
validations: { type: Array<Function>, required: true },
});
//Events we emit during component lifecycle
const emit = defineEmits<{
(e: "error", value: string): void;
(e: "empty", value: boolean): void;
(e: "update:modelValue", value: Object): void;
}>();
const selectedValue = ref({});
//We initialize the error message handling for validation
const error = ref<string | undefined>("");
//We call all the validations
const validateInput = () => {
if (props.validations) {
error.value = props.validations
.map((validation) => validation(selectedValue.value))
.filter((errorMessage) => {
if (errorMessage) return true;
return false;
})
.shift() ?? "";
}
emit("empty", isEmpty(selectedValue));
};
//We watch for input changes to emit events
watch(selectedValue, () => {
if (error.value) {
error.value = "";
}
emit("update:modelValue",
props.modelValue.find((entry) => entry.value === selectedValue.value)
);
});
//We watch for error changes to emit events
watch(error, () => emit("error", error.value));
</script>
9 changes: 8 additions & 1 deletion frontend/src/core/CommonTypes.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { Ref } from "vue";

export interface CodeDescrType {
value: string;
text: string;
Expand All @@ -6,4 +8,9 @@ export interface CodeDescrType {
export interface ValidationMessageType {
fieldId: string;
errorMsg: string;
}
}

export const isEmpty = (ref: Ref): boolean => {
const value = ref.value;
return value === undefined || value === null || value === "";
};
29 changes: 17 additions & 12 deletions frontend/src/pages/applyclientnumber/ApplyClientNumberPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,17 @@
<CollapseCard title="Registered business"
id="businessInformationId"
defaultOpen>
<Label label="Choose one these options:"
id="clientTypeLabelId" />

<b-form-group @change="getBusinessName">
<b-form-radio v-model="formData.businessInformation.businessType"
value="R">
I have a BC registered business (corporation, sole proprietorship, society, etc.)
</b-form-radio>
<b-form-radio v-model="formData.businessInformation.businessType"
value="U">I have an unregistered sole proprietorship
</b-form-radio>
</b-form-group>

<RadioInputComponent
:id="'businessType'"
:modelValue="[
{ value: 'R',text: 'I have a BC registered business (corporation, sole proprietorship, society, etc.)' },
{ value: 'U',text: 'I have an unregistered sole proprietorship' },
]"
:validations="[]"
@update:modelValue="formData = { ...formData, businessInformation:{ businessType: $event } }"
/>

<ValidationMessages fieldId = 'businessInformation.businessType'
:validationMessages="validationMessages"
:modelValue="formData.businessInformation.businessType" />
Expand Down Expand Up @@ -141,6 +140,9 @@ import ValidationMessages from "@/common/ValidationMessagesComponent.vue";
import AddressSection from "@/pages/applyclientnumber/AddressSectionComponent.vue";
import ContactSectionComponent from '@/pages/applyclientnumber/ContactSectionComponent.vue';
import RadioInputComponent from '@/components/forms/RadioInputComponent.vue';
const props = defineProps({
submitterInformation: {
type: Object,
Expand All @@ -151,6 +153,9 @@ const props = defineProps({
//---- Form Data ----//
let formData = ref(formDataDto);
watch([formData],() => console.log('Updated',formData.value));
//--- Initializing the Addresses array ---//
addNewAddress(formDataDto.location.addresses);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { describe, it, expect } from 'vitest';

import { mount } from "@vue/test-utils";
import RadioInputComponent from '@/components/forms/RadioInputComponent.vue';

describe("Radio Input Component", () => {

const id = "my-input";
const values = [
{ value: 'A', text: 'First' }, { value: 'B', text: 'Second' }
];
const validations = [(value: any) => (value && value === 'A' ? "Can't select A" : "")];

it("renders the input field with the provided id", () => {

const wrapper = mount(RadioInputComponent, {
props: {
id,
modelValue: values,
validations: [],
},
});

expect(wrapper.find(`#${id}_A`).exists()).toBe(true);
expect(wrapper.find(`#${id}_B`).exists()).toBe(true);
});

it('emits the "error" event when there is a validation error', async () => {

const wrapper = mount(RadioInputComponent, {
props: {
id,
modelValue: values,
validations,
},
});

await wrapper.find('input[type=radio][value=A]').setValue();
await wrapper.find('input[type=radio][value=A]').trigger('change');

const event = wrapper.emitted("error");

expect(event).toBeTruthy();
expect(event).toHaveLength(1);
expect(event[0][0]).toBe("Can't select A");
});

it('emits the "update" event when selected', async () => {

const wrapper = mount(RadioInputComponent, {
props: {
id,
modelValue: values,
validations,
},
});

await wrapper.find('input[type=radio][value=B]').setValue();
await wrapper.find('input[type=radio][value=B]').trigger('change');

const event = wrapper.emitted("update:modelValue");
expect(event).toBeTruthy();
expect(event).toHaveLength(1);
expect(event[0]).toStrictEqual([{ value: 'B', text: 'Second' }]);
});

it('emits the "error" then no error', async () => {

const wrapper = mount(RadioInputComponent, {
props: {
id,
modelValue: values,
validations,
},
});

await wrapper.find('input[type=radio][value=A]').setValue();
await wrapper.find('input[type=radio][value=A]').trigger('change');

const errorEvent = wrapper.emitted("error");
const updateEvent = wrapper.emitted("update:modelValue");

expect(errorEvent).toBeTruthy();
expect(errorEvent).toHaveLength(1);
expect(errorEvent[0][0]).toBe("Can't select A");

expect(updateEvent).toBeTruthy();
expect(updateEvent).toHaveLength(1);
expect(updateEvent[0]).toStrictEqual([{ value: 'A', text: 'First' }]);

await wrapper.find('input[type=radio][value=B]').setValue();
await wrapper.find('input[type=radio][value=B]').trigger('change');

expect(errorEvent).toHaveLength(2);
expect(errorEvent[0][1]).toBeUndefined()

expect(updateEvent).toHaveLength(2);
expect(updateEvent[1]).toStrictEqual([{ value: 'B', text: 'Second' }]);

});
});

0 comments on commit fef4ff2

Please sign in to comment.