Skip to content

Commit

Permalink
Implement drag & reorder functionality & new API
Browse files Browse the repository at this point in the history
NOTES:

- uses html5sortable for drag & reorder
- manual sorting button is located after A-Z button in toolbar
- NEW API method TestPlan.update_case_order()
- Manual reorder & API require the testcases.change_testcaseplan
  permission

- toolbarDropdowns() is called from within toolbarEvents() b/c
  it really doesn't make sense to have this as stand-alone function
- toolbarEvents() is moved within the TestCases.filter() callback
  b/c we need the list of test cases to be fully initialized so we
  can get the initial sorting order and use it later

WARNINGS:

- may want to unbind the on-click event for rows if users are
  having problems with the rows expanding while dragging them
  • Loading branch information
atodorov committed Oct 10, 2020
1 parent c3d514d commit 91e05bf
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 6 deletions.
1 change: 1 addition & 0 deletions tcms/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"dependencies": {
"bootstrap-switch": "3.3.4",
"html5sortable": "0.9.18",
"marked": "1.2.0",
"patternfly": "3.59.5",
"prismjs": "1.21.0",
Expand Down
21 changes: 21 additions & 0 deletions tcms/rpc/api/testplan.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

'add_case',
'remove_case',
'update_case_order',

'add_tag',
'remove_tag',
Expand Down Expand Up @@ -202,6 +203,26 @@ def remove_case(plan_id, case_id):
TestCasePlan.objects.filter(case=case_id, plan=plan_id).delete()


@permissions_required('testcases.change_testcaseplan')
@rpc_method(name='TestPlan.update_case_order')
def update_case_order(plan_id, case_id, sortkey):
"""
.. function:: RPC TestPlan.update_case_order(plan_id, case_id, sortkey)
Update sortkey which controls display order of the given test case in
the given test plan.
:param plan_id: PK of TestPlan holding the selected TestCase
:type plan_id: int
:param case_id: PK of TestCase to be modified
:type case_id: int
:param sortkey: Ordering value, e.g. 10, 20, 30
:type sortkey: int
:raises PermissionDenied: if missing *testcases.delete_testcaseplan* permission
"""
TestCasePlan.objects.filter(case=case_id, plan=plan_id).update(sortkey=sortkey)


@permissions_required('attachments.view_attachment')
@rpc_method(name='TestPlan.list_attachments')
def list_attachments(plan_id, **kwargs):
Expand Down
41 changes: 37 additions & 4 deletions tcms/testplans/static/testplans/js/get.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,14 @@ $(document).ready(function() {
drawTestCases(allTestCases, testPlanId, permissions);
treeViewBind();

// b/c treeViewBind() will modfy handlers/visibility for both
// b/c treeViewBind() will modify handlers/visibility for both
// test plan family tree and the test cases tree
adjustTestPlanFamilyTree();
});

toolbarDropdowns();
toolbarEvents(testPlanId, permissions);
// b/c drag & reorder needs the initial order of test cases and
// they may not be fully loaded when Sortable() is initialized!
toolbarEvents(testPlanId, permissions);
});

collapseDocumentText();
});
Expand Down Expand Up @@ -278,6 +279,7 @@ function attachEvents(testCases, testPlanId, permissions) {
}

function toolbarEvents(testPlanId, permissions) {
toolbarDropdowns();

$('.js-checkbox-toolbar').click(function(ev) {
const isChecked = ev.target.checked;
Expand Down Expand Up @@ -320,6 +322,37 @@ function toolbarEvents(testPlanId, permissions) {
sortTestCases();
});


// always initialize the sortable list however you can only
// move items using the handle icon on the left which becomes
// visible only when the manual sorting button is clicked
sortable('#testcases-list', {
handle: '.handle',
itemSerializer: (serializedItem, sortableContainer) => {
return parseInt(serializedItem.node.getAttribute('data-testcase-pk'))
}
});

// IMPORTANT: this is not empty b/c sortable() is initialized *after*
// all of the test cases have been rendered !!!
const initialOrder = sortable('#testcases-list', 'serialize')[0].items;

$('.js-toolbar-manual-sort').click(function(event) {
$(this).blur();
$('.js-toolbar-manual-sort').find('span').toggleClass(['fa-sort', 'fa-check-square']);
$('.js-testcase-sort-handler, .js-testcase-expand-arrow, .js-testcase-checkbox').toggleClass('hidden');

const currentOrder = sortable('#testcases-list', 'serialize')[0].items;

// rows have been rearranged and the results must be committed to the DB
if (currentOrder.join() !== initialOrder.join()) {
currentOrder.forEach(function(tc_pk, index) {
jsonRPC('TestPlan.update_case_order', [testPlanId, tc_pk, index*10], function(result) {});
});
}
});


$('.js-toolbar-priority').click(function(ev) {
let selectedCases = getSelectedTestCases();

Expand Down
15 changes: 13 additions & 2 deletions tcms/testplans/templates/testplans/get.html
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,12 @@ <h2 class="card-pf-title">
<span class="fa fa-sort-alpha-asc" data-order="1"></span>
<span class="fa fa-sort-alpha-desc hidden" data-order="-1"></span>
</button>

{% if perms.testcases.change_testcaseplan %}
<button class="btn btn-link js-toolbar-manual-sort" type="button" title="{% trans 'Re-order cases' %}">
<span class="fa fa-sort"></span>
</button>
{% endif %}
</div>
</div>
<div class="form-group">
Expand Down Expand Up @@ -195,6 +201,7 @@ <h2 class="card-pf-title">
{% if perms.testcases.change_testcase %}
<li><a href="#">{% trans 'Reviewer' %}</a></li>
{% endif %}

</ul>
</div>

Expand Down Expand Up @@ -240,10 +247,13 @@ <h2 class="card-pf-title">
<div class="list-group-item js-testcase-row" data-testcase-pk="">
<div class="list-group-item-header">
<div class="list-view-pf-main-info">
<div class="list-view-pf-left">
<div class="list-view-pf-left hidden js-testcase-sort-handler">
<span class="fa fa-sort handle"></span>
</div>
<div class="list-view-pf-left js-testcase-expand-arrow">
<span class="fa fa-angle-right"></span>
</div>
<div class="list-view-pf-checkbox" style="margin-top: 0; margin-bottom: 0">
<div class="list-view-pf-checkbox js-testcase-checkbox" style="margin-top: 0; margin-bottom: 0">
<input type="checkbox">
</div>
<div class="list-view-pf-actions" style="margin-top: 0; margin-bottom: 0;">
Expand Down Expand Up @@ -359,6 +369,7 @@ <h2 class="card-pf-title">
<li data-filter-type="reviewer"><a>{% trans 'Reviewer' %}</a></li>
</template>

<script src="{% static 'html5sortable/dist/html5sortable.min.js' %}"></script>
<script src="{% static 'typeahead.js/dist/typeahead.jquery.min.js' %}"></script>
<script src="{% static 'js/utils.js' %}"></script>
<script src="{% static 'js/jsonrpc.js' %}"></script>
Expand Down

0 comments on commit 91e05bf

Please sign in to comment.