Number Card and Form Component Updates #37

Merged
an.di merged 10 commits from adilallo/component/NumberedCardUpdate into main 2026-02-04 21:16:06 +00:00
5 changed files with 285 additions and 410 deletions
Showing only changes of commit 0e7985287f - Show all commits
+11 -74
View File
@@ -75,50 +75,6 @@ export default function ComponentsPreview() {
<div className="bg-[var(--color-surface-default-secondary)] rounded-[var(--radius-300,12px)] p-[var(--spacing-scale-032)] space-y-[var(--spacing-scale-024)]"> <div className="bg-[var(--color-surface-default-secondary)] rounded-[var(--radius-300,12px)] p-[var(--spacing-scale-032)] space-y-[var(--spacing-scale-024)]">
<div className="space-y-[var(--spacing-scale-016)]"> <div className="space-y-[var(--spacing-scale-016)]">
<div>
<h3 className="font-inter text-[20px] leading-[24px] font-semibold text-[var(--color-content-default-primary)] mb-[var(--spacing-scale-012)]">
All Sizes
</h3>
<div className="space-y-[var(--spacing-scale-016)]">
<SelectInput
label="Small Select Input"
placeholder="Choose an option"
size="small"
value={selectValue}
onChange={(data) => setSelectValue(data.target.value)}
options={[
{ value: "option1", label: "Option 1" },
{ value: "option2", label: "Option 2" },
{ value: "option3", label: "Option 3" },
]}
/>
<SelectInput
label="Medium Select Input"
placeholder="Choose an option"
size="medium"
value={selectValue}
onChange={(data) => setSelectValue(data.target.value)}
options={[
{ value: "option1", label: "Option 1" },
{ value: "option2", label: "Option 2" },
{ value: "option3", label: "Option 3" },
]}
/>
<SelectInput
label="Large Select Input"
placeholder="Choose an option"
size="large"
value={selectValue}
onChange={(data) => setSelectValue(data.target.value)}
options={[
{ value: "option1", label: "Option 1" },
{ value: "option2", label: "Option 2" },
{ value: "option3", label: "Option 3" },
]}
/>
</div>
</div>
<div> <div>
<h3 className="font-inter text-[20px] leading-[24px] font-semibold text-[var(--color-content-default-primary)] mb-[var(--spacing-scale-012)]"> <h3 className="font-inter text-[20px] leading-[24px] font-semibold text-[var(--color-content-default-primary)] mb-[var(--spacing-scale-012)]">
States States
@@ -134,6 +90,17 @@ export default function ComponentsPreview() {
{ value: "option3", label: "Option 3" }, { value: "option3", label: "Option 3" },
]} ]}
/> />
<SelectInput
label="Interactive Select Input (click = active)"
placeholder="Choose an option"
value={selectValue}
onChange={(data) => setSelectValue(data.target.value)}
options={[
{ value: "option1", label: "Option 1" },
{ value: "option2", label: "Option 2" },
{ value: "option3", label: "Option 3" },
]}
/>
<SelectInput <SelectInput
label="Disabled Select Input" label="Disabled Select Input"
placeholder="Choose an option" placeholder="Choose an option"
@@ -158,36 +125,6 @@ export default function ComponentsPreview() {
/> />
</div> </div>
</div> </div>
<div>
<h3 className="font-inter text-[20px] leading-[24px] font-semibold text-[var(--color-content-default-primary)] mb-[var(--spacing-scale-012)]">
Label Variants
</h3>
<div className="space-y-[var(--spacing-scale-016)]">
<SelectInput
label="Default Label"
placeholder="Choose an option"
value=""
labelVariant="default"
options={[
{ value: "option1", label: "Option 1" },
{ value: "option2", label: "Option 2" },
{ value: "option3", label: "Option 3" },
]}
/>
<SelectInput
label="Horizontal Label"
placeholder="Choose an option"
value=""
labelVariant="horizontal"
options={[
{ value: "option1", label: "Option 1" },
{ value: "option2", label: "Option 2" },
{ value: "option3", label: "Option 3" },
]}
/>
</div>
</div>
</div> </div>
</div> </div>
</section> </section>
@@ -22,12 +22,10 @@ const SelectInputContainer = forwardRef<HTMLButtonElement, SelectInputProps>(
{ {
id, id,
label, label,
labelVariant = "default", state: externalState = "default",
size = "medium",
state = "default",
disabled = false, disabled = false,
error = false, error = false,
placeholder = "Select an option", placeholder = "Choose an option",
className = "", className = "",
children, children,
value, value,
@@ -45,6 +43,14 @@ const SelectInputContainer = forwardRef<HTMLButtonElement, SelectInputProps>(
const selectRef = useRef<HTMLButtonElement>(null); const selectRef = useRef<HTMLButtonElement>(null);
const menuRef = useRef<HTMLDivElement>(null); const menuRef = useRef<HTMLDivElement>(null);
// Internal state management: track if focused and how (mouse vs keyboard)
const [isFocused, setIsFocused] = useState(false);
const [focusMethod, setFocusMethod] = useState<"mouse" | "keyboard" | null>(null);
const wasMouseDownRef = useRef(false);
// Determine if we should auto-manage focus (only when state is "default" or undefined)
const shouldAutoManageFocus = externalState === "default" || externalState === undefined;
// Sync internal state with external value prop // Sync internal state with external value prop
useEffect(() => { useEffect(() => {
if (value !== undefined && value !== selectedValue) { if (value !== undefined && value !== selectedValue) {
@@ -69,7 +75,6 @@ const SelectInputContainer = forwardRef<HTMLButtonElement, SelectInputProps>(
if (onChange) { if (onChange) {
onChange({ target: { value: optionValue, text: optionText } }); onChange({ target: { value: optionValue, text: optionText } });
} }
// Return focus to the select button for accessibility
if (selectRef.current) { if (selectRef.current) {
selectRef.current.focus(); selectRef.current.focus();
} }
@@ -77,6 +82,13 @@ const SelectInputContainer = forwardRef<HTMLButtonElement, SelectInputProps>(
[onChange], [onChange],
); );
// Handle mouse down to detect mouse clicks
const handleMouseDown = useCallback(() => {
if (!disabled && shouldAutoManageFocus) {
wasMouseDownRef.current = true;
}
}, [disabled, shouldAutoManageFocus]);
// Handle select button click // Handle select button click
const handleSelectClick = useCallback(() => { const handleSelectClick = useCallback(() => {
if (!disabled) { if (!disabled) {
@@ -99,145 +111,47 @@ const SelectInputContainer = forwardRef<HTMLButtonElement, SelectInputProps>(
[disabled, isOpen], [disabled, isOpen],
); );
const getSizeStyles = (): string => { // Handle focus to detect mouse vs keyboard
const baseStyles = "w-full"; const handleFocus = useCallback(() => {
if (disabled) return;
switch (size) { const method = wasMouseDownRef.current ? "mouse" : "keyboard";
case "small": {
const smallHeight = if (shouldAutoManageFocus) {
labelVariant === "horizontal" ? "h-[30px]" : "h-[32px]"; setIsFocused(true);
return `${baseStyles} ${smallHeight} pl-[12px] pr-[36px] py-[8px] text-[10px] leading-[14px]`; setFocusMethod(method);
wasMouseDownRef.current = false;
} }
case "medium": }, [disabled, shouldAutoManageFocus]);
return `${baseStyles} h-[36px] pl-[12px] pr-[36px] py-[8px] text-[14px] leading-[20px]`;
case "large": // Handle blur
return `${baseStyles} h-[40px] pl-[12px] pr-[40px] py-[8px] text-[16px] leading-[24px]`; const handleBlur = useCallback(() => {
default: if (shouldAutoManageFocus) {
return `${baseStyles} h-[36px] pl-[12px] pr-[36px] py-[8px] text-[14px] leading-[20px]`; setIsFocused(false);
setFocusMethod(null);
wasMouseDownRef.current = false;
} }
}; }, [shouldAutoManageFocus]);
const getLabelSizeStyles = (): string => { // Determine actual state:
switch (size) { // - Active: when clicked (mouse focus) or when dropdown is open
case "small": // - Focus: when tabbed (keyboard focus)
return "text-[12px] leading-[14px]"; // - Default: when not focused
case "medium": const actualState = shouldAutoManageFocus
return "text-[14px] leading-[16px]"; ? isOpen || isFocused
case "large": ? focusMethod === "mouse" || isOpen
return "text-[16px] leading-[20px]"; ? "active"
default: : "focus"
return "text-[14px] leading-[16px]"; : "default"
} : externalState;
};
const getStateStyles = (): { // Determine if select is filled (has selected value)
select: string; const isFilled = Boolean(selectedValue && selectedValue.trim().length > 0);
label: string;
} => {
if (disabled) {
return {
select:
"bg-[var(--color-content-default-secondary)] border-[var(--color-border-default-tertiary)] cursor-not-allowed opacity-40",
label: "text-[var(--color-content-default-secondary)]",
};
}
if (error) {
return {
select: "border-[var(--color-border-default-utility-negative)]",
label: "text-[var(--color-content-default-secondary)]",
};
}
switch (state) {
case "hover":
return {
select:
"border-[var(--color-border-default-tertiary)] shadow-[0_0_0_2px_var(--color-border-default-tertiary)]",
label: "text-[var(--color-content-default-secondary)]",
};
case "focus":
return {
select:
"border-[var(--color-border-default-utility-info)] shadow-[0_0_5px_3px_#3281F8]",
label: "text-[var(--color-content-default-secondary)]",
};
default:
return {
select: "border-[var(--color-border-default-tertiary)]",
label: "text-[var(--color-content-default-secondary)]",
};
}
};
const getBorderRadius = (): string => {
switch (size) {
case "small":
return "rounded-[var(--measures-radius-small)]";
case "medium":
return "rounded-[var(--measures-radius-medium)]";
case "large":
return "rounded-[var(--measures-radius-large)]";
default:
return "rounded-[var(--measures-radius-medium)]";
}
};
const sizeStyles = getSizeStyles();
const labelSizeStyles = getLabelSizeStyles();
const stateStyles = getStateStyles();
const borderRadius = getBorderRadius();
const selectClasses = `
${sizeStyles}
${stateStyles.select}
${borderRadius}
bg-[var(--color-background-default-primary)]
text-[var(--color-content-default-primary)]
border
font-inter
font-normal
appearance-none
cursor-pointer
transition-all
duration-200
focus:outline-none
focus-visible:border focus-visible:border-[var(--color-border-default-utility-info)] focus-visible:shadow-[0_0_5px_3px_#3281F8]
text-left
justify-start
hover:shadow-[0_0_0_2px_var(--color-border-default-tertiary)]
${className}
`
.trim()
.replace(/\s+/g, " ");
const labelClasses = `
${labelSizeStyles}
${stateStyles.label}
font-inter
font-medium
block
mb-[4px]
`
.trim()
.replace(/\s+/g, " ");
const containerClasses =
labelVariant === "horizontal"
? "flex items-center gap-[12px]"
: "flex flex-col";
const chevronClasses = `${
size === "large" ? "w-5 h-5" : "w-4 h-4"
} text-[var(--color-content-default-primary)] transition-transform duration-200 ${
isOpen ? "rotate-180" : ""
}`;
// Get display text for selected value // Get display text for selected value
const getDisplayText = (): string => { const getDisplayText = (): string => {
if (!selectedValue) return placeholder; if (!selectedValue) return placeholder;
// Handle options prop
if (options && Array.isArray(options)) { if (options && Array.isArray(options)) {
const selectedOption = options.find( const selectedOption = options.find(
(option) => option.value === selectedValue, (option) => option.value === selectedValue,
@@ -245,7 +159,6 @@ const SelectInputContainer = forwardRef<HTMLButtonElement, SelectInputProps>(
return selectedOption ? selectedOption.label : placeholder; return selectedOption ? selectedOption.label : placeholder;
} }
// Handle children (option elements)
const selectedOption = Children.toArray(children).find( const selectedOption = Children.toArray(children).find(
( (
child, child,
@@ -270,11 +183,9 @@ const SelectInputContainer = forwardRef<HTMLButtonElement, SelectInputProps>(
<SelectInputView <SelectInputView
label={label} label={label}
placeholder={placeholder} placeholder={placeholder}
size={size} state={actualState}
state={state}
disabled={disabled} disabled={disabled}
error={error} error={error}
labelVariant={labelVariant}
className={className} className={className}
options={options} options={options}
selectId={selectId} selectId={selectId}
@@ -282,12 +193,12 @@ const SelectInputContainer = forwardRef<HTMLButtonElement, SelectInputProps>(
isOpen={isOpen} isOpen={isOpen}
selectedValue={selectedValue} selectedValue={selectedValue}
displayText={getDisplayText()} displayText={getDisplayText()}
selectClasses={selectClasses} isFilled={isFilled}
labelClasses={labelClasses}
containerClasses={containerClasses}
chevronClasses={chevronClasses}
onButtonClick={handleSelectClick} onButtonClick={handleSelectClick}
onButtonKeyDown={handleKeyDown} onButtonKeyDown={handleKeyDown}
onButtonMouseDown={handleMouseDown}
onButtonFocus={handleFocus}
onButtonBlur={handleBlur}
onOptionClick={handleOptionSelect} onOptionClick={handleOptionSelect}
selectRef={selectRef} selectRef={selectRef}
menuRef={menuRef} menuRef={menuRef}
+105 -23
View File
@@ -1,4 +1,5 @@
import React, { Children, type ReactNode } from "react"; import React, { Children, type ReactNode } from "react";
import { getAssetPath, ASSETS } from "../../../lib/assetUtils";
import SelectDropdown from "../SelectDropdown"; import SelectDropdown from "../SelectDropdown";
import SelectOption from "../SelectOption"; import SelectOption from "../SelectOption";
import type { SelectOptionData } from "./SelectInput.types"; import type { SelectOptionData } from "./SelectInput.types";
@@ -6,11 +7,9 @@ import type { SelectOptionData } from "./SelectInput.types";
export interface SelectInputViewProps { export interface SelectInputViewProps {
label?: string; label?: string;
placeholder: string; placeholder: string;
size: "small" | "medium" | "large"; state: "default" | "active" | "hover" | "focus";
state: "default" | "hover" | "focus";
disabled: boolean; disabled: boolean;
error: boolean; error: boolean;
labelVariant: "default" | "horizontal";
className: string; className: string;
options?: SelectOptionData[]; options?: SelectOptionData[];
children?: ReactNode; children?: ReactNode;
@@ -20,13 +19,13 @@ export interface SelectInputViewProps {
isOpen: boolean; isOpen: boolean;
selectedValue: string; selectedValue: string;
displayText: string; displayText: string;
selectClasses: string; isFilled: boolean;
labelClasses: string;
containerClasses: string;
chevronClasses: string;
// Callbacks // Callbacks
onButtonClick: () => void; onButtonClick: () => void;
onButtonKeyDown: (_e: React.KeyboardEvent<HTMLButtonElement>) => void; onButtonKeyDown: (_e: React.KeyboardEvent<HTMLButtonElement>) => void;
onButtonMouseDown?: () => void;
onButtonFocus?: () => void;
onButtonBlur?: () => void;
onOptionClick: (_value: string, _text: string) => void; onOptionClick: (_value: string, _text: string) => void;
// Refs // Refs
selectRef: React.RefObject<HTMLButtonElement>; selectRef: React.RefObject<HTMLButtonElement>;
@@ -39,10 +38,9 @@ export interface SelectInputViewProps {
export function SelectInputView({ export function SelectInputView({
label, label,
placeholder: _placeholder, placeholder: _placeholder,
size, state,
disabled, disabled,
error: _error, error,
labelVariant: _labelVariant,
options, options,
children, children,
selectId, selectId,
@@ -50,12 +48,12 @@ export function SelectInputView({
isOpen, isOpen,
selectedValue, selectedValue,
displayText, displayText,
selectClasses, isFilled,
labelClasses,
containerClasses,
chevronClasses,
onButtonClick, onButtonClick,
onButtonKeyDown, onButtonKeyDown,
onButtonMouseDown,
onButtonFocus,
onButtonBlur,
onOptionClick, onOptionClick,
selectRef, selectRef,
menuRef, menuRef,
@@ -63,34 +61,111 @@ export function SelectInputView({
ariaInvalid, ariaInvalid,
...props ...props
}: SelectInputViewProps) { }: SelectInputViewProps) {
// Styles based on Figma design
const containerClasses = "flex flex-col gap-[8px]";
const labelClasses = "text-[14px] leading-[20px] font-medium font-inter text-[var(--color-content-default-primary)]";
// Button styles per Figma
const getButtonClasses = (): string => {
const baseClasses = `
w-full
h-[40px]
px-[12px]
py-[8px]
text-[16px]
font-medium
leading-[20px]
rounded-[8px]
border
border-solid
flex
items-center
justify-between
gap-[12px]
transition-all
duration-200
focus:outline-none
focus:ring-0
cursor-pointer
appearance-none
m-0
`.trim().replace(/\s+/g, " ");
if (disabled) {
return `${baseClasses} bg-[var(--color-surface-default-secondary)] text-[var(--color-content-inverse-tertiary,#2d2d2d)] border-[var(--color-border-default-primary)] cursor-not-allowed opacity-40`;
}
if (error) {
return `${baseClasses} bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-primary)] border-2 border-[var(--color-border-default-utility-negative)]`;
}
if (state === "focus") {
// Focus state: secondary background, tertiary border, with focus ring
return `${baseClasses} bg-[var(--color-surface-default-secondary)] text-[var(--color-content-default-primary)] border border-solid border-[var(--color-border-default-tertiary)]`;
}
if (state === "active" || isOpen) {
// Active state per Figma: secondary background, tertiary border
return `${baseClasses} bg-[var(--color-surface-default-secondary)] text-[var(--color-content-default-primary)] border-[var(--color-border-default-tertiary)]`;
}
// Default state per Figma: secondary background, primary border (subtle)
return `${baseClasses} bg-[var(--color-surface-default-secondary)] text-[var(--color-content-default-primary)] border-[var(--color-border-default-primary)]`;
};
const buttonClasses = getButtonClasses();
// Text color based on filled state
const textColorClass = isFilled
? "text-[var(--color-content-default-primary)]"
: "text-[var(--color-content-default-tertiary,#b4b4b4)]";
// Chevron icon
const chevronClasses = `w-5 h-5 text-[var(--color-content-default-primary)] transition-transform duration-200 ${
isOpen ? "rotate-180" : ""
}`;
return ( return (
<div className={containerClasses}> <div className={containerClasses}>
{label && ( {label && (
<div className="flex flex-wrap gap-[var(--measures-spacing-200,4px_8px)] items-baseline pr-[var(--measures-spacing-100,4px)] relative shrink-0 w-full">
<div className="flex gap-[var(--measures-spacing-050,2px)] items-center relative shrink-0">
<label <label
id={labelId} id={labelId}
htmlFor={selectId} className={labelClasses}
className={`${labelClasses} text-[var(--color-content-default-secondary)]`}
> >
{label} {label}
</label> </label>
<div className="relative shrink-0 size-[12px]">
<img
src={getAssetPath(ASSETS.ICON_HELP)}
alt="Help"
className="block max-w-none size-full"
/>
</div>
</div>
</div>
)} )}
<div className="relative"> <div className="relative">
<button <button
ref={selectRef} ref={selectRef}
id={selectId} id={selectId}
disabled={disabled} disabled={disabled}
className={selectClasses} className={buttonClasses}
aria-labelledby={ariaLabelledby} aria-labelledby={ariaLabelledby}
aria-invalid={ariaInvalid} aria-invalid={ariaInvalid}
aria-expanded={isOpen} aria-expanded={isOpen}
aria-haspopup="listbox" aria-haspopup="listbox"
onClick={onButtonClick} onClick={onButtonClick}
onKeyDown={onButtonKeyDown} onKeyDown={onButtonKeyDown}
{...props} onMouseDown={onButtonMouseDown}
onFocus={onButtonFocus}
onBlur={onButtonBlur}
> >
<span className="text-left">{displayText}</span> <span className={`flex-1 text-left pr-[32px] ${textColorClass}`}>
</button> {displayText}
<div className="absolute inset-y-0 right-0 flex items-center pr-[12px] pointer-events-none"> </span>
<div className="flex items-center justify-center shrink-0">
<svg <svg
className={chevronClasses} className={chevronClasses}
fill="none" fill="none"
@@ -105,6 +180,13 @@ export function SelectInputView({
/> />
</svg> </svg>
</div> </div>
</button>
{state === "focus" && (
<div
className="absolute border-2 border-solid border-[var(--color-border-inverse-primary)] inset-0 rounded-[8px] shadow-[0px_0px_0px_2px_var(--color-border-default-primary)] pointer-events-none z-10"
aria-hidden="true"
/>
)}
{isOpen && ( {isOpen && (
<div <div
@@ -117,7 +199,7 @@ export function SelectInputView({
<SelectOption <SelectOption
key={option.value} key={option.value}
selected={option.value === selectedValue} selected={option.value === selectedValue}
size={size} size="medium"
onClick={() => onOptionClick(option.value, option.label)} onClick={() => onOptionClick(option.value, option.label)}
> >
{option.label} {option.label}
@@ -136,7 +218,7 @@ export function SelectInputView({
<SelectOption <SelectOption
key={optionProps.value} key={optionProps.value}
selected={optionProps.value === selectedValue} selected={optionProps.value === selectedValue}
size={size} size="medium"
onClick={() => onClick={() =>
onOptionClick( onOptionClick(
optionProps.value, optionProps.value,
+73 -126
View File
@@ -4,18 +4,13 @@ import SelectInput from "../app/components/SelectInput";
export default { export default {
title: "Forms/SelectInput", title: "Forms/SelectInput",
component: SelectInput, component: SelectInput,
parameters: {
layout: "centered",
},
argTypes: { argTypes: {
size: {
control: { type: "select" },
options: ["small", "medium", "large"],
},
labelVariant: {
control: { type: "select" },
options: ["default", "horizontal"],
},
state: { state: {
control: { type: "select" }, control: { type: "select" },
options: ["default", "hover", "focus", "error", "disabled"], options: ["default", "active", "focus"],
}, },
disabled: { disabled: {
control: { type: "boolean" }, control: { type: "boolean" },
@@ -48,178 +43,130 @@ const Template = (args) => {
); );
}; };
// Default story
export const Default = Template.bind({}); export const Default = Template.bind({});
Default.args = { Default.args = {
label: "Default Select Input", label: "Default Select Input",
placeholder: "Select", placeholder: "Choose an option",
}; state: "default",
export const Small = Template.bind({});
Small.args = {
label: "Small Select Input",
size: "small",
placeholder: "Select",
};
export const Medium = Template.bind({});
Medium.args = {
label: "Medium Select Input",
size: "medium",
placeholder: "Select",
};
export const Large = Template.bind({});
Large.args = {
label: "Large Select Input",
size: "large",
placeholder: "Select",
};
export const DefaultLabel = Template.bind({});
DefaultLabel.args = {
label: "Default (Top Label)",
labelVariant: "default",
placeholder: "Select",
};
export const HorizontalLabel = Template.bind({});
HorizontalLabel.args = {
label: "Horizontal (Left Label)",
labelVariant: "horizontal",
placeholder: "Select",
}; };
// States
export const Active = Template.bind({}); export const Active = Template.bind({});
Active.args = { Active.args = {
label: "Active State", label: "Active State",
state: "default", placeholder: "Choose an option",
placeholder: "Select", state: "active",
};
export const Hover = Template.bind({});
Hover.args = {
label: "Hover State",
state: "hover",
placeholder: "Select",
}; };
export const Focus = Template.bind({}); export const Focus = Template.bind({});
Focus.args = { Focus.args = {
label: "Focus State", label: "Focus State",
placeholder: "Choose an option",
state: "focus", state: "focus",
placeholder: "Select",
}; };
export const Error = Template.bind({}); export const Error = Template.bind({});
Error.args = { Error.args = {
label: "Error State", label: "Error State",
placeholder: "Choose an option",
error: true, error: true,
placeholder: "Select",
}; };
export const Disabled = Template.bind({}); export const Disabled = Template.bind({});
Disabled.args = { Disabled.args = {
label: "Disabled State", label: "Disabled State",
disabled: true,
placeholder: "Select",
};
export const Interactive = Template.bind({});
Interactive.args = {
label: "Interactive Select Input",
placeholder: "Choose an option", placeholder: "Choose an option",
disabled: true,
}; };
// Comparison stories // Interactive example
export const AllSizes = () => { export const Interactive = (args) => {
const [smallValue, setSmallValue] = useState(""); const [value, setValue] = useState("");
const [mediumValue, setMediumValue] = useState("");
const [largeValue, setLargeValue] = useState("");
return ( return (
<div className="space-y-4"> <div className="space-y-4">
<SelectInput <SelectInput
label="Small" {...args}
size="small" value={value}
value={smallValue} onChange={(data) => setValue(data.target.value)}
onChange={(data) => setSmallValue(data.target.value)}
placeholder="Select"
options={[ options={[
{ value: "item1", label: "Context Menu Item 1" }, { value: "option1", label: "Option 1" },
{ value: "item2", label: "Context Menu Item 2" }, { value: "option2", label: "Option 2" },
{ value: "item3", label: "Context Menu Item 3" }, { value: "option3", label: "Option 3" },
]}
/>
<SelectInput
label="Medium"
size="medium"
value={mediumValue}
onChange={(data) => setMediumValue(data.target.value)}
placeholder="Select"
options={[
{ value: "item1", label: "Context Menu Item 1" },
{ value: "item2", label: "Context Menu Item 2" },
{ value: "item3", label: "Context Menu Item 3" },
]}
/>
<SelectInput
label="Large"
size="large"
value={largeValue}
onChange={(data) => setLargeValue(data.target.value)}
placeholder="Select"
options={[
{ value: "item1", label: "Context Menu Item 1" },
{ value: "item2", label: "Context Menu Item 2" },
{ value: "item3", label: "Context Menu Item 3" },
]} ]}
/> />
<p className="text-sm text-gray-600">Current value: "{value}"</p>
</div> </div>
); );
}; };
Interactive.args = {
label: "Interactive Select Input",
placeholder: "Choose an option",
state: "default",
};
export const AllStates = () => { // All states comparison
const [defaultValue, setDefaultValue] = useState(""); export const AllStates = () => (
const [errorValue, setErrorValue] = useState(""); <div className="space-y-6">
const [disabledValue, setDisabledValue] = useState(""); <div>
<h3 className="text-lg font-semibold mb-4">Select Input States</h3>
return (
<div className="space-y-4"> <div className="space-y-4">
<SelectInput <SelectInput
label="Default State" label="Default State"
value={defaultValue} placeholder="Choose an option"
onChange={(data) => setDefaultValue(data.target.value)} value=""
placeholder="Select"
options={[ options={[
{ value: "item1", label: "Context Menu Item 1" }, { value: "option1", label: "Option 1" },
{ value: "item2", label: "Context Menu Item 2" }, { value: "option2", label: "Option 2" },
{ value: "item3", label: "Context Menu Item 3" }, { value: "option3", label: "Option 3" },
]}
/>
<SelectInput
label="Active State"
placeholder="Choose an option"
state="active"
value=""
options={[
{ value: "option1", label: "Option 1" },
{ value: "option2", label: "Option 2" },
{ value: "option3", label: "Option 3" },
]}
/>
<SelectInput
label="Focus State"
placeholder="Choose an option"
state="focus"
value=""
options={[
{ value: "option1", label: "Option 1" },
{ value: "option2", label: "Option 2" },
{ value: "option3", label: "Option 3" },
]} ]}
/> />
<SelectInput <SelectInput
label="Error State" label="Error State"
placeholder="Choose an option"
error={true} error={true}
value={errorValue} value=""
onChange={(data) => setErrorValue(data.target.value)}
placeholder="Select"
options={[ options={[
{ value: "item1", label: "Context Menu Item 1" }, { value: "option1", label: "Option 1" },
{ value: "item2", label: "Context Menu Item 2" }, { value: "option2", label: "Option 2" },
{ value: "item3", label: "Context Menu Item 3" }, { value: "option3", label: "Option 3" },
]} ]}
/> />
<SelectInput <SelectInput
label="Disabled State" label="Disabled State"
placeholder="Choose an option"
disabled={true} disabled={true}
value={disabledValue} value=""
onChange={(data) => setDisabledValue(data.target.value)}
placeholder="Select"
options={[ options={[
{ value: "item1", label: "Context Menu Item 1" }, { value: "option1", label: "Option 1" },
{ value: "item2", label: "Context Menu Item 2" }, { value: "option2", label: "Option 2" },
{ value: "item3", label: "Context Menu Item 3" }, { value: "option3", label: "Option 3" },
]} ]}
/> />
</div> </div>
); </div>
}; </div>
);
+1 -3
View File
@@ -16,9 +16,7 @@ componentTestSuite<SelectInputProps>({
], ],
} as SelectInputProps, } as SelectInputProps,
requiredProps: ["options"], requiredProps: ["options"],
optionalProps: { optionalProps: {},
size: "medium",
},
primaryRole: "button", primaryRole: "button",
testCases: { testCases: {
renders: true, renders: true,