diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/authorsSocials.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/authorsSocials.test.ts index 9bc8afaf2c..ecbc3ea12a 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/authorsSocials.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/authorsSocials.test.ts @@ -21,11 +21,13 @@ describe('normalizeSocials', () => { twitch: 'gingergeek', youtube: 'gingergeekuk', mastodon: 'Mastodon', + email: 'seb@example.com', }; expect(normalizeSocials(socials)).toMatchInlineSnapshot(` { "bluesky": "https://bsky.app/profile/gingergeek.co.uk", + "email": "mailto:seb@example.com", "github": "https://github.com/ozakione", "instagram": "https://www.instagram.com/thisweekinreact", "linkedin": "https://www.linkedin.com/in/ozakione/", @@ -48,11 +50,13 @@ describe('normalizeSocials', () => { instaGRam: 'thisweekinreact', BLUESKY: 'gingergeek.co.uk', tHrEaDs: 'gingergeekuk', + eMAil: 'seb@example.com', }; expect(normalizeSocials(socials)).toMatchInlineSnapshot(` { "bluesky": "https://bsky.app/profile/gingergeek.co.uk", + "email": "mailto:seb@example.com", "github": "https://github.com/ozakione", "instagram": "https://www.instagram.com/thisweekinreact", "linkedin": "https://www.linkedin.com/in/ozakione/", @@ -69,6 +73,7 @@ describe('normalizeSocials', () => { linkedin: 'https://linkedin.com/ozakione', github: 'https://github.com/ozakione', stackoverflow: 'https://stackoverflow.com/ozakione', + email: 'mailto:seb@example.com', }; expect(normalizeSocials(socials)).toEqual(socials); @@ -81,10 +86,12 @@ describe('normalizeSocials', () => { github: 'https://github.com/ozakione', stackoverflow: 'https://stackoverflow.com/ozakione', mastodon: 'https://hachyderm.io/@hachyderm', + email: 'mailto:seb@example.com', }; expect(normalizeSocials(socials)).toMatchInlineSnapshot(` { + "email": "mailto:seb@example.com", "github": "https://github.com/ozakione", "linkedin": "https://www.linkedin.com/in/ozakione/", "mastodon": "https://hachyderm.io/@hachyderm", diff --git a/packages/docusaurus-plugin-content-blog/src/authorsSocials.ts b/packages/docusaurus-plugin-content-blog/src/authorsSocials.ts index 80bb3c5864..1517381e06 100644 --- a/packages/docusaurus-plugin-content-blog/src/authorsSocials.ts +++ b/packages/docusaurus-plugin-content-blog/src/authorsSocials.ts @@ -27,6 +27,7 @@ export const AuthorSocialsSchema = Joi.object({ mastodon: Joi.string(), twitch: Joi.string(), youtube: Joi.string(), + email: Joi.string(), }).unknown(); type PredefinedPlatformNormalizer = (value: string) => string; @@ -47,12 +48,12 @@ const PredefinedPlatformNormalizers: Record< mastodon: (handle: string) => `https://mastodon.social/@${handle}`, // can be in format user@other.server and it will redirect if needed twitch: (handle: string) => `https://twitch.tv/${handle}`, youtube: (handle: string) => `https://youtube.com/@${handle}`, // https://support.google.com/youtube/answer/6180214?hl=en + email: (email: string) => `mailto:${email}`, }; type SocialEntry = [string, string]; function normalizeSocialEntry([platform, value]: SocialEntry): SocialEntry { - const normalizer = PredefinedPlatformNormalizers[platform.toLowerCase()]; if (typeof value !== 'string') { throw new Error( `Author socials should be usernames/userIds/handles, or fully qualified HTTP(s) absolute URLs. @@ -60,7 +61,9 @@ Social platform '${platform}' has illegal value '${value}'`, ); } const isAbsoluteUrl = - value.startsWith('http://') || value.startsWith('https://'); + value.startsWith('http://') || + value.startsWith('https://') || + value.startsWith('mailto:'); if (isAbsoluteUrl) { return [platform, value]; } else if (value.includes('/')) { @@ -69,6 +72,7 @@ Social platform '${platform}' has illegal value '${value}'`, Social platform '${platform}' has illegal value '${value}'`, ); } + const normalizer = PredefinedPlatformNormalizers[platform.toLowerCase()]; if (normalizer && !isAbsoluteUrl) { const normalizedPlatform = platform.toLowerCase(); const normalizedValue = normalizer(value); diff --git a/packages/docusaurus-theme-classic/src/theme-classic.d.ts b/packages/docusaurus-theme-classic/src/theme-classic.d.ts index c801c97e79..c38431f8b7 100644 --- a/packages/docusaurus-theme-classic/src/theme-classic.d.ts +++ b/packages/docusaurus-theme-classic/src/theme-classic.d.ts @@ -1894,6 +1894,14 @@ declare module '@theme/Icon/Socials/Twitch' { export default function Twitch(props: Props): ReactNode; } +declare module '@theme/Icon/Socials/Email' { + import type {ComponentProps, ReactNode} from 'react'; + + export interface Props extends ComponentProps<'svg'> {} + + export default function Email(props: Props): ReactNode; +} + declare module '@theme/Icon/Socials/Mastodon' { import type {ComponentProps, ReactNode} from 'react'; diff --git a/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/Socials/index.tsx b/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/Socials/index.tsx index 1ba60c5ae7..e163a603de 100644 --- a/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/Socials/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/Blog/Components/Author/Socials/index.tsx @@ -23,6 +23,7 @@ import Threads from '@theme/Icon/Socials/Threads'; import Youtube from '@theme/Icon/Socials/YouTube'; import Mastodon from '@theme/Icon/Socials/Mastodon'; import Twitch from '@theme/Icon/Socials/Twitch'; +import Email from '@theme/Icon/Socials/Email'; import styles from './styles.module.css'; @@ -42,6 +43,7 @@ const SocialPlatformConfigs: Record = { mastodon: {Icon: Mastodon, label: 'Mastodon'}, youtube: {Icon: Youtube, label: 'YouTube'}, twitch: {Icon: Twitch, label: 'Twitch'}, + email: {Icon: Email, label: 'Email'}, }; function getSocialPlatformConfig(platformKey: string): SocialPlatformConfig { @@ -57,7 +59,7 @@ function SocialLink({platform, link}: {platform: string; link: string}) { const {Icon, label} = getSocialPlatformConfig(platform); return ( - + ); } diff --git a/packages/docusaurus-theme-classic/src/theme/Icon/Socials/Default/index.tsx b/packages/docusaurus-theme-classic/src/theme/Icon/Socials/Default/index.tsx index 29f676caa9..b66fd118e9 100644 --- a/packages/docusaurus-theme-classic/src/theme/Icon/Socials/Default/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/Icon/Socials/Default/index.tsx @@ -7,7 +7,7 @@ import type {ReactNode, SVGProps} from 'react'; -// SVG Source: https://tabler.io/ +// SVG Source: https://github.com/tabler/tabler-icons function DefaultSocial(props: SVGProps): ReactNode { return ( ): ReactNode { strokeLinejoin="round" {...props}> - - - - - + + + + + ); } diff --git a/packages/docusaurus-theme-classic/src/theme/Icon/Socials/Email/index.tsx b/packages/docusaurus-theme-classic/src/theme/Icon/Socials/Email/index.tsx new file mode 100644 index 0000000000..b44991aa8b --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/Icon/Socials/Email/index.tsx @@ -0,0 +1,28 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import type {ReactNode, SVGProps} from 'react'; + +// SVG Source: https://github.com/tabler/tabler-icons +function Email(props: SVGProps): ReactNode { + return ( + + + + + + ); +} +export default Email; diff --git a/website/_dogfooding/_blog tests/2024-07-03-multiple-authors.mdx b/website/_dogfooding/_blog tests/2024-07-03-multiple-authors.mdx index b13b5215bd..59446635e7 100644 --- a/website/_dogfooding/_blog tests/2024-07-03-multiple-authors.mdx +++ b/website/_dogfooding/_blog tests/2024-07-03-multiple-authors.mdx @@ -11,6 +11,7 @@ authors: linkedin: https://www.linkedin.com/in/sebastienlorber/ instagram: https://www.instagram.com/thisweekinreact/ newsletter: https://thisweekinreact.com/newsletter + email: seb@example.com - name: Sébastien Lorber imageURL: https://github.com/slorber.png socials: diff --git a/website/_dogfooding/_blog tests/authors.yml b/website/_dogfooding/_blog tests/authors.yml index a7dac6a959..987c70a2c9 100644 --- a/website/_dogfooding/_blog tests/authors.yml +++ b/website/_dogfooding/_blog tests/authors.yml @@ -6,6 +6,7 @@ slorber: page: true socials: x: sebastienlorber + email: seb@example.com ozaki: name: ozaki diff --git a/website/docs/api/plugins/plugin-content-blog.mdx b/website/docs/api/plugins/plugin-content-blog.mdx index 8dee6fdcda..f8a17e4347 100644 --- a/website/docs/api/plugins/plugin-content-blog.mdx +++ b/website/docs/api/plugins/plugin-content-blog.mdx @@ -281,7 +281,7 @@ type AuthorKey = string; // Social platform name -> Social platform link // Example: {MyPlatform: 'https://myplatform.com/myusername'} // Pre-defined platforms -// ("x", "github", "twitter", "linkedin", "stackoverflow", "instagram", "bluesky", "mastodon", "threads", "twitch", "youtube") accept handles: +// ("x", "github", "twitter", "linkedin", "stackoverflow", "instagram", "bluesky", "mastodon", "threads", "twitch", "youtube", "email") accept handles: // Example: {github: 'slorber'} type AuthorSocials = Record; @@ -368,6 +368,7 @@ slorber: socials: x: sebastienlorber github: slorber + email: seb@example.com jmarcey: name: Joel Marcey diff --git a/website/docs/blog.mdx b/website/docs/blog.mdx index aec3c7b3a9..92a9551150 100644 --- a/website/docs/blog.mdx +++ b/website/docs/blog.mdx @@ -62,6 +62,7 @@ authors: socials: x: sebastienlorber github: slorber + email: seb@example.com tags: [hello, docusaurus-v2] image: https://i.imgur.com/mErPwqL.png hide_table_of_contents: false @@ -303,6 +304,7 @@ slorber: socials: x: sebastienlorber github: slorber + email: seb@example.com ``` :::tip