From 9473f9dcb9e91a5a769c83b778b136b7d5a0cb26 Mon Sep 17 00:00:00 2001 From: DDDDD12138 <43703884+DDDDD12138@users.noreply.github.com> Date: Sat, 12 Apr 2025 06:17:11 +0800 Subject: [PATCH] fix(components): [tabs] ensure correct event order (#20384) --- .../components/tabs/__tests__/tabs.test.tsx | 110 ++++++++++++++++++ packages/components/tabs/src/tabs.tsx | 2 +- 2 files changed, 111 insertions(+), 1 deletion(-) diff --git a/packages/components/tabs/__tests__/tabs.test.tsx b/packages/components/tabs/__tests__/tabs.test.tsx index 381c0dbd19..6395329596 100644 --- a/packages/components/tabs/__tests__/tabs.test.tsx +++ b/packages/components/tabs/__tests__/tabs.test.tsx @@ -837,4 +837,114 @@ describe('Tabs.vue', () => { await flushPromises() expect(activeName.value).toBe('tab2') }) + + test('event order: tabClick -> update:modelValue -> tabChange', async () => { + const events: string[] = [] + const handleTabClick = vi.fn(() => { + events.push('tabClick') + }) + const handleUpdateModelValue = vi.fn(() => { + events.push('update:modelValue') + }) + const handleTabChange = vi.fn(() => { + events.push('tabChange') + }) + + const activeName = ref('tab1') + const wrapper = mount(() => ( + + + Tab 1 content + + + Tab 2 content + + + )) + + await nextTick() + + const navWrapper = wrapper.findComponent(TabNav) + const navItemsWrapper = navWrapper.findAll('.el-tabs__item') + + // Click on the second tab + await navItemsWrapper[1].trigger('click') + + // Verify all events have been called + expect(handleTabClick).toHaveBeenCalledTimes(1) + expect(handleUpdateModelValue).toHaveBeenCalledTimes(1) + expect(handleTabChange).toHaveBeenCalledTimes(1) + + // Verify the event call order + expect(events).toEqual(['tabClick', 'update:modelValue', 'tabChange']) + }) + + test('event order with beforeLeave: tabClick -> update:modelValue -> tabChange', async () => { + const events: string[] = [] + const handleTabClick = vi.fn(() => { + events.push('tabClick') + }) + const handleUpdateModelValue = vi.fn(() => { + events.push('update:modelValue') + }) + const handleTabChange = vi.fn(() => { + events.push('tabChange') + }) + + // Create a beforeLeave function that uses a timer + const beforeLeave = () => + new Promise((resolve) => setTimeout(resolve, 100)) + + const activeName = ref('tab1') + const wrapper = mount(() => ( + + + Tab 1 content + + + Tab 2 content + + + )) + + await nextTick() + + const navWrapper = wrapper.findComponent(TabNav) + const navItemsWrapper = navWrapper.findAll('.el-tabs__item') + + // Click on the second tab + await navItemsWrapper[1].trigger('click') + + // Verify only tabClick is called immediately + expect(handleTabClick).toHaveBeenCalledTimes(1) + expect(handleUpdateModelValue).toHaveBeenCalledTimes(0) + expect(handleTabChange).toHaveBeenCalledTimes(0) + expect(events).toEqual(['tabClick']) + + // Verify the model value has not been updated yet (waiting for timer) + expect(activeName.value).toBe('tab1') + + // Fast-forward time to trigger the setTimeout callback + await beforeLeave() + await flushPromises() + + // Verify all events have been called in the correct order + expect(handleUpdateModelValue).toHaveBeenCalledTimes(1) + expect(handleTabChange).toHaveBeenCalledTimes(1) + expect(events).toEqual(['tabClick', 'update:modelValue', 'tabChange']) + + // Verify the model value has been updated + expect(activeName.value).toBe('tab2') + }) }) diff --git a/packages/components/tabs/src/tabs.tsx b/packages/components/tabs/src/tabs.tsx index b1587f0df5..f12fd12495 100644 --- a/packages/components/tabs/src/tabs.tsx +++ b/packages/components/tabs/src/tabs.tsx @@ -150,8 +150,8 @@ const Tabs = defineComponent({ event: Event ) => { if (tab.props.disabled) return - setCurrentName(tabName, true) emit('tabClick', tab, event) + setCurrentName(tabName, true) } const handleTabRemove = (pane: TabsPaneContext, ev: Event) => {