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) => {