Update text input component
This commit is contained in:
+152
-595
@@ -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 (
|
||||
<div className="min-h-screen bg-[var(--color-surface-default-primary)] p-[var(--spacing-scale-032)]">
|
||||
@@ -37,600 +22,172 @@ export default function ComponentsPreview() {
|
||||
</p>
|
||||
</header>
|
||||
|
||||
{/* Button Section */}
|
||||
{/* Text Input Section */}
|
||||
<section className="space-y-[var(--spacing-scale-024)]">
|
||||
<h2 className="font-bricolage-grotesque text-[32px] leading-[40px] font-bold text-[var(--color-content-default-primary)]">
|
||||
Button Component
|
||||
Text Input Component
|
||||
</h2>
|
||||
|
||||
<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>
|
||||
<h3 className="font-inter text-[20px] leading-[24px] font-semibold text-[var(--color-content-default-primary)] mb-[var(--spacing-scale-012)]">
|
||||
All Variants
|
||||
States
|
||||
</h3>
|
||||
<div className="flex flex-wrap gap-[var(--spacing-scale-012)]">
|
||||
<Button variant="filled" size="medium">
|
||||
Filled
|
||||
</Button>
|
||||
<Button variant="filled-inverse" size="medium">
|
||||
Filled Inverse
|
||||
</Button>
|
||||
<Button variant="outline" size="medium">
|
||||
Outline
|
||||
</Button>
|
||||
<Button variant="outline-inverse" size="medium">
|
||||
Outline Inverse
|
||||
</Button>
|
||||
<Button variant="ghost" size="medium">
|
||||
Ghost
|
||||
</Button>
|
||||
<Button variant="ghost-inverse" size="medium">
|
||||
Ghost Inverse
|
||||
</Button>
|
||||
<Button variant="danger" size="medium">
|
||||
Danger
|
||||
</Button>
|
||||
<Button variant="danger-inverse" size="medium">
|
||||
Danger Inverse
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="font-inter text-[20px] leading-[24px] font-semibold text-[var(--color-content-default-primary)] mb-[var(--spacing-scale-012)]">
|
||||
All Sizes - Danger Variant
|
||||
</h3>
|
||||
<div className="flex flex-wrap gap-[var(--spacing-scale-012)] items-center">
|
||||
<Button variant="danger" size="xsmall">
|
||||
XSmall
|
||||
</Button>
|
||||
<Button variant="danger" size="small">
|
||||
Small
|
||||
</Button>
|
||||
<Button variant="danger" size="medium">
|
||||
Medium
|
||||
</Button>
|
||||
<Button variant="danger" size="large">
|
||||
Large
|
||||
</Button>
|
||||
<Button variant="danger" size="xlarge">
|
||||
XLarge
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="font-inter text-[20px] leading-[24px] font-semibold text-[var(--color-content-default-primary)] mb-[var(--spacing-scale-012)]">
|
||||
All Sizes - Danger Inverse Variant
|
||||
</h3>
|
||||
<div className="flex flex-wrap gap-[var(--spacing-scale-012)] items-center">
|
||||
<Button variant="danger-inverse" size="xsmall">
|
||||
XSmall
|
||||
</Button>
|
||||
<Button variant="danger-inverse" size="small">
|
||||
Small
|
||||
</Button>
|
||||
<Button variant="danger-inverse" size="medium">
|
||||
Medium
|
||||
</Button>
|
||||
<Button variant="danger-inverse" size="large">
|
||||
Large
|
||||
</Button>
|
||||
<Button variant="danger-inverse" size="xlarge">
|
||||
XLarge
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="font-inter text-[20px] leading-[24px] font-semibold text-[var(--color-content-default-primary)] mb-[var(--spacing-scale-012)]">
|
||||
All Sizes - Ghost Variant
|
||||
</h3>
|
||||
<div className="flex flex-wrap gap-[var(--spacing-scale-012)] items-center">
|
||||
<Button variant="ghost" size="xsmall">
|
||||
XSmall
|
||||
</Button>
|
||||
<Button variant="ghost" size="small">
|
||||
Small
|
||||
</Button>
|
||||
<Button variant="ghost" size="medium">
|
||||
Medium
|
||||
</Button>
|
||||
<Button variant="ghost" size="large">
|
||||
Large
|
||||
</Button>
|
||||
<Button variant="ghost" size="xlarge">
|
||||
XLarge
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="font-inter text-[20px] leading-[24px] font-semibold text-[var(--color-content-default-primary)] mb-[var(--spacing-scale-012)]">
|
||||
All Sizes - Ghost Inverse Variant
|
||||
</h3>
|
||||
<div className="flex flex-wrap gap-[var(--spacing-scale-012)] items-center">
|
||||
<Button variant="ghost-inverse" size="xsmall">
|
||||
XSmall
|
||||
</Button>
|
||||
<Button variant="ghost-inverse" size="small">
|
||||
Small
|
||||
</Button>
|
||||
<Button variant="ghost-inverse" size="medium">
|
||||
Medium
|
||||
</Button>
|
||||
<Button variant="ghost-inverse" size="large">
|
||||
Large
|
||||
</Button>
|
||||
<Button variant="ghost-inverse" size="xlarge">
|
||||
XLarge
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="font-inter text-[20px] leading-[24px] font-semibold text-[var(--color-content-default-primary)] mb-[var(--spacing-scale-012)]">
|
||||
States - Danger Variant
|
||||
</h3>
|
||||
<div className="flex flex-wrap gap-[var(--spacing-scale-012)]">
|
||||
<Button variant="danger" size="medium">
|
||||
Normal
|
||||
</Button>
|
||||
<Button variant="danger" size="medium" disabled>
|
||||
Disabled
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="font-inter text-[20px] leading-[24px] font-semibold text-[var(--color-content-default-primary)] mb-[var(--spacing-scale-012)]">
|
||||
States - Danger Inverse Variant
|
||||
</h3>
|
||||
<div className="flex flex-wrap gap-[var(--spacing-scale-012)]">
|
||||
<Button variant="danger-inverse" size="medium">
|
||||
Normal
|
||||
</Button>
|
||||
<Button variant="danger-inverse" size="medium" disabled>
|
||||
Disabled
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="font-inter text-[20px] leading-[24px] font-semibold text-[var(--color-content-default-primary)] mb-[var(--spacing-scale-012)]">
|
||||
States - Ghost Variant
|
||||
</h3>
|
||||
<div className="flex flex-wrap gap-[var(--spacing-scale-012)]">
|
||||
<Button variant="ghost" size="medium">
|
||||
Normal
|
||||
</Button>
|
||||
<Button variant="ghost" size="medium" disabled>
|
||||
Disabled
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="font-inter text-[20px] leading-[24px] font-semibold text-[var(--color-content-default-primary)] mb-[var(--spacing-scale-012)]">
|
||||
States - Ghost Inverse Variant
|
||||
</h3>
|
||||
<div className="flex flex-wrap gap-[var(--spacing-scale-012)]">
|
||||
<Button variant="ghost-inverse" size="medium">
|
||||
Normal
|
||||
</Button>
|
||||
<Button variant="ghost-inverse" size="medium" disabled>
|
||||
Disabled
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Tooltip Section */}
|
||||
<section className="space-y-[var(--spacing-scale-024)]">
|
||||
<h2 className="font-bricolage-grotesque text-[32px] leading-[40px] font-bold text-[var(--color-content-default-primary)]">
|
||||
Tooltip Component
|
||||
</h2>
|
||||
|
||||
<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="flex flex-wrap gap-[var(--spacing-scale-024)] items-center">
|
||||
<Tooltip text="Tooltip positioned at top" position="top">
|
||||
<Button variant="filled" size="medium">
|
||||
Hover me (Top)
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip text="Tooltip positioned at bottom" position="bottom">
|
||||
<Button variant="filled-inverse" size="medium">
|
||||
Hover me (Bottom)
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip text="Disabled tooltip" disabled>
|
||||
<Button variant="ghost" size="medium">
|
||||
Disabled Tooltip
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip text="Tooltip with icon button" position="top">
|
||||
<button className="p-[var(--spacing-scale-012)] rounded-full hover:bg-[var(--color-surface-default-tertiary)] transition-colors">
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M10 9V11M10 15H10.01M19 10C19 14.9706 14.9706 19 10 19C5.02944 19 1 14.9706 1 10C1 5.02944 5.02944 1 10 1C14.9706 1 19 5.02944 19 10Z"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Alert Section */}
|
||||
<section className="space-y-[var(--spacing-scale-024)]">
|
||||
<h2 className="font-bricolage-grotesque text-[32px] leading-[40px] font-bold text-[var(--color-content-default-primary)]">
|
||||
Alert Component
|
||||
</h2>
|
||||
|
||||
<div className="space-y-[var(--spacing-scale-024)]">
|
||||
{/* Toast Alerts */}
|
||||
<div className="bg-[var(--color-surface-default-secondary)] rounded-[var(--radius-300,12px)] p-[var(--spacing-scale-032)] space-y-[var(--spacing-scale-016)]">
|
||||
<h3 className="font-inter text-[20px] leading-[24px] font-semibold text-[var(--color-content-default-primary)]">
|
||||
Toast Alerts
|
||||
</h3>
|
||||
|
||||
{alertVisible.default && (
|
||||
<Alert
|
||||
title="Short alert banner message goes here"
|
||||
description="Nascetur ipsum a nisi tempor cras nam neque volutpat. Aliquam id est faucibus nunc quis. Eleifend suspendisse."
|
||||
status="default"
|
||||
type="toast"
|
||||
onClose={() =>
|
||||
setAlertVisible({ ...alertVisible, default: false })
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
{alertVisible.positive && (
|
||||
<Alert
|
||||
title="Short alert banner message goes here"
|
||||
description="Nascetur ipsum a nisi tempor cras nam neque volutpat. Aliquam id est faucibus nunc quis. Eleifend suspendisse."
|
||||
status="positive"
|
||||
type="toast"
|
||||
onClose={() =>
|
||||
setAlertVisible({ ...alertVisible, positive: false })
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
{alertVisible.warning && (
|
||||
<Alert
|
||||
title="Short alert banner message goes here"
|
||||
description="Nascetur ipsum a nisi tempor cras nam neque volutpat. Aliquam id est faucibus nunc quis. Eleifend suspendisse."
|
||||
status="warning"
|
||||
type="toast"
|
||||
onClose={() =>
|
||||
setAlertVisible({ ...alertVisible, warning: false })
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
{alertVisible.danger && (
|
||||
<Alert
|
||||
title="Short alert banner message goes here"
|
||||
description="Nascetur ipsum a nisi tempor cras nam neque volutpat. Aliquam id est faucibus nunc quis. Eleifend suspendisse."
|
||||
status="danger"
|
||||
type="toast"
|
||||
onClose={() =>
|
||||
setAlertVisible({ ...alertVisible, danger: false })
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Banner Alerts */}
|
||||
<div className="bg-[var(--color-surface-default-secondary)] rounded-[var(--radius-300,12px)] p-[var(--spacing-scale-032)] space-y-[var(--spacing-scale-016)]">
|
||||
<h3 className="font-inter text-[20px] leading-[24px] font-semibold text-[var(--color-content-default-primary)]">
|
||||
Banner Alerts
|
||||
</h3>
|
||||
|
||||
{alertVisible.banner && (
|
||||
<Alert
|
||||
title="Short alert banner message goes here"
|
||||
description="Nascetur ipsum a nisi tempor cras nam neque volutpat. Aliquam id est faucibus nunc quis. Eleifend suspendisse."
|
||||
status="default"
|
||||
type="banner"
|
||||
onClose={() =>
|
||||
setAlertVisible({ ...alertVisible, banner: false })
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Alert
|
||||
title="Positive banner alert"
|
||||
description="This is a positive banner message"
|
||||
status="positive"
|
||||
type="banner"
|
||||
/>
|
||||
|
||||
<Alert
|
||||
title="Warning banner alert"
|
||||
description="This is a warning banner message"
|
||||
status="warning"
|
||||
type="banner"
|
||||
/>
|
||||
|
||||
<Alert
|
||||
title="Danger banner alert"
|
||||
description="This is a danger banner message"
|
||||
status="danger"
|
||||
type="banner"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Stepper Section */}
|
||||
<section className="space-y-[var(--spacing-scale-024)]">
|
||||
<h2 className="font-bricolage-grotesque text-[32px] leading-[40px] font-bold text-[var(--color-content-default-primary)]">
|
||||
Stepper Component
|
||||
</h2>
|
||||
|
||||
<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>
|
||||
<p className="font-inter text-[14px] leading-[20px] text-[var(--color-content-default-secondary)] mb-[var(--spacing-scale-008)]">
|
||||
Step 1 of 5
|
||||
</p>
|
||||
<Stepper active={1} totalSteps={5} />
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-inter text-[14px] leading-[20px] text-[var(--color-content-default-secondary)] mb-[var(--spacing-scale-008)]">
|
||||
Step 2 of 5
|
||||
</p>
|
||||
<Stepper active={2} totalSteps={5} />
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-inter text-[14px] leading-[20px] text-[var(--color-content-default-secondary)] mb-[var(--spacing-scale-008)]">
|
||||
Step 3 of 5
|
||||
</p>
|
||||
<Stepper active={3} totalSteps={5} />
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-inter text-[14px] leading-[20px] text-[var(--color-content-default-secondary)] mb-[var(--spacing-scale-008)]">
|
||||
Step 4 of 5
|
||||
</p>
|
||||
<Stepper active={4} totalSteps={5} />
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-inter text-[14px] leading-[20px] text-[var(--color-content-default-secondary)] mb-[var(--spacing-scale-008)]">
|
||||
Step 5 of 5
|
||||
</p>
|
||||
<Stepper active={5} totalSteps={5} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Progress Section */}
|
||||
<section className="space-y-[var(--spacing-scale-024)]">
|
||||
<h2 className="font-bricolage-grotesque text-[32px] leading-[40px] font-bold text-[var(--color-content-default-primary)]">
|
||||
Progress Component
|
||||
</h2>
|
||||
|
||||
<div className="bg-white 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>
|
||||
<p className="font-inter text-[14px] leading-[20px] text-[var(--color-content-default-secondary)] mb-[var(--spacing-scale-008)]">
|
||||
Progress: 1-0
|
||||
</p>
|
||||
<Progress progress="1-0" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-inter text-[14px] leading-[20px] text-[var(--color-content-default-secondary)] mb-[var(--spacing-scale-008)]">
|
||||
Progress: 1-1
|
||||
</p>
|
||||
<Progress progress="1-1" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-inter text-[14px] leading-[20px] text-[var(--color-content-default-secondary)] mb-[var(--spacing-scale-008)]">
|
||||
Progress: 1-2
|
||||
</p>
|
||||
<Progress progress="1-2" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-inter text-[14px] leading-[20px] text-[var(--color-content-default-secondary)] mb-[var(--spacing-scale-008)]">
|
||||
Progress: 1-3
|
||||
</p>
|
||||
<Progress progress="1-3" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-inter text-[14px] leading-[20px] text-[var(--color-content-default-secondary)] mb-[var(--spacing-scale-008)]">
|
||||
Progress: 1-4
|
||||
</p>
|
||||
<Progress progress="1-4" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-inter text-[14px] leading-[20px] text-[var(--color-content-default-secondary)] mb-[var(--spacing-scale-008)]">
|
||||
Progress: 1-5
|
||||
</p>
|
||||
<Progress progress="1-5" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-inter text-[14px] leading-[20px] text-[var(--color-content-default-secondary)] mb-[var(--spacing-scale-008)]">
|
||||
Progress: 2-0
|
||||
</p>
|
||||
<Progress progress="2-0" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-inter text-[14px] leading-[20px] text-[var(--color-content-default-secondary)] mb-[var(--spacing-scale-008)]">
|
||||
Progress: 2-1
|
||||
</p>
|
||||
<Progress progress="2-1" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-inter text-[14px] leading-[20px] text-[var(--color-content-default-secondary)] mb-[var(--spacing-scale-008)]">
|
||||
Progress: 2-2
|
||||
</p>
|
||||
<Progress progress="2-2" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-inter text-[14px] leading-[20px] text-[var(--color-content-default-secondary)] mb-[var(--spacing-scale-008)]">
|
||||
Progress: 3-0
|
||||
</p>
|
||||
<Progress progress="3-0" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-inter text-[14px] leading-[20px] text-[var(--color-content-default-secondary)] mb-[var(--spacing-scale-008)]">
|
||||
Progress: 3-1
|
||||
</p>
|
||||
<Progress progress="3-1" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-inter text-[14px] leading-[20px] text-[var(--color-content-default-secondary)] mb-[var(--spacing-scale-008)]">
|
||||
Progress: 3-2
|
||||
</p>
|
||||
<Progress progress="3-2" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Create Component Section */}
|
||||
<section className="space-y-[var(--spacing-scale-024)]">
|
||||
<h2 className="font-bricolage-grotesque text-[32px] leading-[40px] font-bold text-[var(--color-content-default-primary)]">
|
||||
Create Component
|
||||
</h2>
|
||||
|
||||
<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)]">
|
||||
<Button
|
||||
variant="filled-inverse"
|
||||
size="medium"
|
||||
onClick={() => setCreateOpen(true)}
|
||||
>
|
||||
Open Create Dialog
|
||||
</Button>
|
||||
|
||||
<div className="space-y-[var(--spacing-scale-008)]">
|
||||
<p className="font-inter text-[14px] leading-[20px] text-[var(--color-content-default-secondary)]">
|
||||
Step {createStep} of 3
|
||||
</p>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="small"
|
||||
onClick={() => setCreateStep((prev) => Math.max(1, prev - 1))}
|
||||
disabled={createStep === 1}
|
||||
>
|
||||
Previous Step
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="small"
|
||||
onClick={() => setCreateStep((prev) => Math.min(3, prev + 1))}
|
||||
disabled={createStep === 3}
|
||||
>
|
||||
Next Step
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Create
|
||||
isOpen={createOpen}
|
||||
onClose={() => 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}
|
||||
>
|
||||
<div className="space-y-[var(--spacing-scale-024)]">
|
||||
{createStep === 1 && (
|
||||
<InputWithCounter
|
||||
label="Label"
|
||||
placeholder="Policy name"
|
||||
value={policyName}
|
||||
onChange={setPolicyName}
|
||||
maxLength={48}
|
||||
showHelpIcon
|
||||
/>
|
||||
)}
|
||||
{createStep === 2 && (
|
||||
<div className="space-y-[var(--spacing-scale-008)]">
|
||||
<Input
|
||||
label="Conflict Resolution Method"
|
||||
placeholder="Enter method"
|
||||
value=""
|
||||
/>
|
||||
<p className="font-inter text-[14px] leading-[20px] text-[var(--color-content-default-primary)]">
|
||||
Select how conflicts should be resolved in your group.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{createStep === 3 && (
|
||||
<div className="space-y-[var(--spacing-scale-016)]">
|
||||
<p className="font-inter text-[16px] leading-[24px] text-[var(--color-content-default-primary)]">
|
||||
Review your policy configuration before finalizing.
|
||||
</p>
|
||||
<div className="bg-[var(--color-surface-default-secondary)] rounded-[var(--radius-200,8px)] p-[var(--spacing-scale-016)]">
|
||||
<p className="font-inter text-[14px] leading-[20px] text-[var(--color-content-default-secondary)]">
|
||||
Policy details will appear here
|
||||
</p>
|
||||
</div>
|
||||
<TextInput
|
||||
label="Default Text Input"
|
||||
placeholder="Enter text"
|
||||
value={defaultInputValue}
|
||||
onChange={(e) => setDefaultInputValue(e.target.value)}
|
||||
/>
|
||||
<TextInput
|
||||
label="Interactive Text Input (click = active, tab = focus)"
|
||||
placeholder="Enter text"
|
||||
value={activeInputValue}
|
||||
onChange={(e) => setActiveInputValue(e.target.value)}
|
||||
/>
|
||||
<TextInput
|
||||
label="Disabled Text Input"
|
||||
placeholder="Enter text"
|
||||
value=""
|
||||
disabled
|
||||
/>
|
||||
<TextInput
|
||||
label="Error Text Input"
|
||||
placeholder="Enter text"
|
||||
value={errorInputValue}
|
||||
onChange={(e) => setErrorInputValue(e.target.value)}
|
||||
error
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</Create>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* IconCard Component Section */}
|
||||
{/* Select Input Section */}
|
||||
<section className="space-y-[var(--spacing-scale-024)]">
|
||||
<h2 className="font-bricolage-grotesque text-[32px] leading-[40px] font-bold text-[var(--color-content-default-primary)]">
|
||||
IconCard Component
|
||||
Select Input Component
|
||||
</h2>
|
||||
|
||||
<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="flex flex-wrap gap-[var(--spacing-scale-024)]">
|
||||
<IconCard
|
||||
icon={
|
||||
<img
|
||||
src={getAssetPath("assets/Vector_WorkerCoop.svg")}
|
||||
alt=""
|
||||
className="w-[36px] h-[36px]"
|
||||
width="36"
|
||||
height="36"
|
||||
<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" },
|
||||
]}
|
||||
/>
|
||||
}
|
||||
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
|
||||
}}
|
||||
/>
|
||||
<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>
|
||||
<h3 className="font-inter text-[20px] leading-[24px] font-semibold text-[var(--color-content-default-primary)] mb-[var(--spacing-scale-012)]">
|
||||
States
|
||||
</h3>
|
||||
<div className="space-y-[var(--spacing-scale-016)]">
|
||||
<SelectInput
|
||||
label="Default Select Input"
|
||||
placeholder="Choose an option"
|
||||
value=""
|
||||
options={[
|
||||
{ value: "option1", label: "Option 1" },
|
||||
{ value: "option2", label: "Option 2" },
|
||||
{ value: "option3", label: "Option 3" },
|
||||
]}
|
||||
/>
|
||||
<SelectInput
|
||||
label="Disabled Select Input"
|
||||
placeholder="Choose an option"
|
||||
value=""
|
||||
disabled
|
||||
options={[
|
||||
{ value: "option1", label: "Option 1" },
|
||||
{ value: "option2", label: "Option 2" },
|
||||
{ value: "option3", label: "Option 3" },
|
||||
]}
|
||||
/>
|
||||
<SelectInput
|
||||
label="Error Select Input"
|
||||
placeholder="Choose an option"
|
||||
value=""
|
||||
error
|
||||
options={[
|
||||
{ value: "option1", label: "Option 1" },
|
||||
{ value: "option2", label: "Option 2" },
|
||||
{ value: "option3", label: "Option 3" },
|
||||
]}
|
||||
/>
|
||||
</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>
|
||||
</section>
|
||||
|
||||
@@ -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<HTMLInputElement, InputProps>(
|
||||
(
|
||||
{
|
||||
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<HTMLInputElement>(disabled, {
|
||||
onChange,
|
||||
onFocus,
|
||||
onBlur,
|
||||
});
|
||||
|
||||
return (
|
||||
<InputView
|
||||
ref={ref}
|
||||
inputId={inputId}
|
||||
labelId={labelId}
|
||||
size={size}
|
||||
labelVariant={labelVariant}
|
||||
state={state}
|
||||
disabled={disabled}
|
||||
error={error}
|
||||
label={label}
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
name={name}
|
||||
type={type}
|
||||
className={className}
|
||||
containerClasses={containerClasses}
|
||||
labelClasses={labelClasses}
|
||||
inputClasses={inputClasses}
|
||||
borderRadius={currentSize.radius}
|
||||
handleChange={handleChange}
|
||||
handleFocus={handleFocus}
|
||||
handleBlur={handleBlur}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
InputContainer.displayName = "Input";
|
||||
|
||||
export default memo(InputContainer);
|
||||
@@ -1,62 +0,0 @@
|
||||
import { forwardRef } from "react";
|
||||
import type { InputViewProps } from "./Input.types";
|
||||
|
||||
export const InputView = forwardRef<HTMLInputElement, InputViewProps>(
|
||||
(
|
||||
{
|
||||
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 (
|
||||
<div className={containerClasses}>
|
||||
{label && (
|
||||
<label
|
||||
id={labelId}
|
||||
htmlFor={inputId}
|
||||
className={`${labelClasses} font-inter font-medium text-[var(--color-content-default-secondary)]`}
|
||||
>
|
||||
{label}
|
||||
</label>
|
||||
)}
|
||||
<div className={disabled ? "opacity-40" : ""}>
|
||||
<input
|
||||
ref={ref}
|
||||
id={inputId}
|
||||
name={name}
|
||||
type={type}
|
||||
value={value}
|
||||
placeholder={placeholder}
|
||||
onChange={handleChange}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
disabled={disabled}
|
||||
className={inputClasses}
|
||||
style={{ borderRadius }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
InputView.displayName = "InputView";
|
||||
@@ -1,2 +0,0 @@
|
||||
export { default } from "./Input.container";
|
||||
export type { InputProps } from "./Input.types";
|
||||
@@ -1,2 +0,0 @@
|
||||
export { default } from "./Select.container";
|
||||
export type { SelectProps, SelectOptionData } from "./Select.types";
|
||||
+7
-7
@@ -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<HTMLButtonElement, SelectProps>(
|
||||
const SelectInputContainer = forwardRef<HTMLButtonElement, SelectInputProps>(
|
||||
(
|
||||
{
|
||||
id,
|
||||
@@ -38,7 +38,7 @@ const SelectContainer = forwardRef<HTMLButtonElement, SelectProps>(
|
||||
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<HTMLButtonElement, SelectProps>(
|
||||
};
|
||||
|
||||
return (
|
||||
<SelectView
|
||||
<SelectInputView
|
||||
label={label}
|
||||
placeholder={placeholder}
|
||||
size={size}
|
||||
@@ -299,6 +299,6 @@ const SelectContainer = forwardRef<HTMLButtonElement, SelectProps>(
|
||||
},
|
||||
);
|
||||
|
||||
SelectContainer.displayName = "Select";
|
||||
SelectInputContainer.displayName = "SelectInput";
|
||||
|
||||
export default memo(SelectContainer);
|
||||
export default memo(SelectInputContainer);
|
||||
+1
-1
@@ -5,7 +5,7 @@ export interface SelectOptionData {
|
||||
label: string;
|
||||
}
|
||||
|
||||
export interface SelectProps {
|
||||
export interface SelectInputProps {
|
||||
id?: string;
|
||||
label?: string;
|
||||
labelVariant?: "default" | "horizontal";
|
||||
+4
-4
@@ -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 (
|
||||
<div className={containerClasses}>
|
||||
{label && (
|
||||
@@ -0,0 +1,2 @@
|
||||
export { default } from "./SelectInput.container";
|
||||
export type { SelectInputProps, SelectOptionData } from "./SelectInput.types";
|
||||
@@ -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<HTMLInputElement, TextInputProps>(
|
||||
(
|
||||
{
|
||||
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<HTMLInputElement>(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<HTMLInputElement>) => {
|
||||
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 (
|
||||
<TextInputView
|
||||
ref={ref}
|
||||
inputId={inputId}
|
||||
labelId={labelId}
|
||||
state={actualState}
|
||||
disabled={disabled}
|
||||
error={error}
|
||||
label={label}
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
name={name}
|
||||
type={type}
|
||||
className={className}
|
||||
containerClasses={containerClasses}
|
||||
labelClasses={labelClasses}
|
||||
inputClasses={`${inputClasses} ${textColorClass}`}
|
||||
borderRadius={sizeStyles.radius}
|
||||
handleChange={handleChange}
|
||||
handleFocus={handleFocus}
|
||||
handleBlur={handleBlur}
|
||||
handleMouseDown={handleMouseDown}
|
||||
showHelpIcon={showHelpIcon}
|
||||
isFilled={isFilled}
|
||||
inputWrapperClasses={stateStyles.inputWrapper}
|
||||
focusRingClasses={stateStyles.focusRing}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
TextInputContainer.displayName = "TextInput";
|
||||
|
||||
export default memo(TextInputContainer);
|
||||
@@ -1,9 +1,7 @@
|
||||
export interface InputProps extends Omit<
|
||||
export interface TextInputProps extends Omit<
|
||||
React.InputHTMLAttributes<HTMLInputElement>,
|
||||
"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<HTMLInputElement>) => void;
|
||||
onBlur?: (_e: React.FocusEvent<HTMLInputElement>) => 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<HTMLInputElement>) => void;
|
||||
handleFocus: (_e: React.FocusEvent<HTMLInputElement>) => void;
|
||||
handleBlur: (_e: React.FocusEvent<HTMLInputElement>) => void;
|
||||
handleMouseDown?: () => void;
|
||||
showHelpIcon?: boolean;
|
||||
isFilled?: boolean;
|
||||
inputWrapperClasses?: string;
|
||||
focusRingClasses?: string;
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import { forwardRef } from "react";
|
||||
import { getAssetPath, ASSETS } from "../../../lib/assetUtils";
|
||||
import type { TextInputViewProps } from "./TextInput.types";
|
||||
|
||||
export const TextInputView = forwardRef<HTMLInputElement, TextInputViewProps>(
|
||||
(
|
||||
{
|
||||
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 (
|
||||
<div className={containerClasses}>
|
||||
{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
|
||||
id={labelId}
|
||||
htmlFor={inputId}
|
||||
className={`${labelClasses} font-inter font-medium text-[var(--color-content-default-primary)]`}
|
||||
>
|
||||
{label}
|
||||
</label>
|
||||
{showHelpIcon && (
|
||||
<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={inputWrapperClasses}>
|
||||
<div className={disabled ? "opacity-40" : ""}>
|
||||
<input
|
||||
ref={ref}
|
||||
id={inputId}
|
||||
name={name}
|
||||
type={type}
|
||||
value={value}
|
||||
placeholder={placeholder}
|
||||
onChange={handleChange}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
onMouseDown={handleMouseDown}
|
||||
disabled={disabled}
|
||||
className={inputClasses}
|
||||
style={{ borderRadius }}
|
||||
/>
|
||||
</div>
|
||||
{focusRingClasses && (
|
||||
<div className={focusRingClasses} aria-hidden="true" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
TextInputView.displayName = "TextInputView";
|
||||
@@ -0,0 +1,2 @@
|
||||
export { default } from "./TextInput.container";
|
||||
export type { TextInputProps } from "./TextInput.types";
|
||||
@@ -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;
|
||||
|
||||
@@ -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: (
|
||||
<div className="space-y-4">
|
||||
<Input label="Label" placeholder="Policy name" value="" />
|
||||
<TextInput label="Label" placeholder="Policy name" value="" />
|
||||
<p className="text-[12px] text-[var(--color-content-default-tertiary)]">
|
||||
0/48
|
||||
</p>
|
||||
@@ -77,7 +77,7 @@ WithStepper.args = {
|
||||
description: "You can also combine or add new approaches to the list",
|
||||
children: (
|
||||
<div className="space-y-4">
|
||||
<Input label="Label" placeholder="Policy name" value="" />
|
||||
<TextInput label="Label" placeholder="Policy name" value="" />
|
||||
<p className="text-[12px] text-[var(--color-content-default-tertiary)]">
|
||||
0/48
|
||||
</p>
|
||||
@@ -155,7 +155,7 @@ NextButtonDisabled.args = {
|
||||
description: "You can also combine or add new approaches to the list",
|
||||
children: (
|
||||
<div className="space-y-4">
|
||||
<Input label="Label" placeholder="Policy name" value="" />
|
||||
<TextInput label="Label" placeholder="Policy name" value="" />
|
||||
<p className="text-[12px] text-[var(--color-content-default-tertiary)]">
|
||||
0/48
|
||||
</p>
|
||||
|
||||
@@ -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 (
|
||||
<Select
|
||||
<SelectInput
|
||||
{...args}
|
||||
value={value}
|
||||
onChange={setValue}
|
||||
onChange={(data) => setValue(data.target.value)}
|
||||
options={[
|
||||
{ value: "option1", label: "Option 1" },
|
||||
{ value: "option2", label: "Option 2" },
|
||||
@@ -50,27 +50,27 @@ const Template = (args) => {
|
||||
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {
|
||||
label: "Default Select",
|
||||
label: "Default Select Input",
|
||||
placeholder: "Select",
|
||||
};
|
||||
|
||||
export const Small = Template.bind({});
|
||||
Small.args = {
|
||||
label: "Small Select",
|
||||
label: "Small Select Input",
|
||||
size: "small",
|
||||
placeholder: "Select",
|
||||
};
|
||||
|
||||
export const Medium = Template.bind({});
|
||||
Medium.args = {
|
||||
label: "Medium Select",
|
||||
label: "Medium Select Input",
|
||||
size: "medium",
|
||||
placeholder: "Select",
|
||||
};
|
||||
|
||||
export const Large = Template.bind({});
|
||||
Large.args = {
|
||||
label: "Large Select",
|
||||
label: "Large Select Input",
|
||||
size: "large",
|
||||
placeholder: "Select",
|
||||
};
|
||||
@@ -126,7 +126,7 @@ Disabled.args = {
|
||||
|
||||
export const Interactive = Template.bind({});
|
||||
Interactive.args = {
|
||||
label: "Interactive Select",
|
||||
label: "Interactive Select Input",
|
||||
placeholder: "Choose an option",
|
||||
};
|
||||
|
||||
@@ -138,39 +138,42 @@ export const AllSizes = () => {
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<Select
|
||||
<SelectInput
|
||||
label="Small"
|
||||
size="small"
|
||||
value={smallValue}
|
||||
onChange={(e) => setSmallValue(e.target.value)}
|
||||
onChange={(data) => setSmallValue(data.target.value)}
|
||||
placeholder="Select"
|
||||
>
|
||||
<option value="item1">Context Menu Item 1</option>
|
||||
<option value="item2">Context Menu Item 2</option>
|
||||
<option value="item3">Context Menu Item 3</option>
|
||||
</Select>
|
||||
<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="Medium"
|
||||
size="medium"
|
||||
value={mediumValue}
|
||||
onChange={(e) => setMediumValue(e.target.value)}
|
||||
onChange={(data) => setMediumValue(data.target.value)}
|
||||
placeholder="Select"
|
||||
>
|
||||
<option value="item1">Context Menu Item 1</option>
|
||||
<option value="item2">Context Menu Item 2</option>
|
||||
<option value="item3">Context Menu Item 3</option>
|
||||
</Select>
|
||||
<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={(e) => setLargeValue(e.target.value)}
|
||||
onChange={(data) => setLargeValue(data.target.value)}
|
||||
placeholder="Select"
|
||||
>
|
||||
<option value="item1">Context Menu Item 1</option>
|
||||
<option value="item2">Context Menu Item 2</option>
|
||||
<option value="item3">Context Menu Item 3</option>
|
||||
</Select>
|
||||
options={[
|
||||
{ value: "item1", label: "Context Menu Item 1" },
|
||||
{ value: "item2", label: "Context Menu Item 2" },
|
||||
{ value: "item3", label: "Context Menu Item 3" },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -182,38 +185,41 @@ export const AllStates = () => {
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<Select
|
||||
<SelectInput
|
||||
label="Default State"
|
||||
value={defaultValue}
|
||||
onChange={(e) => setDefaultValue(e.target.value)}
|
||||
onChange={(data) => setDefaultValue(data.target.value)}
|
||||
placeholder="Select"
|
||||
>
|
||||
<option value="item1">Context Menu Item 1</option>
|
||||
<option value="item2">Context Menu Item 2</option>
|
||||
<option value="item3">Context Menu Item 3</option>
|
||||
</Select>
|
||||
<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="Error State"
|
||||
error={true}
|
||||
value={errorValue}
|
||||
onChange={(e) => setErrorValue(e.target.value)}
|
||||
onChange={(data) => setErrorValue(data.target.value)}
|
||||
placeholder="Select"
|
||||
>
|
||||
<option value="item1">Context Menu Item 1</option>
|
||||
<option value="item2">Context Menu Item 2</option>
|
||||
<option value="item3">Context Menu Item 3</option>
|
||||
</Select>
|
||||
<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="Disabled State"
|
||||
disabled={true}
|
||||
value={disabledValue}
|
||||
onChange={(e) => setDisabledValue(e.target.value)}
|
||||
onChange={(data) => setDisabledValue(data.target.value)}
|
||||
placeholder="Select"
|
||||
>
|
||||
<option value="item1">Context Menu Item 1</option>
|
||||
<option value="item2">Context Menu Item 2</option>
|
||||
<option value="item3">Context Menu Item 3</option>
|
||||
</Select>
|
||||
options={[
|
||||
{ value: "item1", label: "Context Menu Item 1" },
|
||||
{ value: "item2", label: "Context Menu Item 2" },
|
||||
{ value: "item3", label: "Context Menu Item 3" },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -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) => <Input {...args} />;
|
||||
const Template = (args) => <TextInput {...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 (
|
||||
<div className="space-y-4">
|
||||
<Input
|
||||
<TextInput
|
||||
{...args}
|
||||
value={value}
|
||||
onChange={(e) => 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 = () => (
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold mb-4">Small Size</h3>
|
||||
<div className="space-y-4">
|
||||
<Input
|
||||
<TextInput
|
||||
label="Small Default"
|
||||
placeholder="Small with top label"
|
||||
size="small"
|
||||
@@ -186,13 +186,13 @@ export const AllSizes = () => (
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold mb-4">Medium Size</h3>
|
||||
<div className="space-y-4">
|
||||
<Input
|
||||
<TextInput
|
||||
label="Medium Default"
|
||||
placeholder="Medium with top label"
|
||||
size="medium"
|
||||
labelVariant="default"
|
||||
/>
|
||||
<Input
|
||||
<TextInput
|
||||
label="Medium Horizontal"
|
||||
placeholder="Medium with left label"
|
||||
size="medium"
|
||||
@@ -204,13 +204,13 @@ export const AllSizes = () => (
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold mb-4">Large Size</h3>
|
||||
<div className="space-y-4">
|
||||
<Input
|
||||
<TextInput
|
||||
label="Large Default"
|
||||
placeholder="Large with top label"
|
||||
size="large"
|
||||
labelVariant="default"
|
||||
/>
|
||||
<Input
|
||||
<TextInput
|
||||
label="Large Horizontal"
|
||||
placeholder="Large with left label"
|
||||
size="large"
|
||||
@@ -225,39 +225,39 @@ export const AllSizes = () => (
|
||||
export const AllStates = () => (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold mb-4">Input States</h3>
|
||||
<h3 className="text-lg font-semibold mb-4">Text Input States</h3>
|
||||
<div className="space-y-4">
|
||||
<Input
|
||||
<TextInput
|
||||
label="Default State"
|
||||
placeholder="Default input"
|
||||
size="medium"
|
||||
state="default"
|
||||
/>
|
||||
<Input
|
||||
<TextInput
|
||||
label="Active State"
|
||||
placeholder="Active input"
|
||||
size="medium"
|
||||
state="active"
|
||||
/>
|
||||
<Input
|
||||
<TextInput
|
||||
label="Hover State"
|
||||
placeholder="Hover input"
|
||||
size="medium"
|
||||
state="hover"
|
||||
/>
|
||||
<Input
|
||||
<TextInput
|
||||
label="Focus State"
|
||||
placeholder="Focused input"
|
||||
size="medium"
|
||||
state="focus"
|
||||
/>
|
||||
<Input
|
||||
<TextInput
|
||||
label="Error State"
|
||||
placeholder="Error input"
|
||||
size="medium"
|
||||
error={true}
|
||||
/>
|
||||
<Input
|
||||
<TextInput
|
||||
label="Disabled State"
|
||||
placeholder="Disabled input"
|
||||
size="medium"
|
||||
@@ -4,7 +4,7 @@ import { screen, fireEvent, waitFor } from "@testing-library/react";
|
||||
import "@testing-library/jest-dom/vitest";
|
||||
import { renderWithProviders } from "../utils/test-utils";
|
||||
import Create from "../../app/components/Create";
|
||||
import Input from "../../app/components/Input";
|
||||
import TextInput from "../../app/components/TextInput";
|
||||
|
||||
type CreateProps = React.ComponentProps<typeof Create>;
|
||||
|
||||
@@ -167,7 +167,7 @@ describe("Create", () => {
|
||||
it("traps focus within create dialog", async () => {
|
||||
renderWithProviders(
|
||||
<Create {...defaultProps}>
|
||||
<Input label="Test Input" />
|
||||
<TextInput label="Test Input" />
|
||||
</Create>,
|
||||
);
|
||||
|
||||
|
||||
@@ -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<typeof Select>;
|
||||
type SelectInputProps = React.ComponentProps<typeof SelectInput>;
|
||||
|
||||
componentTestSuite<SelectProps>({
|
||||
component: Select,
|
||||
name: "Select",
|
||||
componentTestSuite<SelectInputProps>({
|
||||
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",
|
||||
@@ -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<typeof Input>;
|
||||
type TextInputProps = React.ComponentProps<typeof TextInput>;
|
||||
|
||||
componentTestSuite<InputProps>({
|
||||
component: Input,
|
||||
name: "Input",
|
||||
componentTestSuite<TextInputProps>({
|
||||
component: TextInput,
|
||||
name: "TextInput",
|
||||
props: {
|
||||
label: "Test input",
|
||||
} as InputProps,
|
||||
label: "Test text input",
|
||||
} as TextInputProps,
|
||||
requiredProps: ["label"],
|
||||
optionalProps: {
|
||||
placeholder: "Enter value",
|
||||
Reference in New Issue
Block a user