Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/page area #4

Open
wants to merge 8 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6,141 changes: 2,594 additions & 3,547 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"@testing-library/jest-dom": "^6.4.6",
"@testing-library/react": "^16.0.0",
"@types/node": "^20.14.9",
"@types/react": "^18.3.3",
"@types/react": "^18.3.10",
"@types/react-dom": "^18.3.0",
"@typescript-eslint/eslint-plugin": "^7.13.1",
"@typescript-eslint/parser": "^7.13.1",
Expand All @@ -50,7 +50,8 @@
"prettier": "^3.3.2",
"prettier-plugin-tailwindcss": "^0.6.5",
"tailwindcss": "^3.4.4",
"typescript": "^5.2.2",
"ts-node": "^10.9.2",
"typescript": "^5.6.2",
"vite": "^5.3.1",
"vitest": "^1.6.0"
}
Expand Down
12 changes: 2 additions & 10 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

import { Suspense } from 'react';
import { Route, Routes } from 'react-router-dom';
import { Outlet } from 'react-router-dom';
import './App.css';
import ThemeHandler from './components/common/handler/ThemeHandler';
import Navbar from './components/pegase/navbar/Navbar';
import PegaseStar from './components/pegase/star/PegaseStar';
import { UserContext } from './contexts/UserContext';
import { menuBottomData, menuTopData } from './mocks/data/features/menuData.mock';
import { routes } from './routes';
import { PEGASE_NAVBAR_ID } from './shared/constants';
import { THEME_COLOR } from './shared/types';

Expand All @@ -24,13 +22,7 @@ function App() {
<Navbar id={PEGASE_NAVBAR_ID} bottomItems={menuBottomData} topItems={menuTopData} />
<div className="flex h-full w-full flex-col">
<PegaseStar />
<Suspense>
<Routes>
{Object.entries(routes).map(([key, route]) => (
<Route key={key} path={route.path} Component={route.component} />
))}
</Routes>
</Suspense>
<Outlet />
</div>
</UserContext.Provider>
</div>
Expand Down
4 changes: 2 additions & 2 deletions src/components/common/data/stdTable/TableCore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const COMMON_HEADER_CLASSES = 'px-1 py-0.5 text-left font-semibold';
const headerClassBuilder = <TData,>({ table, header, columnSize }: TableHeaderProps<TData>) =>
clsx(
COMMON_HEADER_CLASSES,
columnSize === 'meta' ? header.column.columnDef.meta?.sizeClassNames ?? '' : '',
columnSize === 'meta' ? (header.column.columnDef.meta?.sizeClassNames ?? '') : '',
table.options.columnResizeMode ? 'group relative' : '',
);

Expand Down Expand Up @@ -98,7 +98,7 @@ const tableStyleBuilder = <TData,>(table: Table<TData>, columnSize: ColumnSizeTy
const TableCore = <TData,>({ table, id: propId, striped, trClassName, columnSize = 'meta' }: TableCoreProps<TData>) => {
const id = useStdId('table-', propId);
const trClasses = clsx(trClassName, striped && 'even:bg-primary-200');

console.log('Rowsssssssssssssssssssssssssss:', table.getRowModel().rows);
return (
<table className={tableClassBuilder(table)} id={id} style={tableStyleBuilder(table, columnSize)}>
<thead>
Expand Down
109 changes: 109 additions & 0 deletions src/components/common/forms/stdInputText/StdInputText.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

import { useStdId } from '@/hooks/common/useStdId';
import { StdChangeHandler } from '@/shared/types/index';
import { StdIconId } from '@/shared/utils/common/mappings/iconMaps';
import StdButton from '@common/base/stdButton/StdButton';
import { useRef } from 'react';
import StdRequiredIndicator from '../stdRequiredIndicator/StdRequiredIndicator';

import { textClassBuilder } from './textClassBuilder';

export type TextVariant = 'outlined' | 'text';

export interface StdInputTextProps {
value: string;
label?: string;
onChange?: StdChangeHandler<string>;
onBlur?: (e: React.FocusEvent<{ value: string }>) => void;
id?: string;
placeHolder?: string;
disabled?: boolean;
variant?: TextVariant;
helperText?: string;
error?: boolean;
maxLength?: number;
password?: boolean;
required?: boolean;
autoFocus?: boolean;
}

const StdInputText = ({
label = '',
onChange,
onBlur,
value,
id: propsId,
variant = 'text',
disabled = false,
error = false,
password = false,
required = false,
placeHolder,
helperText,
maxLength,
autoFocus = false,
}: StdInputTextProps) => {
const inputRef = useRef<HTMLInputElement>(null);
const id = useStdId('input', propsId);
const { wrapperInputClasses, labelClasses, inputClasses, helperClasses, buttonClasses } = textClassBuilder(
variant,
disabled,
error,
!value || disabled,
);

const clearValue = () => {
if (!onChange) {
return;
}
void onChange('');
inputRef.current?.focus();
};

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (!onChange) {
return;
}
void onChange(e.target.value);
};

return (
<div className="inline-flex w-full flex-col items-start justify-start">
<div className={labelClasses}>
<label htmlFor={id}>
{label}
{required && <StdRequiredIndicator />}
</label>
{maxLength && <span>{`${value?.length ?? 0}/${maxLength}`}</span>}
</div>
<div className={wrapperInputClasses}>
<input
ref={inputRef}
className={inputClasses}
value={value}
aria-label={label}
placeholder={placeHolder}
type={password ? 'password' : 'text'}
name={value}
onChange={handleChange}
onBlur={onBlur}
disabled={disabled}
id={id}
required={required}
autoFocus={autoFocus}
/>
<div className={buttonClasses}>
<StdButton icon={StdIconId.Close} variant="text" color="secondary" size="extraSmall" onClick={clearValue} />
</div>
</div>
<span className={helperClasses}>{helperText}</span>
</div>
);
};

export default StdInputText;
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

import { render, screen } from '@testing-library/react';
import { userEvent } from '@testing-library/user-event';

import { useState } from 'react';
import StdInputText, { TextVariant } from '../StdInputText';

const TEST_LABEL = 'Label';
const TEST_HELPER = 'Helper';
const TEST_ID = 'Id';
const TEST_PLACEHOLDER = 'Placeholder';
const TEST_DEFAULT = 'default';
const TEST_VARIANT = 'test' as TextVariant;

const defaultInputSetup = () => {
const onChange = vitest.fn();
const component = render(
<StdInputText
label={TEST_LABEL}
helperText={TEST_HELPER}
id={TEST_ID}
placeHolder={TEST_PLACEHOLDER}
value={TEST_DEFAULT}
variant={TEST_VARIANT}
onChange={onChange}
/>,
);
const input: HTMLInputElement = screen.getByLabelText(TEST_LABEL);
const label: HTMLSpanElement = screen.getByText(TEST_LABEL);
const helper: HTMLSpanElement = screen.getByText(TEST_HELPER);
return { ...component, input, label, helper, onChange };
};

describe('StdInputText', () => {
it('renders the default StdInputText component', () => {
const { input, label, helper } = defaultInputSetup();
expect(input).toBeInTheDocument();
expect(label).toBeInTheDocument();
expect(helper).toBeInTheDocument();
expect(document.querySelector(`#${TEST_ID}`)).toBeInTheDocument();
});

it('update Props should update display', () => {
const { rerender, input, label, helper } = defaultInputSetup();
const NEW_LABEL = 'TEST_LABEL';
const NEW_HELPER = 'TEST_HELPER';
const NEW_PLACEHOLDER = 'TEST_PLACEHOLDER';
const NEW_DEFAULT = 'NEW_DEFAULT';
rerender(
<StdInputText
label={NEW_LABEL}
helperText={NEW_HELPER}
placeHolder={NEW_PLACEHOLDER}
variant={TEST_VARIANT}
value={NEW_DEFAULT}
/>,
);
expect(input.value).toBe(NEW_DEFAULT);
expect(label.textContent).toBe(NEW_LABEL);
expect(helper.textContent).toBe(NEW_HELPER);
expect(input.placeholder).toBe(NEW_PLACEHOLDER);
});

it('required should add "*" to the end of label', () => {
const component = render(<StdInputText label={TEST_LABEL} required value="" />);
const input: HTMLInputElement = screen.getByLabelText(TEST_LABEL);
expect(input).toHaveAttribute('required');
const star = screen.getByText('*');
expect(star).toBeInTheDocument();
component.rerender(<StdInputText label={TEST_LABEL} value="" />);
expect(star).not.toBeInTheDocument();
});

const StatefulInputTextPassword = () => {
const [value, setValue] = useState('');
return <StdInputText label={TEST_LABEL} value={value} password onChange={(e) => setValue(e)} />;
};
it('password input should not display keys', async () => {
const user = userEvent.setup();
render(<StatefulInputTextPassword />);
const input: HTMLInputElement = screen.getByLabelText(TEST_LABEL);
expect(input.value).toBe('');

await user.type(input, 'test');
expect(input.value).toBe('test');
expect(input).toHaveAttribute('type', 'password');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

import {
textClassBuilder,
VARIANT_CLASSES,
CLEAR_CLASSES,
HELPER_CLASSES,
COMMON_VARIANT_CLASSES,
TEXT_CLASSES,
VARIANT_DISABLED_CLASSES,
ERROR_CLASSES,
INPUT_CLASSES,
BUTTON_CLASSES,
HIDE_BUTTON_CLASSES,
} from '../textClassBuilder';

describe('textClassBuilder function', () => {
it('should have the common classes', () => {
expect(textClassBuilder('outlined', false, false, false).wrapperInputClasses.includes(COMMON_VARIANT_CLASSES)).toBe(
true,
);
expect(textClassBuilder('outlined', false, false, false).clearClasses.includes(CLEAR_CLASSES)).toBe(true);
expect(textClassBuilder('outlined', false, false, false).helperClasses.includes(HELPER_CLASSES)).toBe(true);
expect(textClassBuilder('outlined', false, false, false).inputClasses.includes(INPUT_CLASSES)).toBe(true);
expect(textClassBuilder('outlined', false, false, false).labelClasses.includes(TEXT_CLASSES)).toBe(true);
expect(textClassBuilder('outlined', false, false, false).buttonClasses.includes(BUTTON_CLASSES)).toBe(true);
expect(textClassBuilder('text', false, false, false).wrapperInputClasses.includes(COMMON_VARIANT_CLASSES)).toBe(
true,
);
expect(textClassBuilder('text', false, false, false).clearClasses.includes(CLEAR_CLASSES)).toBe(true);
expect(textClassBuilder('text', false, false, false).helperClasses.includes(HELPER_CLASSES)).toBe(true);
expect(textClassBuilder('text', false, false, false).inputClasses.includes(INPUT_CLASSES)).toBe(true);
expect(textClassBuilder('text', false, false, false).labelClasses.includes(TEXT_CLASSES)).toBe(true);
expect(textClassBuilder('text', false, false, false).buttonClasses.includes(BUTTON_CLASSES)).toBe(true);
});
it('should have the proper variant and type classes', () => {
expect(
textClassBuilder('outlined', false, false, false).wrapperInputClasses.includes(VARIANT_CLASSES.outlined),
).toBe(true);
expect(textClassBuilder('text', false, false, false).wrapperInputClasses.includes(VARIANT_CLASSES.text)).toBe(true);
});
it('should have the proper disabled classes', () => {
expect(textClassBuilder('outlined', true, false, false).inputClasses.includes(VARIANT_DISABLED_CLASSES)).toBe(true);
expect(textClassBuilder('outlined', true, false, false).clearClasses.includes(VARIANT_DISABLED_CLASSES)).toBe(true);
expect(textClassBuilder('text', true, false, false).inputClasses.includes(VARIANT_DISABLED_CLASSES)).toBe(true);
expect(textClassBuilder('text', true, false, false).clearClasses.includes(VARIANT_DISABLED_CLASSES)).toBe(true);
});
it('should have the proper error classes', () => {
expect(textClassBuilder('outlined', true, true, false).inputClasses.includes(ERROR_CLASSES.input.outlined)).toBe(
true,
);
expect(textClassBuilder('outlined', true, true, false).labelClasses.includes(ERROR_CLASSES.text)).toBe(true);
expect(textClassBuilder('outlined', true, true, false).helperClasses.includes(ERROR_CLASSES.text)).toBe(true);
expect(textClassBuilder('text', true, true, false).inputClasses.includes(ERROR_CLASSES.input.text)).toBe(true);
expect(textClassBuilder('text', true, true, false).labelClasses.includes(ERROR_CLASSES.text)).toBe(true);
expect(textClassBuilder('text', true, true, false).helperClasses.includes(ERROR_CLASSES.text)).toBe(true);
});
it('button should be hidden when you request it', () => {
expect(textClassBuilder('outlined', false, false, true).buttonClasses.includes(HIDE_BUTTON_CLASSES)).toBe(true);
expect(textClassBuilder('text', false, false, true).buttonClasses.includes(HIDE_BUTTON_CLASSES)).toBe(true);
});
});
42 changes: 42 additions & 0 deletions src/components/common/forms/stdInputText/textClassBuilder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

import { clsx } from 'clsx';
import type { TextVariant } from './StdInputText';

export const TEXT_CLASSES = 'px-0.5 py-0.25 text-body-s text-gray-700 inline-flex justify-between w-full';
export const HELPER_CLASSES = 'h-2 px-1 text-body-s text-gray-700';
export const CLEAR_CLASSES = 'outline-none border border-gray-w focus:rounded focus:border-primary-600';
export const INPUT_CLASSES =
'w-full outline-none bg-transparent [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none';

export const COMMON_VARIANT_CLASSES = 'h-4 pl-2 pr-2 w-full outline-none border inline-flex';

export const VARIANT_CLASSES = {
outlined: 'bg-gray-w hover:bg-gray-50 focus-within:border-primary-600 rounded border-gray-400',
text: 'bg-gray-w hover:bg-gray-50 focus-within:border-b-primary-600 border-gray-w border-b-gray-400',
};
export const VARIANT_DISABLED_CLASSES = '[&]:bg-gray-100 hover:bg-gray-100 [&]:text-gray-500 [&]:cursor-not-allowed';

export const BUTTON_CLASSES = 'flex w-2.5 [&>button]:p-0';
export const HIDE_BUTTON_CLASSES = 'invisible';

export const ERROR_CLASSES = {
text: '[&&]:text-error-600',
input: {
outlined: '[&]:border-error-600',
text: '[&]:border-b-error-600',
},
};

export const textClassBuilder = (variant: TextVariant, disabled: boolean, error: boolean, hideButton: boolean) => ({
labelClasses: clsx(TEXT_CLASSES, error && ERROR_CLASSES.text),
wrapperInputClasses: clsx(COMMON_VARIANT_CLASSES, VARIANT_CLASSES[variant]),
inputClasses: clsx(INPUT_CLASSES, disabled && VARIANT_DISABLED_CLASSES, error && ERROR_CLASSES.input[variant]),
helperClasses: clsx(HELPER_CLASSES, error && ERROR_CLASSES.text),
clearClasses: clsx(CLEAR_CLASSES, disabled && VARIANT_DISABLED_CLASSES),
buttonClasses: clsx(BUTTON_CLASSES, hideButton && HIDE_BUTTON_CLASSES),
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

const StdRequiredIndicator = () => <span className="text-error-600">*</span>;
export default StdRequiredIndicator;
Loading