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

feat(TokenEnterAmount): Add new Enter Amount component and useEnterAmount hook #6242

Merged
merged 13 commits into from
Nov 26, 2024

Conversation

sviderock
Copy link
Contributor

@sviderock sviderock commented Nov 18, 2024

Description

1/5 PR for new Enter Amount component. This PR implements a new component but doesn't use it anywhere. The tests are absent as this component will be covered with its usages in the follow-up PRs.

This is only done to reduce amount of lines to review per PR as at the end this whole feature is about 1k new lines and -1k of removed lines.

Test plan

Omit tests for now but in the follow-up PRs the following test files will be testing this component:

  • EnterAmount.test.tsx
  • EarnEnterAmount.test.tsx
  • SwapScreen.test.tsx

There will also be some test files that are gonna be fixed alongside. Please, see the follow-up PRs for more details.

Related issues

Backwards compatibility

Yes

Network scalability

If a new NetworkId and/or Network are added in the future, the changes in this PR will:

  • Continue to work without code changes, OR trigger a compilation error (guaranteeing we find it when a new network is added)

Copy link

codecov bot commented Nov 19, 2024

Codecov Report

Attention: Patch coverage is 92.41379% with 11 lines in your changes missing coverage. Please review.

Project coverage is 88.97%. Comparing base (3516a13) to head (2e0adc1).
Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/components/TokenEnterAmount.tsx 92.41% 11 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #6242      +/-   ##
==========================================
+ Coverage   88.94%   88.97%   +0.02%     
==========================================
  Files         738      739       +1     
  Lines       31498    31643     +145     
  Branches     5838     5890      +52     
==========================================
+ Hits        28016    28153     +137     
- Misses       3282     3290       +8     
  Partials      200      200              
Files with missing lines Coverage Δ
src/components/TokenEnterAmount.tsx 92.41% <92.41%> (ø)

... and 1 file with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 3516a13...2e0adc1. Read the comment docs.

@sviderock sviderock marked this pull request as ready for review November 19, 2024 08:24
Comment on lines 78 to 84
const formattedInputValue = useMemo(() => {
const number = groupNumber(inputValue)
.replaceAll('.', decimalSeparator)
.replaceAll('group', groupingSeparator)
if (amountType === 'token') return number
return number !== '' ? `${localCurrencySymbol}${number}` : ''
}, [inputValue, amountType, localCurrencySymbol])
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I figured its easier to add currency sign before whatever is typed in the amount field rather than trying to parse it while having that sign and manage all the use-cases when it whether includes the sign (when typing in fiat) or not (when typing in crypto).

}
}

return (
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kudos to @kathaypacific for implementing 95% of this component 🚀

Copy link
Collaborator

@kathaypacific kathaypacific left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💥 looking great!! i had just a few small questions and suggestions for tests :)

inputValue: '1',
tokenAmount: '1',
localAmount: '$0.1',
amountType: 'token' as AmountEnteredIn,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
amountType: 'token' as AmountEnteredIn,
amountType: 'token' as const,

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kathaypacific considering this is more for the intellisense purpose I would keep it as is!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm not sure i understand - explicit casting like this is generally dangerous because 'blah' as AmountEnteredIn will not produce any errors whereas 'blah' as const will

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kathaypacific but calling intellisense on the empty string will provide you available options of token and local

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i see, so we are prioritising the experience of writing this value. my concern comes down to as SomeType is a way to ignore all the static type checking functionality that Typescript gives us, so I try to avoid it. if the allowed values of AmountEnteredIn changes then this test will not be flagged, but i guess at runtime the tests will fail (slower time to uncover the problem). we've been quite inconsistent with casting using the type or const though so it's also fine by me to keep it like this

toggleAmountType: mockToggleAmountType,
onOpenTokenPicker: mockOnOpenTokenPicker,
editable: true,
testID: 'tokenEnterAmount',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

super nit: i think the pattern is to use TitleCase for test id's

Suggested change
testID: 'tokenEnterAmount',
testID: 'TokenEnterAmount',

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kathaypacific Sorry, this was the part generated by Chat GPT so I just kept it unchanged 🤦
Will change!

testID: 'tokenEnterAmount',
}

it('renders without crashing', () => {
Copy link
Collaborator

@kathaypacific kathaypacific Nov 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: i think that this test is unnecessary, the scenario is already covered by the following tests (since the input to the render method is the same, and asserting on elements already ensures that the component has successfully rendered without errors)

<TokenEnterAmount {...defaultProps} />
</Provider>
)
expect(getByTestId('tokenEnterAmount/TokenName')).toBeTruthy()
Copy link
Collaborator

@kathaypacific kathaypacific Nov 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
expect(getByTestId('tokenEnterAmount/TokenName')).toBeTruthy()
expect(getByTestId('tokenEnterAmount/TokenName')).toHaveTextContent('CELO on Celo Alfajores')

expect(getByTestId('tokenEnterAmount/TokenSelect')).toBeTruthy()
expect(getByTestId('tokenEnterAmount/TokenBalance')).toBeTruthy()
expect(getByTestId('tokenEnterAmount/TokenBalance').props.children.props.i18nKey).toBe(
'tokenEnterAmount.availableBalance'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if it's possible we should avoid digging into props since then we test implementation details rather than user outcomes...could instead consider doing something like

expect(getByTestId('tokenEnterAmount/TokenBalance')).toHaveTextContent(
      'tokenEnterAmount.availableBalance'
    )
    expect(getByTestId('tokenEnterAmount/TokenBalance')).toHaveTextContent('5.00 CELO')

?

Copy link
Contributor Author

@sviderock sviderock Nov 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kathaypacific This makes sense! I've tried initially with getByText but for some reason for this particular test it couldn't find an element with that text (probably, cause it was including multiple nested Text tags so it wasn't able to match it). But your suggestion might work! Will try it out.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works!

const input = getByTestId('tokenEnterAmount/TokenAmountInput')
const converted = getByTestId('tokenEnterAmount/ExchangeAmount')
expect(input.props.value).toBe('1')
expect(converted.props.children).toBe(`${APPROX_SYMBOL} $0.1`)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
expect(converted.props.children).toBe(`${APPROX_SYMBOL} $0.1`)
expect(converted).toHaveTextContent(`${APPROX_SYMBOL} $0.1`)

)
const input = getByTestId('tokenEnterAmount/TokenAmountInput')

expect(input.props.editable).toBe(false)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
expect(input.props.editable).toBe(false)
expect(input).toBeDisabled()

</Provider>
)
const exchangeAmount = getByTestId('tokenEnterAmount/ExchangeAmount')
expect(exchangeAmount.props.children).toBe(`${APPROX_SYMBOL} $0.1`)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
expect(exchangeAmount.props.children).toBe(`${APPROX_SYMBOL} $0.1`)
expect(exchangeAmount).toHaveTextContent(`${APPROX_SYMBOL} $0.1`)

const BORDER_RADIUS = 12

function groupNumber(value: string) {
return value.replace(/\B(?=(\d{3})+(?!\d))(?<!\.\d*)/g, 'group')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we please add a comment about what this regex does? 🙈 i would definitely benefit from this, i don't speak regex fluently

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kathaypacific I've refactored this function a bit in the next PR and added comments explaining how it works!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, i wish i hadn't spent time trying to understand this implementation then....😅 going forward it'd be helpful to consider not including changes that will be undone in subsequent PRs or adding a note about it :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kathaypacific I'm sorry, definitely my bad! I should've put this component and the new useEnterAmount hook into a single PR. Sorry!

const formattedInputValue = useMemo(() => {
const number = groupNumber(inputValue)
.replaceAll('.', decimalSeparator)
.replaceAll('group', groupingSeparator)
Copy link
Collaborator

@kathaypacific kathaypacific Nov 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we add a couple of tests around the expected outcome of this formatting function? i think i may be missing something, but i'm surprised that we can freely use '.' here - if the inputValue is 1.234.567,888, would this function still work? (unsure if this is a real scenario and if consumers of this component will pass in formatted values, but this formatting convention is real)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kathaypacific you're right! It was a bug that I've revealed once I was working on a useEnterAmount hook in the next PR. I've refactored this part as well so it should be resolved there!

In order to not keep these issues in the codebase - I would wait for the next PR to also get approved and will merge them both in a batch!

Copy link
Collaborator

@kathaypacific kathaypacific left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✨🚀

expect(getByText('CELO on Celo Alfajores')).toBeTruthy()
expect(getByTestId('TokenEnterAmount/SwitchTokens')).toBeTruthy()
expect(getByTestId('TokenEnterAmount/TokenSelect')).toBeTruthy()
expect(getByTestId('TokenEnterAmount/TokenBalance')).toBeTruthy()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: you can remove this line, since we are asserting on it toHaveTextContent next. there might be other places where you can omit the toBeTruthy() check too

@sviderock sviderock added this pull request to the merge queue Nov 25, 2024
@sviderock sviderock removed this pull request from the merge queue due to a manual request Nov 25, 2024
### Description
2/5 PR for new Enter Amount component. This PR introduces
`useEnterAmount` hook that uses the very same logic `EnterAmount.tsx`
and `EarnEnterAmount.tsx` uses for handling:
- text input management (regex check, onChange, setting the right value
for the right type etc.)
- whenever data changes - creates a derived state with all the necessary
data for any processing currently present (can be extended in the
future)

This change is supposed to reduce a lot of boilerplate once it is used
in the corresponding flows (please, check the follow-up PRs).

### Test plan
No tests for now. Tests will be present starting the next (3/5) PR which
is related to applying this hook and new component to the Send flow.

### Related issues

- Relates to RET-1208
- 
### Backwards compatibility
Yes

### Network scalability

If a new NetworkId and/or Network are added in the future, the changes
in this PR will:

- [x] Continue to work without code changes, OR trigger a compilation
error (guaranteeing we find it when a new network is added)
@sviderock sviderock changed the title feat(TokenEnterAmount): Add new Enter Amount component feat(TokenEnterAmount): Add new Enter Amount component and useEnterAmount hook Nov 26, 2024
@sviderock sviderock added this pull request to the merge queue Nov 26, 2024
Merged via the queue into main with commit 8aa9921 Nov 26, 2024
16 checks passed
@sviderock sviderock deleted the slava/add-new-enter-amount-component branch November 26, 2024 14:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants