diff --git a/web/cashtab-components/README.md b/web/cashtab-components/README.md index f0a5690ae..bc8302a66 100644 --- a/web/cashtab-components/README.md +++ b/web/cashtab-components/README.md @@ -1,206 +1,206 @@ # Cashtab React Components Interact with the Cashtab wallet to support eCash payments in web apps. Download the Cashtab extension [here](https://chrome.google.com/webstore/detail/cashtab/obldfcmebhllhjlhjbnghaipekcppeag) This project is based on [badger-components-react](https://github.com/Bitcoin-com/badger-components-react) ## Notes Some features from `badger-components-react` are not yet fully supported. Upcoming features: - [ ] SLPA transactions - [ ] OP Return text - [ ] successFn and failureFn props - [ ] Bip70-style invoices - [ ] Websocket monitoring of payment status # Build on eCash (XEC) A set of React components and helpers to integrate eCash (XEC) and tokens into your app with ease. Integrates with the Cashtab wallet. ## Get Started - [Homepage](https://e.cash/) - [Component Showcase](https://laughing-villani-8cfcaf.netlify.app/) - [Cashtab Extension](https://chrome.google.com/webstore/detail/cashtab/obldfcmebhllhjlhjbnghaipekcppeag) - [NPM page](https://www.npmjs.com/package/cashtab-components) ### Install Component ```bash $ npm install --save cashtab-components ``` ### Install Peer Dependencies This library depends on the following three peer dependencies - `styled-components` ^4.4.1 ```bash $ npm install --save styled-components@4.4.1 ``` ### Add to React Project ```js import { CashtabButton, CashtabBadge } from 'cashtab-components'; const Example = props => { // eatBCH bitcoin cash address const toAddress = 'bitcoincash:pp8skudq3x5hzw8ew7vzsw8tn4k8wxsqsv0lt0mf3g'; // Random SLP address const toSLPAddress = 'simpleledger:qq6qcjt6xlkeqzdwkhdvfyl2q2d2wafkgg8phzcqez'; // tokenId const nakamotoID = 'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb'; return ( <> {/* Minimal Examples */} - {/* Price in bcha */} - - + {/* Price in XEC */} + + {/* Price in SLP tokens - NAKAMOTO in this example */} {/* More Complex Examples, pricing in fiat */} console.log('Payment success callback')} failFn={() => console.warn('Payment failed or cancelled callback') } /> console.log('success example function called')} failFn={() => console.log('fail example function called')} /> - {/* Pricing in BCHA */} + {/* Pricing in XEC */} ); }; export default Example; ``` ### Create a Custom Cashtab Button / Integration ```js import React from 'react' import { CashtabBase, formatAmount } from 'cashtab-components' import styled from 'styled-components' const CoolButton = styled.button` background-color: rebeccapurple; color: lime; border-radius: 24px; ` const MyButton extends React.Component { render() { // Props from higher order component const { handleClick, to, step, price, currency, coinType, coinDecimals, coinSymbol, amount, showQR, isRepeatable, repeatTimeout, watchAddress, } = this.props; return (

Donate {price}{currency} to {to}

Satoshis: {formatAmount(amount, coinDecimals)}

Custom looking button with render
) } } // Wrap with CashtabBase higher order component export default CashtabBase(MyButton); ``` ### Control Step from app When accepting payments, the state of the payment should be handled by the backend of your application. As such, you can pass in `stepControlled` with the values of `fresh`, `pending` or `complete` to indicate which part of the payment the user is on. ## Development with Storybook To develop additions to this project, run the local storybook development server with ### Setup ```bash $ npm i $ npm run storybook ``` Navigate to [http://localhost:9001](http://localhost:9001) to view your stories. They automatically update as you develop ✨. Storybook will pick up stories from the `*stories.tsx` file in each components folder. To build a static version of storybook for deployment ```bash $ npm run build-storybook Deploy contents of `/storybook-static` ``` diff --git a/web/cashtab-components/src/components/CashtabBadge/CashtabBadge.stories.tsx b/web/cashtab-components/src/components/CashtabBadge/CashtabBadge.stories.tsx index a31985cc3..3abf2298c 100644 --- a/web/cashtab-components/src/components/CashtabBadge/CashtabBadge.stories.tsx +++ b/web/cashtab-components/src/components/CashtabBadge/CashtabBadge.stories.tsx @@ -1,109 +1,109 @@ import { Meta, Story } from '@storybook/react/types-6-0'; import CashtabBadge from './CashtabBadge'; import type { CashtabBadgeProps } from './CashtabBadge'; import { currencyOptions } from '../../utils/currency-helpers'; import Ticker from '../../atoms/Ticker'; // [ SPICE, NAKAMOTO, DOGECASH, BROC ] const tokenIdOptions = [ '4de69e374a8ed21cbddd47f2338cc0f479dc58daa2bbe11cd604ca488eca0ddf', 'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb', '3916a24a051f8b3833a7fd128be51dd93015555ed9142d6106ec03267f5cdc4c', '259908ae44f46ef585edef4bcc1e50dc06e4c391ac4be929fae27235b8158cf1', ]; const Template: Story = (args: CashtabBadgeProps) => ( ); export const Standard = Template.bind({}); Standard.args = { price: 0.05, currency: 'USD', to: 'ecash:qrcl220pxeec78vnchwyh6fsdyf60uv9tca7668slm', }; export const MostProps = Template.bind({}); MostProps.args = { price: 0.0025, currency: 'GBP', to: 'ecash:qrcl220pxeec78vnchwyh6fsdyf60uv9tca7668slm', isRepeatable: true, repeatTimeout: 4000, text: 'My Cash Button', showAmount: true, showBorder: true, showQR: false, }; export const Minimal = Template.bind({}); Minimal.args = { amount: 0.01, to: 'ecash:qrcl220pxeec78vnchwyh6fsdyf60uv9tca7668slm', showAmount: false, showQR: true, }; export const Fiat = Template.bind({}); Fiat.args = { price: 3.5, currency: 'CAD', text: 'Pay with Cashtab', to: 'ecash:qrcl220pxeec78vnchwyh6fsdyf60uv9tca7668slm', }; Fiat.storyName = 'price in fiat'; -export const BCHA = Template.bind({}); -BCHA.args = { +export const XEC = Template.bind({}); +XEC.args = { coinType: Ticker.coinSymbol, amount: 0.33, to: 'ecash:qrcl220pxeec78vnchwyh6fsdyf60uv9tca7668slm', }; -BCHA.storyName = `price in ${Ticker.coinSymbol}`; +XEC.storyName = `price in ${Ticker.coinSymbol}`; export const SLPA = Template.bind({}); SLPA.args = { coinType: Ticker.tokenTicker, tokenId: tokenIdOptions[0], amount: 100, to: 'etoken:qrcl220pxeec78vnchwyh6fsdyf60uv9tcnqnc3hmv', showQR: true, }; SLPA.storyName = `price in ${Ticker.tokenTicker}`; export const StepControlled = Template.bind({}); StepControlled.args = { amount: 0.012, to: 'ecash:qrcl220pxeec78vnchwyh6fsdyf60uv9tca7668slm', stepControlled: 'fresh', }; StepControlled.storyName = `Controlled Step`; export default { title: 'CashtabBadge', component: CashtabBadge, argTypes: { currency: { control: { type: 'select', options: currencyOptions, }, }, tokenId: { control: { type: 'select', options: tokenIdOptions, }, }, stepControlled: { control: { type: 'select', options: ['fresh', 'pending', 'complete'], }, }, }, } as Meta; diff --git a/web/cashtab-components/src/components/CashtabButton/CashtabButton.stories.tsx b/web/cashtab-components/src/components/CashtabButton/CashtabButton.stories.tsx index 4f4e53887..558bdcac7 100644 --- a/web/cashtab-components/src/components/CashtabButton/CashtabButton.stories.tsx +++ b/web/cashtab-components/src/components/CashtabButton/CashtabButton.stories.tsx @@ -1,109 +1,109 @@ import { Meta, Story } from '@storybook/react/types-6-0'; import CashtabButton from './CashtabButton'; import type { CashtabButtonProps } from './CashtabButton'; import { currencyOptions } from '../../utils/currency-helpers'; import Ticker from '../../atoms/Ticker'; // [ SPICE, NAKAMOTO, DOGECASH, BROC ] const tokenIdOptions = [ '4de69e374a8ed21cbddd47f2338cc0f479dc58daa2bbe11cd604ca488eca0ddf', 'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb', '3916a24a051f8b3833a7fd128be51dd93015555ed9142d6106ec03267f5cdc4c', '259908ae44f46ef585edef4bcc1e50dc06e4c391ac4be929fae27235b8158cf1', ]; const Template: Story = (args: CashtabButtonProps) => ( ); export const Standard = Template.bind({}); Standard.args = { price: 0.05, currency: 'USD', to: 'ecash:qrcl220pxeec78vnchwyh6fsdyf60uv9tca7668slm', }; export const MostProps = Template.bind({}); MostProps.args = { price: 0.0025, currency: 'GBP', to: 'ecash:qrcl220pxeec78vnchwyh6fsdyf60uv9tca7668slm', isRepeatable: true, repeatTimeout: 4000, text: 'My Cash Button', showAmount: true, showBorder: true, showQR: false, }; export const Minimal = Template.bind({}); Minimal.args = { amount: 0.01, to: 'ecash:qrcl220pxeec78vnchwyh6fsdyf60uv9tca7668slm', showAmount: false, showQR: true, }; export const Fiat = Template.bind({}); Fiat.args = { price: 3.5, currency: 'CAD', text: 'Pay with Cashtab', to: 'ecash:qrcl220pxeec78vnchwyh6fsdyf60uv9tca7668slm', }; Fiat.storyName = 'price in fiat'; -export const BCHA = Template.bind({}); -BCHA.args = { +export const XEC = Template.bind({}); +XEC.args = { coinType: Ticker.coinSymbol, amount: 0.33, to: 'ecash:qrcl220pxeec78vnchwyh6fsdyf60uv9tca7668slm', }; -BCHA.storyName = `price in ${Ticker.coinSymbol}`; +XEC.storyName = `price in ${Ticker.coinSymbol}`; export const SLPA = Template.bind({}); SLPA.args = { coinType: Ticker.tokenTicker, tokenId: tokenIdOptions[0], amount: 100, to: 'etoken:qrcl220pxeec78vnchwyh6fsdyf60uv9tcnqnc3hmv', showQR: true, }; SLPA.storyName = `price in ${Ticker.tokenTicker}`; export const StepControlled = Template.bind({}); StepControlled.args = { amount: 0.012, to: 'ecash:qrcl220pxeec78vnchwyh6fsdyf60uv9tca7668slm', stepControlled: 'fresh', }; StepControlled.storyName = `Controlled Step`; export default { title: 'CashtabButton', component: CashtabButton, argTypes: { currency: { control: { type: 'select', options: currencyOptions, }, }, tokenId: { control: { type: 'select', options: tokenIdOptions, }, }, stepControlled: { control: { type: 'select', options: ['fresh', 'pending', 'complete'], }, }, }, } as Meta; diff --git a/web/cashtab-components/src/components/PriceDisplay/README.md b/web/cashtab-components/src/components/PriceDisplay/README.md index 5cbe80422..f0be03401 100644 --- a/web/cashtab-components/src/components/PriceDisplay/README.md +++ b/web/cashtab-components/src/components/PriceDisplay/README.md @@ -1,7 +1,7 @@ # PriceDisplay -Show a price display item in fiat, BCHA, or SLPA. +Show a price display item in fiat, XEC, or SLPA. For open BIP70 invoices, get price and currency info from the invoice. Used in CashtabBadge and CashtabButton. Useful tool when building custom Cashtab integrations. diff --git a/web/cashtab-components/src/utils/__tests__/cashtab-helpers.test.ts b/web/cashtab-components/src/utils/__tests__/cashtab-helpers.test.ts index 947546a23..426cdedbe 100644 --- a/web/cashtab-components/src/utils/__tests__/cashtab-helpers.test.ts +++ b/web/cashtab-components/src/utils/__tests__/cashtab-helpers.test.ts @@ -1,33 +1,33 @@ import { buildPriceEndpoint, priceToSatoshis, adjustAmount, } from '../cashtab-helpers'; describe('Helper functions match expected performance', () => { it('Component uses expected price endpoint', () => { expect(buildPriceEndpoint('USD')).toBe( 'https://api.coingecko.com/api/v3/simple/price?ids=bitcoin-cash-abc-2&vs_currencies=USD&include_last_updated_at=true', ); }); - it('Correctly returns BCHA amount in satoshis given (1) fiat price of BCHA and (2) fiat amount of requested satoshis', () => { + it('Correctly returns XEC amount in satoshis given (1) fiat price of XEC and (2) fiat amount of requested satoshis', () => { expect(priceToSatoshis(20, 10)).toBe(50000000); }); - it('Converts amount in satoshis as number to amount in BCHA as string', () => { + it('Converts amount in satoshis as number to amount in XEC as string', () => { expect(adjustAmount(50000000, 8, true)).toBe('0.5'); }); - it('Converts amount in BCHA as number to amount in satoshis as string', () => { + it('Converts amount in XEC as number to amount in satoshis as string', () => { expect(adjustAmount(0.5, 8, false)).toBe('50000000'); }); it('Converts amount in base unit of arbitrary decimal places as number to amount in as string', () => { expect(adjustAmount(50000000, 10, true)).toBe('0.005'); }); it('Converts amount with arbitrary decimal places as number to amount as base unit (no decimal places) as string', () => { expect(adjustAmount(0.005, 10, false)).toBe('50000000'); }); });