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

Add option to remove filters from select-type prompts #463

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,8 @@ survey.AskOne(prompt, &content)
By default, the user can filter for options in Select and MultiSelects by typing while the prompt
is active. This will filter out all options that don't contain the typed string anywhere in their name, ignoring case.

### Defining a custom filter

A custom filter function can also be provided to change this behavior:

```golang
Expand All @@ -287,7 +289,7 @@ func myFilter(filterValue string, optValue string, optIndex int) bool {
survey.AskOne(prompt, &color, survey.WithFilter(myFilter))
```

## Keeping the filter active
### Keeping the filter active

By default the filter will disappear if the user selects one of the filtered elements. Once the user selects one element the filter setting is gone.

Expand All @@ -305,6 +307,20 @@ However the user can prevent this from happening and keep the filter active for
survey.AskOne(prompt, &color, survey.WithKeepFilter(true))
```

### Removing filters

Filtering can be removed from a prompt with the `HideFilter` option, restricting selection movements to only the arrow keys, as follows:

```golang
prompt := &survey.Select{
Message: "Choose a color:",
Options: []string{"red", "blue", "green"},
HideFilter: true,
}

survey.AskOne(prompt, &color)
```

## Validation

Validating individual responses for a particular question can be done by defining a
Expand Down
16 changes: 10 additions & 6 deletions multiselect.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type MultiSelect struct {
VimMode bool
FilterMessage string
Filter func(filter string, value string, index int) bool
HideFilter bool
Description func(value string, index int) string
filter string
selectedIndex int
Expand All @@ -39,13 +40,14 @@ type MultiSelect struct {
// data available to the templates when processing
type MultiSelectTemplateData struct {
MultiSelect
Answer string
ShowAnswer bool
PageEntries []core.OptionAnswer
Checked map[int]bool
SelectedIndex int
Answer string
ShowAnswer bool
Comment on lines -43 to +47
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was rearranged to better match select.go

ShowHelp bool
HideFilter bool
Description func(value string, index int) string
PageEntries []core.OptionAnswer
Config *PromptConfig

// These fields are used when rendering an individual option
Expand Down Expand Up @@ -77,10 +79,10 @@ var MultiSelectQuestionTemplate = `
{{end}}
{{- if .ShowHelp }}{{- color .Config.Icons.Help.Format }}{{ .Config.Icons.Help.Text }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}}
{{- color .Config.Icons.Question.Format }}{{ .Config.Icons.Question.Text }} {{color "reset"}}
{{- color "default+hb"}}{{ .Message }}{{ .FilterMessage }}{{color "reset"}}
{{- color "default+hb"}}{{ .Message }}{{- if not .HideFilter }}{{ .FilterMessage }}{{end}}{{color "reset"}}
{{- if .ShowAnswer}}{{color "cyan"}} {{.Answer}}{{color "reset"}}{{"\n"}}
{{- else }}
{{- " "}}{{- 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"}}
{{- " "}}{{- 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}}{{- if not .HideFilter }}, type to filter{{end}}{{- 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 @@ -140,7 +142,7 @@ func (m *MultiSelect) OnChange(key rune, config *PromptConfig) {
runeFilter := []rune(m.filter)
m.filter = string(runeFilter[0 : len(runeFilter)-1])
}
} else if key >= terminal.KeySpace {
} else if key >= terminal.KeySpace && !m.HideFilter {
m.filter += string(key)
m.VimMode = false
} else if !config.RemoveSelectAll && key == terminal.KeyArrowRight {
Expand Down Expand Up @@ -188,6 +190,7 @@ func (m *MultiSelect) OnChange(key rune, config *PromptConfig) {
SelectedIndex: idx,
Checked: m.checked,
ShowHelp: m.showingHelp,
HideFilter: m.HideFilter,
Description: m.Description,
PageEntries: opts,
Config: config,
Expand Down Expand Up @@ -282,6 +285,7 @@ func (m *MultiSelect) Prompt(config *PromptConfig) (interface{}, error) {
tmplData := MultiSelectTemplateData{
MultiSelect: *m,
SelectedIndex: idx,
HideFilter: m.HideFilter,
Description: m.Description,
Checked: m.checked,
PageEntries: opts,
Expand Down
109 changes: 109 additions & 0 deletions multiselect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,115 @@ func TestMultiSelectPromptKeepFilter(t *testing.T) {
}
}

func TestMultiSelectPromptHideFilter(t *testing.T) {
tests := []PromptTest{
{
"attempt to filter and select end options when the filter is hidden",
&MultiSelect{
Message: "What color do you prefer:",
Options: []string{"green", "red", "blue", "yellow"},
HideFilter: true,
},
func(c expectConsole) {
c.ExpectString("What color do you prefer: [Use arrows to move, space to select, <right> to all, <left> to none]")
// Attempt to filter "blue" and "yellow"
c.Send("l")
// Select "green"
c.Send(" ")
// Select "red"
c.Send(string(terminal.KeyArrowDown))
c.Send(" ")
c.SendLine("")
c.ExpectEOF()
},
[]core.OptionAnswer{
{Value: "green", Index: 0},
{Value: "red", Index: 1},
},
},
{
"filter and select end options when the filter is clearly not hidden",
&MultiSelect{
Message: "What color do you prefer:",
Options: []string{"green", "red", "blue", "yellow"},
HideFilter: false,
},
func(c expectConsole) {
c.ExpectString("What color do you prefer: [Use arrows to move, space to select, <right> to all, <left> to none, type to filter]")
// Filter "blue" and "yellow"
c.Send("l")
// Select "blue"
c.Send(" ")
// Select "yellow"
c.Send(string(terminal.KeyArrowDown))
c.Send(" ")
c.SendLine("")
c.ExpectEOF()
},
[]core.OptionAnswer{
{Value: "blue", Index: 2},
{Value: "yellow", Index: 3},
},
},
{
"filter and select end options using the default filter behavior",
&MultiSelect{
Message: "What color do you prefer:",
Options: []string{"green", "red", "blue", "yellow"},
},
func(c expectConsole) {
c.ExpectString("What color do you prefer: [Use arrows to move, space to select, <right> to all, <left> to none, type to filter]")
// Filter "blue" and "yellow"
c.Send("l")
// Select "blue"
c.Send(" ")
// Select "yellow"
c.Send(string(terminal.KeyArrowDown))
c.Send(" ")
c.SendLine("")
c.ExpectEOF()
},
[]core.OptionAnswer{
{Value: "blue", Index: 2},
{Value: "yellow", Index: 3},
},
},
{
"verify that help text is shown when requested, even if the filter is hidden",
&MultiSelect{
Message: "What color do you prefer:",
Options: []string{"green", "red", "blue", "yellow"},
Help: "We all have a favorite :)",
HideFilter: true,
},
func(c expectConsole) {
c.ExpectString("What color do you prefer: [Use arrows to move, space to select, <right> to all, <left> to none, ? for more help]")
// Display help message
c.Send("?")
c.ExpectString("We all have a favorite :)")
// Select "green"
c.Send(" ")
// Select "blue"
c.Send(string(terminal.KeyArrowDown))
c.Send(string(terminal.KeyArrowDown))
c.Send(" ")
c.SendLine("")
c.ExpectEOF()
},
[]core.OptionAnswer{
{Value: "green", Index: 0},
{Value: "blue", Index: 2},
},
},
}

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

func TestMultiSelectPromptRemoveSelectAll(t *testing.T) {
tests := []PromptTest{
{
Expand Down
12 changes: 8 additions & 4 deletions select.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type Select struct {
VimMode bool
FilterMessage string
Filter func(filter string, value string, index int) bool
HideFilter bool
Description func(value string, index int) string
filter string
selectedIndex int
Expand All @@ -43,6 +44,7 @@ type SelectTemplateData struct {
Answer string
ShowAnswer bool
ShowHelp bool
HideFilter bool
Description func(value string, index int) string
Config *PromptConfig

Expand Down Expand Up @@ -74,10 +76,10 @@ var SelectQuestionTemplate = `
{{end}}
{{- if .ShowHelp }}{{- color .Config.Icons.Help.Format }}{{ .Config.Icons.Help.Text }} {{ .Help }}{{color "reset"}}{{"\n"}}{{end}}
{{- color .Config.Icons.Question.Format }}{{ .Config.Icons.Question.Text }} {{color "reset"}}
{{- color "default+hb"}}{{ .Message }}{{ .FilterMessage }}{{color "reset"}}
{{- color "default+hb"}}{{ .Message }}{{- if not .HideFilter }}{{ .FilterMessage }}{{end}}{{color "reset"}}
{{- if .ShowAnswer}}{{color "cyan"}} {{.Answer}}{{color "reset"}}{{"\n"}}
{{- else}}
{{- " "}}{{- color "cyan"}}[Use arrows to move, type to filter{{- if and .Help (not .ShowHelp)}}, {{ .Config.HelpInput }} for more help{{end}}]{{color "reset"}}
{{- " "}}{{- color "cyan"}}[Use arrows to move{{- if not .HideFilter }}, type to filter{{end}}{{- 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 @@ -140,7 +142,7 @@ func (s *Select) OnChange(key rune, config *PromptConfig) bool {
s.filter = string(runeFilter[0 : len(runeFilter)-1])
// we removed the last value in the filter
}
} else if key >= terminal.KeySpace {
} else if key >= terminal.KeySpace && !s.HideFilter { // todo config
s.filter += string(key)
// make sure vim mode is disabled
s.VimMode = false
Expand Down Expand Up @@ -175,6 +177,7 @@ func (s *Select) OnChange(key rune, config *PromptConfig) bool {
Select: *s,
SelectedIndex: idx,
ShowHelp: s.showingHelp,
HideFilter: s.HideFilter,
Description: s.Description,
PageEntries: opts,
Config: config,
Expand Down Expand Up @@ -267,8 +270,9 @@ func (s *Select) Prompt(config *PromptConfig) (interface{}, error) {
tmplData := SelectTemplateData{
Select: *s,
SelectedIndex: idx,
Description: s.Description,
ShowHelp: s.showingHelp,
HideFilter: s.HideFilter,
Description: s.Description,
PageEntries: opts,
Config: config,
}
Expand Down
89 changes: 89 additions & 0 deletions select_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -374,3 +374,92 @@ func TestSelectPrompt(t *testing.T) {
})
}
}

func TestSelectPromptHideFilter(t *testing.T) {
tests := []PromptTest{
{
"attempt to filter and select the end option when the filter is hidden",
&Select{
Message: "What color do you prefer:",
Options: []string{"green", "red", "blue"},
HideFilter: true,
},
func(c expectConsole) {
c.ExpectString("What color do you prefer: [Use arrows to move]")
// Attempt to filter "blue"
c.Send("b")
// Select "green"
c.SendLine("")
c.ExpectEOF()
},
core.OptionAnswer{
Value: "green", Index: 0,
},
},
{
"filter and select the end option when the filter is clearly not hidden",
&Select{
Message: "What color do you prefer:",
Options: []string{"green", "red", "blue"},
HideFilter: false,
},
func(c expectConsole) {
c.ExpectString("What color do you prefer: [Use arrows to move, type to filter]")
// Filter "blue"
c.Send("b")
// Select "blue"
c.SendLine("")
c.ExpectEOF()
},
core.OptionAnswer{
Value: "blue", Index: 2,
},
},
{
"filter and select the end option using the default filter behavior",
&Select{
Message: "What color do you prefer:",
Options: []string{"green", "red", "blue"},
},
func(c expectConsole) {
c.ExpectString("What color do you prefer: [Use arrows to move, type to filter]")
// Filter "blue"
c.Send("l")
// Select "blue"
c.SendLine("")
c.ExpectEOF()
},
core.OptionAnswer{
Value: "blue", Index: 2,
},
},
{
"verify that help text is shown when requested, even if the filter is hidden",
&Select{
Message: "What color do you prefer:",
Options: []string{"green", "red", "blue"},
Help: "We all have a favorite :)",
HideFilter: true,
},
func(c expectConsole) {
c.ExpectString("What color do you prefer: [Use arrows to move, ? for more help]")
// Display help message
c.Send("?")
c.ExpectString("We all have a favorite :)")
// Select "green"
c.Send(" ")
c.SendLine("")
c.ExpectEOF()
},
core.OptionAnswer{
Value: "green", Index: 0,
},
},
}

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