diff --git a/pwa-devdocs/src/_config.yml b/pwa-devdocs/src/_config.yml index 9bb9549bd3..63f79a5f44 100644 --- a/pwa-devdocs/src/_config.yml +++ b/pwa-devdocs/src/_config.yml @@ -39,7 +39,11 @@ defaults: path: "venia-pwa-concept" values: toc-group: "venia-pwa-concept" - + - + scope: + path: "tutorials" + values: + toc-group: "tutorials" algolia: application_id: 'E642SEDTHL' diff --git a/pwa-devdocs/src/_data/top-nav.yml b/pwa-devdocs/src/_data/top-nav.yml index 9b46eeab9f..e453d0dbd4 100644 --- a/pwa-devdocs/src/_data/top-nav.yml +++ b/pwa-devdocs/src/_data/top-nav.yml @@ -9,3 +9,6 @@ - label: Venia Storefront url: /venia-pwa-concept/ + +- label: Tutorials + url: /tutorials/ diff --git a/pwa-devdocs/src/_data/tutorials.yml b/pwa-devdocs/src/_data/tutorials.yml new file mode 100644 index 0000000000..2edf11f5aa --- /dev/null +++ b/pwa-devdocs/src/_data/tutorials.yml @@ -0,0 +1,16 @@ +title: Tutorials +entries: + - label: Overview + url: /tutorials/ + + - label: Hello UPWARD + entries: + + - label: Creating a simple server + url: /tutorials/hello-upward/simple-server/ + + - label: Using the TemplateResolver + url: /tutorials/hello-upward/using-template-resolver/ + + - label: Adding React + url: /tutorials/hello-upward/adding-react/ diff --git a/pwa-devdocs/src/_includes/layout/sidebar-nav-root-item.html b/pwa-devdocs/src/_includes/layout/sidebar-nav-root-item.html index 5f54818d35..e807da0901 100644 --- a/pwa-devdocs/src/_includes/layout/sidebar-nav-root-item.html +++ b/pwa-devdocs/src/_includes/layout/sidebar-nav-root-item.html @@ -10,6 +10,9 @@ {% elsif page.toc-group == "venia-pwa-concept" %} {% assign toc = site.data.venia-pwa-concept %} +{% elsif page.toc-group == "tutorials" %} + {% assign toc = site.data.tutorials %} + {% endif %} -{% include layout/toc.html %} \ No newline at end of file +{% include layout/toc.html %} diff --git a/pwa-devdocs/src/_scss/main.scss b/pwa-devdocs/src/_scss/main.scss index ac95e7672d..dc5a013495 100644 --- a/pwa-devdocs/src/_scss/main.scss +++ b/pwa-devdocs/src/_scss/main.scss @@ -8,3 +8,4 @@ @import "src/material-icons"; @import "src/site-header-overrides.scss"; +@import "src/code-block-style-overrides.scss"; diff --git a/pwa-devdocs/src/_scss/src/_code-block-style-overrides.scss b/pwa-devdocs/src/_scss/src/_code-block-style-overrides.scss new file mode 100644 index 0000000000..af523ee1b9 --- /dev/null +++ b/pwa-devdocs/src/_scss/src/_code-block-style-overrides.scss @@ -0,0 +1,13 @@ +.highlight .gd, +.highlight .gi { + border: none; + background: none; +} + +.highlight .gi { + color: #00B218; +} + +.highlight .gd { + color: #FF3B6F; +} diff --git a/pwa-devdocs/src/tutorials/hello-upward/adding-react/index.md b/pwa-devdocs/src/tutorials/hello-upward/adding-react/index.md new file mode 100644 index 0000000000..c1cea29bbb --- /dev/null +++ b/pwa-devdocs/src/tutorials/hello-upward/adding-react/index.md @@ -0,0 +1,176 @@ +--- +title: Adding React +--- + +This tutorial teaches you how to add webpack to your UPWARD project and use it to create a React application. + +This tutorial builds on the project described in the previous [Using the TemplateResolver][] topic. + +## Install dependencies + +Add babel, webpack, and react dependencies to the project: + +```sh +yarn add @babel/core @babel/preset-env babel-loader webpack webpack-cli @babel/preset-react react react-dom +``` + +In addition to installing React, this command installs [babel][] configurations and [webpack][]. + +In general, React components are written using ES6([ECMAScript 2015][]) and [JSX][] syntax for the convenience and ease of readability they provide. +Using [babel][] and [webpack][] is a common way to incorporate ES6 and JSX into a React project. + +## Modify template + +Modify the `hello-world.mst` template to replace the original message with a "root" DOM node for the React application: + +{% raw %} + +```diff +{{> templates/open-document}} + + {{ title }} + + {{> templates/open-body}} + +- Hello Template World! ++
Loading...
++ ++ + + {{> templates/close-document}} +``` + +{% endraw %} + +This update renders a loading message in the root DOM and executes the `app.js` script after the browser parses the HTML document. + +## Create webpack config + +Create a `webpack.config.js` file to configure webpack behavior: + +```js +const path = require('path'); + +module.exports = { + mode: 'development', + entry: './src/hello-world.js', + output: { + filename: 'app.js', + path: path.resolve(__dirname, 'dist') + }, + module: { + rules: [ + { + test: /\.js$/, + exclude: /node_modules/, + loader: 'babel-loader' + } + ] + } +}; +``` + +This configuration file tells webpack to transpile all JavaScript files using babel and bundle `src/hello-world.js` with its dependencies into `dist/app.js`. + +## Create babel config + +Create a `.babelrc` file to configure babel: + +```text +{ + "presets": ["@babel/preset-env","@babel/preset-react"] +} +``` + +This configuration file tells babel what presets to use during JavaScript transpilation. +The presets used in this example, `@babel/preset-env` and `@babel/preset-react`, are common React development presets. + +## Create the React application + +Create a `src` directory in your project's root directory and a `hello-world.js` file inside that directory with the following content: + +```jsx +import React from 'react'; +import ReactDOM from 'react-dom'; + +class App extends React.Component { + render() { + return

Hello React World!

; + } +} + +ReactDOM.render(, document.getElementById('root')); +``` + +This file defines an `App` React component that returns a "Hello React World!" message wrapped inside `h1` tags. +It uses the `ReactDOM` module to inject the content of the application into the "root" DOM element in the document. + +## Make the application script publicly available + +Edit the `spec.yml` file to add a new URL pattern to check in the ConditionalResolver and a new `appScript` object. + +```diff +... + +response: + resolver: conditional + when: + - matches: request.url.pathname + pattern: '^/?$' + use: helloWorld + - matches: request.url.pathname + pattern: '^/hello-world/?$' + use: helloWorld ++ - matches: request.url.pathname ++ pattern: '^/js/app.js' ++ use: appScript + default: notFound + +helloWorld: +... + +notFound: +... + ++ appScript: ++ inline: ++ status: ++ resolver: inline ++ inline: 200 ++ headers: ++ resolver: inline ++ inline: ++ content-type: ++ resolver: inline ++ inline: 'application/javascript' ++ body: ++ resolver: file ++ file: ++ resolver: inline ++ inline: './dist/app.js' ++ encoding: ++ resolver: inline ++ inline: 'utf-8' +``` + +This update uses a [FileResolver][] which instructs the UPWARD server to respond with the contents of `dist/app.js` when there is a request for the `js/app.js` path. + +## Create the bundle and start the server + +Create the `dist/app.js` bundle from `src/hello-world.js` and start the UPWARD server: + +```sh +npx webpack && node server.js +``` + +When webpack bundles `src/hello-world.js` into `dist/app.js`, it includes its dependencies, such as React and ReactDOM. + +When you navigate to the server, you will see the React application render the "Hello React World!" message using the App component. + +[Using the TemplateResolver]: {{site.baseurl}}{%link tutorials/hello-upward/using-template-resolver/index.md %} + +[ecmascript 2015]: http://www.ecma-international.org/ecma-262/6.0/index.html +[jsx]: https://reactjs.org/docs/introducing-jsx.html +[babel]: https://babeljs.io/ +[webpack]: https://webpack.js.org/ +[FileResolver]: https://github.com/magento-research/pwa-studio/tree/develop/packages/upward-spec#fileresolver diff --git a/pwa-devdocs/src/tutorials/hello-upward/simple-server/index.md b/pwa-devdocs/src/tutorials/hello-upward/simple-server/index.md new file mode 100644 index 0000000000..f59a66b36a --- /dev/null +++ b/pwa-devdocs/src/tutorials/hello-upward/simple-server/index.md @@ -0,0 +1,214 @@ +--- +title: Creating a simple server +--- + +This tutorial teaches the basics of reading and writing an UPWARD specification file by creating a simple web server that returns a "Hello World" string. + +## Prerequisites + +- [Node][] >= 10.14.1 +- [Yarn][] (recommended) or [NPM][] + +## Initial setup + +1. Create the project directory and navigate into it: + + ```sh + mkdir hello-upward && cd hello-upward + ``` + +1. Use Yarn or NPM to initialize the project's `package.json` file: + + ```sh + yarn init -y + ``` + + Running this command creates a `package.json` file in your current directory. + The `package.json` file contains information about your project, such as name, version, dependencies, and runnable commands. + + The `-y` parameter is used in this tutorial to skip the interactive session. + Omit this parameter if you want to fill out information about your project during initialization. + +1. Install the `@magento/upward-js` and `express` packages: + + ```sh + yarn add @magento/upward-js express + ``` + + The [`upward-js`][] package contains modules for deploying an UPWARD-compliant server. + + [Express][] is a web framework for Node.js. + It is a dependency for the `upward-js` modules used in this tutorial. + +## Create the initial UPWARD spec + +An UPWARD server requires an [UPWARD specification][] file to tell it how to respond to requests. +Create a new file called `spec.yml` with the following content: + +```yml +status: response.status +headers: response.headers +body: response.body + +response: + resolver: inline + inline: + status: + resolver: inline + inline: 200 + headers: + resolver: inline + inline: + content-type: + resolver: inline + inline: 'text/string' + body: + resolver: inline + inline: 'Hello World!' +``` + +The first three lines set the `status`, `headers`, and `body` values required for an UPWARD server response. +These values are set by traversing a decision tree defined in the specification file. + +In this example, they are set to the `response.status`, `response.headers`, and `response.body` values defined in the `response` object. +These values are set using an UPWARD [InlineResolver][], which resolves the value of the `inline` property to a literal value. + +When passed to an UPWARD server, this file instructs the server to respond with a `200 OK` status code, content of type `text/string`, and a value of `Hello World!` in the response body for every request. + +## Create and run the server + +Create a `server.js` file with logic for starting the Node implementation of an UPWARD server. + +```js +const { createUpwardServer } = require('@magento/upward-js'); + +createUpwardServer({ + upwardPath: 'spec.yml', + bindLocal: true, + logUrl: true, + port: 8080 +}); +``` + +This file imports the `createUpwardServer` module from the `@magento/upward-js` package and uses it to start a web server. + +The script specifies the location of the UPWARD specification file through the `upwardPath` configuration and sets the port number of the server to `8080`. +The `logUrl` configuration tells the script to display the URL of the server and `bindLocal` tells it to create and bind to local HTTP server. + +Use `node` to run the server script: + +```sh +node server.js +``` + +When the server starts, the URL for the server is displayed in the terminal. + +```sh +> node server.js + +http://0.0.0.0:8080/ +``` + +Navigate to this URL in your browser to see the "Hello World!" message. + +## Define a 404 response + +When you browse to the server, every path request returns the same message. +To restrict this message to a specific path, use a ConditionalResolver. + +Edit `spec.yml` and replace the InlineResolver content of the `response` object with a [ConditionalResolver][] to define which paths return "Hello World!". + +```diff +status: response.status +headers: response.headers +body: response.body + +response: +- resolver: inline +- inline: +- status: +- resolver: inline +- inline: 200 +- headers: +- resolver: inline +- inline: +- content-type: +- resolver: inline +- inline: 'text/string' +- body: +- resolver: inline +- inline: 'Hello World!' ++ resolver: conditional ++ when: ++ - matches: request.url.pathname ++ pattern: '^/?$' ++ use: helloWorld ++ - matches: request.url.pathname ++ pattern: '^/hello-world/?$' ++ use: helloWorld ++ default: notFound +``` + +This code tells the server to look at the requested URL path and check to see if it matches the root or `/hello-world` path. +If it matches, the server uses an object called `helloWorld` to resolve the response. +For all other paths, the server resolves the response using the `notFound` object. + +After the `response` object, define the following `helloWorld` and `notFound` objects in the `spec.yml` file: + +```yml +helloWorld: + inline: + status: + resolver: inline + inline: 200 + headers: + resolver: inline + inline: + content-type: + resolver: inline + inline: 'text/string' + body: + resolver: inline + inline: 'Hello World!' + +notFound: + inline: + status: + resolver: inline + inline: 404 + headers: + resolver: inline + inline: + content-type: + resolver: inline + inline: 'text/string' + body: + resolver: inline + inline: 'Page not found!' +``` + +Now, when you start the server and navigate to the application, only the root and `/hello-world` path return the "Hello World!" message. +All other paths return the `404` response. + +Valid URLS: + +* `http://0.0.0.0:8080/` +* `http://0.0.0.0:8080/hello-world` + +Examples of invalid URLS: + +* `http://0.0.0.0:8080/goodbye-world` +* `http://0.0.0.0:8080/something-else` + +**Next:** [Rendering web pages using the TemplateResolver][] + +[upward specification]: https://github.com/magento-research/pwa-studio/tree/master/packages/upward-spec +[node]: https://nodejs.org +[yarn]: https://yarnpkg.com/en/ +[npm]: https://www.npmjs.com/get-npm +[express]: https://expressjs.com/ + +[`upward-js`]: {{site.baseurl}}{% link technologies/upward/reference-implementation/index.md %} +[Rendering web pages using the TemplateResolver]: {{site.baseurl}}{% link tutorials/hello-upward/using-template-resolver/index.md %} +[InlineResolver]: https://github.com/magento-research/pwa-studio/tree/develop/packages/upward-spec#inlineresolver +[ConditionalResolver]: https://github.com/magento-research/pwa-studio/tree/develop/packages/upward-spec#conditionalresolver diff --git a/pwa-devdocs/src/tutorials/hello-upward/using-template-resolver/index.md b/pwa-devdocs/src/tutorials/hello-upward/using-template-resolver/index.md new file mode 100644 index 0000000000..cb890f6f5b --- /dev/null +++ b/pwa-devdocs/src/tutorials/hello-upward/using-template-resolver/index.md @@ -0,0 +1,107 @@ +--- +title: Using the TemplateResolver +--- + +In this tutorial, you will learn how to use the [TemplateResolver][] in your UPWARD specification file to serve an HTML page rendered server-side. + +This tutorial builds on the project described in the previous [Creating a simple server][] tutorial. + +## Create web page templates + +In your project directory, create a `templates` directory. This directory will house [Mustache][] template files for the different pieces of the HTML page. + +**`open-document.mst`** + +```mustache + + + + + +``` + +This template partial creates the beginning of the HTML response. +The `head` tag is left open to allow custom metadata, such as title, scripts, and styles. + +**`open-body.mst`** + +```mustache + + +``` + +This template partial closes the `head` tag and begins the `body` tag of the HTML response. + +**`close-document.mst`** + +```mustache + + +``` + +This template partial closes the `body` and `html` tag for the HTML response. + +**`hello-world.mst`** + +{% raw %} + +```mustache +{{> templates/open-document}} + + {{ title }} + + {{> templates/open-body}} + + Hello Template World! + + {{> templates/close-document}} +``` + +{% endraw %} + +This template uses the previously defined template partials to create a complete HTML response. +The body contains the "Hello Template World!" message in bold and sets the page title to a `title` variable, which is available through the template context. + +## Add TemplateResolver + +Modify the `helloWorld` object in the `spec.yml` file and replace the simple text response to an actual HTML page. + +```diff +helloWorld: + inline: + status: + resolver: inline + inline: 200 + headers: + resolver: inline + inline: + content-type: + resolver: inline +- inline: 'text/string' ++ inline: 'text/html' + body: +- resolver: inline +- inline: 'Hello World!' ++ resolver: template ++ engine: mustache ++ template: './templates/hello-world.mst' ++ provide: ++ title: ++ resolver: inline ++ inline: 'This is the page title!' +``` + +This new code replaces the InlineResolver with a TemplateResolver in the response body. +This TemplateResolver configuration sets the rendering engine to `mustache`, since the templates created previously are in Mustache format. +It also provides the `title` variable to the context during template render. + +Now, when you start the server and navigate to the root or `/hello-world` path, you get an actual HTML webpage instead of text. +View the page source for the page to see the HTML rendered from the templates. + +**Next:** [Adding React to the server][] + +[Creating a simple server]: {{site.baseurl}}{%link tutorials/hello-upward/simple-server/index.md %} +[Adding React to the server]: {{site.baseurl}}{%link tutorials/hello-upward/adding-react/index.md %} + +[Mustache]: https://mustache.github.io/mustache.5.html +[TemplateResolver]: https://github.com/magento-research/pwa-studio/tree/develop/packages/upward-spec#templateresolver diff --git a/pwa-devdocs/src/tutorials/index.md b/pwa-devdocs/src/tutorials/index.md index d584fbf875..8f6c980ca4 100644 --- a/pwa-devdocs/src/tutorials/index.md +++ b/pwa-devdocs/src/tutorials/index.md @@ -1,5 +1,5 @@ --- -title: +title: Tutorials --- -{% include content-not-available.md %} \ No newline at end of file +This section contains tutorials that will help you become familiar with the different tools provided by PWA Studio.