{"_id":"584b3e3101634d1900dc6200","version":{"_id":"55a6e72f8cc73e0d00096638","project":"55a6e72e8cc73e0d00096635","hasReference":false,"__v":29,"hasDoc":true,"createdAt":"2015-07-15T23:05:19.125Z","releaseDate":"2015-07-15T23:05:19.125Z","categories":["55a6e7308cc73e0d00096639","55b7ed07aea7c8190058badb","5604567c0c78b00d0039b191","5605e6f23a93940d002b3e4a","5605f2bba4574a0d00811365","5605f309a4574a0d00811366","5608e3b98aedf50d0004cf8f","5608e4318aedf50d0004cf90","5608e6b5a7cc2f0d00d9754d","5608e6d331beb60d001b6560","5608f879a7cc2f0d00d97580","560b097887b71d0d000d3bd9","560b13cbafa0990d00979545","560b5cbec341310d00de2a01","560b5cd0c341310d00de2a02","566a35b81e08750d00a0c49b","566a3e8503b4b20d00d02a4a","567889d307bf6a0d0083edc8","569c8b7c15bb390d00db6f9d","56b254dc65ddf50d0076ba8f","57a8ebc4cdeea20e001d2a63","57e48a4000c8680e00fae6e7","5808216773557d0f00a1e428","58105ad54a8aa50f00aa4cba","58105bf298aea40f00afa3ba","58105f548a4aed0f00d67536","581061b898aea40f00afa3be","584b3de7e5f3a42300df6ef7","596839a75965d400155bb750"],"is_deprecated":false,"is_hidden":false,"is_beta":false,"is_stable":true,"codename":"","version_clean":"1.0.0","version":"1.0"},"parentDoc":null,"project":"55a6e72e8cc73e0d00096635","user":"55a6caa022cfa321008e01d6","__v":0,"category":{"_id":"584b3de7e5f3a42300df6ef7","project":"55a6e72e8cc73e0d00096635","__v":0,"version":"55a6e72f8cc73e0d00096638","sync":{"url":"","isSync":false},"reference":false,"createdAt":"2016-12-09T23:27:35.491Z","from_sync":false,"order":16,"slug":"javascript-api","title":"JavaScript Customization"},"updates":[],"next":{"pages":[],"description":""},"createdAt":"2016-12-09T23:28:49.824Z","link_external":false,"link_url":"","githubsync":"","sync_unique":"","hidden":false,"api":{"settings":"","results":{"codes":[]},"auth":"required","params":[],"url":""},"isReference":false,"order":5,"body":"This example shows how to use React and React Material-UI to implement a slideout drawer with a coupon acquisition flow. This entry includes:\n\n* [A Video of What We're Building](#what)\n* [Install the Dependencies](#install)\n* [Import the Dependencies](#import)\n* [Update webpack.conf.js](#webpack)\n* [Update Update app.js](#app)\n* [Add the <x-coupon-drawer> Element to the Page](#drawer)\n* [Call initReact from base.html](#initReact)\n* [Create an assets/js/components Folder](#folder)\n* [Create a CouponDrawer.js File](#coupondrawer)\n* [Create a VerticalLinearStepper.js File](#stepper)\n\n\n## <span id=\"what\"> A Video of What We're Building </span>\n[block:html]\n{\n  \"html\": \"<iframe width=\\\"560\\\" height=\\\"315\\\" src=\\\"https://www.youtube.com/embed/sudvuxJFxKc\\\" frameborder=\\\"0\\\" allowfullscreen></iframe>\"\n}\n[/block]\n## <span id=\"install\"> Install the Dependencies </span>\n\nMaterial-UI requires the `react-tap-event-plugin` module. Also, `document-register-element` is needed to polyfill `document.registerElement`. The babel presets and plugins are needed to support `Object.assign`, `react`, and `Material-UI`, respectively:\n\n```\nnpm install -save-dev document-register-element material-ui react react-dom react-tap-event-plugin  \nnpm install -save-dev babel-plugin-transform-object-assign babel-preset-react babel-preset-stage-1\n```\n\n## <span id=\"import\"> Import the Dependencies </span>\n\nNext, import the new dependencies into `<theme-name>/assets/js/app.js`\n\nNote the `CouponDrawer` import at the bottom. This file doesn't yet exist, but we'll shortly create a React component with this name:\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/72925b7-app.png\",\n        \"app.png\",\n        2176,\n        1500,\n        \"#1c1c1c\"\n      ],\n      \"sizing\": \"full\",\n      \"caption\": \"app.js imports\"\n    }\n  ]\n}\n[/block]\n## <span id=\"webpack\"> Update webpack.conf.js </span>\n\nUpdate `webpack.conf.js` with the new presets and plug-ins, as shown here:\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/e3550a5-webpack-int.png\",\n        \"webpack-int.png\",\n        2178,\n        1172,\n        \"#1c1d1e\"\n      ],\n      \"caption\": \"webpack.config.js: presets and plugins\",\n      \"sizing\": \"full\"\n    }\n  ]\n}\n[/block]\n## <span id=\"app\"> Update app.js </span>\n\nAdd the following code to the bottom of `<theme-name>/assets/js/app.js`:\n\n```\nwindow.initReact = function(contextJSON = '{}') {\n    injectTapEventPlugin();\n    const context = JSON.parse(contextJSON);\n    const proto = Object.create(HTMLElement.prototype, {\n        attachedCallback: {\n            value: function() {\n                const mountPoint = document.createElement('span');\n                const attrs = [].reduce.call(this.attributes, (memo, attr) => {\n                    memo[attr.name] = attr.value;\n                    return memo;\n                }, {});\n                const data = Object.assign({}, context, attrs);\n                \n                this.appendChild(mountPoint);\n                ReactDOM.render(\n                    <MuiThemeProvider>\n                        <CouponDrawer data={data} />\n                    </MuiThemeProvider>,\n                    mountPoint\n                );\n            }\n        }\n    });\n    document.registerElement('x-coupon-drawer', {prototype: proto});\n}\n```\n\nThis sets up a handler for attaching an `<x-coupon-drawer>` element to the page. We're using React here to render the `CouponDrawer` component. This block of code was taken and modified from: https://facebook.github.io/react/docs/web-components.html#using-react-in-your-web-components.\n\n## <span id=\"drawer\"> Add the <x-coupon-drawer> Element to the Page </span>\n\nAdd this in `<theme-name>/templates/layout/base.html`. (See the image below.) We're using this layout template for this example, although you would follow the same steps in any other template.\n\n## <span id=\"initReact\"> Call initReact from base.html </span>\n\nAdd a call to `window.initReact`. We're continuing to work with the `base.html` page for this example. Notice that we're also passing in the jsContext here. The `initReact` method will merge this context with an object created from the attributes placed upon the `<x-coupon-drawer>`, and will pass the combined data along to the React component:\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/e1e6bf4-base.png\",\n        \"base.png\",\n        2182,\n        834,\n        \"#1c1c1c\"\n      ],\n      \"caption\": \"base.html setup\"\n    }\n  ]\n}\n[/block]\n## <span id=\"folder\"> Create an assets/js/components Folder </span>\n\nWe'll store our React components here.\n\n## <span id=\"coupondrawer\"> Create a CouponDrawer.js File </span>\n\nCreate a `<theme-name>/assets/js/components/CouponDrawer.js` file. Populate this file with the following code block (which is adapted from http://www.material-ui.com/#/components/drawer):\n\n```\nimport React from 'react';\nimport Drawer from 'material-ui/Drawer';\nimport AppBar from 'material-ui/AppBar';\nimport RaisedButton from 'material-ui/RaisedButton';\nimport VerticalLinearStepper from './VerticalLinearStepper';\n\nexport default class CouponDrawer extends React.Component {\n\n  constructor(props) {\n    super(props);\n    this.state = {open: false};\n  }\n\n  handleToggle = () => this.setState({open: !this.state.open});\n\n  render() {\n    return (\n      <div>\n        <RaisedButton\n          label=\"Click here to get a coupon!\"\n          onTouchTap={this.handleToggle}\n        />\n        <Drawer width={200} openSecondary={true} open={this.state.open} >\n          <AppBar title=\"Coupon magic\" titleStyle={{fontSize: 14}} />\n          <VerticalLinearStepper />\n        </Drawer>\n      </div>\n    );\n  }\n}\n```\n\n## <span id=\"stepper\"> Create a VerticalLinearStepper.js File </span>\n\nCreate a `<theme-name>/assets/js/components/VerticalLinearStepper.js` file. Populate this file with the following code block:\n\n```\nimport React from 'react';\nimport {\n  Step,\n  Stepper,\n  StepButton,\n  StepContent,\n} from 'material-ui/Stepper';\nimport RaisedButton from 'material-ui/RaisedButton';\nimport FlatButton from 'material-ui/FlatButton';\nimport TextField from 'material-ui/TextField';\n\n/**\n * A basic vertical non-linear implementation\n */\nclass VerticalLinearStepper extends React.Component {\n\n  constructor(props) {\n      super(props);\n  }\n\n  state = {\n    stepIndex: 0,\n  };\n\n  handleNext = () => {\n    const {stepIndex} = this.state;\n    if (stepIndex < 2) {\n      this.setState({stepIndex: stepIndex + 1});\n    }\n  };\n\n  handlePrev = () => {\n    const {stepIndex} = this.state;\n    if (stepIndex > 0) {\n      this.setState({stepIndex: stepIndex - 1});\n    }\n  };\n\n  renderStepActions(step) {\n    return (\n      <div style={{margin: '12px 0'}}>\n        <RaisedButton\n          label=\"Next\"\n          disableTouchRipple={true}\n          disableFocusRipple={true}\n          primary={true}\n          onTouchTap={this.handleNext}\n          style={{marginRight: 12}}\n        />\n        {step > 0 && (\n          <FlatButton\n            label=\"Back\"\n            disableTouchRipple={true}\n            disableFocusRipple={true}\n            onTouchTap={this.handlePrev}\n          />\n        )}\n      </div>\n    );\n  }\n\n  render() {\n    const {stepIndex} = this.state;\n\n    return (\n      <div style={{maxWidth: 380, maxHeight: 400, margin: 'auto'}}>\n        <Stepper\n          activeStep={stepIndex}\n          linear={false}\n          orientation=\"vertical\"\n        >\n          <Step>\n            <StepButton onTouchTap={() => this.setState({stepIndex: 0})}>\n              Do this\n            </StepButton>\n            <StepContent>\n              <p>\n                Navigate to <a href=\"http://example.com\">here</a> and get your code.\n              </p>\n              {this.renderStepActions(0)}\n            </StepContent>\n          </Step>\n          <Step>\n            <StepButton onTouchTap={() => this.setState({stepIndex: 1})}>\n              Then this\n            </StepButton>\n            <StepContent>\n              <TextField hintText=\"Enter your code here\" style={{height: 100, width: 100}}/>\n              {this.renderStepActions(1)}\n            </StepContent>\n          </Step>\n          <Step>\n            <StepButton onTouchTap={() => this.setState({stepIndex: 2})}>\n              Collect your coupon!\n            </StepButton>\n            <StepContent>\n              <p>\n                <img src=\"https://upload.wikimedia.org/wikipedia/commons/thumb/9/9b/Wikipedia_mobile_en.svg/296px-Wikipedia_mobile_en.svg.png\"/>\n              </p>\n              {this.renderStepActions(2)}\n            </StepContent>\n          </Step>\n        </Stepper>\n      </div>\n    );\n  }\n}\n\nexport default VerticalLinearStepper;\n```\n[block:callout]\n{\n  \"type\": \"info\",\n  \"title\": \"Notes on the VerticalLinearStepper.js Example\",\n  \"body\": \"* The above code block was adapted from: http://www.material-ui.com/#/components/stepper.\\n\\n* We must register a custom element to set up the \\\"root\\\" of every React component we create.  However, within a React component, we can import other React components without having to register them.  For example, here we register `x-coupon-drawer` as a custom element that renders the React `CouponDrawer` component. However, within `CouponDrawer`, we can simply import the `VerticalLinearStepper` component needing to set it up in the same way.\"\n}\n[/block]","excerpt":"Third-party JavaScript","slug":"js-npm-adv","type":"basic","title":"Advanced npm Example"}

Advanced npm Example

Third-party JavaScript

This example shows how to use React and React Material-UI to implement a slideout drawer with a coupon acquisition flow. This entry includes: * [A Video of What We're Building](#what) * [Install the Dependencies](#install) * [Import the Dependencies](#import) * [Update webpack.conf.js](#webpack) * [Update Update app.js](#app) * [Add the <x-coupon-drawer> Element to the Page](#drawer) * [Call initReact from base.html](#initReact) * [Create an assets/js/components Folder](#folder) * [Create a CouponDrawer.js File](#coupondrawer) * [Create a VerticalLinearStepper.js File](#stepper) ## <span id="what"> A Video of What We're Building </span> [block:html] { "html": "<iframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/sudvuxJFxKc\" frameborder=\"0\" allowfullscreen></iframe>" } [/block] ## <span id="install"> Install the Dependencies </span> Material-UI requires the `react-tap-event-plugin` module. Also, `document-register-element` is needed to polyfill `document.registerElement`. The babel presets and plugins are needed to support `Object.assign`, `react`, and `Material-UI`, respectively: ``` npm install -save-dev document-register-element material-ui react react-dom react-tap-event-plugin npm install -save-dev babel-plugin-transform-object-assign babel-preset-react babel-preset-stage-1 ``` ## <span id="import"> Import the Dependencies </span> Next, import the new dependencies into `<theme-name>/assets/js/app.js` Note the `CouponDrawer` import at the bottom. This file doesn't yet exist, but we'll shortly create a React component with this name: [block:image] { "images": [ { "image": [ "https://files.readme.io/72925b7-app.png", "app.png", 2176, 1500, "#1c1c1c" ], "sizing": "full", "caption": "app.js imports" } ] } [/block] ## <span id="webpack"> Update webpack.conf.js </span> Update `webpack.conf.js` with the new presets and plug-ins, as shown here: [block:image] { "images": [ { "image": [ "https://files.readme.io/e3550a5-webpack-int.png", "webpack-int.png", 2178, 1172, "#1c1d1e" ], "caption": "webpack.config.js: presets and plugins", "sizing": "full" } ] } [/block] ## <span id="app"> Update app.js </span> Add the following code to the bottom of `<theme-name>/assets/js/app.js`: ``` window.initReact = function(contextJSON = '{}') { injectTapEventPlugin(); const context = JSON.parse(contextJSON); const proto = Object.create(HTMLElement.prototype, { attachedCallback: { value: function() { const mountPoint = document.createElement('span'); const attrs = [].reduce.call(this.attributes, (memo, attr) => { memo[attr.name] = attr.value; return memo; }, {}); const data = Object.assign({}, context, attrs); this.appendChild(mountPoint); ReactDOM.render( <MuiThemeProvider> <CouponDrawer data={data} /> </MuiThemeProvider>, mountPoint ); } } }); document.registerElement('x-coupon-drawer', {prototype: proto}); } ``` This sets up a handler for attaching an `<x-coupon-drawer>` element to the page. We're using React here to render the `CouponDrawer` component. This block of code was taken and modified from: https://facebook.github.io/react/docs/web-components.html#using-react-in-your-web-components. ## <span id="drawer"> Add the <x-coupon-drawer> Element to the Page </span> Add this in `<theme-name>/templates/layout/base.html`. (See the image below.) We're using this layout template for this example, although you would follow the same steps in any other template. ## <span id="initReact"> Call initReact from base.html </span> Add a call to `window.initReact`. We're continuing to work with the `base.html` page for this example. Notice that we're also passing in the jsContext here. The `initReact` method will merge this context with an object created from the attributes placed upon the `<x-coupon-drawer>`, and will pass the combined data along to the React component: [block:image] { "images": [ { "image": [ "https://files.readme.io/e1e6bf4-base.png", "base.png", 2182, 834, "#1c1c1c" ], "caption": "base.html setup" } ] } [/block] ## <span id="folder"> Create an assets/js/components Folder </span> We'll store our React components here. ## <span id="coupondrawer"> Create a CouponDrawer.js File </span> Create a `<theme-name>/assets/js/components/CouponDrawer.js` file. Populate this file with the following code block (which is adapted from http://www.material-ui.com/#/components/drawer): ``` import React from 'react'; import Drawer from 'material-ui/Drawer'; import AppBar from 'material-ui/AppBar'; import RaisedButton from 'material-ui/RaisedButton'; import VerticalLinearStepper from './VerticalLinearStepper'; export default class CouponDrawer extends React.Component { constructor(props) { super(props); this.state = {open: false}; } handleToggle = () => this.setState({open: !this.state.open}); render() { return ( <div> <RaisedButton label="Click here to get a coupon!" onTouchTap={this.handleToggle} /> <Drawer width={200} openSecondary={true} open={this.state.open} > <AppBar title="Coupon magic" titleStyle={{fontSize: 14}} /> <VerticalLinearStepper /> </Drawer> </div> ); } } ``` ## <span id="stepper"> Create a VerticalLinearStepper.js File </span> Create a `<theme-name>/assets/js/components/VerticalLinearStepper.js` file. Populate this file with the following code block: ``` import React from 'react'; import { Step, Stepper, StepButton, StepContent, } from 'material-ui/Stepper'; import RaisedButton from 'material-ui/RaisedButton'; import FlatButton from 'material-ui/FlatButton'; import TextField from 'material-ui/TextField'; /** * A basic vertical non-linear implementation */ class VerticalLinearStepper extends React.Component { constructor(props) { super(props); } state = { stepIndex: 0, }; handleNext = () => { const {stepIndex} = this.state; if (stepIndex < 2) { this.setState({stepIndex: stepIndex + 1}); } }; handlePrev = () => { const {stepIndex} = this.state; if (stepIndex > 0) { this.setState({stepIndex: stepIndex - 1}); } }; renderStepActions(step) { return ( <div style={{margin: '12px 0'}}> <RaisedButton label="Next" disableTouchRipple={true} disableFocusRipple={true} primary={true} onTouchTap={this.handleNext} style={{marginRight: 12}} /> {step > 0 && ( <FlatButton label="Back" disableTouchRipple={true} disableFocusRipple={true} onTouchTap={this.handlePrev} /> )} </div> ); } render() { const {stepIndex} = this.state; return ( <div style={{maxWidth: 380, maxHeight: 400, margin: 'auto'}}> <Stepper activeStep={stepIndex} linear={false} orientation="vertical" > <Step> <StepButton onTouchTap={() => this.setState({stepIndex: 0})}> Do this </StepButton> <StepContent> <p> Navigate to <a href="http://example.com">here</a> and get your code. </p> {this.renderStepActions(0)} </StepContent> </Step> <Step> <StepButton onTouchTap={() => this.setState({stepIndex: 1})}> Then this </StepButton> <StepContent> <TextField hintText="Enter your code here" style={{height: 100, width: 100}}/> {this.renderStepActions(1)} </StepContent> </Step> <Step> <StepButton onTouchTap={() => this.setState({stepIndex: 2})}> Collect your coupon! </StepButton> <StepContent> <p> <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/9/9b/Wikipedia_mobile_en.svg/296px-Wikipedia_mobile_en.svg.png"/> </p> {this.renderStepActions(2)} </StepContent> </Step> </Stepper> </div> ); } } export default VerticalLinearStepper; ``` [block:callout] { "type": "info", "title": "Notes on the VerticalLinearStepper.js Example", "body": "* The above code block was adapted from: http://www.material-ui.com/#/components/stepper.\n\n* We must register a custom element to set up the \"root\" of every React component we create. However, within a React component, we can import other React components without having to register them. For example, here we register `x-coupon-drawer` as a custom element that renders the React `CouponDrawer` component. However, within `CouponDrawer`, we can simply import the `VerticalLinearStepper` component needing to set it up in the same way." } [/block]