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

Bug: stop watching unless viewportSpy=true is passed to modifier #270

Merged
merged 8 commits into from
Apr 27, 2021
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
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,11 +208,14 @@ export default Component.extend(InViewportMixin, {

Default: `false`

`viewportSpy: true` is often useful when you have "infinite lists" that need to keep loading more data.
`viewportSpy: false` is often useful for one time loading of artwork, metrics, etc when the come into the viewport.

If you support IE11 and detect and run logic `onExit`, then it is necessary to have this `true` to that the requestAnimationFrame watching your sentinel is not torn down.

When `true`, the library will continually watch the `Component` and re-fire hooks whenever it enters or leaves the viewport. Because this is expensive, this behaviour is opt-in. When false, the Mixin will only watch the `Component` until it enters the viewport once, and then it sets `viewportEntered` to `true` (permanently), and unbinds listeners. This reduces the load on the Ember run loop and your application.

NOTE: If using IntersectionObserver (default), viewportSpy wont put too much of a tax on your application. However, for browsers (Safari) that don't currently support IntersectionObserver, we fallback to rAF. Depending on your use case, the default of `false` may be acceptable.
NOTE: If using IntersectionObserver (default), viewportSpy wont put too much of a tax on your application. However, for browsers (Safari < 12.1) that don't currently support IntersectionObserver, we fallback to rAF. Depending on your use case, the default of `false` may be acceptable.

- `viewportDidScroll: boolean`

Expand Down
4 changes: 4 additions & 0 deletions addon/modifiers/in-viewport.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ export default class InViewportModifier extends Modifier {
if (this.args.named.onEnter) {
this.args.named.onEnter.call(null, this.element);
}

if (!this.options.viewportSpy) {
this.inViewport.stopWatching(this.element);
}
}

@action
Expand Down
22 changes: 22 additions & 0 deletions tests/acceptance/infinity-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,28 @@ module('Acceptance | infinity-scrollable', function (hooks) {
'{{in-viewport}} modifier',
'has title'
);

document.querySelector('.infinity-item-19').scrollIntoView(false);

await waitUntil(
() => {
return findAll('.infinity-item').length === 30;
},
{ timeoutMessage: 'did not find all items in time' }
);

await settled();

assert.equal(
findAll('.infinity-item').length,
30,
'after infinity has more items'
);
assert.equal(
find('h1').textContent.trim(),
'{{in-viewport}} modifier',
'has title'
);
});

test('works with in-viewport modifier (rAF)', async function (assert) {
Expand Down
32 changes: 22 additions & 10 deletions tests/dummy/app/components/my-modifier.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,35 @@
import Component from '@glimmer/component';
import { action, set } from '@ember/object';
import InViewportMixin from 'ember-in-viewport';
import { action } from '@ember/object';
import { inject as service } from '@ember/service';

export default class MyModifier extends Component {
@service inViewport;

export default class MyModifier extends Component.extend(InViewportMixin) {
@action
setupInViewport(element) {
this.watchElement(element);
const viewportSpy = true;
const viewportTolerance = {
bottom: 300,
};
const { onEnter } = this.inViewport.watchElement(element, {
viewportSpy,
viewportTolerance,
});
onEnter(this.didEnterViewport.bind(this));
}

constructor() {
super(...arguments);

set(this, 'viewportSpy', true);
set(this, 'viewportTolerance', {
bottom: 300,
});
}

didEnterViewport() {
this.infinityLoad();
this.args.infinityLoad();
}

willDestroy() {
super.willDestroy(...arguments);

const loader = document.getElementById('loader');
this.inViewport.stopWatching(loader);
}
}
10 changes: 10 additions & 0 deletions tests/dummy/app/controllers/infinity-built-in-modifiers.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ export default class InfinityBuiltInModifiers extends Controller {
element.textContent = '{{in-viewport}} modifier';
}

@action
setTitleGreen() {
document.querySelector('h1#green-target').style = 'color: green';
}

@action
removeTitleGreen() {
document.querySelector('h1#green-target').style = '';
}

@action
didEnterViewport(/*artwork, i, element*/) {
const arr = Array.apply(null, Array(10));
Expand Down
49 changes: 20 additions & 29 deletions tests/dummy/app/controllers/infinity-modifier.js
Original file line number Diff line number Diff line change
@@ -1,46 +1,37 @@
import Controller from '@ember/controller';
import { set, action } from '@ember/object';
import { later } from '@ember/runloop';
import { Promise } from 'rsvp';
import { action, set } from '@ember/object';

const images = ['jarjan', 'aio___', 'kushsolitary', 'kolage', 'idiot', 'gt'];
let rect =
'<rect x="10" y="10" width="30" height="30" stroke="black" fill="transparent" stroke-width="5"/>';
let circle =
'<circle cx="25" cy="75" r="20" stroke="red" fill="transparent" stroke-width="5"/>';
let line =
'<line x1="10" x2="50" y1="110" y2="150" stroke="orange" stroke-width="5"/>';

const images = [rect, circle, line];
const arr = Array.apply(null, Array(10));
const models = [
...arr.map(() => {
return {
bgColor: 'E8D26F',
url: `https://s3.amazonaws.com/uifaces/faces/twitter/${
images[(Math.random() * images.length) | 0]
}/128.jpg`,
};
}),
...arr.map(() => `${images[(Math.random() * images.length) | 0]}`),
];

export default class InfinityModifier extends Controller {
constructor() {
super(...arguments);
this.viewportToleranceOverride = {
bottom: 200,
};
}

models = models;

@action
infinityLoad() {
const arr = Array.apply(null, Array(10));
const newModels = [
...arr.map(() => {
return {
bgColor: '0790EB',
url: `https://s3.amazonaws.com/uifaces/faces/twitter/${
images[(Math.random() * images.length) | 0]
}/128.jpg`,
};
}),
...arr.map(() => `${images[(Math.random() * images.length) | 0]}`),
];

return new Promise((resolve) => {
later(() => {
const models = this.models;
models.push(...newModels);
set(this, 'models', Array.prototype.slice.call(models));
resolve();
}, 0);
});
const models = this.models;
models.push(...newModels);
set(this, 'models', Array.prototype.slice.call(models));
}
}
84 changes: 45 additions & 39 deletions tests/dummy/app/templates/infinity-built-in-modifiers.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -4,42 +4,48 @@
<LinkTo @route="infinity-built-in-modifiers" @query={{hash direction="enter"}}>enter</LinkTo>
<LinkTo @route="infinity-built-in-modifiers" @query={{hash direction="exit"}}>exit</LinkTo>
</nav>
<h1 {{in-viewport onEnter=this.setTitle viewportTolerance=this.other}}></h1>
{{#if (eq this.direction "both")}}
<ul class="infinity-container">
{{#each this.models as |artwork i|}}
<li
class="infinity-item infinity-item-{{i}}"
>
<DummyArtwork @artwork={{artwork}} @artworkProfile="dummy" />
</li>
{{/each}}
<div
class="sentinel"
{{in-viewport
onEnter=this.didEnterViewport
onExit=this.didExitViewport
viewportTolerance=this.viewportTolerance
scrollableArea=".infinity-container"
}}
></div>
</ul>
{{else if (eq this.direction "enter")}}
<ul>
{{#each this.models as |artwork|}}
<li>
<DummyArtwork @artwork={{artwork}} @artworkProfile="dummy" />
</li>
{{/each}}
<div class="sentinel" {{in-viewport onEnter=this.didEnterViewport}}></div>
</ul>
{{else if (eq this.direction "exit")}}
<ul>
{{#each this.models as |artwork|}}
<li>
<DummyArtwork @artwork={{artwork}} @artworkProfile="dummy" />
</li>
{{/each}}
<div class="sentinel" {{in-viewport onExit=this.didExitViewport}}></div>
</ul>
{{/if}}
<div style="height: 2500px;">
<h1 id="green-target" {{in-viewport onEnter=this.setTitle viewportTolerance=this.other}}></h1>

{{#if (eq this.direction "both")}}
<ul class="infinity-container">
{{#each this.models as |artwork i|}}
<li
class="infinity-item infinity-item-{{i}}"
>
<DummyArtwork @artwork={{artwork}} @artworkProfile="dummy" />
</li>
{{/each}}
<div
class="sentinel"
{{in-viewport
onEnter=this.didEnterViewport
onExit=this.didExitViewport
viewportTolerance=this.viewportTolerance
scrollableArea=".infinity-container"
viewportSpy=true
}}
></div>
</ul>
{{else if (eq this.direction "enter")}}
<ul>
{{#each this.models as |artwork|}}
<li>
<DummyArtwork @artwork={{artwork}} @artworkProfile="dummy" />
</li>
{{/each}}
<div class="sentinel" {{in-viewport onEnter=this.didEnterViewport}}></div>
</ul>
{{else if (eq this.direction "exit")}}
<ul>
{{#each this.models as |artwork|}}
<li>
<DummyArtwork @artwork={{artwork}} @artworkProfile="dummy" />
</li>
{{/each}}
<div class="sentinel" {{in-viewport onExit=this.didExitViewport}}></div>
</ul>
{{/if}}

<div {{in-viewport onEnter=this.setTitleGreen onExit=this.removeTitleGreen viewportSpy=true}}></div>
</div>
11 changes: 6 additions & 5 deletions tests/dummy/app/templates/infinity-modifier.hbs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<ul>
{{#each this.models as |artwork|}}
<li>
<DummyArtwork @artwork={{artwork}} @artworkProfile="dummy" />
</li>

{{#each this.models as |val|}}
<div class="infinity-class-item">
<svg width="200" height="250" version="1.1" xmlns="http://www.w3.org/2000/svg">
{{{val}}}
</svg>
</div>
{{/each}}
</ul>

Expand Down
9 changes: 5 additions & 4 deletions tests/integration/components/my-component-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ module('Integration | Component | my component', function (hooks) {
top: 1,
};
await render(hbs`
{{#my-component viewportEnabled=true viewportToleranceOverride=viewportToleranceOverride}}
<MyComponent @viewportEnabled={{true}} @viewportToleranceOverride={{this.viewportToleranceOverride}}>
template block text
{{/my-component}}
</MyComponent>
`);

assert.equal(this.element.textContent.trim(), 'template block text');
Expand All @@ -28,10 +28,11 @@ module('Integration | Component | my component', function (hooks) {
bottom: 0,
};
this.intersectionThreshold = 1.0;

await render(hbs`
{{#my-component viewportEnabled=true viewportTolerance=viewportTolerance intersectionThreshold=intersectionThreshold}}
<MyComponent @viewportEnabled={{true}} @viewportToleranceOverride={{this.viewportToleranceOverride}} @intersectionThreshold={{this.intersectionThreshold}}>
template block text
{{/my-component}}
</MyComponent>
`);

assert.equal(this.element.textContent.trim(), 'template block text');
Expand Down