Skip to content

Commit

Permalink
Implement simple spinner component
Browse files Browse the repository at this point in the history
  • Loading branch information
cgero-eth committed Sep 19, 2023
1 parent 8f4e6fe commit 20c7638
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 0 deletions.
8 changes: 8 additions & 0 deletions src/components/illustrationHuman/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export { IllustrationHuman, type IIllustrationHumanProps } from './illustrationHuman';
export type {
IllustrationHumanAccessory,
IllustrationHumanBody,
IllustrationHumanExpression,
IllustrationHumanHairs,
IllustrationHumanSunglasses,
} from './illustrationHumanType';
4 changes: 4 additions & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
export * from './button';
export * from './icon';
export * from './illustrationHuman';
export * from './illustrationObject';
export * from './spinner';
1 change: 1 addition & 0 deletions src/components/spinner/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Spinner, type ISpinnerProps } from './spinner';
22 changes: 22 additions & 0 deletions src/components/spinner/spinner.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { Meta, StoryObj } from '@storybook/react';
import { Spinner } from './spinner';

const meta: Meta<typeof Spinner> = {
title: 'components/Spinner',
component: Spinner,
tags: ['autodocs'],
};

type Story = StoryObj<typeof Spinner>;

/**
* Default usage example of the Spinner component.
*/
export const Default: Story = {
args: {
variant: 'neutral',
size: 'lg',
},
};

export default meta;
19 changes: 19 additions & 0 deletions src/components/spinner/spinner.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { render, screen } from '@testing-library/react';
import { Spinner, type ISpinnerProps } from './spinner';

describe('<Spinner /> component', () => {
const createTestComponent = (props?: Partial<ISpinnerProps>) => {
const completeProps: ISpinnerProps = {
variant: 'primary',
size: 'md',
...props,
};

return <Spinner {...completeProps} />;
};

it('renders a spinner', () => {
render(createTestComponent());
expect(screen.getByRole('progressbar')).toBeInTheDocument();
});
});
50 changes: 50 additions & 0 deletions src/components/spinner/spinner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import classNames from 'classnames';
import { type HTMLAttributes } from 'react';

export type SpinnerSize = 'sm' | 'md' | 'lg' | 'xl';
export type SpinnerVariant = 'neutral' | 'primary' | 'success' | 'warning' | 'critical';

export interface ISpinnerProps extends HTMLAttributes<HTMLDivElement> {
/**
* Size of the spinner.
*/
size: SpinnerSize;
/**
* Variant of the spinner.
*/
variant: SpinnerVariant;
}

const sizeToDimension: Record<SpinnerSize, number> = {
sm: 16,
md: 20,
lg: 24,
xl: 32,
};

const variantToClassNames: Record<SpinnerVariant, string> = {
neutral: 'border-neutral-100 border-t-primary-400',
primary: 'border-neutral-0 border-t-primary-300',
success: 'border-success-300 border-t-success-800',
warning: 'border-warning-300 border-t-warning-800',
critical: 'border-critical-300 border-t-critical-800',
};

/**
* Spinner UI component
*/
export const Spinner: React.FC<ISpinnerProps> = (props) => {
const { size, variant, style, className, ...otherProps } = props;

const spinerSize = sizeToDimension[size];
const computedStyle = { width: spinerSize, height: spinerSize, ...style };

return (
<div
role="progressbar"
className={classNames('animate-spin rounded-full border-2', variantToClassNames[variant], className)}
style={computedStyle}
{...otherProps}
/>
);
};

0 comments on commit 20c7638

Please sign in to comment.