Skip to content

Commit

Permalink
feat(uiSrefActive): allow active & active-eq on same element
Browse files Browse the repository at this point in the history
closes #1997
  • Loading branch information
joshuahiggins authored and christopherthielen committed Jul 8, 2015
1 parent 6ae6fe2 commit 3d7f267
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 23 deletions.
43 changes: 22 additions & 21 deletions src/stateDirectives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,13 +222,14 @@ $StateRefActiveDirective.$inject = ['$state', '$stateParams', '$interpolate'];
function $StateRefActiveDirective($state, $stateParams, $interpolate) {
return {
restrict: "A",
controller: ['$scope', '$element', '$attrs', function ($scope, $element, $attrs) {
var states = [], activeClass;
controller: ['$scope', '$element', '$attrs', '$timeout', function ($scope, $element, $attrs, $timeout) {
var states = [], activeClass, activeEqClass;

// There probably isn't much point in $observing this
// uiSrefActive and uiSrefActiveEq share the same directive object with some
// slight difference in logic routing
activeClass = $interpolate($attrs.uiSrefActiveEq || $attrs.uiSrefActive || '', false)($scope);
activeClass = $interpolate($attrs.uiSrefActive || '', false)($scope);
activeEqClass = $interpolate($attrs.uiSrefActiveEq || '', false)($scope);

// Allow uiSref to communicate with uiSrefActive[Equals]
this.$$addStateInfo = function (newState, newParams) {
Expand All @@ -246,34 +247,34 @@ function $StateRefActiveDirective($state, $stateParams, $interpolate) {

// Update route state
function update() {
if (anyMatch()) {
$element.addClass(activeClass);
} else {
$element.removeClass(activeClass);
}
}

function anyMatch() {
for (var i = 0; i < states.length; i++) {
if (isMatch(states[i].state, states[i].params)) {
return true;
if (anyMatch(states[i].state, states[i].params)) {
addClass($element, activeClass);
} else {
removeClass($element, activeClass);
}
}
return false;
}

function isMatch(state, params) {
if (typeof $attrs.uiSrefActiveEq !== 'undefined') {
return $state.is(state.name, params);
} else {
return $state.includes(state.name, params);
if (exactMatch(states[i].state, states[i].params)) {
addClass($element, activeEqClass);
} else {
removeClass($element, activeEqClass);
}
}
return $state.includes(state.name) && matchesParams();
}

function matchesParams() {
return !params || equalForKeys(params, $stateParams);
}

function addClass(el, className) { $timeout(function () { el.addClass(className); }); }

function removeClass(el, className) { el.removeClass(className); }

function anyMatch(state, params) { return $state.includes(state.name, params); }

function exactMatch(state, params) { return $state.is(state.name, params); }

}]
};
}
Expand Down
46 changes: 44 additions & 2 deletions test/stateDirectivesSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -426,8 +426,16 @@ describe('uiSrefActive', function() {
});
}));

beforeEach(inject(function($document) {
beforeEach(inject(function($document, $timeout) {
document = $document[0];
timeoutFlush = function () {
try {
$timeout.flush();
} catch (e) {
// Angular 1.0.8 throws 'No deferred tasks to be flushed' if there is nothing in queue.
// Behave as Angular >=1.1.5 and do nothing in such case.
}
}
}));

it('should update class for sibling uiSref', inject(function($rootScope, $q, $compile, $state) {
Expand All @@ -438,11 +446,12 @@ describe('uiSrefActive', function() {
expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('');
$state.transitionTo('contacts.item', { id: 1 });
$q.flush();

timeoutFlush();
expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('active');

$state.transitionTo('contacts.item', { id: 2 });
$q.flush();
timeoutFlush();
expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('');
}));

Expand All @@ -454,10 +463,12 @@ describe('uiSrefActive', function() {
expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('');
$state.transitionTo('contacts.item.detail', { id: 5, foo: 'bar' });
$q.flush();
timeoutFlush();
expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('active');

$state.transitionTo('contacts.item.detail', { id: 5, foo: 'baz' });
$q.flush();
timeoutFlush();
expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('');
}));

Expand All @@ -468,10 +479,12 @@ describe('uiSrefActive', function() {

$state.transitionTo('contacts.item.edit', { id: 1 });
$q.flush();
timeoutFlush();
expect(a.attr('class')).toMatch(/active/);

$state.transitionTo('contacts.item.edit', { id: 4 });
$q.flush();
timeoutFlush();
expect(a.attr('class')).not.toMatch(/active/);
}));

Expand All @@ -482,28 +495,51 @@ describe('uiSrefActive', function() {

$state.transitionTo('contacts.item', { id: 1 });
$q.flush();
timeoutFlush();
expect(a.attr('class')).toMatch(/active/);

$state.transitionTo('contacts.item.edit', { id: 1 });
$q.flush();
timeoutFlush();
expect(a.attr('class')).not.toMatch(/active/);
}));

it('should match on child states when active-equals and active-equals-eq is used', inject(function($rootScope, $q, $compile, $state, $timeout) {
template = $compile('<div><a ui-sref="contacts.item({ id: 1 })" ui-sref-active="active" ui-sref-active-eq="active-eq">Contacts</a></div>')($rootScope);
$rootScope.$digest();
var a = angular.element(template[0].getElementsByTagName('a')[0]);

$state.transitionTo('contacts.item', { id: 1 });
$q.flush();
timeoutFlush();
expect(a.attr('class')).toMatch(/active/);
expect(a.attr('class')).toMatch(/active-eq/);

$state.transitionTo('contacts.item.edit', { id: 1 });
$q.flush();
timeoutFlush();
expect(a.attr('class')).toMatch(/active/);
expect(a.attr('class')).not.toMatch(/active-eq/);
}));

it('should resolve relative state refs', inject(function($rootScope, $q, $compile, $state) {
el = angular.element('<section><div ui-view></div></section>');
template = $compile(el)($rootScope);
$rootScope.$digest();

$state.transitionTo('contacts');
$q.flush();
timeoutFlush();
expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('ng-scope');

$state.transitionTo('contacts.item', { id: 6 });
$q.flush();
timeoutFlush();
expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('ng-scope active');

$state.transitionTo('contacts.item', { id: 5 });
$q.flush();
timeoutFlush();
expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('ng-scope');
}));

Expand All @@ -516,10 +552,12 @@ describe('uiSrefActive', function() {

$state.transitionTo('contacts.item', { id: 1 });
$q.flush();
timeoutFlush();
expect(angular.element(template[0]).attr('class')).toBe('ng-scope active');

$state.transitionTo('contacts.item', { id: 2 });
$q.flush();
timeoutFlush();
expect(angular.element(template[0]).attr('class')).toBe('ng-scope active');
}));

Expand All @@ -534,10 +572,12 @@ describe('uiSrefActive', function() {

$state.transitionTo('contacts.item', { id: 1 });
$q.flush();
timeoutFlush();
expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('');

$state.transitionTo('contacts.lazy');
$q.flush();
timeoutFlush();
expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('active');
}));

Expand All @@ -552,10 +592,12 @@ describe('uiSrefActive', function() {

$state.transitionTo('contacts.item', { id: 1 });
$q.flush();
timeoutFlush();
expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('');

$state.transitionTo('contacts.lazy');
$q.flush();
timeoutFlush();
expect(angular.element(template[0].querySelector('a')).attr('class')).toBe('active');
}));
});

0 comments on commit 3d7f267

Please sign in to comment.