From 69bf367d378dffb88d744bd0d078db344768660b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leandro=20L=C3=B3pez?= Date: Fri, 1 Jul 2022 12:31:26 -0300 Subject: [PATCH] feat(spinners): Construct new spinners with `WithSpinner` + `WithStyle` options (#148) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add spinner.New test Signed-off-by: Leandro López (inkel) * Add spinner.Option type and spinner.WithSpinner option Signed-off-by: Leandro López (inkel) * Allow passing options in spinner.New This doesn't break existing code as it uses variadic arguments, so any existing code as the following should continue to work: s := spinner.New() s.spinner = spinner.Dot This change allows for instead of those two lines, having a call: s := spinner.New(spinner.WithSpinner(spinner.Dot)) Signed-off-by: Leandro López (inkel) * Add spinner.WithX option for each spinner.Spinner Signed-off-by: Leandro López (inkel) * Refactor spinner tests Signed-off-by: Leandro López (inkel) * Add spinner.WithStyle option function Signed-off-by: Leandro López (inkel) * refactor: remove With... Spinner aliases Co-authored-by: Maas Lalani --- spinner/spinner.go | 30 ++++++++++++++++++-- spinner/spinner_test.go | 61 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 spinner/spinner_test.go diff --git a/spinner/spinner.go b/spinner/spinner.go index ced6b1f..e034112 100644 --- a/spinner/spinner.go +++ b/spinner/spinner.go @@ -109,11 +109,17 @@ func (m Model) ID() int { } // New returns a model with default values. -func New() Model { - return Model{ +func New(opts ...Option) Model { + m := Model{ Spinner: Line, id: nextID(), } + + for _, opt := range opts { + opt(&m) + } + + return m } // NewModel returns a model with default values. @@ -199,3 +205,23 @@ func (m Model) tick(id, tag int) tea.Cmd { func Tick() tea.Msg { return TickMsg{Time: time.Now()} } + +// Option is used to set options in New. For example: +// +// spinner := New(WithSpinner(Dot)) +// +type Option func(*Model) + +// WithSpinner is an option to set the spinner. +func WithSpinner(spinner Spinner) Option { + return func(m *Model) { + m.Spinner = spinner + } +} + +// WithStyle is an option to set the spinner style. +func WithStyle(style lipgloss.Style) Option { + return func(m *Model) { + m.Style = style + } +} diff --git a/spinner/spinner_test.go b/spinner/spinner_test.go new file mode 100644 index 0000000..959200b --- /dev/null +++ b/spinner/spinner_test.go @@ -0,0 +1,61 @@ +package spinner_test + +import ( + "testing" + + "github.com/charmbracelet/bubbles/spinner" +) + +func TestSpinnerNew(t *testing.T) { + assertEqualSpinner := func(t *testing.T, exp, got spinner.Spinner) { + t.Helper() + + if exp.FPS != got.FPS { + t.Errorf("expecting %d FPS, got %d", exp.FPS, got.FPS) + } + + if e, g := len(exp.Frames), len(got.Frames); e != g { + t.Fatalf("expecting %d frames, got %d", e, g) + } + + for i, e := range exp.Frames { + if g := got.Frames[i]; e != g { + t.Errorf("expecting frame index %d with value %q, got %q", i, e, g) + } + } + } + t.Run("default", func(t *testing.T) { + s := spinner.New() + + assertEqualSpinner(t, spinner.Line, s.Spinner) + }) + + t.Run("WithSpinner", func(t *testing.T) { + customSpinner := spinner.Spinner{ + Frames: []string{"a", "b", "c", "d"}, + FPS: 16, + } + + s := spinner.New(spinner.WithSpinner(customSpinner)) + + assertEqualSpinner(t, customSpinner, s.Spinner) + }) + + tests := map[string]spinner.Spinner{ + "Line": spinner.Line, + "Dot": spinner.Dot, + "MiniDot": spinner.MiniDot, + "Jump": spinner.Jump, + "Pulse": spinner.Pulse, + "Points": spinner.Points, + "Globe": spinner.Globe, + "Moon": spinner.Moon, + "Monkey": spinner.Monkey, + } + + for name, s := range tests { + t.Run(name, func(t *testing.T) { + assertEqualSpinner(t, spinner.New(spinner.WithSpinner(s)).Spinner, s) + }) + } +}