diff --git a/src/terminal/prompt-select-styled-params.ts b/src/terminal/prompt-select-styled-params.ts new file mode 100644 index 00000000000..34e01bc93b2 --- /dev/null +++ b/src/terminal/prompt-select-styled-params.ts @@ -0,0 +1,30 @@ +import { stylePromptHint, stylePromptMessage } from "./prompt-style.js"; + +type SelectParamsLike = { + message: string; + options: readonly object[]; +}; + +type PromptSelectStylers = { + message: (value: string) => string; + hint: (value: string) => string | undefined; +}; + +const defaultStylers: PromptSelectStylers = { + message: stylePromptMessage, + hint: stylePromptHint, +}; + +export function styleSelectParams( + params: TParams, + stylers: PromptSelectStylers = defaultStylers, +): TParams { + return { + ...params, + message: stylers.message(params.message), + options: params.options.map((opt) => { + const hint = "hint" in opt && typeof opt.hint === "string" ? opt.hint : undefined; + return hint === undefined ? opt : { ...opt, hint: stylers.hint(hint) }; + }), + } as TParams; +} diff --git a/src/terminal/prompt-select-styled.test.ts b/src/terminal/prompt-select-styled.test.ts index 528d2160c88..c89d0e43fa2 100644 --- a/src/terminal/prompt-select-styled.test.ts +++ b/src/terminal/prompt-select-styled.test.ts @@ -1,45 +1,23 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { describe, expect, it } from "vitest"; +import { styleSelectParams } from "./prompt-select-styled-params.js"; -const { selectMock, stylePromptMessageMock, stylePromptHintMock } = vi.hoisted(() => ({ - selectMock: vi.fn(), - stylePromptMessageMock: vi.fn((value: string) => `msg:${value}`), - stylePromptHintMock: vi.fn((value: string) => `hint:${value}`), -})); - -vi.mock("@clack/prompts", () => ({ - select: selectMock, -})); - -vi.mock("./prompt-style.js", () => ({ - stylePromptMessage: stylePromptMessageMock, - stylePromptHint: stylePromptHintMock, -})); - -import { selectStyled } from "./prompt-select-styled.js"; - -describe("selectStyled", () => { - beforeEach(() => { - selectMock.mockClear(); - stylePromptMessageMock.mockClear(); - stylePromptHintMock.mockClear(); - }); - - it("styles message and option hints before delegating to clack select", () => { - const expected = Symbol("selected"); - selectMock.mockReturnValue(expected); - - const result = selectStyled({ - message: "Pick channel", - options: [ - { value: "stable", label: "Stable", hint: "Tagged releases" }, - { value: "dev", label: "Dev" }, - ], - }); - - expect(result).toBe(expected); - expect(stylePromptMessageMock).toHaveBeenCalledWith("Pick channel"); - expect(stylePromptHintMock).toHaveBeenCalledWith("Tagged releases"); - expect(selectMock).toHaveBeenCalledWith({ +describe("styleSelectParams", () => { + it("styles message and option hints before select receives params", () => { + expect( + styleSelectParams( + { + message: "Pick channel", + options: [ + { value: "stable", label: "Stable", hint: "Tagged releases" }, + { value: "dev", label: "Dev" }, + ], + }, + { + message: (value) => `msg:${value}`, + hint: (value) => `hint:${value}`, + }, + ), + ).toEqual({ message: "msg:Pick channel", options: [ { value: "stable", label: "Stable", hint: "hint:Tagged releases" }, @@ -47,4 +25,24 @@ describe("selectStyled", () => { ], }); }); + + it("keeps unhinted options unchanged", () => { + const option = { value: "dev", label: "Dev" }; + const params = styleSelectParams( + { + message: "Pick channel", + options: [option], + }, + { + message: (value) => `msg:${value}`, + hint: (value) => `hint:${value}`, + }, + ); + + expect(params).toEqual({ + message: "msg:Pick channel", + options: [{ value: "dev", label: "Dev" }], + }); + expect(params.options[0]).toBe(option); + }); }); diff --git a/src/terminal/prompt-select-styled.ts b/src/terminal/prompt-select-styled.ts index 40c86285c15..7af07935f8f 100644 --- a/src/terminal/prompt-select-styled.ts +++ b/src/terminal/prompt-select-styled.ts @@ -1,12 +1,6 @@ import { select } from "@clack/prompts"; -import { stylePromptHint, stylePromptMessage } from "./prompt-style.js"; +import { styleSelectParams } from "./prompt-select-styled-params.js"; export function selectStyled(params: Parameters>[0]) { - return select({ - ...params, - message: stylePromptMessage(params.message), - options: params.options.map((opt) => - opt.hint === undefined ? opt : { ...opt, hint: stylePromptHint(opt.hint) }, - ), - }); + return select(styleSelectParams(params)); }