Skip to content

Commit

Permalink
Feat(@inquirer/rawlist): Allow string choices (backward compat with v…
Browse files Browse the repository at this point in the history
…9 & prior)

Ref #1527
  • Loading branch information
SBoudrias committed Sep 1, 2024
1 parent 54bbe3f commit 9cc7aa7
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 10 deletions.
4 changes: 3 additions & 1 deletion packages/rawlist/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,13 @@ type Choice<Value> = {

Here's each property:

- `value`: The value is what will be returned by `await select()`.
- `value`: The value is what will be returned by `await rawlist()`.
- `name`: This is the string displayed in the choice list.
- `short`: Once the prompt is done (press enter), we'll use `short` if defined to render next to the question. By default we'll use `name`.
- `key`: The key of the choice. Displayed as `key) name`.

`choices` can also be an array of string, in which case the string will be used both as the `value` and the `name`.

## Theming

You can theme a prompt by passing a `theme` object option. The theme object only need to includes the keys you wish to modify, we'll fallback on the defaults for the rest.
Expand Down
22 changes: 22 additions & 0 deletions packages/rawlist/rawlist.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,28 @@ describe('rawlist prompt', () => {
await expect(answer).resolves.toEqual(4);
});

it('works with string choices', async () => {
const { answer, events, getScreen } = await render(rawlist, {
message: 'Select a number',
choices: ['1', '2', '3', '4', '5'],
});

events.type('4');
expect(getScreen()).toMatchInlineSnapshot(`
"? Select a number 4
1) 1
2) 2
3) 3
4) 4
5) 5"
`);

events.keypress('enter');
expect(getScreen()).toMatchInlineSnapshot('"? Select a number 4"');

await expect(answer).resolves.toEqual('4');
});

it('uses custom `key`, and `short` once a value is selected', async () => {
const { answer, events, getScreen } = await render(rawlist, {
message: 'Select a country',
Expand Down
58 changes: 49 additions & 9 deletions packages/rawlist/src/index.mts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
createPrompt,
useMemo,
useState,
useKeypress,
usePrefix,
Expand All @@ -20,21 +21,62 @@ type Choice<Value> = {
key?: string;
};

type RawlistConfig<Value> = {
type NormalizedChoice<Value> = {
value: Value;
name: string;
short: string;
key: string;
};

type RawlistConfig<
Value,
ChoicesObject =
| ReadonlyArray<string | Separator>
| ReadonlyArray<Choice<Value> | Separator>,
> = {
message: string;
choices: ReadonlyArray<Choice<Value> | Separator>;
choices: ChoicesObject extends ReadonlyArray<string | Separator>
? ChoicesObject
: ReadonlyArray<Choice<Value> | Separator>;
theme?: PartialDeep<Theme>;
};

function isSelectableChoice<T>(
choice: undefined | Separator | Choice<T>,
): choice is Choice<T> {
choice: undefined | Separator | NormalizedChoice<T>,
): choice is NormalizedChoice<T> {
return choice != null && !Separator.isSeparator(choice);
}

function normalizeChoices<Value>(
choices: ReadonlyArray<string | Separator> | ReadonlyArray<Choice<Value> | Separator>,
): Array<NormalizedChoice<Value> | Separator> {
let index = 0;
return choices.map((choice) => {
if (Separator.isSeparator(choice)) return choice;

index += 1;
if (typeof choice === 'string') {
return {
value: choice as Value,
name: choice,
short: choice,
key: String(index),
};
}

const name = choice.name ?? String(choice.value);
return {
value: choice.value,
name,
short: choice.short ?? name,
key: choice.key ?? String(index),
};
});
}

export default createPrompt(
<Value,>(config: RawlistConfig<Value>, done: (value: Value) => void) => {
const { choices } = config;
const choices = useMemo(() => normalizeChoices(config.choices), [config.choices]);
const [status, setStatus] = useState<string>('pending');
const [value, setValue] = useState<string>('');
const [errorMsg, setError] = useState<string>();
Expand Down Expand Up @@ -76,17 +118,15 @@ export default createPrompt(
return `${prefix} ${message} ${theme.style.answer(value)}`;
}

let index = 0;
const choicesStr = choices
.map((choice) => {
if (Separator.isSeparator(choice)) {
return ` ${choice.separator}`;
}

index += 1;
const line = ` ${choice.key || index}) ${String(choice.name || choice.value)}`;
const line = ` ${choice.key}) ${choice.name}`;

if (choice.key === value.toLowerCase() || String(index) === value) {
if (choice.key === value.toLowerCase()) {
return theme.style.highlight(line);
}

Expand Down

0 comments on commit 9cc7aa7

Please sign in to comment.