Skip to content

Conversation

@lukasmasuch
Copy link
Collaborator

@lukasmasuch lukasmasuch commented Dec 10, 2025

Describe your changes

Fixes and issues where elements from previous dialogs were still showing up in a different dialog as stale elements. This was fixed by always calculating the dialog ID and only keeping the existing children in case it's the same dialog -> which expects the same type of children.

Before:

Screen.Recording.2025-12-10.at.20.36.38.mov

After:

Screen.Recording.2025-12-10.at.20.37.27.mov

GitHub Issue Link (if applicable)

Testing Plan

  • Added e2e and unit tests.

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
Contributor

snyk-io bot commented Dec 10, 2025

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.

@github-actions
Copy link
Contributor

github-actions bot commented Dec 10, 2025

✅ PR preview is ready!

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

@lukasmasuch lukasmasuch added security-assessment-completed Security assessment has been completed for PR change:bugfix PR contains bug fix implementation impact:users PR changes affect end users labels Dec 10, 2025
@lukasmasuch lukasmasuch marked this pull request as ready for review December 10, 2025 19:40
Copilot AI review requested due to automatic review settings December 10, 2025 19:40
@lukasmasuch
Copy link
Collaborator Author

@cursor review

Copy link
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

This PR fixes issue #10907 where stale elements from a previously opened dialog would briefly appear when opening a different dialog. The fix introduces a stable dialog identity computed from the dialog's attributes (title, dismissible, width, on_dismiss) and uses this identity to determine whether to preserve children when replacing a dialog block.

Key Changes:

  • Dialog IDs are now always computed and set on the block proto, not just when on_dismiss is activated
  • Frontend logic now checks dialog identity before inheriting children from a previous dialog block
  • Added comprehensive unit and E2E tests to verify the fix

Reviewed changes

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

Show a summary per file
File Description
lib/streamlit/elements/lib/dialog.py Refactored to always compute and set dialog element_id on block_proto.id, moving it outside the on_dismiss conditional block
frontend/lib/src/render-tree/AppRoot.ts Added logic to prevent inheriting children when replacing a dialog with a different identity
frontend/lib/src/render-tree/AppRoot.test.ts Added unit tests verifying that dialogs with different identities don't inherit children, while same-identity dialogs do
e2e_playwright/st_dialog_test.py Added E2E test reproducing issue #10907 and verifying the fix prevents stale content
e2e_playwright/st_dialog.py Added test dialogs (fast and slow) to support the new E2E test case

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no bugs!


@lukasmasuch lukasmasuch added the ai-review If applied to PR or issue will run AI review workflow label Dec 10, 2025
@github-actions github-actions bot removed the ai-review If applied to PR or issue will run AI review workflow label Dec 10, 2025
@github-actions
Copy link
Contributor

Summary

This PR fixes issue #10907 where elements from previous dialogs were showing up as stale content in different dialogs. The bug occurred when:

  1. User opens Dialog A (with content like text inputs)
  2. User dismisses Dialog A
  3. User opens Dialog B (which may take time to render)
  4. During the loading of Dialog B, content from Dialog A would briefly appear

The fix introduces a dialog identity check that prevents the frontend from inheriting children from an existing dialog block when replacing it with a different dialog.

Changes:

  • Python Backend (lib/streamlit/elements/lib/dialog.py): Always compute and assign a stable element ID to dialogs based on their attributes (title, dismissible, width, on_dismiss)
  • TypeScript Frontend (frontend/lib/src/render-tree/AppRoot.ts): When replacing a dialog block, check if the dialog identity (ID) has changed and clear children if so

Code Quality

Python Changes (dialog.py)

The refactoring is clean and follows good practices:

  • The element_id computation was moved from being conditional (only when on_dismiss is activated) to always being computed
  • The ID is now set on block_proto.id which propagates to the frontend
  • Comments clearly explain the purpose: distinguishing dialogs and preventing stale content
  • The existing widget registration logic continues to work as before

TypeScript Changes (AppRoot.ts)

The fix is minimal and targeted:

const isDialogWithDifferentIdentity =
  block.dialog &&
  existingNode.deltaBlock.dialog &&
  block.id !== existingNode.deltaBlock.id

if (!isDialogWithDifferentIdentity) {
  children = existingNode.children
}

Strengths:

  • ✅ Only affects dialog blocks - all other block types continue with existing behavior
  • ✅ Defensive null checks ensure both blocks must be dialogs with dialog data
  • ✅ Clear comment explaining the fix and referencing issue st.dialog shows stale elements from previous dialog #10907
  • ✅ Follows the existing pattern in addBlock for handling block children inheritance

Test Coverage

Frontend Unit Tests (AppRoot.test.ts)

Two comprehensive tests added:

  1. removes a dialog block's children when replacing with a different dialog identity

    • Creates Dialog 1 with children
    • Replaces with Dialog 2 (different identity)
    • Verifies Dialog 2 has zero children
  2. preserves a dialog block's children when replacing with the same dialog identity

    • Creates a dialog with children
    • Replaces with same dialog (same identity)
    • Verifies children are preserved

Both tests follow the existing patterns in the test file and provide good coverage of the new behavior.

E2E Test (st_dialog_test.py)

Test: test_switching_dialogs_does_not_show_stale_content

The test accurately reproduces the issue:

  1. Opens "Fast Dialog" (contains text input and specific content)
  2. Dismisses it via Escape key
  3. Opens "Slow Dialog" (has 1-second delay before rendering)
  4. Immediately verifies no Fast Dialog content appears
  5. Waits for Slow Dialog content to load
  6. Re-verifies no stale content from Fast Dialog

Best practices followed:

  • ✅ Uses expect for auto-wait assertions
  • ✅ Uses stable locators (get_by_test_id, get_by_text)
  • ✅ Descriptive docstring explaining the test scenario and issue reference
  • ✅ No unnecessary screenshots
  • ✅ Tests the specific bug behavior, not implementation details

Backwards Compatibility

Fully backwards compatible

  • Existing dialogs will continue to work exactly as before
  • The only behavioral change is that switching between different dialogs no longer shows stale content
  • Same dialog (same identity) still preserves children as before, maintaining widget state
  • No protobuf changes required - the existing optional string id = 12 field in Block.proto is utilized

Security & Risk

No security concerns

  • This is purely a UI rendering fix
  • The change is scoped specifically to dialog block handling
  • No sensitive data handling changes
  • No network or authentication changes

Risk Assessment: Low

  • The conditional logic is defensive with proper null checks
  • Both "positive" and "negative" cases are tested
  • The fix only applies when both the existing and new blocks are dialog types
  • All other block types are unaffected

Recommendations

No changes are required. The implementation is clean, well-tested, and addresses the issue appropriately.

Minor observations (not blocking):

  1. The E2E test uses time.sleep(1) in the slow dialog - this is intentional and necessary to reproduce the race condition where stale content would appear. This is acceptable for this specific test scenario.

  2. The test could optionally verify that the dialog title changes as well, but the current assertions are sufficient to validate the fix.

Verdict

APPROVED: This is a well-implemented, properly tested bug fix that addresses issue #10907 with minimal, targeted changes. The fix is backwards compatible and poses no security or regression risks. Both the frontend and backend changes are clean and follow existing code patterns in the repository.


This is an automated AI review. Please verify the feedback and use your judgment.

@jrieke
Copy link
Collaborator

jrieke commented Dec 11, 2025

That's amazing, thanks for fixing this, Lukas!!

# Compute a stable identity for the dialog based on its attributes.
# This ID is used in the frontend to distinguish between different dialogs
# and prevent showing stale content from a previous dialog (issue #10907).
# It's also used for widget registration when on_dismiss is activated.
Copy link
Collaborator

Choose a reason for hiding this comment

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

It might be good to add that we don't want this to be a widget in the default case because it would affect the usage in fragments and caching.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Added more comments 👍

@lukasmasuch lukasmasuch enabled auto-merge (squash) December 11, 2025 14:35
@streamlit streamlit deleted a comment from github-actions bot Dec 11, 2025
@lukasmasuch lukasmasuch merged commit 5319def into develop Dec 11, 2025
43 checks passed
@lukasmasuch lukasmasuch deleted the fix/old-dialog-content-showing branch December 11, 2025 14:53
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 impact:users PR changes affect end users security-assessment-completed Security assessment has been completed for PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

st.dialog shows stale elements from previous dialog

4 participants