Skip to content
11 changes: 5 additions & 6 deletions doc/source/user_guide/style.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,7 @@
"\n",
"styles = [\n",
" hover(),\n",
" {'selector': \"th\", 'props': [(\"font-size\", \"150%\"),\n",
" (\"text-align\", \"center\")]}\n",
" {'selector': \"th\", 'props': [(\"font-size\", \"150%\"), (\"text-align\", \"center\")]}\n",
"]\n",
"\n",
"df.style.set_table_styles(styles)"
Expand Down Expand Up @@ -224,7 +223,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"We can also chain all of the above by setting the `overwrite` argument to `False` so that it preserves previous settings."
"We can also chain all of the above by setting the `overwrite` argument to `False` so that it preserves previous settings. We also show the CSS string input rather than the list of tuples."
]
},
{
Expand All @@ -238,13 +237,13 @@
" set_table_styles(styles).\\\n",
" set_table_styles({\n",
" 'A': [{'selector': '',\n",
" 'props': [('color', 'red')]}],\n",
" 'props': 'color:red;'}],\n",
" 'B': [{'selector': 'td',\n",
" 'props': [('color', 'blue')]}]\n",
" 'props': 'color:blue;'}]\n",
" }, axis=0, overwrite=False).\\\n",
" set_table_styles({\n",
" 3: [{'selector': 'td',\n",
" 'props': [('color', 'green')]}]\n",
" 'props': 'color:green;font-weight:bold;'}]\n",
" }, axis=1, overwrite=False)\n",
"s"
]
Expand Down
1 change: 1 addition & 0 deletions doc/source/whatsnew/v1.3.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ Other enhancements
- :meth:`.Styler.set_tooltips` allows on hover tooltips to be added to styled HTML dataframes.
- :meth:`Series.loc.__getitem__` and :meth:`Series.loc.__setitem__` with :class:`MultiIndex` now raising helpful error message when indexer has too many dimensions (:issue:`35349`)
- :meth:`pandas.read_stata` and :class:`StataReader` support reading data from compressed files.
- :meth:`.Styler.set_tooltips_class` and :meth:`.Styler.set_table_styles` amended to optionally allow certain css-string input arguments.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

locate after the other note, L56 (which is missing an issue number; use the PR number for it)


.. ---------------------------------------------------------------------------

Expand Down
55 changes: 51 additions & 4 deletions pandas/io/formats/style.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,8 +279,8 @@ def set_tooltips_class(
----------
name : str, default None
Name of the tooltip class used in CSS, should conform to HTML standards.
properties : list-like, default None
List of (attr, value) tuples; see example.
properties : list-like or str, default None
List of (attr, value) tuples or a valid CSS string; see example.

Returns
-------
Expand Down Expand Up @@ -311,6 +311,8 @@ def set_tooltips_class(
... ('visibility', 'hidden'),
... ('position', 'absolute'),
... ('z-index', 1)])
>>> df.style.set_tooltips_class(name='tt-add',
... properties='visibility:hidden; position:absolute; z-index:1;')
"""
self._init_tooltips()
assert self.tooltips is not None # mypy requirement
Expand Down Expand Up @@ -1172,13 +1174,20 @@ def set_table_styles(self, table_styles, axis=0, overwrite=True) -> Styler:
... 'props': [('background-color', 'yellow')]}]
... )

Or with CSS strings

>>> df.style.set_table_styles(
... [{'selector': 'tr:hover',
... 'props': 'background-color: yellow; font-size: 1em;']}]
... )

Adding column styling by name

>>> df.style.set_table_styles({
... 'A': [{'selector': '',
... 'props': [('color', 'red')]}],
... 'B': [{'selector': 'td',
... 'props': [('color', 'blue')]}]
... 'props': 'color: blue;']}]
... }, overwrite=False)

Adding row styling
Expand All @@ -1203,6 +1212,14 @@ def set_table_styles(self, table_styles, axis=0, overwrite=True) -> Styler:
for s in styles
]

table_styles = [
{
"selector": s["selector"],
"props": _maybe_convert_css_to_tuples(s["props"]),
}
for s in table_styles
]

if not overwrite and self.table_styles is not None:
self.table_styles.extend(table_styles)
else:
Expand Down Expand Up @@ -1843,7 +1860,12 @@ def _class_styles(self):
-------
styles : List
"""
return [{"selector": f".{self.class_name}", "props": self.class_properties}]
return [
{
"selector": f".{self.class_name}",
"props": _maybe_convert_css_to_tuples(self.class_properties),
}
]

def _pseudo_css(self, uuid: str, name: str, row: int, col: int, text: str):
"""
Expand Down Expand Up @@ -2025,3 +2047,28 @@ def _maybe_wrap_formatter(
else:
msg = f"Expected a string, got {na_rep} instead"
raise TypeError(msg)


def _maybe_convert_css_to_tuples(
style: Union[str, Sequence[Tuple[str, Union[str, int, float]]]]
):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add an output type (maybe define this at the top as CSSProperties or similar)

"""
Convert css-string to sequence of tuples format if needed.
'color:red; border:1px solid black;' -> [('color', 'red'),
('border','1px solid red')]
"""
if isinstance(style, str):
s = style.split(";")
try:
ret = [
(x.split(":")[0].strip(), x.split(":")[1].strip())
for x in s
if x.strip() != ""
]
return ret
except IndexError:
raise ValueError(
"Styles supplied as string must follow CSS rule formats, "
f"for example 'attr: val;'. {style} was given."
)
return style
38 changes: 36 additions & 2 deletions pandas/tests/io/formats/test_style.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@
import pandas._testing as tm

jinja2 = pytest.importorskip("jinja2")
from pandas.io.formats.style import Styler, _get_level_lengths # isort:skip
from pandas.io.formats.style import ( # isort:skip
Styler,
_get_level_lengths,
_maybe_convert_css_to_tuples,
)


class TestStyler:
Expand Down Expand Up @@ -1167,7 +1171,7 @@ def test_unique_id(self):
assert np.unique(ids).size == len(ids)

def test_table_styles(self):
style = [{"selector": "th", "props": [("foo", "bar")]}]
style = [{"selector": "th", "props": [("foo", "bar")]}] # default format
styler = Styler(self.df, table_styles=style)
result = " ".join(styler.render().split())
assert "th { foo: bar; }" in result
Expand All @@ -1177,6 +1181,24 @@ def test_table_styles(self):
assert styler is result
assert styler.table_styles == style

# GH 39563
style = [{"selector": "th", "props": "foo:bar;"}] # css string format
styler = self.df.style.set_table_styles(style)
result = " ".join(styler.render().split())
assert "th { foo: bar; }" in result

def test_maybe_convert_css_to_tuples(self):
expected = [("a", "b"), ("c", "d e")]
assert _maybe_convert_css_to_tuples("a:b;c:d e;") == expected
assert _maybe_convert_css_to_tuples("a: b ;c: d e ") == expected
expected = []
assert _maybe_convert_css_to_tuples("") == expected

def test_maybe_convert_css_to_tuples_err(self):
msg = "Styles supplied as string must follow CSS rule formats"
with pytest.raises(ValueError, match=msg):
_maybe_convert_css_to_tuples("err")

def test_table_attributes(self):
attributes = 'class="foo" data-bar'
styler = Styler(self.df, table_attributes=attributes)
Expand Down Expand Up @@ -1897,6 +1919,18 @@ def test_tooltip_class(self):
in s
)

# GH 39563
s = (
Styler(df, uuid_len=0)
.set_tooltips(DataFrame([["tooltip"]]))
.set_tooltips_class(name="other-class", properties="color:green;color:red;")
.render()
)
assert (
"#T__ .other-class {\n color: green;\n color: red;\n "
in s
)


@td.skip_if_no_mpl
class TestStylerMatplotlibDep:
Expand Down