From 9c26894e42473901385a5a876c9925c2efa69503 Mon Sep 17 00:00:00 2001 From: Ale Mercado Date: Tue, 31 Mar 2026 16:08:21 -0400 Subject: [PATCH] feat: add --accessible flag to huh interactive prompts --- internal/config/config.go | 1 + internal/config/flags.go | 1 + internal/iostreams/forms.go | 3 ++ internal/iostreams/forms_test.go | 48 ++++++++++++++++++++++++++++++++ 4 files changed, 53 insertions(+) diff --git a/internal/config/config.go b/internal/config/config.go index 9993d742..ba8037c8 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -58,6 +58,7 @@ type Config struct { SlackTestTraceFlag bool TeamFlag string TokenFlag string + Accessible bool NoColor bool // Feature experiments diff --git a/internal/config/flags.go b/internal/config/flags.go index f2180181..d964f083 100644 --- a/internal/config/flags.go +++ b/internal/config/flags.go @@ -37,6 +37,7 @@ func (c *Config) InitializeGlobalFlags(cmd *cobra.Command) { cmd.PersistentFlags().BoolVarP(&c.DeprecatedDevFlag, "dev", "d", false, "use dev apis") // Can be removed after v0.25.0 cmd.PersistentFlags().StringVarP(&c.DeprecatedWorkspaceFlag, "workspace", "", "", "select workspace or organization by domain name or team ID") cmd.PersistentFlags().StringSliceVarP(&c.ExperimentsFlag, "experiment", "e", nil, "use the experiment(s) in the command") + cmd.PersistentFlags().BoolVarP(&c.Accessible, "accessible", "", false, "use accessible prompts for screen readers") cmd.PersistentFlags().BoolVarP(&c.ForceFlag, "force", "f", false, "ignore warnings and continue executing command") cmd.PersistentFlags().BoolVarP(&c.NoColor, "no-color", "", false, "remove styles and formatting from outputs") cmd.PersistentFlags().BoolVarP(&c.SkipUpdateFlag, "skip-update", "s", false, "skip checking for latest version of CLI") diff --git a/internal/iostreams/forms.go b/internal/iostreams/forms.go index 0ee0b78c..b0a2a8b8 100644 --- a/internal/iostreams/forms.go +++ b/internal/iostreams/forms.go @@ -37,6 +37,9 @@ func newForm(io *IOStreams, field huh.Field) *huh.Form { } else { form = form.WithTheme(style.ThemeSurvey()) } + if io != nil && io.config.Accessible { + form = form.WithAccessible(true) + } return form } diff --git a/internal/iostreams/forms_test.go b/internal/iostreams/forms_test.go index 31df93e2..319c305a 100644 --- a/internal/iostreams/forms_test.go +++ b/internal/iostreams/forms_test.go @@ -415,6 +415,54 @@ func TestFormsUseSlackTheme(t *testing.T) { }) } +func TestFormsAccessible(t *testing.T) { + fsMock := slackdeps.NewFsMock() + osMock := slackdeps.NewOsMock() + osMock.AddDefaultMocks() + cfg := config.NewConfig(fsMock, osMock) + cfg.Accessible = true + io := NewIOStreams(cfg, fsMock, osMock) + + t.Run("select form accepts valid numbered input", func(t *testing.T) { + var selected string + f := buildSelectForm(io, "Pick one", []string{"A", "B", "C"}, SelectPromptConfig{}, &selected) + + var out strings.Builder + err := f.WithOutput(&out).WithInput(strings.NewReader("2\n")).Run() + + assert.NoError(t, err) + assert.Equal(t, "B", selected) + assert.Contains(t, out.String(), "1. A") + assert.Contains(t, out.String(), "2. B") + assert.Contains(t, out.String(), "3. C") + assert.Contains(t, out.String(), "Enter a number between 1 and 3") + }) + + t.Run("confirm form accepts yes/no input", func(t *testing.T) { + var choice bool + f := buildConfirmForm(io, "Continue?", &choice) + + var out strings.Builder + err := f.WithOutput(&out).WithInput(strings.NewReader("y\n")).Run() + + assert.NoError(t, err) + assert.True(t, choice) + assert.Contains(t, out.String(), "Continue?") + }) + + t.Run("input form accepts text input", func(t *testing.T) { + var input string + f := buildInputForm(io, "Name?", InputPromptConfig{}, &input) + + var out strings.Builder + err := f.WithOutput(&out).WithInput(strings.NewReader("my-app\n")).Run() + + assert.NoError(t, err) + assert.Equal(t, "my-app", input) + assert.Contains(t, out.String(), "Name?") + }) +} + func TestFormsUseSurveyTheme(t *testing.T) { t.Run("multi-select uses survey prefix without lipgloss", func(t *testing.T) { var selected []string