feat(ui): add character counter to controlled Input and Textarea components
Co-authored-by: ragusa-it <196988693+ragusa-it@users.noreply.github.com>
This commit is contained in:
@@ -5,3 +5,7 @@
|
|||||||
## 2024-05-24 - Accessible Loading Buttons
|
## 2024-05-24 - Accessible Loading Buttons
|
||||||
**Learning:** Replacing button text with a spinner destroys the accessible name.
|
**Learning:** Replacing button text with a spinner destroys the accessible name.
|
||||||
**Action:** Use `aria-busy="true"`, keep children in DOM (visually hidden via opacity/class), and overlay spinner absolutely. Ensure wrapper element replicates flex layout (gap/alignment) to prevent layout shifts.
|
**Action:** Use `aria-busy="true"`, keep children in DOM (visually hidden via opacity/class), and overlay spinner absolutely. Ensure wrapper element replicates flex layout (gap/alignment) to prevent layout shifts.
|
||||||
|
|
||||||
|
## 2024-05-24 - Enhancing Inputs Safely
|
||||||
|
**Learning:** Adding features like character counters to generic input components must handle both controlled and uncontrolled states. Assuming a component is controlled (using `props.value`) can break uncontrolled usage by showing stale data (e.g., sticking at 0/100).
|
||||||
|
**Action:** When enhancing generic components, detect uncontrolled state (e.g., `props.value === undefined`) and either implement internal state tracking or gracefully degrade (hide the feature) to avoid misleading UX.
|
||||||
|
|||||||
@@ -61,3 +61,10 @@
|
|||||||
font-size: 0.8125rem;
|
font-size: 0.8125rem;
|
||||||
color: var(--md-sys-color-error);
|
color: var(--md-sys-color-error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.charCount {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--md-sys-color-outline);
|
||||||
|
text-align: right;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
|||||||
@@ -30,6 +30,11 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
|
|||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
{error && <span id={errorId} className={styles.error}>{error}</span>}
|
{error && <span id={errorId} className={styles.error}>{error}</span>}
|
||||||
|
{props.maxLength !== undefined && props.value !== undefined && (
|
||||||
|
<div className={styles.charCount} aria-hidden="true">
|
||||||
|
{String(props.value).length} / {props.maxLength}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -66,6 +71,11 @@ export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
{error && <span id={errorId} className={styles.error}>{error}</span>}
|
{error && <span id={errorId} className={styles.error}>{error}</span>}
|
||||||
|
{props.maxLength !== undefined && props.value !== undefined && (
|
||||||
|
<div className={styles.charCount} aria-hidden="true">
|
||||||
|
{String(props.value).length} / {props.maxLength}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user