Skip to content

Commit f77f057

Browse files
committed
Merge pull request #213 from pineconellc/fix_sticky_topbar_position
Fix sticky topbar position
2 parents 839768f + a0dd5f8 commit f77f057

File tree

2 files changed

+148
-120
lines changed

2 files changed

+148
-120
lines changed

src/topbar/test/topbar.spec.js

+139-109
Original file line numberDiff line numberDiff line change
@@ -1,131 +1,161 @@
1-
describe('topbar directive', function () {
2-
var $rootScope, element, $document;
1+
describe('topbar directive', function() {
2+
var $rootScope, $document, $compile, $window, element, containerElement;
33

4-
var setMobile = function(windowMock){
5-
windowMock.matchMedia.andReturn({matches: false});
6-
};
7-
8-
var setDesktop = function(windowMock){
9-
windowMock.matchMedia.andReturn({matches: true});
10-
};
4+
beforeEach(function() {
5+
$window = angular.extend(window, {
6+
pageYOffset: 0,
7+
scrollTo: jasmine.createSpy('scrollTo'),
8+
matchMedia: jasmine.createSpy('matchMedia').andReturn({matches: false}),
9+
});
1110

12-
beforeEach(module('template/topbar/top-bar.html'));
13-
beforeEach(module('template/topbar/has-dropdown.html'));
14-
beforeEach(module('template/topbar/toggle-top-bar.html'));
15-
beforeEach(module('template/topbar/top-bar-section.html'));
16-
beforeEach(module('template/topbar/top-bar-dropdown.html'));
11+
module(
12+
'mm.foundation.topbar',
13+
'template/topbar/top-bar.html',
14+
'template/topbar/has-dropdown.html',
15+
'template/topbar/toggle-top-bar.html',
16+
'template/topbar/top-bar-section.html',
17+
'template/topbar/top-bar-dropdown.html',
18+
{$window: $window});
1719

18-
beforeEach(module('mm.foundation.topbar', function($provide){
19-
window.matchMedia = jasmine.createSpy('matchMedia').andReturn({matches: false});
20-
$provide.value('$window', window);
21-
}));
22-
23-
beforeEach(inject(function(_$compile_, _$rootScope_, _$document_) {
24-
$compile = _$compile_;
25-
$rootScope = _$rootScope_;
26-
$document = _$document_;
27-
28-
element = $compile(
29-
'<body>' +
30-
'<div class="fixed" id="container">' +
31-
'<top-bar scrolltop="false">' +
32-
'<ul class="title-area">' +
33-
'<li class="name">' +
34-
'<h1><a href="#">My Site</a></h1>' +
35-
'</li>' +
36-
'<li toggle-top-bar id="menu-toggle" class="menu-icon">' +
37-
'<a href="#">Menu</a>' +
38-
'</li>' +
39-
'</ul>' +
40-
'<top-bar-section id="top-section">' +
41-
'<ul class="right">' +
42-
'<li class="active">' +
43-
'<a href="#">Active</a>' +
44-
'</li>' +
45-
'<li has-dropdown>' +
46-
'<a id="dropdown" href="#">Dropdown</a>' +
47-
'<ul top-bar-dropdown>' +
48-
'<li><a id="alink" href="#">First link in dropdown</a></li>' +
49-
'</ul>' +
50-
'</li>' +
51-
'</ul>' +
52-
'<ul class="left">' +
53-
'<li><a href="#">Left</a></li>' +
54-
'</ul>' +
55-
'</top-bar-section>' +
56-
'</top-bar>' +
57-
'<div>Content</div>' +
58-
'</div>' +
59-
'</body>')($rootScope);
60-
$('body', $document).append(element);
61-
$rootScope.$digest();
62-
}));
20+
inject(function(_$compile_, _$rootScope_, _$document_) {
21+
$compile = _$compile_;
22+
$rootScope = _$rootScope_;
23+
$document = _$document_;
24+
});
25+
});
6326

6427
beforeEach(inject(function ($rootScope) {
6528
this.addMatchers({
6629
toHaveDropDownsOpen: function(noOfLevels) {
67-
var dropDownDomEls = this.actual.find('li.has-dropdown.not-click');
30+
var dropDownDomEls = element.find('li.has-dropdown.not-click');
6831
return dropDownDomEls.length === noOfLevels;
6932
},
7033
toHaveMobileMenuOpen: function() {
71-
var els = this.actual.find('nav.top-bar.expanded');
72-
return !!els.length;
34+
return element.hasClass("expanded");
7335
}
7436
});
7537
}));
7638

77-
it('has a "top-bar" css class', function() {
78-
expect(element.children('nav')).toHaveClass('top-bar');
79-
});
39+
var setMobile = function() {
40+
$window.matchMedia.andReturn({matches: false});
41+
};
8042

81-
it('has a drop down open on large screen', inject(function($window) {
82-
setDesktop($window);
83-
$('#dropdown', element).trigger('mouseenter');
84-
expect($window.matchMedia).toHaveBeenCalled();
85-
expect(element).toHaveDropDownsOpen(1);
86-
}));
43+
var setDesktop = function() {
44+
$window.matchMedia.andReturn({matches: true});
45+
};
8746

88-
it('has no drop downs open on small screen', inject(function($window) {
89-
setMobile($window);
90-
$('#dropdown', element).trigger('mouseenter');
91-
expect($window.matchMedia).toHaveBeenCalled();
92-
expect(element).toHaveDropDownsOpen(0);
93-
}));
47+
var compileDirective = function(markup) {
48+
if (angular.isUndefined(markup)) {
49+
markup =
50+
'<div class="fixed" id="container">' +
51+
'<top-bar scrolltop="false">' +
52+
'<ul class="title-area">' +
53+
'<li class="name">' +
54+
'<h1><a href="#">My Site</a></h1>' +
55+
'</li>' +
56+
'<li toggle-top-bar id="menu-toggle" class="menu-icon">' +
57+
'<a href="#">Menu</a>' +
58+
'</li>' +
59+
'</ul>' +
60+
'<top-bar-section id="top-section">' +
61+
'<ul class="right">' +
62+
'<li class="active">' +
63+
'<a href="#">Active</a>' +
64+
'</li>' +
65+
'<li has-dropdown>' +
66+
'<a id="dropdown" href="#">Dropdown</a>' +
67+
'<ul top-bar-dropdown>' +
68+
'<li><a id="alink" href="#">First link in dropdown</a></li>' +
69+
'</ul>' +
70+
'</li>' +
71+
'</ul>' +
72+
'<ul class="left">' +
73+
'<li><a href="#">Left</a></li>' +
74+
'</ul>' +
75+
'</top-bar-section>' +
76+
'</top-bar>' +
77+
'<div>Content</div>' +
78+
'</div>';
79+
}
9480

95-
it('has no mobile menu opening on large screen', inject(function($window) {
96-
setDesktop($window);
97-
$('#menu-toggle', element).trigger('click');
98-
expect($window.matchMedia).toHaveBeenCalled();
99-
expect(element).not.toHaveMobileMenuOpen();
100-
}));
81+
containerElement = $compile(markup)($rootScope);
82+
$('body', $document).append(element);
83+
$rootScope.$digest();
84+
element = containerElement.find("nav");
85+
};
10186

102-
it('opens and closes mobile menu on small screen', inject(function($window) {
103-
setMobile($window);
104-
$('#menu-toggle', element).trigger('click');
105-
expect($window.matchMedia).toHaveBeenCalled();
106-
expect(element).toHaveMobileMenuOpen();
107-
$('#menu-toggle', element).trigger('click');
108-
expect(element).not.toHaveMobileMenuOpen();
109-
}));
87+
describe("basic behavior", function() {
88+
beforeEach(function() {
89+
compileDirective();
90+
});
11091

111-
it('opens the submenu when a dropdown is clicked on mobile', inject(function($window) {
112-
setMobile($window);
113-
$('#menu-toggle', element).trigger('click');
114-
expect($window.matchMedia).toHaveBeenCalled();
115-
var beforeHeight = $('.top-bar', element)[0].style.height;
116-
$('#dropdown', element).trigger('click');
117-
var afterHeight = $('.top-bar', element)[0].style.height;
118-
expect(afterHeight).toNotEqual(beforeHeight);
119-
expect($('#top-section', element)[0].style.left).toEqual('-100%');
120-
}));
92+
it('has a "top-bar" css class', function() {
93+
expect(element).toHaveClass('top-bar');
94+
});
95+
96+
it('has a drop down open on large screen', inject(function($window) {
97+
setDesktop();
98+
$('#dropdown', element).trigger('mouseenter');
99+
expect($window.matchMedia).toHaveBeenCalled();
100+
expect(element).toHaveDropDownsOpen(1);
101+
}));
102+
103+
it('has no drop downs open on small screen', inject(function($window) {
104+
setMobile();
105+
$('#dropdown', element).trigger('mouseenter');
106+
expect($window.matchMedia).toHaveBeenCalled();
107+
expect(element).toHaveDropDownsOpen(0);
108+
}));
121109

122-
// it('has f-topbar-fixed class on body after link is clicked on a fixed mobile topbar', inject(function($window) {
123-
// setMobile($window);
124-
// $('#menu-toggle', element).trigger('click');
125-
// expect($window.matchMedia).toHaveBeenCalled();
126-
// $('#dropdown', element).trigger('click');
127-
// $('#alink', element).trigger('click');
128-
// expect($('body', $document)).toHaveClass('f-topbar-fixed');
129-
// }));
110+
it('has no mobile menu opening on large screen', inject(function($window) {
111+
setDesktop($window);
112+
$('#menu-toggle', element).trigger('click');
113+
expect($window.matchMedia).toHaveBeenCalled();
114+
expect(element).not.toHaveMobileMenuOpen();
115+
}));
130116

117+
it('opens and closes mobile menu on small screen', inject(function($window) {
118+
setMobile();
119+
$('#menu-toggle', element).trigger('click');
120+
expect($window.matchMedia).toHaveBeenCalled();
121+
expect(element).toHaveMobileMenuOpen();
122+
$('#menu-toggle', element).trigger('click');
123+
expect(element).not.toHaveMobileMenuOpen();
124+
}));
125+
126+
it('opens the submenu when a dropdown is clicked on mobile', inject(function($window) {
127+
var topSection = element.find('#top-section');
128+
var menuToggle = element.find('#menu-toggle');
129+
setMobile();
130+
expect(element.hasClass("expanded")).toBe(false);
131+
menuToggle.trigger('click');
132+
expect($window.matchMedia).toHaveBeenCalled();
133+
expect(element.hasClass("expanded")).toBe(true);
134+
}));
135+
});
136+
137+
describe("sticky", function() {
138+
var markup;
139+
140+
beforeEach(function() {
141+
markup =
142+
'<div class="sticky">' +
143+
'<top-bar>' +
144+
'<ul class="title-area">' +
145+
'<li class="name">' +
146+
'<h1><a href="#">My Site</a></h1>' +
147+
'</li>' +
148+
'<li toggle-top-bar id="menu-toggle" class="menu-icon">' +
149+
'<a href="#">Menu</a>' +
150+
'</li>' +
151+
'</ul>' +
152+
'</top-bar>' +
153+
'</div>';
154+
});
155+
156+
it('does not apply the fixed class on initial load', function() {
157+
compileDirective(markup);
158+
expect(containerElement.hasClass('fixed')).toBe(false);
159+
});
160+
});
131161
});

src/topbar/topbar.js

+9-11
Original file line numberDiff line numberDiff line change
@@ -150,14 +150,13 @@ angular.module("mm.foundation.topbar", ['mm.foundation.mediaQueries'])
150150
return;
151151
}
152152

153-
var $class = angular.element($document[0].querySelector('.' + scope.settings.stickyClass));
154153
var distance = stickyoffset;
155154

156-
if ($window.pageYOffset > distance && !$class.hasClass('fixed')) {
157-
$class.addClass('fixed');
155+
if ($window.pageYOffset > distance && !topbarContainer.hasClass('fixed')) {
156+
topbarContainer.addClass('fixed');
158157
body.css('padding-top', scope.originalHeight + 'px');
159-
} else if ($window.pageYOffset <= distance && $class.hasClass('fixed')) {
160-
$class.removeClass('fixed');
158+
} else if ($window.pageYOffset <= distance && topbarContainer.hasClass('fixed')) {
159+
topbarContainer.removeClass('fixed');
161160
body.css('padding-top', '');
162161
}
163162
};
@@ -193,7 +192,7 @@ angular.module("mm.foundation.topbar", ['mm.foundation.mediaQueries'])
193192

194193
var expand = (on === undefined) ? !topbar.hasClass('expanded') : on;
195194

196-
if (expand){
195+
if (expand) {
197196
topbar.addClass('expanded');
198197
}
199198
else {
@@ -233,7 +232,7 @@ angular.module("mm.foundation.topbar", ['mm.foundation.mediaQueries'])
233232
if(topbarContainer.hasClass('fixed') || isSticky() ) {
234233
scope.stickyTopbar = true;
235234
scope.height = topbarContainer[0].offsetHeight;
236-
var stickyoffset = topbarContainer[0].getBoundingClientRect().top;
235+
var stickyoffset = topbarContainer[0].getBoundingClientRect().top + $window.pageYOffset;
237236
} else {
238237
scope.height = topbar[0].offsetHeight;
239238
}
@@ -248,13 +247,12 @@ angular.module("mm.foundation.topbar", ['mm.foundation.mediaQueries'])
248247
}
249248
});
250249

251-
252250
angular.element($window).bind('resize', onResize);
253-
angular.element($window).bind("scroll", onScroll);
251+
angular.element($window).bind('scroll', onScroll);
254252

255253
scope.$on('$destroy', function() {
256-
angular.element($window).unbind("scroll", onResize);
257-
angular.element($window).unbind("resize", onScroll);
254+
angular.element($window).unbind('scroll', onResize);
255+
angular.element($window).unbind('resize', onScroll);
258256
});
259257

260258
if (topbarContainer.hasClass('fixed')) {

0 commit comments

Comments
 (0)