This repository has been archived by the owner on Dec 1, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
348 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
<pre>{{pane.content}}</pre> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
<div class="bs-docs-section" ng-controller="TabDemoCtrl"> | ||
|
||
<div class="page-header"> | ||
<h1 id="tabs">Tabs <a class="small" href="//github.com/mgcrea/angular-strap/blob/master/src/tab/tab.js" target="_blank">tab.js</a> | ||
</h1> | ||
<code>mgcrea.ngStrap.tab</code> | ||
</div> | ||
|
||
|
||
<h2 id="tabs-examples">Examples</h2> | ||
<p>Add quick, dynamic tab functionality to transition through panes of local content.</p> | ||
|
||
<h3>Live demo <a class="small" href="#" target="_blank"><i class="fa fa-edit" data-title="edit in plunker" data-placement="right" bs-tooltip></i></a> | ||
</h2> | ||
<pre class="bs-example-scope">$scope.tabs = {{tabs | json}};</pre> | ||
<div class="bs-example" append-source> | ||
<!-- ngModel is optional --> | ||
<div ng-model="tabs.activeTab" bs-tabs="tabs"> | ||
</div> | ||
</div> | ||
<div class="bs-example" style="padding-bottom: 24px;"> | ||
<label>activeTab:</label> | ||
<strong class="text-danger">{{tabs.activeTab}}</strong> | ||
<div class="btn-group" ng-model="tabs.activeTab" bs-radio-group> | ||
<label class="btn btn-default"> | ||
<input type="radio" class="btn btn-default" value="0">First</label> | ||
<label class="btn btn-default"> | ||
<input type="radio" class="btn btn-default" value="1">Second</label> | ||
<label class="btn btn-default"> | ||
<input type="radio" class="btn btn-default" value="2">Third</label> | ||
</div> | ||
</div> | ||
</h3> | ||
|
||
<h2 id="tabs-usage">Usage</h2> | ||
<p>Append a <code>bs-tabs</code>attribute to any element to enable the directive.</p> | ||
|
||
<div class="bs-callout bs-callout-info"> | ||
<h4>Custom animations</h4> | ||
<p>Pane animation is done with <code>ngClass('active')</code> callbacks and require custom CSS.</p> | ||
<pre class="bs-exemple-code"> | ||
<code class="css" highlight-block> | ||
.tabs.animation-fade .tab-pane { | ||
opacity: 1; | ||
transition: opacity .3s ease; | ||
min-height:60px; | ||
&.active-add { | ||
display: block; | ||
opacity: 0; | ||
&.active-add-active { | ||
opacity: 1; | ||
} | ||
} | ||
&.active-remove { | ||
display: none; | ||
} | ||
} | ||
</code> | ||
</pre> | ||
</div> | ||
|
||
<h3>Options</h3> | ||
<p>Options can be passed via data attributes or as an <a href="http://docs.angularjs.org/guide/expression">AngularJS expression</a> to evaluate as an object on | ||
<code>bs-tabs</code>. For data attributes, append the option name to <code>data-</code>, as in <code>data-animation=""</code>.</p> | ||
<div class="table-responsive"> | ||
<table class="table table-bordered table-striped"> | ||
<thead> | ||
<tr> | ||
<th style="width: 100px;">Name</th> | ||
<th style="width: 100px;">type</th> | ||
<th style="width: 50px;">default</th> | ||
<th>description</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
<tr> | ||
<td>animation</td> | ||
<td>string</td> | ||
<td>animation-fade</td> | ||
<td>apply a CSS animation to the popover with <code>ngAnimate</code></td> | ||
</tr> | ||
<tr> | ||
<td>template</td> | ||
<td>path</td> | ||
<td>false</td> | ||
<td> | ||
<p>If a remote URL is provided, overrides the default template</p> | ||
</td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
</div> | ||
<div class="callout callout-info"> | ||
<h4>Default options</h4> | ||
<p>You can override global defaults for the plugin with <code>$tabProvider.defaults</code></p> | ||
<div class="highlight"> | ||
<pre class="bs-exemple-code"> | ||
<code class="javascript" highlight-block> | ||
angular.module('myApp') | ||
.config(function($tabProvider) { | ||
angular.extend($tabProvider.defaults, { | ||
animation: 'animation-flipX' | ||
}); | ||
}) | ||
</code> | ||
</pre> | ||
</div> | ||
</div> | ||
|
||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
'use strict'; | ||
|
||
angular.module('mgcrea.ngStrapDocs') | ||
|
||
.controller('TabDemoCtrl', function($scope, $templateCache) { | ||
|
||
$scope.tabs = [ | ||
{title:'Home', content: 'Raw denim you probably haven\'t heard of them jean shorts Austin. Nesciunt tofu stumptown aliqua, retro synth master cleanse. Mustache cliche tempor, williamsburg carles vegan helvetica.'}, | ||
{title:'Profile', content: 'Food truck fixie locavore, accusamus mcsweeney\'s marfa nulla single-origin coffee squid. Exercitation +1 labore velit, blog sartorial PBR leggings next level wes anderson artisan four loko farm-to-table craft beer twee.'}, | ||
{title:'About', template: 'tab/docs/pane.tpl.demo.html', content: 'Etsy mixtape wayfarers, ethical wes anderson tofu before they sold out mcsweeney\'s organic lomo retro fanny pack lo-fi farm-to-table readymade.'} | ||
]; | ||
|
||
$scope.tabs.activeTab = 1; | ||
|
||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
'use strict'; | ||
|
||
angular.module('mgcrea.ngStrap.tab', []) | ||
|
||
.run(function($templateCache) { | ||
|
||
$templateCache.put('$pane', '{{pane.content}}'); | ||
|
||
var template = '<ul class="nav nav-tabs">' + | ||
'<li ng-repeat="pane in panes" ng-class="{active:$index==active}">' + | ||
'<a data-toggle="tab" ng-click="setActive($index, $event)" data-index="{{$index}}">{{pane.title}}</a>' + | ||
'</li>' + | ||
'</ul>' + | ||
'<div class="tab-content">' + | ||
'<div ng-repeat="pane in panes" class="tab-pane" ng-class="[$index==active?\'active\':\'\']" ng-include="pane.template || \'$pane\'"></div>' + | ||
'</div>'; | ||
|
||
$templateCache.put('$tabs', template); | ||
|
||
}) | ||
|
||
.provider('$tab', function() { | ||
|
||
var defaults = this.defaults = { | ||
animation: 'animation-fade', | ||
template: '$tabs' | ||
}; | ||
|
||
this.$get = function() { | ||
return {defaults: defaults}; | ||
}; | ||
|
||
}) | ||
|
||
.directive('bsTabs', function($window, $animate, $tab) { | ||
|
||
var defaults = $tab.defaults; | ||
|
||
return { | ||
restrict: 'EAC', | ||
scope: true, | ||
require: '?ngModel', | ||
templateUrl: function(element, attr) { | ||
return attr.template || defaults.template; | ||
}, | ||
link: function postLink(scope, element, attr, controller) { | ||
|
||
// Directive options | ||
var options = defaults; | ||
angular.forEach(['animation'/*, 'template'*/], function(key) { | ||
if(angular.isDefined(attr[key])) options[key] = attr[key]; | ||
}); | ||
|
||
// Require scope as an object | ||
attr.bsTabs && scope.$watch(attr.bsTabs, function(newValue, oldValue) { | ||
scope.panes = newValue; | ||
}, true); | ||
|
||
// Add base class | ||
element.addClass('tabs'); | ||
|
||
// Support animations | ||
if(options.animation) { | ||
element.addClass(options.animation); | ||
} | ||
|
||
scope.active = scope.activePane = 0; | ||
// view -> model | ||
scope.setActive = function(index, ev) { | ||
scope.active = index; | ||
if(controller) { | ||
controller.$setViewValue(index); | ||
} | ||
}; | ||
|
||
// model -> view | ||
if(controller) { | ||
controller.$render = function() { | ||
scope.active = controller.$modelValue * 1; | ||
}; | ||
} | ||
|
||
} | ||
}; | ||
|
||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
./../../../test/.jshintrc |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
'use strict'; | ||
|
||
describe('tab', function () { | ||
|
||
var $compile, $templateCache, scope, sandboxEl; | ||
|
||
beforeEach(module('ngSanitize')); | ||
beforeEach(module('mgcrea.ngStrap.tab')); | ||
|
||
beforeEach(inject(function (_$rootScope_, _$compile_, _$templateCache_) { | ||
scope = _$rootScope_.$new(); | ||
sandboxEl = $('<div>').attr('id', 'sandbox').appendTo($('body')); | ||
$compile = _$compile_; | ||
$templateCache = _$templateCache_; | ||
})); | ||
|
||
afterEach(function() { | ||
scope.$destroy(); | ||
sandboxEl.remove(); | ||
}); | ||
|
||
// Templates | ||
|
||
var templates = { | ||
'default': { | ||
scope: {tab: {active: 1, counter: 0},tabs: [ | ||
{title:'Home', content: 'Raw denim you probably haven\'t heard of...'}, | ||
{title:'Profile', content: 'Food truck fixie locavore...'}, | ||
{title:'About', content: 'Etsy mixtape wayfarers...'} | ||
]}, | ||
element: '<div bs-tabs="tabs"></div>' | ||
}, | ||
'binding-ngModel': { | ||
element: '<div ng-model="tab.active" bs-tabs="tabs"></div>' | ||
}, | ||
'options-animation': { | ||
element: '<div data-animation="animation-flipX" bs-tabs="tabs"></div>' | ||
}, | ||
'options-template': { | ||
element: '<div data-template="custom" bs-tabs="tabs"></div>' | ||
} | ||
}; | ||
|
||
function compileDirective(template, locals) { | ||
template = templates[template]; | ||
angular.extend(scope, template.scope || templates['default'].scope, locals); | ||
var element = $(template.element).appendTo(sandboxEl); | ||
element = $compile(element)(scope); | ||
scope.$digest(); | ||
return jQuery(element[0]); | ||
} | ||
|
||
// Tests | ||
|
||
describe('with default template', function () { | ||
|
||
it('should correctly compile inner content', function() { | ||
var elm = compileDirective('default'); | ||
expect(sandboxEl.find('.nav-tabs > li').length).toBe(scope.tabs.length); | ||
expect(sandboxEl.find('.nav-tabs > li:eq(0)').text()).toBe(scope.tabs[0].title); | ||
expect(sandboxEl.find('.tab-content > .tab-pane').length).toBe(scope.tabs.length); | ||
expect(sandboxEl.find('.tab-content > .tab-pane:eq(0)').text()).toBe(scope.tabs[0].content); | ||
}); | ||
|
||
it('should navigate between panes on click', function() { | ||
var elm = compileDirective('default'); | ||
expect(sandboxEl.find('.nav-tabs > li.active').text()).toBe(scope.tabs[0].title); | ||
expect(sandboxEl.find('.tab-content > .tab-pane.active').text()).toBe(scope.tabs[0].content); | ||
sandboxEl.find('.nav-tabs > li:eq(1) > a').triggerHandler('click'); | ||
expect(sandboxEl.find('.nav-tabs > li.active').text()).toBe(scope.tabs[1].title); | ||
expect(sandboxEl.find('.tab-content > .tab-pane.active').text()).toBe(scope.tabs[1].content); | ||
sandboxEl.find('.nav-tabs > li:eq(0) > a').triggerHandler('click'); | ||
expect(sandboxEl.find('.nav-tabs > li.active').text()).toBe(scope.tabs[0].title); | ||
expect(sandboxEl.find('.tab-content > .tab-pane.active').text()).toBe(scope.tabs[0].content); | ||
}); | ||
|
||
}); | ||
|
||
describe('data-binding', function() { | ||
|
||
it('should correctly apply model changes to the view', function() { | ||
var elm = compileDirective('binding-ngModel'); | ||
expect(sandboxEl.find('.nav-tabs > li.active').index()).toBe(scope.tab.active); | ||
expect(sandboxEl.find('.tab-content > .tab-pane.active').index()).toBe(scope.tab.active); | ||
scope.tab.active = 2; | ||
scope.$digest(); | ||
expect(sandboxEl.find('.nav-tabs > li.active').index()).toBe(scope.tab.active); | ||
expect(sandboxEl.find('.tab-content > .tab-pane.active').index()).toBe(scope.tab.active); | ||
}); | ||
|
||
it('should correctly apply view changes to the model', function() { | ||
var elm = compileDirective('binding-ngModel'); | ||
sandboxEl.find('.nav-tabs > li:eq(0) > a').triggerHandler('click'); | ||
expect(scope.tab.active).toBe(0); | ||
}); | ||
|
||
}); | ||
|
||
describe('options', function () { | ||
|
||
describe('animation', function () { | ||
|
||
it('should default to `animation-fade` animation', function() { | ||
var elm = compileDirective('default'); | ||
expect(sandboxEl.children('.tabs').hasClass('animation-fade')).toBeTruthy(); | ||
}); | ||
|
||
it('should support custom animation', function() { | ||
var elm = compileDirective('options-animation'); | ||
expect(sandboxEl.children('.tabs').hasClass('animation-flipX')).toBeTruthy(); | ||
}); | ||
|
||
}); | ||
|
||
describe('template', function () { | ||
|
||
it('should support custom template', function() { | ||
$templateCache.put('custom', '<div class="tabs"><div class="tab-pane" ng-repeat="pane in panes">foo: {{pane.content}}</div></div>'); | ||
var elm = compileDirective('options-template'); | ||
expect(sandboxEl.find('.tab-pane:eq(0)').text()).toBe('foo: ' + scope.tabs[0].content); | ||
}); | ||
|
||
it('should support template with ngClick', function() { | ||
$templateCache.put('custom', '<div class="tabs"><div class="tab-pane" ng-repeat="pane in panes"><a class="btn" ng-click="tab.counter=tab.counter+1">{{foo.title}}</a></div></div>'); | ||
var elm = compileDirective('options-template'); | ||
angular.element(elm[0]).triggerHandler('mouseenter'); | ||
expect(angular.element(sandboxEl.find('.tab-pane:eq(0) > .btn')[0]).triggerHandler('click')); | ||
expect(scope.tab.counter).toBe(1); | ||
}); | ||
|
||
}); | ||
|
||
}); | ||
|
||
}); |