diff options
Diffstat (limited to 'src/components/atoms')
| -rw-r--r-- | src/components/atoms/loaders/spinner.module.scss | 48 | ||||
| -rw-r--r-- | src/components/atoms/loaders/spinner.stories.tsx | 28 | ||||
| -rw-r--r-- | src/components/atoms/loaders/spinner.test.tsx | 14 | ||||
| -rw-r--r-- | src/components/atoms/loaders/spinner.tsx | 37 | 
4 files changed, 127 insertions, 0 deletions
| diff --git a/src/components/atoms/loaders/spinner.module.scss b/src/components/atoms/loaders/spinner.module.scss new file mode 100644 index 0000000..8d818a2 --- /dev/null +++ b/src/components/atoms/loaders/spinner.module.scss @@ -0,0 +1,48 @@ +@use "@styles/abstracts/functions" as fun; + +.wrapper { +  display: flex; +  flex-flow: row wrap; +  align-items: center; +  justify-content: center; +  gap: var(--spacing-2xs); +  margin: var(--spacing-md) 0; +} + +.ball { +  width: fun.convert-px(8); +  height: fun.convert-px(8); +  background: linear-gradient( +    to right, +    var(--color-primary-light) 0%, +    var(--color-primary-lighter) 100% +  ); +  border-radius: 50%; +  animation: spinner 1.4s infinite ease-in-out both; + +  &:first-child { +    animation-delay: -0.32s; +  } + +  &:nth-child(2) { +    animation-delay: -0.16s; +  } +} + +.text { +  margin-left: var(--spacing-xs); +  color: var(--color-primary-darker); +  text-align: center; +} + +@keyframes spinner { +  0%, +  80%, +  100% { +    transform: scale(0); +  } + +  40% { +    transform: scale(1); +  } +} diff --git a/src/components/atoms/loaders/spinner.stories.tsx b/src/components/atoms/loaders/spinner.stories.tsx new file mode 100644 index 0000000..86c316e --- /dev/null +++ b/src/components/atoms/loaders/spinner.stories.tsx @@ -0,0 +1,28 @@ +import { ComponentMeta, ComponentStory } from '@storybook/react'; +import { IntlProvider } from 'react-intl'; +import SpinnerComponent from './spinner'; + +export default { +  title: 'Atoms/Loaders', +  component: SpinnerComponent, +  argTypes: { +    message: { +      control: { +        type: 'text', +      }, +      description: 'Loading message.', +      type: { +        name: 'string', +        required: false, +      }, +    }, +  }, +} as ComponentMeta<typeof SpinnerComponent>; + +const Template: ComponentStory<typeof SpinnerComponent> = (args) => ( +  <IntlProvider locale="en"> +    <SpinnerComponent {...args} /> +  </IntlProvider> +); + +export const Spinner = Template.bind({}); diff --git a/src/components/atoms/loaders/spinner.test.tsx b/src/components/atoms/loaders/spinner.test.tsx new file mode 100644 index 0000000..0a6db91 --- /dev/null +++ b/src/components/atoms/loaders/spinner.test.tsx @@ -0,0 +1,14 @@ +import { render, screen } from '@test-utils'; +import Spinner from './spinner'; + +describe('Spinner', () => { +  it('renders a spinner loader', () => { +    render(<Spinner />); +    expect(screen.getByText('Loading...')).toBeInTheDocument(); +  }); + +  it('renders a spinner loader with a custom message', () => { +    render(<Spinner message="Submitting" />); +    expect(screen.getByText('Submitting')).toBeInTheDocument(); +  }); +}); diff --git a/src/components/atoms/loaders/spinner.tsx b/src/components/atoms/loaders/spinner.tsx new file mode 100644 index 0000000..57b0a43 --- /dev/null +++ b/src/components/atoms/loaders/spinner.tsx @@ -0,0 +1,37 @@ +import { FC } from 'react'; +import { useIntl } from 'react-intl'; +import styles from './spinner.module.scss'; + +type SpinnerProps = { +  /** +   * The loading message. Default: "Loading...". +   */ +  message?: string; +}; + +/** + * Spinner component + * + * Render a loading message with animation. + */ +const Spinner: FC<SpinnerProps> = ({ message }) => { +  const intl = useIntl(); + +  return ( +    <div className={styles.wrapper}> +      <div className={styles.ball}></div> +      <div className={styles.ball}></div> +      <div className={styles.ball}></div> +      <div className={styles.text}> +        {message || +          intl.formatMessage({ +            defaultMessage: 'Loading...', +            description: 'Spinner: loading text', +            id: 'q9cJQe', +          })} +      </div> +    </div> +  ); +}; + +export default Spinner; | 
