From 65aa2af95d801ebcb2a688b1dbb91f769f09a7f0 Mon Sep 17 00:00:00 2001 From: perry Date: Tue, 20 Jan 2015 21:44:01 -0600 Subject: [PATCH] feat(infiniteScroll): upgrade infinite scrolling spinner to use new ion-spinner directive --- .../controller/infiniteScrollController.js | 6 ++- js/angular/directive/infiniteScroll.js | 10 ++++- scss/_scaffolding.scss | 24 +++++------- .../angular/directive/infiniteScroll.unit.js | 38 +++++++++++++++---- 4 files changed, 53 insertions(+), 25 deletions(-) diff --git a/js/angular/controller/infiniteScrollController.js b/js/angular/controller/infiniteScrollController.js index 9f7dce53074..e1b0a2c7da3 100644 --- a/js/angular/controller/infiniteScrollController.js +++ b/js/angular/controller/infiniteScrollController.js @@ -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() { diff --git a/js/angular/directive/infiniteScroll.js b/js/angular/directive/infiniteScroll.js index 163186af5ab..2425861c979 100644 --- a/js/angular/directive/infiniteScroll.js +++ b/js/angular/directive/infiniteScroll.js @@ -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 @@ -67,7 +70,10 @@ IonicModule return { restrict: 'E', require: ['?^$ionicScroll', 'ionInfiniteScroll'], - template: '', + template: function($element, $attrs){ + if ($attrs.icon) return ''; + return ''; + }, scope: true, controller: '$ionInfiniteScroll', link: function($scope, $element, $attrs, ctrls) { diff --git a/scss/_scaffolding.scss b/scss/_scaffolding.scss index 533d180b45f..bdbaa8b209b 100644 --- a/scss/_scaffolding.scss +++ b/scss/_scaffolding.scss @@ -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; diff --git a/test/unit/angular/directive/infiniteScroll.unit.js b/test/unit/angular/directive/infiniteScroll.unit.js index 1cfded6455e..5b2eacb972b 100644 --- a/test/unit/angular/directive/infiniteScroll.unit.js +++ b/test/unit/angular/directive/infiniteScroll.unit.js @@ -1,6 +1,5 @@ describe('ionicInfiniteScroll directive', function() { beforeEach(module('ionic')); - var scrollTopValue = 50; var scrollTopMaxValue = 60; var scrollLeftValue = 101; @@ -37,6 +36,7 @@ describe('ionicInfiniteScroll directive', function() { $element: angular.element('
') }); $compile(element)(scope); + ionic.requestAnimationFrame = function() {}; ctrl = element.controller('ionInfiniteScroll'); scope.$apply(); }); @@ -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) { @@ -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); @@ -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); }); @@ -194,42 +204,52 @@ 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); }); @@ -237,6 +257,7 @@ describe('ionicInfiniteScroll directive', 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); @@ -245,6 +266,7 @@ describe('ionicInfiniteScroll directive', function() { el.controller('$ionicScroll').$element.triggerHandler('scroll'); expect(el.hasClass('active')).toBe(false); + ionic.requestAnimationFrame = function() {}; }); }); @@ -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();