diff --git a/.bitmap b/.bitmap index d52e0127f2b2..9462d6910c81 100644 --- a/.bitmap +++ b/.bitmap @@ -1766,6 +1766,20 @@ "mainFile": "index.ts", "rootDir": "components/ui/navigation/lane-switcher" }, + "ui/pages/preview-not-found": { + "name": "ui/pages/preview-not-found", + "scope": "teambit.ui-foundation", + "version": "0.0.84", + "mainFile": "index.ts", + "rootDir": "components/ui/pages/preview-not-found" + }, + "ui/pages/static-error": { + "name": "ui/pages/static-error", + "scope": "teambit.ui-foundation", + "version": "0.0.92", + "mainFile": "index.ts", + "rootDir": "components/ui/pages/static-error" + }, "ui/preview-placeholder": { "name": "ui/preview-placeholder", "scope": "teambit.preview", @@ -1787,6 +1801,13 @@ "mainFile": "index.ts", "rootDir": "components/ui/react-router/slot-router" }, + "ui/rendering/html": { + "name": "ui/rendering/html", + "scope": "teambit.ui-foundation", + "version": "0.0.85", + "mainFile": "index.ts", + "rootDir": "components/ui/rendering/html" + }, "ui/test-compare": { "name": "ui/test-compare", "scope": "teambit.defender", diff --git a/components/ui/pages/preview-not-found/image-icon.tsx b/components/ui/pages/preview-not-found/image-icon.tsx new file mode 100644 index 000000000000..5b03871187d1 --- /dev/null +++ b/components/ui/pages/preview-not-found/image-icon.tsx @@ -0,0 +1,12 @@ +import React, { SVGProps } from 'react'; + +export function ImageIcon(props: SVGProps) { + return ( + + + + ); +} diff --git a/components/ui/pages/preview-not-found/index.ts b/components/ui/pages/preview-not-found/index.ts new file mode 100644 index 000000000000..81af1d5daa91 --- /dev/null +++ b/components/ui/pages/preview-not-found/index.ts @@ -0,0 +1,2 @@ +export { PreviewNotFoundPage } from './preview-not-found'; +export type { PreviewNotFoundPageProps } from './preview-not-found'; diff --git a/components/ui/pages/preview-not-found/preview-not-found.tsx b/components/ui/pages/preview-not-found/preview-not-found.tsx new file mode 100644 index 000000000000..27cd5d5c1852 --- /dev/null +++ b/components/ui/pages/preview-not-found/preview-not-found.tsx @@ -0,0 +1,32 @@ +import React, { CSSProperties } from 'react'; +import { ImageIcon } from './image-icon'; + +const styles: Record = { + container: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + + height: '100%', + }, + image: { + width: '2.6em', + marginBottom: '1em', + }, + message: { + fontSize: '1em', + textAlign: 'center', + }, +}; + +export type PreviewNotFoundPageProps = React.HTMLAttributes; + +export function PreviewNotFoundPage(props: PreviewNotFoundPageProps) { + return ( +
+ +
No preview available
+
+ ); +} diff --git a/components/ui/pages/static-error/index.ts b/components/ui/pages/static-error/index.ts new file mode 100644 index 000000000000..67cc00eb2406 --- /dev/null +++ b/components/ui/pages/static-error/index.ts @@ -0,0 +1 @@ +export { noPreview, notFound, serverError } from './static-error-pages'; diff --git a/components/ui/pages/static-error/render-page.tsx b/components/ui/pages/static-error/render-page.tsx new file mode 100644 index 000000000000..5ec12d6b2f15 --- /dev/null +++ b/components/ui/pages/static-error/render-page.tsx @@ -0,0 +1,14 @@ +import React, { ReactNode } from 'react'; +import { renderToStaticMarkup } from 'react-dom/server'; +import { Html, Assets } from '@teambit/ui-foundation.ui.rendering.html'; + +export function fullPageToStaticString(content: ReactNode, assets?: Assets) { + const html = ( + + {content} + + ); + const stringified = renderToStaticMarkup(html); + + return stringified; +} diff --git a/components/ui/pages/static-error/static-error-pages.tsx b/components/ui/pages/static-error/static-error-pages.tsx new file mode 100644 index 000000000000..7be830065063 --- /dev/null +++ b/components/ui/pages/static-error/static-error-pages.tsx @@ -0,0 +1,56 @@ +import React from 'react'; +import { staticBookFontClass, staticBookFontUrl } from '@teambit/base-ui.theme.fonts.book'; +import { NotFoundPage } from '@teambit/design.ui.pages.not-found'; +import { ServerErrorPage } from '@teambit/design.ui.pages.server-error'; +import { PreviewNotFoundPage } from '@teambit/ui-foundation.ui.pages.preview-not-found'; + +import { fullPageToStaticString } from './render-page'; + +const center = ` + body { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + + font-family: sans-serif; + } +`; + +const sizing = ` + body { + font-size: 18px; + color: #878c9a; + width: 100% !important; + height: 500px; + } + + @media screen and (max-width: 250px) { + body { + font-size: 14px; + color: #c7c7c7; + } + } +`; + +const assets = { + style: [center], + css: [staticBookFontUrl], +}; + +const noPreviewAssets = { + style: [center, sizing], + css: [staticBookFontUrl], +}; + +export function notFound(): string { + return fullPageToStaticString(, assets); +} + +export function serverError(): string { + return fullPageToStaticString(, assets); +} + +export function noPreview(): string { + return fullPageToStaticString(, noPreviewAssets); +} diff --git a/components/ui/rendering/html/dev-tools.tsx b/components/ui/rendering/html/dev-tools.tsx new file mode 100644 index 000000000000..1fbf0020817a --- /dev/null +++ b/components/ui/rendering/html/dev-tools.tsx @@ -0,0 +1,10 @@ +import React from 'react'; + +export function CrossIframeDevTools() { + return ( + + ); +} diff --git a/components/ui/rendering/html/full-height-style.tsx b/components/ui/rendering/html/full-height-style.tsx new file mode 100644 index 000000000000..09b207617d8d --- /dev/null +++ b/components/ui/rendering/html/full-height-style.tsx @@ -0,0 +1,6 @@ +import React from 'react'; + +export function FullHeightStyle() { + // return ; + return ; +} diff --git a/components/ui/rendering/html/html.tsx b/components/ui/rendering/html/html.tsx new file mode 100644 index 000000000000..c2eb55332503 --- /dev/null +++ b/components/ui/rendering/html/html.tsx @@ -0,0 +1,92 @@ +import React from 'react'; +import { CrossIframeDevTools } from './dev-tools'; +import { MountPoint, fillMountPoint } from './mount-point'; +import { popAssets, StoredAssets } from './stored-assets'; +import { SsrStyles, removeSsrStyles } from './ssr-styles'; +import { FullHeightStyle } from './full-height-style'; + +export const LOAD_EVENT = '_DOM_LOADED_'; + +export type Assets = Partial<{ + /** page title */ + title: string; + /** js files to load */ + js: string[]; + /** css files to load */ + css: string[]; + /** raw css styles */ + style: string[]; + /** raw data to be stored in the dom. Use Html.popAssets to retrieve it from the dom */ + json: Record; +}>; + +export interface HtmlProps extends React.HtmlHTMLAttributes { + withDevTools?: boolean; + fullHeight?: boolean; + assets?: Assets; + ssr?: boolean; + notifyParentOnLoad?: boolean; +} + +const NotifyParentScript = () => ( + +); + +/** html template for the main UI, when ssr is active */ +export function Html({ + assets = {}, + withDevTools = false, + fullHeight, + ssr, + children = , + notifyParentOnLoad = true, + ...rest +}: HtmlProps) { + return ( + + + {assets.title || 'bit scope'} + + + + {ssr && } + {fullHeight && } + {withDevTools && } + + {assets.style?.map((x, idx) => ( + + ))} + {assets.css?.map((x, idx) => ( + + ))} + {notifyParentOnLoad && } + + + {children} + {assets.json && } + {/* load scripts after showing the the whole html */} + {assets.js?.map((x, idx) => ( +