From e625f21ecc3c52b4030851a574574621032c5685 Mon Sep 17 00:00:00 2001 From: Simon Boudrias Date: Sat, 7 Dec 2024 13:21:31 -0500 Subject: [PATCH] Feat(confirm): Toggle value on tab keypress. Fix #731 --- packages/confirm/confirm.test.ts | 35 +++++++++++++++++++++++++------- packages/confirm/src/index.ts | 23 ++++++++++++++++----- 2 files changed, 46 insertions(+), 12 deletions(-) diff --git a/packages/confirm/confirm.test.ts b/packages/confirm/confirm.test.ts index 871f1eb9f..49e1e6160 100644 --- a/packages/confirm/confirm.test.ts +++ b/packages/confirm/confirm.test.ts @@ -16,7 +16,7 @@ describe('confirm prompt', () => { events.keypress('enter'); await expect(answer).resolves.toEqual(true); - expect(getScreen()).toMatchInlineSnapshot('"✔ Do you want to proceed? yes"'); + expect(getScreen()).toMatchInlineSnapshot(`"✔ Do you want to proceed? Yes"`); }); it('handles "no"', async () => { @@ -32,7 +32,7 @@ describe('confirm prompt', () => { events.keypress('enter'); await expect(answer).resolves.toEqual(false); - expect(getScreen()).toMatchInlineSnapshot('"✔ Do you want to proceed? no"'); + expect(getScreen()).toMatchInlineSnapshot(`"✔ Do you want to proceed? No"`); }); it('handles "y"', async () => { @@ -48,7 +48,7 @@ describe('confirm prompt', () => { events.keypress('enter'); await expect(answer).resolves.toEqual(true); - expect(getScreen()).toMatchInlineSnapshot('"✔ Do you want to proceed? yes"'); + expect(getScreen()).toMatchInlineSnapshot(`"✔ Do you want to proceed? Yes"`); }); it('handles "n"', async () => { @@ -64,7 +64,7 @@ describe('confirm prompt', () => { events.keypress('enter'); await expect(answer).resolves.toEqual(false); - expect(getScreen()).toMatchInlineSnapshot('"✔ Do you want to proceed? no"'); + expect(getScreen()).toMatchInlineSnapshot(`"✔ Do you want to proceed? No"`); }); it('uses default (yes) on empty input', async () => { @@ -78,7 +78,7 @@ describe('confirm prompt', () => { events.keypress('enter'); await expect(answer).resolves.toEqual(true); - expect(getScreen()).toMatchInlineSnapshot('"✔ Do you want to proceed? yes"'); + expect(getScreen()).toMatchInlineSnapshot(`"✔ Do you want to proceed? Yes"`); }); it('uses default (no) on empty input', async () => { @@ -92,7 +92,7 @@ describe('confirm prompt', () => { events.keypress('enter'); await expect(answer).resolves.toEqual(false); - expect(getScreen()).toMatchInlineSnapshot('"✔ Do you want to proceed? no"'); + expect(getScreen()).toMatchInlineSnapshot(`"✔ Do you want to proceed? No"`); }); it('uses default on gibberish input', async () => { @@ -107,7 +107,7 @@ describe('confirm prompt', () => { events.keypress('enter'); await expect(answer).resolves.toEqual(true); - expect(getScreen()).toMatchInlineSnapshot('"✔ Do you want to proceed? yes"'); + expect(getScreen()).toMatchInlineSnapshot(`"✔ Do you want to proceed? Yes"`); }); it('supports transformer option', async () => { @@ -123,4 +123,25 @@ describe('confirm prompt', () => { await expect(answer).resolves.toEqual(true); expect(getScreen()).toMatchInlineSnapshot('"✔ Do you want to proceed? Oui!"'); }); + + it('toggle between values with the tab key', async () => { + const { answer, events, getScreen } = await render(confirm, { + message: 'Do you want to proceed?', + }); + + expect(getScreen()).toMatchInlineSnapshot('"? Do you want to proceed? (Y/n)"'); + + events.keypress('tab'); + expect(getScreen()).toMatchInlineSnapshot(`"? Do you want to proceed? (Y/n) No"`); + + events.keypress('tab'); + expect(getScreen()).toMatchInlineSnapshot(`"? Do you want to proceed? (Y/n) Yes"`); + + events.keypress('tab'); + expect(getScreen()).toMatchInlineSnapshot(`"? Do you want to proceed? (Y/n) No"`); + + events.keypress('enter'); + await expect(answer).resolves.toEqual(false); + expect(getScreen()).toMatchInlineSnapshot('"✔ Do you want to proceed? No"'); + }); }); diff --git a/packages/confirm/src/index.ts b/packages/confirm/src/index.ts index dda3c111e..64fa6595e 100644 --- a/packages/confirm/src/index.ts +++ b/packages/confirm/src/index.ts @@ -17,8 +17,19 @@ type ConfirmConfig = { theme?: PartialDeep; }; +function getBooleanValue(value: string, defaultValue?: boolean): boolean { + let answer = defaultValue !== false; + if (/^(y|yes)/i.test(value)) answer = true; + else if (/^(n|no)/i.test(value)) answer = false; + return answer; +} + +function boolToString(value: boolean): string { + return value ? 'Yes' : 'No'; +} + export default createPrompt((config, done) => { - const { transformer = (answer) => (answer ? 'yes' : 'no') } = config; + const { transformer = boolToString } = config; const [status, setStatus] = useState('idle'); const [value, setValue] = useState(''); const theme = makeTheme(config.theme); @@ -26,13 +37,15 @@ export default createPrompt((config, done) => { useKeypress((key, rl) => { if (isEnterKey(key)) { - let answer = config.default !== false; - if (/^(y|yes)/i.test(value)) answer = true; - else if (/^(n|no)/i.test(value)) answer = false; - + const answer = getBooleanValue(value, config.default); setValue(transformer(answer)); setStatus('done'); done(answer); + } else if (key.name === 'tab') { + const answer = boolToString(!getBooleanValue(value, config.default)); + rl.clearLine(0); // Remove the tab character. + rl.write(answer); + setValue(answer); } else { setValue(rl.line); }