Skip to content

Commit

Permalink
feat(Amount): add new props
Browse files Browse the repository at this point in the history
  • Loading branch information
snitin315 committed Jan 21, 2024
1 parent a625d15 commit 7624c0b
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 30 deletions.
4 changes: 2 additions & 2 deletions packages/blade/src/components/Amount/Amount.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Title } from '@storybook/addon-docs';
import type { AmountProps } from './Amount';
import { Amount as AmountComponent } from './Amount';
import type { AmountHeadingProps, AmountDisplayProps, AmountBodyProps } from './amountTokens';
import { currencyPrefixMapping } from './amountTokens';
import { currencyIndicatorMapping } from './amountTokens';
import { getStyledPropsArgTypes } from '~components/Box/BaseBox/storybookArgTypes';
import BaseBox from '~components/Box/BaseBox';
import { Sandbox } from '~utils/storybook/Sandbox';
Expand Down Expand Up @@ -176,7 +176,7 @@ HumanizeSuffix.args = {
HumanizeSuffix.storyName = 'Humanize Suffix';

const AmountCurrencyTemplate: StoryFn<typeof AmountComponent> = (args) => {
const values = Object.keys(currencyPrefixMapping);
const values = Object.keys(currencyIndicatorMapping);

return (
<BaseBox justifyContent="flex-start" maxHeight="300px" overflowY="auto">
Expand Down
106 changes: 81 additions & 25 deletions packages/blade/src/components/Amount/Amount.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import type { AmountTypeProps, Currency } from './amountTokens';
import {
normalAmountSizes,
getCurrencyAbbreviations,
currencyPrefixMapping,
currencyIndicatorMapping,
subtleFontSizes,
amountLineHeights,
} from './amountTokens';
import { StyledAmountWrapper } from './StyledAmountWrapper';
import type { BaseTextProps } from '~components/Typography/BaseText/types';
import BaseBox from '~components/Box/BaseBox';
import type { TestID } from '~utils/types';
Expand All @@ -19,7 +20,6 @@ import { assignWithoutSideEffects } from '~utils/assignWithoutSideEffects';
import { throwBladeError } from '~utils/logger';
import { objectKeysWithType } from '~utils/objectKeysWithType';
import { BaseText } from '~components/Typography/BaseText';
import { Text } from '~components/Typography';
import type { FontFamily } from '~tokens/global';

type AmountCommonProps = {
Expand Down Expand Up @@ -52,13 +52,31 @@ type AmountCommonProps = {
* @default 'currency-symbol'
*/
currencyIndicator?: 'currency-symbol' | 'currency-code';
/**
* Determines the position of the currency indicator.
*
* @default 'left'
*/
currencyPosition?: 'left' | 'right';
/**
* The currency of the amount. Note that this component
* only displays the provided value in the specified currency, it does not perform any currency conversion.
*
* @default 'INR'
* */
currency?: Currency;
/**
* Determines the position of the denomination.
*
* @default 'right'
*/
denominationPosition?: 'left' | 'right';
/**
* If true, the amount text will have a line through it.
*
* @default false
*/
isStrikethrough?: boolean;
} & TestID &
StyledPropsBlade;

Expand Down Expand Up @@ -90,6 +108,7 @@ const AmountValue = ({
weight = 'regular',
amountValueColor,
isAffixSubtle,
isStrikethrough,
suffix,
}: AmountValue): ReactElement => {
const isReactNative = getPlatformType() === 'react-native';
Expand All @@ -99,11 +118,14 @@ const AmountValue = ({
const integer = value.split('.')[0];
const decimal = value.split('.')[1];

// Native does not support alignItems of Text inside a div, insted we need to wrap is in a Text
const AmountWrapper = getPlatformType() === 'react-native' ? Text : React.Fragment;

return (
<AmountWrapper>
<StyledAmountWrapper
isStrikethrough={isStrikethrough}
color={amountValueColor}
fontWeight={weight}
fontSize={normalAmountSizes[type][size]}
lineHeight={amountLineHeights[type][size]}
>
<BaseText
fontSize={normalAmountSizes[type][size]}
fontWeight={weight}
Expand All @@ -124,7 +146,7 @@ const AmountValue = ({
>
{decimal || '00'}
</BaseText>
</AmountWrapper>
</StyledAmountWrapper>
);
}
return (
Expand All @@ -134,6 +156,7 @@ const AmountValue = ({
fontFamily={numberFontFamily}
color={amountValueColor}
lineHeight={amountLineHeights[type][size]}
textDecorationLine={isStrikethrough ? 'line-through' : 'none'}
>
{value}
</BaseText>
Expand All @@ -159,37 +182,53 @@ export const addCommas = (amountValue: number, currency: Currency, decimalPlaces
* ie: for INR 2000 => 2K
* for MYR 2000000 => 2M
*/
export const getHumanizedAmount = (amountValue: number, currency: Currency): string => {
export const getHumanizedAmount = ({
value,
currency,
denominationPosition,
}: {
value: number;
currency: Currency;
denominationPosition?: AmountCommonProps['denominationPosition'];
}): string => {
let amountValue = value;
const abbreviations = getCurrencyAbbreviations(currency);

const abbreviation = abbreviations.find((abbr) => amountValue >= abbr.value);

if (abbreviation) {
amountValue = amountValue / abbreviation.value;
const formattedAmountValue = getFlooredFixed(amountValue, 2);
return addCommas(formattedAmountValue, currency) + abbreviation.symbol;
} else {
return amountValue.toString();

if (denominationPosition === 'right') {
return `${addCommas(formattedAmountValue, currency)}${abbreviation.symbol}`;
}

return `${abbreviation.symbol}${addCommas(formattedAmountValue, currency)}`;
}

return amountValue.toString();
};

type FormatAmountWithSuffixType = {
suffix: AmountProps['suffix'];
value: number;
currency: Currency;
denominationPosition?: AmountCommonProps['denominationPosition'];
};

export const formatAmountWithSuffix = ({
suffix,
value,
currency,
denominationPosition,
}: FormatAmountWithSuffixType): string => {
switch (suffix) {
case 'decimals': {
const decimalNumber = getFlooredFixed(value, 2);
return addCommas(decimalNumber, currency, 2);
}
case 'humanize': {
return getHumanizedAmount(value, currency);
return getHumanizedAmount({ value, currency, denominationPosition });
}
case 'none': {
return addCommas(getFlooredFixed(value, 0), currency);
Expand All @@ -206,10 +245,13 @@ const _Amount = ({
size = 'medium',
weight = 'regular',
isAffixSubtle = true,
isStrikethrough = false,
color,
currencyIndicator = 'currency-symbol',
testID,
currencyPosition = 'left',
denominationPosition = 'right',
currency = 'INR',
testID,
...styledProps
}: AmountProps): ReactElement => {
if (__DEV__) {
Expand Down Expand Up @@ -252,8 +294,8 @@ const _Amount = ({
}
}

const currencyPrefix = currencyPrefixMapping[currency][currencyIndicator];
const renderedValue = formatAmountWithSuffix({ suffix, value, currency });
const currencySymbolOrCode = currencyIndicatorMapping[currency][currencyIndicator];
const renderedValue = formatAmountWithSuffix({ suffix, value, currency, denominationPosition });
const { amountValueColor } = getTextColorProps({
color,
});
Expand All @@ -274,24 +316,38 @@ const _Amount = ({
alignItems="baseline"
flexDirection="row"
>
<BaseText
marginRight="spacing.1"
fontWeight={weight}
fontSize={currencyFontSize}
color={amountValueColor}
as={isReactNative ? undefined : 'span'}
>
{currencyPrefix}
</BaseText>
{currencyPosition === 'left' && (
<BaseText
marginRight="spacing.1"
fontWeight={weight}
fontSize={currencyFontSize}
color={amountValueColor}
as={isReactNative ? undefined : 'span'}
>
{currencySymbolOrCode}
</BaseText>
)}
<AmountValue
value={renderedValue}
amountValueColor={amountValueColor}
type={type}
weight={weight}
size={size}
isAffixSubtle={isAffixSubtle}
isStrikethrough={isStrikethrough}
suffix={suffix}
/>
{currencyPosition === 'right' && (
<BaseText
marginLeft="spacing.1"
fontWeight={weight}
fontSize={currencyFontSize}
color={amountValueColor}
as={isReactNative ? undefined : 'span'}
>
{currencySymbolOrCode}
</BaseText>
)}
</BaseBox>
</BaseBox>
);
Expand Down
23 changes: 23 additions & 0 deletions packages/blade/src/components/Amount/StyledAmountWrapper.web.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import styled from 'styled-components';
import BaseBox from '~components/Box/BaseBox';
import getIn from '~utils/lodashButBetter/get';

const StyledAmountWrapper = styled(BaseBox)((props) => ({
display: 'inline-block',
position: 'relative',

...(props.isStrikethrough && {
'&:before': {
content: '""',
width: '100%',
height: '2px',
position: 'absolute',
top: '50%',
fontWeight: getIn(props.theme.typography.fonts.weight, props.fontWeight),
lineHeight: getIn(props.theme.typography.fonts.lineHeight, props.lineHeight),
backgroundColor: getIn(props.theme.colors, props.color),
},
}),
}));

export { StyledAmountWrapper };
6 changes: 3 additions & 3 deletions packages/blade/src/components/Amount/amountTokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ const amountLineHeights: Record<
};

// All the supported currency codes are taken from Razorpay's Merchant Dashboard codebase
const currencyPrefixMapping = {
const currencyIndicatorMapping = {
AED: { 'currency-symbol': 'د.إ', 'currency-code': 'AED' },
ALL: { 'currency-symbol': 'Lek', 'currency-code': 'ALL' },
AMD: { 'currency-symbol': '֏', 'currency-code': 'AMD' },
Expand Down Expand Up @@ -202,7 +202,7 @@ type CurrencyAbbreviation = {
symbol: string;
};

type Currency = keyof typeof currencyPrefixMapping;
type Currency = keyof typeof currencyIndicatorMapping;

const getCurrencyAbbreviations = (currency: Currency): CurrencyAbbreviation[] => {
if (currency === 'INR') {
Expand All @@ -224,7 +224,7 @@ export {
subtleFontSizes,
normalAmountSizes,
amountLineHeights,
currencyPrefixMapping,
currencyIndicatorMapping,
getCurrencyAbbreviations,
};

Expand Down

0 comments on commit 7624c0b

Please sign in to comment.