From 585a4cedbfd80d96df13ae44341ec4a47156cd5b Mon Sep 17 00:00:00 2001 From: Danny White <3104761+dnywh@users.noreply.github.com> Date: Wed, 25 Mar 2026 19:03:17 +1100 Subject: [PATCH] fix: keep recent items labels in sync after renames (#44155) ## What kind of change does this PR introduce? Bug fix. ## What is the current behavior? The `Recent items` list shown in the table editor empty-state can keep showing an old label after a table or SQL snippet is renamed. That UI is mounted from: - `/project/[ref]/editor` - `/project/[ref]/editor/new` ## What is the new behavior? - Re-adding an existing recent item now refreshes its label and metadata instead of only bumping the timestamp. - Updating an open tab label now also updates the matching recent item, so rename flows stay aligned with the recent-items list. - The fix is applied in the shared tabs store, so table and SQL editor tab state stay consistent even though the visible `Recent items` UI currently appears in the table editor empty-state. - Adds a small store-level regression test for both cases. | Before | After | | --- | --- | | test Table Editor Mallet Toolshed
Supabase-DA49998A-FE09-4197-8EE7-5D5366FABDC7 | test Table Editor Mallet
Toolshed Supabase-8F2B6CC9-0721-4348-A094-EE94D6919B08 | --- apps/studio/state/tabs.test.ts | 62 ++++++++++++++++++++++++++++++++++ apps/studio/state/tabs.tsx | 21 ++++++++++-- 2 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 apps/studio/state/tabs.test.ts diff --git a/apps/studio/state/tabs.test.ts b/apps/studio/state/tabs.test.ts new file mode 100644 index 00000000000..21915e58d46 --- /dev/null +++ b/apps/studio/state/tabs.test.ts @@ -0,0 +1,62 @@ +import { beforeEach, describe, expect, it } from 'vitest' + +import { createTabsState } from './tabs' +import { ENTITY_TYPE } from '@/data/entity-types/entity-type-constants' + +describe('tabs recent items', () => { + beforeEach(() => { + localStorage.clear() + }) + + it('refreshes an existing recent item label when the tab is re-added', () => { + const store = createTabsState('default') + + store.addRecentItem({ + id: 'r-1', + type: ENTITY_TYPE.TABLE, + label: 'tasks', + metadata: { + schema: 'public', + name: 'tasks', + tableId: 1, + }, + }) + + store.addRecentItem({ + id: 'r-1', + type: ENTITY_TYPE.TABLE, + label: 'routines', + metadata: { + schema: 'public', + name: 'routines', + tableId: 1, + }, + }) + + expect(store.recentItems).toHaveLength(1) + expect(store.recentItems[0].label).toBe('routines') + expect(store.recentItems[0].metadata?.name).toBe('routines') + }) + + it('keeps recent items aligned when an open tab label changes', () => { + const store = createTabsState('default') + + store.addTab({ + id: 'r-1', + type: ENTITY_TYPE.TABLE, + label: 'tasks', + metadata: { + schema: 'public', + name: 'tasks', + tableId: 1, + }, + isPreview: false, + }) + + store.updateTab('r-1', { label: 'routines' }) + + expect(store.tabsMap['r-1'].label).toBe('routines') + expect(store.recentItems[0].label).toBe('routines') + expect(store.recentItems[0].metadata?.name).toBe('routines') + }) +}) diff --git a/apps/studio/state/tabs.tsx b/apps/studio/state/tabs.tsx index 2ded8c04b08..d757d1c61ae 100644 --- a/apps/studio/state/tabs.tsx +++ b/apps/studio/state/tabs.tsx @@ -110,7 +110,21 @@ function getSavedTabs(ref: string) { } } -function createTabsState(projectRef: string) { +const getRecentItemLabel = (tab: Pick) => + tab.label || tab.metadata?.name || 'Untitled' + +const syncRecentItemWithTab = (item: RecentItem, tab: Pick) => { + const nextLabel = getRecentItemLabel(tab) + + item.label = nextLabel + item.metadata = { + ...item.metadata, + ...tab.metadata, + name: nextLabel, + } +} + +export function createTabsState(projectRef: string) { const recentItems = getSavedRecentItems(projectRef) const { openTabs, activeTab, tabsMap, previewTabId } = getSavedTabs(projectRef) @@ -125,6 +139,7 @@ function createTabsState(projectRef: string) { if (existingItem) { // If it exists, update its timestamp existingItem.timestamp = Date.now() + syncRecentItemWithTab(existingItem, tab) return // Exit the function } @@ -132,7 +147,7 @@ function createTabsState(projectRef: string) { const recentItem: RecentItem = { id: tab.id, // Set the ID type: tab.type, // Set the type - label: tab.label || 'Untitled', // Set the label or default to 'Untitled' + label: getRecentItemLabel(tab), // Set the label or default to 'Untitled' timestamp: Date.now(), // Set the current timestamp metadata: tab.metadata, // Set the metadata } @@ -211,6 +226,8 @@ function createTabsState(projectRef: string) { if (!!store.tabsMap[id]) { if ('label' in updates) { store.tabsMap[id].label = updates.label + const recentItem = store.recentItems.find((item) => item.id === id) + if (recentItem) syncRecentItemWithTab(recentItem, store.tabsMap[id]) } if ('scrollTop' in updates && store.tabsMap[id].metadata) { store.tabsMap[id].metadata.scrollTop = updates.scrollTop