From b1ec40264b808916f2c5cc840800b56f32d41614 Mon Sep 17 00:00:00 2001 From: Jan Shanel Date: Fri, 11 Aug 2023 15:22:33 +0200 Subject: [PATCH] /docs/usage page (#202) * Usage page init * Add resend version of usage * dual resend and nodemailer content * few more touches * Fixing and improving content * formatting * finetune * Add Ul mdx element stylings * Improve integrations texts * fix mdx styling * Make usage steps more real world --- .../components/MdxComponents/DocElements.tsx | 12 ++ .../MdxComponents/EmailSourceTabs.tsx | 28 ++++ src/docs/components/MdxComponents/index.ts | 12 +- src/docs/components/MdxComponents/utils.tsx | 29 +++- src/docs/constants/docsItems.ts | 5 + src/docs/content/Usage.mdx | 131 ++++++++++++++++++ src/docs/content/emails-data/hello-world.tsx | 49 +++++++ src/docs/content/index.ts | 1 + src/mdx-components.tsx | 5 +- 9 files changed, 267 insertions(+), 5 deletions(-) create mode 100644 src/docs/components/MdxComponents/EmailSourceTabs.tsx create mode 100644 src/docs/content/Usage.mdx create mode 100644 src/docs/content/emails-data/hello-world.tsx diff --git a/src/docs/components/MdxComponents/DocElements.tsx b/src/docs/components/MdxComponents/DocElements.tsx index 65b63e63..a5fe57e8 100644 --- a/src/docs/components/MdxComponents/DocElements.tsx +++ b/src/docs/components/MdxComponents/DocElements.tsx @@ -91,3 +91,15 @@ export const MdxCode = ({ }: React.ComponentPropsWithoutRef<"code">) => ( {children} ); + +export const MdxOl = ({ children }: React.ComponentPropsWithoutRef<"ol">) => ( +
    + {children} +
+); + +export const MdxUl = ({ children }: React.ComponentPropsWithoutRef<"ul">) => ( + +); diff --git a/src/docs/components/MdxComponents/EmailSourceTabs.tsx b/src/docs/components/MdxComponents/EmailSourceTabs.tsx new file mode 100644 index 00000000..f89bb314 --- /dev/null +++ b/src/docs/components/MdxComponents/EmailSourceTabs.tsx @@ -0,0 +1,28 @@ +import { TabbedCode, TabbedCodeItem } from "@components/Code"; +import { getContentEmail } from "@components/MdxComponents/utils"; + +export type ComponentSourceTabsProps = { + emailId: string; +}; + +export const EmailSourceTabs = async ({ + emailId, +}: ComponentSourceTabsProps) => { + const emailSource = await getContentEmail(emailId); + + const tabs: TabbedCodeItem[] = [ + { + id: `${emailSource.id}.tsx`, + htmlCode: emailSource.source, + }, + ]; + return ( +
+ +
+ ); +}; diff --git a/src/docs/components/MdxComponents/index.ts b/src/docs/components/MdxComponents/index.ts index 96ec5e19..10ac1526 100644 --- a/src/docs/components/MdxComponents/index.ts +++ b/src/docs/components/MdxComponents/index.ts @@ -1,13 +1,23 @@ import { ComponentSourceTabsProps } from "./ComponentSourceTabs"; export { type ComponentSourceTabsProps }; -export { MdxH1, MdxH2, MdxH3, MdxP, MdxA, MdxCode } from "./DocElements"; +export { + MdxH1, + MdxH2, + MdxH3, + MdxP, + MdxA, + MdxCode, + MdxUl, + MdxOl, +} from "./DocElements"; export { DocArticle } from "./DocArticle"; export { DocCollapsible } from "./DocCollapsible"; export { DocPre } from "./DocPre"; export { ComponentDemo } from "./ComponentDemo"; export { ComponentSourceTabs } from "./ComponentSourceTabs"; export { ComponentExamples } from "./ComponentExamples"; +export { EmailSourceTabs } from "./EmailSourceTabs"; export { getComponentSource, getComponentData, diff --git a/src/docs/components/MdxComponents/utils.tsx b/src/docs/components/MdxComponents/utils.tsx index 748751dd..1ed124c8 100644 --- a/src/docs/components/MdxComponents/utils.tsx +++ b/src/docs/components/MdxComponents/utils.tsx @@ -19,7 +19,8 @@ export const getComponentData = (type: string) => { return component; }; -export const CONTENT_DIR = "src/docs/examples"; +export const CONTENT_DIR = "src/docs/content"; +export const EXAMPLES_DIR = "src/docs/examples"; export const SOURCE_DIR = "src/mailingui/components"; /** @@ -32,7 +33,12 @@ export const getDemo = async (type: string): Promise => { const component = getComponentData(type); // Create directory path for component type - const demoPath = join(process.cwd(), CONTENT_DIR, component.type, "Demo.tsx"); + const demoPath = join( + process.cwd(), + EXAMPLES_DIR, + component.type, + "Demo.tsx" + ); // Initiate instance of highlighter const highlighter = await getHighlighter(); @@ -84,7 +90,7 @@ export const getComponentExamples = async (componentType: string) => { const component = getComponentData(componentType); // Create directory path for component type - const typePath = join(process.cwd(), CONTENT_DIR, component.type); + const typePath = join(process.cwd(), EXAMPLES_DIR, component.type); // Read all the files in that dir const files = readdirSync(typePath) @@ -121,3 +127,20 @@ export const getComponentExamples = async (componentType: string) => { return examples; }; + +export const getContentEmail = async (emailId: string) => { + const emailPath = join( + process.cwd(), + CONTENT_DIR, + "emails-data", + `${emailId}.tsx` + ); + const highlighter = await getHighlighter(); + const data = readFileSync(emailPath, "utf8"); + const source = await highlight(highlighter, data); + + return { + id: emailId, + source, + }; +}; diff --git a/src/docs/constants/docsItems.ts b/src/docs/constants/docsItems.ts index ec85646f..2b61284c 100644 --- a/src/docs/constants/docsItems.ts +++ b/src/docs/constants/docsItems.ts @@ -23,6 +23,11 @@ export const docsItems: DocItems = [ label: "Installation", description: "Learn about the MailingUI", }, + { + href: "/docs/usage", + label: "Usage", + description: "Learn how to use MailingUI", + }, ], }, { diff --git a/src/docs/content/Usage.mdx b/src/docs/content/Usage.mdx new file mode 100644 index 00000000..4e3d4069 --- /dev/null +++ b/src/docs/content/Usage.mdx @@ -0,0 +1,131 @@ +import { EmailSourceTabs } from "src/docs/components/MdxComponents"; + +import { + Tabs, + TabsContent, + TabsList, + TabsTrigger, +} from "@components/Tabs/Tabs"; + +# Usage + +As **MailingUI** is collection of email components developed on top of [react-email](https://react.email) library, it also utilize its rendering capabilities to efficiently generate accurate HTML markup that is optimized for various email clients. Once the HTML markup is rendered, you have the flexibility to employ any preferred email sending service or library to effectively deliver your emails. + +Usually you would use Javascript integration of email providers to send emails from your application. In this quick guide, we'll illustrate how to use **MailingUI** with [Resend](https://resend.com/) and [Nodemailer](https://nodemailer.com) providers. For alternative providers, please refer to their documentation or explore [Usefull links section](/docs/usage#usefull-links) for more information. + +## 1. Install dependencies + +For **MailingUI** install [see Installation section](/docs/installation). + + +
+ + Using Resend + Using Nodemailer + +
+ + + ```bash + npm install resend + ``` + + + ```bash + npm install nodemailer + ``` + +
+ +## 2. Create email template + +You can use both **MailingUI** components installed and and any other _react-email_ components to create email template. For example: + + + +## 3. Send email + +Usually you would place email integration on server side of your Next.js application like API route handler or similar. Our example uses Next.js app router _route handlers_ to send email. + + +
+ + Using Resend + Using Nodemailer + +
+ + + Given _Resend_ is operated by same team as _react-email_, email rendering is built-in in _Resend's SDK_. Thus you can use its sendEmail method directly. +
+ + ```ts + import { NextResponse } from 'next/server'; + import { Resend } from "resend"; + import HelloWorldEmail from "@/emails/test"; + + const resend = new Resend(process.env.RESEND_API_KEY); + + export async function POST() { + try { + const data = await resend.emails.send({ + from: process.env.RESEND_SENDER, + to: "user@email.com", + subject: "hello world", + react: HelloWorldEmail({farewellMessage: "Goodbye from MailingUI team!"}), + }); + return NextResponse.json(data); + } catch (error) { + return NextResponse.json({ error }); + } + } + ``` + +
+ + With Nodemailer (and generally most others providers) you need to render email template first and then send it with provider's API. +
+ + ```ts + import { NextResponse } from 'next/server'; + import { render } from "@react-email/render"; + import Nodemailer from "nodemailer"; + import HelloWorldEmail from "@/emails/test"; + + const nodemailer = Nodemailer.createTransport({ + host: process.env.NODEMAILER_HOST, + port: 465, + secure: true, + auth: { + user: process.env.NODEMAILER_USER, + pass: process.env.NODEMAILER_PASS, + }, + }); + + export async function POST() { + try { + const emailHtml = render(HelloWorldEmail({ + farewellMessage: "Goodbye from MailingUI team!" + })); + const data = await nodemailer.sendMail({ + from: process.env.NODEMAILER_SENDER, + to: "user@email.com", + subject: "hello world", + html: emailHtml, + }); + return NextResponse.json(data); + } catch (error) { + return NextResponse.json({ error }); + } + } + ``` + +
+
+ +# Usefull links + +To gain a deeper understanding of how to utilize combination of _react-email_, _MailingUI_ components and email providers services, please refer to following resources: + +- [render utility](https://react.email/docs/utilities/render) - how to use render utility, with more examples (plain text etc.) +- [react-email integrations](https://react.email/docs/integrations/overview) - other email sending services integrations, with _react-email_ examples diff --git a/src/docs/content/emails-data/hello-world.tsx b/src/docs/content/emails-data/hello-world.tsx new file mode 100644 index 00000000..944595e7 --- /dev/null +++ b/src/docs/content/emails-data/hello-world.tsx @@ -0,0 +1,49 @@ +import { Body, Container, Html, Row } from "@react-email/components"; +import { Text } from "@mailingui/components"; + +type EmailProps = { + welcomeMessage?: string; + farewellMessage?: string; +}; + +const HelloWorld = (props?: EmailProps) => { + const { welcomeMessage, farewellMessage } = { + welcomeMessage: "Hello World!", + ...props, + }; + return ( + + + + + {welcomeMessage} + + + + I am email written in React utilizing MailingUI components. + + + {farewellMessage && ( + + {farewellMessage} + + )} + + + + ); +}; +export default HelloWorld; + +const main = { + backgroundColor: "#f6f9fc", + padding: "60px 0 122px 0", +}; + +const container = { + backgroundColor: "#ffffff", + border: "1px solid #f0f0f0", + padding: "40px", + fontFamily: + "'Inter', 'HelveticaNeue-Light', 'Helvetica Neue Light', 'Helvetica Neue', Helvetica, Arial, 'Lucida Grande', sans-serif", +}; diff --git a/src/docs/content/index.ts b/src/docs/content/index.ts index edc3831d..0daeea01 100644 --- a/src/docs/content/index.ts +++ b/src/docs/content/index.ts @@ -4,6 +4,7 @@ import { ComponentType } from "react"; export const mdxDocs: Record> = { installation: dynamic(() => import(`src/docs/content/Installation.mdx`)), + usage: dynamic(() => import(`src/docs/content/Usage.mdx`)), components: dynamic(() => import(`src/docs/content/Components.mdx`)), "components/badges": dynamic( () => import(`src/docs/content/components/badges.mdx`) diff --git a/src/mdx-components.tsx b/src/mdx-components.tsx index 342d7b7e..3cca79e7 100644 --- a/src/mdx-components.tsx +++ b/src/mdx-components.tsx @@ -7,6 +7,8 @@ import { MdxA, MdxCode, DocPre, + MdxUl, + MdxOl, } from "@components/MdxComponents"; // This file allows you to provide custom React components @@ -23,12 +25,13 @@ export function useMDXComponents(components: MDXComponents): MDXComponents { // ), h1: MdxH1, h2: MdxH2, - h3: MdxH3, p: MdxP, pre: ({ children }) => {children}, a: MdxA, code: MdxCode, + ol: MdxOl, + ul: MdxUl, ...components, }; }