Files
74e410bb67 chore(mobile): upgrade Tamagui from v1 to v2 (#7497)
* chore(mobile): upgrade Tamagui from v1 to v2

Upgrade all @tamagui/* packages from v1 (^1.126.3) to v2 (2.0.0-rc.26).
Replace deprecated @tamagui/animations-moti with animations-reanimated.
Rewrite SafeButton to use v2 compound component pattern (Button.Text)
with correct font weight, disabled state, and icon coloring.
Patch @tamagui/image for style override bug (upstream fix merged but
not yet on npm: https://github.com/tamagui/tamagui/pull/3966).

* fix(mobile): resolve Tamagui tokens in Loader before passing to SVG

The Loader component passes color directly to react-native-progress
(CircleSnail), which uses react-native-svg internally. SVG doesn't
understand Tamagui theme tokens like "$color", causing repeated
"not a valid color or brush" console warnings.

* fix(mobile): center nav bar title in scrollable headers

Remove flex: 1 from headerTitle Animated.View so the native stack
header centers the title element itself. Add headerTitleAlign: 'center'
and match right spacer width (40px) to the back button circle.

* fix(mobile): fix Tamagui v2 component API breakages

- SafeButton: rewrite with Button.Text compound pattern, add textColor
  prop for forced light-mode overrides, restore fontWeight 700 default
- SafeInput: replace removed `editable` prop with `readOnly`, use
  GetProps<StyledInput> to fix type conflicts, cast theme tokens
- SafeCard: use `src` instead of `source`, `objectFit` instead of
  `resizeMode` for Tamagui v2 Image
- ParametersButton: migrate from raw Button to SafeButton
- Badge: fix dark mode background token (backgroundMain → borderLight)
- Raw Buttons: move fontWeight to Button.Text in ImportSigner/Ledger
- Onboarding: migrate color prop to textColor for dark mode fix
- Assets errors: use secondary variant + textColor instead of inline bg

* fix(mobile): preserve element children and text-button accent color in SafeButton

- Only wrap string/number children in Button.Text; render element
  children (ActivityIndicator, Loader) directly to avoid invalid
  text-child nesting on native.
- Use theme.primary for the text variant so text-style buttons
  retain their accent color instead of blending with body text.

* fix(mobile): replace raw Tamagui Button with SafeButton in ImportSuccess

The Copy button on the signer import success screen was broken after the
Tamagui v2 upgrade because it used a raw Tamagui Button. Replace it with
SafeButton (secondary, $sm) for consistent styling and v2 compatibility.

* fix(mobile): fix Jest transform config for Tamagui v2 and update snapshots

Add tamagui and @tamagui/* to transformIgnorePatterns so Jest can parse
the ESM exports in Tamagui v2. Update 22 snapshots reflecting the new
style output and remove stale fontFamily assertion in DataRow test.

---------

Co-authored-by: Hanzo Dev <[email protected]>
2026-03-20 16:39:59 +01:00

140 lines
4.2 KiB
TypeScript

import type { Preview } from '@storybook/react'
import { useColorScheme } from 'react-native'
import { NavigationContainer, NavigationIndependentTree } from '@react-navigation/native'
import { StorybookThemeProvider } from '@/src/theme/provider/storybookTheme'
import { SafeToastProvider } from '@/src/theme/provider/toastProvider'
import { SafeAreaProvider } from 'react-native-safe-area-context'
import { PortalProvider, View } from 'tamagui'
import { createNavigationContainerRef } from '@react-navigation/native'
import { Provider } from 'react-redux'
import { configureStore } from '@reduxjs/toolkit'
import { rootReducer } from '@/src/store'
import { FLUSH, PAUSE, PERSIST, PURGE, REGISTER, REHYDRATE } from 'redux-persist'
import { cgwClient } from '@safe-global/store/gateway/cgwClient'
import { web3API } from '@/src/store/signersBalance'
import { TOKEN_LISTS } from '@/src/store/settingsSlice'
import { chainsAdapter } from '@safe-global/store/gateway/chains'
import { mockChain } from '@/src/tests/mocks'
import { CONFIG_SERVICE_KEY } from '@/src/config/constants'
const navigationRef = createNavigationContainerRef()
// Create a mock Redux store for Storybook
const createStorybookStore = () => {
const mockChainsState = chainsAdapter.setAll(chainsAdapter.getInitialState(), [mockChain])
return configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
},
}).concat(cgwClient.middleware, web3API.middleware),
preloadedState: {
settings: {
onboardingVersionSeen: '',
themePreference: 'light',
currency: 'usd',
tokenList: TOKEN_LISTS.TRUSTED,
env: {
rpc: {},
tenderly: {
url: '',
accessToken: '',
},
},
},
activeSafe: {
chainId: '1',
address: '0x1234567890123456789012345678901234567890',
threshold: 1,
owners: [],
nonce: 0,
version: '1.3.0',
},
[cgwClient.reducerPath]: {
queries: {
[`getChainsConfigV2("${CONFIG_SERVICE_KEY}")`]: {
status: 'fulfilled',
data: mockChainsState,
},
},
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any,
})
}
const storybookStore = createStorybookStore()
// Navigation wrapper component for Storybook
// Uses NavigationIndependentTree to isolate from any parent navigation
const NavigationWrapper = ({ children }: { children: React.ReactNode }) => {
return (
<NavigationIndependentTree>
<NavigationContainer
ref={navigationRef}
documentTitle={{
enabled: false,
}}
>
{children}
</NavigationContainer>
</NavigationIndependentTree>
)
}
const preview: Preview = {
parameters: {
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
},
globalTypes: {
theme: {
description: 'Global theme for components',
defaultValue: '',
toolbar: {
title: 'Theme',
icon: 'circlehollow',
items: [
{ value: 'light', icon: 'circlehollow', title: 'Light' },
{ value: 'dark', icon: 'circle', title: 'Dark' },
],
dynamicTitle: true,
},
},
},
tags: ['autodocs'],
decorators: [
(Story, context) => {
const colorScheme = useColorScheme()
const theme = context.globals.theme || colorScheme || 'light'
return (
<Provider store={storybookStore}>
<PortalProvider shouldAddRootHost>
<NavigationWrapper>
<SafeAreaProvider>
<StorybookThemeProvider theme={theme}>
<SafeToastProvider>
<View style={{ padding: 16, flex: 1 }} backgroundColor={'$background'}>
<Story />
</View>
</SafeToastProvider>
</StorybookThemeProvider>
</SafeAreaProvider>
</NavigationWrapper>
</PortalProvider>
</Provider>
)
},
],
}
export default preview