-
Notifications
You must be signed in to change notification settings - Fork 6
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
Add Price formatter and Vis comp #465
Merged
+280
−28
Merged
Changes from 10 commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
3d28a83
add price formatter helper
bthaile 5e73041
add token price UI component
bthaile b8b3a30
make sure ending part is not null when adding to price
bthaile ff425df
use new token price component to standardize displaying token price
bthaile 517e0e6
better vis on subscript of zero in fractional prices
bthaile b47f8fe
simplify tests, add docs to formatPrice
bthaile 1665dcd
remove unneeded flag. all price data is returned by /api/prices, no n…
bthaile 409cb1c
more generous adding prices to priceMap, use evmAddress and fall back…
bthaile 8d91d71
evmAddress is not on TokenInfo type, updating tokenlist.d.ts doesnt h…
bthaile 2b84363
fix merge conflict with dev branch
bthaile d409456
use correct scentific subscript notation
bthaile File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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,94 @@ | ||
import { describe, it, expect } from 'vitest'; | ||
|
||
import { formatPrice } from './formatTokenValue'; | ||
|
||
describe('formatPrice', () => { | ||
it('should handle zero', () => { | ||
const result = formatPrice(0, 4); | ||
expect(result).toEqual({ | ||
price: 0, | ||
formattedPrice: { leadingPart: '', zeroPart: null, endingPart: null }, | ||
}); | ||
}); | ||
|
||
it('should format numbers >= 1 with two decimal places', () => { | ||
const testCases = [ | ||
{ input: 1.23456, expected: '1.23' }, | ||
{ input: 123.456789, expected: '123.46' }, | ||
{ input: 1000, expected: '1000.00' }, | ||
]; | ||
|
||
testCases.forEach(({ input, expected }) => { | ||
const result = formatPrice(input, 4); | ||
expect(result).toEqual({ | ||
price: input, | ||
formattedPrice: { | ||
leadingPart: expected, | ||
zeroPart: null, | ||
endingPart: null, | ||
}, | ||
}); | ||
}); | ||
}); | ||
|
||
it('should format numbers < 1 based on threshold', () => { | ||
const testCases = [ | ||
{ | ||
input: 0.123456, | ||
threshold: 4, | ||
expected: { leadingPart: '0.12', zeroPart: null, endingPart: null }, | ||
}, | ||
{ | ||
input: 0.0001234, | ||
threshold: 4, | ||
expected: { leadingPart: '0.00012', zeroPart: null, endingPart: null }, | ||
}, | ||
{ | ||
input: 0.00001234, | ||
threshold: 4, | ||
expected: { leadingPart: '0.0', zeroPart: 3, endingPart: '12' }, | ||
}, | ||
{ | ||
input: 0.00000001234, | ||
threshold: 4, | ||
expected: { leadingPart: '0.0', zeroPart: 6, endingPart: '12' }, | ||
}, | ||
]; | ||
|
||
testCases.forEach(({ input, threshold, expected }) => { | ||
const result = formatPrice(input, threshold); | ||
expect(result).toEqual({ | ||
price: input, | ||
formattedPrice: expected, | ||
}); | ||
}); | ||
}); | ||
|
||
it('should respect different threshold values', () => { | ||
const testCases = [ | ||
{ | ||
input: 0.0001234, | ||
threshold: 3, | ||
expected: { leadingPart: '0.0', zeroPart: 2, endingPart: '12' }, | ||
}, | ||
{ | ||
input: 0.0001234, | ||
threshold: 5, | ||
expected: { leadingPart: '0.00012', zeroPart: null, endingPart: null }, | ||
}, | ||
{ | ||
input: 0.00000001234, | ||
threshold: 10, | ||
expected: { leadingPart: '0.000000012', zeroPart: null, endingPart: null }, | ||
}, | ||
]; | ||
|
||
testCases.forEach(({ input, threshold, expected }) => { | ||
const result = formatPrice(input, threshold); | ||
expect(result).toEqual({ | ||
price: input, | ||
formattedPrice: expected, | ||
}); | ||
}); | ||
}); | ||
}); |
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 @@ | ||
interface PriceParts { | ||
leadingPart: string; | ||
zeroPart: number | null; | ||
endingPart: string | null; | ||
} | ||
|
||
interface FormattedPrice { | ||
price: number; | ||
formattedPrice: PriceParts; | ||
} | ||
|
||
/** | ||
* Condenses the price to a more readable format. | ||
* @param price - The price to format. | ||
* @param zeroCondenseThreshold - The number of zeros to condense. example: 4 would condense 0.0000123 to 0.0(3)12. | ||
* First zero after decimal point is maintained for readability. | ||
* @returns The formatted price. | ||
*/ | ||
export function formatPrice(price: number, zeroCondenseThreshold = 4): FormattedPrice { | ||
if (price === 0 || price === null || price === undefined) { | ||
return { | ||
price, | ||
formattedPrice: { | ||
leadingPart: '', | ||
zeroPart: null, | ||
endingPart: null, | ||
}, | ||
}; | ||
} | ||
|
||
if (price >= 1) { | ||
return { | ||
price, | ||
formattedPrice: { | ||
leadingPart: price.toFixed(2), | ||
zeroPart: null, | ||
endingPart: null, | ||
}, | ||
}; | ||
} | ||
|
||
// Convert to non-scientific notation string | ||
const priceStr = price.toFixed(20); | ||
const parts = priceStr.split('.'); | ||
const decimal = parts.length > 1 ? parts[1] : '0'; | ||
|
||
// Count total zeros after the decimal point | ||
let totalZeros = 0; | ||
let firstNonZeroIndex = 0; | ||
|
||
for (let i = 0; i < decimal.length; i++) { | ||
if (decimal[i] === '0') { | ||
totalZeros++; | ||
} else { | ||
firstNonZeroIndex = i; | ||
break; | ||
} | ||
} | ||
|
||
// If we don't have enough total zeros to meet threshold, format with 2 significant digits | ||
if (totalZeros < zeroCondenseThreshold) { | ||
const significantPart = decimal.slice(firstNonZeroIndex, firstNonZeroIndex + 2); | ||
const formattedNumber = `0.${'0'.repeat(firstNonZeroIndex)}${significantPart}`; | ||
return { | ||
price, | ||
formattedPrice: { | ||
leadingPart: formattedNumber, | ||
zeroPart: null, | ||
endingPart: null, | ||
}, | ||
}; | ||
} | ||
|
||
// Break up the price into parts for condensed format | ||
// zeroPart should be totalZeros - 1 since first zero is in leadingPart | ||
const significantDigits = decimal.slice(firstNonZeroIndex, firstNonZeroIndex + 2); | ||
return { | ||
price, | ||
formattedPrice: { | ||
leadingPart: '0.0', | ||
zeroPart: totalZeros - 1, | ||
endingPart: significantDigits, | ||
}, | ||
}; | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import React from 'react'; | ||
|
||
import { formatPrice } from '@/shared/utils/formatTokenValue'; | ||
|
||
interface TokenPriceProps { | ||
value: number | string; | ||
className?: string; | ||
showPrefix?: boolean; | ||
prefix?: string; | ||
postFix?: string; | ||
} | ||
|
||
export const TokenPrice: React.FC<TokenPriceProps> = ({ | ||
value, | ||
className = '', | ||
prefix = '$', | ||
postFix = '', | ||
}) => { | ||
if (value === 0 || value === null || value === undefined) { | ||
return <span className={className}>{''}</span>; | ||
} | ||
|
||
// convert value to number if it's a string | ||
const valueNumber = typeof value === 'string' ? parseFloat(value) : value; | ||
|
||
const { formattedPrice } = formatPrice(valueNumber); | ||
const { leadingPart, zeroPart, endingPart } = formattedPrice; | ||
|
||
return ( | ||
<span className={className}> | ||
{prefix} | ||
<span style={leadingPart === '' ? { padding: '0 0.25rem' } : undefined}>{leadingPart}</span> | ||
{zeroPart !== null && ( | ||
<sub | ||
style={{ | ||
fontSize: '0.7em', | ||
verticalAlign: '-0.25em', | ||
}} | ||
> | ||
{zeroPart} | ||
</sub> | ||
)} | ||
{endingPart !== null && endingPart} | ||
{postFix && <span style={{ marginLeft: '0.25rem' }}>{postFix}</span>} | ||
</span> | ||
); | ||
}; |
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
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was this a bug?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I noticed that tokenInfo address was the cadence address and evmAddress was a property on TokenInfo. In effect it was a bug.