Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 2 additions & 2 deletions src/components/button/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ReactNode } from 'react';
import { Spinner } from '../spinner/Spinner';
import Loader from '../loader/Loader';

interface ButtonProps {
id?: string;
Expand Down Expand Up @@ -73,7 +73,7 @@ export const Button = ({
ring-offset-2 ring-offset-transparent transition-all duration-100 ease-in-out
focus-visible:ring-primary/50 ${styles} ${className}`}
>
{loading && <Spinner size={18} />}
{loading && <Loader size={18} />}
<div className="flex items-center justify-center space-x-2" data-cy={buttonChildrenDataCy}>
{children}
</div>
Expand Down
86 changes: 46 additions & 40 deletions src/components/button/__test__/__snapshots__/Button.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -541,10 +541,53 @@ exports[`Button component > Primary loading button should render correctly 1`] =
disabled=""
type="button"
>
<div>
<svg
class="animate-spin undefined"
fill="none"
height="18"
role="img"
viewBox="0 0 24 24"
width="18"
xmlns="http://www.w3.org/2000/svg"
>
<circle
class="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
stroke-width="4"
/>
<path
class="opacity-75"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824
3 7.938l3-2.647z"
fill="currentColor"
/>
</svg>
</div>
<div
class="flex items-center justify-center space-x-2"
/>
</button>
</div>
</body>,
"container": <div>
<button
class="h-10 px-5 relative flex shrink-0 select-none flex-row items-center justify-center space-x-2
whitespace-nowrap rounded-lg text-base font-medium outline-none ring-2 ring-primary/0
ring-offset-2 ring-offset-transparent transition-all duration-100 ease-in-out
focus-visible:ring-primary/50 bg-primary-dark active:bg-primary-dark text-white shadow-sm "
disabled=""
type="button"
>
<div>
<svg
class="animate-spin "
class="animate-spin undefined"
fill="none"
height="18"
role="img"
viewBox="0 0 24 24"
width="18"
xmlns="http://www.w3.org/2000/svg"
Expand All @@ -560,48 +603,11 @@ exports[`Button component > Primary loading button should render correctly 1`] =
<path
class="opacity-75"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824
3 7.938l3-2.647z"
3 7.938l3-2.647z"
fill="currentColor"
/>
</svg>
<div
class="flex items-center justify-center space-x-2"
/>
</button>
</div>
</body>,
"container": <div>
<button
class="h-10 px-5 relative flex shrink-0 select-none flex-row items-center justify-center space-x-2
whitespace-nowrap rounded-lg text-base font-medium outline-none ring-2 ring-primary/0
ring-offset-2 ring-offset-transparent transition-all duration-100 ease-in-out
focus-visible:ring-primary/50 bg-primary-dark active:bg-primary-dark text-white shadow-sm "
disabled=""
type="button"
>
<svg
class="animate-spin "
fill="none"
height="18"
viewBox="0 0 24 24"
width="18"
xmlns="http://www.w3.org/2000/svg"
>
<circle
class="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
stroke-width="4"
/>
<path
class="opacity-75"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824
3 7.938l3-2.647z"
fill="currentColor"
/>
</svg>
</div>
<div
class="flex items-center justify-center space-x-2"
/>
Expand Down
11 changes: 7 additions & 4 deletions src/components/contextMenu/__test__/ContextMenu.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { render, fireEvent, screen } from '@testing-library/react';
import ContextMenu, { ContextMenuProps, MenuItemType } from '../ContextMenu';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import React from 'react';
import { render, fireEvent, screen } from '@testing-library/react';
import ContextMenu, { ContextMenuProps } from '../ContextMenu';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { MenuItemType } from '../../menu/Menu';

interface TestItem {
name: string;
Expand Down Expand Up @@ -29,7 +30,9 @@ describe('ContextMenu Component', () => {
return render(<ContextMenu {...props} />);
};

beforeEach(() => {});
afterEach(() => {
vi.clearAllMocks();
});

it('should match snapshot', () => {
const contextMenu = renderContextMenu();
Expand Down
2 changes: 1 addition & 1 deletion src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ export * from './checkbox/Checkbox';
export * from './input/Input';
export * from './textArea/TextArea';
export * from './button/Button';
export * from './loader/Loader';
export * from './switch/Switch';
export * from './radio-button/RadioButton';
export * from './avatar/Avatar';
export * from './spinner/Spinner';
export * from './slider/RangeSlider';
export * from './dialog/Dialog';
export * from './modal/Modal';
Expand Down
85 changes: 85 additions & 0 deletions src/components/loader/Loader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import React from 'react';
import '../../styles/Loader.css';

interface LoaderProps {
classNameContainer?: string;
classNameLoader?: string;
classNameText?: string;
type?: 'spinner' | 'pulse';
text?: string;
size?: number;
}

/**
* Loader component.
*
* @property {string} [classNameContainer]
* - Optional class name for the container wrapping the loader.
* Useful for applying custom styles to the outermost container.
*
* @property {string} [classNameLoader]
* - Optional class name for the loader element itself (spinner or pulse).
* Allows custom styling of the loading animation.
*
* @property {string} [classNameText]
* - Optional class name for the text displayed below the loader.
* Allows style or adjust the appearance of the text.
*
* @property {'spinner' | 'pulse'} [type='spinner']
* - Determines the type of loader to render.
* Can be `'spinner'` for a rotating animation or `'pulse'` for a pulsing effect.
* Defaults to `'spinner'`.
*
* @property {string} [text]
* - Optional text to display below the loader.
*
* @property {number} [size=32]
* - Size of the spinner loader in pixels.
* Applies to the width and height of the SVG element for the `'spinner'` type.
* Defaults to `32`.
*/

const Loader: React.FC<LoaderProps> = ({
classNameContainer,
classNameLoader,
classNameText,
type = 'spinner',
text,
size = 32,
}) => {
const isSpinner = type === 'spinner';

return (
<div className={classNameContainer}>
{isSpinner ? (
<>
<svg
className={`animate-spin ${classNameLoader}`}
width={size}
height={size}
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
role="img"
>
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824
3 7.938l3-2.647z"
></path>
</svg>
{text && <p className={classNameText}>{text}</p>}
</>
) : (
<div className={`loader-container ${classNameLoader}`}>
<div className="loader06"></div>
{text && <p className={`loader-text ${classNameText}`}>{text}</p>}
</div>
)}
</div>
);
};

export default Loader;
75 changes: 75 additions & 0 deletions src/components/loader/__test__/Loader.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import React from 'react';
import { render } from '@testing-library/react';
import { expect } from 'chai';
import { afterEach, describe, it, vi } from 'vitest';
import Loader from '../Loader';

describe('Loader Component', () => {
const renderLoader = (props = {}) => {
return render(<Loader {...props} />);
};

afterEach(() => {
vi.clearAllMocks();
});

it('should match snapshot', () => {
const loader = renderLoader();
expect(loader).toMatchSnapshot();
});

it('renders a spinner loader by default', () => {
const { getByRole } = renderLoader();
const spinner = getByRole('img', { hidden: true });
expect(spinner).toBeInTheDocument();
expect(spinner).toHaveClass('animate-spin');
});

it('renders a pulse loader when type is "pulse"', () => {
const { getByText } = renderLoader({ type: 'pulse' });
const pulseLoader = getByText('', { selector: '.loader06' });
expect(pulseLoader).toBeInTheDocument();
});

it('renders the provided text below the spinner loader', () => {
const text = 'Loading data...';
const { getByText } = renderLoader({ text });
const textElement = getByText(text);
expect(textElement).toBeInTheDocument();
});

it('renders the provided text below the pulse loader', () => {
const text = 'Loading data...';
const { getByText } = renderLoader({ type: 'pulse', text });
const textElement = getByText(text);
expect(textElement).toBeInTheDocument();
expect(textElement).toHaveClass('loader-text');
});

it('applies custom class to the container', () => {
const customClass = 'custom-container';
const { container } = renderLoader({ classNameContainer: customClass });
expect(container.firstChild).toHaveClass(customClass);
});

it('applies custom class to the loader', () => {
const customClass = 'custom-loader';
const { container } = renderLoader({ classNameLoader: customClass });
const loader = container.firstChild?.firstChild;
expect(loader).toHaveClass(customClass);
});

it('renders with the correct size for the spinner', () => {
const size = 64;
const { getByRole } = renderLoader({ size });
const spinner = getByRole('img', { hidden: true });
expect(spinner).toHaveAttribute('width', `${size}`);
expect(spinner).toHaveAttribute('height', `${size}`);
});

it('does not render text if none is provided', () => {
const { queryByText } = renderLoader();
const textElement = queryByText(/./);
expect(textElement).toBeNull();
});
});
Loading
Loading