ratatui/tests/widgets_table.rs
Dheepak Krishnamurthy 9df6cebb58
feat: Table column calculation uses layout spacing (#824)
This uses the new `spacing` feature of the `Layout` struct to allocate
columns spacing in the `Table` widget.
This changes the behavior of the table column layout in the following
ways:

1. Selection width is always allocated.
- if a user does not want a selection width ever they should use
`HighlightSpacing::Never`
2. Column spacing is prioritized over other constraints
- if a user does not want column spacing, they should use
`Table::new(...).column_spacing(0)`

---------

Co-authored-by: Josh McKinney <joshka@users.noreply.github.com>
2024-01-16 21:51:25 -05:00

988 lines
38 KiB
Rust
Executable file

#![allow(deprecated)]
use ratatui::{
backend::TestBackend,
buffer::Buffer,
layout::Constraint,
style::{Color, Modifier, Style},
text::{Line, Span},
widgets::{Block, Borders, Cell, HighlightSpacing, Row, Table, TableState},
Terminal,
};
#[test]
fn widgets_table_column_spacing_can_be_changed() {
let test_case = |column_spacing, expected| {
let backend = TestBackend::new(30, 10);
let mut terminal = Terminal::new(backend).unwrap();
terminal
.draw(|f| {
let size = f.size();
let table = Table::new(
vec![
Row::new(vec!["Row11", "Row12", "Row13"]),
Row::new(vec!["Row21", "Row22", "Row23"]),
Row::new(vec!["Row31", "Row32", "Row33"]),
Row::new(vec!["Row41", "Row42", "Row43"]),
],
[
Constraint::Length(5),
Constraint::Length(5),
Constraint::Length(5),
],
)
.header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
.block(Block::default().borders(Borders::ALL))
.column_spacing(column_spacing);
f.render_widget(table, size);
})
.unwrap();
terminal.backend().assert_buffer(&expected);
};
// no space between columns
test_case(
0,
Buffer::with_lines(vec![
"┌────────────────────────────┐",
"│Head1Head2Head3 │",
"│ │",
"│Row11Row12Row13 │",
"│Row21Row22Row23 │",
"│Row31Row32Row33 │",
"│Row41Row42Row43 │",
"│ │",
"│ │",
"└────────────────────────────┘",
]),
);
// one space between columns
test_case(
1,
Buffer::with_lines(vec![
"┌────────────────────────────┐",
"│Head1 Head2 Head3 │",
"│ │",
"│Row11 Row12 Row13 │",
"│Row21 Row22 Row23 │",
"│Row31 Row32 Row33 │",
"│Row41 Row42 Row43 │",
"│ │",
"│ │",
"└────────────────────────────┘",
]),
);
// enough space to just not hide the third column
test_case(
6,
Buffer::with_lines(vec![
"┌────────────────────────────┐",
"│Head1 Head2 Head3 │",
"│ │",
"│Row11 Row12 Row13 │",
"│Row21 Row22 Row23 │",
"│Row31 Row32 Row33 │",
"│Row41 Row42 Row43 │",
"│ │",
"│ │",
"└────────────────────────────┘",
]),
);
// enough space to hide part of the third column
test_case(
7,
Buffer::with_lines(vec![
"┌────────────────────────────┐",
"│Head1 Head2 Head│",
"│ │",
"│Row11 Row12 Row1│",
"│Row21 Row22 Row2│",
"│Row31 Row32 Row3│",
"│Row41 Row42 Row4│",
"│ │",
"│ │",
"└────────────────────────────┘",
]),
);
}
#[test]
fn widgets_table_columns_widths_can_use_fixed_length_constraints() {
let test_case = |widths, expected| {
let backend = TestBackend::new(30, 10);
let mut terminal = Terminal::new(backend).unwrap();
terminal
.draw(|f| {
let size = f.size();
let table = Table::new(
vec![
Row::new(vec!["Row11", "Row12", "Row13"]),
Row::new(vec!["Row21", "Row22", "Row23"]),
Row::new(vec!["Row31", "Row32", "Row33"]),
Row::new(vec!["Row41", "Row42", "Row43"]),
],
widths,
)
.header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
.block(Block::default().borders(Borders::ALL));
f.render_widget(table, size);
})
.unwrap();
terminal.backend().assert_buffer(&expected);
};
// columns of zero width show nothing
test_case(
&[
Constraint::Length(0),
Constraint::Length(0),
Constraint::Length(0),
],
Buffer::with_lines(vec![
"┌────────────────────────────┐",
"│ │",
"│ │",
"│ │",
"│ │",
"│ │",
"│ │",
"│ │",
"│ │",
"└────────────────────────────┘",
]),
);
// columns of 1 width trim
test_case(
&[
Constraint::Length(1),
Constraint::Length(1),
Constraint::Length(1),
],
Buffer::with_lines(vec![
"┌────────────────────────────┐",
"│H H H │",
"│ │",
"│R R R │",
"│R R R │",
"│R R R │",
"│R R R │",
"│ │",
"│ │",
"└────────────────────────────┘",
]),
);
// columns of large width just before pushing a column off
test_case(
&[
Constraint::Length(8),
Constraint::Length(8),
Constraint::Length(8),
],
Buffer::with_lines(vec![
"┌────────────────────────────┐",
"│Head1 Head2 Head3 │",
"│ │",
"│Row11 Row12 Row13 │",
"│Row21 Row22 Row23 │",
"│Row31 Row32 Row33 │",
"│Row41 Row42 Row43 │",
"│ │",
"│ │",
"└────────────────────────────┘",
]),
);
}
#[test]
fn widgets_table_columns_widths_can_use_percentage_constraints() {
#[track_caller]
fn test_case(widths: &[Constraint], expected: Buffer) {
let backend = TestBackend::new(30, 10);
let mut terminal = Terminal::new(backend).unwrap();
terminal
.draw(|f| {
let size = f.size();
let table = Table::new(
vec![
Row::new(vec!["Row11", "Row12", "Row13"]),
Row::new(vec!["Row21", "Row22", "Row23"]),
Row::new(vec!["Row31", "Row32", "Row33"]),
Row::new(vec!["Row41", "Row42", "Row43"]),
],
widths,
)
.header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
.block(Block::default().borders(Borders::ALL))
.column_spacing(0);
f.render_widget(table, size);
})
.unwrap();
terminal.backend().assert_buffer(&expected);
}
// columns of zero width show nothing
test_case(
&[
Constraint::Percentage(0),
Constraint::Percentage(0),
Constraint::Percentage(0),
],
Buffer::with_lines(vec![
"┌────────────────────────────┐",
"│ │",
"│ │",
"│ │",
"│ │",
"│ │",
"│ │",
"│ │",
"│ │",
"└────────────────────────────┘",
]),
);
// columns of not enough width trims the data
test_case(
&[
Constraint::Percentage(11),
Constraint::Percentage(11),
Constraint::Percentage(11),
],
Buffer::with_lines(vec![
"┌────────────────────────────┐",
"│HeaHeaHea │",
"│ │",
"│RowRowRow │",
"│RowRowRow │",
"│RowRowRow │",
"│RowRowRow │",
"│ │",
"│ │",
"└────────────────────────────┘",
]),
);
// columns of large width just before pushing a column off
test_case(
&[
Constraint::Percentage(33),
Constraint::Percentage(33),
Constraint::Percentage(33),
],
Buffer::with_lines(vec![
"┌────────────────────────────┐",
"│Head1 Head2 Head3 │",
"│ │",
"│Row11 Row12 Row13 │",
"│Row21 Row22 Row23 │",
"│Row31 Row32 Row33 │",
"│Row41 Row42 Row43 │",
"│ │",
"│ │",
"└────────────────────────────┘",
]),
);
// percentages summing to 100 should give equal widths
test_case(
&[Constraint::Percentage(50), Constraint::Percentage(50)],
Buffer::with_lines(vec![
"┌────────────────────────────┐",
"│Head1 Head2 │",
"│ │",
"│Row11 Row12 │",
"│Row21 Row22 │",
"│Row31 Row32 │",
"│Row41 Row42 │",
"│ │",
"│ │",
"└────────────────────────────┘",
]),
);
}
#[test]
fn widgets_table_columns_widths_can_use_mixed_constraints() {
#[track_caller]
fn test_case(widths: &[Constraint], expected: Buffer) {
let backend = TestBackend::new(30, 10);
let mut terminal = Terminal::new(backend).unwrap();
terminal
.draw(|f| {
let size = f.size();
let table = Table::new(
vec![
Row::new(vec!["Row11", "Row12", "Row13"]),
Row::new(vec!["Row21", "Row22", "Row23"]),
Row::new(vec!["Row31", "Row32", "Row33"]),
Row::new(vec!["Row41", "Row42", "Row43"]),
],
widths,
)
.header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
.block(Block::default().borders(Borders::ALL));
f.render_widget(table, size);
})
.unwrap();
terminal.backend().assert_buffer(&expected);
}
// columns of zero width show nothing
test_case(
&[
Constraint::Percentage(0),
Constraint::Length(0),
Constraint::Percentage(0),
],
Buffer::with_lines(vec![
"┌────────────────────────────┐",
"│ │",
"│ │",
"│ │",
"│ │",
"│ │",
"│ │",
"│ │",
"│ │",
"└────────────────────────────┘",
]),
);
// columns of not enough width trims the data
test_case(
&[
Constraint::Percentage(11),
Constraint::Length(20),
Constraint::Percentage(11),
],
Buffer::with_lines(vec![
"┌────────────────────────────┐",
"│Hea Head2 Hea│",
"│ │",
"│Row Row12 Row│",
"│Row Row22 Row│",
"│Row Row32 Row│",
"│Row Row42 Row│",
"│ │",
"│ │",
"└────────────────────────────┘",
]),
);
// columns of large width just before pushing a column off
test_case(
&[
Constraint::Percentage(33),
Constraint::Length(10),
Constraint::Percentage(33),
],
Buffer::with_lines(vec![
"┌────────────────────────────┐",
"│Head1 Head2 Head3 │",
"│ │",
"│Row11 Row12 Row13 │",
"│Row21 Row22 Row23 │",
"│Row31 Row32 Row33 │",
"│Row41 Row42 Row43 │",
"│ │",
"│ │",
"└────────────────────────────┘",
]),
);
// This test is unstable and should not be in the test suite
//
// // columns of large size (>100% total) hide the last column
// test_case(
// &[
// Constraint::Percentage(60),
// Constraint::Length(10),
// Constraint::Proportional(60),
// ],
// Buffer::with_lines(vec![
// "┌────────────────────────────┐",
// "│Head1 Head2 │",
// "│ │",
// "│Row11 Row12 │",
// "│Row21 Row22 │",
// "│Row31 Row32 │",
// "│Row41 Row42 │",
// "│ │",
// "│ │",
// "└────────────────────────────┘",
// ]),
// );
}
#[test]
fn widgets_table_columns_widths_can_use_ratio_constraints() {
#[track_caller]
fn test_case(widths: &[Constraint], expected: Buffer) {
let backend = TestBackend::new(30, 10);
let mut terminal = Terminal::new(backend).unwrap();
terminal
.draw(|f| {
let size = f.size();
let table = Table::new(
vec![
Row::new(vec!["Row11", "Row12", "Row13"]),
Row::new(vec!["Row21", "Row22", "Row23"]),
Row::new(vec!["Row31", "Row32", "Row33"]),
Row::new(vec!["Row41", "Row42", "Row43"]),
],
widths,
)
.header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
.block(Block::default().borders(Borders::ALL))
.column_spacing(0);
f.render_widget(table, size);
})
.unwrap();
terminal.backend().assert_buffer(&expected);
}
// columns of zero width show nothing
test_case(
&[
Constraint::Ratio(0, 1),
Constraint::Ratio(0, 1),
Constraint::Ratio(0, 1),
],
Buffer::with_lines(vec![
"┌────────────────────────────┐",
"│ │",
"│ │",
"│ │",
"│ │",
"│ │",
"│ │",
"│ │",
"│ │",
"└────────────────────────────┘",
]),
);
// columns of not enough width trims the data
test_case(
&[
Constraint::Ratio(1, 9),
Constraint::Ratio(1, 9),
Constraint::Ratio(1, 9),
],
Buffer::with_lines(vec![
"┌────────────────────────────┐",
"│HeaHeaHea │",
"│ │",
"│RowRowRow │",
"│RowRowRow │",
"│RowRowRow │",
"│RowRowRow │",
"│ │",
"│ │",
"└────────────────────────────┘",
]),
);
// columns of large width just before pushing a column off
test_case(
&[
Constraint::Ratio(1, 3),
Constraint::Ratio(1, 3),
Constraint::Ratio(1, 3),
],
Buffer::with_lines(vec![
"┌────────────────────────────┐",
"│Head1 Head2 Head3 │",
"│ │",
"│Row11 Row12 Row13 │",
"│Row21 Row22 Row23 │",
"│Row31 Row32 Row33 │",
"│Row41 Row42 Row43 │",
"│ │",
"│ │",
"└────────────────────────────┘",
]),
);
// percentages summing to 100 should give equal widths
test_case(
&[Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)],
Buffer::with_lines(vec![
"┌────────────────────────────┐",
"│Head1 Head2 │",
"│ │",
"│Row11 Row12 │",
"│Row21 Row22 │",
"│Row31 Row32 │",
"│Row41 Row42 │",
"│ │",
"│ │",
"└────────────────────────────┘",
]),
);
}
#[test]
fn widgets_table_can_have_rows_with_multi_lines() {
let test_case = |state: &mut TableState, expected: Buffer| {
let backend = TestBackend::new(30, 8);
let mut terminal = Terminal::new(backend).unwrap();
terminal
.draw(|f| {
let size = f.size();
let table = Table::new(
vec![
Row::new(vec!["Row11", "Row12", "Row13"]),
Row::new(vec!["Row21", "Row22", "Row23"]).height(2),
Row::new(vec!["Row31", "Row32", "Row33"]),
Row::new(vec!["Row41", "Row42", "Row43"]).height(2),
],
[
Constraint::Length(5),
Constraint::Length(5),
Constraint::Length(5),
],
)
.header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
.block(Block::default().borders(Borders::ALL))
.highlight_symbol(">> ")
.column_spacing(1);
f.render_stateful_widget(table, size, state);
})
.unwrap();
terminal.backend().assert_buffer(&expected);
};
let mut state = TableState::default();
// no selection
test_case(
&mut state,
Buffer::with_lines(vec![
"┌────────────────────────────┐",
"│Head1 Head2 Head3 │",
"│ │",
"│Row11 Row12 Row13 │",
"│Row21 Row22 Row23 │",
"│ │",
"│Row31 Row32 Row33 │",
"└────────────────────────────┘",
]),
);
// select first
state.select(Some(0));
test_case(
&mut state,
Buffer::with_lines(vec![
"┌────────────────────────────┐",
"│ Head1 Head2 Head3 │",
"│ │",
"│>> Row11 Row12 Row13 │",
"│ Row21 Row22 Row23 │",
"│ │",
"│ Row31 Row32 Row33 │",
"└────────────────────────────┘",
]),
);
// select second (we don't show partially the 4th row)
state.select(Some(1));
test_case(
&mut state,
Buffer::with_lines(vec![
"┌────────────────────────────┐",
"│ Head1 Head2 Head3 │",
"│ │",
"│ Row11 Row12 Row13 │",
"│>> Row21 Row22 Row23 │",
"│ │",
"│ Row31 Row32 Row33 │",
"└────────────────────────────┘",
]),
);
// select 4th (we don't show partially the 1st row)
state.select(Some(3));
test_case(
&mut state,
Buffer::with_lines(vec![
"┌────────────────────────────┐",
"│ Head1 Head2 Head3 │",
"│ │",
"│ Row31 Row32 Row33 │",
"│>> Row41 Row42 Row43 │",
"│ │",
"│ │",
"└────────────────────────────┘",
]),
);
}
#[test]
fn widgets_table_enable_always_highlight_spacing() {
let test_case = |state: &mut TableState, space: HighlightSpacing, expected: Buffer| {
let backend = TestBackend::new(30, 8);
let mut terminal = Terminal::new(backend).unwrap();
terminal
.draw(|f| {
let size = f.size();
let table = Table::new(
vec![
Row::new(vec!["Row11", "Row12", "Row13"]),
Row::new(vec!["Row21", "Row22", "Row23"]).height(2),
Row::new(vec!["Row31", "Row32", "Row33"]),
Row::new(vec!["Row41", "Row42", "Row43"]).height(2),
],
[
Constraint::Length(5),
Constraint::Length(5),
Constraint::Length(5),
],
)
.header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
.block(Block::default().borders(Borders::ALL))
.highlight_symbol(">> ")
.highlight_spacing(space)
.column_spacing(1);
f.render_stateful_widget(table, size, state);
})
.unwrap();
terminal.backend().assert_buffer(&expected);
};
assert_eq!(HighlightSpacing::default(), HighlightSpacing::WhenSelected);
let mut state = TableState::default();
// no selection, "WhenSelected" should only allocate if selected
test_case(
&mut state,
HighlightSpacing::default(),
Buffer::with_lines(vec![
"┌────────────────────────────┐",
"│Head1 Head2 Head3 │",
"│ │",
"│Row11 Row12 Row13 │",
"│Row21 Row22 Row23 │",
"│ │",
"│Row31 Row32 Row33 │",
"└────────────────────────────┘",
]),
);
// no selection, "Always" should allocate regardless if selected or not
test_case(
&mut state,
HighlightSpacing::Always,
Buffer::with_lines(vec![
"┌────────────────────────────┐",
"│ Head1 Head2 Head3 │",
"│ │",
"│ Row11 Row12 Row13 │",
"│ Row21 Row22 Row23 │",
"│ │",
"│ Row31 Row32 Row33 │",
"└────────────────────────────┘",
]),
);
// no selection, "Never" should never allocate regadless if selected or not
test_case(
&mut state,
HighlightSpacing::Never,
Buffer::with_lines(vec![
"┌────────────────────────────┐",
"│Head1 Head2 Head3 │",
"│ │",
"│Row11 Row12 Row13 │",
"│Row21 Row22 Row23 │",
"│ │",
"│Row31 Row32 Row33 │",
"└────────────────────────────┘",
]),
);
// select first, "WhenSelected" should only allocate if selected
state.select(Some(0));
test_case(
&mut state,
HighlightSpacing::default(),
Buffer::with_lines(vec![
"┌────────────────────────────┐",
"│ Head1 Head2 Head3 │",
"│ │",
"│>> Row11 Row12 Row13 │",
"│ Row21 Row22 Row23 │",
"│ │",
"│ Row31 Row32 Row33 │",
"└────────────────────────────┘",
]),
);
// select first, "Always" should allocate regardless if selected or not
state.select(Some(0));
test_case(
&mut state,
HighlightSpacing::Always,
Buffer::with_lines(vec![
"┌────────────────────────────┐",
"│ Head1 Head2 Head3 │",
"│ │",
"│>> Row11 Row12 Row13 │",
"│ Row21 Row22 Row23 │",
"│ │",
"│ Row31 Row32 Row33 │",
"└────────────────────────────┘",
]),
);
// select first, "Never" should never allocate regadless if selected or not
state.select(Some(0));
test_case(
&mut state,
HighlightSpacing::Never,
Buffer::with_lines(vec![
"┌────────────────────────────┐",
"│Head1 Head2 Head3 │",
"│ │",
"│Row11 Row12 Row13 │",
"│Row21 Row22 Row23 │",
"│ │",
"│Row31 Row32 Row33 │",
"└────────────────────────────┘",
]),
);
}
#[test]
fn widgets_table_can_have_elements_styled_individually() {
let backend = TestBackend::new(30, 4);
let mut terminal = Terminal::new(backend).unwrap();
let mut state = TableState::default();
state.select(Some(0));
terminal
.draw(|f| {
let size = f.size();
let table = Table::new(
vec![
Row::new(vec!["Row11", "Row12", "Row13"])
.style(Style::default().fg(Color::Green)),
Row::new(vec![
Cell::from("Row21"),
Cell::from("Row22").style(Style::default().fg(Color::Yellow)),
Cell::from(Line::from(vec![
Span::raw("Row"),
Span::styled("23", Style::default().fg(Color::Blue)),
]))
.style(Style::default().fg(Color::Red)),
])
.style(Style::default().fg(Color::LightGreen)),
],
[
Constraint::Length(6),
Constraint::Length(6),
Constraint::Length(6),
],
)
.header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
.block(Block::default().borders(Borders::LEFT | Borders::RIGHT))
.highlight_symbol(">> ")
.highlight_style(Style::default().add_modifier(Modifier::BOLD))
.column_spacing(1);
f.render_stateful_widget(table, size, &mut state);
})
.unwrap();
let mut expected = Buffer::with_lines(vec![
"│ Head1 Head2 Head3 │",
"│ │",
"│>> Row11 Row12 Row13 │",
"│ Row21 Row22 Row23 │",
]);
// First row = row color + highlight style
for col in 1..=28 {
expected.get_mut(col, 2).set_style(
Style::default()
.fg(Color::Green)
.add_modifier(Modifier::BOLD),
);
}
// Second row:
// 1. row color
for col in 1..=28 {
expected
.get_mut(col, 3)
.set_style(Style::default().fg(Color::LightGreen));
}
// 2. cell color
for col in 11..=16 {
expected
.get_mut(col, 3)
.set_style(Style::default().fg(Color::Yellow));
}
for col in 18..=23 {
expected
.get_mut(col, 3)
.set_style(Style::default().fg(Color::Red));
}
// 3. text color
for col in 21..=22 {
expected
.get_mut(col, 3)
.set_style(Style::default().fg(Color::Blue));
}
terminal.backend().assert_buffer(&expected);
}
#[test]
fn widgets_table_should_render_even_if_empty() {
let backend = TestBackend::new(30, 4);
let mut terminal = Terminal::new(backend).unwrap();
terminal
.draw(|f| {
let size = f.size();
let table = Table::new(
Vec::<Row>::new(),
[
Constraint::Length(6),
Constraint::Length(6),
Constraint::Length(6),
],
)
.header(Row::new(vec!["Head1", "Head2", "Head3"]))
.block(Block::default().borders(Borders::LEFT | Borders::RIGHT))
.column_spacing(1);
f.render_widget(table, size);
})
.unwrap();
let expected = Buffer::with_lines(vec![
"│Head1 Head2 Head3 │",
"│ │",
"│ │",
"│ │",
]);
terminal.backend().assert_buffer(&expected);
}
#[test]
fn widgets_table_columns_dont_panic() {
let test_case = |state: &mut TableState, table: Table, width: u16| {
let backend = TestBackend::new(width, 8);
let mut terminal = Terminal::new(backend).unwrap();
terminal
.draw(|f| {
let size = f.size();
f.render_stateful_widget(table, size, state);
})
.unwrap();
};
// based on https://github.com/fdehau/tui-rs/issues/470#issuecomment-852562848
let table1_width = 98;
let table1 = Table::new(
vec![Row::new(vec!["r1", "r2", "r3", "r4"])],
[
Constraint::Percentage(15),
Constraint::Percentage(15),
Constraint::Percentage(25),
Constraint::Percentage(45),
],
)
.header(Row::new(vec!["h1", "h2", "h3", "h4"]))
.block(Block::default().borders(Borders::ALL))
.highlight_symbol(">> ")
.column_spacing(1);
let mut state = TableState::default();
// select first, which would cause a panic before fix
state.select(Some(0));
test_case(&mut state, table1.clone(), table1_width);
}
#[test]
fn widgets_table_should_clamp_offset_if_rows_are_removed() {
let backend = TestBackend::new(30, 8);
let mut terminal = Terminal::new(backend).unwrap();
let mut state = TableState::default();
// render with 6 items => offset will be at 2
state.select(Some(5));
terminal
.draw(|f| {
let size = f.size();
let table = Table::new(
vec![
Row::new(vec!["Row01", "Row02", "Row03"]),
Row::new(vec!["Row11", "Row12", "Row13"]),
Row::new(vec!["Row21", "Row22", "Row23"]),
Row::new(vec!["Row31", "Row32", "Row33"]),
Row::new(vec!["Row41", "Row42", "Row43"]),
Row::new(vec!["Row51", "Row52", "Row53"]),
],
[
Constraint::Length(5),
Constraint::Length(5),
Constraint::Length(5),
],
)
.header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
.block(Block::default().borders(Borders::ALL))
.column_spacing(1);
f.render_stateful_widget(table, size, &mut state);
})
.unwrap();
let expected = Buffer::with_lines(vec![
"┌────────────────────────────┐",
"│Head1 Head2 Head3 │",
"│ │",
"│Row21 Row22 Row23 │",
"│Row31 Row32 Row33 │",
"│Row41 Row42 Row43 │",
"│Row51 Row52 Row53 │",
"└────────────────────────────┘",
]);
terminal.backend().assert_buffer(&expected);
// render with 1 item => offset will be at 1
state.select(Some(1));
terminal
.draw(|f| {
let size = f.size();
let table = Table::new(
vec![Row::new(vec!["Row31", "Row32", "Row33"])],
[
Constraint::Length(5),
Constraint::Length(5),
Constraint::Length(5),
],
)
.header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
.block(Block::default().borders(Borders::ALL))
.column_spacing(1);
f.render_stateful_widget(table, size, &mut state);
})
.unwrap();
let expected = Buffer::with_lines(vec![
"┌────────────────────────────┐",
"│Head1 Head2 Head3 │",
"│ │",
"│Row31 Row32 Row33 │",
"│ │",
"│ │",
"│ │",
"└────────────────────────────┘",
]);
terminal.backend().assert_buffer(&expected);
}