-
Notifications
You must be signed in to change notification settings - Fork 273
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
6fe3ec1
commit c9c7c3e
Showing
31 changed files
with
551 additions
and
372 deletions.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@coinbase/onchainkit': patch | ||
--- | ||
|
||
**feat**: Implement the fund button integrated with Coinbase Onramp. By @steveviselli-cb #1322 |
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,9 @@ | ||
import { FundButton } from '@coinbase/onchainkit/fund'; | ||
|
||
export default function FundDemo() { | ||
return ( | ||
<div className="mx-auto grid w-1/2 gap-8"> | ||
<FundButton /> | ||
</div> | ||
); | ||
} |
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,93 @@ | ||
import '@testing-library/jest-dom'; | ||
import { fireEvent, render, screen } from '@testing-library/react'; | ||
import { type Mock, afterEach, describe, expect, it, vi } from 'vitest'; | ||
import { openPopup } from '../../internal/utils/openPopup'; | ||
import { useGetFundingUrl } from '../hooks/useGetFundingUrl'; | ||
import { getFundingPopupSize } from '../utils/getFundingPopupSize'; | ||
import { FundButton } from './FundButton'; | ||
|
||
vi.mock('../hooks/useGetFundingUrl', () => ({ | ||
useGetFundingUrl: vi.fn(), | ||
})); | ||
|
||
vi.mock('../utils/getFundingPopupSize', () => ({ | ||
getFundingPopupSize: vi.fn(), | ||
})); | ||
|
||
vi.mock('../../internal/utils/openPopup', () => ({ | ||
openPopup: vi.fn(), | ||
})); | ||
|
||
describe('WalletDropdownFundLink', () => { | ||
afterEach(() => { | ||
vi.clearAllMocks(); | ||
}); | ||
|
||
it('renders the fund button with the fundingUrl prop when it is defined', () => { | ||
const fundingUrl = 'https://props.funding.url'; | ||
const { height, width } = { height: 200, width: 100 }; | ||
(getFundingPopupSize as Mock).mockReturnValue({ height, width }); | ||
|
||
render(<FundButton fundingUrl={fundingUrl} />); | ||
|
||
expect(useGetFundingUrl).not.toHaveBeenCalled(); | ||
const buttonElement = screen.getByRole('button'); | ||
expect(screen.getByText('Fund')).toBeInTheDocument(); | ||
|
||
fireEvent.click(buttonElement); | ||
expect(getFundingPopupSize as Mock).toHaveBeenCalledWith('md', fundingUrl); | ||
expect(openPopup as Mock).toHaveBeenCalledWith({ | ||
url: fundingUrl, | ||
height, | ||
width, | ||
target: undefined, | ||
}); | ||
}); | ||
|
||
it('renders the fund button with the default fundingUrl when the fundingUrl prop is undefined', () => { | ||
const fundingUrl = 'https://default.funding.url'; | ||
const { height, width } = { height: 200, width: 100 }; | ||
(useGetFundingUrl as Mock).mockReturnValue(fundingUrl); | ||
(getFundingPopupSize as Mock).mockReturnValue({ height, width }); | ||
|
||
render(<FundButton />); | ||
|
||
expect(useGetFundingUrl).toHaveBeenCalled(); | ||
const buttonElement = screen.getByRole('button'); | ||
|
||
fireEvent.click(buttonElement); | ||
expect(getFundingPopupSize as Mock).toHaveBeenCalledWith('md', fundingUrl); | ||
expect(openPopup as Mock).toHaveBeenCalledWith({ | ||
url: fundingUrl, | ||
height, | ||
width, | ||
target: undefined, | ||
}); | ||
}); | ||
|
||
it('renders a disabled fund button when no funding URL is provided and the default cannot be fetched', () => { | ||
(useGetFundingUrl as Mock).mockReturnValue(undefined); | ||
|
||
render(<FundButton />); | ||
|
||
expect(useGetFundingUrl).toHaveBeenCalled(); | ||
const buttonElement = screen.getByRole('button'); | ||
expect(buttonElement).toHaveClass('pointer-events-none'); | ||
|
||
fireEvent.click(buttonElement); | ||
expect(openPopup as Mock).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it('renders the fund button as a link when the openIn prop is set to tab', () => { | ||
const fundingUrl = 'https://props.funding.url'; | ||
const { height, width } = { height: 200, width: 100 }; | ||
(getFundingPopupSize as Mock).mockReturnValue({ height, width }); | ||
|
||
render(<FundButton fundingUrl={fundingUrl} openIn="tab" />); | ||
|
||
expect(useGetFundingUrl).not.toHaveBeenCalled(); | ||
const linkElement = screen.getByRole('link'); | ||
expect(screen.getByText('Fund')).toBeInTheDocument(); | ||
expect(linkElement).toHaveAttribute('href', fundingUrl); | ||
}); | ||
}); |
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,86 @@ | ||
import { useCallback } from 'react'; | ||
import { addSvg } from '../../internal/svg/addSvg'; | ||
import { openPopup } from '../../internal/utils/openPopup'; | ||
import { cn, color, pressable, text } from '../../styles/theme'; | ||
import { useGetFundingUrl } from '../hooks/useGetFundingUrl'; | ||
import type { FundButtonReact } from '../types'; | ||
import { getFundingPopupSize } from '../utils/getFundingPopupSize'; | ||
|
||
export function FundButton({ | ||
className, | ||
disabled = false, | ||
fundingUrl, | ||
hideIcon = false, | ||
hideText = false, | ||
openIn = 'popup', | ||
popupSize = 'md', | ||
rel, | ||
target, | ||
text: buttonText = 'Fund', | ||
}: FundButtonReact) { | ||
// If the fundingUrl prop is undefined, fallback to our recommended funding URL based on the wallet type | ||
const fundingUrlToRender = fundingUrl ?? useGetFundingUrl(); | ||
const isDisabled = disabled || !fundingUrlToRender; | ||
|
||
const handleClick = useCallback( | ||
(e: React.MouseEvent) => { | ||
e.preventDefault(); | ||
if (fundingUrlToRender) { | ||
const { height, width } = getFundingPopupSize( | ||
popupSize, | ||
fundingUrlToRender, | ||
); | ||
openPopup({ | ||
url: fundingUrlToRender, | ||
height, | ||
width, | ||
target, | ||
}); | ||
} | ||
}, | ||
[fundingUrlToRender, popupSize, target], | ||
); | ||
|
||
const classNames = cn( | ||
pressable.primary, | ||
'rounded-xl px-4 py-3', | ||
'inline-flex items-center justify-center space-x-2 disabled', | ||
isDisabled && pressable.disabled, | ||
text.headline, | ||
color.inverse, | ||
className, | ||
); | ||
|
||
const buttonContent = ( | ||
<> | ||
{/* h-6 is to match the icon height to the line-height set by text.headline */} | ||
{hideIcon || <span className="flex h-6 items-center">{addSvg}</span>} | ||
{hideText || <span>{buttonText}</span>} | ||
</> | ||
); | ||
|
||
if (openIn === 'tab') { | ||
return ( | ||
<a | ||
className={classNames} | ||
href={fundingUrlToRender} | ||
// If openIn is 'tab', default target to _blank so we don't accidentally navigate in the current tab | ||
target={target ?? '_blank'} | ||
rel={rel} | ||
> | ||
{buttonContent} | ||
</a> | ||
); | ||
} | ||
|
||
return ( | ||
<button | ||
className={classNames} | ||
onClick={handleClick} | ||
type="button" | ||
disabled={isDisabled} | ||
> | ||
{buttonContent} | ||
</button> | ||
); | ||
} |
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
Oops, something went wrong.