From b740a528e69493c706aee4ef7d605379d07d8dfc Mon Sep 17 00:00:00 2001 From: Kaylee <65376239+KayleeWilliams@users.noreply.github.com> Date: Mon, 17 Feb 2025 18:39:15 +0000 Subject: [PATCH 01/19] feat(react-email): added a theme switcher to the dev preview (#1749) Signed-off-by: dependabot[bot] Co-authored-by: gabriel miranda Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Bu Kinoshita <6929565+bukinoshita@users.noreply.github.com> --- .changeset/dirty-needles-chew.md | 5 + .../src/app/preview/[...slug]/preview.tsx | 29 +++-- .../src/components/icons/icon-moon.tsx | 16 +++ .../src/components/icons/icon-sun.tsx | 16 +++ packages/react-email/src/components/shell.tsx | 8 +- .../react-email/src/components/topbar.tsx | 112 ++++++++++++++++-- .../src/hooks/use-iframe-color-scheme.ts | 35 ++++++ 7 files changed, 186 insertions(+), 35 deletions(-) create mode 100644 .changeset/dirty-needles-chew.md create mode 100644 packages/react-email/src/components/icons/icon-moon.tsx create mode 100644 packages/react-email/src/components/icons/icon-sun.tsx create mode 100644 packages/react-email/src/hooks/use-iframe-color-scheme.ts diff --git a/.changeset/dirty-needles-chew.md b/.changeset/dirty-needles-chew.md new file mode 100644 index 0000000000..acc7dd42e2 --- /dev/null +++ b/.changeset/dirty-needles-chew.md @@ -0,0 +1,5 @@ +--- +"react-email": minor +--- + +Theme switcher for email template diff --git a/packages/react-email/src/app/preview/[...slug]/preview.tsx b/packages/react-email/src/app/preview/[...slug]/preview.tsx index e33195ede1..3f1c664aee 100644 --- a/packages/react-email/src/app/preview/[...slug]/preview.tsx +++ b/packages/react-email/src/app/preview/[...slug]/preview.tsx @@ -1,7 +1,7 @@ 'use client'; import { usePathname, useRouter, useSearchParams } from 'next/navigation'; -import React from 'react'; +import React, { useRef } from 'react'; import { Toaster } from 'sonner'; import type { EmailRenderingResult } from '../../../actions/render-email-by-path'; import { CodeContainer } from '../../../components/code-container'; @@ -9,6 +9,7 @@ import { Shell } from '../../../components/shell'; import { Tooltip } from '../../../components/tooltip'; import { useEmailRenderingResult } from '../../../hooks/use-email-rendering-result'; import { useHotreload } from '../../../hooks/use-hot-reload'; +import { useIframeColorScheme } from '../../../hooks/use-iframe-color-scheme'; import { useRenderingMetadata } from '../../../hooks/use-rendering-metadata'; import { RenderingError } from './rendering-error'; @@ -29,6 +30,7 @@ const Preview = ({ const pathname = usePathname(); const searchParams = useSearchParams(); + const activeTheme = searchParams.get('theme') ?? 'light'; const activeView = searchParams.get('view') ?? 'desktop'; const activeLang = searchParams.get('lang') ?? 'jsx'; @@ -43,6 +45,9 @@ const Preview = ({ serverRenderingResult, ); + const iframeRef = useRef(null); + useIframeColorScheme(iframeRef, activeTheme); + if (process.env.NEXT_PUBLIC_IS_BUILDING !== 'true') { // this will not change on runtime so it doesn't violate // the rules of hooks @@ -60,28 +65,20 @@ const Preview = ({ }); } - const handleViewChange = (view: string) => { - const params = new URLSearchParams(searchParams); - params.set('view', view); - router.push(`${pathname}?${params.toString()}`); - }; + const hasNoErrors = typeof renderedEmailMetadata !== 'undefined'; - const handleLangChange = (lang: string) => { + const setActiveLang = (lang: string) => { const params = new URLSearchParams(searchParams); params.set('view', 'source'); params.set('lang', lang); router.push(`${pathname}?${params.toString()}`); }; - const hasNoErrors = typeof renderedEmailMetadata !== 'undefined'; - return ( {/* This relative is so that when there is any error the user can still switch between emails */}
@@ -93,7 +90,8 @@ const Preview = ({ <> {activeView === 'desktop' && (