Repository navigation
feat(forms): render any native form control under rx.form.control #7859
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
98a8dba
fix(forms): take a form control's name from its rx.form.field
claude 25fbf38
Merge remote-tracking branch 'origin/main' into claude/clever-ride-al…
claude fa1e5e4
fix(forms): declare the name prop of rx.form.control
claude e467cad
test(forms): assert the whole warning and error messages
claude a207df6
fix(forms): read form field roles dynamically and name the rendered c…
claude be04db7
feat(forms): render any native form control under rx.form.control
claude 6f7d8c2
Merge remote-tracking branch 'upstream/main' into pr-7859
FarhanAliRaza 16c8209
fix(forms): render a form control's child with asChild even when as_c…
FarhanAliRaza File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
1 change: 1 addition & 0 deletions
1
packages/reflex-components-radix/news/+form-control-children.feature.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| `rx.form.control` now accepts `rx.text_area`, `rx.el.input`, `rx.el.textarea` and `rx.el.select` as its child, besides `rx.input` and `rx.debounce_input`, and renders a child as the control without `as_child=True`. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
144 changes: 144 additions & 0 deletions
144
tests/integration/tests_playwright/test_radix_form_control.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,144 @@ | ||
| """Integration tests for the controls ``rx.form.control`` renders. | ||
|
|
||
| Radix ``Form.Control`` renders an input or, with ``as_child``, the native form | ||
| control its child renders, and passes it the enclosing field's ``name`` and | ||
| the ``id`` its label points to. Every control the Python wrapper accepts must | ||
| receive both, submit under the field's name and take part in validation. | ||
| """ | ||
|
|
||
| import json | ||
| from collections.abc import Generator | ||
|
|
||
| import pytest | ||
| from playwright.sync_api import Page, expect | ||
|
|
||
| from reflex.testing import AppHarness | ||
|
|
||
| FIELD_NAMES = ( | ||
|
FarhanAliRaza marked this conversation as resolved.
Resolved
|
||
| "input", | ||
| "controlled", | ||
| "text_area", | ||
| "el_input", | ||
| "el_textarea", | ||
| "el_select", | ||
| ) | ||
|
|
||
|
|
||
| def RadixFormControlApp(): | ||
| """A Radix form with one field per kind of control.""" | ||
| import reflex as rx | ||
|
|
||
| class State(rx.State): | ||
| typed: rx.Field[str] = rx.field("") | ||
| submitted: rx.Field[dict[str, str]] = rx.field(default_factory=dict) | ||
|
|
||
| @rx.event | ||
| def set_typed(self, value: str): | ||
| self.typed = value | ||
|
|
||
| @rx.event | ||
| def on_submit(self, form_data: dict[str, str]): | ||
| self.submitted = form_data | ||
|
|
||
| def form_field(name: str, control: rx.Component) -> rx.Component: | ||
| return rx.form.field( | ||
| rx.form.label(name), | ||
| rx.form.control(control), | ||
| rx.form.message("Required", match="valueMissing", id=f"{name}-message"), | ||
| name=name, | ||
| ) | ||
|
|
||
| @rx.page("/") | ||
| def index(): | ||
| return rx.vstack( | ||
| rx.input( | ||
| value=State.router.session.client_token, read_only=True, id="token" | ||
| ), | ||
| rx.form.root( | ||
| form_field("input", rx.input(default_value="input value")), | ||
| form_field( | ||
| "controlled", | ||
| rx.input(value=State.typed, on_change=State.set_typed), | ||
| ), | ||
| form_field("text_area", rx.text_area(required=True)), | ||
| form_field("el_input", rx.el.input(default_value="native input")), | ||
| form_field( | ||
| "el_textarea", rx.el.textarea(default_value="native textarea") | ||
| ), | ||
| form_field( | ||
| "el_select", | ||
| rx.el.select( | ||
| rx.el.option("Apple", value="apple"), | ||
| rx.el.option("Pear", value="pear"), | ||
| default_value="pear", | ||
| ), | ||
| ), | ||
| rx.form.submit(rx.button("Submit", id="submit"), as_child=True), | ||
| on_submit=State.on_submit, | ||
| ), | ||
| rx.text(State.typed, id="typed"), | ||
| rx.text(State.submitted.to_string(), id="submitted"), | ||
| ) | ||
|
|
||
| app = rx.App() # noqa: F841 | ||
|
|
||
|
|
||
| @pytest.fixture(scope="module") | ||
| def form_control_app(tmp_path_factory) -> Generator[AppHarness, None, None]: | ||
| """Start RadixFormControlApp via AppHarness. | ||
|
|
||
| Args: | ||
| tmp_path_factory: pytest fixture for creating temporary directories. | ||
|
|
||
| Yields: | ||
| Running AppHarness instance. | ||
| """ | ||
| with AppHarness.create( | ||
| root=tmp_path_factory.mktemp("radix_form_control"), | ||
| app_source=RadixFormControlApp, | ||
| ) as harness: | ||
| assert harness.app_instance is not None, "app is not running" | ||
| yield harness | ||
|
|
||
|
|
||
| def test_form_controls_take_their_field_name(form_control_app: AppHarness, page: Page): | ||
| """Each control carries its field's name, its label's id, and validates. | ||
|
|
||
| Args: | ||
| form_control_app: AppHarness running the test app. | ||
| page: Playwright page. | ||
| """ | ||
| assert form_control_app.frontend_url is not None | ||
|
|
||
| page_errors: list[str] = [] | ||
| page.on("pageerror", lambda exc: page_errors.append(str(exc))) | ||
|
|
||
| page.goto(form_control_app.frontend_url) | ||
| expect(page.locator("#token")).not_to_have_value("") | ||
|
|
||
| # The label resolves through the id Radix gives the control. | ||
| for name in FIELD_NAMES: | ||
| expect(page.get_by_label(name, exact=True)).to_have_attribute("name", name) | ||
|
|
||
| # The required textarea blocks submission and shows its field's message. | ||
| page.click("#submit") | ||
| expect(page.locator("#text_area-message")).to_have_text("Required") | ||
| expect(page.locator("#submitted")).to_have_text("{}") | ||
|
|
||
| page.get_by_label("controlled", exact=True).fill("typed value") | ||
| expect(page.locator("#typed")).to_have_text("typed value") | ||
| page.get_by_label("text_area", exact=True).fill("textarea value") | ||
| page.get_by_label("el_select", exact=True).select_option("apple") | ||
| page.click("#submit") | ||
|
|
||
| expect(page.locator("#text_area-message")).to_have_count(0) | ||
| expect(page.locator("#submitted")).not_to_have_text("{}") | ||
| assert json.loads(page.locator("#submitted").text_content() or "") == { | ||
| "input": "input value", | ||
| "controlled": "typed value", | ||
| "text_area": "textarea value", | ||
| "el_input": "native input", | ||
| "el_textarea": "native textarea", | ||
| "el_select": "apple", | ||
| } | ||
| assert page_errors == [] | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.