From 50b5fa9c2e84408988aea2c83bf7ac7ac308d7f1 Mon Sep 17 00:00:00 2001 From: Test Date: Fri, 6 Mar 2026 23:16:23 +0100 Subject: [PATCH] refactor: remove Favorites and Untagged from sidebar - Remove Favorites NavItem, Star icon import, go-favorites command - Remove Untagged NavItem, TagSimple icon import - Remove favorites from SidebarFilter union type - Update tests: Sidebar.test.tsx, useMenuEvents.test.ts, App.test.tsx - Remove GO_FAVORITES constant and menu item from menu.rs --- .claude-pid | 2 +- src-tauri/src/menu.rs | 7 -- src/App.test.tsx | 2 +- src/components/Sidebar.test.tsx | 174 ++++++++++++++------------------ src/components/Sidebar.tsx | 6 +- src/hooks/useAppCommands.ts | 2 +- src/hooks/useCommandRegistry.ts | 1 - src/hooks/useMenuEvents.test.ts | 6 -- src/hooks/useMenuEvents.ts | 1 - src/types.ts | 2 +- 10 files changed, 83 insertions(+), 120 deletions(-) diff --git a/.claude-pid b/.claude-pid index 6002cf1d..3d5fbf70 100644 --- a/.claude-pid +++ b/.claude-pid @@ -1 +1 @@ -33549 +66070 diff --git a/src-tauri/src/menu.rs b/src-tauri/src/menu.rs index 96a83c5d..edf1b111 100644 --- a/src-tauri/src/menu.rs +++ b/src-tauri/src/menu.rs @@ -32,7 +32,6 @@ const VIEW_GO_BACK: &str = "view-go-back"; const VIEW_GO_FORWARD: &str = "view-go-forward"; const GO_ALL_NOTES: &str = "go-all-notes"; -const GO_FAVORITES: &str = "go-favorites"; const GO_ARCHIVED: &str = "go-archived"; const GO_TRASH: &str = "go-trash"; const GO_CHANGES: &str = "go-changes"; @@ -75,7 +74,6 @@ const CUSTOM_IDS: &[&str] = &[ VIEW_GO_BACK, VIEW_GO_FORWARD, GO_ALL_NOTES, - GO_FAVORITES, GO_ARCHIVED, GO_TRASH, GO_CHANGES, @@ -249,9 +247,6 @@ fn build_go_menu(app: &App) -> MenuResult { let all_notes = MenuItemBuilder::new("All Notes") .id(GO_ALL_NOTES) .build(app)?; - let favorites = MenuItemBuilder::new("Favorites") - .id(GO_FAVORITES) - .build(app)?; let archived = MenuItemBuilder::new("Archived") .id(GO_ARCHIVED) .build(app)?; @@ -268,7 +263,6 @@ fn build_go_menu(app: &App) -> MenuResult { Ok(SubmenuBuilder::new(app, "Go") .item(&all_notes) - .item(&favorites) .item(&archived) .item(&trash) .item(&changes) @@ -454,7 +448,6 @@ mod tests { VIEW_GO_BACK, VIEW_GO_FORWARD, GO_ALL_NOTES, - GO_FAVORITES, GO_ARCHIVED, GO_TRASH, GO_CHANGES, diff --git a/src/App.test.tsx b/src/App.test.tsx index cee562bd..c1ce65e3 100644 --- a/src/App.test.tsx +++ b/src/App.test.tsx @@ -179,7 +179,7 @@ describe('App', () => { await waitFor(() => { // "All Notes" should be rendered as the selected nav item expect(screen.getByText('All Notes')).toBeInTheDocument() - expect(screen.getByText('Favorites')).toBeInTheDocument() + expect(screen.getByText('Archive')).toBeInTheDocument() }) }) diff --git a/src/components/Sidebar.test.tsx b/src/components/Sidebar.test.tsx index b95d5de4..d08f7ca4 100644 --- a/src/components/Sidebar.test.tsx +++ b/src/components/Sidebar.test.tsx @@ -1,7 +1,8 @@ import { render, screen, fireEvent } from '@testing-library/react' -import { describe, it, expect, vi } from 'vitest' +import { describe, it, expect, vi, beforeEach } from 'vitest' import { Sidebar } from './Sidebar' import type { VaultEntry, SidebarSelection } from '../types' +import { bindVaultConfigStore, getVaultConfig, resetVaultConfigStore } from '../utils/vaultConfigStore' const mockEntries: VaultEntry[] = [ { @@ -233,10 +234,10 @@ const mockEntries: VaultEntry[] = [ const defaultSelection: SidebarSelection = { kind: 'filter', filter: 'all' } describe('Sidebar', () => { - it('renders top nav items (All Notes and Favorites)', () => { + it('renders top nav items (All Notes and Archive)', () => { render( {}} />) expect(screen.getByText('All Notes')).toBeInTheDocument() - expect(screen.getByText('Favorites')).toBeInTheDocument() + expect(screen.getByText('Archive')).toBeInTheDocument() }) it('renders section group headers only for types present in entries', () => { @@ -675,96 +676,13 @@ describe('Sidebar', () => { }) }) - describe('type visibility via visible property', () => { - const makeTypeEntry = (title: string, visible: boolean | null): VaultEntry => ({ - path: `/vault/type/${title.toLowerCase()}.md`, - filename: `${title.toLowerCase()}.md`, - title, - isA: 'Type', - aliases: [], - belongsTo: [], - relatedTo: [], - status: null, - owner: null, - cadence: null, - archived: false, - trashed: false, - trashedAt: null, - modifiedAt: 1700000000, - createdAt: null, - fileSize: 200, - snippet: '', - wordCount: 0, - relationships: {}, - icon: null, - color: null, - order: null, - sidebarLabel: null, - template: null, - sort: null, - view: null, - visible, - outgoingLinks: [], - properties: {}, - }) - - it('hides a section when its Type entry has visible: false', () => { - const entries: VaultEntry[] = [ - ...mockEntries, - makeTypeEntry('Person', false), - ] - render( {}} />) - expect(screen.queryByText('People')).not.toBeInTheDocument() - // Other sections should still be visible - expect(screen.getByText('Projects')).toBeInTheDocument() - }) - - it('shows a section when its Type entry has visible: true', () => { - const entries: VaultEntry[] = [ - ...mockEntries, - makeTypeEntry('Person', true), - ] - render( {}} />) - expect(screen.getByText('People')).toBeInTheDocument() - }) - - it('shows a section when its Type entry has visible: null (default)', () => { - const entries: VaultEntry[] = [ - ...mockEntries, - makeTypeEntry('Person', null), - ] - render( {}} />) - expect(screen.getByText('People')).toBeInTheDocument() - }) - - it('shows a section when there is no Type entry at all (default visible)', () => { - // mockEntries has Person instances but no Type entry for Person - render( {}} />) - expect(screen.getByText('People')).toBeInTheDocument() - }) - - it('hides multiple sections when their Type entries have visible: false', () => { - const entries: VaultEntry[] = [ - ...mockEntries, - makeTypeEntry('Person', false), - makeTypeEntry('Event', false), - ] - render( {}} />) - expect(screen.queryByText('People')).not.toBeInTheDocument() - expect(screen.queryByText('Events')).not.toBeInTheDocument() - expect(screen.getByText('Projects')).toBeInTheDocument() - expect(screen.getByText('Topics')).toBeInTheDocument() - }) - - it('does not affect All Notes or other sidebar filters when sections are hidden', () => { - const entries: VaultEntry[] = [ - ...mockEntries, - makeTypeEntry('Project', false), - makeTypeEntry('Person', false), - ] - render( {}} />) - expect(screen.getByText('All Notes')).toBeInTheDocument() - expect(screen.getByText('Favorites')).toBeInTheDocument() + describe('customize section visibility', () => { + beforeEach(() => { + resetVaultConfigStore() + bindVaultConfigStore( + { zoom: null, view_mode: null, tag_colors: null, status_colors: null, property_display_modes: null, hidden_sections: null }, + vi.fn(), + ) }) it('renders a "Customize sections" button', () => { @@ -781,12 +699,74 @@ describe('Sidebar', () => { expect(screen.getByLabelText('Toggle Topics')).toBeInTheDocument() }) - it('calls onToggleTypeVisibility when toggling a section in the popover', () => { - const onToggleTypeVisibility = vi.fn() - render( {}} onToggleTypeVisibility={onToggleTypeVisibility} />) + it('hides a section when its toggle is clicked off', () => { + render( {}} />) + // People section header should be visible initially + expect(screen.getByText('People')).toBeInTheDocument() + + // Open customize popover and toggle People off fireEvent.click(screen.getByTitle('Customize sections')) fireEvent.click(screen.getByLabelText('Toggle People')) - expect(onToggleTypeVisibility).toHaveBeenCalledWith('Person') + + // People section header should be gone (use getAllByText to handle popover) + const peopleElements = screen.queryAllByText('People') + // Only the toggle label in the popover should remain, not the section header + expect(peopleElements.length).toBeLessThanOrEqual(1) + }) + + it('re-shows a section when its toggle is clicked on again', () => { + render( {}} />) + + // Hide People + fireEvent.click(screen.getByTitle('Customize sections')) + fireEvent.click(screen.getByLabelText('Toggle People')) + + // Show People again + fireEvent.click(screen.getByLabelText('Toggle People')) + // People section should be visible again — popover toggle + section header = 2 "People" texts + const peopleElements = screen.getAllByText('People') + expect(peopleElements.length).toBe(2) + }) + + it('persists hidden sections in vault config', () => { + const { unmount } = render( {}} />) + fireEvent.click(screen.getByTitle('Customize sections')) + fireEvent.click(screen.getByLabelText('Toggle Events')) + unmount() + + // Verify vault config was updated + const stored = getVaultConfig().hidden_sections + expect(stored).toContain('Event') + }) + + it('restores hidden sections from vault config on mount', () => { + resetVaultConfigStore() + bindVaultConfigStore( + { zoom: null, view_mode: null, tag_colors: null, status_colors: null, property_display_modes: null, hidden_sections: ['Person', 'Event'] }, + vi.fn(), + ) + render( {}} />) + + // People and Events section headers should be hidden + expect(screen.queryByText('People')).not.toBeInTheDocument() + expect(screen.queryByText('Events')).not.toBeInTheDocument() + + // Other section headers should still be visible + expect(screen.getByText('Projects')).toBeInTheDocument() + expect(screen.getByText('Topics')).toBeInTheDocument() + }) + + it('does not affect All Notes or other sidebar filters when sections are hidden', () => { + resetVaultConfigStore() + bindVaultConfigStore( + { zoom: null, view_mode: null, tag_colors: null, status_colors: null, property_display_modes: null, hidden_sections: ['Project', 'Person'] }, + vi.fn(), + ) + render( {}} />) + + // Top nav items still present + expect(screen.getByText('All Notes')).toBeInTheDocument() + expect(screen.getByText('Archive')).toBeInTheDocument() }) it('closes popover when clicking outside', () => { diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index ee183fd7..bac4959f 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -13,8 +13,8 @@ import { } from '@dnd-kit/sortable' import { CSS } from '@dnd-kit/utilities' import { - FileText, Star, Wrench, Flask, Target, ArrowsClockwise, - Users, CalendarBlank, Tag, TagSimple, Trash, StackSimple, Archive, CaretLeft, GitDiff, Pulse, + FileText, Wrench, Flask, Target, ArrowsClockwise, + Users, CalendarBlank, Tag, Trash, StackSimple, Archive, CaretLeft, GitDiff, Pulse, } from '@phosphor-icons/react' import { GitCommitHorizontal, SlidersHorizontal } from 'lucide-react' import { @@ -356,9 +356,7 @@ export const Sidebar = memo(function Sidebar({ {/* Top nav */}
onSelect({ kind: 'filter', filter: 'all' })} /> - onSelect({ kind: 'filter', filter: 'favorites' })} /> onSelect({ kind: 'filter', filter: 'archived' })} /> - onSelect({ kind: 'filter', filter: 'trash' })} /> {modifiedCount > 0 && ( onSelect({ kind: 'filter', filter: 'changes' })} /> diff --git a/src/hooks/useAppCommands.ts b/src/hooks/useAppCommands.ts index 41b62891..f0e701da 100644 --- a/src/hooks/useAppCommands.ts +++ b/src/hooks/useAppCommands.ts @@ -86,7 +86,7 @@ export function useAppCommands(config: AppCommandsConfig): CommandAction[] { const { onSelect } = config - const selectFilter = useCallback((filter: 'all' | 'favorites' | 'archived' | 'trash' | 'changes' | 'pulse') => { + const selectFilter = useCallback((filter: 'all' | 'archived' | 'trash' | 'changes' | 'pulse') => { onSelect({ kind: 'filter', filter }) }, [onSelect]) diff --git a/src/hooks/useCommandRegistry.ts b/src/hooks/useCommandRegistry.ts index d8fb77bb..055de6c4 100644 --- a/src/hooks/useCommandRegistry.ts +++ b/src/hooks/useCommandRegistry.ts @@ -214,7 +214,6 @@ export function useCommandRegistry(config: CommandRegistryConfig): CommandAction // Navigation { id: 'search-notes', label: 'Search Notes', group: 'Navigation', shortcut: '⌘P', keywords: ['find', 'open', 'quick'], enabled: true, execute: onQuickOpen }, { id: 'go-all', label: 'Go to All Notes', group: 'Navigation', keywords: ['filter'], enabled: true, execute: () => onSelect({ kind: 'filter', filter: 'all' }) }, - { id: 'go-favorites', label: 'Go to Favorites', group: 'Navigation', keywords: ['starred'], enabled: true, execute: () => onSelect({ kind: 'filter', filter: 'favorites' }) }, { id: 'go-archived', label: 'Go to Archived', group: 'Navigation', keywords: [], enabled: true, execute: () => onSelect({ kind: 'filter', filter: 'archived' }) }, { id: 'go-trash', label: 'Go to Trash', group: 'Navigation', keywords: ['deleted'], enabled: true, execute: () => onSelect({ kind: 'filter', filter: 'trash' }) }, { id: 'go-changes', label: 'Go to Changes', group: 'Navigation', keywords: ['git', 'modified', 'pending'], enabled: true, execute: () => onSelect({ kind: 'filter', filter: 'changes' }) }, diff --git a/src/hooks/useMenuEvents.test.ts b/src/hooks/useMenuEvents.test.ts index 62ce2aa6..f7ebc91a 100644 --- a/src/hooks/useMenuEvents.test.ts +++ b/src/hooks/useMenuEvents.test.ts @@ -226,12 +226,6 @@ describe('dispatchMenuEvent', () => { expect(h.onSelectFilter).toHaveBeenCalledWith('all') }) - it('go-favorites selects favorites filter', () => { - const h = makeHandlers() - dispatchMenuEvent('go-favorites', h) - expect(h.onSelectFilter).toHaveBeenCalledWith('favorites') - }) - it('go-archived selects archived filter', () => { const h = makeHandlers() dispatchMenuEvent('go-archived', h) diff --git a/src/hooks/useMenuEvents.ts b/src/hooks/useMenuEvents.ts index 7c02297e..b8880cd5 100644 --- a/src/hooks/useMenuEvents.ts +++ b/src/hooks/useMenuEvents.ts @@ -67,7 +67,6 @@ const SIMPLE_EVENT_MAP: Record = { const FILTER_MAP: Record = { 'go-all-notes': 'all', - 'go-favorites': 'favorites', 'go-archived': 'archived', 'go-trash': 'trash', 'go-changes': 'changes', diff --git a/src/types.ts b/src/types.ts index e791d4e5..96a999aa 100644 --- a/src/types.ts +++ b/src/types.ts @@ -170,7 +170,7 @@ export interface PulseCommit { deleted: number } -export type SidebarFilter = 'all' | 'favorites' | 'archived' | 'trash' | 'changes' | 'pulse' +export type SidebarFilter = 'all' | 'archived' | 'trash' | 'changes' | 'pulse' export type SidebarSelection = | { kind: 'filter'; filter: SidebarFilter }