#![cfg(feature = "popover")]
use iced::keyboard::{self, Key, key::Named};
use iced::widget::{Button, column, text, text_input};
use iced::{Element, Length, Point};
use iced_cube::Chord;
use iced_cube::overlay::popover::{Action, Side, default_keymap, popover};
use iced_test::simulator::{self, Simulator};
#[derive(Debug, Clone, PartialEq)]
enum Message {
Toggle,
Dismiss,
Apply,
Behind,
Width(String),
Max(String),
}
fn button(label: &'static str) -> Button<'static, Message> {
iced::widget::button(text(label))
}
fn view(open: bool, dismissable: bool) -> Element<'static, Message> {
let content = column![text("Dimensions"), button("Apply").on_press(Message::Apply)].spacing(8);
let popover = popover(button("Open").on_press(Message::Toggle), content)
.open(open)
.side(Side::Bottom);
let popover = if dismissable {
popover.on_dismiss(Message::Dismiss)
} else {
popover
};
column![
popover,
button("Behind")
.on_press(Message::Behind)
.width(Length::Fixed(400.0))
]
.into()
}
fn messages(ui: Simulator<'_, Message>) -> Vec<Message> {
ui.into_messages().collect()
}
fn click_at(ui: &mut Simulator<'_, Message>, position: Point) {
ui.point_at(position);
let _ = ui.simulate(simulator::click());
}
#[test]
fn closed_popover_shows_only_the_trigger() {
let mut ui = simulator::simulator(view(false, true));
assert!(ui.find("Open").is_ok());
assert!(ui.find("Dimensions").is_err());
ui.click("Open").expect("trigger is rendered");
assert_eq!(messages(ui), vec![Message::Toggle]);
}
#[test]
fn open_popover_renders_its_content_and_it_is_interactive() {
let mut ui = simulator::simulator(view(true, true));
assert!(ui.find("Dimensions").is_ok());
ui.click("Apply").expect("content is rendered");
assert_eq!(messages(ui), vec![Message::Apply]);
}
#[test]
fn escape_dismisses_an_open_popover() {
let mut ui = simulator::simulator(view(true, true));
let _ = ui.tap_key(Key::Named(Named::Escape));
assert_eq!(messages(ui), vec![Message::Dismiss]);
let mut closed = simulator::simulator(view(false, true));
let _ = closed.tap_key(Key::Named(Named::Escape));
assert!(messages(closed).is_empty());
}
#[test]
fn clicking_outside_dismisses() {
let mut ui = simulator::simulator(view(true, true));
click_at(&mut ui, Point::new(900.0, 700.0));
assert_eq!(messages(ui), vec![Message::Dismiss]);
}
#[test]
fn the_click_that_dismisses_still_reaches_what_it_landed_on() {
let mut ui = simulator::simulator(view(true, true));
let behind = ui
.find("Behind")
.expect("button behind is rendered")
.bounds();
click_at(&mut ui, Point::new(390.0, behind.center_y()));
assert_eq!(messages(ui), vec![Message::Dismiss, Message::Behind]);
}
#[test]
fn the_keymap_decides_which_keys_dismiss() {
let keymap = default_keymap()
.unbind(&Chord::named(Named::Escape))
.bind(Chord::character('q'), Action::Close);
let element: Element<'_, Message> = popover(button("Open"), text("Content"))
.open(true)
.keymap(keymap)
.on_dismiss(Message::Dismiss)
.into();
let mut ui = simulator::simulator(element);
let _ = ui.tap_key(Key::Named(Named::Escape));
let _ = ui.tap_key(Key::Character("q".into()));
assert_eq!(messages(ui), vec![Message::Dismiss]);
}
#[test]
fn clicking_inside_does_not_reach_the_widgets_underneath() {
let mut ui = simulator::simulator(view(true, true));
ui.click("Dimensions").expect("content is rendered");
assert!(messages(ui).is_empty());
}
#[test]
fn the_trigger_toggles_without_being_dismissed() {
let mut ui = simulator::simulator(view(true, true));
ui.click("Open").expect("trigger is rendered");
assert_eq!(messages(ui), vec![Message::Toggle]);
}
fn form() -> Element<'static, Message> {
let content = column![
text_input("Width", "").on_input(Message::Width),
text_input("Max", "").on_input(Message::Max),
]
.spacing(8);
column![
popover(button("Open").on_press(Message::Toggle), content)
.open(true)
.on_dismiss(Message::Dismiss),
text_input("Outside", "").on_input(|_| Message::Behind),
]
.into()
}
fn shift_tab() -> iced::Event {
iced::Event::Keyboard(keyboard::Event::KeyPressed {
key: Key::Named(Named::Tab),
modified_key: Key::Named(Named::Tab),
physical_key: keyboard::key::Physical::Unidentified(
keyboard::key::NativeCode::Unidentified,
),
location: keyboard::Location::Standard,
modifiers: keyboard::Modifiers::SHIFT,
text: None,
repeat: false,
})
}
#[test]
fn tab_moves_between_the_fields_in_the_panel_and_wraps() {
let mut ui = simulator::simulator(form());
ui.click("Width").expect("first field");
let _ = ui.tap_key(Key::Named(Named::Tab));
let _ = ui.typewrite("5");
let _ = ui.tap_key(Key::Named(Named::Tab));
let _ = ui.typewrite("7");
let _ = ui.simulate([shift_tab()]);
let _ = ui.typewrite("9");
let fields: Vec<&str> = messages(ui)
.iter()
.map(|message| match message {
Message::Width(_) => "Width",
Message::Max(_) => "Max",
_ => "other",
})
.collect();
assert_eq!(
fields,
["Max", "Width", "Max"],
"Tab, Tab wrapping to the first field, then Shift+Tab back"
);
}
#[test]
fn tab_outside_the_panel_is_left_alone() {
let mut ui = simulator::simulator(form());
ui.click("Outside").expect("field behind the popover");
assert_eq!(
ui.tap_key(Key::Named(Named::Tab)),
iced::event::Status::Ignored
);
}
#[test]
fn without_on_dismiss_only_the_app_closes_it() {
let mut ui = simulator::simulator(view(true, false));
let _ = ui.tap_key(Key::Named(Named::Escape));
click_at(&mut ui, Point::new(900.0, 700.0));
assert!(messages(ui).is_empty());
}