Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SP-537 사용자 평점 그래프 컴포넌트 #175

Merged
merged 18 commits into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
23 changes: 23 additions & 0 deletions .github/workflows/chromatic.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Deploy Storybook to Chromatic
on:
push:
branches:
- dev
pull_request:
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install dependencies
run: yarn install --immutable --immutable-cache --check-cache
- name: Run Chromatic
uses: chromaui/[email protected]
with:
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
onlyChanged: true
exitZeroOnChanges: true
1 change: 1 addition & 0 deletions .storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { GlobalStyle } from '@/Styles/globalStyles';
import { withRouter } from 'storybook-addon-remix-react-router';
import { handlers } from '../src/Mocks/handlers';
import { initialize, mswLoader } from 'msw-storybook-addon';
import '../src/App.css';

initialize(
{
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"start:windows": "set HOST=local.ludoapi.store &&yarn dev",
"test": "jest",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
"build-storybook": "storybook build",
"chromatic": "chromatic"
},
"dependencies": {
"@hookform/error-message": "^2.0.1",
Expand Down Expand Up @@ -62,6 +63,7 @@
"@typescript-eslint/eslint-plugin": "^6.14.0",
"@typescript-eslint/parser": "^6.14.0",
"@vitejs/plugin-react": "^4.2.1",
"chromatic": "^11.3.0",
"eslint": "^8.55.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.5",
Expand Down
83 changes: 83 additions & 0 deletions src/Components/CircularRate/CircularRate.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import type { Meta, StoryObj } from '@storybook/react';
import { CircularRate } from '.';
import { theme } from '@/Styles/theme';

const meta = {
component: CircularRate,
argTypes: {
percentage: {
control: {
type: 'range',
min: 0,
max: 100,
},
},
},
args: {
percentage: 70,
label: '전문성',
},
} satisfies Meta<typeof CircularRate>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Default: Story = {};

/** 시작과 끝점이 같은 그라데이션 */
export const CirculatingGradient: Story = {
args: {
gradientColors: [theme.color.purple1, theme.color.orange1, theme.color.purple1],
percentage: 100,
},
};

/** 단색 그래프 */
export const Monotone: Story = {
args: {
gradientColors: [theme.color.purple1, theme.color.purple1],
},
};

/** 적극성 */
export const Proactiveness: Story = {
args: {
percentage: 70,
label: '적극성',
},
};

/** 큰 사이즈 */
export const Bigger: Story = {
args: {
size: 100,
},
};

/** 굵은 그래프 선 굵기 */
export const Bold: Story = {
args: {
barWeight: 8,
},
};

/** 0% */
export const ZeroPercent: Story = {
args: {
percentage: 0,
},
};

/** 1% */
export const OnePercent: Story = {
args: {
percentage: 1,
},
};

/** 100% */
export const FullPercent: Story = {
args: {
percentage: 100,
},
};
109 changes: 109 additions & 0 deletions src/Components/CircularRate/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { theme } from '@/Styles/theme';
import { useRef } from 'react';
import styled from 'styled-components';

export interface CircularRateProps {
/** 0부터 100 사이의 평가 지수 */
percentage: number;

/** 선 두께 */
barWeight?: number;

/** 전체 상자 크기. 단위는 픽셀. */
size?: number;

/** 그래디언트 색상 */
gradientColors?: string[];

/** 해당 지표의 이름 */
label: string;
}

let x = 0;

// 고유 id 생성용 훅
const useMaskId = () => useRef(++x).current;

/** 사용자 평가 지수를 원형 그래프로 표현하는 컴포넌트 */
export const CircularRate = ({
percentage,
barWeight = 4,
size = 80,
gradientColors = [theme.color.purple1, theme.color.orange1],
label,
}: CircularRateProps) => {
const maskId = useMaskId();

// 내접원의 지름 / 반지름
const diameter = size - barWeight,
r = diameter / 2;

return (
<Box>
<svg xmlns="http://www.w3.org/2000/svg" width={size} height={size}>
<circle cx="50%" cy="50%" r={r} fill="none" stroke="#F2F3F3" strokeWidth={barWeight} />
<mask id={`ring-${maskId}`}>
abiriadev marked this conversation as resolved.
Show resolved Hide resolved
<circle
cx="50%"
cy="50%"
r={r}
fill="none"
stroke="white"
strokeLinecap={percentage === 0 ? 'butt' : 'round'}
strokeWidth={barWeight}
// 원에 외접하는 정사각형의 길이를 원주로 변환
strokeDasharray={`${(percentage / 100) * diameter * Math.PI}px 1000%`}
style={{
transform: 'rotateY(180deg) rotateZ(-90deg)',
transformOrigin: 'center',
}}
/>
</mask>
<foreignObject width={size} height={size} mask={`url(#ring-${maskId})`}>
<GradientBox $colors={gradientColors} />
</foreignObject>
</svg>
<InnerBox $size={size}>
<ProgressText>{percentage}%</ProgressText>
<LabelText>{label}</LabelText>
</InnerBox>
</Box>
);
};

const Box = styled.div`
position: relative;
`;

const GradientBox = styled.div<{ $colors: Array<string> }>`
width: 100%;
height: 100%;
background-image: conic-gradient(${({ $colors }) => $colors.reverse().join(', ')});
abiriadev marked this conversation as resolved.
Show resolved Hide resolved
`;

const InnerBox = styled.div<{ $size: number }>`
position: absolute;
top: 0;
left: 0;
width: ${({ $size }) => $size}px;
height: ${({ $size }) => $size}px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
`;

const ProgressText = styled.span`
height: 24px;
font-size: 18px;
font-weight: 500;
line-height: 24px;
color: ${({ theme }) => theme.color.black5};
`;

const LabelText = styled.span`
font-size: 16;
font-weight: 400;
line-height: '24px';
color: ${({ theme }) => theme.color.black2};
`;
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Meta, StoryObj } from '@storybook/react';
import DropdownItem from '.';
import { fn } from '@storybook/test';

const meta = {
component: DropdownItem,
Expand Down
Loading