diff --git a/src/components/NoteItem.tsx b/src/components/NoteItem.tsx index 1a2ace4a..14058f2c 100644 --- a/src/components/NoteItem.tsx +++ b/src/components/NoteItem.tsx @@ -82,6 +82,10 @@ function resolveChipValues( allEntries: VaultEntry[], typeEntryMap: Record, ): PropertyChipValue[] { + if (propName.toLowerCase() === 'status') { + const formatted = formatChipValue(entry.status) + return formatted ? [{ label: formatted, noteIcon: null, typeIcon: null }] : [] + } // Check relationships first (wikilink values) const relKey = Object.keys(entry.relationships).find((k) => k.toLowerCase() === propName.toLowerCase()) if (relKey) { diff --git a/src/components/NoteList.rendering.test.tsx b/src/components/NoteList.rendering.test.tsx index 43e8f185..3aa340d9 100644 --- a/src/components/NoteList.rendering.test.tsx +++ b/src/components/NoteList.rendering.test.tsx @@ -193,6 +193,83 @@ describe('NoteList rendering', () => { expect(onUpdateInboxNoteListProperties).toHaveBeenCalledWith(['Priority', 'Owner']) }) + it('shows status in the type column picker when at least one note has it set', () => { + const entries = [ + makeTypeDefinition('Book'), + makeEntry({ + path: '/vault/book.md', + filename: 'book.md', + title: 'Book Note', + isA: 'Book', + status: 'Active', + createdAt: 1700000000, + }), + ] + + renderNoteList({ + entries, + selection: { kind: 'sectionGroup', type: 'Book' }, + onUpdateTypeSort: () => undefined, + }) + + act(() => { + openNoteListPropertiesPicker('type') + }) + + expect(screen.getByTestId('list-properties-popover')).toBeInTheDocument() + expect(screen.getByRole('checkbox', { name: 'status' })).toBeInTheDocument() + }) + + it('keeps blank statuses out of the type column picker', () => { + const entries = [ + makeTypeDefinition('Book'), + makeEntry({ + path: '/vault/book.md', + filename: 'book.md', + title: 'Book Note', + isA: 'Book', + status: '', + properties: { Owner: 'Luca' }, + createdAt: 1700000000, + }), + ] + + renderNoteList({ + entries, + selection: { kind: 'sectionGroup', type: 'Book' }, + onUpdateTypeSort: () => undefined, + }) + + act(() => { + openNoteListPropertiesPicker('type') + }) + + expect(screen.getByTestId('list-properties-popover')).toBeInTheDocument() + expect(screen.getByRole('checkbox', { name: 'Owner' })).toBeInTheDocument() + expect(screen.queryByRole('checkbox', { name: 'status' })).not.toBeInTheDocument() + }) + + it('renders status as a note-list chip when a type displays it', () => { + const entries = [ + makeTypeDefinition('Book', ['status']), + makeEntry({ + path: '/vault/book.md', + filename: 'book.md', + title: 'Book Note', + isA: 'Book', + status: 'Active', + createdAt: 1700000000, + }), + ] + + renderNoteList({ + entries, + selection: { kind: 'sectionGroup', type: 'Book' }, + }) + + expect(screen.getByTestId('property-chips')).toHaveTextContent('Active') + }) + it('uses inbox overrides when configured', () => { const entries = [ makeTypeDefinition('Book', ['Priority']), diff --git a/src/components/note-list/noteListHooks.ts b/src/components/note-list/noteListHooks.ts index 7208d621..6f13aa85 100644 --- a/src/components/note-list/noteListHooks.ts +++ b/src/components/note-list/noteListHooks.ts @@ -288,9 +288,14 @@ export function useVisibleNotesSync({ visibleNotesRef, isEntityView, searched, s // --- useListPropertyPicker --- +function hasScalarListPropertyValue(value: string | null): boolean { + return value !== null && value.trim() !== '' +} + function collectAvailableProperties(entries: VaultEntry[]): string[] { const keys = new Set() for (const entry of entries) { + if (hasScalarListPropertyValue(entry.status)) keys.add('status') for (const key of Object.keys(entry.properties ?? {})) keys.add(key) for (const key of Object.keys(entry.relationships ?? {})) keys.add(key) } @@ -324,6 +329,58 @@ export interface NoteListPropertyPicker { triggerTitle: string } +interface BuildInboxPropertyPickerParams { + isInboxView: boolean + onUpdateInboxNoteListProperties?: (value: string[] | null) => void + inboxAvailableProperties: string[] + hasCustomInboxProperties: boolean + inboxNoteListProperties?: string[] | null + inboxDefaultDisplay: string[] +} + +function buildInboxPropertyPicker({ + isInboxView, + onUpdateInboxNoteListProperties, + inboxAvailableProperties, + hasCustomInboxProperties, + inboxNoteListProperties, + inboxDefaultDisplay, +}: BuildInboxPropertyPickerParams): NoteListPropertyPicker | null { + if (!isInboxView || !onUpdateInboxNoteListProperties) return null + + return { + scope: 'inbox', + availableProperties: inboxAvailableProperties, + currentDisplay: hasCustomInboxProperties ? inboxNoteListProperties ?? [] : inboxDefaultDisplay, + onSave: onUpdateInboxNoteListProperties, + triggerTitle: 'Customize Inbox columns', + } +} + +interface BuildTypePropertyPickerParams { + isSectionGroup: boolean + typeDocument: VaultEntry | null + onUpdateTypeSort?: (path: string, key: string, value: string | number | boolean | string[] | null) => void + typeAvailableProperties: string[] +} + +function buildTypePropertyPicker({ + isSectionGroup, + typeDocument, + onUpdateTypeSort, + typeAvailableProperties, +}: BuildTypePropertyPickerParams): NoteListPropertyPicker | null { + if (!isSectionGroup || !typeDocument || !onUpdateTypeSort) return null + + return { + scope: 'type', + availableProperties: typeAvailableProperties, + currentDisplay: typeDocument.listPropertiesDisplay ?? [], + onSave: (value: string[] | null) => onUpdateTypeSort(typeDocument.path, '_list_properties_display', value), + triggerTitle: 'Customize columns', + } +} + interface UseListPropertyPickerParams { entries: VaultEntry[] selection: SidebarSelection @@ -368,27 +425,19 @@ export function useListPropertyPicker({ const inboxDisplayOverride = isInboxView && hasCustomInboxProperties ? inboxNoteListProperties : null const propertyPicker = useMemo(() => { - if (isInboxView && onUpdateInboxNoteListProperties) { - return { - scope: 'inbox', - availableProperties: inboxAvailableProperties, - currentDisplay: hasCustomInboxProperties ? inboxNoteListProperties ?? [] : inboxDefaultDisplay, - onSave: onUpdateInboxNoteListProperties, - triggerTitle: 'Customize Inbox columns', - } - } - - if (isSectionGroup && typeDocument && onUpdateTypeSort) { - return { - scope: 'type', - availableProperties: typeAvailableProperties, - currentDisplay: typeDocument.listPropertiesDisplay ?? [], - onSave: (value: string[] | null) => onUpdateTypeSort(typeDocument.path, '_list_properties_display', value), - triggerTitle: 'Customize columns', - } - } - - return null + return buildInboxPropertyPicker({ + isInboxView, + onUpdateInboxNoteListProperties, + inboxAvailableProperties, + hasCustomInboxProperties, + inboxNoteListProperties, + inboxDefaultDisplay, + }) ?? buildTypePropertyPicker({ + isSectionGroup, + typeDocument, + onUpdateTypeSort, + typeAvailableProperties, + }) }, [ hasCustomInboxProperties, inboxAvailableProperties,