chore: update postcss to version 8.5.14 and sort font weights in typography components

This commit is contained in:
Amruth Pillai
2026-05-10 22:09:37 +02:00
parent 64ac3ff328
commit 3cd228bd84
9 changed files with 102 additions and 29 deletions
@@ -0,0 +1,31 @@
// @vitest-environment happy-dom
import { render } from "@testing-library/react";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { FontWeightCombobox } from "./combobox";
const comboboxMock = vi.hoisted(() => ({
props: undefined as { onValueChange?: (value: string[] | null) => void } | undefined,
}));
vi.mock("@/components/ui/combobox", () => ({
Combobox: (props: { onValueChange?: (value: string[] | null) => void }) => {
comboboxMock.props = props;
return null;
},
}));
describe("FontWeightCombobox", () => {
beforeEach(() => {
comboboxMock.props = undefined;
});
it("emits selected font weights in ascending order", () => {
const onValueChange = vi.fn();
render(<FontWeightCombobox fontFamily="Source Sans 3" value={["400"]} onValueChange={onValueChange} />);
comboboxMock.props?.onValueChange?.(["800", "600", "400"]);
expect(onValueChange).toHaveBeenCalledWith(["400", "600", "800"]);
});
});
@@ -1,6 +1,6 @@
import type { MultiComboboxProps, SingleComboboxProps } from "@/components/ui/combobox";
import { useMemo } from "react";
import { fontList, getFont, getFontDisplayName, getFontSearchKeywords } from "@reactive-resume/fonts";
import { useCallback, useMemo } from "react";
import { fontList, getFont, getFontDisplayName, getFontSearchKeywords, sortFontWeights } from "@reactive-resume/fonts";
import { cn } from "@reactive-resume/utils/style";
import { Combobox } from "@/components/ui/combobox";
import { FontDisplay } from "./font-display";
@@ -54,14 +54,20 @@ export function FontFamilyCombobox({ className, ...props }: FontFamilyComboboxPr
type FontWeightComboboxProps = Omit<MultiComboboxProps, "options" | "multiple"> & { fontFamily: string };
export function FontWeightCombobox({ fontFamily, ...props }: FontWeightComboboxProps) {
export function FontWeightCombobox({
fontFamily,
onValueChange,
value,
defaultValue,
...props
}: FontWeightComboboxProps) {
const options = useMemo(() => {
const fontData = getFont(fontFamily);
let weights: string[] = [];
if (fontData && Array.isArray(fontData.weights) && fontData.weights.length > 0) {
weights = fontData.weights as string[];
weights = sortFontWeights(fontData.weights);
} else {
// Fallback to all possible weights
weights = ["100", "200", "300", "400", "500", "600", "700", "800", "900"];
@@ -74,5 +80,27 @@ export function FontWeightCombobox({ fontFamily, ...props }: FontWeightComboboxP
}));
}, [fontFamily]);
return <Combobox {...props} multiple options={options} />;
const sortedValue = useMemo(() => (value ? sortFontWeights(value) : value), [value]);
const sortedDefaultValue = useMemo(
() => (defaultValue ? sortFontWeights(defaultValue) : defaultValue),
[defaultValue],
);
const handleValueChange = useCallback(
(nextValue: string[] | null) => {
onValueChange?.(nextValue ? sortFontWeights(nextValue) : nextValue);
},
[onValueChange],
);
return (
<Combobox
{...props}
value={sortedValue}
defaultValue={sortedDefaultValue}
onValueChange={handleValueChange}
multiple
options={options}
/>
);
}
+4
View File
@@ -171,6 +171,10 @@ export function getWebFontSource(family: string, weight: FontWeight = "400", ita
return webFont.files[key] ?? (italic ? webFont.files[weight] : undefined) ?? webFont.preview;
}
export function sortFontWeights<T extends string>(fontWeights: T[]): T[] {
return [...fontWeights].sort((a, b) => Number(a) - Number(b));
}
export function getFallbackWebFontFamilies(family: string) {
if (isStandardPdfFontFamily(family)) return [];
+4 -5
View File
@@ -3,6 +3,7 @@ import type { Template } from "@reactive-resume/schema/templates";
import type { ComponentType } from "react";
import type { SectionTitleResolver } from "./section-title";
import { Document } from "@react-pdf/renderer";
import { useMemo } from "react";
import { RenderProvider } from "./context";
import { registerFonts } from "./hooks/use-register-fonts";
import { getTemplatePage } from "./templates";
@@ -22,14 +23,12 @@ export type ResumeDocumentProps = {
export const ResumeDocument = ({ data, template, resolveSectionTitle }: ResumeDocumentProps) => {
const TemplatePageComponent = getTemplatePage(template);
const typography = registerFonts(data.metadata.typography);
const typography = registerFonts(data.metadata.typography) as Typography;
// `registerFonts` widens `fontFamily` to `string | string[]` for CJK
// fallback (#2986); the cast carries that wider runtime value through
// `ResumeData` without changing the public schema.
const resumeData =
typography === data.metadata.typography
? data
: { ...data, metadata: { ...data.metadata, typography: typography as unknown as Typography } };
const resumeData = useMemo(() => ({ ...data, metadata: { ...data.metadata, typography } }), [data, typography]);
return (
<RenderProvider data={resumeData} resolveSectionTitle={resolveSectionTitle}>
@@ -85,4 +85,18 @@ describe("registerFonts", () => {
}),
);
});
it("returns typography with font weights sorted ascending", async () => {
vi.spyOn(Font, "register").mockImplementation(() => {});
const { registerFonts } = await import("./use-register-fonts");
const pdfTypography = registerFonts({
...typography,
body: { ...typography.body, fontFamily: "Source Sans 3", fontWeights: ["800", "600", "400"] },
heading: { ...typography.heading, fontFamily: "Source Sans 3", fontWeights: ["900", "500"] },
});
expect(pdfTypography.body.fontWeights).toEqual(["400", "600", "800"]);
expect(pdfTypography.heading.fontWeights).toEqual(["500", "900"]);
});
});
+5 -6
View File
@@ -6,6 +6,7 @@ import {
getPdfCjkFallbackFontFamily,
getWebFontSource,
isStandardPdfFontFamily,
sortFontWeights,
} from "@reactive-resume/fonts";
type FontWeightRange = {
@@ -53,15 +54,13 @@ const resolvePdfFontFamily = (family: string) => {
const resolvePdfTypography = (typography: Typography): Typography => {
const bodyFontFamily = resolvePdfFontFamily(typography.body.fontFamily);
const headingFontFamily = resolvePdfFontFamily(typography.heading.fontFamily);
if (bodyFontFamily === typography.body.fontFamily && headingFontFamily === typography.heading.fontFamily) {
return typography;
}
const bodyFontWeights = sortFontWeights(typography.body.fontWeights);
const headingFontWeights = sortFontWeights(typography.heading.fontWeights);
return {
...typography,
body: { ...typography.body, fontFamily: bodyFontFamily },
heading: { ...typography.heading, fontFamily: headingFontFamily },
body: { ...typography.body, fontFamily: bodyFontFamily, fontWeights: bodyFontWeights },
heading: { ...typography.heading, fontFamily: headingFontFamily, fontWeights: headingFontWeights },
};
};
+1
View File
@@ -39,6 +39,7 @@
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@typescript/native-preview": "7.0.0-dev.20260510.1",
"postcss": "^8.5.14",
"tailwindcss": "^4.3.0",
"typescript": "^6.0.3"
}
+7 -11
View File
@@ -4,6 +4,9 @@ settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
overrides:
postcss@<8.5.10: ^8.5.14
importers:
.:
@@ -813,6 +816,9 @@ importers:
'@typescript/native-preview':
specifier: 7.0.0-dev.20260510.1
version: 7.0.0-dev.20260510.1
postcss:
specifier: ^8.5.14
version: 8.5.14
tailwindcss:
specifier: ^4.3.0
version: 4.3.0
@@ -7558,10 +7564,6 @@ packages:
postcss-value-parser@4.2.0:
resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
postcss@8.4.31:
resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==}
engines: {node: ^10 || ^12 || >=14}
postcss@8.5.14:
resolution: {integrity: sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==}
engines: {node: ^10 || ^12 || >=14}
@@ -15419,7 +15421,7 @@ snapshots:
'@swc/helpers': 0.5.15
baseline-browser-mapping: 2.10.29
caniuse-lite: 1.0.30001792
postcss: 8.4.31
postcss: 8.5.14
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
styled-jsx: 5.1.6(@babel/core@7.29.0)(babel-plugin-macros@3.1.0)(react@19.2.6)
@@ -15836,12 +15838,6 @@ snapshots:
postcss-value-parser@4.2.0: {}
postcss@8.4.31:
dependencies:
nanoid: 3.3.12
picocolors: 1.1.1
source-map-js: 1.2.1
postcss@8.5.14:
dependencies:
nanoid: 3.3.12
+3 -2
View File
@@ -8,5 +8,6 @@ allowBuilds:
msw: true
sharp: true
minimumReleaseAgeExclude:
- postcss@8.5.10
- ip-address@10.1.1
- postcss@8.5.14
overrides:
postcss@<8.5.10: ^8.5.14