diff --git a/.changeset/thin-socks-travel.md b/.changeset/thin-socks-travel.md new file mode 100644 index 00000000..89e23f09 --- /dev/null +++ b/.changeset/thin-socks-travel.md @@ -0,0 +1,5 @@ +--- +"@clack/prompts": minor +--- + +Added support for custom frames in spinner prompt diff --git a/packages/prompts/src/spinner.ts b/packages/prompts/src/spinner.ts index 4bad1686..7766d33c 100644 --- a/packages/prompts/src/spinner.ts +++ b/packages/prompts/src/spinner.ts @@ -16,6 +16,8 @@ export interface SpinnerOptions extends CommonOptions { onCancel?: () => void; cancelMessage?: string; errorMessage?: string; + frames?: string[]; + delay?: number; } export interface SpinnerResult { @@ -31,9 +33,9 @@ export const spinner = ({ output = process.stdout, cancelMessage, errorMessage, + frames = unicode ? ['◒', '◐', '◓', '◑'] : ['•', 'o', 'O', '0'], + delay = unicode ? 80 : 120, }: SpinnerOptions = {}): SpinnerResult => { - const frames = unicode ? ['◒', '◐', '◓', '◑'] : ['•', 'o', 'O', '0']; - const delay = unicode ? 80 : 120; const isCI = isCIFn(); let unblock: () => void; diff --git a/packages/prompts/test/__snapshots__/spinner.test.ts.snap b/packages/prompts/test/__snapshots__/spinner.test.ts.snap index f63dcc50..1e6468de 100644 --- a/packages/prompts/test/__snapshots__/spinner.test.ts.snap +++ b/packages/prompts/test/__snapshots__/spinner.test.ts.snap @@ -1,5 +1,51 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`spinner (isCI = false) > indicator customization > custom delay 1`] = ` +[ + "", + "│ +", + "◒ ", + "", + "", + "◐ ", + "", + "", + "◓ ", + "", + "", + "◑ ", + "", + "", + "◇ +", + "", +] +`; + +exports[`spinner (isCI = false) > indicator customization > custom frames 1`] = ` +[ + "", + "│ +", + "🐴 ", + "", + "", + "🦋 ", + "", + "", + "🐙 ", + "", + "", + "🐶 ", + "", + "", + "◇ +", + "", +] +`; + exports[`spinner (isCI = false) > message > sets message for next frame 1`] = ` [ "", @@ -9,6 +55,11 @@ exports[`spinner (isCI = false) > message > sets message for next frame 1`] = ` "", "", "◐ foo", + "", + "", + "◇ +", + "", ] `; @@ -104,6 +155,11 @@ exports[`spinner (isCI = false) > start > renders frames at interval 1`] = ` "", "", "◑ ", + "", + "", + "◇ +", + "", ] `; @@ -113,6 +169,11 @@ exports[`spinner (isCI = false) > start > renders message 1`] = ` "│ ", "◒ foo", + "", + "", + "◇ +", + "", ] `; @@ -122,6 +183,11 @@ exports[`spinner (isCI = false) > start > renders timer when indicator is "timer "│ ", "◒ [0s]", + "", + "", + "◇ [0s] +", + "", ] `; @@ -195,6 +261,38 @@ exports[`spinner (isCI = false) > stop > renders submit symbol and stops spinner ] `; +exports[`spinner (isCI = true) > indicator customization > custom delay 1`] = ` +[ + "", + "│ +", + "◒ ...", + " +", + "", + "", + "◇ +", + "", +] +`; + +exports[`spinner (isCI = true) > indicator customization > custom frames 1`] = ` +[ + "", + "│ +", + "🐴 ...", + " +", + "", + "", + "◇ +", + "", +] +`; + exports[`spinner (isCI = true) > message > sets message for next frame 1`] = ` [ "", @@ -206,6 +304,13 @@ exports[`spinner (isCI = true) > message > sets message for next frame 1`] = ` "", "", "◐ foo...", + " +", + "", + "", + "◇ +", + "", ] `; @@ -292,6 +397,13 @@ exports[`spinner (isCI = true) > start > renders frames at interval 1`] = ` "│ ", "◒ ...", + " +", + "", + "", + "◇ +", + "", ] `; @@ -301,6 +413,13 @@ exports[`spinner (isCI = true) > start > renders message 1`] = ` "│ ", "◒ foo...", + " +", + "", + "", + "◇ +", + "", ] `; @@ -310,6 +429,13 @@ exports[`spinner (isCI = true) > start > renders timer when indicator is "timer" "│ ", "◒ ...", + " +", + "", + "", + "◇ [0s] +", + "", ] `; diff --git a/packages/prompts/test/spinner.test.ts b/packages/prompts/test/spinner.test.ts index 95f97ce6..350506cd 100644 --- a/packages/prompts/test/spinner.test.ts +++ b/packages/prompts/test/spinner.test.ts @@ -45,6 +45,8 @@ describe.each(['true', 'false'])('spinner (isCI = %s)', (isCI) => { vi.advanceTimersByTime(80); } + result.stop(); + expect(output.buffer).toMatchSnapshot(); }); @@ -55,6 +57,8 @@ describe.each(['true', 'false'])('spinner (isCI = %s)', (isCI) => { vi.advanceTimersByTime(80); + result.stop(); + expect(output.buffer).toMatchSnapshot(); }); @@ -65,6 +69,8 @@ describe.each(['true', 'false'])('spinner (isCI = %s)', (isCI) => { vi.advanceTimersByTime(80); + result.stop(); + expect(output.buffer).toMatchSnapshot(); }); }); @@ -145,6 +151,40 @@ describe.each(['true', 'false'])('spinner (isCI = %s)', (isCI) => { vi.advanceTimersByTime(80); + result.stop(); + + expect(output.buffer).toMatchSnapshot(); + }); + }); + + describe('indicator customization', () => { + test('custom frames', () => { + const result = prompts.spinner({ output, frames: ['🐴', '🦋', '🐙', '🐶'] }); + + result.start(); + + // there are 4 frames + for (let i = 0; i < 4; i++) { + vi.advanceTimersByTime(80); + } + + result.stop(); + + expect(output.buffer).toMatchSnapshot(); + }); + + test('custom delay', () => { + const result = prompts.spinner({ output, delay: 200 }); + + result.start(); + + // there are 4 frames + for (let i = 0; i < 4; i++) { + vi.advanceTimersByTime(200); + } + + result.stop(); + expect(output.buffer).toMatchSnapshot(); }); });