Skip to content

Commit

Permalink
Merge pull request #38 from artsy/more-expansive-device-detection-for…
Browse files Browse the repository at this point in the history
…-example

[example] More expansive device detection and not load hidden imaged
  • Loading branch information
alloy committed Dec 1, 2018
2 parents 336c4e0 + cdc5057 commit 31f6506
Show file tree
Hide file tree
Showing 11 changed files with 266 additions and 137 deletions.
5 changes: 4 additions & 1 deletion .vscode/settings.json
Expand Up @@ -18,5 +18,8 @@
},
"tslint.autoFixOnSave": true,
"typescript.tsdk": "./node_modules/typescript/lib",
"debug.node.autoAttach": "on"
"debug.node.autoAttach": "on",
"cSpell.words": [
"resizable"
]
}
55 changes: 54 additions & 1 deletion example/app.tsx
Expand Up @@ -34,8 +34,37 @@ const LargeStyle: CSSProperties = {
backgroundColor: "red",
}

// From https://www.smashingmagazine.com/2013/07/simple-responsive-images-with-css-background-images/
const Img: React.SFC<
{ src: string; aspectRatio: number } & React.HTMLProps<HTMLSpanElement>
> = ({ src, aspectRatio, style, ...props }) => (
<span
{...props}
style={{
width: "100%",
display: "inline-block",
fontSize: "0",
lineHeight: "0",
verticalAlign: "middle",
backgroundSize: "100%",
backgroundPosition: "50% 50%",
backgroundRepeat: "no-repeat",
backgroundImage: `url(${src})`,
...style,
}}
>
<span
style={{
display: "block",
height: "0",
paddingTop: `${(aspectRatio * 100).toFixed(2)}%`,
}}
/>
</span>
)

export const App: React.SFC = () => (
<div style={{ width: "400px" }}>
<div>
<div>
<h1>
Default <code>&lt;div&gt;</code> container
Expand Down Expand Up @@ -135,5 +164,29 @@ export const App: React.SFC = () => (
</div>
</Media>
</div>
<div>
<h1>Example of not loading hidden images</h1>
<Media lessThan="md">
<Img
aspectRatio={1.261044176706827}
src="https://d32dm0rphc51dk.cloudfront.net/JAo7pAN1p63YwolybeZgOg/small.jpg"
style={{ width: "100px" }}
/>
</Media>
<Media at="md">
<Img
aspectRatio={1.261044176706827}
src="https://d32dm0rphc51dk.cloudfront.net/JAo7pAN1p63YwolybeZgOg/medium.jpg"
style={{ width: "400px" }}
/>
</Media>
<Media at="lg">
<Img
aspectRatio={1.261044176706827}
src="https://d32dm0rphc51dk.cloudfront.net/JAo7pAN1p63YwolybeZgOg/large.jpg"
style={{ width: "800px" }}
/>
</Media>
</div>
</div>
)
57 changes: 0 additions & 57 deletions example/onlyMatchListForUserAgent.ts

This file was deleted.

173 changes: 120 additions & 53 deletions example/server.tsx
Expand Up @@ -2,67 +2,85 @@ import Webpack from "webpack"
import WebpackDevServer from "webpack-dev-server"
import webpackConfig from "./webpack.config"
import express from "express"

import ReactDOMServer from "react-dom/server"
import React from "react"
import chalk from "chalk"

import { findDevice } from "@artsy/detect-responsive-traits"

import { createMediaStyle, MediaContextProvider, SSRStyleID } from "./setup"
import {
createMediaStyle,
findBreakpointsForWidths,
findBreakpointAtWidth,
MediaContextProvider,
SortedBreakpoints,
SSRStyleID,
} from "./setup"
import { App } from "./app"
import { onlyMatchListForUserAgent } from "./onlyMatchListForUserAgent"

const app = express()

app.get("/", (_req, res) => {
res.send(`
<!DOCTYPE html>
<html>
<body>
<ul>
<li><a href="/ssr-only">server-side rendering <em>only</em></a></li>
<li><a href="/client-only">client-side rendering <em>only</em></a></li>
<li><a href="/rehydration">server-side rendering <em>and</em> client-side rehydration</a></li>
</ul>
</body>
</html>
`)
})
/**
* Find the breakpoints and interactions that the server should render
*/
function onlyMatchListForUserAgent(userAgent: string): OnlyMatchList {
const device = findDevice(userAgent)
if (!device) {
log(userAgent)
return null
} else {
const supportsHover = device.touch ? "notHover" : "hover"
const onlyMatch: OnlyMatchList = device.resizable
? [
supportsHover,
...findBreakpointsForWidths(device.minWidth, device.maxWidth),
]
: [
supportsHover,
findBreakpointAtWidth(device.minWidth),
findBreakpointAtWidth(device.maxWidth),
]
log(userAgent, onlyMatch, device.description)
return onlyMatch
}
}

/**
* Demonstrate server-side _only_ rendering
*/
app.get("/ssr-only", (req, res) => {
const onlyMatch = onlyMatchListForUserAgent(req.header("User-Agent"))
res.send(`
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style type="text/css">${createMediaStyle()}</style>
</head>
<body>
res.send(
template({
includeCSS: true,
body: `
<div id="react-root">
${ReactDOMServer.renderToString(
<MediaContextProvider onlyMatch={onlyMatch}>
<MediaContextProvider
onlyMatch={onlyMatchListForUserAgent(req.header("User-Agent"))}
>
<App />
</MediaContextProvider>
)}
</div>
</body>
</html>
`)
`,
})
)
})

/**
* Demonstrate server-side rendering _with_ client-side JS rehydration
*/
app.get("/rehydration", (req, res) => {
const onlyMatch = onlyMatchListForUserAgent(req.header("User-Agent"))
res.send(`
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style type="text/css" id="${SSRStyleID}">${createMediaStyle()}</style>
</head>
<body>
res.send(
template({
includeCSS: true,
body: `
<div id="loading-indicator">Loading…</div>
<div id="react-root">
${ReactDOMServer.renderToString(
<MediaContextProvider onlyMatch={onlyMatch}>
<MediaContextProvider
onlyMatch={onlyMatchListForUserAgent(req.header("User-Agent"))}
>
<App />
</MediaContextProvider>
)}
Expand All @@ -77,19 +95,19 @@ app.get("/rehydration", (req, res) => {
document.getElementById("loading-indicator").remove();
}, 1000)
</script>
</body>
</html>
`)
`,
})
)
})

/**
* Demonstrate client-side JS _only_ rendering
*/
app.get("/client-only", (_req, res) => {
res.send(`
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
res.send(
template({
includeCSS: false,
body: `
<div id="react-root">Loading…</div>
<script>
setTimeout(function () {
Expand All @@ -98,11 +116,60 @@ app.get("/client-only", (_req, res) => {
document.getElementsByTagName("head")[0].appendChild(script);
}, 1000)
</script>
</body>
</html>
`)
`,
})
)
})

/**
* Misc things that are not of interest for demonstrating react-responsive-media
*/

// TODO: Simplify this hideous typing.
type OnlyMatchList = Array<"hover" | "notHover" | (typeof SortedBreakpoints)[0]>

app.get("/", (_req, res) => {
res.send(
template({
includeCSS: false,
body: `
<ul>
<li><a href="/ssr-only">server-side rendering <em>only</em></a></li>
<li><a href="/client-only">client-side rendering <em>only</em></a></li>
<li><a href="/rehydration">server-side rendering <em>and</em> client-side rehydration</a></li>
</ul>
`,
})
)
})

const template = ({ includeCSS, body }) => `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5">
${
includeCSS
? `<style type="text/css" id="${SSRStyleID}">${createMediaStyle()}</style>`
: ""
}
</head>
<body>
${body}
</body>
</html>
`

function log(userAgent: string, onlyMatch?: string[], device?: string) {
// tslint:disable-next-line:no-console
console.log(
`Render: ${chalk.green(onlyMatch ? onlyMatch.join(", ") : "ALL")}\n` +
`Device: ${device ? chalk.green(device) : chalk.red("n/a")}\n` +
`User-Agent: ${chalk.yellow(userAgent)}\n`
)
}

const compiler = Webpack(webpackConfig)
const devServerOptions = Object.assign({}, webpackConfig.devServer, {
stats: {
Expand Down
3 changes: 2 additions & 1 deletion example/setup.ts
Expand Up @@ -17,7 +17,8 @@ const ExampleAppMedia = createMedia({
export const Media = ExampleAppMedia.Media
export const MediaContextProvider = ExampleAppMedia.MediaContextProvider
export const createMediaStyle = ExampleAppMedia.createMediaStyle
export const findBreakpointsForWidth = ExampleAppMedia.findBreakpointsForWidth
export const findBreakpointsForWidths = ExampleAppMedia.findBreakpointsForWidths
export const findBreakpointAtWidth = ExampleAppMedia.findBreakpointAtWidth
export const SortedBreakpoints = ExampleAppMedia.SortedBreakpoints

export const SSRStyleID = "ssr-rrm-style"
7 changes: 7 additions & 0 deletions example/tsconfig.json
@@ -0,0 +1,7 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"rootDirs": ["../example", "../src"],
"resolveJsonModule": true
}
}
3 changes: 3 additions & 0 deletions package.json
Expand Up @@ -41,13 +41,15 @@
"react": "^16.3.0"
},
"devDependencies": {
"@artsy/detect-responsive-traits": "^0.0.1",
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.0",
"@babel/node": "^7.0.0",
"@babel/plugin-proposal-class-properties": "^7.1.0",
"@babel/preset-env": "^7.0.0",
"@babel/preset-react": "^7.0.0",
"@babel/preset-typescript": "^7.0.0",
"@types/chalk": "^2.2.0",
"@types/express": "^4.16.0",
"@types/jest": "^23.1.0",
"@types/node": "^10.3.0",
Expand All @@ -59,6 +61,7 @@
"babel-jest": "^23.0.1",
"babel-loader": "^8.0.4",
"babel-preset-env": "^1.7.0",
"chalk": "^2.4.1",
"concurrently": "^3.5.1",
"conventional-changelog-ember": "^2.0.0",
"express": "^4.16.4",
Expand Down

0 comments on commit 31f6506

Please sign in to comment.