diff --git a/packages/checkbox/README.md b/packages/checkbox/README.md
index b188c4942..88587a6f5 100644
--- a/packages/checkbox/README.md
+++ b/packages/checkbox/README.md
@@ -77,9 +77,16 @@ type Theme = {
unchecked: string;
cursor: string;
};
+ helpMode: 'always' | 'never' | 'auto';
};
```
+### `theme.helpMode`
+
+- `auto` (default): Hide the help tips after an interaction occurs. The scroll tip will hide after any interactions, the selection tip will hide as soon as a first selection is done.
+- `always`: The help tips will always show and never hide.
+- `never`: The help tips will never show.
+
# License
Copyright (c) 2023 Simon Boudrias (twitter: [@vaxilart](https://twitter.com/Vaxilart))
diff --git a/packages/checkbox/checkbox.test.mts b/packages/checkbox/checkbox.test.mts
index 1e2fd02cb..2d4bd88ed 100644
--- a/packages/checkbox/checkbox.test.mts
+++ b/packages/checkbox/checkbox.test.mts
@@ -50,8 +50,7 @@ describe('checkbox prompt', () => {
◯ 4
◯ 5
◯ 6
- ◯ 7
- (Use arrow keys to reveal more choices)"
+ ◯ 7"
`);
events.keypress('enter');
@@ -89,8 +88,7 @@ describe('checkbox prompt', () => {
◯ 4
◯ 5
◯ 6
- ◯ 7
- (Use arrow keys to reveal more choices)"
+ ◯ 7"
`);
events.keypress('enter');
@@ -128,8 +126,7 @@ describe('checkbox prompt', () => {
◯ 3
◯ 4
◯ 5
- ◯ 6
- (Use arrow keys to reveal more choices)"
+ ◯ 6"
`);
events.keypress('enter');
@@ -168,8 +165,7 @@ describe('checkbox prompt', () => {
◯ 9
◯ 10
◯ 11
- ❯◉ 12
- (Use arrow keys to reveal more choices)"
+ ❯◉ 12"
`);
events.keypress('enter');
@@ -208,8 +204,7 @@ describe('checkbox prompt', () => {
◯ 10
◯ 11
❯◉ 12
- ──────────────
- (Use arrow keys to reveal more choices)"
+ ──────────────"
`);
events.keypress('enter');
@@ -233,8 +228,7 @@ describe('checkbox prompt', () => {
❯◉ 4
◯ 5
◯ 6
- ◯ 7
- (Use arrow keys to reveal more choices)"
+ ◯ 7"
`);
events.keypress('enter');
@@ -310,8 +304,7 @@ describe('checkbox prompt', () => {
expect(getScreen()).toMatchInlineSnapshot(`
"? Select a number
❯◉ 11
- ◉ 12
- (Use arrow keys to reveal more choices)"
+ ◉ 12"
`);
events.keypress('enter');
@@ -462,8 +455,7 @@ describe('checkbox prompt', () => {
❯◉ 4
◯ 5
◯ 6
- ◯ 7
- (Use arrow keys to reveal more choices)"
+ ◯ 7"
`);
events.keypress('a');
@@ -476,8 +468,7 @@ describe('checkbox prompt', () => {
❯◉ 4
◉ 5
◉ 6
- ◉ 7
- (Use arrow keys to reveal more choices)"
+ ◉ 7"
`);
events.keypress('a');
@@ -490,8 +481,7 @@ describe('checkbox prompt', () => {
❯◯ 4
◯ 5
◯ 6
- ◯ 7
- (Use arrow keys to reveal more choices)"
+ ◯ 7"
`);
events.keypress('a');
@@ -515,8 +505,7 @@ describe('checkbox prompt', () => {
❯◉ 4
◯ 5
◯ 6
- ◯ 7
- (Use arrow keys to reveal more choices)"
+ ◯ 7"
`);
events.keypress('a');
@@ -544,8 +533,7 @@ describe('checkbox prompt', () => {
❯◉ 8
◯ 9
◯ 10
- ◯ 11
- (Use arrow keys to reveal more choices)"
+ ◯ 11"
`);
events.keypress('i');
@@ -568,8 +556,7 @@ describe('checkbox prompt', () => {
◯ 4
◯ 5
◯ 6
- ◯ 7
- (Use arrow keys to reveal more choices)"
+ ◯ 7"
`);
events.keypress('enter');
@@ -634,7 +621,6 @@ describe('checkbox prompt', () => {
◯ 5
◯ 6
◯ 7
- (Use arrow keys to reveal more choices)
> At least one choice must be selected"
`);
@@ -647,8 +633,7 @@ describe('checkbox prompt', () => {
◯ 4
◯ 5
◯ 6
- ◯ 7
- (Use arrow keys to reveal more choices)"
+ ◯ 7"
`);
events.keypress('enter');
@@ -679,7 +664,6 @@ describe('checkbox prompt', () => {
◯ 5
◯ 6
◯ 7
- (Use arrow keys to reveal more choices)
> Please select only one choice"
`);
@@ -688,63 +672,238 @@ describe('checkbox prompt', () => {
await expect(answer).resolves.toEqual([1]);
});
- it('renderSelectedChoices', async () => {
- const { answer, events, getScreen } = await render(checkbox, {
- message: 'Select your favourite number.',
- choices: numberedChoices,
- theme: {
- style: {
- renderSelectedChoices(selected: { value: number }[]) {
- if (selected.length > 1) {
- return `You have selected ${selected[0].value} and ${selected.length - 1} more.`;
- }
- return `You have selected ${selected
- .slice(0, 1)
- .map((c) => c.value)
- .join(', ')}.`;
+ describe('theme: style.renderSelectedChoices', () => {
+ it('renderSelectedChoices', async () => {
+ const { answer, events, getScreen } = await render(checkbox, {
+ message: 'Select your favourite number.',
+ choices: numberedChoices,
+ theme: {
+ style: {
+ renderSelectedChoices(selected: { value: number }[]) {
+ if (selected.length > 1) {
+ return `You have selected ${selected[0].value} and ${selected.length - 1} more.`;
+ }
+ return `You have selected ${selected
+ .slice(0, 1)
+ .map((c) => c.value)
+ .join(', ')}.`;
+ },
},
},
- },
+ });
+
+ events.keypress('space');
+ events.keypress('down');
+ events.keypress('space');
+ events.keypress('down');
+ events.keypress('space');
+ events.keypress('enter');
+
+ await answer;
+ expect(getScreen()).toMatchInlineSnapshot(
+ '"? Select your favourite number. You have selected 1 and 2 more."',
+ );
});
- events.keypress('space');
- events.keypress('down');
- events.keypress('space');
- events.keypress('down');
- events.keypress('space');
- events.keypress('enter');
-
- await answer;
- expect(getScreen()).toMatchInlineSnapshot(
- '"? Select your favourite number. You have selected 1 and 2 more."',
- );
- });
-
- it('renderSelectedChoices - using allChoices parameter', async () => {
- const { answer, events, getScreen } = await render(checkbox, {
- message: 'Select your favourite number.',
- choices: numberedChoices,
- theme: {
- style: {
- renderSelectedChoices(
- selected: { value: number }[],
- all: ({ value: number } | Separator)[],
- ) {
- return `You have selected ${selected.length} out of ${all.length} options.`;
+ it('using allChoices parameter', async () => {
+ const { answer, events, getScreen } = await render(checkbox, {
+ message: 'Select your favourite number.',
+ choices: numberedChoices,
+ theme: {
+ style: {
+ renderSelectedChoices(
+ selected: { value: number }[],
+ all: ({ value: number } | Separator)[],
+ ) {
+ return `You have selected ${selected.length} out of ${all.length} options.`;
+ },
},
},
- },
+ });
+
+ events.keypress('space');
+ events.keypress('down');
+ events.keypress('down');
+ events.keypress('space');
+ events.keypress('enter');
+
+ await answer;
+ expect(getScreen()).toMatchInlineSnapshot(
+ '"? Select your favourite number. You have selected 2 out of 12 options."',
+ );
});
+ });
- events.keypress('space');
- events.keypress('down');
- events.keypress('down');
- events.keypress('space');
- events.keypress('enter');
+ describe('theme: helpMode', () => {
+ const scrollTip = '(Use arrow keys to reveal more choices)';
+ const selectTip = 'Press to select';
+
+ it('helpMode: auto', async () => {
+ const { answer, events, getScreen } = await render(checkbox, {
+ message: 'Select a number',
+ choices: numberedChoices,
+ theme: { helpMode: 'auto' },
+ });
+
+ expect(getScreen()).toMatchInlineSnapshot(`
+ "? Select a number (Press to select, to toggle all, to invert
+ selection, and to proceed)
+ ❯◯ 1
+ ◯ 2
+ ◯ 3
+ ◯ 4
+ ◯ 5
+ ◯ 6
+ ◯ 7
+ (Use arrow keys to reveal more choices)"
+ `);
+ expect(getScreen()).toContain(scrollTip);
+ expect(getScreen()).toContain(selectTip);
+
+ events.keypress('down');
+ expect(getScreen()).toMatchInlineSnapshot(`
+ "? Select a number (Press to select, to toggle all, to invert
+ selection, and to proceed)
+ ◯ 1
+ ❯◯ 2
+ ◯ 3
+ ◯ 4
+ ◯ 5
+ ◯ 6
+ ◯ 7"
+ `);
+ expect(getScreen()).not.toContain(scrollTip);
+ expect(getScreen()).toContain(selectTip);
+
+ events.keypress('space');
+ expect(getScreen()).toMatchInlineSnapshot(`
+ "? Select a number
+ ◯ 1
+ ❯◉ 2
+ ◯ 3
+ ◯ 4
+ ◯ 5
+ ◯ 6
+ ◯ 7"
+ `);
+ expect(getScreen()).not.toContain(scrollTip);
+ expect(getScreen()).not.toContain(selectTip);
+
+ events.keypress('enter');
+ await expect(answer).resolves.toEqual([2]);
+ expect(getScreen()).toMatchInlineSnapshot(`"? Select a number 2"`);
+ });
- await answer;
- expect(getScreen()).toMatchInlineSnapshot(
- '"? Select your favourite number. You have selected 2 out of 12 options."',
- );
+ it('helpMode: always', async () => {
+ const { answer, events, getScreen } = await render(checkbox, {
+ message: 'Select a number',
+ choices: numberedChoices,
+ theme: { helpMode: 'always' },
+ });
+
+ expect(getScreen()).toMatchInlineSnapshot(`
+ "? Select a number (Press to select, to toggle all, to invert
+ selection, and to proceed)
+ ❯◯ 1
+ ◯ 2
+ ◯ 3
+ ◯ 4
+ ◯ 5
+ ◯ 6
+ ◯ 7
+ (Use arrow keys to reveal more choices)"
+ `);
+ expect(getScreen()).toContain(scrollTip);
+ expect(getScreen()).toContain(selectTip);
+
+ events.keypress('down');
+ expect(getScreen()).toMatchInlineSnapshot(`
+ "? Select a number (Press to select, to toggle all, to invert
+ selection, and to proceed)
+ ◯ 1
+ ❯◯ 2
+ ◯ 3
+ ◯ 4
+ ◯ 5
+ ◯ 6
+ ◯ 7
+ (Use arrow keys to reveal more choices)"
+ `);
+ expect(getScreen()).toContain(scrollTip);
+ expect(getScreen()).toContain(selectTip);
+
+ events.keypress('space');
+ expect(getScreen()).toMatchInlineSnapshot(`
+ "? Select a number (Press to select, to toggle all, to invert
+ selection, and to proceed)
+ ◯ 1
+ ❯◉ 2
+ ◯ 3
+ ◯ 4
+ ◯ 5
+ ◯ 6
+ ◯ 7
+ (Use arrow keys to reveal more choices)"
+ `);
+ expect(getScreen()).toContain(scrollTip);
+ expect(getScreen()).toContain(selectTip);
+
+ events.keypress('enter');
+ await expect(answer).resolves.toEqual([2]);
+ expect(getScreen()).toMatchInlineSnapshot(`"? Select a number 2"`);
+ });
+
+ it('helpMode: never', async () => {
+ const { answer, events, getScreen } = await render(checkbox, {
+ message: 'Select a number',
+ choices: numberedChoices,
+ theme: { helpMode: 'never' },
+ });
+
+ expect(getScreen()).toMatchInlineSnapshot(`
+ "? Select a number
+ ❯◯ 1
+ ◯ 2
+ ◯ 3
+ ◯ 4
+ ◯ 5
+ ◯ 6
+ ◯ 7"
+ `);
+ expect(getScreen()).not.toContain(scrollTip);
+ expect(getScreen()).not.toContain(selectTip);
+
+ events.keypress('down');
+ expect(getScreen()).toMatchInlineSnapshot(`
+ "? Select a number
+ ◯ 1
+ ❯◯ 2
+ ◯ 3
+ ◯ 4
+ ◯ 5
+ ◯ 6
+ ◯ 7"
+ `);
+ expect(getScreen()).not.toContain(scrollTip);
+ expect(getScreen()).not.toContain(selectTip);
+
+ events.keypress('space');
+ expect(getScreen()).toMatchInlineSnapshot(`
+ "? Select a number
+ ◯ 1
+ ❯◉ 2
+ ◯ 3
+ ◯ 4
+ ◯ 5
+ ◯ 6
+ ◯ 7"
+ `);
+ expect(getScreen()).not.toContain(scrollTip);
+ expect(getScreen()).not.toContain(selectTip);
+
+ events.keypress('enter');
+ await expect(answer).resolves.toEqual([2]);
+ expect(getScreen()).toMatchInlineSnapshot(`"? Select a number 2"`);
+ });
});
});
diff --git a/packages/checkbox/src/index.mts b/packages/checkbox/src/index.mts
index 3fae3ffd3..969e89e33 100644
--- a/packages/checkbox/src/index.mts
+++ b/packages/checkbox/src/index.mts
@@ -4,6 +4,7 @@ import {
useKeypress,
usePrefix,
usePagination,
+ useRef,
useMemo,
makeTheme,
isUpKey,
@@ -33,6 +34,7 @@ type CheckboxTheme = {
allChoices: ReadonlyArray | Separator>,
) => string;
};
+ helpMode: 'always' | 'never' | 'auto';
};
const checkboxTheme: CheckboxTheme = {
@@ -46,6 +48,7 @@ const checkboxTheme: CheckboxTheme = {
renderSelectedChoices: (selectedChoices) =>
selectedChoices.map((choice) => choice.name || choice.value).join(', '),
},
+ helpMode: 'auto',
};
type Choice = {
@@ -102,6 +105,7 @@ export default createPrompt(
} = config;
const theme = makeTheme(checkboxTheme, config.theme);
const prefix = usePrefix({ theme });
+ const firstRender = useRef(true);
const [status, setStatus] = useState('pending');
const [items, setItems] = useState>>(
choices.map((choice) => ({ ...choice })),
@@ -195,7 +199,6 @@ export default createPrompt(
},
pageSize,
loop,
- theme,
});
if (status === 'done') {
@@ -207,10 +210,16 @@ export default createPrompt(
return `${prefix} ${message} ${answer}`;
}
- let helpTip = '';
- if (showHelpTip && (instructions === undefined || instructions)) {
+ let helpTipTop = '';
+ let helpTipBottom = '';
+ if (
+ theme.helpMode === 'always' ||
+ (theme.helpMode === 'auto' &&
+ showHelpTip &&
+ (instructions === undefined || instructions))
+ ) {
if (typeof instructions === 'string') {
- helpTip = instructions;
+ helpTipTop = instructions;
} else {
const keys = [
`${theme.style.key('space')} to select`,
@@ -218,16 +227,25 @@ export default createPrompt(
`${theme.style.key('i')} to invert selection`,
`and ${theme.style.key('enter')} to proceed`,
];
- helpTip = ` (Press ${keys.join(', ')})`;
+ helpTipTop = ` (Press ${keys.join(', ')})`;
+ }
+
+ if (
+ items.length > pageSize &&
+ (theme.helpMode === 'always' ||
+ (theme.helpMode === 'auto' && firstRender.current))
+ ) {
+ helpTipBottom = `\n${theme.style.help('(Use arrow keys to reveal more choices)')}`;
+ firstRender.current = false;
}
}
let error = '';
if (errorMsg) {
- error = theme.style.error(errorMsg);
+ error = `\n${theme.style.error(errorMsg)}`;
}
- return `${prefix} ${message}${helpTip}\n${page}\n${error}${ansiEscapes.cursorHide}`;
+ return `${prefix} ${message}${helpTipTop}\n${page}${helpTipBottom}${error}${ansiEscapes.cursorHide}`;
},
);
diff --git a/packages/core/README.md b/packages/core/README.md
index bd1438223..90e11cc96 100644
--- a/packages/core/README.md
+++ b/packages/core/README.md
@@ -141,7 +141,6 @@ export default createPrompt((config, done) => {
renderItem: ({ item, index, isActive }) => `${isActive ? ">" : " "}${index}. ${item.toString()}`
pageSize: config.pageSize,
loop: config.loop,
- theme, config.theme,
});
return `... ${page}`;
diff --git a/packages/core/src/lib/pagination/use-pagination.mts b/packages/core/src/lib/pagination/use-pagination.mts
index fa9d86431..f004c68f1 100644
--- a/packages/core/src/lib/pagination/use-pagination.mts
+++ b/packages/core/src/lib/pagination/use-pagination.mts
@@ -1,7 +1,6 @@
import type { Prettify } from '@inquirer/type';
import { useRef } from '../use-ref.mjs';
import { readlineWidth } from '../utils.mjs';
-import { makeTheme } from '../make-theme.mjs';
import { type Theme } from '../theme.mjs';
import { lines, type Layout } from './lines.mjs';
import { finite, infinite } from './position.mjs';
@@ -12,7 +11,6 @@ export function usePagination({
renderItem,
pageSize,
loop = true,
- theme: defaultTheme,
}: {
items: readonly T[];
/** The index of the active item. */
@@ -26,7 +24,6 @@ export function usePagination({
theme?: Theme;
}): string {
const state = useRef({ position: 0, lastActive: 0 });
- const theme = makeTheme(defaultTheme);
const position = loop
? infinite({
@@ -45,7 +42,7 @@ export function usePagination({
state.current.position = position;
state.current.lastActive = active;
- const visibleLines = lines({
+ return lines({
items,
width: readlineWidth(),
renderItem,
@@ -53,10 +50,4 @@ export function usePagination({
position,
pageSize,
}).join('\n');
-
- if (items.length > pageSize) {
- return `${visibleLines}\n${theme.style.help('(Use arrow keys to reveal more choices)')}`;
- }
-
- return visibleLines;
}
diff --git a/packages/prompts/package.json b/packages/prompts/package.json
index 80a090a31..d8c31e693 100644
--- a/packages/prompts/package.json
+++ b/packages/prompts/package.json
@@ -85,5 +85,8 @@
"@inquirer/rawlist": "^2.1.3",
"@inquirer/select": "^2.2.3"
},
+ "devDependencies": {
+ "@inquirer/type": "^1.2.1"
+ },
"homepage": "https://github.com/SBoudrias/Inquirer.js/blob/master/packages/prompts/README.md"
}
diff --git a/packages/prompts/prompts.test.mts b/packages/prompts/prompts.test.mts
index 0a550aac9..114583968 100644
--- a/packages/prompts/prompts.test.mts
+++ b/packages/prompts/prompts.test.mts
@@ -1,4 +1,5 @@
import { describe, it, expect } from 'vitest';
+import { Expect, Equal } from '@inquirer/type';
import {
checkbox,
confirm,
@@ -22,3 +23,13 @@ describe('@inquirer/prompts', () => {
expect(select).toBeTypeOf('function');
});
});
+
+/**
+ * Type assertions to validate the interfaces.
+ */
+Expect<
+ Equal<
+ Parameters[0]['theme']['helpMode'],
+ Parameters[0]['theme']['helpMode']
+ >
+>;
diff --git a/packages/select/README.md b/packages/select/README.md
index b237f8876..a51a65ffd 100644
--- a/packages/select/README.md
+++ b/packages/select/README.md
@@ -80,9 +80,16 @@ type Theme = {
icon: {
cursor: string;
};
+ helpMode: 'always' | 'never' | 'auto';
};
```
+### `theme.helpMode`
+
+- `auto` (default): Hide the help tips after an interaction occurs.
+- `always`: The help tips will always show and never hide.
+- `never`: The help tips will never show.
+
# License
Copyright (c) 2023 Simon Boudrias (twitter: [@vaxilart](https://twitter.com/Vaxilart))
diff --git a/packages/select/select.test.mts b/packages/select/select.test.mts
index 95ede1dac..b3f09b26e 100644
--- a/packages/select/select.test.mts
+++ b/packages/select/select.test.mts
@@ -1,4 +1,4 @@
-import { describe, it, expect, vi } from 'vitest';
+import { describe, it, expect, vi, afterEach } from 'vitest';
import { render } from '@inquirer/testing';
import { ValidationError } from '@inquirer/core';
import select, { Separator } from './src/index.mjs';
@@ -18,6 +18,10 @@ const numberedChoices = [
{ value: 12 },
];
+afterEach(() => {
+ vi.useRealTimers();
+});
+
describe('select prompt', () => {
it('use arrow keys to select an option', async () => {
const { answer, events, getScreen } = await render(select, {
@@ -47,8 +51,7 @@ describe('select prompt', () => {
4
5
6
- 7
- (Use arrow keys to reveal more choices)"
+ 7"
`);
events.keypress('enter');
@@ -91,8 +94,7 @@ describe('select prompt', () => {
❯ 4
5
6
- 7
- (Use arrow keys to reveal more choices)"
+ 7"
`);
events.keypress('enter');
@@ -164,8 +166,7 @@ describe('select prompt', () => {
expect(getScreen()).toMatchInlineSnapshot(`
"? Select a number
❯ 11
- 12
- (Use arrow keys to reveal more choices)"
+ 12"
`);
events.keypress('enter');
@@ -248,8 +249,7 @@ describe('select prompt', () => {
expect(getScreen()).toMatchInlineSnapshot(`
"? Select a number
11
- ❯ 12
- (Use arrow keys to reveal more choices)"
+ ❯ 12"
`);
events.keypress('enter');
@@ -278,8 +278,7 @@ describe('select prompt', () => {
"? Select a number
11
❯ 12
- ──────────────
- (Use arrow keys to reveal more choices)"
+ ──────────────"
`);
events.keypress('enter');
@@ -560,4 +559,122 @@ describe('select prompt', () => {
vi.runAllTimers();
await expect(answer).resolves.toEqual('US');
});
+
+ describe('theme: helpMode', () => {
+ const scrollTip = '(Use arrow keys to reveal more choices)';
+
+ it('helpMode: auto', async () => {
+ const { answer, events, getScreen } = await render(select, {
+ message: 'Select a number',
+ choices: numberedChoices,
+ theme: { helpMode: 'auto' },
+ });
+
+ expect(getScreen()).toMatchInlineSnapshot(`
+ "? Select a number
+ ❯ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ (Use arrow keys to reveal more choices)"
+ `);
+ expect(getScreen()).toContain(scrollTip);
+
+ events.keypress('down');
+ expect(getScreen()).toMatchInlineSnapshot(`
+ "? Select a number
+ 1
+ ❯ 2
+ 3
+ 4
+ 5
+ 6
+ 7"
+ `);
+ expect(getScreen()).not.toContain(scrollTip);
+
+ events.keypress('enter');
+ await expect(answer).resolves.toEqual(2);
+ expect(getScreen()).toMatchInlineSnapshot('"? Select a number 2"');
+ });
+
+ it('helpMode: always', async () => {
+ const { answer, events, getScreen } = await render(select, {
+ message: 'Select a number',
+ choices: numberedChoices,
+ theme: { helpMode: 'always' },
+ });
+
+ expect(getScreen()).toMatchInlineSnapshot(`
+ "? Select a number
+ ❯ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ (Use arrow keys to reveal more choices)"
+ `);
+ expect(getScreen()).toContain(scrollTip);
+
+ events.keypress('down');
+ expect(getScreen()).toMatchInlineSnapshot(`
+ "? Select a number
+ 1
+ ❯ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ (Use arrow keys to reveal more choices)"
+ `);
+ expect(getScreen()).toContain(scrollTip);
+
+ events.keypress('enter');
+ await expect(answer).resolves.toEqual(2);
+ expect(getScreen()).toMatchInlineSnapshot('"? Select a number 2"');
+ });
+
+ it('helpMode: never', async () => {
+ const { answer, events, getScreen } = await render(select, {
+ message: 'Select a number',
+ choices: numberedChoices,
+ theme: { helpMode: 'never' },
+ });
+
+ expect(getScreen()).toMatchInlineSnapshot(`
+ "? Select a number
+ ❯ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7"
+ `);
+ expect(getScreen()).not.toContain(scrollTip);
+
+ events.keypress('down');
+ expect(getScreen()).toMatchInlineSnapshot(`
+ "? Select a number
+ 1
+ ❯ 2
+ 3
+ 4
+ 5
+ 6
+ 7"
+ `);
+ expect(getScreen()).not.toContain(scrollTip);
+
+ events.keypress('enter');
+ await expect(answer).resolves.toEqual(2);
+ expect(getScreen()).toMatchInlineSnapshot('"? Select a number 2"');
+ });
+ });
});
diff --git a/packages/select/src/index.mts b/packages/select/src/index.mts
index 0e2201a4b..1b545186b 100644
--- a/packages/select/src/index.mts
+++ b/packages/select/src/index.mts
@@ -24,11 +24,13 @@ import ansiEscapes from 'ansi-escapes';
type SelectTheme = {
icon: { cursor: string };
style: { disabled: (text: string) => string };
+ helpMode: 'always' | 'never' | 'auto';
};
const selectTheme: SelectTheme = {
icon: { cursor: figures.pointer },
style: { disabled: (text: string) => chalk.dim(`- ${text}`) },
+ helpMode: 'auto',
};
type Choice = {
@@ -142,10 +144,19 @@ export default createPrompt(
const message = theme.style.message(config.message);
- let helpTip;
- if (firstRender.current && items.length <= pageSize) {
+ let helpTipTop = '';
+ let helpTipBottom = '';
+ if (
+ theme.helpMode === 'always' ||
+ (theme.helpMode === 'auto' && firstRender.current)
+ ) {
firstRender.current = false;
- helpTip = theme.style.help('(Use arrow keys)');
+
+ if (items.length > pageSize) {
+ helpTipBottom = `\n${theme.style.help('(Use arrow keys to reveal more choices)')}`;
+ } else {
+ helpTipTop = theme.style.help('(Use arrow keys)');
+ }
}
const page = usePagination- >({
@@ -169,7 +180,6 @@ export default createPrompt(
},
pageSize,
loop,
- theme,
});
if (status === 'done') {
@@ -184,7 +194,7 @@ export default createPrompt(
? `\n${selectedChoice.description}`
: ``;
- return `${[prefix, message, helpTip].filter(Boolean).join(' ')}\n${page}${choiceDescription}${ansiEscapes.cursorHide}`;
+ return `${[prefix, message, helpTipTop].filter(Boolean).join(' ')}\n${page}${choiceDescription}${helpTipBottom}${ansiEscapes.cursorHide}`;
},
);
diff --git a/packages/type/src/index.mts b/packages/type/src/index.mts
index ecd7f091c..f72018725 100644
--- a/packages/type/src/index.mts
+++ b/packages/type/src/index.mts
@@ -22,3 +22,25 @@ export type Prompt = (
config: Config,
context?: Context,
) => CancelablePromise;
+
+/**
+ * Utility types used for writing tests
+ *
+ * Equal checks that A and B are the same type, and returns
+ * either `true` or `false`.
+ *
+ * You can use it in combination with `Expect` to write type
+ * inference unit tests:
+ *
+ * ```ts
+ * type t = Expect<
+ * Equal, { a?: string }>
+ * >
+ * ```
+ */
+export type Equal =
+ (() => T extends X ? 1 : 2) extends () => T extends Y ? 1 : 2 ? true : false;
+
+export type Expect = T;
+
+export type Not = T extends true ? false : true;
diff --git a/yarn.lock b/yarn.lock
index 22e8cf583..66afd8c95 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -504,6 +504,7 @@ __metadata:
"@inquirer/password": "npm:^2.1.3"
"@inquirer/rawlist": "npm:^2.1.3"
"@inquirer/select": "npm:^2.2.3"
+ "@inquirer/type": "npm:^1.2.1"
languageName: unknown
linkType: soft
@@ -544,7 +545,7 @@ __metadata:
languageName: unknown
linkType: soft
-"@inquirer/type@npm:^1.2.2, @inquirer/type@workspace:packages/type":
+"@inquirer/type@npm:^1.2.1, @inquirer/type@npm:^1.2.2, @inquirer/type@workspace:packages/type":
version: 0.0.0-use.local
resolution: "@inquirer/type@workspace:packages/type"
languageName: unknown