mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-07-13 14:35:33 +10:00
chore: update postcss to version 8.5.14 and sort font weights in typography components
This commit is contained in:
@@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 [];
|
||||
|
||||
|
||||
@@ -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"]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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 },
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
Generated
+7
-11
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user