diff --git a/src/components/base/radio/Radio.vue b/src/components/base/radio/Radio.vue
new file mode 100644
index 000000000..09e96ddd8
--- /dev/null
+++ b/src/components/base/radio/Radio.vue
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/base/radio/RadioGroup.vue b/src/components/base/radio/RadioGroup.vue
new file mode 100644
index 000000000..5c4b29be2
--- /dev/null
+++ b/src/components/base/radio/RadioGroup.vue
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
diff --git a/src/components/base/radio/__tests__/Radio.spec.ts b/src/components/base/radio/__tests__/Radio.spec.ts
new file mode 100644
index 000000000..26b5a505d
--- /dev/null
+++ b/src/components/base/radio/__tests__/Radio.spec.ts
@@ -0,0 +1,98 @@
+import { describe, expect, it } from "vitest";
+import { VRadio } from "../index";
+import { mount } from "@vue/test-utils";
+
+describe("Radio", () => {
+ it("should render", () => {
+ expect(VRadio).toBeDefined();
+ expect(mount(VRadio).html()).toMatchSnapshot();
+ });
+
+ it("should work with v-model", async function () {
+ const wrapper = mount({
+ data() {
+ return {
+ value: "",
+ };
+ },
+ template: "",
+ components: {
+ VRadio,
+ },
+ });
+
+ expect(wrapper.findComponent(VRadio).classes()).not.toContain(
+ "radio-wrapper-checked"
+ );
+
+ await wrapper.setData({ value: "bar" });
+
+ expect(wrapper.findComponent(VRadio).classes()).toContain(
+ "radio-wrapper-checked"
+ );
+ });
+
+ it("should work with label prop", async function () {
+ const wrapper = mount(VRadio, {
+ props: {
+ value: "foo",
+ },
+ });
+ expect(wrapper.html()).not.toContain("label");
+
+ await wrapper.setProps({ label: "foo" });
+
+ expect(wrapper.html()).toContain("label");
+ expect(wrapper.find("label").text()).toBe("foo");
+ });
+
+ it("should work with multiple radio", async function () {
+ const wrapper = mount({
+ data() {
+ return {
+ value: "foo",
+ };
+ },
+ template:
+ "" +
+ "",
+ components: {
+ VRadio,
+ },
+ });
+
+ expect(wrapper.findAllComponents(VRadio).length).toBe(2);
+ expect(wrapper.findAllComponents(VRadio)[0].classes()).toContain(
+ "radio-wrapper-checked"
+ );
+
+ // set value to bar
+ await wrapper.setData({ value: "bar" });
+
+ expect(wrapper.findAllComponents(VRadio)[0].classes()).not.toContain(
+ "radio-wrapper-checked"
+ );
+ expect(wrapper.findAllComponents(VRadio)[1].classes()).toContain(
+ "radio-wrapper-checked"
+ );
+
+ // click on the first radio
+ await wrapper
+ .findAllComponents(VRadio)[0]
+ .find('input[type="radio"]')
+ .trigger("change");
+
+ expect(wrapper.findAllComponents(VRadio)[0].classes()).toContain(
+ "radio-wrapper-checked"
+ );
+
+ // click on the second radio
+ await wrapper
+ .findAllComponents(VRadio)[1]
+ .find('input[type="radio"]')
+ .trigger("change");
+ expect(wrapper.findAllComponents(VRadio)[1].classes()).toContain(
+ "radio-wrapper-checked"
+ );
+ });
+});
diff --git a/src/components/base/radio/__tests__/RadioGroup.spec.ts b/src/components/base/radio/__tests__/RadioGroup.spec.ts
new file mode 100644
index 000000000..0cddd2c15
--- /dev/null
+++ b/src/components/base/radio/__tests__/RadioGroup.spec.ts
@@ -0,0 +1,135 @@
+import { describe, expect, it } from "vitest";
+import { VRadio, VRadioGroup } from "../index";
+import { mount } from "@vue/test-utils";
+
+describe("RadioGroup", () => {
+ it("should render", function () {
+ expect(VRadioGroup).toBeDefined();
+ expect(
+ mount(VRadioGroup, {
+ props: {
+ options: [
+ {
+ value: "foo",
+ label: "foo",
+ },
+ {
+ value: "bar",
+ label: "bar",
+ },
+ ],
+ },
+ }).html()
+ ).toMatchSnapshot();
+ });
+
+ it("should work with options prop", function () {
+ const wrapper = mount({
+ data() {
+ return {
+ options: [
+ {
+ value: "foo",
+ label: "foo",
+ },
+ {
+ value: "bar",
+ label: "bar",
+ },
+ ],
+ };
+ },
+ template: '',
+ components: {
+ VRadioGroup,
+ },
+ });
+
+ expect(wrapper.findAllComponents(VRadio).length).toBe(2);
+ expect(wrapper.findAllComponents(VRadio)[0].vm.$props.value).toBe("foo");
+ expect(wrapper.findAllComponents(VRadio)[0].vm.$props.label).toBe("foo");
+ expect(wrapper.findAllComponents(VRadio)[1].vm.$props.value).toBe("bar");
+ expect(wrapper.findAllComponents(VRadio)[1].vm.$props.label).toBe("bar");
+ });
+
+ it("should work with v-model", async function () {
+ const wrapper = mount({
+ data() {
+ return {
+ value: "foo",
+ options: [
+ {
+ value: "foo",
+ label: "foo",
+ },
+ {
+ value: "bar",
+ label: "bar",
+ },
+ ],
+ };
+ },
+ template: '',
+ components: {
+ VRadioGroup,
+ },
+ });
+
+ expect(wrapper.findAllComponents(VRadio)[0].classes()).toContain(
+ "radio-wrapper-checked"
+ );
+
+ await wrapper
+ .findAllComponents(VRadio)[1]
+ .find('input[type="radio"]')
+ .trigger("change");
+
+ expect(wrapper.findAllComponents(VRadio)[0].classes()).not.toContain(
+ "radio-wrapper-checked"
+ );
+ expect(wrapper.findAllComponents(VRadio)[1].classes()).toContain(
+ "radio-wrapper-checked"
+ );
+ });
+
+ it("should work with valueKey and labelKey props", async function () {
+ const wrapper = mount({
+ data() {
+ return {
+ value: "foo",
+ options: [
+ {
+ id: "foo",
+ name: "foo",
+ },
+ {
+ id: "bar",
+ name: "bar",
+ },
+ ],
+ };
+ },
+ template:
+ '',
+ components: {
+ VRadioGroup,
+ },
+ });
+
+ expect(
+ wrapper.findAllComponents(VRadio)[0].find("input").attributes("value")
+ ).toBe("foo");
+ expect(
+ wrapper.findAllComponents(VRadio)[0].find(".radio-label").text()
+ ).toBe("foo");
+
+ await wrapper
+ .findAllComponents(VRadio)[1]
+ .find('input[type="radio"]')
+ .trigger("change");
+
+ expect(wrapper.findAllComponents(VRadio)[1].classes()).toContain(
+ "radio-wrapper-checked"
+ );
+ });
+});
diff --git a/src/components/base/radio/__tests__/__snapshots__/Radio.spec.ts.snap b/src/components/base/radio/__tests__/__snapshots__/Radio.spec.ts.snap
new file mode 100644
index 000000000..e6510e34b
--- /dev/null
+++ b/src/components/base/radio/__tests__/__snapshots__/Radio.spec.ts.snap
@@ -0,0 +1,8 @@
+// Vitest Snapshot v1
+
+exports[`Radio > should render 1`] = `
+"
"
+`;
diff --git a/src/components/base/radio/__tests__/__snapshots__/RadioGroup.spec.ts.snap b/src/components/base/radio/__tests__/__snapshots__/RadioGroup.spec.ts.snap
new file mode 100644
index 000000000..224bcd3c1
--- /dev/null
+++ b/src/components/base/radio/__tests__/__snapshots__/RadioGroup.spec.ts.snap
@@ -0,0 +1,12 @@
+// Vitest Snapshot v1
+
+exports[`RadioGroup > should render 1`] = `
+""
+`;
diff --git a/src/components/base/radio/index.ts b/src/components/base/radio/index.ts
new file mode 100644
index 000000000..57469df51
--- /dev/null
+++ b/src/components/base/radio/index.ts
@@ -0,0 +1,2 @@
+export { default as VRadio } from "./Radio.vue";
+export { default as VRadioGroup } from "./RadioGroup.vue";
diff --git a/src/views/ViewComponents.vue b/src/views/ViewComponents.vue
index 12b6e7efe..9f0d2721a 100644
--- a/src/views/ViewComponents.vue
+++ b/src/views/ViewComponents.vue
@@ -42,20 +42,20 @@
Size:
@@ -87,7 +87,7 @@
Disabled:
-
+
@@ -97,8 +97,8 @@
{{ option.label }}
@@ -109,14 +109,32 @@
{{ option.label }}
+
+ Radio
+ Radio:
+
+
+
+ Radio Group:
+
+
+
+
@@ -125,12 +143,14 @@
import { FilledLayout } from "../layouts";
import { VButton } from "@/components/base/button";
import { VInput } from "@/components/base/input";
-import { VSelect, VOption } from "@/components/base/select";
+import { VOption, VSelect } from "@/components/base/select";
import { VTextarea } from "@/components/base/textarea";
+import { VRadio, VRadioGroup } from "@/components/base/radio";
import { ref } from "vue";
const inputValue = ref();
const selectValue = ref();
+const radioValue = ref("apple");
const selectData = [
{
@@ -142,4 +162,19 @@ const selectData = [
label: "2",
},
];
+
+const radioData = [
+ {
+ value: "banana",
+ label: "Banana",
+ },
+ {
+ value: "apple",
+ label: "Apple",
+ },
+ {
+ value: "orange",
+ label: "Orange",
+ },
+];