Skip to content

Commit 7dd5a44

Browse files
ost-ptkComp0te
andauthored
build(deps): DEP-10 — styled-components 6 (WALLET-1337) (#1387)
* build(deps): DEP-7 — redux 5 + react-redux 9 + reselect 5 + redux-saga 1.5 (WALLET-1333) Bump redux 4.2.1 -> 5.0.1, react-redux 8.1.3 -> 9.3.0, reselect 4.1.7 -> 5.2.0, and redux-saga 1.2.3 -> 1.5.0. Fix a dead deep-import (`react-redux/es/exports`) left over from react-redux 8, which no longer resolves under react-redux 9's ESM `exports` map. Wrap four previously-unmemoized selectors (selectVaultAccountsPublicKeys, selectVaultAccountsExceptLedgersAccounts, selectAllContactsNames, selectAllContactsPublicKeys) in createSelector, and split selectIsAccountConnected's tuple-returning input selector into two scalar input selectors — both were returning a new reference on every call, which reselect 5's new default inputStabilityCheck/react-redux 9's stabilityCheck now surface as dev-mode warnings. * build(deps): DEP-8 — bump react-router-dom 7 + yup 1 + resolvers 5 + react-hook-form 7.80 (WALLET-1335) Atomic bump of 4 coupled packages: @hookform/resolvers@5 is typed against yup@1 and peers on react-hook-form@^7.55, so all three must move together; react-router-dom@7 tags along since it needs the same Node 20 baseline. Mechanical fallout fixed: 12 deep imports of @hookform/resolvers/yup/dist/yup -> @hookform/resolvers/yup (v5 dist layout changed), 2 deep imports of yup/es/types -> yup (v1 dropped es/), and use-typed-location.ts's Location import moved from the now-removed @remix-run/router package to react-router-dom's re-export. Type reconciliation for yup 1's stricter inference (yupResolver vs FormValues) is deliberately out of scope here and follows in later commits. * fix(forms): DEP-8 — yup 1 type reconciliation for password/phrase/buy-cspr forms (WALLET-1335) yup 1 infers Yup.string() without .required()/.defined() as an optional/undefinable output, which broke yupResolver's strict typing against useForm<FormValues>() wherever the *FormValues interface declared the field as a plain required string. - form-validation-rules.ts: add .required() to useCreatePasswordRule, useVerifyPasswordAgainstHashRule, useRepeatPasswordRule, and useValidSecretPhraseRule — all four back genuinely required fields (password, confirm password, secret phrase), so this is also a type fix and a validation-completeness fix. - buy-cspr.ts: casperAmount is an unvalidated derived/display field (set via setValue, never user-required), so relax BuyCSPRFormValues.casperAmount to string | undefined instead of forcing .required(). yup's inferred schema type is structurally asymmetric between Input and Output for non-required fields, so no single FormValues shape satisfies Resolver<F, any, F> here; cast the resolver to Resolver<BuyCSPRFormValues> to bridge that gap. * fix(forms): DEP-8 — yup 1 type reconciliation for transfer amount union schema (WALLET-1335) useTransferAmountForm picks between two differently-shaped yup schemas (erc20AmountFormSchema has paymentAmount, csprAmountFormSchema doesn't), making amountFormSchema's type a TS union. keyof of a union is the intersection of keys, so the resolver's inferred `names` type silently dropped paymentAmount, breaking assignability against Resolver<TransferAmountFormValues, ...>. Add an unvalidated paymentAmount field to csprAmountFormSchema so both union branches share the same key set (paymentAmount stays ERC-20-only in practice — this is purely to align the inferred type, not a validation change for CSPR transfers). That alone doesn't fully close the gap because of the same Input/Output asymmetry yup produces for unvalidated fields (as in buy-cspr.ts), so cast the resolver to Resolver<TransferAmountFormValues> as well. * fix(forms): DEP-8 — yup 1 type reconciliation for file import + StringSchema cleanup (WALLET-1335) Yup.mixed() with no generic now infers its .test() callback argument as yup 1's narrow AnyPresentValue type, breaking .length/index access on secretKeyFile's file-type validators. Give it an explicit Yup.mixed<FileList>() — the field is bound to a native <input type="file"> and was always a FileList at runtime, so also fix ImportAccountFormValues.secretKeyFile from the pre-existing (wrong) string | undefined to FileList | undefined. Same Input/Output asymmetry as buy-cspr.ts/transfer.ts requires the same yupResolver(...) as Resolver<FormValues> cast. Also fix an unrelated pre-existing type bug surfaced during review: unlock-vault/index.tsx and password-protection-page/index.tsx typed their Worker message payload's isPasswordCorrect as Yup.StringSchema<...> instead of boolean, which is what verify-password-worker.ts actually posts back (verifyPasswordAgainstHash returns Promise<boolean>). The wrong type never caused a runtime issue (TS can't check cross-Worker message shapes, and both call sites only did truthy/falsy checks) but was worth correcting while touching yup types in this area. This closes out the type-reconciliation phase of the yup 1 / resolvers 5 / react-hook-form 7.80 bump — npm run tsc is now clean project-wide. * test(e2e): DEP-8 — fix router-v7-exposed rename-account flake + review-flow missing awaits (WALLET-1335) rename-account.spec.ts: react-router 7 no longer unmounts the outgoing route synchronously with the incoming one (v6 did), so after navigate() there can be a brief window (confirmed via reproduction: under ~10ms) where the account-settings page's stale heading and the Home header banner both render the same account name. The unscoped getByText(name) assertion hit a Playwright strict-mode violation deterministically. Scope both assertions to the header banner, which is what the test actually intends to verify post-close. review-flow.spec.ts: 8 popupExpect(...).toBeVisible() calls across all three tests were missing await (pre-existing since #1151, unrelated to this bump), so the test function could return before the assertion resolved. Add the missing awaits. * build(deps): DEP-9 — i18n + formatting stack (i18next 26, react-i18next 17, @formatjs/intl 4) (WALLET-1334) Bumps i18next-http-backend 3.0.5 -> 4.0.0 (finishes the DEP-2 security fix, drops the bundled cross-fetch since MV3 service workers have native fetch), i18next 23 -> 26, react-i18next 14 -> 17, i18next-browser-languagedetector 7 -> 8, @formatjs/intl 2 -> 4 (pure ESM), and the i18next-conv dev tool 15 -> 17. i18next-parser stays on hold as an independent extractor. Extends jest.config.js transformIgnorePatterns to transpile @formatjs/intl v4's ESM dependency tree, including the unscoped intl-messageformat transitive that isn't covered by the @formatjs scope alone. Converts the two locale dev scripts to ESM to fix a latent ERR_REQUIRE_ESM from i18next-conv already being ESM-only. No changes needed in src/libs/i18n/i18n.ts or language-detector.ts — the runtime config and custom detector shape are forward-compatible. * build(deps): bump styled-components to v6, add @emotion/is-prop-valid * feat(ui): add shared StyleSheetManager with global prop filtering * fix(ui): update styled-components v6 API/type usages across UI library * feat(security): replace unsafe-inline with per-build nonce in style-src CSP * feat(ui): wrap app trees in CspStyleSheetManager * fix(ui): compose isPropValid into local shouldForwardProp overrides styled-components v6 resolves shouldForwardProp as componentLevel || contextLevel: a component's own .withConfig({shouldForwardProp}) fully replaces the StyleSheetManager-provided filter rather than composing with it. typography.tsx (StyledTypography, StyledHeader) and svg-icon.tsx (Container) each define a local shouldForwardProp that only excludes their one component-specific prop, so every other custom prop (color, active, wordBreak, ellipsis, isDarkMode, displayContext, ...) was forwarded straight to the DOM, unfiltered by the global CspStyleSheetManager filter. Confirmed via manual browser QA (React "does not recognize the X prop" warnings + invalid DOM attributes) and by reading styled-components 6.4.3's compiled resolution logic directly. * revert: defer CSP style-src nonce hardening (P1.5a) out of DEP-10 Reverts commit 5aa885c. Manual QA surfaced two libraries that inject inline <style> tags with no nonce awareness under the new nonce-only style-src: style-loader's runtime (used for the mac-scrollbar and react-loading-skeleton CSS imports — fixable later via __webpack_nonce__, which style-loader does support) and @lottiefiles/react-lottie-player's vendored style-inject utility (no nonce support at all, would need either a CSP hash for its static injected CSS or a library swap). The StyleSheetManager wiring this ticket already built (CspStyleSheetManager mounted in all 5 apps) stays as-is and needs no rework when the nonce work resumes — it already passes process.env.CSP_NONCE through as a no-op until that env var is defined again. style-src reverts to 'unsafe-inline' for now; P1.5(a) tracked as a separate follow-up once the lottie-player situation is resolved. * docs(ui): clarify CSP_NONCE prop is a no-op until follow-up ticket * fix(ui): add missing & to Button pseudo-selectors ':hover'/':active'/':focus' object keys (with or without a leading space) compile to a descendant-combinator selector (`.btn :hover`, matching any hovered descendant) rather than the button's own state (`.btn:hover`) — CSS object-key syntax requires an explicit '&' prefix to target the host element itself. Pre-existing since 2022 (commit a831936), unrelated to the styled-components v6 bump (button.tsx had zero diff before this fix), but found and fixed during this ticket's manual QA: hovering the Send/More icons (descendants of the button) was applying the button's intended hover/active background to the icon itself instead of the button, and the button's focus outline was never actually suppressed. --------- Co-authored-by: Vynnyk Dmytro <simbiatoff@gmail.com>
1 parent 68525e3 commit 7dd5a44

16 files changed

Lines changed: 187 additions & 198 deletions

File tree

package-lock.json

Lines changed: 57 additions & 107 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,10 @@
5353
"npm run format:write"
5454
]
5555
},
56-
"resolutions": {
57-
"styled-components": "^5"
58-
},
5956
"//dependencyNotes": "@scure/bip32 and @scure/bip39 are intentionally held at v1 (1.6.2 / 1.2.1): casper-js-sdk@5.0.12 pins @scure/* ^1 and shares the top-level copy. Bumping to v2 would fork BIP32/39 into two impls and pull @noble/curves@2/@noble/hashes@2 (wrong-address risk, no upside). Bump in lockstep only when the SDK/core move off @scure ^1. See WALLET-1330.",
6057
"dependencies": {
6158
"@bringweb3/chrome-extension-kit": "1.7.0",
59+
"@emotion/is-prop-valid": "^1.4.0",
6260
"@formatjs/intl": "^4.1.14",
6361
"@hookform/resolvers": "^5.4.0",
6462
"@lapo/asn1js": "^2.1.3",
@@ -106,7 +104,7 @@
106104
"redux": "^5.0.1",
107105
"redux-saga": "^1.5.0",
108106
"reselect": "^5.2.0",
109-
"styled-components": "5.3.11",
107+
"styled-components": "6.4.3",
110108
"typesafe-actions": "5.1.0",
111109
"yup": "^1.7.1"
112110
},
@@ -145,7 +143,6 @@
145143
"@types/react": "^18.2.55",
146144
"@types/react-dom": "^18.3.0",
147145
"@types/react-virtualized": "^9.21.30",
148-
"@types/styled-components": "^5.1.36",
149146
"@typescript-eslint/parser": "^5.62.0",
150147
"babel-eslint": "^10.1.0",
151148
"babel-loader": "^10.0.0",

src/apps/connect-to-app/index.tsx

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,12 @@ import { useSystemThemeDetector } from '@hooks/use-system-theme-detector';
2222
import '@libs/i18n/i18n';
2323
import { ErrorBoundary } from '@libs/layout';
2424
import { newQueryClient } from '@libs/services/query-client';
25-
import { GlobalStyle, darkTheme, lightTheme } from '@libs/ui';
25+
import {
26+
CspStyleSheetManager,
27+
GlobalStyle,
28+
darkTheme,
29+
lightTheme
30+
} from '@libs/ui';
2631

2732
const Tree = () => {
2833
const [state, setState] = useState<PopupState | null>(null);
@@ -58,16 +63,18 @@ const Tree = () => {
5863

5964
return (
6065
<Suspense fallback={null}>
61-
<ThemeProvider theme={isDarkMode ? darkTheme : lightTheme}>
62-
<GlobalStyle />
63-
<ReduxProvider store={store}>
64-
<QueryClientProvider client={newQueryClient}>
65-
<ErrorBoundary>
66-
<AppRouter />
67-
</ErrorBoundary>
68-
</QueryClientProvider>
69-
</ReduxProvider>
70-
</ThemeProvider>
66+
<CspStyleSheetManager>
67+
<ThemeProvider theme={isDarkMode ? darkTheme : lightTheme}>
68+
<GlobalStyle />
69+
<ReduxProvider store={store}>
70+
<QueryClientProvider client={newQueryClient}>
71+
<ErrorBoundary>
72+
<AppRouter />
73+
</ErrorBoundary>
74+
</QueryClientProvider>
75+
</ReduxProvider>
76+
</ThemeProvider>
77+
</CspStyleSheetManager>
7178
</Suspense>
7279
);
7380
};

src/apps/import-account-with-file/index.tsx

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@ import { useSystemThemeDetector } from '@hooks/use-system-theme-detector';
2020
import '@libs/i18n/i18n';
2121
import { ErrorBoundary } from '@libs/layout';
2222
import { newQueryClient } from '@libs/services/query-client';
23-
import { GlobalStyle, darkTheme, lightTheme } from '@libs/ui';
23+
import {
24+
CspStyleSheetManager,
25+
GlobalStyle,
26+
darkTheme,
27+
lightTheme
28+
} from '@libs/ui';
2429

2530
import { AppRouter } from './app-router';
2631

@@ -56,16 +61,18 @@ const Tree = () => {
5661

5762
return (
5863
<Suspense fallback={null}>
59-
<ThemeProvider theme={isDarkMode ? darkTheme : lightTheme}>
60-
<GlobalStyle />
61-
<ReduxProvider store={store}>
62-
<QueryClientProvider client={newQueryClient}>
63-
<ErrorBoundary>
64-
<AppRouter />
65-
</ErrorBoundary>
66-
</QueryClientProvider>
67-
</ReduxProvider>
68-
</ThemeProvider>
64+
<CspStyleSheetManager>
65+
<ThemeProvider theme={isDarkMode ? darkTheme : lightTheme}>
66+
<GlobalStyle />
67+
<ReduxProvider store={store}>
68+
<QueryClientProvider client={newQueryClient}>
69+
<ErrorBoundary>
70+
<AppRouter />
71+
</ErrorBoundary>
72+
</QueryClientProvider>
73+
</ReduxProvider>
74+
</ThemeProvider>
75+
</CspStyleSheetManager>
6976
</Suspense>
7077
);
7178
};

src/apps/onboarding/index.tsx

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { onboardingAppInit } from '@background/redux/windowManagement/actions';
1616
import '@libs/i18n/i18n';
1717
import { ErrorBoundary } from '@libs/layout';
1818
import { newQueryClient } from '@libs/services/query-client';
19-
import { GlobalStyle, lightTheme } from '@libs/ui';
19+
import { CspStyleSheetManager, GlobalStyle, lightTheme } from '@libs/ui';
2020

2121
const Tree = () => {
2222
const [state, setState] = useState<PopupState | null>(null);
@@ -36,16 +36,18 @@ const Tree = () => {
3636

3737
return (
3838
<Suspense fallback={null}>
39-
<ThemeProvider theme={lightTheme}>
40-
<GlobalStyle />
41-
<ReduxProvider store={store}>
42-
<QueryClientProvider client={newQueryClient}>
43-
<ErrorBoundary>
44-
<AppRouter />
45-
</ErrorBoundary>
46-
</QueryClientProvider>
47-
</ReduxProvider>
48-
</ThemeProvider>
39+
<CspStyleSheetManager>
40+
<ThemeProvider theme={lightTheme}>
41+
<GlobalStyle />
42+
<ReduxProvider store={store}>
43+
<QueryClientProvider client={newQueryClient}>
44+
<ErrorBoundary>
45+
<AppRouter />
46+
</ErrorBoundary>
47+
</QueryClientProvider>
48+
</ReduxProvider>
49+
</ThemeProvider>
50+
</CspStyleSheetManager>
4951
</Suspense>
5052
);
5153
};

src/apps/popup/index.tsx

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,12 @@ import { useSystemThemeDetector } from '@hooks/use-system-theme-detector';
2121

2222
import { ErrorBoundary } from '@libs/layout';
2323
import { newQueryClient } from '@libs/services/query-client';
24-
import { GlobalStyle, darkTheme, lightTheme } from '@libs/ui';
24+
import {
25+
CspStyleSheetManager,
26+
GlobalStyle,
27+
darkTheme,
28+
lightTheme
29+
} from '@libs/ui';
2530

2631
import { AppRouter } from './app-router';
2732

@@ -59,16 +64,18 @@ const Tree = () => {
5964

6065
return (
6166
<Suspense fallback={null}>
62-
<ThemeProvider theme={isDarkMode ? darkTheme : lightTheme}>
63-
<GlobalStyle />
64-
<ReduxProvider store={store}>
65-
<QueryClientProvider client={newQueryClient}>
66-
<ErrorBoundary>
67-
<AppRouter />
68-
</ErrorBoundary>
69-
</QueryClientProvider>
70-
</ReduxProvider>
71-
</ThemeProvider>
67+
<CspStyleSheetManager>
68+
<ThemeProvider theme={isDarkMode ? darkTheme : lightTheme}>
69+
<GlobalStyle />
70+
<ReduxProvider store={store}>
71+
<QueryClientProvider client={newQueryClient}>
72+
<ErrorBoundary>
73+
<AppRouter />
74+
</ErrorBoundary>
75+
</QueryClientProvider>
76+
</ReduxProvider>
77+
</ThemeProvider>
78+
</CspStyleSheetManager>
7279
</Suspense>
7380
);
7481
};

src/apps/signature-request/index.tsx

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,12 @@ import { useSystemThemeDetector } from '@hooks/use-system-theme-detector';
2222
import '@libs/i18n/i18n';
2323
import { ErrorBoundary } from '@libs/layout';
2424
import { newQueryClient } from '@libs/services/query-client';
25-
import { GlobalStyle, darkTheme, lightTheme } from '@libs/ui';
25+
import {
26+
CspStyleSheetManager,
27+
GlobalStyle,
28+
darkTheme,
29+
lightTheme
30+
} from '@libs/ui';
2631

2732
import { AppRouter } from './app-router';
2833

@@ -60,16 +65,18 @@ const Tree = () => {
6065

6166
return (
6267
<Suspense fallback={null}>
63-
<ThemeProvider theme={isDarkMode ? darkTheme : lightTheme}>
64-
<GlobalStyle />
65-
<ReduxProvider store={store}>
66-
<QueryClientProvider client={newQueryClient}>
67-
<ErrorBoundary>
68-
<AppRouter />
69-
</ErrorBoundary>
70-
</QueryClientProvider>
71-
</ReduxProvider>
72-
</ThemeProvider>
68+
<CspStyleSheetManager>
69+
<ThemeProvider theme={isDarkMode ? darkTheme : lightTheme}>
70+
<GlobalStyle />
71+
<ReduxProvider store={store}>
72+
<QueryClientProvider client={newQueryClient}>
73+
<ErrorBoundary>
74+
<AppRouter />
75+
</ErrorBoundary>
76+
</QueryClientProvider>
77+
</ReduxProvider>
78+
</ThemeProvider>
79+
</CspStyleSheetManager>
7380
</Suspense>
7481
);
7582
};

src/libs/layout/containers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ export const activityBottomPseudoElementRules = css<BorderBottomPseudoElementPro
271271
background: ${({ theme }) => theme.color.backgroundSecondary};
272272
`;
273273

274-
export const BorderContainer = styled.div`
274+
export const BorderContainer = styled.div<BorderBottomPseudoElementProps>`
275275
& > *:last-child {
276276
${borderBottomPseudoElementRules};
277277
}

src/libs/ui/components/avatar/avatar.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Maybe } from 'casper-wallet-core/src/typings/common';
22
import React from 'react';
3-
import styled, { DefaultTheme, useTheme } from 'styled-components';
3+
import styled, { useTheme } from 'styled-components';
44

55
import { isValidAccountHash, isValidPublicKey } from '@src/utils';
66

@@ -24,8 +24,8 @@ const ConnectionStatusBadgeContainer = styled(AlignedFlexRow)`
2424
z-index: 1;
2525
`;
2626

27-
export const BackgroundWrapper = styled.div(
28-
({ size, theme }: { size: number; theme: DefaultTheme }) => ({
27+
export const BackgroundWrapper = styled.div<{ size: number }>(
28+
({ size, theme }) => ({
2929
borderRadius: theme.borderRadius.eight,
3030
height: `${size}px`,
3131
width: `${size}px`,

src/libs/ui/components/button/button.tsx

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,11 @@ const BaseButton = styled.button<BaseButtonProps>(
4545
padding: '12px'
4646
}),
4747

48-
':focus': {
48+
'&:focus': {
4949
outline: 'none'
5050
},
5151

52-
':hover': {
52+
'&:hover': {
5353
cursor: 'pointer'
5454
},
5555

@@ -64,10 +64,10 @@ const PrimaryBlueButton = styled(BaseButton)<BaseButtonProps>(
6464
color: theme.color.contentOnFill,
6565
background: theme.color.fillPrimary,
6666

67-
': hover': {
67+
'&:hover': {
6868
background: theme.color.fillPrimaryHover
6969
},
70-
': active': {
70+
'&:active': {
7171
background: theme.color.fillPrimaryClick
7272
},
7373

@@ -83,10 +83,10 @@ const PrimaryRedButton = styled(BaseButton)<BaseButtonProps>(
8383
color: theme.color.contentOnFill,
8484
background: theme.color.fillCritical,
8585

86-
': hover': {
86+
'&:hover': {
8787
background: theme.color.fillCriticalHover
8888
},
89-
': active': {
89+
'&:active': {
9090
background: theme.color.fillCriticalClick
9191
},
9292

@@ -102,10 +102,10 @@ const SecondaryBlueButton = styled(BaseButton)<BaseButtonProps>(
102102
color: theme.color.contentAction,
103103
background: getLinearGradientColor(theme.color.fillSecondary),
104104

105-
': hover': {
105+
'&:hover': {
106106
background: getLinearGradientColor(theme.color.fillSecondaryHover)
107107
},
108-
': active': {
108+
'&:active': {
109109
background: theme.color.fillNeutral
110110
},
111111

@@ -121,10 +121,10 @@ const SecondaryRedButton = styled(BaseButton)<BaseButtonProps>(
121121
color: theme.color.fillCritical,
122122
background: getLinearGradientColor(theme.color.fillSecondary),
123123

124-
': hover': {
124+
'&:hover': {
125125
background: getLinearGradientColor(theme.color.fillSecondaryHover)
126126
},
127-
': active': {
127+
'&:active': {
128128
background: theme.color.fillNeutral
129129
},
130130

@@ -140,10 +140,10 @@ const UtilityButton = styled(BaseButton)<BaseButtonProps>(
140140
color: theme.color.contentOnFill,
141141
background: theme.color.fillPrimary,
142142

143-
': hover': {
143+
'&:hover': {
144144
background: theme.color.fillPrimaryHover
145145
},
146-
': active': {
146+
'&:active': {
147147
background: theme.color.fillPrimaryClick
148148
},
149149

0 commit comments

Comments
 (0)