diff --git a/src/common-components/button-question/button-question.spec.js b/src/common-components/button-question/button-question.spec.js
new file mode 100644
index 00000000..3002920e
--- /dev/null
+++ b/src/common-components/button-question/button-question.spec.js
@@ -0,0 +1,190 @@
+import { shallowMount } from "@vue/test-utils";
+import buttonQuestion from "@/common-components/button-question/button-question";
+import { getMountOptions } from "@/helpers/unit-test-helper.js";
+
+jest.mock("@/store", () => { return {}; }, { virtual: true });
+
+describe("buttonQuestion.vue", () => {
+ it("Should show overflow classes on fieldset if isOverflowScrollable is true", () => {
+ // Act
+ const wrapper = shallowMount(buttonQuestion, {
+ propsData: {
+ isOverflowScrollable: true,
+ groupName: "group-name"
+ }
+ });
+
+ // Assert
+ const fieldSet = wrapper.find('fieldset');
+ expect(fieldSet.classes()).toContain("overflow-scroll");
+ });
+});
+
+describe("buttonQuestion.vue", () => {
+ it("Fieldset classes should contain row if button type is listCard", () => {
+ // Act
+ const wrapper = shallowMount(buttonQuestion, {
+ propsData: {
+ buttonType: "listCard",
+ groupName: "group-name"
+ }
+ });
+ // Assert
+ const Div = wrapper.find('fieldset div');
+ expect(Div.classes()).toContain("row");
+ });
+});
+
+describe("buttonQuestion.vue", () => {
+ it("Fieldset classes should contain d-flex if button type is listButtonHorizontal", () => {
+ // Act
+ const wrapper = shallowMount(buttonQuestion, {
+ propsData: {
+ buttonType: "listButtonHorizontal",
+ groupName: "group-name"
+ }
+ });
+ // Assert
+ const Div = wrapper.find('fieldset div');
+ expect(Div.classes()).toContain("d-flex");
+ });
+});
+
+describe("buttonQuestion.vue", () => {
+ it("Fieldset classes should contain ui-radio if button type is radio", () => {
+ // Act
+ const wrapper = shallowMount(buttonQuestion, {
+ propsData: {
+ buttonType: "radio",
+ groupName: "group-name"
+ }
+ });
+ // Assert
+ const Div = wrapper.find('fieldset div');
+ expect(Div.classes()).toContain("ui-radio");
+ });
+});
+
+
+// testing a computed property
+describe("buttonQuestion.vue", () => {
+ it("getColLength should return '12' if prop isWide is set to true", () => {
+ // Act
+ const localThis = { isWide: true }
+
+ expect(buttonQuestion.computed.getColLength.call(localThis)).toBe("12");
+ });
+});
+
+describe("buttonQuestion.vue", () => {
+ it("getColLength should return '' if prop isWide is set to false", () => {
+ // Act
+ const localThis = {
+ isWide: false,
+ answers: ['a', 'b'],
+ groupName: "group-name"
+ }
+
+ expect(buttonQuestion.computed.getColLength.call(localThis)).toBe("");
+ });
+});
+
+describe("buttonQuestion.vue", () => {
+ it("Should return answer.Text if prop useTextForValue is true", async () => {
+ // Act
+ const localThis = { useTextForValue: true };
+ const answer = { 'Name': 'testName', 'Text': 'testText' };
+
+ // Assert
+ expect(buttonQuestion.methods.getValue.call(localThis, answer)).toBe('testText');
+ });
+});
+
+describe("buttonQuestion.vue", () => {
+ it("Should return answer.Name if prop useTextForValue is false and answer.Name exists", async () => {
+ // Act
+ const localThis = { useTextForValue: false };
+ const answer = { 'Name': 'testName', 'Text': 'testText' };
+
+ // Assert
+ expect(buttonQuestion.methods.getValue.call(localThis, answer)).toBe('testName');
+ });
+});
+
+describe("buttonQuestion.vue", () => {
+ it("Should trigger event modelValue change to new value on when radio button selected", async () => {
+ // Act
+ const wrapper = shallowMount(buttonQuestion, setupMocks({propsData: {groupName: "group-name"}}));
+ await wrapper.setProps({
+ answers: ["2022", "2021", "2020"],
+ isMultiSelect: false,
+ modelValue: []
+ });
+ const val = { checkValue: true, value: "2021", }
+ wrapper.vm.handleCheckedChanged(val);
+ // Assert
+ expect(wrapper.emitted()["update:modelValue"][0]).toEqual([["2021"]]);
+ });
+});
+
+describe("buttonQuestion.vue", () => {
+ it("Should add values to array on checkbox click", () => {
+ // Act
+ const wrapper = shallowMount(buttonQuestion, setupMocks({
+ propsData: {
+ modelValue: ["2022", "2021", "2020"],
+ isMultiSelect: true,
+ groupName: "group-name"
+ }
+ }));
+ const val = { checkValue: true, value: "2019", }
+ wrapper.vm.handleCheckedChanged(val);
+ // Assert
+ expect(wrapper.emitted()["update:modelValue"][0]).toEqual([["2022", "2021", "2020", "2019"]]);
+ });
+});
+
+describe("buttonQuestion.vue", () => {
+ it("Should add a value to this.selectedValues if prop isMultiSelect is true, checkValue is true and this.selectedValues already exists", () => {
+ // Act
+ const wrapper = shallowMount(buttonQuestion, setupMocks({
+ propsData: {
+ isMultiSelect: true,
+ modelValue: ['a', 'b'],
+ groupName: "group-name"
+ }
+ }));
+ const val = { checkValue: true, value: "2021", }
+ wrapper.vm.handleCheckedChanged(val);
+
+ // Assert
+ expect(wrapper.vm.selectedValues).toEqual(["a", "b", "2021"]);
+ });
+});
+
+describe("buttonQuestion.vue", () => {
+ it("Should remove a value to this.selectedValues if prop isMultiSelect is true, checkValue is false and this.selectedValues already exists", () => {
+ // Act
+ const wrapper = shallowMount(buttonQuestion, setupMocks({
+ propsData: {
+ isMultiSelect: true,
+ modelValue: ['a', 'b'],
+ groupName: "group-name"
+ }
+ }));
+
+ const val = { checkValue: false, value: "a", }
+ wrapper.vm.handleCheckedChanged(val);
+
+ // Assert
+ expect(wrapper.vm.selectedValues).toEqual(["b"]);
+ });
+});
+
+function setupMocks(mountOptionsMockData = {}) {
+ const defaultMountOptions = { route: { query: { fmgPage: 'page-name' } } };
+ const baseMountOptions = getMountOptions(Object.assign(defaultMountOptions, mountOptionsMockData));
+ const allMountOptions = Object.assign(defaultMountOptions, baseMountOptions);
+
+ return allMountOptions;
+}
diff --git a/src/common-components/button-question/button-question.vue b/src/common-components/button-question/button-question.vue
new file mode 100644
index 00000000..10337778
--- /dev/null
+++ b/src/common-components/button-question/button-question.vue
@@ -0,0 +1,253 @@
+
+
+
+ {{ questionText }}
+
+
+
+
+ {{ questionText }}
+ {{(isMultiSelect && answers && answers.length > 1) ? 'Select one or more options below.' : 'Select an option below.' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/ux-components/list-button-horizontal/list-button-horizontal.spec.js b/src/ux-components/list-button-horizontal/list-button-horizontal.spec.js
new file mode 100644
index 00000000..e69de29b
diff --git a/src/ux-components/list-button-horizontal/list-button-horizontal.vue b/src/ux-components/list-button-horizontal/list-button-horizontal.vue
new file mode 100644
index 00000000..e69de29b
diff --git a/src/ux-components/list-button/list-button-vue b/src/ux-components/list-button/list-button-vue
new file mode 100644
index 00000000..e69de29b
diff --git a/src/ux-components/list-button/list-button.spec.js b/src/ux-components/list-button/list-button.spec.js
new file mode 100644
index 00000000..e69de29b
diff --git a/src/ux-components/list-card/list-card.spec.js b/src/ux-components/list-card/list-card.spec.js
new file mode 100644
index 00000000..e69de29b
diff --git a/src/ux-components/list-card/list-card.vue b/src/ux-components/list-card/list-card.vue
new file mode 100644
index 00000000..e69de29b
diff --git a/src/ux-components/radio/radio.spec.js b/src/ux-components/radio/radio.spec.js
new file mode 100644
index 00000000..e69de29b
diff --git a/src/ux-components/radio/radio.vue b/src/ux-components/radio/radio.vue
new file mode 100644
index 00000000..e69de29b