diff --git a/table/table.go b/table/table.go index 1bef671..0380149 100644 --- a/table/table.go +++ b/table/table.go @@ -7,6 +7,10 @@ import ( "github.com/charmbracelet/x/ansi" ) +// HeaderRow denotes the header's row index used when rendering headers. Use +// this value when looking to customize header styles in StyleFunc. +const HeaderRow int = -1 + // StyleFunc is the style function that determines the style of a Cell. // // It takes the row and column of the cell as an input and determines the @@ -235,15 +239,15 @@ func (t *Table) String() string { // the StyleFunc after the headers and rows. Update the widths for a final // time. for i, cell := range t.headers { - t.widths[i] = max(t.widths[i], lipgloss.Width(t.style(0, i).Render(cell))) - t.heights[0] = max(t.heights[0], lipgloss.Height(t.style(0, i).Render(cell))) + t.widths[i] = max(t.widths[i], lipgloss.Width(t.style(HeaderRow, i).Render(cell))) + t.heights[0] = max(t.heights[0], lipgloss.Height(t.style(HeaderRow, i).Render(cell))) } for r := 0; r < t.data.Rows(); r++ { for i := 0; i < t.data.Columns(); i++ { cell := t.data.At(r, i) - rendered := t.style(r+1, i).Render(cell) + rendered := t.style(r, i).Render(cell) t.heights[r+btoi(hasHeaders)] = max(t.heights[r+btoi(hasHeaders)], lipgloss.Height(rendered)) t.widths[i] = max(t.widths[i], lipgloss.Width(rendered)) } @@ -452,7 +456,7 @@ func (t *Table) constructHeaders() string { s.WriteString(t.borderStyle.Render(t.border.Left)) } for i, header := range t.headers { - s.WriteString(t.style(0, i). + s.WriteString(t.style(HeaderRow, i). MaxHeight(1). Width(t.widths[i]). MaxWidth(t.widths[i]). @@ -537,7 +541,7 @@ func (t *Table) constructRow(index int, isOverflow bool) string { cell = t.data.At(index, c) } - cells = append(cells, t.style(index+1, c). + cells = append(cells, t.style(index, c). Height(height). MaxHeight(height). Width(t.widths[c]). diff --git a/table/table_test.go b/table/table_test.go index fbe1881..ca8d43e 100644 --- a/table/table_test.go +++ b/table/table_test.go @@ -7,11 +7,12 @@ import ( "github.com/charmbracelet/lipgloss" "github.com/charmbracelet/x/ansi" + "github.com/charmbracelet/x/exp/golden" ) var TableStyle = func(row, col int) lipgloss.Style { switch { - case row == 0: + case row == HeaderRow: return lipgloss.NewStyle().Padding(0, 1).Align(lipgloss.Center) case row%2 == 0: return lipgloss.NewStyle().Padding(0, 1) @@ -65,7 +66,7 @@ func TestTableExample(t *testing.T) { BorderStyle(lipgloss.NewStyle().Foreground(lipgloss.Color("99"))). StyleFunc(func(row, col int) lipgloss.Style { switch { - case row == 0: + case row == HeaderRow: return HeaderStyle case row%2 == 0: return EvenRowStyle @@ -91,8 +92,8 @@ func TestTableExample(t *testing.T) { └──────────┴───────────────────────────────┴─────────────────┘ `) - if table.String() != expected { - t.Fatalf("expected:\n\n%s\n\ngot:\n\n%s", expected, table.String()) + if got := ansi.Strip(table.String()); got != expected { + t.Fatalf("expected:\n\n%s\n\ngot:\n\n%s", expected, got) } } @@ -526,7 +527,7 @@ func TestTableRowSeparators(t *testing.T) { func TestTableHeights(t *testing.T) { styleFunc := func(row, col int) lipgloss.Style { - if row == 0 { + if row == HeaderRow { return lipgloss.NewStyle().Padding(0, 1) } if col == 0 { @@ -584,7 +585,7 @@ func TestTableHeights(t *testing.T) { func TestTableMultiLineRowSeparator(t *testing.T) { styleFunc := func(row, col int) lipgloss.Style { - if row == 0 { + if row == HeaderRow { return lipgloss.NewStyle().Padding(0, 1) } if col == 0 { @@ -1140,6 +1141,33 @@ func TestTableHeightWithOffset(t *testing.T) { } } +func TestStyleFunc(t *testing.T) { + TestStyle := func(row, col int) lipgloss.Style { + switch { + // this is the header + case row == HeaderRow: + return lipgloss.NewStyle().Align(lipgloss.Center) + // this is the first row of data + case row == 0: + return lipgloss.NewStyle().Padding(0, 1).Align(lipgloss.Right) + default: + return lipgloss.NewStyle().Padding(0, 1) + } + } + + table := New(). + Border(lipgloss.NormalBorder()). + StyleFunc(TestStyle). + Headers("LANGUAGE", "FORMAL", "INFORMAL"). + Row("Chinese", "Nǐn hǎo", "Nǐ hǎo"). + Row("French", "Bonjour", "Salut"). + Row("Japanese", "こんにちは", "やあ"). + Row("Russian", "Zdravstvuyte", "Privet"). + Row("Spanish", "Hola", "¿Qué tal?") + + golden.RequireEqual(t, []byte(table.String())) +} + func TestClearRows(t *testing.T) { defer func() { if r := recover(); r != nil { diff --git a/table/testdata/TestStyleFunc.golden b/table/testdata/TestStyleFunc.golden new file mode 100644 index 0000000..e81cb45 --- /dev/null +++ b/table/testdata/TestStyleFunc.golden @@ -0,0 +1,9 @@ +┌──────────┬──────────────┬───────────┐ +│ LANGUAGE │ FORMAL │ INFORMAL │ +├──────────┼──────────────┼───────────┤ +│ Chinese │ Nǐn hǎo │ Nǐ hǎo │ +│ French │ Bonjour │ Salut │ +│ Japanese │ こんにちは │ やあ │ +│ Russian │ Zdravstvuyte │ Privet │ +│ Spanish │ Hola │ ¿Qué tal? │ +└──────────┴──────────────┴───────────┘ \ No newline at end of file