Skip to content

Commit ab78f3f

Browse files
authored
fix: guard against selection without range (#953)
1 parent bf00145 commit ab78f3f

File tree

2 files changed

+78
-3
lines changed

2 files changed

+78
-3
lines changed

src/utils/focus/selection.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -217,14 +217,13 @@ export function moveSelection(node: Element, direction: -1 | 1) {
217217
} else {
218218
const selection = node.ownerDocument.getSelection()
219219

220-
/* istanbul ignore if */
221-
if (!selection) {
220+
if (!selection?.focusNode) {
222221
return
223222
}
224223

225224
if (selection.isCollapsed) {
226225
const nextPosition = getNextCursorPosition(
227-
selection.focusNode as Node,
226+
selection.focusNode,
228227
selection.focusOffset,
229228
direction,
230229
)

tests/utils/focus/selection.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
setSelection,
55
setSelectionRange,
66
modifySelection,
7+
moveSelection,
78
} from '#src/utils'
89
import {setup} from '#testHelpers'
910

@@ -183,3 +184,78 @@ describe('update selection when moving focus into element with own selection imp
183184
expect(document.getSelection()).toHaveProperty('focusOffset', 0)
184185
})
185186
})
187+
188+
describe('move selection', () => {
189+
test('do nothing without a selection range', () => {
190+
const {element} = setup(`<div tabindex="0"></div>`)
191+
document.getSelection()?.removeAllRanges()
192+
193+
moveSelection(element, 1)
194+
195+
expect(document.getSelection()).toHaveProperty('rangeCount', 0)
196+
})
197+
198+
test('move to next cursor position', () => {
199+
const {element} = setup(`<div tabindex="0">foo</div>`, {
200+
selection: {focusNode: 'div/text()', focusOffset: 1},
201+
})
202+
203+
moveSelection(element, 1)
204+
205+
expect(document.getSelection()).toHaveProperty(
206+
'focusNode',
207+
element.firstChild,
208+
)
209+
expect(document.getSelection()).toHaveProperty('focusOffset', 2)
210+
})
211+
212+
test('move to next cursor position', () => {
213+
const {element} = setup(`<div tabindex="0">foo</div>`, {
214+
selection: {focusNode: 'div/text()', focusOffset: 1},
215+
})
216+
217+
moveSelection(element, 1)
218+
219+
expect(document.getSelection()).toHaveProperty(
220+
'focusNode',
221+
element.firstChild,
222+
)
223+
expect(document.getSelection()).toHaveProperty('focusOffset', 2)
224+
expect(document.getSelection()).toHaveProperty('isCollapsed', true)
225+
})
226+
227+
test('collapse range', () => {
228+
const {element} = setup(`<div tabindex="0">foo</div>`, {
229+
selection: {focusNode: 'div/text()', anchorOffset: 1, focusOffset: 2},
230+
})
231+
232+
moveSelection(element, 1)
233+
234+
expect(document.getSelection()).toHaveProperty(
235+
'focusNode',
236+
element.firstChild,
237+
)
238+
expect(document.getSelection()).toHaveProperty('focusOffset', 2)
239+
expect(document.getSelection()).toHaveProperty('isCollapsed', true)
240+
})
241+
242+
test('move cursor in input', () => {
243+
const {element} = setup(`<input value="foo"/>`)
244+
245+
moveSelection(element, 1)
246+
247+
expect(element).toHaveProperty('selectionStart', 1)
248+
expect(element).toHaveProperty('selectionEnd', 1)
249+
})
250+
251+
test('collapse range in input', () => {
252+
const {element} = setup(`<input value="foo"/>`, {
253+
selection: {anchorOffset: 1, focusOffset: 2},
254+
})
255+
256+
moveSelection(element, 1)
257+
258+
expect(element).toHaveProperty('selectionStart', 2)
259+
expect(element).toHaveProperty('selectionEnd', 2)
260+
})
261+
})

0 commit comments

Comments
 (0)