diff --git a/bubbletea/bubbletea.go b/bubbletea/bubbletea.go index 84d67c9..ec7a22c 100644 --- a/bubbletea/bubbletea.go +++ b/bubbletea/bubbletea.go @@ -9,8 +9,14 @@ import ( // Model represents the component that wraps the `bubbletea` Model interface. type Model interface { - // tea.Model is the base interface. - tea.Model + // Init initializes the model and returns the next tea command. + Init() tea.Cmd + + // Update updates the model and returns the same model and the next tea command. + Update(msg tea.Msg) (Model, tea.Cmd) + + // View returns the model view representation. + View() string // Run runs and returns its result. Run(modelResult any) (any, error) @@ -35,12 +41,14 @@ func (b BubbleTea) Init() tea.Cmd { // Update is the `BubbleTea` method required for implementing the `Model` interface. func (b BubbleTea) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - return b.currentModel.Update(msg) + model, cmd := b.currentModel.Update(msg) + b.currentModel = model + return b, cmd } // View is the `BubbleTea` method required for implementing the `Model` interface. func (b BubbleTea) View() string { - return b.baseStyle.Render(b.currentModel.View()) + "\n" + return b.currentModel.View() } // Run runs the `BubbleTea` component and returns its result. diff --git a/bubbletea/bubbletea_update_test.go b/bubbletea/bubbletea_update_test.go index 2bad46b..b2d2635 100644 --- a/bubbletea/bubbletea_update_test.go +++ b/bubbletea/bubbletea_update_test.go @@ -14,7 +14,7 @@ func TestBubbleTeaUpdate(t *testing.T) { subTestName string initialBubbletea tea.Model updateParams tea.Msg - expectedModel *ChoicesModel + expectedModel *BubbleTea expectedCmd tea.Cmd }{ { @@ -24,7 +24,8 @@ func TestBubbleTeaUpdate(t *testing.T) { BaseStyle: NewBaseStyle(), }), updateParams: nil, - expectedModel: NewChoicesModel(&ChoicesModelParams{ + expectedModel: New(&Params{ + Model: NewChoicesModel(&ChoicesModelParams{}), BaseStyle: NewBaseStyle(), }), expectedCmd: (tea.Cmd)(nil), @@ -38,7 +39,8 @@ func TestBubbleTeaUpdate(t *testing.T) { updateParams: tea.KeyMsg{ Type: tea.KeyCtrlC, }, - expectedModel: NewChoicesModel(&ChoicesModelParams{ + expectedModel: New(&Params{ + Model: NewChoicesModel(&ChoicesModelParams{}), BaseStyle: NewBaseStyle(), }), expectedCmd: tea.Quit, @@ -54,10 +56,13 @@ func TestBubbleTeaUpdate(t *testing.T) { updateParams: tea.KeyMsg{ Type: tea.KeyEnter, }, - expectedModel: NewChoicesModel(&ChoicesModelParams{ - Cursor: 0, - Choices: []string{"test-choice-0"}, - Choice: "test-choice-0", + expectedModel: New(&Params{ + Model: NewChoicesModel(&ChoicesModelParams{ + Cursor: 0, + Choices: []string{"test-choice-0"}, + Choice: "test-choice-0", + BaseStyle: NewBaseStyle(), + }), BaseStyle: NewBaseStyle(), }), expectedCmd: tea.Quit, @@ -74,9 +79,11 @@ func TestBubbleTeaUpdate(t *testing.T) { Type: tea.KeyRunes, Runes: []rune("j"), }, - expectedModel: NewChoicesModel(&ChoicesModelParams{ - Cursor: 1, - Choices: []string{"test-choice-0", "test-choice-1"}, + expectedModel: New(&Params{ + Model: NewChoicesModel(&ChoicesModelParams{ + Cursor: 1, + Choices: []string{"test-choice-0", "test-choice-1"}, + }), BaseStyle: NewBaseStyle(), }), expectedCmd: nil, @@ -94,9 +101,11 @@ func TestBubbleTeaUpdate(t *testing.T) { Type: tea.KeyRunes, Runes: []rune("j"), }, - expectedModel: NewChoicesModel(&ChoicesModelParams{ - Cursor: 0, - Choices: []string{"test-choice-0", "test-choice-1"}, + expectedModel: New(&Params{ + Model: NewChoicesModel(&ChoicesModelParams{ + Cursor: 0, + Choices: []string{"test-choice-0", "test-choice-1"}, + }), BaseStyle: NewBaseStyle(), }), expectedCmd: nil, @@ -114,9 +123,11 @@ func TestBubbleTeaUpdate(t *testing.T) { Type: tea.KeyRunes, Runes: []rune("k"), }, - expectedModel: NewChoicesModel(&ChoicesModelParams{ - Cursor: 0, - Choices: []string{"test-choice-0", "test-choice-1"}, + expectedModel: New(&Params{ + Model: NewChoicesModel(&ChoicesModelParams{ + Cursor: 0, + Choices: []string{"test-choice-0", "test-choice-1"}, + }), BaseStyle: NewBaseStyle(), }), expectedCmd: nil, @@ -134,9 +145,11 @@ func TestBubbleTeaUpdate(t *testing.T) { Type: tea.KeyRunes, Runes: []rune("k"), }, - expectedModel: NewChoicesModel(&ChoicesModelParams{ - Cursor: 0, - Choices: []string{"test-choice-0"}, + expectedModel: New(&Params{ + Model: NewChoicesModel(&ChoicesModelParams{ + Cursor: 0, + Choices: []string{"test-choice-0"}, + }), BaseStyle: NewBaseStyle(), }), expectedCmd: nil, diff --git a/bubbletea/bubbletea_view_test.go b/bubbletea/bubbletea_view_test.go index 0c8d127..b690b20 100644 --- a/bubbletea/bubbletea_view_test.go +++ b/bubbletea/bubbletea_view_test.go @@ -24,7 +24,7 @@ func TestBubbleTeaView(t *testing.T) { }), BaseStyle: NewBaseStyle(), }), - expectedView: "┌────────────────────────────────────────────────────────────────────────────────┐\n│ │\n│ ┌──────────────────────────────────────────────────────────────────────────────│\n│ ──┐ │\n│ │ │\n│ │ │\n│ │ test-header: │\n│ │ │\n│ │ (•) test-choice-0 │\n│ │ │\n│ │ │\n│ │ │\n│ │ (press q to quit) │\n│ │ │\n│ │ │\n│ │ │\n│ └──────────────────────────────────────────────────────────────────────────────│\n│ ──┘ │\n│ │\n└────────────────────────────────────────────────────────────────────────────────┘\n", + expectedView: "┌────────────────────────────────────────────────────────────────────────────────┐\n│ │\n│ test-header: │\n│ (•) test-choice-0 │\n│ │\n│ (press q to quit) │\n│ │\n└────────────────────────────────────────────────────────────────────────────────┘\n", }, { subTestName: "Handles success view result with multiple choices", @@ -37,7 +37,7 @@ func TestBubbleTeaView(t *testing.T) { }), BaseStyle: NewBaseStyle(), }), - expectedView: "┌────────────────────────────────────────────────────────────────────────────────┐\n│ │\n│ ┌──────────────────────────────────────────────────────────────────────────────│\n│ ──┐ │\n│ │ │\n│ │ │\n│ │ test-header: │\n│ │ │\n│ │ (•) test-choice-0 │\n│ │ │\n│ │ ( ) test-choice-1 │\n│ │ │\n│ │ │\n│ │ │\n│ │ (press q to quit) │\n│ │ │\n│ │ │\n│ │ │\n│ └──────────────────────────────────────────────────────────────────────────────│\n│ ──┘ │\n│ │\n└────────────────────────────────────────────────────────────────────────────────┘\n", + expectedView: "┌────────────────────────────────────────────────────────────────────────────────┐\n│ │\n│ test-header: │\n│ (•) test-choice-0 │\n│ ( ) test-choice-1 │\n│ │\n│ (press q to quit) │\n│ │\n└────────────────────────────────────────────────────────────────────────────────┘\n", }, } diff --git a/bubbletea/choicesmodel.go b/bubbletea/choicesmodel.go index 2c4746f..78ccb44 100644 --- a/bubbletea/choicesmodel.go +++ b/bubbletea/choicesmodel.go @@ -3,7 +3,6 @@ package bubbletea import ( "strings" - "github.com/charmbracelet/bubbles/table" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" ) @@ -22,10 +21,6 @@ type ChoicesModel struct { // ui is the UI of the CLI. ui ChoicesModelUI - // table is the bubbletea table model. - // TODO(@chris-ramon): Make it available through multiple model support. - table table.Model //nolint:golint,unused - // baseStyle is the base styling of the BubbleTea component. baseStyle lipgloss.Style } @@ -42,7 +37,7 @@ func (b ChoicesModel) Init() tea.Cmd { } // Update is the `BubbleTea` method required for implementing the `Model` interface. -func (b ChoicesModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { //nolint:golint,ireturn +func (b *ChoicesModel) Update(msg tea.Msg) (Model, tea.Cmd) { //nolint:golint,ireturn keyMsg, ok := msg.(tea.KeyMsg) if !ok { return b, nil diff --git a/bubbletea/tablemodel.go b/bubbletea/tablemodel.go index d0634cb..5ed8c14 100644 --- a/bubbletea/tablemodel.go +++ b/bubbletea/tablemodel.go @@ -8,12 +8,12 @@ import ( // `TableModel` represents the component that implements the `Model` interface. type TableModel struct { + // baseStyle is the base styling for the `table` model. + baseStyle lipgloss.Style + // table is the `bubbletea` table model. // TODO(@chris-ramon): Wire to the `TableModel` component. table table.Model - - // baseStyle is the base styling for the `table` model. - baseStyle lipgloss.Style } // `Init` is the `TableModel` method required for implementing the `Model` interface. @@ -84,7 +84,18 @@ type TableModelParams struct { // `NewTableModel` returns a pointer to a `TableModel`. func NewTableModel(p *TableModelParams) *TableModel { + columns := []table.Column{} + rows := []table.Row{} + + t := table.New( + table.WithColumns(columns), + table.WithRows(rows), + table.WithFocused(true), + table.WithHeight(7), + ) + return &TableModel{ baseStyle: p.BaseStyle, + table: t, } }