From ffb4e866ed38c1da5479020d7287ca464c3dbce0 Mon Sep 17 00:00:00 2001 From: Philipp Reinking Date: Sat, 17 Oct 2026 01:26:32 +0200 Subject: [PATCH 1/3] Keep an empty Max characters empty on a long text question Co-Authored-By: Claude Sonnet 5.5 --- resources/js/components/CharCount.vue | 2 +- .../Factory/Main/ConfigureTextarea.test.ts | 67 +++++++++++++++++++ .../Factory/Main/ConfigureTextarea.vue | 9 +-- resources/js/types/models.d.ts | 2 +- 4 files changed, 74 insertions(+), 6 deletions(-) create mode 100644 resources/js/components/Factory/Main/ConfigureTextarea.test.ts diff --git a/resources/js/components/CharCount.vue b/resources/js/components/CharCount.vue index bf528e5c..0a05ce7b 100644 --- a/resources/js/components/CharCount.vue +++ b/resources/js/components/CharCount.vue @@ -19,7 +19,7 @@ import { computed, ref } from "vue"; const props = defineProps<{ text?: string; - maxChars?: number; + maxChars?: number | null; }>(); const charCount = ref(props.text?.length ?? 0); diff --git a/resources/js/components/Factory/Main/ConfigureTextarea.test.ts b/resources/js/components/Factory/Main/ConfigureTextarea.test.ts new file mode 100644 index 00000000..e15f4743 --- /dev/null +++ b/resources/js/components/Factory/Main/ConfigureTextarea.test.ts @@ -0,0 +1,67 @@ +import { flushPromises, mount } from "@vue/test-utils"; +import { createPinia, setActivePinia } from "pinia"; +import { describe, expect, it, vi } from "vitest"; +import ConfigureTextarea from "./ConfigureTextarea.vue"; +import { useWorkbench } from "@/stores"; + +const saved = vi.hoisted(() => ({ + options: {} as Record, +})); + +vi.mock("../utils/useInteractionsUtils", () => ({ + useInteractionsUtils: () => ({ + findOrCreate: async () => ({ id: 7, label: "", options: saved.options }), + }), +})); + +// a long text answer that was saved before, with the given options +const reopen = async (options: Record) => { + saved.options = options; + const pinia = createPinia(); + setActivePinia(pinia); + const workbench = useWorkbench(); + workbench.block = { + interactions: [{ type: "textarea" }], + } as unknown as FormBlockModel; + const save = vi + .spyOn(workbench, "updateInteraction") + .mockImplementation(() => {}); + + const wrapper = mount(ConfigureTextarea, { + global: { plugins: [pinia], provide: { block: { type: "input" } } }, + }); + await flushPromises(); + + return { wrapper, save }; +}; + +describe("long text max characters", () => { + it("stays empty and saves nothing when a question with no limit reopens", async () => { + const { wrapper, save } = await reopen({ rows: 5, max_chars: null }); + + expect( + (wrapper.find("input#textarea-max-chars").element as HTMLInputElement) + .value, + ).toBe(""); + expect(save).not.toHaveBeenCalled(); + }); + + it("shows the saved limit when a question with a limit reopens", async () => { + const { wrapper } = await reopen({ rows: 5, max_chars: 120 }); + + expect( + (wrapper.find("input#textarea-max-chars").element as HTMLInputElement) + .value, + ).toBe("120"); + }); + + it("saves no limit when the owner empties the field", async () => { + const { wrapper, save } = await reopen({ rows: 5, max_chars: 500 }); + + await wrapper.find("input#textarea-max-chars").setValue(""); + await flushPromises(); + + expect(save).toHaveBeenCalledTimes(1); + expect(save.mock.lastCall?.[0].options.max_chars).toBeNull(); + }); +}); diff --git a/resources/js/components/Factory/Main/ConfigureTextarea.vue b/resources/js/components/Factory/Main/ConfigureTextarea.vue index 8791ebeb..bf271507 100644 --- a/resources/js/components/Factory/Main/ConfigureTextarea.vue +++ b/resources/js/components/Factory/Main/ConfigureTextarea.vue @@ -31,7 +31,7 @@ (""); const rows = ref(5); -const maxChars = ref(500); +// empty means no limit; a new question starts at 500 +const maxChars = ref(500); const interaction = ref(null) as unknown as Ref; onMounted(async () => { @@ -65,7 +66,7 @@ onMounted(async () => { label.value = interaction.value.label; rows.value = interaction.value.options?.rows ?? 5; - maxChars.value = interaction.value.options?.max_chars ?? 500; + maxChars.value = isNew ? 500 : (interaction.value.options?.max_chars ?? ""); await settled(isNew); } }); @@ -76,7 +77,7 @@ const settled = useSettingsWatch([label, rows, maxChars], (newValues) => { label: newValues[0], options: { rows: Math.max(1, Math.min(10, parseInt(newValues[1]))), - max_chars: parseInt(newValues[2]), + max_chars: parseInt(newValues[2]) || null, }, }; diff --git a/resources/js/types/models.d.ts b/resources/js/types/models.d.ts index 359fcffb..b6d4232d 100644 --- a/resources/js/types/models.d.ts +++ b/resources/js/types/models.d.ts @@ -167,7 +167,7 @@ type FormBlockInteractionType = type FormBlockInteractionSettings = { rows?: number; - max_chars?: number; + max_chars?: number | null; minDate?: string; maxDate?: string; noPastDates?: boolean; From 3b8e8d17ac4f00ddea81f957f95bc6f629bccbe7 Mon Sep 17 00:00:00 2001 From: Philipp Reinking Date: Sat, 17 Oct 2026 01:33:28 +0200 Subject: [PATCH 2/3] Add changelog line for #291 Co-Authored-By: Claude Sonnet 5.5 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49d103c0..9a215b73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ Lines marked **Upgrade step** need you to act after updating. ### Fixed +- A long text question with an empty "Max characters" now has no limit, also when you open it again. Before, the editor showed 500 and saved it back. New long text questions still start at 500. (#291) - A new long text question now shows in your form and in the editor preview, also before you set any options for it. Before, it failed to show. (#286) - Ticking "Remember me" on the login page now keeps you logged in after your browser session ends. Before, it was ignored. (#227) - The counter under the text editor shows the length of your text as soon as a text opens. Before, it showed a larger number until your first edit. (#228) From 7d58c3dc8ab4d46f37ee44ae7d0df6a127672ea3 Mon Sep 17 00:00:00 2001 From: Philipp Reinking Date: Sat, 17 Oct 2026 01:38:30 +0200 Subject: [PATCH 3/3] Move changelog line below #286 Co-Authored-By: Claude Sonnet 5.5 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a215b73..1914ef17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,8 +36,8 @@ Lines marked **Upgrade step** need you to act after updating. ### Fixed -- A long text question with an empty "Max characters" now has no limit, also when you open it again. Before, the editor showed 500 and saved it back. New long text questions still start at 500. (#291) - A new long text question now shows in your form and in the editor preview, also before you set any options for it. Before, it failed to show. (#286) +- A long text question with an empty "Max characters" now has no limit, also when you open it again. Before, the editor showed 500 and saved it back. New long text questions still start at 500. (#291) - Ticking "Remember me" on the login page now keeps you logged in after your browser session ends. Before, it was ignored. (#227) - The counter under the text editor shows the length of your text as soon as a text opens. Before, it showed a larger number until your first edit. (#228) - A question or completion page text that holds only an image keeps the image when you save it. Before, it was saved as empty and the image was lost. (#229)