use crate::component::Component;
use crate::element::Element;
use crate::hooks::Hooks;
use crate::props::{Anchor, TextProps, ViewProps};
#[derive(Clone, PartialEq, Default)]
pub struct TooltipProps {
pub message: String,
pub anchor: Anchor,
}
pub struct Tooltip;
impl Component for Tooltip {
type Props = TooltipProps;
fn render(props: &TooltipProps, hooks: &mut Hooks) -> Element {
let theme = hooks.use_theme();
Element::view(
ViewProps {
overlay: Some(props.anchor),
padding: 0,
background: theme.accent,
..Default::default()
},
vec![Element::text(TextProps {
content: props.message.clone(),
color: theme.surface,
..Default::default()
})],
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::testing::TestTerminal;
#[tokio::test]
async fn shows_the_message_at_the_top_left_anchor() {
let t = TestTerminal::new(
20,
5,
Element::component::<Tooltip>(TooltipProps {
message: "hint".into(),
anchor: Anchor::TopLeft,
}),
)
.unwrap();
let frame = t.frame_text();
let lines: Vec<&str> = frame.lines().collect();
assert!(
lines[1].starts_with(" hint"),
"expected \"hint\" one cell in on the second line, got {:?}",
lines[1]
);
for (i, line) in lines.iter().enumerate() {
if i != 1 {
assert!(
!line.contains("hint"),
"unexpected hint on line {i}: {line:?}"
);
}
}
}
#[tokio::test]
async fn shows_the_message_at_the_bottom_right_anchor() {
let t = TestTerminal::new(
20,
5,
Element::component::<Tooltip>(TooltipProps {
message: "hint".into(),
anchor: Anchor::BottomRight,
}),
)
.unwrap();
let frame = t.frame_text();
let lines: Vec<&str> = frame.lines().collect();
let target_line = lines[3];
assert!(
target_line.trim_end().ends_with("hint"),
"expected \"hint\" near the right edge on line 3, got {target_line:?}"
);
for (i, line) in lines.iter().enumerate() {
if i != 3 {
assert!(
!line.contains("hint"),
"unexpected hint on line {i}: {line:?}"
);
}
}
}
}