Skip to content

Commit 961e47a

Browse files
committed
[BREAKING] Avoid deprecated APIs in Ember 3.24
- @Items passed in to CollectionScrollView should be a TrackedArray if you want the component to automatically update - replaced ember-ref-modifier with ember-ref-bucket - convert ScrollViewApi to native class that does not use Evented mixin - Remove support for Evented API from emitter-action helper
1 parent d9f0fb8 commit 961e47a

File tree

13 files changed

+4103
-4458
lines changed

13 files changed

+4103
-4458
lines changed

addon/components/collection-scroll-view/collection-items/index.js

Lines changed: 4 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@ class Cell {
2121
}
2222
}
2323

24-
function noop() {}
25-
2624
export default class CollectionScrollViewCollectionItems extends Component {
2725
_contentSize;
2826
cells = A();
@@ -33,6 +31,7 @@ export default class CollectionScrollViewCollectionItems extends Component {
3331
@reads('args.scrollTop', 0) scrollTop;
3432
@reads('args.estimatedSize', { width: 0, height: 0 }) estimatedSize;
3533
@reads('args.cellLayout') cellLayout;
34+
@reads('args.items', []) items; // args.items should be a TrackedArray if you want it this component to update when it changes
3635

3736
get clientWidth() {
3837
let { clientSize, estimatedSize } = this.args;
@@ -49,41 +48,6 @@ export default class CollectionScrollViewCollectionItems extends Component {
4948
this.rerender();
5049
}
5150

52-
willDestroyElement() {
53-
let { items } = this;
54-
if (items && items.removeArrayObserver) {
55-
items.removeArrayObserver(this, {
56-
willChange: noop,
57-
didChange: 'safeRerender'
58-
});
59-
}
60-
}
61-
62-
get items(){
63-
let rawItems = this.args.items;
64-
65-
if (this._rawItems !== rawItems) {
66-
this._rawItems = rawItems;
67-
if (this._items && this._items.removeArrayObserver) {
68-
this._items.removeArrayObserver(this, {
69-
willChange: noop,
70-
didChange: 'safeRerender'
71-
});
72-
}
73-
let items = A(rawItems);
74-
this._items = items;
75-
76-
if (items && items.addArrayObserver) {
77-
items.addArrayObserver(this, {
78-
willChange: noop,
79-
didChange: 'safeRerender'
80-
});
81-
}
82-
return items;
83-
}
84-
return this._items;
85-
}
86-
8751
get contentSize() {
8852
this._contentSize = this._contentSize || this.cellLayout.contentSize(this.clientWidth, this.clientHeight);
8953
return this._contentSize;
@@ -101,7 +65,6 @@ export default class CollectionScrollViewCollectionItems extends Component {
10165

10266
get renderCells() {
10367
let { cellLayout, cells, items, scrollLeft, scrollTop, clientWidth, clientHeight } = this;
104-
if (!items) { return []; }
10568
const numItems = items.length;
10669
if (cellLayout.length !== numItems) {
10770
cellLayout.length = numItems;
@@ -122,7 +85,7 @@ export default class CollectionScrollViewCollectionItems extends Component {
12285

12386
for (i=0; i<count; i++) {
12487
itemIndex = index+i;
125-
itemKey = identity(items.objectAt(itemIndex));
88+
itemKey = identity(items[itemIndex]);
12689
if (priorMap) {
12790
cell = priorMap[itemKey];
12891
}
@@ -143,7 +106,7 @@ export default class CollectionScrollViewCollectionItems extends Component {
143106
if (!cellMap[cell.key]) {
144107
if (newItems.length) {
145108
itemIndex = newItems.pop();
146-
let item = items.objectAt(itemIndex);
109+
let item = items[itemIndex];
147110
itemKey = identity(item);
148111
style = cellLayout.formatItemStyle(itemIndex, clientWidth, clientHeight);
149112
cell.style = style;
@@ -161,7 +124,7 @@ export default class CollectionScrollViewCollectionItems extends Component {
161124

162125
for (i = 0; i < newItems.length; i++) {
163126
itemIndex = newItems[i];
164-
let item = items.objectAt(itemIndex);
127+
let item = items[itemIndex];
165128
itemKey = identity(item);
166129
style = cellLayout.formatItemStyle(itemIndex, clientWidth, clientHeight);
167130
cell = new Cell(itemKey, item, itemIndex, style);

addon/components/collection-scroll-view/index.hbs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
@clientSizeChange={{this.clientSizeChange}}
1717
@scrollChange={{this.scrollChange}}
1818
@scrolledToTopChange={{this.onScrolledToTopChange}}
19-
{{ref this 'element'}}
19+
{{create-ref 'element'}}
2020
...attributes
2121
as |scrollViewApi|
2222
>

addon/components/collection-scroll-view/index.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ import { cached } from 'ember-cached-decorator-polyfill';
44
import { tracked } from '@glimmer/tracking';
55
import { action } from '@ember/object';
66
import { next, schedule } from '@ember/runloop';
7+
import { ref } from 'ember-ref-bucket';
78

89
/* A component which integrates a ScrollView with ember-collection */
910
export default class CollectionScrollView extends Component {
10-
element;
11+
@ref("element") element;
1112

1213
@tracked headerDimensions;
1314
@tracked scrollTop = 0;
@@ -108,7 +109,7 @@ export default class CollectionScrollView extends Component {
108109
return;
109110
}
110111
let { items } = this.args;
111-
let itemIndex = items.indexOf(items.findBy('id', id));
112+
let itemIndex = items.indexOf(items.find(i => i.id === id));
112113
if (itemIndex >= 0) {
113114
let { y } = this.cellLayout.positionAt(itemIndex);
114115
scrollViewApi.scrollTo(y + this.headerHeight, true);

addon/components/scroll-view.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,17 +162,18 @@ class ScrollView extends Component {
162162
}
163163
}
164164

165-
@action
166-
willDestroyEl(element) {
165+
willDestroy() {
166+
this.unbindScrollerEvents(this.scrollViewElement);
167167
this.scrollViewElement = null;
168-
this.unbindScrollerEvents(element);
169168
this._scrollPositionCallbacks = [];
170169
this.remember(this._lastKey);
171170
if (DEBUG) {
172171
if (Ember.testing) {
173172
window.SIMULATE_SCROLL_VIEW_MEASUREMENT_LOOP = null;
174173
}
174+
this._trackIsScrollingForWaiter(false);
175175
}
176+
super.willDestroy(...arguments);
176177
}
177178

178179
setupScroller() {
@@ -569,7 +570,7 @@ class ScrollView extends Component {
569570

570571
@cached
571572
get scrollViewApi() {
572-
return ScrollViewApi.create({
573+
return new ScrollViewApi({
573574
_scrollComponent: this,
574575
});
575576
}

addon/helpers/emitter-action.js

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,9 @@ export default class extends Helper {
2121
super.willDestroy(...arguments);
2222
}
2323

24-
get useEventedApi() {
25-
return this.emitter.on && this.emitter.off;
26-
}
27-
2824
startListening() {
2925
let { handler, emitter, eventName } = this;
30-
if (this.useEventedApi) {
31-
emitter.on(eventName, this, handler);
32-
} else {
33-
emitter.addEventListener(eventName, handler);
34-
}
26+
emitter.addEventListener(eventName, handler);
3527
this.isListening = true;
3628
}
3729

@@ -40,11 +32,7 @@ export default class extends Helper {
4032
if (!isListening) {
4133
return;
4234
}
43-
if (this.useEventedApi) {
44-
emitter.off(eventName, this, handler);
45-
} else {
46-
emitter.removeEventListener(eventName, handler);
47-
}
35+
emitter.removeEventListener(eventName, handler);
4836
this.isListening = false;
4937
}
5038
}

addon/templates/components/scroll-view.hbs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
class="ScrollView {{this.extraCssClasses}}"
33
...attributes
44
{{did-insert this.didInsert}}
5-
{{will-destroy this.willDestroyEl}}
65
{{did-update this.onContentHeightChanged @contentHeight}}
76
{{did-update this.onKeyUpdated @key}}
87
>

addon/utils/scroll-view-api.js

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
/* eslint-disable ember/no-classic-classes */
2-
import Evented from '@ember/object/evented';
31
import EmberObject from '@ember/object';
2+
import EventEmitter from 'eventemitter3';
3+
import { action } from '@ember/object';
44

5-
export default EmberObject.extend(Evented, {
6-
init(){
7-
this._super(...arguments);
8-
let { _scrollComponent } = this;
5+
export default class extends EmberObject {
6+
events = new EventEmitter();
7+
constructor(args){
8+
super(...arguments);
9+
let _scrollComponent = this._scrollComponent = args._scrollComponent;
910
this.scrollToBottom = _scrollComponent.scrollToBottom.bind(_scrollComponent);
1011
this.scrollToElement = _scrollComponent.scrollToElement.bind(_scrollComponent);
1112
this.scrollToTop = _scrollComponent.scrollToTop.bind(_scrollComponent);
@@ -14,8 +15,16 @@ export default EmberObject.extend(Evented, {
1415
this.getViewHeight = _scrollComponent.getViewHeight.bind(_scrollComponent);
1516
this.registerScrollPositionCallback = _scrollComponent.registerScrollPositionCallback.bind(_scrollComponent);
1617
this.unregisterScrollPositionCallback = _scrollComponent.unregisterScrollPositionCallback.bind(_scrollComponent);
17-
},
18+
}
1819
scrollingChanged(value) {
19-
this.trigger('isScrollingChanged', value);
20+
this.events.emit('isScrollingChanged', value);
21+
}
22+
@action
23+
addEventListener() {
24+
this.events.addListener(...arguments);
25+
}
26+
@action
27+
removeEventListener() {
28+
this.events.removeListener(...arguments);
2029
}
21-
});
30+
}

config/ember-try.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@ module.exports = async function() {
2121
}
2222
}
2323
},
24+
{
25+
name: 'ember-lts-3.24',
26+
npm: {
27+
devDependencies: {
28+
'ember-source': '~3.24.0'
29+
}
30+
}
31+
},
2432
{
2533
name: 'ember-release',
2634
npm: {

package.json

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,26 +26,29 @@
2626
"test:ember-compatibility": "ember try:each"
2727
},
2828
"dependencies": {
29-
"@ember/render-modifiers": "^1.0.2",
29+
"@ember/render-modifiers": "^2.1.0",
3030
"@ember/test-waiters": "^2.4.4",
3131
"@glimmer/tracking": "^1.1.2",
3232
"broccoli-stew": "^3.0.0",
33-
"ember-auto-import": "^1.6.0",
34-
"ember-cached-decorator-polyfill": "^0.1.3",
33+
"ember-auto-import": "^2.6.3",
34+
"ember-cached-decorator-polyfill": "^1.0.2",
3535
"ember-cli-babel": "7.26.6",
36-
"ember-cli-htmlbars": "^5.7.1",
36+
"ember-cli-htmlbars": "^5.7.2",
3737
"ember-composable-helpers": "^5.0.0",
3838
"ember-concurrency": "^2.3.7",
3939
"ember-concurrency-decorators": "^2.0.3",
4040
"ember-named-blocks-polyfill": "^0.2.5",
41-
"ember-on-resize-modifier": "^0.3.0",
42-
"ember-ref-modifier": "^1.0.1",
41+
"ember-on-resize-modifier": "^2.0.2",
42+
"ember-ref-bucket": "^5.0.4",
4343
"ember-render-helpers": "^0.2.0",
4444
"ember-resize-observer-polyfill": "^0.0.1",
4545
"ember-set-helper": "^2.0.1",
4646
"ember-truth-helpers": "^3.1.1",
47+
"eventemitter3": "4.0.7",
4748
"hammerjs": "^2.0.8",
48-
"macro-decorators": "^0.1.2"
49+
"macro-decorators": "^0.1.2",
50+
"tracked-built-ins": "^3.1.1",
51+
"webpack": "5"
4952
},
5053
"devDependencies": {
5154
"@babel/core": "^7.14.3",

tests/integration/components/collection-scroll-view-test.js

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import hbs from 'htmlbars-inline-precompile';
44
import { find, render, waitFor, waitUntil } from '@ember/test-helpers';
55
import { scrollPosition, scrollDown, waitForOpacity } from '../../helpers/scrolling';
66
import EmberObject from '@ember/object';
7-
import Evented from '@ember/object/evented';
87
import { timeout } from 'ember-concurrency';
8+
import EventEmitter from 'eventemitter3';
99

1010
const SCROLL_CONTAINER = '[data-test-scroll-container]';
1111
const SCROLLBAR_THUMB = '[data-test-scroll-bar] [data-test-thumb]';
@@ -25,6 +25,19 @@ function waitUntilText(text) {
2525
return waitUntil(() => find(SCROLL_CONTAINER).textContent.includes(text));
2626
}
2727

28+
class FakeRevealService extends EmberObject {
29+
events = new EventEmitter();
30+
addEventListener() {
31+
this.events.addListener(...arguments);
32+
}
33+
removeEventListener() {
34+
this.events.addListener(...arguments);
35+
}
36+
trigger() {
37+
this.events.emit(...arguments);
38+
}
39+
}
40+
2841
module('Integration | Component | collection-scroll-view', function(hooks) {
2942
setupRenderingTest(hooks);
3043

@@ -80,10 +93,16 @@ module('Integration | Component | collection-scroll-view', function(hooks) {
8093
duration: 700
8194
});
8295
await waitForOpacity(SCROLLBAR_THUMB, '1');
83-
await waitUntil(() => find(SCROLLBAR_THUMB).offsetHeight === 230);
84-
assert.equal(find(SCROLLBAR_THUMB).offsetHeight, 230);
96+
await waitUntil(() => {
97+
// console.log('find(SCROLLBAR_THUMB).offsetHeight', find(SCROLLBAR_THUMB).offsetHeight);
98+
return find(SCROLLBAR_THUMB).offsetHeight === 231;
99+
});
100+
assert.equal(find(SCROLLBAR_THUMB).offsetHeight, 231);
85101
await scrollPromise;
86-
await waitUntil(() => scrollPosition(find(SCROLL_CONTAINER)) <= -390);
102+
await waitUntil(() => {
103+
// console.log('scrollPosition(find(SCROLL_CONTAINER))', scrollPosition(find(SCROLL_CONTAINER)));
104+
return scrollPosition(find(SCROLL_CONTAINER)) <= -390;
105+
});
87106
assert.ok(scrollPosition(find(SCROLL_CONTAINER)) <= -390);
88107
assert.dom(SCROLL_CONTAINER).containsText('Ten');
89108
assert.dom(SCROLL_CONTAINER).containsText('Four');
@@ -102,8 +121,8 @@ module('Integration | Component | collection-scroll-view', function(hooks) {
102121
});
103122

104123
test('it accepts reveal service and scrolls item into view', async function(assert) {
105-
let fakeRevealService = EmberObject.extend(Evented).create();
106-
this.set('revealService', fakeRevealService);
124+
let fakeRevealService = new FakeRevealService();
125+
this.set('revealService', fakeRevealService);
107126
await render(EXAMPLE_1_HBS);
108127
assert.dom(SCROLL_CONTAINER).doesNotContainText('Eight');
109128
fakeRevealService.trigger('revealItemById', { id: '8' });
@@ -113,7 +132,7 @@ module('Integration | Component | collection-scroll-view', function(hooks) {
113132
});
114133

115134
test('revealItemById does not scroll if source is within the CollectionScrollView', async function(assert) {
116-
let fakeRevealService = EmberObject.extend(Evented).create();
135+
let fakeRevealService = new FakeRevealService();
117136
this.set('revealService', fakeRevealService);
118137
await render(EXAMPLE_1_HBS);
119138
fakeRevealService.trigger('revealItemById', { id: '4', source: document.querySelector('[data-list-item-id="4"]') });
@@ -178,7 +197,7 @@ module('Integration | Component | collection-scroll-view', function(hooks) {
178197
assert.dom(SCROLL_CONTAINER).containsText('Eight');
179198
assert.dom(SCROLL_CONTAINER).containsText('Nine');
180199
assert.dom(SCROLL_CONTAINER).containsText('Ten');
181-
assert.equal(scrollPosition(find(SCROLL_CONTAINER)), -720);
200+
assert.equal(scrollPosition(find(SCROLL_CONTAINER)), -719);
182201
});
183202

184203
test('it renders part of the header and the beginning of the collection at scrollTop 180', async function(assert) {
@@ -203,14 +222,14 @@ module('Integration | Component | collection-scroll-view', function(hooks) {
203222
await render(HBS_WITH_HEADER);
204223
assert.dom(SCROLL_CONTAINER).containsText('This list is fancy');
205224
await waitFor(ITEMS_CONTAINER);
206-
assert.equal(find(ITEMS_CONTAINER).getBoundingClientRect().height, 1000);
225+
assert.equal(Math.round(find(ITEMS_CONTAINER).getBoundingClientRect().height), 1000);
207226
assertDoNotOverlap(assert, `${SCROLL_CONTAINER} h1`, `[data-list-item-id="${this.items[0].id}"]`);
208227
assert.dom(SCROLL_CONTAINER).containsText('One');
209228
assert.dom(SCROLL_CONTAINER).containsText('Three');
210229
assert.dom(SCROLL_CONTAINER).doesNotContainText('Four');
211230
this.set('h1Height', 80);
212231
assertDoNotOverlap(assert, `${SCROLL_CONTAINER} h1`, `[data-list-item-id="${this.items[0].id}"]`);
213-
assert.equal(find(ITEMS_CONTAINER).getBoundingClientRect().height, 1000);
232+
assert.equal(Math.round(find(ITEMS_CONTAINER).getBoundingClientRect().height), 1000);
214233
assert.dom(SCROLL_CONTAINER).containsText('One');
215234
assert.dom(SCROLL_CONTAINER).containsText('Three');
216235
await waitUntilText('Four');

0 commit comments

Comments
 (0)