Skip to content

fix(widgets): clear stale date validation UI on form clear#14066

Merged
sfc-gh-bnisco merged 1 commit intodevelopfrom
02-22-fix_widgets_clear_stale_date_validation_ui_on_form_clear
Feb 23, 2026
Merged

fix(widgets): clear stale date validation UI on form clear#14066
sfc-gh-bnisco merged 1 commit intodevelopfrom
02-22-fix_widgets_clear_stale_date_validation_ui_on_form_clear

Conversation

@sfc-gh-bnisco
Copy link
Copy Markdown
Collaborator

@sfc-gh-bnisco sfc-gh-bnisco commented Feb 22, 2026

Describe your changes

Fixed validation error state persistence in DateInput and DateTimeInput widgets when forms are cleared. Previously, validation errors would remain visible even after form submission/clearing, creating a confusing user experience. Now both widgets properly reset their error states when the form is cleared through the onFormCleared callback.

For DateTimeInput, also refactored the initialization logic to ensure pendingDate is properly initialized with the correct initial value instead of relying on the committed date state.

GitHub Issue Link (if applicable)

Fixes #14052

Testing Plan

  • Unit Tests (JS and/or Python)
    • Added test cases for both DateInput and DateTimeInput widgets that verify validation errors are cleared when forms are submitted

Contribution License Agreement

By submitting this pull request you agree that all contributions to this project are made under the Apache 2.0 license.

@snyk-io
Copy link
Copy Markdown
Contributor

snyk-io bot commented Feb 22, 2026

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

Copy link
Copy Markdown
Collaborator Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 22, 2026

✅ PR preview is ready!

Name Link
📦 Wheel file https://core-previews.s3-us-west-2.amazonaws.com/pr-14066/streamlit-1.54.0-py3-none-any.whl
📦 @streamlit/component-v2-lib Download from artifacts
🕹️ Preview app pr-14066.streamlit.app (☁️ Deploy here if not accessible)

@sfc-gh-bnisco sfc-gh-bnisco added change:bugfix PR contains bug fix implementation impact:users PR changes affect end users security-assessment-completed feature:st.date_input Related to the `st.date_input` widget feature:st.datetime_input Related to the `st.datetime_input` widget ai-review If applied to PR or issue will run AI review workflow labels Feb 22, 2026 — with Graphite App
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a UX issue in the frontend widget layer where DateInput / DateTimeInput validation errors could remain visible after a clear_on_submit form clear, by ensuring local UI error state is reset when the form is cleared.

Changes:

  • DateTimeInput: initialize pendingDate from the same initial committed value logic, and reset pendingDate on form clear so the derived validation error UI disappears.
  • DateInput: wire onFormCleared to clear the local error state, and refactor error-reset into a small callback.
  • Add unit tests for both widgets verifying validation errors are cleared after form submission/clear.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
frontend/lib/src/components/widgets/DateTimeInput/DateTimeInput.tsx Resets pendingDate on form clear and refactors initial pendingDate initialization to avoid relying on post-hook committed state.
frontend/lib/src/components/widgets/DateTimeInput/DateTimeInput.test.tsx Adds coverage ensuring the validation tooltip disappears after a clear-on-submit form clear.
frontend/lib/src/components/widgets/DateInput/DateInput.tsx Clears local error state via onFormCleared to prevent stale validation UI after form clear.
frontend/lib/src/components/widgets/DateInput/DateInput.test.tsx Adds coverage ensuring the validation tooltip disappears after a clear-on-submit form clear.

@github-actions github-actions bot removed the ai-review If applied to PR or issue will run AI review workflow label Feb 22, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Consolidated Code Review: PR #14066 — fix(widgets): clear stale date validation UI on form clear

Summary

This PR fixes a bug (#14052) where validation error states (red border, error tooltip) persist in DateInput and DateTimeInput widgets after a form with clear_on_submit=True is submitted. The root cause is that the form-clear path resets the widget value via useBasicWidgetState but never invokes handleChange, which is the only place that clears the local error state.

The fix registers onFormCleared callbacks with useBasicWidgetState in both widgets to explicitly clear stale validation UI on form clear. For DateTimeInput, the initialization logic is also refactored so pendingDate/prevCommittedDate are declared before useBasicWidgetState (required so setPendingDate is available for the onFormCleared callback).

Changed files:

  • DateInput.tsx — adds a resetError callback as onFormCleared; reuses it in handleChange.
  • DateTimeInput.tsx — adds onFormCleared to reset pendingDate; refactors initialization via getInitialCommittedDate helper.
  • DateInput.test.tsx / DateTimeInput.test.tsx — one new test each verifying error state clears on form submit.

Reviewer Consensus

Both reviewers (gpt-5.3-codex-high and opus-4.6-thinking) approved the PR. There were no disagreements on the assessment — both found the fix targeted, low-risk, well-tested, and following established patterns. The reviewers' observations are complementary rather than conflicting.

Code Quality

Agreement: Both reviewers agree the implementation is clean and follows existing widget-state patterns (e.g., NumberInput).

DateInput.tsx — Minimal and well-structured. The resetError callback is properly memoized via useCallback([], []) (stable since setError from useState is stable). Reusing resetError() in handleChange instead of an inline setError(null) improves consistency. The dependency array sort is a minor style improvement.

DateTimeInput.tsx — The changes are sound but more involved. The getInitialCommittedDate helper duplicates the initialization logic from useBasicWidgetState (getStateFromWidgetMgr ?? (setValue ? getCurrState : getDefault)). I verified this matches the hook's actual implementation. This duplication is necessary because pendingDate and prevCommittedDate must be declared before useBasicWidgetState so setPendingDate is available for the onFormCleared callback. While pragmatic, this introduces a coupling — if useBasicWidgetState's initialization logic changes, this helper could silently drift. A brief comment noting this would be helpful.

One subtle behavioral difference: useState(getInitialCommittedDate) is now called twice (for pendingDate and prevCommittedDate), producing two distinct Date objects. This means committedDate !== prevCommittedDate may be true on the first render, triggering the sync block once on mount. This is harmless (React handles setState-during-render correctly and stabilizes in a single extra pass) but is a minor behavioral change from the original code.

Test Coverage

Agreement: Both reviewers found the unit test coverage solid and the test patterns appropriate.

Both new tests follow a well-structured pattern:

  1. Render widget inside a form with clear_on_submit=True
  2. Type an out-of-range date to trigger validation error
  3. Assert error tooltip is visible (positive assertion)
  4. Submit the form
  5. Assert error tooltip is gone (anti-regression assertion)
  6. Assert input value resets to default

The tests use proper RTL patterns: userEvent for interactions, getByTestId/queryByTestId for presence/absence, waitFor for async state, and act for widget manager operations.

Agreement on gap: Both reviewers note the absence of dedicated E2E tests for this specific regression scenario. Both consider this acceptable for a focused bug fix but recommend E2E coverage as a follow-up.

Backwards Compatibility

Agreement: No API or protocol changes. The onFormCleared callback is an optional, pre-existing parameter in useBasicWidgetState. The fix only adds behavior (clearing error state on form clear) without changing existing behavior paths.

Security & Risk

Agreement: No security concerns. Changes are purely client-side UI state management. Regression risk is low and localized to form-clear flows.

Accessibility

Agreement: No accessibility impact. Error display mechanisms (including ARIA patterns via stTooltipErrorHoverTarget) remain unchanged.

Recommendations

  1. Add a comment noting the initialization coupling in DateTimeInput.tsx at getInitialCommittedDate, e.g.: "Mirrors the initialization logic from useBasicWidgetState — keep in sync if that hook's init changes." (Non-blocking)

  2. Consider E2E test coverage for the error-clears-on-form-submit scenario in both st_date_input_test.py and st_datetime_input_test.py. (Non-blocking, can be a follow-up)

  3. Consider resetting isEmpty in DateInput's onFormCleared: Pre-existing issue where isEmpty can be stale after form clear. Not a regression from this PR, but a natural enhancement. (Non-blocking)

Verdict

APPROVED — Clean, well-tested bug fix that correctly addresses stale validation error states on form clear, following established patterns. Both reviewers agree it is ready to merge. The recommendations above are non-blocking improvements.

Missing Reviews

None — both expected models (gpt-5.3-codex-high and opus-4.6-thinking) completed their reviews.


Consolidated review by opus-4.6-thinking.


📋 Review by `gpt-5.3-codex-high`

Summary

This PR fixes stale validation UI in DateInput and DateTimeInput when a clear_on_submit form is submitted. It also improves DateTimeInput initialization so temporary selection state is seeded from the correct initial committed value.

Code Quality

The implementation is clean and follows existing widget-state patterns:

  • DateInput now resets local error state via onFormCleared, which directly addresses the stale tooltip/error styling bug.
  • DateTimeInput now resets pending state on form clear and initializes pending/previous committed state from the same source of truth used by the widget manager flow.
  • No problematic side effects, unsafe mutations, or anti-patterns were introduced in the changed files.

Test Coverage

Coverage is good at the unit-test layer for the regression:

  • Added focused tests in frontend/lib/src/components/widgets/DateInput/DateInput.test.tsx and frontend/lib/src/components/widgets/DateTimeInput/DateTimeInput.test.tsx that verify error state is cleared after form submit/clear.
  • Existing e2e coverage already exercises datetime form reset behavior, but there is still no dedicated e2e assertion for this exact regression path (invalid value -> error visible -> form submit clears error UI), especially for DateInput.

Backwards Compatibility

No API or protocol changes were introduced. Behavior changes are limited to client-side UI state reset in form-clear flows and are backwards compatible with expected clear_on_submit semantics.

Security & Risk

No security concerns identified. Regression risk is low and localized to frontend widget state synchronization during form-clear events.

Accessibility

No new accessibility issues identified in the diff. The change does not alter keyboard interaction patterns, semantics, or accessible naming.

Recommendations

  1. Add one e2e scenario per widget (st.date_input, st.datetime_input) that explicitly verifies error UI is removed after clear_on_submit form submission.

Verdict

APPROVED: The fix is targeted, low-risk, and adequately validated with unit tests; it is ready to merge.


This is an automated AI review by gpt-5.3-codex-high. Please verify the feedback and use your judgment.

📋 Review by `opus-4.6-thinking`

Summary

This PR fixes a bug where validation error states (red border, error tooltip) persist in DateInput and DateTimeInput widgets after a form with clear_on_submit=True is submitted. The root cause is that the form-clear path resets the widget value via useBasicWidgetState but never invokes handleChange, which is the only place that clears the error state. The fix registers onFormCleared callbacks with useBasicWidgetState to explicitly clear stale validation UI on form clear.

Changed files:

  • DateInput.tsx — adds a resetError callback passed as onFormCleared to clear the error state.
  • DateTimeInput.tsx — adds an onFormCleared callback that resets pendingDate to the default (which clears the derived error memo), and refactors pendingDate/prevCommittedDate initialization to use a helper that replicates the hook's init logic.
  • DateInput.test.tsx / DateTimeInput.test.tsx — adds one test each verifying the error clears on form submit.

Code Quality

DateInput.tsx — Clean and minimal. The resetError callback is properly memoized with useCallback([], []) (stable since setError from useState is stable). Reusing resetError() inside handleChange instead of the inline setError(null) is a nice consistency improvement. The alphabetically-sorted dependency array is a minor style improvement.

DateTimeInput.tsx — The changes are sound but more involved:

  1. getInitialCommittedDate (line 79–86): This helper duplicates the initialization logic from useBasicWidgetState (getStateFromWidgetMgr ?? (setValue ? getCurrState : getDefault)). This is necessary because pendingDate and prevCommittedDate must be declared before useBasicWidgetState (so setPendingDate is available for the onFormCleared callback), and these states need the same initial value. The duplication is pragmatic but introduces a coupling — if useBasicWidgetState's initialization logic changes, this could silently drift. A brief comment noting this coupling would be helpful.

  2. Initialization with separate function calls (lines 89–96): useState(getInitialCommittedDate) is called twice, producing two distinct Date objects for the same timestamp. This means committedDate !== prevCommittedDate will be true on the first render, triggering the sync block once on mount. This is harmless (React handles setState-during-render correctly, and the sync stabilizes in a single extra render pass) but is a behavioral difference from the original code where both were initialized with the same committedDate reference.

  3. Inline useCallback for onFormCleared (line 109–111): The callback depends on [element], which changes identity on each protobuf message. This is consistent with how other widgets handle element-dependent callbacks and is fine since useFormClearHelper already re-subscribes on element changes.

Test Coverage

Both new tests follow a solid pattern:

  1. Render widget inside a form with clear_on_submit=True
  2. Type an out-of-range date to trigger validation error
  3. Assert error tooltip is visible (positive assertion)
  4. Submit the form
  5. Assert error tooltip is gone (negative/anti-regression assertion)
  6. Assert input value resets to default

The tests use appropriate RTL patterns: userEvent for interactions, getByTestId/queryByTestId for presence/absence checks, waitFor for async state changes, and act for widget manager operations.

Minor gap: There are no E2E tests for this scenario. Given this is a focused bug fix with good unit test coverage, this is acceptable, but E2E coverage for date validation in clear_on_submit forms would strengthen confidence in the future.

Backwards Compatibility

No breaking changes. The onFormCleared callback is an optional parameter in useBasicWidgetState that was already supported. The fix only adds behavior (clearing error state on form clear) without changing any existing behavior paths.

Security & Risk

No security concerns. The changes are purely UI state management (clearing local React state on form clear). No new data flows, network calls, or user data handling introduced.

Regression risk: Low. The DateInput change is minimal (just clearing error state). The DateTimeInput change is slightly riskier due to the initialization refactor, but the sync block (committedDate !== prevCommittedDate) provides a safety net that reconciles pendingDate with the committed value regardless of initialization.

Accessibility

No accessibility impact. The changes only affect when error states are cleared, not how they are displayed. The error tooltip mechanism (which uses proper ARIA patterns via stTooltipErrorHoverTarget) remains unchanged.

Recommendations

  1. Add a comment noting the initialization coupling in DateTimeInput.tsx at getInitialCommittedDate (line 79), e.g.: "Mirrors the initialization logic from useBasicWidgetState — keep in sync if that hook's init changes."

  2. Consider resetting isEmpty in DateInput's onFormCleared: Currently, only error is reset. If a user clears the input (making isEmpty = true) and then submits the form, isEmpty remains true even though the value has been reset to the default. This is a pre-existing issue and not a regression from this PR, but fixing it alongside the error state would be a natural enhancement.

  3. Consider E2E test coverage: Adding a test case to e2e_playwright/st_date_input_test.py (and/or st_datetime_input_test.py) that validates the error clears on form submit with clear_on_submit=True would provide end-to-end confidence. This could be done as a follow-up.

Verdict

APPROVED: Clean, well-tested bug fix that correctly clears stale validation error states on form clear, following established patterns from other widgets (e.g., NumberInput). The minor observations above are non-blocking.


This is an automated AI review by opus-4.6-thinking.

When clear_on_submit resets date widgets to their defaults, local validation state can remain stale because the normal change handler may not run if the committed value is unchanged.
Add explicit onFormCleared handling to reset local widget state in DateInput and DateTimeInput, and add regression tests for invalid in-form values with non-empty defaults to verify that error UI is cleared after form submission.
@sfc-gh-bnisco sfc-gh-bnisco force-pushed the 02-22-fix_widgets_clear_stale_date_validation_ui_on_form_clear branch from 3fda6f6 to 6fdde49 Compare February 22, 2026 23:39
@sfc-gh-bnisco sfc-gh-bnisco marked this pull request as ready for review February 22, 2026 23:57
Copy link
Copy Markdown
Collaborator

@lukasmasuch lukasmasuch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍

@sfc-gh-bnisco sfc-gh-bnisco enabled auto-merge (squash) February 23, 2026 16:48
@sfc-gh-bnisco sfc-gh-bnisco merged commit 9c8bd5c into develop Feb 23, 2026
43 checks passed
@sfc-gh-bnisco sfc-gh-bnisco deleted the 02-22-fix_widgets_clear_stale_date_validation_ui_on_form_clear branch February 23, 2026 16:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change:bugfix PR contains bug fix implementation feature:st.date_input Related to the `st.date_input` widget feature:st.datetime_input Related to the `st.datetime_input` widget impact:users PR changes affect end users

Projects

None yet

Development

Successfully merging this pull request may close these issues.

st.date_input: error state persists after form submit with clear_on_submit=True (non-None default)

3 participants