diff --git a/packages/electrode-archetype-njs-module-dev b/packages/electrode-archetype-njs-module-dev deleted file mode 160000 index 419782f2c..000000000 --- a/packages/electrode-archetype-njs-module-dev +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 419782f2c3000d1374a3e8fc191b8dd34356308d diff --git a/packages/electrode-react-context/.eslintrc b/packages/electrode-react-context/.eslintrc new file mode 100644 index 000000000..5f6ea6525 --- /dev/null +++ b/packages/electrode-react-context/.eslintrc @@ -0,0 +1,3 @@ +--- +extends: + - "./node_modules/electrode-archetype-njs-module-dev/config/eslint/.eslintrc-node" diff --git a/packages/electrode-react-context/.npmignore b/packages/electrode-react-context/.npmignore new file mode 100644 index 000000000..c318ddda3 --- /dev/null +++ b/packages/electrode-react-context/.npmignore @@ -0,0 +1,229 @@ + +# Created by https://www.gitignore.io/api/osx,node + +### OSX ### +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + + +### Node ### +# Logs +logs +*.log +npm-debug.log* + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directory +# https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git +node_modules + +# Optional npm cache directory +.npm + +# Optional REPL history +.node_repl_history + +### Vim template +[._]*.s[a-w][a-z] +[._]s[a-w][a-z] +*.un~ +Session.vim +.netrwhist +*~ +### GitBook template +# Node rules: +## Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +## Dependency directory +## Commenting this out is preferred by some people, see +## https://docs.npmjs.com/misc/faq#should-i-check-my-node_modules-folder-into-git +node_modules + +# Book build output +_book + +# eBook build output +*.epub +*.mobi +*.pdf +### TortoiseGit template +# Project-level settings +/.tgitconfig +### Xcode template +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## Build generated +build/ +DerivedData + +## Various settings +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata + +## Other +*.xccheckout +*.moved-aside +*.xcuserstate +### Emacs template +# -*- mode: gitignore; -*- +*~ +\#*\# +/.emacs.desktop +/.emacs.desktop.lock +*.elc +auto-save-list +tramp +.\#* + +# Org-mode +.org-id-locations +*_archive + +# flymake-mode +*_flymake.* + +# eshell files +/eshell/history +/eshell/lastdir + +# elpa packages +/elpa/ + +# reftex files +*.rel + +# AUCTeX auto folder +/auto/ + +# cask packages +.cask/ +### OSX template +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio + +*.iml + +## Directory-based project format: +.idea/ +# if you remove the above rule, at least ignore the following: + +# User-specific stuff: +# .idea/workspace.xml +# .idea/tasks.xml +# .idea/dictionaries + +# Sensitive or high-churn files: +# .idea/dataSources.ids +# .idea/dataSources.xml +# .idea/sqlDataSources.xml +# .idea/dynamic.xml +# .idea/uiDesigner.xml + +# Gradle: +# .idea/gradle.xml +# .idea/libraries + +# Mongo Explorer plugin: +# .idea/mongoSettings.xml + +## File-based project format: +*.ipr +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +/xclap.js +/.npmignore +.nyc_output +/test +/.eslintrc + diff --git a/packages/electrode-react-context/README.md b/packages/electrode-react-context/README.md new file mode 100644 index 000000000..624f1ba0b --- /dev/null +++ b/packages/electrode-react-context/README.md @@ -0,0 +1,99 @@ +# Electrode React Context + +Higher order React component used to add an `app` object property to the global React context. Each Electrode application can pass arbitrary props to make available to components via `this.context.app` but at a minimum should include the hapi `request`. + +## Installing + +``` +npm install electrode-react-context --save +``` + +## Usage + +The typical way to use the context is when contructing the app router. The `electrodeContext` wrapper is used to construct the parent Route component. + +~~~js +import electrodeContext from "electrode-react-context"; +import uiConfig from "electrode-ui-config"; + +// This function is invoked both on client and server. The `request` will be undefined on the client. +export const createRoutes = (request) => { + return ( + + + + + + + + ); +}; +~~~ + +Now all components in the app have access to an `app` property in the global context. This should be passed as the last argument to calls to functions exported by `electrode-ui-logger` and `electrode-cookies`. The `app.request` is needed for server-rendering since continuation local storage has been deprecated. By always passing in `app` component authors need not worry whether their component is rendering client or server-side. + +~~~js +import log from "electrode-ui-logger"; +import config from "electrode-ui-config"; + +export class Home extends React.Component { + constructor(props, context) { + super(props, context); + } + + render() { + log.info("Rendering Home component", this.context.app); + return ( + + + + ); + } +} + +Home.contextTypes = { + app: PropTypes.object +}; +~~~ + +### Functional Components + +Context is also available to functional components as the second argument. The `Home` component above could be re-written as: + +~~~js +export const Home = (props, {app}) => { + log.info("Rendering Home component", app); + return ( + + + + ); +}; +~~~ + +## Working with cookies + +The `electrode-cookies` module requires the `request` option when being invoked on the server. Since the `app` has the `request`, it can act as the `options` argument: + +~~~js +import Cookies from "electrode-cookies"; + +const CookieComponent = (props, {app}) => { + const cookieValue = Cookies.get("cookieName", app); + return
{cookieValue}
; +}; + +CookieComponent.contextTypes = { + app: PropTypes.object +}; +~~~ + +If you need to pass additional cookie specific options, you could do something like this: + +~~~js +Cookies.get("cookieName", Object.assign({}, app, { matchSubStr: true })); +~~~ \ No newline at end of file diff --git a/packages/electrode-react-context/lib/context-provider.js b/packages/electrode-react-context/lib/context-provider.js new file mode 100644 index 000000000..88c436b70 --- /dev/null +++ b/packages/electrode-react-context/lib/context-provider.js @@ -0,0 +1,26 @@ +"use strict"; +/* eslint-disable no-var */ + +var React = require("react"); +var PropTypes = require("prop-types"); +var createReactClass = require("create-react-class"); + +// This function is called both during SSR (from server/app.js) and CSR (client/app.jsx). The +// request arg will be undefined when called during CSR. +var contextProvider = function(Component, appContext) { + var ContextProvider = createReactClass({ + childContextTypes: { + app: PropTypes.object + }, + getChildContext: function() { + return {app: appContext}; + }, + render: function() { + return React.createElement(Component, this.props); + } + }); + + return ContextProvider; +}; + +module.exports = contextProvider; diff --git a/packages/electrode-react-context/package.json b/packages/electrode-react-context/package.json new file mode 100644 index 000000000..954b92d66 --- /dev/null +++ b/packages/electrode-react-context/package.json @@ -0,0 +1,53 @@ +{ + "name": "electrode-react-context", + "version": "1.0.0", + "description": "React HoC for providing app in the global context", + "main": "lib/context-provider.js", + "scripts": { + "test": "clap check", + "format": "prettier --write --print-width 100 *.js `find . -type d -d 1 -exec echo '{}/**/*.js' \\; | egrep -v '(/node_modules/|/dist/|/coverage/)'`" + }, + "keywords": [ + "react", + "context", + "electrode" + ], + "repository": { + "type": "git", + "url": "https://github.com/electrode-io/electrode.git" + }, + "license": "Apache-2.0", + "peerDependencies": { + "react": "^15.0.0 || ^0.14.0", + "react-dom": "^15.0.0 || ^0.14.0" + }, + "dependencies": { + "create-react-class": "^15.6.0", + "prop-types": "^15.5.10" + }, + "devDependencies": { + "electrode-archetype-njs-module-dev": "^2.2.0", + "react": "^15.0.0 || ^0.14.8", + "react-dom": "^15.0.0 || ^0.14.0" + }, + "nyc": { + "all": true, + "check-coverage": true, + "statements": 97.26, + "branches": 94.14, + "functions": 97.89, + "lines": 97.2, + "cache": true, + "reporter": [ + "lcov", + "text", + "text-summary" + ], + "exclude": [ + "coverage", + "*clap.js", + "gulpfile.js", + "test" + ] + } +} diff --git a/packages/electrode-react-context/test/.eslintrc b/packages/electrode-react-context/test/.eslintrc new file mode 100644 index 000000000..e9da91c41 --- /dev/null +++ b/packages/electrode-react-context/test/.eslintrc @@ -0,0 +1,3 @@ +--- +extends: + - "../node_modules/electrode-archetype-njs-module-dev/config/eslint/.eslintrc-test" \ No newline at end of file diff --git a/packages/electrode-react-context/test/mocha.opts b/packages/electrode-react-context/test/mocha.opts new file mode 100644 index 000000000..3caade9dd --- /dev/null +++ b/packages/electrode-react-context/test/mocha.opts @@ -0,0 +1,2 @@ +--require node_modules/electrode-archetype-njs-module-dev/config/test/setup.js +--recursive \ No newline at end of file diff --git a/packages/electrode-react-context/test/spec/context-provider.spec.js b/packages/electrode-react-context/test/spec/context-provider.spec.js new file mode 100644 index 000000000..0d48ba797 --- /dev/null +++ b/packages/electrode-react-context/test/spec/context-provider.spec.js @@ -0,0 +1,25 @@ +"use strict"; + +const React = require("react"); +const PropTypes = require("prop-types"); +const ReactDOMServer = require("react-dom/server"); +const contextProvider = require("../../lib/context-provider"); + +describe("contextProvider", () => { + it("exposes the app in React context", () => { + class Test extends React.Component { + render() { + return React.createElement("div", null, this.context.app.name); + } + } + + Test.contextTypes = { + app: PropTypes.object + }; + + const rendered = ReactDOMServer.renderToStaticMarkup( + React.createElement(contextProvider(Test, {name: "walmart"}))); + + expect(rendered).to.equal("
walmart
"); + }); +}); diff --git a/packages/electrode-react-context/xclap.js b/packages/electrode-react-context/xclap.js new file mode 100644 index 000000000..dff8954c4 --- /dev/null +++ b/packages/electrode-react-context/xclap.js @@ -0,0 +1,2 @@ +"use strict"; +require("electrode-archetype-njs-module-dev")(); \ No newline at end of file