diff --git a/packages/frontend/@n8n/design-system/src/components/N8nRecycleScroller/RecycleScroller.test.ts b/packages/frontend/@n8n/design-system/src/components/N8nRecycleScroller/RecycleScroller.test.ts index 8324ef9aa26..da28ca8d518 100644 --- a/packages/frontend/@n8n/design-system/src/components/N8nRecycleScroller/RecycleScroller.test.ts +++ b/packages/frontend/@n8n/design-system/src/components/N8nRecycleScroller/RecycleScroller.test.ts @@ -1,4 +1,6 @@ import { render } from '@testing-library/vue'; +import { mount } from '@vue/test-utils'; +import { nextTick } from 'vue'; import N8nRecycleScroller from './RecycleScroller.vue'; @@ -25,5 +27,37 @@ describe('components', () => { ); expect(wrapper.html()).toMatchSnapshot(); }); + + it('scrolls to an item by key using cached item positions', async () => { + const originalOffsetHeight = Object.getOwnPropertyDescriptor( + HTMLElement.prototype, + 'offsetHeight', + ); + Object.defineProperty(HTMLElement.prototype, 'offsetHeight', { + configurable: true, + value: itemSize, + }); + + try { + const wrapper = mount(N8nRecycleScroller, { + props: { + itemSize, + itemKey, + items, + }, + }); + + await nextTick(); + wrapper.vm.scrollToKey('10'); + + expect(wrapper.find('.recycle-scroller-wrapper').element.scrollTop).toBe(1000); + } finally { + if (originalOffsetHeight) { + Object.defineProperty(HTMLElement.prototype, 'offsetHeight', originalOffsetHeight); + } else { + Reflect.deleteProperty(HTMLElement.prototype, 'offsetHeight'); + } + } + }); }); }); diff --git a/packages/frontend/@n8n/design-system/src/components/N8nRecycleScroller/RecycleScroller.vue b/packages/frontend/@n8n/design-system/src/components/N8nRecycleScroller/RecycleScroller.vue index ece198bdf0c..cc3eee31531 100644 --- a/packages/frontend/@n8n/design-system/src/components/N8nRecycleScroller/RecycleScroller.vue +++ b/packages/frontend/@n8n/design-system/src/components/N8nRecycleScroller/RecycleScroller.vue @@ -182,6 +182,22 @@ function onScroll() { scrollTop.value = wrapperRef.value.scrollTop; } + +function scrollToKey(key: Item[Key]) { + if (!wrapperRef.value) { + return; + } + + const position = itemPositionCache.value[key]; + if (position === undefined) { + return; + } + + wrapperRef.value.scrollTop = position; + scrollTop.value = position; +} + +defineExpose({ scrollToKey });