mirror of https://github.com/halo-dev/halo
feat: add loading state for switch component (#4688)
#### What type of PR is this? /area console /kind feature /milestone 2.10.x #### What this PR does / why we need it: VSwitch 组件支持传入 loading 属性以显示加载状态。 此外,此 PR 为插件启动/停止的开关适配了这个特性用于测试。 <img width="460" alt="image" src="https://github.com/halo-dev/halo/assets/21301288/b78221fe-6b53-4f8c-ba00-6cea2c45b5de"> #### Which issue(s) this PR fixes: Fixes #4687 #### Special notes for your reviewer: 测试插件启动/停止时是否显示加载状态 #### Does this PR introduce a user-facing change? ```release-note Console 端的 VSwitch 组件支持传入 loading 属性以显示加载状态。 ```pull/4680/head
parent
b2d7221316
commit
df22b4b5ea
|
@ -3,10 +3,12 @@ const props = withDefaults(
|
|||
defineProps<{
|
||||
modelValue?: boolean;
|
||||
disabled?: boolean;
|
||||
loading?: boolean;
|
||||
}>(),
|
||||
{
|
||||
modelValue: false,
|
||||
disabled: false,
|
||||
loading: false,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -16,7 +18,7 @@ const emit = defineEmits<{
|
|||
}>();
|
||||
|
||||
const handleChange = () => {
|
||||
if (props.disabled) return;
|
||||
if (props.disabled || props.loading) return;
|
||||
|
||||
emit("update:modelValue", !props.modelValue);
|
||||
emit("change", !props.modelValue);
|
||||
|
@ -28,13 +30,13 @@ const handleChange = () => {
|
|||
:class="{
|
||||
'bg-gray-200': !modelValue,
|
||||
'!bg-primary': modelValue,
|
||||
'switch-disabled': disabled,
|
||||
'switch-disabled': disabled || loading,
|
||||
}"
|
||||
aria-checked="false"
|
||||
class="switch-inner"
|
||||
role="switch"
|
||||
type="button"
|
||||
:disabled="disabled"
|
||||
:disabled="disabled || loading"
|
||||
@click="handleChange"
|
||||
>
|
||||
<span
|
||||
|
@ -45,7 +47,28 @@ const handleChange = () => {
|
|||
aria-hidden="true"
|
||||
class="switch-indicator"
|
||||
>
|
||||
<slot name="icon" />
|
||||
<svg
|
||||
v-if="loading"
|
||||
class="animate-spin"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<circle
|
||||
class="opacity-0"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
stroke-width="4"
|
||||
></circle>
|
||||
<path
|
||||
class="opacity-30"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</svg>
|
||||
<slot v-else name="icon" />
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -1,9 +1,25 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { VSwitch } from "../index";
|
||||
import { mount } from "@vue/test-utils";
|
||||
import { mount, shallowMount } from "@vue/test-utils";
|
||||
|
||||
describe("Switch", () => {
|
||||
it("should render", () => {
|
||||
expect(mount(VSwitch)).toBeDefined();
|
||||
});
|
||||
|
||||
it("emits the correct events when clicked", () => {
|
||||
const wrapper = shallowMount(VSwitch);
|
||||
wrapper.find("button").trigger("click");
|
||||
expect(wrapper.emitted("update:modelValue")).toBeTruthy();
|
||||
expect(wrapper.emitted("change")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("does not emit events when disabled or loading", () => {
|
||||
const wrapper = shallowMount(VSwitch, {
|
||||
props: { disabled: true, loading: true },
|
||||
});
|
||||
wrapper.find("button").trigger("click");
|
||||
expect(wrapper.emitted("update:modelValue")).toBeFalsy();
|
||||
expect(wrapper.emitted("change")).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -21,7 +21,7 @@ const { changingStatus, changeStatus } = usePluginLifeCycle(plugin);
|
|||
<div class="flex items-center">
|
||||
<VSwitch
|
||||
:model-value="plugin.spec.enabled"
|
||||
:disabled="changingStatus"
|
||||
:loading="changingStatus"
|
||||
@click="changeStatus"
|
||||
/>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue