Skip to content

Commit 49f28e5

Browse files
authored
Scroll to focus point of expanded selections (#5968)
* Confirm undo-after-deletion scroll bug * Scroll to focus point of expanded selections
1 parent c66a771 commit 49f28e5

File tree

3 files changed

+59
-12
lines changed

3 files changed

+59
-12
lines changed

.changeset/violet-moons-hope.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'slate-react': minor
3+
---
4+
5+
Scroll to focus point of expanded selections in `defaultScrollSelectionIntoView`

packages/slate-react/src/components/editable.tsx

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1952,18 +1952,17 @@ export const defaultScrollSelectionIntoView = (
19521952
editor: ReactEditor,
19531953
domRange: DOMRange
19541954
) => {
1955-
// This was affecting the selection of multiple blocks and dragging behavior,
1956-
// so enabled only if the selection has been collapsed.
1957-
if (
1958-
domRange.getBoundingClientRect &&
1959-
(!editor.selection ||
1960-
(editor.selection && Range.isCollapsed(editor.selection)))
1961-
) {
1962-
const leafEl = domRange.startContainer.parentElement!
1955+
// Scroll to the focus point of the selection, in case the selection is expanded
1956+
const isBackward = !!editor.selection && Range.isBackward(editor.selection)
1957+
const domFocusPoint = domRange.cloneRange()
1958+
domFocusPoint.collapse(isBackward)
1959+
1960+
if (domFocusPoint.getBoundingClientRect) {
1961+
const leafEl = domFocusPoint.startContainer.parentElement!
19631962

1964-
// COMPAT: In Chrome, domRange.getBoundingClientRect() can return zero dimensions for valid ranges (e.g. line breaks).
1963+
// COMPAT: In Chrome, domFocusPoint.getBoundingClientRect() can return zero dimensions for valid ranges (e.g. line breaks).
19651964
// When this happens, do not scroll like most editors do.
1966-
const domRect = domRange.getBoundingClientRect()
1965+
const domRect = domFocusPoint.getBoundingClientRect()
19671966
const isZeroDimensionRect =
19681967
domRect.width === 0 &&
19691968
domRect.height === 0 &&
@@ -1979,8 +1978,9 @@ export const defaultScrollSelectionIntoView = (
19791978
}
19801979
}
19811980

1982-
// Default behavior: use domRange's getBoundingClientRect
1983-
leafEl.getBoundingClientRect = domRange.getBoundingClientRect.bind(domRange)
1981+
// Default behavior: use domFocusPoint's getBoundingClientRect
1982+
leafEl.getBoundingClientRect =
1983+
domFocusPoint.getBoundingClientRect.bind(domFocusPoint)
19841984
scrollIntoView(leafEl, {
19851985
scrollMode: 'if-needed',
19861986
})

playwright/integration/examples/richtext.test.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,46 @@ test.describe('On richtext example', () => {
2121
'Hello World'
2222
)
2323
})
24+
25+
test('undo scrolls back to restored text after deletion and scroll away', async ({
26+
page,
27+
}) => {
28+
const editor = page.getByRole('textbox')
29+
30+
await editor.press('ControlOrMeta+A')
31+
await editor.pressSequentially('First paragraph.')
32+
await editor.press('Enter')
33+
await editor.pressSequentially('Second paragraph.')
34+
35+
// Insert enough content to be scrollable
36+
for (let i = 0; i < 20; i++) {
37+
await editor.press('Enter')
38+
await editor.pressSequentially('Extra paragraph.')
39+
}
40+
41+
const firstParagraph = editor.getByText('First paragraph.')
42+
const secondParagraph = editor.getByText('Second paragraph.')
43+
44+
// Scroll back to top and select first paragraph
45+
await firstParagraph.click({ clickCount: 3 })
46+
47+
await expect(firstParagraph).toBeVisible()
48+
await expect(firstParagraph).toBeInViewport()
49+
await expect(secondParagraph).toBeVisible()
50+
await expect(secondParagraph).toBeInViewport()
51+
52+
await editor.press('Backspace')
53+
await expect(firstParagraph).toBeHidden()
54+
55+
await editor.press(
56+
process.platform === 'darwin' ? 'Meta+ArrowDown' : 'Control+End'
57+
)
58+
await expect(secondParagraph).not.toBeInViewport()
59+
60+
// Undo deletion
61+
await editor.press('ControlOrMeta+Z')
62+
63+
await expect(firstParagraph).toBeVisible()
64+
await expect(firstParagraph).toBeInViewport()
65+
})
2466
})

0 commit comments

Comments
 (0)