From ce0bce973cccb2dfbfb7638173b7bdd7fa1882aa Mon Sep 17 00:00:00 2001 From: Paul Valladares <85648028+dreyfus92@users.noreply.github.com> Date: Mon, 15 Jun 2026 23:54:19 -0600 Subject: [PATCH 1/6] feat(prompts): add opt-in instructions footer for multiselect prompts --- .changeset/calm-feet-lead.md | 5 + examples/changesets/index.ts | 3 + packages/prompts/src/common.ts | 18 ++ packages/prompts/src/group-multi-select.ts | 19 +- packages/prompts/src/multi-select.ts | 16 +- .../group-multi-select.test.ts.snap | 276 ++++++++++++++++++ .../__snapshots__/multi-select.test.ts.snap | 272 +++++++++++++++++ .../prompts/test/group-multi-select.test.ts | 18 ++ packages/prompts/test/multi-select.test.ts | 57 ++++ 9 files changed, 676 insertions(+), 8 deletions(-) create mode 100644 .changeset/calm-feet-lead.md diff --git a/.changeset/calm-feet-lead.md b/.changeset/calm-feet-lead.md new file mode 100644 index 00000000..33c4f52b --- /dev/null +++ b/.changeset/calm-feet-lead.md @@ -0,0 +1,5 @@ +--- +"@clack/prompts": minor +--- + +Add an opt-in `instructions` option to `multiselect` and `groupMultiselect`. When enabled, the active prompt shows a keyboard hints footer. Defaults to `false`, so existing prompts are unchanged. diff --git a/examples/changesets/index.ts b/examples/changesets/index.ts index cb9226db..c561aaf4 100644 --- a/examples/changesets/index.ts +++ b/examples/changesets/index.ts @@ -19,6 +19,7 @@ async function main() { packages: () => p.groupMultiselect({ message: 'Which packages would you like to include?', + instructions: true, options: { 'changed packages': [ { value: '@scope/a' }, @@ -36,6 +37,7 @@ async function main() { const packages = results.packages ?? []; return p.multiselect({ message: `Which packages should have a ${color.red('major')} bump?`, + instructions: true, options: packages.map((value) => ({ value })), required: false, }); @@ -47,6 +49,7 @@ async function main() { if (possiblePackages.length === 0) return; return p.multiselect({ message: `Which packages should have a ${color.yellow('minor')} bump?`, + instructions: true, options: possiblePackages.map((value) => ({ value })), required: false, }); diff --git a/packages/prompts/src/common.ts b/packages/prompts/src/common.ts index cf348b38..53217592 100644 --- a/packages/prompts/src/common.ts +++ b/packages/prompts/src/common.ts @@ -73,3 +73,21 @@ export interface CommonOptions { signal?: AbortSignal; withGuide?: boolean; } + +export const MULTISELECT_INSTRUCTIONS = [ + `${styleText('dim', '↑/↓')} to navigate`, + `${styleText('dim', 'Space:')} select`, + `${styleText('dim', 'Enter:')} confirm`, +]; + +export function formatInstructionFooter( + instructions: string[] | null, + hasGuide: boolean +): { text: string; lineCount: number } { + const guidePrefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : ''; + const footerLines = instructions ? [`${guidePrefix}${instructions.join(' • ')}`] : []; + if (hasGuide) { + footerLines.push(styleText('cyan', S_BAR_END)); + } + return { text: footerLines.join('\n'), lineCount: footerLines.length + 1 }; +} diff --git a/packages/prompts/src/group-multi-select.ts b/packages/prompts/src/group-multi-select.ts index 4ddc3d3f..8717f269 100644 --- a/packages/prompts/src/group-multi-select.ts +++ b/packages/prompts/src/group-multi-select.ts @@ -2,6 +2,8 @@ import { styleText } from 'node:util'; import { GroupMultiSelectPrompt, settings, wrapTextWithPrefix } from '@clack/core'; import { type CommonOptions, + MULTISELECT_INSTRUCTIONS, + formatInstructionFooter, S_BAR, S_BAR_END, S_CHECKBOX_ACTIVE, @@ -58,6 +60,12 @@ export interface GroupMultiSelectOptions extends CommonOptions { * @default 0 */ groupSpacing?: number; + + /** + * Show keyboard instructions below the option list. + * @default false + */ + instructions?: boolean; } /** @@ -189,6 +197,7 @@ export const groupMultiselect = (opts: GroupMultiSelectOptions) => ); }; const required = opts.required ?? true; + const showInstructions = opts.instructions ?? false; return new GroupMultiSelectPrompt({ options: opts.options, @@ -285,9 +294,11 @@ export const groupMultiselect = (opts: GroupMultiSelectOptions) => } default: { const guidePrefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : ''; - // Calculate rowPadding: title lines + footer lines (S_BAR_END + trailing newline) const titleLineCount = title.split('\n').length; - const footerLineCount = (hasGuide ? 1 : 0) + 1; // guide line + trailing newline + const { text: footerText, lineCount: footerLineCount } = formatInstructionFooter( + showInstructions ? MULTISELECT_INSTRUCTIONS : null, + hasGuide + ); const optionsText = limitOptions({ output: opts.output, options: this.options, @@ -297,9 +308,7 @@ export const groupMultiselect = (opts: GroupMultiSelectOptions) => rowPadding: titleLineCount + footerLineCount, style: styleOption, }).join(`\n${guidePrefix}`); - return `${title}${guidePrefix}${optionsText}\n${ - hasGuide ? styleText('cyan', S_BAR_END) : '' - }\n`; + return `${title}${guidePrefix}${optionsText}\n${footerText}\n`; } } }, diff --git a/packages/prompts/src/multi-select.ts b/packages/prompts/src/multi-select.ts index 28b27aab..aed7eaa5 100644 --- a/packages/prompts/src/multi-select.ts +++ b/packages/prompts/src/multi-select.ts @@ -2,6 +2,8 @@ import { styleText } from 'node:util'; import { MultiSelectPrompt, settings, wrapTextWithPrefix } from '@clack/core'; import { type CommonOptions, + MULTISELECT_INSTRUCTIONS, + formatInstructionFooter, S_BAR, S_BAR_END, S_CHECKBOX_ACTIVE, @@ -20,6 +22,11 @@ export interface MultiSelectOptions extends CommonOptions { maxItems?: number; required?: boolean; cursorAt?: Value; + /** + * Show keyboard instructions below the option list. + * @default false + */ + instructions?: boolean; } const computeLabel = (label: string, format: (text: string) => string) => { return label @@ -70,6 +77,7 @@ export const multiselect = (opts: MultiSelectOptions) => { return `${styleText('dim', S_CHECKBOX_INACTIVE)} ${computeLabel(label, (text) => styleText('dim', text))}`; }; const required = opts.required ?? true; + const showInstructions = opts.instructions ?? false; return new MultiSelectPrompt({ options: opts.options, @@ -171,9 +179,11 @@ export const multiselect = (opts: MultiSelectOptions) => { } default: { const prefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : ''; - // Calculate rowPadding: title lines + footer lines (S_BAR_END + trailing newline) const titleLineCount = title.split('\n').length; - const footerLineCount = hasGuide ? 2 : 1; // S_BAR_END + trailing newline + const { text: footerText, lineCount: footerLineCount } = formatInstructionFooter( + showInstructions ? MULTISELECT_INSTRUCTIONS : null, + hasGuide + ); return `${title}${prefix}${limitOptions({ output: opts.output, options: this.options, @@ -182,7 +192,7 @@ export const multiselect = (opts: MultiSelectOptions) => { columnPadding: prefix.length, rowPadding: titleLineCount + footerLineCount, style: styleOption, - }).join(`\n${prefix}`)}\n${hasGuide ? styleText('cyan', S_BAR_END) : ''}\n`; + }).join(`\n${prefix}`)}\n${footerText}\n`; } } }, diff --git a/packages/prompts/test/__snapshots__/group-multi-select.test.ts.snap b/packages/prompts/test/__snapshots__/group-multi-select.test.ts.snap index d6537c17..8bdb9c79 100644 --- a/packages/prompts/test/__snapshots__/group-multi-select.test.ts.snap +++ b/packages/prompts/test/__snapshots__/group-multi-select.test.ts.snap @@ -389,6 +389,144 @@ exports[`groupMultiselect (isCI = false) > initial values can be set 1`] = ` ] `; +exports[`groupMultiselect (isCI = false) > instructions: true renders footer 1`] = ` +[ + "", + "│ +◆ foo +│ ◻ group1 +│ │ ◻ group1value0 +│ └ ◻ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm +└ +", + "", + "", + "", + "◇ foo +│", + " +", + "", +] +`; + +exports[`groupMultiselect (isCI = false) > instructions: true with maxItems renders a sliding window 1`] = ` +[ + "", + "│ +◆ foo +│ ◻ group1 +│ │ ◻ group1value0 +│ │ ◻ group1value1 +│ │ ◻ group1value2 +│ │ ◻ group1value3 +│ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm +└ +", + "", + "", + "", + "│ ◻ group1 +│ │ ◻ group1value0 +│ │ ◻ group1value1 +│ │ ◻ group1value2 +│ │ ◻ group1value3 +│ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm +└ +", + "", + "", + "", + "│ │ ◻ group1value0 +│ │ ◻ group1value1 +│ │ ◻ group1value2 +│ │ ◻ group1value3 +│ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm +└ +", + "", + "", + "", + "│ │ ◻ group1value1 +│ │ ◻ group1value2 +│ │ ◻ group1value3 +│ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm +└ +", + "", + "", + "", + "│ ... +│ │ ◻ group1value1 +│ │ ◻ group1value2 +│ │ ◻ group1value3 +│ │ ◻ group1value4 +│ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm +└ +", + "", + "", + "", + "│ │ ◻ group1value2 +│ │ ◻ group1value3 +│ │ ◻ group1value4 +│ └ ◻ group1value5 +│ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm +└ +", + "", + "", + "", + "│ │ ◻ group1value3 +│ │ ◻ group1value4 +│ └ ◻ group1value5 +│ ◻ group2 +│ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm +└ +", + "", + "", + "", + "│ └ ◼ group1value5", + "", + "", + "", + "", + "◇ foo +│ group1value5", + " +", + "", +] +`; + +exports[`groupMultiselect (isCI = false) > instructions: true with withGuide: false renders footer without guide 1`] = ` +[ + "", + "◆ foo +◻ group1 +│ ◻ group1value0 +└ ◻ group1value1 +↑/↓ to navigate • Space: select • Enter: confirm +", + "", + "", + "◇ foo +", + " +", + "", +] +`; + exports[`groupMultiselect (isCI = false) > maxItems renders a sliding window 1`] = ` [ "", @@ -1298,6 +1436,144 @@ exports[`groupMultiselect (isCI = true) > initial values can be set 1`] = ` ] `; +exports[`groupMultiselect (isCI = true) > instructions: true renders footer 1`] = ` +[ + "", + "│ +◆ foo +│ ◻ group1 +│ │ ◻ group1value0 +│ └ ◻ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm +└ +", + "", + "", + "", + "◇ foo +│", + " +", + "", +] +`; + +exports[`groupMultiselect (isCI = true) > instructions: true with maxItems renders a sliding window 1`] = ` +[ + "", + "│ +◆ foo +│ ◻ group1 +│ │ ◻ group1value0 +│ │ ◻ group1value1 +│ │ ◻ group1value2 +│ │ ◻ group1value3 +│ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm +└ +", + "", + "", + "", + "│ ◻ group1 +│ │ ◻ group1value0 +│ │ ◻ group1value1 +│ │ ◻ group1value2 +│ │ ◻ group1value3 +│ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm +└ +", + "", + "", + "", + "│ │ ◻ group1value0 +│ │ ◻ group1value1 +│ │ ◻ group1value2 +│ │ ◻ group1value3 +│ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm +└ +", + "", + "", + "", + "│ │ ◻ group1value1 +│ │ ◻ group1value2 +│ │ ◻ group1value3 +│ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm +└ +", + "", + "", + "", + "│ ... +│ │ ◻ group1value1 +│ │ ◻ group1value2 +│ │ ◻ group1value3 +│ │ ◻ group1value4 +│ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm +└ +", + "", + "", + "", + "│ │ ◻ group1value2 +│ │ ◻ group1value3 +│ │ ◻ group1value4 +│ └ ◻ group1value5 +│ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm +└ +", + "", + "", + "", + "│ │ ◻ group1value3 +│ │ ◻ group1value4 +│ └ ◻ group1value5 +│ ◻ group2 +│ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm +└ +", + "", + "", + "", + "│ └ ◼ group1value5", + "", + "", + "", + "", + "◇ foo +│ group1value5", + " +", + "", +] +`; + +exports[`groupMultiselect (isCI = true) > instructions: true with withGuide: false renders footer without guide 1`] = ` +[ + "", + "◆ foo +◻ group1 +│ ◻ group1value0 +└ ◻ group1value1 +↑/↓ to navigate • Space: select • Enter: confirm +", + "", + "", + "◇ foo +", + " +", + "", +] +`; + exports[`groupMultiselect (isCI = true) > maxItems renders a sliding window 1`] = ` [ "", diff --git a/packages/prompts/test/__snapshots__/multi-select.test.ts.snap b/packages/prompts/test/__snapshots__/multi-select.test.ts.snap index 38714036..e0206cd8 100644 --- a/packages/prompts/test/__snapshots__/multi-select.test.ts.snap +++ b/packages/prompts/test/__snapshots__/multi-select.test.ts.snap @@ -173,6 +173,142 @@ exports[`multiselect (isCI = false) > global withGuide: false removes guide 1`] ] `; +exports[`multiselect (isCI = false) > instructions: true renders footer 1`] = ` +[ + "", + "│ +◆ foo +│ ◻ opt0 +│ ◻ opt1 +│ ↑/↓ to navigate • Space: select • Enter: confirm +└ +", + "", + "", + "", + "◇ foo +│ none", + " +", + "", +] +`; + +exports[`multiselect (isCI = false) > instructions: true with maxItems renders a sliding window 1`] = ` +[ + "", + "│ +◆ foo +│ ◻ opt0 +│ ◻ opt1 +│ ◻ opt2 +│ ◻ opt3 +│ ◻ opt4 +│ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm +└ +", + "", + "", + "", + "│ ◻ opt0 +│ ◻ opt1 +│ ◻ opt2 +│ ◻ opt3 +│ ◻ opt4 +│ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm +└ +", + "", + "", + "", + "│ ◻ opt1 +│ ◻ opt2 +│ ◻ opt3 +│ ◻ opt4 +│ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm +└ +", + "", + "", + "", + "│ ◻ opt2 +│ ◻ opt3 +│ ◻ opt4 +│ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm +└ +", + "", + "", + "", + "│ ... +│ ◻ opt2 +│ ◻ opt3 +│ ◻ opt4 +│ ◻ opt5 +│ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm +└ +", + "", + "", + "", + "│ ◻ opt3 +│ ◻ opt4 +│ ◻ opt5 +│ ◻ opt6 +│ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm +└ +", + "", + "", + "", + "│ ◻ opt4 +│ ◻ opt5 +│ ◻ opt6 +│ ◻ opt7 +│ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm +└ +", + "", + "", + "", + "│ ◼ opt6", + "", + "", + "", + "", + "◇ foo +│ opt6", + " +", + "", +] +`; + +exports[`multiselect (isCI = false) > instructions: true with withGuide: false renders footer without guide 1`] = ` +[ + "", + "◆ foo +◻ opt0 +◻ opt1 +↑/↓ to navigate • Space: select • Enter: confirm +", + "", + "", + "◇ foo +none", + " +", + "", +] +`; + exports[`multiselect (isCI = false) > maxItems renders a sliding window 1`] = ` [ "", @@ -953,6 +1089,142 @@ exports[`multiselect (isCI = true) > global withGuide: false removes guide 1`] = ] `; +exports[`multiselect (isCI = true) > instructions: true renders footer 1`] = ` +[ + "", + "│ +◆ foo +│ ◻ opt0 +│ ◻ opt1 +│ ↑/↓ to navigate • Space: select • Enter: confirm +└ +", + "", + "", + "", + "◇ foo +│ none", + " +", + "", +] +`; + +exports[`multiselect (isCI = true) > instructions: true with maxItems renders a sliding window 1`] = ` +[ + "", + "│ +◆ foo +│ ◻ opt0 +│ ◻ opt1 +│ ◻ opt2 +│ ◻ opt3 +│ ◻ opt4 +│ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm +└ +", + "", + "", + "", + "│ ◻ opt0 +│ ◻ opt1 +│ ◻ opt2 +│ ◻ opt3 +│ ◻ opt4 +│ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm +└ +", + "", + "", + "", + "│ ◻ opt1 +│ ◻ opt2 +│ ◻ opt3 +│ ◻ opt4 +│ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm +└ +", + "", + "", + "", + "│ ◻ opt2 +│ ◻ opt3 +│ ◻ opt4 +│ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm +└ +", + "", + "", + "", + "│ ... +│ ◻ opt2 +│ ◻ opt3 +│ ◻ opt4 +│ ◻ opt5 +│ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm +└ +", + "", + "", + "", + "│ ◻ opt3 +│ ◻ opt4 +│ ◻ opt5 +│ ◻ opt6 +│ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm +└ +", + "", + "", + "", + "│ ◻ opt4 +│ ◻ opt5 +│ ◻ opt6 +│ ◻ opt7 +│ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm +└ +", + "", + "", + "", + "│ ◼ opt6", + "", + "", + "", + "", + "◇ foo +│ opt6", + " +", + "", +] +`; + +exports[`multiselect (isCI = true) > instructions: true with withGuide: false renders footer without guide 1`] = ` +[ + "", + "◆ foo +◻ opt0 +◻ opt1 +↑/↓ to navigate • Space: select • Enter: confirm +", + "", + "", + "◇ foo +none", + " +", + "", +] +`; + exports[`multiselect (isCI = true) > maxItems renders a sliding window 1`] = ` [ "", diff --git a/packages/prompts/test/group-multi-select.test.ts b/packages/prompts/test/group-multi-select.test.ts index 33769ebf..ac49ea56 100644 --- a/packages/prompts/test/group-multi-select.test.ts +++ b/packages/prompts/test/group-multi-select.test.ts @@ -481,4 +481,22 @@ describe.each(['true', 'false'])('groupMultiselect (isCI = %s)', (isCI) => { expect(value).toEqual(['group1value0']); expect(output.buffer).toMatchSnapshot(); }); + + test('instructions: true renders footer', async () => { + const result = prompts.groupMultiselect({ + message: 'foo', + input, + output, + instructions: true, + required: false, + options: { + group1: [{ value: 'group1value0' }, { value: 'group1value1' }], + }, + }); + + input.emit('keypress', '', { name: 'return' }); + + await result; + expect(output.buffer).toMatchSnapshot(); + }); }); diff --git a/packages/prompts/test/multi-select.test.ts b/packages/prompts/test/multi-select.test.ts index 3f79555b..6d04d856 100644 --- a/packages/prompts/test/multi-select.test.ts +++ b/packages/prompts/test/multi-select.test.ts @@ -437,4 +437,61 @@ describe.each(['true', 'false'])('multiselect (isCI = %s)', (isCI) => { expect(value).toEqual(['opt0']); expect(output.buffer).toMatchSnapshot(); }); + + test('instructions: true renders footer', async () => { + const result = prompts.multiselect({ + message: 'foo', + options: [{ value: 'opt0' }, { value: 'opt1' }], + instructions: true, + required: false, + input, + output, + }); + + input.emit('keypress', '', { name: 'return' }); + + await result; + expect(output.buffer).toMatchSnapshot(); + }); + + test('instructions: true with withGuide: false renders footer without guide', async () => { + const result = prompts.multiselect({ + message: 'foo', + options: [{ value: 'opt0' }, { value: 'opt1' }], + instructions: true, + required: false, + withGuide: false, + input, + output, + }); + + input.emit('keypress', '', { name: 'return' }); + + await result; + expect(output.buffer).toMatchSnapshot(); + }); + + test('instructions: true with maxItems renders a sliding window', async () => { + const result = prompts.multiselect({ + message: 'foo', + options: [...Array(12).keys()].map((k) => ({ + value: `opt${k}`, + })), + maxItems: 6, + instructions: true, + input, + output, + }); + + for (let i = 0; i < 6; i++) { + input.emit('keypress', '', { name: 'down' }); + } + input.emit('keypress', '', { name: 'space' }); + input.emit('keypress', '', { name: 'return' }); + + const value = await result; + + expect(value).toEqual(['opt6']); + expect(output.buffer).toMatchSnapshot(); + }); }); From 00fcfcc2690cfc5d20cb8713633c98a930d2efd7 Mon Sep 17 00:00:00 2001 From: Paul Valladares <85648028+dreyfus92@users.noreply.github.com> Date: Mon, 15 Jun 2026 23:58:01 -0600 Subject: [PATCH 2/6] format --- packages/prompts/src/group-multi-select.ts | 2 +- packages/prompts/src/multi-select.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/prompts/src/group-multi-select.ts b/packages/prompts/src/group-multi-select.ts index 8717f269..1da4cb27 100644 --- a/packages/prompts/src/group-multi-select.ts +++ b/packages/prompts/src/group-multi-select.ts @@ -2,8 +2,8 @@ import { styleText } from 'node:util'; import { GroupMultiSelectPrompt, settings, wrapTextWithPrefix } from '@clack/core'; import { type CommonOptions, - MULTISELECT_INSTRUCTIONS, formatInstructionFooter, + MULTISELECT_INSTRUCTIONS, S_BAR, S_BAR_END, S_CHECKBOX_ACTIVE, diff --git a/packages/prompts/src/multi-select.ts b/packages/prompts/src/multi-select.ts index aed7eaa5..32fa160d 100644 --- a/packages/prompts/src/multi-select.ts +++ b/packages/prompts/src/multi-select.ts @@ -2,8 +2,8 @@ import { styleText } from 'node:util'; import { MultiSelectPrompt, settings, wrapTextWithPrefix } from '@clack/core'; import { type CommonOptions, - MULTISELECT_INSTRUCTIONS, formatInstructionFooter, + MULTISELECT_INSTRUCTIONS, S_BAR, S_BAR_END, S_CHECKBOX_ACTIVE, From 1e674ac59a8f73d6bfbdf8f5021a950868a1fece Mon Sep 17 00:00:00 2001 From: Paul Valladares <85648028+dreyfus92@users.noreply.github.com> Date: Tue, 16 Jun 2026 12:07:28 -0600 Subject: [PATCH 3/6] apply suggestions from @43081j's review --- .changeset/calm-feet-lead.md | 2 +- packages/prompts/src/common.ts | 10 +- packages/prompts/src/group-multi-select.ts | 7 +- packages/prompts/src/multi-select.ts | 7 +- packages/prompts/src/select.ts | 32 ++- .../group-multi-select.test.ts.snap | 232 ---------------- .../test/__snapshots__/select.test.ts.snap | 262 ++++++++++++++++++ packages/prompts/test/select.test.ts | 15 + 8 files changed, 315 insertions(+), 252 deletions(-) diff --git a/.changeset/calm-feet-lead.md b/.changeset/calm-feet-lead.md index 33c4f52b..8085ad28 100644 --- a/.changeset/calm-feet-lead.md +++ b/.changeset/calm-feet-lead.md @@ -2,4 +2,4 @@ "@clack/prompts": minor --- -Add an opt-in `instructions` option to `multiselect` and `groupMultiselect`. When enabled, the active prompt shows a keyboard hints footer. Defaults to `false`, so existing prompts are unchanged. +Add an opt-in `instructions` option to `select`, `multiselect`, and `groupMultiselect`. When enabled, the active prompt shows a keyboard hints footer matching the autocomplete style. Defaults to `false`, so existing prompts are unchanged. diff --git a/packages/prompts/src/common.ts b/packages/prompts/src/common.ts index 53217592..1a0755c1 100644 --- a/packages/prompts/src/common.ts +++ b/packages/prompts/src/common.ts @@ -74,20 +74,14 @@ export interface CommonOptions { withGuide?: boolean; } -export const MULTISELECT_INSTRUCTIONS = [ - `${styleText('dim', '↑/↓')} to navigate`, - `${styleText('dim', 'Space:')} select`, - `${styleText('dim', 'Enter:')} confirm`, -]; - export function formatInstructionFooter( instructions: string[] | null, hasGuide: boolean -): { text: string; lineCount: number } { +): string[] { const guidePrefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : ''; const footerLines = instructions ? [`${guidePrefix}${instructions.join(' • ')}`] : []; if (hasGuide) { footerLines.push(styleText('cyan', S_BAR_END)); } - return { text: footerLines.join('\n'), lineCount: footerLines.length + 1 }; + return footerLines; } diff --git a/packages/prompts/src/group-multi-select.ts b/packages/prompts/src/group-multi-select.ts index 1da4cb27..bf627bf8 100644 --- a/packages/prompts/src/group-multi-select.ts +++ b/packages/prompts/src/group-multi-select.ts @@ -3,7 +3,6 @@ import { GroupMultiSelectPrompt, settings, wrapTextWithPrefix } from '@clack/cor import { type CommonOptions, formatInstructionFooter, - MULTISELECT_INSTRUCTIONS, S_BAR, S_BAR_END, S_CHECKBOX_ACTIVE, @@ -12,7 +11,7 @@ import { symbol, } from './common.js'; import { limitOptions } from './limit-options.js'; -import type { Option } from './select.js'; +import { MULTISELECT_INSTRUCTIONS, type Option } from './select.js'; /** * Options for the {@link groupMultiselect} prompt. @@ -295,10 +294,12 @@ export const groupMultiselect = (opts: GroupMultiSelectOptions) => default: { const guidePrefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : ''; const titleLineCount = title.split('\n').length; - const { text: footerText, lineCount: footerLineCount } = formatInstructionFooter( + const footerLines = formatInstructionFooter( showInstructions ? MULTISELECT_INSTRUCTIONS : null, hasGuide ); + const footerText = footerLines.join('\n'); + const footerLineCount = footerLines.length + 1; const optionsText = limitOptions({ output: opts.output, options: this.options, diff --git a/packages/prompts/src/multi-select.ts b/packages/prompts/src/multi-select.ts index 32fa160d..30c5dc60 100644 --- a/packages/prompts/src/multi-select.ts +++ b/packages/prompts/src/multi-select.ts @@ -3,7 +3,6 @@ import { MultiSelectPrompt, settings, wrapTextWithPrefix } from '@clack/core'; import { type CommonOptions, formatInstructionFooter, - MULTISELECT_INSTRUCTIONS, S_BAR, S_BAR_END, S_CHECKBOX_ACTIVE, @@ -13,7 +12,7 @@ import { symbolBar, } from './common.js'; import { limitOptions } from './limit-options.js'; -import type { Option } from './select.js'; +import { MULTISELECT_INSTRUCTIONS, type Option } from './select.js'; export interface MultiSelectOptions extends CommonOptions { message: string; @@ -180,10 +179,12 @@ export const multiselect = (opts: MultiSelectOptions) => { default: { const prefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : ''; const titleLineCount = title.split('\n').length; - const { text: footerText, lineCount: footerLineCount } = formatInstructionFooter( + const footerLines = formatInstructionFooter( showInstructions ? MULTISELECT_INSTRUCTIONS : null, hasGuide ); + const footerText = footerLines.join('\n'); + const footerLineCount = footerLines.length + 1; return `${title}${prefix}${limitOptions({ output: opts.output, options: this.options, diff --git a/packages/prompts/src/select.ts b/packages/prompts/src/select.ts index a3407cdb..b88d47f2 100644 --- a/packages/prompts/src/select.ts +++ b/packages/prompts/src/select.ts @@ -2,8 +2,8 @@ import { styleText } from 'node:util'; import { SelectPrompt, settings, wrapTextWithPrefix } from '@clack/core'; import { type CommonOptions, + formatInstructionFooter, S_BAR, - S_BAR_END, S_RADIO_ACTIVE, S_RADIO_INACTIVE, symbol, @@ -11,6 +11,17 @@ import { } from './common.js'; import { limitOptions } from './limit-options.js'; +export const SELECT_INSTRUCTIONS = [ + `${styleText('dim', '↑/↓')} to navigate`, + `${styleText('dim', 'Enter:')} confirm`, +]; + +export const MULTISELECT_INSTRUCTIONS = [ + `${styleText('dim', '↑/↓')} to navigate`, + `${styleText('dim', 'Space:')} select`, + `${styleText('dim', 'Enter:')} confirm`, +]; + type Primitive = Readonly; export type Option = Value extends Primitive @@ -70,6 +81,12 @@ export interface SelectOptions extends CommonOptions { options: Option[]; initialValue?: Value; maxItems?: number; + /** + * Show keyboard instructions below the option list. + * @default false + * @since 1.1.0 + */ + instructions?: boolean; } const computeLabel = (label: string, format: (text: string) => string) => { @@ -106,6 +123,8 @@ export const select = (opts: SelectOptions) => { } }; + const showInstructions = opts.instructions ?? false; + return new SelectPrompt({ options: opts.options, signal: opts.signal, @@ -145,10 +164,13 @@ export const select = (opts: SelectOptions) => { } default: { const prefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : ''; - const prefixEnd = hasGuide ? styleText('cyan', S_BAR_END) : ''; - // Calculate rowPadding: title lines + footer lines (S_BAR_END + trailing newline) const titleLineCount = title.split('\n').length; - const footerLineCount = hasGuide ? 2 : 1; // S_BAR_END + trailing newline (or just trailing newline) + const footerLines = formatInstructionFooter( + showInstructions ? SELECT_INSTRUCTIONS : null, + hasGuide + ); + const footerText = footerLines.join('\n'); + const footerLineCount = footerLines.length + 1; return `${title}${prefix}${limitOptions({ output: opts.output, cursor: this.cursor, @@ -158,7 +180,7 @@ export const select = (opts: SelectOptions) => { rowPadding: titleLineCount + footerLineCount, style: (item, active) => opt(item, item.disabled ? 'disabled' : active ? 'active' : 'inactive'), - }).join(`\n${prefix}`)}\n${prefixEnd}\n`; + }).join(`\n${prefix}`)}\n${footerText}\n`; } } }, diff --git a/packages/prompts/test/__snapshots__/group-multi-select.test.ts.snap b/packages/prompts/test/__snapshots__/group-multi-select.test.ts.snap index 8bdb9c79..46bedfb2 100644 --- a/packages/prompts/test/__snapshots__/group-multi-select.test.ts.snap +++ b/packages/prompts/test/__snapshots__/group-multi-select.test.ts.snap @@ -411,122 +411,6 @@ exports[`groupMultiselect (isCI = false) > instructions: true renders footer 1`] ] `; -exports[`groupMultiselect (isCI = false) > instructions: true with maxItems renders a sliding window 1`] = ` -[ - "", - "│ -◆ foo -│ ◻ group1 -│ │ ◻ group1value0 -│ │ ◻ group1value1 -│ │ ◻ group1value2 -│ │ ◻ group1value3 -│ ... -│ ↑/↓ to navigate • Space: select • Enter: confirm -└ -", - "", - "", - "", - "│ ◻ group1 -│ │ ◻ group1value0 -│ │ ◻ group1value1 -│ │ ◻ group1value2 -│ │ ◻ group1value3 -│ ... -│ ↑/↓ to navigate • Space: select • Enter: confirm -└ -", - "", - "", - "", - "│ │ ◻ group1value0 -│ │ ◻ group1value1 -│ │ ◻ group1value2 -│ │ ◻ group1value3 -│ ... -│ ↑/↓ to navigate • Space: select • Enter: confirm -└ -", - "", - "", - "", - "│ │ ◻ group1value1 -│ │ ◻ group1value2 -│ │ ◻ group1value3 -│ ... -│ ↑/↓ to navigate • Space: select • Enter: confirm -└ -", - "", - "", - "", - "│ ... -│ │ ◻ group1value1 -│ │ ◻ group1value2 -│ │ ◻ group1value3 -│ │ ◻ group1value4 -│ ... -│ ↑/↓ to navigate • Space: select • Enter: confirm -└ -", - "", - "", - "", - "│ │ ◻ group1value2 -│ │ ◻ group1value3 -│ │ ◻ group1value4 -│ └ ◻ group1value5 -│ ... -│ ↑/↓ to navigate • Space: select • Enter: confirm -└ -", - "", - "", - "", - "│ │ ◻ group1value3 -│ │ ◻ group1value4 -│ └ ◻ group1value5 -│ ◻ group2 -│ ... -│ ↑/↓ to navigate • Space: select • Enter: confirm -└ -", - "", - "", - "", - "│ └ ◼ group1value5", - "", - "", - "", - "", - "◇ foo -│ group1value5", - " -", - "", -] -`; - -exports[`groupMultiselect (isCI = false) > instructions: true with withGuide: false renders footer without guide 1`] = ` -[ - "", - "◆ foo -◻ group1 -│ ◻ group1value0 -└ ◻ group1value1 -↑/↓ to navigate • Space: select • Enter: confirm -", - "", - "", - "◇ foo -", - " -", - "", -] -`; - exports[`groupMultiselect (isCI = false) > maxItems renders a sliding window 1`] = ` [ "", @@ -1458,122 +1342,6 @@ exports[`groupMultiselect (isCI = true) > instructions: true renders footer 1`] ] `; -exports[`groupMultiselect (isCI = true) > instructions: true with maxItems renders a sliding window 1`] = ` -[ - "", - "│ -◆ foo -│ ◻ group1 -│ │ ◻ group1value0 -│ │ ◻ group1value1 -│ │ ◻ group1value2 -│ │ ◻ group1value3 -│ ... -│ ↑/↓ to navigate • Space: select • Enter: confirm -└ -", - "", - "", - "", - "│ ◻ group1 -│ │ ◻ group1value0 -│ │ ◻ group1value1 -│ │ ◻ group1value2 -│ │ ◻ group1value3 -│ ... -│ ↑/↓ to navigate • Space: select • Enter: confirm -└ -", - "", - "", - "", - "│ │ ◻ group1value0 -│ │ ◻ group1value1 -│ │ ◻ group1value2 -│ │ ◻ group1value3 -│ ... -│ ↑/↓ to navigate • Space: select • Enter: confirm -└ -", - "", - "", - "", - "│ │ ◻ group1value1 -│ │ ◻ group1value2 -│ │ ◻ group1value3 -│ ... -│ ↑/↓ to navigate • Space: select • Enter: confirm -└ -", - "", - "", - "", - "│ ... -│ │ ◻ group1value1 -│ │ ◻ group1value2 -│ │ ◻ group1value3 -│ │ ◻ group1value4 -│ ... -│ ↑/↓ to navigate • Space: select • Enter: confirm -└ -", - "", - "", - "", - "│ │ ◻ group1value2 -│ │ ◻ group1value3 -│ │ ◻ group1value4 -│ └ ◻ group1value5 -│ ... -│ ↑/↓ to navigate • Space: select • Enter: confirm -└ -", - "", - "", - "", - "│ │ ◻ group1value3 -│ │ ◻ group1value4 -│ └ ◻ group1value5 -│ ◻ group2 -│ ... -│ ↑/↓ to navigate • Space: select • Enter: confirm -└ -", - "", - "", - "", - "│ └ ◼ group1value5", - "", - "", - "", - "", - "◇ foo -│ group1value5", - " -", - "", -] -`; - -exports[`groupMultiselect (isCI = true) > instructions: true with withGuide: false renders footer without guide 1`] = ` -[ - "", - "◆ foo -◻ group1 -│ ◻ group1value0 -└ ◻ group1value1 -↑/↓ to navigate • Space: select • Enter: confirm -", - "", - "", - "◇ foo -", - " -", - "", -] -`; - exports[`groupMultiselect (isCI = true) > maxItems renders a sliding window 1`] = ` [ "", diff --git a/packages/prompts/test/__snapshots__/select.test.ts.snap b/packages/prompts/test/__snapshots__/select.test.ts.snap index b213bff7..21c83011 100644 --- a/packages/prompts/test/__snapshots__/select.test.ts.snap +++ b/packages/prompts/test/__snapshots__/select.test.ts.snap @@ -236,6 +236,137 @@ exports[`select (isCI = false) > handles mixed size re-renders 1`] = ` ] `; +exports[`select (isCI = false) > instructions: true renders footer 1`] = ` +[ + "", + "│ +◆ foo +│ ● opt0 +│ ○ opt1 +│ ↑/↓ to navigate • Enter: confirm +└ +", + "", + "", + "", + "◇ foo +│ opt0", + " +", + "", +] +`; + +exports[`select (isCI = false) > instructions: true with maxItems renders a sliding window 1`] = ` +[ + "", + "│ +◆ foo +│ ● opt0 +│ ○ opt1 +│ ○ opt2 +│ ○ opt3 +│ ○ opt4 +│ ... +│ ↑/↓ to navigate • Enter: confirm +└ +", + "", + "", + "", + "│ ○ opt0 +│ ● opt1 +│ ○ opt2 +│ ○ opt3 +│ ○ opt4 +│ ... +│ ↑/↓ to navigate • Enter: confirm +└ +", + "", + "", + "", + "│ ○ opt1 +│ ● opt2 +│ ○ opt3 +│ ○ opt4 +│ ... +│ ↑/↓ to navigate • Enter: confirm +└ +", + "", + "", + "", + "│ ○ opt2 +│ ● opt3 +│ ○ opt4 +│ ... +│ ↑/↓ to navigate • Enter: confirm +└ +", + "", + "", + "", + "│ ... +│ ○ opt2 +│ ○ opt3 +│ ● opt4 +│ ○ opt5 +│ ... +│ ↑/↓ to navigate • Enter: confirm +└ +", + "", + "", + "", + "│ ○ opt3 +│ ○ opt4 +│ ● opt5 +│ ○ opt6 +│ ... +│ ↑/↓ to navigate • Enter: confirm +└ +", + "", + "", + "", + "│ ○ opt4 +│ ○ opt5 +│ ● opt6 +│ ○ opt7 +│ ... +│ ↑/↓ to navigate • Enter: confirm +└ +", + "", + "", + "", + "◇ foo +│ opt6", + " +", + "", +] +`; + +exports[`select (isCI = false) > instructions: true with withGuide: false renders footer without guide 1`] = ` +[ + "", + "◆ foo +● opt0 +○ opt1 +↑/↓ to navigate • Enter: confirm +", + "", + "", + "◇ foo +opt0", + " +", + "", +] +`; + exports[`select (isCI = false) > renders disabled options 1`] = ` [ "", @@ -715,6 +846,137 @@ exports[`select (isCI = true) > handles mixed size re-renders 1`] = ` ] `; +exports[`select (isCI = true) > instructions: true renders footer 1`] = ` +[ + "", + "│ +◆ foo +│ ● opt0 +│ ○ opt1 +│ ↑/↓ to navigate • Enter: confirm +└ +", + "", + "", + "", + "◇ foo +│ opt0", + " +", + "", +] +`; + +exports[`select (isCI = true) > instructions: true with maxItems renders a sliding window 1`] = ` +[ + "", + "│ +◆ foo +│ ● opt0 +│ ○ opt1 +│ ○ opt2 +│ ○ opt3 +│ ○ opt4 +│ ... +│ ↑/↓ to navigate • Enter: confirm +└ +", + "", + "", + "", + "│ ○ opt0 +│ ● opt1 +│ ○ opt2 +│ ○ opt3 +│ ○ opt4 +│ ... +│ ↑/↓ to navigate • Enter: confirm +└ +", + "", + "", + "", + "│ ○ opt1 +│ ● opt2 +│ ○ opt3 +│ ○ opt4 +│ ... +│ ↑/↓ to navigate • Enter: confirm +└ +", + "", + "", + "", + "│ ○ opt2 +│ ● opt3 +│ ○ opt4 +│ ... +│ ↑/↓ to navigate • Enter: confirm +└ +", + "", + "", + "", + "│ ... +│ ○ opt2 +│ ○ opt3 +│ ● opt4 +│ ○ opt5 +│ ... +│ ↑/↓ to navigate • Enter: confirm +└ +", + "", + "", + "", + "│ ○ opt3 +│ ○ opt4 +│ ● opt5 +│ ○ opt6 +│ ... +│ ↑/↓ to navigate • Enter: confirm +└ +", + "", + "", + "", + "│ ○ opt4 +│ ○ opt5 +│ ● opt6 +│ ○ opt7 +│ ... +│ ↑/↓ to navigate • Enter: confirm +└ +", + "", + "", + "", + "◇ foo +│ opt6", + " +", + "", +] +`; + +exports[`select (isCI = true) > instructions: true with withGuide: false renders footer without guide 1`] = ` +[ + "", + "◆ foo +● opt0 +○ opt1 +↑/↓ to navigate • Enter: confirm +", + "", + "", + "◇ foo +opt0", + " +", + "", +] +`; + exports[`select (isCI = true) > renders disabled options 1`] = ` [ "", diff --git a/packages/prompts/test/select.test.ts b/packages/prompts/test/select.test.ts index 447a0e1b..0d536bc9 100644 --- a/packages/prompts/test/select.test.ts +++ b/packages/prompts/test/select.test.ts @@ -340,6 +340,21 @@ describe.each(['true', 'false'])('select (isCI = %s)', (isCI) => { expect(output.buffer).toMatchSnapshot(); }); + test('instructions: true renders footer', async () => { + const result = prompts.select({ + message: 'foo', + options: [{ value: 'opt0' }, { value: 'opt1' }], + instructions: true, + input, + output, + }); + + input.emit('keypress', '', { name: 'return' }); + + await result; + expect(output.buffer).toMatchSnapshot(); + }); + test('correctly limits options with explicit multiline message', async () => { output.rows = 12; From 786d4b53f9e2a96c289240e4be2572959b01d642 Mon Sep 17 00:00:00 2001 From: Paul Valladares <85648028+dreyfus92@users.noreply.github.com> Date: Tue, 16 Jun 2026 14:03:47 -0600 Subject: [PATCH 4/6] apply more suggestions --- packages/prompts/src/common.ts | 7 ++----- packages/prompts/src/group-multi-select.ts | 12 +++++++----- packages/prompts/src/multi-select.ts | 17 ++++++++++++----- packages/prompts/src/select.ts | 16 ++++++---------- 4 files changed, 27 insertions(+), 25 deletions(-) diff --git a/packages/prompts/src/common.ts b/packages/prompts/src/common.ts index 1a0755c1..e4a0c379 100644 --- a/packages/prompts/src/common.ts +++ b/packages/prompts/src/common.ts @@ -74,12 +74,9 @@ export interface CommonOptions { withGuide?: boolean; } -export function formatInstructionFooter( - instructions: string[] | null, - hasGuide: boolean -): string[] { +export function formatInstructionFooter(instructions: string[], hasGuide: boolean): string[] { const guidePrefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : ''; - const footerLines = instructions ? [`${guidePrefix}${instructions.join(' • ')}`] : []; + const footerLines = [`${guidePrefix}${instructions.join(' • ')}`]; if (hasGuide) { footerLines.push(styleText('cyan', S_BAR_END)); } diff --git a/packages/prompts/src/group-multi-select.ts b/packages/prompts/src/group-multi-select.ts index bf627bf8..c343641b 100644 --- a/packages/prompts/src/group-multi-select.ts +++ b/packages/prompts/src/group-multi-select.ts @@ -11,7 +11,8 @@ import { symbol, } from './common.js'; import { limitOptions } from './limit-options.js'; -import { MULTISELECT_INSTRUCTIONS, type Option } from './select.js'; +import { MULTISELECT_INSTRUCTIONS } from './multi-select.js'; +import type { Option } from './select.js'; /** * Options for the {@link groupMultiselect} prompt. @@ -294,10 +295,11 @@ export const groupMultiselect = (opts: GroupMultiSelectOptions) => default: { const guidePrefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : ''; const titleLineCount = title.split('\n').length; - const footerLines = formatInstructionFooter( - showInstructions ? MULTISELECT_INSTRUCTIONS : null, - hasGuide - ); + const footerLines = showInstructions + ? formatInstructionFooter(MULTISELECT_INSTRUCTIONS, hasGuide) + : hasGuide + ? [styleText('cyan', S_BAR_END)] + : []; const footerText = footerLines.join('\n'); const footerLineCount = footerLines.length + 1; const optionsText = limitOptions({ diff --git a/packages/prompts/src/multi-select.ts b/packages/prompts/src/multi-select.ts index 30c5dc60..b78ed8de 100644 --- a/packages/prompts/src/multi-select.ts +++ b/packages/prompts/src/multi-select.ts @@ -12,7 +12,13 @@ import { symbolBar, } from './common.js'; import { limitOptions } from './limit-options.js'; -import { MULTISELECT_INSTRUCTIONS, type Option } from './select.js'; +import type { Option } from './select.js'; + +export const MULTISELECT_INSTRUCTIONS = [ + `${styleText('dim', '↑/↓')} to navigate`, + `${styleText('dim', 'Space:')} select`, + `${styleText('dim', 'Enter:')} confirm`, +]; export interface MultiSelectOptions extends CommonOptions { message: string; @@ -179,10 +185,11 @@ export const multiselect = (opts: MultiSelectOptions) => { default: { const prefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : ''; const titleLineCount = title.split('\n').length; - const footerLines = formatInstructionFooter( - showInstructions ? MULTISELECT_INSTRUCTIONS : null, - hasGuide - ); + const footerLines = showInstructions + ? formatInstructionFooter(MULTISELECT_INSTRUCTIONS, hasGuide) + : hasGuide + ? [styleText('cyan', S_BAR_END)] + : []; const footerText = footerLines.join('\n'); const footerLineCount = footerLines.length + 1; return `${title}${prefix}${limitOptions({ diff --git a/packages/prompts/src/select.ts b/packages/prompts/src/select.ts index b88d47f2..8fe6be1a 100644 --- a/packages/prompts/src/select.ts +++ b/packages/prompts/src/select.ts @@ -4,6 +4,7 @@ import { type CommonOptions, formatInstructionFooter, S_BAR, + S_BAR_END, S_RADIO_ACTIVE, S_RADIO_INACTIVE, symbol, @@ -16,12 +17,6 @@ export const SELECT_INSTRUCTIONS = [ `${styleText('dim', 'Enter:')} confirm`, ]; -export const MULTISELECT_INSTRUCTIONS = [ - `${styleText('dim', '↑/↓')} to navigate`, - `${styleText('dim', 'Space:')} select`, - `${styleText('dim', 'Enter:')} confirm`, -]; - type Primitive = Readonly; export type Option = Value extends Primitive @@ -165,10 +160,11 @@ export const select = (opts: SelectOptions) => { default: { const prefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : ''; const titleLineCount = title.split('\n').length; - const footerLines = formatInstructionFooter( - showInstructions ? SELECT_INSTRUCTIONS : null, - hasGuide - ); + const footerLines = showInstructions + ? formatInstructionFooter(SELECT_INSTRUCTIONS, hasGuide) + : hasGuide + ? [styleText('cyan', S_BAR_END)] + : []; const footerText = footerLines.join('\n'); const footerLineCount = footerLines.length + 1; return `${title}${prefix}${limitOptions({ From e47c59ab3d1771da80337dc4e157556c616efdcf Mon Sep 17 00:00:00 2001 From: Paul Valladares <85648028+dreyfus92@users.noreply.github.com> Date: Tue, 16 Jun 2026 14:04:18 -0600 Subject: [PATCH 5/6] remove: orphaned snapshots from select tests --- .../test/__snapshots__/select.test.ts.snap | 220 ------------------ 1 file changed, 220 deletions(-) diff --git a/packages/prompts/test/__snapshots__/select.test.ts.snap b/packages/prompts/test/__snapshots__/select.test.ts.snap index 21c83011..a1b00774 100644 --- a/packages/prompts/test/__snapshots__/select.test.ts.snap +++ b/packages/prompts/test/__snapshots__/select.test.ts.snap @@ -257,116 +257,6 @@ exports[`select (isCI = false) > instructions: true renders footer 1`] = ` ] `; -exports[`select (isCI = false) > instructions: true with maxItems renders a sliding window 1`] = ` -[ - "", - "│ -◆ foo -│ ● opt0 -│ ○ opt1 -│ ○ opt2 -│ ○ opt3 -│ ○ opt4 -│ ... -│ ↑/↓ to navigate • Enter: confirm -└ -", - "", - "", - "", - "│ ○ opt0 -│ ● opt1 -│ ○ opt2 -│ ○ opt3 -│ ○ opt4 -│ ... -│ ↑/↓ to navigate • Enter: confirm -└ -", - "", - "", - "", - "│ ○ opt1 -│ ● opt2 -│ ○ opt3 -│ ○ opt4 -│ ... -│ ↑/↓ to navigate • Enter: confirm -└ -", - "", - "", - "", - "│ ○ opt2 -│ ● opt3 -│ ○ opt4 -│ ... -│ ↑/↓ to navigate • Enter: confirm -└ -", - "", - "", - "", - "│ ... -│ ○ opt2 -│ ○ opt3 -│ ● opt4 -│ ○ opt5 -│ ... -│ ↑/↓ to navigate • Enter: confirm -└ -", - "", - "", - "", - "│ ○ opt3 -│ ○ opt4 -│ ● opt5 -│ ○ opt6 -│ ... -│ ↑/↓ to navigate • Enter: confirm -└ -", - "", - "", - "", - "│ ○ opt4 -│ ○ opt5 -│ ● opt6 -│ ○ opt7 -│ ... -│ ↑/↓ to navigate • Enter: confirm -└ -", - "", - "", - "", - "◇ foo -│ opt6", - " -", - "", -] -`; - -exports[`select (isCI = false) > instructions: true with withGuide: false renders footer without guide 1`] = ` -[ - "", - "◆ foo -● opt0 -○ opt1 -↑/↓ to navigate • Enter: confirm -", - "", - "", - "◇ foo -opt0", - " -", - "", -] -`; - exports[`select (isCI = false) > renders disabled options 1`] = ` [ "", @@ -867,116 +757,6 @@ exports[`select (isCI = true) > instructions: true renders footer 1`] = ` ] `; -exports[`select (isCI = true) > instructions: true with maxItems renders a sliding window 1`] = ` -[ - "", - "│ -◆ foo -│ ● opt0 -│ ○ opt1 -│ ○ opt2 -│ ○ opt3 -│ ○ opt4 -│ ... -│ ↑/↓ to navigate • Enter: confirm -└ -", - "", - "", - "", - "│ ○ opt0 -│ ● opt1 -│ ○ opt2 -│ ○ opt3 -│ ○ opt4 -│ ... -│ ↑/↓ to navigate • Enter: confirm -└ -", - "", - "", - "", - "│ ○ opt1 -│ ● opt2 -│ ○ opt3 -│ ○ opt4 -│ ... -│ ↑/↓ to navigate • Enter: confirm -└ -", - "", - "", - "", - "│ ○ opt2 -│ ● opt3 -│ ○ opt4 -│ ... -│ ↑/↓ to navigate • Enter: confirm -└ -", - "", - "", - "", - "│ ... -│ ○ opt2 -│ ○ opt3 -│ ● opt4 -│ ○ opt5 -│ ... -│ ↑/↓ to navigate • Enter: confirm -└ -", - "", - "", - "", - "│ ○ opt3 -│ ○ opt4 -│ ● opt5 -│ ○ opt6 -│ ... -│ ↑/↓ to navigate • Enter: confirm -└ -", - "", - "", - "", - "│ ○ opt4 -│ ○ opt5 -│ ● opt6 -│ ○ opt7 -│ ... -│ ↑/↓ to navigate • Enter: confirm -└ -", - "", - "", - "", - "◇ foo -│ opt6", - " -", - "", -] -`; - -exports[`select (isCI = true) > instructions: true with withGuide: false renders footer without guide 1`] = ` -[ - "", - "◆ foo -● opt0 -○ opt1 -↑/↓ to navigate • Enter: confirm -", - "", - "", - "◇ foo -opt0", - " -", - "", -] -`; - exports[`select (isCI = true) > renders disabled options 1`] = ` [ "", From 873779c6075e6dbe5e0a4a2a124f3ec63057b814 Mon Sep 17 00:00:00 2001 From: Paul Valladares <85648028+dreyfus92@users.noreply.github.com> Date: Tue, 16 Jun 2026 19:06:13 -0600 Subject: [PATCH 6/6] refactor(prompts): always display keyboard instruction footers for select, multiselect, and groupMultiselect prompts --- .changeset/calm-feet-lead.md | 2 +- examples/changesets/index.ts | 3 - packages/prompts/src/group-multi-select.ts | 13 +- packages/prompts/src/multi-select.ts | 12 +- packages/prompts/src/select.ts | 15 +- .../group-multi-select.test.ts.snap | 512 ++++++++++-------- .../__snapshots__/multi-select.test.ts.snap | 506 +++++++++-------- .../test/__snapshots__/select.test.ts.snap | 376 ++++++++++--- .../prompts/test/group-multi-select.test.ts | 18 - packages/prompts/test/multi-select.test.ts | 22 +- packages/prompts/test/select.test.ts | 26 +- 11 files changed, 886 insertions(+), 619 deletions(-) diff --git a/.changeset/calm-feet-lead.md b/.changeset/calm-feet-lead.md index 8085ad28..3d312945 100644 --- a/.changeset/calm-feet-lead.md +++ b/.changeset/calm-feet-lead.md @@ -2,4 +2,4 @@ "@clack/prompts": minor --- -Add an opt-in `instructions` option to `select`, `multiselect`, and `groupMultiselect`. When enabled, the active prompt shows a keyboard hints footer matching the autocomplete style. Defaults to `false`, so existing prompts are unchanged. +Add keyboard instruction footers to `select`, `multiselect`, and `groupMultiselect` in the active state, matching autocomplete. No option — always shown. diff --git a/examples/changesets/index.ts b/examples/changesets/index.ts index c561aaf4..cb9226db 100644 --- a/examples/changesets/index.ts +++ b/examples/changesets/index.ts @@ -19,7 +19,6 @@ async function main() { packages: () => p.groupMultiselect({ message: 'Which packages would you like to include?', - instructions: true, options: { 'changed packages': [ { value: '@scope/a' }, @@ -37,7 +36,6 @@ async function main() { const packages = results.packages ?? []; return p.multiselect({ message: `Which packages should have a ${color.red('major')} bump?`, - instructions: true, options: packages.map((value) => ({ value })), required: false, }); @@ -49,7 +47,6 @@ async function main() { if (possiblePackages.length === 0) return; return p.multiselect({ message: `Which packages should have a ${color.yellow('minor')} bump?`, - instructions: true, options: possiblePackages.map((value) => ({ value })), required: false, }); diff --git a/packages/prompts/src/group-multi-select.ts b/packages/prompts/src/group-multi-select.ts index c343641b..054228f6 100644 --- a/packages/prompts/src/group-multi-select.ts +++ b/packages/prompts/src/group-multi-select.ts @@ -60,12 +60,6 @@ export interface GroupMultiSelectOptions extends CommonOptions { * @default 0 */ groupSpacing?: number; - - /** - * Show keyboard instructions below the option list. - * @default false - */ - instructions?: boolean; } /** @@ -197,7 +191,6 @@ export const groupMultiselect = (opts: GroupMultiSelectOptions) => ); }; const required = opts.required ?? true; - const showInstructions = opts.instructions ?? false; return new GroupMultiSelectPrompt({ options: opts.options, @@ -295,11 +288,7 @@ export const groupMultiselect = (opts: GroupMultiSelectOptions) => default: { const guidePrefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : ''; const titleLineCount = title.split('\n').length; - const footerLines = showInstructions - ? formatInstructionFooter(MULTISELECT_INSTRUCTIONS, hasGuide) - : hasGuide - ? [styleText('cyan', S_BAR_END)] - : []; + const footerLines = formatInstructionFooter(MULTISELECT_INSTRUCTIONS, hasGuide); const footerText = footerLines.join('\n'); const footerLineCount = footerLines.length + 1; const optionsText = limitOptions({ diff --git a/packages/prompts/src/multi-select.ts b/packages/prompts/src/multi-select.ts index b78ed8de..a04df770 100644 --- a/packages/prompts/src/multi-select.ts +++ b/packages/prompts/src/multi-select.ts @@ -27,11 +27,6 @@ export interface MultiSelectOptions extends CommonOptions { maxItems?: number; required?: boolean; cursorAt?: Value; - /** - * Show keyboard instructions below the option list. - * @default false - */ - instructions?: boolean; } const computeLabel = (label: string, format: (text: string) => string) => { return label @@ -82,7 +77,6 @@ export const multiselect = (opts: MultiSelectOptions) => { return `${styleText('dim', S_CHECKBOX_INACTIVE)} ${computeLabel(label, (text) => styleText('dim', text))}`; }; const required = opts.required ?? true; - const showInstructions = opts.instructions ?? false; return new MultiSelectPrompt({ options: opts.options, @@ -185,11 +179,7 @@ export const multiselect = (opts: MultiSelectOptions) => { default: { const prefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : ''; const titleLineCount = title.split('\n').length; - const footerLines = showInstructions - ? formatInstructionFooter(MULTISELECT_INSTRUCTIONS, hasGuide) - : hasGuide - ? [styleText('cyan', S_BAR_END)] - : []; + const footerLines = formatInstructionFooter(MULTISELECT_INSTRUCTIONS, hasGuide); const footerText = footerLines.join('\n'); const footerLineCount = footerLines.length + 1; return `${title}${prefix}${limitOptions({ diff --git a/packages/prompts/src/select.ts b/packages/prompts/src/select.ts index 8fe6be1a..5d012b13 100644 --- a/packages/prompts/src/select.ts +++ b/packages/prompts/src/select.ts @@ -4,7 +4,6 @@ import { type CommonOptions, formatInstructionFooter, S_BAR, - S_BAR_END, S_RADIO_ACTIVE, S_RADIO_INACTIVE, symbol, @@ -76,12 +75,6 @@ export interface SelectOptions extends CommonOptions { options: Option[]; initialValue?: Value; maxItems?: number; - /** - * Show keyboard instructions below the option list. - * @default false - * @since 1.1.0 - */ - instructions?: boolean; } const computeLabel = (label: string, format: (text: string) => string) => { @@ -118,8 +111,6 @@ export const select = (opts: SelectOptions) => { } }; - const showInstructions = opts.instructions ?? false; - return new SelectPrompt({ options: opts.options, signal: opts.signal, @@ -160,11 +151,7 @@ export const select = (opts: SelectOptions) => { default: { const prefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : ''; const titleLineCount = title.split('\n').length; - const footerLines = showInstructions - ? formatInstructionFooter(SELECT_INSTRUCTIONS, hasGuide) - : hasGuide - ? [styleText('cyan', S_BAR_END)] - : []; + const footerLines = formatInstructionFooter(SELECT_INSTRUCTIONS, hasGuide); const footerText = footerLines.join('\n'); const footerLineCount = footerLines.length + 1; return `${title}${prefix}${limitOptions({ diff --git a/packages/prompts/test/__snapshots__/group-multi-select.test.ts.snap b/packages/prompts/test/__snapshots__/group-multi-select.test.ts.snap index 46bedfb2..2319ef31 100644 --- a/packages/prompts/test/__snapshots__/group-multi-select.test.ts.snap +++ b/packages/prompts/test/__snapshots__/group-multi-select.test.ts.snap @@ -9,6 +9,7 @@ exports[`groupMultiselect (isCI = false) > can be aborted by a signal 1`] = ` │ └ ◻ group1value0 │ ◻ group2 │ └ ◻ group2value0 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", " @@ -25,45 +26,50 @@ exports[`groupMultiselect (isCI = false) > can deselect an option 1`] = ` │ ◻ group1 │ │ ◻ group1value0 │ └ ◻ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ group1 │ │ ◻ group1value0 │ └ ◻ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◼ group1value0", - "", - "", + "", + "", "", "", "│ │ ◼ group1value0 │ └ ◻ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ group1 │ │ ◼ group1value0 │ └ ◼ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ group1 │ │ ◼ group1value0 │ └ ◻ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -82,17 +88,19 @@ exports[`groupMultiselect (isCI = false) > can select a group 1`] = ` │ ◻ group1 │ │ ◻ group1value0 │ └ ◻ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ group1 │ │ ◼ group1value0 │ └ ◼ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -111,37 +119,41 @@ exports[`groupMultiselect (isCI = false) > can select a group by selecting all m │ ◻ group1 │ │ ◻ group1value0 │ └ ◻ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ group1 │ │ ◻ group1value0 │ └ ◻ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◼ group1value0", - "", - "", + "", + "", "", "", "│ │ ◼ group1value0 │ └ ◻ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ group1 │ │ ◼ group1value0 │ └ ◼ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -161,36 +173,39 @@ exports[`groupMultiselect (isCI = false) > can select multiple options 1`] = ` │ │ ◻ group1value0 │ │ ◻ group1value1 │ └ ◻ group1value2 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ group1 │ │ ◻ group1value0 │ │ ◻ group1value1 │ └ ◻ group1value2 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◼ group1value0", - "", - "", + "", + "", "", "", "│ │ ◼ group1value0 │ │ ◻ group1value1 │ └ ◻ group1value2 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◼ group1value1", - "", - "", + "", + "", "", "", "◇ foo @@ -209,9 +224,10 @@ exports[`groupMultiselect (isCI = false) > can submit empty selection when requi │ ◻ group1 │ │ ◻ group1value0 │ └ ◻ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -230,14 +246,15 @@ exports[`groupMultiselect (isCI = false) > cursorAt sets initial selection 1`] = │ ◻ group1 │ │ ◻ group1value0 │ └ ◻ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ └ ◼ group1value1", - "", - "", + "", + "", "", "", "◇ foo @@ -255,7 +272,7 @@ exports[`groupMultiselect (isCI = false) > global withGuide: false removes guide ◻ group1 │ ◻ group1value0 └ ◻ group1value1 - +↑/↓ to navigate • Space: select • Enter: confirm ", "", "", @@ -263,7 +280,7 @@ exports[`groupMultiselect (isCI = false) > global withGuide: false removes guide "◻ group1 │ ◻ group1value0 └ ◻ group1value1 - +↑/↓ to navigate • Space: select • Enter: confirm ", "", "", @@ -289,27 +306,30 @@ exports[`groupMultiselect (isCI = false) > groupSpacing > negative spacing is ig │ └ ◻ group1value0 │ ◻ group2 │ └ ◻ group2value0 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ group1 │ └ ◻ group1value0 │ ◻ group2 │ └ ◻ group2value0 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ group1 │ └ ◼ group1value0 │ ◻ group2 │ └ ◻ group2value0 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -333,9 +353,10 @@ exports[`groupMultiselect (isCI = false) > groupSpacing > renders spaced groups │ │ ◻ group2 │ └ ◻ group2value0 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ group1 @@ -344,9 +365,10 @@ exports[`groupMultiselect (isCI = false) > groupSpacing > renders spaced groups │ │ ◻ group2 │ └ ◻ group2value0 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ group1 @@ -355,9 +377,10 @@ exports[`groupMultiselect (isCI = false) > groupSpacing > renders spaced groups │ │ ◻ group2 │ └ ◻ group2value0 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -376,27 +399,6 @@ exports[`groupMultiselect (isCI = false) > initial values can be set 1`] = ` │ ◻ group1 │ │ ◻ group1value0 │ └ ◼ group1value1 -└ -", - "", - "", - "", - "◇ foo -│ group1value1", - " -", - "", -] -`; - -exports[`groupMultiselect (isCI = false) > instructions: true renders footer 1`] = ` -[ - "", - "│ -◆ foo -│ ◻ group1 -│ │ ◻ group1value0 -│ └ ◻ group1value1 │ ↑/↓ to navigate • Space: select • Enter: confirm └ ", @@ -404,7 +406,7 @@ exports[`groupMultiselect (isCI = false) > instructions: true renders footer 1`] "", "", "◇ foo -│", +│ group1value1", " ", "", @@ -422,9 +424,10 @@ exports[`groupMultiselect (isCI = false) > maxItems renders a sliding window 1`] │ │ ◻ group1value2 │ │ ◻ group1value3 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ group1 @@ -433,9 +436,10 @@ exports[`groupMultiselect (isCI = false) > maxItems renders a sliding window 1`] │ │ ◻ group1value2 │ │ ◻ group1value3 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◻ group1value0 @@ -443,18 +447,20 @@ exports[`groupMultiselect (isCI = false) > maxItems renders a sliding window 1`] │ │ ◻ group1value2 │ │ ◻ group1value3 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◻ group1value1 │ │ ◻ group1value2 │ │ ◻ group1value3 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ... @@ -463,9 +469,10 @@ exports[`groupMultiselect (isCI = false) > maxItems renders a sliding window 1`] │ │ ◻ group1value3 │ │ ◻ group1value4 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◻ group1value2 @@ -473,9 +480,10 @@ exports[`groupMultiselect (isCI = false) > maxItems renders a sliding window 1`] │ │ ◻ group1value4 │ └ ◻ group1value5 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◻ group1value3 @@ -483,14 +491,15 @@ exports[`groupMultiselect (isCI = false) > maxItems renders a sliding window 1`] │ └ ◻ group1value5 │ ◻ group2 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ └ ◼ group1value5", - "", - "", + "", + "", "", "", "◇ foo @@ -509,9 +518,10 @@ exports[`groupMultiselect (isCI = false) > renders error when nothing selected 1 │ ◻ group1 │ │ ◻ group1value0 │ └ ◻ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "▲ foo @@ -528,14 +538,15 @@ exports[`groupMultiselect (isCI = false) > renders error when nothing selected 1 │ ◻ group1 │ │ ◻ group1value0 │ └ ◻ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◼ group1value0", - "", - "", + "", + "", "", "", "◇ foo @@ -556,9 +567,10 @@ exports[`groupMultiselect (isCI = false) > renders message with options 1`] = ` │ └ ◻ group1value1 │ ◻ group2 │ └ ◻ group2value0 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ group1 @@ -566,14 +578,15 @@ exports[`groupMultiselect (isCI = false) > renders message with options 1`] = ` │ └ ◻ group1value1 │ ◻ group2 │ └ ◻ group2value0 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◼ group1value0", - "", - "", + "", + "", "", "", "◇ foo @@ -592,14 +605,15 @@ exports[`groupMultiselect (isCI = false) > selectableGroups = false > cannot sel │  group1 │  ◻ group1value0 │  ◻ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│  ◼ group1value0", - "", - "", + "", + "", "", "", "◇ foo @@ -618,26 +632,28 @@ exports[`groupMultiselect (isCI = false) > selectableGroups = false > selecting │  group1 │  ◻ group1value0 │  ◻ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│  ◼ group1value0", - "", - "", + "", + "", "", "", "│  ◼ group1value0 │  ◻ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│  ◼ group1value1", - "", - "", + "", + "", "", "", "◇ foo @@ -659,9 +675,10 @@ exports[`groupMultiselect (isCI = false) > sliding window loops downwards 1`] = │ │ ◻ group1value2 │ │ ◻ group1value3 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ group1 @@ -670,9 +687,10 @@ exports[`groupMultiselect (isCI = false) > sliding window loops downwards 1`] = │ │ ◻ group1value2 │ │ ◻ group1value3 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◻ group1value0 @@ -680,18 +698,20 @@ exports[`groupMultiselect (isCI = false) > sliding window loops downwards 1`] = │ │ ◻ group1value2 │ │ ◻ group1value3 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◻ group1value1 │ │ ◻ group1value2 │ │ ◻ group1value3 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ... @@ -700,9 +720,10 @@ exports[`groupMultiselect (isCI = false) > sliding window loops downwards 1`] = │ │ ◻ group1value3 │ │ ◻ group1value4 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◻ group1value2 @@ -710,9 +731,10 @@ exports[`groupMultiselect (isCI = false) > sliding window loops downwards 1`] = │ │ ◻ group1value4 │ └ ◻ group1value5 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◻ group1value3 @@ -720,9 +742,10 @@ exports[`groupMultiselect (isCI = false) > sliding window loops downwards 1`] = │ └ ◻ group1value5 │ ◻ group2 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◻ group1value4 @@ -730,9 +753,10 @@ exports[`groupMultiselect (isCI = false) > sliding window loops downwards 1`] = │ ◻ group2 │ │ ◻ group2value0 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ └ ◻ group1value5 @@ -740,9 +764,10 @@ exports[`groupMultiselect (isCI = false) > sliding window loops downwards 1`] = │ │ ◻ group2value0 │ │ ◻ group2value1 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ group2 @@ -750,9 +775,10 @@ exports[`groupMultiselect (isCI = false) > sliding window loops downwards 1`] = │ │ ◻ group2value1 │ │ ◻ group2value2 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◻ group2value0 @@ -760,9 +786,10 @@ exports[`groupMultiselect (isCI = false) > sliding window loops downwards 1`] = │ │ ◻ group2value2 │ │ ◻ group2value3 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◻ group2value1 @@ -770,24 +797,27 @@ exports[`groupMultiselect (isCI = false) > sliding window loops downwards 1`] = │ │ ◻ group2value3 │ │ ◻ group2value4 │ └ ◻ group2value5 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◻ group2value3 │ │ ◻ group2value4 │ └ ◻ group2value5 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◻ group2value4 │ └ ◻ group2value5 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ group1 @@ -796,9 +826,10 @@ exports[`groupMultiselect (isCI = false) > sliding window loops downwards 1`] = │ │ ◻ group1value2 │ │ ◻ group1value3 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ group1 @@ -807,14 +838,15 @@ exports[`groupMultiselect (isCI = false) > sliding window loops downwards 1`] = │ │ ◻ group1value2 │ │ ◻ group1value3 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◼ group1value0", - "", - "", + "", + "", "", "", "◇ foo @@ -836,9 +868,10 @@ exports[`groupMultiselect (isCI = false) > sliding window loops upwards 1`] = ` │ │ ◻ group1value2 │ │ ◻ group1value3 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ... @@ -847,14 +880,15 @@ exports[`groupMultiselect (isCI = false) > sliding window loops upwards 1`] = ` │ │ ◻ group2value3 │ │ ◻ group2value4 │ └ ◻ group2value5 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ └ ◼ group2value5", - "", - "", + "", + "", "", "", "◇ foo @@ -873,22 +907,24 @@ exports[`groupMultiselect (isCI = false) > values can be non-primitive 1`] = ` │ ◻ group1 │ │ ◻ value0 │ └ ◻ value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ group1 │ │ ◻ value0 │ └ ◻ value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◼ value0", - "", - "", + "", + "", "", "", "◇ foo @@ -906,7 +942,7 @@ exports[`groupMultiselect (isCI = false) > withGuide: false removes guide 1`] = ◻ group1 │ ◻ group1value0 └ ◻ group1value1 - +↑/↓ to navigate • Space: select • Enter: confirm ", "", "", @@ -914,7 +950,7 @@ exports[`groupMultiselect (isCI = false) > withGuide: false removes guide 1`] = "◻ group1 │ ◻ group1value0 └ ◻ group1value1 - +↑/↓ to navigate • Space: select • Enter: confirm ", "", "", @@ -940,6 +976,7 @@ exports[`groupMultiselect (isCI = true) > can be aborted by a signal 1`] = ` │ └ ◻ group1value0 │ ◻ group2 │ └ ◻ group2value0 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", " @@ -956,45 +993,50 @@ exports[`groupMultiselect (isCI = true) > can deselect an option 1`] = ` │ ◻ group1 │ │ ◻ group1value0 │ └ ◻ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ group1 │ │ ◻ group1value0 │ └ ◻ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◼ group1value0", - "", - "", + "", + "", "", "", "│ │ ◼ group1value0 │ └ ◻ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ group1 │ │ ◼ group1value0 │ └ ◼ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ group1 │ │ ◼ group1value0 │ └ ◻ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -1013,17 +1055,19 @@ exports[`groupMultiselect (isCI = true) > can select a group 1`] = ` │ ◻ group1 │ │ ◻ group1value0 │ └ ◻ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ group1 │ │ ◼ group1value0 │ └ ◼ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -1042,37 +1086,41 @@ exports[`groupMultiselect (isCI = true) > can select a group by selecting all me │ ◻ group1 │ │ ◻ group1value0 │ └ ◻ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ group1 │ │ ◻ group1value0 │ └ ◻ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◼ group1value0", - "", - "", + "", + "", "", "", "│ │ ◼ group1value0 │ └ ◻ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ group1 │ │ ◼ group1value0 │ └ ◼ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -1092,36 +1140,39 @@ exports[`groupMultiselect (isCI = true) > can select multiple options 1`] = ` │ │ ◻ group1value0 │ │ ◻ group1value1 │ └ ◻ group1value2 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ group1 │ │ ◻ group1value0 │ │ ◻ group1value1 │ └ ◻ group1value2 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◼ group1value0", - "", - "", + "", + "", "", "", "│ │ ◼ group1value0 │ │ ◻ group1value1 │ └ ◻ group1value2 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◼ group1value1", - "", - "", + "", + "", "", "", "◇ foo @@ -1140,9 +1191,10 @@ exports[`groupMultiselect (isCI = true) > can submit empty selection when requir │ ◻ group1 │ │ ◻ group1value0 │ └ ◻ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -1161,14 +1213,15 @@ exports[`groupMultiselect (isCI = true) > cursorAt sets initial selection 1`] = │ ◻ group1 │ │ ◻ group1value0 │ └ ◻ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ └ ◼ group1value1", - "", - "", + "", + "", "", "", "◇ foo @@ -1186,7 +1239,7 @@ exports[`groupMultiselect (isCI = true) > global withGuide: false removes guide ◻ group1 │ ◻ group1value0 └ ◻ group1value1 - +↑/↓ to navigate • Space: select • Enter: confirm ", "", "", @@ -1194,7 +1247,7 @@ exports[`groupMultiselect (isCI = true) > global withGuide: false removes guide "◻ group1 │ ◻ group1value0 └ ◻ group1value1 - +↑/↓ to navigate • Space: select • Enter: confirm ", "", "", @@ -1220,27 +1273,30 @@ exports[`groupMultiselect (isCI = true) > groupSpacing > negative spacing is ign │ └ ◻ group1value0 │ ◻ group2 │ └ ◻ group2value0 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ group1 │ └ ◻ group1value0 │ ◻ group2 │ └ ◻ group2value0 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ group1 │ └ ◼ group1value0 │ ◻ group2 │ └ ◻ group2value0 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -1264,9 +1320,10 @@ exports[`groupMultiselect (isCI = true) > groupSpacing > renders spaced groups 1 │ │ ◻ group2 │ └ ◻ group2value0 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ group1 @@ -1275,9 +1332,10 @@ exports[`groupMultiselect (isCI = true) > groupSpacing > renders spaced groups 1 │ │ ◻ group2 │ └ ◻ group2value0 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ group1 @@ -1286,9 +1344,10 @@ exports[`groupMultiselect (isCI = true) > groupSpacing > renders spaced groups 1 │ │ ◻ group2 │ └ ◻ group2value0 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -1307,27 +1366,6 @@ exports[`groupMultiselect (isCI = true) > initial values can be set 1`] = ` │ ◻ group1 │ │ ◻ group1value0 │ └ ◼ group1value1 -└ -", - "", - "", - "", - "◇ foo -│ group1value1", - " -", - "", -] -`; - -exports[`groupMultiselect (isCI = true) > instructions: true renders footer 1`] = ` -[ - "", - "│ -◆ foo -│ ◻ group1 -│ │ ◻ group1value0 -│ └ ◻ group1value1 │ ↑/↓ to navigate • Space: select • Enter: confirm └ ", @@ -1335,7 +1373,7 @@ exports[`groupMultiselect (isCI = true) > instructions: true renders footer 1`] "", "", "◇ foo -│", +│ group1value1", " ", "", @@ -1353,9 +1391,10 @@ exports[`groupMultiselect (isCI = true) > maxItems renders a sliding window 1`] │ │ ◻ group1value2 │ │ ◻ group1value3 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ group1 @@ -1364,9 +1403,10 @@ exports[`groupMultiselect (isCI = true) > maxItems renders a sliding window 1`] │ │ ◻ group1value2 │ │ ◻ group1value3 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◻ group1value0 @@ -1374,18 +1414,20 @@ exports[`groupMultiselect (isCI = true) > maxItems renders a sliding window 1`] │ │ ◻ group1value2 │ │ ◻ group1value3 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◻ group1value1 │ │ ◻ group1value2 │ │ ◻ group1value3 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ... @@ -1394,9 +1436,10 @@ exports[`groupMultiselect (isCI = true) > maxItems renders a sliding window 1`] │ │ ◻ group1value3 │ │ ◻ group1value4 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◻ group1value2 @@ -1404,9 +1447,10 @@ exports[`groupMultiselect (isCI = true) > maxItems renders a sliding window 1`] │ │ ◻ group1value4 │ └ ◻ group1value5 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◻ group1value3 @@ -1414,14 +1458,15 @@ exports[`groupMultiselect (isCI = true) > maxItems renders a sliding window 1`] │ └ ◻ group1value5 │ ◻ group2 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ └ ◼ group1value5", - "", - "", + "", + "", "", "", "◇ foo @@ -1440,9 +1485,10 @@ exports[`groupMultiselect (isCI = true) > renders error when nothing selected 1` │ ◻ group1 │ │ ◻ group1value0 │ └ ◻ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "▲ foo @@ -1459,14 +1505,15 @@ exports[`groupMultiselect (isCI = true) > renders error when nothing selected 1` │ ◻ group1 │ │ ◻ group1value0 │ └ ◻ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◼ group1value0", - "", - "", + "", + "", "", "", "◇ foo @@ -1487,9 +1534,10 @@ exports[`groupMultiselect (isCI = true) > renders message with options 1`] = ` │ └ ◻ group1value1 │ ◻ group2 │ └ ◻ group2value0 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ group1 @@ -1497,14 +1545,15 @@ exports[`groupMultiselect (isCI = true) > renders message with options 1`] = ` │ └ ◻ group1value1 │ ◻ group2 │ └ ◻ group2value0 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◼ group1value0", - "", - "", + "", + "", "", "", "◇ foo @@ -1523,14 +1572,15 @@ exports[`groupMultiselect (isCI = true) > selectableGroups = false > cannot sele │  group1 │  ◻ group1value0 │  ◻ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│  ◼ group1value0", - "", - "", + "", + "", "", "", "◇ foo @@ -1549,26 +1599,28 @@ exports[`groupMultiselect (isCI = true) > selectableGroups = false > selecting a │  group1 │  ◻ group1value0 │  ◻ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│  ◼ group1value0", - "", - "", + "", + "", "", "", "│  ◼ group1value0 │  ◻ group1value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│  ◼ group1value1", - "", - "", + "", + "", "", "", "◇ foo @@ -1590,9 +1642,10 @@ exports[`groupMultiselect (isCI = true) > sliding window loops downwards 1`] = ` │ │ ◻ group1value2 │ │ ◻ group1value3 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ group1 @@ -1601,9 +1654,10 @@ exports[`groupMultiselect (isCI = true) > sliding window loops downwards 1`] = ` │ │ ◻ group1value2 │ │ ◻ group1value3 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◻ group1value0 @@ -1611,18 +1665,20 @@ exports[`groupMultiselect (isCI = true) > sliding window loops downwards 1`] = ` │ │ ◻ group1value2 │ │ ◻ group1value3 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◻ group1value1 │ │ ◻ group1value2 │ │ ◻ group1value3 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ... @@ -1631,9 +1687,10 @@ exports[`groupMultiselect (isCI = true) > sliding window loops downwards 1`] = ` │ │ ◻ group1value3 │ │ ◻ group1value4 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◻ group1value2 @@ -1641,9 +1698,10 @@ exports[`groupMultiselect (isCI = true) > sliding window loops downwards 1`] = ` │ │ ◻ group1value4 │ └ ◻ group1value5 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◻ group1value3 @@ -1651,9 +1709,10 @@ exports[`groupMultiselect (isCI = true) > sliding window loops downwards 1`] = ` │ └ ◻ group1value5 │ ◻ group2 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◻ group1value4 @@ -1661,9 +1720,10 @@ exports[`groupMultiselect (isCI = true) > sliding window loops downwards 1`] = ` │ ◻ group2 │ │ ◻ group2value0 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ └ ◻ group1value5 @@ -1671,9 +1731,10 @@ exports[`groupMultiselect (isCI = true) > sliding window loops downwards 1`] = ` │ │ ◻ group2value0 │ │ ◻ group2value1 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ group2 @@ -1681,9 +1742,10 @@ exports[`groupMultiselect (isCI = true) > sliding window loops downwards 1`] = ` │ │ ◻ group2value1 │ │ ◻ group2value2 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◻ group2value0 @@ -1691,9 +1753,10 @@ exports[`groupMultiselect (isCI = true) > sliding window loops downwards 1`] = ` │ │ ◻ group2value2 │ │ ◻ group2value3 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◻ group2value1 @@ -1701,24 +1764,27 @@ exports[`groupMultiselect (isCI = true) > sliding window loops downwards 1`] = ` │ │ ◻ group2value3 │ │ ◻ group2value4 │ └ ◻ group2value5 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◻ group2value3 │ │ ◻ group2value4 │ └ ◻ group2value5 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◻ group2value4 │ └ ◻ group2value5 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ group1 @@ -1727,9 +1793,10 @@ exports[`groupMultiselect (isCI = true) > sliding window loops downwards 1`] = ` │ │ ◻ group1value2 │ │ ◻ group1value3 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ group1 @@ -1738,14 +1805,15 @@ exports[`groupMultiselect (isCI = true) > sliding window loops downwards 1`] = ` │ │ ◻ group1value2 │ │ ◻ group1value3 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◼ group1value0", - "", - "", + "", + "", "", "", "◇ foo @@ -1767,9 +1835,10 @@ exports[`groupMultiselect (isCI = true) > sliding window loops upwards 1`] = ` │ │ ◻ group1value2 │ │ ◻ group1value3 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ... @@ -1778,14 +1847,15 @@ exports[`groupMultiselect (isCI = true) > sliding window loops upwards 1`] = ` │ │ ◻ group2value3 │ │ ◻ group2value4 │ └ ◻ group2value5 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ └ ◼ group2value5", - "", - "", + "", + "", "", "", "◇ foo @@ -1804,22 +1874,24 @@ exports[`groupMultiselect (isCI = true) > values can be non-primitive 1`] = ` │ ◻ group1 │ │ ◻ value0 │ └ ◻ value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ group1 │ │ ◻ value0 │ └ ◻ value1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ │ ◼ value0", - "", - "", + "", + "", "", "", "◇ foo @@ -1837,7 +1909,7 @@ exports[`groupMultiselect (isCI = true) > withGuide: false removes guide 1`] = ` ◻ group1 │ ◻ group1value0 └ ◻ group1value1 - +↑/↓ to navigate • Space: select • Enter: confirm ", "", "", @@ -1845,7 +1917,7 @@ exports[`groupMultiselect (isCI = true) > withGuide: false removes guide 1`] = ` "◻ group1 │ ◻ group1value0 └ ◻ group1value1 - +↑/↓ to navigate • Space: select • Enter: confirm ", "", "", diff --git a/packages/prompts/test/__snapshots__/multi-select.test.ts.snap b/packages/prompts/test/__snapshots__/multi-select.test.ts.snap index e0206cd8..40aeb081 100644 --- a/packages/prompts/test/__snapshots__/multi-select.test.ts.snap +++ b/packages/prompts/test/__snapshots__/multi-select.test.ts.snap @@ -7,6 +7,7 @@ exports[`multiselect (isCI = false) > can be aborted by a signal 1`] = ` ◆ foo │ ◻ opt0 │ ◻ opt1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", " @@ -22,9 +23,10 @@ exports[`multiselect (isCI = false) > can cancel 1`] = ` ◆ foo │ ◻ opt0 │ ◻ opt1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "■ foo @@ -42,14 +44,15 @@ exports[`multiselect (isCI = false) > can render option hints 1`] = ` ◆ foo │ ◻ opt0 (Hint 0) │ ◻ opt1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ opt0 (Hint 0)", - "", - "", + "", + "", "", "", "◇ foo @@ -67,14 +70,15 @@ exports[`multiselect (isCI = false) > can set cursorAt to preselect an option 1` ◆ foo │ ◻ opt0 │ ◻ opt1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ opt1", - "", - "", + "", + "", "", "", "◇ foo @@ -92,14 +96,15 @@ exports[`multiselect (isCI = false) > can set custom labels 1`] = ` ◆ foo │ ◻ Option 0 │ ◻ Option 1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ Option 0", - "", - "", + "", + "", "", "", "◇ foo @@ -117,9 +122,10 @@ exports[`multiselect (isCI = false) > can set initial values 1`] = ` ◆ foo │ ◻ opt0 │ ◼ opt1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -137,9 +143,10 @@ exports[`multiselect (isCI = false) > can submit without selection when required ◆ foo │ ◻ opt0 │ ◻ opt1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -156,7 +163,7 @@ exports[`multiselect (isCI = false) > global withGuide: false removes guide 1`] "◆ foo ◻ opt0 ◻ opt1 - +↑/↓ to navigate • Space: select • Enter: confirm ", "", "", @@ -173,28 +180,7 @@ exports[`multiselect (isCI = false) > global withGuide: false removes guide 1`] ] `; -exports[`multiselect (isCI = false) > instructions: true renders footer 1`] = ` -[ - "", - "│ -◆ foo -│ ◻ opt0 -│ ◻ opt1 -│ ↑/↓ to navigate • Space: select • Enter: confirm -└ -", - "", - "", - "", - "◇ foo -│ none", - " -", - "", -] -`; - -exports[`multiselect (isCI = false) > instructions: true with maxItems renders a sliding window 1`] = ` +exports[`multiselect (isCI = false) > maxItems accounts for instruction footer 1`] = ` [ "", "│ @@ -291,24 +277,6 @@ exports[`multiselect (isCI = false) > instructions: true with maxItems renders a ] `; -exports[`multiselect (isCI = false) > instructions: true with withGuide: false renders footer without guide 1`] = ` -[ - "", - "◆ foo -◻ opt0 -◻ opt1 -↑/↓ to navigate • Space: select • Enter: confirm -", - "", - "", - "◇ foo -none", - " -", - "", -] -`; - exports[`multiselect (isCI = false) > maxItems renders a sliding window 1`] = ` [ "", @@ -320,9 +288,10 @@ exports[`multiselect (isCI = false) > maxItems renders a sliding window 1`] = ` │ ◻ opt3 │ ◻ opt4 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ opt0 @@ -331,9 +300,10 @@ exports[`multiselect (isCI = false) > maxItems renders a sliding window 1`] = ` │ ◻ opt3 │ ◻ opt4 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ opt1 @@ -341,18 +311,20 @@ exports[`multiselect (isCI = false) > maxItems renders a sliding window 1`] = ` │ ◻ opt3 │ ◻ opt4 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ opt2 │ ◻ opt3 │ ◻ opt4 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ... @@ -361,9 +333,10 @@ exports[`multiselect (isCI = false) > maxItems renders a sliding window 1`] = ` │ ◻ opt4 │ ◻ opt5 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ opt3 @@ -371,9 +344,10 @@ exports[`multiselect (isCI = false) > maxItems renders a sliding window 1`] = ` │ ◻ opt5 │ ◻ opt6 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ opt4 @@ -381,14 +355,15 @@ exports[`multiselect (isCI = false) > maxItems renders a sliding window 1`] = ` │ ◻ opt6 │ ◻ opt7 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ opt6", - "", - "", + "", + "", "", "", "◇ foo @@ -407,14 +382,15 @@ exports[`multiselect (isCI = false) > renders disabled options 1`] = ` │ ◻ opt0 │ ◻ opt1 │ ◻ opt2 (Hint 2) +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ opt1", - "", - "", + "", + "", "", "", "◇ foo @@ -425,6 +401,24 @@ exports[`multiselect (isCI = false) > renders disabled options 1`] = ` ] `; +exports[`multiselect (isCI = false) > renders instructions without guide 1`] = ` +[ + "", + "◆ foo +◻ opt0 +◻ opt1 +↑/↓ to navigate • Space: select • Enter: confirm +", + "", + "", + "◇ foo +none", + " +", + "", +] +`; + exports[`multiselect (isCI = false) > renders message 1`] = ` [ "", @@ -432,14 +426,15 @@ exports[`multiselect (isCI = false) > renders message 1`] = ` ◆ foo │ ◻ opt0 │ ◻ opt1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ opt0", - "", - "", + "", + "", "", "", "◇ foo @@ -458,27 +453,29 @@ exports[`multiselect (isCI = false) > renders multiple cancelled values 1`] = ` │ ◻ opt0 │ ◻ opt1 │ ◻ opt2 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ opt0", - "", - "", + "", + "", "", "", "│ ◼ opt0 │ ◻ opt1 │ ◻ opt2 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ opt1", - "", - "", + "", + "", "", "", "■ foo @@ -498,34 +495,37 @@ exports[`multiselect (isCI = false) > renders multiple selected options 1`] = ` │ ◻ opt0 │ ◻ opt1 │ ◻ opt2 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ opt0", - "", - "", + "", + "", "", "", "│ ◼ opt0 │ ◻ opt1 │ ◻ opt2 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ opt1", - "", - "", + "", + "", "", "", "│ ◼ opt1 │ ◻ opt2 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -543,9 +543,10 @@ exports[`multiselect (isCI = false) > renders validation errors 1`] = ` ◆ foo │ ◻ opt0 │ ◻ opt1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "▲ foo @@ -560,9 +561,10 @@ exports[`multiselect (isCI = false) > renders validation errors 1`] = ` "◆ foo │ ◼ opt0 │ ◻ opt1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -581,24 +583,27 @@ exports[`multiselect (isCI = false) > shows hints for all selected options 1`] = │ ◼ opt0 (Hint 0) │ ◼ opt1 (Hint 1) │ ◻ opt2 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ opt0 (Hint 0) │ ◼ opt1 (Hint 1) │ ◻ opt2 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ opt1 (Hint 1) │ ◻ opt2 (Hint 2) +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -620,9 +625,10 @@ exports[`multiselect (isCI = false) > sliding window loops downwards 1`] = ` │ ◻ opt3 │ ◻ opt4 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ opt0 @@ -631,9 +637,10 @@ exports[`multiselect (isCI = false) > sliding window loops downwards 1`] = ` │ ◻ opt3 │ ◻ opt4 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ opt1 @@ -641,18 +648,20 @@ exports[`multiselect (isCI = false) > sliding window loops downwards 1`] = ` │ ◻ opt3 │ ◻ opt4 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ opt2 │ ◻ opt3 │ ◻ opt4 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ... @@ -661,9 +670,10 @@ exports[`multiselect (isCI = false) > sliding window loops downwards 1`] = ` │ ◻ opt4 │ ◻ opt5 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ opt3 @@ -671,9 +681,10 @@ exports[`multiselect (isCI = false) > sliding window loops downwards 1`] = ` │ ◻ opt5 │ ◻ opt6 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ opt4 @@ -681,9 +692,10 @@ exports[`multiselect (isCI = false) > sliding window loops downwards 1`] = ` │ ◻ opt6 │ ◻ opt7 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ opt5 @@ -691,9 +703,10 @@ exports[`multiselect (isCI = false) > sliding window loops downwards 1`] = ` │ ◻ opt7 │ ◻ opt8 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ opt6 @@ -701,9 +714,10 @@ exports[`multiselect (isCI = false) > sliding window loops downwards 1`] = ` │ ◻ opt8 │ ◻ opt9 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ opt7 @@ -711,24 +725,27 @@ exports[`multiselect (isCI = false) > sliding window loops downwards 1`] = ` │ ◻ opt9 │ ◻ opt10 │ ◻ opt11 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ opt9 │ ◻ opt10 │ ◻ opt11 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ opt10 │ ◻ opt11 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ opt0 @@ -737,14 +754,15 @@ exports[`multiselect (isCI = false) > sliding window loops downwards 1`] = ` │ ◻ opt3 │ ◻ opt4 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ opt0", - "", - "", + "", + "", "", "", "◇ foo @@ -766,9 +784,10 @@ exports[`multiselect (isCI = false) > sliding window loops upwards 1`] = ` │ ◻ opt3 │ ◻ opt4 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ... @@ -777,14 +796,15 @@ exports[`multiselect (isCI = false) > sliding window loops upwards 1`] = ` │ ◻ opt9 │ ◻ opt10 │ ◻ opt11 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ opt11", - "", - "", + "", + "", "", "", "◇ foo @@ -801,7 +821,7 @@ exports[`multiselect (isCI = false) > withGuide: false removes guide 1`] = ` "◆ foo ◻ opt0 ◻ opt1 - +↑/↓ to navigate • Space: select • Enter: confirm ", "", "", @@ -831,14 +851,15 @@ exports[`multiselect (isCI = false) > wraps cancelled state with long options 1` │ 1 Option 1 Option 1 Option  │ 1 Option 1 Option 1 Option  │ 1 Option 1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ Option 0 Option 0 Option ", - "", - "", + "", + "", "", "", "■ foo @@ -862,14 +883,15 @@ exports[`multiselect (isCI = false) > wraps long messages 1`] = ` │ foo foo foo foo foo foo foo │ ◻ opt0 │ ◻ opt1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ opt0", - "", - "", + "", + "", "", "", "◇ foo foo foo foo foo foo foo @@ -895,14 +917,15 @@ exports[`multiselect (isCI = false) > wraps success state with long options 1`] │ 1 Option 1 Option 1 Option  │ 1 Option 1 Option 1 Option  │ 1 Option 1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ Option 0 Option 0 Option ", - "", - "", + "", + "", "", "", "◇ foo @@ -923,6 +946,7 @@ exports[`multiselect (isCI = true) > can be aborted by a signal 1`] = ` ◆ foo │ ◻ opt0 │ ◻ opt1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", " @@ -938,9 +962,10 @@ exports[`multiselect (isCI = true) > can cancel 1`] = ` ◆ foo │ ◻ opt0 │ ◻ opt1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "■ foo @@ -958,14 +983,15 @@ exports[`multiselect (isCI = true) > can render option hints 1`] = ` ◆ foo │ ◻ opt0 (Hint 0) │ ◻ opt1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ opt0 (Hint 0)", - "", - "", + "", + "", "", "", "◇ foo @@ -983,14 +1009,15 @@ exports[`multiselect (isCI = true) > can set cursorAt to preselect an option 1`] ◆ foo │ ◻ opt0 │ ◻ opt1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ opt1", - "", - "", + "", + "", "", "", "◇ foo @@ -1008,14 +1035,15 @@ exports[`multiselect (isCI = true) > can set custom labels 1`] = ` ◆ foo │ ◻ Option 0 │ ◻ Option 1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ Option 0", - "", - "", + "", + "", "", "", "◇ foo @@ -1033,9 +1061,10 @@ exports[`multiselect (isCI = true) > can set initial values 1`] = ` ◆ foo │ ◻ opt0 │ ◼ opt1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -1053,9 +1082,10 @@ exports[`multiselect (isCI = true) > can submit without selection when required ◆ foo │ ◻ opt0 │ ◻ opt1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -1072,7 +1102,7 @@ exports[`multiselect (isCI = true) > global withGuide: false removes guide 1`] = "◆ foo ◻ opt0 ◻ opt1 - +↑/↓ to navigate • Space: select • Enter: confirm ", "", "", @@ -1089,28 +1119,7 @@ exports[`multiselect (isCI = true) > global withGuide: false removes guide 1`] = ] `; -exports[`multiselect (isCI = true) > instructions: true renders footer 1`] = ` -[ - "", - "│ -◆ foo -│ ◻ opt0 -│ ◻ opt1 -│ ↑/↓ to navigate • Space: select • Enter: confirm -└ -", - "", - "", - "", - "◇ foo -│ none", - " -", - "", -] -`; - -exports[`multiselect (isCI = true) > instructions: true with maxItems renders a sliding window 1`] = ` +exports[`multiselect (isCI = true) > maxItems accounts for instruction footer 1`] = ` [ "", "│ @@ -1207,24 +1216,6 @@ exports[`multiselect (isCI = true) > instructions: true with maxItems renders a ] `; -exports[`multiselect (isCI = true) > instructions: true with withGuide: false renders footer without guide 1`] = ` -[ - "", - "◆ foo -◻ opt0 -◻ opt1 -↑/↓ to navigate • Space: select • Enter: confirm -", - "", - "", - "◇ foo -none", - " -", - "", -] -`; - exports[`multiselect (isCI = true) > maxItems renders a sliding window 1`] = ` [ "", @@ -1236,9 +1227,10 @@ exports[`multiselect (isCI = true) > maxItems renders a sliding window 1`] = ` │ ◻ opt3 │ ◻ opt4 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ opt0 @@ -1247,9 +1239,10 @@ exports[`multiselect (isCI = true) > maxItems renders a sliding window 1`] = ` │ ◻ opt3 │ ◻ opt4 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ opt1 @@ -1257,18 +1250,20 @@ exports[`multiselect (isCI = true) > maxItems renders a sliding window 1`] = ` │ ◻ opt3 │ ◻ opt4 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ opt2 │ ◻ opt3 │ ◻ opt4 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ... @@ -1277,9 +1272,10 @@ exports[`multiselect (isCI = true) > maxItems renders a sliding window 1`] = ` │ ◻ opt4 │ ◻ opt5 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ opt3 @@ -1287,9 +1283,10 @@ exports[`multiselect (isCI = true) > maxItems renders a sliding window 1`] = ` │ ◻ opt5 │ ◻ opt6 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ opt4 @@ -1297,14 +1294,15 @@ exports[`multiselect (isCI = true) > maxItems renders a sliding window 1`] = ` │ ◻ opt6 │ ◻ opt7 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ opt6", - "", - "", + "", + "", "", "", "◇ foo @@ -1323,14 +1321,15 @@ exports[`multiselect (isCI = true) > renders disabled options 1`] = ` │ ◻ opt0 │ ◻ opt1 │ ◻ opt2 (Hint 2) +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ opt1", - "", - "", + "", + "", "", "", "◇ foo @@ -1341,6 +1340,24 @@ exports[`multiselect (isCI = true) > renders disabled options 1`] = ` ] `; +exports[`multiselect (isCI = true) > renders instructions without guide 1`] = ` +[ + "", + "◆ foo +◻ opt0 +◻ opt1 +↑/↓ to navigate • Space: select • Enter: confirm +", + "", + "", + "◇ foo +none", + " +", + "", +] +`; + exports[`multiselect (isCI = true) > renders message 1`] = ` [ "", @@ -1348,14 +1365,15 @@ exports[`multiselect (isCI = true) > renders message 1`] = ` ◆ foo │ ◻ opt0 │ ◻ opt1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ opt0", - "", - "", + "", + "", "", "", "◇ foo @@ -1374,27 +1392,29 @@ exports[`multiselect (isCI = true) > renders multiple cancelled values 1`] = ` │ ◻ opt0 │ ◻ opt1 │ ◻ opt2 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ opt0", - "", - "", + "", + "", "", "", "│ ◼ opt0 │ ◻ opt1 │ ◻ opt2 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ opt1", - "", - "", + "", + "", "", "", "■ foo @@ -1414,34 +1434,37 @@ exports[`multiselect (isCI = true) > renders multiple selected options 1`] = ` │ ◻ opt0 │ ◻ opt1 │ ◻ opt2 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ opt0", - "", - "", + "", + "", "", "", "│ ◼ opt0 │ ◻ opt1 │ ◻ opt2 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ opt1", - "", - "", + "", + "", "", "", "│ ◼ opt1 │ ◻ opt2 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -1459,9 +1482,10 @@ exports[`multiselect (isCI = true) > renders validation errors 1`] = ` ◆ foo │ ◻ opt0 │ ◻ opt1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "▲ foo @@ -1476,9 +1500,10 @@ exports[`multiselect (isCI = true) > renders validation errors 1`] = ` "◆ foo │ ◼ opt0 │ ◻ opt1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -1497,24 +1522,27 @@ exports[`multiselect (isCI = true) > shows hints for all selected options 1`] = │ ◼ opt0 (Hint 0) │ ◼ opt1 (Hint 1) │ ◻ opt2 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ opt0 (Hint 0) │ ◼ opt1 (Hint 1) │ ◻ opt2 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ opt1 (Hint 1) │ ◻ opt2 (Hint 2) +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -1536,9 +1564,10 @@ exports[`multiselect (isCI = true) > sliding window loops downwards 1`] = ` │ ◻ opt3 │ ◻ opt4 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ opt0 @@ -1547,9 +1576,10 @@ exports[`multiselect (isCI = true) > sliding window loops downwards 1`] = ` │ ◻ opt3 │ ◻ opt4 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ opt1 @@ -1557,18 +1587,20 @@ exports[`multiselect (isCI = true) > sliding window loops downwards 1`] = ` │ ◻ opt3 │ ◻ opt4 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ opt2 │ ◻ opt3 │ ◻ opt4 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ... @@ -1577,9 +1609,10 @@ exports[`multiselect (isCI = true) > sliding window loops downwards 1`] = ` │ ◻ opt4 │ ◻ opt5 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ opt3 @@ -1587,9 +1620,10 @@ exports[`multiselect (isCI = true) > sliding window loops downwards 1`] = ` │ ◻ opt5 │ ◻ opt6 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ opt4 @@ -1597,9 +1631,10 @@ exports[`multiselect (isCI = true) > sliding window loops downwards 1`] = ` │ ◻ opt6 │ ◻ opt7 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ opt5 @@ -1607,9 +1642,10 @@ exports[`multiselect (isCI = true) > sliding window loops downwards 1`] = ` │ ◻ opt7 │ ◻ opt8 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ opt6 @@ -1617,9 +1653,10 @@ exports[`multiselect (isCI = true) > sliding window loops downwards 1`] = ` │ ◻ opt8 │ ◻ opt9 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ opt7 @@ -1627,24 +1664,27 @@ exports[`multiselect (isCI = true) > sliding window loops downwards 1`] = ` │ ◻ opt9 │ ◻ opt10 │ ◻ opt11 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ opt9 │ ◻ opt10 │ ◻ opt11 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ opt10 │ ◻ opt11 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◻ opt0 @@ -1653,14 +1693,15 @@ exports[`multiselect (isCI = true) > sliding window loops downwards 1`] = ` │ ◻ opt3 │ ◻ opt4 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ opt0", - "", - "", + "", + "", "", "", "◇ foo @@ -1682,9 +1723,10 @@ exports[`multiselect (isCI = true) > sliding window loops upwards 1`] = ` │ ◻ opt3 │ ◻ opt4 │ ... +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ... @@ -1693,14 +1735,15 @@ exports[`multiselect (isCI = true) > sliding window loops upwards 1`] = ` │ ◻ opt9 │ ◻ opt10 │ ◻ opt11 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ opt11", - "", - "", + "", + "", "", "", "◇ foo @@ -1717,7 +1760,7 @@ exports[`multiselect (isCI = true) > withGuide: false removes guide 1`] = ` "◆ foo ◻ opt0 ◻ opt1 - +↑/↓ to navigate • Space: select • Enter: confirm ", "", "", @@ -1747,14 +1790,15 @@ exports[`multiselect (isCI = true) > wraps cancelled state with long options 1`] │ 1 Option 1 Option 1 Option  │ 1 Option 1 Option 1 Option  │ 1 Option 1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ Option 0 Option 0 Option ", - "", - "", + "", + "", "", "", "■ foo @@ -1778,14 +1822,15 @@ exports[`multiselect (isCI = true) > wraps long messages 1`] = ` │ foo foo foo foo foo foo foo │ ◻ opt0 │ ◻ opt1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ opt0", - "", - "", + "", + "", "", "", "◇ foo foo foo foo foo foo foo @@ -1811,14 +1856,15 @@ exports[`multiselect (isCI = true) > wraps success state with long options 1`] = │ 1 Option 1 Option 1 Option  │ 1 Option 1 Option 1 Option  │ 1 Option 1 +│ ↑/↓ to navigate • Space: select • Enter: confirm └ ", - "", + "", "", "", "│ ◼ Option 0 Option 0 Option ", - "", - "", + "", + "", "", "", "◇ foo diff --git a/packages/prompts/test/__snapshots__/select.test.ts.snap b/packages/prompts/test/__snapshots__/select.test.ts.snap index a1b00774..4bc53b46 100644 --- a/packages/prompts/test/__snapshots__/select.test.ts.snap +++ b/packages/prompts/test/__snapshots__/select.test.ts.snap @@ -7,6 +7,7 @@ exports[`select (isCI = false) > can be aborted by a signal 1`] = ` ◆ foo │ ● opt0 │ ○ opt1 +│ ↑/↓ to navigate • Enter: confirm └ ", " @@ -22,9 +23,10 @@ exports[`select (isCI = false) > can cancel 1`] = ` ◆ foo │ ● opt0 │ ○ opt1 +│ ↑/↓ to navigate • Enter: confirm └ ", - "", + "", "", "", "■ foo @@ -46,8 +48,8 @@ exports[`select (isCI = false) > correctly limits options when message wraps to │ multiple lines │ ● Option 0 │ ○ Option 1 -│ ○ Option 2 │ ... +│ ↑/↓ to navigate • Enter: confirm └ ", "", @@ -55,35 +57,29 @@ exports[`select (isCI = false) > correctly limits options when message wraps to "", "│ ○ Option 0 │ ● Option 1 -│ ○ Option 2 -│ ... -└ -", - "", - "", - "", - "│ ○ Option 1 -│ ● Option 2 │ ... +│ ↑/↓ to navigate • Enter: confirm └ ", "", "", "", "│ ... -│ ● Option 3 -│ ○ Option 4 +│ ● Option 2 │ ... +│ ↑/↓ to navigate • Enter: confirm └ ", "", "", - "", - "│ ● Option 4 -│ ○ Option 5 -│ ... -└ -", + "", + "│ ● Option 3", + "", + "", + "", + "", + "│ ● Option 4", + "", "", "", "", @@ -108,8 +104,8 @@ exports[`select (isCI = false) > correctly limits options with explicit multilin │ ● Option 0 │ ○ Option 1 │ ○ Option 2 -│ ○ Option 3 │ ... +│ ↑/↓ to navigate • Enter: confirm └ ", "", @@ -118,8 +114,8 @@ exports[`select (isCI = false) > correctly limits options with explicit multilin "│ ○ Option 0 │ ● Option 1 │ ○ Option 2 -│ ○ Option 3 │ ... +│ ↑/↓ to navigate • Enter: confirm └ ", "", @@ -127,18 +123,18 @@ exports[`select (isCI = false) > correctly limits options with explicit multilin "", "│ ○ Option 1 │ ● Option 2 -│ ○ Option 3 │ ... +│ ↑/↓ to navigate • Enter: confirm └ ", "", "", "", "│ ... -│ ○ Option 2 │ ● Option 3 │ ○ Option 4 │ ... +│ ↑/↓ to navigate • Enter: confirm └ ", "", @@ -161,16 +157,18 @@ exports[`select (isCI = false) > down arrow selects next option 1`] = ` ◆ foo │ ● opt0 │ ○ opt1 +│ ↑/↓ to navigate • Enter: confirm └ ", - "", + "", "", "", "│ ○ opt0 │ ● opt1 +│ ↑/↓ to navigate • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -187,7 +185,7 @@ exports[`select (isCI = false) > global withGuide: false removes guide 1`] = ` "◆ foo ● opt0 ○ opt1 - +↑/↓ to navigate • Enter: confirm ", "", "", @@ -213,16 +211,17 @@ exports[`select (isCI = false) > handles mixed size re-renders 1`] = ` │ Long Option │ Long Option │ ... +│ ↑/↓ to navigate • Enter: confirm └ ", - "", + "", "", "│ ◆ Whatever │ ... -│ ○ Option 1 │ ○ Option 2 │ ● Option 3 +│ ↑/↓ to navigate • Enter: confirm └ ", "", @@ -236,21 +235,92 @@ exports[`select (isCI = false) > handles mixed size re-renders 1`] = ` ] `; -exports[`select (isCI = false) > instructions: true renders footer 1`] = ` +exports[`select (isCI = false) > maxItems accounts for instruction footer 1`] = ` [ "", "│ ◆ foo │ ● opt0 │ ○ opt1 +│ ○ opt2 +│ ○ opt3 +│ ○ opt4 +│ ... │ ↑/↓ to navigate • Enter: confirm └ ", - "", + "", + "", + "", + "│ ○ opt0 +│ ● opt1 +│ ○ opt2 +│ ○ opt3 +│ ○ opt4 +│ ... +│ ↑/↓ to navigate • Enter: confirm +└ +", + "", + "", + "", + "│ ○ opt1 +│ ● opt2 +│ ○ opt3 +│ ○ opt4 +│ ... +│ ↑/↓ to navigate • Enter: confirm +└ +", + "", + "", + "", + "│ ○ opt2 +│ ● opt3 +│ ○ opt4 +│ ... +│ ↑/↓ to navigate • Enter: confirm +└ +", + "", + "", + "", + "│ ... +│ ○ opt2 +│ ○ opt3 +│ ● opt4 +│ ○ opt5 +│ ... +│ ↑/↓ to navigate • Enter: confirm +└ +", + "", + "", + "", + "│ ○ opt3 +│ ○ opt4 +│ ● opt5 +│ ○ opt6 +│ ... +│ ↑/↓ to navigate • Enter: confirm +└ +", + "", + "", + "", + "│ ○ opt4 +│ ○ opt5 +│ ● opt6 +│ ○ opt7 +│ ... +│ ↑/↓ to navigate • Enter: confirm +└ +", + "", "", "", "◇ foo -│ opt0", +│ opt6", " ", "", @@ -265,9 +335,10 @@ exports[`select (isCI = false) > renders disabled options 1`] = ` │ ○ Option 0 │ ● Option 1 │ ○ Option 2 (Hint 2) +│ ↑/↓ to navigate • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -278,6 +349,24 @@ exports[`select (isCI = false) > renders disabled options 1`] = ` ] `; +exports[`select (isCI = false) > renders instructions without guide 1`] = ` +[ + "", + "◆ foo +● opt0 +○ opt1 +↑/↓ to navigate • Enter: confirm +", + "", + "", + "◇ foo +opt0", + " +", + "", +] +`; + exports[`select (isCI = false) > renders multi-line option labels 1`] = ` [ "", @@ -286,17 +375,19 @@ exports[`select (isCI = false) > renders multi-line option labels 1`] = ` │ ● Option 0 │ with multiple lines │ ○ Option 1 +│ ↑/↓ to navigate • Enter: confirm └ ", - "", + "", "", "", "│ ○ Option 0 │ with multiple lines │ ● Option 1 +│ ↑/↓ to navigate • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -314,9 +405,10 @@ exports[`select (isCI = false) > renders option hints 1`] = ` ◆ foo │ ● opt0 (Hint 0) │ ○ opt1 +│ ↑/↓ to navigate • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -334,9 +426,10 @@ exports[`select (isCI = false) > renders option labels 1`] = ` ◆ foo │ ● Option 0 │ ○ Option 1 +│ ↑/↓ to navigate • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -354,9 +447,10 @@ exports[`select (isCI = false) > renders options and message 1`] = ` ◆ foo │ ● opt0 │ ○ opt1 +│ ↑/↓ to navigate • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -374,23 +468,26 @@ exports[`select (isCI = false) > up arrow selects previous option 1`] = ` ◆ foo │ ● opt0 │ ○ opt1 +│ ↑/↓ to navigate • Enter: confirm └ ", - "", + "", "", "", "│ ○ opt0 │ ● opt1 +│ ↑/↓ to navigate • Enter: confirm └ ", - "", + "", "", "", "│ ● opt0 │ ○ opt1 +│ ↑/↓ to navigate • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -407,7 +504,7 @@ exports[`select (isCI = false) > withGuide: false removes guide 1`] = ` "◆ foo ● opt0 ○ opt1 - +↑/↓ to navigate • Enter: confirm ", "", "", @@ -430,9 +527,10 @@ exports[`select (isCI = false) > wraps long cancelled message 1`] = ` │ foo foo foo foo foo foo foo │ foo foo foo foo │ ○ Option 1 +│ ↑/↓ to navigate • Enter: confirm └ ", - "", + "", "", "", "■ foo @@ -457,9 +555,10 @@ exports[`select (isCI = false) > wraps long messages 1`] = ` │ foo foo foo foo foo foo foo │ ● opt0 │ ○ opt1 +│ ↑/↓ to navigate • Enter: confirm └ ", - "", + "", "", "", "◇ foo foo foo foo foo foo foo @@ -483,9 +582,10 @@ exports[`select (isCI = false) > wraps long results 1`] = ` │ foo foo foo foo foo foo foo │ foo foo foo foo │ ○ Option 1 +│ ↑/↓ to navigate • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -507,6 +607,7 @@ exports[`select (isCI = true) > can be aborted by a signal 1`] = ` ◆ foo │ ● opt0 │ ○ opt1 +│ ↑/↓ to navigate • Enter: confirm └ ", " @@ -522,9 +623,10 @@ exports[`select (isCI = true) > can cancel 1`] = ` ◆ foo │ ● opt0 │ ○ opt1 +│ ↑/↓ to navigate • Enter: confirm └ ", - "", + "", "", "", "■ foo @@ -546,8 +648,8 @@ exports[`select (isCI = true) > correctly limits options when message wraps to m │ multiple lines │ ● Option 0 │ ○ Option 1 -│ ○ Option 2 │ ... +│ ↑/↓ to navigate • Enter: confirm └ ", "", @@ -555,35 +657,29 @@ exports[`select (isCI = true) > correctly limits options when message wraps to m "", "│ ○ Option 0 │ ● Option 1 -│ ○ Option 2 -│ ... -└ -", - "", - "", - "", - "│ ○ Option 1 -│ ● Option 2 │ ... +│ ↑/↓ to navigate • Enter: confirm └ ", "", "", "", "│ ... -│ ● Option 3 -│ ○ Option 4 +│ ● Option 2 │ ... +│ ↑/↓ to navigate • Enter: confirm └ ", "", "", - "", - "│ ● Option 4 -│ ○ Option 5 -│ ... -└ -", + "", + "│ ● Option 3", + "", + "", + "", + "", + "│ ● Option 4", + "", "", "", "", @@ -608,8 +704,8 @@ exports[`select (isCI = true) > correctly limits options with explicit multiline │ ● Option 0 │ ○ Option 1 │ ○ Option 2 -│ ○ Option 3 │ ... +│ ↑/↓ to navigate • Enter: confirm └ ", "", @@ -618,8 +714,8 @@ exports[`select (isCI = true) > correctly limits options with explicit multiline "│ ○ Option 0 │ ● Option 1 │ ○ Option 2 -│ ○ Option 3 │ ... +│ ↑/↓ to navigate • Enter: confirm └ ", "", @@ -627,18 +723,18 @@ exports[`select (isCI = true) > correctly limits options with explicit multiline "", "│ ○ Option 1 │ ● Option 2 -│ ○ Option 3 │ ... +│ ↑/↓ to navigate • Enter: confirm └ ", "", "", "", "│ ... -│ ○ Option 2 │ ● Option 3 │ ○ Option 4 │ ... +│ ↑/↓ to navigate • Enter: confirm └ ", "", @@ -661,16 +757,18 @@ exports[`select (isCI = true) > down arrow selects next option 1`] = ` ◆ foo │ ● opt0 │ ○ opt1 +│ ↑/↓ to navigate • Enter: confirm └ ", - "", + "", "", "", "│ ○ opt0 │ ● opt1 +│ ↑/↓ to navigate • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -687,7 +785,7 @@ exports[`select (isCI = true) > global withGuide: false removes guide 1`] = ` "◆ foo ● opt0 ○ opt1 - +↑/↓ to navigate • Enter: confirm ", "", "", @@ -713,16 +811,17 @@ exports[`select (isCI = true) > handles mixed size re-renders 1`] = ` │ Long Option │ Long Option │ ... +│ ↑/↓ to navigate • Enter: confirm └ ", - "", + "", "", "│ ◆ Whatever │ ... -│ ○ Option 1 │ ○ Option 2 │ ● Option 3 +│ ↑/↓ to navigate • Enter: confirm └ ", "", @@ -736,21 +835,92 @@ exports[`select (isCI = true) > handles mixed size re-renders 1`] = ` ] `; -exports[`select (isCI = true) > instructions: true renders footer 1`] = ` +exports[`select (isCI = true) > maxItems accounts for instruction footer 1`] = ` [ "", "│ ◆ foo │ ● opt0 │ ○ opt1 +│ ○ opt2 +│ ○ opt3 +│ ○ opt4 +│ ... │ ↑/↓ to navigate • Enter: confirm └ ", - "", + "", + "", + "", + "│ ○ opt0 +│ ● opt1 +│ ○ opt2 +│ ○ opt3 +│ ○ opt4 +│ ... +│ ↑/↓ to navigate • Enter: confirm +└ +", + "", + "", + "", + "│ ○ opt1 +│ ● opt2 +│ ○ opt3 +│ ○ opt4 +│ ... +│ ↑/↓ to navigate • Enter: confirm +└ +", + "", + "", + "", + "│ ○ opt2 +│ ● opt3 +│ ○ opt4 +│ ... +│ ↑/↓ to navigate • Enter: confirm +└ +", + "", + "", + "", + "│ ... +│ ○ opt2 +│ ○ opt3 +│ ● opt4 +│ ○ opt5 +│ ... +│ ↑/↓ to navigate • Enter: confirm +└ +", + "", + "", + "", + "│ ○ opt3 +│ ○ opt4 +│ ● opt5 +│ ○ opt6 +│ ... +│ ↑/↓ to navigate • Enter: confirm +└ +", + "", + "", + "", + "│ ○ opt4 +│ ○ opt5 +│ ● opt6 +│ ○ opt7 +│ ... +│ ↑/↓ to navigate • Enter: confirm +└ +", + "", "", "", "◇ foo -│ opt0", +│ opt6", " ", "", @@ -765,9 +935,10 @@ exports[`select (isCI = true) > renders disabled options 1`] = ` │ ○ Option 0 │ ● Option 1 │ ○ Option 2 (Hint 2) +│ ↑/↓ to navigate • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -778,6 +949,24 @@ exports[`select (isCI = true) > renders disabled options 1`] = ` ] `; +exports[`select (isCI = true) > renders instructions without guide 1`] = ` +[ + "", + "◆ foo +● opt0 +○ opt1 +↑/↓ to navigate • Enter: confirm +", + "", + "", + "◇ foo +opt0", + " +", + "", +] +`; + exports[`select (isCI = true) > renders multi-line option labels 1`] = ` [ "", @@ -786,17 +975,19 @@ exports[`select (isCI = true) > renders multi-line option labels 1`] = ` │ ● Option 0 │ with multiple lines │ ○ Option 1 +│ ↑/↓ to navigate • Enter: confirm └ ", - "", + "", "", "", "│ ○ Option 0 │ with multiple lines │ ● Option 1 +│ ↑/↓ to navigate • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -814,9 +1005,10 @@ exports[`select (isCI = true) > renders option hints 1`] = ` ◆ foo │ ● opt0 (Hint 0) │ ○ opt1 +│ ↑/↓ to navigate • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -834,9 +1026,10 @@ exports[`select (isCI = true) > renders option labels 1`] = ` ◆ foo │ ● Option 0 │ ○ Option 1 +│ ↑/↓ to navigate • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -854,9 +1047,10 @@ exports[`select (isCI = true) > renders options and message 1`] = ` ◆ foo │ ● opt0 │ ○ opt1 +│ ↑/↓ to navigate • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -874,23 +1068,26 @@ exports[`select (isCI = true) > up arrow selects previous option 1`] = ` ◆ foo │ ● opt0 │ ○ opt1 +│ ↑/↓ to navigate • Enter: confirm └ ", - "", + "", "", "", "│ ○ opt0 │ ● opt1 +│ ↑/↓ to navigate • Enter: confirm └ ", - "", + "", "", "", "│ ● opt0 │ ○ opt1 +│ ↑/↓ to navigate • Enter: confirm └ ", - "", + "", "", "", "◇ foo @@ -907,7 +1104,7 @@ exports[`select (isCI = true) > withGuide: false removes guide 1`] = ` "◆ foo ● opt0 ○ opt1 - +↑/↓ to navigate • Enter: confirm ", "", "", @@ -930,9 +1127,10 @@ exports[`select (isCI = true) > wraps long cancelled message 1`] = ` │ foo foo foo foo foo foo foo │ foo foo foo foo │ ○ Option 1 +│ ↑/↓ to navigate • Enter: confirm └ ", - "", + "", "", "", "■ foo @@ -957,9 +1155,10 @@ exports[`select (isCI = true) > wraps long messages 1`] = ` │ foo foo foo foo foo foo foo │ ● opt0 │ ○ opt1 +│ ↑/↓ to navigate • Enter: confirm └ ", - "", + "", "", "", "◇ foo foo foo foo foo foo foo @@ -983,9 +1182,10 @@ exports[`select (isCI = true) > wraps long results 1`] = ` │ foo foo foo foo foo foo foo │ foo foo foo foo │ ○ Option 1 +│ ↑/↓ to navigate • Enter: confirm └ ", - "", + "", "", "", "◇ foo diff --git a/packages/prompts/test/group-multi-select.test.ts b/packages/prompts/test/group-multi-select.test.ts index ac49ea56..33769ebf 100644 --- a/packages/prompts/test/group-multi-select.test.ts +++ b/packages/prompts/test/group-multi-select.test.ts @@ -481,22 +481,4 @@ describe.each(['true', 'false'])('groupMultiselect (isCI = %s)', (isCI) => { expect(value).toEqual(['group1value0']); expect(output.buffer).toMatchSnapshot(); }); - - test('instructions: true renders footer', async () => { - const result = prompts.groupMultiselect({ - message: 'foo', - input, - output, - instructions: true, - required: false, - options: { - group1: [{ value: 'group1value0' }, { value: 'group1value1' }], - }, - }); - - input.emit('keypress', '', { name: 'return' }); - - await result; - expect(output.buffer).toMatchSnapshot(); - }); }); diff --git a/packages/prompts/test/multi-select.test.ts b/packages/prompts/test/multi-select.test.ts index 6d04d856..4f05598c 100644 --- a/packages/prompts/test/multi-select.test.ts +++ b/packages/prompts/test/multi-select.test.ts @@ -438,27 +438,10 @@ describe.each(['true', 'false'])('multiselect (isCI = %s)', (isCI) => { expect(output.buffer).toMatchSnapshot(); }); - test('instructions: true renders footer', async () => { + test('renders instructions without guide', async () => { const result = prompts.multiselect({ message: 'foo', options: [{ value: 'opt0' }, { value: 'opt1' }], - instructions: true, - required: false, - input, - output, - }); - - input.emit('keypress', '', { name: 'return' }); - - await result; - expect(output.buffer).toMatchSnapshot(); - }); - - test('instructions: true with withGuide: false renders footer without guide', async () => { - const result = prompts.multiselect({ - message: 'foo', - options: [{ value: 'opt0' }, { value: 'opt1' }], - instructions: true, required: false, withGuide: false, input, @@ -471,14 +454,13 @@ describe.each(['true', 'false'])('multiselect (isCI = %s)', (isCI) => { expect(output.buffer).toMatchSnapshot(); }); - test('instructions: true with maxItems renders a sliding window', async () => { + test('maxItems accounts for instruction footer', async () => { const result = prompts.multiselect({ message: 'foo', options: [...Array(12).keys()].map((k) => ({ value: `opt${k}`, })), maxItems: 6, - instructions: true, input, output, }); diff --git a/packages/prompts/test/select.test.ts b/packages/prompts/test/select.test.ts index 0d536bc9..f0c92ce9 100644 --- a/packages/prompts/test/select.test.ts +++ b/packages/prompts/test/select.test.ts @@ -340,11 +340,11 @@ describe.each(['true', 'false'])('select (isCI = %s)', (isCI) => { expect(output.buffer).toMatchSnapshot(); }); - test('instructions: true renders footer', async () => { + test('renders instructions without guide', async () => { const result = prompts.select({ message: 'foo', options: [{ value: 'opt0' }, { value: 'opt1' }], - instructions: true, + withGuide: false, input, output, }); @@ -355,6 +355,28 @@ describe.each(['true', 'false'])('select (isCI = %s)', (isCI) => { expect(output.buffer).toMatchSnapshot(); }); + test('maxItems accounts for instruction footer', async () => { + const result = prompts.select({ + message: 'foo', + options: [...Array(12).keys()].map((k) => ({ + value: `opt${k}`, + })), + maxItems: 6, + input, + output, + }); + + for (let i = 0; i < 6; i++) { + input.emit('keypress', '', { name: 'down' }); + } + input.emit('keypress', '', { name: 'return' }); + + const value = await result; + + expect(value).toEqual('opt6'); + expect(output.buffer).toMatchSnapshot(); + }); + test('correctly limits options with explicit multiline message', async () => { output.rows = 12;