feat: pre-assign type when creating note from type section

When the user clicks "+" in the note list while a type section is
selected (e.g. Projects), the new note now gets that type pre-set in
its frontmatter. Previously, all notes were created as generic "Note"
type regardless of the selected section.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
lucaronin
2026-04-02 10:06:40 +02:00
parent c64a3e5d52
commit 49aff89de4
3 changed files with 75 additions and 2 deletions

View File

@@ -208,6 +208,20 @@ describe('NoteList', () => {
expect(screen.queryByText('Matteo Cellini')).not.toBeInTheDocument()
})
it('passes selected type when creating note from type section', () => {
const onCreate = vi.fn()
render(<NoteList {...defaultFilterProps} entries={mockEntries} selection={{ kind: 'sectionGroup', type: 'Project' }} selectedNote={null} onSelectNote={noopSelect} onReplaceActiveTab={noopReplace} onCreateNote={onCreate} />)
fireEvent.click(screen.getByTitle('Create new note'))
expect(onCreate).toHaveBeenCalledWith('Project')
})
it('passes undefined type when creating note from All Notes', () => {
const onCreate = vi.fn()
render(<NoteList {...defaultFilterProps} entries={mockEntries} selection={allSelection} selectedNote={null} onSelectNote={noopSelect} onReplaceActiveTab={noopReplace} onCreateNote={onCreate} />)
fireEvent.click(screen.getByTitle('Create new note'))
expect(onCreate).toHaveBeenCalledWith(undefined)
})
it('shows entity pinned at top with grouped children', () => {
render(
<NoteList {...defaultFilterProps} entries={mockEntries} selection={{ kind: 'entity', entry: mockEntries[0] }} selectedNote={null} onSelectNote={noopSelect} onReplaceActiveTab={noopReplace} onCreateNote={vi.fn()} />

View File

@@ -32,7 +32,7 @@ interface NoteListProps {
sidebarCollapsed?: boolean
onSelectNote: (entry: VaultEntry) => void
onReplaceActiveTab: (entry: VaultEntry) => void
onCreateNote: () => void
onCreateNote: (type?: string) => void
onBulkArchive?: (paths: string[]) => void
onBulkTrash?: (paths: string[]) => void
onBulkRestore?: (paths: string[]) => void
@@ -97,12 +97,15 @@ function NoteListInner({ entries, selection, selectedNote, noteListFilter, onNot
<NoteItem key={entry.path} entry={entry} isSelected={selectedNote?.path === entry.path} isMultiSelected={multiSelect.selectedPaths.has(entry.path)} isHighlighted={entry.path === noteListKeyboard.highlightedPath} noteStatus={resolvedGetNoteStatus(entry.path)} typeEntryMap={typeEntryMap} onClickNote={handleClickNote} onPrefetch={prefetchNoteContent} />
), [selectedNote?.path, handleClickNote, typeEntryMap, resolvedGetNoteStatus, multiSelect.selectedPaths, noteListKeyboard.highlightedPath])
const handleCreateNote = useCallback(() => {
onCreateNote(selection.kind === 'sectionGroup' ? selection.type : undefined)
}, [onCreateNote, selection])
const toggleGroup = useCallback((label: string) => { setCollapsedGroups((prev) => toggleSetMember(prev, label)) }, [])
const title = resolveHeaderTitle(selection, typeDocument)
return (
<div className="flex flex-col select-none overflow-hidden border-r border-border bg-card text-foreground" style={{ height: '100%' }}>
<NoteListHeader title={title} typeDocument={typeDocument} isEntityView={isEntityView} isTrashView={isTrashView} trashCount={searched.length} listSort={listSort} listDirection={listDirection} customProperties={customProperties} sidebarCollapsed={sidebarCollapsed} searchVisible={searchVisible} search={search} onSortChange={handleSortChange} onCreateNote={onCreateNote} onOpenType={onReplaceActiveTab} onToggleSearch={toggleSearch} onSearchChange={setSearch} onEmptyTrash={onEmptyTrash} />
<NoteListHeader title={title} typeDocument={typeDocument} isEntityView={isEntityView} isTrashView={isTrashView} trashCount={searched.length} listSort={listSort} listDirection={listDirection} customProperties={customProperties} sidebarCollapsed={sidebarCollapsed} searchVisible={searchVisible} search={search} onSortChange={handleSortChange} onCreateNote={handleCreateNote} onOpenType={onReplaceActiveTab} onToggleSearch={toggleSearch} onSearchChange={setSearch} onEmptyTrash={onEmptyTrash} />
<div className="relative flex flex-1 flex-col overflow-hidden outline-none" style={{ minHeight: 0 }} tabIndex={0} onKeyDown={noteListKeyboard.handleKeyDown} onFocus={noteListKeyboard.handleFocus} data-testid="note-list-container">
<div className="flex-1 overflow-hidden" style={{ minHeight: 0 }}>
{entitySelection ? (

View File

@@ -0,0 +1,56 @@
import { test, expect } from '@playwright/test'
test('clicking + in type section creates note with that type', async ({ page }) => {
await page.goto('/')
await page.waitForTimeout(2000)
// Click on "Projects" in the sidebar to select that type section
const projectsItem = page.locator('[data-testid="sidebar-section-Projects"]').or(page.locator('.app__sidebar').locator('text=Projects').first())
await projectsItem.click()
await page.waitForTimeout(1000)
// Click the "+" button to create a new note
await page.click('[title="Create new note"]')
await page.waitForTimeout(1000)
// The new note should have type-based naming (e.g., "Untitled project N")
const titleInput = page.locator('[data-testid="title-field-input"]')
if (await titleInput.count() > 0) {
const value = await titleInput.inputValue()
console.log('New note title:', value)
expect(value.toLowerCase()).toContain('project')
}
// Toggle raw editor to see frontmatter
await page.keyboard.press('Meta+Shift+m')
await page.waitForTimeout(500)
const rawEditor = page.locator('.cm-content')
if (await rawEditor.count() > 0) {
const content = await rawEditor.textContent()
console.log('Raw content:', content?.substring(0, 200))
expect(content).toContain('type: Project')
}
})
test('clicking + in All Notes creates generic note', async ({ page }) => {
await page.goto('/')
await page.waitForTimeout(2000)
// Click on "All Notes" in sidebar
await page.locator('text=All Notes').first().click()
await page.waitForTimeout(500)
// Click the "+" button
await page.click('[title="Create new note"]')
await page.waitForTimeout(1000)
// The new note should be a generic "Note" type
const titleInput = page.locator('[data-testid="title-field-input"]')
if (await titleInput.count() > 0) {
const value = await titleInput.inputValue()
console.log('New note title:', value)
expect(value.toLowerCase()).toContain('note')
expect(value.toLowerCase()).not.toContain('project')
}
})