Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

fix($animate): correctly handle pin() host elements #13783

Closed
wants to merge 1 commit into from
Closed
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
23 changes: 10 additions & 13 deletions src/ngAnimate/animateQueue.js
Original file line number Diff line number Diff line change
Expand Up @@ -624,25 +624,22 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
// there is no need to continue traversing at this point
if (parentAnimationDetected && animateChildren === false) break;

if (!rootElementDetected) {
// angular doesn't want to attempt to animate elements outside of the application
// therefore we need to ensure that the rootElement is an ancestor of the current element
rootElementDetected = isMatchingElement(parentElement, $rootElement);
if (!rootElementDetected) {
parentHost = parentElement.data(NG_ANIMATE_PIN_DATA);
if (parentHost) {
parentElement = parentHost;
rootElementDetected = true;
}
}
}

if (!bodyElementDetected) {
// we also need to ensure that the element is or will be apart of the body element
// otherwise it is pointless to even issue an animation to be rendered
bodyElementDetected = isMatchingElement(parentElement, bodyElement);
}

if (!rootElementDetected) {
// If no rootElement is detected, check if the parentElement is pinned to another element
parentHost = parentElement.data(NG_ANIMATE_PIN_DATA);
if (parentHost) {
// The pin target element becomes the next parent element
parentElement = parentHost;
continue;
}
}

parentElement = parentElement.parent();
}

Expand Down
106 changes: 84 additions & 22 deletions test/ngAnimate/animateSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1483,10 +1483,14 @@ describe("animations", function() {
return new $$AnimateRunner();
};
});

return function($animate) {
$animate.enabled(true);
};
}));

it('should throw if the arguments are not elements',
inject(function($animate, $compile, $document, $rootScope, $rootElement) {
inject(function($animate, $rootElement) {

var element = jqLite('<div></div>');

Expand All @@ -1505,7 +1509,7 @@ describe("animations", function() {
they('should animate an element inside a pinned element that is the $prop element',
['same', 'parent', 'grandparent'],
function(elementRelation) {
inject(function($animate, $compile, $document, $rootElement, $rootScope) {
inject(function($animate, $document, $rootElement, $rootScope) {

var pinElement, animateElement;

Expand Down Expand Up @@ -1543,34 +1547,92 @@ describe("animations", function() {
});
});

it('should adhere to the disabled state of the hosted parent when an element is pinned',
inject(function($animate, $compile, $document, $rootElement, $rootScope) {
they('should not animate an element when the pinned ($prop) element, is pinned to an element that is not a child of the $rootElement',
['same', 'parent', 'grandparent'],
function(elementRelation) {
inject(function($animate, $document, $rootElement, $rootScope) {

var pinElement, animateElement, pinTargetElement = jqLite('<div></div>');

var innerParent = jqLite('<div></div>');
jqLite($document[0].body).append(innerParent);
innerParent.append($rootElement);
var innerChild = jqLite('<div></div>');
$rootElement.append(innerChild);
var innerParent = jqLite('<div></div>');
jqLite($document[0].body).append(innerParent);
innerParent.append($rootElement);

var element = jqLite('<div></div>');
jqLite($document[0].body).append(element);
switch (elementRelation) {
case 'same':
pinElement = jqLite('<div id="animate"></div>');
break;
case 'parent':
pinElement = jqLite('<div><div id="animate"></div></div>');
break;
case 'grandparent':
pinElement = jqLite('<div><div><div id="animate"></div></div></div>');
break;
}

$animate.pin(element, innerChild);
// Append both the pin element and the pinTargetElement outside the app root
jqLite($document[0].body).append(pinElement);
jqLite($document[0].body).append(pinTargetElement);

$animate.enabled(innerChild, false);
animateElement = jqLite($document[0].getElementById('animate'));

$animate.addClass(element, 'blue');
$rootScope.$digest();
expect(capturedAnimation).toBeFalsy();
$animate.addClass(animateElement, 'red');
$rootScope.$digest();
expect(capturedAnimation).toBeFalsy();

$animate.enabled(innerChild, true);
$animate.pin(pinElement, pinTargetElement);

$animate.addClass(element, 'red');
$rootScope.$digest();
expect(capturedAnimation).toBeTruthy();
$animate.addClass(animateElement, 'blue');
$rootScope.$digest();
expect(capturedAnimation).toBeFalsy();

dealoc(element);
}));
dealoc(pinElement);
});
});

they('should adhere to the disabled state of the hosted parent when the $prop element is pinned',
['same', 'parent', 'grandparent'],
function(elementRelation) {
inject(function($animate, $document, $rootElement, $rootScope) {

var pinElement, animateElement, pinHostElement = jqLite('<div></div>');

var innerParent = jqLite('<div></div>');
jqLite($document[0].body).append(innerParent);
innerParent.append($rootElement);

switch (elementRelation) {
case 'same':
pinElement = jqLite('<div id="animate"></div>');
break;
case 'parent':
pinElement = jqLite('<div><div id="animate"></div></div>');
break;
case 'grandparent':
pinElement = jqLite('<div><div><div id="animate"></div></div></div>');
break;
}

$rootElement.append(pinHostElement);
jqLite($document[0].body).append(pinElement);
animateElement = jqLite($document[0].getElementById('animate'));

$animate.pin(pinElement, pinHostElement);
$animate.enabled(pinHostElement, false);

$animate.addClass(animateElement, 'blue');
$rootScope.$digest();
expect(capturedAnimation).toBeFalsy();

$animate.enabled(pinHostElement, true);

$animate.addClass(animateElement, 'red');
$rootScope.$digest();
expect(capturedAnimation).toBeTruthy();

dealoc(pinElement);
});
});
});

describe('callbacks', function() {
Expand Down