Rewrite <panel> and <collapsible-group> directives
Use 'collapse' directive from angular-bootstrap inside them instead of hand-written bootstrap css transitions. Clicking on panel title no longer collapses/expands panel contents - this is done in order enable panel entity name in-line editing in next commit. Change-Id: Ifcc32cd74a5482a59b417333824522ebf48c73b5 Closes-Bug: #1411636 Closes-Bug: #1428719
This commit is contained in:
parent
6fa8b92891
commit
e5b8fb8a3a
|
@ -39,6 +39,7 @@ module.exports = function (config) {
|
|||
'merlin/static/merlin/js/libs/underscore/underscore-min.js',
|
||||
'merlin/static/merlin/js/libs/js-yaml/dist/js-yaml.min.js',
|
||||
'merlin/static/merlin/js/custom-libs/barricade.js',
|
||||
'merlin/static/merlin/js/custom-libs/ui-bootstrap-tpls-0.12.1.js',
|
||||
// explicitly require first module definition file to avoid errors
|
||||
'merlin/static/merlin/js/merlin.init.js',
|
||||
'merlin/static/merlin/js/merlin.*.js',
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
'use strict';
|
||||
|
||||
function disableClickDefaultBehaviour(element) {
|
||||
element.find('a[data-toggle="collapse"]')
|
||||
element.find('a[ng-click]')
|
||||
.on('click', function(e) {
|
||||
e.preventDefault();
|
||||
return true;
|
||||
|
@ -42,6 +42,7 @@
|
|||
},
|
||||
link: function(scope, element, attrs) {
|
||||
scope.removable = $parse(attrs.removable)();
|
||||
scope.isCollapsed = false;
|
||||
disableClickDefaultBehaviour(element);
|
||||
}
|
||||
}
|
||||
|
@ -58,6 +59,7 @@
|
|||
},
|
||||
link: function(scope, element, attrs) {
|
||||
disableClickDefaultBehaviour(element);
|
||||
scope.isCollapsed = false;
|
||||
if ( attrs.onAdd && attrs.additive !== 'false' ) {
|
||||
scope.additive = true;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular.module('merlin', [])
|
||||
angular.module('merlin', ['ui.bootstrap'])
|
||||
.config(function($interpolateProvider) {
|
||||
// Replacing the default angular symbol
|
||||
// allow us to mix angular with django templates
|
||||
|
|
|
@ -45,16 +45,6 @@
|
|||
background-color: inherit;
|
||||
border: none;
|
||||
padding-left: 20px;
|
||||
a:before {
|
||||
font-family: 'FontAwesome';
|
||||
content: "\f0d7";
|
||||
margin-left: -10px;
|
||||
float: left;
|
||||
color: grey;
|
||||
}
|
||||
a.collapsed:before {
|
||||
content: "\f0da";
|
||||
}
|
||||
}
|
||||
.panel-body {
|
||||
padding-left: 20px;
|
||||
|
@ -75,19 +65,9 @@
|
|||
padding-left: 5px;
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
}
|
||||
h5 {
|
||||
font-weight: bold;
|
||||
&[data-toggle="collapse"] {
|
||||
&:before {
|
||||
font-family: 'FontAwesome';
|
||||
content: "\f147";
|
||||
font-weight: normal;
|
||||
float: left;
|
||||
color: grey;
|
||||
}
|
||||
&.collapsed:before {
|
||||
content: "\f196";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
<div class="section">
|
||||
<div class="three-columns">
|
||||
<div class="section-heading three-columns">
|
||||
<div class="both-columns">
|
||||
<h5><a data-toggle="collapse" data-target="#elem-{$ $id $}" href="#">{$ title $}</a></h5>
|
||||
<h5><a ng-click="isCollapsed = !isCollapsed" class="collapse-entries" href="#">
|
||||
<i class="fa" ng-class="isCollapsed ? 'fa-plus-square-o' : 'fa-minus-square-o'"></i></a>
|
||||
{$ title $}</h5>
|
||||
</div>
|
||||
<div ng-show="additive" class="add-btn button-column">
|
||||
<div ng-show="additive" class="add-btn button-column add-entry">
|
||||
<button class="btn btn-default btn-sm pull-right" ng-click="onAdd()">
|
||||
<i class="fa fa-plus"></i></button>
|
||||
</div>
|
||||
<div ng-show="removable" class="add-btn button-column">
|
||||
<div ng-show="removable" class="add-btn button-column remove-entry">
|
||||
<a href="#" ng-click="onRemove()">
|
||||
<i class="fa fa-times-circle pull-right"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="elem-{$ $id $}" class="collapse in" ng-transclude>
|
||||
<div class="section-body" collapse="isCollapsed" ng-transclude>
|
||||
</div>
|
||||
</div>
|
|
@ -1,11 +1,11 @@
|
|||
<div class="panel panel-default merlin-panel">
|
||||
<div class="panel-heading" ng-show="title">
|
||||
<h4 class="panel-title">
|
||||
<a data-toggle="collapse" data-target="#elem-{$ $id $}" href="#">{$ title $}</a>
|
||||
<a ng-click="isCollapsed = !isCollapsed" href="#">
|
||||
<i class="fa fa-lg" ng-class="isCollapsed ? 'fa-caret-right' : 'fa-caret-down'"></i></a>
|
||||
{$ title $}
|
||||
<a href="#" ng-click="onRemove()"><i ng-show="removable" class="fa fa-times-circle pull-right"></i></a></h4>
|
||||
</div>
|
||||
<div id="elem-{$ $id $}" ng-class="title ? 'panel-collapse collapse in' : ''">
|
||||
<div class="panel-body" ng-transclude>
|
||||
</div>
|
||||
<div collapse="isCollapsed" class="panel-body" ng-transclude>
|
||||
</div>
|
||||
</div>
|
|
@ -42,24 +42,35 @@ describe('merlin directives', function() {
|
|||
}
|
||||
|
||||
function getPanelRemoveButton(panelElem) {
|
||||
var iTag = panelElem.find('i').eq(0);
|
||||
var iTag = panelElem.find('i').eq(1);
|
||||
return iTag.hasClass('fa-times-circle') && iTag;
|
||||
}
|
||||
|
||||
function getCollapseBtn(groupElem) {
|
||||
return groupElem.find('a').eq(0);
|
||||
}
|
||||
|
||||
function getPanelBody(panelElem) {
|
||||
var div = panelElem.children().children().eq(1).children();
|
||||
var div = panelElem.children().children().eq(1);
|
||||
return div.hasClass('panel-body') && div;
|
||||
}
|
||||
|
||||
function makePanelElem(contents) {
|
||||
return $compile('<panel ' + contents + '></panel>')($scope);
|
||||
var panel = $compile('<panel ' + contents + '></panel>')($scope);
|
||||
$scope.$digest();
|
||||
return panel;
|
||||
}
|
||||
|
||||
function makePanelWithInnerTags() {
|
||||
var element = $compile('<panel><span class="inner"></span></panel>')($scope);
|
||||
$scope.$digest();
|
||||
return element;
|
||||
}
|
||||
|
||||
it('shows panel heading when and only when title is passed via attr', function() {
|
||||
var title = 'My Panel',
|
||||
element1 = makePanelElem('title="' + title +'"'),
|
||||
element2 = makePanelElem('');
|
||||
$scope.$digest();
|
||||
|
||||
expect(getPanelHeading(element1).hasClass('ng-hide')).toBe(false);
|
||||
expect(element1.html()).toContain(title);
|
||||
|
@ -72,7 +83,6 @@ describe('merlin directives', function() {
|
|||
|
||||
element1 = makePanelElem('title="' + title +'" removable="true"');
|
||||
element2 = makePanelElem('title="' + title +'"');
|
||||
$scope.$digest();
|
||||
|
||||
expect(getPanelRemoveButton(element1).hasClass('ng-hide')).toBe(false);
|
||||
expect(getPanelRemoveButton(element2).hasClass('ng-hide')).toBe(true);
|
||||
|
@ -86,17 +96,37 @@ describe('merlin directives', function() {
|
|||
element1 = makePanelElem(
|
||||
'title="' + title +'" removable="true" on-remove="remove()"');
|
||||
element2 = makePanelElem('title="' + title +'" on-remove="remove()"');
|
||||
$scope.$digest();
|
||||
|
||||
expect(getPanelRemoveButton(element1).hasClass('ng-hide')).toBe(false);
|
||||
expect(getPanelRemoveButton(element2).hasClass('ng-hide')).toBe(true);
|
||||
});
|
||||
|
||||
it('contents are inserted into div.panel-body tag', function() {
|
||||
var element = $compile('<panel><span class="inner"></span></panel>')($scope);
|
||||
$scope.$digest();
|
||||
var panel = makePanelWithInnerTags();
|
||||
|
||||
expect(getPanelBody(element).find('span').hasClass('inner')).toBe(true);
|
||||
expect(getPanelBody(panel).find('span').hasClass('inner')).toBe(true);
|
||||
});
|
||||
|
||||
it('starts as being expanded', function() {
|
||||
var panel = makePanelWithInnerTags(),
|
||||
body = getPanelBody(panel);
|
||||
|
||||
expect(body.hasClass('collapse')).toBe(true);
|
||||
expect(body.hasClass('in')).toBe(true);
|
||||
});
|
||||
|
||||
it('starts to collapse after pressing on triangle next to group title', function() {
|
||||
// NOTE(tsufiev): I wasn't able to test the final .collapse state (without .in)
|
||||
// most probably due to transition from .collapse.in -> .collapsing -> .collapse
|
||||
// is made with means of CSS, not
|
||||
var element = makePanelWithInnerTags(),
|
||||
body = getPanelBody(element),
|
||||
link = getCollapseBtn(element);
|
||||
|
||||
link.triggerHandler('click');
|
||||
|
||||
expect(body.hasClass('collapse')).toBe(false);
|
||||
expect(body.hasClass('collapsing')).toBe(true);
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -104,30 +134,65 @@ describe('merlin directives', function() {
|
|||
describe('<collapsible-group>', function() {
|
||||
function getGroupBody(groupElem) {
|
||||
var div = groupElem.children().children().eq(1);
|
||||
return div.hasClass('collapse') && div;
|
||||
return div.hasClass('section-body') && div;
|
||||
}
|
||||
|
||||
function getGroupRemoveBtn(groupElem) {
|
||||
var div = groupElem.children().children().eq(0).children().eq(2);
|
||||
return div.hasClass('add-btn') && div;
|
||||
return div.hasClass('remove-entry') && div;
|
||||
}
|
||||
|
||||
function getGroupAddBtn(groupElem) {
|
||||
var div = groupElem.children().children().eq(0).children().eq(1);
|
||||
return div.hasClass('add-btn') && div;
|
||||
return div.hasClass('add-entry') && div;
|
||||
}
|
||||
|
||||
function getCollapseBtn(groupElem) {
|
||||
return groupElem.children().children().eq(0).children().eq(0).find('a');
|
||||
}
|
||||
|
||||
function makeGroupElement(contents) {
|
||||
return $compile(
|
||||
var group = $compile(
|
||||
'<collapsible-group ' + contents + '></collapsible-group>')($scope);
|
||||
$scope.$digest();
|
||||
return group;
|
||||
}
|
||||
|
||||
function makeGroupWithInnerTags() {
|
||||
var group = $compile(
|
||||
'<collapsible-group><span class="inner"></span></collapsible-group>'
|
||||
)($scope);
|
||||
$scope.$digest();
|
||||
return group;
|
||||
}
|
||||
|
||||
it('starts as being expanded', function() {
|
||||
var element = makeGroupWithInnerTags(),
|
||||
body = getGroupBody(element);
|
||||
|
||||
expect(body.hasClass('collapse')).toBe(true);
|
||||
expect(body.hasClass('in')).toBe(true);
|
||||
});
|
||||
|
||||
it('starts to collapse after pressing on triangle next to group title', function() {
|
||||
// NOTE(tsufiev): I wasn't able to test the final .collapse state (without .in)
|
||||
// most probably due to transition from .collapse.in -> .collapsing -> .collapse
|
||||
// is made with means of CSS, not
|
||||
var element = makeGroupWithInnerTags(),
|
||||
body = getGroupBody(element),
|
||||
link = getCollapseBtn(element);
|
||||
|
||||
link.triggerHandler('click');
|
||||
|
||||
expect(body.hasClass('collapse')).toBe(false);
|
||||
expect(body.hasClass('collapsing')).toBe(true);
|
||||
});
|
||||
|
||||
it('requires to specify just `on-remove` to make group removable', function() {
|
||||
var element1, element2;
|
||||
$scope.remove = function() {};
|
||||
element1 = makeGroupElement('');
|
||||
element2 = makeGroupElement('on-remove="remove()"');
|
||||
$scope.$digest();
|
||||
|
||||
expect(getGroupRemoveBtn(element1).hasClass('ng-hide')).toBe(true);
|
||||
expect(getGroupRemoveBtn(element2).hasClass('ng-hide')).toBe(false);
|
||||
|
@ -138,7 +203,6 @@ describe('merlin directives', function() {
|
|||
$scope.add = function() {};
|
||||
element1 = makeGroupElement('');
|
||||
element2 = makeGroupElement('on-add="add()"');
|
||||
$scope.$digest();
|
||||
|
||||
expect(getGroupAddBtn(element1).hasClass('ng-hide')).toBe(true);
|
||||
expect(getGroupAddBtn(element2).hasClass('ng-hide')).toBe(false);
|
||||
|
@ -148,15 +212,12 @@ describe('merlin directives', function() {
|
|||
var element;
|
||||
$scope.add = function() {};
|
||||
element = makeGroupElement('on-add="add()" additive="false"');
|
||||
$scope.$digest();
|
||||
|
||||
expect(getGroupAddBtn(element).hasClass('ng-hide')).toBe(true);
|
||||
});
|
||||
|
||||
it('contents are inserted into div.collapse tag', function() {
|
||||
var element = $compile(
|
||||
'<collapsible-group><span class="inner"></span></collapsible-group>')($scope);
|
||||
$scope.$digest();
|
||||
var element = makeGroupWithInnerTags();
|
||||
|
||||
expect(getGroupBody(element).find('span').hasClass('inner')).toBe(true);
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue