-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* 💄 style(type): barChartDataType에 label을 string으로 수정 및 value에 null 제거, NullableBarChartType 추가 * 📝 docs(story): label을 string으로 수정 * 💄 style(css): light/secondary 색상 변경 * ✨ feat(component): recommendChart 추가 * ✅ test(component): recommendChart 테스트 코드 추가 ISSUES CLOSED: #12
- Loading branch information
Showing
10 changed files
with
334 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
85 changes: 85 additions & 0 deletions
85
src/components/charts/RecommendChart/RecommendChart.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
import type { Meta, StoryObj } from "@storybook/react"; | ||
import RecommendChart from "./RecommendChart"; | ||
|
||
const meta = { | ||
title: "charts/RecommendChart", | ||
component: RecommendChart, | ||
parameters: { | ||
layout: "centered", | ||
}, | ||
tags: ["autodocs"], | ||
} satisfies Meta<typeof RecommendChart>; | ||
|
||
export default meta; | ||
type Story = StoryObj<typeof RecommendChart>; | ||
|
||
const data = [ | ||
{ label: "적극 매수", value: 8 }, | ||
{ label: "매수", value: 10 }, | ||
{ label: "보유", value: 7 }, | ||
{ label: "매도", value: 3 }, | ||
{ label: "적극 매도", value: 2 }, | ||
]; | ||
|
||
export const Default: Story = { | ||
render: () => <RecommendChart data={data} width={320} height={128} />, | ||
}; | ||
|
||
const ActiveBuyData = [ | ||
{ label: "적극 매수", value: 18 }, | ||
{ label: "매수", value: 10 }, | ||
{ label: "보유", value: 7 }, | ||
{ label: "매도", value: 3 }, | ||
{ label: "적극 매도", value: 2 }, | ||
]; | ||
|
||
export const ActiveBuy: Story = { | ||
render: () => <RecommendChart data={ActiveBuyData} width={320} height={128} />, | ||
}; | ||
|
||
const BuyData = [ | ||
{ label: "적극 매수", value: 8 }, | ||
{ label: "매수", value: 10 }, | ||
{ label: "보유", value: 7 }, | ||
{ label: "매도", value: 3 }, | ||
{ label: "적극 매도", value: 2 }, | ||
]; | ||
|
||
export const Buy: Story = { | ||
render: () => <RecommendChart data={BuyData} width={320} height={128} />, | ||
}; | ||
|
||
const HoldData = [ | ||
{ label: "적극 매수", value: 8 }, | ||
{ label: "매수", value: 10 }, | ||
{ label: "보유", value: 17 }, | ||
{ label: "매도", value: 3 }, | ||
{ label: "적극 매도", value: 2 }, | ||
]; | ||
export const Hold: Story = { | ||
render: () => <RecommendChart data={HoldData} width={320} height={128} />, | ||
}; | ||
|
||
const SellData = [ | ||
{ label: "적극 매수", value: 8 }, | ||
{ label: "매수", value: 10 }, | ||
{ label: "보유", value: 7 }, | ||
{ label: "매도", value: 13 }, | ||
{ label: "적극 매도", value: 2 }, | ||
]; | ||
|
||
export const Sell: Story = { | ||
render: () => <RecommendChart data={SellData} width={320} height={128} />, | ||
}; | ||
|
||
const ActiveSellData = [ | ||
{ label: "적극 매수", value: 8 }, | ||
{ label: "매수", value: 10 }, | ||
{ label: "보유", value: 7 }, | ||
{ label: "매도", value: 3 }, | ||
{ label: "적극 매도", value: 12 }, | ||
]; | ||
|
||
export const ActiveSell: Story = { | ||
render: () => <RecommendChart data={ActiveSellData} width={320} height={128} />, | ||
}; |
72 changes: 72 additions & 0 deletions
72
src/components/charts/RecommendChart/RecommendChart.styles.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import { tv } from "@utils/customTV"; | ||
|
||
export const RecommendChartVariants = tv({ | ||
slots: { | ||
root: "flex gap-md", | ||
square: "text-primary-on font-bold rounded-md flex justify-center items-center text-lg", | ||
barChart: "", | ||
barBg: "fill-surface-container-high", | ||
bar: "", | ||
labelText: "text-sm fill-surface-on", | ||
}, | ||
variants: { | ||
label: { | ||
"적극 매수": { | ||
square: "bg-primary fill-primary-on", | ||
}, | ||
매수: { | ||
square: "bg-secondary fill-secondary-on", | ||
}, | ||
보유: { | ||
square: "bg-surface-inverse fill-surface-on-inverse", | ||
}, | ||
매도: { | ||
square: "bg-error-container-on fill-error-container", | ||
}, | ||
"적극 매도": { | ||
square: "bg-error fill-error-on", | ||
}, | ||
}, | ||
isMaxValue: { | ||
true: { bar: "fill-secondary" }, | ||
false: { bar: "fill-surface-on/20" }, | ||
}, | ||
}, | ||
compoundVariants: [ | ||
{ | ||
label: "적극 매수", | ||
isMaxValue: true, | ||
class: { | ||
bar: "fill-primary", | ||
}, | ||
}, | ||
{ | ||
label: "매수", | ||
isMaxValue: true, | ||
class: { | ||
bar: "fill-secondary", | ||
}, | ||
}, | ||
{ | ||
label: "보유", | ||
isMaxValue: true, | ||
class: { | ||
bar: "fill-surface-inverse", | ||
}, | ||
}, | ||
{ | ||
label: "매도", | ||
isMaxValue: true, | ||
class: { | ||
bar: "fill-error-container-on", | ||
}, | ||
}, | ||
{ | ||
label: "적극 매도", | ||
isMaxValue: true, | ||
class: { | ||
bar: "fill-error", | ||
}, | ||
}, | ||
], | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import { scaleBand, scaleLinear, sum } from "d3"; | ||
import BandAxis from "@charts/BandAxis"; | ||
import { RecommendChartVariants } from "./RecommendChart.styles"; | ||
import { RecommendChartProps, RecommendLabelType } from "./RecommendChart.types"; | ||
|
||
// TODO 반응형 처리 필요 | ||
// TODO 나중에 Bar 애니메이션 처리하면서 Bar 컴포넌트 따로 빼서 BarChart랑 Bar 컴포넌트 공유하기 | ||
const RecommendChart = ({ width, height, data, className, ...props }: RecommendChartProps) => { | ||
const total = sum(data, (d) => d.value); | ||
|
||
const labelScale = scaleBand() | ||
.domain(data.map((d) => d.label.toString())) | ||
.range([0, height]) | ||
.padding(0.6); | ||
|
||
const bandAxisWidth = 64; | ||
const barChartWidth = width - (bandAxisWidth + height); | ||
|
||
const valueScale = scaleLinear().domain([0, total]).range([0, barChartWidth]); | ||
|
||
const { label: maxLabel, value: maxValue } = data.reduce( | ||
(acc, cur) => { | ||
if (acc.value <= cur.value) { | ||
acc = cur; | ||
} | ||
|
||
return acc; | ||
}, | ||
{ label: "", value: -1 }, | ||
); | ||
|
||
const { root, bar, barBg, barChart, labelText, square } = RecommendChartVariants(); | ||
|
||
return ( | ||
<div className={root({ className })} {...props}> | ||
<svg width={height} height={height} className={square({ label: maxLabel as RecommendLabelType })}> | ||
<text textAnchor="middle" dominantBaseline={"central"} transform={`translate(${height / 2}, ${height / 2})`}> | ||
{maxLabel} | ||
</text> | ||
</svg> | ||
<svg width={barChartWidth} className={barChart()}> | ||
{data.map((d, i) => { | ||
return ( | ||
<g key={`bar-${i}`}> | ||
<rect | ||
width={valueScale(total)} | ||
height={labelScale.bandwidth()} | ||
y={labelScale(d.label.toString())} | ||
rx={6} | ||
className={barBg()} | ||
/> | ||
<rect | ||
width={valueScale(d.value)} | ||
height={labelScale.bandwidth()} | ||
y={labelScale(d.label)} | ||
rx={6} | ||
className={bar({ label: maxLabel as RecommendLabelType, isMaxValue: maxValue === d.value })} | ||
/> | ||
</g> | ||
); | ||
})} | ||
</svg> | ||
<BandAxis | ||
axisScale={labelScale} | ||
orient="RIGHT" | ||
width={bandAxisWidth} | ||
height={height} | ||
className={labelText()} | ||
lineHide | ||
/> | ||
</div> | ||
); | ||
}; | ||
|
||
export default RecommendChart; |
16 changes: 16 additions & 0 deletions
16
src/components/charts/RecommendChart/RecommendChart.types.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import type { VariantProps } from "tailwind-variants"; | ||
import type { ComponentPropsWithInnerRef } from "@customTypes/utilType"; | ||
import { BarChartDataType } from "@charts/BarChart"; | ||
import { RecommendChartVariants } from "./RecommendChart.styles"; | ||
|
||
export type RecommendLabelType = "적극 매수" | "매수" | "보유" | "매도" | "적극 매도"; | ||
|
||
type RecommendChartBaseProps = { | ||
data: BarChartDataType[]; | ||
width: number; | ||
height: number; | ||
}; | ||
|
||
export type RecommendChartProps = ComponentPropsWithInnerRef<"div"> & | ||
VariantProps<typeof RecommendChartVariants> & | ||
RecommendChartBaseProps; |
65 changes: 65 additions & 0 deletions
65
src/components/charts/RecommendChart/__test__/RecommendChart.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
/* eslint-disable testing-library/no-node-access */ | ||
|
||
/* eslint-disable testing-library/no-container */ | ||
import { composeStories } from "@storybook/react"; | ||
import { render } from "@testing-library/react"; | ||
import { describe, expect, it } from "vitest"; | ||
import * as stories from "../RecommendChart.stories"; | ||
|
||
describe("recommend chart", () => { | ||
const { Default, ActiveBuy, ActiveSell, Buy, Hold, Sell } = composeStories(stories); | ||
|
||
it("에러 없이 렌더링", () => { | ||
render(<Default />); | ||
}); | ||
|
||
it("10개의 rect 렌더링", () => { | ||
const { container } = render(<Default />); | ||
|
||
const bars = container.querySelectorAll("rect"); | ||
|
||
expect(bars.length).toBe(10); | ||
}); | ||
|
||
it("적극 매수 데이터에서 적극 매수 텍스트를 보여줘야합니다.", () => { | ||
const { container } = render(<ActiveBuy />); | ||
|
||
const squareLabel = container.querySelector("svg > text"); | ||
|
||
expect(squareLabel).toHaveTextContent("적극 매수"); | ||
}); | ||
|
||
it("매수 데이터에서 매수 텍스트를 보여줘야합니다.", () => { | ||
const { container } = render(<Buy />); | ||
|
||
const squareLabel = container.querySelector("svg > text"); | ||
|
||
expect(squareLabel).toHaveTextContent("매수"); | ||
expect(squareLabel).not.toHaveTextContent("적극 매수"); | ||
}); | ||
|
||
it("보유 데이터에서 보유 텍스트를 보여줘야합니다.", () => { | ||
const { container } = render(<Hold />); | ||
|
||
const squareLabel = container.querySelector("svg > text"); | ||
|
||
expect(squareLabel).toHaveTextContent("보유"); | ||
}); | ||
|
||
it("매도 데이터에서 매도 텍스트를 보여줘야합니다.", () => { | ||
const { container } = render(<Sell />); | ||
|
||
const squareLabel = container.querySelector("svg > text"); | ||
|
||
expect(squareLabel).toHaveTextContent("매도"); | ||
expect(squareLabel).not.toHaveTextContent("적극 매도"); | ||
}); | ||
|
||
it("적극 매도 데이터에서 적극 매도 텍스트를 보여줘야합니다.", () => { | ||
const { container } = render(<ActiveSell />); | ||
|
||
const squareLabel = container.querySelector("svg > text"); | ||
|
||
expect(squareLabel).toHaveTextContent("적극 매도"); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import RecommendChart from "./RecommendChart"; | ||
import { RecommendChartVariants } from "./RecommendChart.styles"; | ||
import type { RecommendChartProps, RecommendLabelType } from "./RecommendChart.types"; | ||
|
||
export type { RecommendChartProps, RecommendLabelType }; | ||
|
||
export { RecommendChartVariants }; | ||
|
||
export default RecommendChart; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters