build(deps): DEP-10 — styled-components 6 (WALLET-1337)#1387
Merged
Comp0te merged 21 commits intoJul 3, 2026
Conversation
…a 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.
…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.
…-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.
…on 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.
…gSchema 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.
…w-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.
…ux-9-reselect-5-redux-saga-1-5
…x-saga-1-5' into WALLET-1335-dep-8-router-forms-react-router-7-yup-1-resolvers-5-react-hook-form-7-80
…dep-8-router-forms-react-router-7-yup-1-resolvers-5-react-hook-form-7-80 # Conflicts: # package-lock.json # package.json
…dep-9-i-18-n-formatting-stack
…xt 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.
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.
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.
Base automatically changed from
WALLET-1334-dep-9-i-18-n-formatting-stack
to
release/2.6.0
July 2, 2026 20:16
…dep-10-styled-components-5-to-6 # Conflicts: # package-lock.json # package.json
':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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Upgrades
styled-components5.3.11 → 6.4.3. v6 removes automatic DOM-prop filtering (v5 silently dropped non-HTML custom props likecolor,active,loadingbefore they reached the DOM; v6 forwards everything, triggering React warnings and invalid DOM attributes).CspStyleSheetManagerwrapper (src/libs/ui/style-sheet-manager.tsx, using@emotion/is-prop-valid), mounted in all 5 UI app entry points, instead of renaming ~313 custom-prop usages across ~140 files.typography.tsx,svg-icon.tsx(both had localwithConfig({shouldForwardProp})that needed the v6 signature update — see note below),list.tsx(removedFlattenInterpolation/ThemePropstypes),global-style.ts,containers.ts,avatar.tsx(assorted v6 type-inference tightenings, discovered via a fulltsc --noEmitpass, not caught by the original grep-based audit).shouldForwardPropascomponentLevel || contextLevel— a component's own.withConfig({shouldForwardProp})fully replaces (does not compose with) the globalStyleSheetManagerfilter. The 3withConfigsites in this codebase each only excluded their one local prop, so every other custom prop on those 3 components was still leaking to the DOM. Fixed by composingisPropValidinto all 3 local callbacks.Scope note
This ticket originally also planned to replace
'unsafe-inline'with a per-build CSP nonce instyle-src(a separate security-hardening item). That work was implemented, then reverted after manual QA found@lottiefiles/react-lottie-playerinjects inline<style>tags with zero nonce support (no config hook, vendoredstyle-injectutility) — under a nonce-onlystyle-srcthis broke rendering. CSP-nonce hardening is deferred to a follow-up ticket, pending a decision on the lottie-player situation (CSP hash for its static injected CSS vs. swapping tolottie-webdirectly).style-srcstays'unsafe-inline', unchanged from before this PR. TheCspStyleSheetManagerwrapper this PR adds needs no rework when that follow-up lands — it already passesprocess.env.CSP_NONCEthrough as a harmless no-op.Jira: https://make-software.atlassian.net/browse/WALLET-1337
Test plan
npm run ci-check(format:check + lint + tsc + test) — all greennpm run build:chrome/build:firefox— both succeed; verified generated CSP unchanged ('unsafe-inline', no nonce)npm run build:safari— webpack/JS compile phase succeeds; Xcode PACK step fails on a pre-existing, unrelated issue (stale hardcoded chunk filenames inproject.pbxproj, tracked separately, coordinated with DEP-11)shouldForwardPropcomposition bugextension-manifest-reviewersubagent — 0 findings; confirmed the CSP-nonce introduction+revert is a byte-exact no-op onwebpack.config.js/src/utils.ts, all 3 manifest JSON files untouched