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

Commit

Permalink
fix(subheader): fix hidden directives inside of sticky clone.
Browse files Browse the repository at this point in the history
* Currently the subheader was not working properly with `ng-if`'s inside of its content.
  This was caused by a wrong recompile of the already compiled transcluded clone.

* This commit also fixes the meaningless / useless tests.
   Before we never imported the `subheader` module into its tests.
   Also we never tested the $mdSticky service together with the subheader, because
   we had a really ugly mock for the `$mdSticky` service, which just tested the call arguments.

Fixes #8500. Closes #8504.

Closes #8505
  • Loading branch information
devversion authored and ThomasBurleson committed May 19, 2016
1 parent 715dd34 commit 44140ce
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 43 deletions.
18 changes: 9 additions & 9 deletions src/components/subheader/subheader.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,17 +77,17 @@ function MdSubheaderDirective($mdSticky, $compile, $mdTheming, $mdUtil) {
// compiled clone below will only be a comment tag (since they replace their elements with
// a comment) which cannot be properly passed to the $mdSticky; so we wrap it in our own
// DIV to ensure we have something $mdSticky can use
var wrapperHtml = '<div class="_md-subheader-wrapper">' + outerHTML + '</div>';
var stickyClone = $compile(wrapperHtml)(scope);
var wrapper = angular.element('<div class="_md-subheader-wrapper">' + outerHTML + '</div>');

// Append the sticky
$mdSticky(scope, element, stickyClone);
// Immediately append our transcluded clone into the wrapper.
// We don't have to recompile the element again, because the clone is already
// compiled in it's transclusion scope. If we recompile the outerHTML of the new clone, we would lose
// our ngIf's and other previous registered bindings / properties.
getContent(wrapper).append(clone);

// Delay initialization until after any `ng-if`/`ng-repeat`/etc has finished before
// attempting to create the clone
$mdUtil.nextTick(function() {
getContent(stickyClone).append(clone);
});
// Make the element sticky and provide the stickyClone our self, to avoid recompilation of the subheader
// element.
$mdSticky(scope, element, wrapper);
});
}
}
Expand Down
120 changes: 86 additions & 34 deletions src/components/subheader/subheader.spec.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,31 @@
describe('mdSubheader', function() {
var $mdStickyMock,
basicHtml = '<md-subheader>Hello world!</md-header>',
pageScope, element, controller;

var BASIC_SUBHEADER = '<md-subheader>Hello world!</md-subheader>';
var pageScope, element, controller, contentElement;
var $rootScope, $timeout, $exceptionHandler;

beforeEach(module('material.components.subheader', function($provide) {
$mdStickyMock = function() {
$mdStickyMock.args = Array.prototype.slice.call(arguments);
};
$provide.value('$mdSticky', $mdStickyMock);
}));
beforeEach(module('material.components.subheader'));

beforeEach(inject(function(_$rootScope_, _$timeout_, _$exceptionHandler_) {
$rootScope = _$rootScope_;
$timeout = _$timeout_;
$exceptionHandler = _$exceptionHandler_;
beforeEach(inject(function($injector) {
$rootScope = $injector.get('$rootScope');
$timeout = $injector.get('$timeout');
$exceptionHandler = $injector.get('$exceptionHandler');
}));


it('should have `._md` class indicator', inject(function() {
build('<div><md-subheader>Hello {{ to }}!</md-subheader></div>');
pageScope.to = 'world';
pageScope.$digest();
build(BASIC_SUBHEADER);

expect(element.children().hasClass('_md')).toBe(true);
expect(element.hasClass('_md')).toBe(true);
}));


it('preserves content', function() {
build('<div><md-subheader>Hello {{ to }}!</md-subheader></div>');
build(
'<div>' +
'<md-subheader>Hello {{ to }}!</md-subheader>' +
'</div>'
);

pageScope.to = 'world';
pageScope.$digest();

Expand All @@ -39,23 +35,22 @@ describe('mdSubheader', function() {
});

it('implements $mdSticky', function() {
build(basicHtml);
build(BASIC_SUBHEADER);

expect($mdStickyMock.args[0]).toBe(pageScope);
var cloneScope = element.scope();

expect(cloneScope).toBe(pageScope);
});

it('applies the theme to the header and clone', function() {
build('<div md-theme="somethingElse">' + basicHtml + '</div>');

// Grab the real element
var element = $mdStickyMock.args[1];
build('<div md-theme="somethingElse">' + BASIC_SUBHEADER + '</div>');

// The subheader now wraps the clone in a DIV in case of ng-if usage, so we have to search for
// the proper element.
var clone = angular.element($mdStickyMock.args[2][0].querySelector('.md-subheader'));
var clone = getCloneElement();

expect(element.hasClass('md-somethingElse-theme')).toBe(true);
expect(clone.hasClass('md-somethingElse-theme')).toBe(true);
expect(getSubheader().classList).toContain('md-somethingElse-theme');
expect(getSubheader(clone).classList).toContain('md-somethingElse-theme');
});

it('applies the proper scope to the clone', function() {
Expand All @@ -64,11 +59,10 @@ describe('mdSubheader', function() {
pageScope.to = 'world';
pageScope.$apply();

var element = $mdStickyMock.args[1];
var clone = $mdStickyMock.args[2];
var clone = getCloneElement();

expect(element.text().trim()).toEqual('Hello world!');
expect(clone.text().trim()).toEqual('Hello world!');
expect(getSubheader().textContent.trim()).toEqual('Hello world!');
expect(getSubheader(clone).textContent.trim()).toEqual('Hello world!');
});

it('supports ng-if', function() {
Expand All @@ -78,6 +72,39 @@ describe('mdSubheader', function() {
expect(element[0].querySelectorAll('.md-subheader').length).toEqual(1);
});

it('should support ng-if inside of stickyClone', function() {
build(
'<div>' +
'<md-subheader>' +
'Foo' +
'<span ng-if="isBar">Bar</span>' +
'</md-subheader>' +
'</div>'
);

var clone = getCloneElement()[0];

expect(clone.textContent.trim()).toBe('Foo');

pageScope.$apply('isBar = true');

expect(clone.textContent.trim()).toBe('FooBar');
});

it('should work with a ng-if directive inside of the stickyClone', function() {
build(
'<div>' +
'<md-subheader>' +
'<span ng-repeat="item in [0, 1, 2, 3]">{{ item }}</span>' +
'</md-subheader>' +
'</div>'
);

var cloneContent = getCloneElement()[0].querySelector('._md-subheader-content');

expect(cloneContent.children.length).toBe(4);
});

it('supports ng-repeat', function() {
build('<div><md-subheader ng-repeat="i in [1,2,3]">Test {{i}}</md-subheader></div>');

Expand All @@ -86,13 +113,38 @@ describe('mdSubheader', function() {
});

function build(template) {
inject(function($compile) {
inject(function($compile, $timeout) {
pageScope = $rootScope.$new();
element = $compile(template)(pageScope);

contentElement = $compile('<md-content>' + template + '</md-content>')(pageScope);

// Flush the timeout, which prepends the sticky clone to the md-content.
$timeout.flush();

// When the contentElement only has only one children then the current
// browser supports sticky elements natively.
if (contentElement.children().length === 1) {
element = getCloneElement();
} else {
// When the browser doesn't support sticky elements natively we will have a sticky clone.
// The sticky clone element will be always prepended, which means that we have to use the child
// at the second position.
element = contentElement.children().eq(1);
}

controller = element.controller('mdSubheader');

pageScope.$apply();
$timeout.flush();
});
}

function getSubheader(el) {
return (el || element)[0].querySelector('.md-subheader');
}

function getCloneElement() {
// The clone element will be always prepended, which means that we have to get the child at index zero.
return contentElement.children().eq(0);
}
});

0 comments on commit 44140ce

Please sign in to comment.