import type { Meta, StoryObj } from '@storybook/react' import React from 'react' import { viewports } from '../../utils/viewports' import { Modal, ModalContent, ModalTitle } from '../Modal' import { TransactionalModalTemplate } from './TransactionalModalTemplate' import type { TransactionModalState } from './types' import { initialState, reducer } from './useTransactionalModal' const meta: Meta = { title: 'Blocks/TransactionalModal', component: TransactionalModalTemplate, parameters: { chromatic: { viewports: viewports.all }, layout: 'centered', }, } export default meta type Story = StoryObj const baseState: TransactionModalState = { currentStepIndex: 1, nextStepIndex: 2, error: undefined, isTransactionComplete: false, progressBarSteps: [ { label: 'Label', status: 'DONE' }, { label: 'Label', status: 'ACTIVE' }, { label: 'Label', status: 'PENDING' }, ], steps: [ { id: 'step-1', label: 'Initiating' }, { id: 'step-2', label: 'Transaction title', description: 'Transaction description', }, { id: 'step-3', label: 'Completed' }, ], successHeading: 'Transaction complete', successBody: 'Everything was processed.', context: 'transaction-modal', closeButtonText: 'Close', } const txHash = '012378E2f3b02dCC8A00c420b99Ae06a474A1327' const closeHiddenState: TransactionModalState = { ...baseState, steps: baseState.steps.map((step) => step.id === 'step-2' ? { ...step, isCloseHidden: true } : step, ), } const longStepLabelsState: TransactionModalState = { ...baseState, currentStepIndex: 1, nextStepIndex: 2, progressBarSteps: [ { label: 'Approve registration request cancellation', status: 'DONE' }, { label: 'Receive Log Trigger upkeep edit confirmation', status: 'ACTIVE' }, { label: 'Await native token transfer confirmation', status: 'PENDING' }, ], steps: [ { id: 'step-1', label: 'Approve registration request cancellation' }, { id: 'step-2', label: 'Receive Log Trigger upkeep edit confirmation', description: 'Waiting for on-chain confirmation for the requested operation.', }, { id: 'step-3', label: 'Await native token transfer confirmation' }, ], } const singleTransactionHashOnlyState: TransactionModalState = { ...baseState, transactions: [{ hash: txHash }], } const typedTransactionsState: TransactionModalState = { ...baseState, transactions: [ { hash: txHash, type: '10 Testnet ETH' }, { hash: txHash, type: '10 Testnet LINK' }, ], successLinkText: 'View transaction', successLinkHref: '/details', } const successWithMultipleTransactionsState: TransactionModalState = { ...typedTransactionsState, isTransactionComplete: true, currentStepIndex: 2, progressBarSteps: [ { label: 'Label', status: 'DONE' }, { label: 'Label', status: 'DONE' }, { label: 'Label', status: 'DONE' }, ], successHeading: 'Transaction title', successBody: 'Transaction description', } const renderTemplate = ( state: TransactionModalState, isInescapable = false, { onCloseClick, onSuccessClick, }: { onCloseClick?: VoidFunction onSuccessClick?: VoidFunction } = {}, ) => ( Transactional Modal undefined)} onCloseClick={onCloseClick ?? (() => undefined)} stepId={state.steps[state.currentStepIndex]?.id ?? 'step'} contextId={state.context} isInescapable={isInescapable} /> ) const animatedTransactionConfig = { steps: [ { id: 'approve-registration', label: 'Approve upkeep registration', description: 'Confirm the LINK transfer and upkeep registration in your wallet.', }, { id: 'wait-confirmation', label: 'Receive onchain confirmation', description: 'Waiting for the registry and billing transactions to be confirmed.', }, { id: 'sync-dashboard', label: 'Sync dashboard state', description: 'Refreshing your upkeep details so the new registration appears in the UI.', }, ], successHeading: 'Upkeep registration complete', successBody: 'Your upkeep is now registered and the billing transactions were confirmed successfully.', context: 'storybook-animated-transaction', } const getAnimatedInitialState = () => reducer(initialState, { type: 'START_TRANSACTION', ...animatedTransactionConfig, }) const AnimatedFlowTemplate = () => { const [state, dispatch] = React.useReducer( reducer, undefined, getAnimatedInitialState, ) const [runId, setRunId] = React.useState(0) React.useEffect(() => { dispatch({ type: 'START_TRANSACTION', ...animatedTransactionConfig }) const showSignedTransactions = setTimeout(() => { dispatch({ type: 'NEXT_STEP', transactions: [ { hash: txHash, type: '5 LINK billing approval' }, { hash: `${txHash}9`, type: 'Automation upkeep registration' }, ], }) }, 1500) const finishFlow = setTimeout(() => { dispatch({ type: 'COMPLETE_TRANSACTION', successLinkText: 'View upkeep', successLinkHref: '/automation/upkeeps/123', closeButtonText: 'Animate Again', }) }, 3200) return () => { clearTimeout(showSignedTransactions) clearTimeout(finishFlow) } }, [runId]) return renderTemplate(state, true, { onCloseClick: () => { if (state.isTransactionComplete) { setRunId((currentRunId) => currentRunId + 1) } }, }) } export const Default: Story = { render: () => renderTemplate({ ...baseState, transactions: [ { hash: txHash, type: 'Request' }, { hash: txHash, type: 'Request' }, ], successLinkText: 'Label', successLinkHref: '/details', }), } export const DefaultInescapable: Story = { render: () => renderTemplate( { ...baseState, transactions: [{ hash: txHash, type: 'Request' }], }, true, ), } export const DefaultCloseHidden: Story = { render: () => renderTemplate({ ...closeHiddenState, transactions: [ { hash: txHash, type: 'Request' }, { hash: txHash, type: 'Request' }, ], successLinkText: 'Label', successLinkHref: '/details', }), } export const ErrorState: Story = { render: () => renderTemplate({ ...baseState, error: 'Transaction failed.', progressBarSteps: [ { label: 'Label', status: 'DONE' }, { label: 'Label', status: 'ERROR' }, { label: 'Label', status: 'PENDING' }, ], transactions: [{ hash: txHash, type: 'Request' }], }), } export const SuccessState: Story = { render: () => renderTemplate({ ...successWithMultipleTransactionsState, successLinkText: 'Label', successLinkHref: '/details', }), } export const LongStepLabels: Story = { render: () => renderTemplate({ ...longStepLabelsState, transactions: [{ hash: txHash, type: 'Request' }], successLinkText: 'Label', successLinkHref: '/details', }), } export const SingleTransactionHashOnly: Story = { render: () => renderTemplate(singleTransactionHashOnlyState), } export const SingleTransactionHashOnlyInescapable: Story = { render: () => renderTemplate( { ...closeHiddenState, transactions: [{ hash: txHash }], }, true, ), } export const ErrorWithTransactionType: Story = { render: () => renderTemplate({ ...typedTransactionsState, error: 'Something went real bad...', progressBarSteps: [ { label: 'Label', status: 'DONE' }, { label: 'Label', status: 'ERROR' }, { label: 'Label', status: 'PENDING' }, ], }), } export const SuccessStateTerminal: Story = { render: () => renderTemplate(successWithMultipleTransactionsState, true), } export const AnimatedFlow: Story = { parameters: { chromatic: { disableSnapshot: true }, }, render: () => , }