Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fresh-facts-search.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@clack/prompts": minor
---

Add `showInstructions` option to `select`, `multiselect`, and `groupMultiselect`. Keyboard hints remain shown by default; pass `showInstructions: false` to hide them.
Comment thread
natemoo-re marked this conversation as resolved.
13 changes: 12 additions & 1 deletion packages/prompts/src/group-multi-select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ export interface GroupMultiSelectOptions<Value> extends CommonOptions {
* @default 0
*/
groupSpacing?: number;

/**
* Show keyboard instructions below the option list.
* @default true
*/
showInstructions?: boolean;
}

/**
Expand Down Expand Up @@ -191,6 +197,7 @@ export const groupMultiselect = <Value>(opts: GroupMultiSelectOptions<Value>) =>
);
};
const required = opts.required ?? true;
const showInstructions = opts.showInstructions ?? true;

return new GroupMultiSelectPrompt({
options: opts.options,
Expand Down Expand Up @@ -288,7 +295,11 @@ export const groupMultiselect = <Value>(opts: GroupMultiSelectOptions<Value>) =>
default: {
const guidePrefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : '';
const titleLineCount = title.split('\n').length;
const footerLines = formatInstructionFooter(MULTISELECT_INSTRUCTIONS, 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({
Expand Down
12 changes: 11 additions & 1 deletion packages/prompts/src/multi-select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ export interface MultiSelectOptions<Value> extends CommonOptions {
maxItems?: number;
required?: boolean;
cursorAt?: Value;
/**
* Show keyboard instructions below the option list.
* @default true
*/
showInstructions?: boolean;
}
const computeLabel = (label: string, format: (text: string) => string) => {
return label
Expand Down Expand Up @@ -77,6 +82,7 @@ export const multiselect = <Value>(opts: MultiSelectOptions<Value>) => {
return `${styleText('dim', S_CHECKBOX_INACTIVE)} ${computeLabel(label, (text) => styleText('dim', text))}`;
};
const required = opts.required ?? true;
const showInstructions = opts.showInstructions ?? true;

return new MultiSelectPrompt({
options: opts.options,
Expand Down Expand Up @@ -179,7 +185,11 @@ export const multiselect = <Value>(opts: MultiSelectOptions<Value>) => {
default: {
const prefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : '';
const titleLineCount = title.split('\n').length;
const footerLines = formatInstructionFooter(MULTISELECT_INSTRUCTIONS, 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({
Expand Down
14 changes: 13 additions & 1 deletion packages/prompts/src/select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
type CommonOptions,
formatInstructionFooter,
S_BAR,
S_BAR_END,
S_RADIO_ACTIVE,
S_RADIO_INACTIVE,
symbol,
Expand Down Expand Up @@ -75,6 +76,11 @@ export interface SelectOptions<Value> extends CommonOptions {
options: Option<Value>[];
initialValue?: Value;
maxItems?: number;
/**
* Show keyboard instructions below the option list.
* @default true
*/
showInstructions?: boolean;
}

const computeLabel = (label: string, format: (text: string) => string) => {
Expand Down Expand Up @@ -111,6 +117,8 @@ export const select = <Value>(opts: SelectOptions<Value>) => {
}
};

const showInstructions = opts.showInstructions ?? true;

return new SelectPrompt({
options: opts.options,
signal: opts.signal,
Expand Down Expand Up @@ -151,7 +159,11 @@ export const select = <Value>(opts: SelectOptions<Value>) => {
default: {
const prefix = hasGuide ? `${styleText('cyan', S_BAR)} ` : '';
const titleLineCount = title.split('\n').length;
const footerLines = formatInstructionFooter(SELECT_INSTRUCTIONS, 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({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,27 @@ exports[`groupMultiselect (isCI = false) > selectableGroups = false > selecting
]
`;

exports[`groupMultiselect (isCI = false) > showInstructions: false hides instruction footer 1`] = `
[
"<cursor.hide>",
"│
◆ foo
│ ◻ group1
│ │ ◻ group1value0
│ └ ◻ group1value1
└
",
"<cursor.backward count=999><cursor.up count=6>",
"<cursor.down count=1>",
"<erase.down>",
"◇ foo
│",
"
",
"<cursor.show>",
]
`;

exports[`groupMultiselect (isCI = false) > sliding window loops downwards 1`] = `
[
"<cursor.hide>",
Expand Down Expand Up @@ -1631,6 +1652,27 @@ exports[`groupMultiselect (isCI = true) > selectableGroups = false > selecting a
]
`;

exports[`groupMultiselect (isCI = true) > showInstructions: false hides instruction footer 1`] = `
[
"<cursor.hide>",
"│
◆ foo
│ ◻ group1
│ │ ◻ group1value0
│ └ ◻ group1value1
└
",
"<cursor.backward count=999><cursor.up count=6>",
"<cursor.down count=1>",
"<erase.down>",
"◇ foo
│",
"
",
"<cursor.show>",
]
`;

exports[`groupMultiselect (isCI = true) > sliding window loops downwards 1`] = `
[
"<cursor.hide>",
Expand Down
40 changes: 40 additions & 0 deletions packages/prompts/test/__snapshots__/multi-select.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,26 @@ exports[`multiselect (isCI = false) > renders validation errors 1`] = `
]
`;

exports[`multiselect (isCI = false) > showInstructions: false hides instruction footer 1`] = `
[
"<cursor.hide>",
"│
◆ foo
│ ◻ opt0
│ ◻ opt1
└
",
"<cursor.backward count=999><cursor.up count=5>",
"<cursor.down count=1>",
"<erase.down>",
"◇ foo
│ none",
"
",
"<cursor.show>",
]
`;

exports[`multiselect (isCI = false) > shows hints for all selected options 1`] = `
[
"<cursor.hide>",
Expand Down Expand Up @@ -1514,6 +1534,26 @@ exports[`multiselect (isCI = true) > renders validation errors 1`] = `
]
`;

exports[`multiselect (isCI = true) > showInstructions: false hides instruction footer 1`] = `
[
"<cursor.hide>",
"│
◆ foo
│ ◻ opt0
│ ◻ opt1
└
",
"<cursor.backward count=999><cursor.up count=5>",
"<cursor.down count=1>",
"<erase.down>",
"◇ foo
│ none",
"
",
"<cursor.show>",
]
`;

exports[`multiselect (isCI = true) > shows hints for all selected options 1`] = `
[
"<cursor.hide>",
Expand Down
40 changes: 40 additions & 0 deletions packages/prompts/test/__snapshots__/select.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,26 @@ exports[`select (isCI = false) > renders options and message 1`] = `
]
`;

exports[`select (isCI = false) > showInstructions: false hides instruction footer 1`] = `
[
"<cursor.hide>",
"│
◆ foo
│ ● opt0
│ ○ opt1
└
",
"<cursor.backward count=999><cursor.up count=5>",
"<cursor.down count=1>",
"<erase.down>",
"◇ foo
│ opt0",
"
",
"<cursor.show>",
]
`;

exports[`select (isCI = false) > up arrow selects previous option 1`] = `
[
"<cursor.hide>",
Expand Down Expand Up @@ -1061,6 +1081,26 @@ exports[`select (isCI = true) > renders options and message 1`] = `
]
`;

exports[`select (isCI = true) > showInstructions: false hides instruction footer 1`] = `
[
"<cursor.hide>",
"│
◆ foo
│ ● opt0
│ ○ opt1
└
",
"<cursor.backward count=999><cursor.up count=5>",
"<cursor.down count=1>",
"<erase.down>",
"◇ foo
│ opt0",
"
",
"<cursor.show>",
]
`;

exports[`select (isCI = true) > up arrow selects previous option 1`] = `
[
"<cursor.hide>",
Expand Down
18 changes: 18 additions & 0 deletions packages/prompts/test/group-multi-select.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -481,4 +481,22 @@ describe.each(['true', 'false'])('groupMultiselect (isCI = %s)', (isCI) => {
expect(value).toEqual(['group1value0']);
expect(output.buffer).toMatchSnapshot();
});

test('showInstructions: false hides instruction footer', async () => {
const result = prompts.groupMultiselect({
message: 'foo',
input,
output,
showInstructions: false,
required: false,
options: {
group1: [{ value: 'group1value0' }, { value: 'group1value1' }],
},
});

input.emit('keypress', '', { name: 'return' });

await result;
expect(output.buffer).toMatchSnapshot();
});
});
16 changes: 16 additions & 0 deletions packages/prompts/test/multi-select.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -476,4 +476,20 @@ describe.each(['true', 'false'])('multiselect (isCI = %s)', (isCI) => {
expect(value).toEqual(['opt6']);
expect(output.buffer).toMatchSnapshot();
});

test('showInstructions: false hides instruction footer', async () => {
const result = prompts.multiselect({
message: 'foo',
options: [{ value: 'opt0' }, { value: 'opt1' }],
showInstructions: false,
required: false,
input,
output,
});

input.emit('keypress', '', { name: 'return' });

await result;
expect(output.buffer).toMatchSnapshot();
});
});
15 changes: 15 additions & 0 deletions packages/prompts/test/select.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,21 @@ describe.each(['true', 'false'])('select (isCI = %s)', (isCI) => {
expect(output.buffer).toMatchSnapshot();
});

test('showInstructions: false hides instruction footer', async () => {
const result = prompts.select({
message: 'foo',
options: [{ value: 'opt0' }, { value: 'opt1' }],
showInstructions: false,
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;

Expand Down
Loading