diff --git a/Taskfile.yaml b/Taskfile.yaml index 35072035d..e53ff53e4 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -1,6 +1,6 @@ # https://taskfile.dev -version: '3' +version: "3" tasks: lint: @@ -12,3 +12,8 @@ tasks: desc: Run tests cmds: - go test ./... {{.CLI_ARGS}} + + test:table: + desc: Run tests with table test + cmds: + - go test ./table {{.CLI_ARGS}} diff --git a/go.mod b/go.mod index 2f022a5f7..f78ba7a45 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/atotto/clipboard v0.1.4 github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.1 github.com/charmbracelet/harmonica v0.2.0 - github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.1 + github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.1.0.20250603140710-946081c6f1a5 github.com/charmbracelet/x/ansi v0.8.0 github.com/charmbracelet/x/exp/golden v0.0.0-20250207160936-21c02780d27a github.com/dustin/go-humanize v1.0.1 @@ -19,7 +19,7 @@ require ( require ( github.com/aymanbagabas/go-udiff v0.2.0 // indirect - github.com/charmbracelet/colorprofile v0.3.0 // indirect + github.com/charmbracelet/colorprofile v0.3.1 // indirect github.com/charmbracelet/x/cellbuf v0.0.13 // indirect github.com/charmbracelet/x/input v0.3.4 // indirect github.com/charmbracelet/x/term v0.2.1 // indirect @@ -28,5 +28,5 @@ require ( github.com/muesli/cancelreader v0.2.2 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect golang.org/x/sync v0.12.0 // indirect - golang.org/x/sys v0.31.0 // indirect + golang.org/x/sys v0.32.0 // indirect ) diff --git a/go.sum b/go.sum index 5d01a6674..118c8a96b 100644 --- a/go.sum +++ b/go.sum @@ -6,12 +6,12 @@ github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWp github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA= github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.1 h1:RvpXiXuPAuaKCHPCsE/lK5+zztnNDTSCa0CpeeIKdDU= github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.1/go.mod h1:qbcZLI5z8R49v9xBdU5V5Dh5D2uccx8wSwBqxQyErqc= -github.com/charmbracelet/colorprofile v0.3.0 h1:KtLh9uuu1RCt+Hml4s6Hz+kB1PfV3wi++1h5ia65yKQ= -github.com/charmbracelet/colorprofile v0.3.0/go.mod h1:oHJ340RS2nmG1zRGPmhJKJ/jf4FPNNk0P39/wBPA1G0= +github.com/charmbracelet/colorprofile v0.3.1 h1:k8dTHMd7fgw4bnFd7jXTLZrSU/CQrKnL3m+AxCzDz40= +github.com/charmbracelet/colorprofile v0.3.1/go.mod h1:/GkGusxNs8VB/RSOh3fu0TJmQ4ICMMPApIIVn0KszZ0= github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ= github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= -github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.1 h1:D9AJJuYTN5pvz6mpIGO1ijLKpfTYSHOtKGgwoTQ4Gog= -github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.1/go.mod h1:tRlx/Hu0lo/j9viunCN2H+Ze6JrmdjQlXUQvvArgaOc= +github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.1.0.20250603140710-946081c6f1a5 h1:vq3WdrGHQ6T0ZBYdRhn2toyQXDD+uneu3IdcMni7J5g= +github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.1.0.20250603140710-946081c6f1a5/go.mod h1:EJWvaCrhOhNGVZMvcjc0yVryl4qqpMs8tz0r9WyEkdQ= github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE= github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q= github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k= @@ -45,5 +45,5 @@ golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= diff --git a/table/table.go b/table/table.go index 953ec125d..75ed35bb9 100644 --- a/table/table.go +++ b/table/table.go @@ -1,15 +1,14 @@ -// Package table provides a simple table component for Bubble Tea applications. +// Package table provides a table component for Bubble Tea applications. package table import ( - "strings" + "reflect" "github.com/charmbracelet/bubbles/v2/help" "github.com/charmbracelet/bubbles/v2/key" - "github.com/charmbracelet/bubbles/v2/viewport" tea "github.com/charmbracelet/bubbletea/v2" "github.com/charmbracelet/lipgloss/v2" - "github.com/mattn/go-runewidth" + "github.com/charmbracelet/lipgloss/v2/table" ) // Model defines a state for the table widget. @@ -17,24 +16,12 @@ type Model struct { KeyMap KeyMap Help help.Model - cols []Column - rows []Row - cursor int - focus bool - styles Styles + cursor int + focus bool + styles Styles + useStyleFunc bool - viewport viewport.Model - start int - end int -} - -// Row represents one line in the table. -type Row []string - -// Column defines the table structure. -type Column struct { - Title string - Width int + table *table.Table } // KeyMap defines keybindings. It satisfies to the help.KeyMap interface, which @@ -101,108 +88,327 @@ func DefaultKeyMap() KeyMap { } } -// Styles contains style definitions for this list component. By default, these -// values are generated by DefaultStyles. +// Styles contains style definitions for this table component. Load default +// styles to your table with [DefaultStyles]. type Styles struct { Header lipgloss.Style Cell lipgloss.Style Selected lipgloss.Style } -// DefaultStyles returns a set of default style definitions for this table. +// DefaultStyles returns sensible default table styles. func DefaultStyles() Styles { return Styles{ - Selected: lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("212")), Header: lipgloss.NewStyle().Bold(true).Padding(0, 1), Cell: lipgloss.NewStyle().Padding(0, 1), + Selected: lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("212")).Padding(0, 1), } } -// SetStyles sets the table styles. -func (m *Model) SetStyles(s Styles) { - m.styles = s - m.UpdateViewport() +// NewFromTemplate lets you create a table [Model] from Lip Gloss' +// [table.Table]. +func NewFromTemplate(t *table.Table) *Model { + return &Model{ + cursor: 0, + KeyMap: DefaultKeyMap(), + Help: help.New(), + table: t, + useStyleFunc: true, + } } -// Option is used to set options in New. For example: +// SetBorder is a shorthand function for setting or unsetting borders on a +// table. The arguments work as follows: // -// table := New(WithColumns([]Column{{Title: "ID", Width: 10}})) -type Option func(*Model) +// With one argument, the argument is applied to all sides. +// +// With two arguments, the arguments are applied to the vertical and horizontal +// sides, in that order. +// +// With three arguments, the arguments are applied to the top side, the +// horizontal sides, and the bottom side, in that order. +// +// With four arguments, the arguments are applied clockwise starting from the +// top side, followed by the right side, then the bottom, and finally the left. +// +// With five arguments, the arguments are applied clockwise starting from the +// top side, followed by the right side, then the bottom, and finally the left. +// The final value will set the row separator. +// +// With six arguments, the arguments are applied clockwise starting from the +// top side, followed by the right side, then the bottom, and finally the left. +// The final two values will set the row and column separators in that order. +// +// With more than six arguments nothing will be set. +func (m *Model) SetBorder(s ...bool) *Model { + top, right, bottom, left, rowSeparator, columnSeparator := m.whichSides(s...) + m.table. + BorderTop(top). + BorderRight(right). + BorderBottom(bottom). + BorderLeft(left). + BorderRow(rowSeparator). + BorderColumn(columnSeparator) + return m +} -// New creates a new model for the table widget. -func New(opts ...Option) Model { - m := Model{ - cursor: 0, - viewport: viewport.New(viewport.WithHeight(20)), //nolint:mnd +// Border sets the kind of border to use for the table. See [lipgloss.Border]. +func (m *Model) Border(border lipgloss.Border) *Model { + m.table.Border(border) + return m +} - KeyMap: DefaultKeyMap(), - Help: help.New(), - styles: DefaultStyles(), - } +// BorderStyle sets the style for the table border. +func (m *Model) BorderStyle(style lipgloss.Style) *Model { + m.table.BorderStyle(style) + return m +} - for _, opt := range opts { - opt(&m) - } +// BorderBottom sets the bottom border. +func (m *Model) BorderBottom(v bool) *Model { + m.table.BorderBottom(v) + return m +} - m.UpdateViewport() +// BorderTop sets the top border. +func (m *Model) BorderTop(v bool) *Model { + m.table.BorderTop(v) + return m +} +// BorderLeft sets the left border. +func (m *Model) BorderLeft(v bool) *Model { + m.table.BorderLeft(v) return m } -// WithColumns sets the table columns (headers). -func WithColumns(cols []Column) Option { - return func(m *Model) { - m.cols = cols - } +// BorderRight sets the right border. +func (m *Model) BorderRight(v bool) *Model { + m.table.BorderRight(v) + return m +} + +// BorderColumn sets the column border. +func (m *Model) BorderColumn(v bool) *Model { + m.table.BorderColumn(v) + return m +} + +// BorderHeader sets the header border. +func (m *Model) BorderHeader(v bool) *Model { + m.table.BorderHeader(v) + return m +} + +// BorderRow sets the row borders. +func (m *Model) BorderRow(v bool) *Model { + m.table.BorderRow(v) + return m } -// WithRows sets the table rows (data). -func WithRows(rows []Row) Option { +// Options + +// Option is used to set options in [New]. For example: +// +// table := New(WithHeaders([]string{"Rank", "City", "Country", "Population"})) +type Option func(*Model) + +// WithHeaders sets the table headers. This function is used as an [Option] in +// when creating a table with [New]. +func WithHeaders(headers ...string) Option { return func(m *Model) { - m.rows = rows + m.SetHeaders(headers...) } } -// WithHeight sets the height of the table. +// WithHeight sets the height of the table. The given height will be the total +// table height including borders, margins, and padding. This function is used +// as an [Option] in when creating a table with [New]. func WithHeight(h int) Option { return func(m *Model) { - m.viewport.SetHeight(h - lipgloss.Height(m.headersView())) + m.table.Height(h) } } -// WithWidth sets the width of the table. +// WithWidth sets the width of the table. The given width will be the total +// table width including borders, margins, and padding. This function is used as +// an [Option] in when creating a table with [New]. func WithWidth(w int) Option { return func(m *Model) { - m.viewport.SetWidth(w) + m.table.Width(w) } } -// WithFocused sets the focus state of the table. +// WithRows sets the table rows. This function is used as an [Option] in when +// creating a table with [New]. +func WithRows(rows ...[]string) Option { + return func(m *Model) { + m.SetRows(rows...) + } +} + +// WithFocused sets the focus state of the table. This function is used as an +// [Option] in when creating a table with [New]. func WithFocused(f bool) Option { return func(m *Model) { m.focus = f } } -// WithStyles sets the table styles. +// WithStyles sets the table styles. This function is used as an [Option] in +// when creating a table with [New]. func WithStyles(s Styles) Option { return func(m *Model) { - m.styles = s + m.SetStyles(s) } } -// WithKeyMap sets the key map. +// WithStyleFunc sets the table [table.StyleFunc] for conditional styling. This +// function is used as an [Option] in when creating a table with [New]. +func WithStyleFunc(s table.StyleFunc) Option { + return func(m *Model) { + m.useStyleFunc = true + m.table.StyleFunc(s) + } +} + +// WithKeyMap sets the [KeyMap]. This function is used as an [Option] in when +// creating a table with [New]. func WithKeyMap(km KeyMap) Option { return func(m *Model) { m.KeyMap = km } } -// Update is the Bubble Tea update loop. +// Setters + +// SetHeaders sets the table headers. +func (m *Model) SetHeaders(headers ...string) *Model { + m.table.Headers(headers...) + return m +} + +// SetRows sets the table rows. +func (m *Model) SetRows(rows ...[]string) *Model { + m.table.Rows(rows...) + return m +} + +// SetCursor sets the cursor position in the table. +func (m *Model) SetCursor(n int) *Model { + m.cursor = clamp(n, 0, m.RowCount()-1) + return m +} + +// SetHeight sets the width of the table. The given height will be the total +// table height including borders, margins, and padding. +func (m *Model) SetHeight(h int) *Model { + m.table.Height(h) + return m +} + +// SetWidth sets the width of the table. The given width will be the total +// table width including borders, margins, and padding. +func (m *Model) SetWidth(w int) *Model { + m.table.Width(w) + return m +} + +// SetYOffset sets the YOffset position in the table. +func (m *Model) SetYOffset(n int) *Model { + var ( + minimum = 0 + maximum = m.RowCount() - m.table.VisibleRows() + yOffset = clamp(n, minimum, maximum) + ) + m.table.YOffset(yOffset) + return m +} + +// SetStyles sets the table styles, only applying non-empty [Styles]. Note: using +// [Model.SetStyleFunc] will override styles set in this function. +func (m *Model) SetStyles(s Styles) *Model { + if !reflect.DeepEqual(s.Selected, lipgloss.Style{}) { + m.styles.Selected = s.Selected + } + if !reflect.DeepEqual(s.Header, lipgloss.Style{}) { + m.styles.Header = s.Header + } + if !reflect.DeepEqual(s.Cell, lipgloss.Style{}) { + m.styles.Cell = s.Cell + } + return m +} + +// OverwriteStyles sets the table styles, overwriting all existing styles. Note: +// using [Model.SetStyleFunc] will override styles set in this function. +func (m *Model) OverwriteStyles(s Styles) *Model { + m.styles = s + return m +} + +// LipglossTable sets the inner [lipgloss.Table]. +func (m *Model) LipglossTable(t *table.Table) { + var ( + previousHeaders = m.table.GetHeaders() + previousData = m.table.GetData() + ) + if len(t.GetHeaders()) == 0 { + t = t.Headers(previousHeaders...) + } + if t.GetData() == nil || t.GetData().Rows() == 0 { + t = t.Rows(table.DataToMatrix(previousData)...) + } + m.table = t + m.useStyleFunc = true +} + +// SetStyleFunc sets the table's custom [table.StyleFunc]. Use this for conditional +// styling e.g. styling a cell by its contents or by index. +func (m *Model) SetStyleFunc(s table.StyleFunc) *Model { + m.useStyleFunc = true + m.table.StyleFunc(s) + return m +} + +// Creation + +// New creates a new model for the table widget. +func New(opts ...Option) *Model { + m := Model{ + cursor: 0, + KeyMap: DefaultKeyMap(), + Help: help.New(), + table: table.New(), + } + + m.SetStyles(DefaultStyles()) + + // Set border defaults here + m.Border(lipgloss.NormalBorder()) + m.BorderTop(true) + m.BorderBottom(true) + m.BorderLeft(true) + m.BorderRight(true) + m.BorderColumn(false) + m.BorderRow(false) + m.BorderHeader(true) + + for _, opt := range opts { + opt(&m) + } + + return &m +} + +// Bubble Tea Methods + +// Update is the Bubble Tea [tea.Model] update loop. func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { if !m.focus { return m, nil } + height := m.table.VisibleRows() - 1 switch msg := msg.(type) { case tea.KeyPressMsg: @@ -212,13 +418,21 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { case key.Matches(msg, m.KeyMap.LineDown): m.MoveDown(1) case key.Matches(msg, m.KeyMap.PageUp): - m.MoveUp(m.viewport.Height()) + if m.cursor > m.table.FirstVisibleRowIndex() { + m.SetCursor(m.table.FirstVisibleRowIndex()) + } else { + m.MoveUp(height) + } case key.Matches(msg, m.KeyMap.PageDown): - m.MoveDown(m.viewport.Height()) + if m.cursor < m.table.LastVisibleRowIndex() { + m.SetCursor(m.table.LastVisibleRowIndex()) + } else { + m.MoveDown(height) + } case key.Matches(msg, m.KeyMap.HalfPageUp): - m.MoveUp(m.viewport.Height() / 2) //nolint:mnd + m.MoveUp(height / 2) //nolint:mnd case key.Matches(msg, m.KeyMap.HalfPageDown): - m.MoveDown(m.viewport.Height() / 2) //nolint:mnd + m.MoveDown(height / 2) //nolint:mnd case key.Matches(msg, m.KeyMap.GotoTop): m.GotoTop() case key.Matches(msg, m.KeyMap.GotoBottom): @@ -229,27 +443,33 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { return m, nil } -// Focused returns the focus state of the table. -func (m Model) Focused() bool { - return m.focus -} - // Focus focuses the table, allowing the user to move around the rows and // interact. func (m *Model) Focus() { m.focus = true - m.UpdateViewport() } // Blur blurs the table, preventing selection or movement. func (m *Model) Blur() { m.focus = false - m.UpdateViewport() } -// View renders the component. +// View renders the table [Model]. func (m Model) View() string { - return m.headersView() + "\n" + m.viewport.View() + if !m.useStyleFunc { + // Update the position-sensitive styles as the cursor position may have + // changed in Update. + m.table.StyleFunc(func(row, _ int) lipgloss.Style { + if row == m.cursor { + return m.styles.Selected + } + if row == table.HeaderRow { + return m.styles.Header + } + return m.styles.Cell + }) + } + return m.table.String() } // HelpView is a helper method for rendering the help menu from the keymap. @@ -259,86 +479,26 @@ func (m Model) HelpView() string { return m.Help.View(m.KeyMap) } -// UpdateViewport updates the list content based on the previously defined -// columns and rows. -func (m *Model) UpdateViewport() { - renderedRows := make([]string, 0, len(m.rows)) - - // Render only rows from: m.cursor-m.viewport.Height to: m.cursor+m.viewport.Height - // Constant runtime, independent of number of rows in a table. - // Limits the number of renderedRows to a maximum of 2*m.viewport.Height - if m.cursor >= 0 { - m.start = clamp(m.cursor-m.viewport.Height(), 0, m.cursor) - } else { - m.start = 0 - } - m.end = clamp(m.cursor+m.viewport.Height(), m.cursor, len(m.rows)) - for i := m.start; i < m.end; i++ { - renderedRows = append(renderedRows, m.renderRow(i)) - } - - m.viewport.SetContent( - lipgloss.JoinVertical(lipgloss.Left, renderedRows...), - ) -} - -// SelectedRow returns the selected row. -// You can cast it to your own implementation. -func (m Model) SelectedRow() Row { - if m.cursor < 0 || m.cursor >= len(m.rows) { - return nil - } +// Getters - return m.rows[m.cursor] +// Focused returns the focus state of the table. +func (m Model) Focused() bool { + return m.focus } // Rows returns the current rows. -func (m Model) Rows() []Row { - return m.rows -} - -// Columns returns the current columns. -func (m Model) Columns() []Column { - return m.cols -} - -// SetRows sets a new rows state. -func (m *Model) SetRows(r []Row) { - m.rows = r - - if m.cursor > len(m.rows)-1 { - m.cursor = len(m.rows) - 1 - } - - m.UpdateViewport() +func (m Model) Rows() [][]string { + return table.DataToMatrix(m.table.GetData()) } -// SetColumns sets a new columns state. -func (m *Model) SetColumns(c []Column) { - m.cols = c - m.UpdateViewport() +// RowCount returns the number of rows in the table. +func (m Model) RowCount() int { + return m.table.GetData().Rows() } -// SetWidth sets the width of the viewport of the table. -func (m *Model) SetWidth(w int) { - m.viewport.SetWidth(w) - m.UpdateViewport() -} - -// SetHeight sets the height of the viewport of the table. -func (m *Model) SetHeight(h int) { - m.viewport.SetHeight(h - lipgloss.Height(m.headersView())) - m.UpdateViewport() -} - -// Height returns the viewport height of the table. -func (m Model) Height() int { - return m.viewport.Height() -} - -// Width returns the viewport width of the table. -func (m Model) Width() int { - return m.viewport.Width() +// Headers returns the current headers. +func (m Model) Headers() []string { + return m.table.GetHeaders() } // Cursor returns the index of the selected row. @@ -346,47 +506,36 @@ func (m Model) Cursor() int { return m.cursor } -// SetCursor sets the cursor position in the table. -func (m *Model) SetCursor(n int) { - m.cursor = clamp(n, 0, len(m.rows)-1) - m.UpdateViewport() +// SelectedRow returns the selected row. You can cast it to your own +// implementation. +func (m Model) SelectedRow() []string { + if m.cursor < 0 || m.cursor >= m.RowCount() { + return nil + } + + return table.DataToMatrix(m.table.GetData())[m.cursor] } +// Movement + // MoveUp moves the selection up by any number of rows. // It can not go above the first row. func (m *Model) MoveUp(n int) { - m.cursor = clamp(m.cursor-n, 0, len(m.rows)-1) - - offset := m.viewport.YOffset() - switch { - case m.start == 0: - offset = clamp(offset, 0, m.cursor) - case m.start < m.viewport.Height(): - offset = clamp(clamp(offset+n, 0, m.cursor), 0, m.viewport.Height()) - case offset >= 1: - offset = clamp(offset+n, 1, m.viewport.Height()) + m.SetCursor(m.cursor - n) + + if m.cursor < m.table.FirstVisibleRowIndex() { + m.SetYOffset(m.table.GetYOffset() - n) } - m.viewport.SetYOffset(offset) - m.UpdateViewport() } // MoveDown moves the selection down by any number of rows. // It can not go below the last row. func (m *Model) MoveDown(n int) { - m.cursor = clamp(m.cursor+n, 0, len(m.rows)-1) - m.UpdateViewport() - - offset := m.viewport.YOffset() - switch { - case m.end == len(m.rows) && offset > 0: - offset = clamp(offset-n, 1, m.viewport.Height()) - case m.cursor > (m.end-m.start)/2 && offset > 0: - offset = clamp(offset-n, 1, m.cursor) - case offset > 1: - case m.cursor > offset+m.viewport.Height()-1: - offset = clamp(offset+1, 0, 1) + m.SetCursor(m.cursor + n) + + if m.cursor > m.table.LastVisibleRowIndex() { + m.SetYOffset(m.table.GetYOffset() + n) } - m.viewport.SetYOffset(offset) } // GotoTop moves the selection to the first row. @@ -396,58 +545,70 @@ func (m *Model) GotoTop() { // GotoBottom moves the selection to the last row. func (m *Model) GotoBottom() { - m.MoveDown(len(m.rows)) -} - -// FromValues create the table rows from a simple string. It uses `\n` by -// default for getting all the rows and the given separator for the fields on -// each row. -func (m *Model) FromValues(value, separator string) { - rows := []Row{} - for _, line := range strings.Split(value, "\n") { - r := Row{} - for _, field := range strings.Split(line, separator) { - r = append(r, field) - } - rows = append(rows, r) - } - - m.SetRows(rows) -} - -func (m Model) headersView() string { - s := make([]string, 0, len(m.cols)) - for _, col := range m.cols { - if col.Width <= 0 { - continue - } - style := lipgloss.NewStyle().Width(col.Width).MaxWidth(col.Width).Inline(true) - renderedCell := style.Render(runewidth.Truncate(col.Title, col.Width, "…")) - s = append(s, m.styles.Header.Render(renderedCell)) - } - return lipgloss.JoinHorizontal(lipgloss.Top, s...) + m.MoveDown(m.RowCount()) } -func (m *Model) renderRow(r int) string { - s := make([]string, 0, len(m.cols)) - for i, value := range m.rows[r] { - if m.cols[i].Width <= 0 { - continue - } - style := lipgloss.NewStyle().Width(m.cols[i].Width).MaxWidth(m.cols[i].Width).Inline(true) - renderedCell := m.styles.Cell.Render(style.Render(runewidth.Truncate(value, m.cols[i].Width, "…"))) - s = append(s, renderedCell) - } - - row := lipgloss.JoinHorizontal(lipgloss.Top, s...) - - if r == m.cursor { - return m.styles.Selected.Render(row) - } - - return row -} +// Helpers func clamp(v, low, high int) int { return min(max(v, low), high) } + +// whichSides is a helper method for setting values on sides of a block based on +// the number of arguments given. +// 0: set all sides to true +// 1: set all sides to given arg +// 2: top -> bottom +// 3: top -> horizontal -> bottom +// 4: top -> right -> bottom -> left +// 5: top -> right -> bottom -> left -> rowSeparator +// 6: top -> right -> bottom -> left -> rowSeparator -> columnSeparator +func (m Model) whichSides(s ...bool) (top, right, bottom, left, rowSeparator, columnSeparator bool) { + // set the separators to true unless otherwise set. + rowSeparator = m.table.GetBorderRow() + columnSeparator = m.table.GetBorderColumn() + + switch len(s) { + case 1: + top = s[0] + right = s[0] + bottom = s[0] + left = s[0] + rowSeparator = s[0] + columnSeparator = s[0] + case 2: + top = s[0] + right = s[1] + bottom = s[0] + left = s[1] + case 3: + top = s[0] + right = s[1] + bottom = s[2] + left = s[1] + case 4: + top = s[0] + right = s[1] + bottom = s[2] + left = s[3] + case 5: + top = s[0] + right = s[1] + bottom = s[2] + left = s[3] + rowSeparator = s[4] + case 6: + top = s[0] + right = s[1] + bottom = s[2] + left = s[3] + rowSeparator = s[4] + columnSeparator = s[5] + default: + top = m.table.GetBorderTop() + right = m.table.GetBorderRight() + bottom = m.table.GetBorderBottom() + left = m.table.GetBorderLeft() + } + return top, right, bottom, left, rowSeparator, columnSeparator +} diff --git a/table/table_test.go b/table/table_test.go index 78db2424b..aa37746c1 100644 --- a/table/table_test.go +++ b/table/table_test.go @@ -1,356 +1,501 @@ package table import ( + "fmt" + "image/color" "reflect" - "strings" "testing" - "github.com/charmbracelet/bubbles/v2/help" - "github.com/charmbracelet/bubbles/v2/viewport" "github.com/charmbracelet/lipgloss/v2" + "github.com/charmbracelet/lipgloss/v2/table" "github.com/charmbracelet/x/ansi" "github.com/charmbracelet/x/exp/golden" ) -var testCols = []Column{ - {Title: "col1", Width: 10}, - {Title: "col2", Width: 10}, - {Title: "col3", Width: 10}, -} - -func TestNew(t *testing.T) { - tests := map[string]struct { - opts []Option - want Model - }{ - "Default": { - want: Model{ - // Default fields - cursor: 0, - viewport: viewport.New( - viewport.WithWidth(0), - viewport.WithHeight(20), - ), - KeyMap: DefaultKeyMap(), - Help: help.New(), - styles: DefaultStyles(), - }, - }, - "WithColumns": { - opts: []Option{ - WithColumns([]Column{ - {Title: "Foo", Width: 1}, - {Title: "Bar", Width: 2}, - }), - }, - want: Model{ - // Default fields - cursor: 0, - viewport: viewport.New( - viewport.WithWidth(0), - viewport.WithHeight(20), - ), - KeyMap: DefaultKeyMap(), - Help: help.New(), - styles: DefaultStyles(), - - // Modified fields - cols: []Column{ - {Title: "Foo", Width: 1}, - {Title: "Bar", Width: 2}, - }, - }, - }, - "WithColumns; WithRows": { - opts: []Option{ - WithColumns([]Column{ - {Title: "Foo", Width: 1}, - {Title: "Bar", Width: 2}, - }), - WithRows([]Row{ - {"1", "Foo"}, - {"2", "Bar"}, - }), - }, - want: Model{ - // Default fields - cursor: 0, - viewport: viewport.New( - viewport.WithWidth(0), - viewport.WithHeight(20), - ), - KeyMap: DefaultKeyMap(), - Help: help.New(), - styles: DefaultStyles(), - - // Modified fields - cols: []Column{ - {Title: "Foo", Width: 1}, - {Title: "Bar", Width: 2}, - }, - rows: []Row{ - {"1", "Foo"}, - {"2", "Bar"}, - }, - }, - }, - "WithHeight": { - opts: []Option{ - WithHeight(10), - }, - want: Model{ - // Default fields - cursor: 0, - KeyMap: DefaultKeyMap(), - Help: help.New(), - styles: DefaultStyles(), - - // Modified fields - // Viewport height is 1 less than the provided height when no header is present since lipgloss.Height adds 1 - viewport: viewport.New( - viewport.WithWidth(0), - viewport.WithHeight(9), - ), - }, - }, - "WithWidth": { - opts: []Option{ - WithWidth(10), - }, - want: Model{ - // Default fields - cursor: 0, - KeyMap: DefaultKeyMap(), - Help: help.New(), - styles: DefaultStyles(), - - // Modified fields - // Viewport height is 1 less than the provided height when no header is present since lipgloss.Height adds 1 - viewport: viewport.New( - viewport.WithWidth(10), - viewport.WithHeight(20), - ), - }, - }, - "WithFocused": { - opts: []Option{ - WithFocused(true), - }, - want: Model{ - // Default fields - cursor: 0, - viewport: viewport.New( - viewport.WithWidth(0), - viewport.WithHeight(20), - ), - KeyMap: DefaultKeyMap(), - Help: help.New(), - styles: DefaultStyles(), - - // Modified fields - focus: true, - }, - }, - "WithStyles": { - opts: []Option{ - WithStyles(Styles{}), - }, - want: Model{ - // Default fields - cursor: 0, - viewport: viewport.New( - viewport.WithWidth(0), - viewport.WithHeight(20), - ), - KeyMap: DefaultKeyMap(), - Help: help.New(), - - // Modified fields - styles: Styles{}, - }, - }, - "WithKeyMap": { - opts: []Option{ - WithKeyMap(KeyMap{}), - }, - want: Model{ - // Default fields - cursor: 0, - viewport: viewport.New( - viewport.WithWidth(0), - viewport.WithHeight(20), - ), - Help: help.New(), - styles: DefaultStyles(), - - // Modified fields - KeyMap: KeyMap{}, - }, - }, +var ( + niceMargins = lipgloss.NewStyle().Padding(0, 1) + headers = []string{"Rank", "City", "Country", "Population"} + rows = [][]string{ + {"1", "Tokyo", "Japan", "37,274,000"}, + {"2", "Delhi", "India", "32,065,760"}, + {"3", "Shanghai", "China", "28,516,904"}, + {"4", "Dhaka", "Bangladesh", "22,478,116"}, + {"5", "São Paulo", "Brazil", "22,429,800"}, + {"6", "Mexico City", "Mexico", "22,085,140"}, + {"7", "Cairo", "Egypt", "21,750,020"}, + {"8", "Beijing", "China", "21,333,332"}, + {"9", "Mumbai", "India", "20,961,472"}, + {"10", "Osaka", "Japan", "19,059,856"}, + {"11", "Chongqing", "China", "16,874,740"}, + {"12", "Karachi", "Pakistan", "16,839,950"}, + {"13", "Istanbul", "Turkey", "15,636,243"}, + {"14", "Kinshasa", "DR Congo", "15,628,085"}, + {"15", "Lagos", "Nigeria", "15,387,639"}, + {"16", "Buenos Aires", "Argentina", "15,369,919"}, } +) - for name, tc := range tests { - t.Run(name, func(t *testing.T) { - tc.want.UpdateViewport() - - got := New(tc.opts...) - - // NOTE(@andreynering): Funcs have different references, so we need - // to clear them out to compare the structs. - tc.want.viewport.LeftGutterFunc = nil - got.viewport.LeftGutterFunc = nil - - if !reflect.DeepEqual(tc.want, got) { - t.Errorf("\n\nwant %v\n\ngot %v", tc.want, got) - } - }) +// Tests + +func TestNewBash(t *testing.T) { + headers := []string{"Rank", "City", "Country", "Population"} + rows := [][]string{ + {"1", "Tokyo", "Japan", "37,274,000"}, + {"2", "Delhi", "India", "32,065,760"}, + {"3", "Shanghai", "China", "28,516,904"}, + {"4", "Dhaka", "Bangladesh", "22,478,116"}, + {"5", "São Paulo", "Brazil", "22,429,800"}, + {"6", "Mexico City", "Mexico", "22,085,140"}, + {"7", "Cairo", "Egypt", "21,750,020"}, + {"8", "Beijing", "China", "21,333,332"}, + {"9", "Mumbai", "India", "20,961,472"}, + {"10", "Osaka", "Japan", "19,059,856"}, } + t.Run("new with options", func(t *testing.T) { + tb := New( + WithHeaders(headers...), + WithRows(rows...), + WithHeight(10), + ) + tb.View() + }) + t.Run("new, no options", func(t *testing.T) { + tb := New().SetHeaders(headers...).SetRows(rows...) + tb.View() + }) } func TestModel_FromValues(t *testing.T) { - input := "foo1,bar1\nfoo2,bar2\nfoo3,bar3" - table := New(WithColumns([]Column{{Title: "Foo"}, {Title: "Bar"}})) - table.FromValues(input, ",") - - if len(table.rows) != 3 { - t.Fatalf("expect table to have 3 rows but it has %d", len(table.rows)) + table := New( + WithHeaders("Foo", "Bar"), + WithRows( + []string{"foo1", "bar1"}, + []string{"foo2", "bar2"}, + []string{"foo3", "bar3"}, + )) + + if table.RowCount() != 3 { + t.Fatalf("expect table to have 3 rows but it has %d", table.RowCount()) } - expect := []Row{ + expect := [][]string{ {"foo1", "bar1"}, {"foo2", "bar2"}, {"foo3", "bar3"}, } - if !reflect.DeepEqual(table.rows, expect) { - t.Fatalf("\n\nwant %v\n\ngot %v", expect, table.rows) + if !reflect.DeepEqual(table.Rows(), expect) { + t.Fatalf("\n\nwant %v\n\ngot %v", expect, table.Rows()) } } func TestModel_FromValues_WithTabSeparator(t *testing.T) { - input := "foo1.\tbar1\nfoo,bar,baz\tbar,2" - table := New(WithColumns([]Column{{Title: "Foo"}, {Title: "Bar"}})) - table.FromValues(input, "\t") + table := New( + WithHeaders("Foo", "Bar"), + WithRows( + []string{"foo1.", "bar1"}, + []string{"foo,bar,baz", "bar,2"}, + ), + ) - if len(table.rows) != 2 { - t.Fatalf("expect table to have 2 rows but it has %d", len(table.rows)) + if table.RowCount() != 2 { + t.Fatalf("expect table to have 2 rows but it has %d", table.RowCount()) } - expect := []Row{ + expect := [][]string{ {"foo1.", "bar1"}, {"foo,bar,baz", "bar,2"}, } - if !reflect.DeepEqual(table.rows, expect) { - t.Fatalf("\n\nwant %v\n\ngot %v", expect, table.rows) + if !reflect.DeepEqual(table.Rows(), expect) { + t.Fatalf("\n\nwant %v\n\ngot %v", expect, table.Rows()) + } + t.Run("new with options", func(t *testing.T) { + tb := New( + WithHeaders(headers...), + WithRows(rows...), + WithHeight(10), + ) + tb.View() + }) + t.Run("new, no options", func(t *testing.T) { + tb := New().SetHeaders(headers...).SetRows(rows...) + tb.View() + }) +} + +func TestTableAlignment(t *testing.T) { + headers := []string{ + "Name", + "Country of Origin", + "Dunk-able", + } + rows := [][]string{ + {"Chocolate Digestives", "UK", "Yes"}, + {"Tim Tams", "Australia", "No"}, + {"Hobnobs", "UK", "Yes"}, } + t.Run("NoBorder", func(t *testing.T) { + biscuits := New( + WithHeaders(headers...), + WithRows(rows...), + ). + // Remove default border. + SetBorder(false). + // Remove default border under header. + BorderHeader(false). + // Strip styles. + SetStyleFunc(func(_, _ int) lipgloss.Style { + return niceMargins + }) + golden.RequireEqual(t, []byte(biscuits.View())) + }) + t.Run("WithBorder", func(t *testing.T) { + biscuits := New( + WithHeaders(headers...), + WithRows(rows...), + ). + // Strip styles + SetStyleFunc(func(_, _ int) lipgloss.Style { + return niceMargins + }) + golden.RequireEqual(t, []byte(biscuits.View())) + }) } -func TestModel_RenderRow(t *testing.T) { +// Test Styles + +func TestOverwriteStyles(t *testing.T) { tests := []struct { - name string - table *Model - expected string + name string + styles Styles }{ - { - name: "simple row", - table: &Model{ - rows: []Row{{"Foooooo", "Baaaaar", "Baaaaaz"}}, - cols: testCols, - styles: Styles{Cell: lipgloss.NewStyle()}, - }, - expected: "Foooooo Baaaaar Baaaaaz ", - }, - { - name: "simple row with truncations", - table: &Model{ - rows: []Row{{"Foooooooooo", "Baaaaaaaaar", "Quuuuuuuuux"}}, - cols: testCols, - styles: Styles{Cell: lipgloss.NewStyle()}, - }, - expected: "Foooooooo…Baaaaaaaa…Quuuuuuuu…", - }, - { - name: "simple row avoiding truncations", - table: &Model{ - rows: []Row{{"Fooooooooo", "Baaaaaaaar", "Quuuuuuuux"}}, - cols: testCols, - styles: Styles{Cell: lipgloss.NewStyle()}, - }, - expected: "FoooooooooBaaaaaaaarQuuuuuuuux", - }, + {"ClearStyles", Styles{ + Selected: lipgloss.NewStyle(), + Header: lipgloss.NewStyle(), + Cell: lipgloss.NewStyle(), + }}, + {"NewStyles", Styles{ + Selected: niceMargins.Foreground(lipgloss.Color("68")), + Header: niceMargins, + Cell: niceMargins, + }}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - row := tc.table.renderRow(0) - if row != tc.expected { - t.Fatalf("\n\nWant: \n%s\n\nGot: \n%s\n", tc.expected, row) - } + tb := New( + WithHeaders(headers...), + WithRows(rows...), + WithFocused(true), + WithHeight(10), + ) + tb.OverwriteStyles(tc.styles) + golden.RequireEqual(t, []byte(tb.View())) }) } } -func TestTableAlignment(t *testing.T) { - t.Run("No border", func(t *testing.T) { - biscuits := New( - WithHeight(5), - WithColumns([]Column{ - {Title: "Name", Width: 25}, - {Title: "Country of Origin", Width: 16}, - {Title: "Dunk-able", Width: 12}, - }), - WithRows([]Row{ - {"Chocolate Digestives", "UK", "Yes"}, - {"Tim Tams", "Australia", "No"}, - {"Hobnobs", "UK", "Yes"}, - }), +func TestSetStyles(t *testing.T) { + tests := []struct { + name string + styles Styles + }{ + {"EmptyStyles", Styles{ + Selected: lipgloss.NewStyle(), + Header: lipgloss.NewStyle(), + Cell: lipgloss.NewStyle(), + }}, + {"NewStyles", Styles{ + Selected: niceMargins.Background(lipgloss.Color("68")), + Header: niceMargins, + Cell: niceMargins, + }}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + table := New( + WithHeaders(headers...), + WithRows(rows...), + WithFocused(true), + WithHeight(10), + ) + + table.SetStyles(tc.styles) + golden.RequireEqual(t, []byte(table.View())) + }) + } +} + +func TestSetStyleFunc(t *testing.T) { + t.Run("ClearStylesWithStyleFunc", func(t *testing.T) { + tb := New( + WithHeaders(headers...), + WithRows(rows...), + WithFocused(true), + WithHeight(10), ) - got := ansiStrip(biscuits.View()) - golden.RequireEqual(t, []byte(got)) + tb.SetStyleFunc(table.StyleFunc(func(row, col int) lipgloss.Style { + if row == tb.Cursor() { + return niceMargins.Background(lipgloss.Color("68")) + } + return niceMargins + })) + golden.RequireEqual(t, []byte(tb.View())) }) - t.Run("With border", func(t *testing.T) { - baseStyle := lipgloss.NewStyle(). - BorderStyle(lipgloss.NormalBorder()). - BorderForeground(lipgloss.Color("240")) - - s := DefaultStyles() - s.Header = s.Header. - BorderStyle(lipgloss.NormalBorder()). - BorderForeground(lipgloss.Color("240")). - BorderBottom(true). - Bold(false) +} - biscuits := New( - WithHeight(5), - WithColumns([]Column{ - {Title: "Name", Width: 25}, - {Title: "Country of Origin", Width: 16}, - {Title: "Dunk-able", Width: 12}, - }), - WithRows([]Row{ - {"Chocolate Digestives", "UK", "Yes"}, - {"Tim Tams", "Australia", "No"}, - {"Hobnobs", "UK", "Yes"}, - }), - WithStyles(s), - ) - got := ansiStrip(baseStyle.Render(biscuits.View())) - golden.RequireEqual(t, []byte(got)) +func TestSetBorder(t *testing.T) { + tests := []struct { + name string + borders []bool + }{ + {"UnsetAllBorders", []bool{false}}, + {"SetAllBorders", []bool{true}}, + {"VerticalBordersOnly", []bool{true, false}}, + {"NoTopBorder", []bool{false, true, true}}, + {"NoLeftBorder", []bool{true, true, true, false}}, + {"RowSeparatorAndNoRightBorder", []bool{true, false, true, true, true}}, + {"SetRowAndColumnSeparators", []bool{false, false, false, false, true, true}}, + {"InvalidNumberOfArguments", []bool{true, false, false, false, false, true, true}}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + tb := New( + WithHeaders(headers...), + WithRows(rows...), + WithFocused(true), + WithHeight(10), + ).SetBorder(tc.borders...) + golden.RequireEqual(t, []byte(tb.View())) + }) + } +} + +func TestNewFromTemplate(t *testing.T) { + // Using Pokemon example from https://github.com/charmbracelet/lipgloss. + baseStyle := lipgloss.NewStyle().Padding(0, 1) + headerStyle := baseStyle.Foreground(lipgloss.Color("252")).Bold(true) + selectedStyle := baseStyle.Foreground(lipgloss.Color("#01BE85")).Background(lipgloss.Color("#00432F")) + typeColors := map[string]color.Color{ + "Bug": lipgloss.Color("#D7FF87"), + "Electric": lipgloss.Color("#FDFF90"), + "Fire": lipgloss.Color("#FF7698"), + "Flying": lipgloss.Color("#FF87D7"), + "Grass": lipgloss.Color("#75FBAB"), + "Ground": lipgloss.Color("#FF875F"), + "Normal": lipgloss.Color("#929292"), + "Poison": lipgloss.Color("#7D5AFC"), + "Water": lipgloss.Color("#00E2C7"), + } + dimTypeColors := map[string]color.Color{ + "Bug": lipgloss.Color("#97AD64"), + "Electric": lipgloss.Color("#FCFF5F"), + "Fire": lipgloss.Color("#BA5F75"), + "Flying": lipgloss.Color("#C97AB2"), + "Grass": lipgloss.Color("#59B980"), + "Ground": lipgloss.Color("#C77252"), + "Normal": lipgloss.Color("#727272"), + "Poison": lipgloss.Color("#634BD0"), + "Water": lipgloss.Color("#439F8E"), + } + + pokemonHeaders := []string{"#", "Name", "Type 1", "Type 2", "Japanese", "Official Rom."} + pokemonData := [][]string{ + {"1", "Bulbasaur", "Grass", "Poison", "フシギダネ", "Fushigidane"}, + {"2", "Ivysaur", "Grass", "Poison", "フシギソウ", "Fushigisou"}, + {"3", "Venusaur", "Grass", "Poison", "フシギバナ", "Fushigibana"}, + {"4", "Charmander", "Fire", "", "ヒトカゲ", "Hitokage"}, + {"5", "Charmeleon", "Fire", "", "リザード", "Lizardo"}, + {"6", "Charizard", "Fire", "Flying", "リザードン", "Lizardon"}, + {"7", "Squirtle", "Water", "", "ゼニガメ", "Zenigame"}, + {"8", "Wartortle", "Water", "", "カメール", "Kameil"}, + {"9", "Blastoise", "Water", "", "カメックス", "Kamex"}, + {"10", "Caterpie", "Bug", "", "キャタピー", "Caterpie"}, + {"11", "Metapod", "Bug", "", "トランセル", "Trancell"}, + {"12", "Butterfree", "Bug", "Flying", "バタフリー", "Butterfree"}, + {"13", "Weedle", "Bug", "Poison", "ビードル", "Beedle"}, + {"14", "Kakuna", "Bug", "Poison", "コクーン", "Cocoon"}, + {"15", "Beedrill", "Bug", "Poison", "スピアー", "Spear"}, + {"16", "Pidgey", "Normal", "Flying", "ポッポ", "Poppo"}, + {"17", "Pidgeotto", "Normal", "Flying", "ピジョン", "Pigeon"}, + {"18", "Pidgeot", "Normal", "Flying", "ピジョット", "Pigeot"}, + {"19", "Rattata", "Normal", "", "コラッタ", "Koratta"}, + {"20", "Raticate", "Normal", "", "ラッタ", "Ratta"}, + {"21", "Spearow", "Normal", "Flying", "オニスズメ", "Onisuzume"}, + {"22", "Fearow", "Normal", "Flying", "オニドリル", "Onidrill"}, + {"23", "Ekans", "Poison", "", "アーボ", "Arbo"}, + {"24", "Arbok", "Poison", "", "アーボック", "Arbok"}, + {"25", "Pikachu", "Electric", "", "ピカチュウ", "Pikachu"}, + {"26", "Raichu", "Electric", "", "ライチュウ", "Raichu"}, + {"27", "Sandshrew", "Ground", "", "サンド", "Sand"}, + {"28", "Sandslash", "Ground", "", "サンドパン", "Sandpan"}, + } + + lipglossTable := table.New(). + Border(lipgloss.NormalBorder()). + BorderStyle(lipgloss.NewStyle().Foreground(lipgloss.Color("238"))). + Headers(pokemonHeaders...). + Width(80). + Rows(pokemonData...). + StyleFunc(func(row, col int) lipgloss.Style { + if row == table.HeaderRow { + return headerStyle + } + + if pokemonData[row][1] == "Pikachu" { + return selectedStyle + } + + even := row%2 == 0 + + switch col { + case 2, 3: // Type 1 + 2 + c := typeColors + if even { + c = dimTypeColors + } + + color := c[fmt.Sprint(pokemonData[row][col])] + return baseStyle.Foreground(color) + } + + if even { + return baseStyle.Foreground(lipgloss.Color("245")) + } + return baseStyle.Foreground(lipgloss.Color("252")) + }) + + bubblesTable := NewFromTemplate(lipglossTable) + golden.RequireEqual(t, []byte(bubblesTable.View())) +} + +func TestLipglossTable(t *testing.T) { + var ( + baseStyle = lipgloss.NewStyle().Padding(0, 1) + headerStyle = baseStyle.Foreground(lipgloss.Color("252")).Bold(true) + borderStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("238")) + styleFunc = func(row, col int) lipgloss.Style { + if row == table.HeaderRow { + return headerStyle + } + if row%2 == 0 { + return baseStyle.Foreground(lipgloss.Color("245")) + } + return baseStyle.Foreground(lipgloss.Color("252")) + } + ) + + t.Run("WithoutPreviousData", func(t *testing.T) { + lipglossTable := table.New(). + Border(lipgloss.NormalBorder()). + BorderStyle(borderStyle). + Width(80). + StyleFunc(styleFunc) + bubblesTable := New().SetHeaders(headers...).SetRows(rows...) + bubblesTable.LipglossTable(lipglossTable) + golden.RequireEqual(t, []byte(bubblesTable.View())) + }) + + t.Run("WithPreviousData", func(t *testing.T) { + lipglossTable := table.New(). + Border(lipgloss.NormalBorder()). + BorderStyle(borderStyle). + Width(80). + StyleFunc(styleFunc). + Headers(headers...). + Rows(rows...) + bubblesTable := New() + bubblesTable.LipglossTable(lipglossTable) + golden.RequireEqual(t, []byte(bubblesTable.View())) + }) +} + +// Examples + +func ExampleOption() { + niceMargins := lipgloss.NewStyle().Padding(0, 1) + headers := []string{"Rank", "City", "Country", "Population"} + rows := [][]string{ + {"1", "Tokyo", "Japan", "37,274,000"}, + {"2", "Delhi", "India", "32,065,760"}, + {"3", "Shanghai", "China", "28,516,904"}, + {"4", "Dhaka", "Bangladesh", "22,478,116"}, + {"5", "São Paulo", "Brazil", "22,429,800"}, + {"6", "Mexico City", "Mexico", "22,085,140"}, + {"7", "Cairo", "Egypt", "21,750,020"}, + {"8", "Beijing", "China", "21,333,332"}, + {"9", "Mumbai", "India", "20,961,472"}, + {"10", "Osaka", "Japan", "19,059,856"}, + } + t := New( + WithHeaders(headers...), + WithRows(rows...), + WithFocused(true), + WithHeight(10), + ).OverwriteStyles(Styles{ + Selected: niceMargins, + Header: niceMargins, + Cell: niceMargins, }) + fmt.Println(t.View()) + // Output: + //┌───────────────────────────────────────────┐ + //│ Rank  City   Country   Population │ + //├───────────────────────────────────────────┤ + //│ 1   Tokyo   Japan   37,274,000 │ + //│ 2   Delhi   India   32,065,760 │ + //│ 3   Shanghai   China   28,516,904 │ + //│ 4   Dhaka   Bangladesh  22,478,116 │ + //│ 5   São Paulo   Brazil   22,429,800 │ + //│ …   …   …   …  │ + //└───────────────────────────────────────────┘ } -func ansiStrip(s string) string { - // Replace all \r\n with \n - s = strings.ReplaceAll(s, "\r\n", "\n") - return ansi.Strip(s) +func ExampleModel_SetRows() { + niceMargins := lipgloss.NewStyle().Padding(0, 1) + headers := []string{"Rank", "City", "Country", "Population"} + rows := [][]string{ + {"1", "Tokyo", "Japan", "37,274,000"}, + {"2", "Delhi", "India", "32,065,760"}, + {"3", "Shanghai", "China", "28,516,904"}, + {"4", "Dhaka", "Bangladesh", "22,478,116"}, + {"5", "São Paulo", "Brazil", "22,429,800"}, + {"6", "Mexico City", "Mexico", "22,085,140"}, + {"7", "Cairo", "Egypt", "21,750,020"}, + {"8", "Beijing", "China", "21,333,332"}, + {"9", "Mumbai", "India", "20,961,472"}, + {"10", "Osaka", "Japan", "19,059,856"}, + } + tb := New(). + SetHeaders(headers...). + SetRows(rows...). + SetHeight(10). + OverwriteStyles(Styles{ + Selected: niceMargins, + Header: niceMargins, + Cell: niceMargins, + }) + fmt.Println(tb.View()) + // Output: + //┌───────────────────────────────────────────┐ + //│ Rank  City   Country   Population │ + //├───────────────────────────────────────────┤ + //│ 1   Tokyo   Japan   37,274,000 │ + //│ 2   Delhi   India   32,065,760 │ + //│ 3   Shanghai   China   28,516,904 │ + //│ 4   Dhaka   Bangladesh  22,478,116 │ + //│ 5   São Paulo   Brazil   22,429,800 │ + //│ …   …   …   …  │ + //└───────────────────────────────────────────┘ } func TestCursorNavigation(t *testing.T) { tests := map[string]struct { - rows []Row + rows [][]string action func(*Model) want int }{ "New": { - rows: []Row{ + rows: [][]string{ {"r1"}, {"r2"}, {"r3"}, @@ -359,7 +504,7 @@ func TestCursorNavigation(t *testing.T) { want: 0, }, "MoveDown": { - rows: []Row{ + rows: [][]string{ {"r1"}, {"r2"}, {"r3"}, @@ -371,7 +516,7 @@ func TestCursorNavigation(t *testing.T) { want: 2, }, "MoveUp": { - rows: []Row{ + rows: [][]string{ {"r1"}, {"r2"}, {"r3"}, @@ -384,7 +529,7 @@ func TestCursorNavigation(t *testing.T) { want: 1, }, "GotoBottom": { - rows: []Row{ + rows: [][]string{ {"r1"}, {"r2"}, {"r3"}, @@ -396,7 +541,7 @@ func TestCursorNavigation(t *testing.T) { want: 3, }, "GotoTop": { - rows: []Row{ + rows: [][]string{ {"r1"}, {"r2"}, {"r3"}, @@ -409,7 +554,7 @@ func TestCursorNavigation(t *testing.T) { want: 0, }, "SetCursor": { - rows: []Row{ + rows: [][]string{ {"r1"}, {"r2"}, {"r3"}, @@ -421,7 +566,7 @@ func TestCursorNavigation(t *testing.T) { want: 2, }, "MoveDown with overflow": { - rows: []Row{ + rows: [][]string{ {"r1"}, {"r2"}, {"r3"}, @@ -433,7 +578,7 @@ func TestCursorNavigation(t *testing.T) { want: 3, }, "MoveUp with overflow": { - rows: []Row{ + rows: [][]string{ {"r1"}, {"r2"}, {"r3"}, @@ -446,7 +591,7 @@ func TestCursorNavigation(t *testing.T) { want: 0, }, "Blur does not stop movement": { - rows: []Row{ + rows: [][]string{ {"r1"}, {"r2"}, {"r3"}, @@ -462,8 +607,11 @@ func TestCursorNavigation(t *testing.T) { for name, tc := range tests { t.Run(name, func(t *testing.T) { - table := New(WithColumns(testCols), WithRows(tc.rows)) - tc.action(&table) + table := New( + WithHeaders("col1", "col2", "col3"), + WithRows(tc.rows...), + ) + tc.action(table) if table.Cursor() != tc.want { t.Errorf("want %d, got %d", tc.want, table.Cursor()) @@ -473,258 +621,192 @@ func TestCursorNavigation(t *testing.T) { } func TestModel_SetRows(t *testing.T) { - table := New(WithColumns(testCols)) + table := New(WithHeaders("col1", "col2", "col3")) - if len(table.rows) != 0 { - t.Fatalf("want 0, got %d", len(table.rows)) + if table.RowCount() != 0 { + t.Fatalf("want 0, got %d", table.RowCount()) } - table.SetRows([]Row{{"r1"}, {"r2"}}) + table.SetRows([]string{"r1"}, []string{"r2"}) - if len(table.rows) != 2 { - t.Fatalf("want 2, got %d", len(table.rows)) + if table.RowCount() != 2 { + t.Fatalf("want 2, got %d", table.RowCount()) } - want := []Row{{"r1"}, {"r2"}} - if !reflect.DeepEqual(table.rows, want) { - t.Fatalf("\n\nwant %v\n\ngot %v", want, table.rows) + want := [][]string{{"r1"}, {"r2"}} + if !reflect.DeepEqual(table.Rows(), want) { + t.Fatalf("\n\nwant %v\n\ngot %v", want, table.Rows()) } } -func TestModel_SetColumns(t *testing.T) { +func TestModel_SetHeaders(t *testing.T) { table := New() - if len(table.cols) != 0 { - t.Fatalf("want 0, got %d", len(table.cols)) + if len(table.Headers()) != 0 { + t.Fatalf("want 0, got %d", len(table.Headers())) } - table.SetColumns([]Column{{Title: "Foo"}, {Title: "Bar"}}) + table.SetHeaders("Foo", "Bar") - if len(table.cols) != 2 { - t.Fatalf("want 2, got %d", len(table.cols)) + if len(table.Headers()) != 2 { + t.Fatalf("want 2, got %d", len(table.Headers())) } - want := []Column{{Title: "Foo"}, {Title: "Bar"}} - if !reflect.DeepEqual(table.cols, want) { - t.Fatalf("\n\nwant %v\n\ngot %v", want, table.cols) + want := []string{"Foo", "Bar"} + if !reflect.DeepEqual(table.Headers(), want) { + t.Fatalf("\n\nwant %v\n\ngot %v", want, table.Headers()) } } func TestModel_View(t *testing.T) { tests := map[string]struct { - modelFunc func() Model + modelFunc func() *Model skip bool }{ - // TODO(?): should the view/output of empty tables use the same default height? (this has height 21) "Empty": { - modelFunc: func() Model { + modelFunc: func() *Model { return New() }, }, - "Single row and column": { - modelFunc: func() Model { + "SingleRowAndColumn": { + modelFunc: func() *Model { return New( - WithColumns([]Column{ - {Title: "Name", Width: 25}, - }), - WithRows([]Row{ - {"Chocolate Digestives"}, - }), + WithHeaders("Name"), + WithRows( + []string{"Chocolate Digestives"}, + ), ) }, }, - "Multiple rows and columns": { - modelFunc: func() Model { + "MultipleRowsAndColumns": { + modelFunc: func() *Model { return New( - WithColumns([]Column{ - {Title: "Name", Width: 25}, - {Title: "Country of Origin", Width: 16}, - {Title: "Dunk-able", Width: 12}, - }), - WithRows([]Row{ - {"Chocolate Digestives", "UK", "Yes"}, - {"Tim Tams", "Australia", "No"}, - {"Hobnobs", "UK", "Yes"}, - }), + WithHeaders("Name", "Country of Origin", "Dunk-able"), + WithRows( + []string{"Chocolate Digestives", "UK", "Yes"}, + []string{"Tim Tams", "Australia", "No"}, + []string{"Hobnobs", "UK", "Yes"}, + ), ) }, }, - // TODO(fix): since the table height is tied to the viewport height, adding vertical padding to the headers' height directly increases the table height. - "Extra padding": { - modelFunc: func() Model { + "ExtraPadding": { + modelFunc: func() *Model { s := DefaultStyles() s.Header = lipgloss.NewStyle().Padding(2, 2) s.Cell = lipgloss.NewStyle().Padding(2, 2) return New( - WithHeight(10), - WithColumns([]Column{ - {Title: "Name", Width: 25}, - {Title: "Country of Origin", Width: 16}, - {Title: "Dunk-able", Width: 12}, - }), - WithRows([]Row{ - {"Chocolate Digestives", "UK", "Yes"}, - {"Tim Tams", "Australia", "No"}, - {"Hobnobs", "UK", "Yes"}, - }), + WithHeaders("Name", "Country of Origin", "Dunk-able"), + WithRows( + []string{"Chocolate Digestives", "UK", "Yes"}, + []string{"Tim Tams", "Australia", "No"}, + []string{"Hobnobs", "UK", "Yes"}, + ), WithStyles(s), ) }, }, - "No padding": { - modelFunc: func() Model { + "NoPadding": { + modelFunc: func() *Model { s := DefaultStyles() s.Header = lipgloss.NewStyle() s.Cell = lipgloss.NewStyle() return New( WithHeight(10), - WithColumns([]Column{ - {Title: "Name", Width: 25}, - {Title: "Country of Origin", Width: 16}, - {Title: "Dunk-able", Width: 12}, - }), - WithRows([]Row{ - {"Chocolate Digestives", "UK", "Yes"}, - {"Tim Tams", "Australia", "No"}, - {"Hobnobs", "UK", "Yes"}, - }), + WithHeaders("Name", "Country of Origin", "Dunk-able"), + WithRows( + []string{"Chocolate Digestives", "UK", "Yes"}, + []string{"Tim Tams", "Australia", "No"}, + []string{"Hobnobs", "UK", "Yes"}, + ), WithStyles(s), ) }, }, - // TODO(?): the total height is modified with borderd headers, however not with bordered cells. Is this expected/desired? - "Bordered headers": { - modelFunc: func() Model { + "BorderedHeaders": { + modelFunc: func() *Model { return New( - WithColumns([]Column{ - {Title: "Name", Width: 25}, - {Title: "Country of Origin", Width: 16}, - {Title: "Dunk-able", Width: 12}, - }), - WithRows([]Row{ - {"Chocolate Digestives", "UK", "Yes"}, - {"Tim Tams", "Australia", "No"}, - {"Hobnobs", "UK", "Yes"}, - }), + WithHeaders("Name", "Country of Origin", "Dunk-able"), + WithRows( + []string{"Chocolate Digestives", "UK", "Yes"}, + []string{"Tim Tams", "Australia", "No"}, + []string{"Hobnobs", "UK", "Yes"}, + ), WithStyles(Styles{ Header: lipgloss.NewStyle().BorderStyle(lipgloss.NormalBorder()), }), ) }, }, - // TODO(fix): Headers are not horizontally aligned with cells due to the border adding width to the cells. - "Bordered cells": { - modelFunc: func() Model { + "BorderedCells": { + modelFunc: func() *Model { return New( - WithColumns([]Column{ - {Title: "Name", Width: 25}, - {Title: "Country of Origin", Width: 16}, - {Title: "Dunk-able", Width: 12}, - }), - WithRows([]Row{ - {"Chocolate Digestives", "UK", "Yes"}, - {"Tim Tams", "Australia", "No"}, - {"Hobnobs", "UK", "Yes"}, - }), + WithHeaders("Name", "Country of Origin", "Dunk-able"), + WithRows( + []string{"Chocolate Digestives", "UK", "Yes"}, + []string{"Tim Tams", "Australia", "No"}, + []string{"Hobnobs", "UK", "Yes"}, + ), WithStyles(Styles{ Cell: lipgloss.NewStyle().BorderStyle(lipgloss.NormalBorder()), }), ) }, }, - "Manual height greater than rows": { - modelFunc: func() Model { + // FIXME(@andreynering): Fix in Lip Gloss? Potentially add extra empty lines to the bottom of the table. + "ManualHeightGreaterThanRows": { + modelFunc: func() *Model { return New( - WithHeight(6), - WithColumns([]Column{ - {Title: "Name", Width: 25}, - {Title: "Country of Origin", Width: 16}, - {Title: "Dunk-able", Width: 12}, - }), - WithRows([]Row{ - {"Chocolate Digestives", "UK", "Yes"}, - {"Tim Tams", "Australia", "No"}, - {"Hobnobs", "UK", "Yes"}, - }), + WithHeight(15), + WithHeaders("Name", "Country of Origin", "Dunk-able"), + WithRows( + []string{"Chocolate Digestives", "UK", "Yes"}, + []string{"Tim Tams", "Australia", "No"}, + []string{"Hobnobs", "UK", "Yes"}, + ), ) }, }, - "Manual height less than rows": { - modelFunc: func() Model { + "ManualHeightLessThanRows": { + modelFunc: func() *Model { return New( WithHeight(2), - WithColumns([]Column{ - {Title: "Name", Width: 25}, - {Title: "Country of Origin", Width: 16}, - {Title: "Dunk-able", Width: 12}, - }), - WithRows([]Row{ - {"Chocolate Digestives", "UK", "Yes"}, - {"Tim Tams", "Australia", "No"}, - {"Hobnobs", "UK", "Yes"}, - }), + WithHeaders("Name", "Country of Origin", "Dunk-able"), + WithRows( + []string{"Chocolate Digestives", "UK", "Yes"}, + []string{"Tim Tams", "Australia", "No"}, + []string{"Hobnobs", "UK", "Yes"}, + ), ) }, }, - // TODO(fix): spaces are added to the right of the viewport to fill the width, but the headers end as though they are not aware of the width. - "Manual width greater than columns": { - modelFunc: func() Model { + "ManualWidthGreaterThanColumns": { + modelFunc: func() *Model { return New( WithWidth(80), - WithColumns([]Column{ - {Title: "Name", Width: 25}, - {Title: "Country of Origin", Width: 16}, - {Title: "Dunk-able", Width: 12}, - }), - WithRows([]Row{ - {"Chocolate Digestives", "UK", "Yes"}, - {"Tim Tams", "Australia", "No"}, - {"Hobnobs", "UK", "Yes"}, - }), + WithHeaders("Name", "Country of Origin", "Dunk-able"), + WithRows( + []string{"Chocolate Digestives", "UK", "Yes"}, + []string{"Tim Tams", "Australia", "No"}, + []string{"Hobnobs", "UK", "Yes"}, + ), ) }, }, - // TODO(fix): Setting the table width does not affect the total headers' width. Cells are wrapped. - // Headers are not affected. Truncation/resizing should match lipgloss.table functionality. - "Manual width less than columns": { - modelFunc: func() Model { + "ManualWidthLessThanColumns": { + modelFunc: func() *Model { return New( WithWidth(30), - WithColumns([]Column{ - {Title: "Name", Width: 25}, - {Title: "Country of Origin", Width: 16}, - {Title: "Dunk-able", Width: 12}, - }), - WithRows([]Row{ - {"Chocolate Digestives", "UK", "Yes"}, - {"Tim Tams", "Australia", "No"}, - {"Hobnobs", "UK", "Yes"}, - }), + WithHeaders("Name", "Country of Origin", "Dunk-able"), + WithRows( + []string{"Chocolate Digestives", "UK", "Yes"}, + []string{"Tim Tams", "Australia", "No"}, + []string{"Hobnobs", "UK", "Yes"}, + ), ) }, - skip: true, - }, - "Modified viewport height": { - modelFunc: func() Model { - m := New( - WithColumns([]Column{ - {Title: "Name", Width: 25}, - {Title: "Country of Origin", Width: 16}, - {Title: "Dunk-able", Width: 12}, - }), - WithRows([]Row{ - {"Chocolate Digestives", "UK", "Yes"}, - {"Tim Tams", "Australia", "No"}, - {"Hobnobs", "UK", "Yes"}, - }), - ) - - m.viewport.SetHeight(2) - - return m - }, }, } @@ -736,9 +818,7 @@ func TestModel_View(t *testing.T) { table := tc.modelFunc() - got := ansi.Strip(table.View()) - - golden.RequireEqual(t, []byte(got)) + golden.RequireEqual(t, []byte(table.View())) }) } } @@ -754,16 +834,12 @@ func TestModel_View_CenteredInABox(t *testing.T) { table := New( WithHeight(6), WithWidth(80), - WithColumns([]Column{ - {Title: "Name", Width: 25}, - {Title: "Country of Origin", Width: 16}, - {Title: "Dunk-able", Width: 12}, - }), - WithRows([]Row{ - {"Chocolate Digestives", "UK", "Yes"}, - {"Tim Tams", "Australia", "No"}, - {"Hobnobs", "UK", "Yes"}, - }), + WithHeaders("Name", "Country of Origin", "Dunk-able"), + WithRows( + []string{"Chocolate Digestives", "UK", "Yes"}, + []string{"Tim Tams", "Australia", "No"}, + []string{"Hobnobs", "UK", "Yes"}, + ), ) tableView := ansi.Strip(table.View()) diff --git a/table/testdata/TestLipglossTable/WithPreviousData.golden b/table/testdata/TestLipglossTable/WithPreviousData.golden new file mode 100644 index 000000000..3cb3da3a3 --- /dev/null +++ b/table/testdata/TestLipglossTable/WithPreviousData.golden @@ -0,0 +1,20 @@ +┌───────────────────┬───────────────────┬───────────────────┬──────────────────┐ +│ Rank  │ City  │ Country  │ Population  │ +├───────────────────┼───────────────────┼───────────────────┼──────────────────┤ +│ 1  │ Tokyo  │ Japan  │ 37,274,000  │ +│ 2  │ Delhi  │ India  │ 32,065,760  │ +│ 3  │ Shanghai  │ China  │ 28,516,904  │ +│ 4  │ Dhaka  │ Bangladesh  │ 22,478,116  │ +│ 5  │ São Paulo  │ Brazil  │ 22,429,800  │ +│ 6  │ Mexico City  │ Mexico  │ 22,085,140  │ +│ 7  │ Cairo  │ Egypt  │ 21,750,020  │ +│ 8  │ Beijing  │ China  │ 21,333,332  │ +│ 9  │ Mumbai  │ India  │ 20,961,472  │ +│ 10  │ Osaka  │ Japan  │ 19,059,856  │ +│ 11  │ Chongqing  │ China  │ 16,874,740  │ +│ 12  │ Karachi  │ Pakistan  │ 16,839,950  │ +│ 13  │ Istanbul  │ Turkey  │ 15,636,243  │ +│ 14  │ Kinshasa  │ DR Congo  │ 15,628,085  │ +│ 15  │ Lagos  │ Nigeria  │ 15,387,639  │ +│ 16  │ Buenos Aires  │ Argentina  │ 15,369,919  │ +└───────────────────┴───────────────────┴───────────────────┴──────────────────┘ \ No newline at end of file diff --git a/table/testdata/TestLipglossTable/WithoutPreviousData.golden b/table/testdata/TestLipglossTable/WithoutPreviousData.golden new file mode 100644 index 000000000..3cb3da3a3 --- /dev/null +++ b/table/testdata/TestLipglossTable/WithoutPreviousData.golden @@ -0,0 +1,20 @@ +┌───────────────────┬───────────────────┬───────────────────┬──────────────────┐ +│ Rank  │ City  │ Country  │ Population  │ +├───────────────────┼───────────────────┼───────────────────┼──────────────────┤ +│ 1  │ Tokyo  │ Japan  │ 37,274,000  │ +│ 2  │ Delhi  │ India  │ 32,065,760  │ +│ 3  │ Shanghai  │ China  │ 28,516,904  │ +│ 4  │ Dhaka  │ Bangladesh  │ 22,478,116  │ +│ 5  │ São Paulo  │ Brazil  │ 22,429,800  │ +│ 6  │ Mexico City  │ Mexico  │ 22,085,140  │ +│ 7  │ Cairo  │ Egypt  │ 21,750,020  │ +│ 8  │ Beijing  │ China  │ 21,333,332  │ +│ 9  │ Mumbai  │ India  │ 20,961,472  │ +│ 10  │ Osaka  │ Japan  │ 19,059,856  │ +│ 11  │ Chongqing  │ China  │ 16,874,740  │ +│ 12  │ Karachi  │ Pakistan  │ 16,839,950  │ +│ 13  │ Istanbul  │ Turkey  │ 15,636,243  │ +│ 14  │ Kinshasa  │ DR Congo  │ 15,628,085  │ +│ 15  │ Lagos  │ Nigeria  │ 15,387,639  │ +│ 16  │ Buenos Aires  │ Argentina  │ 15,369,919  │ +└───────────────────┴───────────────────┴───────────────────┴──────────────────┘ \ No newline at end of file diff --git a/table/testdata/TestModel_View/BorderedCells.golden b/table/testdata/TestModel_View/BorderedCells.golden new file mode 100644 index 000000000..237b81911 --- /dev/null +++ b/table/testdata/TestModel_View/BorderedCells.golden @@ -0,0 +1,11 @@ +┌────────────────────────────────────────────────────┐ +│ Name   Country of Origin  Dunk-able │ +├────────────────────────────────────────────────────┤ +│ Chocolate Digestives  UK   Yes  │ +│┌────────────────────┐┌─────────────────┐┌─────────┐│ +││Tim Tams ││Australia ││No ││ +│└────────────────────┘└─────────────────┘└─────────┘│ +│┌────────────────────┐┌─────────────────┐┌─────────┐│ +││Hobnobs ││UK ││Yes ││ +│└────────────────────┘└─────────────────┘└─────────┘│ +└────────────────────────────────────────────────────┘ \ No newline at end of file diff --git a/table/testdata/TestModel_View/BorderedHeaders.golden b/table/testdata/TestModel_View/BorderedHeaders.golden new file mode 100644 index 000000000..56987366a --- /dev/null +++ b/table/testdata/TestModel_View/BorderedHeaders.golden @@ -0,0 +1,9 @@ +┌────────────────────────────────────────────────────┐ +│┌────────────────────┐┌─────────────────┐┌─────────┐│ +││Name ││Country of Origin││Dunk-able││ +│└────────────────────┘└─────────────────┘└─────────┘│ +├────────────────────────────────────────────────────┤ +│ Chocolate Digestives  UK   Yes  │ +│ Tim Tams   Australia   No  │ +│ Hobnobs   UK   Yes  │ +└────────────────────────────────────────────────────┘ \ No newline at end of file diff --git a/table/testdata/TestModel_View/Bordered_cells.golden b/table/testdata/TestModel_View/Bordered_cells.golden deleted file mode 100644 index 71e95988b..000000000 --- a/table/testdata/TestModel_View/Bordered_cells.golden +++ /dev/null @@ -1,21 +0,0 @@ -Name Country of Orig…Dunk-able -┌─────────────────────────┐┌────────────────┐┌────────────┐ -│Chocolate Digestives ││UK ││Yes │ -└─────────────────────────┘└────────────────┘└────────────┘ -┌─────────────────────────┐┌────────────────┐┌────────────┐ -│Tim Tams ││Australia ││No │ -└─────────────────────────┘└────────────────┘└────────────┘ -┌─────────────────────────┐┌────────────────┐┌────────────┐ -│Hobnobs ││UK ││Yes │ -└─────────────────────────┘└────────────────┘└────────────┘ - - - - - - - - - - - \ No newline at end of file diff --git a/table/testdata/TestModel_View/Bordered_headers.golden b/table/testdata/TestModel_View/Bordered_headers.golden deleted file mode 100644 index 0e260bae2..000000000 --- a/table/testdata/TestModel_View/Bordered_headers.golden +++ /dev/null @@ -1,23 +0,0 @@ -┌─────────────────────────┐┌────────────────┐┌────────────┐ -│Name ││Country of Orig…││Dunk-able │ -└─────────────────────────┘└────────────────┘└────────────┘ -Chocolate Digestives UK Yes -Tim Tams Australia No -Hobnobs UK Yes - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/table/testdata/TestModel_View/Empty.golden b/table/testdata/TestModel_View/Empty.golden index 7b050800f..e69de29bb 100644 --- a/table/testdata/TestModel_View/Empty.golden +++ b/table/testdata/TestModel_View/Empty.golden @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/table/testdata/TestModel_View/ExtraPadding.golden b/table/testdata/TestModel_View/ExtraPadding.golden new file mode 100644 index 000000000..7aa3c3b46 --- /dev/null +++ b/table/testdata/TestModel_View/ExtraPadding.golden @@ -0,0 +1,19 @@ +┌──────────────────────────────────────────────────────────┐ +│ │ +│ │ +│  Name     Country of Origin    Dunk-able  │ +│ │ +│ │ +├──────────────────────────────────────────────────────────┤ +│ Chocolate Digestives   UK   Yes  │ +│ │ +│ │ +│  Tim Tams     Australia     No   │ +│ │ +│ │ +│ │ +│ │ +│  Hobnobs     UK     Yes   │ +│ │ +│ │ +└──────────────────────────────────────────────────────────┘ \ No newline at end of file diff --git a/table/testdata/TestModel_View/Extra_padding.golden b/table/testdata/TestModel_View/Extra_padding.golden deleted file mode 100644 index d6f6b76e8..000000000 --- a/table/testdata/TestModel_View/Extra_padding.golden +++ /dev/null @@ -1,14 +0,0 @@ - - -  Name     Country of Orig…    Dunk-able    - - - - -  Chocolate Digestives     UK     Yes    - - - - -  Tim Tams     Australia     No    - \ No newline at end of file diff --git a/table/testdata/TestModel_View/ManualHeightGreaterThanRows.golden b/table/testdata/TestModel_View/ManualHeightGreaterThanRows.golden new file mode 100644 index 000000000..747e8ee46 --- /dev/null +++ b/table/testdata/TestModel_View/ManualHeightGreaterThanRows.golden @@ -0,0 +1,7 @@ +┌────────────────────────────────────────────────────┐ +│ Name   Country of Origin  Dunk-able │ +├────────────────────────────────────────────────────┤ +│ Chocolate Digestives  UK   Yes  │ +│ Tim Tams   Australia   No  │ +│ Hobnobs   UK   Yes  │ +└────────────────────────────────────────────────────┘ \ No newline at end of file diff --git a/table/testdata/TestModel_View/ManualHeightLessThanRows.golden b/table/testdata/TestModel_View/ManualHeightLessThanRows.golden new file mode 100644 index 000000000..8573f95a6 --- /dev/null +++ b/table/testdata/TestModel_View/ManualHeightLessThanRows.golden @@ -0,0 +1,2 @@ +┌────────────────────────────────────────────────────┐ +│ Name   Country of Origin  Dunk-able │ \ No newline at end of file diff --git a/table/testdata/TestModel_View/ManualWidthGreaterThanColumns.golden b/table/testdata/TestModel_View/ManualWidthGreaterThanColumns.golden new file mode 100644 index 000000000..45d3d9607 --- /dev/null +++ b/table/testdata/TestModel_View/ManualWidthGreaterThanColumns.golden @@ -0,0 +1,7 @@ +┌──────────────────────────────────────────────────────────────────────────────┐ +│ Name   Country of Origin   Dunk-able  │ +├──────────────────────────────────────────────────────────────────────────────┤ +│ Chocolate Digestives   UK   Yes  │ +│ Tim Tams   Australia   No  │ +│ Hobnobs   UK   Yes  │ +└──────────────────────────────────────────────────────────────────────────────┘ \ No newline at end of file diff --git a/table/testdata/TestModel_View/ManualWidthLessThanColumns.golden b/table/testdata/TestModel_View/ManualWidthLessThanColumns.golden new file mode 100644 index 000000000..b54c17dba --- /dev/null +++ b/table/testdata/TestModel_View/ManualWidthLessThanColumns.golden @@ -0,0 +1,10 @@ +┌────────────────────────────┐ +│ Name   Count…  Dunk-a… │ +├────────────────────────────┤ +│ Chocolate  UK   Yes  │ +│ Digestive  │ +│ s  │ +│ Tim Tams   Austra  No  │ +│  lia  │ +│ Hobnobs   UK   Yes  │ +└────────────────────────────┘ \ No newline at end of file diff --git a/table/testdata/TestModel_View/Manual_height_greater_than_rows.golden b/table/testdata/TestModel_View/Manual_height_greater_than_rows.golden deleted file mode 100644 index f89de1df9..000000000 --- a/table/testdata/TestModel_View/Manual_height_greater_than_rows.golden +++ /dev/null @@ -1,6 +0,0 @@ - Name   Country of Orig…  Dunk-able   - Chocolate Digestives   UK   Yes   - Tim Tams   Australia   No   - Hobnobs   UK   Yes   - - \ No newline at end of file diff --git a/table/testdata/TestModel_View/Manual_height_less_than_rows.golden b/table/testdata/TestModel_View/Manual_height_less_than_rows.golden deleted file mode 100644 index 83bded11d..000000000 --- a/table/testdata/TestModel_View/Manual_height_less_than_rows.golden +++ /dev/null @@ -1,2 +0,0 @@ - Name   Country of Orig…  Dunk-able   - Chocolate Digestives   UK   Yes   \ No newline at end of file diff --git a/table/testdata/TestModel_View/Manual_width_greater_than_columns.golden b/table/testdata/TestModel_View/Manual_width_greater_than_columns.golden deleted file mode 100644 index a1c06fee1..000000000 --- a/table/testdata/TestModel_View/Manual_width_greater_than_columns.golden +++ /dev/null @@ -1,21 +0,0 @@ - Name   Country of Orig…  Dunk-able   - Chocolate Digestives   UK   Yes   - Tim Tams   Australia   No   - Hobnobs   UK   Yes   - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/table/testdata/TestModel_View/Manual_width_less_than_columns.golden b/table/testdata/TestModel_View/Manual_width_less_than_columns.golden deleted file mode 100644 index d2bc25b96..000000000 --- a/table/testdata/TestModel_View/Manual_width_less_than_columns.golden +++ /dev/null @@ -1,21 +0,0 @@ - Name Country of Origin Dunk-able - Chocolate Digestives UK Yes - Tim Tams Australia No - Hobnobs UK Yes - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/table/testdata/TestModel_View/Modified_viewport_height.golden b/table/testdata/TestModel_View/Modified_viewport_height.golden deleted file mode 100644 index 98a44eeb9..000000000 --- a/table/testdata/TestModel_View/Modified_viewport_height.golden +++ /dev/null @@ -1,3 +0,0 @@ - Name   Country of Orig…  Dunk-able   - Chocolate Digestives   UK   Yes   - Tim Tams   Australia   No   \ No newline at end of file diff --git a/table/testdata/TestModel_View/MultipleRowsAndColumns.golden b/table/testdata/TestModel_View/MultipleRowsAndColumns.golden new file mode 100644 index 000000000..747e8ee46 --- /dev/null +++ b/table/testdata/TestModel_View/MultipleRowsAndColumns.golden @@ -0,0 +1,7 @@ +┌────────────────────────────────────────────────────┐ +│ Name   Country of Origin  Dunk-able │ +├────────────────────────────────────────────────────┤ +│ Chocolate Digestives  UK   Yes  │ +│ Tim Tams   Australia   No  │ +│ Hobnobs   UK   Yes  │ +└────────────────────────────────────────────────────┘ \ No newline at end of file diff --git a/table/testdata/TestModel_View/Multiple_rows_and_columns.golden b/table/testdata/TestModel_View/Multiple_rows_and_columns.golden deleted file mode 100644 index d1dffc5ba..000000000 --- a/table/testdata/TestModel_View/Multiple_rows_and_columns.golden +++ /dev/null @@ -1,21 +0,0 @@ - Name   Country of Orig…  Dunk-able   - Chocolate Digestives   UK   Yes   - Tim Tams   Australia   No   - Hobnobs   UK   Yes   - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/table/testdata/TestModel_View/NoPadding.golden b/table/testdata/TestModel_View/NoPadding.golden new file mode 100644 index 000000000..747e8ee46 --- /dev/null +++ b/table/testdata/TestModel_View/NoPadding.golden @@ -0,0 +1,7 @@ +┌────────────────────────────────────────────────────┐ +│ Name   Country of Origin  Dunk-able │ +├────────────────────────────────────────────────────┤ +│ Chocolate Digestives  UK   Yes  │ +│ Tim Tams   Australia   No  │ +│ Hobnobs   UK   Yes  │ +└────────────────────────────────────────────────────┘ \ No newline at end of file diff --git a/table/testdata/TestModel_View/No_padding.golden b/table/testdata/TestModel_View/No_padding.golden deleted file mode 100644 index f74874668..000000000 --- a/table/testdata/TestModel_View/No_padding.golden +++ /dev/null @@ -1,10 +0,0 @@ -Name Country of Orig…Dunk-able -Chocolate Digestives UK Yes -Tim Tams Australia No -Hobnobs UK Yes - - - - - - \ No newline at end of file diff --git a/table/testdata/TestModel_View/SingleRowAndColumn.golden b/table/testdata/TestModel_View/SingleRowAndColumn.golden new file mode 100644 index 000000000..908a00cce --- /dev/null +++ b/table/testdata/TestModel_View/SingleRowAndColumn.golden @@ -0,0 +1,5 @@ +┌──────────────────────┐ +│ Name  │ +├──────────────────────┤ +│ Chocolate Digestives │ +└──────────────────────┘ \ No newline at end of file diff --git a/table/testdata/TestModel_View/Single_row_and_column.golden b/table/testdata/TestModel_View/Single_row_and_column.golden deleted file mode 100644 index 36d5110eb..000000000 --- a/table/testdata/TestModel_View/Single_row_and_column.golden +++ /dev/null @@ -1,21 +0,0 @@ - Name   - Chocolate Digestives   - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/table/testdata/TestNewFromTemplate.golden b/table/testdata/TestNewFromTemplate.golden new file mode 100644 index 000000000..b300d75e6 --- /dev/null +++ b/table/testdata/TestNewFromTemplate.golden @@ -0,0 +1,32 @@ +┌────────────┬────────────┬───────────┬───────────┬────────────┬───────────────┐ +│ #  │ Name  │ Type 1  │ Type 2  │ Japanese  │ Official Rom. │ +├────────────┼────────────┼───────────┼───────────┼────────────┼───────────────┤ +│ 1  │ Bulbasaur  │ Grass  │ Poison  │ フシギダネ │ Fushigidane  │ +│ 2  │ Ivysaur  │ Grass  │ Poison  │ フシギソウ │ Fushigisou  │ +│ 3  │ Venusaur  │ Grass  │ Poison  │ フシギバナ │ Fushigibana  │ +│ 4  │ Charmander │ Fire  │   │ ヒトカゲ  │ Hitokage  │ +│ 5  │ Charmeleon │ Fire  │   │ リザード  │ Lizardo  │ +│ 6  │ Charizard  │ Fire  │ Flying  │ リザードン │ Lizardon  │ +│ 7  │ Squirtle  │ Water  │   │ ゼニガメ  │ Zenigame  │ +│ 8  │ Wartortle  │ Water  │   │ カメール  │ Kameil  │ +│ 9  │ Blastoise  │ Water  │   │ カメックス │ Kamex  │ +│ 10  │ Caterpie  │ Bug  │   │ キャタピー │ Caterpie  │ +│ 11  │ Metapod  │ Bug  │   │ トランセル │ Trancell  │ +│ 12  │ Butterfree │ Bug  │ Flying  │ バタフリー │ Butterfree  │ +│ 13  │ Weedle  │ Bug  │ Poison  │ ビードル  │ Beedle  │ +│ 14  │ Kakuna  │ Bug  │ Poison  │ コクーン  │ Cocoon  │ +│ 15  │ Beedrill  │ Bug  │ Poison  │ スピアー  │ Spear  │ +│ 16  │ Pidgey  │ Normal  │ Flying  │ ポッポ  │ Poppo  │ +│ 17  │ Pidgeotto  │ Normal  │ Flying  │ ピジョン  │ Pigeon  │ +│ 18  │ Pidgeot  │ Normal  │ Flying  │ ピジョット │ Pigeot  │ +│ 19  │ Rattata  │ Normal  │   │ コラッタ  │ Koratta  │ +│ 20  │ Raticate  │ Normal  │   │ ラッタ  │ Ratta  │ +│ 21  │ Spearow  │ Normal  │ Flying  │ オニスズメ │ Onisuzume  │ +│ 22  │ Fearow  │ Normal  │ Flying  │ オニドリル │ Onidrill  │ +│ 23  │ Ekans  │ Poison  │   │ アーボ  │ Arbo  │ +│ 24  │ Arbok  │ Poison  │   │ アーボック │ Arbok  │ +│ 25  │ Pikachu  │ Electric  │   │ ピカチュウ │ Pikachu  │ +│ 26  │ Raichu  │ Electric  │   │ ライチュウ │ Raichu  │ +│ 27  │ Sandshrew  │ Ground  │   │ サンド  │ Sand  │ +│ 28  │ Sandslash  │ Ground  │   │ サンドパン │ Sandpan  │ +└────────────┴────────────┴───────────┴───────────┴────────────┴───────────────┘ \ No newline at end of file diff --git a/table/testdata/TestOverwriteStyles/ClearStyles.golden b/table/testdata/TestOverwriteStyles/ClearStyles.golden new file mode 100644 index 000000000..f70a09014 --- /dev/null +++ b/table/testdata/TestOverwriteStyles/ClearStyles.golden @@ -0,0 +1,10 @@ +┌────────────────────────────────────┐ +│RankCity Country Population│ +├────────────────────────────────────┤ +│1 Tokyo Japan 37,274,000│ +│2 Delhi India 32,065,760│ +│3 Shanghai China 28,516,904│ +│4 Dhaka Bangladesh22,478,116│ +│5 São Paulo Brazil 22,429,800│ +│… … … … │ +└────────────────────────────────────┘ \ No newline at end of file diff --git a/table/testdata/TestOverwriteStyles/NewStyles.golden b/table/testdata/TestOverwriteStyles/NewStyles.golden new file mode 100644 index 000000000..3c3dfe81d --- /dev/null +++ b/table/testdata/TestOverwriteStyles/NewStyles.golden @@ -0,0 +1,10 @@ +┌────────────────────────────────────────────┐ +│ Rank  City   Country   Population │ +├────────────────────────────────────────────┤ +│ 1   Tokyo   Japan   37,274,000 │ +│ 2   Delhi   India   32,065,760 │ +│ 3   Shanghai   China   28,516,904 │ +│ 4   Dhaka   Bangladesh  22,478,116 │ +│ 5   São Paulo   Brazil   22,429,800 │ +│ …   …   …   …  │ +└────────────────────────────────────────────┘ \ No newline at end of file diff --git a/table/testdata/TestSetBorder/InvalidNumberOfArguments.golden b/table/testdata/TestSetBorder/InvalidNumberOfArguments.golden new file mode 100644 index 000000000..06932cc5c --- /dev/null +++ b/table/testdata/TestSetBorder/InvalidNumberOfArguments.golden @@ -0,0 +1,10 @@ +┌────────────────────────────────────────────┐ +│ Rank  City   Country   Population │ +├────────────────────────────────────────────┤ +│ 1   Tokyo   Japan   37,274,000 │ +│ 2   Delhi   India   32,065,760 │ +│ 3   Shanghai   China   28,516,904 │ +│ 4   Dhaka   Bangladesh  22,478,116 │ +│ 5   São Paulo   Brazil   22,429,800 │ +│ …   …   …   …  │ +└────────────────────────────────────────────┘ \ No newline at end of file diff --git a/table/testdata/TestSetBorder/NoLeftBorder.golden b/table/testdata/TestSetBorder/NoLeftBorder.golden new file mode 100644 index 000000000..ff3188702 --- /dev/null +++ b/table/testdata/TestSetBorder/NoLeftBorder.golden @@ -0,0 +1,10 @@ +────────────────────────────────────────────┐ + Rank  City   Country   Population │ +────────────────────────────────────────────┤ + 1   Tokyo   Japan   37,274,000 │ + 2   Delhi   India   32,065,760 │ + 3   Shanghai   China   28,516,904 │ + 4   Dhaka   Bangladesh  22,478,116 │ + 5   São Paulo   Brazil   22,429,800 │ + …   …   …   …  │ +────────────────────────────────────────────┘ \ No newline at end of file diff --git a/table/testdata/TestSetBorder/NoTopBorder.golden b/table/testdata/TestSetBorder/NoTopBorder.golden new file mode 100644 index 000000000..07e3bd1e7 --- /dev/null +++ b/table/testdata/TestSetBorder/NoTopBorder.golden @@ -0,0 +1,10 @@ +│ Rank  City   Country   Population │ +├────────────────────────────────────────────┤ +│ 1   Tokyo   Japan   37,274,000 │ +│ 2   Delhi   India   32,065,760 │ +│ 3   Shanghai   China   28,516,904 │ +│ 4   Dhaka   Bangladesh  22,478,116 │ +│ 5   São Paulo   Brazil   22,429,800 │ +│ 6   Mexico City   Mexico   22,085,140 │ +│ …   …   …   …  │ +└────────────────────────────────────────────┘ \ No newline at end of file diff --git a/table/testdata/TestSetBorder/RowSeparatorAndNoRightBorder.golden b/table/testdata/TestSetBorder/RowSeparatorAndNoRightBorder.golden new file mode 100644 index 000000000..b05b56a02 --- /dev/null +++ b/table/testdata/TestSetBorder/RowSeparatorAndNoRightBorder.golden @@ -0,0 +1,9 @@ +┌──────────────────────────────────────────── +│ Rank  City   Country   Population  +├──────────────────────────────────────────── +│ 1   Tokyo   Japan   37,274,000  +├──────────────────────────────────────────── +│ 2   Delhi   India   32,065,760  +├──────────────────────────────────────────── +│ …   …   …   …  +└──────────────────────────────────────────── \ No newline at end of file diff --git a/table/testdata/TestSetBorder/SetAllBorders.golden b/table/testdata/TestSetBorder/SetAllBorders.golden new file mode 100644 index 000000000..93f7321a9 --- /dev/null +++ b/table/testdata/TestSetBorder/SetAllBorders.golden @@ -0,0 +1,9 @@ +┌──────┬──────────────┬────────────┬────────────┐ +│ Rank │ City  │ Country  │ Population │ +├──────┼──────────────┼────────────┼────────────┤ +│ 1  │ Tokyo  │ Japan  │ 37,274,000 │ +├──────┼──────────────┼────────────┼────────────┤ +│ 2  │ Delhi  │ India  │ 32,065,760 │ +├──────┼──────────────┼────────────┼────────────┤ +│ …  │ …  │ …  │ …  │ +└──────┴──────────────┴────────────┴────────────┘ \ No newline at end of file diff --git a/table/testdata/TestSetBorder/SetRowAndColumnSeparators.golden b/table/testdata/TestSetBorder/SetRowAndColumnSeparators.golden new file mode 100644 index 000000000..b52328d51 --- /dev/null +++ b/table/testdata/TestSetBorder/SetRowAndColumnSeparators.golden @@ -0,0 +1,9 @@ + Rank │ City  │ Country  │ Population  +──────┼──────────────┼────────────┼──────────── + 1  │ Tokyo  │ Japan  │ 37,274,000  +──────┼──────────────┼────────────┼──────────── + 2  │ Delhi  │ India  │ 32,065,760  +──────┼──────────────┼────────────┼──────────── + 3  │ Shanghai  │ China  │ 28,516,904  +──────┼──────────────┼────────────┼──────────── + …  │ …  │ …  │ …  \ No newline at end of file diff --git a/table/testdata/TestSetBorder/UnsetAllBorders.golden b/table/testdata/TestSetBorder/UnsetAllBorders.golden new file mode 100644 index 000000000..940f92c38 --- /dev/null +++ b/table/testdata/TestSetBorder/UnsetAllBorders.golden @@ -0,0 +1,10 @@ + Rank  City   Country   Population  +──────────────────────────────────────────── + 1   Tokyo   Japan   37,274,000  + 2   Delhi   India   32,065,760  + 3   Shanghai   China   28,516,904  + 4   Dhaka   Bangladesh  22,478,116  + 5   São Paulo   Brazil   22,429,800  + 6   Mexico City   Mexico   22,085,140  + 7   Cairo   Egypt   21,750,020  + …   …   …   …  \ No newline at end of file diff --git a/table/testdata/TestSetBorder/VerticalBordersOnly.golden b/table/testdata/TestSetBorder/VerticalBordersOnly.golden new file mode 100644 index 000000000..ed68321fc --- /dev/null +++ b/table/testdata/TestSetBorder/VerticalBordersOnly.golden @@ -0,0 +1,10 @@ +──────────────────────────────────────────── + Rank  City   Country   Population  +──────────────────────────────────────────── + 1   Tokyo   Japan   37,274,000  + 2   Delhi   India   32,065,760  + 3   Shanghai   China   28,516,904  + 4   Dhaka   Bangladesh  22,478,116  + 5   São Paulo   Brazil   22,429,800  + …   …   …   …  +──────────────────────────────────────────── \ No newline at end of file diff --git a/table/testdata/TestSetStyleFunc/ClearStylesWithStyleFunc.golden b/table/testdata/TestSetStyleFunc/ClearStylesWithStyleFunc.golden new file mode 100644 index 000000000..175b9d532 --- /dev/null +++ b/table/testdata/TestSetStyleFunc/ClearStylesWithStyleFunc.golden @@ -0,0 +1,10 @@ +┌────────────────────────────────────────────┐ +│ Rank  City   Country   Population │ +├────────────────────────────────────────────┤ +│ 1   Tokyo   Japan   37,274,000 │ +│ 2   Delhi   India   32,065,760 │ +│ 3   Shanghai   China   28,516,904 │ +│ 4   Dhaka   Bangladesh  22,478,116 │ +│ 5   São Paulo   Brazil   22,429,800 │ +│ …   …   …   …  │ +└────────────────────────────────────────────┘ \ No newline at end of file diff --git a/table/testdata/TestSetStyles/EmptyStyles.golden b/table/testdata/TestSetStyles/EmptyStyles.golden new file mode 100644 index 000000000..06932cc5c --- /dev/null +++ b/table/testdata/TestSetStyles/EmptyStyles.golden @@ -0,0 +1,10 @@ +┌────────────────────────────────────────────┐ +│ Rank  City   Country   Population │ +├────────────────────────────────────────────┤ +│ 1   Tokyo   Japan   37,274,000 │ +│ 2   Delhi   India   32,065,760 │ +│ 3   Shanghai   China   28,516,904 │ +│ 4   Dhaka   Bangladesh  22,478,116 │ +│ 5   São Paulo   Brazil   22,429,800 │ +│ …   …   …   …  │ +└────────────────────────────────────────────┘ \ No newline at end of file diff --git a/table/testdata/TestSetStyles/NewStyles.golden b/table/testdata/TestSetStyles/NewStyles.golden new file mode 100644 index 000000000..175b9d532 --- /dev/null +++ b/table/testdata/TestSetStyles/NewStyles.golden @@ -0,0 +1,10 @@ +┌────────────────────────────────────────────┐ +│ Rank  City   Country   Population │ +├────────────────────────────────────────────┤ +│ 1   Tokyo   Japan   37,274,000 │ +│ 2   Delhi   India   32,065,760 │ +│ 3   Shanghai   China   28,516,904 │ +│ 4   Dhaka   Bangladesh  22,478,116 │ +│ 5   São Paulo   Brazil   22,429,800 │ +│ …   …   …   …  │ +└────────────────────────────────────────────┘ \ No newline at end of file diff --git a/table/testdata/TestTableAlignment/NoBorder.golden b/table/testdata/TestTableAlignment/NoBorder.golden new file mode 100644 index 000000000..ed5c59c5f --- /dev/null +++ b/table/testdata/TestTableAlignment/NoBorder.golden @@ -0,0 +1,4 @@ + Name   Country of Origin  Dunk-able  + Chocolate Digestives  UK   Yes  + Tim Tams   Australia   No  + Hobnobs   UK   Yes  \ No newline at end of file diff --git a/table/testdata/TestTableAlignment/No_border.golden b/table/testdata/TestTableAlignment/No_border.golden deleted file mode 100644 index 767f71b67..000000000 --- a/table/testdata/TestTableAlignment/No_border.golden +++ /dev/null @@ -1,5 +0,0 @@ - Name   Country of Orig…  Dunk-able   - Chocolate Digestives   UK   Yes   - Tim Tams   Australia   No   - Hobnobs   UK   Yes   - \ No newline at end of file diff --git a/table/testdata/TestTableAlignment/WithBorder.golden b/table/testdata/TestTableAlignment/WithBorder.golden new file mode 100644 index 000000000..f670d2005 --- /dev/null +++ b/table/testdata/TestTableAlignment/WithBorder.golden @@ -0,0 +1,7 @@ +┌────────────────────────────────────────────────────┐ +│ Name   Country of Origin  Dunk-able │ +├────────────────────────────────────────────────────┤ +│ Chocolate Digestives  UK   Yes  │ +│ Tim Tams   Australia   No  │ +│ Hobnobs   UK   Yes  │ +└────────────────────────────────────────────────────┘ \ No newline at end of file diff --git a/table/testdata/TestTableAlignment/With_border.golden b/table/testdata/TestTableAlignment/With_border.golden deleted file mode 100644 index 64c906a47..000000000 --- a/table/testdata/TestTableAlignment/With_border.golden +++ /dev/null @@ -1,8 +0,0 @@ -┌───────────────────────────────────────────────────────────┐ -│ Name   Country of Orig…  Dunk-able  │ -│───────────────────────────────────────────────────────────│ -│ Chocolate Digestives   UK   Yes  │ -│ Tim Tams   Australia   No  │ -│ Hobnobs   UK   Yes  │ -│ │ -└───────────────────────────────────────────────────────────┘ \ No newline at end of file