From dee2b4de623111d90a4bb9f04582594a85b21ef1 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Fri, 28 Aug 2026 17:53:10 -0700 Subject: [PATCH] fix(secrets): preserve caret when revealing values --- .../secret-value-field.test.tsx | 27 ++++++++++++++++++- .../secret-value-field/secret-value-field.tsx | 6 ++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/secrets/components/secret-value-field/secret-value-field.test.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/secrets/components/secret-value-field/secret-value-field.test.tsx index 9daca621db1..f84f35f685f 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/secrets/components/secret-value-field/secret-value-field.test.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/secrets/components/secret-value-field/secret-value-field.test.tsx @@ -6,7 +6,12 @@ import { createRoot, type Root } from 'react-dom/client' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' vi.mock('@sim/emcn', () => ({ - ChipInput: (props: ComponentProps<'input'>) => , + ChipInput: ({ + inputClassName, + ...props + }: ComponentProps<'input'> & { inputClassName?: string }) => ( + + ), })) import { SecretValueField } from '@/app/workspace/[workspaceId]/settings/components/secrets/components/secret-value-field/secret-value-field' @@ -33,6 +38,26 @@ afterEach(() => { }) describe('SecretValueField', () => { + it('preserves the caret position when revealing an editable value', () => { + const value = 'editable-secret-value' + act(() => root.render()) + + expect(input().value).toBe(value) + expect(input().className).toContain('[-webkit-text-security:disc]') + + input().setSelectionRange(15, 15) + act(() => input().focus()) + + expect(input().value).toBe(value) + expect(input().selectionStart).toBe(15) + expect(input().readOnly).toBe(false) + expect(input().className).not.toContain('[-webkit-text-security:disc]') + + act(() => input().blur()) + expect(input().value).toBe(value) + expect(input().className).toContain('[-webkit-text-security:disc]') + }) + it('lets a read-only viewer reveal an allowed value without making it editable', () => { act(() => root.render()) diff --git a/apps/sim/app/workspace/[workspaceId]/settings/components/secrets/components/secret-value-field/secret-value-field.tsx b/apps/sim/app/workspace/[workspaceId]/settings/components/secrets/components/secret-value-field/secret-value-field.tsx index 7284b9e9b7b..0e8dcfdacb2 100644 --- a/apps/sim/app/workspace/[workspaceId]/settings/components/secrets/components/secret-value-field/secret-value-field.tsx +++ b/apps/sim/app/workspace/[workspaceId]/settings/components/secrets/components/secret-value-field/secret-value-field.tsx @@ -55,8 +55,11 @@ export function SecretValueField({ const editable = canEdit && !readOnly const revealable = canEdit || canReveal const maskActive = revealable && !unmasked && !focused + const visuallyMaskEditableValue = editable && maskActive const displayValue = - !revealable || (maskActive && value.length > 0) ? BULLET.repeat(VIEWER_MASK_LENGTH) : value + !revealable || (!editable && maskActive && value.length > 0) + ? BULLET.repeat(VIEWER_MASK_LENGTH) + : value return ( { if (editable) onChange?.(event.target.value) }}