Skip to content

Commit

Permalink
Merge pull request #12 from bensmithett/un-snakecase
Browse files Browse the repository at this point in the history
snake_case ➡️ camelCase/TitleCase
  • Loading branch information
bensmithett authored Jul 11, 2020
2 parents 84fd26e + 439f786 commit 760603a
Show file tree
Hide file tree
Showing 20 changed files with 103 additions and 53 deletions.
2 changes: 1 addition & 1 deletion .node-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
12.16.0
14.5.0
7 changes: 7 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"printWidth": 120,
"quoteProps": "as-needed",
"semi": false,
"singleQuote": true,
"trailingComma": "none"
}
2 changes: 1 addition & 1 deletion .storybook/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { configure, addDecorator, addParameters } from '@storybook/react'
import React from 'react'
import {createRenderer} from 'fela'
import {RendererProvider} from 'react-fela'
import {cssReset} from '../app/components/global_css'
import cssReset from '../app/components/cssReset'

// Setup Fela client runtime
const renderer = createRenderer({devMode: true})
Expand Down
47 changes: 47 additions & 0 deletions app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Tropical's 4 core files

## `build.js`

A script run in Node.js that:

- Uses Webpack to compile `prerender.js` and `client.js` so they can run, respectively, in Node.js and the browser.
- Calls the default export of `prerender.js` to (you guessed it!) prerender your static site.

## `client.js`

The JS file loaded by all pages in a `<script>` tag in the browser.

If using Tropical's `asIsland` helper to enable component hydration, import those components into this file and pass them to `hydrateIslands`.

## `hydrationHelpers.js`

Optional helpers for enabling targeted component [hydration](https://reactjs.org/docs/react-dom.html#hydrate) (see below).

## `prerender.js`

Prerenders your static site:

- Builds a HTML file from every `.js` or `.mdx` page in `pages`
- Builds a [JSON Feed](https://www.jsonfeed.org/) containing every page with `collection: 'posts'` in its metadata

## Hydration helpers

### `asIsland(componentName, Component, options)`

A [higher order component](https://reactjs.org/docs/higher-order-components.html) that annotates your prerendered components for hydration by `hydrateIslands`.

#### Props

- `componentName`: the name of the component, as configured in `hydrateIslands` *(string, required)*
- `Component`: any React component whose props can be serialised with `JSON.stringify` *(React component, required)*
- `options`: *(object)*
- `islandTag`: the HTML element to wrap the component with *(string, default: 'div')*
- `islandProps`: any props to be passed to the island element *(object, default: {})*

### `hydrateIslands(components)`

Hydrates any components in the browser that were prerendered using `asIsland`.

#### Props

- `components`: an object containing the components to be hydrated *(object, required)*
4 changes: 2 additions & 2 deletions app/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Client JS bundle Webpack config
const clientConfig = mode => {
return {
entry: {
client: path.join(__dirname, './entry.client.js')
client: path.join(__dirname, './client.js')
},
mode,
module: {
Expand Down Expand Up @@ -92,7 +92,7 @@ Prerender JS bundle Webpack config
const prerenderConfig = (mode) => {
return {
entry: {
prerender: path.join(__dirname, './entry.prerender.js')
prerender: path.join(__dirname, './prerender.js')
},
target: 'node',
node: {
Expand Down
6 changes: 3 additions & 3 deletions app/entry.client.js → app/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ If you remove the following code, the prerendered component won't be hydrated.

import { createRenderer } from 'fela'
import { rehydrate } from 'fela-dom'
import { hydrateIslands } from './hydration_helpers'
import { hydrateIslands } from './hydrationHelpers'

// Hydrate Fela styles
const felaRenderer = createRenderer()
rehydrate(felaRenderer)

// Hydrate Tropical islands
import WelcomeBanner from './components/welcome_banner/welcome_banner'
import ExampleComponent from './components/ExampleComponent/ExampleComponent'

hydrateIslands({
WelcomeBanner
ExampleComponent
}, felaRenderer)
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React, { useState, useEffect } from 'react'
import { useFela } from 'react-fela'
import island from '../../images/island.jpg'
import {asIsland} from '../../hydration_helpers'
import image from './gunayala.jpg'

const styles = {
root: {
Expand All @@ -27,7 +26,7 @@ const styles = {
}
}

export default function WelcomeBanner ({ alertMessage }) {
export default function ExampleComponent ({ alertMessage }) {
const { css } = useFela()
const [ isMounted, setMounted ] = useState(false)

Expand All @@ -36,9 +35,9 @@ export default function WelcomeBanner ({ alertMessage }) {
return (
<div className={css(styles.root)}>
<p>
Welcome to your <a href='https://github.com/bensmithett/tropical/'>Tropical</a> site!
Welcome to your <a href='https://tropical.js.org'>Tropical</a> site!
</p>
<img src={island} alt='Guna Yala, Panama' className={css(styles.img)} />
<img src={image} alt='Guna Yala, Panama' className={css(styles.img)} />
<p>
<button
className={css(styles.button)}
Expand All @@ -52,4 +51,7 @@ export default function WelcomeBanner ({ alertMessage }) {
)
}

export const WelcomeBannerIsland = asIsland('WelcomeBanner', WelcomeBanner)
// Remember, by default a component is only prerendered.
// Use a version wrapped with the asIsland() helper to enable hydration in the browser.
import { asIsland } from '../../hydrationHelpers'
export const ExampleComponentIsland = asIsland('ExampleComponent', ExampleComponent)
6 changes: 6 additions & 0 deletions app/components/ExampleComponent/ExampleComponent.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import React from 'react'
import ExampleComponent from './ExampleComponent'

export default { title: 'ExampleComponent' }

export const basic = () => <ExampleComponent alertMessage={'Hello!'} />
File renamed without changes
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react'
import { useFela } from 'react-fela'

export default function PostList ({ posts }) {
export default function ExamplePostList ({ posts }) {
const { css } = useFela()

return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React from 'react'
import PostList from './post_list'
import ExamplePostList from './ExamplePostList'

export default { title: 'PostList' }
export default { title: 'ExamplePostList' }

export const basic = () => (
<PostList
<ExamplePostList
posts={[
{
urlPath: 'post-1',
Expand Down
12 changes: 6 additions & 6 deletions app/components/README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
# 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.
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.

Tropical components are built with 2 opinionated conventions:

- Style with Fela
- Style with [Fela](https://fela.js.org/)
- Opt-in client-side JS

## Style with Fela

Tropical 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:
Tropical uses [Fela](https://fela.js.org/) for styling components. See the [Fela docs](https://fela.js.org/) for more info, but essentially you write CSS-in-JS that looks like inline styles, which Fela translates into optimised atomic classes:

```js
<button className={css({
Expand All @@ -24,7 +24,7 @@ Tropical uses [Fela](https://fela.js.org/) for styling components. See the Fela

By default, Tropical components are **only prerendered** and have **no client-side JS behaviour**.

For example, by default this button will appear in the page's prerendered HTML, but nothing will happen when it's clicked.
For example, this button will appear in the page's prerendered HTML, but by default nothing will happen when it's clicked.

```js
const HiButton = () => (
Expand All @@ -36,7 +36,7 @@ const HiButton = () => (

### 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`.
Your prerendered HTML can be [progressively enhanced](https://en.wikipedia.org/wiki/Progressive_enhancement) in the browser by any client-side JS added to `client.js`.

### React component hydration

Expand All @@ -49,4 +49,4 @@ In many React-based frameworks, the top-level page component is hydrated along w

By contrast, Tropical 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`).
Helpers are provided in `hydrationHelpers.js` simplify hydration (though you may hydrate manually in `client.js`).
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export function cssReset (felaRenderer) {
export default function cssReset (felaRenderer) {
felaRenderer.renderStatic({
fontSize: '16px',
fontFamily: 'sans-serif',
Expand Down
6 changes: 0 additions & 6 deletions app/components/welcome_banner/welcome_banner.stories.js

This file was deleted.

File renamed without changes.
File renamed without changes.
4 changes: 2 additions & 2 deletions app/layouts/README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# 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.
Layouts allow you to template the HTML _around_ your page content (concept borrowed from [Rails](https://api.rubyonrails.org/classes/ActionView/Layouts.html)).

This could include things like:

- Tags for your `<head>`
- "Wrapper" divs, or a shared header, footer or sidebar
- 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 Tropical prerender function will put everything in the right place in the final rendered HTML document.

Expand Down
16 changes: 8 additions & 8 deletions app/pages/example-post.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -19,33 +19,33 @@ If you remove the `collection` and `date` from this file's `meta`, this will jus

MDX lets you render JSX alongside your Markdown, so you can include custom HTML...

import island from '../images/island.jpg'
import image from '../components/ExampleComponent/gunayala.jpg'

<figure>
<img src={island} alt='An island' width={400} />
<img src={image} alt='Guna Yala, Panama' width={400} />
<figcaption>MDX is pretty cool...</figcaption>
</figure>

----

... as well as reuse existing components. Note that every page receives the `posts` collection as a prop.

import PostList from '../components/post_list/post_list'
import ExamplePostList from '../components/ExamplePostList/ExamplePostList'

<PostList posts={props.posts} />
<ExamplePostList posts={props.posts} />

---

Remember, components are only prerendered by default.

If you want to automatically rehydrate on the client (aka isomorphic/universal) you'll need to import a component wrapped with the `asIsland` higher order component.
If you want to automatically [hydrate](https://reactjs.org/docs/react-dom.html#hydrate) a component on the client, you'll need to import a component wrapped with the `asIsland` higher order component.

import WelcomeBanner, { WelcomeBannerIsland } from '../components/welcome_banner/welcome_banner'
import ExampleComponent, { ExampleComponentIsland } from '../components/ExampleComponent/ExampleComponent'

## Prerendered only

<WelcomeBanner alertMessage='An yeel itoe' />
<ExampleComponent alertMessage="This component isn't hydrated, so this alert will never be seen" />

## Rehydrated

<WelcomeBannerIsland alertMessage='An yeel itoe' />
<ExampleComponentIsland alertMessage="This component is hydrated, so this alert will be seen" />
16 changes: 5 additions & 11 deletions app/pages/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,16 @@ export const meta = {
}

import React from 'react'
import { Helmet } from 'react-helmet-async'
import PostList from '../components/post_list/post_list'
import ExamplePostList from '../components/ExamplePostList/ExamplePostList'

/*
By default, React components are only used to build your prerendered HTML.
If you want to automatically rehydrate on the client (aka isomorphic/universal) you can
wrap your original component with the `asIsland` higher order component for use in pages.
*/
import { WelcomeBannerIsland } from '../components/welcome_banner/welcome_banner'
// Note we're importing ExampleComponent wrapped in asIsland, so it will be hydrated in the browser.
import { ExampleComponentIsland } from '../components/ExampleComponent/ExampleComponent'

export default function IndexPage ({ posts }) {
return (
<>
<WelcomeBannerIsland alertMessage='Hello!' />
<PostList posts={posts} />
<ExampleComponentIsland alertMessage='Hello!' />
<ExamplePostList posts={posts} />
</>
)
}
4 changes: 2 additions & 2 deletions app/entry.prerender.js → app/prerender.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ import { createRenderer } from 'fela'
import { RendererProvider } from 'react-fela'
import { renderToMarkup } from 'fela-dom'
import { Helmet } from 'react-helmet'
import { cssReset } from './components/global_css'
import DefaultLayout from './layouts/default_layout'
import cssReset from './components/cssReset'
import DefaultLayout from './layouts/DefaultLayout'

export default function prerender (manifest, mode) {
/*
Expand Down

0 comments on commit 760603a

Please sign in to comment.