Merge branch 'develop' into feat/backoffice-login
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
export * from './forgot-step';
|
||||
export * from './otp-form';
|
||||
export * from './input-form';
|
||||
export * from './input-field';
|
||||
export * from './pagination';
|
||||
export * from './modal/modal';
|
||||
export * from './stepper';
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export * from './input-field';
|
||||
+3
-3
@@ -1,9 +1,9 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import InputForm from './input-form';
|
||||
import { InputField } from './input-field';
|
||||
|
||||
describe('InputForm Component', () => {
|
||||
it('renders correctly with disabled prop', () => {
|
||||
render(<InputForm label="Test Label" disabled={true} />);
|
||||
render(<InputField label="Test Label" disabled={true} />);
|
||||
|
||||
const input = screen.getByLabelText('Test Label');
|
||||
expect(input).toBeDisabled();
|
||||
@@ -11,7 +11,7 @@ describe('InputForm Component', () => {
|
||||
});
|
||||
|
||||
it('renders correctly without disabled prop', () => {
|
||||
render(<InputForm label="Test Label" disabled={false} />);
|
||||
render(<InputField label="Test Label" disabled={false} />);
|
||||
|
||||
const input = screen.getByLabelText('Test Label');
|
||||
expect(input).not.toBeDisabled();
|
||||
+3
-3
@@ -1,9 +1,9 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { InputForm } from './input-form';
|
||||
import { InputField } from './input-field';
|
||||
|
||||
const meta = {
|
||||
title: 'Molecules/Input Form',
|
||||
component: InputForm,
|
||||
component: InputField,
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
docs: {
|
||||
@@ -27,7 +27,7 @@ Cek dan inspect element pada story With HtmlFor untuk melihat hasilnya.
|
||||
},
|
||||
},
|
||||
tags: ['autodocs'],
|
||||
} satisfies Meta<typeof InputForm>;
|
||||
} satisfies Meta<typeof InputField>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
+4
-8
@@ -7,10 +7,9 @@ import {
|
||||
import { Input } from '../../atoms';
|
||||
import { cn } from '@imphnen-frontend-service/utils';
|
||||
|
||||
type TInputType = 'text' | 'email' | 'password' | 'file';
|
||||
type TInputSize = 'sm' | 'md' | 'lg';
|
||||
|
||||
type TInputFormProps = Omit<
|
||||
export type TInputType = 'text' | 'email' | 'password' | 'file';
|
||||
export type TInputSize = 'sm' | 'md' | 'lg';
|
||||
export type TInputFieldProps = Omit<
|
||||
DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>,
|
||||
'size' | 'type'
|
||||
> & {
|
||||
@@ -19,7 +18,6 @@ type TInputFormProps = Omit<
|
||||
size?: TInputSize;
|
||||
error?: string;
|
||||
disabled?: boolean;
|
||||
|
||||
helperText?: string;
|
||||
htmlFor?: string;
|
||||
};
|
||||
@@ -39,7 +37,7 @@ const sizeClasses: Record<TInputSize, { label: string; helperText: string }> = {
|
||||
},
|
||||
};
|
||||
|
||||
export const InputForm: FC<TInputFormProps> = ({
|
||||
export const InputField: FC<TInputFieldProps> = ({
|
||||
label,
|
||||
placeholder,
|
||||
type = 'text',
|
||||
@@ -88,5 +86,3 @@ export const InputForm: FC<TInputFormProps> = ({
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default InputForm;
|
||||
@@ -1 +0,0 @@
|
||||
export * from './input-form';
|
||||
@@ -0,0 +1,21 @@
|
||||
import {
|
||||
InputField,
|
||||
TInputFieldProps,
|
||||
} from '@imphnen-frontend-service/ui/molecules';
|
||||
import {
|
||||
FieldValues,
|
||||
useController,
|
||||
UseControllerProps,
|
||||
} from 'react-hook-form';
|
||||
|
||||
export type TControlledInputFieldProps<T extends FieldValues> =
|
||||
UseControllerProps<T> & TInputFieldProps;
|
||||
|
||||
export const ControlledInputField = <T extends FieldValues>(
|
||||
props: TControlledInputFieldProps<T>
|
||||
) => {
|
||||
const { field, fieldState } = useController<T>(props);
|
||||
return (
|
||||
<InputField error={fieldState.error?.message} {...{ ...props, ...field }} />
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from './controlled-input-field';
|
||||
@@ -1,6 +1,7 @@
|
||||
export * from './navbar';
|
||||
export * from "./modals-gacha";
|
||||
export * from "./auth-banner";
|
||||
export * from './backoffice-sidebar'
|
||||
export * from './datatable'
|
||||
export * from './filter'
|
||||
export * from './modals-gacha';
|
||||
export * from './auth-banner';
|
||||
export * from './backoffice-sidebar';
|
||||
export * from './datatable';
|
||||
export * from './filter';
|
||||
export * from './controlled-field';
|
||||
|
||||
@@ -163,7 +163,7 @@ describe('Navbar', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('has correct ARIA role for navigation', () => {
|
||||
it('has correct ARIA role for nav', () => {
|
||||
const { container }: RenderResult = render(
|
||||
<BrowserRouter>
|
||||
<Navbar />
|
||||
@@ -171,6 +171,6 @@ describe('Navbar', () => {
|
||||
);
|
||||
|
||||
const header: HTMLElement | null = container.querySelector('header');
|
||||
expect(header).toHaveAttribute('role', 'navigation');
|
||||
expect(header).toHaveAttribute('role', 'nav');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import { MenuOutlined } from '@ant-design/icons';
|
||||
import { Button } from '@imphnen-frontend-service/ui/atoms';
|
||||
import { FC, ReactElement, useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Button } from '../../atoms/button';
|
||||
import { useSession } from '@imphnen-frontend-service/utils';
|
||||
|
||||
export const Navbar: FC = (): ReactElement => {
|
||||
const [isDropdownOpen, setDropdownOpen] = useState(false);
|
||||
const { session, signOut, isAuthenticated } = useSession();
|
||||
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="bg-primary-50 w-full px-[32px] pt-[32px] md:px-[60px] md:pt-[60px] lg:px-[80px] sticky top-0 z-50">
|
||||
<header
|
||||
className="bg-white shadow-lg rounded-lg min-h-[47px] max-h-[47px] md:min-h-[60px] md:max-h-[60px] lg:min-h-[71px] lg:max-h-[71px] flex justify-between w-full max-w-[1280px] xl:mx-auto"
|
||||
role="navigation"
|
||||
role="nav"
|
||||
>
|
||||
<div className="flex w-full items-center justify-between p-4 md:px-[32px] md:py-[10px]">
|
||||
<div className="flex items-center">
|
||||
@@ -40,20 +42,27 @@ export const Navbar: FC = (): ReactElement => {
|
||||
<Link to="#">Merch Gacha</Link>
|
||||
</Button>
|
||||
</li>
|
||||
<li>
|
||||
<Button
|
||||
size="md"
|
||||
className="lg:text-[19px] lg:max-h-[44px] text-neutral-50 hover:text-neutral-200 transition-colors"
|
||||
>
|
||||
<Link to="/login">Login</Link>
|
||||
</Button>
|
||||
</li>
|
||||
{!isAuthenticated ? (
|
||||
<li>
|
||||
<Button>Login</Button>
|
||||
</li>
|
||||
) : (
|
||||
<li className="flex gap-x-4">
|
||||
<span className="text-lg">{session.user?.fullname}</span>
|
||||
<div
|
||||
onClick={signOut}
|
||||
className="text-lg text-red-500 font-bold"
|
||||
>
|
||||
Logout
|
||||
</div>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
<button
|
||||
className={`md:hidden duration-200 ${
|
||||
isDropdownOpen ? 'transform rotate-90' : ''
|
||||
}`}
|
||||
onClick={() => setDropdownOpen(!isDropdownOpen)}
|
||||
onClick={() => setIsDropdownOpen(!isDropdownOpen)}
|
||||
>
|
||||
<MenuOutlined style={{ color: '#1a8ce6' }} />
|
||||
</button>
|
||||
@@ -77,14 +86,18 @@ export const Navbar: FC = (): ReactElement => {
|
||||
Merch Gacha
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
to="#"
|
||||
className="block text-gray-600 transition-colors px-4 py-2 text-center font-semibold"
|
||||
>
|
||||
Login
|
||||
</Link>
|
||||
</li>
|
||||
{!isAuthenticated ? (
|
||||
<li>
|
||||
<Link
|
||||
to="#"
|
||||
className="block text-gray-600 transition-colors px-4 py-2 text-center font-semibold"
|
||||
>
|
||||
Login
|
||||
</Link>
|
||||
</li>
|
||||
) : (
|
||||
<li>{session.user?.fullname}</li>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user