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

SwitchCase 하위 타입스크립트/리액트 버전 타입 호환성을 위해 반환 타입 수정 #677

Merged
merged 2 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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
Loading