172 lines
4.3 KiB
TypeScript
172 lines
4.3 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
import {
|
|
TableHandlesExtension,
|
|
TableHandlesView,
|
|
} from '../../node_modules/@blocknote/core/src/extensions/TableHandles/TableHandles'
|
|
|
|
function createTableBlock() {
|
|
return {
|
|
id: 'table-block',
|
|
type: 'table',
|
|
content: {
|
|
type: 'tableContent',
|
|
rows: [
|
|
{ cells: ['Head 1', 'Head 2'] },
|
|
{ cells: ['A', 'B'] },
|
|
],
|
|
},
|
|
}
|
|
}
|
|
|
|
function mountTableHandlesExtension() {
|
|
const editorRoot = document.createElement('div')
|
|
document.body.appendChild(editorRoot)
|
|
|
|
const editor = {
|
|
headless: true,
|
|
isEditable: true,
|
|
prosemirrorView: {
|
|
root: document,
|
|
},
|
|
transact: vi.fn(),
|
|
}
|
|
|
|
const extension = TableHandlesExtension()({ editor: editor as never })
|
|
const plugin = extension.prosemirrorPlugins?.[0]
|
|
|
|
if (!plugin?.spec.view) {
|
|
throw new Error('TableHandlesExtension did not register a plugin view')
|
|
}
|
|
|
|
const view = plugin.spec.view({
|
|
dom: editorRoot,
|
|
root: document,
|
|
} as never) as TableHandlesView
|
|
|
|
return { editor, extension, view }
|
|
}
|
|
|
|
describe('BlockNote table handles regression', () => {
|
|
afterEach(() => {
|
|
document.body.innerHTML = ''
|
|
})
|
|
|
|
it('hides stale table handles instead of throwing when tbody is missing during update', () => {
|
|
const block = createTableBlock()
|
|
const editorRoot = document.createElement('div')
|
|
document.body.appendChild(editorRoot)
|
|
|
|
const editor = {
|
|
getBlock: vi.fn(() => block),
|
|
}
|
|
const emitUpdate = vi.fn()
|
|
|
|
const view = new TableHandlesView(
|
|
editor as never,
|
|
{
|
|
dom: editorRoot,
|
|
root: document,
|
|
} as never,
|
|
emitUpdate,
|
|
)
|
|
|
|
view.state = {
|
|
block,
|
|
show: true,
|
|
showAddOrRemoveRowsButton: true,
|
|
showAddOrRemoveColumnsButton: true,
|
|
rowIndex: 0,
|
|
colIndex: 0,
|
|
} as never
|
|
|
|
const staleTableWrapper = document.createElement('div')
|
|
editorRoot.appendChild(staleTableWrapper)
|
|
view.tableElement = staleTableWrapper
|
|
|
|
expect(() => view.update()).not.toThrow()
|
|
expect(view.state?.show).toBe(false)
|
|
expect(view.state?.showAddOrRemoveRowsButton).toBe(false)
|
|
expect(view.state?.showAddOrRemoveColumnsButton).toBe(false)
|
|
expect(emitUpdate).toHaveBeenCalled()
|
|
|
|
view.destroy()
|
|
})
|
|
|
|
it('ignores stale table drag starts instead of throwing when hover state is unavailable', () => {
|
|
const { editor, extension, view } = mountTableHandlesExtension()
|
|
|
|
expect(() =>
|
|
extension.colDragStart({ dataTransfer: null, clientX: 10 }),
|
|
).not.toThrow()
|
|
expect(() =>
|
|
extension.rowDragStart({ dataTransfer: null, clientY: 10 }),
|
|
).not.toThrow()
|
|
expect(editor.transact).not.toHaveBeenCalled()
|
|
|
|
view.destroy()
|
|
})
|
|
|
|
it('ignores stale table drag end events instead of throwing after state disappears', () => {
|
|
const { extension, view } = mountTableHandlesExtension()
|
|
|
|
expect(() => extension.dragEnd()).not.toThrow()
|
|
|
|
view.destroy()
|
|
})
|
|
|
|
it('cancels stale table drops instead of throwing when no hovered row or column is available', () => {
|
|
const block = createTableBlock()
|
|
const editorRoot = document.createElement('div')
|
|
document.body.appendChild(editorRoot)
|
|
|
|
const editor = {
|
|
getBlock: vi.fn(() => block),
|
|
}
|
|
const emitUpdate = vi.fn()
|
|
|
|
const view = new TableHandlesView(
|
|
editor as never,
|
|
{
|
|
dom: editorRoot,
|
|
root: document,
|
|
} as never,
|
|
emitUpdate,
|
|
)
|
|
|
|
view.state = {
|
|
block,
|
|
show: true,
|
|
showAddOrRemoveRowsButton: true,
|
|
showAddOrRemoveColumnsButton: true,
|
|
referencePosTable: {
|
|
left: 0,
|
|
top: 0,
|
|
right: 100,
|
|
bottom: 100,
|
|
},
|
|
rowIndex: undefined,
|
|
colIndex: undefined,
|
|
draggingState: {
|
|
draggedCellOrientation: 'row',
|
|
originalIndex: 0,
|
|
mousePos: 10,
|
|
},
|
|
widgetContainer: undefined,
|
|
} as never
|
|
|
|
const dropEvent = {
|
|
preventDefault: vi.fn(),
|
|
}
|
|
|
|
expect(() => view.dropHandler(dropEvent as never)).not.toThrow()
|
|
expect(dropEvent.preventDefault).toHaveBeenCalled()
|
|
expect(view.state?.draggingState).toBeUndefined()
|
|
expect(view.state?.show).toBe(false)
|
|
expect(view.state?.rowIndex).toBeUndefined()
|
|
expect(view.state?.colIndex).toBeUndefined()
|
|
expect(emitUpdate).toHaveBeenCalled()
|
|
|
|
view.destroy()
|
|
})
|
|
})
|