diff --git a/list/list.go b/list/list.go index 17e6e15..00d27a0 100644 --- a/list/list.go +++ b/list/list.go @@ -10,15 +10,16 @@ import ( "strings" "time" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" + "github.com/charmbracelet/x/ansi" + "github.com/sahilm/fuzzy" + "github.com/charmbracelet/bubbles/help" "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/paginator" "github.com/charmbracelet/bubbles/spinner" "github.com/charmbracelet/bubbles/textinput" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" - "github.com/charmbracelet/x/ansi" - "github.com/sahilm/fuzzy" ) // Item is an item that appears in the list. @@ -269,6 +270,34 @@ func (m *Model) SetShowTitle(v bool) { m.updatePagination() } +// SetFilterText explicitly sets the filter text without relying on user input. +// It also sets the filterState to a sane default of FilterApplied, but this +// can be changed with SetFilterState +func (m *Model) SetFilterText(filter string) { + m.filterState = Filtering + m.FilterInput.SetValue(filter) + cmd := filterItems(*m) + msg := cmd() + fmm, _ := msg.(FilterMatchesMsg) + m.filteredItems = filteredItems(fmm) + m.filterState = FilterApplied + m.Paginator.Page = 0 + m.cursor = 0 + m.FilterInput.CursorEnd() + m.updatePagination() + m.updateKeybindings() +} + +// Helper method for setting the filtering state manually +func (m *Model) SetFilterState(state FilterState) { + m.Paginator.Page = 0 + m.cursor = 0 + m.filterState = state + m.FilterInput.CursorEnd() + m.FilterInput.Focus() + m.updateKeybindings() +} + // ShowTitle returns whether or not the title bar is set to be rendered. func (m Model) ShowTitle() bool { return m.showTitle diff --git a/list/list_test.go b/list/list_test.go index 2c925aa..2627e5b 100644 --- a/list/list_test.go +++ b/list/list_test.go @@ -3,6 +3,7 @@ package list import ( "fmt" "io" + "reflect" "strings" "testing" @@ -11,7 +12,7 @@ import ( type item string -func (i item) FilterValue() string { return "" } +func (i item) FilterValue() string { return string(i) } type itemDelegate struct{} @@ -72,3 +73,65 @@ func TestCustomStatusBarItemName(t *testing.T) { t.Fatalf("Error: expected view to contain %s", expected) } } + +func TestSetFilterText(t *testing.T) { + tc := []Item{item("foo"), item("bar"), item("baz")} + + list := New(tc, itemDelegate{}, 10, 10) + list.SetFilterText("ba") + + list.SetFilterState(Unfiltered) + expected := tc + // TODO: replace with slices.Equal() when project move to go1.18 or later + if !reflect.DeepEqual(list.VisibleItems(), expected) { + t.Fatalf("Error: expected view to contain only %s", expected) + } + + list.SetFilterState(Filtering) + expected = []Item{item("bar"), item("baz")} + if !reflect.DeepEqual(list.VisibleItems(), expected) { + t.Fatalf("Error: expected view to contain only %s", expected) + } + + list.SetFilterState(FilterApplied) + if !reflect.DeepEqual(list.VisibleItems(), expected) { + t.Fatalf("Error: expected view to contain only %s", expected) + } +} + +func TestSetFilterState(t *testing.T) { + tc := []Item{item("foo"), item("bar"), item("baz")} + + list := New(tc, itemDelegate{}, 10, 10) + list.SetFilterText("ba") + + list.SetFilterState(Unfiltered) + expected, notExpected := "up", "clear filter" + + lines := strings.Split(list.View(), "\n") + footer := lines[len(lines)-1] + + if !strings.Contains(footer, expected) || strings.Contains(footer, notExpected) { + t.Fatalf("Error: expected view to contain '%s' not '%s'", expected, notExpected) + } + + list.SetFilterState(Filtering) + expected, notExpected = "filter", "more" + + lines = strings.Split(list.View(), "\n") + footer = lines[len(lines)-1] + + if !strings.Contains(footer, expected) || strings.Contains(footer, notExpected) { + t.Fatalf("Error: expected view to contain '%s' not '%s'", expected, notExpected) + } + + list.SetFilterState(FilterApplied) + expected = "clear" + + lines = strings.Split(list.View(), "\n") + footer = lines[len(lines)-1] + + if !strings.Contains(footer, expected) { + t.Fatalf("Error: expected view to contain '%s'", expected) + } +}