Skip to content

Commit ca8d03d

Browse files
committed
Merge branch 'main' into row-witchcraft-and-wizardry
2 parents 4a6b598 + 4d1d83b commit ca8d03d

File tree

4 files changed

+162
-4
lines changed

4 files changed

+162
-4
lines changed

.changeset/busy-baths-work.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+
Fixes rendering of multi-line messages and options in select prompt.

packages/prompts/src/select.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,16 @@ export interface SelectOptions<Value> extends CommonOptions {
7272
maxItems?: number;
7373
}
7474

75+
const computeLabel = (label: string, format: (text: string) => string) => {
76+
if (!label.includes('\n')) {
77+
return format(label);
78+
}
79+
return label
80+
.split('\n')
81+
.map((line) => format(line))
82+
.join('\n');
83+
};
84+
7585
export const select = <Value>(opts: SelectOptions<Value>) => {
7686
const opt = (
7787
option: Option<Value>,
@@ -80,19 +90,19 @@ export const select = <Value>(opts: SelectOptions<Value>) => {
8090
const label = option.label ?? String(option.value);
8191
switch (state) {
8292
case 'disabled':
83-
return `${color.gray(S_RADIO_INACTIVE)} ${color.gray(label)}${
93+
return `${color.gray(S_RADIO_INACTIVE)} ${computeLabel(label, color.gray)}${
8494
option.hint ? ` ${color.dim(`(${option.hint ?? 'disabled'})`)}` : ''
8595
}`;
8696
case 'selected':
87-
return `${color.dim(label)}`;
97+
return `${computeLabel(label, color.dim)}`;
8898
case 'active':
8999
return `${color.green(S_RADIO_ACTIVE)} ${label}${
90100
option.hint ? ` ${color.dim(`(${option.hint})`)}` : ''
91101
}`;
92102
case 'cancelled':
93-
return `${color.strikethrough(color.dim(label))}`;
103+
return `${computeLabel(label, (str) => color.strikethrough(color.dim(str)))}`;
94104
default:
95-
return `${color.dim(S_RADIO_INACTIVE)} ${color.dim(label)}`;
105+
return `${color.dim(S_RADIO_INACTIVE)} ${computeLabel(label, color.dim)}`;
96106
}
97107
};
98108

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

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,35 @@ exports[`select (isCI = false) > renders disabled options 1`] = `
122122
]
123123
`;
124124
125+
exports[`select (isCI = false) > renders multi-line option labels 1`] = `
126+
[
127+
"<cursor.hide>",
128+
"│
129+
◆ foo
130+
│ ● Option 0
131+
│ with multiple lines
132+
│ ○ Option 1
133+
└
134+
",
135+
"<cursor.backward count=999><cursor.up count=6>",
136+
"<cursor.down count=2>",
137+
"<erase.down>",
138+
"│ ○ Option 0
139+
│ with multiple lines
140+
│ ● Option 1
141+
└
142+
",
143+
"<cursor.backward count=999><cursor.up count=6>",
144+
"<cursor.down count=1>",
145+
"<erase.down>",
146+
"◇ foo
147+
│ Option 1",
148+
"
149+
",
150+
"<cursor.show>",
151+
]
152+
`;
153+
125154
exports[`select (isCI = false) > renders option hints 1`] = `
126155
[
127156
"<cursor.hide>",
@@ -245,6 +274,30 @@ exports[`select (isCI = false) > wraps long cancelled message 1`] = `
245274
]
246275
`;
247276
277+
exports[`select (isCI = false) > wraps long messages 1`] = `
278+
[
279+
"<cursor.hide>",
280+
"│
281+
◆ foo foo foo foo foo foo foo
282+
│ foo foo foo foo foo foo
283+
│ foo foo foo foo foo foo foo
284+
│ ● opt0
285+
│ ○ opt1
286+
└
287+
",
288+
"<cursor.backward count=999><cursor.up count=7>",
289+
"<cursor.down count=1>",
290+
"<erase.down>",
291+
"◇ foo foo foo foo foo foo foo
292+
│ foo foo foo foo foo foo
293+
│ foo foo foo foo foo foo foo
294+
│ opt0",
295+
"
296+
",
297+
"<cursor.show>",
298+
]
299+
`;
300+
248301
exports[`select (isCI = false) > wraps long results 1`] = `
249302
[
250303
"<cursor.hide>",
@@ -395,6 +448,35 @@ exports[`select (isCI = true) > renders disabled options 1`] = `
395448
]
396449
`;
397450
451+
exports[`select (isCI = true) > renders multi-line option labels 1`] = `
452+
[
453+
"<cursor.hide>",
454+
"│
455+
◆ foo
456+
│ ● Option 0
457+
│ with multiple lines
458+
│ ○ Option 1
459+
└
460+
",
461+
"<cursor.backward count=999><cursor.up count=6>",
462+
"<cursor.down count=2>",
463+
"<erase.down>",
464+
"│ ○ Option 0
465+
│ with multiple lines
466+
│ ● Option 1
467+
└
468+
",
469+
"<cursor.backward count=999><cursor.up count=6>",
470+
"<cursor.down count=1>",
471+
"<erase.down>",
472+
"◇ foo
473+
│ Option 1",
474+
"
475+
",
476+
"<cursor.show>",
477+
]
478+
`;
479+
398480
exports[`select (isCI = true) > renders option hints 1`] = `
399481
[
400482
"<cursor.hide>",
@@ -518,6 +600,30 @@ exports[`select (isCI = true) > wraps long cancelled message 1`] = `
518600
]
519601
`;
520602
603+
exports[`select (isCI = true) > wraps long messages 1`] = `
604+
[
605+
"<cursor.hide>",
606+
"│
607+
◆ foo foo foo foo foo foo foo
608+
│ foo foo foo foo foo foo
609+
│ foo foo foo foo foo foo foo
610+
│ ● opt0
611+
│ ○ opt1
612+
└
613+
",
614+
"<cursor.backward count=999><cursor.up count=7>",
615+
"<cursor.down count=1>",
616+
"<erase.down>",
617+
"◇ foo foo foo foo foo foo foo
618+
│ foo foo foo foo foo foo
619+
│ foo foo foo foo foo foo foo
620+
│ opt0",
621+
"
622+
",
623+
"<cursor.show>",
624+
]
625+
`;
626+
521627
exports[`select (isCI = true) > wraps long results 1`] = `
522628
[
523629
"<cursor.hide>",

packages/prompts/test/select.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,43 @@ describe.each(['true', 'false'])('select (isCI = %s)', (isCI) => {
212212
expect(output.buffer).toMatchSnapshot();
213213
});
214214

215+
test('wraps long messages', async () => {
216+
output.columns = 40;
217+
218+
const result = prompts.select({
219+
message: 'foo '.repeat(20).trim(),
220+
options: [{ value: 'opt0' }, { value: 'opt1' }],
221+
input,
222+
output,
223+
});
224+
225+
input.emit('keypress', '', { name: 'return' });
226+
227+
const value = await result;
228+
229+
expect(value).toEqual('opt0');
230+
expect(output.buffer).toMatchSnapshot();
231+
});
232+
233+
test('renders multi-line option labels', async () => {
234+
const result = prompts.select({
235+
message: 'foo',
236+
options: [
237+
{ value: 'opt0', label: 'Option 0\nwith multiple lines' },
238+
{ value: 'opt1', label: 'Option 1' },
239+
],
240+
input,
241+
output,
242+
});
243+
244+
input.emit('keypress', '', { name: 'down' });
245+
input.emit('keypress', '', { name: 'return' });
246+
247+
await result;
248+
249+
expect(output.buffer).toMatchSnapshot();
250+
});
251+
215252
test('handles mixed size re-renders', async () => {
216253
output.rows = 10;
217254

0 commit comments

Comments
 (0)