From f5ded86d257564751f6ddbb4a53b6267087dac85 Mon Sep 17 00:00:00 2001 From: Simon Boudrias Date: Sun, 1 Sep 2024 15:39:31 -0400 Subject: [PATCH] Fix(@inquirer/expand): Fix rendering of selected values when prompt is done --- packages/expand/expand.test.mts | 6 ++-- packages/expand/src/index.mts | 57 ++++++++++++++++++--------------- 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/packages/expand/expand.test.mts b/packages/expand/expand.test.mts index 89b45d6ee..6b098b5a4 100644 --- a/packages/expand/expand.test.mts +++ b/packages/expand/expand.test.mts @@ -72,7 +72,7 @@ describe('expand prompt', () => { `); events.keypress('enter'); - expect(getScreen()).toMatchInlineSnapshot('"? Overwrite this file? abort"'); + expect(getScreen()).toMatchInlineSnapshot(`"? Overwrite this file? Abort"`); await expect(answer).resolves.toEqual('abort'); }); @@ -139,7 +139,7 @@ describe('expand prompt', () => { `); events.keypress('enter'); - expect(getScreen()).toMatchInlineSnapshot('"? Overwrite this file? abort"'); + expect(getScreen()).toMatchInlineSnapshot(`"? Overwrite this file? Abort"`); await expect(answer).resolves.toEqual('abort'); }); @@ -245,7 +245,7 @@ describe('expand prompt', () => { expect(getScreen()).toMatchInlineSnapshot('"? Overwrite this file? (Yadxh)"'); events.keypress('enter'); - expect(getScreen()).toMatchInlineSnapshot('"? Overwrite this file? overwrite"'); + expect(getScreen()).toMatchInlineSnapshot(`"? Overwrite this file? Overwrite"`); await expect(answer).resolves.toEqual('overwrite'); }); diff --git a/packages/expand/src/index.mts b/packages/expand/src/index.mts index 3beebe330..cd07aff18 100644 --- a/packages/expand/src/index.mts +++ b/packages/expand/src/index.mts @@ -1,5 +1,6 @@ import { createPrompt, + useMemo, useState, useKeypress, usePrefix, @@ -10,44 +11,49 @@ import { import type { PartialDeep } from '@inquirer/type'; import colors from 'yoctocolors-cjs'; -type ExpandChoice = +type Choice = | { key: string; name: string } | { key: string; value: string } | { key: string; name: string; value: string }; +type NormalizedChoice = { + value: string; + name: string; + key: string; +}; + type ExpandConfig = { message: string; - choices: ReadonlyArray; + choices: ReadonlyArray; default?: string; expanded?: boolean; theme?: PartialDeep; }; +function normalizeChoices(choices: readonly Choice[]): NormalizedChoice[] { + return choices.map((choice) => { + const name: string = 'name' in choice ? choice.name : String(choice.value); + const value = 'value' in choice ? choice.value : name; + return { + value, + name, + key: choice.key.toLowerCase(), + }; + }); +} + const helpChoice = { key: 'h', name: 'Help, list all options', value: undefined, }; -function getChoiceKey(choice: ExpandChoice, key: 'name' | 'value'): string { - if (key === 'name') { - if ('name' in choice) return choice.name; - return choice.value; - } - - if ('value' in choice) return choice.value; - return choice.name; -} - export default createPrompt((config, done) => { - const { - choices, - default: defaultKey = 'h', - expanded: defaultExpandState = false, - } = config; + const { default: defaultKey = 'h' } = config; + const choices = useMemo(() => normalizeChoices(config.choices), [config.choices]); const [status, setStatus] = useState('pending'); const [value, setValue] = useState(''); - const [expanded, setExpanded] = useState(defaultExpandState); + const [expanded, setExpanded] = useState(config.expanded ?? false); const [errorMsg, setError] = useState(); const theme = makeTheme(config.theme); const prefix = usePrefix({ theme }); @@ -60,10 +66,10 @@ export default createPrompt((config, done) => { } else { const selectedChoice = choices.find(({ key }) => key === answer); if (selectedChoice) { - const finalValue = getChoiceKey(selectedChoice, 'value'); - setValue(finalValue); setStatus('done'); - done(finalValue); + // Set the value as we might've selected the default one. + setValue(answer); + done(selectedChoice.value); } else if (value === '') { setError('Please input a value'); } else { @@ -79,8 +85,9 @@ export default createPrompt((config, done) => { const message = theme.style.message(config.message); if (status === 'done') { - // TODO: `value` should be the display name instead of the raw value. - return `${prefix} ${message} ${theme.style.answer(value)}`; + // If the prompt is done, it's safe to assume there is a selected value. + const selectedChoice = choices.find(({ key }) => key === value) as NormalizedChoice; + return `${prefix} ${message} ${theme.style.answer(selectedChoice.name)}`; } const allChoices = expanded ? choices : [...choices, helpChoice]; @@ -103,7 +110,7 @@ export default createPrompt((config, done) => { shortChoices = ''; longChoices = allChoices .map((choice) => { - const line = ` ${choice.key}) ${getChoiceKey(choice, 'name')}`; + const line = ` ${choice.key}) ${choice.name}`; if (choice.key === value.toLowerCase()) { return theme.style.highlight(line); } @@ -116,7 +123,7 @@ export default createPrompt((config, done) => { let helpTip = ''; const currentOption = allChoices.find(({ key }) => key === value.toLowerCase()); if (currentOption) { - helpTip = `${colors.cyan('>>')} ${getChoiceKey(currentOption, 'name')}`; + helpTip = `${colors.cyan('>>')} ${currentOption.name}`; } let error = '';