mirror of
https://github.com/luxfi/safe-wallet.git
synced 2026-10-30 15:19:21 +00:00
* docs: add specification for shadcn storybook migration
Add comprehensive planning documents for design system migration:
- spec.md: Feature specification with 5 user stories
- plan.md: Implementation plan with technical context
- research.md: MSW, Chromatic, and story pattern findings
- data-model.md: Component inventory schema
- tasks.md: 117 implementation tasks organized by user story
- quickstart.md: Developer guide for creating stories
- contracts/: Story templates for UI, common, and page components
This establishes the foundation for systematic Storybook coverage
to enable designer collaboration on the shadcn migration.
* feat: add MSW fixture infrastructure for Storybook stories
## MSW Infrastructure
- Add fixture-based handlers using real API data from staging CGW
- Create fixtures for balances, portfolio, positions, safes, chains
- Support 5 scenarios: efSafe, vitalik, spamTokens, safeTokenHolder, empty
- Add feature flag testing via chain config overrides (POSITIONS, PORTFOLIO_ENDPOINT)
- Include fetch-fixtures.ts script to refresh fixture data
## Storybook Decorators
- Add LayoutDecorator for page-level stories with sidebar/header
- Add MockProviderDecorator for Redux/context providers
## Component Inventory Tool
- Create AST-based scanner for component discovery
- Add coverage tracking and dependency analysis
- Implement priority scoring for story creation order
- Add inventory and coverage-report CLI commands
## Documentation Updates
- Update plan.md, spec.md, tasks.md to reflect fixture-based approach
- Update quickstart.md with fixtureHandlers usage examples
* chore: complete Phase 4 MSW fixture expansion (User Story 2)
- Add Safe Apps fixtures fetched from staging CGW (mainnet.json)
- Add createSafeAppsHandlersFromFixture to MSW handlers
- Update createHandlersFromFixture to include Safe Apps by default
- Create dependency-audit.ts script for analyzing fixture coverage gaps
- Add msw-fixtures.md documentation for fixture system
- Fix inventory scanner path resolution for workspace execution
- Rename scripts to storybook:inventory, storybook:coverage, storybook:dependencies
Phase 4 checkpoint complete:
- Core fixtures: balances, portfolio, positions, safes, chains, safe-apps
- Utility handlers: transactions, auth, relay, messages, web3 RPC
- Scripts: storybook:inventory, storybook:coverage, storybook:dependencies
* docs: remove shadcn references, focus on existing component coverage
- Rename spec from "shadcn migration" to "Storybook Coverage Expansion"
- Remove all shadcn/ui component tasks (will be added in future phase)
- Expand Phase 5 tasks to cover existing MUI components by category
- Update component counts to match current inventory (330 total, 14 with stories)
- Fix Storybook version: 8.x → 10.x
- Add current coverage status section to tasks.md
* feat: add sidebar component stories (T042-T044)
- SafeHeaderInfo.stories.tsx: Default, Loading, LargeBalance, ZeroBalance,
MultipleOwners, Counterfactual, PolygonChain variants
- MultiAccountContextMenu.stories.tsx: Default, WithoutAddNetwork,
MultipleChains, LongName, NoName, TestnetOnly variants
- QrModal.stories.tsx: Default, WithChainPrefix, and multiple network
variants (Polygon, Arbitrum, Optimism, Base)
Phase 5.1 complete - sidebar stories now enable page-level stories.
* docs: add MSW best practices for Storybook stories
- Document regex URL patterns (wildcard strings unreliable in MSW v2)
- Add mswLoader requirement for docs mode
- Document RTK Query requirements (deployed: true, loaded: true)
- Update story template with MSW best practices and valid TS syntax
- Fix AssetsTable story to work with MSW fixtures
- Add mockServiceWorker.js to eslint ignores
- Fix MSW index exports to only include working fixtures
* feat: add real component stories for Dashboard, Assets, Overview, PendingTxs, SafeAppList, and OwnerList
- Dashboard stories render real Dashboard component directly (no mock layout)
- Overview stories include wallet connected and no wallet states
- OwnerList stories include ENS names and address book entries
- Assets and PendingTxs stories with realistic MSW fixtures
- SafeAppList stories with real app data
- Add localhost:6006 to OFFICIAL_HOSTS for Storybook logo display
- Fix LayoutDecorator structure and preview padding for fullscreen layouts
- Add CLAUDE.storybook.md with Storybook patterns knowledge base
* feat: add 12 family-based stories with full-page-first ordering
Add mockup-based stories for component families to achieve comprehensive
Storybook coverage without complex Redux/context dependencies:
Components:
- AddressBook: Full page, table, dialogs (create/edit/import/remove)
- Batch: Full UI, indicator, sidebar states
- NFTs: Full page, grid, loading/empty, preview modal, collections
- NotificationCenter: Bell with popover, notification variants
- SafeMessages: Full page, message types, signers, signing flow
- Welcome: Welcome screen and onboarding states
Features:
- Bridge: Full page, widget, disabled/in-progress states, history
- NoFeeCampaign: Dashboard banner, gas warning, eligibility states
- Proposers: Full list, chips, edit/delete dialogs
- Speedup: Modal, in-progress/success states, gas slider
- TargetedOutreach: Popup overlay, survey, announcement variants
- TxNotes: History view, input, character limit states
Documentation:
- Add "Story Export Order" section to quickstart.md documenting
that full-page/comprehensive stories should be exported first
to appear at the top of Storybook sidebar
* chore: update spec to family and page based stories
* feat: add 7 family-based stories for 100% top-level coverage
Add mockup-based stories with full-page-first pattern for:
- tx-flow: Token transfer, sign message, swap flows
- recovery: Recovery page, status variants, settings
- myAccounts: Account list, pinned safes, multi-chain
- new-safe: Create/load flows, owner configuration
- walletconnect: Connection form, sessions, proposals
- safe-shield: Security analysis, severity levels
- counterfactual: Activation flow, balance states
This completes Phase 10 T121-T128, achieving 100% coverage
of non-skipped top-level component groups.
* chore: update tasks.md with Phase 10 completion status
Mark T121-T128 as complete:
- T121: tx-flow stories
- T122: recovery stories
- T123: myAccounts stories
- T124: new-safe stories
- T125: walletconnect stories
- T127: safe-shield stories
- T128: counterfactual stories
Update coverage status:
- 36/41 groups covered (88%)
- 5 groups intentionally skipped
- 100% coverage of non-skipped groups achieved
* feat: add scrollable all-states stories with docs-style wrappers
Add AllStates/AllSteps stories as first export in each multi-step feature:
- NewSafe: CreateSafeAllSteps (4 steps)
- TxFlow: TokenTransferAllSteps (4 steps)
- WalletConnect: WalletConnectAllStates (6 states)
- Counterfactual: ActivationAllStates (6 states)
- SafeShield: SafeShieldAllStates (6 states)
- Recovery: RecoveryAllStates (5 states)
- MyAccounts: MyAccountsAllStates (6 states)
Each state wrapped with StateWrapper showing:
- State/step name heading
- Description of the state
- Grey background container with full UI
* tests: update story snapshots after all-states story additions
Update snapshots affected by new scrollable all-states stories:
- AssetsTable: story ordering changed
- DateTime: snapshot format update
- TxStatusChip: removed obsolete story snapshots
- HnSignupIntro: new snapshot
- SwapOrderConfirmationView: snapshot update
* chore: add storybook coverage generation script
* chore: update storybook coverage scripts and documentation
* chore: add tests for storybook scripts
* chore: deterministic output of storybook coverage by order and prettier
* chore: use emotion cache in storybook to align design themes of app and storybook
* chore: add full layout to dashboard story
* chore: tx queue to PendingTxsList
* chore: move story mocking to separate file
* chore: use createMockStory on bigger stories
* chore: use createMockStory in more story files
* chores: enable portfolio and positions feature on all dashboard stories
* chore(storybook): use default feature flags in all stories
- Remove explicit features: { portfolio: false, positions: false } overrides
- All stories now use DEFAULT_FEATURES (portfolio, positions, swaps enabled)
- Update Dashboard stories to use wallet: 'owner' for swap button visibility
- Update documentation to clarify feature flag defaults
* chore(storybook): add page stories and improve mock infrastructure
- Add stories for all pages in src/stories/pages/ directory
- Add spaces feature support with auth state and API mocks
- Add useIsOfficialHost mock for Storybook
- Add query parameter support to createMockStory
- Document decorator stacking behavior in AGENTS.md
- Remove Dashboard.stories.tsx (duplicate of home)
- Include .storybook in tsconfig.json for ESLint
* chore(storybook): add Chromatic visual regression testing
- Add GitHub Actions workflow for Chromatic on PRs and pushes to dev/main
- Configure Chromatic with TurboSnap for cost optimization
- Add light/dark theme modes for visual testing
- Fix Storybook build by mocking next/image loader that caused
"unsupported file type" errors with static image imports
* chore(storybook): enable junit report for Chromatic
* chore(storybook): add chromatic package and local run script
* fix(storybook): remove incompatible junitReport option from chromatic config
junitReport and exitOnceUploaded are incompatible options in Chromatic CLI.
* chore(storybook): auto-accept chromatic changes on dev and main
* fix(storybook): exclude SVGs from asset/resource rule for Chromatic
SVGs were being treated as asset URLs instead of React components,
causing createElement errors in Chromatic. Now SVGs are excluded
so they go through SVGR loader instead.
* chore(storybook): set chromatic viewport to 1280px for full-page layouts
* fix(storybook): deep merge safeInfo.data in story overrides
Allows partial overrides like `data: { deployed: false }` to work
correctly by spreading the base safeInfo data instead of replacing it.
* docs: add Chromatic visual regression testing section (#7081)
* docs: add Chromatic visual regression testing section
* chore: trigger chromatic workflow
* chore: trigger chromatic
* fix(storybook): remove viewports from chromatic config
Chromatic does not support viewports and modes on the same story.
Remove viewports config and rely on modes only for light/dark theme testing.
Also fix StoreOverrides type for safeInfo.data property access.
* fix(storybook): add emotion insertion point for correct CSS ordering
Add preview-head.html with emotion-insertion-point meta tag to ensure
MUI styles are loaded first in production builds. This fixes sidebar
styling issues in Chromatic snapshots.
Also update Home story to use safeTokenHolder scenario (smaller balance)
and add chromatic viewport modes.
* chore(storybook): optimize Chromatic snapshots
- Add theme toolbar switcher with light/dark mode toggle
- Disable Chromatic snapshots for feature documentation stories
- Add !chromatic tags to high-variance story variants
- Reduces redundant snapshots while maintaining visual coverage
* chore: fix prettier formatting in chromatic.config.json
---------
* chore(storybook): add Vite-based Storybook configuration
- Add .storybook-vite/ with Vite-specific config (main.ts, preview.tsx)
- Add custom Vite plugin for @/public/* imports (SVGs via SVGR, images as next/image-compatible objects)
- Add querystring polyfill mock for browser compatibility
- Rename storybook:full to storybook:lazy and make lazy compilation opt-in
- New commands: storybook (normal), storybook:lazy (with lazy compilation), storybook:vite (Vite build)
- Update tsconfig.json to include .storybook-vite files
* chore(chromatic): limit snapshots to 3 core page stories
Only snapshot Home, Balances, and Transactions/History pages
to reduce Chromatic usage during development.
* fix(storybook): add transaction handlers for queue and history pages
Add MSW handlers for transaction-related API endpoints:
- Transaction history list handler
- Transaction details handler with txData: null to avoid parsing errors
- Updated createMockStory to include transaction handlers
The txData is set to null because the Receipt component's data parsing
logic throws errors with mock transaction data structures.
* chore(chromatic): snapshot transaction queue instead of history
Update Chromatic config to capture the Queue page rather than History,
as it better demonstrates the transaction handling with pending items.
* docs: document transaction mocking limitations in AGENTS.md
Add a "Known Limitation" section noting that transaction page stories
have basic MSW handlers but txData mocking is incomplete. Expanding
transaction details may show errors or incomplete data. Further work
is needed to match the CGW response format expected by UI components.
* chore: remove unused storybook script references
Remove storybook:inventory, storybook:coverage, and storybook:dependencies
scripts that reference non-existent files. Only storybook:generate-coverage
is needed and points to the actual generate-storybook-coverage.ts file.
* fix(chromatic): remove onlyChanged flag conflicting with onlyStoryNames
Chromatic CLI doesn't allow both --only-changed and --only-story-names
to be used together. Since we want to limit snapshots to specific stories
to save credits, remove the onlyChanged option.
* tests(storybook): update snapshots for MUI class name changes
Update 196 snapshots to reflect MUI CSS class name changes
from css-* to mui-style-* prefix format.
* fix(chromatic): remove onlyChanged from workflow to use config file
The Chromatic action was passing onlyChanged: true which conflicts with
onlyStoryNames in chromatic.config.json. Remove from workflow since
config file settings take precedence.
* refactor(scripts): reduce cyclomatic complexity in storybook utilities
- Extract lookup tables (PACKAGE_DETECTORS, API_PATTERNS, CATEGORY_PATTERNS, EXPORT_EXTRACTORS) to replace conditional chains
- Add helper functions for isolated concerns (isExternalPackage, detectPackageTypes, categorizeImport, etc.)
- Use switch statements for type discrimination in formatCoverageSource
- Add Code Complexity Guidelines section to AGENTS.md
* refactor(scripts): extract helpers to reduce complexity in storybook utilities
- family.ts: Extract resolveStoryPath and extractStoryExportNames helpers
- generate-storybook-coverage.ts: Extract buildGroupInfoMap and determineCoverageType helpers
- fetch-fixtures.ts: Extract parseSafeArg, fetchSingleSafe, fetchAllFixtures helpers
- chromatic.config.json: Remove invalid externals option causing CI failure
* refactor(scripts): extract generateTopLevelSection helpers to fix CodeScene
Extract formatCoveredGroupRow, formatUncoveredGroupRow, formatSkippedGroupRow,
generateUncoveredGroupsSection, and generateSkippedGroupsSection helpers
to eliminate nested conditional logic in generateTopLevelSection.
---------
Co-authored-by: Hanzo Dev <[email protected]>
211 lines
5.2 KiB
TypeScript
211 lines
5.2 KiB
TypeScript
import { faker } from '@faker-js/faker'
|
|
import { createMockAddress } from './safeFactory'
|
|
|
|
/**
|
|
* Transaction mock data factory
|
|
*
|
|
* Generates mock transaction data for Storybook stories.
|
|
*/
|
|
|
|
export type MockTransactionInfo = {
|
|
type: 'Custom' | 'Transfer' | 'SettingsChange' | 'Creation'
|
|
to?: {
|
|
value: string
|
|
name: string | null
|
|
logoUri: string | null
|
|
}
|
|
dataSize?: string
|
|
value?: string
|
|
isCancellation?: boolean
|
|
methodName?: string | null
|
|
}
|
|
|
|
export type MockTransactionDetails = {
|
|
txInfo: MockTransactionInfo
|
|
safeAddress: string
|
|
txId: string
|
|
txStatus: 'AWAITING_CONFIRMATIONS' | 'AWAITING_EXECUTION' | 'SUCCESS' | 'FAILED' | 'CANCELLED'
|
|
executedAt: number | null
|
|
txHash: string | null
|
|
detailedExecutionInfo?: {
|
|
type: 'MULTISIG'
|
|
nonce: number
|
|
confirmationsRequired: number
|
|
confirmationsSubmitted: number
|
|
confirmations: Array<{
|
|
signer: { value: string }
|
|
signature: string
|
|
submittedAt: number
|
|
}>
|
|
}
|
|
}
|
|
|
|
export type MockQueuedTransaction = {
|
|
type: 'TRANSACTION' | 'LABEL' | 'CONFLICT_HEADER'
|
|
transaction?: MockTransactionDetails
|
|
label?: string
|
|
nonce?: number
|
|
}
|
|
|
|
/**
|
|
* Create a mock transaction ID
|
|
*/
|
|
export const createMockTxId = (safeAddress?: string, safeTxHash?: string): string => {
|
|
const safe = safeAddress ?? createMockAddress()
|
|
const hash = safeTxHash ?? faker.string.hexadecimal({ length: 64, prefix: '0x' })
|
|
return `multisig_${safe}_${hash}`
|
|
}
|
|
|
|
/**
|
|
* Create a mock transaction hash
|
|
*/
|
|
export const createMockTxHash = (): string => faker.string.hexadecimal({ length: 64, prefix: '0x' })
|
|
|
|
/**
|
|
* Create mock transaction info
|
|
*/
|
|
export const createMockTransactionInfo = (overrides?: Partial<MockTransactionInfo>): MockTransactionInfo => ({
|
|
type: 'Custom',
|
|
to: {
|
|
value: createMockAddress(),
|
|
name: 'Test Contract',
|
|
logoUri: null,
|
|
},
|
|
dataSize: '100',
|
|
value: '0',
|
|
isCancellation: false,
|
|
methodName: null,
|
|
...overrides,
|
|
})
|
|
|
|
/**
|
|
* Create mock transaction details
|
|
*/
|
|
export const createMockTransactionDetails = (overrides?: Partial<MockTransactionDetails>): MockTransactionDetails => {
|
|
const safeAddress = overrides?.safeAddress ?? createMockAddress()
|
|
|
|
return {
|
|
txInfo: createMockTransactionInfo(),
|
|
safeAddress,
|
|
txId: createMockTxId(safeAddress),
|
|
txStatus: 'AWAITING_CONFIRMATIONS',
|
|
executedAt: null,
|
|
txHash: null,
|
|
...overrides,
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create mock confirmation
|
|
*/
|
|
export const createMockConfirmation = (signerAddress?: string) => ({
|
|
signer: { value: signerAddress ?? createMockAddress() },
|
|
signature: faker.string.hexadecimal({ length: 130, prefix: '0x' }),
|
|
submittedAt: Date.now(),
|
|
})
|
|
|
|
/**
|
|
* Create mock detailed execution info
|
|
*/
|
|
export const createMockExecutionInfo = (confirmationsRequired: number, confirmationsSubmitted: number, nonce = 0) => ({
|
|
type: 'MULTISIG' as const,
|
|
nonce,
|
|
confirmationsRequired,
|
|
confirmationsSubmitted,
|
|
confirmations: Array.from({ length: confirmationsSubmitted }, () => createMockConfirmation()),
|
|
})
|
|
|
|
/**
|
|
* Preset transaction configurations for common scenarios
|
|
*/
|
|
export const transactionMocks = {
|
|
/** Transaction awaiting first confirmation */
|
|
pendingFirst: () =>
|
|
createMockTransactionDetails({
|
|
txStatus: 'AWAITING_CONFIRMATIONS',
|
|
detailedExecutionInfo: createMockExecutionInfo(2, 0),
|
|
}),
|
|
|
|
/** Transaction with 1 of 2 confirmations */
|
|
pendingSecond: () =>
|
|
createMockTransactionDetails({
|
|
txStatus: 'AWAITING_CONFIRMATIONS',
|
|
detailedExecutionInfo: createMockExecutionInfo(2, 1),
|
|
}),
|
|
|
|
/** Transaction ready for execution */
|
|
readyToExecute: () =>
|
|
createMockTransactionDetails({
|
|
txStatus: 'AWAITING_EXECUTION',
|
|
detailedExecutionInfo: createMockExecutionInfo(2, 2),
|
|
}),
|
|
|
|
/** Successfully executed transaction */
|
|
executed: () =>
|
|
createMockTransactionDetails({
|
|
txStatus: 'SUCCESS',
|
|
executedAt: Date.now() - 3600000, // 1 hour ago
|
|
txHash: createMockTxHash(),
|
|
detailedExecutionInfo: createMockExecutionInfo(2, 2),
|
|
}),
|
|
|
|
/** Failed transaction */
|
|
failed: () =>
|
|
createMockTransactionDetails({
|
|
txStatus: 'FAILED',
|
|
executedAt: Date.now() - 3600000,
|
|
txHash: createMockTxHash(),
|
|
}),
|
|
|
|
/** Cancelled transaction */
|
|
cancelled: () =>
|
|
createMockTransactionDetails({
|
|
txStatus: 'CANCELLED',
|
|
txInfo: createMockTransactionInfo({ isCancellation: true }),
|
|
}),
|
|
|
|
/** ETH transfer transaction */
|
|
ethTransfer: () =>
|
|
createMockTransactionDetails({
|
|
txInfo: createMockTransactionInfo({
|
|
type: 'Transfer',
|
|
value: '1000000000000000000', // 1 ETH
|
|
methodName: null,
|
|
dataSize: '0',
|
|
}),
|
|
}),
|
|
|
|
/** Contract interaction */
|
|
contractCall: () =>
|
|
createMockTransactionDetails({
|
|
txInfo: createMockTransactionInfo({
|
|
type: 'Custom',
|
|
methodName: 'transfer',
|
|
dataSize: '68',
|
|
}),
|
|
}),
|
|
}
|
|
|
|
/**
|
|
* Create a mock queued transaction list
|
|
*/
|
|
export const createMockQueuedList = (transactions: MockTransactionDetails[]) => ({
|
|
count: transactions.length,
|
|
next: null,
|
|
previous: null,
|
|
results: transactions.map((tx) => ({
|
|
type: 'TRANSACTION' as const,
|
|
transaction: tx,
|
|
})),
|
|
})
|
|
|
|
/**
|
|
* Create an empty transaction history
|
|
*/
|
|
export const createEmptyHistory = () => ({
|
|
count: 0,
|
|
next: null,
|
|
previous: null,
|
|
results: [],
|
|
})
|