Skip to content

Commit 698d5be

Browse files
author
Dobromir Hristov
authored
Keep scroll position on page nav when there is a filter applied (#695)
resolves rdar://110300795 * refactor: keep scroll position on page nav when there is a filter applied. Keep scroll if you filter and your current page is in the results. * chore: update tests
1 parent f26f215 commit 698d5be

File tree

2 files changed

+32
-16
lines changed

2 files changed

+32
-16
lines changed

src/components/Navigator/NavigatorCard.vue

+8-6
Original file line numberDiff line numberDiff line change
@@ -866,18 +866,20 @@ export default {
866866
async scrollToElement() {
867867
await waitFrames(1);
868868
if (!this.$refs.scroller) return;
869-
// if we are filtering, it makes more sense to scroll to top of list
870-
if (this.hasFilter && !this.deprecatedHidden) {
871-
this.$refs.scroller.scrollToItem(0);
872-
return;
873-
}
874869
// check if the current element is visible and needs scrolling into
875870
const element = document.getElementById(this.activeUID);
876871
// check if there is such an item AND the item is inside scroller area
877872
if (element && this.getChildPositionInScroller(element) === 0) return;
878873
// find the index of the current active UID in the nodes to render
879874
const index = this.nodesToRender.findIndex(child => child.uid === this.activeUID);
880-
if (index === -1) return;
875+
// check if the item is currently not rendered
876+
if (index === -1) {
877+
// if we are filtering, it makes more sense to scroll to top of list
878+
if (this.hasFilter && !this.deprecatedHidden) {
879+
this.$refs.scroller.scrollToItem(0);
880+
}
881+
return;
882+
}
881883
// check if the element is visible
882884
// call the scroll method on the `scroller` component.
883885
this.$refs.scroller.scrollToItem(index);

tests/unit/components/Navigator/NavigatorCard.spec.js

+24-10
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,12 @@ function attachDivWithID(id) {
230230
document.body.appendChild(div);
231231
}
232232

233+
function detachDivWithID(id) {
234+
const el = document.getElementById(id);
235+
if (!el) return;
236+
document.body.removeChild(el);
237+
}
238+
233239
describe('NavigatorCard', () => {
234240
beforeEach(() => {
235241
document.body.innerHTML = '';
@@ -1108,11 +1114,11 @@ describe('NavigatorCard', () => {
11081114
// item is not scrolled to
11091115
expect(DynamicScrollerStub.methods.scrollToItem).toHaveBeenCalledTimes(0);
11101116
const filter = wrapper.find(FilterInput);
1117+
// the filter is on the grandchild of the current activeUID
11111118
filter.vm.$emit('input', root0Child1GrandChild0.title);
11121119
await flushPromises();
1113-
// assert list is scrolled to the top
1114-
expect(DynamicScrollerStub.methods.scrollToItem).toHaveBeenCalledTimes(1);
1115-
expect(DynamicScrollerStub.methods.scrollToItem).toHaveBeenCalledWith(0);
1120+
// assert list is not scrolled, if item is in viewport or current activeUID is rendered
1121+
expect(DynamicScrollerStub.methods.scrollToItem).toHaveBeenCalledTimes(0);
11161122
// assert only the parens of the match are visible
11171123
const all = wrapper.findAll(NavigatorCardItem);
11181124
expect(all).toHaveLength(3);
@@ -1145,8 +1151,7 @@ describe('NavigatorCard', () => {
11451151
// make sure we match at both the top item as well as one of its children
11461152
filter.vm.$emit('input', 'Second');
11471153
await flushPromises();
1148-
expect(DynamicScrollerStub.methods.scrollToItem).toHaveBeenCalledTimes(1);
1149-
expect(DynamicScrollerStub.methods.scrollToItem).toHaveBeenCalledWith(0);
1154+
expect(DynamicScrollerStub.methods.scrollToItem).toHaveBeenCalledTimes(0);
11501155
// assert only the parens of the match are visible
11511156
const all = wrapper.findAll(NavigatorCardItem);
11521157
expect(all).toHaveLength(2);
@@ -1161,8 +1166,7 @@ describe('NavigatorCard', () => {
11611166
await flushPromises();
11621167
filter.vm.$emit('input', root0.title);
11631168
await flushPromises();
1164-
expect(DynamicScrollerStub.methods.scrollToItem).toHaveBeenCalledTimes(1);
1165-
expect(DynamicScrollerStub.methods.scrollToItem).toHaveBeenCalledWith(0);
1169+
expect(DynamicScrollerStub.methods.scrollToItem).toHaveBeenCalledTimes(0);
11661170
// assert only the parens of the match are visible
11671171
let all = wrapper.findAll(NavigatorCardItem);
11681172
expect(all).toHaveLength(1);
@@ -1201,6 +1205,8 @@ describe('NavigatorCard', () => {
12011205
await flushPromises();
12021206
const filter = wrapper.find(FilterInput);
12031207
filter.vm.$emit('update:selectedTags', [FILTER_TAGS_TO_LABELS.articles]);
1208+
// this item is not an article
1209+
detachDivWithID(root0Child0.uid);
12041210
await flushPromises();
12051211
expect(DynamicScrollerStub.methods.scrollToItem).toHaveBeenCalledTimes(1);
12061212
expect(DynamicScrollerStub.methods.scrollToItem).toHaveBeenLastCalledWith(0);
@@ -1219,6 +1225,7 @@ describe('NavigatorCard', () => {
12191225
const filter = wrapper.find(FilterInput);
12201226
await flushPromises();
12211227
filter.vm.$emit('update:selectedTags', [FILTER_TAGS_TO_LABELS.tutorials]);
1228+
detachDivWithID(root0Child0.uid);
12221229
await flushPromises();
12231230
expect(DynamicScrollerStub.methods.scrollToItem).toHaveBeenCalledTimes(1);
12241231
expect(DynamicScrollerStub.methods.scrollToItem).toHaveBeenCalledWith(0);
@@ -1234,6 +1241,7 @@ describe('NavigatorCard', () => {
12341241
const filter = wrapper.find(FilterInput);
12351242
await flushPromises();
12361243
filter.vm.$emit('update:selectedTags', [FILTER_TAGS_TO_LABELS.articles]);
1244+
detachDivWithID(root0Child0.uid);
12371245
await flushPromises();
12381246
expect(DynamicScrollerStub.methods.scrollToItem).toHaveBeenCalledTimes(1);
12391247
expect(DynamicScrollerStub.methods.scrollToItem).toHaveBeenCalledWith(0);
@@ -1251,6 +1259,8 @@ describe('NavigatorCard', () => {
12511259
expect(all.at(0).props('item')).toEqual(root0);
12521260
expect(all.at(1).props('item')).toEqual(root0Child1);
12531261
expect(all.at(2).props('item')).toEqual(root0Child1GrandChild0);
1262+
expect(DynamicScrollerStub.methods.scrollToItem).toHaveBeenCalledTimes(2);
1263+
expect(DynamicScrollerStub.methods.scrollToItem).toHaveBeenLastCalledWith(0);
12541264
});
12551265

12561266
it('allows opening an item, that has a filter match', async () => {
@@ -2201,7 +2211,7 @@ describe('NavigatorCard', () => {
22012211
});
22022212

22032213
describe('scroll to item', () => {
2204-
it('resets the scroll position, if initiating a filter', async () => {
2214+
it('resets the scroll position, if filtering, current page is NOT in the results', async () => {
22052215
attachDivWithID(root0Child0.uid);
22062216
// simulate item is above the scrollarea
22072217
getChildPositionInScroller.mockReturnValueOnce(1);
@@ -2210,10 +2220,14 @@ describe('NavigatorCard', () => {
22102220
expect(DynamicScrollerStub.methods.scrollToItem).toHaveBeenCalledTimes(1);
22112221
expect(DynamicScrollerStub.methods.scrollToItem).toHaveBeenLastCalledWith(1);
22122222
// initiate a filter
2213-
wrapper.find(FilterInput).vm.$emit('input', root0Child1.title);
2223+
wrapper.find(FilterInput).vm.$emit('input', root1.title);
2224+
// we have to manually remove it from the DOM, as we are mocking lots of stuff
2225+
detachDivWithID(root0Child0.uid);
22142226
await wrapper.vm.$nextTick();
22152227
// assert filter is applied
2216-
expect(wrapper.findAll(NavigatorCardItem)).toHaveLength(2);
2228+
const items = wrapper.findAll(NavigatorCardItem);
2229+
expect(items).toHaveLength(1);
2230+
expect(items.at(0).props('item')).toEqual(root1);
22172231
// assert scroller has been reset
22182232
expect(DynamicScrollerStub.methods.scrollToItem).toHaveBeenCalledTimes(2);
22192233
expect(DynamicScrollerStub.methods.scrollToItem).toHaveBeenLastCalledWith(0);

0 commit comments

Comments
 (0)