@@ -0,0 +1,717 @@
5+ style::{Color, Modifier, Style},
7+ widgets::{Block, Borders, Cell, Row, Table, TableState},
12+fn widgets_table_column_spacing_can_be_changed() {
13+ let test_case = |column_spacing, expected| {
14+ let backend = TestBackend::new(30, 10);
15+ let mut terminal = Terminal::new(backend).unwrap();
20+ let table = Table::new(vec![
21+ Row::new(vec!["Row11", "Row12", "Row13"]),
22+ Row::new(vec!["Row21", "Row22", "Row23"]),
23+ Row::new(vec!["Row31", "Row32", "Row33"]),
24+ Row::new(vec!["Row41", "Row42", "Row43"]),
26+ .header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
27+ .block(Block::default().borders(Borders::ALL))
29+ Constraint::Length(5),
30+ Constraint::Length(5),
31+ Constraint::Length(5),
33+ .column_spacing(column_spacing);
34+ f.render_widget(table, size);
37+ terminal.backend().assert_buffer(&expected);
40+ // no space between columns
43+ Buffer::with_lines(vec![
44+ "┌────────────────────────────┐",
45+ "│Head1Head2Head3 │",
47+ "│Row11Row12Row13 │",
48+ "│Row21Row22Row23 │",
49+ "│Row31Row32Row33 │",
50+ "│Row41Row42Row43 │",
53+ "└────────────────────────────┘",
57+ // one space between columns
60+ Buffer::with_lines(vec![
61+ "┌────────────────────────────┐",
62+ "│Head1 Head2 Head3 │",
64+ "│Row11 Row12 Row13 │",
65+ "│Row21 Row22 Row23 │",
66+ "│Row31 Row32 Row33 │",
67+ "│Row41 Row42 Row43 │",
70+ "└────────────────────────────┘",
74+ // enough space to just not hide the third column
77+ Buffer::with_lines(vec![
78+ "┌────────────────────────────┐",
79+ "│Head1 Head2 Head3 │",
81+ "│Row11 Row12 Row13 │",
82+ "│Row21 Row22 Row23 │",
83+ "│Row31 Row32 Row33 │",
84+ "│Row41 Row42 Row43 │",
87+ "└────────────────────────────┘",
91+ // enough space to hide part of the third column
94+ Buffer::with_lines(vec![
95+ "┌────────────────────────────┐",
96+ "│Head1 Head2 Head│",
98+ "│Row11 Row12 Row1│",
99+ "│Row21 Row22 Row2│",
100+ "│Row31 Row32 Row3│",
101+ "│Row41 Row42 Row4│",
104+ "└────────────────────────────┘",
110+fn widgets_table_columns_widths_can_use_fixed_length_constraints() {
111+ let test_case = |widths, expected| {
112+ let backend = TestBackend::new(30, 10);
113+ let mut terminal = Terminal::new(backend).unwrap();
117+ let size = f.size();
118+ let table = Table::new(vec![
119+ Row::new(vec!["Row11", "Row12", "Row13"]),
120+ Row::new(vec!["Row21", "Row22", "Row23"]),
121+ Row::new(vec!["Row31", "Row32", "Row33"]),
122+ Row::new(vec!["Row41", "Row42", "Row43"]),
124+ .header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
125+ .block(Block::default().borders(Borders::ALL))
127+ f.render_widget(table, size);
130+ terminal.backend().assert_buffer(&expected);
133+ // columns of zero width show nothing
136+ Constraint::Length(0),
137+ Constraint::Length(0),
138+ Constraint::Length(0),
140+ Buffer::with_lines(vec![
141+ "┌────────────────────────────┐",
150+ "└────────────────────────────┘",
154+ // columns of 1 width trim
157+ Constraint::Length(1),
158+ Constraint::Length(1),
159+ Constraint::Length(1),
161+ Buffer::with_lines(vec![
162+ "┌────────────────────────────┐",
171+ "└────────────────────────────┘",
175+ // columns of large width just before pushing a column off
178+ Constraint::Length(8),
179+ Constraint::Length(8),
180+ Constraint::Length(8),
182+ Buffer::with_lines(vec![
183+ "┌────────────────────────────┐",
184+ "│Head1 Head2 Head3 │",
186+ "│Row11 Row12 Row13 │",
187+ "│Row21 Row22 Row23 │",
188+ "│Row31 Row32 Row33 │",
189+ "│Row41 Row42 Row43 │",
192+ "└────────────────────────────┘",
198+fn widgets_table_columns_widths_can_use_percentage_constraints() {
199+ let test_case = |widths, expected| {
200+ let backend = TestBackend::new(30, 10);
201+ let mut terminal = Terminal::new(backend).unwrap();
205+ let size = f.size();
206+ let table = Table::new(vec![
207+ Row::new(vec!["Row11", "Row12", "Row13"]),
208+ Row::new(vec!["Row21", "Row22", "Row23"]),
209+ Row::new(vec!["Row31", "Row32", "Row33"]),
210+ Row::new(vec!["Row41", "Row42", "Row43"]),
212+ .header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
213+ .block(Block::default().borders(Borders::ALL))
216+ f.render_widget(table, size);
219+ terminal.backend().assert_buffer(&expected);
222+ // columns of zero width show nothing
225+ Constraint::Percentage(0),
226+ Constraint::Percentage(0),
227+ Constraint::Percentage(0),
229+ Buffer::with_lines(vec![
230+ "┌────────────────────────────┐",
239+ "└────────────────────────────┘",
243+ // columns of not enough width trims the data
246+ Constraint::Percentage(11),
247+ Constraint::Percentage(11),
248+ Constraint::Percentage(11),
250+ Buffer::with_lines(vec![
251+ "┌────────────────────────────┐",
260+ "└────────────────────────────┘",
264+ // columns of large width just before pushing a column off
267+ Constraint::Percentage(33),
268+ Constraint::Percentage(33),
269+ Constraint::Percentage(33),
271+ Buffer::with_lines(vec![
272+ "┌────────────────────────────┐",
273+ "│Head1 Head2 Head3 │",
275+ "│Row11 Row12 Row13 │",
276+ "│Row21 Row22 Row23 │",
277+ "│Row31 Row32 Row33 │",
278+ "│Row41 Row42 Row43 │",
281+ "└────────────────────────────┘",
285+ // percentages summing to 100 should give equal widths
287+ &[Constraint::Percentage(50), Constraint::Percentage(50)],
288+ Buffer::with_lines(vec![
289+ "┌────────────────────────────┐",
298+ "└────────────────────────────┘",
304+fn widgets_table_columns_widths_can_use_mixed_constraints() {
305+ let test_case = |widths, expected| {
306+ let backend = TestBackend::new(30, 10);
307+ let mut terminal = Terminal::new(backend).unwrap();
311+ let size = f.size();
312+ let table = Table::new(vec![
313+ Row::new(vec!["Row11", "Row12", "Row13"]),
314+ Row::new(vec!["Row21", "Row22", "Row23"]),
315+ Row::new(vec!["Row31", "Row32", "Row33"]),
316+ Row::new(vec!["Row41", "Row42", "Row43"]),
318+ .header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
319+ .block(Block::default().borders(Borders::ALL))
321+ f.render_widget(table, size);
324+ terminal.backend().assert_buffer(&expected);
327+ // columns of zero width show nothing
330+ Constraint::Percentage(0),
331+ Constraint::Length(0),
332+ Constraint::Percentage(0),
334+ Buffer::with_lines(vec![
335+ "┌────────────────────────────┐",
344+ "└────────────────────────────┘",
348+ // columns of not enough width trims the data
351+ Constraint::Percentage(11),
352+ Constraint::Length(20),
353+ Constraint::Percentage(11),
355+ Buffer::with_lines(vec![
356+ "┌────────────────────────────┐",
365+ "└────────────────────────────┘",
369+ // columns of large width just before pushing a column off
372+ Constraint::Percentage(33),
373+ Constraint::Length(10),
374+ Constraint::Percentage(33),
376+ Buffer::with_lines(vec![
377+ "┌────────────────────────────┐",
378+ "│Head1 Head2 Head3 │",
380+ "│Row11 Row12 Row13 │",
381+ "│Row21 Row22 Row23 │",
382+ "│Row31 Row32 Row33 │",
383+ "│Row41 Row42 Row43 │",
386+ "└────────────────────────────┘",
390+ // columns of large size (>100% total) hide the last column
393+ Constraint::Percentage(60),
394+ Constraint::Length(10),
395+ Constraint::Percentage(60),
397+ Buffer::with_lines(vec![
398+ "┌────────────────────────────┐",
407+ "└────────────────────────────┘",
413+fn widgets_table_columns_widths_can_use_ratio_constraints() {
414+ let test_case = |widths, expected| {
415+ let backend = TestBackend::new(30, 10);
416+ let mut terminal = Terminal::new(backend).unwrap();
420+ let size = f.size();
421+ let table = Table::new(vec![
422+ Row::new(vec!["Row11", "Row12", "Row13"]),
423+ Row::new(vec!["Row21", "Row22", "Row23"]),
424+ Row::new(vec!["Row31", "Row32", "Row33"]),
425+ Row::new(vec!["Row41", "Row42", "Row43"]),
427+ .header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
428+ .block(Block::default().borders(Borders::ALL))
431+ f.render_widget(table, size);
434+ terminal.backend().assert_buffer(&expected);
437+ // columns of zero width show nothing
440+ Constraint::Ratio(0, 1),
441+ Constraint::Ratio(0, 1),
442+ Constraint::Ratio(0, 1),
444+ Buffer::with_lines(vec![
445+ "┌────────────────────────────┐",
454+ "└────────────────────────────┘",
458+ // columns of not enough width trims the data
461+ Constraint::Ratio(1, 9),
462+ Constraint::Ratio(1, 9),
463+ Constraint::Ratio(1, 9),
465+ Buffer::with_lines(vec![
466+ "┌────────────────────────────┐",
475+ "└────────────────────────────┘",
479+ // columns of large width just before pushing a column off
482+ Constraint::Ratio(1, 3),
483+ Constraint::Ratio(1, 3),
484+ Constraint::Ratio(1, 3),
486+ Buffer::with_lines(vec![
487+ "┌────────────────────────────┐",
488+ "│Head1 Head2 Head3 │",
490+ "│Row11 Row12 Row13 │",
491+ "│Row21 Row22 Row23 │",
492+ "│Row31 Row32 Row33 │",
493+ "│Row41 Row42 Row43 │",
496+ "└────────────────────────────┘",
500+ // percentages summing to 100 should give equal widths
502+ &[Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)],
503+ Buffer::with_lines(vec![
504+ "┌────────────────────────────┐",
513+ "└────────────────────────────┘",
519+fn widgets_table_can_have_rows_with_multi_lines() {
520+ let test_case = |state: &mut TableState, expected: Buffer| {
521+ let backend = TestBackend::new(30, 8);
522+ let mut terminal = Terminal::new(backend).unwrap();
525+ let size = f.size();
526+ let table = Table::new(vec![
527+ Row::new(vec!["Row11", "Row12", "Row13"]),
528+ Row::new(vec!["Row21", "Row22", "Row23"]).height(2),
529+ Row::new(vec!["Row31", "Row32", "Row33"]),
530+ Row::new(vec!["Row41", "Row42", "Row43"]).height(2),
532+ .header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
533+ .block(Block::default().borders(Borders::ALL))
534+ .highlight_symbol(">> ")
536+ Constraint::Length(5),
537+ Constraint::Length(5),
538+ Constraint::Length(5),
541+ f.render_stateful_widget(table, size, state);
544+ terminal.backend().assert_buffer(&expected);
547+ let mut state = TableState::default();
551+ Buffer::with_lines(vec![
552+ "┌────────────────────────────┐",
553+ "│Head1 Head2 Head3 │",
555+ "│Row11 Row12 Row13 │",
556+ "│Row21 Row22 Row23 │",
558+ "│Row31 Row32 Row33 │",
559+ "└────────────────────────────┘",
564+ state.select(Some(0));
567+ Buffer::with_lines(vec![
568+ "┌────────────────────────────┐",
569+ "│ Head1 Head2 Head3 │",
571+ "│>> Row11 Row12 Row13 │",
572+ "│ Row21 Row22 Row23 │",
574+ "│ Row31 Row32 Row33 │",
575+ "└────────────────────────────┘",
579+ // select second (we don't show partially the 4th row)
580+ state.select(Some(1));
583+ Buffer::with_lines(vec![
584+ "┌────────────────────────────┐",
585+ "│ Head1 Head2 Head3 │",
587+ "│ Row11 Row12 Row13 │",
588+ "│>> Row21 Row22 Row23 │",
590+ "│ Row31 Row32 Row33 │",
591+ "└────────────────────────────┘",
595+ // select 4th (we don't show partially the 1st row)
596+ state.select(Some(3));
599+ Buffer::with_lines(vec![
600+ "┌────────────────────────────┐",
601+ "│ Head1 Head2 Head3 │",
603+ "│ Row31 Row32 Row33 │",
604+ "│>> Row41 Row42 Row43 │",
607+ "└────────────────────────────┘",
613+fn widgets_table_can_have_elements_styled_individually() {
614+ let backend = TestBackend::new(30, 4);
615+ let mut terminal = Terminal::new(backend).unwrap();
616+ let mut state = TableState::default();
617+ state.select(Some(0));
620+ let size = f.size();
621+ let table = Table::new(vec![
622+ Row::new(vec!["Row11", "Row12", "Row13"]).style(Style::default().fg(Color::Green)),
624+ Cell::from("Row21"),
625+ Cell::from("Row22").style(Style::default().fg(Color::Yellow)),
626+ Cell::from(Spans::from(vec![
628+ Span::styled("23", Style::default().fg(Color::Blue)),
630+ .style(Style::default().fg(Color::Red)),
632+ .style(Style::default().fg(Color::LightGreen)),
634+ .header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
635+ .block(Block::default().borders(Borders::LEFT | Borders::RIGHT))
636+ .highlight_symbol(">> ")
637+ .highlight_style(Style::default().add_modifier(Modifier::BOLD))
639+ Constraint::Length(6),
640+ Constraint::Length(6),
641+ Constraint::Length(6),
644+ f.render_stateful_widget(table, size, &mut state);
648+ let mut expected = Buffer::with_lines(vec![
649+ "│ Head1 Head2 Head3 │",
651+ "│>> Row11 Row12 Row13 │",
652+ "│ Row21 Row22 Row23 │",
654+ // First row = row color + highlight style
656+ expected.get_mut(col, 2).set_style(
659+ .add_modifier(Modifier::BOLD),
667+ .set_style(Style::default().fg(Color::LightGreen));
670+ for col in 11..=16 {
673+ .set_style(Style::default().fg(Color::Yellow));
675+ for col in 18..=23 {
678+ .set_style(Style::default().fg(Color::Red));
681+ for col in 21..=22 {
684+ .set_style(Style::default().fg(Color::Blue));
686+ terminal.backend().assert_buffer(&expected);
690+fn widgets_table_should_render_even_if_empty() {
691+ let backend = TestBackend::new(30, 4);
692+ let mut terminal = Terminal::new(backend).unwrap();
695+ let size = f.size();
696+ let table = Table::new(vec![])
697+ .header(Row::new(vec!["Head1", "Head2", "Head3"]))
698+ .block(Block::default().borders(Borders::LEFT | Borders::RIGHT))
700+ Constraint::Length(6),
701+ Constraint::Length(6),
702+ Constraint::Length(6),
705+ f.render_widget(table, size);
709+ let expected = Buffer::with_lines(vec![
710+ "│Head1 Head2 Head3 │",
716+ terminal.backend().assert_buffer(&expected);