Skip to content

Commit

Permalink
feat: add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
QuiiBz committed Dec 29, 2023
1 parent c3be099 commit 7dd459a
Show file tree
Hide file tree
Showing 16 changed files with 5,460 additions and 47 deletions.
7 changes: 5 additions & 2 deletions apps/dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"build": "next build",
"start": "next start",
"lint": "next lint",
"typecheck": "tsc --noEmit"
"typecheck": "tsc --noEmit",
"test": "vitest"
},
"dependencies": {
"@dnd-kit/core": "^6.1.0",
Expand All @@ -30,7 +31,9 @@
"@types/react": "^18",
"@types/react-dom": "^18",
"autoprefixer": "^10.0.1",
"happy-dom": "^12.10.3",
"postcss": "^8",
"tailwindcss": "^3.3.0"
"tailwindcss": "^3.3.0",
"vitest": "^1.1.0"
}
}
2 changes: 1 addition & 1 deletion apps/dashboard/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default function RootLayout({
<html lang="en">
<body className={inter.className}>
{children}
<Toaster />
<Toaster closeButton richColors />
</body>
</html>
)
Expand Down
81 changes: 59 additions & 22 deletions apps/dashboard/src/components/LeftPanel/ExportSection.tsx
Original file line number Diff line number Diff line change
@@ -1,54 +1,91 @@
import { useState } from "react";
import { flushSync } from "react-dom";
import { toast } from "sonner";
import { Button } from "../forms/Button";
import { PngIcon } from "../icons/PngIcon";
import { SvgIcon } from "../icons/SvgIcon";
import { useOg } from "../OgEditor";
import { domToReactLike, exportToPng, exportToSvg } from "../../lib/export";
import { domToReactElements, exportToPng, exportToSvg } from "../../lib/export";
import type { FontData } from "../../lib/fonts";
import { loadFonts } from "../../lib/fonts";

export function ExportSection() {
const { rootRef, elements, setSelectedElement } = useOg()
const [isLoading, setIsLoading] = useState(false)

async function exportSvg(openInNewTab = true) {
if (!rootRef.current) {
return {}
}

async function exportSvg(showProgress = true) {
flushSync(() => {
setSelectedElement(null)
})

setIsLoading(true)
async function run() {
setIsLoading(true)

const reactLike = domToReactLike(rootRef.current, 'This is a dynamic text')
const fonts = await loadFonts(elements)
const svg = await exportToSvg(reactLike, fonts)
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- rootRef is always defined
const reactElements = domToReactElements(rootRef.current!, 'This is a dynamic text')
const fonts = await loadFonts(elements)
const svg = await exportToSvg(reactElements, fonts)

setIsLoading(false)
setIsLoading(false)

if (openInNewTab) {
const blob = new Blob([svg], { type: 'image/svg+xml' })
const url = URL.createObjectURL(blob)
window.open(url)
return { fonts, svg }
}

return { fonts, svg }
return new Promise<{
fonts: FontData[];
svg: string;
}>((resolve, reject) => {
if (showProgress) {
let svg: string

toast.promise(run(), {
loading: 'Exporting to SVG...',
success: (data) => {
resolve(data)
svg = data.svg

return 'SVG exported!'
},
action: {
label: 'Download',
onClick: () => {
const blob = new Blob([svg], { type: 'image/svg+xml' })
const url = URL.createObjectURL(blob)
window.open(url)
}
}
});
}

run().then(resolve).catch(reject)
})
}

async function exportPng() {
const { svg, fonts } = await exportSvg(false)

setIsLoading(true)
let png: Uint8Array

const png = await exportToPng(svg ?? '', fonts?.map(font => new Uint8Array(font.data)) ?? [])
async function run() {
setIsLoading(true)

setIsLoading(false)
png = await exportToPng(svg, fonts.map(font => new Uint8Array(font.data)))

setIsLoading(false)
}

const blob = new Blob([png], { type: 'image/png' })
const url = URL.createObjectURL(blob)
window.open(url)
toast.promise(run(), {
loading: 'Exporting to PNG...',
success: 'PNG exported!',
action: {
label: 'Download',
onClick: () => {
const blob = new Blob([png], { type: 'image/png' })
const url = URL.createObjectURL(blob)
window.open(url)
}
}
});
}

return (
Expand Down
4 changes: 2 additions & 2 deletions apps/dashboard/src/components/OgImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { OGElement } from "../lib/types"

async function loadOgImage(elements: OGElement[]) {
const fonts = await loadFonts(elements)
const reactLike = {
const reactElements = {
type: 'div',
props: {
style: {
Expand All @@ -24,7 +24,7 @@ async function loadOgImage(elements: OGElement[]) {
}
}

const svg = await exportToSvg(reactLike, fonts)
const svg = await exportToSvg(reactElements, fonts)
return `data:image/svg+xml;base64,${btoa(svg)}`
}

Expand Down
18 changes: 16 additions & 2 deletions apps/dashboard/src/components/OgSplash.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { MouseEvent, ReactNode } from "react";
import { Suspense, useEffect, useState } from "react";
import Link from "next/link";
import { useRouter, useSearchParams } from "next/navigation";
import { toast } from "sonner";
import { INITIAL_ELEMENTS, createElementId } from "../lib/elements";
import type { OGElement } from "../lib/types";
import type { Template } from "../lib/templates";
Expand Down Expand Up @@ -91,7 +92,7 @@ export function OgSplash({ route }: OgSplashProps) {
if (push) {
router.push(`/?i=${id}`)
} else {
setOgImages([{ id: key, content: template.elements }, ...ogImages])
setOgImages(images => [{ id: key, content: template.elements }, ...images])
}
}
}
Expand All @@ -104,6 +105,7 @@ export function OgSplash({ route }: OgSplashProps) {
event.stopPropagation();

copy()
toast('Image duplicated!')
}
}

Expand All @@ -113,7 +115,19 @@ export function OgSplash({ route }: OgSplashProps) {
event.stopPropagation();

localStorage.removeItem(ogImage.id)
setOgImages(ogImages.filter(({ id }) => id !== ogImage.id))
setOgImages(images => images.filter(({ id }) => id !== ogImage.id))

toast('Image deleted!', {
action: {
label: 'Undo',
onClick: () => {
localStorage.setItem(ogImage.id, JSON.stringify(ogImage.content))
setOgImages(images => [ogImage, ...images])

toast('Image restored!')
}
},
})
}
}

Expand Down
Loading

0 comments on commit 7dd459a

Please sign in to comment.