fix: align Notes counts with explicit type
This commit is contained in:
@@ -262,6 +262,23 @@ describe('NoteList filter pills', () => {
|
||||
expect(screen.queryByText('Open Project 1')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('shows only explicit Note entries for the Notes type filter', () => {
|
||||
const noteEntries = [
|
||||
makeEntry({ title: 'Note Type', isA: 'Type', path: '/types/note.md', filename: 'note.md' }),
|
||||
makeEntry({ title: 'Explicit Note', isA: 'Note', path: '/explicit-note.md', filename: 'explicit-note.md' }),
|
||||
makeEntry({ title: 'Untyped Note', isA: null, path: '/untyped-note.md', filename: 'untyped-note.md' }),
|
||||
makeEntry({ title: 'Archived Explicit Note', isA: 'Note', archived: true, path: '/archived-note.md', filename: 'archived-note.md' }),
|
||||
]
|
||||
|
||||
renderNoteList({ entries: noteEntries, selection: { kind: 'sectionGroup', type: 'Note' } })
|
||||
|
||||
expect(screen.getByText('Explicit Note')).toBeInTheDocument()
|
||||
expect(screen.queryByText('Untyped Note')).not.toBeInTheDocument()
|
||||
expect(screen.queryByText('Archived Explicit Note')).not.toBeInTheDocument()
|
||||
expect(screen.getByTestId('filter-pill-open')).toHaveTextContent('1')
|
||||
expect(screen.getByTestId('filter-pill-archived')).toHaveTextContent('1')
|
||||
})
|
||||
|
||||
it('shows the archived empty state when a section has no archived notes', () => {
|
||||
renderNoteList({
|
||||
entries: projectEntries.filter((entry) => !entry.archived),
|
||||
|
||||
@@ -780,6 +780,14 @@ describe('Sidebar', () => {
|
||||
icon: null, color: null, order: null, sidebarLabel: null, template: null, sort: null,
|
||||
outgoingLinks: [], properties: {},
|
||||
},
|
||||
{
|
||||
path: '/vault/binary-note.pdf', filename: 'binary-note.pdf', title: 'Binary Note',
|
||||
isA: 'Note', aliases: [], belongsTo: [], relatedTo: [], status: null, owner: null,
|
||||
cadence: null, archived: false, modifiedAt: 1700000000,
|
||||
createdAt: null, fileSize: 150, snippet: '', wordCount: 0, relationships: {},
|
||||
icon: null, color: null, order: null, sidebarLabel: null, template: null, sort: null,
|
||||
outgoingLinks: [], properties: {}, fileKind: 'binary',
|
||||
},
|
||||
]
|
||||
|
||||
it('shows Notes section when Note entries exist', () => {
|
||||
@@ -787,13 +795,40 @@ describe('Sidebar', () => {
|
||||
expect(screen.getByText('Notes')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('counts both explicit and untyped notes in Notes section chip', () => {
|
||||
it('counts only explicit Note entries in the Notes section chip', () => {
|
||||
render(<Sidebar entries={noteEntries} selection={defaultSelection} onSelect={() => {}} />)
|
||||
const notesHeader = screen.getByText('Notes').closest('[class*="group/section"]')!
|
||||
expect(notesHeader.textContent).toContain('1')
|
||||
})
|
||||
|
||||
it('ignores non-markdown Note entries in the Notes section chip', () => {
|
||||
render(<Sidebar entries={noteEntries} selection={defaultSelection} onSelect={() => {}} />)
|
||||
const notesHeader = screen.getByText('Notes').closest('[class*="group/section"]')!
|
||||
expect(notesHeader.textContent).toContain('1')
|
||||
expect(notesHeader.textContent).not.toContain('2')
|
||||
})
|
||||
|
||||
it('keeps the Notes section count aligned when an entry changes to or from Note', () => {
|
||||
const { rerender } = render(<Sidebar entries={noteEntries} selection={defaultSelection} onSelect={() => {}} />)
|
||||
let notesHeader = screen.getByText('Notes').closest('[class*="group/section"]')!
|
||||
expect(notesHeader.textContent).toContain('1')
|
||||
|
||||
const withoutExplicitNote = noteEntries.map((entry) =>
|
||||
entry.path === '/vault/explicit-note.md' ? { ...entry, isA: null } : entry,
|
||||
)
|
||||
rerender(<Sidebar entries={withoutExplicitNote} selection={defaultSelection} onSelect={() => {}} />)
|
||||
notesHeader = screen.getByText('Notes').closest('[class*="group/section"]')!
|
||||
expect(notesHeader.textContent).toBe('Notes')
|
||||
|
||||
const withNewExplicitNote = noteEntries.map((entry) =>
|
||||
entry.path === '/vault/untyped-note.md' ? { ...entry, isA: 'Note' } : entry,
|
||||
)
|
||||
rerender(<Sidebar entries={withNewExplicitNote} selection={defaultSelection} onSelect={() => {}} />)
|
||||
notesHeader = screen.getByText('Notes').closest('[class*="group/section"]')!
|
||||
expect(notesHeader.textContent).toContain('2')
|
||||
})
|
||||
|
||||
it('shows Notes section for untyped entries even without explicit Note entries', () => {
|
||||
it('does not show Notes section for untyped entries without explicit Note entries', () => {
|
||||
const untypedOnly: VaultEntry[] = [
|
||||
{
|
||||
path: '/vault/plain.md', filename: 'plain.md', title: 'Plain Note',
|
||||
@@ -805,7 +840,7 @@ describe('Sidebar', () => {
|
||||
},
|
||||
]
|
||||
render(<Sidebar entries={untypedOnly} selection={defaultSelection} onSelect={() => {}} />)
|
||||
expect(screen.getByText('Notes')).toBeInTheDocument()
|
||||
expect(screen.queryByText('Notes')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import { TypeCustomizePopover } from '../TypeCustomizePopover'
|
||||
import { useDragRegion } from '../../hooks/useDragRegion'
|
||||
import { SidebarGroupHeader } from './SidebarGroupHeader'
|
||||
import { SidebarViewItem } from './SidebarViewItem'
|
||||
import { countByFilter } from '../../utils/noteListHelpers'
|
||||
|
||||
export { SidebarTopNav } from './SidebarTopNav'
|
||||
export { FavoritesSection } from './FavoritesSection'
|
||||
@@ -94,9 +95,7 @@ function SortableSection({
|
||||
sectionProps: SidebarSectionProps
|
||||
}) {
|
||||
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id: group.type })
|
||||
const itemCount = sectionProps.entries.filter((entry) =>
|
||||
!entry.archived && (group.type === 'Note' ? (entry.isA === 'Note' || !entry.isA) : entry.isA === group.type),
|
||||
).length
|
||||
const itemCount = countByFilter(sectionProps.entries, group.type).open
|
||||
const isRenaming = sectionProps.renamingType === group.type
|
||||
|
||||
return (
|
||||
|
||||
@@ -31,13 +31,10 @@ const isMarkdown = (e: VaultEntry) => e.fileKind === 'markdown' || !e.fileKind
|
||||
const isActive = (e: VaultEntry) => !e.archived
|
||||
const isSupportedSectionType = (type: string) => !isLegacyJournalingType(type)
|
||||
|
||||
function resolveEntrySectionType(entry: VaultEntry): string {
|
||||
return entry.isA || 'Note'
|
||||
}
|
||||
|
||||
function shouldCollectActiveType(entry: VaultEntry): boolean {
|
||||
if (!isActive(entry) || !isMarkdown(entry)) return false
|
||||
return isSupportedSectionType(resolveEntrySectionType(entry))
|
||||
if (!entry.isA) return false
|
||||
return isSupportedSectionType(entry.isA)
|
||||
}
|
||||
|
||||
function shouldIncludeTypeDefinition(name: string, entry: VaultEntry): boolean {
|
||||
@@ -45,12 +42,12 @@ function shouldIncludeTypeDefinition(name: string, entry: VaultEntry): boolean {
|
||||
return isSupportedSectionType(name)
|
||||
}
|
||||
|
||||
/** Collect unique isA values from active (non-archived) markdown entries. Untyped entries count as 'Note'. */
|
||||
/** Collect unique explicit isA values from active (non-archived) markdown entries. */
|
||||
export function collectActiveTypes(entries: VaultEntry[]): Set<string> {
|
||||
const types = new Set<string>()
|
||||
for (const e of entries) {
|
||||
if (!shouldCollectActiveType(e)) continue
|
||||
types.add(resolveEntrySectionType(e))
|
||||
types.add(e.isA!)
|
||||
}
|
||||
return types
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user