Skip to content

Commit 02aae5f

Browse files
authored
feat(experiment): inline sample descriptions in plain text (#400)
1 parent 2ddf970 commit 02aae5f

File tree

7 files changed

+64
-32
lines changed

7 files changed

+64
-32
lines changed

cmd/project/create_samples.go

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"sort"
2222
"strings"
2323

24+
"github.com/slackapi/slack-cli/internal/experiment"
2425
"github.com/slackapi/slack-cli/internal/iostreams"
2526
"github.com/slackapi/slack-cli/internal/pkg/create"
2627
"github.com/slackapi/slack-cli/internal/shared"
@@ -64,14 +65,26 @@ func promptSampleSelection(ctx context.Context, clients *shared.ClientFactory, s
6465
}
6566

6667
sortedRepos := sortRepos(filteredRepos)
67-
selectOptions := createSelectOptions(sortedRepos)
68+
selectOptions := make([]string, len(sortedRepos))
69+
for i, r := range sortedRepos {
70+
if !clients.Config.WithExperimentOn(experiment.Charm) {
71+
selectOptions[i] = fmt.Sprint(i+1, ". ", r.Name)
72+
} else {
73+
selectOptions[i] = r.Name
74+
}
75+
}
6876

6977
var selectedTemplate string
7078
selection, err = clients.IO.SelectPrompt(ctx, "Select a sample to build upon:", selectOptions, iostreams.SelectPromptConfig{
7179
Description: func(value string, index int) string {
72-
return sortedRepos[index].Description + "\n https://github.com/" + sortedRepos[index].FullName
80+
desc := sortedRepos[index].Description
81+
if !clients.Config.WithExperimentOn(experiment.Charm) {
82+
desc += "\n https://github.com/" + sortedRepos[index].FullName
83+
}
84+
return desc
7385
},
7486
Flag: clients.Config.Flags.Lookup("template"),
87+
Help: fmt.Sprintf("Guided tutorials can be found at %s", style.LinkText("https://docs.slack.dev/samples")),
7588
PageSize: 4, // Supports standard terminal height (24 rows)
7689
Required: true,
7790
Template: embedPromptSamplesTmpl,
@@ -127,18 +140,3 @@ func sortRepos(sampleRepos []create.GithubRepo) []create.GithubRepo {
127140
})
128141
return sortedRepos
129142
}
130-
131-
// createSelectOptions takes in a list of repositories
132-
// and returns an array of strings, each value being
133-
// equal to the repository name (ie, deno-starter-template)
134-
// and prepended with a number for a prompt visual aid
135-
func createSelectOptions(filteredRepos []create.GithubRepo) []string {
136-
// Create a slice of repository names to use as
137-
// the primary item selection in the prompt
138-
selectOptions := make([]string, 0)
139-
for i, f := range filteredRepos {
140-
selectOption := fmt.Sprint(i+1, ". ", f.Name)
141-
selectOptions = append(selectOptions, selectOption)
142-
}
143-
return selectOptions
144-
}

cmd/project/create_samples_test.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -174,9 +174,3 @@ func TestSamples_SortRepos(t *testing.T) {
174174
assert.Equal(t, sortedRepos[3].StargazersCount, 0, "Expected sortedRepos[3].StargazersCount to equal 0")
175175
assert.Equal(t, sortedRepos[3].Description, "This is a new sample")
176176
}
177-
178-
func TestSamples_CreateSelectOptions(t *testing.T) {
179-
selectOptions := createSelectOptions(mockGitHubRepos)
180-
assert.Equal(t, len(selectOptions), 4, "Expected selectOptions length to be 4")
181-
assert.Contains(t, selectOptions[0], mockGitHubRepos[0].Name, "Expected selectOptions[0] to contain mockGitHubRepos[0].Name")
182-
}

cmd/project/create_template.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,6 @@ func promptTemplateSelection(cmd *cobra.Command, clients *shared.ClientFactory,
132132

133133
// Prompt to choose a category
134134
selection, err := clients.IO.SelectPrompt(ctx, promptForCategory, titlesForCategory, iostreams.SelectPromptConfig{
135-
Description: func(value string, index int) string {
136-
return optionsForCategory[index].Description
137-
},
138135
Flag: clients.Config.Flags.Lookup("template"),
139136
Required: true,
140137
Template: templateForCategory,
@@ -168,9 +165,6 @@ func promptTemplateSelection(cmd *cobra.Command, clients *shared.ClientFactory,
168165

169166
// Prompt to choose a template
170167
selection, err := clients.IO.SelectPrompt(ctx, prompt, titles, iostreams.SelectPromptConfig{
171-
Description: func(value string, index int) string {
172-
return options[index].Description
173-
},
174168
Flag: clients.Config.Flags.Lookup("template"),
175169
Required: true,
176170
Template: template,

internal/iostreams/charm.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,16 @@ func buildSelectForm(msg string, options []string, cfg SelectPromptConfig, selec
7272
for _, opt := range options {
7373
key := opt
7474
if cfg.Description != nil {
75-
if desc := cfg.Description(opt, len(opts)); desc != "" {
76-
key = opt + "\n " + desc
75+
if desc := style.RemoveEmoji(cfg.Description(opt, len(opts))); desc != "" {
76+
key = opt + " - " + desc
7777
}
7878
}
7979
opts = append(opts, huh.NewOption(key, opt))
8080
}
8181

8282
field := huh.NewSelect[string]().
8383
Title(msg).
84+
Description(cfg.Help).
8485
Options(opts...).
8586
Value(selected)
8687

internal/iostreams/survey.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,9 +409,10 @@ var selectQuestionTemplate = fmt.Sprintf(`
409409

410410
// SelectPromptConfig holds additional config for a survey.Select prompt
411411
type SelectPromptConfig struct {
412-
Description func(value string, index int) string // Optional text displayed below each prompt option
412+
Description func(value string, index int) string // Optional text displayed with each prompt option
413413
Flag *pflag.Flag // The single flag substitute for this prompt
414414
Flags []*pflag.Flag // Otherwise multiple flag substitutes for this prompt
415+
Help string // Optional help text displayed below the select title
415416
PageSize int // The number of options displayed before the user needs to scroll
416417
Required bool // If a response is required
417418
Template string // Custom formatting of the selection prompt

internal/style/style.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,20 @@ func RemoveANSI(str string) string {
4545
return ansiRegex.ReplaceAllString(str, "")
4646
}
4747

48+
// RemoveEmoji strips non-ASCII characters (such as emoji) from a string
49+
// and collapses any resulting extra whitespace.
50+
//
51+
// https://en.wikipedia.org/wiki/ASCII#Printable_character_table
52+
func RemoveEmoji(str string) string {
53+
var b strings.Builder
54+
for _, r := range str {
55+
if r <= 127 {
56+
b.WriteRune(r)
57+
}
58+
}
59+
return strings.Join(strings.Fields(b.String()), " ")
60+
}
61+
4862
// ToggleStyles sets styles and formatting values to the active state
4963
func ToggleStyles(active bool) {
5064
isStyleEnabled = active

internal/style/style_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,36 @@ func TestRemoveANSI(t *testing.T) {
5858
}
5959
}
6060

61+
func TestRemoveEmoji(t *testing.T) {
62+
tests := map[string]struct {
63+
input string
64+
expected string
65+
}{
66+
"plain text is unchanged": {
67+
input: "A simple description",
68+
expected: "A simple description",
69+
},
70+
"emoji flags are removed": {
71+
input: "A translation bot 🇨🇳 🇮🇹 🇹🇭 🇫🇷",
72+
expected: "A translation bot",
73+
},
74+
"mixed emoji and text": {
75+
input: "Hello 🌍 world 🚀 test",
76+
expected: "Hello world test",
77+
},
78+
"empty string": {
79+
input: "",
80+
expected: "",
81+
},
82+
}
83+
for name, tc := range tests {
84+
t.Run(name, func(t *testing.T) {
85+
actual := RemoveEmoji(tc.input)
86+
assert.Equal(t, tc.expected, actual)
87+
})
88+
}
89+
}
90+
6191
func TestToggleStyles(t *testing.T) {
6292
defer func() {
6393
ToggleStyles(false)

0 commit comments

Comments
 (0)