Skip to content
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

Add scrollTo helper #698

Merged
merged 1 commit into from
Apr 20, 2020
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
55 changes: 55 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,55 @@
import getElement from './-get-element';
import fireEvent from './fire-event';
import settled from '../settled';
import { nextTickPromise } from '../-utils';
import { isElement } from './-target';

/**
Scrolls DOM element or selector to the given coordinates.
@public
@param {string|HTMLElement} target the element or selector to trigger scroll on
nlfurniss marked this conversation as resolved.
Show resolved Hide resolved
@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
*/
export default function scrollTo(
target: string | HTMLElement,
x: number,
y: number
): Promise<void> {
return nextTickPromise().then(() => {
if (!target) {
throw new Error('Must pass an element or selector to `scrollTo`.');
}

if (x === undefined || y === undefined) {
throw new Error('Must pass both x and y coordinates to `scrollTo`.');
}

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

if (!isElement(element)) {
throw new Error(
`"target" must be an element, but was a ${element.nodeType} when calling \`scrollTo('${target}')\`.`
);
}
rwjblue marked this conversation as resolved.
Show resolved Hide resolved

element.scrollTop = y;
element.scrollLeft = x;

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 @@ -40,3 +40,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';
114 changes: 114 additions & 0 deletions tests/integration/dom/scroll-to-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { module, test } from 'qunit';
import hbs from 'htmlbars-inline-precompile';
import {
render,
scrollTo,
setupContext,
setupRenderingContext,
teardownContext,
} from '@ember/test-helpers';
import hasEmberVersion from '@ember/test-helpers/has-ember-version';

module('DOM Helper: scroll-to', function (hooks) {
if (!hasEmberVersion(2, 4)) {
return;
}

hooks.beforeEach(async function () {
await setupContext(this);
nlfurniss marked this conversation as resolved.
Show resolved Hide resolved
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('It throws an error if a target is not supplied', async function (assert) {
assert.rejects(
scrollTo('', 0, 0),
new Error('Must pass an element or selector to `scrollTo`.')
);
});

test('It throws an error if all coordinates are not supplied', async function (assert) {
await render(hbs`<div class="container"></div>`);

assert.rejects(scrollTo('.container', 0), /Must pass both x and y coordinates/);
assert.rejects(scrollTo('.container', undefined, 0), /Must pass both x and y coordinates/);
});

test('It throws an error if the target is not found', async function (assert) {
await render(hbs`<div class="container"></div>`);

assert.rejects(scrollTo('.container2', 0, 0), /Element not found when calling/);
});

test('It throws an error if the target is not an element', async function (assert) {
assert.rejects(scrollTo(document, 0, 0), /"target" must be an element/);
assert.rejects(scrollTo(window, 0, 0), /"target" must be an element/);
});
});