Fixed rule conflict management. Added User guide for formatting

This commit is contained in:
2026-02-08 00:12:24 +01:00
parent fc38196ad9
commit 8e059df68a
7 changed files with 1452 additions and 17 deletions

View File

@@ -11,6 +11,7 @@ from myfasthtml.core.formatting.dataclasses import (
BooleanFormatter,
TextFormatter,
EnumFormatter,
ConstantFormatter,
)
from myfasthtml.core.formatting.dsl import (
parse_dsl,
@@ -342,11 +343,23 @@ column status:
format.enum(source={"draft": "Brouillon", "published": "Publie"})
"""
rules = parse_dsl(dsl)
formatter = rules[0].rule.formatter
assert isinstance(formatter, EnumFormatter)
assert formatter.source == {"draft": "Brouillon", "published": "Publie"}
def test_i_can_parse_format_constant(self):
"""Test parsing format.constant with fixed value."""
dsl = """
column placeholder:
format.constant(value="N/A")
"""
rules = parse_dsl(dsl)
formatter = rules[0].rule.formatter
assert isinstance(formatter, ConstantFormatter)
assert formatter.value == "N/A"
# =============================================================================
# Condition Tests
@@ -379,9 +392,9 @@ column amount:
assert condition is not None
assert condition.operator == operator
@pytest.mark.parametrize("unary_op", ["isempty", "isnotempty"])
@pytest.mark.parametrize("unary_op", ["isempty", "isnotempty", "isnan"])
def test_i_can_parse_unary_conditions(self, unary_op):
"""Test parsing unary conditions (isempty, isnotempty)."""
"""Test parsing unary conditions (isempty, isnotempty, isnan)."""
dsl = f"""
column name:
style("neutral") if value {unary_op}

View File

@@ -215,6 +215,75 @@ class TestConflictResolution:
assert "color: red" in css
def test_style_and_formatter_fusion(self):
"""
Test that style and formatter can come from different rules.
Scenario:
- Rule 1: format("EUR") - unconditional formatter
- Rule 2: style("secondary") if value > col.X - conditional style
When condition is met:
- Style from Rule 2 (higher specificity for style)
- Formatter from Rule 1 (only rule with formatter)
- Both should be applied (fusion)
"""
engine = FormattingEngine()
rules = [
FormatRule(formatter=NumberFormatter(precision=2, suffix="")), # unconditional
FormatRule(
condition=Condition(operator=">", value={"col": "budget"}),
style=Style(preset="secondary") # conditional
),
]
row_data = {"budget": 100}
# Case 1: Condition met (value > budget)
css, formatted = engine.apply_format(rules, cell_value=150, row_data=row_data)
assert css is not None
assert "var(--color-secondary)" in css # Style from Rule 2
assert formatted == "150.00 €" # Formatter from Rule 1
# Case 2: Condition not met (value <= budget)
css, formatted = engine.apply_format(rules, cell_value=50, row_data=row_data)
assert css is None # No style (Rule 2 doesn't match)
assert formatted == "50.00 €" # Formatter from Rule 1 still applies
def test_multiple_styles_and_formatters_highest_specificity_wins(self):
"""
Test that style and formatter are resolved independently with specificity.
Rules:
- Rule 1: style("neutral") - unconditional
- Rule 2: format("EUR") - unconditional
- Rule 3: style("error") if value < 0 - conditional style
- Rule 4: format.number(precision=0) if value < 0 - conditional formatter
When value < 0:
- Style from Rule 3 (higher specificity)
- Formatter from Rule 4 (higher specificity)
"""
engine = FormattingEngine()
rules = [
FormatRule(style=Style(preset="neutral")),
FormatRule(formatter=NumberFormatter(precision=2, suffix="")),
FormatRule(
condition=Condition(operator="<", value=0),
style=Style(preset="error")
),
FormatRule(
condition=Condition(operator="<", value=0),
formatter=NumberFormatter(precision=0, suffix="")
),
]
css, formatted = engine.apply_format(rules, cell_value=-5.67)
assert "var(--color-error)" in css # Rule 3 wins for style
assert formatted == "-6 €" # Rule 4 wins for formatter (precision=0)
class TestWithRowData:
def test_condition_with_column_reference(self):