diff --git a/apps/backoffice/src/app/login/page.tsx b/apps/backoffice/src/app/login/page.tsx deleted file mode 100644 index cbdc657..0000000 --- a/apps/backoffice/src/app/login/page.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { FC, ReactElement } from 'react'; -import { Button, PasswordInput } from '@imphnen-frontend-service/ui/atoms'; -import { InputForm } from '@imphnen-frontend-service/ui/molecules'; - -export const Components: FC = (): ReactElement => { - return ( -
-
- -

- Welcome to IMPHNEN Backoffice -

- -
-
Password
- -
- -
-
- ); -}; - -export default Components; diff --git a/apps/backoffice/src/app/page.tsx b/apps/backoffice/src/app/page.tsx index f135208..1cc55a0 100644 --- a/apps/backoffice/src/app/page.tsx +++ b/apps/backoffice/src/app/page.tsx @@ -1,7 +1,31 @@ import { FC, ReactElement } from 'react'; +import { Button } from '@imphnen-frontend-service/ui/atoms'; +import { InputForm } from '@imphnen-frontend-service/ui/molecules'; export const Components: FC = (): ReactElement => { - return <>Hallo; + return ( +
+
+ +

+ Welcome to IMPHNEN Backoffice +

+ + + +
+
+ ); }; export default Components; diff --git a/libs/ui/src/atoms/index.ts b/libs/ui/src/atoms/index.ts index c107e65..dc8c232 100644 --- a/libs/ui/src/atoms/index.ts +++ b/libs/ui/src/atoms/index.ts @@ -1,4 +1,3 @@ export * from './button'; export * from './input'; -export * from './password-input'; export * from './textarea'; diff --git a/libs/ui/src/atoms/input/input.spec.tsx b/libs/ui/src/atoms/input/input.spec.tsx index 4fdf16b..a0d57cc 100644 --- a/libs/ui/src/atoms/input/input.spec.tsx +++ b/libs/ui/src/atoms/input/input.spec.tsx @@ -1,36 +1,15 @@ -import { describe, it, expect } from "vitest"; -import { render, screen } from "@testing-library/react"; -import { Input } from "./input"; +import { describe, it, expect } from 'vitest'; +import { render, screen } from '@testing-library/react'; +import { Input } from './input'; -describe("Test Input Component", () => { - it("renders the input with placeholder text", () => { +describe('Test Input Component', () => { + it('renders the input with placeholder text', () => { render(); - expect(screen.getByPlaceholderText("Placeholder")).toBeInTheDocument(); + expect(screen.getByPlaceholderText('Placeholder')).toBeInTheDocument(); }); - // it("renders the input with different sizes", () => { - // const sizeClasses = { - // sm: "text-[10px] max-h-[28px]", - // md: "text-[12px] max-h-[30px]", - // lg: "text-[15px] max-h-[34px]", - // }; - // Object.entries(sizeClasses).forEach(([size, className]) => { - // render(); - // expect(screen.getByRole("textbox")).toHaveClass(className); - // }); - // }); - - // it("renders the input with different types", () => { - // const types = ["text", "password", "email", "number"]; - // types.forEach((type) => { - // render(); - // const input = screen.getByRole("textbox"); - // expect(input).toHaveAttribute("type", type); - // }); - // }); - it("disables the input field when 'disabled' prop is set", () => { render(); - expect(screen.getByRole("textbox")).toBeDisabled(); + expect(screen.getByRole('textbox')).toBeDisabled(); }); }); diff --git a/libs/ui/src/atoms/input/input.stories.tsx b/libs/ui/src/atoms/input/input.stories.tsx index 1e2b442..371aaa7 100644 --- a/libs/ui/src/atoms/input/input.stories.tsx +++ b/libs/ui/src/atoms/input/input.stories.tsx @@ -4,16 +4,6 @@ import { Input } from './input'; const meta: Meta = { title: 'Components/Input', component: Input, - argTypes: { - type: { - control: 'select', - options: ['text', 'email'], - }, - size: { - control: 'select', - options: ['sm', 'md', 'lg'], - }, - }, tags: ['autodocs'], } satisfies Meta; @@ -23,6 +13,7 @@ type Story = StoryObj; export const Large: Story = { args: { size: 'lg', + type: 'text', placeholder: 'Placeholder', }, }; @@ -30,6 +21,7 @@ export const Large: Story = { export const Medium: Story = { args: { size: 'md', + type: 'text', placeholder: 'Placeholder', }, }; @@ -37,6 +29,7 @@ export const Medium: Story = { export const Small: Story = { args: { size: 'sm', + type: 'text', placeholder: 'Placeholder', }, }; @@ -55,6 +48,13 @@ export const EmailInput: Story = { }, }; +export const PasswordInput: Story = { + args: { + size: 'md', + type: 'password', + }, +}; + export const Disabled: Story = { args: { size: 'md', diff --git a/libs/ui/src/atoms/input/input.tsx b/libs/ui/src/atoms/input/input.tsx index da288f5..69006b8 100644 --- a/libs/ui/src/atoms/input/input.tsx +++ b/libs/ui/src/atoms/input/input.tsx @@ -3,10 +3,13 @@ import { FC, InputHTMLAttributes, ReactElement, + useState, } from 'react'; +import { EyeInvisibleOutlined, EyeOutlined } from '@ant-design/icons'; // Import Ant Design icons import { cn } from '@imphnen-frontend-service/utils'; +import { Button } from '../button'; -type TInputType = 'text' | 'email'; +type TInputType = 'text' | 'email' | 'password'; type TInputSize = 'sm' | 'md' | 'lg'; type TInputProps = Omit< @@ -15,38 +18,70 @@ type TInputProps = Omit< > & { type?: TInputType; size?: TInputSize; + disabled?: boolean; }; -const sizeClasses: Record = { - sm: 'text-[10px] max-h-[28px]', - md: 'text-[12px] max-h-[30px]', - lg: 'text-[15px] max-h-[34px]', -}; +const sizeClasses: Record = + { + sm: { textSize: 'text-[10px] max-h-[28px]', iconSize: 'text-[10px]' }, + md: { textSize: 'text-[12px] max-h-[30px]', iconSize: 'text-[12px]' }, + lg: { textSize: 'text-[15px] max-h-[34px]', iconSize: 'text-[15px]' }, + }; const disabledClass = 'opacity-50 hover:border-neutral-200 cursor-not-allowed'; export const Input: FC = ({ - size = 'md', type = 'text', + size = 'md', placeholder = 'Placeholder', disabled, className, ...rest }): ReactElement => { + const [showPassword, setShowPassword] = useState(false); // State for password visibility + + const togglePasswordVisibility = () => { + if (!disabled) setShowPassword((prev) => !prev); + }; + const mergedClassName = cn( - 'px-[12px] py-[8px] text-neutral-800 placeholder:text-neutral-300 border border-neutral-200 hover:border-blue-300 focus:outline-1 focus:outline-blue-500 rounded-md font-bai-jamjuree', - sizeClasses[size], + 'px-[12px] py-[8px] text-neutral-800 placeholder:text-neutral-300 border border-neutral-200 hover:border-blue-300 focus:outline-1 focus:outline-blue-500 rounded-md font-bai-jamjuree w-full', + sizeClasses[size].textSize, disabled && disabledClass, className ); return ( - +
+ + {type === 'password' && ( +
+ +
+ )} +
); }; diff --git a/libs/ui/src/atoms/password-input/index.ts b/libs/ui/src/atoms/password-input/index.ts deleted file mode 100644 index aa25c3b..0000000 --- a/libs/ui/src/atoms/password-input/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./password-input"; \ No newline at end of file diff --git a/libs/ui/src/atoms/password-input/password-input.stories.tsx b/libs/ui/src/atoms/password-input/password-input.stories.tsx deleted file mode 100644 index d2db711..0000000 --- a/libs/ui/src/atoms/password-input/password-input.stories.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { Meta, StoryObj } from '@storybook/react'; -import { PasswordInput } from './password-input'; - -const meta: Meta = { - title: 'Components/Password Input', - component: PasswordInput, - argTypes: { - size: { - control: 'select', - options: ['sm', 'md', 'lg'], - }, - }, -}; - -export default meta; -type Story = StoryObj; - -export const Large: Story = { - args: { - size: 'lg', - placeholder: 'Placeholder', - }, -}; - -export const Medium: Story = { - args: { - size: 'md', - placeholder: 'Placeholder', - }, -}; - -export const Small: Story = { - args: { - size: 'sm', - placeholder: 'Placeholder', - }, -}; - -export const Input: Story = { - args: { - size: 'md', - placeholder: 'Placeholder', - }, -}; - -export const Disabled: Story = { - args: { - size: 'md', - disabled: true, - }, -}; - -export const WithError: Story = { - args: { - size: 'md', - error: 'This field is required', - }, -}; diff --git a/libs/ui/src/atoms/password-input/password-input.tsx b/libs/ui/src/atoms/password-input/password-input.tsx deleted file mode 100644 index 85372fa..0000000 --- a/libs/ui/src/atoms/password-input/password-input.tsx +++ /dev/null @@ -1,111 +0,0 @@ -import { - DetailedHTMLProps, - FC, - InputHTMLAttributes, - ReactElement, - useState, -} from 'react'; -import { cn } from '@imphnen-frontend-service/utils'; -import { Button } from '@imphnen-frontend-service/ui/atoms'; -import { EyeInvisibleOutlined, EyeOutlined } from '@ant-design/icons'; - -type TPasswordInputType = 'password'; -type TPasswordInputSize = 'sm' | 'md' | 'lg'; - -type TPasswordInputProps = Omit< - DetailedHTMLProps, HTMLInputElement>, - 'size' | 'type' -> & { - type?: TPasswordInputType; - size?: TPasswordInputSize; - error?: string; -}; - -const sizeClasses: Record = { - sm: 'text-[10px] max-h-[28px]', - md: 'text-[12px] max-h-[30px]', - lg: 'text-[15px] max-h-[34px]', -}; - -const iconSizeClasses: Record = { - sm: 'text-[10px]', - md: 'text-[12px]', - lg: 'text-[16px]', -}; - -const disabledClass = 'opacity-50 hover:border-neutral-200 cursor-not-allowed'; -const errorClass = - 'border-danger-500 hover:border-danger-500 focus:outline-danger-500'; - -export const PasswordInput: FC = ({ - size = 'md', - placeholder = 'Placeholder', - disabled, - error, - className, - ...rest -}): ReactElement => { - const [showPassword, setShowPassword] = useState(false); - - const togglePasswordVisibility = () => { - if (!disabled) setShowPassword((prev) => !prev); - }; - - const mergedClassName = cn( - 'px-[12px] py-[8px] text-neutral-800 placeholder:text-neutral-300 border border-neutral-200 hover:border-blue-300 focus:outline-1 focus:outline-blue-500 rounded-md font-bai-jamjuree', - sizeClasses[size], - disabled && disabledClass, - error && errorClass, - className - ); - - return ( - <> -
- -
- -
-
- {error &&

{error}

} - - ); -}; diff --git a/libs/ui/src/molecules/input-form/input-form.stories.tsx b/libs/ui/src/molecules/input-form/input-form.stories.tsx index 53dd42c..024805a 100644 --- a/libs/ui/src/molecules/input-form/input-form.stories.tsx +++ b/libs/ui/src/molecules/input-form/input-form.stories.tsx @@ -34,36 +34,48 @@ type Story = StoryObj; export const Large: Story = { args: { - label: 'Description', - placeholder: 'Enter description', + label: 'Label', + placeholder: 'Placeholder', type: 'text', size: 'lg', + helperText: 'Helper Text', }, }; export const Medium: Story = { args: { - label: 'Address', - placeholder: 'Enter your address', + label: 'Label', + placeholder: 'Placeholder', type: 'text', size: 'md', + helperText: 'Helper Text', }, }; export const Small: Story = { args: { - label: 'Phone', - placeholder: 'Enter your phone number', + label: 'Label', + placeholder: 'Placeholder', type: 'text', size: 'sm', + helperText: 'Helper Text', }, }; export const Default: Story = { args: { - label: 'Email', - placeholder: 'Enter your email', - type: 'email', + label: 'Default', + placeholder: 'Enter your name', + type: 'text', + size: 'md', + }, +}; + +export const PasswordInput: Story = { + args: { + label: 'Password', + placeholder: 'Enter your password', + type: 'password', size: 'md', }, }; @@ -80,8 +92,8 @@ export const WithHelperText: Story = { export const WithoutHelperText: Story = { args: { - label: 'Password', - placeholder: 'Enter your password', + label: 'Username', + placeholder: 'Enter your username', type: 'text', size: 'md', }, @@ -104,6 +116,16 @@ export const WithHtmlFor: Story = { type: 'text', size: 'md', htmlFor: 'fullname-input', - helperText: 'Enter your legal full name', + helperText: 'Click on the label', + }, +}; + +export const Disabled: Story = { + args: { + label: 'Disabled', + placeholder: 'This field is disabled', + type: 'text', + size: 'md', + disabled: true, }, }; diff --git a/libs/ui/src/molecules/input-form/input-form.tsx b/libs/ui/src/molecules/input-form/input-form.tsx index f7078cd..1af658d 100644 --- a/libs/ui/src/molecules/input-form/input-form.tsx +++ b/libs/ui/src/molecules/input-form/input-form.tsx @@ -7,7 +7,7 @@ import { import { Input } from '../../atoms'; import { cn } from '@imphnen-frontend-service/utils'; -type TInputType = 'text' | 'email'; +type TInputType = 'text' | 'email' | 'password'; type TInputSize = 'sm' | 'md' | 'lg'; type TInputFormProps = Omit< @@ -27,7 +27,7 @@ type TInputFormProps = Omit< const sizeClasses: Record = { lg: { label: 'text-p3 font-medium', - helperText: 'text-label2 font-normal', + helperText: 'text-label3 font-normal', }, md: { label: 'text-label1 font-medium', @@ -35,7 +35,7 @@ const sizeClasses: Record = { }, sm: { label: 'text-label2 font-medium', - helperText: 'text-label3 font-normal', + helperText: 'text-label2 font-normal', }, }; @@ -53,7 +53,13 @@ export const InputForm: FC = ({ }): ReactElement => { return (
-