Skip to content

Commit 86e36d8

Browse files
authored
feat: add withGuide support to select prompt (#453)
1 parent 0256238 commit 86e36d8

File tree

4 files changed

+122
-8
lines changed

4 files changed

+122
-8
lines changed

‎.changeset/brave-monkeys-slide.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@clack/prompts": patch
3+
---
4+
5+
Add `withGuide` support to select prompt

‎packages/prompts/src/select.ts‎

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { SelectPrompt, wrapTextWithPrefix } from '@clack/core';
1+
import { SelectPrompt, settings, wrapTextWithPrefix } from '@clack/core';
22
import color from 'picocolors';
33
import {
44
type CommonOptions,
@@ -113,6 +113,7 @@ export const select = <Value>(opts: SelectOptions<Value>) => {
113113
output: opts.output,
114114
initialValue: opts.initialValue,
115115
render() {
116+
const hasGuide = (opts.withGuide ?? settings.withGuide) !== false;
116117
const titlePrefix = `${symbol(this.state)} `;
117118
const titlePrefixBar = `${symbolBar(this.state)} `;
118119
const messageLines = wrapTextWithPrefix(
@@ -121,11 +122,11 @@ export const select = <Value>(opts: SelectOptions<Value>) => {
121122
titlePrefixBar,
122123
titlePrefix
123124
);
124-
const title = `${color.gray(S_BAR)}\n${messageLines}\n`;
125+
const title = `${hasGuide ? `${color.gray(S_BAR)}\n` : ''}${messageLines}\n`;
125126

126127
switch (this.state) {
127128
case 'submit': {
128-
const submitPrefix = `${color.gray(S_BAR)} `;
129+
const submitPrefix = hasGuide ? `${color.gray(S_BAR)} ` : '';
129130
const wrappedLines = wrapTextWithPrefix(
130131
opts.output,
131132
opt(this.options[this.cursor], 'selected'),
@@ -134,19 +135,20 @@ export const select = <Value>(opts: SelectOptions<Value>) => {
134135
return `${title}${wrappedLines}`;
135136
}
136137
case 'cancel': {
137-
const cancelPrefix = `${color.gray(S_BAR)} `;
138+
const cancelPrefix = hasGuide ? `${color.gray(S_BAR)} ` : '';
138139
const wrappedLines = wrapTextWithPrefix(
139140
opts.output,
140141
opt(this.options[this.cursor], 'cancelled'),
141142
cancelPrefix
142143
);
143-
return `${title}${wrappedLines}\n${color.gray(S_BAR)}`;
144+
return `${title}${wrappedLines}${hasGuide ? `\n${color.gray(S_BAR)}` : ''}`;
144145
}
145146
default: {
146-
const prefix = `${color.cyan(S_BAR)} `;
147+
const prefix = hasGuide ? `${color.cyan(S_BAR)} ` : '';
148+
const prefixEnd = hasGuide ? color.cyan(S_BAR_END) : '';
147149
// Calculate rowPadding: title lines + footer lines (S_BAR_END + trailing newline)
148150
const titleLineCount = title.split('\n').length;
149-
const footerLineCount = 2; // S_BAR_END + trailing newline
151+
const footerLineCount = hasGuide ? 2 : 1; // S_BAR_END + trailing newline (or just trailing newline)
150152
return `${title}${prefix}${limitOptions({
151153
output: opts.output,
152154
cursor: this.cursor,
@@ -156,7 +158,7 @@ export const select = <Value>(opts: SelectOptions<Value>) => {
156158
rowPadding: titleLineCount + footerLineCount,
157159
style: (item, active) =>
158160
opt(item, item.disabled ? 'disabled' : active ? 'active' : 'inactive'),
159-
}).join(`\n${prefix}`)}\n${color.cyan(S_BAR_END)}\n`;
161+
}).join(`\n${prefix}`)}\n${prefixEnd}\n`;
160162
}
161163
}
162164
},

‎packages/prompts/test/__snapshots__/select.test.ts.snap‎

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,24 @@ exports[`select (isCI = false) > down arrow selects next option 1`] = `
181181
]
182182
`;
183183
184+
exports[`select (isCI = false) > global withGuide: false removes guide 1`] = `
185+
[
186+
"<cursor.hide>",
187+
"◆ foo
188+
● opt0
189+
○ opt1
190+
191+
",
192+
"<cursor.backward count=999><cursor.up count=4>",
193+
"<erase.down>",
194+
"◇ foo
195+
opt0",
196+
"
197+
",
198+
"<cursor.show>",
199+
]
200+
`;
201+
184202
exports[`select (isCI = false) > handles mixed size re-renders 1`] = `
185203
[
186204
"<cursor.hide>",
@@ -363,6 +381,24 @@ exports[`select (isCI = false) > up arrow selects previous option 1`] = `
363381
]
364382
`;
365383
384+
exports[`select (isCI = false) > withGuide: false removes guide 1`] = `
385+
[
386+
"<cursor.hide>",
387+
"◆ foo
388+
● opt0
389+
○ opt1
390+
391+
",
392+
"<cursor.backward count=999><cursor.up count=4>",
393+
"<erase.down>",
394+
"◇ foo
395+
opt0",
396+
"
397+
",
398+
"<cursor.show>",
399+
]
400+
`;
401+
366402
exports[`select (isCI = false) > wraps long cancelled message 1`] = `
367403
[
368404
"<cursor.hide>",
@@ -625,6 +661,24 @@ exports[`select (isCI = true) > down arrow selects next option 1`] = `
625661
]
626662
`;
627663
664+
exports[`select (isCI = true) > global withGuide: false removes guide 1`] = `
665+
[
666+
"<cursor.hide>",
667+
"◆ foo
668+
● opt0
669+
○ opt1
670+
671+
",
672+
"<cursor.backward count=999><cursor.up count=4>",
673+
"<erase.down>",
674+
"◇ foo
675+
opt0",
676+
"
677+
",
678+
"<cursor.show>",
679+
]
680+
`;
681+
628682
exports[`select (isCI = true) > handles mixed size re-renders 1`] = `
629683
[
630684
"<cursor.hide>",
@@ -807,6 +861,24 @@ exports[`select (isCI = true) > up arrow selects previous option 1`] = `
807861
]
808862
`;
809863
864+
exports[`select (isCI = true) > withGuide: false removes guide 1`] = `
865+
[
866+
"<cursor.hide>",
867+
"◆ foo
868+
● opt0
869+
○ opt1
870+
871+
",
872+
"<cursor.backward count=999><cursor.up count=4>",
873+
"<erase.down>",
874+
"◇ foo
875+
opt0",
876+
"
877+
",
878+
"<cursor.show>",
879+
]
880+
`;
881+
810882
exports[`select (isCI = true) > wraps long cancelled message 1`] = `
811883
[
812884
"<cursor.hide>",

‎packages/prompts/test/select.test.ts‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { updateSettings } from '@clack/core';
12
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test, vi } from 'vitest';
23
import * as prompts from '../src/index.js';
34
import { MockReadable, MockWritable } from './test-utils.js';
@@ -23,6 +24,7 @@ describe.each(['true', 'false'])('select (isCI = %s)', (isCI) => {
2324

2425
afterEach(() => {
2526
vi.restoreAllMocks();
27+
updateSettings({ withGuide: true });
2628
});
2729

2830
test('renders options and message', async () => {
@@ -305,6 +307,39 @@ describe.each(['true', 'false'])('select (isCI = %s)', (isCI) => {
305307
expect(output.buffer).toMatchSnapshot();
306308
});
307309

310+
test('withGuide: false removes guide', async () => {
311+
const result = prompts.select({
312+
message: 'foo',
313+
options: [{ value: 'opt0' }, { value: 'opt1' }],
314+
withGuide: false,
315+
input,
316+
output,
317+
});
318+
319+
input.emit('keypress', '', { name: 'return' });
320+
321+
await result;
322+
323+
expect(output.buffer).toMatchSnapshot();
324+
});
325+
326+
test('global withGuide: false removes guide', async () => {
327+
updateSettings({ withGuide: false });
328+
329+
const result = prompts.select({
330+
message: 'foo',
331+
options: [{ value: 'opt0' }, { value: 'opt1' }],
332+
input,
333+
output,
334+
});
335+
336+
input.emit('keypress', '', { name: 'return' });
337+
338+
await result;
339+
340+
expect(output.buffer).toMatchSnapshot();
341+
});
342+
308343
test('correctly limits options with explicit multiline message', async () => {
309344
output.rows = 12;
310345

0 commit comments

Comments
 (0)