From a78f56317abc217747db0078810777b29877f674 Mon Sep 17 00:00:00 2001 From: Ben Smithett Date: Wed, 8 Jul 2020 18:10:06 +1000 Subject: [PATCH 1/2] Add documentation, rename isomorphic to hydration & remove document template --- README.md | 2 +- app/components/README.md | 52 ++++++++++++++++++ .../welcome_banner/welcome_banner.js | 2 +- app/entry.client.js | 18 +++--- app/entry.prerender.js | 23 +++++++- ...orphic_helpers.js => hydration_helpers.js} | 2 +- app/layouts/README.md | 31 +++++++++++ app/layouts/default_layout.js | 2 +- app/layouts/document_template.js | 21 ------- app/{images => layouts}/favicon.png | Bin 10 files changed, 119 insertions(+), 34 deletions(-) create mode 100644 app/components/README.md rename app/{isomorphic_helpers.js => hydration_helpers.js} (94%) create mode 100644 app/layouts/README.md delete mode 100644 app/layouts/document_template.js rename app/{images => layouts}/favicon.png (100%) diff --git a/README.md b/README.md index 29526c1..c98c090 100644 --- a/README.md +++ b/README.md @@ -11,4 +11,4 @@ Welcome to your new [San Blas](https://sanblas.netlify.com/) site! - Install dependencies: `yarn` or `npm install` - `yarn start` or `npm start` & visit http://localhost:5000 -[Read the docs](https://sanblas.netlify.com/) for more. +Explore the `app` folder or [read the docs](https://sanblas.netlify.com/) for more. diff --git a/app/components/README.md b/app/components/README.md new file mode 100644 index 0000000..6f3bbae --- /dev/null +++ b/app/components/README.md @@ -0,0 +1,52 @@ +# Components + +Components are the building blocks that make up your pages and layouts. They're where you define HTML, CSS and (optionally) client-side JS behaviour. Build them quickly in Storybook, then compose them into pages. + +San Blas components are built with 2 opinionated conventions: + +- Style with Fela +- Opt-in client-side JS + +## Style with Fela + +San Blas uses [Fela](https://fela.js.org/) for styling components. See the Fela docs and provided examples, but essentially you write CSS-in-JS that looks like inline styles, which Fela translates into optimised atomic classes: + +```js + +) +``` + +### Progressive enhancement + +Prerendered component HTML can be [progressively enhanced](https://en.wikipedia.org/wiki/Progressive_enhancement) in the browser by any client-side JS added to `entry.client.js`. + +### React component hydration + +A single React component can be used to both: + +- prerender static HTML in a server environment, and +- attach client-side behaviour to that prerendered HTML in a browser environment via [hydration](https://reactjs.org/docs/react-dom.html#hydrate) + +In many React-based frameworks, the top-level page component is hydrated along with every component that makes up that page, regardless of whether they all actually have client-side behaviour that requires hydrating. + +By contrast, San Blas requires you to **opt specific components in** to hydration (you may of course choose to hydrate your top-level component). + +Helpers are provided in `hydration_helpers.js` simplify hydration (though you may hydrate manually in `entry.client.js`). diff --git a/app/components/welcome_banner/welcome_banner.js b/app/components/welcome_banner/welcome_banner.js index 93bda4c..058db0c 100644 --- a/app/components/welcome_banner/welcome_banner.js +++ b/app/components/welcome_banner/welcome_banner.js @@ -1,7 +1,7 @@ import React, { useState, useEffect } from 'react' import { useFela } from 'react-fela' import island from '../../images/island.jpg' -import {asIsland} from '../../isomorphic_helpers' +import {asIsland} from '../../hydration_helpers' const styles = { root: { diff --git a/app/entry.client.js b/app/entry.client.js index c15a4e6..78ae366 100644 --- a/app/entry.client.js +++ b/app/entry.client.js @@ -10,25 +10,27 @@ Analytics snippets, ReactDOM.render(), fancy graphs, etc... /* ⚛️ Optional: -Rehydrate prerendered React/Fela components using San Blas isomorphic helpers. +Hydrate prerendered React/Fela components using San Blas hydration helpers. -If you use asIsland() to render components in your pages, the following code -will rehydrate those components with the same props. +If you wrap components in your pages with the asIsland() HOC, the following code +will hydrate those components with the same props they were rendered with. -If you remove this code, the prerendered component HTML won't be rehydrated. +All components requiring hydration must be explicitly imported and passed to hydrateIslands(). + +If you remove the following code, the prerendered component won't be hydrated. */ import { createRenderer } from 'fela' import { rehydrate } from 'fela-dom' -import { rehydrateIslands } from './isomorphic_helpers' +import { hydrateIslands } from './hydration_helpers' -// Rehydrate Fela styles +// Hydrate Fela styles const felaRenderer = createRenderer() rehydrate(felaRenderer) -// Rehydrate San Blas islands +// Hydrate San Blas islands import WelcomeBanner from './components/welcome_banner/welcome_banner' -rehydrateIslands({ +hydrateIslands({ WelcomeBanner }, felaRenderer) diff --git a/app/entry.prerender.js b/app/entry.prerender.js index d4373d4..df636ca 100644 --- a/app/entry.prerender.js +++ b/app/entry.prerender.js @@ -21,7 +21,6 @@ import { RendererProvider } from 'react-fela' import { renderToMarkup } from 'fela-dom' import { Helmet } from 'react-helmet' import { cssReset } from './components/global_css' -import documentTemplate from './layouts/document_template' import DefaultLayout from './layouts/default_layout' export default function prerender (manifest, mode) { @@ -163,3 +162,25 @@ function buildJSONFeedFile (pageProps) { console.log(chalk.green(`🏝 JSON Feed built: ${outputFilePath}`)) }) } + +function documentTemplate ({ + stylesHTML, + bodyHTML, + helmet, + clientBundlePath +}) { + return ` + + + ${helmet.title.toString()} + ${helmet.meta.toString()} + ${helmet.link.toString()} + ${stylesHTML} + + + ${bodyHTML} + + + + ` +} diff --git a/app/isomorphic_helpers.js b/app/hydration_helpers.js similarity index 94% rename from app/isomorphic_helpers.js rename to app/hydration_helpers.js index 2a350d6..7c5098b 100644 --- a/app/isomorphic_helpers.js +++ b/app/hydration_helpers.js @@ -25,7 +25,7 @@ export function asIsland (componentName, Component, { return Hoc } -export function rehydrateIslands (islands, felaRenderer) { +export function hydrateIslands (islands, felaRenderer) { document.querySelectorAll('[data-sanblas-hydrate-as]').forEach(island => { const Component = islands[island.dataset.sanblasHydrateAs] const componentProps = JSON.parse(island.dataset.sanblasHydrateWith) diff --git a/app/layouts/README.md b/app/layouts/README.md new file mode 100644 index 0000000..b44eec2 --- /dev/null +++ b/app/layouts/README.md @@ -0,0 +1,31 @@ +# Layouts + +Layouts, a concept borrowed from [Rails](https://api.rubyonrails.org/classes/ActionView/Layouts.html), allow you to template the HTML _around_ your page content. + +This could include things like: + +- Tags for your `` +- "Wrapper" divs, or a shared header, footer or sidebar + +Unlike Rails, you don't need to directly template your layout's `html`, `head` or `body` tags. Instead, manage those tags with `Helmet` and the San Blas prerender function will put everything in the right place in the final rendered HTML document. + +## Layout props + +Layout components are rendered with 2 props: + +- `meta` (object): the `meta` object exported by each page +- `children` (React Element): the page content + +## Specifying a layout for a page + +Pages use the `DefaultLayout` component unless another component is specified in the page's `meta.Layout`: + +```js +import DifferentLayout from '../layouts/different_layout' + +export const meta = { + title: 'My Page Title', + description: 'This page has a different layout', + Layout: DifferentLayout +} +``` diff --git a/app/layouts/default_layout.js b/app/layouts/default_layout.js index c34bb16..a1e3605 100644 --- a/app/layouts/default_layout.js +++ b/app/layouts/default_layout.js @@ -1,6 +1,6 @@ import React from 'react' import { Helmet } from 'react-helmet' -import favicon from '../images/favicon.png' +import favicon from './favicon.png' export default function DefaultLayout ({ meta, children }) { return ( diff --git a/app/layouts/document_template.js b/app/layouts/document_template.js deleted file mode 100644 index 18c34c2..0000000 --- a/app/layouts/document_template.js +++ /dev/null @@ -1,21 +0,0 @@ -export default function documentTemplate ({ - stylesHTML, - bodyHTML, - helmet, - clientBundlePath -}) { - return ` - - - ${helmet.title.toString()} - ${helmet.meta.toString()} - ${helmet.link.toString()} - ${stylesHTML} - - - ${bodyHTML} - - - - ` -} diff --git a/app/images/favicon.png b/app/layouts/favicon.png similarity index 100% rename from app/images/favicon.png rename to app/layouts/favicon.png From 094fde3a03d62ea5d3babb3052a68bbad69e0f3a Mon Sep 17 00:00:00 2001 From: Ben Smithett Date: Wed, 8 Jul 2020 18:17:28 +1000 Subject: [PATCH 2/2] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index da59ee6..37d326c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sanblas", - "version": "3.0.0", + "version": "4.0.0", "scripts": { "start": "concurrently \"node ./app/build.js development\" \"serve\" --kill-others --prefix-colors=yellow.dim,cyan.dim", "build": "node ./app/build.js production",