feat(blog): Add support for email social icon + resize default social icon a bit (#11425)
Some checks failed
Argos CI / take-screenshots (push) Has been cancelled
Build Hash Router / Build Hash Router (push) Has been cancelled
Canary Release / Publish Canary (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Continuous Releases / Continuous Releases (push) Has been cancelled
E2E Tests / E2E — Yarn v1 (20) (push) Has been cancelled
E2E Tests / E2E — Yarn v1 (20.0) (push) Has been cancelled
E2E Tests / E2E — Yarn v1 (22) (push) Has been cancelled
E2E Tests / E2E — Yarn v1 (24) (push) Has been cancelled
E2E Tests / E2E — Yarn v1 Windows (push) Has been cancelled
E2E Tests / E2E — Yarn Berry (node-modules, -s) (push) Has been cancelled
E2E Tests / E2E — Yarn Berry (node-modules, -st) (push) Has been cancelled
E2E Tests / E2E — Yarn Berry (pnp, -s) (push) Has been cancelled
E2E Tests / E2E — Yarn Berry (pnp, -st) (push) Has been cancelled
E2E Tests / E2E — npm (push) Has been cancelled
E2E Tests / E2E — pnpm (push) Has been cancelled

This commit is contained in:
Sébastien Lorber 2025-09-23 15:08:02 +02:00 committed by GitHub
parent 50ca86aa04
commit 70f6312c0d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 64 additions and 10 deletions

View File

@ -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",

View File

@ -27,6 +27,7 @@ export const AuthorSocialsSchema = Joi.object<AuthorSocials>({
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);

View File

@ -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';

View File

@ -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<string, SocialPlatformConfig> = {
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 (
<Link className={styles.authorSocialLink} href={link} title={label}>
<Icon className={clsx(styles.authorSocialLink)} />
<Icon className={clsx(styles.authorSocialIcon)} />
</Link>
);
}

View File

@ -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<SVGSVGElement>): ReactNode {
return (
<svg
@ -22,11 +22,11 @@ function DefaultSocial(props: SVGProps<SVGSVGElement>): ReactNode {
strokeLinejoin="round"
{...props}>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M3 12a9 9 0 1 0 18 0a9 9 0 0 0 -18 0" />
<path d="M3.6 9h16.8" />
<path d="M3.6 15h16.8" />
<path d="M11.5 3a17 17 0 0 0 0 18" />
<path d="M12.5 3a17 17 0 0 1 0 18" />
<path d="M1.2 12a10.8 10.8 0 1 0 21.6 0a10.8 10.8 0 0 0 -21.6 0" />
<path d="M1.92 8.4h20.16" />
<path d="M1.92 15.6h20.16" />
<path d="M11.4 1.2a20.4 20.4 0 0 0 0 21.6" />
<path d="M12.6 1.2a20.4 20.4 0 0 1 0 21.6" />
</svg>
);
}

View File

@ -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<SVGSVGElement>): ReactNode {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
{...props}>
<path stroke="none" d="M0 0h24v24H0z" />
<path d="M7.2 12a4.8 4.8 0 1 0 9.6 0 4.8 4.8 0 1 0-9.6 0" />
<path d="M16.8 12v1.8a3 3 0 0 0 6 0V12a10.8 10.8 0 1 0-6.6 9.936" />
</svg>
);
}
export default Email;

View File

@ -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:

View File

@ -6,6 +6,7 @@ slorber:
page: true
socials:
x: sebastienlorber
email: seb@example.com
ozaki:
name: ozaki

View File

@ -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<string, string>;
@ -368,6 +368,7 @@ slorber:
socials:
x: sebastienlorber
github: slorber
email: seb@example.com
jmarcey:
name: Joel Marcey

View File

@ -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