Skip to content

Commit

Permalink
Add scroll-to helper
Browse files Browse the repository at this point in the history
  • Loading branch information
nlfurniss committed Aug 31, 2019
1 parent 7ff19c7 commit ff5d5d5
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 0 deletions.
49 changes: 49 additions & 0 deletions addon-test-support/@ember/test-helpers/dom/scroll-to.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import getElement from './-get-element';
import fireEvent from './fire-event';
import settled from '../settled';
import { nextTickPromise } from '../-utils';

/**
Scrolls DOM element or selector to the given coordinates.
@public
@param {string|HTMLElement|window} target the element or selector to trigger scroll on
@param {Number} x x-coordinate
@param {Number} y y-coordinate
@return {Promise<void>} resolves when settled
@example
<caption>
Scroll DOM element to specific coordinates
</caption>
scrollTo('#my-long-div', 0, 0); // scroll to top
scrollTo('#my-long-div', 0, 100); // scroll down
scrollTo(window, 0, 0); // scroll window to top
*/
export default function scrollTo(
target: string | HTMLElement | Window,
x: number,
y: number
): Promise<void> {
return nextTickPromise().then(() => {
if (!target) {
throw new Error('Must pass an element or selector to `scrollTo`.');
}

let element = getElement(target);
if (!element) {
throw new Error(`Element not found when calling \`scrollTo('${target}')\`.`);
}

if (element instanceof HTMLElement) {
element.scrollTop = y;
element.scrollLeft = x;
} else if (target instanceof Window) {
target.scrollTo(x, y);
}

fireEvent(element, 'scroll');

return settled();
});
}
1 change: 1 addition & 0 deletions addon-test-support/@ember/test-helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,4 @@ export { default as getRootElement } from './dom/get-root-element';
export { default as find } from './dom/find';
export { default as findAll } from './dom/find-all';
export { default as typeIn } from './dom/type-in';
export { default as scrollTo } from './dom/scroll-to';
100 changes: 100 additions & 0 deletions tests/integration/dom/scroll-to-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { module, test } from 'qunit';
import hbs from 'htmlbars-inline-precompile';
import {
render,
scrollTo,
setupContext,
setupRenderingContext,
teardownContext,
} from '@ember/test-helpers';

module('DOM Helper: scroll-to', function(hooks) {
hooks.beforeEach(async function() {
await setupContext(this);
await setupRenderingContext(this);
});

hooks.afterEach(async function() {
await teardownContext(this);
});

test('Scroll in vertical direction', async function(assert) {
assert.expect(1);

let currentScrollPosition = 0;
let scrollAmount = 50;
this.callback = e => {
currentScrollPosition = e.target.scrollTop;
};

await render(hbs`
<div
style="height: 200px; overflow-y: auto;"
class="container"
onscroll={{action callback}}
>
<ul>
<li class="item" style="height: 100px;">A</li>
<li class="item" style="height: 100px;">B</li>
<li class="item" style="height: 100px;">C</li>
<li class="item" style="height: 100px;">D</li>
<li class="item" style="height: 100px;">E</li>
</ul>
</div>
`);

await scrollTo('.container', 0, scrollAmount);

assert.equal(
currentScrollPosition,
scrollAmount,
'After use of the `scrollTop` a paint cycle is triggered and the callback is called'
);
});

test('Scroll in horizontal direction', async function(assert) {
let currentScrollPosition = 0;
let scrollAmount = 50;
this.callback = e => {
currentScrollPosition = e.target.scrollLeft;
};

await render(hbs`
<div
style="width: 200px; overflow-x: auto; white-space: nowrap;"
class="container"
onscroll={{action callback}}
>
<div class="item" style="width: 100px; height: 100px; display: inline-block">A</div>
<div class="item" style="width: 100px; height: 100px; display: inline-block">B</div>
<div class="item" style="width: 100px; height: 100px; display: inline-block">C</div>
<div class="item" style="width: 100px; height: 100px; display: inline-block">D</div>
<div class="item" style="width: 100px; height: 100px; display: inline-block">E</div>
</div>
`);

await scrollTo('.container', scrollAmount, 0);

assert.equal(
currentScrollPosition,
scrollAmount,
'After use of the `scrollLeft` a paint cycle is triggered and the callback is called'
);
});

test('Scroll on window works', async function(assert) {
await render(hbs`
<style>
html, body {
height: 200vh;
overflow-y: auto;
width: 100vw;
}
</style>
`);

await scrollTo(window, 0, 100);

assert.equal(window.scrollY, 100, 'scrollTo scrolls window the appropriate amount');
});
});

0 comments on commit ff5d5d5

Please sign in to comment.