refactor: Migrate to monorepo

BREAKING CHANGE: Complete structure overhaul
This commit is contained in:
deytenit committed 2026-01-24 23:28:36 +03:00
1 parent 5300b4bae0
commit 46a2f68342
496 files changed
+45388 -7531

No files matched your search

+95
View File
@@ -0,0 +1,95 @@
import { cn } from './cn';
/**
* Test suite for the cn utility function
*/
describe('shared/helpers/cn/cn', () => {
it('combines simple class names', () => {
expect(cn('p-4', 'bg-red-500')).toBe('p-4 bg-red-500');
expect(cn('flex', 'items-center', 'justify-between')).toBe('flex items-center justify-between');
});
it('resolves conflicting Tailwind classes using tailwind-merge', () => {
expect(cn('p-4 bg-red-500', 'bg-blue-500')).toBe('p-4 bg-blue-500');
expect(cn('text-sm font-normal', 'font-bold')).toBe('text-sm font-bold');
expect(cn('px-2 py-1', 'p-4')).toBe('p-4');
});
it('handles conditional classes using clsx', () => {
// Test with actual conditional values (simulating real usage)
const activeCondition = Math.random() > -1; // Always true but not constant
const inactiveCondition = Math.random() < -1; // Always false but not constant
expect(cn('p-4', activeCondition && 'bg-red-500')).toBe('p-4 bg-red-500');
expect(cn('p-4', inactiveCondition && 'bg-red-500')).toBe('p-4');
// Test null and undefined conditions
const nullCondition: string | null = null;
const undefinedCondition: string | undefined = undefined;
expect(cn('p-4', nullCondition)).toBe('p-4');
expect(cn('p-4', undefinedCondition)).toBe('p-4');
});
it('handles object-based conditional classes', () => {
expect(cn('p-4', { 'bg-red-500': true, 'text-white': false })).toBe('p-4 bg-red-500');
expect(cn('p-4', { 'bg-red-500': false, 'text-white': true })).toBe('p-4 text-white');
});
it('handles array inputs', () => {
expect(cn(['flex', 'items-center'], 'justify-between')).toBe('flex items-center justify-between');
expect(cn(['p-4', null, 'bg-red-500'])).toBe('p-4 bg-red-500');
});
it('handles empty inputs', () => {
expect(cn()).toBe('');
expect(cn('', null, undefined, false)).toBe('');
});
it('combines complex scenarios', () => {
// Simulate dynamic conditions and variant selection
const getIsActive = (): boolean => true;
const getPrimaryVariant = (): 'primary' | 'secondary' => 'primary';
const getSecondaryVariant = (): 'primary' | 'secondary' => 'secondary';
const isActive = getIsActive();
const primaryVariant = getPrimaryVariant();
const secondaryVariant = getSecondaryVariant();
// Test primary variant
expect(
cn(
'base-class',
isActive && 'active-class',
{
'primary-class': primaryVariant === 'primary',
'secondary-class': primaryVariant === 'secondary',
},
['flex', 'items-center'],
'bg-red-500 bg-blue-500' // Should resolve to bg-blue-500
)
).toBe('base-class active-class primary-class flex items-center bg-blue-500');
// Test secondary variant
expect(
cn(
'base-class',
isActive && 'active-class',
{
'primary-class': secondaryVariant === 'primary',
'secondary-class': secondaryVariant === 'secondary',
},
['flex', 'items-center']
)
).toBe('base-class active-class secondary-class flex items-center');
});
it('properly handles spacing and responsive classes', () => {
expect(
cn('p-2 p-4', 'sm:p-6', 'md:p-8')
).toBe('p-4 sm:p-6 md:p-8');
expect(
cn('m-2', 'mx-4', 'ml-6')
).toBe('m-2 mx-4 ml-6');
});
});
+22
View File
@@ -0,0 +1,22 @@
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
/**
* Combines and merges CSS class names using clsx and tailwind-merge.
*
* This utility function combines the power of clsx for conditional class handling
* with tailwind-merge for proper Tailwind CSS class deduplication and conflict resolution.
*
* @param inputs - Class values to combine (strings, objects, arrays, etc.)
* @returns Merged class string with conflicts resolved
*
* @example
* ```typescript
* cn('px-4 py-2', 'bg-blue-500') // 'px-4 py-2 bg-blue-500'
* cn('px-4', { 'px-8': true }) // 'px-8' (px-8 overrides px-4)
* cn('text-red-500', undefined, 'text-blue-500') // 'text-blue-500'
* ```
*/
export function cn(...inputs: ClassValue[]): string {
return twMerge(clsx(inputs));
}
+5
View File
@@ -0,0 +1,5 @@
/**
* Shared helper utilities for common functionality.
*/
export { cn } from './cn/cn';
+2
View File
@@ -0,0 +1,2 @@
export * from './types';
export * from './functions';
+14
View File
@@ -0,0 +1,14 @@
/**
* Props interface for components that accept a className prop.
*
* @example
* ```typescript
* interface ButtonProps extends ClassNameProps {
* children: string;
* }
* ```
*/
export interface ClassNameProps {
/** Optional CSS class name */
className?: string;
}
+6
View File
@@ -0,0 +1,6 @@
/**
* Shared type definitions for common patterns.
*/
export type { ClassNameProps } from './classNameProps';
export type { TestIdProps } from './testIdProps';
+14
View File
@@ -0,0 +1,14 @@
/**
* Props interface for components that accept a test ID prop for React Testing Library.
*
* @example
* ```typescript
* interface ButtonProps extends TestIdProps {
* children: string;
* }
* ```
*/
export interface TestIdProps {
/** Test identifier for React Testing Library queries */
'data-testid'?: string;
}