Toggle Group component with storybook and testing

This commit is contained in:
adilallo
2025-10-14 17:00:27 -06:00
parent 929729a67f
commit 460237fc66
6 changed files with 1118 additions and 62 deletions
+137
View File
@@ -0,0 +1,137 @@
import React, { memo, useCallback, useId, forwardRef } from "react";
const ToggleGroup = memo(
forwardRef((props, ref) => {
const {
children,
className = "",
position = "left",
state = "default",
showText = true,
ariaLabel,
onChange,
onFocus,
onBlur,
...rest
} = props;
const groupId = useId();
// Position-based styling for border radius
const getPositionStyles = useCallback((pos) => {
switch (pos) {
case "left":
return "rounded-l-[var(--measures-radius-medium)] rounded-r-none";
case "middle":
return "rounded-none";
case "right":
return "rounded-r-[var(--measures-radius-medium)] rounded-l-none";
default:
return "rounded-[var(--measures-radius-medium)]";
}
}, []);
// State-based styling
const getStateStyles = useCallback((state) => {
switch (state) {
case "hover":
return "bg-[var(--color-magenta-magenta100)] text-[var(--color-content-default-primary)]";
case "focus":
return "bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-primary)] shadow-[0_0_5px_1px_#3281F8]";
case "selected":
return "bg-[var(--color-magenta-magenta100)] text-[var(--color-content-default-primary)] shadow-[inset_0_0_0_1px_var(--color-border-default-secondary)]";
case "default":
default:
return "bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-primary)]";
}
}, []);
const positionStyles = getPositionStyles(position);
const stateStyles = getStateStyles(state);
const handleClick = useCallback(
(e) => {
if (onChange) {
onChange(e);
}
},
[onChange]
);
const handleFocus = useCallback(
(e) => {
if (onFocus) {
onFocus(e);
}
},
[onFocus]
);
const handleBlur = useCallback(
(e) => {
if (onBlur) {
onBlur(e);
}
},
[onBlur]
);
const handleKeyDown = useCallback(
(e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
if (onChange) {
onChange(e);
}
}
},
[onChange]
);
const toggleClasses = `
${positionStyles}
${stateStyles}
py-[var(--measures-spacing-008)]
px-[var(--measures-spacing-008)]
gap-[var(--measures-spacing-008)]
font-inter
font-medium
text-[12px]
leading-[12px]
cursor-pointer
transition-all
duration-200
focus:outline-none
focus-visible:shadow-[0_0_5px_1px_#3281F8]
hover:bg-[var(--color-magenta-magenta100)]
flex
items-center
justify-center
${className}
`
.trim()
.replace(/\s+/g, " ");
return (
<button
ref={ref}
id={groupId}
type="button"
role="button"
aria-label={ariaLabel || (showText ? undefined : "Toggle option")}
onClick={handleClick}
onKeyDown={handleKeyDown}
onFocus={handleFocus}
onBlur={handleBlur}
className={toggleClasses}
{...rest}
>
{showText ? children : children || "☰"}
</button>
);
})
);
ToggleGroup.displayName = "ToggleGroup";
export default ToggleGroup;