Make table BatchAction help text configurable
Now the BatchAction help text is consistent. This patch make the BatchAction/DeleteAction help text configurable, so horizon user can understand the BatchAction (mostly dangerous) more clearly. Implements blueprint: make-batchaction-help-text-configurable Anoter blueprint: add-batchactions-help-text do the "add appropriate help text" work. Change-Id: I08c219cf0b918a28da60ca74830a1e17f5453a2f
This commit is contained in:
parent
4eb1c20951
commit
a65258f77e
@ -114,7 +114,10 @@ all of the boilerplate associated with writing these types of actions and
|
||||
provides consistent error handling, logging, and user-facing interaction.
|
||||
|
||||
It is worth noting that ``BatchAction`` and ``DeleteAction`` are extensions
|
||||
of the standard ``Action`` class.
|
||||
of the standard ``Action`` class. Some ``BatchAction`` or ``DeleteAction``
|
||||
classes may cause some unrecoverable results, like deleted images or
|
||||
unrecoverable instances. It may be helpful to specify specific help_text to
|
||||
explain the concern to the user, such as "Deleted images are not recoverable".
|
||||
|
||||
Preemptive actions
|
||||
------------------
|
||||
|
@ -174,6 +174,7 @@ horizon.datatables.confirm = function (action) {
|
||||
$modal_parent = $(action).closest('.modal'),
|
||||
name_array = [],
|
||||
closest_table_id, action_string, name_string,
|
||||
help_text,
|
||||
title, body, modal, form;
|
||||
if($action.hasClass("disabled")) {
|
||||
return;
|
||||
@ -198,8 +199,9 @@ horizon.datatables.confirm = function (action) {
|
||||
}
|
||||
name_string = interpolate(gettext("You have selected %s. "), [name_string]);
|
||||
}
|
||||
help_text = $(action).attr("help_text");
|
||||
title = interpolate(gettext("Confirm %s"), [action_string]);
|
||||
body = name_string + gettext("Please confirm your selection. This action cannot be undone.");
|
||||
body = name_string + gettext("Please confirm your selection. ") + help_text;
|
||||
modal = horizon.modals.create(title, body, action_string);
|
||||
modal.modal();
|
||||
if($modal_parent.length) {
|
||||
|
@ -635,8 +635,15 @@ class BatchAction(Action):
|
||||
|
||||
Optional location to redirect after completion of the delete
|
||||
action. Defaults to the current page.
|
||||
|
||||
.. attribute:: help_text
|
||||
|
||||
Optional message for providing an appropriate help text for
|
||||
the horizon user.
|
||||
"""
|
||||
|
||||
help_text = _("This action cannot be undone.")
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(BatchAction, self).__init__(**kwargs)
|
||||
|
||||
@ -696,6 +703,8 @@ class BatchAction(Action):
|
||||
# Keep record of successfully handled objects
|
||||
self.success_ids = []
|
||||
|
||||
self.help_text = kwargs.get('help_text', self.help_text)
|
||||
|
||||
def _allowed(self, request, datum=None):
|
||||
# Override the default internal action method to prevent batch
|
||||
# actions from appearing on tables with no data.
|
||||
|
@ -1,5 +1,5 @@
|
||||
{% if action.method != "GET" %}
|
||||
<button {{ action.attr_string|safe }} name="action" value="{{ action.table.name }}__{{ action.name }}__{{ row_id }}" type="submit">{{ action.verbose_name }}</button>
|
||||
<button {{ action.attr_string|safe }} {% if action.help_text %}help_text="{{ action.help_text }}"{% endif %} name="action" value="{{ action.table.name }}__{{ action.name }}__{{ row_id }}" type="submit">{{ action.verbose_name }}</button>
|
||||
{% else %}
|
||||
<a href='{{ action.bound_url }}' {{ action.attr_string|safe }}>{{ action.verbose_name }}</a>
|
||||
{% endif %}
|
||||
|
@ -1,5 +1,5 @@
|
||||
{% if action.method != "GET" %}
|
||||
<button {{ action.attr_string|safe }} name="action" value="{{ action.get_param_name }}" type="submit">
|
||||
<button {{ action.attr_string|safe }} name="action" value="{{ action.get_param_name }}" {% if action.help_text %}help_text="{{ action.help_text }}"{% endif %} type="submit">
|
||||
{% if action.icon != None %}<span class="fa fa-{{ action.icon }}"></span> {% endif %}
|
||||
{% if action.handles_multiple %}{{ action.verbose_name_plural }}{% else %}{{ action.verbose_name }}{% endif %}
|
||||
</button>
|
||||
|
@ -132,6 +132,13 @@ class MyBatchAction(tables.BatchAction):
|
||||
pass
|
||||
|
||||
|
||||
class MyBatchActionWithHelpText(MyBatchAction):
|
||||
name = "batch_help"
|
||||
help_text = "this is help."
|
||||
action_present = "BatchHelp"
|
||||
action_past = "BatchedHelp"
|
||||
|
||||
|
||||
class MyToggleAction(tables.BatchAction):
|
||||
name = "toggle"
|
||||
action_present = ("Down", "Up")
|
||||
@ -237,8 +244,10 @@ class MyTable(tables.DataTable):
|
||||
columns = ('id', 'name', 'value', 'optional', 'status')
|
||||
row_class = MyRow
|
||||
column_class = MyColumn
|
||||
table_actions = (MyFilterAction, MyAction, MyBatchAction)
|
||||
row_actions = (MyAction, MyLinkAction, MyBatchAction, MyToggleAction)
|
||||
table_actions = (MyFilterAction, MyAction, MyBatchAction,
|
||||
MyBatchActionWithHelpText)
|
||||
row_actions = (MyAction, MyLinkAction, MyBatchAction, MyToggleAction,
|
||||
MyBatchActionWithHelpText)
|
||||
|
||||
|
||||
class MyServerFilterTable(MyTable):
|
||||
@ -250,7 +259,8 @@ class MyServerFilterTable(MyTable):
|
||||
row_class = MyRow
|
||||
column_class = MyColumn
|
||||
table_actions = (MyServerFilterAction, MyAction, MyBatchAction)
|
||||
row_actions = (MyAction, MyLinkAction, MyBatchAction, MyToggleAction)
|
||||
row_actions = (MyAction, MyLinkAction, MyBatchAction, MyToggleAction,
|
||||
MyBatchActionWithHelpText)
|
||||
|
||||
|
||||
class MyTableSelectable(MyTable):
|
||||
@ -338,6 +348,7 @@ class DataTableTests(test.TestCase):
|
||||
# Actions (these also test ordering)
|
||||
self.assertQuerysetEqual(self.table.base_actions.values(),
|
||||
['<MyBatchAction: batch>',
|
||||
'<MyBatchActionWithHelpText: batch_help>',
|
||||
'<MyAction: delete>',
|
||||
'<MyFilterAction: filter>',
|
||||
'<MyLinkAction: login>',
|
||||
@ -345,12 +356,14 @@ class DataTableTests(test.TestCase):
|
||||
self.assertQuerysetEqual(self.table.get_table_actions(),
|
||||
['<MyFilterAction: filter>',
|
||||
'<MyAction: delete>',
|
||||
'<MyBatchAction: batch>'])
|
||||
'<MyBatchAction: batch>',
|
||||
'<MyBatchActionWithHelpText: batch_help>'])
|
||||
self.assertQuerysetEqual(self.table.get_row_actions(TEST_DATA[0]),
|
||||
['<MyAction: delete>',
|
||||
'<MyLinkAction: login>',
|
||||
'<MyBatchAction: batch>',
|
||||
'<MyToggleAction: toggle>'])
|
||||
'<MyToggleAction: toggle>',
|
||||
'<MyBatchActionWithHelpText: batch_help>'])
|
||||
# Auto-generated columns
|
||||
multi_select = self.table.columns['multi_select']
|
||||
self.assertEqual("multi_select", multi_select.auto)
|
||||
@ -561,15 +574,30 @@ class DataTableTests(test.TestCase):
|
||||
self.assertContains(resp, "my_table__filter__q", 1)
|
||||
self.assertContains(resp, "my_table__delete", 1)
|
||||
self.assertContains(resp, 'id="my_table__action_delete"', 1)
|
||||
|
||||
# Table BatchActions
|
||||
self.assertContains(resp, 'id="my_table__action_batch_help"', 1)
|
||||
self.assertContains(resp, 'help_text="this is help."', 1)
|
||||
self.assertContains(resp, 'BatchHelp Item', 1)
|
||||
|
||||
# Row actions
|
||||
row_actions = self.table.render_row_actions(TEST_DATA[0])
|
||||
resp = http.HttpResponse(row_actions)
|
||||
self.assertContains(resp, "<li", 3)
|
||||
self.assertContains(resp, "<li", 4)
|
||||
self.assertContains(resp, "my_table__delete__1", 1)
|
||||
self.assertContains(resp, "my_table__toggle__1", 1)
|
||||
self.assertContains(resp, "/auth/login/", 1)
|
||||
self.assertContains(resp, "ajax-modal", 1)
|
||||
self.assertContains(resp, 'id="my_table__row_1__action_delete"', 1)
|
||||
|
||||
# Row BatchActions
|
||||
row_actions = self.table.render_row_actions(TEST_DATA[0])
|
||||
resp = http.HttpResponse(row_actions)
|
||||
self.assertContains(resp, 'id="my_table__row_1__action_batch_help"', 1)
|
||||
self.assertContains(resp, 'help_text="this is help."', 1)
|
||||
self.assertContains(resp, 'value="my_table__batch_help__1"', 1)
|
||||
self.assertContains(resp, 'BatchHelp Item', 1)
|
||||
|
||||
# Whole table
|
||||
resp = http.HttpResponse(self.table.render())
|
||||
self.assertContains(resp, '<table id="my_table"', 1)
|
||||
@ -802,11 +830,17 @@ class DataTableTests(test.TestCase):
|
||||
toggle_action = self.table.get_row_actions(TEST_DATA_3[0])[2]
|
||||
self.assertEqual("Batch Item", unicode(toggle_action.verbose_name))
|
||||
|
||||
# Batch action with custom help text
|
||||
req = self.factory.get('/my_url/')
|
||||
self.table = MyTable(req, TEST_DATA_3)
|
||||
toggle_action = self.table.get_row_actions(TEST_DATA_3[0])[4]
|
||||
self.assertEqual("BatchHelp Item", unicode(toggle_action.verbose_name))
|
||||
|
||||
# Single object toggle action
|
||||
# GET page - 'up' to 'down'
|
||||
req = self.factory.get('/my_url/')
|
||||
self.table = MyTable(req, TEST_DATA_3)
|
||||
self.assertEqual(4, len(self.table.get_row_actions(TEST_DATA_3[0])))
|
||||
self.assertEqual(5, len(self.table.get_row_actions(TEST_DATA_3[0])))
|
||||
toggle_action = self.table.get_row_actions(TEST_DATA_3[0])[3]
|
||||
self.assertEqual("Down Item", unicode(toggle_action.verbose_name))
|
||||
|
||||
@ -827,7 +861,7 @@ class DataTableTests(test.TestCase):
|
||||
# GET page - 'down' to 'up'
|
||||
req = self.factory.get('/my_url/')
|
||||
self.table = MyTable(req, TEST_DATA_2)
|
||||
self.assertEqual(3, len(self.table.get_row_actions(TEST_DATA_2[0])))
|
||||
self.assertEqual(4, len(self.table.get_row_actions(TEST_DATA_2[0])))
|
||||
toggle_action = self.table.get_row_actions(TEST_DATA_2[0])[2]
|
||||
self.assertEqual("Up Item", unicode(toggle_action.verbose_name))
|
||||
|
||||
@ -883,7 +917,8 @@ class DataTableTests(test.TestCase):
|
||||
self.assertQuerysetEqual(self.table.get_table_actions(),
|
||||
['<MyFilterAction: filter>',
|
||||
'<MyAction: delete>',
|
||||
'<MyBatchAction: batch>'])
|
||||
'<MyBatchAction: batch>',
|
||||
'<MyBatchActionWithHelpText: batch_help>'])
|
||||
|
||||
# Zero objects in table
|
||||
# BatchAction not available
|
||||
|
@ -57,6 +57,10 @@ class LaunchImage(tables.LinkAction):
|
||||
|
||||
|
||||
class DeleteImage(tables.DeleteAction):
|
||||
# NOTE: The bp/add-batchactions-help-text
|
||||
# will add appropriate help text to some batch/delete actions.
|
||||
help_text = _("Deleted images are not recoverable.")
|
||||
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ungettext_lazy(
|
||||
|
@ -46,6 +46,8 @@ class ImagesAndSnapshotsTests(test.TestCase):
|
||||
|
||||
res = self.client.get(INDEX_URL)
|
||||
self.assertTemplateUsed(res, 'project/images/index.html')
|
||||
self.assertContains(res, 'help_text="Deleted images'
|
||||
' are not recoverable."')
|
||||
self.assertIn('images_table', res.context)
|
||||
images_table = res.context['images_table']
|
||||
images = images_table.data
|
||||
|
Loading…
x
Reference in New Issue
Block a user