Skip to content

Commit

Permalink
feat(email): storybook like email package
Browse files Browse the repository at this point in the history
  • Loading branch information
douglasduteil committed Oct 17, 2024
1 parent 442f3d2 commit e14d9c4
Show file tree
Hide file tree
Showing 24 changed files with 1,358 additions and 119 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/node_modules
node_modules
*.swp
*.swo
*.orig
Expand Down
353 changes: 235 additions & 118 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
"#cypress/*": "./cypress/*.ts"
},
"main": "src/index.js",
"workspaces": [
"packages/*"
],
"scripts": {
"build": "run-s build:**",
"build:assets": "vite build --clearScreen false",
Expand Down
62 changes: 62 additions & 0 deletions packages/email/.storybook/ChangeView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//

export class ChangeView extends HTMLElement {
static tag = "x-change-view" as const;
static define() {
customElements.define(ChangeView.tag, this);
}
constructor() {
super();
this.attachShadow({ mode: "open" });
}
connectedCallback() {
this.#render();

this.#inputs.forEach((element) => {
element.addEventListener("click", () => {
this.#view = element.value as "desktop" | "html";
});
});
}
#render() {
if (!this.shadowRoot) return;
this.shadowRoot.innerHTML = (
<ChangeViewUI view={ChangeView.current} />
).toString();
}

//

static get current(): "desktop" | "html" {
const view = new URLSearchParams(location.search).get("view");
return view === "html" ? view : "desktop";
}

set #view(value: "desktop" | "html") {
const url = new URL(location.href);
url.searchParams.set("view", value);
window.location.href = url.toString();
}

//

get #inputs() {
if (!this.shadowRoot) throw new Error("ShadowRoot not found");
return this.shadowRoot.querySelectorAll("button");
}
}

ChangeView.define();

function ChangeViewUI({ view }: { view: string }) {
return (
<nav>
<button disabled={view === "desktop"} value="desktop">
Desktop
</button>
<button disabled={view === "html"} value="html">
HTML
</button>
</nav>
);
}
142 changes: 142 additions & 0 deletions packages/email/.storybook/SendEmailFormWebComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
//

type SendEmailOptions = {
sender: { name: string; email: string };
to: {
name: string;
email: string;
}[];
subject: string;
htmlContent: string;
};

//

export class SendEmailFormWebComponent extends HTMLElement {
static tag = "x-send-email-form" as const;
static define() {
customElements.define(SendEmailFormWebComponent.tag, this);
}
static BREVO_API_KEY = import.meta.env["VITE_BREVO_API_KEY"];

//

constructor() {
super();
this.attachShadow({ mode: "open" });
}

connectedCallback() {
if (!SendEmailFormWebComponent.BREVO_API_KEY) {
console.warn(
"No API key found for brevo, please set VITE_BREVO_API_KEY env variable if you want to test the email in a real email client environment",
);
return;
}

this.#render();

this.#form.addEventListener("submit", (e) => {
e.preventDefault();
this.submit(this.innerHTML);
});
}

async submit(template: string) {
const headers = new Headers();
headers.append("accept", "application/json");
headers.append("api-key", SendEmailFormWebComponent.BREVO_API_KEY);
headers.append("content-type", "application/json");

await fetch("https://api.brevo.com/v3/smtp/email", {
method: "POST",
headers,
body: JSON.stringify({
htmlContent: template,
sender: {
name: "MonComptePro",
email: "[email protected]",
},
subject: this.#object.value,
to: [{ name: "Ike Proconnect", email: this.#to.value }],
} as SendEmailOptions),
redirect: "follow",
});
}

//

#render() {
if (!this.shadowRoot) return;
this.shadowRoot.innerHTML = (
<details
style={{
backgroundColor: "white",
boxShadow: "0px 2px 6px 0px rgba(0, 0, 18, 0.16)",
position: "fixed",
bottom: "8px",
right: "8px",
}}
>
<summary style={{ background: "gainsboro", padding: "8px" }}>
📨
</summary>

<form
style={{
display: "flex",
flexDirection: "column",
padding: "8px",
}}
>
<label style={{ display: "flex", alignItems: "center" }}>
<div style={{ marginRight: "8px" }}>To</div>
<input
name="to"
value="[email protected]"
style={{ flexGrow: 1 }}
/>
</label>
<label style={{ display: "flex", alignItems: "center" }}>
<div style={{ marginRight: "8px" }}>Subject</div>
<input
name="object"
value="[Localhost] test email"
style={{ flexGrow: 1 }}
/>
</label>
<div
style={{
display: "flex",
alignItems: "end",
justifyContent: "space-between",
}}
>
<p style={{ marginBottom: "0" }}>
Powered by <a href="https://brevo.com">Brevo</a>
</p>
<button>Send</button>
</div>
</form>
</details>
).toString();
}

get #form() {
const element = this.shadowRoot?.querySelector("form");
if (!element) throw new Error("No form found");
return element;
}
get #to() {
const element = this.shadowRoot?.querySelector("input[name=to]");
if (!element) throw new Error("No input[name=to] found");
return element as HTMLInputElement;
}
get #object() {
const element = this.shadowRoot?.querySelector("input[name=object]");
if (!element) throw new Error("No input[name=object] found");
return element as HTMLInputElement;
}
}

SendEmailFormWebComponent.define();
28 changes: 28 additions & 0 deletions packages/email/.storybook/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<!doctype html>
<html
lang="en"
style="
margin: 0;
padding: 0;
font-family:
-apple-system,
BlinkMacSystemFont,
Segoe UI,
Roboto,
Oxygen-Sans,
Ubuntu,
Cantarell,
Helvetica Neue,
sans-serif;
"
>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Storybook like . Email Preview</title>
</head>
<body style="margin: 0; padding: 0">
<div id="root"></div>
<script type="module" src="/.storybook/index.tsx"></script>
</body>
</html>
Loading

0 comments on commit e14d9c4

Please sign in to comment.