mirror of https://github.com/halo-dev/halo-admin
parent
d66ee9c391
commit
6d45b96e4e
@ -0,0 +1,56 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
const props = defineProps({
|
||||||
|
checked: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: [String, Number, Boolean],
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const id = ["checkbox", props.name, props.value]
|
||||||
|
.filter((item) => !!item)
|
||||||
|
.join("-");
|
||||||
|
|
||||||
|
const emit = defineEmits(["update:checked", "change"]);
|
||||||
|
|
||||||
|
function handleChange(e: Event) {
|
||||||
|
const { checked } = e.target as HTMLInputElement;
|
||||||
|
emit("update:checked", checked);
|
||||||
|
emit("change", e);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
:class="{ 'checkbox-wrapper-checked': checked }"
|
||||||
|
class="checkbox-wrapper"
|
||||||
|
>
|
||||||
|
<div class="checkbox-inner">
|
||||||
|
<input
|
||||||
|
:id="id"
|
||||||
|
:checked="checked"
|
||||||
|
:value="value"
|
||||||
|
type="checkbox"
|
||||||
|
@change="handleChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<label v-if="label" :for="id" class="checkbox-label">
|
||||||
|
{{ label }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<style lang="scss">
|
||||||
|
.checkbox-wrapper {
|
||||||
|
@apply flex;
|
||||||
|
@apply items-center;
|
||||||
|
@apply box-border;
|
||||||
|
@apply flex-grow-0;
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,59 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { VCheckbox } from "./index";
|
||||||
|
import type { PropType } from "vue";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: {
|
||||||
|
type: Object as PropType<Array<string>>,
|
||||||
|
default: () => {
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
type: Object as PropType<Array<Record<string, string>>>,
|
||||||
|
},
|
||||||
|
valueKey: {
|
||||||
|
type: String,
|
||||||
|
default: "value",
|
||||||
|
},
|
||||||
|
labelKey: {
|
||||||
|
type: String,
|
||||||
|
default: "label",
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(["update:modelValue", "change"]);
|
||||||
|
|
||||||
|
function handleChange(e: Event) {
|
||||||
|
const { value, checked } = e.target as HTMLInputElement;
|
||||||
|
const checkedValues = [...props.modelValue];
|
||||||
|
|
||||||
|
if (checked) {
|
||||||
|
checkedValues.push(value);
|
||||||
|
} else {
|
||||||
|
checkedValues.splice(checkedValues.indexOf(value), 1);
|
||||||
|
}
|
||||||
|
emit("update:modelValue", checkedValues);
|
||||||
|
emit("change", checkedValues);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="checkbox-group-wrapper">
|
||||||
|
<VCheckbox
|
||||||
|
v-for="(option, index) in options"
|
||||||
|
:key="index"
|
||||||
|
:checked="modelValue.includes(option[valueKey])"
|
||||||
|
:label="option[labelKey]"
|
||||||
|
:name="name"
|
||||||
|
:value="option[valueKey]"
|
||||||
|
@change="handleChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<style lang="scss">
|
||||||
|
.checkbox-group-wrapper {
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,61 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { VCheckbox } from "../index";
|
||||||
|
import { mount } from "@vue/test-utils";
|
||||||
|
|
||||||
|
describe("CheckBox", () => {
|
||||||
|
it("should render", () => {
|
||||||
|
expect(VCheckbox).toBeDefined();
|
||||||
|
expect(mount(VCheckbox).html()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should work with v-model:checked", async function () {
|
||||||
|
const wrapper = mount({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
checked: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<v-checkbox v-model:checked="checked"/>
|
||||||
|
`,
|
||||||
|
components: {
|
||||||
|
VCheckbox,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.find("input").element.checked).toBe(false);
|
||||||
|
expect(wrapper.findComponent(VCheckbox).classes()).not.toContain(
|
||||||
|
"checkbox-wrapper-checked"
|
||||||
|
);
|
||||||
|
|
||||||
|
// change checked value
|
||||||
|
await wrapper.setData({ checked: true });
|
||||||
|
expect(wrapper.find("input").element.checked).toBe(true);
|
||||||
|
expect(wrapper.findComponent(VCheckbox).classes()).toContain(
|
||||||
|
"checkbox-wrapper-checked"
|
||||||
|
);
|
||||||
|
|
||||||
|
// click on checkbox
|
||||||
|
await wrapper.find("input").setValue(false);
|
||||||
|
expect(wrapper.vm.checked).toBe(false);
|
||||||
|
expect(wrapper.find("input").element.checked).toBe(false);
|
||||||
|
expect(wrapper.findComponent(VCheckbox).classes()).not.toContain(
|
||||||
|
"checkbox-wrapper-checked"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should work with label prop", async function () {
|
||||||
|
const wrapper = mount(VCheckbox, {
|
||||||
|
props: {
|
||||||
|
checked: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.html()).not.toContain("label");
|
||||||
|
|
||||||
|
await wrapper.setProps({ label: "label" });
|
||||||
|
|
||||||
|
expect(wrapper.html()).toContain("label");
|
||||||
|
expect(wrapper.find("label").text()).toBe("label");
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,124 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { VCheckbox, VCheckboxGroup } from "../index";
|
||||||
|
import { mount } from "@vue/test-utils";
|
||||||
|
|
||||||
|
const options = [
|
||||||
|
{
|
||||||
|
value: "foo",
|
||||||
|
label: "foo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "bar",
|
||||||
|
label: "bar",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
describe("CheckBoxGroup", () => {
|
||||||
|
it("should render", () => {
|
||||||
|
expect(VCheckboxGroup).toBeDefined();
|
||||||
|
expect(
|
||||||
|
mount(VCheckboxGroup, {
|
||||||
|
props: {
|
||||||
|
options,
|
||||||
|
},
|
||||||
|
}).html()
|
||||||
|
).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should work with options prop", function () {
|
||||||
|
const wrapper = mount({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
options: options,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<v-checkbox-group :options="options" />
|
||||||
|
`,
|
||||||
|
components: {
|
||||||
|
VCheckboxGroup,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.findAllComponents(VCheckbox).length).toBe(2);
|
||||||
|
expect(wrapper.findAllComponents(VCheckbox)[0].vm.$props.value).toBe("foo");
|
||||||
|
expect(wrapper.findAllComponents(VCheckbox)[0].vm.$props.label).toBe("foo");
|
||||||
|
expect(wrapper.findAllComponents(VCheckbox)[1].vm.$props.value).toBe("bar");
|
||||||
|
expect(wrapper.findAllComponents(VCheckbox)[1].vm.$props.label).toBe("bar");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should work with v-model", async function () {
|
||||||
|
const wrapper = mount({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
value: ["foo"],
|
||||||
|
options: options,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<v-checkbox-group v-model="value" :options="options" />
|
||||||
|
`,
|
||||||
|
components: {
|
||||||
|
VCheckboxGroup,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(wrapper.findAllComponents(VCheckbox)[0].classes()).toContain(
|
||||||
|
"checkbox-wrapper-checked"
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
wrapper.findAllComponents(VCheckbox)[0].find("input").element.checked
|
||||||
|
).toBe(true);
|
||||||
|
|
||||||
|
// mock click event
|
||||||
|
await wrapper.findAllComponents(VCheckbox)[1].find("input").setValue(true);
|
||||||
|
|
||||||
|
expect(wrapper.findAllComponents(VCheckbox)[1].classes()).toContain(
|
||||||
|
"checkbox-wrapper-checked"
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
wrapper.findAllComponents(VCheckbox)[1].find("input").element.checked
|
||||||
|
).toBe(true);
|
||||||
|
expect(wrapper.vm.value).toEqual(["foo", "bar"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
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: `
|
||||||
|
<v-checkbox-group v-model="value" :options="options" value-key="id" label-key="name" />
|
||||||
|
`,
|
||||||
|
components: {
|
||||||
|
VCheckboxGroup,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
wrapper.findAllComponents(VCheckbox)[0].find("input").attributes("value")
|
||||||
|
).toBe("foo");
|
||||||
|
expect(
|
||||||
|
wrapper.findAllComponents(VCheckbox)[0].find(".checkbox-label").text()
|
||||||
|
).toBe("foo");
|
||||||
|
|
||||||
|
await wrapper.findAllComponents(VCheckbox)[1].find("input").setValue(true);
|
||||||
|
expect(wrapper.findAllComponents(VCheckbox)[1].classes()).toContain(
|
||||||
|
"checkbox-wrapper-checked"
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(wrapper.vm.value).toEqual(["foo", "bar"]);
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,8 @@
|
|||||||
|
// Vitest Snapshot v1
|
||||||
|
|
||||||
|
exports[`CheckBox > should render 1`] = `
|
||||||
|
"<div class=\\"checkbox-wrapper\\">
|
||||||
|
<div class=\\"checkbox-inner\\"><input id=\\"checkbox\\" type=\\"checkbox\\" value=\\"false\\"></div>
|
||||||
|
<!--v-if-->
|
||||||
|
</div>"
|
||||||
|
`;
|
@ -0,0 +1,12 @@
|
|||||||
|
// Vitest Snapshot v1
|
||||||
|
|
||||||
|
exports[`CheckBoxGroup > should render 1`] = `
|
||||||
|
"<div class=\\"checkbox-group-wrapper\\">
|
||||||
|
<div class=\\"checkbox-wrapper\\">
|
||||||
|
<div class=\\"checkbox-inner\\"><input id=\\"checkbox-foo\\" type=\\"checkbox\\" value=\\"foo\\"></div><label for=\\"checkbox-foo\\" class=\\"checkbox-label\\">foo</label>
|
||||||
|
</div>
|
||||||
|
<div class=\\"checkbox-wrapper\\">
|
||||||
|
<div class=\\"checkbox-inner\\"><input id=\\"checkbox-bar\\" type=\\"checkbox\\" value=\\"bar\\"></div><label for=\\"checkbox-bar\\" class=\\"checkbox-label\\">bar</label>
|
||||||
|
</div>
|
||||||
|
</div>"
|
||||||
|
`;
|
@ -0,0 +1,2 @@
|
|||||||
|
export { default as VCheckbox } from "./CheckBox.vue";
|
||||||
|
export { default as VCheckboxGroup } from "./CheckBoxGroup.vue";
|
@ -1,8 +1,8 @@
|
|||||||
// Vitest Snapshot v1
|
// Vitest Snapshot v1
|
||||||
|
|
||||||
exports[`Radio > should render 1`] = `
|
exports[`Radio > should render 1`] = `
|
||||||
"<div class=\\"radio-wrapper radio-wrapper-checked\\">
|
"<div class=\\"radio-wrapper-checked radio-wrapper\\">
|
||||||
<div class=\\"radio-inner\\"><input type=\\"radio\\" id=\\"radio\\" value=\\"false\\"></div>
|
<div class=\\"radio-inner\\"><input id=\\"radio\\" type=\\"radio\\" value=\\"false\\"></div>
|
||||||
<!--v-if-->
|
<!--v-if-->
|
||||||
</div>"
|
</div>"
|
||||||
`;
|
`;
|
||||||
|
Loading…
Reference in new issue