* refactor: decompose Editor.tsx into focused subcomponents and hooks Extract from the monolithic Editor component (cc=35, 213 LoC, score 7.82): - useDiffMode hook: diff state + toggle/commit-diff handlers - useEditorFocus hook: new-note focus event listener - editorSchema: WikiLink spec + BlockNote schema (module-level) - SingleEditorView: BlockNote editor + suggestion menus - EditorContent: breadcrumb bar + diff/editor/loading views - EditorRightPanel: AI chat / Inspector panel switching - suggestionEnrichment util: shared enrichment logic (eliminates duplication) Editor.tsx is now 10.0 code health (was 7.82). All 25 existing tests pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: extract standalone functions from NoteList and Sidebar to reduce cc NoteList: extract createNoteStatusResolver and toggleSetMember from NoteListInner (cc 13→8, score 9.04→9.38). Sidebar: extract applyCustomization from Sidebar (cc 10→7, score 9.09→10.0). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add tests for useDiffMode, useEditorFocus, and suggestionEnrichment Cover extracted hook/utility logic: diff toggle/reset, editor focus event listener with adaptive timing, and suggestion item enrichment pipeline. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: update architecture for editor decomposition and write .claude-done Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
49 lines
1.5 KiB
TypeScript
49 lines
1.5 KiB
TypeScript
import { describe, it, expect, vi, afterEach } from 'vitest'
|
|
import { renderHook } from '@testing-library/react'
|
|
import { useEditorFocus } from './useEditorFocus'
|
|
|
|
describe('useEditorFocus', () => {
|
|
afterEach(() => { vi.restoreAllMocks() })
|
|
|
|
function setup(isMounted: boolean) {
|
|
const editor = { focus: vi.fn() }
|
|
const mountedRef = { current: isMounted }
|
|
renderHook(() => useEditorFocus(editor, mountedRef))
|
|
return editor
|
|
}
|
|
|
|
it('focuses editor via rAF when already mounted', async () => {
|
|
const rAF = vi.spyOn(window, 'requestAnimationFrame').mockImplementation((cb) => { cb(0); return 0 })
|
|
const editor = setup(true)
|
|
|
|
window.dispatchEvent(new CustomEvent('laputa:focus-editor'))
|
|
|
|
expect(rAF).toHaveBeenCalled()
|
|
expect(editor.focus).toHaveBeenCalled()
|
|
})
|
|
|
|
it('focuses editor via setTimeout when not yet mounted', () => {
|
|
vi.useFakeTimers()
|
|
const editor = setup(false)
|
|
|
|
window.dispatchEvent(new CustomEvent('laputa:focus-editor'))
|
|
|
|
expect(editor.focus).not.toHaveBeenCalled()
|
|
vi.advanceTimersByTime(80)
|
|
expect(editor.focus).toHaveBeenCalled()
|
|
vi.useRealTimers()
|
|
})
|
|
|
|
it('cleans up event listener on unmount', () => {
|
|
const editor = { focus: vi.fn() }
|
|
const mountedRef = { current: true }
|
|
const { unmount } = renderHook(() => useEditorFocus(editor, mountedRef))
|
|
|
|
unmount()
|
|
vi.spyOn(window, 'requestAnimationFrame').mockImplementation((cb) => { cb(0); return 0 })
|
|
window.dispatchEvent(new CustomEvent('laputa:focus-editor'))
|
|
|
|
expect(editor.focus).not.toHaveBeenCalled()
|
|
})
|
|
})
|