diff --git a/package.json b/package.json index 357cbac..ae0e489 100644 --- a/package.json +++ b/package.json @@ -52,5 +52,9 @@ "@storybook/addon-docs": "^10.2.16", "playwright": "^1.58.2", "@vitest/browser-playwright": "^4.0.18" + }, + "dependencies": { + "clsx": "^2.1.1", + "tailwind-merge": "^3.5.0" } } diff --git a/src/shared/lib/cn.test.ts b/src/shared/lib/cn.test.ts new file mode 100644 index 0000000..30e1b43 --- /dev/null +++ b/src/shared/lib/cn.test.ts @@ -0,0 +1,16 @@ +import { describe, expect, it } from 'vitest'; +import { cn } from './cn'; + +describe('cn', () => { + it('merges class strings', () => { + expect(cn('foo', 'bar')).toBe('foo bar'); + }); + + it('deduplicates conflicting Tailwind classes (last wins)', () => { + expect(cn('p-4', 'p-8')).toBe('p-8'); + }); + + it('ignores falsy values', () => { + expect(cn('foo', false, undefined, null, 'bar')).toBe('foo bar'); + }); +}); diff --git a/src/shared/lib/cn.ts b/src/shared/lib/cn.ts new file mode 100644 index 0000000..c18709b --- /dev/null +++ b/src/shared/lib/cn.ts @@ -0,0 +1,7 @@ +import { clsx, type ClassValue } from 'clsx'; +import { twMerge } from 'tailwind-merge'; + +/** Merge Tailwind classes without conflicts. Later classes win on collision. */ +export function cn(...inputs: ClassValue[]): string { + return twMerge(clsx(inputs)); +} diff --git a/src/shared/lib/index.ts b/src/shared/lib/index.ts index b1c13e7..71e048f 100644 --- a/src/shared/lib/index.ts +++ b/src/shared/lib/index.ts @@ -1 +1,2 @@ export * from './api'; +export { cn } from './cn'; diff --git a/vitest.unit.config.ts b/vitest.unit.config.ts new file mode 100644 index 0000000..f2217ce --- /dev/null +++ b/vitest.unit.config.ts @@ -0,0 +1,34 @@ +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { svelte } from "@sveltejs/vite-plugin-svelte"; +import { defineConfig } from "vitest/config"; + +const dirname = + typeof __dirname !== "undefined" + ? __dirname + : path.dirname(fileURLToPath(import.meta.url)); + +export default defineConfig({ + plugins: [ + svelte({ + hot: !process.env.VITEST, + }), + ], + test: { + globals: true, + environment: "jsdom", + setupFiles: ["./src/tests/setup.ts"], + include: ["src/**/*.{test,spec}.{js,ts}"], + exclude: ["src/tests/e2e/**/*.{test,spec}.{js,ts}"], + }, + resolve: { + alias: { + $lib: path.resolve("./src/lib"), + $shared: path.resolve("./src/shared"), + $pages: path.resolve("./src/pages"), + $features: path.resolve("./src/features"), + $entities: path.resolve("./src/entities"), + $widgets: path.resolve("./src/widgets"), + }, + }, +});