@yusufalperendumlu/component-library 0.0.6 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,70 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import { cva } from 'class-variance-authority';
3
+ import { IoClose } from 'react-icons/io5';
4
+ import clsx from 'clsx';
5
+
6
+ import { toastIcons } from './icons';
7
+ import { ToastProps } from './Toast.types';
8
+
9
+ const toastVariants = cva(
10
+ 'flex items-center justify-between w-full max-w-xs px-4 py-2 rounded-md shadow-md transition-opacity duration-300',
11
+ {
12
+ variants: {
13
+ type: {
14
+ success: 'bg-green-500 text-white',
15
+ error: 'bg-red-500 text-white',
16
+ },
17
+ position: {
18
+ 'top-left': 'fixed top-4 left-4',
19
+ 'top-right': 'fixed top-4 right-4',
20
+ 'bottom-left': 'fixed bottom-4 left-4',
21
+ 'bottom-right': 'fixed bottom-4 right-4',
22
+ },
23
+ },
24
+ defaultVariants: {
25
+ type: 'success',
26
+ position: 'top-right',
27
+ },
28
+ }
29
+ );
30
+
31
+ const Toast: React.FC<ToastProps> = ({
32
+ title,
33
+ description,
34
+ duration,
35
+ type,
36
+ position,
37
+ }) => {
38
+ const [isOpen, setIsOpen] = useState(true);
39
+
40
+ const handleClose = () => {
41
+ setIsOpen(false);
42
+ };
43
+
44
+ useEffect(() => {
45
+ if (!duration) return;
46
+
47
+ const timer = setTimeout(() => {
48
+ handleClose();
49
+ }, duration);
50
+
51
+ return () => clearTimeout(timer);
52
+ }, [duration]);
53
+
54
+ if (!isOpen) return null;
55
+
56
+ return (
57
+ <div className={clsx(toastVariants({ type, position }))}>
58
+ <span>{toastIcons[type]}</span>
59
+ <div className='flex flex-col ml-2 flex-1'>
60
+ <span className='font-semibold'>{title}</span>
61
+ <span className='text-sm'>{description}</span>
62
+ </div>
63
+ <button onClick={handleClose} className='ml-4'>
64
+ <IoClose size={16} />
65
+ </button>
66
+ </div>
67
+ );
68
+ };
69
+
70
+ export default Toast;
@@ -0,0 +1,7 @@
1
+ export type ToastProps = {
2
+ title: string;
3
+ description: string;
4
+ duration?: number;
5
+ type: 'success' | 'error';
6
+ position: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
7
+ };
@@ -0,0 +1,9 @@
1
+ import { TiTick } from 'react-icons/ti';
2
+ import { FaTriangleExclamation } from 'react-icons/fa6';
3
+
4
+ export const toastIcons = {
5
+ success: (
6
+ <TiTick className='text-green-500 h-6 w-6 bg-white p-1 rounded-full text-sm mr-2' />
7
+ ),
8
+ error: <FaTriangleExclamation className='text-white text-lg mr-2' />,
9
+ };
File without changes
@@ -1,124 +1,124 @@
1
- @tailwind base;
2
- @tailwind components;
3
- @tailwind utilities;
4
- /*
5
- @custom-variant dark (&:is(.dark *));
6
-
7
- :root {
8
- --background: oklch(1 0 0);
9
- --foreground: oklch(0.145 0 0);
10
- --card: oklch(1 0 0);
11
- --card-foreground: oklch(0.145 0 0);
12
- --popover: oklch(1 0 0);
13
- --popover-foreground: oklch(0.145 0 0);
14
- --primary: oklch(0.205 0 0);
15
- --primary-foreground: oklch(0.985 0 0);
16
- --secondary: oklch(0.97 0 0);
17
- --secondary-foreground: oklch(0.205 0 0);
18
- --muted: oklch(0.97 0 0);
19
- --muted-foreground: oklch(0.556 0 0);
20
- --accent: oklch(0.97 0 0);
21
- --accent-foreground: oklch(0.205 0 0);
22
- --destructive: oklch(0.577 0.245 27.325);
23
- --destructive-foreground: oklch(0.577 0.245 27.325);
24
- --border: oklch(0.922 0 0);
25
- --input: oklch(0.922 0 0);
26
- --ring: oklch(0.708 0 0);
27
- --chart-1: oklch(0.646 0.222 41.116);
28
- --chart-2: oklch(0.6 0.118 184.704);
29
- --chart-3: oklch(0.398 0.07 227.392);
30
- --chart-4: oklch(0.828 0.189 84.429);
31
- --chart-5: oklch(0.769 0.188 70.08);
32
- --radius: 0.625rem;
33
- --sidebar: oklch(0.985 0 0);
34
- --sidebar-foreground: oklch(0.145 0 0);
35
- --sidebar-primary: oklch(0.205 0 0);
36
- --sidebar-primary-foreground: oklch(0.985 0 0);
37
- --sidebar-accent: oklch(0.97 0 0);
38
- --sidebar-accent-foreground: oklch(0.205 0 0);
39
- --sidebar-border: oklch(0.922 0 0);
40
- --sidebar-ring: oklch(0.708 0 0);
41
- }
42
-
43
- .dark {
44
- --background: oklch(0.145 0 0);
45
- --foreground: oklch(0.985 0 0);
46
- --card: oklch(0.145 0 0);
47
- --card-foreground: oklch(0.985 0 0);
48
- --popover: oklch(0.145 0 0);
49
- --popover-foreground: oklch(0.985 0 0);
50
- --primary: oklch(0.985 0 0);
51
- --primary-foreground: oklch(0.205 0 0);
52
- --secondary: oklch(0.269 0 0);
53
- --secondary-foreground: oklch(0.985 0 0);
54
- --muted: oklch(0.269 0 0);
55
- --muted-foreground: oklch(0.708 0 0);
56
- --accent: oklch(0.269 0 0);
57
- --accent-foreground: oklch(0.985 0 0);
58
- --destructive: oklch(0.396 0.141 25.723);
59
- --destructive-foreground: oklch(0.637 0.237 25.331);
60
- --border: oklch(0.269 0 0);
61
- --input: oklch(0.269 0 0);
62
- --ring: oklch(0.556 0 0);
63
- --chart-1: oklch(0.488 0.243 264.376);
64
- --chart-2: oklch(0.696 0.17 162.48);
65
- --chart-3: oklch(0.769 0.188 70.08);
66
- --chart-4: oklch(0.627 0.265 303.9);
67
- --chart-5: oklch(0.645 0.246 16.439);
68
- --sidebar: oklch(0.205 0 0);
69
- --sidebar-foreground: oklch(0.985 0 0);
70
- --sidebar-primary: oklch(0.488 0.243 264.376);
71
- --sidebar-primary-foreground: oklch(0.985 0 0);
72
- --sidebar-accent: oklch(0.269 0 0);
73
- --sidebar-accent-foreground: oklch(0.985 0 0);
74
- --sidebar-border: oklch(0.269 0 0);
75
- --sidebar-ring: oklch(0.439 0 0);
76
- }
77
-
78
- @theme inline {
79
- --color-background: var(--background);
80
- --color-foreground: var(--foreground);
81
- --color-card: var(--card);
82
- --color-card-foreground: var(--card-foreground);
83
- --color-popover: var(--popover);
84
- --color-popover-foreground: var(--popover-foreground);
85
- --color-primary: var(--primary);
86
- --color-primary-foreground: var(--primary-foreground);
87
- --color-secondary: var(--secondary);
88
- --color-secondary-foreground: var(--secondary-foreground);
89
- --color-muted: var(--muted);
90
- --color-muted-foreground: var(--muted-foreground);
91
- --color-accent: var(--accent);
92
- --color-accent-foreground: var(--accent-foreground);
93
- --color-destructive: var(--destructive);
94
- --color-destructive-foreground: var(--destructive-foreground);
95
- --color-border: var(--border);
96
- --color-input: var(--input);
97
- --color-ring: var(--ring);
98
- --color-chart-1: var(--chart-1);
99
- --color-chart-2: var(--chart-2);
100
- --color-chart-3: var(--chart-3);
101
- --color-chart-4: var(--chart-4);
102
- --color-chart-5: var(--chart-5);
103
- --radius-sm: calc(var(--radius) - 4px);
104
- --radius-md: calc(var(--radius) - 2px);
105
- --radius-lg: var(--radius);
106
- --radius-xl: calc(var(--radius) + 4px);
107
- --color-sidebar: var(--sidebar);
108
- --color-sidebar-foreground: var(--sidebar-foreground);
109
- --color-sidebar-primary: var(--sidebar-primary);
110
- --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
111
- --color-sidebar-accent: var(--sidebar-accent);
112
- --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
113
- --color-sidebar-border: var(--sidebar-border);
114
- --color-sidebar-ring: var(--sidebar-ring);
115
- }
116
-
117
- @layer base {
118
- * {
119
- @apply border-border outline-ring/50;
120
- }
121
- body {
122
- @apply bg-background text-foreground;
123
- }
124
- } */
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+ /*
5
+ @custom-variant dark (&:is(.dark *));
6
+
7
+ :root {
8
+ --background: oklch(1 0 0);
9
+ --foreground: oklch(0.145 0 0);
10
+ --card: oklch(1 0 0);
11
+ --card-foreground: oklch(0.145 0 0);
12
+ --popover: oklch(1 0 0);
13
+ --popover-foreground: oklch(0.145 0 0);
14
+ --primary: oklch(0.205 0 0);
15
+ --primary-foreground: oklch(0.985 0 0);
16
+ --secondary: oklch(0.97 0 0);
17
+ --secondary-foreground: oklch(0.205 0 0);
18
+ --muted: oklch(0.97 0 0);
19
+ --muted-foreground: oklch(0.556 0 0);
20
+ --accent: oklch(0.97 0 0);
21
+ --accent-foreground: oklch(0.205 0 0);
22
+ --destructive: oklch(0.577 0.245 27.325);
23
+ --destructive-foreground: oklch(0.577 0.245 27.325);
24
+ --border: oklch(0.922 0 0);
25
+ --input: oklch(0.922 0 0);
26
+ --ring: oklch(0.708 0 0);
27
+ --chart-1: oklch(0.646 0.222 41.116);
28
+ --chart-2: oklch(0.6 0.118 184.704);
29
+ --chart-3: oklch(0.398 0.07 227.392);
30
+ --chart-4: oklch(0.828 0.189 84.429);
31
+ --chart-5: oklch(0.769 0.188 70.08);
32
+ --radius: 0.625rem;
33
+ --sidebar: oklch(0.985 0 0);
34
+ --sidebar-foreground: oklch(0.145 0 0);
35
+ --sidebar-primary: oklch(0.205 0 0);
36
+ --sidebar-primary-foreground: oklch(0.985 0 0);
37
+ --sidebar-accent: oklch(0.97 0 0);
38
+ --sidebar-accent-foreground: oklch(0.205 0 0);
39
+ --sidebar-border: oklch(0.922 0 0);
40
+ --sidebar-ring: oklch(0.708 0 0);
41
+ }
42
+
43
+ .dark {
44
+ --background: oklch(0.145 0 0);
45
+ --foreground: oklch(0.985 0 0);
46
+ --card: oklch(0.145 0 0);
47
+ --card-foreground: oklch(0.985 0 0);
48
+ --popover: oklch(0.145 0 0);
49
+ --popover-foreground: oklch(0.985 0 0);
50
+ --primary: oklch(0.985 0 0);
51
+ --primary-foreground: oklch(0.205 0 0);
52
+ --secondary: oklch(0.269 0 0);
53
+ --secondary-foreground: oklch(0.985 0 0);
54
+ --muted: oklch(0.269 0 0);
55
+ --muted-foreground: oklch(0.708 0 0);
56
+ --accent: oklch(0.269 0 0);
57
+ --accent-foreground: oklch(0.985 0 0);
58
+ --destructive: oklch(0.396 0.141 25.723);
59
+ --destructive-foreground: oklch(0.637 0.237 25.331);
60
+ --border: oklch(0.269 0 0);
61
+ --input: oklch(0.269 0 0);
62
+ --ring: oklch(0.556 0 0);
63
+ --chart-1: oklch(0.488 0.243 264.376);
64
+ --chart-2: oklch(0.696 0.17 162.48);
65
+ --chart-3: oklch(0.769 0.188 70.08);
66
+ --chart-4: oklch(0.627 0.265 303.9);
67
+ --chart-5: oklch(0.645 0.246 16.439);
68
+ --sidebar: oklch(0.205 0 0);
69
+ --sidebar-foreground: oklch(0.985 0 0);
70
+ --sidebar-primary: oklch(0.488 0.243 264.376);
71
+ --sidebar-primary-foreground: oklch(0.985 0 0);
72
+ --sidebar-accent: oklch(0.269 0 0);
73
+ --sidebar-accent-foreground: oklch(0.985 0 0);
74
+ --sidebar-border: oklch(0.269 0 0);
75
+ --sidebar-ring: oklch(0.439 0 0);
76
+ }
77
+
78
+ @theme inline {
79
+ --color-background: var(--background);
80
+ --color-foreground: var(--foreground);
81
+ --color-card: var(--card);
82
+ --color-card-foreground: var(--card-foreground);
83
+ --color-popover: var(--popover);
84
+ --color-popover-foreground: var(--popover-foreground);
85
+ --color-primary: var(--primary);
86
+ --color-primary-foreground: var(--primary-foreground);
87
+ --color-secondary: var(--secondary);
88
+ --color-secondary-foreground: var(--secondary-foreground);
89
+ --color-muted: var(--muted);
90
+ --color-muted-foreground: var(--muted-foreground);
91
+ --color-accent: var(--accent);
92
+ --color-accent-foreground: var(--accent-foreground);
93
+ --color-destructive: var(--destructive);
94
+ --color-destructive-foreground: var(--destructive-foreground);
95
+ --color-border: var(--border);
96
+ --color-input: var(--input);
97
+ --color-ring: var(--ring);
98
+ --color-chart-1: var(--chart-1);
99
+ --color-chart-2: var(--chart-2);
100
+ --color-chart-3: var(--chart-3);
101
+ --color-chart-4: var(--chart-4);
102
+ --color-chart-5: var(--chart-5);
103
+ --radius-sm: calc(var(--radius) - 4px);
104
+ --radius-md: calc(var(--radius) - 2px);
105
+ --radius-lg: var(--radius);
106
+ --radius-xl: calc(var(--radius) + 4px);
107
+ --color-sidebar: var(--sidebar);
108
+ --color-sidebar-foreground: var(--sidebar-foreground);
109
+ --color-sidebar-primary: var(--sidebar-primary);
110
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
111
+ --color-sidebar-accent: var(--sidebar-accent);
112
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
113
+ --color-sidebar-border: var(--sidebar-border);
114
+ --color-sidebar-ring: var(--sidebar-ring);
115
+ }
116
+
117
+ @layer base {
118
+ * {
119
+ @apply border-border outline-ring/50;
120
+ }
121
+ body {
122
+ @apply bg-background text-foreground;
123
+ }
124
+ } */
@@ -1,81 +1,81 @@
1
- import "@testing-library/jest-dom";
2
-
3
- import React from "react";
4
- import { render, screen, fireEvent } from "@testing-library/react";
5
- import Autocomplete from "../components/Autocomplete";
6
- import {
7
- Option,
8
- AutocompleteProps,
9
- } from "../components/Autocomplete/Autocomplete.types";
10
-
11
- describe("Autocomplete component", () => {
12
- const options: Option[] = [
13
- { id: 1, name: "Apple" },
14
- { id: 2, name: "Banana" },
15
- { id: 3, name: "Orange" },
16
- ];
17
-
18
- const setup = (props?: Partial<AutocompleteProps>) => {
19
- const defaultProps: AutocompleteProps = {
20
- options,
21
- selected: [],
22
- setSelected: jest.fn(),
23
- placeholder: "Search...",
24
- showChosen: false,
25
- ...props,
26
- };
27
-
28
- render(<Autocomplete {...defaultProps} />);
29
- return defaultProps;
30
- };
31
-
32
- it("renders input with placeholder", () => {
33
- setup();
34
- expect(screen.getByPlaceholderText("Search...")).toBeInTheDocument();
35
- });
36
-
37
- it("filters options based on input", () => {
38
- setup();
39
-
40
- const input = screen.getByPlaceholderText("Search...");
41
- fireEvent.change(input, { target: { value: "ban" } });
42
-
43
- expect(screen.getByText("Banana")).toBeInTheDocument();
44
- expect(screen.queryByText("Apple")).not.toBeInTheDocument();
45
- });
46
-
47
- it("calls setSelected on item click (single selection)", () => {
48
- const mockSetSelected = jest.fn();
49
- setup({ setSelected: mockSetSelected });
50
-
51
- const input = screen.getByPlaceholderText("Search...");
52
- fireEvent.change(input, { target: { value: "oran" } });
53
-
54
- const option = screen.getByText("Orange");
55
- fireEvent.click(option);
56
-
57
- expect(mockSetSelected).toHaveBeenCalledWith([{ id: 3, name: "Orange" }]);
58
- });
59
-
60
- it("supports multiple selections when showChosen is true", () => {
61
- const mockSetSelected = jest.fn();
62
- setup({ setSelected: mockSetSelected, showChosen: true });
63
-
64
- const input = screen.getByPlaceholderText("Search...");
65
- fireEvent.change(input, { target: { value: "app" } });
66
-
67
- fireEvent.click(screen.getByText("Apple"));
68
-
69
- expect(mockSetSelected).toHaveBeenCalledWith([{ id: 1, name: "Apple" }]);
70
- });
71
-
72
- it("does not show placeholder if item is selected and showChosen is false", () => {
73
- setup({
74
- selected: [{ id: 2, name: "Banana" }],
75
- showChosen: false,
76
- });
77
-
78
- expect(screen.queryByPlaceholderText("Search...")).not.toBeInTheDocument();
79
- expect(screen.getByText("Banana")).toBeInTheDocument();
80
- });
81
- });
1
+ import '@testing-library/jest-dom';
2
+
3
+ import React from 'react';
4
+ import { render, screen, fireEvent } from '@testing-library/react';
5
+ import Autocomplete from '../components/Autocomplete';
6
+ import {
7
+ Option,
8
+ AutocompleteProps,
9
+ } from '../components/Autocomplete/Autocomplete.types';
10
+
11
+ describe('Autocomplete component', () => {
12
+ const options: Option[] = [
13
+ { id: 1, name: 'Apple' },
14
+ { id: 2, name: 'Banana' },
15
+ { id: 3, name: 'Orange' },
16
+ ];
17
+
18
+ const setup = (props?: Partial<AutocompleteProps>) => {
19
+ const defaultProps: AutocompleteProps = {
20
+ options,
21
+ selected: [],
22
+ setSelected: jest.fn(),
23
+ placeholder: 'Search...',
24
+ showChosen: false,
25
+ ...props,
26
+ };
27
+
28
+ render(<Autocomplete {...defaultProps} />);
29
+ return defaultProps;
30
+ };
31
+
32
+ it('renders input with placeholder', () => {
33
+ setup();
34
+ expect(screen.getByPlaceholderText('Search...')).toBeInTheDocument();
35
+ });
36
+
37
+ it('filters options based on input', () => {
38
+ setup();
39
+
40
+ const input = screen.getByPlaceholderText('Search...');
41
+ fireEvent.change(input, { target: { value: 'ban' } });
42
+
43
+ expect(screen.getByText('Banana')).toBeInTheDocument();
44
+ expect(screen.queryByText('Apple')).not.toBeInTheDocument();
45
+ });
46
+
47
+ it('calls setSelected on item click (single selection)', () => {
48
+ const mockSetSelected = jest.fn();
49
+ setup({ setSelected: mockSetSelected });
50
+
51
+ const input = screen.getByPlaceholderText('Search...');
52
+ fireEvent.change(input, { target: { value: 'oran' } });
53
+
54
+ const option = screen.getByText('Orange');
55
+ fireEvent.click(option);
56
+
57
+ expect(mockSetSelected).toHaveBeenCalledWith([{ id: 3, name: 'Orange' }]);
58
+ });
59
+
60
+ it('supports multiple selections when showChosen is true', () => {
61
+ const mockSetSelected = jest.fn();
62
+ setup({ setSelected: mockSetSelected, showChosen: true });
63
+
64
+ const input = screen.getByPlaceholderText('Search...');
65
+ fireEvent.change(input, { target: { value: 'app' } });
66
+
67
+ fireEvent.click(screen.getByText('Apple'));
68
+
69
+ expect(mockSetSelected).toHaveBeenCalledWith([{ id: 1, name: 'Apple' }]);
70
+ });
71
+
72
+ it('does not show placeholder if item is selected and showChosen is false', () => {
73
+ setup({
74
+ selected: [{ id: 2, name: 'Banana' }],
75
+ showChosen: false,
76
+ });
77
+
78
+ expect(screen.queryByPlaceholderText('Search...')).not.toBeInTheDocument();
79
+ expect(screen.getByText('Banana')).toBeInTheDocument();
80
+ });
81
+ });
@@ -1,48 +1,48 @@
1
- import "@testing-library/jest-dom";
2
- import { render, screen, fireEvent } from "@testing-library/react";
3
- import Button from "../components/Button";
4
- import type { IButtonProps } from "../components/Button/Button.types";
5
-
6
- describe("Button component", () => {
7
- it("renders with given title", () => {
8
- render(<Button variant="outline" title="Click Me" />);
9
- expect(screen.getByText("Click Me")).toBeInTheDocument();
10
- });
11
-
12
- it("calls onClick when clicked", () => {
13
- const handleClick = jest.fn();
14
- render(<Button variant="outline" title="Click Me" onClick={handleClick} />);
15
- fireEvent.click(screen.getByText("Click Me"));
16
- expect(handleClick).toHaveBeenCalledTimes(1);
17
- });
18
-
19
- it("is disabled when disabled prop is true", () => {
20
- render(<Button variant="danger" title="Disabled" disabled />);
21
- const button = screen.getByText("Disabled") as HTMLButtonElement;
22
- expect(button).toBeDisabled();
23
- });
24
-
25
- it('applies the correct class for variant "primary"', () => {
26
- render(<Button title="Primary" variant="primary" />);
27
- const button = screen.getByText("Primary");
28
- expect(button.className).toMatch(/bg-\[#5876EE\]/); // Tailwind class match
29
- });
30
-
31
- it('applies the correct class for size "large"', () => {
32
- render(<Button variant="primary" title="Large Button" size="large" />);
33
- const button = screen.getByText("Large Button");
34
- expect(button.className).toMatch(/w-full/);
35
- });
36
-
37
- it("allows custom className via props", () => {
38
- render(
39
- <Button
40
- variant="secondary"
41
- title="Custom Class"
42
- className="custom-class"
43
- />
44
- );
45
- const button = screen.getByText("Custom Class");
46
- expect(button.className).toMatch(/custom-class/);
47
- });
48
- });
1
+ import '@testing-library/jest-dom';
2
+ import { render, screen, fireEvent } from '@testing-library/react';
3
+ import Button from '../components/Button';
4
+ import type { IButtonProps } from '../components/Button/Button.types';
5
+
6
+ describe('Button component', () => {
7
+ it('renders with given title', () => {
8
+ render(<Button variant='outline' title='Click Me' />);
9
+ expect(screen.getByText('Click Me')).toBeInTheDocument();
10
+ });
11
+
12
+ it('calls onClick when clicked', () => {
13
+ const handleClick = jest.fn();
14
+ render(<Button variant='outline' title='Click Me' onClick={handleClick} />);
15
+ fireEvent.click(screen.getByText('Click Me'));
16
+ expect(handleClick).toHaveBeenCalledTimes(1);
17
+ });
18
+
19
+ it('is disabled when disabled prop is true', () => {
20
+ render(<Button variant='danger' title='Disabled' disabled />);
21
+ const button = screen.getByText('Disabled') as HTMLButtonElement;
22
+ expect(button).toBeDisabled();
23
+ });
24
+
25
+ it('applies the correct class for variant "primary"', () => {
26
+ render(<Button title='Primary' variant='primary' />);
27
+ const button = screen.getByText('Primary');
28
+ expect(button.className).toMatch(/bg-\[#5876EE\]/); // Tailwind class match
29
+ });
30
+
31
+ it('applies the correct class for size "large"', () => {
32
+ render(<Button variant='primary' title='Large Button' size='large' />);
33
+ const button = screen.getByText('Large Button');
34
+ expect(button.className).toMatch(/w-full/);
35
+ });
36
+
37
+ it('allows custom className via props', () => {
38
+ render(
39
+ <Button
40
+ variant='secondary'
41
+ title='Custom Class'
42
+ className='custom-class'
43
+ />
44
+ );
45
+ const button = screen.getByText('Custom Class');
46
+ expect(button.className).toMatch(/custom-class/);
47
+ });
48
+ });