Skip to content

dialog: Merge button_props instead of replacing them - #3126

Merged
huacnlee merged 4 commits into
mainfrom
dialog-button-props-merge
Sep 19, 2026
Merged

huacnlee merged 4 commits into
mainfrom
dialog-button-props-merge

Conversation

@huacnlee

@huacnlee huacnlee commented Sep 18, 2026

Copy link
Copy Markdown
Member

Problem

Reported downstream (ai-desktop / pilot): AlertDialog::confirm() only sets
show_cancel on the dialog's button props, and Dialog::on_ok / on_cancel /
on_close write their callbacks into that same value — but button_props
assigned the whole value over it. So this quietly lost the Cancel button:

alert
    .confirm()
    .button_props(DialogButtonProps::default().ok_text("Delete").ok_variant(Danger))

and this quietly lost the callback:

alert.on_ok(|_, _, _| true).button_props(DialogButtonProps::default().ok_text("Delete"))

Downstream had to repeat .show_cancel(true) at every call site and leave a
comment saying "button_props replaces the whole value". Dialog::button_props
had the same behavior.

Change

  • Every field of DialogButtonProps is unset until a builder sets it
    (ok_variant, cancel_variant, show_cancel and the three callbacks became
    Option), and button_props merges rather than assigns: the fields the
    value sets win, the rest of what the dialog already carries survives, in any
    call order. Unset fields fall back at render time to the defaults they had
    before — Primary for the OK variant, ButtonVariant::default() for Cancel,
    no Cancel button, callbacks that close the dialog.

  • AlertDialog gains ok_text, ok_variant, cancel_text and
    cancel_variant, so the common dangerous confirmation no longer has to build
    a props bundle:

    alert.title("Delete “Roadmap”?").confirm().ok_text("Delete").ok_variant(ButtonVariant::Danger)

    They are on AlertDialog only. A plain Dialog renders its buttons through
    footer, so the same builders there would compile and do nothing — exactly
    the kind of API a downstream reader (or model) would mistake for a working
    one.

  • AlertDialog now keeps one copy of the button props, on the base dialog,
    instead of a second copy it had to reconcile while building the surface.

  • A cancelled AlertDialog now reports on_close as well as on_cancel, the
    pair a cancelled Dialog already reported. The second copy's replace had
    dropped the alert's on_close on the way to the surface; the shell host test
    (window_effects_host.rs) had encoded that with a close count of four and now
    expects five.

The DialogButtonProps builder signatures are unchanged and Default still
works, so this is not a breaking change — no Breaking Changes section needed.

Tests

crates/component/src/dialog/alert_dialog.rs and dialog.rs:

  • .confirm() followed by .button_props(…) keeps the Cancel button;
  • .button_props(…) followed by .confirm() does too;
  • .on_ok(…) followed by .button_props(…) still runs the callback once;
  • the direct builders resolve to the same props as a button_props value;
  • successive Dialog::button_props calls merge with what the dialog carries.

The first, third and fifth fail against the old replace semantics (verified by
temporarily restoring them); the other two only compile with the new builders.

cargo test -p gpui-component --lib dialog   # 11 passed
cargo test -p gpui-component --lib          # 529 passed
cargo clippy -p gpui-component -p gpui-component-story -p gpui-component-shell --all-targets -- --deny warnings   # clean
cargo fmt --check                           # clean
typos crates/component/src/dialog website   # clean

Docs

website/component/alert-dialog.md and its website/zh-CN/component/
counterpart: the dangerous-confirmation examples now use the direct builders,
and the page states that button_props overrides only the fields the value
sets. website/component/dialog.md (both locales) gets a one-paragraph
"Action Buttons" note: a Dialog puts its own buttons in footer and has them
dispatch Confirm/Cancel, on_ok/on_cancel decide Enter/Esc, and a
confirmation with default buttons is AlertDialog.
skills/gpui-kit/references/usage.md and the AlertDialog story follow the same
style.

🤖 Generated with Claude Code

`AlertDialog::confirm` set `show_cancel` on the dialog's button props, and
`Dialog::on_ok`/`on_cancel`/`on_close` wrote their callbacks into the same
value, but `button_props` then assigned the whole value over it. A caller who
wrote `.confirm().button_props(DialogButtonProps::default().ok_text("Delete"))`
lost the Cancel button, and one who set a callback before the props lost the
callback, so downstream code had to repeat `.show_cancel(true)` at every call
site and remember which order was safe.

Every field of `DialogButtonProps` is now unset until a builder sets it, and
`button_props` merges: the fields the value sets win, the rest of what the
dialog carries survives, in any call order. Unset fields fall back to the
defaults they had before — `Primary` for the OK variant, no Cancel button,
callbacks that close the dialog.

`AlertDialog` also gains `ok_text`, `ok_variant`, `cancel_text` and
`cancel_variant`, so the common dangerous confirmation no longer builds a props
bundle:

    alert.title("Delete “Roadmap”?").confirm().ok_text("Delete").ok_variant(Danger)

They live on `AlertDialog` only: a plain `Dialog` renders its buttons through
`footer`, so the same builders there would compile and do nothing.

`AlertDialog` now keeps one copy of the button props, on the base dialog,
rather than a second copy it had to reconcile when building the surface.

The `DialogButtonProps` builder signatures are unchanged and `Default` still
works, so this is not a breaking change.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@huacnlee
huacnlee force-pushed the dialog-button-props-merge branch from c143220 to 5c30f12 Compare September 18, 2026 11:29
huacnlee and others added 2 commits September 18, 2026 23:56
…l host test

The count of four encoded the replace bug: `AlertDialog::render` put its
own button props over the base dialog's, which is where `on_close`
lived, so a cancelled alert reported `on_cancel` alone. With the props
merged the alert reports the same pair a cancelled `Dialog` does.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@huacnlee
huacnlee enabled auto-merge (squash) September 18, 2026 16:11
@huacnlee
huacnlee merged commit 7e2003c into main Sep 19, 2026
12 checks passed
@huacnlee
huacnlee deleted the dialog-button-props-merge branch September 19, 2026 05:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant