Repository: flask-admin/flask-admin

Issue:
Feature Request: provide accessible field names for boolean rendered icons in list view
In the list view, the "Edit record" and "Delete record" icons have corresponding accessible text attached to them [using the `title` attribute](https://github.com/flask-admin/flask-admin/blob/3f9bda85b3a6040ee0327474fbf5e3515467b58a/flask_admin/templates/bootstrap4/admin/model/row_actions.html#L34).

![image](https://user-images.githubusercontent.com/55152140/175300711-b505e366-b0d2-457e-971e-36e8fe5bf948.png)

It could be useful to have similar text attached to boolean values that are rendered as icons/glyphs - particularly when viewing large lists where the column header list has been scrolled out-of-view.

I'll include a possible implementation as a pull request alongside the feature request.


Discussion:

Resolving patch:
diff --git a/flask_admin/model/base.py b/flask_admin/model/base.py
--- a/flask_admin/model/base.py
+++ b/flask_admin/model/base.py
@@ -4,6 +4,7 @@
 import mimetypes
 import time
 from math import ceil
+import inspect
 
 from werkzeug.utils import secure_filename
 
@@ -1846,7 +1847,18 @@ def _get_list_value(self, context, model, name, column_formatters,
                 type_fmt = formatter
                 break
         if type_fmt is not None:
-            value = type_fmt(self, value)
+            try:
+                value = type_fmt(self, value, name)
+            except TypeError:
+                spec = inspect.getfullargspec(type_fmt)
+
+                if len(spec.args) == 2:
+                    warnings.warn(f'Please update your type formatter {type_fmt} to '
+                                  'include additional `name` parameter.')
+                else:
+                    raise
+
+                value = type_fmt(self, value)
 
         return value
 
diff --git a/flask_admin/model/typefmt.py b/flask_admin/model/typefmt.py
--- a/flask_admin/model/typefmt.py
+++ b/flask_admin/model/typefmt.py
@@ -8,7 +8,7 @@
     Enum = None
 
 
-def null_formatter(view, value):
+def null_formatter(view, value, name):
     """
         Return `NULL` as the string for `None` value
 
@@ -18,7 +18,7 @@ def null_formatter(view, value):
     return Markup('<i>NULL</i>')
 
 
-def empty_formatter(view, value):
+def empty_formatter(view, value, name):
     """
         Return empty string for `None` value
 
@@ -28,7 +28,7 @@ def empty_formatter(view, value):
     return ''
 
 
-def bool_formatter(view, value):
+def bool_formatter(view, value, name):
     """
         Return check icon if value is `True` or empty string otherwise.
 
@@ -37,10 +37,11 @@ def bool_formatter(view, value):
     """
     glyph = 'ok-circle' if value else 'minus-sign'
     fa = 'fa-check-circle' if value else 'fa-minus-circle'
-    return Markup('<span class="fa %s glyphicon glyphicon-%s icon-%s"></span>' % (fa, glyph, glyph))
+    label = f'{name}: {"true" if value else "false"}'
+    return Markup('<span class="fa %s glyphicon glyphicon-%s icon-%s" title="%s"></span>' % (fa, glyph, glyph, label))
 
 
-def list_formatter(view, values):
+def list_formatter(view, values, name):
     """
         Return string with comma separated values
 
@@ -50,7 +51,7 @@ def list_formatter(view, values):
     return u', '.join(text_type(v) for v in values)
 
 
-def enum_formatter(view, value):
+def enum_formatter(view, value, name):
     """
         Return the name of the enumerated member.
 
@@ -60,7 +61,7 @@ def enum_formatter(view, value):
     return value.name
 
 
-def dict_formatter(view, value):
+def dict_formatter(view, value, name):
     """
         Removes unicode entities when displaying dict as string. Also unescapes
         non-ASCII characters stored in the JSON.


Test patch:
diff --git a/flask_admin/tests/test_model.py b/flask_admin/tests/test_model.py
--- a/flask_admin/tests/test_model.py
+++ b/flask_admin/tests/test_model.py
@@ -652,7 +652,7 @@ def test_export_csv():
         ",6\r\n" == data
 
     # test column_formatters_export and column_formatters_export
-    type_formatters = {type(None): lambda view, value: "null"}
+    type_formatters = {type(None): lambda view, value, name: "null"}
 
     view = MockModelView(
         Model, view_data, can_export=True, column_list=['col1', 'col2'],