Skip to content
This repository has been archived by the owner on Apr 19, 2024. It is now read-only.

Commit

Permalink
Add a RemoveSelectAll and RemoveSelectNone config to multi-select (#439)
Browse files Browse the repository at this point in the history
* Add a RemoveSelectAll config to multi-select

* AddRemoveSelectNone config

* Remove boolean from Remove methods
  • Loading branch information
ces131 committed Sep 9, 2022
1 parent e47352f commit 55474c3
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 12 deletions.
33 changes: 33 additions & 0 deletions README.md
Expand Up @@ -357,6 +357,39 @@ All of the prompts have a `Help` field which can be defined to provide more info
}
```

## Removing the "Select All" and "Select None" options

By default, users can select all of the multi-select options using the right arrow key. To prevent users from being able to do this (and remove the `<right> to all` message from the prompt), use the option `WithRemoveSelectAll`:

```golang
import (
"github.com/AlecAivazis/survey/v2"
)

number := ""
prompt := &survey.Input{
Message: "This question has the select all option removed",
}

survey.AskOne(prompt, &number, survey.WithRemoveSelectAll())
```

Also by default, users can use the left arrow key to unselect all of the options. To prevent users from being able to do this (and remove the `<left> to none` message from the prompt), use the option `WithRemoveSelectNone`:

```golang
import (
"github.com/AlecAivazis/survey/v2"
)

number := ""
prompt := &survey.Input{
Message: "This question has the select all option removed",
}

survey.AskOne(prompt, &number, survey.WithRemoveSelectNone())
```


### Changing the input rune

In some situations, `?` is a perfectly valid response. To handle this, you can change the rune that survey
Expand Down
6 changes: 3 additions & 3 deletions multiselect.go
Expand Up @@ -71,7 +71,7 @@ var MultiSelectQuestionTemplate = `
{{- color "default+hb"}}{{ .Message }}{{ .FilterMessage }}{{color "reset"}}
{{- if .ShowAnswer}}{{color "cyan"}} {{.Answer}}{{color "reset"}}{{"\n"}}
{{- else }}
{{- " "}}{{- color "cyan"}}[Use arrows to move, space to select, <right> to all, <left> to none, type to filter{{- if and .Help (not .ShowHelp)}}, {{ .Config.HelpInput }} for more help{{end}}]{{color "reset"}}
{{- " "}}{{- color "cyan"}}[Use arrows to move, space to select,{{- if not .Config.RemoveSelectAll }} <right> to all,{{end}}{{- if not .Config.RemoveSelectNone }} <left> to none,{{end}} type to filter{{- if and .Help (not .ShowHelp)}}, {{ .Config.HelpInput }} for more help{{end}}]{{color "reset"}}
{{- "\n"}}
{{- range $ix, $option := .PageEntries}}
{{- template "option" $.IterateOption $ix $option}}
Expand Down Expand Up @@ -134,14 +134,14 @@ func (m *MultiSelect) OnChange(key rune, config *PromptConfig) {
} else if key >= terminal.KeySpace {
m.filter += string(key)
m.VimMode = false
} else if key == terminal.KeyArrowRight {
} else if !config.RemoveSelectAll && key == terminal.KeyArrowRight {
for _, v := range options {
m.checked[v.Index] = true
}
if !config.KeepFilter {
m.filter = ""
}
} else if key == terminal.KeyArrowLeft {
} else if !config.RemoveSelectNone && key == terminal.KeyArrowLeft {
for _, v := range options {
m.checked[v.Index] = false
}
Expand Down
66 changes: 66 additions & 0 deletions multiselect_test.go
Expand Up @@ -579,3 +579,69 @@ func TestMultiSelectPromptKeepFilter(t *testing.T) {
})
}
}

func TestMultiSelectPromptRemoveSelectAll(t *testing.T) {
tests := []PromptTest{
{
"multi select with remove select all option",
&MultiSelect{
Message: "What color do you prefer:",
Options: []string{"green", "red", "light-green", "blue", "black", "yellow", "purple"},
},
func(c expectConsole) {
c.ExpectString("What color do you prefer: [Use arrows to move, space to select, <left> to none, type to filter]")
// Select the first option "green"
c.Send(" ")

// attempt to select all (this shouldn't do anything)
c.Send(string(terminal.KeyArrowRight))

// end the session
c.SendLine("")
c.ExpectEOF()
},
[]core.OptionAnswer{ // we should only have one option selected, not all of them
{Value: "green", Index: 0},
},
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
RunPromptTestRemoveSelectAll(t, test)
})
}
}

func TestMultiSelectPromptRemoveSelectNone(t *testing.T) {
tests := []PromptTest{
{
"multi select with remove select none option",
&MultiSelect{
Message: "What color do you prefer:",
Options: []string{"green", "red", "light-green", "blue", "black", "yellow", "purple"},
},
func(c expectConsole) {
c.ExpectString("What color do you prefer: [Use arrows to move, space to select, <right> to all, type to filter]")
// Select the first option "green"
c.Send(" ")

// attempt to unselect all (this shouldn't do anything)
c.Send(string(terminal.KeyArrowLeft))

// end the session
c.SendLine("")
c.ExpectEOF()
},
[]core.OptionAnswer{ // we should only have one option selected, not all of them
{Value: "green", Index: 0},
},
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
RunPromptTestRemoveSelectNone(t, test)
})
}
}
38 changes: 29 additions & 9 deletions survey.go
Expand Up @@ -56,8 +56,10 @@ func defaultAskOptions() *AskOptions {
// include this option if it matches
return strings.Contains(strings.ToLower(value), filter)
},
KeepFilter: false,
ShowCursor: false,
KeepFilter: false,
ShowCursor: false,
RemoveSelectAll: false,
RemoveSelectNone: false,
},
}
}
Expand Down Expand Up @@ -111,13 +113,15 @@ type Question struct {

// PromptConfig holds the global configuration for a prompt
type PromptConfig struct {
PageSize int
Icons IconSet
HelpInput string
SuggestInput string
Filter func(filter string, option string, index int) bool
KeepFilter bool
ShowCursor bool
PageSize int
Icons IconSet
HelpInput string
SuggestInput string
Filter func(filter string, option string, index int) bool
KeepFilter bool
ShowCursor bool
RemoveSelectAll bool
RemoveSelectNone bool
}

// Prompt is the primary interface for the objects that can take user input
Expand Down Expand Up @@ -175,6 +179,22 @@ func WithKeepFilter(KeepFilter bool) AskOpt {
}
}

// WithRemoveSelectAll remove the select all option in Multiselect
func WithRemoveSelectAll() AskOpt {
return func(options *AskOptions) error {
options.PromptConfig.RemoveSelectAll = true
return nil
}
}

// WithRemoveSelectNone remove the select none/unselect all in Multiselect
func WithRemoveSelectNone() AskOpt {
return func(options *AskOptions) error {
options.PromptConfig.RemoveSelectNone = true
return nil
}
}

// WithValidator specifies a validator to use while prompting the user
func WithValidator(v Validator) AskOpt {
return func(options *AskOptions) error {
Expand Down
32 changes: 32 additions & 0 deletions survey_test.go
Expand Up @@ -97,6 +97,38 @@ func RunPromptTestKeepFilter(t *testing.T, test PromptTest) {
require.Equal(t, test.expected, answer)
}

func RunPromptTestRemoveSelectAll(t *testing.T, test PromptTest) {
t.Helper()
var answer interface{}
RunTest(t, test.procedure, func(stdio terminal.Stdio) error {
var err error
if p, ok := test.prompt.(wantsStdio); ok {
p.WithStdio(stdio)
}
config := defaultPromptConfig()
config.RemoveSelectAll = true
answer, err = test.prompt.Prompt(config)
return err
})
require.Equal(t, test.expected, answer)
}

func RunPromptTestRemoveSelectNone(t *testing.T, test PromptTest) {
t.Helper()
var answer interface{}
RunTest(t, test.procedure, func(stdio terminal.Stdio) error {
var err error
if p, ok := test.prompt.(wantsStdio); ok {
p.WithStdio(stdio)
}
config := defaultPromptConfig()
config.RemoveSelectNone = true
answer, err = test.prompt.Prompt(config)
return err
})
require.Equal(t, test.expected, answer)
}

func TestPagination_tooFew(t *testing.T) {
// a small list of options
choices := core.OptionAnswerList([]string{"choice1", "choice2", "choice3"})
Expand Down

0 comments on commit 55474c3

Please sign in to comment.