diff --git a/app/components-preview/page.tsx b/app/components-preview/page.tsx index 4d8e60c..1d3346d 100644 --- a/app/components-preview/page.tsx +++ b/app/components-preview/page.tsx @@ -1,29 +1,14 @@ "use client"; import { useState } from "react"; -import Tooltip from "../components/Tooltip"; -import Alert from "../components/Alert"; -import Button from "../components/Button"; -import Stepper from "../components/Stepper"; -import Progress from "../components/Progress"; -import Create from "../components/Create"; -import Input from "../components/Input"; -import InputWithCounter from "../components/InputWithCounter"; -import IconCard from "../components/IconCard"; -import { getAssetPath } from "../../lib/assetUtils"; +import TextInput from "../components/TextInput"; +import SelectInput from "../components/SelectInput"; export default function ComponentsPreview() { - const [alertVisible, setAlertVisible] = useState({ - default: true, - positive: true, - warning: true, - danger: true, - banner: true, - }); - - const [createOpen, setCreateOpen] = useState(false); - const [createStep, setCreateStep] = useState(1); - const [policyName, setPolicyName] = useState(""); + const [defaultInputValue, setDefaultInputValue] = useState(""); + const [activeInputValue, setActiveInputValue] = useState(""); + const [errorInputValue, setErrorInputValue] = useState(""); + const [selectValue, setSelectValue] = useState(""); return (
@@ -37,600 +22,172 @@ export default function ComponentsPreview() {

- {/* Button Section */} + {/* Text Input Section */}

- Button Component + Text Input Component

- All Variants + States

-
- - - - - - - - -
-
- -
-

- All Sizes - Danger Variant -

-
- - - - - -
-
- -
-

- All Sizes - Danger Inverse Variant -

-
- - - - - -
-
- -
-

- All Sizes - Ghost Variant -

-
- - - - - -
-
- -
-

- All Sizes - Ghost Inverse Variant -

-
- - - - - -
-
- -
-

- States - Danger Variant -

-
- - -
-
- -
-

- States - Danger Inverse Variant -

-
- - -
-
- -
-

- States - Ghost Variant -

-
- - -
-
- -
-

- States - Ghost Inverse Variant -

-
- - -
-
-
-
-
- - {/* Tooltip Section */} -
-

- Tooltip Component -

- -
-
- - - - - - - - - - - - - - - -
-
-
- - {/* Alert Section */} -
-

- Alert Component -

- -
- {/* Toast Alerts */} -
-

- Toast Alerts -

- - {alertVisible.default && ( - - setAlertVisible({ ...alertVisible, default: false }) - } - /> - )} - - {alertVisible.positive && ( - - setAlertVisible({ ...alertVisible, positive: false }) - } - /> - )} - - {alertVisible.warning && ( - - setAlertVisible({ ...alertVisible, warning: false }) - } - /> - )} - - {alertVisible.danger && ( - - setAlertVisible({ ...alertVisible, danger: false }) - } - /> - )} -
- - {/* Banner Alerts */} -
-

- Banner Alerts -

- - {alertVisible.banner && ( - - setAlertVisible({ ...alertVisible, banner: false }) - } - /> - )} - - - - - - -
-
-
- - {/* Stepper Section */} -
-

- Stepper Component -

- -
-
-
-

- Step 1 of 5 -

- -
-
-

- Step 2 of 5 -

- -
-
-

- Step 3 of 5 -

- -
-
-

- Step 4 of 5 -

- -
-
-

- Step 5 of 5 -

- -
-
-
-
- - {/* Progress Section */} -
-

- Progress Component -

- -
-
-
-

- Progress: 1-0 -

- -
-
-

- Progress: 1-1 -

- -
-
-

- Progress: 1-2 -

- -
-
-

- Progress: 1-3 -

- -
-
-

- Progress: 1-4 -

- -
-
-

- Progress: 1-5 -

- -
-
-

- Progress: 2-0 -

- -
-
-

- Progress: 2-1 -

- -
-
-

- Progress: 2-2 -

- -
-
-

- Progress: 3-0 -

- -
-
-

- Progress: 3-1 -

- -
-
-

- Progress: 3-2 -

- -
-
-
-
- - {/* Create Component Section */} -
-

- Create Component -

- -
-
- - -
-

- Step {createStep} of 3 -

- - -
-
-
- - setCreateOpen(false)} - title={ - createStep === 1 - ? "What do you call your group's new policy?" - : createStep === 2 - ? "How should conflicts be resolved?" - : "Review your policy" - } - description="You can also combine or add new approaches to the list" - showBackButton={true} - showNextButton={true} - onBack={() => setCreateStep((prev) => Math.max(1, prev - 1))} - onNext={() => setCreateStep((prev) => Math.min(3, prev + 1))} - backButtonText="Back" - nextButtonText={createStep === 3 ? "Finish" : "Next"} - nextButtonDisabled={createStep === 1 && !policyName.trim()} - currentStep={createStep} - totalSteps={3} - > -
- {createStep === 1 && ( - - )} - {createStep === 2 && ( -
- -

- Select how conflicts should be resolved in your group. -

-
- )} - {createStep === 3 && (
-

- Review your policy configuration before finalizing. -

-
-

- Policy details will appear here -

-
+ setDefaultInputValue(e.target.value)} + /> + setActiveInputValue(e.target.value)} + /> + + setErrorInputValue(e.target.value)} + error + />
- )} +
+
- + - {/* IconCard Component Section */} + {/* Select Input Section */}

- IconCard Component + Select Input Component

-
- +
+

+ All Sizes +

+
+ setSelectValue(data.target.value)} + options={[ + { value: "option1", label: "Option 1" }, + { value: "option2", label: "Option 2" }, + { value: "option3", label: "Option 3" }, + ]} /> - } - title="Worker's cooperatives" - description="Employee-owned businesses often need to clarify how power is shared, decisions are made, and how processes operate within their organizations." - onClick={() => { - // IconCard clicked handler - }} - /> + setSelectValue(data.target.value)} + options={[ + { value: "option1", label: "Option 1" }, + { value: "option2", label: "Option 2" }, + { value: "option3", label: "Option 3" }, + ]} + /> + setSelectValue(data.target.value)} + options={[ + { value: "option1", label: "Option 1" }, + { value: "option2", label: "Option 2" }, + { value: "option3", label: "Option 3" }, + ]} + /> +
+
+ +
+

+ States +

+
+ + + +
+
+ +
+

+ Label Variants +

+
+ + +
+
diff --git a/app/components/Input/Input.container.tsx b/app/components/Input/Input.container.tsx deleted file mode 100644 index 7804504..0000000 --- a/app/components/Input/Input.container.tsx +++ /dev/null @@ -1,176 +0,0 @@ -"use client"; - -import { memo, forwardRef } from "react"; -import { useComponentId, useFormField } from "../../hooks"; -import { InputView } from "./Input.view"; -import type { InputProps } from "./Input.types"; - -const InputContainer = forwardRef( - ( - { - size = "medium", - labelVariant = "default", - state = "default", - disabled = false, - error = false, - label, - placeholder, - value, - onChange, - onFocus, - onBlur, - id, - name, - type = "text", - className = "", - ...props - }, - ref, - ) => { - // Generate unique ID for accessibility if not provided - const { id: inputId, labelId } = useComponentId("input", id); - - // Size variants - const sizeStyles: Record< - string, - { - input: string; - label: string; - container: string; - radius: string; - } - > = { - small: { - input: - labelVariant === "horizontal" - ? "h-[30px] px-[12px] py-[8px] text-[10px]" - : "h-[32px] px-[12px] py-[8px] text-[10px]", - label: "text-[12px] leading-[14px] font-medium", - container: "gap-[4px]", - radius: "var(--measures-radius-small)", - }, - medium: { - input: "h-[36px] px-[12px] py-[8px] text-[14px] leading-[20px]", - label: "text-[14px] leading-[16px] font-medium", - container: "gap-[8px]", - radius: "var(--measures-radius-medium)", - }, - large: { - input: "h-[40px] px-[12px] py-[8px] text-[16px] leading-[24px]", - label: "text-[16px] leading-[20px] font-medium", - container: "gap-[12px]", - radius: "var(--measures-radius-large)", - }, - }; - - // State styles - const getStateStyles = (): { - input: string; - label: string; - } => { - if (disabled) { - return { - input: - "bg-[var(--color-content-default-secondary)] text-[var(--color-content-default-primary)] border border-[var(--color-border-default-tertiary)] cursor-not-allowed", - label: "text-[var(--color-content-default-secondary)]", - }; - } - - if (error) { - return { - input: - "bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-primary)] border border-[var(--color-border-default-utility-negative)]", - label: "text-[var(--color-content-default-secondary)]", - }; - } - - switch (state) { - case "active": - return { - input: - "bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-primary)] border border-[var(--color-border-default-tertiary)]", - label: "text-[var(--color-content-default-secondary)]", - }; - case "hover": - return { - input: - "bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-primary)] border 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 { - input: - "bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-primary)] border border-[var(--color-border-default-utility-info)] shadow-[0_0_5px_3px_#3281F8]", - label: "text-[var(--color-content-default-secondary)]", - }; - default: - return { - input: - "bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-primary)] border border-[var(--color-border-default-tertiary)] hover:shadow-[0_0_0_2px_var(--color-border-default-tertiary)]", - label: "text-[var(--color-content-default-secondary)]", - }; - } - }; - - const stateStyles = getStateStyles(); - const currentSize = sizeStyles[size]; - - // Container classes based on label variant - const containerClasses = - labelVariant === "horizontal" - ? `flex items-center gap-[12px]` - : `flex flex-col ${currentSize.container}`; - - const labelClasses = - labelVariant === "horizontal" - ? `${currentSize.label} font-inter min-w-fit` - : `${currentSize.label} font-inter`; - - const inputClasses = ` - w-full border transition-all duration-200 ease-in-out - focus:outline-none focus:ring-0 - ${currentSize.input} - ${stateStyles.input} - ${className} - `.trim(); - - // Form field handlers with disabled state handling - const { handleChange, handleFocus, handleBlur } = - useFormField(disabled, { - onChange, - onFocus, - onBlur, - }); - - return ( - - ); - }, -); - -InputContainer.displayName = "Input"; - -export default memo(InputContainer); diff --git a/app/components/Input/Input.view.tsx b/app/components/Input/Input.view.tsx deleted file mode 100644 index aae9d0a..0000000 --- a/app/components/Input/Input.view.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { forwardRef } from "react"; -import type { InputViewProps } from "./Input.types"; - -export const InputView = forwardRef( - ( - { - inputId, - labelId, - label, - placeholder, - value, - name, - type, - disabled, - size: _size, - labelVariant: _labelVariant, - state: _state, - error: _error, - className: _className, - containerClasses, - labelClasses, - inputClasses, - borderRadius, - handleChange, - handleFocus, - handleBlur, - }, - ref, - ) => { - return ( -
- {label && ( - - )} -
- -
-
- ); - }, -); - -InputView.displayName = "InputView"; diff --git a/app/components/Input/index.tsx b/app/components/Input/index.tsx deleted file mode 100644 index c788c53..0000000 --- a/app/components/Input/index.tsx +++ /dev/null @@ -1,2 +0,0 @@ -export { default } from "./Input.container"; -export type { InputProps } from "./Input.types"; diff --git a/app/components/Select/index.tsx b/app/components/Select/index.tsx deleted file mode 100644 index 881e661..0000000 --- a/app/components/Select/index.tsx +++ /dev/null @@ -1,2 +0,0 @@ -export { default } from "./Select.container"; -export type { SelectProps, SelectOptionData } from "./Select.types"; diff --git a/app/components/Select/Select.container.tsx b/app/components/SelectInput/SelectInput.container.tsx similarity index 95% rename from app/components/Select/Select.container.tsx rename to app/components/SelectInput/SelectInput.container.tsx index 1fab32c..7a179bc 100644 --- a/app/components/Select/Select.container.tsx +++ b/app/components/SelectInput/SelectInput.container.tsx @@ -14,10 +14,10 @@ import React, { useEffect, } from "react"; import { useClickOutside } from "../../hooks"; -import { SelectView } from "./Select.view"; -import type { SelectProps } from "./Select.types"; +import { SelectInputView } from "./SelectInput.view"; +import type { SelectInputProps } from "./SelectInput.types"; -const SelectContainer = forwardRef( +const SelectInputContainer = forwardRef( ( { id, @@ -38,7 +38,7 @@ const SelectContainer = forwardRef( ref, ) => { const generatedId = useId(); - const selectId = id || `select-${generatedId}`; + const selectId = id || `select-input-${generatedId}`; const labelId = `${selectId}-label`; const [isOpen, setIsOpen] = useState(false); const [selectedValue, setSelectedValue] = useState(value || ""); @@ -267,7 +267,7 @@ const SelectContainer = forwardRef( }; return ( - ( }, ); -SelectContainer.displayName = "Select"; +SelectInputContainer.displayName = "SelectInput"; -export default memo(SelectContainer); +export default memo(SelectInputContainer); diff --git a/app/components/Select/Select.types.ts b/app/components/SelectInput/SelectInput.types.ts similarity index 93% rename from app/components/Select/Select.types.ts rename to app/components/SelectInput/SelectInput.types.ts index 5e082d2..b3dd7b4 100644 --- a/app/components/Select/Select.types.ts +++ b/app/components/SelectInput/SelectInput.types.ts @@ -5,7 +5,7 @@ export interface SelectOptionData { label: string; } -export interface SelectProps { +export interface SelectInputProps { id?: string; label?: string; labelVariant?: "default" | "horizontal"; diff --git a/app/components/Select/Select.view.tsx b/app/components/SelectInput/SelectInput.view.tsx similarity index 96% rename from app/components/Select/Select.view.tsx rename to app/components/SelectInput/SelectInput.view.tsx index 1c8ad9b..ad49ad3 100644 --- a/app/components/Select/Select.view.tsx +++ b/app/components/SelectInput/SelectInput.view.tsx @@ -1,9 +1,9 @@ import React, { Children, type ReactNode } from "react"; import SelectDropdown from "../SelectDropdown"; import SelectOption from "../SelectOption"; -import type { SelectOptionData } from "./Select.types"; +import type { SelectOptionData } from "./SelectInput.types"; -export interface SelectViewProps { +export interface SelectInputViewProps { label?: string; placeholder: string; size: "small" | "medium" | "large"; @@ -36,7 +36,7 @@ export interface SelectViewProps { ariaInvalid?: boolean; } -export function SelectView({ +export function SelectInputView({ label, placeholder: _placeholder, size, @@ -62,7 +62,7 @@ export function SelectView({ ariaLabelledby, ariaInvalid, ...props -}: SelectViewProps) { +}: SelectInputViewProps) { return (
{label && ( diff --git a/app/components/SelectInput/index.tsx b/app/components/SelectInput/index.tsx new file mode 100644 index 0000000..46ef063 --- /dev/null +++ b/app/components/SelectInput/index.tsx @@ -0,0 +1,2 @@ +export { default } from "./SelectInput.container"; +export type { SelectInputProps, SelectOptionData } from "./SelectInput.types"; diff --git a/app/components/TextInput/TextInput.container.tsx b/app/components/TextInput/TextInput.container.tsx new file mode 100644 index 0000000..bb7ff0d --- /dev/null +++ b/app/components/TextInput/TextInput.container.tsx @@ -0,0 +1,227 @@ +"use client"; + +import { memo, forwardRef, useState, useRef } from "react"; +import { useComponentId, useFormField } from "../../hooks"; +import { TextInputView } from "./TextInput.view"; +import type { TextInputProps } from "./TextInput.types"; + +const TextInputContainer = forwardRef( + ( + { + state: externalState = "default", + disabled = false, + error = false, + label, + placeholder, + value, + onChange, + onFocus, + onBlur, + id, + name, + type = "text", + className = "", + showHelpIcon = true, + ...props + }, + ref, + ) => { + // Generate unique ID for accessibility if not provided + const { id: inputId, labelId } = useComponentId("text-input", id); + + // 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) + // If state is "active", "hover", or "focus", respect it and don't override + const shouldAutoManageFocus = externalState === "default" || externalState === undefined; + + // Determine actual state: + // - Active: when clicked (mouse focus) + // - Focus: when tabbed (keyboard focus) + // - Default: when not focused + const actualState = shouldAutoManageFocus + ? isFocused + ? focusMethod === "mouse" + ? "active" + : "focus" + : "default" + : externalState; + + // Determine if input is filled (has value) + const isFilled = Boolean(value && value.trim().length > 0); + + // Fixed size styles (medium only per Figma designs) + const sizeStyles = { + input: "h-[40px] px-[12px] py-[8px] text-[16px]", + label: "text-[14px] leading-[20px] font-medium", + container: "gap-[8px]", + radius: "var(--measures-radius-200,8px)", + }; + + // State styles based on Figma designs + const getStateStyles = (): { + input: string; + label: string; + inputWrapper: string; + focusRing: string; + } => { + if (disabled) { + return { + input: + "bg-[var(--color-surface-default-secondary)] text-[var(--color-content-inverse-tertiary,#2d2d2d)] border border-solid border-[var(--color-border-default-primary)] cursor-not-allowed", + label: "text-[var(--color-content-default-secondary)]", + inputWrapper: "relative", + focusRing: "", + }; + } + + if (error) { + const filledStyles = isFilled + ? "font-medium leading-[20px]" + : "font-normal leading-[24px]"; + return { + input: `bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-primary)] border-2 border-solid border-[var(--color-border-default-utility-negative)] ${filledStyles}`, + label: "text-[var(--color-content-default-secondary)]", + inputWrapper: "relative", + focusRing: "", + }; + } + + switch (actualState) { + case "active": { + const filledStyles = isFilled + ? "font-medium leading-[20px]" + : "font-normal leading-[24px]"; + return { + input: `bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-primary)] border-2 border-solid border-[var(--color-border-default-tertiary)] ${filledStyles}`, + label: "text-[var(--color-content-default-secondary)]", + inputWrapper: "relative", + focusRing: "", + }; + } + case "focus": { + const filledStyles = isFilled + ? "font-medium leading-[20px]" + : "font-normal leading-[24px]"; + return { + input: `bg-[var(--color-surface-default-secondary)] text-[var(--color-content-default-primary)] border border-solid border-[var(--color-border-default-tertiary)] ${filledStyles}`, + label: "text-[var(--color-content-default-secondary)]", + inputWrapper: "relative", + focusRing: + "absolute border-2 border-solid border-[var(--color-border-inverse-primary)] inset-0 rounded-[var(--measures-radius-200,8px)] shadow-[0px_0px_0px_2px_var(--color-border-default-primary)] pointer-events-none", + }; + } + default: { + const filledStyles = isFilled + ? "font-medium leading-[20px]" + : "font-normal leading-[24px]"; + // Default state uses primary border (matches Figma - border color same as background, so border is subtle) + return { + input: `bg-[var(--color-surface-default-secondary)] text-[var(--color-content-default-primary)] border border-solid border-[var(--color-border-default-primary)] ${filledStyles}`, + label: "text-[var(--color-content-default-secondary)]", + inputWrapper: "relative", + focusRing: "", + }; + } + } + }; + + const stateStyles = getStateStyles(); + + // Container classes (default label variant only) + const containerClasses = `flex flex-col ${sizeStyles.container}`; + + const labelClasses = `${sizeStyles.label} font-inter`; + + // Base classes without border (border is added in state styles) + const inputClasses = ` + w-full transition-all duration-200 ease-in-out + focus:outline-none focus:ring-0 + placeholder:text-[var(--color-content-default-tertiary,#b4b4b4)] + ${sizeStyles.input} + ${stateStyles.input} + ${className} + `.trim(); + + // Text color for filled text (placeholder color is handled above) + const textColorClass = isFilled + ? "text-[var(--color-content-default-primary)]" + : "text-[var(--color-content-default-tertiary,#b4b4b4)]"; + + // Form field handlers with disabled state handling + const { handleChange, handleBlur } = useFormField(disabled, { + onChange, + onBlur: (e) => { + if (shouldAutoManageFocus) { + setIsFocused(false); + setFocusMethod(null); + wasMouseDownRef.current = false; + } + onBlur?.(e); + }, + }); + + // Handle mouse down to detect mouse clicks + const handleMouseDown = () => { + if (!disabled && shouldAutoManageFocus) { + wasMouseDownRef.current = true; + } + }; + + // Custom focus handler to detect mouse vs keyboard + const handleFocus = (e: React.FocusEvent) => { + if (disabled) return; + + // Detect if focus came from keyboard (Tab) or mouse (click) + // If mouseDown was detected before focus, it's a mouse click (active) + // Otherwise, it's keyboard navigation (focus) + const method = wasMouseDownRef.current ? "mouse" : "keyboard"; + + if (shouldAutoManageFocus) { + setIsFocused(true); + setFocusMethod(method); + // Reset mouse down flag after focus is processed + wasMouseDownRef.current = false; + } + + onFocus?.(e); + }; + + return ( + + ); + }, +); + +TextInputContainer.displayName = "TextInput"; + +export default memo(TextInputContainer); diff --git a/app/components/Input/Input.types.ts b/app/components/TextInput/TextInput.types.ts similarity index 79% rename from app/components/Input/Input.types.ts rename to app/components/TextInput/TextInput.types.ts index 59d4aeb..ef8e0fd 100644 --- a/app/components/Input/Input.types.ts +++ b/app/components/TextInput/TextInput.types.ts @@ -1,9 +1,7 @@ -export interface InputProps extends Omit< +export interface TextInputProps extends Omit< React.InputHTMLAttributes, "size" | "onChange" | "onFocus" | "onBlur" > { - size?: "small" | "medium" | "large"; - labelVariant?: "default" | "horizontal"; state?: "default" | "active" | "hover" | "focus"; disabled?: boolean; error?: boolean; @@ -14,13 +12,12 @@ export interface InputProps extends Omit< onFocus?: (_e: React.FocusEvent) => void; onBlur?: (_e: React.FocusEvent) => void; className?: string; + showHelpIcon?: boolean; } -export interface InputViewProps { +export interface TextInputViewProps { inputId: string; labelId: string; - size: "small" | "medium" | "large"; - labelVariant: "default" | "horizontal"; state: "default" | "active" | "hover" | "focus"; disabled: boolean; error: boolean; @@ -37,4 +34,9 @@ export interface InputViewProps { handleChange: (_e: React.ChangeEvent) => void; handleFocus: (_e: React.FocusEvent) => void; handleBlur: (_e: React.FocusEvent) => void; + handleMouseDown?: () => void; + showHelpIcon?: boolean; + isFilled?: boolean; + inputWrapperClasses?: string; + focusRingClasses?: string; } diff --git a/app/components/TextInput/TextInput.view.tsx b/app/components/TextInput/TextInput.view.tsx new file mode 100644 index 0000000..c815050 --- /dev/null +++ b/app/components/TextInput/TextInput.view.tsx @@ -0,0 +1,83 @@ +import { forwardRef } from "react"; +import { getAssetPath, ASSETS } from "../../../lib/assetUtils"; +import type { TextInputViewProps } from "./TextInput.types"; + +export const TextInputView = forwardRef( + ( + { + inputId, + labelId, + label, + placeholder, + value, + name, + type, + disabled, + error: _error, + className: _className, + containerClasses, + labelClasses, + inputClasses, + borderRadius, + handleChange, + handleFocus, + handleBlur, + handleMouseDown, + showHelpIcon = true, + inputWrapperClasses = "relative", + focusRingClasses = "", + }, + ref, + ) => { + return ( +
+ {label && ( +
+
+ + {showHelpIcon && ( +
+ Help +
+ )} +
+
+ )} +
+
+ +
+ {focusRingClasses && ( + +
+ ); + }, +); + +TextInputView.displayName = "TextInputView"; diff --git a/app/components/TextInput/index.tsx b/app/components/TextInput/index.tsx new file mode 100644 index 0000000..cf1b18f --- /dev/null +++ b/app/components/TextInput/index.tsx @@ -0,0 +1,2 @@ +export { default } from "./TextInput.container"; +export type { TextInputProps } from "./TextInput.types"; diff --git a/lib/assetUtils.ts b/lib/assetUtils.ts index 5001820..6f62476 100644 --- a/lib/assetUtils.ts +++ b/lib/assetUtils.ts @@ -62,4 +62,7 @@ export const ASSETS = { // Tooltip icons ICON_POINTER: "assets/Icon_Pointer.svg", + + // Help icon + ICON_HELP: "assets/Icon_Help.svg", } as const; diff --git a/stories/Create.stories.js b/stories/Create.stories.js index 61c58f8..276c9f2 100644 --- a/stories/Create.stories.js +++ b/stories/Create.stories.js @@ -1,5 +1,5 @@ import Create from "../app/components/Create"; -import Input from "../app/components/Input"; +import TextInput from "../app/components/TextInput"; export default { title: "Components/Create", @@ -57,7 +57,7 @@ Default.args = { description: "You can also combine or add new approaches to the list", children: (
- +

0/48

@@ -77,7 +77,7 @@ WithStepper.args = { description: "You can also combine or add new approaches to the list", children: (
- +

0/48

@@ -155,7 +155,7 @@ NextButtonDisabled.args = { description: "You can also combine or add new approaches to the list", children: (
- +

0/48

diff --git a/stories/Select.stories.js b/stories/SelectInput.stories.js similarity index 62% rename from stories/Select.stories.js rename to stories/SelectInput.stories.js index 6d5a814..bc4e518 100644 --- a/stories/Select.stories.js +++ b/stories/SelectInput.stories.js @@ -1,9 +1,9 @@ import React, { useState } from "react"; -import Select from "../app/components/Select"; +import SelectInput from "../app/components/SelectInput"; export default { - title: "Forms/Select", - component: Select, + title: "Forms/SelectInput", + component: SelectInput, argTypes: { size: { control: { type: "select" }, @@ -35,10 +35,10 @@ export default { const Template = (args) => { const [value, setValue] = useState(""); return ( - setSmallValue(e.target.value)} + onChange={(data) => setSmallValue(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" }, + ]} + />
); }; @@ -182,38 +185,41 @@ export const AllStates = () => { return (
- - - + options={[ + { value: "item1", label: "Context Menu Item 1" }, + { value: "item2", label: "Context Menu Item 2" }, + { value: "item3", label: "Context Menu Item 3" }, + ]} + />
); }; diff --git a/stories/Input.stories.js b/stories/TextInput.stories.js similarity index 89% rename from stories/Input.stories.js rename to stories/TextInput.stories.js index b5b6f34..2f37e69 100644 --- a/stories/Input.stories.js +++ b/stories/TextInput.stories.js @@ -1,9 +1,9 @@ import React from "react"; -import Input from "../app/components/Input"; +import TextInput from "../app/components/TextInput"; export default { - title: "Forms/Input", - component: Input, + title: "Forms/TextInput", + component: TextInput, parameters: { layout: "centered", }, @@ -38,12 +38,12 @@ export default { }, }; -const Template = (args) => ; +const Template = (args) => ; // Default story export const Default = Template.bind({}); Default.args = { - label: "Default Input", + label: "Default Text Input", placeholder: "Enter text...", size: "medium", labelVariant: "default", @@ -53,7 +53,7 @@ Default.args = { // Size variants export const Small = Template.bind({}); Small.args = { - label: "Small Input", + label: "Small Text Input", placeholder: "Small size", size: "small", labelVariant: "default", @@ -62,7 +62,7 @@ Small.args = { export const Medium = Template.bind({}); Medium.args = { - label: "Medium Input", + label: "Medium Text Input", placeholder: "Medium size", size: "medium", labelVariant: "default", @@ -71,7 +71,7 @@ Medium.args = { export const Large = Template.bind({}); Large.args = { - label: "Large Input", + label: "Large Text Input", placeholder: "Large size", size: "large", labelVariant: "default", @@ -151,7 +151,7 @@ export const Interactive = (args) => { return (
- setValue(e.target.value)} @@ -161,7 +161,7 @@ export const Interactive = (args) => { ); }; Interactive.args = { - label: "Interactive Input", + label: "Interactive Text Input", placeholder: "Type something...", size: "medium", labelVariant: "default", @@ -174,7 +174,7 @@ export const AllSizes = () => (

Small Size

- (

Medium Size

- - (

Large Size

- - ( export const AllStates = () => (
-

Input States

+

Text Input States

- - - - - - ; @@ -167,7 +167,7 @@ describe("Create", () => { it("traps focus within create dialog", async () => { renderWithProviders( - + , ); diff --git a/tests/components/Select.test.tsx b/tests/components/SelectInput.test.tsx similarity index 68% rename from tests/components/Select.test.tsx rename to tests/components/SelectInput.test.tsx index b4df46e..d5bc074 100644 --- a/tests/components/Select.test.tsx +++ b/tests/components/SelectInput.test.tsx @@ -1,20 +1,20 @@ import React from "react"; -import Select from "../../app/components/Select"; +import SelectInput from "../../app/components/SelectInput"; import { componentTestSuite } from "../utils/componentTestSuite"; -type SelectProps = React.ComponentProps; +type SelectInputProps = React.ComponentProps; -componentTestSuite({ - component: Select, - name: "Select", +componentTestSuite({ + component: SelectInput, + name: "SelectInput", props: { - label: "Test Select", + label: "Test Select Input", placeholder: "Select an option", options: [ { value: "option1", label: "Option 1" }, { value: "option2", label: "Option 2" }, ], - } as SelectProps, + } as SelectInputProps, requiredProps: ["options"], optionalProps: { size: "medium", diff --git a/tests/components/Input.test.tsx b/tests/components/TextInput.test.tsx similarity index 64% rename from tests/components/Input.test.tsx rename to tests/components/TextInput.test.tsx index ac7243a..c63f8ad 100644 --- a/tests/components/Input.test.tsx +++ b/tests/components/TextInput.test.tsx @@ -1,15 +1,15 @@ import React from "react"; -import Input from "../../app/components/Input"; +import TextInput from "../../app/components/TextInput"; import { componentTestSuite } from "../utils/componentTestSuite"; -type InputProps = React.ComponentProps; +type TextInputProps = React.ComponentProps; -componentTestSuite({ - component: Input, - name: "Input", +componentTestSuite({ + component: TextInput, + name: "TextInput", props: { - label: "Test input", - } as InputProps, + label: "Test text input", + } as TextInputProps, requiredProps: ["label"], optionalProps: { placeholder: "Enter value",