split toast into alert and toast

This commit is contained in:
2026-11-01 09:20:10 +07:00
parent 9ddf8fe90f
commit 5a32401e25
10 changed files with 954 additions and 235 deletions
+113
View File
@@ -0,0 +1,113 @@
use std::cell::RefCell;
use std::rc::Rc;
use gpui::{
AnyWindowHandle, AppContext as _, InteractiveElement as _, IntoElement, Modifiers,
ParentElement as _, Render, Styled as _, TestAppContext, VisualTestContext, Window, div, point,
px,
};
use gpui_native_core::alert::{Alert, AlertSeverity};
use gpui_native_theme::Theme;
struct Harness {
severity: AlertSeverity,
dismissed: Rc<RefCell<bool>>,
}
impl Render for Harness {
fn render(&mut self, _window: &mut Window, _cx: &mut gpui::Context<Self>) -> impl IntoElement {
let dismissed = self.dismissed.clone();
div().size_full().p_4().child(
div().debug_selector(|| "test-alert".to_string()).child(
Alert::new("test-alert")
.title("Heads up")
.severity(self.severity)
.message("Secondary line.")
.on_dismiss(move |_| *dismissed.borrow_mut() = true),
),
)
}
}
/// The alert stretches across the top of the content area; its close button
/// sits in the banner's top-right corner.
fn alert_window(
severity: AlertSeverity,
) -> (VisualTestContext, AnyWindowHandle, Rc<RefCell<bool>>) {
let mut cx = TestAppContext::single();
cx.update(Theme::init);
let dismissed = Rc::new(RefCell::new(false));
let harness_dismissed = dismissed.clone();
let window = cx.add_window(move |_, _| Harness {
severity,
dismissed: harness_dismissed,
});
let window = window.into();
cx.update_window(window, |_, window, cx| {
window.draw(cx).clear(cx);
})
.unwrap();
(
VisualTestContext::from_window(window, &cx),
window,
dismissed,
)
}
/// A banner with only a message paints, so the title stays optional.
#[test]
fn a_message_only_alert_renders() {
let mut cx = TestAppContext::single();
cx.update(Theme::init);
let window = cx.add_window(|_, _| MessageOnlyHarness);
let window = window.into();
let mut cx = VisualTestContext::from_window(window, &cx);
cx.update(|window, cx| {
window.draw(cx).clear(cx);
});
assert!(cx.debug_bounds("message-only-alert").is_some());
}
struct MessageOnlyHarness;
impl Render for MessageOnlyHarness {
fn render(&mut self, _window: &mut Window, _cx: &mut gpui::Context<Self>) -> impl IntoElement {
div().size_full().p_4().child(
div()
.debug_selector(|| "message-only-alert".to_string())
.child(Alert::new("message-only").message("Only line.")),
)
}
}
/// Clicking the close button is how the user acts on the alert; the banner
/// reports the intent instead of hiding itself.
#[test]
fn closing_the_alert_reports_dismissal() {
let (mut cx, _window, dismissed) = alert_window(AlertSeverity::Danger);
let bounds = cx.debug_bounds("test-alert").expect("alert rendered");
cx.simulate_click(
point(bounds.right() - px(14.), bounds.top() + px(14.)),
Modifiers::default(),
);
assert!(*dismissed.borrow(), "the close button reported dismissal");
}
/// Every severity paints a banner.
#[test]
fn every_severity_renders_a_banner() {
for severity in [
AlertSeverity::Info,
AlertSeverity::Success,
AlertSeverity::Warning,
AlertSeverity::Danger,
] {
let (mut cx, _window, _dismissed) = alert_window(severity);
assert!(
cx.debug_bounds("test-alert").is_some(),
"{severity:?} alert rendered"
);
}
}