feat: add space component and test case

Signed-off-by: Ryan Wang <i@ryanc.cc>
pull/581/head
Ryan Wang 2022-04-21 15:48:59 +08:00
parent 19c74b3cde
commit d35c973f0c
7 changed files with 248 additions and 54 deletions

View File

@ -0,0 +1,65 @@
<script lang="ts" setup>
import type { Align, Direction, Spacing } from "./interface";
import { SpacingSize } from "./interface";
import type { PropType } from "vue";
import { computed } from "vue";
const props = defineProps({
spacing: {
type: String as PropType<Spacing>,
default: "xs",
},
direction: {
type: String as PropType<Direction>,
default: "row",
},
align: {
type: String as PropType<Align>,
default: "center",
},
});
const wrapperClasses = computed(() => {
const { direction, align } = props;
return [`space-direction-${direction}`, `space-align-${align}`];
});
</script>
<template>
<div
class="space-wrapper"
:class="wrapperClasses"
:style="`gap: ${SpacingSize[spacing]}px`"
>
<slot />
</div>
</template>
<style lang="scss">
.space-wrapper {
@apply inline-flex;
@apply box-border;
&.space-direction-row {
@apply flex-row;
}
&.space-direction-column {
@apply flex-col;
}
&.space-align-center {
@apply items-center;
}
&.space-align-start {
@apply items-start;
}
&.space-align-end {
@apply items-end;
}
&.space-align-stretch {
@apply items-stretch;
}
}
</style>

View File

@ -0,0 +1,48 @@
import { describe, expect, it } from "vitest";
import { VSpace } from "../index";
import { mount } from "@vue/test-utils";
import { SpacingSize } from "../interface";
describe("Space", () => {
it("should render", function () {
expect(VSpace).toBeDefined();
});
it("should work with spacing prop", function () {
Object.keys(SpacingSize).forEach((key: string) => {
const wrapper = mount(VSpace, {
propsData: {
spacing: key,
},
});
expect(wrapper.attributes()["style"]).toContain(
`gap: ${SpacingSize[key]}px`
);
wrapper.unmount();
});
});
it("should work with direction prop", function () {
["row", "column"].forEach((direction: string) => {
const wrapper = mount(VSpace, {
propsData: {
direction: direction,
},
});
expect(wrapper.classes()).toContain(`space-direction-${direction}`);
wrapper.unmount();
});
});
it("should work with align prop", function () {
["center", "start", "end", "stretch"].forEach((align: string) => {
const wrapper = mount(VSpace, {
propsData: {
align: align,
},
});
expect(wrapper.classes()).toContain(`space-align-${align}`);
wrapper.unmount();
});
});
});

View File

@ -0,0 +1 @@
export { default as VSpace } from "./Space.vue";

View File

@ -0,0 +1,9 @@
export type Spacing = "xs" | "sm" | "md" | "lg";
export type Direction = "row" | "column";
export type Align = "start" | "end" | "center" | "stretch";
export const SpacingSize: Record<string, number> = {
xs: 10,
sm: 12,
md: 16,
lg: 20,
};

View File

@ -22,6 +22,8 @@ import IconSettings from "~icons/ri/settings-4-line";
import IconPlug from "~icons/ri/plug-2-line"; import IconPlug from "~icons/ri/plug-2-line";
// @ts-ignore // @ts-ignore
import IconEye from "~icons/ri/eye-line"; import IconEye from "~icons/ri/eye-line";
// @ts-ignore
import IconFolder from "~icons/ri/folder-2-line";
export { export {
IconDashboard, IconDashboard,
IconArrowRight, IconArrowRight,
@ -35,4 +37,5 @@ export {
IconSettings, IconSettings,
IconPlug, IconPlug,
IconEye, IconEye,
IconFolder,
}; };

View File

@ -10,6 +10,7 @@ import {
IconPlug, IconPlug,
IconSettings, IconSettings,
IconUserSettings, IconUserSettings,
IconFolder,
} from "@/core/icons"; } from "@/core/icons";
import type { Component } from "vue"; import type { Component } from "vue";
@ -57,7 +58,7 @@ export const menus: MenuGroupType[] = [
{ {
name: "附件", name: "附件",
path: "/attachment", path: "/attachment",
icon: IconDashboard, icon: IconFolder,
}, },
], ],
}, },

View File

@ -5,76 +5,89 @@
<h1 class="text-xl font-bold mb-2">Button</h1> <h1 class="text-xl font-bold mb-2">Button</h1>
<h2 class="mb-1">Type:</h2> <h2 class="mb-1">Type:</h2>
<div class="mb-3"> <div class="mb-3">
<VButton class="mr-2" type="primary">Primary</VButton> <VSpace>
<VButton class="mr-2" type="secondary">Secondary</VButton> <VButton type="primary">Primary</VButton>
<VButton class="mr-2" type="danger">Danger</VButton> <VButton type="secondary">Secondary</VButton>
<VButton type="default">Default</VButton> <VButton type="danger">Danger</VButton>
<VButton type="default">Default</VButton>
</VSpace>
</div> </div>
<h2 class="mb-1">Size:</h2> <h2 class="mb-1">Size:</h2>
<div class="mb-3"> <div class="mb-3">
<VButton class="mr-2" size="lg" type="secondary">Large</VButton> <VSpace>
<VButton class="mr-2" type="secondary"> Default</VButton> <VButton size="lg" type="secondary">Large</VButton>
<VButton class="mr-2" size="sm" type="secondary"> sm</VButton> <VButton type="secondary"> Default</VButton>
<VButton size="xs" type="secondary"> xs</VButton> <VButton size="sm" type="secondary"> sm</VButton>
<VButton size="xs" type="secondary"> xs</VButton>
</VSpace>
</div> </div>
<h2 class="mb-1">Circle:</h2> <h2 class="mb-1">Circle:</h2>
<div class="mb-3"> <div class="mb-3">
<VButton circle class="mr-2" size="lg" type="secondary"> lg</VButton> <VSpace>
<VButton circle class="mr-2" type="secondary"> d</VButton> <VButton circle size="lg" type="secondary"> lg</VButton>
<VButton circle class="mr-2" size="sm" type="secondary"> sm</VButton> <VButton circle type="secondary"> d</VButton>
<VButton circle size="xs" type="secondary"> xs</VButton> <VButton circle size="sm" type="secondary"> sm</VButton>
<VButton circle size="xs" type="secondary"> xs</VButton>
</VSpace>
</div> </div>
<h2 class="mb-1">Block:</h2> <h2 class="mb-1">Block:</h2>
<div class="mb-3"> <div class="mb-3">
<VButton block class="mb-2" type="primary"> Primary</VButton> <VSpace direction="column" class="w-full">
<VButton block class="mb-2" type="secondary"> Secondary</VButton> <VButton block type="primary"> Primary</VButton>
<VButton block type="danger"> Danger</VButton> <VButton block type="secondary"> Secondary</VButton>
<VButton block type="danger"> Danger</VButton>
</VSpace>
</div> </div>
<h2 class="mb-1">Disabled:</h2> <h2 class="mb-1">Disabled:</h2>
<div> <div>
<VButton class="mr-2" disabled type="primary"> Primary</VButton> <VSpace>
<VButton class="mr-2" disabled type="secondary"> Secondary</VButton> <VButton disabled type="primary"> Primary</VButton>
<VButton disabled type="danger"> Danger</VButton> <VButton disabled type="secondary"> Secondary</VButton>
<VButton disabled type="danger"> Danger</VButton>
</VSpace>
</div> </div>
<h2 class="mb-1">Loading:</h2> <h2 class="mb-1">Loading:</h2>
<div class="mb-3"> <div class="mb-3">
<VButton class="mr-2" type="primary" :loading="buttonLoading"> <VSpace>
Primary <VButton type="default" @click="buttonLoading = !buttonLoading">
</VButton> {{ buttonLoading ? "停止" : "启动" }}
<VButton class="mr-2" type="secondary" :loading="buttonLoading"> </VButton>
Secondary <VButton type="primary" :loading="buttonLoading"> Primary </VButton>
</VButton> <VButton type="secondary" :loading="buttonLoading">
<VButton class="mr-2" type="danger" :loading="buttonLoading"> Secondary
Danger </VButton>
</VButton> <VButton type="danger" :loading="buttonLoading"> Danger </VButton>
<VButton type="default" :loading="buttonLoading"> Default </VButton> <VButton type="default" :loading="buttonLoading"> Default </VButton>
</VSpace>
</div> </div>
<h2 class="mb-1">Icon:</h2> <h2 class="mb-1">Icon:</h2>
<div class="mb-3"> <div class="mb-3">
<VButton class="mr-2" size="lg" type="secondary"> <VSpace>
<template #icon> <VButton size="lg" type="secondary">
<IconSettings /> <template #icon>
</template> <IconSettings />
Large </template>
</VButton> Large
<VButton class="mr-2" type="secondary"> </VButton>
<template #icon> <VButton type="secondary">
<IconSettings /> <template #icon>
</template> <IconSettings />
Default </template>
</VButton> Default
<VButton class="mr-2" size="sm" type="secondary"> </VButton>
<template #icon> <VButton size="sm" type="secondary">
<IconSettings /> <template #icon>
</template> <IconSettings />
sm </template>
</VButton> sm
<VButton size="xs" type="secondary"> </VButton>
<template #icon> <VButton size="xs" type="secondary">
<IconSettings /> <template #icon>
</template> <IconSettings />
xs </template>
</VButton> xs
</VButton>
</VSpace>
</div> </div>
</section> </section>
<section class="box border-2 rounded p-2 mb-3"> <section class="box border-2 rounded p-2 mb-3">
@ -193,6 +206,44 @@
</span> </span>
</div> </div>
</section> </section>
<section class="box border-2 rounded p-2 mb-3">
<h1 class="text-xl font-bold mb-2">Space</h1>
<div class="mb-3">
<VRadio
v-for="(option, index) in ['row', 'column']"
:key="index"
v-model="spaceState.direction"
:label="option"
:value="option"
name="direction"
></VRadio>
<VRadio
v-for="(option, index) in ['start', 'center', 'end', 'stretch']"
:key="index"
v-model="spaceState.align"
:label="option"
:value="option"
name="align"
></VRadio>
<VRadio
v-for="(option, index) in ['xs', 'sm', 'md', 'lg']"
:key="index"
v-model="spaceState.spacing"
:label="option"
:value="option"
name="spacing"
></VRadio>
<VSpace
:direction="spaceState.direction"
:spacing="spaceState.spacing"
:align="spaceState.align"
>
<div>Control</div>
<VButton type="primary">确定</VButton>
<VButton>取消</VButton>
</VSpace>
</div>
</section>
</div> </div>
</FilledLayout> </FilledLayout>
</template> </template>
@ -206,7 +257,13 @@ import { VOption, VSelect } from "@/components/base/select";
import { VTextarea } from "@/components/base/textarea"; import { VTextarea } from "@/components/base/textarea";
import { VRadio, VRadioGroup } from "@/components/base/radio"; import { VRadio, VRadioGroup } from "@/components/base/radio";
import { VCheckbox, VCheckboxGroup } from "@/components/base/checkbox"; import { VCheckbox, VCheckboxGroup } from "@/components/base/checkbox";
import { ref } from "vue"; import { VSpace } from "@/components/base/space";
import { reactive, ref } from "vue";
import type {
Align,
Direction,
Spacing,
} from "@/components/base/space/interface";
const buttonLoading = ref(true); const buttonLoading = ref(true);
const inputValue = ref(); const inputValue = ref();
@ -215,6 +272,16 @@ const radioValue = ref("apple");
const checkboxValue = ref(false); const checkboxValue = ref(false);
const checkboxGroupValue = ref(["apple"]); const checkboxGroupValue = ref(["apple"]);
const spaceState = reactive<{
direction: Direction;
spacing: Spacing;
align: Align;
}>({
direction: "row",
spacing: "xs",
align: "center",
});
const selectData = [ const selectData = [
{ {
value: "1", value: "1",