refactor(portfolio): formatPriceTime util + PriceSessionBadge 추출 + 테스트
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHXzpsZQxKG9hQmNRfZjRS
This commit is contained in:
@@ -5,26 +5,7 @@ import {
|
||||
Tooltip as ChartTooltip,
|
||||
} from 'recharts';
|
||||
import { formatNumber, formatPercent, toNumeric, profitColorClass, numFitClass } from '../stockUtils';
|
||||
|
||||
const formatPriceTime = (iso) => {
|
||||
if (!iso) return '';
|
||||
const d = new Date(iso);
|
||||
if (Number.isNaN(d.getTime())) return '';
|
||||
return `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`;
|
||||
};
|
||||
|
||||
const PriceSessionBadge = ({ session, asOf }) => {
|
||||
if (session !== 'NXT_AFTER' && session !== 'NXT_PRE') return null;
|
||||
const isPre = session === 'NXT_PRE';
|
||||
const label = isPre ? 'NXT 프리' : 'NXT';
|
||||
const desc = isPre ? 'NXT 프리마켓 거래가' : 'NXT 야간거래 (15:30~20:00)';
|
||||
const time = formatPriceTime(asOf);
|
||||
return (
|
||||
<span className="pf-nxt-badge" title={time ? `${desc} · ${time}` : desc}>
|
||||
{label}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
import PriceSessionBadge from './portfolio/PriceSessionBadge';
|
||||
|
||||
const PortfolioTab = ({ pf, asset, handleSell, handleSaveSnapshot }) => (
|
||||
<>
|
||||
|
||||
17
src/pages/stock/components/portfolio/PriceSessionBadge.jsx
Normal file
17
src/pages/stock/components/portfolio/PriceSessionBadge.jsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import React from 'react';
|
||||
import { formatPriceTime } from './portfolioUtils';
|
||||
|
||||
const PriceSessionBadge = ({ session, asOf }) => {
|
||||
if (session !== 'NXT_AFTER' && session !== 'NXT_PRE') return null;
|
||||
const isPre = session === 'NXT_PRE';
|
||||
const label = isPre ? 'NXT 프리' : 'NXT';
|
||||
const desc = isPre ? 'NXT 프리마켓 거래가' : 'NXT 야간거래 (15:30~20:00)';
|
||||
const time = formatPriceTime(asOf);
|
||||
return (
|
||||
<span className="pf-nxt-badge" title={time ? `${desc} · ${time}` : desc}>
|
||||
{label}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export default PriceSessionBadge;
|
||||
@@ -0,0 +1,17 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import PriceSessionBadge from './PriceSessionBadge.jsx';
|
||||
|
||||
describe('PriceSessionBadge', () => {
|
||||
it('NXT_AFTER → "NXT", NXT_PRE → "NXT 프리"', () => {
|
||||
const { unmount } = render(<PriceSessionBadge session="NXT_AFTER" />);
|
||||
expect(screen.getByText('NXT')).toBeInTheDocument();
|
||||
unmount();
|
||||
render(<PriceSessionBadge session="NXT_PRE" />);
|
||||
expect(screen.getByText('NXT 프리')).toBeInTheDocument();
|
||||
});
|
||||
it('그 외 세션 → 렌더 없음', () => {
|
||||
const { container } = render(<PriceSessionBadge session="REGULAR" />);
|
||||
expect(container).toBeEmptyDOMElement();
|
||||
});
|
||||
});
|
||||
6
src/pages/stock/components/portfolio/portfolioUtils.js
Normal file
6
src/pages/stock/components/portfolio/portfolioUtils.js
Normal file
@@ -0,0 +1,6 @@
|
||||
export const formatPriceTime = (iso) => {
|
||||
if (!iso) return '';
|
||||
const d = new Date(iso);
|
||||
if (Number.isNaN(d.getTime())) return '';
|
||||
return `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`;
|
||||
};
|
||||
14
src/pages/stock/components/portfolio/portfolioUtils.test.js
Normal file
14
src/pages/stock/components/portfolio/portfolioUtils.test.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { formatPriceTime } from './portfolioUtils.js';
|
||||
|
||||
describe('formatPriceTime', () => {
|
||||
it('유효 ISO → HH:MM (제로패드)', () => {
|
||||
expect(formatPriceTime('2026-07-10T09:05:00')).toBe('09:05');
|
||||
expect(formatPriceTime('2026-07-10T18:30:00')).toBe('18:30');
|
||||
});
|
||||
it('falsy/invalid → 빈 문자열', () => {
|
||||
expect(formatPriceTime('')).toBe('');
|
||||
expect(formatPriceTime(null)).toBe('');
|
||||
expect(formatPriceTime('not-a-date')).toBe('');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user