mirror of https://github.com/halo-dev/halo-admin
parent
6858ab3be0
commit
9815a30744
23
package.json
23
package.json
|
@ -7,20 +7,22 @@
|
|||
"build": "vue-tsc --noEmit && vite build",
|
||||
"preview": "vite preview --port 5050",
|
||||
"test:unit": "vitest --environment jsdom --run",
|
||||
"test:unit:watch": "vitest --environment jsdom --watch",
|
||||
"test:unit:coverage": "vitest run --environment jsdom --coverage",
|
||||
"test:e2e": "start-server-and-test preview http://127.0.0.1:5050/ 'cypress open'",
|
||||
"test:e2e:ci": "start-server-and-test preview http://127.0.0.1:5050/ 'cypress run'",
|
||||
"typecheck": "vue-tsc --noEmit -p tsconfig.vitest.json --composite false",
|
||||
"typecheck": "vue-tsc --noEmit -p tsconfig.app.json --composite false",
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
|
||||
"story:dev": "histoire dev",
|
||||
"story:build": "histoire build"
|
||||
},
|
||||
"dependencies": {
|
||||
"pinia": "^2.0.11",
|
||||
"pinia": "^2.0.12",
|
||||
"vue": "^3.2.31",
|
||||
"vue-router": "^4.0.13"
|
||||
"vue-router": "^4.0.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rushstack/eslint-patch": "^1.1.0",
|
||||
"@rushstack/eslint-patch": "^1.1.1",
|
||||
"@types/jsdom": "^16.2.14",
|
||||
"@types/node": "^16.11.26",
|
||||
"@vitejs/plugin-vue": "^2.2.4",
|
||||
|
@ -29,16 +31,17 @@
|
|||
"@vue/eslint-config-typescript": "^10.0.0",
|
||||
"@vue/test-utils": "^2.0.0-rc.18",
|
||||
"@vue/tsconfig": "^0.1.3",
|
||||
"autoprefixer": "^10.4.2",
|
||||
"cypress": "^9.5.1",
|
||||
"eslint": "^8.10.0",
|
||||
"autoprefixer": "^10.4.4",
|
||||
"c8": "^7.11.0",
|
||||
"cypress": "^9.5.2",
|
||||
"eslint": "^8.11.0",
|
||||
"eslint-plugin-cypress": "^2.12.1",
|
||||
"eslint-plugin-vue": "^8.5.0",
|
||||
"histoire": "^0.1.3",
|
||||
"histoire": "^0.2.0",
|
||||
"husky": "^7.0.4",
|
||||
"jsdom": "^19.0.0",
|
||||
"postcss": "^8.4.8",
|
||||
"prettier": "^2.5.1",
|
||||
"postcss": "^8.4.12",
|
||||
"prettier": "^2.6.0",
|
||||
"sass": "^1.49.9",
|
||||
"start-server-and-test": "^1.14.0",
|
||||
"tailwindcss": "^3.0.23",
|
||||
|
|
528
pnpm-lock.yaml
528
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
@ -2,10 +2,39 @@
|
|||
<Story title="Button">
|
||||
<Variant title="Type">
|
||||
<template #default>
|
||||
<div class="themeable-default">
|
||||
<VButton type="primary"> Hello world </VButton>
|
||||
<VButton type="secondary"> Hello world </VButton>
|
||||
</div>
|
||||
<VButton type="primary"> primary </VButton>
|
||||
<VButton type="secondary"> secondary </VButton>
|
||||
<VButton type="danger"> danger </VButton>
|
||||
</template>
|
||||
</Variant>
|
||||
<Variant title="Size">
|
||||
<template #default>
|
||||
<VButton type="secondary" size="lg"> lg </VButton>
|
||||
<VButton type="secondary"> default </VButton>
|
||||
<VButton type="secondary" size="sm"> sm </VButton>
|
||||
<VButton type="secondary" size="xs"> xs </VButton>
|
||||
</template>
|
||||
</Variant>
|
||||
<Variant title="Circle">
|
||||
<template #default>
|
||||
<VButton type="secondary" size="lg" circle> lg </VButton>
|
||||
<VButton type="secondary" circle> d </VButton>
|
||||
<VButton type="secondary" size="sm" circle> sm </VButton>
|
||||
<VButton type="secondary" size="xs" circle> xs </VButton>
|
||||
</template>
|
||||
</Variant>
|
||||
<Variant title="Block">
|
||||
<template #default>
|
||||
<VButton type="primary" block> primary </VButton>
|
||||
<VButton type="secondary" block> secondary </VButton>
|
||||
<VButton type="danger" block> danger </VButton>
|
||||
</template>
|
||||
</Variant>
|
||||
<Variant title="Disabled">
|
||||
<template #default>
|
||||
<VButton type="primary" disabled> primary </VButton>
|
||||
<VButton type="secondary" disabled> secondary </VButton>
|
||||
<VButton type="danger" disabled> danger </VButton>
|
||||
</template>
|
||||
</Variant>
|
||||
</Story>
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
<template>
|
||||
<button :class="[`btn-${size}`, `btn-${type}`]" class="btn">
|
||||
<button
|
||||
class="btn"
|
||||
:class="classes"
|
||||
:disabled="disabled"
|
||||
@click="handleClick"
|
||||
>
|
||||
<slot />
|
||||
</button>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import type { PropType } from "vue";
|
||||
import type { Size, Type } from "@/components/base/button/interface";
|
||||
import { computed } from "vue";
|
||||
|
||||
defineProps({
|
||||
const props = defineProps({
|
||||
type: {
|
||||
type: String as PropType<Type>,
|
||||
default: "default",
|
||||
|
@ -16,26 +22,124 @@ defineProps({
|
|||
type: String as PropType<Size>,
|
||||
default: "md",
|
||||
},
|
||||
circle: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
block: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style>
|
||||
.btn {
|
||||
border-radius: 2px;
|
||||
@apply cursor-pointer;
|
||||
@apply border-none;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
@apply bg-themeable-secondary;
|
||||
@apply text-white;
|
||||
const emit = defineEmits(["click"]);
|
||||
|
||||
const classes = computed(() => {
|
||||
return [
|
||||
`btn-${props.size}`,
|
||||
`btn-${props.type}`,
|
||||
{ "btn-circle": props.circle },
|
||||
{ "btn-block": props.block },
|
||||
];
|
||||
});
|
||||
|
||||
function handleClick() {
|
||||
if (props.disabled || props.loading) return;
|
||||
emit("click");
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.btn {
|
||||
border-radius: 4px;
|
||||
@apply inline-flex;
|
||||
@apply flex-shrink-0;
|
||||
@apply cursor-pointer;
|
||||
@apply select-none;
|
||||
@apply flex-wrap;
|
||||
@apply items-center;
|
||||
@apply justify-center;
|
||||
@apply transition-all;
|
||||
@apply text-center;
|
||||
@apply text-sm;
|
||||
@apply no-underline;
|
||||
@apply h-9;
|
||||
@apply px-4;
|
||||
@apply outline-0;
|
||||
|
||||
&:hover {
|
||||
@apply opacity-90;
|
||||
}
|
||||
|
||||
&:active {
|
||||
@apply opacity-100;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
@apply opacity-50;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
@apply bg-themeable-primary;
|
||||
@apply text-black;
|
||||
background: #4ccba0;
|
||||
@apply text-white;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: #0e1731;
|
||||
@apply text-white;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: #d71d1d;
|
||||
@apply text-white;
|
||||
}
|
||||
|
||||
.btn-block {
|
||||
@apply w-full;
|
||||
@apply block;
|
||||
}
|
||||
|
||||
.btn-lg {
|
||||
@apply h-11;
|
||||
@apply px-5;
|
||||
@apply text-lg;
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
@apply h-7;
|
||||
@apply px-3;
|
||||
@apply text-xs;
|
||||
}
|
||||
|
||||
.btn-xs {
|
||||
@apply h-6;
|
||||
@apply px-2;
|
||||
@apply text-xs;
|
||||
}
|
||||
|
||||
.btn-circle {
|
||||
@apply w-9;
|
||||
@apply p-0;
|
||||
@apply rounded-full;
|
||||
}
|
||||
|
||||
.btn-lg.btn-circle {
|
||||
@apply w-11;
|
||||
}
|
||||
|
||||
.btn-sm.btn-circle {
|
||||
@apply w-7;
|
||||
}
|
||||
|
||||
.btn-xs.btn-circle {
|
||||
@apply w-6;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
import { describe, expect, it, vi } from "vitest";
|
||||
import { VButton } from "../index";
|
||||
import { mount } from "@vue/test-utils";
|
||||
|
||||
describe("Button", () => {
|
||||
it("should render", () => {
|
||||
expect(mount(VButton).html()).contains("button");
|
||||
});
|
||||
|
||||
it("should render with text", () => {
|
||||
expect(mount(VButton, { slots: { default: "Hello Halo" } }).text()).toBe(
|
||||
"Hello Halo"
|
||||
);
|
||||
});
|
||||
|
||||
it("should work with type prop", () => {
|
||||
// default button type is default
|
||||
expect(mount(VButton).find(".btn").classes()).toContain("btn-default");
|
||||
|
||||
["primary", "secondary", "danger"].forEach((type) => {
|
||||
const button = mount(VButton, { props: { type } });
|
||||
expect(button.find(".btn").classes()).toContain(`btn-${type}`);
|
||||
button.unmount();
|
||||
});
|
||||
});
|
||||
|
||||
it("should work with size prop", async () => {
|
||||
// default button size is md
|
||||
expect(mount(VButton).find(".btn").classes()).toContain("btn-md");
|
||||
|
||||
["lg", "sm", "xs"].forEach((size) => {
|
||||
const button = mount(VButton, { props: { size } });
|
||||
expect(button.find(".btn").classes()).toContain(`btn-${size}`);
|
||||
button.unmount();
|
||||
});
|
||||
});
|
||||
|
||||
it("should work with circle prop", async () => {
|
||||
const button = mount(VButton);
|
||||
|
||||
// default: false
|
||||
expect(button.find(".btn").classes()).not.toContain("btn-circle");
|
||||
|
||||
await button.setProps({ circle: true });
|
||||
expect(button.find(".btn").classes()).toContain("btn-circle");
|
||||
});
|
||||
|
||||
it("should work with block prop", async () => {
|
||||
const button = mount(VButton);
|
||||
|
||||
// default: false
|
||||
expect(button.find(".btn").classes()).not.toContain("btn-block");
|
||||
|
||||
await button.setProps({ block: true });
|
||||
expect(button.find(".btn").classes()).toContain("btn-block");
|
||||
});
|
||||
|
||||
it("should work with disabled prop", async () => {
|
||||
const onClick = vi.fn(() => 1);
|
||||
|
||||
// default: false
|
||||
const button = mount(VButton, {
|
||||
emits: { click: onClick },
|
||||
});
|
||||
|
||||
await button.trigger("click");
|
||||
expect(onClick).toHaveBeenCalled();
|
||||
|
||||
onClick.mockReset();
|
||||
await button.setProps({ disabled: true });
|
||||
await button.trigger("click");
|
||||
expect(onClick).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue