refactor: simplify breadcrumb and note-list search ui

This commit is contained in:
lucaronin
2026-04-21 19:03:11 +02:00
parent 05375258a2
commit 3e4e2ebdc6
4 changed files with 45 additions and 26 deletions

View File

@@ -245,6 +245,12 @@ describe('BreadcrumbBar — action buttons always right-aligned', () => {
expect(actions).toBeInTheDocument()
expect(actions).toHaveClass('ml-auto')
})
it('does not render the unused backlinks or more-actions placeholders', () => {
render(<BreadcrumbBar entry={baseEntry} {...defaultProps} />)
expect(screen.queryByRole('button', { name: 'Backlinks are coming soon' })).not.toBeInTheDocument()
expect(screen.queryByRole('button', { name: 'More note actions are coming soon' })).not.toBeInTheDocument()
})
})
describe('BreadcrumbBar — raw editor toggle', () => {

View File

@@ -8,10 +8,8 @@ import { TooltipProvider } from '@/components/ui/tooltip'
import {
GitBranch,
Code,
CursorText,
Sparkle,
SlidersHorizontal,
DotsThree,
Trash,
Archive,
ArrowUUpLeft,
@@ -214,14 +212,6 @@ function DiffAction({
)
}
function PlaceholderAction({ copy, children }: { copy: ActionTooltipCopy; children: ReactNode }) {
return (
<IconActionButton copy={copy} style={DISABLED_ICON_STYLE}>
{children}
</IconActionButton>
)
}
function AIChatAction({ showAIChat, onToggleAIChat }: Pick<BreadcrumbBarProps, 'showAIChat' | 'onToggleAIChat'>) {
const copy: ActionTooltipCopy = {
label: showAIChat ? 'Close the AI panel' : 'Open the AI panel',
@@ -481,16 +471,10 @@ function BreadcrumbActions({
onToggleDiff={onToggleDiff}
/>
{!forceRawMode && <RawToggleButton rawMode={rawMode} onToggleRaw={onToggleRaw} />}
<PlaceholderAction copy={{ label: 'Backlinks are coming soon' }}>
<CursorText size={16} className={BREADCRUMB_ICON_CLASS} />
</PlaceholderAction>
<AIChatAction showAIChat={showAIChat} onToggleAIChat={onToggleAIChat} />
<ArchiveAction archived={entry.archived} onArchive={onArchive} onUnarchive={onUnarchive} />
<DeleteAction onDelete={onDelete} />
<InspectorAction inspectorCollapsed={inspectorCollapsed} onToggleInspector={onToggleInspector} />
<PlaceholderAction copy={{ label: 'More note actions are coming soon' }}>
<DotsThree size={16} className={BREADCRUMB_ICON_CLASS} />
</PlaceholderAction>
</div>
)
}

View File

@@ -295,6 +295,35 @@ describe('NoteList rendering', () => {
}
})
it('keeps the note-list search input full width and shows only an inline spinner while loading', () => {
vi.useFakeTimers()
try {
renderNoteList({
entries: [
makeEntry({ path: '/vault/a.md', filename: 'a.md', title: 'Alpha Strategy' }),
makeEntry({ path: '/vault/b.md', filename: 'b.md', title: 'Beta Note' }),
],
})
fireEvent.click(screen.getByTitle('Search notes'))
fireEvent.change(screen.getByPlaceholderText('Search notes...'), { target: { value: 'strategy' } })
const searchInput = screen.getByPlaceholderText('Search notes...')
expect(searchInput).toHaveClass('pr-8')
expect(searchInput.parentElement).toHaveClass('relative', 'flex-1')
expect(screen.getByTestId('note-list-search-loading')).toBeInTheDocument()
expect(screen.queryByText('Searching...')).not.toBeInTheDocument()
act(() => {
vi.advanceTimersByTime(180)
})
expect(screen.queryByTestId('note-list-search-loading')).not.toBeInTheDocument()
} finally {
vi.useRealTimers()
}
})
it('shows backlinks from outgoing links in entity view', () => {
const entriesWithBacklink = mockEntries.map((entry) =>
entry.path === mockEntries[2].path ? { ...entry, outgoingLinks: ['Build Laputa App'] } : entry,

View File

@@ -55,23 +55,23 @@ export function NoteListHeader({ title, typeDocument, isEntityView, listSort, li
</div>
{searchVisible && (
<div className="border-b border-border px-3 py-2">
<div className="flex items-center gap-3">
<div className="relative flex-1" aria-live="polite">
<Input
ref={searchInputRef}
placeholder="Search notes..."
value={search}
onChange={(e) => onSearchChange(e.target.value)}
onKeyDown={onSearchKeyDown}
className="h-8 text-[13px]"
className="h-8 pr-8 text-[13px]"
/>
<div className="min-w-[92px] text-[12px] text-muted-foreground" aria-live="polite">
{isSearching && (
<span className="flex items-center gap-1" data-testid="note-list-search-loading">
<Loader2 size={12} className="animate-spin" />
Searching...
</span>
)}
</div>
{isSearching && (
<span
className="pointer-events-none absolute inset-y-0 right-3 flex items-center text-muted-foreground"
data-testid="note-list-search-loading"
>
<Loader2 size={12} className="animate-spin" />
</span>
)}
</div>
</div>
)}