diff --git a/package-lock.json b/package-lock.json index 4a63f482..5320a3fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,4 +1,6 @@ - "version": "2.17.1", +{ + "name": "@quintype/amp", + "version": "2.17.2-custom-fonts.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 9ae34222..08c01a3d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@quintype/amp", - "version": "2.17.1", + "version": "2.17.2-custom-fonts.0", "description": "Quintype's AMP component library for publisher apps to create amp layouts", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/src/__fixtures__/config.fixture.tsx b/src/__fixtures__/config.fixture.tsx index 8289235b..b2f39567 100644 --- a/src/__fixtures__/config.fixture.tsx +++ b/src/__fixtures__/config.fixture.tsx @@ -581,7 +581,8 @@ export const ampConfig: AMPConfig = { secondary: { url: "PT+Serif:400,400italic,700,700italic", family: "'PT Serif', sans-serif" - } + }, + custom: "https://fonts.googleapis.com/css?family=Tangerine" }, colors: { primary: "#294f32", diff --git a/src/atoms/fonts/__snapshots__/fonts.test.tsx.snap b/src/atoms/fonts/__snapshots__/fonts.test.tsx.snap index 63998fd7..c8b4ffdf 100644 --- a/src/atoms/fonts/__snapshots__/fonts.test.tsx.snap +++ b/src/atoms/fonts/__snapshots__/fonts.test.tsx.snap @@ -22,5 +22,29 @@ exports[`Fonts component Should match snapshot 1`] = ` href="https://fonts.googleapis.com/css?family=Mukta+Malar:400,400italic,700,700italic&display=swap" rel="preload" /> + + + + `; diff --git a/src/atoms/fonts/fonts.test.tsx b/src/atoms/fonts/fonts.test.tsx index 4b73f67f..ef997c5b 100644 --- a/src/atoms/fonts/fonts.test.tsx +++ b/src/atoms/fonts/fonts.test.tsx @@ -14,9 +14,20 @@ const dummyConfig = { family: '"Mukta Malar",sans-serif' } } + }, + opts: { + render: { + customFonts: { + primary1: "https://fea.assettype.com/malibu/assets/MalibuHeadlineSemiBold1.woff2", + secondary1: "https://fea.assettype.com/malibu/assets/MalibuHeadlineTextRegular1.woff2", + primary2: "https://fea.assettype.com/malibu/assets/MalibuHeadlineSemiBold2.woff2", + secondary2: "https://fea.assettype.com/malibu/assets/MalibuHeadlineTextRegular2.woff2" + } + } } }; + describe("Fonts component", () => { it("Should match snapshot", () => { const wrapper = shallow(); @@ -73,4 +84,388 @@ describe("Fonts component", () => { .prop("href") ).toBe("https://fonts.googleapis.com/css?family=Mukta+Malar:400,400italic,700,700italic&display=swap"); }); + it("Should preconnect to fonts provider and preload custom primary 1 font when passed on opts object", () => { + const wrapper = shallow(); + expect( + wrapper + .find(`link`) + .at(0) + .prop("rel") + ).toBe("preconnect dns-prefetch"); + expect( + wrapper + .find(`link`) + .at(0) + .prop("crossorigin") + ).toBe("anonymous"); + expect( + wrapper + .find(`link`) + .at(1) + .prop("rel") + ).toBe("preload"); + expect( + wrapper + .find(`link`) + .at(1) + .prop("crossorigin") + ).toBe("anonymous"); + expect( + wrapper + .find(`link`) + .at(1) + .prop("href") + ).toBe("https://fonts.googleapis.com/css?family=Mukta+Malar:300,400,600,700&display=swap"); + expect( + wrapper + .find(`link`) + .at(2) + .prop("rel") + ).toBe("preload"); + expect( + wrapper + .find(`link`) + .at(2) + .prop("crossorigin") + ).toBe("anonymous"); + expect( + wrapper + .find(`link`) + .at(2) + .prop("href") + ).toBe("https://fonts.googleapis.com/css?family=Mukta+Malar:400,400italic,700,700italic&display=swap"); + expect( + wrapper + .find(`link`) + .at(3) + .prop("rel") + ).toBe("preload"); + expect( + wrapper + .find(`link`) + .at(3) + .prop("crossorigin") + ).toBe("anonymous"); + expect( + wrapper + .find(`link`) + .at(3) + .prop("href") + ).toBe("https://fea.assettype.com/malibu/assets/MalibuHeadlineSemiBold1.woff2"); + }); + it("Should preconnect to fonts provider and preload custom secondary 1 font when passed on opts object", () => { + const wrapper = shallow(); + expect( + wrapper + .find(`link`) + .at(0) + .prop("rel") + ).toBe("preconnect dns-prefetch"); + expect( + wrapper + .find(`link`) + .at(0) + .prop("crossorigin") + ).toBe("anonymous"); + expect( + wrapper + .find(`link`) + .at(1) + .prop("rel") + ).toBe("preload"); + expect( + wrapper + .find(`link`) + .at(1) + .prop("crossorigin") + ).toBe("anonymous"); + expect( + wrapper + .find(`link`) + .at(1) + .prop("href") + ).toBe("https://fonts.googleapis.com/css?family=Mukta+Malar:300,400,600,700&display=swap"); + expect( + wrapper + .find(`link`) + .at(2) + .prop("rel") + ).toBe("preload"); + expect( + wrapper + .find(`link`) + .at(2) + .prop("crossorigin") + ).toBe("anonymous"); + expect( + wrapper + .find(`link`) + .at(2) + .prop("href") + ).toBe("https://fonts.googleapis.com/css?family=Mukta+Malar:400,400italic,700,700italic&display=swap"); + expect( + wrapper + .find(`link`) + .at(3) + .prop("rel") + ).toBe("preload"); + expect( + wrapper + .find(`link`) + .at(3) + .prop("crossorigin") + ).toBe("anonymous"); + expect( + wrapper + .find(`link`) + .at(3) + .prop("href") + ).toBe("https://fea.assettype.com/malibu/assets/MalibuHeadlineSemiBold1.woff2"); + expect( + wrapper + .find(`link`) + .at(4) + .prop("rel") + ).toBe("preload"); + expect( + wrapper + .find(`link`) + .at(4) + .prop("crossorigin") + ).toBe("anonymous"); + expect( + wrapper + .find(`link`) + .at(4) + .prop("href") + ).toBe("https://fea.assettype.com/malibu/assets/MalibuHeadlineTextRegular1.woff2"); + }); + it("Should preconnect to fonts provider and preload custom primary 2 font when passed on opts object", () => { + const wrapper = shallow(); + expect( + wrapper + .find(`link`) + .at(0) + .prop("rel") + ).toBe("preconnect dns-prefetch"); + expect( + wrapper + .find(`link`) + .at(0) + .prop("crossorigin") + ).toBe("anonymous"); + expect( + wrapper + .find(`link`) + .at(1) + .prop("rel") + ).toBe("preload"); + expect( + wrapper + .find(`link`) + .at(1) + .prop("crossorigin") + ).toBe("anonymous"); + expect( + wrapper + .find(`link`) + .at(1) + .prop("href") + ).toBe("https://fonts.googleapis.com/css?family=Mukta+Malar:300,400,600,700&display=swap"); + expect( + wrapper + .find(`link`) + .at(2) + .prop("rel") + ).toBe("preload"); + expect( + wrapper + .find(`link`) + .at(2) + .prop("crossorigin") + ).toBe("anonymous"); + expect( + wrapper + .find(`link`) + .at(2) + .prop("href") + ).toBe("https://fonts.googleapis.com/css?family=Mukta+Malar:400,400italic,700,700italic&display=swap"); + expect( + wrapper + .find(`link`) + .at(3) + .prop("rel") + ).toBe("preload"); + expect( + wrapper + .find(`link`) + .at(3) + .prop("crossorigin") + ).toBe("anonymous"); + expect( + wrapper + .find(`link`) + .at(3) + .prop("href") + ).toBe("https://fea.assettype.com/malibu/assets/MalibuHeadlineSemiBold1.woff2"); + expect( + wrapper + .find(`link`) + .at(4) + .prop("rel") + ).toBe("preload"); + expect( + wrapper + .find(`link`) + .at(4) + .prop("crossorigin") + ).toBe("anonymous"); + expect( + wrapper + .find(`link`) + .at(4) + .prop("href") + ).toBe("https://fea.assettype.com/malibu/assets/MalibuHeadlineTextRegular1.woff2"); + expect( + wrapper + .find(`link`) + .at(5) + .prop("rel") + ).toBe("preload"); + expect( + wrapper + .find(`link`) + .at(5) + .prop("crossorigin") + ).toBe("anonymous"); + expect( + wrapper + .find(`link`) + .at(5) + .prop("href") + ).toBe("https://fea.assettype.com/malibu/assets/MalibuHeadlineSemiBold2.woff2"); + }); + it("Should preconnect to fonts provider and preload custom secondary 2 font when passed on opts object", () => { + const wrapper = shallow(); + expect( + wrapper + .find(`link`) + .at(0) + .prop("rel") + ).toBe("preconnect dns-prefetch"); + expect( + wrapper + .find(`link`) + .at(0) + .prop("crossorigin") + ).toBe("anonymous"); + expect( + wrapper + .find(`link`) + .at(1) + .prop("rel") + ).toBe("preload"); + expect( + wrapper + .find(`link`) + .at(1) + .prop("crossorigin") + ).toBe("anonymous"); + expect( + wrapper + .find(`link`) + .at(1) + .prop("href") + ).toBe("https://fonts.googleapis.com/css?family=Mukta+Malar:300,400,600,700&display=swap"); + expect( + wrapper + .find(`link`) + .at(2) + .prop("rel") + ).toBe("preload"); + expect( + wrapper + .find(`link`) + .at(2) + .prop("crossorigin") + ).toBe("anonymous"); + expect( + wrapper + .find(`link`) + .at(2) + .prop("href") + ).toBe("https://fonts.googleapis.com/css?family=Mukta+Malar:400,400italic,700,700italic&display=swap"); + expect( + wrapper + .find(`link`) + .at(3) + .prop("rel") + ).toBe("preload"); + expect( + wrapper + .find(`link`) + .at(3) + .prop("crossorigin") + ).toBe("anonymous"); + expect( + wrapper + .find(`link`) + .at(3) + .prop("href") + ).toBe("https://fea.assettype.com/malibu/assets/MalibuHeadlineSemiBold1.woff2"); + expect( + wrapper + .find(`link`) + .at(4) + .prop("rel") + ).toBe("preload"); + expect( + wrapper + .find(`link`) + .at(4) + .prop("crossorigin") + ).toBe("anonymous"); + expect( + wrapper + .find(`link`) + .at(4) + .prop("href") + ).toBe("https://fea.assettype.com/malibu/assets/MalibuHeadlineTextRegular1.woff2"); + expect( + wrapper + .find(`link`) + .at(5) + .prop("rel") + ).toBe("preload"); + expect( + wrapper + .find(`link`) + .at(5) + .prop("crossorigin") + ).toBe("anonymous"); + expect( + wrapper + .find(`link`) + .at(5) + .prop("href") + ).toBe("https://fea.assettype.com/malibu/assets/MalibuHeadlineSemiBold2.woff2"); + expect( + wrapper + .find(`link`) + .at(6) + .prop("rel") + ).toBe("preload"); + expect( + wrapper + .find(`link`) + .at(6) + .prop("crossorigin") + ).toBe("anonymous"); + expect( + wrapper + .find(`link`) + .at(6) + .prop("href") + ).toBe("https://fea.assettype.com/malibu/assets/MalibuHeadlineTextRegular2.woff2"); + }); }); diff --git a/src/atoms/fonts/fonts.tsx b/src/atoms/fonts/fonts.tsx index 209458b8..6275a8fe 100644 --- a/src/atoms/fonts/fonts.tsx +++ b/src/atoms/fonts/fonts.tsx @@ -1,10 +1,11 @@ import React from "react"; import { Helmet } from "react-helmet"; import { withStoryAndConfig } from "../../context"; +import get from "lodash.get"; export const FontsBase = ({ config }) => { const { primary, secondary } = config.ampConfig.fonts; - + const customFonts = get(config, ["opts", "render", "customFonts"], null); return ( @@ -20,8 +21,49 @@ export const FontsBase = ({ config }) => { crossorigin="anonymous" href={`https://fonts.googleapis.com/css?family=${secondary.url}&display=swap`} /> + + + + ); }; +/** + * Amp library supports google fonts as well as custom fonts. This Fonts component accepts primary and secondary google fonts from BOlD AMP settings `settings > configure > AMP` and four different custom fonts from render props `customFonts`. These custom font urls are passed to the link tags that are preloaded. + * In case customFonts is passed in the config, it is rendered along with the fonts mentioned in BOLD. + * + * Custom fonts can be hosted on allowed origins or can be added in the public folder of your app. + * If added in public folder, mention the asset host and path in the url like this "https://fea.assettype.com/malibu/assets/ + MalibuHeadlineSemiBold.woff2". + * + * The following origins are allowlisted and allowed for font serving via link tags as per official AMP documentation: + * + * Typography.com: https://cloud.typography.com + * Fonts.com: https://fast.fonts.net + * Google Fonts: https://fonts.googleapis.com + * Typekit: https://use.typekit.net + * Font Awesome: https://maxcdn.bootstrapcdn.com, https://use.fontawesome.com + * + * + * ```javascript + * ... + * ampRoutes(app, { + * render: { + * customFonts?: { + primary1?: "https://fea.assettype.com/malibu/assets/MalibuHeadlineSemiBold.woff2"; + secondary1?: "https://fea.assettype.com/malibu/assets/MalibuHeadlineTextRegular.woff2"; + primary2?: "https://fea.assettype.com/malibu/assets/MalibuHeadlineSemiBold2.woff2"; + secondary2?: "https://fea.assettype.com/malibu/assets/MalibuHeadlineTextRegular2.woff2"; + }; + * } + * }) + * ... + * ``` + * + * @category Atoms + * @module Fonts + * @component + */ + export const Fonts = withStoryAndConfig(FontsBase); diff --git a/src/atoms/subscriptions/subscriptions-paywall/__snapshots__/subscription-paywall.test.tsx.snap b/src/atoms/subscriptions/subscriptions-paywall/__snapshots__/subscription-paywall.test.tsx.snap index c2d717f2..86902e74 100644 --- a/src/atoms/subscriptions/subscriptions-paywall/__snapshots__/subscription-paywall.test.tsx.snap +++ b/src/atoms/subscriptions/subscriptions-paywall/__snapshots__/subscription-paywall.test.tsx.snap @@ -43,6 +43,7 @@ exports[`Subscriptions should render subscriptions 1`] = ` }, }, "fonts": Object { + "custom": "https://fonts.googleapis.com/css?family=Tangerine", "primary": Object { "family": "\\"Open Sans\\", sans-serif", "url": "Open+Sans:300,400,600,700", diff --git a/src/molecules/related-stories/__snapshots__/related-stories.test.tsx.snap b/src/molecules/related-stories/__snapshots__/related-stories.test.tsx.snap index 7fcac687..468ca1cd 100644 --- a/src/molecules/related-stories/__snapshots__/related-stories.test.tsx.snap +++ b/src/molecules/related-stories/__snapshots__/related-stories.test.tsx.snap @@ -44,6 +44,7 @@ exports[`RelatedStories should render default 1`] = ` }, }, "fonts": Object { + "custom": "https://fonts.googleapis.com/css?family=Tangerine", "primary": Object { "family": "\\"Open Sans\\", sans-serif", "url": "Open+Sans:300,400,600,700", diff --git a/src/templates/generic-story/__snapshots__/generic-story.test.tsx.snap b/src/templates/generic-story/__snapshots__/generic-story.test.tsx.snap index 98e1b81d..25dd46e5 100644 --- a/src/templates/generic-story/__snapshots__/generic-story.test.tsx.snap +++ b/src/templates/generic-story/__snapshots__/generic-story.test.tsx.snap @@ -43,6 +43,7 @@ exports[`GenericStory Template should render 1`] = ` }, }, "fonts": Object { + "custom": "https://fonts.googleapis.com/css?family=Tangerine", "primary": Object { "family": "\\"Open Sans\\", sans-serif", "url": "Open+Sans:300,400,600,700", diff --git a/src/templates/live-blog/__snapshots__/live-blog.validation.test.tsx.snap b/src/templates/live-blog/__snapshots__/live-blog.validation.test.tsx.snap index f8a62320..0e24c11e 100644 --- a/src/templates/live-blog/__snapshots__/live-blog.validation.test.tsx.snap +++ b/src/templates/live-blog/__snapshots__/live-blog.validation.test.tsx.snap @@ -43,6 +43,7 @@ exports[`The LiveBlog Default Template should match snapshot 1`] = ` }, }, "fonts": Object { + "custom": "https://fonts.googleapis.com/css?family=Tangerine", "primary": Object { "family": "\\"Open Sans\\", sans-serif", "url": "Open+Sans:300,400,600,700", diff --git a/src/templates/visual-story/__snapshots__/visual-story.test.tsx.snap b/src/templates/visual-story/__snapshots__/visual-story.test.tsx.snap index 869629e1..731c2286 100644 --- a/src/templates/visual-story/__snapshots__/visual-story.test.tsx.snap +++ b/src/templates/visual-story/__snapshots__/visual-story.test.tsx.snap @@ -43,6 +43,7 @@ exports[`visual story template should match snapshot 1`] = ` }, }, "fonts": Object { + "custom": "https://fonts.googleapis.com/css?family=Tangerine", "primary": Object { "family": "\\"Open Sans\\", sans-serif", "url": "Open+Sans:300,400,600,700", diff --git a/src/types/config.ts b/src/types/config.ts index 15ae703f..4d95ac31 100644 --- a/src/types/config.ts +++ b/src/types/config.ts @@ -73,6 +73,7 @@ export interface AMPConfig { url: string; family: string; }; + custom: string; }; colors: Colors; "logo-url": string; @@ -151,6 +152,12 @@ export interface ConfigOpts { templates?: object; slots?: SlotsTypes; render?: { + customFonts?: { + primary1?: string | null; + secondary1?: string | null; + primary2?: string | null; + secondary2?: string | null; + }; headerCardRender?: (props: Config) => any; navbarRender?: (props: NavbarTypes) => any; relatedStoriesRender?: (props: RelatedStoriesRenderPropTypes) => any;