Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ import {
} from 'vue'
import { useGettext } from 'vue3-gettext'
import type { Editor } from '@tiptap/core'
import { debounce } from 'lodash-es'
import { debounce, isEqual, pick } from 'lodash-es'
import {
OcButton,
OcHiddenAnnouncer,
Expand Down Expand Up @@ -217,20 +217,25 @@ const scrollToCurrentResult = () => {
}
}

let previousSearchOptions = {}

const updateResultState = () => {
const storage = editor.storage.findAndReplace
const results = storage?.results || []
const currentIndex = storage?.currentIndex
const newCount = results.length
const newPosition = currentIndex === null || currentIndex < 0 ? 0 : currentIndex + 1

// Only scroll if the position actually changed
// a new search or option can point to another match at the same position, document edits must not scroll
const positionChanged = newPosition !== unref(currentResultPosition)
const searchOptions = pick(storage, ['searchTerm', 'caseSensitive', 'useRegex', 'wholeWord'])
const searchChanged = !isEqual(searchOptions, previousSearchOptions)
previousSearchOptions = searchOptions

resultCount.value = newCount
currentResultPosition.value = newPosition

if (positionChanged && newPosition > 0) {
if ((positionChanged || searchChanged) && newPosition > 0) {
scrollToCurrentResult()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,86 @@ describe('TextEditorSearchAndReplacePanel', () => {
expect(wrapper.find('[data-testid="editor-search-result-counter"]').text()).toBe('2 / 2')
})

describe('scrolling to the current result', () => {
function mountPanel() {
const { editor, triggerTransaction } = createEditorMock()
const scrollIntoView = vi.fn()
editor.view = {
domAtPos: vi.fn(() => {
const node = document.createElement('span')
node.scrollIntoView = scrollIntoView
return { node }
})
}
mount(TextEditorSearchAndReplacePanel, {
props: {
editor,
closeMenu: vi.fn(),
...createSearchProps(),
searchSearchTerm: 'needle'
},
global: {
plugins: [...defaultPlugins()],
stubs: { OcIcon: true }
}
})
function setSearch(
searchTerm: string,
results: Array<{ from: number; to: number }>,
currentIndex = 0
) {
editor.storage.findAndReplace.searchTerm = searchTerm
editor.storage.findAndReplace.results = results
editor.storage.findAndReplace.currentIndex = results.length ? currentIndex : null
triggerTransaction()
}
return { editor, scrollIntoView, setSearch }
}

it('scrolls to the new current result when the search term changes but the position stays the same', () => {
const { editor, scrollIntoView, setSearch } = mountPanel()
setSearch('last', [{ from: 500, to: 504 }])
expect(editor.view.domAtPos).toHaveBeenLastCalledWith(500)
expect(scrollIntoView).toHaveBeenCalledTimes(1)

setSearch('first', [
{ from: 1, to: 6 },
{ from: 100, to: 105 }
])
expect(editor.view.domAtPos).toHaveBeenLastCalledWith(1)
expect(scrollIntoView).toHaveBeenCalledTimes(2)
})

it('scrolls to the current result when a search option changes', () => {
const { editor, scrollIntoView, setSearch } = mountPanel()
setSearch('needle', [
{ from: 1, to: 7 },
{ from: 100, to: 106 }
])
expect(scrollIntoView).toHaveBeenCalledTimes(1)

editor.storage.findAndReplace.caseSensitive = true
setSearch('needle', [{ from: 100, to: 106 }])
expect(editor.view.domAtPos).toHaveBeenLastCalledWith(100)
expect(scrollIntoView).toHaveBeenCalledTimes(2)
})

it('does not scroll when the document changes but the current result stays the same', () => {
const { scrollIntoView, setSearch } = mountPanel()
setSearch('needle', [
{ from: 10, to: 16 },
{ from: 100, to: 106 }
])
expect(scrollIntoView).toHaveBeenCalledTimes(1)

setSearch('needle', [
{ from: 12, to: 18 },
{ from: 102, to: 108 }
])
expect(scrollIntoView).toHaveBeenCalledTimes(1)
})
})

describe('result announcement', () => {
beforeEach(() => {
vi.useFakeTimers()
Expand Down
Loading