Skip to content
This repository has been archived by the owner on Sep 7, 2020. It is now read-only.

Commit

Permalink
✨ Added: support for Glamor and styled-components out of the …
Browse files Browse the repository at this point in the history
…box.

Now if you use [Glamor](https://github.com/threepointone/glamor/) to
write your style, static rendering will take that into account and will
prerender styles for you. Nothing to setup. It’s even injecting glamor
ids if you want to rehydrate on startup. See [glamor server
documentation](https://github.com/threepointone/glamor/blob/master/docs/
server.md) to setup hydratation (you will need to handle this yourself
in your ``scripts/phenomic.browser.js``.
Since [styled-components](https://styled-components.com/) [use Glamor
under the
hood](styled-components/styled-components#124)
, this will work for this library as well.

Ref #864
  • Loading branch information
MoOx committed Nov 16, 2016
1 parent 0c0fae4 commit 6d703cb
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 27 deletions.
68 changes: 64 additions & 4 deletions src/components/Html/__tests__/__snapshots__/index.js.snap
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
exports[`test should render Html componnent 1`] = `
exports[`test should render Html component 1`] = `
<html
lang="en">
<head>
Expand All @@ -21,11 +21,71 @@ exports[`test should render Html componnent 1`] = `
rel="stylesheet" />
</head>
<body>
<div />
<script />
<div
dangerouslySetInnerHTML={
Object {
"__html": "<div />",
}
}
id="phenomic" />
<script
phenomicStuff={true} />
<script
data-react-helmet={true}
src="test.js" />
</body>
</html>
`;

exports[`test should render Html component when glamor available 1`] = `
<html
lang="en">
<head>
<title
data-react-helmet={true} />
<meta
charSet="utf-8"
data-react-helmet={true} />
<meta
content="IE=edge"
data-react-helmet={true}
httpEquiv="X-UA-Compatible" />
<meta
content="width=device-width, initial-scale=1"
data-react-helmet={true}
name="viewport" />
<style
dangerouslySetInnerHTML={
Object {
"__html": ".stuff {}",
}
}
data-react-helmet={true} />
<link
data-react-helmet={true}
href="test.css"
rel="stylesheet" />
</head>
<body>
<div
dangerouslySetInnerHTML={
Object {
"__html": "<div />",
}
}
id="phenomic" />
<script
phenomicStuff={true} />
<script
dangerouslySetInnerHTML={
Object {
"__html": "window._glam = [{\"id\":\"s\"}]",
}
}
data-react-helmet={true} />
<script
data-react-helmet={true}
src="test.css" />
src="test.js" />
</body>
</html>
`;
34 changes: 30 additions & 4 deletions src/components/Html/__tests__/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,42 @@ import { createRenderer } from "react-addons-test-utils"

import Html from "../index.js"

test("should render Html componnent", () => {
test("should render Html component", () => {
const renderer = createRenderer()
renderer.render(
/* eslint-disable react/jsx-no-bind */
<Html
css={ [ "test.css" ] }
js={ [ "test.css" ] }
renderBody={ () => <div /> }
renderScript={ () => <script /> }
js={ [ "test.js" ] }
renderBody={ () => "<div />" }
renderScript={ () => <script phenomicStuff /> }
/>
)
expect(renderer.getRenderOutput()).toMatchSnapshot()
})

test("should render Html component when glamor available", () => {
jest.mock(
"glamor/server",
() => ({
renderStatic: (render) => ({
html: render(),
css: ".stuff {}",
ids: [ { id: "s" } ],
}),
}),
{ virtual: true }
)
const renderer = createRenderer()
renderer.render(
/* eslint-disable react/jsx-no-bind */
<Html
css={ [ "test.css" ] }
js={ [ "test.js" ] }
renderBody={ () => "<div />" }
renderScript={ () => <script phenomicStuff /> }
/>
)
expect(renderer.getRenderOutput()).toMatchSnapshot()
jest.unmock("glamor/server", { virtual: true })
})
45 changes: 42 additions & 3 deletions src/components/Html/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,52 @@ const defaultMeta = [
]

const Html = (props: Props) => {

// Inject default html metas before
// Those need to be rendered somehow otherwise Helmet won't consider those
renderToString(
<Helmet
htmlAttributes={ defaultHtmlAttributes }
meta={ defaultMeta }
/>
)

// Glamor integration
// https://github.com/threepointone/glamor/blob/master/docs/server.md
let glamorRenderStatic
try {
// $FlowFixMe just ignore glamor as we don't have it as a dep
glamorRenderStatic = require("glamor/server").renderStatic
}
catch (e) {
// skip glamor if not working
}

// render body
let body
if (glamorRenderStatic) {
const glamorResult = glamorRenderStatic(() => props.renderBody())

console.log({ glamorResult })
renderToString(
<Helmet
style={ [
{ "cssText": glamorResult.css },
] }
script={ [
{ "innerHTML": `window._glam = ${
JSON.stringify(glamorResult.ids)
}` },
] }
/>
)
body = glamorResult.html
}

body = body || props.renderBody()

renderToString(
<Helmet
link={ [
...props.css.map((file) => ({ rel: "stylesheet", href: file })),
] }
Expand All @@ -39,8 +79,6 @@ const Html = (props: Props) => {
/>
)

// render body
const body = props.renderBody()
// rewind html metas
const head = Helmet.rewind()

Expand All @@ -51,10 +89,11 @@ const Html = (props: Props) => {
{ head.base.toComponent() }
{ head.title.toComponent() }
{ head.meta.toComponent() }
{ head.style.toComponent() }
{ head.link.toComponent() }
</head>
<body>
{ body }
<div id="phenomic" dangerouslySetInnerHTML={{ __html: body }} />
{ props.renderScript() }
{ head.script.toComponent() }
</body>
Expand Down
26 changes: 10 additions & 16 deletions src/static/url-as-html.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,22 +64,16 @@ export default function(

/* eslint-disable react/no-multi-comp */

const renderBody = () => {
const body = render(
<PhenomicContextProvider
collection={ collectionMin }
metadata={ metadata }
>
<ReduxContextProvider store={ store }>
<RouterContextProvider { ...renderProps } />
</ReduxContextProvider>
</PhenomicContextProvider>
)

return (
<div id="phenomic" dangerouslySetInnerHTML={{ __html: body }} />
)
}
const renderBody = () => render(
<PhenomicContextProvider
collection={ collectionMin }
metadata={ metadata }
>
<ReduxContextProvider store={ store }>
<RouterContextProvider { ...renderProps } />
</ReduxContextProvider>
</PhenomicContextProvider>
)

const renderScript = () => {
if (options.clientScripts) {
Expand Down

7 comments on commit 6d703cb

@kitten
Copy link

@kitten kitten commented on 6d703cb Nov 17, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Saw this and checked it out ;) This is not going to work for Styled Components as it's using a custom StyleSheet. Thus glamor's server methods are not importing the right thing.

https://github.com/styled-components/styled-components/blob/master/src/vendor/glamor/sheet.js

@MoOx
Copy link
Owner Author

@MoOx MoOx commented on 6d703cb Nov 17, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Meh :'(

I guess I will remove the mention of styled-components from the doc and will wait for styled-components/styled-components#124

@kitten
Copy link

@kitten kitten commented on 6d703cb Nov 17, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's really a shame :( I wanted to use it for implementing rehydration on my project, but noticed that this doesn't work. Also, it seems like inserted is not present on the custom StyleSheet, do you know by any chance what this is about? I'm not able to see code related to it in glamor's sheet.

Object.keys(styleSheet.inserted)

@MoOx
Copy link
Owner Author

@MoOx MoOx commented on 6d703cb Nov 17, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have not idea, I just integrated this following glamor docs. I personally don't use this solutions. I use normal CSS (+ css modules) or pure JS solutions (via react-native-web)...

@kitten
Copy link

@kitten kitten commented on 6d703cb Nov 17, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if rehydration doesn't work, you can make the SSR styling work (-ish), if you manually import the stylesheet. So basically this will need the user to do:

StyleSheet.rules().map(rule => rule.cssText).join('\n')

where StyleSheet is in: 'node_modules/styled-components/lib/models/StyleSheet'.

However, this is further complicated by the fact that bundlers will use the bundled version of styled-components by default. So the user potentially needs to use aliases to make this work. I guess there's no "one-fits-all" solution for making both standalone glamor and styled-components work. It doesn't even look like there ever will be one solution for both, as long as styled-components has a custom sheet.js.

@MoOx
Copy link
Owner Author

@MoOx MoOx commented on 6d703cb Nov 17, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will wait for a public API for styled-components then. Phenomic can handle multiple solutions (currently glamor and aphrodite, without having those as deps) so not a big deal.

@geelen
Copy link

@geelen geelen commented on 6d703cb Nov 18, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah we vaguely intend to merge our changes to StyleSheet upstream to Glamor but it's not gonna happen immediately, so we'll make a public API for this stuff so if/when we change implementations there's no changes needed at your end.

Please sign in to comment.