mirror of
https://github.com/Textualize/textual.git
synced 2025-10-17 02:38:12 +03:00
Merge pull request #2444 from davep/overall-important
Make `!important` apply to rules that have "sub-rules"
This commit is contained in:
10
CHANGELOG.md
10
CHANGELOG.md
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed `!important` not applying to `align` https://github.com/Textualize/textual/issues/2420
|
||||
- Fixed `!important` not applying to `border` https://github.com/Textualize/textual/issues/2420
|
||||
- Fixed `!important` not applying to `content-align` https://github.com/Textualize/textual/issues/2420
|
||||
- Fixed `!important` not applying to `outline` https://github.com/Textualize/textual/issues/2420
|
||||
- Fixed `!important` not applying to `overflow` https://github.com/Textualize/textual/issues/2420
|
||||
- Fixed `!important` not applying to `scrollbar-size` https://github.com/Textualize/textual/issues/2420
|
||||
- Fixed `outline-right` not being recognised https://github.com/Textualize/textual/issues/2446
|
||||
|
||||
### Changed
|
||||
|
||||
- Setting attributes with a `compute_` method will now raise an `AttributeError` https://github.com/Textualize/textual/issues/2383
|
||||
|
||||
@@ -252,6 +252,23 @@ class StylesBuilder:
|
||||
else:
|
||||
scalar_error()
|
||||
|
||||
def _distribute_importance(self, prefix: str, suffixes: tuple[str, ...]) -> None:
|
||||
"""Distribute importance amongst all aspects of the given style.
|
||||
|
||||
Args:
|
||||
prefix: The prefix of the style.
|
||||
siffixes: The suffixes to distribute amongst.
|
||||
|
||||
A number of styles can be set with the 'prefix' of the style,
|
||||
providing the values as a series of parameters; or they can be set
|
||||
with specific suffixes. Think `border` vs `border-left`, etc. This
|
||||
method is used to ensure that if the former is set, `!important` is
|
||||
distributed amongst all the suffixes.
|
||||
"""
|
||||
if prefix in self.styles.important:
|
||||
self.styles.important.remove(prefix)
|
||||
self.styles.important.update(f"{prefix}_{suffix}" for suffix in suffixes)
|
||||
|
||||
def process_box_sizing(self, name: str, tokens: list[Token]) -> None:
|
||||
for token in tokens:
|
||||
name, value, _, _, location, _ = token
|
||||
@@ -304,6 +321,7 @@ class StylesBuilder:
|
||||
)
|
||||
rules["overflow_x"] = cast(Overflow, overflow_x)
|
||||
rules["overflow_y"] = cast(Overflow, overflow_y)
|
||||
self._distribute_importance("overflow", ("x", "y"))
|
||||
|
||||
def process_overflow_x(self, name: str, tokens: list[Token]) -> None:
|
||||
self.styles._rules["overflow_x"] = cast(
|
||||
@@ -486,6 +504,7 @@ class StylesBuilder:
|
||||
rules = self.styles._rules
|
||||
rules["border_top"] = rules["border_right"] = border
|
||||
rules["border_bottom"] = rules["border_left"] = border
|
||||
self._distribute_importance("border", ("top", "left", "bottom", "right"))
|
||||
|
||||
def process_border_top(self, name: str, tokens: list[Token]) -> None:
|
||||
self._process_border_edge("top", name, tokens)
|
||||
@@ -508,11 +527,12 @@ class StylesBuilder:
|
||||
rules = self.styles._rules
|
||||
rules["outline_top"] = rules["outline_right"] = border
|
||||
rules["outline_bottom"] = rules["outline_left"] = border
|
||||
self._distribute_importance("outline", ("top", "left", "bottom", "right"))
|
||||
|
||||
def process_outline_top(self, name: str, tokens: list[Token]) -> None:
|
||||
self._process_outline("top", name, tokens)
|
||||
|
||||
def process_parse_border_right(self, name: str, tokens: list[Token]) -> None:
|
||||
def process_outline_right(self, name: str, tokens: list[Token]) -> None:
|
||||
self._process_outline("right", name, tokens)
|
||||
|
||||
def process_outline_bottom(self, name: str, tokens: list[Token]) -> None:
|
||||
@@ -792,6 +812,8 @@ class StylesBuilder:
|
||||
self.styles._rules[f"{name}_horizontal"] = token_horizontal.value # type: ignore
|
||||
self.styles._rules[f"{name}_vertical"] = token_vertical.value # type: ignore
|
||||
|
||||
self._distribute_importance(name, ("horizontal", "vertical"))
|
||||
|
||||
def process_align_horizontal(self, name: str, tokens: list[Token]) -> None:
|
||||
try:
|
||||
value = self._process_enum(name, tokens, VALID_ALIGN_HORIZONTAL)
|
||||
@@ -859,6 +881,7 @@ class StylesBuilder:
|
||||
scrollbar_size_error(name, token2)
|
||||
self.styles._rules["scrollbar_size_horizontal"] = horizontal
|
||||
self.styles._rules["scrollbar_size_vertical"] = vertical
|
||||
self._distribute_importance("scrollbar_size", ("horizontal", "vertical"))
|
||||
|
||||
def process_scrollbar_size_vertical(self, name: str, tokens: list[Token]) -> None:
|
||||
if not tokens:
|
||||
|
||||
102
tests/test_style_importance.py
Normal file
102
tests/test_style_importance.py
Normal file
@@ -0,0 +1,102 @@
|
||||
from textual.app import App, ComposeResult
|
||||
from textual.color import Color
|
||||
from textual.containers import Container
|
||||
from textual.css.scalar import Scalar, ScalarOffset
|
||||
|
||||
|
||||
class StyleApp(App[None]):
|
||||
CSS = """
|
||||
Container {
|
||||
border: round green !important;
|
||||
outline: round green !important;
|
||||
align: right bottom !important;
|
||||
content-align: right bottom !important;
|
||||
offset: 17 23 !important;
|
||||
overflow: hidden hidden !important;
|
||||
padding: 10 20 30 40 !important;
|
||||
scrollbar-size: 23 42 !important;
|
||||
}
|
||||
|
||||
Container.more-specific {
|
||||
border: solid red;
|
||||
outline: solid red;
|
||||
align: center middle;
|
||||
content-align: center middle;
|
||||
offset: 0 0;
|
||||
overflow: scroll scroll;
|
||||
padding: 1 2 3 4;
|
||||
scrollbar-size: 1 2;
|
||||
}
|
||||
"""
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Container(classes="more-specific")
|
||||
|
||||
|
||||
async def test_border_importance():
|
||||
"""Border without sides should support !important"""
|
||||
async with StyleApp().run_test() as pilot:
|
||||
border = pilot.app.query_one(Container).styles.border
|
||||
desired = ("round", Color.parse("green"))
|
||||
assert border.top == desired
|
||||
assert border.left == desired
|
||||
assert border.bottom == desired
|
||||
assert border.right == desired
|
||||
|
||||
|
||||
async def test_outline_importance():
|
||||
"""Outline without sides should support !important"""
|
||||
async with StyleApp().run_test() as pilot:
|
||||
outline = pilot.app.query_one(Container).styles.outline
|
||||
desired = ("round", Color.parse("green"))
|
||||
assert outline.top == desired
|
||||
assert outline.left == desired
|
||||
assert outline.bottom == desired
|
||||
assert outline.right == desired
|
||||
|
||||
|
||||
async def test_align_importance():
|
||||
"""Align without direction should support !important"""
|
||||
async with StyleApp().run_test() as pilot:
|
||||
assert pilot.app.query_one(Container).styles.align == ("right", "bottom")
|
||||
|
||||
|
||||
async def test_content_align_importance():
|
||||
"""Content align without direction should support !important"""
|
||||
async with StyleApp().run_test() as pilot:
|
||||
assert pilot.app.query_one(Container).styles.content_align == (
|
||||
"right",
|
||||
"bottom",
|
||||
)
|
||||
|
||||
|
||||
async def test_offset_importance():
|
||||
"""Offset without direction should support !important"""
|
||||
async with StyleApp().run_test() as pilot:
|
||||
assert pilot.app.query_one(Container).styles.offset == ScalarOffset.from_offset(
|
||||
(17, 23)
|
||||
)
|
||||
|
||||
|
||||
async def test_overflow_importance():
|
||||
"""Overflow without direction should support !important"""
|
||||
async with StyleApp().run_test() as pilot:
|
||||
assert pilot.app.query_one(Container).styles.overflow_x == "hidden"
|
||||
assert pilot.app.query_one(Container).styles.overflow_y == "hidden"
|
||||
|
||||
|
||||
async def test_padding_importance():
|
||||
"""Padding without sides should support !important"""
|
||||
async with StyleApp().run_test() as pilot:
|
||||
padding = pilot.app.query_one(Container).styles.padding
|
||||
assert padding.top == 10
|
||||
assert padding.left == 40
|
||||
assert padding.bottom == 30
|
||||
assert padding.right == 20
|
||||
|
||||
|
||||
async def test_scrollbar_size_importance():
|
||||
"""Scrollbar size without direction should support !important"""
|
||||
async with StyleApp().run_test() as pilot:
|
||||
assert pilot.app.query_one(Container).styles.scrollbar_size_horizontal == 23
|
||||
assert pilot.app.query_one(Container).styles.scrollbar_size_vertical == 42
|
||||
Reference in New Issue
Block a user