Skip to content

Commit

Permalink
feat(infiniteScroll): upgrade infinite scrolling spinner to use new i…
Browse files Browse the repository at this point in the history
…on-spinner directive
  • Loading branch information
perrygovier committed Jan 21, 2015
1 parent 2f0d08e commit 65aa2af
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 25 deletions.
6 changes: 5 additions & 1 deletion js/angular/controller/infiniteScrollController.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ IonicModule
self.isLoading = false;

$scope.icon = function() {
return angular.isDefined($attrs.icon) ? $attrs.icon : 'ion-loading-d';
return angular.isDefined($attrs.icon) ? $attrs.icon : 'ion-load-d';
};

$scope.spinner = function() {
return angular.isDefined($attrs.spinner) ? $attrs.spinner : '';
};

$scope.$on('scroll.infiniteScrollComplete', function() {
Expand Down
10 changes: 8 additions & 2 deletions js/angular/directive/infiniteScroll.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@
* bottom.
* @param {string=} distance The distance from the bottom that the scroll must
* reach to trigger the on-infinite expression. Default: 1%.
* @param {string=} icon The icon to show while loading. Default: 'ion-loading-d'.
* @param {string=} spinner The {@link ionic.directive:ionSpinner} to show while loading. The SVG
* {@link ionic.directive:ionSpinner} is now the default, replacing rotating font icons.
* @param {string=} icon The icon to show while loading. Default: 'ion-load-d'. This is depreicated
* in favor of the SVG {@link ionic.directive:ionSpinner}.
* @param {boolean=} immediate-check Whether to check the infinite scroll bounds immediately on load.
*
* @usage
Expand Down Expand Up @@ -67,7 +70,10 @@ IonicModule
return {
restrict: 'E',
require: ['?^$ionicScroll', 'ionInfiniteScroll'],
template: '<i class="icon {{icon()}} icon-refreshing {{scrollingType}}"></i>',
template: function($element, $attrs){
if ($attrs.icon) return '<i class="icon {{icon()}} icon-refreshing {{scrollingType}}"></i>';
return '<ion-spinner icon="{{spinner()}}"></ion-spinner>';
},
scope: true,
controller: '$ionInfiniteScroll',
link: function($scope, $element, $attrs, ctrls) {
Expand Down
24 changes: 9 additions & 15 deletions scss/_scaffolding.scss
Original file line number Diff line number Diff line change
Expand Up @@ -160,37 +160,31 @@ body.grade-c {
ion-infinite-scroll {
height: 60px;
width: 100%;

display: block;

// @include transition(opacity 0.25s);
@include display-flex();
@include flex-direction(row);
@include justify-content(center);
@include align-items(center);

.icon {

color: #666666;
font-size: 30px;
color: $scroll-refresh-icon-color;
&:before{
-webkit-transform: translate3d(0,0,0);
transform: translate3d(0,0,0);
}
}
&:not(.active) .icon:before{
.icon:before,
.spinner{
-webkit-transform: translate3d(0,0,0);
transform: translate3d(0,0,0);
}
&:not(.active){
.spinner,
.icon:before{
-webkit-transform: translate3d(-1000px,0,0);
transform: translate3d(-1000px,0,0);

}
}
}
// removing the animation when the spinner isn't shown
// this breaks up animations on iOS, so they are left with unnecessary reflows
body:not(.platform-ios) ion-infinite-scroll:not(.active) .icon{
-webkit-animation: none;
animation:none;
}

.overflow-scroll {
overflow-x: hidden;
Expand Down
38 changes: 31 additions & 7 deletions test/unit/angular/directive/infiniteScroll.unit.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
describe('ionicInfiniteScroll directive', function() {
beforeEach(module('ionic'));

var scrollTopValue = 50;
var scrollTopMaxValue = 60;
var scrollLeftValue = 101;
Expand Down Expand Up @@ -37,6 +36,7 @@ describe('ionicInfiniteScroll directive', function() {
$element: angular.element('<div>')
});
$compile(element)(scope);
ionic.requestAnimationFrame = function() {};
ctrl = element.controller('ionInfiniteScroll');
scope.$apply();
});
Expand All @@ -55,6 +55,7 @@ describe('ionicInfiniteScroll directive', function() {
element = parent.find('ion-infinite-scroll');
ionic.animationFrameThrottle = function(cb) { return function() { cb(); }; };
$compile(element)(scope);
ionic.requestAnimationFrame = function() {};
ctrl = element.controller('ionInfiniteScroll');
// create a fake scrollEl since they can't be faked if we're passing in scroll data
if (options) {
Expand Down Expand Up @@ -85,6 +86,7 @@ describe('ionicInfiniteScroll directive', function() {
});

it('should not have class or be loading by default', function() {
ionic.requestAnimationFrame = function() {};
var el = setupJS();
expect(el.hasClass('active')).toBe(false);
expect(ctrl.isLoading).toBe(false);
Expand All @@ -107,22 +109,30 @@ describe('ionicInfiniteScroll directive', function() {
});

describe('icon', function() {
it('should have default icon ion-loading-d', function() {
it('should have platform appropriate default spinner', function() {
var el = setupJS();
var icon = angular.element(el[0].querySelector('.icon'));
expect(icon.hasClass('ion-loading-d')).toBe(true);
var icon = angular.element(el[0].querySelector('.spinner'));
if (ionic.Platform.isAndroid()) {
expect(icon[0].className.indexOf('spinner-android')).not.toBe(-1);
}else {
expect(icon[0].className.indexOf('spinner-ios')).not.toBe(-1);
}
});

it('should allow icon attr blank', function() {
var el = setupJS('icon=""');
var icon = angular.element(el[0].querySelector('.icon'));
expect(icon.hasClass('ion-loading-d')).toBe(false);
var icon = angular.element(el[0].querySelector('.spinner'));
if (ionic.Platform.isAndroid()) {
expect(icon[0].className.indexOf('spinner-android')).not.toBe(-1);
}else {
expect(icon[0].className.indexOf('spinner-ios')).not.toBe(-1);
}
});

it('should allow interpolated icon attr', function() {
var el = setupJS('icon="{{someIcon}}"');
var icon = angular.element(el[0].querySelector('.icon'));
expect(icon.hasClass('ion-loading-d')).toBe(false);
expect(icon.hasClass('ion-load-d')).toBe(false);
el.scope().$apply('someIcon = "super-icon"');
expect(icon.hasClass('super-icon')).toBe(true);
});
Expand Down Expand Up @@ -194,49 +204,60 @@ describe('ionicInfiniteScroll directive', function() {
var el = setupNative('on-infinite="foo=1"');
var evObj = document.createEvent('HTMLEvents');
evObj.initEvent('scroll', true, true, window, 1, 12, 345, 7, 220, false, false, true, false, 0, null);
ionic.requestAnimationFrame = function(cb) { cb(); };
ctrl.scrollEl.dispatchEvent(evObj);

expect(el.hasClass('active')).toBe(false);
ionic.requestAnimationFrame = function() {};
expect(ctrl.isLoading).toBe(false);
expect(el.scope().foo).not.toBe(1);
});
it('should add active and call attr.onInfinite if >= top', function() {
var el = setupJS('on-infinite="foo=1"');
scrollTopValue = scrollTopMaxValue;
ionic.requestAnimationFrame = function(cb) { cb(); };
el.controller('$ionicScroll').$element.triggerHandler('scroll');

expect(el.hasClass('active')).toBe(true);
ionic.requestAnimationFrame = function() {};
expect(ctrl.isLoading).toBe(true);
expect(el.scope().foo).toBe(1);

scrollTopValue = scrollTopMaxValue;
var el = setupNative('on-infinite="foo=1"', {}, { scrollingX: true, scrollingY: true });
ionic.requestAnimationFrame = function(cb) { cb(); };
ctrl.checkBounds();
expect(el.hasClass('active')).toBe(true);
ionic.requestAnimationFrame = function() {};
expect(ctrl.isLoading).toBe(true);
expect(el.scope().foo).toBe(1);
});
it('should add active and call attr.onInfinite if >= left', function() {
var el = setupJS('on-infinite="foo=1"');
scrollLeftValue = scrollLeftMaxValue;
ionic.requestAnimationFrame = function(cb) { cb(); };
el.controller('$ionicScroll').$element.triggerHandler('scroll');

expect(el.hasClass('active')).toBe(true);
ionic.requestAnimationFrame = function() {};
expect(ctrl.isLoading).toBe(true);
expect(el.scope().foo).toBe(1);

scrollLeftValue = scrollLeftMaxValue;
var el = setupNative('on-infinite="foo=1"', {}, { scrollingX: true, scrollingY: true });
ionic.requestAnimationFrame = function(cb) { cb(); };
ctrl.checkBounds();

expect(el.hasClass('active')).toBe(true);
ionic.requestAnimationFrame = function() {};
expect(ctrl.isLoading).toBe(true);
expect(el.scope().foo).toBe(1);
});
it('should not run the event twice if isLoading is true', function() {
var onScrollSpy = jasmine.createSpy('onInfiniteScroll');
var el = setupJS('', { $onInfiniteScroll: onScrollSpy });
scrollTopValue = scrollTopMaxValue;
ionic.requestAnimationFrame = function(cb) { cb(); };
el.controller('$ionicScroll').$element.triggerHandler('scroll');

expect(el.hasClass('active')).toBe(true);
Expand All @@ -245,6 +266,7 @@ describe('ionicInfiniteScroll directive', function() {

el.controller('$ionicScroll').$element.triggerHandler('scroll');
expect(el.hasClass('active')).toBe(false);
ionic.requestAnimationFrame = function() {};
});

});
Expand All @@ -269,9 +291,11 @@ describe('ionicInfiniteScroll directive', function() {
var el = setupJS();
ctrl.isLoading = true;
el.addClass('active');
ionic.requestAnimationFrame = function(cb) { cb(); };
el.scope().$broadcast('scroll.infiniteScrollComplete');
expect(ctrl.isLoading).toBe(false);
expect(el.hasClass('active')).toBe(false);
ionic.requestAnimationFrame = function() {};
expect(el.controller('$ionicScroll').scrollView.resize).not.toHaveBeenCalled();
$timeout.flush();
expect(el.controller('$ionicScroll').scrollView.resize).toHaveBeenCalled();
Expand Down

0 comments on commit 65aa2af

Please sign in to comment.