From 1c93610499a30d0b7d6cb0337c247d7a1b987b75 Mon Sep 17 00:00:00 2001 From: harrywan Date: Tue, 2 Jun 2026 23:47:09 +0800 Subject: [PATCH] feat(switch): add loading prop (#914) --- .../switch/__tests__/switch-914.spec.ts | 73 +++++++++++++++++++ components/switch/switch.vue | 18 ++++- 2 files changed, 87 insertions(+), 4 deletions(-) create mode 100644 components/switch/__tests__/switch-914.spec.ts diff --git a/components/switch/__tests__/switch-914.spec.ts b/components/switch/__tests__/switch-914.spec.ts new file mode 100644 index 00000000..1b5de33d --- /dev/null +++ b/components/switch/__tests__/switch-914.spec.ts @@ -0,0 +1,73 @@ +import { mount } from '@vue/test-utils'; +import FSwitch from '../switch.vue'; + +const prefixCls = 'fes-switch'; + +describe('switch loading prop #914', () => { + test('loading: true shows the loading icon', async () => { + const wrapper = mount(FSwitch, { + props: { + modelValue: false, + loading: true, + }, + }); + + const loadingIcon = wrapper.find(`.${prefixCls}-loading`); + expect(loadingIcon.exists()).toBe(true); + }); + + test('loading: true blocks click toggle', async () => { + const wrapper = mount(FSwitch, { + props: { + modelValue: false, + loading: true, + }, + }); + + expect(wrapper.classes('is-checked')).toBe(false); + + await wrapper.trigger('click'); + + expect(wrapper.classes('is-checked')).toBe(false); + }); + + test('loading: true shows loading icon in checked state', async () => { + const wrapper = mount(FSwitch, { + props: { + modelValue: true, + loading: true, + }, + }); + + expect(wrapper.classes('is-checked')).toBe(true); + const loadingIcon = wrapper.find(`.${prefixCls}-loading`); + expect(loadingIcon.exists()).toBe(true); + }); + + test('loading: false does not show loading icon', async () => { + const wrapper = mount(FSwitch, { + props: { + modelValue: false, + loading: false, + }, + }); + + const loadingIcon = wrapper.find(`.${prefixCls}-loading`); + expect(loadingIcon.exists()).toBe(false); + }); + + test('normal toggle works when loading is false', async () => { + const wrapper = mount(FSwitch, { + props: { + modelValue: false, + loading: false, + }, + }); + + expect(wrapper.classes('is-checked')).toBe(false); + + await wrapper.trigger('click'); + + expect(wrapper.classes('is-checked')).toBe(true); + }); +}); \ No newline at end of file diff --git a/components/switch/switch.vue b/components/switch/switch.vue index 516aafb6..cac37c1e 100644 --- a/components/switch/switch.vue +++ b/components/switch/switch.vue @@ -55,6 +55,10 @@ export const switchProps = { type: String as PropType, default: 'normal', }, + loading: { + type: Boolean, + default: false, + }, } as const satisfies ComponentObjectPropsOptions; export type SwitchProps = ExtractPublicPropTypes; @@ -87,7 +91,10 @@ export default defineComponent({ } }); - const loadingRef = ref(false); + const beforeChangeLoadingRef = ref(false); + const loadingRef = computed( + () => props.loading || beforeChangeLoadingRef.value, + ); const handleChange = () => { ctx.emit(CHANGE_EVENT, currentValue.value); @@ -108,18 +115,21 @@ export default defineComponent({ if (innerDisabled.value) { return; } + if (loadingRef.value) { + return; + } if (isFunction(props.beforeChange)) { - loadingRef.value = true; + beforeChangeLoadingRef.value = true; try { const confirm = await props.beforeChange( currentValue.value, ); - loadingRef.value = false; + beforeChangeLoadingRef.value = false; if (confirm === false) { return; } } catch (e) { - loadingRef.value = false; + beforeChangeLoadingRef.value = false; return; } }