From 8f983b5999ab51140ce8f071a826bc213da4d4ec Mon Sep 17 00:00:00 2001 From: hugo Date: Sun, 22 Feb 2026 07:50:52 +0100 Subject: [PATCH] fix: use titles also in OPML --- .../MenuEditorView/MenuEditorView.tsx | 25 ++++++++++--- .../components/MenuEditorView.test.tsx | 36 +++++++++++++++++++ 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/src/renderer/components/MenuEditorView/MenuEditorView.tsx b/src/renderer/components/MenuEditorView/MenuEditorView.tsx index 0798b94..c19a6de 100644 --- a/src/renderer/components/MenuEditorView/MenuEditorView.tsx +++ b/src/renderer/components/MenuEditorView/MenuEditorView.tsx @@ -226,12 +226,29 @@ export const MenuEditorView: React.FC = () => { ]); const metadataEntries = Object.entries(projectMetadata?.categoryMetadata ?? {}); - setCategoryTitlesByName(Object.fromEntries( + const categoryTitleMap = Object.fromEntries( metadataEntries.map(([name, metadata]) => [name, metadata.title?.trim() || name]), - )); + ); + setCategoryTitlesByName(categoryTitleMap); - setItems(menu.items); - setSelectedId(menu.items[0]?.id ?? null); + const normalizedItems = mapItems(menu.items, (item) => { + if (item.kind !== 'category-archive' || !item.categoryName) { + return item; + } + + const metadataTitle = categoryTitleMap[item.categoryName]?.trim(); + if (!metadataTitle || metadataTitle === item.title) { + return item; + } + + return { + ...item, + title: metadataTitle, + }; + }); + + setItems(normalizedItems); + setSelectedId(normalizedItems[0]?.id ?? null); } catch (error) { console.error('Failed to load menu:', error); showToast.error(tr('menuEditor.loadError')); diff --git a/tests/renderer/components/MenuEditorView.test.tsx b/tests/renderer/components/MenuEditorView.test.tsx index 88b904e..1999e6e 100644 --- a/tests/renderer/components/MenuEditorView.test.tsx +++ b/tests/renderer/components/MenuEditorView.test.tsx @@ -243,6 +243,42 @@ describe('MenuEditorView entry editor', () => { expect(screen.queryByText(/^news$/i)).not.toBeInTheDocument(); }); + it('saves existing category archive entries with metadata title while keeping category slug', async () => { + (window as any).electronAPI.menu.get = vi.fn().mockResolvedValue({ + items: [ + { + id: 'menu-home', + title: 'Home', + kind: 'home', + pageSlug: 'home', + children: [], + }, + { + id: 'cat-news', + title: 'news', + kind: 'category-archive', + categoryName: 'news', + children: [], + }, + ], + }); + + render(); + + await screen.findByText('Newsroom'); + const saveButton = screen.getByRole('button', { name: /^save menu$/i }); + fireEvent.click(saveButton); + + const saveMock = (window as any).electronAPI.menu.save; + expect(saveMock).toHaveBeenCalled(); + const payload = saveMock.mock.calls[0][0]; + + const categoryItem = payload.items.find((item: any) => item.id === 'cat-news'); + expect(categoryItem).toBeDefined(); + expect(categoryItem.title).toBe('Newsroom'); + expect(categoryItem.categoryName).toBe('news'); + }); + it('disables delete action when Home entry is selected', async () => { render();