diff --git a/tailbone/grids/core.py b/tailbone/grids/core.py
index 3f1769cf..b9254c18 100644
--- a/tailbone/grids/core.py
+++ b/tailbone/grids/core.py
@@ -38,6 +38,7 @@ from pyramid.renderers import render
from webhelpers2.html import HTML, tags
from paginate_sqlalchemy import SqlalchemyOrmPage
+from wuttaweb.grids import GridAction as WuttaGridAction
from . import filters as gridfilters
from tailbone.db import Session
from tailbone.util import raw_datetime
@@ -1801,18 +1802,20 @@ class Grid:
return False
-class GridAction(object):
+class GridAction(WuttaGridAction):
"""
- Represents an action available to a grid. This is used to construct the
- 'actions' column when rendering the grid.
+ Represents a "row action" hyperlink within a grid context.
- :param key: Key for the action (e.g. ``'edit'``), unique within
- the grid.
+ This is a subclass of
+ :class:`wuttaweb:wuttaweb.grids.base.GridAction`.
- :param label: Label to be displayed for the action. If not set,
- will be a capitalized version of ``key``.
+ .. warning::
- :param icon: Icon name for the action.
+ This class remains for now, to retain compatibility with
+ existing code. But at some point the WuttaWeb class will
+ supersede this one entirely.
+
+ :param target: HTML "target" attribute for the ```` tag.
:param click_handler: Optional JS click handler for the action.
This value will be rendered as-is within the final grid
@@ -1824,41 +1827,23 @@ class GridAction(object):
* ``$emit('do-something', props.row)``
"""
- def __init__(self, key, label=None, url='#', icon=None, target=None,
- link_class=None, click_handler=None):
- self.key = key
- self.label = label or prettify(key)
- self.icon = icon
- self.url = url
+ def __init__(
+ self,
+ request,
+ key,
+ target=None,
+ click_handler=None,
+ **kwargs,
+ ):
+ # TODO: previously url default was '#' - but i don't think we
+ # need that anymore? guess we'll see..
+ #kwargs.setdefault('url', '#')
+
+ super().__init__(request, key, **kwargs)
+
self.target = target
- self.link_class = link_class
self.click_handler = click_handler
- def get_url(self, row, i):
- """
- Returns an action URL for the given row.
- """
- if callable(self.url):
- return self.url(row, i)
- return self.url
-
- def render_icon(self):
- """
- Render the HTML snippet for the action link icon.
- """
- return HTML.tag('i', class_='fas fa-{}'.format(self.icon))
-
- def render_label(self):
- """
- Render the label "text" within the actions column of a grid
- row. Most actions have a static label that never varies, but
- you can override this to add e.g. HTML content. Note that the
- return value will be treated / rendered as HTML whether or not
- it contains any, so perhaps be careful that it is trusted
- content.
- """
- return self.label
-
class URLMaker(object):
"""
diff --git a/tailbone/templates/grids/b-table.mako b/tailbone/templates/grids/b-table.mako
index 632193b5..da9f2aae 100644
--- a/tailbone/templates/grids/b-table.mako
+++ b/tailbone/templates/grids/b-table.mako
@@ -53,11 +53,11 @@
${b}-table-column>
% endfor
- % if grid.main_actions or grid.more_actions:
+ % if grid.actions:
<${b}-table-column field="actions"
label="Actions"
v-slot="props">
- % for action in grid.main_actions:
+ % for action in grid.actions:
- % if request.use_oruga:
-
- % else:
-
- % endif
- ${action.label}
+ ${action.render_icon_and_label()}
% endfor
diff --git a/tailbone/templates/grids/complete.mako b/tailbone/templates/grids/complete.mako
index fc48916b..93bb6c26 100644
--- a/tailbone/templates/grids/complete.mako
+++ b/tailbone/templates/grids/complete.mako
@@ -163,13 +163,7 @@
target="${action.target}"
% endif
>
- % if request.use_oruga:
-
- ${action.render_label()|n}
- % else:
- ${action.render_icon()|n}
- ${action.render_label()|n}
- % endif
+ ${action.render_icon_and_label()}
% endfor
diff --git a/tailbone/views/master.py b/tailbone/views/master.py
index 0d322da3..097cb229 100644
--- a/tailbone/views/master.py
+++ b/tailbone/views/master.py
@@ -3220,14 +3220,18 @@ class MasterView(View):
def make_action(self, key, url=None, factory=None, **kwargs):
"""
- Make a new :class:`GridAction` instance for the current grid.
+ Make and return a new :class:`~tailbone.grids.core.GridAction`
+ instance.
+
+ This can be called to make actions for any grid, not just the
+ one from :meth:`index()`.
"""
if url is None:
route = '{}.{}'.format(self.get_route_prefix(), key)
url = lambda r, i: self.request.route_url(route, **self.get_action_route_kwargs(r))
if not factory:
factory = grids.GridAction
- return factory(key, url=url, **kwargs)
+ return factory(self.request, key, url=url, **kwargs)
def get_action_route_kwargs(self, obj):
"""
diff --git a/tailbone/views/people.py b/tailbone/views/people.py
index 94c85821..163a9a52 100644
--- a/tailbone/views/people.py
+++ b/tailbone/views/people.py
@@ -552,7 +552,7 @@ class PersonView(MasterView):
if self.request.has_perm('trainwreck.transactions.view'):
url = lambda row, i: self.request.route_url('trainwreck.transactions.view',
uuid=row.uuid)
- g.main_actions.append(grids.GridAction('view', icon='eye', url=url))
+ g.main_actions.append(self.make_action('view', icon='eye', url=url))
g.load_settings()
g.set_enum('system', self.enum.TRAINWRECK_SYSTEM)
diff --git a/tailbone/views/purchasing/receiving.py b/tailbone/views/purchasing/receiving.py
index 55936184..0a305f0a 100644
--- a/tailbone/views/purchasing/receiving.py
+++ b/tailbone/views/purchasing/receiving.py
@@ -40,7 +40,7 @@ from webhelpers2.html import tags, HTML
from wuttaweb.util import get_form_data
-from tailbone import forms, grids
+from tailbone import forms
from tailbone.views.purchasing import PurchasingBatchView
@@ -1031,7 +1031,7 @@ class ReceivingBatchView(PurchasingBatchView):
if batch.is_truck_dump_parent():
permission_prefix = self.get_permission_prefix()
if self.request.has_perm('{}.edit_row'.format(permission_prefix)):
- transform = grids.GridAction('transform',
+ transform = self.make_action('transform',
icon='shuffle',
label="Transform to Unit",
url=self.transform_unit_url)
diff --git a/tailbone/views/roles.py b/tailbone/views/roles.py
index b34b3673..fb834479 100644
--- a/tailbone/views/roles.py
+++ b/tailbone/views/roles.py
@@ -363,7 +363,7 @@ class RoleView(PrincipalMasterView):
if role.users:
users = sorted(role.users, key=lambda u: u.username)
actions = [
- grids.GridAction('view', icon='zoomin',
+ self.make_action('view', icon='zoomin',
url=lambda r, i: self.request.route_url('users.view', uuid=r.uuid))
]
kwargs['users'] = grids.Grid(None, users, ['username', 'active'],
diff --git a/tests/grids/test_core.py b/tests/grids/test_core.py
index e6f9d675..0a8d5d66 100644
--- a/tests/grids/test_core.py
+++ b/tests/grids/test_core.py
@@ -137,3 +137,20 @@ class TestGrid(WebTestCase):
# calling again returns same data
data2 = grid.get_vue_data()
self.assertIs(data2, data)
+
+
+class TestGridAction(WebTestCase):
+
+ def test_constructor(self):
+
+ # null by default
+ action = mod.GridAction(self.request, 'view')
+ self.assertIsNone(action.target)
+ self.assertIsNone(action.click_handler)
+
+ # but can set them
+ action = mod.GridAction(self.request, 'view',
+ target='_blank',
+ click_handler='doSomething(props.row)')
+ self.assertEqual(action.target, '_blank')
+ self.assertEqual(action.click_handler, 'doSomething(props.row)')
diff --git a/tests/views/test_master.py b/tests/views/test_master.py
index 19321496..572875a0 100644
--- a/tests/views/test_master.py
+++ b/tests/views/test_master.py
@@ -3,6 +3,7 @@
from unittest.mock import patch
from tailbone.views import master as mod
+from wuttaweb.grids import GridAction
from tests.util import WebTestCase
@@ -24,3 +25,11 @@ class TestMasterView(WebTestCase):
# sanity / coverage check
kw = view.make_form_kwargs(model_instance=setting)
self.assertIsNotNone(kw['action_url'])
+
+ def test_make_action(self):
+ model = self.app.model
+ with patch.multiple(mod.MasterView, create=True,
+ model_class=model.Setting):
+ view = self.make_view()
+ action = view.make_action('view')
+ self.assertIsInstance(action, GridAction)