Skip to content

Commit

Permalink
SwitchCase 하위 타입스크립트/리액트 버전 타입 호환성을 위해 반환 타입 수정 (#677)
Browse files Browse the repository at this point in the history
* fix(react): SwitchCase 하위 타입스크립트/리액트 버전 타입 호환성을 위해 반환 타입 수정

* fix: formatNumberWithUnits 소수점 처리 개선
  • Loading branch information
ssi02014 authored Jan 13, 2025
1 parent 8b3f288 commit 41a4223
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 96 deletions.
5 changes: 5 additions & 0 deletions .changeset/large-carrots-share.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@modern-kit/react': patch
---

fix(react): SwitchCase 하위 타입스크립트/리액트 버전 타입 호환성을 위해 반환 타입 수정 - @ssi02014
23 changes: 11 additions & 12 deletions docs/docs/react/components/SwithCase.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,18 @@ import { SwitchCase } from '@modern-kit/react';

## Interface
```ts title="typescript"
type Component = Nullable<React.ReactNode>;

interface SwitchCaseProps<Condition extends string | number> {
condition: Condition | null | undefined;
cases: Partial<Record<Condition, Component>>;
defaultCaseComponent?: Component;
interface SwitchCaseProps<Case extends PropertyKey> {
value: Case | null | undefined;
caseBy: Record<Case, React.ReactNode>;
defaultComponent?: React.ReactNode;
}

const SwitchCase: <Condition extends string | number>({
condition,
cases,
defaultCaseComponent,
}: SwitchCaseProps<Condition>) => JSX.Element;
```
```ts title="typescript"
const SwitchCase: <Case extends PropertyKey>({
caseBy,
value,
defaultComponent,
}: SwitchCaseProps<Case>) => JSX.Element;
```

## Usage
Expand Down
16 changes: 15 additions & 1 deletion docs/docs/utils/formatter/formatNumberWithUnits.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

`숫자` 혹은 `숫자로 이루어진 문자열`을 주어진 `단위` 별로 포맷팅하는 함수입니다.

- 버림 단위(`floorUnit`)를 선택할 수 있습니다.
- 소수 일 경우 소수점 자리수(`decimal`)를 선택할 수 있습니다. 단, 버림 단위가 존재할 경우 소수 부분은 제거됩니다.
- 단위 사이 공백 추가 여부(`space`)를 선택할 수 있습니다.
- 쉼표 사용 여부(`commas`)를 선택할 수 있습니다.

<br />

## Code
Expand Down Expand Up @@ -80,12 +85,21 @@ formatNumberWithUnits(1234567, { units: KRW_UNITS, space: true }) // "123만 4,5
formatNumberWithUnits(1234567, { units: KRW_UNITS, space: false }) // "123만4,567"

// 쉼표 사용 여부 (기본값: true)
formatNumberWithUnits(1234567, { units: KRW_UNITS, commas: false }) // "123만 4567"
formatNumberWithUnits(1234567, { units: KRW_UNITS, commas: true }) // "123만 4,567"
formatNumberWithUnits(1234567, { units: KRW_UNITS, commas: false }) // "123만 4567"

// 버림 단위 (기본값: 1)
formatNumberWithUnits(1234567, { units: KRW_UNITS, floorUnit: 1000 }) // "123만 4000"
formatNumberWithUnits(1234567, { units: KRW_UNITS, floorUnit: 10000 }) // "123만"

// 소수점 자리수 (기본값: 0)
formatNumberWithUnits(1234567.123, { units: KRW_UNITS, decimal: 2 }) // "123만 4,567.12"

// floorUnit이 1000으로 설정되어 있어서 1000 단위 미만은 버림 처리되고,
// decimal이 2로 설정되어 있지만 floorUnit 단위 만큼 버림 처리 되었으므로 소수는 표시되지 않습니다.
formatNumberWithUnits(1234567.123, {
units: KRW_UNITS,
decimal: 2,
floorUnit: 1000
}) // "123만 4,000"
```
8 changes: 4 additions & 4 deletions packages/react/src/components/SwitchCase/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ interface SwitchCaseProps<Case extends PropertyKey> {
* @param {Record<Case, React.ReactNode>} props.caseBy - `value` 값에 대응하는 컴포넌트들을 담은 객체
* @param {React.ReactNode} props.defaultComponent - `value`가 `null`이거나 `caseBy`에 해당하는 컴포넌트가 없을 때 렌더링할 기본 컴포넌트
*
* @returns {React.ReactNode} - 조건부로 렌더링된 컴포넌트
* @returns {JSX.Element} - 조건부로 렌더링된 컴포넌트
*
* @example
* ```tsx
Expand All @@ -30,10 +30,10 @@ export const SwitchCase = <Case extends PropertyKey>({
caseBy,
value,
defaultComponent = null,
}: SwitchCaseProps<Case>): React.ReactNode => {
}: SwitchCaseProps<Case>): JSX.Element => {
if (isNil(value)) {
return defaultComponent;
return <>{defaultComponent}</>;
}

return caseBy[value] ?? defaultComponent;
return <>{caseBy[value] ?? defaultComponent}</>;
};
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@ describe('formatNumberWithUnits', () => {
formatNumberWithUnits(1234567.123, { decimal: 2, units: KRW_UNITS })
).toBe('123만 4,567.12');

// floorUnit이 주어지면 소수점 자리수를 적용하지 않습니다.
// floorUnit이 1000으로 설정되어 있어서 1000 단위 미만은 버림 처리되고,
// decimal이 2로 설정되어 있지만 floorUnit 단위 만큼 버림 처리 되었으므로 소수는 표시되지 않습니다.
expect(
formatNumberWithUnits(1234567.123, {
decimal: 2,
Expand All @@ -143,12 +144,6 @@ describe('formatNumberWithUnits', () => {
).toThrow('floorUnit은 1을 포함한 10의 제곱수여야 합니다.');
});

it('floorUnit가 value의 절대값보다 작으면 예외를 발생시킵니다.', () => {
expect(() =>
formatNumberWithUnits(1234567, { floorUnit: 1000000000 })
).toThrow('floorUnit 값은 value의 절대값보다 크거나 같아야 합니다.');
});

it('decimal이 0보다 작으면 예외를 발생시킵니다.', () => {
expect(() =>
formatNumberWithUnits(1234567, { decimal: -1 as unknown as number })
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import { formatNumberWithCommas } from '../../formatter/formatNumberWithCommas';
import { FormatNumberWithUnitsOptions } from './formatNumberWithUnits.types';

interface FormatOptions
extends Omit<Required<FormatNumberWithUnitsOptions>, 'units' | 'floorUnit'> {
value: number;
unit: string;
}

/**
* @description 쉼표 사용 여부에 따라 숫자를 포맷팅하는 함수
*
Expand All @@ -21,46 +15,6 @@ const formatNumberWithConditionalCommas = (
return commas ? formatNumberWithCommas(value) : String(value);
};

/**
* @description 남은 값을 포맷팅하는 내부 유틸 함수
*
* @param {FormatOptions} options - 포맷팅 옵션
* @param {number} options.decimal - 소수점 자리수
* @param {boolean} options.commas - 쉼표 사용 여부
* @returns {string} 포맷팅된 문자열
*/
const formatRemainingValue = ({
value,
decimal,
commas,
}: Omit<FormatOptions, 'space' | 'unit'>): string => {
const formattedValue = Number.isInteger(value)
? value
: value.toFixed(decimal);
return formatNumberWithConditionalCommas(formattedValue, commas);
};

/**
* @description 숫자에 단위를 추가하고 포맷팅하는 내부 유틸 함수
*
* @param {FormatOptions} options - 포맷팅 옵션
* @param {number} options.value - 포맷팅할 숫자
* @param {string} options.unit - 단위
* @param {boolean} options.commas - 쉼표 사용 여부
* @param {boolean} options.space - 단위 사이 공백 추가 여부
* @returns {string} 포맷팅된 문자열
*/
const formatUnitValue = ({
value,
unit,
commas,
space,
}: Omit<FormatOptions, 'decimal'>): string => {
return `${formatNumberWithConditionalCommas(value, commas)}${unit}${
space ? ' ' : ''
}`;
};

/**
* @description 주어진 단위(units)에 따라 숫자를 포맷팅하는 함수
*
Expand All @@ -80,42 +34,45 @@ export const getFormattedValueWithUnits = (

const absoluteValue = Math.abs(value);
const isNegative = value < 0;
const hasFloorUnit = floorUnit > 1;

let formattedResult = '';
let remainingValue = absoluteValue;

// value가 floorUnit(버림 단위)보다 작으면 '0'을 반환
// value의 절대값이 floorUnit(버림 단위)보다 작으면 '0'을 반환
if (absoluteValue < floorUnit) {
throw new Error('floorUnit 값은 value의 절대값보다 크거나 같아야 합니다.');
return '0';
}

// floorUnit이 1보다 큰 경우, 최종 결과에서 floorUnit 미만의 값은 버림
if (floorUnit > 1) {
if (hasFloorUnit) {
remainingValue = Math.floor(remainingValue / floorUnit) * floorUnit;
}

// unit 별로 나누기
for (let i = 0; i < units.length; i++) {
const { unit, value } = units[i];
const quotient = Math.floor(remainingValue / value);
const spaceToUse = space ? ' ' : '';

if (quotient <= 0) continue;

formattedResult += formatUnitValue({
value: quotient,
unit,
commas,
space,
});
formattedResult += `${formatNumberWithConditionalCommas(
quotient,
commas
)}${unit}${spaceToUse}`;
remainingValue %= value;
}

// 남은 remainingValue가 있으면 추가
if (remainingValue > 0) {
formattedResult += formatRemainingValue({
value: remainingValue,
decimal,
commas,
});
// floorUnit이 주어지고, 정수가 아니라면 소수점 자리수를 적용
const shouldApplyDecimal = !hasFloorUnit && !Number.isInteger(value);

formattedResult += formatNumberWithConditionalCommas(
remainingValue.toFixed(shouldApplyDecimal ? decimal : 0),
commas
);
}

// 음수일 경우 앞에 '-' 붙이며, 앞/뒤 공백 제거
Expand Down
22 changes: 10 additions & 12 deletions packages/utils/src/formatter/formatNumberWithUnits/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ import { FormatNumberWithUnitsOptions } from './formatNumberWithUnits.types';
/**
* @description `숫자` 혹은 `숫자로 이루어진 문자열`을 주어진 `단위` 별로 포맷팅하는 함수입니다.
*
* - 버림 단위(`floorUnit`)를 선택할 수 있습니다.
* - 소수 일 경우 소수점 자리수(`decimal`)를 선택할 수 있습니다. 단, 버림 단위가 존재할 경우 소수 부분은 제거됩니다.
* - 단위 사이 공백 추가 여부(`space`)를 선택할 수 있습니다.
* - 쉼표 사용 여부(`commas`)를 선택할 수 있습니다.
*
* @param {number | string} value - 포맷팅할 숫자 또는 숫자로 이루어진 문자열
* @param {FormatNumberWithUnitsOptions} options - 포맷팅 옵션
* @param {Unit[]} [options.units=DEFAULT_UNITS] - 사용할 단위 배열(조,억,만). 직접 정의해서 사용할 수 있습니다.
Expand Down Expand Up @@ -34,23 +39,21 @@ import { FormatNumberWithUnitsOptions } from './formatNumberWithUnits.types';
*
* @example
* // 쉼표 사용 여부 (기본값: true)
* formatNumberWithUnits(1234567, { units: KRW_UNITS, commas: false }) // "123만 4567"
* formatNumberWithUnits(1234567, { units: KRW_UNITS, commas: true }) // "123만 4,567"
* formatNumberWithUnits(1234567, { units: KRW_UNITS, commas: false }) // "123만 4567"
*
* @example
* // 버림 단위 (기본값: 1)
* formatNumberWithUnits(1234567, { units: KRW_UNITS, floorUnit: 1000 }) // "123만 4000"
* formatNumberWithUnits(1234567, { units: KRW_UNITS, floorUnit: 10000 }) // "123만"
*
* @example
* // 소수점 자리수 (기본값: 0)
* formatNumberWithUnits(1234567.123, { units: KRW_UNITS, decimal: 2 }) // "123만 4,567.12"
*
* // floorUnit이 1보다 크면 소수점 자리수를 적용하지 않습니다.
* formatNumberWithUnits(1234567.123, {
* units: KRW_UNITS,
* decimal: 3,
* floorUnit: 1000
* }) // "123만 4,000"
* // floorUnit이 1000으로 설정되어 있어서 1000 단위 미만은 버림 처리되고,
* // decimal이 2로 설정되어 있지만 floorUnit 단위 만큼 버림 처리 되었으므로 소수는 표시되지 않습니다.
* formatNumberWithUnits(1234567.123, { units: KRW_UNITS, decimal: 2, floorUnit: 1000 }) // "123만 4,000"
*/
export function formatNumberWithUnits(
value: number | string,
Expand Down Expand Up @@ -86,11 +89,6 @@ export function formatNumberWithUnits(
throw new Error('decimal은 0 이상의 정수여야 합니다.');
}

// 0일 경우 바로 반환
if (valueToUse === 0) {
return '0';
}

// 포맷팅
return getFormattedValueWithUnits(valueToUse, {
units: sortedUnits,
Expand Down

0 comments on commit 41a4223

Please sign in to comment.