mirror of
https://github.com/aculix/negotium.git
synced 2026-09-11 07:28:17 +00:00
143fcc205d
Cmd/Ctrl+Z did nothing in normal use. The handler bailed out whenever the event target was an input, which was meant to protect the field's own text undo. But the add-task input is where focus usually sits: you click it to add a task and stay there, and on macOS clicking a button doesn't move focus. So the guard killed undo in the one situation it exists for, right after deleting something. It now defers to the field only when the field actually holds text. An empty input has nothing to restore. The decision moves to src/lib/shortcuts.js as a pure function, because the inline version couldn't be tested. Shift+Cmd+Z means redo and no longer triggers an undo.
79 lines
2.5 KiB
JavaScript
79 lines
2.5 KiB
JavaScript
import { describe, it, expect } from 'vitest'
|
|
import { shouldHandleUndo } from './shortcuts.js'
|
|
|
|
const evt = (overrides = {}) => ({
|
|
metaKey: false,
|
|
ctrlKey: false,
|
|
shiftKey: false,
|
|
altKey: false,
|
|
key: 'z',
|
|
target: { tagName: 'BODY' },
|
|
...overrides,
|
|
})
|
|
|
|
describe('shouldHandleUndo', () => {
|
|
it('accepts Cmd+Z', () => {
|
|
expect(shouldHandleUndo(evt({ metaKey: true }))).toBe(true)
|
|
})
|
|
|
|
it('accepts Ctrl+Z', () => {
|
|
expect(shouldHandleUndo(evt({ ctrlKey: true }))).toBe(true)
|
|
})
|
|
|
|
it('accepts an uppercase key from caps lock', () => {
|
|
expect(shouldHandleUndo(evt({ metaKey: true, key: 'Z' }))).toBe(true)
|
|
})
|
|
|
|
it('rejects Z with no modifier', () => {
|
|
expect(shouldHandleUndo(evt())).toBe(false)
|
|
})
|
|
|
|
it('rejects a different letter', () => {
|
|
expect(shouldHandleUndo(evt({ metaKey: true, key: 'y' }))).toBe(false)
|
|
})
|
|
|
|
it('rejects Shift+Cmd+Z, which means redo', () => {
|
|
expect(shouldHandleUndo(evt({ metaKey: true, shiftKey: true }))).toBe(false)
|
|
})
|
|
|
|
it('rejects Alt+Cmd+Z', () => {
|
|
expect(shouldHandleUndo(evt({ metaKey: true, altKey: true }))).toBe(false)
|
|
})
|
|
|
|
it('tolerates a missing key', () => {
|
|
expect(shouldHandleUndo(evt({ metaKey: true, key: undefined }))).toBe(false)
|
|
})
|
|
|
|
// The regression this module exists for: the add-task input is where focus
|
|
// normally sits, so guarding on focus alone disabled undo in the one
|
|
// situation it is actually needed.
|
|
it('handles undo when the focused input is empty', () => {
|
|
const target = { tagName: 'INPUT', value: '' }
|
|
expect(shouldHandleUndo(evt({ metaKey: true, target }))).toBe(true)
|
|
})
|
|
|
|
it('defers to the field when the focused input has text to undo', () => {
|
|
const target = { tagName: 'INPUT', value: 'half-typed task' }
|
|
expect(shouldHandleUndo(evt({ metaKey: true, target }))).toBe(false)
|
|
})
|
|
|
|
it('defers to a textarea holding text', () => {
|
|
const target = { tagName: 'TEXTAREA', value: 'notes' }
|
|
expect(shouldHandleUndo(evt({ metaKey: true, target }))).toBe(false)
|
|
})
|
|
|
|
it('defers to a contenteditable target', () => {
|
|
const target = { tagName: 'DIV', isContentEditable: true }
|
|
expect(shouldHandleUndo(evt({ metaKey: true, target }))).toBe(false)
|
|
})
|
|
|
|
it('handles undo when the target is a button', () => {
|
|
const target = { tagName: 'BUTTON' }
|
|
expect(shouldHandleUndo(evt({ metaKey: true, target }))).toBe(true)
|
|
})
|
|
|
|
it('tolerates a missing target', () => {
|
|
expect(shouldHandleUndo(evt({ metaKey: true, target: null }))).toBe(true)
|
|
})
|
|
})
|