8000 fix(useScroll): use mutationObserver to update arrivedState when the DOM is changed by andylou0102 · Pull Request #4433 · vueuse/vueuse · GitHub
[go: up one dir, main page]

Skip to content

fix(useScroll): use mutationObserver to update arrivedState when the DOM is changed #4433

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jun 19, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
test(useScroll): add basic browser test
  • Loading branch information
andylou0102 committed Feb 1, 2025
commit 5de18ac28e064044cdf0dfdd715df64e6ff0ff27
163 changes: 163 additions & 0 deletions packages/core/useScroll/index.browser.test.ts
8000
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import { page } from '@vitest/browser/context'
import { describe, expect, it } from 'vitest'
import { computed, defineComponent, shallowRef, useTemplateRef } from 'vue'
import { useScroll } from '.'

const Component = defineComponent({
template: `
<div style='padding: 12px; display: flex; gap: 8px'>
<button data-testId="left" @click="goToLeft">goToLeft</button>
<button data-testId="right" @click="goToRight">goToRight</button>
<button data-testId="top" @click="goToTop">goToTop</button>
<button data-testId="bottom" @click="goToBottom">goToBottom</button>
<button data-testId="toggleW 8000 idth" @click="toggleWidth">toggleWidth</button>
<button data-testId="toggleHeight" @click="toggleHeight">toggleHeight</button>
</div>
<pre data-testId="arrivedState">{{ arrivedState }}</pre>
<div
ref="el"
style="width: 300px; height: 300px; margin: auto; overflow: auto;"
>
<div :style></div>
</div>
`,
props: ['observe'],
setup(props) {
const el = useTemplateRef<HTMLElement>('el')
const { x, y, arrivedState } = useScroll(el, { observe: props.observe ?? false })
function triggerScrollManually() {
el.value?.dispatchEvent(new Event('scroll'))
}
function goToLeft() {
x.value = 0
triggerScrollManually()
}
function goToRight() {
x.value = el.value?.scrollWidth || 300
triggerScrollManually()
}
function goToTop() {
y.value = 0
triggerScrollManually()
}
function goToBottom() {
y.value = el.value?.scrollHeight || 300
triggerScrollManually()
}
const height = shallowRef(500)
function toggleHeight() {
if (height.value < 500)
height.value = 500
else
height.value = 300
}
const width = shallowRef(500)
function toggleWidth() {
if (width.value < 500)
width.value = 500
else
width.value = 300
}
const style = computed(() => `width: ${width.value}px; height: ${height.value}px; position: relative;`)
return {
el,
style,
arrivedState,
goToLeft,
goToRight,
goToTop,
goToBottom,
toggleHeight,
toggleWidth,
}
< 8000 /td> },
})

describe('useScroll', () => {
it('should correctly detect leftArrived and rightArrived states when reaching the X-axis boundaries', async () => {
const screen = page.render(Component, { props: { observe: true } })
expect(screen).toBeDefined()
const arrivedState = screen.getByTestId('arrivedState')
await expect.element(arrivedState).toBeVisible()
const rightButton = screen.getByTestId('right')
await expect.element(rightButton).toBeVisible()
await rightButton.click()
expect(arrivedState.query()?.textContent).toMatchInlineSnapshot(`
"{
"left": false,
"right": true,
"top": true,
"bottom": false
}"
`)
const leftButton = screen.getByTestId('left')
await expect.element(leftButton).toBeVisible()
await leftButton.click()
expect(arrivedState.query()?.textContent).toMatchInlineSnapshot(`
"{
"left": true,
"right": false,
"top": true,
"bottom": false
}"
`)
})
it('should correctly detect topArrived and bottomArrived states when reaching the Y-axis boundaries', async () => {
const screen = page.render(Component, { props: { observe: true } })
expect(screen).toBeDefined()
const arrivedState = screen.getByTestId('arrivedState')
await expect.element(arrivedState).toBeVisible()
const bottomButton = screen.getByTestId('bottom')
await expect.element(bottomButton).toBeVisible()
await bottomButton.click()
expect(arrivedState.query()?.textContent).toMatchInlineSnapshot(`
"{
"left": true,
"right": false,
"top": false,
"bottom": true
}"
`)
const topButton = screen.getByTestId('top')
await expect.element(topButton).toBeVisible()
await topButton.click()
expect(arrivedState.query()?.textContent).toMatchInlineSnapshot(`
"{
"left": true,
"right": false,
"top": true,
"bottom": false
}"
`)
})
it('should observe DOM mutations when observe is enabled', async () => {
const screen = page.render(Component, { props: { observe: true } })
expect(screen).toBeDefined()
const arrivedState = screen.getByTestId('arrivedState')
await expect.element(arrivedState).toBeVisible()
const toggleHeightButton = screen.getByTestId('toggleHeight')
const toggleWidthButton = screen.getByTestId('toggleWidth')
await expect.element(toggleHeightButton).toBeVisible()
await expect.element(toggleWidthButton).toBeVisible()
await toggleHeightButton.click()
await toggleWidthButton.click()
expect(arrivedState.query()?.textContent).toMatchInlineSnapshot(`
"{
"left": true,
"right": true,
"top": true,
"bottom": true
}"
`)
await toggleHeightButton.click()
await toggleWidthButton.click()
expect(arrivedState.query()?.textContent).toMatchInlineSnapshot(`
"{
"left": true,
"right": false,
"top": true,
"bottom": false
}"
`)
})
})
16 changes: 8 additions & 8 deletions packages/core/useScroll/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { noop, tryOnMounted, useDebounceFn, useThrottleFn } from '@vueuse/shared
import { computed, reactive, ref, toValue } from 'vue'
import { defaultWindow } from '../_configurable'
import { unrefElement } from '../unrefElement'
import { useMutationObserver } from '../useMutationObserver'
import { useEventListener } from '../useEventListener'
import { useMutationObserver } from '../useMutationObserver'

export interface UseScrollOptions extends ConfigurableWindow {
/**
Expand Down Expand Up @@ -154,8 +154,8 @@ export function useScroll(
})
const scrollContainer
= (_element as Window)?.document?.documentElement
|| (_element as Document)?.documentElement
|| (_element as Element)
|| (_element as Document)?.documentElement
|| (_element as Element)
if (x != null)
internalX.value = scrollContainer.scrollLeft
if (y != null)
Expand Down Expand Up @@ -292,17 +292,17 @@ export function useScroll(
useMutationObserver(
element,
() => {
const _element = toValue(element);
const _element = toValue(element)
if (!_element)
return;
setArrivedState(_element);
return
setArrivedState(_element)
},
{
attributes: true,
childList: true,
subtree: true,
}
);
},
)
}

useEventListener(
Expand Down
0