diff --git a/.gitignore b/.gitignore
index 703cf0cf0..de8839997 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,3 +27,5 @@ node_modules
npm-debug.log
lib
.bower
+
+.vscode
\ No newline at end of file
diff --git a/Gruntfile.js b/Gruntfile.js
index 54fff0373..f11ee97e7 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -139,7 +139,8 @@ module.exports = function (grunt) {
'lib/angular-bootstrap/ui-bootstrap-tpls.js',
'misc/angular-bootstrap-prettify.js',
'lib/lodash/lodash.min.js',
- 'dist/angular-patternfly.js'],
+ 'dist/angular-patternfly.js',
+ 'lib/angular-ui-router/release/angular-ui-router.min.js'],
html5Mode: false,
template: 'grunt-ngdocs-index.tmpl',
styles: ['lib/patternfly/dist/css/patternfly.css', 'lib/patternfly/dist/css/patternfly-additions.css',
diff --git a/bower.json b/bower.json
index 21928db93..feb9df276 100644
--- a/bower.json
+++ b/bower.json
@@ -45,6 +45,7 @@
"patternfly": "~3.14.0"
},
"devDependencies": {
- "angular-mocks": "1.3.0 - 1.5.*"
+ "angular-mocks": "1.3.0 - 1.5.*",
+ "angular-ui-router": "^0.3.2"
}
}
diff --git a/src/navigation/examples/vertical-navigation-basic.js b/src/navigation/examples/vertical-navigation-basic.js
new file mode 100644
index 000000000..9bcd5c4d8
--- /dev/null
+++ b/src/navigation/examples/vertical-navigation-basic.js
@@ -0,0 +1,567 @@
+/**
+ * @ngdoc directive
+ * @name patternfly.navigation.directive:pfVerticalNavigation - Basic
+ *
+ * @description
+ * Directive for vertical navigation. This sets up the nav bar header with the collapse button (hamburger) and the
+ * application brand image (or text) as well as the vertical navigation bar containing the navigation items. This
+ * directive supports primary, secondary, and tertiary navigation with options to allow pinning of the secondary and
+ * tertiary navigation menus as well as the option for persistent secondary menus.
+ *
+ * The remaining parts of the navbar header can be transcluded.
+ *
+ * Tha navigation items are marked active based on the current location and the href value for the item. If not using
+ * href's on the items to navigate, set update-active-items-on-click to "true".
+ *
+ * This directive works in conjunction with the main content container if the 'container-pf-nav-pf-vertical' class
+ * selector is added to the main content container.
+ *
+ * @param {string} brandSrc src for brand image
+ * @param {string} brandAlt Text for product name when brand image is not available
+ * @param {boolean} showBadges Flag if badges are used on navigation items, default: false
+ * @param {boolean} persistentSecondary Flag to use persistent secondary menus, default: false
+ * @param {boolean} hiddenIcons Flag to not show icons on the primary menu, default: false
+ * @param {array} items List of navigation items
+ *
+ *
.title - (string) Name of item to be displayed on the menu
+ *
.iconClass - (string) Classes for icon to be shown on the menu (ex. "fa fa-dashboard")
+ *
.href - (string) href link to navigate to on click
+ *
.children - (array) Submenu items (same structure as top level items)
+ *
.badges - (array) Badges to display for the item, badges with a zero count are not displayed.
+ *
+ *
.count - (number) Count to display in the badge
+ *
.iconClass - (string) Class to use for showing an icon before the count
+ *
.tooltip - (string) Tooltip to display for the badge
+ *
.badgeClass: - (string) Additional class(es) to add to the badge container
+ *
+ *
+ * @param {function} navigateCallback function(item) Callback method invoked on a navigation item click (one with no submenus)
+ * @param {function} itemClickCallback function(item) Callback method invoked on an item click
+ * @param {boolean} updateActiveItemsOnClick Flag if active items should be marked on click rather than on navigation change, default: false
+ * @param {boolean} ignoreMobile Flag if mobile state should be ignored (use only if absolutely necessary) default: false
+ *
+ * @example
+
+
+
\
+ ';
+ });
+
+
+ $(document).ready(function() {
+ $(document).on('click', '#showVerticalNav', function() {
+ $(document.getElementById("verticalNavLayout")).removeClass("hidden");
+ });
+ $(document).on('click', '#hideVerticalNav', function() {
+ $(document.getElementById("verticalNavLayout")).addClass("hidden");
+ });
+ });
+
+
+*/
\ No newline at end of file
diff --git a/src/navigation/examples/vertical-navigation-router.js b/src/navigation/examples/vertical-navigation-router.js
new file mode 100644
index 000000000..4366ee376
--- /dev/null
+++ b/src/navigation/examples/vertical-navigation-router.js
@@ -0,0 +1,159 @@
+/**
+ * @ngdoc directive
+ * @name patternfly.navigation.directive:pfVerticalNavigation - Router
+ *
+ * @description
+ * This example shows how to use pfVerticalNavigation with angular-ui-router's $states and uiSrefs.
+ *
+ * @param {string} brandSrc src for brand image
+ * @param {string} brandAlt Text for product name when brand image is not available
+ * @param {boolean} showBadges Flag if badges are used on navigation items, default: false
+ * @param {boolean} persistentSecondary Flag to use persistent secondary menus, default: false
+ * @param {boolean} hiddenIcons Flag to not show icons on the primary menu, default: false
+ * @param {array} items List of navigation items
+ *
+ *
.title - (string) Name of item to be displayed on the menu
+ *
.iconClass - (string) Classes for icon to be shown on the menu (ex. "fa fa-dashboard")
+ *
.href - (string) href link to navigate to on click
+ *
.children - (array) Submenu items (same structure as top level items)
+ *
.badges - (array) Badges to display for the item, badges with a zero count are not displayed.
+ *
+ *
.count - (number) Count to display in the badge
+ *
.iconClass - (string) Class to use for showing an icon before the count
+ *
.tooltip - (string) Tooltip to display for the badge
+ *
.badgeClass: - (string) Additional class(es) to add to the badge container
+ *
+ *
+ * @param {function} navigateCallback function(item) Callback method invoked on a navigation item click (one with no submenus)
+ * @param {function} itemClickCallback function(item) Callback method invoked on an item click
+ * @param {boolean} updateActiveItemsOnClick Flag if active items should be marked on click rather than on navigation change, default: false
+ * @param {boolean} ignoreMobile Flag if mobile state should be ignored (use only if absolutely necessary) default: false
+ *
+ * @example
+
+
+
'
+ });
+ })
+ .controller('vertNavWithRouterController', ['$scope',
+ function ($scope) {
+ $scope.navigationItems = [
+ {
+ title: "Dashboard",
+ iconClass: "fa fa-dashboard",
+ uiSref: "dashboard"
+ },
+ {
+ title: "Dolor",
+ iconClass : "fa fa-shield",
+ uiSref: "dolor"
+ },
+ {
+ title: "Ipsum",
+ iconClass : "fa fa-space-shuttle",
+ uiSref: "ipsum"
+ },
+ {
+ title: "Exit Demo"
+ }
+ ];
+ $scope.handleNavigateClickRouter = function (item) {
+ if (item.title === "Exit Demo") {
+ angular.element(document.querySelector("#verticalNavWithRouterLayout")).addClass("hidden");
+ }
+ };
+ }
+ ]);
+
+
+ $(document).ready(function() {
+ $(document).on('click', '#showVerticalNavWithRouter', function() {
+ $(document.getElementById("verticalNavWithRouterLayout")).removeClass("hidden");
+ });
+ $(document).on('click', '#hideVerticalNavWithRouter', function() {
+ $(document.getElementById("verticalNavWithRouterLayout")).addClass("hidden");
+ });
+ });
+
+
+*/
\ No newline at end of file
diff --git a/src/navigation/vertical-navigation-directive.js b/src/navigation/vertical-navigation-directive.js
index fc2992b2d..eef1520ca 100755
--- a/src/navigation/vertical-navigation-directive.js
+++ b/src/navigation/vertical-navigation-directive.js
@@ -1,573 +1,13 @@
-/**
- * @ngdoc directive
- * @name patternfly.navigation.directive:pfVerticalNavigation
- *
- * @description
- * Directive for vertical navigation. This sets up the nav bar header with the collapse button (hamburger) and the
- * application brand image (or text) as well as the vertical navigation bar containing the navigation items. This
- * directive supports primary, secondary, and tertiary navigation with options to allow pinning of the secondary and
- * tertiary navigation menus as well as the option for persistent secondary menus.
- *
- * The remaining parts of the navbar header can be transcluded.
- *
- * Tha navigation items are marked active based on the current location and the href value for the item. If not using
- * href's on the items to navigate, set update-active-items-on-click to "true".
- *
- * This directive works in conjunction with the main content container if the 'container-pf-nav-pf-vertical' class
- * selector is added to the main content container.
- *
- * @param {string} brandSrc src for brand image
- * @param {string} brandAlt Text for product name when brand image is not available
- * @param {boolean} showBadges Flag if badges are used on navigation items, default: false
- * @param {boolean} persistentSecondary Flag to use persistent secondary menus, default: false
- * @param {boolean} hiddenIcons Flag to not show icons on the primary menu, default: false
- * @param {array} items List of navigation items
- *
- *
.title - (string) Name of item to be displayed on the menu
- *
.iconClass - (string) Classes for icon to be shown on the menu (ex. "fa fa-dashboard")
- *
.href - (string) href link to navigate to on click
- *
.children - (array) Submenu items (same structure as top level items)
- *
.badges - (array) Badges to display for the item, badges with a zero count are not displayed.
- *
- *
.count - (number) Count to display in the badge
- *
.iconClass - (string) Class to use for showing an icon before the count
- *
.tooltip - (string) Tooltip to display for the badge
- *
.badgeClass: - (string) Additional class(es) to add to the badge container
- *
- *
- * @param {function} navigateCallback function(item) Callback method invoked on a navigation item click (one with no submenus)
- * @param {function} itemClickCallback function(item) Callback method invoked on an item click
- * @param {boolean} updateActiveItemsOnClick Flag if active items should be marked on click rather than on navigation change, default: false
- * @param {boolean} ignoreMobile Flag if mobile state should be ignored (use only if absolutely necessary) default: false
- *
- * @example
-
-
-
\
- ';
- });
-
-
- $(document).ready(function() {
- $(document).on('click', '#showVerticalNav', function() {
- $(document.getElementById("verticalNavLayout")).removeClass("hidden");
- });
- $(document).on('click', '#hideVerticalNav', function() {
- $(document.getElementById("verticalNavLayout")).addClass("hidden");
- });
- });
-
-
-*/
- angular.module('patternfly.navigation').directive('pfVerticalNavigation', ['$location', '$rootScope', '$window', '$document', '$timeout',
- function (location, rootScope, $window, $document, $timeout) {
+angular.module('patternfly.navigation').directive('pfVerticalNavigation', ['$location', '$rootScope', '$window', '$document', '$timeout', '$injector',
+ function (location, rootScope, $window, $document, $timeout, $injector) {
'use strict';
+ var $state;
+
+ // Optional dependency on $state
+ if ($injector.has("$state")) {
+ $state = $injector.get("$state");
+ }
+
return {
restrict: 'A',
scope: {
@@ -826,12 +266,23 @@
var navTo;
if (navItem) {
$scope.showMobileNav = false;
- navTo = navItem.href;
- if (navTo) {
- if (navTo.startsWith('#/')) {
- navTo = navTo.substring(2);
+ if (navItem.uiSref && navItem.href) {
+ throw new Error('Using both uiSref and href on an item is not supported.');
+ }
+ if (navItem.uiSref) {
+ if ($state === undefined) {
+ throw new Error('uiSref is defined on item, but no $state has been injected. ' +
+ 'Did you declare a dependency on "ui.router" module in your app?');
+ }
+ $state.go(navItem.uiSref, navItem.uiSrefOptions);
+ } else {
+ navTo = navItem.href;
+ if (navTo) {
+ if (navTo.startsWith('#/')) {
+ navTo = navTo.substring(2);
+ }
+ location.path(navTo);
}
- location.path(navTo);
}
if ($scope.navigateCallback) {
$scope.navigateCallback(navItem);
diff --git a/test/karma.conf.js b/test/karma.conf.js
index 6b8602239..34b8a6692 100644
--- a/test/karma.conf.js
+++ b/test/karma.conf.js
@@ -28,7 +28,8 @@ module.exports = function(config) {
'test/utils/*.js',
'test/wizard/script.js',
'test/**/*.spec.js',
- 'test/**/*.html'
+ 'test/**/*.html',
+ 'lib/angular-ui-router/release/angular-ui-router.min.js'
],
// list of files to exclude
diff --git a/test/navigation/vertical-navigation.spec.js b/test/navigation/vertical-navigation.spec.js
index f646f8fc2..1ec19783d 100644
--- a/test/navigation/vertical-navigation.spec.js
+++ b/test/navigation/vertical-navigation.spec.js
@@ -32,7 +32,7 @@ describe('Directive: pfVerticalNavigation', function () {
{
title: "Dolor",
iconClass : "fa fa-shield",
- href: "#/dolor",
+ uiSref: "dolor",
badges: [
{
count: 1283,
@@ -691,4 +691,134 @@ describe('Directive: pfVerticalNavigation', function () {
expect(badgesShown.length).toBe(0);
});
+ it('should throw and error if uiSref is used when $state is undefined', function () {
+ var wellDefinedItem = element.find('.nav-pf-vertical > .list-group > .list-group-item:nth-child(2) > a');
+ expect(function() {
+ wellDefinedItem.click();
+ }).toThrow(new Error("uiSref is defined on item, but no $state has been injected. Did you declare a dependency on \"ui.router\" module in your app?"));
+ });
});
+
+
+describe('Directive: pfVerticalNavigation with ui.router', function () {
+ // Setting up some dummy controllers and some dummy states
+ angular.module('mockApp', ['ui.router'])
+ .controller('Controller0', function() {
+ this.message = 'Page 0';
+ }).controller('Controller1', function() {
+ this.message = 'Page 1';
+ }).config(function($stateProvider, $urlRouterProvider) {
+ $urlRouterProvider.otherwise("/state0");
+
+ $stateProvider.state('state0', {
+ url: "/state0",
+ controller: 'Controller0',
+ controllerAs: 'vm'
+ }).state('state1', {
+ url: "/state1",
+ controller: 'Controller1',
+ controllerAs: 'vm'
+ });
+ });
+
+ var $state;
+ var $scope;
+ var $compile;
+ var element;
+ var isolateScope;
+
+ // load the controller's module
+ beforeEach(function () {
+ module('patternfly.navigation', 'patternfly.utils', 'navigation/vertical-navigation.html');
+ });
+
+ beforeEach(module('mockApp'));
+
+ beforeEach(inject(function (_$compile_, _$rootScope_, _$state_) {
+ $compile = _$compile_;
+ $scope = _$rootScope_;
+ $state = _$state_;
+
+ spyOn($state, 'go').and.callThrough();
+ }));
+
+ var compileHTML = function (markup, scope) {
+ element = angular.element(markup);
+ $compile(element)(scope);
+
+ scope.$digest();
+ isolateScope = element.isolateScope();
+ };
+
+ beforeEach(function () {
+ $scope.navigationItems = [
+ {
+ title: "Dashboard",
+ iconClass: "fa fa-dashboard",
+ uiSref: 'state1',
+ uiSrefOptions: 'testing'
+ },
+ {
+ title: "Dolor",
+ iconClass : "fa fa-shield",
+ href: "#/state2",
+ uiSref: 'state2',
+ badges: [
+ {
+ count: 1283,
+ tooltip: "Total number of items"
+ }
+ ]
+ }
+ ];
+
+ $scope.handleNavigateClick = function (item) {
+ $scope.navigateItem = item.title;
+ };
+
+ $scope.handleItemClick = function (item) {
+ $scope.clickItem = item.title;
+ };
+
+ var htmlTmp = '' +
+ '
' +
+ '
' +
+ ' ' +
+ '
' +
+ '
' +
+ '
' +
+ '
' +
+ ' ' +
+ '';
+
+ compileHTML(htmlTmp, $scope);
+ });
+
+ it('should trigger the $state.go() function when an item with ui-sref defined is clicked', function () {
+ var wellDefinedItem = element.find('.nav-pf-vertical > .list-group > .list-group-item:nth-child(1) > a');
+
+ expect($state.current.name).toBe("state0");
+
+ // Click dashboard item
+ wellDefinedItem.click();
+
+ expect($state.go).toHaveBeenCalledWith('state1','testing');
+
+ // Checking successful state transition
+ expect($state.current.name).toBe("state1");
+ expect($state.current.controller).toBe("Controller1");
+ });
+
+ it('should throw and error if both uiSref and href are used on an item', function () {
+ var badDefinedItem = element.find('.nav-pf-vertical > .list-group > .list-group-item:nth-child(2) > a');
+
+ expect( function() {
+ badDefinedItem.click();
+ }).toThrow(new Error('Using both uiSref and href on an item is not supported.'));
+ });
+});
+