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
This commit is contained in:
Test
2026-03-06 23:16:23 +01:00
parent 963e7cf111
commit 50b5fa9c2e
10 changed files with 83 additions and 120 deletions

View File

@@ -1 +1 @@
33549
66070

View File

@@ -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,

View File

@@ -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()
})
})

View File

@@ -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(<Sidebar entries={[]} selection={defaultSelection} onSelect={() => {}} />)
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(<Sidebar entries={entries} selection={defaultSelection} onSelect={() => {}} />)
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(<Sidebar entries={entries} selection={defaultSelection} onSelect={() => {}} />)
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(<Sidebar entries={entries} selection={defaultSelection} onSelect={() => {}} />)
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(<Sidebar entries={mockEntries} selection={defaultSelection} onSelect={() => {}} />)
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(<Sidebar entries={entries} selection={defaultSelection} onSelect={() => {}} />)
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(<Sidebar entries={entries} selection={defaultSelection} onSelect={() => {}} />)
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(<Sidebar entries={mockEntries} selection={defaultSelection} onSelect={() => {}} onToggleTypeVisibility={onToggleTypeVisibility} />)
it('hides a section when its toggle is clicked off', () => {
render(<Sidebar entries={mockEntries} selection={defaultSelection} onSelect={() => {}} />)
// 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(<Sidebar entries={mockEntries} selection={defaultSelection} onSelect={() => {}} />)
// 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(<Sidebar entries={mockEntries} selection={defaultSelection} onSelect={() => {}} />)
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(<Sidebar entries={mockEntries} selection={defaultSelection} onSelect={() => {}} />)
// 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(<Sidebar entries={mockEntries} selection={defaultSelection} onSelect={() => {}} />)
// Top nav items still present
expect(screen.getByText('All Notes')).toBeInTheDocument()
expect(screen.getByText('Archive')).toBeInTheDocument()
})
it('closes popover when clicking outside', () => {

View File

@@ -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 */}
<div className="border-b border-border" style={{ padding: '4px 6px' }}>
<NavItem icon={FileText} label="All Notes" count={activeCount} isActive={isSelectionActive(selection, { kind: 'filter', filter: 'all' })} badgeClassName="bg-primary text-primary-foreground" onClick={() => onSelect({ kind: 'filter', filter: 'all' })} />
<NavItem icon={Star} label="Favorites" isActive={isSelectionActive(selection, { kind: 'filter', filter: 'favorites' })} onClick={() => onSelect({ kind: 'filter', filter: 'favorites' })} />
<NavItem icon={Archive} label="Archive" count={archivedCount} isActive={isSelectionActive(selection, { kind: 'filter', filter: 'archived' })} badgeClassName="text-muted-foreground" badgeStyle={{ background: 'var(--muted)' }} onClick={() => onSelect({ kind: 'filter', filter: 'archived' })} />
<NavItem icon={TagSimple} label="Untagged" disabled />
<NavItem icon={Trash} label="Trash" count={trashedCount} isActive={isSelectionActive(selection, { kind: 'filter', filter: 'trash' })} activeClassName="bg-destructive/10 text-destructive" badgeClassName="text-muted-foreground" badgeStyle={{ background: 'var(--muted)' }} onClick={() => onSelect({ kind: 'filter', filter: 'trash' })} />
{modifiedCount > 0 && (
<NavItem icon={GitDiff} label="Changes" count={modifiedCount} isActive={isSelectionActive(selection, { kind: 'filter', filter: 'changes' })} activeClassName="bg-[color:var(--accent-orange)]/10 text-[var(--accent-orange)]" badgeClassName="text-white" badgeStyle={{ background: 'var(--accent-orange)' }} onClick={() => onSelect({ kind: 'filter', filter: 'changes' })} />

View File

@@ -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])

View File

@@ -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' }) },

View File

@@ -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)

View File

@@ -67,7 +67,6 @@ const SIMPLE_EVENT_MAP: Record<string, SimpleHandler> = {
const FILTER_MAP: Record<string, SidebarFilter> = {
'go-all-notes': 'all',
'go-favorites': 'favorites',
'go-archived': 'archived',
'go-trash': 'trash',
'go-changes': 'changes',

View File

@@ -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 }