Skip to content

fix(theme): keep flat font properties in a mixed configureFonts config - #5066

Open
giaBaoJS wants to merge 1 commit into
callstack:mainfrom
giaBaoJS:fix/configure-fonts-mixed-config
Open

fix(theme): keep flat font properties in a mixed configureFonts config#5066
giaBaoJS wants to merge 1 commit into
callstack:mainfrom
giaBaoJS:fix/configure-fonts-mixed-config

Conversation

@giaBaoJS

Copy link
Copy Markdown

The bug

configureFontsConfig classifies the config as a whole:

constisFlatConfig=Object.values(config).every((value)=>typeofvalue!=='object');

A config may legitimately contain both shapes at once — a font property that should apply to every variant, plus a tweak to one variant. A single per-variant object flips isFlatConfig to false for the whole config, and the Object.assign branch below then spreads every top-level entry as if it were a variant.

Repro:

configureFonts({config: {fontFamily: 'NotoSans',bodyLarge: {fontSize: 18},},});

Two things go wrong:

  1. The requested fontFamily is silently dropped from every variant — bodyLarge.fontFamily is still "System".
  2. The result grows a junk typescale entry from spreading the string into an object:
fontFamily: {"0": "N","1": "o","2": "t","3": "o","4": "S","5": "a","6": "n","7": "s"}

No error, no warning. From the app's point of view the custom font just does not apply.

The same shape is also rejected by the type overloads today, so a TS user gets TS2769: No overload matches this call and has to call configureFonts twice or hand-build the object.

The fix

Partition Object.entries(config) into scalar entries (shared font properties) and object entries (per-variant overrides), instead of classifying the config as a whole. Build every variant as { ...typescale[variant], ...sharedProperties, ...variantOverrides[variant] }.

The two previous branches are special cases of this, so the isFlatConfig early return and the Object.assign both go away:

  • config with only scalars → variantOverrides is empty → every variant gets the scalars (old flat branch)
  • config with only objects → sharedProperties is empty → named variants are merged, others untouched (old per-variant branch)
  • unknown keys with object values still create new custom variants, as before

I also added a third overload for the mixed shape:

exportdefaultfunctionconfigureFonts(params: {config: Partial<TypescaleStyle>&Partial<Record<TypescaleKey,Partial<TypescaleStyle>>>;}): Typescale;

It is placed after the two existing Partial overloads and before the Record<string, TypescaleStyle> one. Neither existing overload accepts a mixed literal (each rejects the other's keys as excess properties), and the custom-variant overload does not either, so this signature only picks up calls that previously failed to compile — existing call sites keep resolving to the overload they resolved to before. I verified both directions: the mixed literal is TS2769 on main and clean with this change, while the flat-only and per-variant-only literals compile on both.

About #4589

I could not reproduce the reporter's Variant titleLarge was not provided properly from this bug, and I don't think this closes it — the warning there lists regular, medium, light, thin, i.e. an MD2-shaped fonts object, which is a different path entirely. Listing it as possibly related only, deliberately not Fixes.

Test plan

Two cases added to src/theme/__tests__/fonts.test.js:

  • a mixed config applies the flat properties to every variant and still applies the per-variant override on top
  • the flat property does not leak into the result as a typescale key

Before the change both fail; the second reports Received: {"0": "N", "1": "o", "2": "t", "3": "o", "4": "S", "5": "a", "6": "n", "7": "s"}. The four existing configureFonts tests pass unchanged, before and after.

yarn test: 55 suites, 737 passed / 1 skipped, 169 snapshots — no snapshot churn. yarn lint and yarn typecheck clean.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@giaBaoJS