From 23617c0422da10de04a6887386d751bb64678c2d Mon Sep 17 00:00:00 2001 From: Kawa Date: Wed, 1 Apr 2026 11:19:17 +0200 Subject: [PATCH] feat(09-05): add helpTextPrefix prop and fix tooltip placement + hover behavior - TextFieldMD3: add helpTextPrefix prop rendered inline left of helpText - Guard changed to (helpText || helpTextPrefix) so icon renders without helpText - FieldRenderer: move tooltip icon from above field to inline with helpText via helpTextPrefix - FieldRenderer: add hoverTooltip state; tooltip visible on mouseEnter, dismisses on mouseLeave - FieldRenderer: click-to-unpin clears hoverTooltip to prevent sticky tooltip after dismiss - PasswordField: same tooltip fix as FieldRenderer text-branch - Select-branch also gains hover support --- src/components/ui/FieldRenderer.tsx | 43 +++++++++++++++++------------ src/components/ui/PasswordField.tsx | 37 +++++++++++++++---------- src/components/ui/TextFieldMD3.tsx | 11 ++++++-- 3 files changed, 57 insertions(+), 34 deletions(-) diff --git a/src/components/ui/FieldRenderer.tsx b/src/components/ui/FieldRenderer.tsx index 3cbd151..dc4167f 100644 --- a/src/components/ui/FieldRenderer.tsx +++ b/src/components/ui/FieldRenderer.tsx @@ -12,6 +12,7 @@ interface FieldRendererProps { export function FieldRenderer({ field, register, error }: FieldRendererProps) { const [showTooltip, setShowTooltip] = useState(false); + const [hoverTooltip, setHoverTooltip] = useState(false); // provider field: single-option select — hide from UI, auto-register with default value if (field.key === 'provider' && field.options?.length === 1) { @@ -48,7 +49,9 @@ export function FieldRenderer({ field, register, error }: FieldRendererProps) { {field.tooltipText && ( )} - {field.tooltipText && showTooltip && ( + {field.tooltipText && (showTooltip || hoverTooltip) && (

{field.tooltipText}

@@ -80,23 +83,23 @@ export function FieldRenderer({ field, register, error }: FieldRendererProps) { } // text (default) — uses TextFieldMD3 for floating label + const tooltipIcon = field.tooltipText ? ( + + ) : undefined; + + const tooltipVisible = showTooltip || hoverTooltip; + return (
- {field.tooltipText && ( - - )} - {field.tooltipText && showTooltip && ( -

- {field.tooltipText} -

- )} + {field.tooltipText && tooltipVisible && ( +

+ {field.tooltipText} +

+ )}
); } diff --git a/src/components/ui/PasswordField.tsx b/src/components/ui/PasswordField.tsx index 00980b1..91da6e4 100644 --- a/src/components/ui/PasswordField.tsx +++ b/src/components/ui/PasswordField.tsx @@ -14,6 +14,7 @@ interface PasswordFieldProps { export function PasswordField({ id, label, error, registration, helpText, tooltipText }: PasswordFieldProps) { const [show, setShow] = useState(false); const [showTooltip, setShowTooltip] = useState(false); + const [hoverTooltip, setHoverTooltip] = useState(false); const toggleButton = ( + ) : undefined; + + const tooltipVisible = showTooltip || hoverTooltip; + return (
- {tooltipText && ( - - )} - {tooltipText && showTooltip && ( -

- {tooltipText} -

- )} + {tooltipText && tooltipVisible && ( +

+ {tooltipText} +

+ )}
); } diff --git a/src/components/ui/TextFieldMD3.tsx b/src/components/ui/TextFieldMD3.tsx index 29442ac..230837f 100644 --- a/src/components/ui/TextFieldMD3.tsx +++ b/src/components/ui/TextFieldMD3.tsx @@ -7,6 +7,7 @@ interface TextFieldMD3Props { registration: UseFormRegisterReturn; type?: 'text' | 'password'; helpText?: string; + helpTextPrefix?: React.ReactNode; required?: boolean; suffix?: React.ReactNode; } @@ -18,6 +19,7 @@ export function TextFieldMD3({ registration, type = 'text', helpText, + helpTextPrefix, required, suffix, }: TextFieldMD3Props) { @@ -62,8 +64,13 @@ export function TextFieldMD3({
{suffix}
)} - {helpText && !error && ( -

{helpText}

+ {(helpText || helpTextPrefix) && !error && ( +
+ {helpTextPrefix} + {helpText && ( +

{helpText}

+ )} +
)} {error && (