Bind widgets to query params - st.text_input & st.text_area#13901
Bind widgets to query params - st.text_input & st.text_area#13901mayagbarnes merged 5 commits intodevelopfrom
st.text_input & st.text_area#13901Conversation
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
✅ PR preview is ready!
|
7efbc30 to
b8c4a44
Compare
There was a problem hiding this comment.
Pull request overview
Adds URL query-parameter binding support to Streamlit’s text widgets, enabling two-way sync between st.text_input / st.text_area values and the browser URL (consistent with existing query-param binding patterns used by other widgets like st.color_picker).
Changes:
- Extend
TextInputandTextAreaprotobuf messages withquery_param_key. - Add
bind="query-params"support in the Python widget implementations (including URL seeding behavior +max_charstruncation on deserialize, and a password safety guard fortext_input). - Add frontend query-param binding registration via
useBasicWidgetState, plus unit tests and Playwright E2E coverage.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| proto/streamlit/proto/TextInput.proto | Adds query_param_key field to support frontend query-param binding. |
| proto/streamlit/proto/TextArea.proto | Adds query_param_key field to support frontend query-param binding. |
| lib/streamlit/elements/widgets/text_widgets.py | Adds bind param plumbing, sets proto query key, enforces password restriction, and wires binding into widget registration. |
| lib/tests/streamlit/elements/text_input_test.py | Adds unit tests validating bind behavior, errors, and proto wiring for st.text_input. |
| lib/tests/streamlit/elements/text_area_test.py | Fixes an incorrect proto access and adds unit tests validating bind behavior for st.text_area. |
| frontend/lib/src/components/widgets/TextInput/TextInput.tsx | Registers query-param binding when proto specifies queryParamKey. |
| frontend/lib/src/components/widgets/TextInput/TextInput.test.tsx | Adds unit tests ensuring query-param binding registration/unregistration behavior. |
| frontend/lib/src/components/widgets/TextArea/TextArea.tsx | Registers query-param binding when proto specifies queryParamKey; adjusts WidgetStateManager state getter behavior. |
| frontend/lib/src/components/widgets/TextArea/TextArea.test.tsx | Adds unit tests ensuring query-param binding registration/unregistration behavior. |
| e2e_playwright/st_text_input.py | Adds bound text input examples used by E2E tests. |
| e2e_playwright/st_text_input_test.py | Adds E2E coverage for URL seeding, URL updates/removal, special chars, and max_chars truncation. |
| e2e_playwright/st_text_area.py | Adds bound text area examples used by E2E tests. |
| e2e_playwright/st_text_area_test.py | Adds E2E coverage for URL seeding, URL updates/removal, special chars, and max_chars truncation. |
SummaryThis PR adds
Code QualityThe implementation is clean, consistent, and follows the established patterns from the existing
Minor note on Test CoverageTest coverage is thorough across all three layers: Python unit tests (
Frontend unit tests (
E2E tests (
One minor concern with E2E tests: The regex Backwards CompatibilityThis change is fully backwards compatible:
Security & Risk
AccessibilityNo accessibility concerns. The frontend changes are limited to wiring up the Recommendations
VerdictAPPROVED: Clean, well-tested implementation that follows established patterns from This is an automated AI review by |
|
|
||
| def test_text_area_query_param_seeding(page: Page, app_port: int): | ||
| """Test that text area value can be seeded from URL query params.""" | ||
| page.goto(f"http://localhost:{app_port}/?bound_text_area=seeded_value") |
There was a problem hiding this comment.
note: this will likely need an update once #13679 is merged. cc @sfc-gh-bnisco
| widgetMgr: WidgetStateManager, | ||
| element: TextAreaProto | ||
| ): TextAreaValue | undefined => { | ||
| return widgetMgr.getStringValue(element) ?? element.default ?? null |
There was a problem hiding this comment.
question: Does widgetMgr.getStringValue return the default value, or was element.default not necessary here?
There was a problem hiding this comment.
element.default was not necessary and it was breaking URL-seeded values. When getStringValue returns undefined on first render, the element.default fallback produced a non-nullish value that short-circuited the hook's fallback to getDefaultState. That prevented useBasicWidgetClientState from ever checking element.setValue / element.value, so URL-seeded values never populated the UI. This also makes TextArea consistent with TextInput, which never had this fallback.
Fixes regression in st.text_area height calculation when using height="content". The issue was introduced in PR streamlit#13901, which removed the element.default fallback from getStateFromWidgetMgr. This caused the textarea to be empty during initial render, leading to incorrect height calculation by useTextInputAutoExpand. The fix adds uiValue to the dependencies of useTextInputAutoExpand, ensuring the height is recalculated when the value is updated. Fixes streamlit#14222
## Describe your changes Fixes regression in `st.text_area` with `height="content"` introduced in v1.55.0 (#14222). - Added `uiValue` to `useTextInputAutoExpand` dependencies so height recalculates when default value is set **Root cause:** PR #13901 removed `element.default` fallback from `getStateFromWidgetMgr`. The textarea starts empty when `useLayoutEffect` calculates auto-expand height; the default value arrives later via `useEffect` but height was never recalculated. ## GitHub Issue Fixes #14222 ## Testing Plan - [x] Unit Tests (`frontend/lib/src/components/widgets/TextArea/TextArea.test.tsx` — 28 tests pass) - [x] E2E Tests (`e2e_playwright/st_text_area_test.py::test_text_area_content_height_expansion` — passes) - [x] E2E Tests (`e2e_playwright/st_text_area_test.py::test_text_area_content_height_default_value` — new regression test) <!-- agent-metrics-start --> <details> <summary>Agent metrics</summary> | Type | Name | Count | |------|------|------:| | subagent | fixing-pr | 2 | </details> <!-- agent-metrics-end -->
## Describe your changes Fixes regression in `st.text_area` with `height="content"` introduced in v1.55.0 (#14222). - Added `uiValue` to `useTextInputAutoExpand` dependencies so height recalculates when default value is set **Root cause:** PR #13901 removed `element.default` fallback from `getStateFromWidgetMgr`. The textarea starts empty when `useLayoutEffect` calculates auto-expand height; the default value arrives later via `useEffect` but height was never recalculated. ## GitHub Issue Fixes #14222 ## Testing Plan - [x] Unit Tests (`frontend/lib/src/components/widgets/TextArea/TextArea.test.tsx` — 28 tests pass) - [x] E2E Tests (`e2e_playwright/st_text_area_test.py::test_text_area_content_height_expansion` — passes) - [x] E2E Tests (`e2e_playwright/st_text_area_test.py::test_text_area_content_height_default_value` — new regression test) <!-- agent-metrics-start --> <details> <summary>Agent metrics</summary> | Type | Name | Count | |------|------|------:| | subagent | fixing-pr | 2 | </details> <!-- agent-metrics-end -->
Describe your changes
Adds the bind parameter to
st.text_input&st.text_areato enable two-way sync between widget values and URL query parameters.StreamlitAPIExceptionwhenbind="query-params"is used withtype="password"ontext_inputelement.defaultfallback ingetStateFromWidgetMgrto align with TextInput and fix URL-seeded values not populating the UITesting Plan