Skip to content

Commit

Permalink
Add AlphaSpinner component (#2237)
Browse files Browse the repository at this point in the history
<!--
  How to write a good PR title:
- Follow [the Conventional Commits
specification](https://www.conventionalcommits.org/en/v1.0.0/).
  - Give as much context as necessary and as little as possible
  - Prefix it with [WIP] while it’s a work in progress
-->

## Self Checklist

- [x] I wrote a PR title in **English** and added an appropriate
**label** to the PR.
- [x] I wrote the commit message in **English** and to follow [**the
Conventional Commits
specification**](https://www.conventionalcommits.org/en/v1.0.0/).
- [x] I [added the
**changeset**](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md)
about the changes that needed to be released. (or didn't have to)
- [x] I wrote or updated **documentation** related to the changes. (or
didn't have to)
- [x] I wrote or updated **tests** related to the changes. (or didn't
have to)
- [x] I tested the changes in various browsers. (or didn't have to)
  - Windows: Chrome, Edge, (Optional) Firefox
  - macOS: Chrome, Edge, Safari, (Optional) Firefox

## Related Issue

<!-- Please link to issue if one exists -->

<!-- Fixes #0000 -->

- Fixes #2236

## Summary

<!-- Please brief explanation of the changes made -->

- `AlphaSpinner` 컴포넌트를 구현합니다. 

## Details

<!-- Please elaborate description of the changes -->

- 기존의 `Spinner` 컴포넌트와 다르게 곡선의 끝부분이 둥글게 디자인되어 있어서 border속성을 이용해서 구현하기가
어려웠습니다. 그래서 `circle` 엘리먼트를 2개 만들고 Indicator에 해당하는 곡선의 stroke-dasharray
속성을 조절하는 것으로 구현했습니다.
- 버튼 컴포넌트 안에서 `AlphaSpinner`를 사용해야 하는데, 버튼의 아이콘 크기에 맞게 크기를 조절을 해야합니다. 약간
고민이 필요해보여 후속 pr로 작업하겠습니다.

### Breaking change? (Yes/No)

<!-- If Yes, please describe the impact and migration path for users -->

- No

## References

<!-- Please list any other resources or points the reviewer should be
aware of -->

-
[스펙(internal)](https://www.notion.so/Spinner-e1e30e3fdcb14a3ba94b900d24e0d58e?d=85604f3bca254e758c57c5949412d470&pvs=5#b887eb2d1ad843458f0ba2a90eee430e)
-
[디자인(internal)](https://www.figma.com/design/KyhPPZeeC0JBmTclJGe3nn/Status?node-id=6%3A69&t=eqYaVwNelOoBo1zv-1)
- 참고한 구현방식: https://atlassian.design/components/spinner/examples
  • Loading branch information
yangwooseong authored May 29, 2024
1 parent a282c2f commit 0947af5
Show file tree
Hide file tree
Showing 8 changed files with 202 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/sweet-knives-divide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@channel.io/bezier-react": patch
---

Add `AlphaSpinner` component
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react'

import { type Meta, type StoryFn } from '@storybook/react'

import { Spinner } from './Spinner'
import { type SpinnerProps } from './Spinner.types'

const meta: Meta<typeof Spinner> = {
component: Spinner,
}

export default meta

const Template: StoryFn<SpinnerProps> = ({ ...args }) => <Spinner {...args} />

export const Primary = {
render: Template,

args: {
size: 'm',
variant: 'secondary',
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
@use '../../styles/mixins/dimension';

@keyframes rotate {
0% {
transform: rotate(0deg);
}

100% {
transform: rotate(360deg);
}
}

.Spinner {
--b-spinner-size: initial;
--b-spinner-track-color: initial;
--b-spinner-indicator-color: initial;
--b-spinner-stroke-width: initial;
--b-spinner-stroke-dasharray: initial;

@include dimension.square(var(--b-spinner-size));

display: inline-flex;
animation: rotate 1s linear infinite;

& .track {
fill: none;
stroke: var(--b-spinner-track-color);
stroke-linecap: round;
stroke-width: var(--b-spinner-stroke-width);
}

& .indicator {
fill: none;
stroke: var(--b-spinner-indicator-color);
stroke-dasharray: var(--b-spinner-stroke-dasharray);
stroke-linecap: round;
stroke-width: var(--b-spinner-stroke-width);
}

&:where(.size-s) {
--b-spinner-size: 28px;
--b-spinner-stroke-width: 4px;
--b-spinner-stroke-dasharray: 40 9999;
}

&:where(.size-m) {
--b-spinner-size: 50px;
--b-spinner-stroke-width: 6px;
--b-spinner-stroke-dasharray: 60 9999;
}

&:where(.variant-primary) {
--b-spinner-track-color: var(--alpha-color-primary-bg-lightest);
--b-spinner-indicator-color: var(--alpha-color-fg-blue-normal);
}

&:where(.variant-secondary) {
--b-spinner-track-color: var(--alpha-color-bg-black-light);
--b-spinner-indicator-color: var(--alpha-color-fg-black-light);
}

&:where(.variant-on-overlay) {
--b-spinner-track-color: var(--alpha-color-bg-absolute-white-lightest);
--b-spinner-indicator-color: var(--alpha-color-fg-absolute-white-light);
}
}
34 changes: 34 additions & 0 deletions packages/bezier-react/src/components/AlphaSpinner/Spinner.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react'

import { render } from '~/src/utils/test'

import { SPINNER_TEST_ID, Spinner } from './Spinner'

describe('Spinner >', () => {
const renderSpinner = (props?: React.ComponentProps<typeof Spinner>) =>
render(<Spinner {...props} />)

it('should render', () => {
const { getByTestId } = renderSpinner()
const renderedSpinner = getByTestId(SPINNER_TEST_ID)
expect(renderedSpinner).toBeInTheDocument()
})

it('should render as a span element by default', () => {
const { getByTestId } = renderSpinner()
const renderedSpinner = getByTestId(SPINNER_TEST_ID)
expect(renderedSpinner.tagName).toBe('SPAN')
})

it('should forward ref', () => {
const ref = React.createRef<HTMLDivElement>()
renderSpinner({ ref })
expect(ref.current).toBeInTheDocument()
})

it('should receive size', () => {
const { getByTestId } = renderSpinner({ size: 'm' })
const renderedSpinner = getByTestId(SPINNER_TEST_ID)
expect(renderedSpinner).toHaveClass('size-m')
})
})
50 changes: 50 additions & 0 deletions packages/bezier-react/src/components/AlphaSpinner/Spinner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React, { forwardRef } from 'react'

import classNames from 'classnames'

import { type SpinnerProps } from './Spinner.types'

import styles from './Spinner.module.scss'

export const SPINNER_TEST_ID = 'bezier-spinner'

export const Spinner = forwardRef<HTMLSpanElement, SpinnerProps>(
function Spinner(
{ className, size, variant = 'secondary', ...rest },
forwardedRef
) {
return (
<span
className={classNames(
styles.Spinner,
size && styles[`size-${size}`],
styles[`variant-${variant}`]
)}
ref={forwardedRef}
data-testid={SPINNER_TEST_ID}
{...rest}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
>
<circle
cx="8"
cy="8"
r="7"
className={styles.track}
vectorEffect="non-scaling-stroke"
/>

<circle
cx="8"
cy="8"
r="7"
className={styles.indicator}
vectorEffect="non-scaling-stroke"
/>
</svg>
</span>
)
}
)
21 changes: 21 additions & 0 deletions packages/bezier-react/src/components/AlphaSpinner/Spinner.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import {
type BezierComponentProps,
type ColorProps,
type SizeProps,
} from '~/src/types/props'

type SpinnerSize = 's' | 'm'

interface SpinnerOwnProps {
/**
* The style variant of Spinner.
* @default 'secondary'
*/
variant?: 'primary' | 'secondary' | 'on-overlay'
}

export interface SpinnerProps
extends Omit<BezierComponentProps<'span'>, keyof ColorProps>,
SizeProps<SpinnerSize>,
ColorProps,
SpinnerOwnProps {}
2 changes: 2 additions & 0 deletions packages/bezier-react/src/components/AlphaSpinner/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { Spinner as AlphaSpinner } from './Spinner'
export { type SpinnerProps as AlphaSpinnerProps } from './Spinner.types'
1 change: 1 addition & 0 deletions packages/bezier-react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export * from '~/src/components/AlphaDialogPrimitive'
export * from '~/src/components/AlphaFloatingButton'
export * from '~/src/components/AlphaFloatingIconButton'
export * from '~/src/components/AlphaIconButton'
export * from '~/src/components/AlphaSpinner'
export * from '~/src/components/AlphaTooltipPrimitive'
export * from '~/src/components/AppProvider'
export * from '~/src/components/AutoFocus'
Expand Down

0 comments on commit 0947af5

Please sign in to comment.