import { Editor } from '@tiptap/core' import Document from '@tiptap/extension-document' import Paragraph from '@tiptap/extension-paragraph' import Text from '@tiptap/extension-text' import { afterEach, beforeEach, describe, expect, it } from 'vite-plus/test' import { DragHandle } from './drag-handle.js' describe('DragHandle', () => { let editor: Editor let dragHandleElement: HTMLElement beforeEach(() => { dragHandleElement = document.createElement('div') dragHandleElement.classList.add('drag-handle') editor = new Editor({ element: document.body, extensions: [ Document, Paragraph, Text, DragHandle.configure({ render: () => dragHandleElement, }), ], content: '

Hello World

', }) }) afterEach(() => { editor.destroy() }) describe('data-dragging attribute', () => { it('should initialize data-dragging attribute to false', () => { expect(dragHandleElement.dataset.dragging).toBe('false') }) it('should set data-dragging to true on dragstart', () => { const dragStartEvent = new DragEvent('dragstart', { bubbles: true, cancelable: true, dataTransfer: new DataTransfer(), }) dragHandleElement.dispatchEvent(dragStartEvent) expect(dragHandleElement.dataset.dragging).toBe('true') }) it('should set data-dragging to false on dragend', () => { const dragStartEvent = new DragEvent('dragstart', { bubbles: true, cancelable: true, dataTransfer: new DataTransfer(), }) dragHandleElement.dispatchEvent(dragStartEvent) expect(dragHandleElement.dataset.dragging).toBe('true') const dragEndEvent = new DragEvent('dragend', { bubbles: true, cancelable: true, }) dragHandleElement.dispatchEvent(dragEndEvent) expect(dragHandleElement.dataset.dragging).toBe('false') }) it('should maintain data-dragging state through multiple drag cycles', () => { const dataTransfer = new DataTransfer() for (let i = 0; i < 3; i += 1) { const dragStartEvent = new DragEvent('dragstart', { bubbles: true, cancelable: true, dataTransfer, }) dragHandleElement.dispatchEvent(dragStartEvent) expect(dragHandleElement.dataset.dragging).toBe('true') const dragEndEvent = new DragEvent('dragend', { bubbles: true, cancelable: true, }) dragHandleElement.dispatchEvent(dragEndEvent) expect(dragHandleElement.dataset.dragging).toBe('false') } }) }) describe('element properties', () => { it('should have draggable set to true', () => { expect(dragHandleElement.draggable).toBe(true) }) it('should have pointer events set to auto initially', () => { expect(dragHandleElement.style.pointerEvents).toBe('auto') }) it('should render the handle wrapper above editor decorations via a z-index', () => { // The wrapper competes in the stacking context with positioned editor // content and decorations (for example the Pages page header/footer chrome // at z-index 2). Without a z-index the handle is painted behind that chrome // and becomes impossible to click, so the wrapper must assert its own. const wrapper = dragHandleElement.parentElement expect(wrapper).not.toBeNull() expect(wrapper?.style.zIndex).toBe('10') }) }) })