Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/reflex-components-radix/news/7431.bugfix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Forward progress values to the Radix Root so its state and ARIA attributes reflect the displayed progress.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ class ProgressRoot(ProgressComponent):
tag = "Root"
alias = "RadixProgressRoot"

value: Var[int | None] = field(doc="The current progress value.")

max: Var[int | None] = field(doc="The maximum progress value.")

radius: Var[LiteralRadius] = field(
doc='Override theme radius for progress bar: "none" | "small" | "medium" | "large" | "full"'
)
Expand Down Expand Up @@ -117,12 +121,16 @@ def create(cls, **props) -> Component:
if "color_scheme" in props:
progress_indicator_props["color_scheme"] = props.pop("color_scheme")

value = props.pop("value", 0)
max_value = props.pop("max", 100)
return ProgressRoot.create(
ProgressIndicator.create(
value=props.pop("value", 0),
max=props.pop("max", 100),
value=value,
max=max_value,
**progress_indicator_props,
),
value=value,
max=max_value,
**props,
)

Expand Down
2 changes: 1 addition & 1 deletion pyi_hashes.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"packages/reflex-components-radix/src/reflex_components_radix/primitives/dialog.pyi": "e67999e6a7433d0eb8c18f4dd185761b",
"packages/reflex-components-radix/src/reflex_components_radix/primitives/drawer.pyi": "172048f74ea5cb7b0e68829d5de3a1fa",
"packages/reflex-components-radix/src/reflex_components_radix/primitives/form.pyi": "2a0611c1ffa91b4c2061ef3697b7ec75",
"packages/reflex-components-radix/src/reflex_components_radix/primitives/progress.pyi": "849b07e27c3916bd24bb576b57e9e2c4",
"packages/reflex-components-radix/src/reflex_components_radix/primitives/progress.pyi": "28496d25212d041a079116381b7d4d51",
"packages/reflex-components-radix/src/reflex_components_radix/primitives/slider.pyi": "2846f40bff377ee688d46055c4f7390a",
"packages/reflex-components-radix/src/reflex_components_radix/themes/__init__.pyi": "6c7e1b9a0a7ca00f029bf6ba0c514fdb",
"packages/reflex-components-radix/src/reflex_components_radix/themes/base.pyi": "bf24641c7b44efcacaf67e2e49947c2c",
Expand Down
67 changes: 67 additions & 0 deletions tests/integration/tests_playwright/test_radix_components.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
"""Playwright integration tests for Radix primitive components."""

from collections.abc import Generator

import pytest
from playwright.sync_api import Page, expect

from reflex.testing import AppHarness


def ProgressApp():
"""App with a stateful primitive Progress component."""
from reflex_components_radix.primitives.progress import progress

import reflex as rx

class ProgressState(rx.State):
value: int = 25

@rx.event
def advance(self):
self.value = 50

app = rx.App()

@app.add_page
def index():
return rx.vstack(
progress(value=ProgressState.value, max=100, id="progress"),
rx.button("Advance", on_click=ProgressState.advance),
)


@pytest.fixture(scope="module")
def progress_app(
app_harness_env: type[AppHarness], tmp_path_factory: pytest.TempPathFactory
) -> Generator[AppHarness, None, None]:
"""Run the primitive Progress app in dev and prod modes.

Args:
app_harness_env: AppHarness class for dev or production mode.
tmp_path_factory: Pytest fixture for creating temporary directories.

Yields:
The running app harness.

"""
with app_harness_env.create(
root=tmp_path_factory.mktemp("radix_progress"),
app_source=ProgressApp,
) as harness:
assert harness.app_instance is not None, "app is not running"
yield harness


def test_progress_aria_value_updates(progress_app: AppHarness, page: Page) -> None:
"""The Root exposes and updates its current value through ARIA."""
assert progress_app.frontend_url is not None

page.goto(progress_app.frontend_url)
progressbar = page.get_by_role("progressbar")
expect(progressbar).to_have_attribute("id", "progress")
expect(progressbar).to_have_attribute("aria-valuenow", "25")
expect(progressbar).to_have_attribute("aria-valuemax", "100")

page.get_by_role("button", name="Advance").click()
expect(progressbar).to_have_attribute("aria-valuenow", "50")
53 changes: 53 additions & 0 deletions tests/units/components/radix/test_primitive_progress.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
"""Tests for the Radix primitive progress wrapper."""

import pytest
from reflex_base.vars.base import Var
from reflex_components_radix.primitives.progress import (
ProgressIndicator,
ProgressRoot,
progress,
)


@pytest.mark.parametrize(
("value", "max_value", "expected_value", "expected_max"),
[
(25, 200, "25", "200"),
(
Var(_js_expr="progress_state.amount", _var_type=int),
Var(_js_expr="progress_state.total", _var_type=int),
"progress_state.amount",
"progress_state.total",
),
],
)
def test_progress_forwards_numeric_props_to_root_and_indicator(
value: int | Var[int],
max_value: int | Var[int],
expected_value: str,
expected_max: str,
) -> None:
"""The Root exposes numeric progress while the Indicator uses it for width."""
component = progress(value=value, max=max_value)

assert isinstance(component, ProgressRoot)
indicator = component.children[0]
assert isinstance(indicator, ProgressIndicator)

for part in (component, indicator):
props = part._render().props
assert props["value"]._js_expr == expected_value
assert props["max"]._js_expr == expected_max

Comment thread
greptile-apps[bot] marked this conversation as resolved.

def test_progress_forwards_defaults_to_root_and_indicator() -> None:
"""The default progress state is shared by both primitive parts."""
component = progress()
assert isinstance(component, ProgressRoot)
indicator = component.children[0]
assert isinstance(indicator, ProgressIndicator)

for part in (component, indicator):
props = part._render().props
assert props["value"]._js_expr == "0"
assert props["max"]._js_expr == "100"
Loading