From 34f432e824e9a7412aa94aadd45f2ba85f43114c Mon Sep 17 00:00:00 2001
From: Nalla Prakash <81852531+prakashNalla@users.noreply.github.com>
Date: Thu, 29 Oct 2026 03:57:40 +0530
Subject: [PATCH] fix(editor): keep a cleared color field editable
---
src/components/editor/ColorPicker.tsx | 9 ++++---
src/components/editor/CustomizePanel.tsx | 2 +-
tests/editor/color-picker.test.ts | 30 ++++++++++++++++++++++++
3 files changed, 37 insertions(+), 4 deletions(-)
create mode 100644 tests/editor/color-picker.test.ts
diff --git a/src/components/editor/ColorPicker.tsx b/src/components/editor/ColorPicker.tsx
index 413e82a..984618b 100644
--- a/src/components/editor/ColorPicker.tsx
+++ b/src/components/editor/ColorPicker.tsx
@@ -1,4 +1,5 @@
-import { useId } from 'react';
+import { useId, useRef } from 'react';
+import { normalizeHexColor } from '@/templates/utils';
interface ColorPickerProps {
label: string;
@@ -10,7 +11,9 @@ export function ColorPicker({ label, value, onChange }: ColorPickerProps) {
const id = useId();
const colorId = `${id}-color`;
const hexId = `${id}-hex`;
- const displayValue = value.startsWith('#') ? value : `#${value}`;
+ const displayValue = value && !value.startsWith('#') ? `#${value}` : value;
+ const lastValid = useRef('#000000');
+ lastValid.current = normalizeHexColor(value, lastValid.current);
return (
@@ -23,7 +26,7 @@ export function ColorPicker({ label, value, onChange }: ColorPickerProps) {
id={colorId}
type="color"
className="editor-color-swatch"
- value={displayValue}
+ value={lastValid.current}
onChange={(e) => onChange(e.target.value)}
/>
diff --git a/src/components/editor/CustomizePanel.tsx b/src/components/editor/CustomizePanel.tsx
index 8e1e1eb..a781bb5 100644
--- a/src/components/editor/CustomizePanel.tsx
+++ b/src/components/editor/CustomizePanel.tsx
@@ -113,7 +113,7 @@ function renderField(
onChange(field.key, v)}
/>
);
diff --git a/tests/editor/color-picker.test.ts b/tests/editor/color-picker.test.ts
new file mode 100644
index 0000000..9f6fcdf
--- /dev/null
+++ b/tests/editor/color-picker.test.ts
@@ -0,0 +1,30 @@
+import { describe, it, expect } from 'vitest';
+import { renderToString } from 'react-dom/server';
+import React from 'react';
+import { CustomizePanel } from '@/components/editor/CustomizePanel';
+import type { TemplateField } from '@/templates/types';
+
+const fields: TemplateField[] = [{ key: 'bgColor', label: 'Background', type: 'color', defaultValue: '#0f172a', group: 'Style' }];
+
+function inputs(bgColor: unknown) {
+ const html = renderToString(
+ React.createElement(CustomizePanel, { fields, params: { bgColor }, onParamChange: () => {}, onReset: () => {} }),
+ );
+ const value = (type: string) => html.match(new RegExp(`]*type="${type}"[^>]*value="([^"]*)"`))?.[1];
+ return { swatch: value('color'), text: value('text') };
+}
+
+describe('ColorPicker', () => {
+ it('leaves a cleared hex field empty instead of filling in #000000', () => {
+ expect(inputs('').text).toBe('');
+ });
+
+ it('shows what was typed and gives the swatch a full #rrggbb color', () => {
+ expect(inputs('#fff')).toEqual({ swatch: '#ffffff', text: '#fff' });
+ expect(inputs('ff8800')).toEqual({ swatch: '#ff8800', text: '#ff8800' });
+ });
+
+ it('keeps a partial value in the text field', () => {
+ expect(inputs('#ff88').text).toBe('#ff88');
+ });
+});