From 54aa997cfd73d7977416ffb2d14f15f7b2e2dffe Mon Sep 17 00:00:00 2001 From: rcourtman Date: Mon, 22 Dec 2025 09:48:40 +0000 Subject: [PATCH] fix: Prevent ignored container inputs from removing trailing newlines. Related to #865 --- .../src/components/Alerts/ThresholdsTable.tsx | 12 +++++++- .../Alerts/__tests__/ThresholdsTable.test.tsx | 30 ++++++++++++++++--- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/frontend-modern/src/components/Alerts/ThresholdsTable.tsx b/frontend-modern/src/components/Alerts/ThresholdsTable.tsx index 897557af0..46ba61301 100644 --- a/frontend-modern/src/components/Alerts/ThresholdsTable.tsx +++ b/frontend-modern/src/components/Alerts/ThresholdsTable.tsx @@ -349,7 +349,17 @@ export function ThresholdsTable(props: ThresholdsTableProps) { const serviceCriticalInputId = 'docker-service-critical-gap'; createEffect(() => { - setDockerIgnoredInput(props.dockerIgnoredPrefixes().join('\n')); + const remote = props.dockerIgnoredPrefixes(); + const local = dockerIgnoredInput(); + const normalizedLocal = normalizeDockerIgnoredInput(local); + + const isSynced = + remote.length === normalizedLocal.length && + remote.every((val, i) => val === normalizedLocal[i]); + + if (!isSynced) { + setDockerIgnoredInput(remote.join('\n')); + } }); const serviceGapValidationMessage = createMemo(() => { diff --git a/frontend-modern/src/components/Alerts/__tests__/ThresholdsTable.test.tsx b/frontend-modern/src/components/Alerts/__tests__/ThresholdsTable.test.tsx index 35a852aeb..c9c0d9e0c 100644 --- a/frontend-modern/src/components/Alerts/__tests__/ThresholdsTable.test.tsx +++ b/frontend-modern/src/components/Alerts/__tests__/ThresholdsTable.test.tsx @@ -185,8 +185,8 @@ const renderThresholdsTable = (options?: { options?.includeReset === false ? undefined : vi.fn(() => { - setPrefixes([]); - }); + setPrefixes([]); + }); const base = baseProps(); @@ -206,8 +206,8 @@ const renderThresholdsTable = (options?: { value: | typeof DEFAULT_DOCKER_DEFAULTS | (( - prev: typeof DEFAULT_DOCKER_DEFAULTS, - ) => typeof DEFAULT_DOCKER_DEFAULTS), + prev: typeof DEFAULT_DOCKER_DEFAULTS, + ) => typeof DEFAULT_DOCKER_DEFAULTS), ) => { const next = typeof value === 'function' @@ -304,6 +304,28 @@ describe('ThresholdsTable docker ignored prefixes', () => { expect(textarea).toHaveValue(''); expect(setHasUnsavedChangesMock).toHaveBeenCalledWith(true); }); + + it('preserves trailing newlines when typing', () => { + const { getPrefixes } = renderThresholdsTable({ includeReset: false }); + const textarea = screen.getByPlaceholderText('runner-') as HTMLTextAreaElement; + + // Simulate typing "abc" + fireEvent.input(textarea, { target: { value: 'abc' } }); + expect(getPrefixes()).toEqual(['abc']); + expect(textarea).toHaveValue('abc'); + + // Simulate hitting Enter ("abc\n") + fireEvent.input(textarea, { target: { value: 'abc\n' } }); + // Prop should still be ['abc'] as "abc" trimmed is "abc", "\n" trimmed is empty + expect(getPrefixes()).toEqual(['abc']); + // Value should NOT be reset to "abc" by the effect, it should remain "abc\n" + expect(textarea).toHaveValue('abc\n'); + + // Simulate typing "d" ("abc\nd") + fireEvent.input(textarea, { target: { value: 'abc\nd' } }); + expect(getPrefixes()).toEqual(['abc', 'd']); + expect(textarea).toHaveValue('abc\nd'); + }); }); describe('ThresholdsTable service gap validation', () => {