diff --git a/.flowconfig b/.flowconfig index f3cc1c3299..fec98e1ad2 100644 --- a/.flowconfig +++ b/.flowconfig @@ -15,4 +15,4 @@ all=warn include_warnings=true module.name_mapper='^@polkadot/app-\(accounts\|addresses\|explorer\|extrinsics\|home\|rpc\|settings\|storage\|toolbox\|vanitygen\)\(.*\)$' -> '/packages/app-\1/src\2' module.name_mapper='^@polkadot/apps\(.*\)$' -> '/packages/apps/src\1' -module.name_mapper='^@polkadot/ui-\(app\|keyring\|signer\)\(.*\)$' -> '/packages/ui-\1/src\2' +module.name_mapper='^@polkadot/ui-\(app\|identicon\|keyring\|react-rx\|react\|signer\)\(.*\)$' -> '/packages/ui-\1/src\2' diff --git a/README.md b/README.md index fe6ac6b5d7..52fe7bbcfa 100644 --- a/README.md +++ b/README.md @@ -29,5 +29,21 @@ The repo is split into a number of packages, each representing an application. T In addition the following libraries are also included in the repo. These are to be moved to the [@polkadot/ui](https://github.com/polkadot-js/ui/) repository once it reaches a base level of stability and usability. (At this point with the framework being tested on the apps above, it makes development easier having it close) -- [ui-keyring](packages/ui-keyring/) A browser-specific wrapper around the base [@polkadot/util-keyring](https://github.com/polkadot-js/util/) libary. - [ui-app](packages/ui-app/) A reactive (using RxJS) application framework with a number of useful shared components. +- [ui-keyring](packages/ui-keyring/) A browser-specific wrapper around the base [@polkadot/util-keyring](https://github.com/polkadot-js/util/) library. +- [ui-signer](packages/ui-signer/) Signer implementation for apps. +- [packages/ui-identicon](packages/ui-identicon/) Identity icon generator with raw publicKey as input +- [packages/ui-react](packages/ui-react) A collection of base React components +- [packages/ui-react-rx](packages/ui-react-rx) Base components that use the RxJS Observable APIs + +## Application + +For development purposes, the apps entry point can be launched with `yarn run start` and accessing the application of [http://localhost:3000](http://localhost:3000) + +## Demos + +Demos for the different libraries can be viewed with `yarn run demo:` and then browsing to [http://localhost:3000](http://localhost:3000). Available demo - + +- `yarn run demo:identicon` +- `yarn run demo:ui` +- `yarn run demo:rx` diff --git a/flow-typed/semver.js b/flow-typed/semver.js new file mode 100644 index 0000000000..6c93a6aba5 --- /dev/null +++ b/flow-typed/semver.js @@ -0,0 +1,8 @@ +// @flow + +declare module 'semver' { + declare module.exports: { + gt: (a: string, b: string) => boolean, + lt: (a: string, b: string) => boolean + } +} diff --git a/jest.config.js b/jest.config.js index f30e81fa36..263482d4d2 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1 +1,10 @@ -module.exports = require('@polkadot/dev-react/config/jest'); +const config = require('@polkadot/dev-react/config/jest'); + +module.exports = Object.assign({}, config, { + moduleNameMapper: { + '@polkadot/app-(accounts|addresses|explorer|extrinsics|rpc|storage|toolbox|vanitygen)(.*)$': '/packages/ui-$1/src/$2', + '@polkadot/ui-(app|identicon|keyring|react-rx|react|signer)(.*)$': '/packages/ui-$1/src/$2', + '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': 'empty/object', + '\\.(css|less)$': 'empty/object' + } +}); diff --git a/lerna.json b/lerna.json index 8451c99ce2..84a269d6f8 100644 --- a/lerna.json +++ b/lerna.json @@ -10,5 +10,5 @@ "packages": [ "packages/*" ], - "version": "0.7.2" + "version": "0.17.0" } diff --git a/package.json b/package.json index b4d0891b07..5c832d8901 100644 --- a/package.json +++ b/package.json @@ -9,10 +9,13 @@ "build": "polkadot-dev-build-babel", "check": "eslint packages && flow check", "clean": "polkadot-dev-clean-build", + "demo:identicon": "webpack-serve --config packages/ui-identicon/webpack.config.js --content packages/ui-identicon --port 3000", + "demo:rx": "webpack-serve --config packages/ui-react-rx/webpack.config.js --content packages/ui-react-rx --port 3000", + "demo:ui": "webpack-serve --config packages/ui-react/webpack.config.js --content packages/ui-react --port 3000", "deploy:ghpages": "gh-pages -d packages/apps/build", "vanitygen": "polkadot-dev-build-babel && node packages/app-vanitygen/build/generator/cli.js", "start": "cd packages/apps && webpack-serve --config webpack.config.js --port 3000", - "test": "echo 'no tests executed'" + "test": "jest --coverage" }, "devDependencies": { "@polkadot/dev-react": "^0.19.11", diff --git a/packages/app-accounts/package.json b/packages/app-accounts/package.json index 45a8184d10..aec9a66a91 100644 --- a/packages/app-accounts/package.json +++ b/packages/app-accounts/package.json @@ -1,6 +1,6 @@ { "name": "@polkadot/app-accounts", - "version": "0.7.2", + "version": "0.17.0", "main": "index.js", "repository": "https://github.com/polkadot-js/apps.git", "author": "Jaco Greeff ", @@ -12,6 +12,6 @@ }, "dependencies": { "@babel/runtime": "^7.0.0-beta.47", - "@polkadot/ui-app": "^0.7.2" + "@polkadot/ui-app": "^0.17.0" } } diff --git a/packages/app-addresses/package.json b/packages/app-addresses/package.json index 65f3528ffa..31c6f04a26 100644 --- a/packages/app-addresses/package.json +++ b/packages/app-addresses/package.json @@ -1,6 +1,6 @@ { "name": "@polkadot/app-addresses", - "version": "0.7.2", + "version": "0.17.0", "main": "index.js", "repository": "https://github.com/polkadot-js/apps.git", "author": "Jaco Greeff ", @@ -12,7 +12,7 @@ }, "dependencies": { "@babel/runtime": "^7.0.0-beta.47", - "@polkadot/app-accounts": "^0.7.2", - "@polkadot/ui-app": "^0.7.2" + "@polkadot/app-accounts": "^0.17.0", + "@polkadot/ui-app": "^0.17.0" } } diff --git a/packages/app-explorer/package.json b/packages/app-explorer/package.json index 3317bec542..8fb19f0d0f 100644 --- a/packages/app-explorer/package.json +++ b/packages/app-explorer/package.json @@ -1,6 +1,6 @@ { "name": "@polkadot/app-explorer", - "version": "0.7.2", + "version": "0.17.0", "main": "index.js", "repository": "https://github.com/polkadot-js/apps.git", "author": "Jaco Greeff ", @@ -14,7 +14,7 @@ "@babel/runtime": "^7.0.0-beta.47", "@polkadot/primitives-codec": "^0.17.2", "@polkadot/primitives-json": "^0.17.2", - "@polkadot/ui-app": "^0.7.2", + "@polkadot/ui-app": "^0.17.0", "@polkadot/util-crypto": "^0.22.2" } } diff --git a/packages/app-extrinsics/package.json b/packages/app-extrinsics/package.json index 27292e0009..8dbc52a136 100644 --- a/packages/app-extrinsics/package.json +++ b/packages/app-extrinsics/package.json @@ -1,6 +1,6 @@ { "name": "@polkadot/app-extrinsics", - "version": "0.7.2", + "version": "0.17.0", "main": "index.js", "repository": "https://github.com/polkadot-js/apps.git", "author": "Jaco Greeff ", @@ -13,8 +13,8 @@ "dependencies": { "@babel/runtime": "^7.0.0-beta.47", "@polkadot/extrinsics-codec": "^0.17.2", - "@polkadot/ui-app": "^0.7.2", - "@polkadot/ui-signer": "^0.7.2", + "@polkadot/ui-app": "^0.17.0", + "@polkadot/ui-signer": "^0.17.0", "react-dropzone": "^4.2.9" } } diff --git a/packages/app-rpc/package.json b/packages/app-rpc/package.json index d463b495c0..fa5520a514 100644 --- a/packages/app-rpc/package.json +++ b/packages/app-rpc/package.json @@ -1,6 +1,6 @@ { "name": "@polkadot/app-rpc", - "version": "0.7.2", + "version": "0.17.0", "main": "index.js", "repository": "https://github.com/polkadot-js/apps.git", "author": "Jaco Greeff ", @@ -12,8 +12,8 @@ }, "dependencies": { "@babel/runtime": "^7.0.0-beta.47", - "@polkadot/ui-app": "^0.7.2", - "@polkadot/ui-keyring": "^0.7.2", - "@polkadot/ui-signer": "^0.7.2" + "@polkadot/ui-app": "^0.17.0", + "@polkadot/ui-keyring": "^0.17.0", + "@polkadot/ui-signer": "^0.17.0" } } diff --git a/packages/app-storage/package.json b/packages/app-storage/package.json index e0862566cf..460fbfa962 100644 --- a/packages/app-storage/package.json +++ b/packages/app-storage/package.json @@ -1,6 +1,6 @@ { "name": "@polkadot/app-storage", - "version": "0.7.2", + "version": "0.17.0", "main": "index.js", "repository": "https://github.com/polkadot-js/apps.git", "author": "Jaco Greeff ", @@ -12,6 +12,6 @@ }, "dependencies": { "@babel/runtime": "^7.0.0-beta.47", - "@polkadot/ui-app": "^0.7.2" + "@polkadot/ui-app": "^0.17.0" } } diff --git a/packages/app-toolbox/package.json b/packages/app-toolbox/package.json index cea1a35b5a..7b651986e3 100644 --- a/packages/app-toolbox/package.json +++ b/packages/app-toolbox/package.json @@ -1,6 +1,6 @@ { "name": "@polkadot/app-toolbox", - "version": "0.7.2", + "version": "0.17.0", "main": "index.js", "repository": "https://github.com/polkadot-js/apps.git", "author": "Jaco Greeff ", @@ -12,7 +12,7 @@ }, "dependencies": { "@babel/runtime": "^7.0.0-beta.47", - "@polkadot/ui-app": "^0.7.2", - "@polkadot/ui-keyring": "^0.7.2" + "@polkadot/ui-app": "^0.17.0", + "@polkadot/ui-keyring": "^0.17.0" } } diff --git a/packages/app-vanitygen/package.json b/packages/app-vanitygen/package.json index b8e4d52f6f..b1bdbee079 100644 --- a/packages/app-vanitygen/package.json +++ b/packages/app-vanitygen/package.json @@ -1,6 +1,6 @@ { "name": "@polkadot/app-vanitygen", - "version": "0.7.2", + "version": "0.17.0", "main": "index.js", "repository": "https://github.com/polkadot-js/apps.git", "author": "Jaco Greeff ", @@ -12,7 +12,7 @@ }, "dependencies": { "@babel/runtime": "^7.0.0-beta.47", - "@polkadot/ui-app": "^0.7.2", + "@polkadot/ui-app": "^0.17.0", "chalk": "^2.4.1", "yargs": "10.1.2" }, diff --git a/packages/apps/package.json b/packages/apps/package.json index 4ad4a44a4c..e999db75d5 100644 --- a/packages/apps/package.json +++ b/packages/apps/package.json @@ -1,6 +1,6 @@ { "name": "@polkadot/apps", - "version": "0.7.2", + "version": "0.17.0", "description": "An Apps portal into the Polkadot network", "main": "index.js", "homepage": ".", @@ -13,16 +13,16 @@ }, "dependencies": { "@babel/runtime": "^7.0.0-beta.47", - "@polkadot/app-accounts": "^0.7.2", - "@polkadot/app-addresses": "^0.7.2", - "@polkadot/app-explorer": "^0.7.2", - "@polkadot/app-extrinsics": "^0.7.2", - "@polkadot/app-rpc": "^0.7.2", - "@polkadot/app-storage": "^0.7.2", - "@polkadot/app-toolbox": "^0.7.2", - "@polkadot/app-vanitygen": "^0.7.2", - "@polkadot/ui-app": "^0.7.2", - "@polkadot/ui-signer": "^0.7.2" + "@polkadot/app-accounts": "^0.17.0", + "@polkadot/app-addresses": "^0.17.0", + "@polkadot/app-explorer": "^0.17.0", + "@polkadot/app-extrinsics": "^0.17.0", + "@polkadot/app-rpc": "^0.17.0", + "@polkadot/app-storage": "^0.17.0", + "@polkadot/app-toolbox": "^0.17.0", + "@polkadot/app-vanitygen": "^0.17.0", + "@polkadot/ui-app": "^0.17.0", + "@polkadot/ui-signer": "^0.17.0" }, "devDependencies": { "babel-loader": "^8.0.0-beta.2", diff --git a/packages/apps/src/App/App.spec.js b/packages/apps/src/App/App.spec.js deleted file mode 100644 index 6e377ce2b8..0000000000 --- a/packages/apps/src/App/App.spec.js +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2017-2018 Jaco Greeff -// This software may be modified and distributed under the terms -// of the ISC license. See the LICENSE file for details. -// @flow - -import React from 'react'; -import ReactDOM from 'react-dom'; -import App from './index'; - -describe('App', () => { - it('renders without crashing', () => { - const div = document.createElement('div'); - - ReactDOM.render(, div); - ReactDOM.unmountComponentAtNode(div); - }); -}); diff --git a/packages/apps/webpack.config.js b/packages/apps/webpack.config.js index 0d20af41cf..da8af24075 100644 --- a/packages/apps/webpack.config.js +++ b/packages/apps/webpack.config.js @@ -10,7 +10,7 @@ const CopyWebpackPlugin = require('copy-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); -const packages = ['app-accounts', 'app-addresses', 'app-explorer', 'app-extrinsics', 'app-rpc', 'app-storage', 'app-toolbox', 'app-vanitygen', 'ui-app', 'ui-keyring', 'ui-signer']; +const packages = ['app-accounts', 'app-addresses', 'app-explorer', 'app-extrinsics', 'app-rpc', 'app-storage', 'app-toolbox', 'app-vanitygen', 'ui-app', 'ui-identicon', 'ui-keyring', 'ui-react-rx', 'ui-react', 'ui-signer']; function createWebpack ({ alias = {}, context, name = 'index' }) { const pkgJson = require(path.join(context, 'package.json')); diff --git a/packages/ui-app/package.json b/packages/ui-app/package.json index f17d9bb2c0..bbe2c48e84 100644 --- a/packages/ui-app/package.json +++ b/packages/ui-app/package.json @@ -1,6 +1,6 @@ { "name": "@polkadot/ui-app", - "version": "0.7.2", + "version": "0.17.0", "main": "index.js", "repository": "https://github.com/polkadot-js/apps.git", "author": "Jaco Greeff ", @@ -14,9 +14,9 @@ "@babel/runtime": "^7.0.0-beta.47", "@polkadot/extrinsics": "^0.17.2", "@polkadot/storage": "^0.17.2", - "@polkadot/ui-keyring": "^0.7.2", - "@polkadot/ui-react": "^0.14.3", - "@polkadot/ui-react-rx": "^0.14.3", + "@polkadot/ui-keyring": "^0.17.0", + "@polkadot/ui-react": "^0.17.0", + "@polkadot/ui-react-rx": "^0.17.0", "i18next": "^11.1.1", "i18next-browser-languagedetector": "^2.2.0", "i18next-xhr-backend": "^1.5.1", diff --git a/packages/ui-identicon/LICENSE b/packages/ui-identicon/LICENSE new file mode 100644 index 0000000000..40b9eba576 --- /dev/null +++ b/packages/ui-identicon/LICENSE @@ -0,0 +1,16 @@ +ISC License (ISC) + +Copyright 2016 Dan Finlay +Copyright 2017-2018 Jaco Greeff + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. diff --git a/packages/ui-identicon/README.md b/packages/ui-identicon/README.md new file mode 100644 index 0000000000..7c388156bc --- /dev/null +++ b/packages/ui-identicon/README.md @@ -0,0 +1,39 @@ +[![polkadotjs](https://img.shields.io/badge/polkadot-js-orange.svg?style=flat-square)](https://polkadot.js.org) +![isc](https://img.shields.io/badge/license-ISC-lightgrey.svg?style=flat-square) +[![style](https://img.shields.io/badge/code%20style-semistandard-lightgrey.svg?style=flat-square)](https://github.com/Flet/semistandard) +[![npm](https://img.shields.io/npm/v/@polkadot/ui-identicon.svg?style=flat-square)](https://www.npmjs.com/package/@polkadot/ui-identicon) +[![travis](https://img.shields.io/travis/polkadot-js/apps.svg?style=flat-square)](https://travis-ci.org/polkadot-js/ui) +[![maintainability](https://img.shields.io/codeclimate/maintainability/polkadot-js/apps.svg?style=flat-square)](https://codeclimate.com/github/polkadot-js/apps/maintainability) +[![coverage](https://img.shields.io/coveralls/polkadot-js/apps.svg?style=flat-square)](https://coveralls.io/github/polkadot-js/apps?branch=master) +[![dependency](https://david-dm.org/polkadot-js/apps.svg?style=flat-square&path=packages/ui-identicon)](https://david-dm.org/polkadot-js/apps?path=packages/ui-identicon) +[![devDependency](https://david-dm.org/polkadot-js/apps/dev-status.svg?style=flat-square&path=packages/ui-identicon)](https://david-dm.org/polkadot-js/apps?path=packages/ui-identicon#info=devDependencies) + +# @polkadot/ui-identicon + +Adapted from [Jazzicon](https://github.com/danfinlay/jazzicon) by Dan Finlay with the following changes - + +- Random values now is read from the Uint8Array supplied (as opposed to having the seed as a number). This allows us to give an publicKey/address as an input and use those values in the pattern generation. +- Upgrade to the underlying [color](https://github.com/Qix-/color) library +- Generate circles as shapes (instead of rectangles) +- Interface updated to take in optional className & style +- Update everywhere to use ES6 +- Split source into self-contained functions (TODO: future testing) +- Everything has been updated to use flow +- Test the library functions +- Copyright headers added (original also under ISC) + +## Usage + +```js +import identicon from '@polkadot/ui-identicon'; + +const publicKey = new Uint8Array([...]); // 32 bytes +const body = document.querySelector('body'); +const identity = identicon(publicKey, 100 /* diameter, optional */, 'my-class' /* className, optional */ /* style. optional */ /* colors, optional */); + +body.append(identity); +``` + +Also see [src/demo.js](src/demo.js) for a randomly generated example. + +![demo](https://raw.githubusercontent.com/polkadot-js/ui/master/packages/ui-identicon/demo.png) diff --git a/packages/ui-identicon/demo.png b/packages/ui-identicon/demo.png new file mode 100644 index 0000000000..5698431db5 Binary files /dev/null and b/packages/ui-identicon/demo.png differ diff --git a/packages/ui-identicon/index.html b/packages/ui-identicon/index.html new file mode 100644 index 0000000000..58667b9ffa --- /dev/null +++ b/packages/ui-identicon/index.html @@ -0,0 +1,17 @@ + + + + + + +
+ + + diff --git a/packages/ui-identicon/package.json b/packages/ui-identicon/package.json new file mode 100644 index 0000000000..be2e8e2f43 --- /dev/null +++ b/packages/ui-identicon/package.json @@ -0,0 +1,21 @@ +{ + "name": "@polkadot/ui-identicon", + "version": "0.17.0", + "description": "Renders an SVG picture representing an address", + "main": "index.js", + "scripts": { + "build": "polkadot-dev-build-babel", + "check": "eslint src && flow check", + "test": "echo \"Tests only available from root wrapper\"" + }, + "author": "Jaco Greeff ", + "license": "ISC", + "dependencies": { + "@babel/runtime": "^7.0.0-beta.47", + "color": "^3.0.0" + }, + "devDependencies": { + "@polkadot/util-crypto": "^0.22.2", + "xmlserializer": "^0.6.1" + } +} diff --git a/packages/ui-identicon/src/colors.js b/packages/ui-identicon/src/colors.js new file mode 100644 index 0000000000..478d2f0bcd --- /dev/null +++ b/packages/ui-identicon/src/colors.js @@ -0,0 +1,30 @@ +// Copyright 2016 Dan Finlay +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import type { Seeder } from './types'; + +type ColorGen = { + (alpha?: number): string +}; + +const Color = require('color'); + +const { COLORS } = require('./defaults'); + +const WOBBLE = 30; + +module.exports = function colors (seeder: Seeder): ColorGen { + const amount = (seeder() * WOBBLE) - (WOBBLE / 2); + const all = COLORS.map((hex) => Color(hex).rotate(amount)); + + return (alpha: number = 0.9): string => { + const index = Math.floor(all.length * seeder()); + + return all.splice(index, 1)[0] + .alpha(alpha) + .string(); + }; +}; diff --git a/packages/ui-identicon/src/colors.spec.js b/packages/ui-identicon/src/colors.spec.js new file mode 100644 index 0000000000..d1d9e09eef --- /dev/null +++ b/packages/ui-identicon/src/colors.spec.js @@ -0,0 +1,34 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. + +const newSeeder = require('./seeder'); +const newColors = require('./colors'); + +describe('colors', () => { + let colors; + + beforeEach(() => { + colors = newColors(newSeeder()); + }); + + it('generates using default alpha', () => { + expect( + colors() + ).toEqual('hsla(166.70000000000005, 98.6%, 27.6%, 0.9)'); + }); + + it('applies specified alpha', () => { + expect( + colors(0.5) + ).toEqual('hsla(166.70000000000005, 98.6%, 27.6%, 0.5)'); + }); + + it('rolates colors', () => { + colors(); + + expect( + colors() + ).not.toEqual('hsla(166.70000000000005, 98.6%, 27.6%, 0.9)'); + }); +}); diff --git a/packages/ui-identicon/src/container.js b/packages/ui-identicon/src/container.js new file mode 100644 index 0000000000..82e0e1eb29 --- /dev/null +++ b/packages/ui-identicon/src/container.js @@ -0,0 +1,30 @@ +// Copyright 2016 Dan Finlay +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +module.exports = function container (diameter: number, background: string = 'white', className: string = '', _style: { [string]: string } = {}): HTMLElement { + const element = document.createElement('div'); + const style = Object.assign({ + background, + borderRadius: `${diameter / 2}px`, + display: 'inline-block', + height: `${diameter}px`, + margin: '0px', + overflow: 'hidden', + padding: '0px', + width: `${diameter}px` + }, _style); + + element.className = className; + element.style.background = background; + + // FIXME Flow seems to believe element.style[key] has number keys + // flowlint-next-line unclear-type:off + Object.keys(style).forEach((key: any) => { + element.style[key] = style[key]; + }); + + return element; +}; diff --git a/packages/ui-identicon/src/container.spec.js b/packages/ui-identicon/src/container.spec.js new file mode 100644 index 0000000000..9b5f5d9e7a --- /dev/null +++ b/packages/ui-identicon/src/container.spec.js @@ -0,0 +1,43 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. + +const container = require('./container'); + +describe('container', () => { + it('applies default styles', () => { + expect( + container(100).style._values + ).toMatchObject({ + 'background': 'white', + 'border-radius': '50px', + 'display': 'inline-block', + 'height': '100px', + 'margin': '0px', + 'overflow': 'hidden', + 'padding': '0px', + 'width': '100px' + }); + }); + + it('overrides with supplied styles', () => { + expect( + container(50, 'black', '', { display: 'block' }).style._values + ).toMatchObject({ + 'background': 'black', + 'border-radius': '25px', + 'display': 'block', + 'height': '50px', + 'margin': '0px', + 'overflow': 'hidden', + 'padding': '0px', + 'width': '50px' + }); + }); + + it('applies the specified className', () => { + expect( + container(100, 'blue', 'testClass').className + ).toEqual('testClass'); + }); +}); diff --git a/packages/ui-identicon/src/defaults.js b/packages/ui-identicon/src/defaults.js new file mode 100644 index 0000000000..85626a7009 --- /dev/null +++ b/packages/ui-identicon/src/defaults.js @@ -0,0 +1,25 @@ +// Copyright 2016 Dan Finlay +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +const COLORS: Array = [ + '#01888C', // teal + '#FC7500', // bright orange + '#034F5D', // dark teal + '#F73F01', // orangered + '#FC1960', // magenta + '#C7144C', // raspberry + '#F3C100', // goldenrod + '#1598F2', // lightning blue + '#2465E1', // sail blue + '#F19E02' // gold +]; + +const SHAPE_COUNT = 5; + +module.exports = { + COLORS, + SHAPE_COUNT +}; diff --git a/packages/ui-identicon/src/demo.js b/packages/ui-identicon/src/demo.js new file mode 100644 index 0000000000..71a74ef563 --- /dev/null +++ b/packages/ui-identicon/src/demo.js @@ -0,0 +1,35 @@ +// Copyright 2016 Dan Finlay +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +const randomAsU8a = require('@polkadot/util-crypto/random/asU8a'); + +const identicon = require('./index'); + +const element = document.getElementById('demo'); + +if (!element) { + throw new Error('Unable to find #demo element'); +} + +function generateIcon (seed: Uint8Array = randomAsU8a(32)): void { + const start = Date.now(); + + element.append( + identicon(seed, 100, 'padded') + ); + + console.log(`Icon generated in ${(Date.now() - start)}ms`); +} + +function generateIcons (count: number = 512): void { + generateIcon(new Uint8Array(32)); + + for (let index = 1; index < count; index++) { + generateIcon(); + } +} + +generateIcons(); diff --git a/packages/ui-identicon/src/index.js b/packages/ui-identicon/src/index.js new file mode 100644 index 0000000000..3acb2c4987 --- /dev/null +++ b/packages/ui-identicon/src/index.js @@ -0,0 +1,32 @@ +// Copyright 2016 Dan Finlay +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +const colors = require('./colors'); +const newContainer = require('./container'); +const newSeeder = require('./seeder'); +const newShape = require('./shape/circle'); +const newElement = require('./svg/element'); +const { SHAPE_COUNT } = require('./defaults'); + +module.exports = function identicon (seed: string | Uint8Array, diameter: number = 256, className: string = '', style?: { [string]: string }): HTMLElement { + const seeder = newSeeder(seed); + const colorGen = colors(seeder); + const outer = newContainer(diameter, 'white', className, style); + const container = newContainer(diameter, colorGen()); + const svg = newElement(diameter); + + outer.appendChild(container); + container.appendChild(svg); + + for (let count = 0; count < SHAPE_COUNT; count++) { + const fill = colorGen(); + const shape = newShape(seeder, fill, diameter, count); + + svg.appendChild(shape); + } + + return outer; +}; diff --git a/packages/ui-identicon/src/index.spec.js b/packages/ui-identicon/src/index.spec.js new file mode 100644 index 0000000000..cd19451879 --- /dev/null +++ b/packages/ui-identicon/src/index.spec.js @@ -0,0 +1,25 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. + +const xmlserializer = require('xmlserializer'); + +const identicon = require('./index'); + +describe('identicon', () => { + it('generates a basic [0,..,0] identicon', () => { + expect( + xmlserializer.serializeToString( + identicon(new Uint8Array(32)) + ) + ).toEqual('
'); + }); + + it('allows overrides', () => { + expect( + xmlserializer.serializeToString( + identicon(new Uint8Array(32), 100, 'testClass', { display: 'block' }) + ) + ).toEqual('
'); + }); +}); diff --git a/packages/ui-identicon/src/seeder.js b/packages/ui-identicon/src/seeder.js new file mode 100644 index 0000000000..21e87563e1 --- /dev/null +++ b/packages/ui-identicon/src/seeder.js @@ -0,0 +1,35 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import type { Seeder } from './types'; + +const isU8a = require('@polkadot/util/is/u8a'); +const u8aFromString = require('@polkadot/util/u8a/fromString'); + +const DIVISOR = 256 * 256; + +module.exports = function seeder (_seed: string | Uint8Array = new Uint8Array(32)): Seeder { + // $FlowFixMe type checking + const seed: Uint8Array = isU8a(_seed) + ? _seed + // $FlowFixMe type has been determined + : u8aFromString(_seed); + + let index = (seed[Math.floor(seed.length / 2)] % seed.length) - 1; + + const next = () => { + index += 1; + + if (index === seed.length) { + index = 0; + } + + return seed[index]; + }; + + return (): number => { + return ((next() * 256) + next()) / DIVISOR; + }; +}; diff --git a/packages/ui-identicon/src/seeder.spec.js b/packages/ui-identicon/src/seeder.spec.js new file mode 100644 index 0000000000..197e7e6414 --- /dev/null +++ b/packages/ui-identicon/src/seeder.spec.js @@ -0,0 +1,27 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. + +const newSeeder = require('./seeder'); + +describe('seeder', () => { + let seeder; + + beforeEach(() => { + seeder = newSeeder(new Uint8Array([1, 2, 3, 4])); + }); + + it('generates numbers using 2 spaces', () => { + expect( + seeder() + ).toEqual(0.0156402587890625); + }); + + it('generates numbers using 2 spaces (incremented)', () => { + seeder(); + + expect( + seeder() + ).toEqual(0.0078582763671875); + }); +}); diff --git a/packages/ui-identicon/src/shape/circle.js b/packages/ui-identicon/src/shape/circle.js new file mode 100644 index 0000000000..cfd74c774e --- /dev/null +++ b/packages/ui-identicon/src/shape/circle.js @@ -0,0 +1,24 @@ +// Copyright 2016 Dan Finlay +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import type { Seeder } from '../types'; + +const newCircle = require('../svg/circle'); +const { SHAPE_COUNT } = require('../defaults'); + +module.exports = function circle (seeder: Seeder, fill: string, diameter: number, count: number): Element { + const center = diameter / 2; + const angle = seeder() * 360; + const radius = (((SHAPE_COUNT - count) / SHAPE_COUNT) * (diameter / 2)) + ((diameter / 8) * seeder()); + const offset = (diameter / 4) * (seeder() + ((count + 1) / SHAPE_COUNT)); + const cx = (offset * Math.sin(angle)) + center; + const cy = (offset * Math.cos(angle)) + center; + const svg = newCircle(radius, cx, cy); + + svg.setAttributeNS(null, 'fill', fill); + + return svg; +}; diff --git a/packages/ui-identicon/src/shape/circle.spec.js b/packages/ui-identicon/src/shape/circle.spec.js new file mode 100644 index 0000000000..984a0f4d82 --- /dev/null +++ b/packages/ui-identicon/src/shape/circle.spec.js @@ -0,0 +1,18 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. + +const xmlserializer = require('xmlserializer'); + +const seeder = require('../seeder'); +const circle = require('./circle'); + +describe('circle', () => { + it('creates a circle shape', () => { + expect( + xmlserializer.serializeToString( + circle(seeder(), 'blue', 50, 2) + ) + ).toEqual(''); + }); +}); diff --git a/packages/ui-identicon/src/shape/square.js b/packages/ui-identicon/src/shape/square.js new file mode 100644 index 0000000000..2af65740d0 --- /dev/null +++ b/packages/ui-identicon/src/shape/square.js @@ -0,0 +1,27 @@ +// Copyright 2016 Dan Finlay +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import type { Seeder } from '../types'; + +const newRect = require('../svg/rect'); +const { SHAPE_COUNT } = require('../defaults'); + +module.exports = function square (seeder: Seeder, fill: string, diameter: number, count: number): Element { + const center = diameter / 2; + const svg = newRect(diameter); + const firstRot = seeder(); + const angle = Math.PI * 2 * firstRot; + const scale = count / SHAPE_COUNT; + const velocity = ((diameter / SHAPE_COUNT) * seeder()) + (scale * diameter); + const tx = (Math.cos(angle) * velocity).toFixed(3); + const ty = (Math.sin(angle) * velocity).toFixed(3); + const rot = ((firstRot * 360) + (seeder() * 180)).toFixed(1); + + svg.setAttributeNS(null, 'transform', `translate(${tx} ${ty}) rotate(${rot} ${center} ${center})`); + svg.setAttributeNS(null, 'fill', fill); + + return svg; +}; diff --git a/packages/ui-identicon/src/svg/circle.js b/packages/ui-identicon/src/svg/circle.js new file mode 100644 index 0000000000..89465cc41b --- /dev/null +++ b/packages/ui-identicon/src/svg/circle.js @@ -0,0 +1,16 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +const svg = require('./svg'); + +module.exports = function circle (r: number, cx: number, cy: number): Element { + const elem = svg('circle'); + + elem.setAttributeNS(null, 'cx', `${cx}`); + elem.setAttributeNS(null, 'cy', `${cy}`); + elem.setAttributeNS(null, 'r', `${r}`); + + return elem; +}; diff --git a/packages/ui-identicon/src/svg/circle.spec.js b/packages/ui-identicon/src/svg/circle.spec.js new file mode 100644 index 0000000000..f9894f01ac --- /dev/null +++ b/packages/ui-identicon/src/svg/circle.spec.js @@ -0,0 +1,17 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. + +const xmlserializer = require('xmlserializer'); + +const circle = require('./circle'); + +describe('circle', () => { + it('creates a basic SVG circle element', () => { + expect( + xmlserializer.serializeToString( + circle(123, 12, 34) + ) + ).toEqual(''); + }); +}); diff --git a/packages/ui-identicon/src/svg/element.js b/packages/ui-identicon/src/svg/element.js new file mode 100644 index 0000000000..0f20023ac1 --- /dev/null +++ b/packages/ui-identicon/src/svg/element.js @@ -0,0 +1,18 @@ +// Copyright 2016 Dan Finlay +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +const svg = require('./svg'); + +module.exports = function element (size: number, type: string = 'svg', x: number = 0, y: number = 0): Element { + const elem = svg(type); + + elem.setAttributeNS(null, 'x', `${x}`); + elem.setAttributeNS(null, 'y', `${y}`); + elem.setAttributeNS(null, 'width', `${size}`); + elem.setAttributeNS(null, 'height', `${size}`); + + return elem; +}; diff --git a/packages/ui-identicon/src/svg/element.spec.js b/packages/ui-identicon/src/svg/element.spec.js new file mode 100644 index 0000000000..960524f721 --- /dev/null +++ b/packages/ui-identicon/src/svg/element.spec.js @@ -0,0 +1,17 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. + +const xmlserializer = require('xmlserializer'); + +const element = require('./element'); + +describe('element', () => { + it('creates a basic SVG element', () => { + expect( + xmlserializer.serializeToString( + element(123) + ) + ).toEqual(''); + }); +}); diff --git a/packages/ui-identicon/src/svg/rect.js b/packages/ui-identicon/src/svg/rect.js new file mode 100644 index 0000000000..023926236d --- /dev/null +++ b/packages/ui-identicon/src/svg/rect.js @@ -0,0 +1,15 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +const element = require('./element'); + +module.exports = function rect (size: number): Element { + const elem = element(size, 'rect'); + + elem.setAttributeNS(null, 'rx', `${size / 16}`); + elem.setAttributeNS(null, 'ry', `${size / 16}`); + + return elem; +}; diff --git a/packages/ui-identicon/src/svg/rect.spec.js b/packages/ui-identicon/src/svg/rect.spec.js new file mode 100644 index 0000000000..79c91b33c2 --- /dev/null +++ b/packages/ui-identicon/src/svg/rect.spec.js @@ -0,0 +1,17 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. + +const xmlserializer = require('xmlserializer'); + +const rect = require('./rect'); + +describe('rect', () => { + it('creates a basic SVG rect element', () => { + expect( + xmlserializer.serializeToString( + rect(123) + ) + ).toEqual(''); + }); +}); diff --git a/packages/ui-identicon/src/svg/svg.js b/packages/ui-identicon/src/svg/svg.js new file mode 100644 index 0000000000..7970838b1c --- /dev/null +++ b/packages/ui-identicon/src/svg/svg.js @@ -0,0 +1,11 @@ +// Copyright 2016 Dan Finlay +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +const SVG_NS = 'http://www.w3.org/2000/svg'; + +module.exports = function svg (type: string): Element { + return document.createElementNS(SVG_NS, type); +}; diff --git a/packages/ui-identicon/src/svg/svg.spec.js b/packages/ui-identicon/src/svg/svg.spec.js new file mode 100644 index 0000000000..2e60eec8c2 --- /dev/null +++ b/packages/ui-identicon/src/svg/svg.spec.js @@ -0,0 +1,17 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. + +const xmlserializer = require('xmlserializer'); + +const svg = require('./svg'); + +describe('svg', () => { + it('creates a basic SVG element', () => { + expect( + xmlserializer.serializeToString( + svg('rect') + ) + ).toEqual(''); + }); +}); diff --git a/packages/ui-identicon/src/types.js b/packages/ui-identicon/src/types.js new file mode 100644 index 0000000000..a6ae9e7bc3 --- /dev/null +++ b/packages/ui-identicon/src/types.js @@ -0,0 +1,6 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +export type Seeder = () => number; diff --git a/packages/ui-identicon/webpack.config.js b/packages/ui-identicon/webpack.config.js new file mode 100644 index 0000000000..16c91129d8 --- /dev/null +++ b/packages/ui-identicon/webpack.config.js @@ -0,0 +1,26 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +module.exports = { + context: __dirname, + entry: './src/demo.js', + mode: 'development', + output: { + path: __dirname, + filename: './demo.js' + }, + module: { + rules: [ + { + test: /\.js$/, + exclude: /(node_modules)/, + use: [ + 'babel-loader' + ] + } + ] + }, + plugins: [] +}; diff --git a/packages/ui-keyring/package.json b/packages/ui-keyring/package.json index f9b92314dc..a9e7e788b7 100644 --- a/packages/ui-keyring/package.json +++ b/packages/ui-keyring/package.json @@ -1,6 +1,6 @@ { "name": "@polkadot/ui-keyring", - "version": "0.7.2", + "version": "0.17.0", "main": "index.js", "repository": "https://github.com/polkadot-js/apps.git", "author": "Jaco Greeff ", @@ -12,7 +12,7 @@ }, "dependencies": { "@babel/runtime": "^7.0.0-beta.47", - "@polkadot/ui-react": "^0.14.3", + "@polkadot/ui-react": "^0.17.0", "@polkadot/util-keyring": "^0.22.2", "store": "^2.0.12" }, diff --git a/packages/ui-react-rx/LICENSE b/packages/ui-react-rx/LICENSE new file mode 100644 index 0000000000..3d150a620a --- /dev/null +++ b/packages/ui-react-rx/LICENSE @@ -0,0 +1,15 @@ +ISC License (ISC) + +Copyright 2017-2018 Jaco Greeff + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. diff --git a/packages/ui-react-rx/README.md b/packages/ui-react-rx/README.md new file mode 100644 index 0000000000..1e89c99705 --- /dev/null +++ b/packages/ui-react-rx/README.md @@ -0,0 +1,46 @@ +[![polkadotjs](https://img.shields.io/badge/polkadot-js-orange.svg?style=flat-square)](https://polkadot.js.org) +![isc](https://img.shields.io/badge/license-ISC-lightgrey.svg?style=flat-square) +[![style](https://img.shields.io/badge/code%20style-semistandard-lightgrey.svg?style=flat-square)](https://github.com/Flet/semistandard) +[![npm](https://img.shields.io/npm/v/@polkadot/ui-react-rx.svg?style=flat-square)](https://www.npmjs.com/package/@polkadot/ui-react-rx) +[![travis](https://img.shields.io/travis/polkadot-js/apps.svg?style=flat-square)](https://travis-ci.org/polkadot-js/ui) +[![maintainability](https://img.shields.io/codeclimate/maintainability/polkadot-js/apps.svg?style=flat-square)](https://codeclimate.com/github/polkadot-js/apps/maintainability) +[![coverage](https://img.shields.io/coveralls/polkadot-js/apps.svg?style=flat-square)](https://coveralls.io/github/polkadot-js/apps?branch=master) +[![dependency](https://david-dm.org/polkadot-js/apps.svg?style=flat-square&path=packages/ui-react-rx)](https://david-dm.org/polkadot-js/apps?path=packages/ui-react-rx) +[![devDependency](https://david-dm.org/polkadot-js/apps/dev-status.svg?style=flat-square&path=packages/ui-react-rx)](https://david-dm.org/polkadot-js/apps?path=packages/ui-react-rx#info=devDependencies) + +# @polkadot/ui-react-rx + +A collection of RxJS enabled React components that operate with the [@polkadot/api-rx](https://github.com/polkadot-js/api) library. It automatically manages subscriptions on behalf of the developer, providing a number of unstyled components that can be used to construct UIs. + +## usage + +Basic usage entails creating a `ContextProvider` and just using the components. For instance, to display the current node time, + +```js +import React from 'react'; +import ReactDOM from 'react-dom'; + +import { Api, NodeTime } from '@polkadot/ui-react-rx'; + +... +ReactDOM.render( + + + , + document.querySelector('#container') +); +... +``` + +All components are provided unstyled making no assumptions on the actual use, however they all support (optionally) the `label`, `className` and `style` attributes, that can be used to style to component. + +```js +... +ReactDOM.render( + + + , + document.querySelector('#container') +); +... +``` diff --git a/packages/ui-react-rx/index.html b/packages/ui-react-rx/index.html new file mode 100644 index 0000000000..6451b7a110 --- /dev/null +++ b/packages/ui-react-rx/index.html @@ -0,0 +1,14 @@ + + + + + + +
+ + + diff --git a/packages/ui-react-rx/package.json b/packages/ui-react-rx/package.json new file mode 100644 index 0000000000..a797c665ae --- /dev/null +++ b/packages/ui-react-rx/package.json @@ -0,0 +1,50 @@ +{ + "name": "@polkadot/ui-react-rx", + "version": "0.17.0", + "description": "A collection of RxJs React components the Polkadot JS API", + "main": "index.js", + "keywords": [ + "Polkadot", + "React", + "RxJs" + ], + "author": "Jaco Greeff ", + "license": "ISC", + "engines": { + "node": ">=8.0" + }, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/polkadot-js/ui.git" + }, + "bugs": { + "url": "https://github.com/polkadot-js/ui/issues" + }, + "homepage": "https://github.com/polkadot-js/ui/tree/master/packages/ui-react-rx#readme", + "scripts": { + "build": "polkadot-dev-build-babel", + "check": "eslint src && flow check", + "test": "echo \"Tests only available from root wrapper\"" + }, + "dependencies": { + "@babel/runtime": "^7.0.0-beta.47", + "@polkadot/api-rx": "^0.13.2", + "@polkadot/extrinsics-codec": "^0.17.2", + "@polkadot/storage": "^0.17.2", + "semver": "^5.5.0" + }, + "devDependencies": { + "babel-loader": "^8.0.0-beta.2", + "enzyme": "^3.3.0", + "enzyme-adapter-react-16": "^1.1.1", + "react": "^16.4.0", + "react-dom": "^16.4.0" + }, + "peerDependencies": { + "react": "^16.4.0" + } +} diff --git a/packages/ui-react-rx/src/Api.js b/packages/ui-react-rx/src/Api.js new file mode 100644 index 0000000000..61fe2cb884 --- /dev/null +++ b/packages/ui-react-rx/src/Api.js @@ -0,0 +1,134 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import type { ProviderInterface } from '@polkadot/api-provider/types'; +import type { RxApiInterface } from '@polkadot/api-rx/types'; +import type { EncodingVersions } from '@polkadot/extrinsics-codec/types'; +import type { ApiProps } from './types'; + +import React from 'react'; +import semver from 'semver'; +import createWsProvider from '@polkadot/api-provider/ws'; +import createApi from '@polkadot/api-rx'; +import defaults from '@polkadot/api-rx/defaults'; + +type Props = { + api?: RxApiInterface, + children: React$Node, + provider?: ProviderInterface, + url?: string +}; + +type State = ApiProps & { + subscriptions: Array; +}; + +const { Consumer, Provider } = React.createContext(); + +function apiSupport (version?: string): EncodingVersions { + return version === undefined || semver.lt(version, '0.2.0') + ? 'poc-1' + : 'latest'; +} + +export default class Api extends React.Component { + static Provider = Provider; + static Consumer = Consumer + + state: State; + + constructor (props: Props) { + super(props); + + const { provider, url = '' } = props; + const api = props.api || createApi( + url && url.length + ? createWsProvider(url) + : provider + ); + const setApi = (api: RxApiInterface): void => { + this.setState({ api }, () => { + this.updateSubscriptions(); + }); + }; + const setApiProvider = (provider?: ProviderInterface): void => + setApi(createApi(provider)); + const setApiWsUrl = (url?: string = defaults.WS_URL): void => + setApiProvider(createWsProvider(url)); + + this.state = { + api, + apiConnected: false, + apiSupport: 'poc-1', + setApi, + setApiProvider, + setApiWsUrl, + subscriptions: [] + }; + } + + componentDidMount () { + this.updateSubscriptions(); + } + + componentWillUnmount () { + this.unsubscribe(); + } + + updateSubscriptions () { + const { api } = this.state; + + this.unsubscribe(); + this.setState({ + subscriptions: + [ + () => api.isConnected().subscribe((isConnected?: boolean) => { + this.setState({ apiConnected: !!isConnected }); + }), + () => api.system.version().subscribe((version?: string) => { + this.setState({ apiSupport: apiSupport(version) }); + }) + ].map((fn: () => rxjs$ISubscription): rxjs$ISubscription | null => { + try { + return fn(); + } catch (error) { + console.error(error); + return null; + } + }) + }); + } + + unsubscribe (): void { + const { subscriptions } = this.state; + + subscriptions.forEach((subscription) => { + if (subscription) { + try { + subscription.unsubscribe(); + } catch (error) { + console.error(error); + } + } + }); + } + + render () { + const { api, apiConnected, apiSupport, setApi, setApiProvider, setApiWsUrl } = this.state; + + return ( + + {this.props.children} + + ); + } +} diff --git a/packages/ui-react-rx/src/Balance.js b/packages/ui-react-rx/src/Balance.js new file mode 100644 index 0000000000..2780c96900 --- /dev/null +++ b/packages/ui-react-rx/src/Balance.js @@ -0,0 +1,14 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import storage from '@polkadot/storage'; + +import numberFormat from './util/numberFormat'; +import withStorageDiv from './with/storageDiv'; + +export default withStorageDiv(storage.staking.public.freeBalanceOf)( + numberFormat, + { className: 'rx--Balance' } +); diff --git a/packages/ui-react-rx/src/BestNumber.js b/packages/ui-react-rx/src/BestNumber.js new file mode 100644 index 0000000000..c1f7d83feb --- /dev/null +++ b/packages/ui-react-rx/src/BestNumber.js @@ -0,0 +1,20 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import type { Header } from '@polkadot/primitives/header'; + +import rpcs from '@polkadot/jsonrpc'; + +import numberFormat from './util/numberFormat'; +import withApiDiv from './with/apiDiv'; + +export default withApiDiv(rpcs.chain.public.newHead)( + (value?: Header): string => { + return value && value.number + ? numberFormat(value.number) + : 'unknown'; + }, + { className: 'rx--BestNumber' } +); diff --git a/packages/ui-react-rx/src/Chain.js b/packages/ui-react-rx/src/Chain.js new file mode 100644 index 0000000000..edf018af43 --- /dev/null +++ b/packages/ui-react-rx/src/Chain.js @@ -0,0 +1,14 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import rpcs from '@polkadot/jsonrpc'; + +import withApiDiv from './with/apiDiv'; + +export default withApiDiv(rpcs.system.public.chain)( + (value: string = 'unknown'): string => + value, + { className: 'rx--Chain' } +); diff --git a/packages/ui-react-rx/src/Connected.js b/packages/ui-react-rx/src/Connected.js new file mode 100644 index 0000000000..b16f22d4e3 --- /dev/null +++ b/packages/ui-react-rx/src/Connected.js @@ -0,0 +1,15 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import withApiDiv from './with/apiDiv'; + +export default withApiDiv({ name: 'isConnected' })( + (value?: boolean = false): string => { + return value + ? 'connected' + : 'disconnected'; + }, + { className: 'rx--Connected' } +); diff --git a/packages/ui-react-rx/src/Div.js b/packages/ui-react-rx/src/Div.js new file mode 100644 index 0000000000..04d07416c6 --- /dev/null +++ b/packages/ui-react-rx/src/Div.js @@ -0,0 +1,30 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import type { BaseProps, BareProps } from './types'; + +import React from 'react'; + +type Props = BaseProps; + +// FIXME Not 100% on how to push through the generic type when used +// flowlint-next-line unclear-type: off +const echoFormatter = (value?: any): any => + value; + +export default function Div ({ children, className = 'rx--Div', render = echoFormatter, rxUpdated = false, label = '', style, value }: Props): React$Node { + const props: BareProps = { + className: [className, rxUpdated ? 'rx--updated' : undefined] + .filter((c) => c) + .join(' ') || undefined, + style + }; + + return ( +
+ {label}{render(value) || ''}{children} +
+ ); +} diff --git a/packages/ui-react-rx/src/NodeName.js b/packages/ui-react-rx/src/NodeName.js new file mode 100644 index 0000000000..cf1a5fa044 --- /dev/null +++ b/packages/ui-react-rx/src/NodeName.js @@ -0,0 +1,14 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import rpcs from '@polkadot/jsonrpc'; + +import withApiDiv from './with/apiDiv'; + +export default withApiDiv(rpcs.system.public.name)( + (value: string = 'unknown'): string => + value, + { className: 'rx--NodeName' } +); diff --git a/packages/ui-react-rx/src/NodeTime.js b/packages/ui-react-rx/src/NodeTime.js new file mode 100644 index 0000000000..a024297848 --- /dev/null +++ b/packages/ui-react-rx/src/NodeTime.js @@ -0,0 +1,19 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import storage from '@polkadot/storage'; + +import withStorageDiv from './with/storageDiv'; + +export default withStorageDiv(storage.timestamp.public.current)( + (value?: Date): string => { + if (!value || value.getTime() === 0) { + return 'unknown'; + } + + return value.toString(); + }, + { className: 'rx--NodeTime' } +); diff --git a/packages/ui-react-rx/src/NodeVersion.js b/packages/ui-react-rx/src/NodeVersion.js new file mode 100644 index 0000000000..2ae5303d20 --- /dev/null +++ b/packages/ui-react-rx/src/NodeVersion.js @@ -0,0 +1,14 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import rpcs from '@polkadot/jsonrpc'; + +import withApiDiv from './with/apiDiv'; + +export default withApiDiv(rpcs.system.public.version)( + (value: string = 'unknown'): string => + value, + { className: 'rx--NodeVersion' } +); diff --git a/packages/ui-react-rx/src/Nonce.js b/packages/ui-react-rx/src/Nonce.js new file mode 100644 index 0000000000..5e90cdb957 --- /dev/null +++ b/packages/ui-react-rx/src/Nonce.js @@ -0,0 +1,14 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import storage from '@polkadot/storage'; + +import numberFormat from './util/numberFormat'; +import withStorageDiv from './with/storageDiv'; + +export default withStorageDiv(storage.system.public.accountIndexOf)( + numberFormat, + { className: 'rx--Nonce' } +); diff --git a/packages/ui-react-rx/src/demo.js b/packages/ui-react-rx/src/demo.js new file mode 100644 index 0000000000..a19de410cc --- /dev/null +++ b/packages/ui-react-rx/src/demo.js @@ -0,0 +1,55 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import React from 'react'; +import ReactDOM from 'react-dom'; +import mockProvider from '@polkadot/api-provider/mock'; + +import Api from './Api'; +import Balance from './Balance'; +import BestNumber from './BestNumber'; +import Chain from './Chain'; +import Connected from './Connected'; +import NodeName from './NodeName'; +import NodeTime from './NodeTime'; +import NodeVersion from './NodeVersion'; +import Nonce from './Nonce'; +import withApi from './with/api'; + +const provider = mockProvider(); + +const ALICE_ADDR = '5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaDtZ'; +const ALICE_PUBLIC = '0xd172a74cda4c865912c32ba0a80a57ae69abae410e5ccb59dee84e2f4432db4f'; + +const element = document.getElementById('demo'); + +if (!element) { + throw new Error('Unable to find #demo element'); +} + +const WithApiDebug = withApi( + (apiProps) => ( +
+      {JSON.stringify(apiProps, undefined, 2)}
+    
+ ) +); + +ReactDOM.render( + + + + + +
+ + + + +
+ +
, + element +); diff --git a/packages/ui-react-rx/src/index.js b/packages/ui-react-rx/src/index.js new file mode 100644 index 0000000000..c77e9a3186 --- /dev/null +++ b/packages/ui-react-rx/src/index.js @@ -0,0 +1,30 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import Api from './Api'; +import Balance from './Balance'; +import BestNumber from './BestNumber'; +import Chain from './Chain'; +import Connected from './Connected'; +import Div from './Div'; +import NodeName from './NodeName'; +import NodeTime from './NodeTime'; +import NodeVersion from './NodeVersion'; +import Nonce from './Nonce'; + +export * from './with'; + +export { + Api, + Balance, + BestNumber, + Chain, + Connected, + Div, + NodeName, + NodeTime, + NodeVersion, + Nonce +}; diff --git a/packages/ui-react-rx/src/types.js b/packages/ui-react-rx/src/types.js new file mode 100644 index 0000000000..b198ba75b8 --- /dev/null +++ b/packages/ui-react-rx/src/types.js @@ -0,0 +1,48 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import type { ProviderInterface } from '@polkadot/api-provider/types'; +import type { RxApiInterface } from '@polkadot/api-rx/types'; +import type { EncodingVersions } from '@polkadot/extrinsics-codec/types'; +import type { Storage$Key$Values } from '@polkadot/storage/types'; + +export type BareProps = { + className?: string, + style?: { [string]: mixed } +}; + +export type ApiProps = { + api: RxApiInterface, + apiConnected: boolean, + apiSupport: EncodingVersions, + setApi: (api: RxApiInterface) => void, + setApiProvider: (provider?: ProviderInterface) => void, + setApiWsUrl: (url?: string) => void +}; + +export type OnChangeCb = rxjs$Subject | (value: T) => void; + +export type ChangeProps = { + onChange?: OnChangeCb +}; + +export type ParamProps = { + params?: Storage$Key$Values +}; + +export type RxProps = { + rxUpdated?: boolean; + rxUpdatedAt?: number; + value?: T; +} + +export type BaseProps = BareProps & ApiProps & ChangeProps & ParamProps & RxProps & { + children?: React$Node, + label?: string, + render?: (value?: T) => React$Node +}; + +// flowlint-next-line unclear-type:off +export type Formatter = (value?: any) => string; diff --git a/packages/ui-react-rx/src/util/intervalSubscribe.js b/packages/ui-react-rx/src/util/intervalSubscribe.js new file mode 100644 index 0000000000..8c17e1b905 --- /dev/null +++ b/packages/ui-react-rx/src/util/intervalSubscribe.js @@ -0,0 +1,22 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import type { RxProps } from '../types'; + +import { interval } from 'rxjs/observable/interval'; + +const interval$ = interval(500); + +export default function intervalSubscribe> (that: React$Component): rxjs$ISubscription { + return interval$.subscribe(() => { + const rxUpdated = (Date.now() - (that.state.rxUpdatedAt || 0)) <= 1500; + + if (rxUpdated !== that.state.rxUpdated) { + that.setState({ + rxUpdated + }); + } + }); +} diff --git a/packages/ui-react-rx/src/util/isEqual.js b/packages/ui-react-rx/src/util/isEqual.js new file mode 100644 index 0000000000..e7d73ae3cb --- /dev/null +++ b/packages/ui-react-rx/src/util/isEqual.js @@ -0,0 +1,8 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +export default function isEqual (a?: T, b?: T): boolean { + return JSON.stringify({ test: a }) === JSON.stringify({ test: b }); +} diff --git a/packages/ui-react-rx/src/util/numberFormat.js b/packages/ui-react-rx/src/util/numberFormat.js new file mode 100644 index 0000000000..3f32c3022b --- /dev/null +++ b/packages/ui-react-rx/src/util/numberFormat.js @@ -0,0 +1,16 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import type BN from 'bn.js'; + +const NUMBER_REGEX = new RegExp('(\\d+?)(?=(\\d{3})+(?!\\d)|$)', 'g'); + +export default function numberFormat (value?: BN | number): string { + if (value === undefined) { + return '0'; + } + + return (value.toString().match(NUMBER_REGEX) || []).join(','); +} diff --git a/packages/ui-react-rx/src/util/triggerChange.js b/packages/ui-react-rx/src/util/triggerChange.js new file mode 100644 index 0000000000..038597d790 --- /dev/null +++ b/packages/ui-react-rx/src/util/triggerChange.js @@ -0,0 +1,25 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import type { OnChangeCb } from '../types'; + +import isFunction from '@polkadot/util/is/function'; +import isObservable from '@polkadot/util/is/observable'; + +export default function triggerChange (value?: T, ...onChange: Array>): void { + if (!onChange || !onChange.length) { + return; + } + + onChange.forEach((onChange) => { + if (isObservable(onChange)) { + // $FlowFixMe type determined + onChange.next(value); + } else if (isFunction(onChange)) { + // $FlowFixMe type determined + onChange(value); + } + }); +} diff --git a/packages/ui-react-rx/src/with/api.js b/packages/ui-react-rx/src/with/api.js new file mode 100644 index 0000000000..4282a0227f --- /dev/null +++ b/packages/ui-react-rx/src/with/api.js @@ -0,0 +1,45 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import type { ApiProps, BareProps, ChangeProps } from '../types'; +import type { DefaultProps } from './types'; + +import React from 'react'; +import assert from '@polkadot/util/assert'; + +import Api from '../Api'; + +type InProps = BareProps & ChangeProps; + +type OutProps = InProps & ApiProps; + +export default function withApi>, InputProps: InProps> (Component: React$ComponentType, defaultProps?: DefaultProps = {}): Class> { + return class WithApi extends React.Component { + constructor (props: InputProps) { + super(props); + + assert(Component, `Expected 'withApi' to wrap a React Component`); + } + + render () { + return ( + + {(apiProps?: ApiProps): React$Node => { + assert(apiProps && apiProps.api, `Application root must be wrapped inside 'rx-react/Api' to provide API context`); + + return ( + // flowlint-next-line inexact-spread:off + + ); + }} + + ); + } + }; +} diff --git a/packages/ui-react-rx/src/with/apiCall.js b/packages/ui-react-rx/src/with/apiCall.js new file mode 100644 index 0000000000..2b260f70d7 --- /dev/null +++ b/packages/ui-react-rx/src/with/apiCall.js @@ -0,0 +1,74 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import type { RxApiInterface$Method } from '@polkadot/api-rx/types'; +import type { ApiProps, BareProps, ChangeProps, ParamProps, RxProps } from '../types'; +import type { ApiMethod, HOC, Options, DefaultProps } from './types'; + +import React from 'react'; +import assert from '@polkadot/util/assert'; + +import isEqual from '../util/isEqual'; +import withObservable from './observable'; +import withApi from './api'; + +type InProps = BareProps & ChangeProps & ParamProps; + +type WithApiProps = InProps & ApiProps; + +type OutProps = WithApiProps & RxProps; + +type State = ParamProps & { + Component: React$ComponentType, + fn: RxApiInterface$Method +} + +export default function withApiCall>, InputProps: InProps, InputApiProps: WithApiProps> ({ name, section }: ApiMethod, options?: Options): HOC { + return (Component: React$ComponentType, defaultProps?: DefaultProps = {}): Class> => { + class WithApiCall extends React.Component> { + state: State; + + constructor (props: InputApiProps) { + super(props); + + const hasSection = !!section; + + assert(!hasSection || props.api[section], `Unable to find 'api.${section}'`); + + const fn: RxApiInterface$Method = hasSection + ? props.api[section][name] + // $FlowFixMe method check to be done + : ((props.api[name]: any): RxApiInterface$Method); + + assert(fn, `Unable to find 'api${hasSection ? '.' : ''}${section || ''}.${name}'`); + + this.state = ({ fn }: $Shape>); + } + + static getDerivedStateFromProps ({ params = [] }: InputApiProps, prevState: State): $Shape> | null { + if (isEqual(params, prevState.params)) { + return null; + } + + const observable = prevState.fn.apply(null, params); + + return { + Component: withObservable(observable, options)(Component, defaultProps), + params + }; + } + + render () { + const { Component } = this.state; + + return ( + + ); + } + } + + return withApi(WithApiCall); + }; +} diff --git a/packages/ui-react-rx/src/with/apiDiv.js b/packages/ui-react-rx/src/with/apiDiv.js new file mode 100644 index 0000000000..78bcedcb43 --- /dev/null +++ b/packages/ui-react-rx/src/with/apiDiv.js @@ -0,0 +1,18 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import type { BaseProps } from '../types'; +import type { ApiMethod, ComponentRenderer, DefaultProps, RenderFn, Options } from './types'; + +import Div from '../Div'; +import withApiCall from './apiCall'; + +export default function withApiDiv> (call: ApiMethod, options?: Options): ComponentRenderer { + return (render: RenderFn, defaultProps?: DefaultProps = {}): React$ComponentType => + withApiCall(call, options)(Div, { + ...defaultProps, + render + }); +} diff --git a/packages/ui-react-rx/src/with/index.js b/packages/ui-react-rx/src/with/index.js new file mode 100644 index 0000000000..4d7cba7375 --- /dev/null +++ b/packages/ui-react-rx/src/with/index.js @@ -0,0 +1,18 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import withApi from './api'; +import withApiCall from './apiCall'; +import withObservable from './observable'; +import withObservableParams from './observableParams'; +import withStorage from './storage'; + +export { + withApi, + withApiCall, + withObservable, + withObservableParams, + withStorage +}; diff --git a/packages/ui-react-rx/src/with/observable.js b/packages/ui-react-rx/src/with/observable.js new file mode 100644 index 0000000000..a1b6583a94 --- /dev/null +++ b/packages/ui-react-rx/src/with/observable.js @@ -0,0 +1,101 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +// TODO: This is now way more messy and way longer than it should be. Maintainability is lacking - apply some effort and split it into managable parts + +import type { BareProps, ChangeProps, RxProps } from '../types'; +import type { HOC, Options, DefaultProps } from './types'; + +import React from 'react'; +import { map } from 'rxjs/operators/map'; + +import assert from '@polkadot/util/assert'; + +import intervalSubscribe from '../util/intervalSubscribe'; +import isEqual from '../util/isEqual'; +import triggerChange from '../util/triggerChange'; +import echoTransform from './transform/echo'; + +type InProps = BareProps & ChangeProps; + +type OutProps = InProps & RxProps; + +type State = RxProps & { + subscriptions: Array; +} + +export default function withObservable>, InputProps: InProps> (observable: rxjs$Observable | rxjs$Subject, { onChange, propName = 'value', transform = echoTransform }: Options = {}): HOC { + return (Component: React$ComponentType, defaultProps?: DefaultProps = {}): Class>> => + class WithObservable extends React.Component> { + state: State; + + constructor (props: InputProps) { + super(props); + + assert(observable, `Component should have Observable to wrap`); + assert(Component, `Expected 'with*' to wrap a React Component`); + + this.state = { + rxUpdated: false, + rxUpdatedAt: 0, + subscriptions: [], + value: void 0 + }; + } + + componentDidMount () { + const subscriptions: Array = [ + observable + .pipe(map(transform)) + .subscribe((value) => { + this.triggerUpdate(this.props, value); + }), + intervalSubscribe(this) + ]; + + this.setState({ + subscriptions + }); + } + + componentWillUnmount () { + this.state.subscriptions.forEach((subscription) => { + subscription.unsubscribe(); + }); + } + + triggerUpdate = (props: InProps, value?: T): void => { + if (isEqual(value, this.state.value)) { + return; + } + + triggerChange(value, onChange, props.onChange || defaultProps.onChange); + + this.setState({ + rxUpdated: true, + rxUpdatedAt: Date.now(), + value + }); + } + + render () { + const { rxUpdated, rxUpdatedAt, value } = this.state; + // flowlint-next-line inexact-spread:off + const _props = { + ...defaultProps, + ...this.props, + rxUpdated, + rxUpdatedAt, + [propName]: value + }; + + delete _props.onChange; + + return ( + + ); + } + }; +} diff --git a/packages/ui-react-rx/src/with/observableParams.js b/packages/ui-react-rx/src/with/observableParams.js new file mode 100644 index 0000000000..9b3ae2c055 --- /dev/null +++ b/packages/ui-react-rx/src/with/observableParams.js @@ -0,0 +1,16 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import type { HOC, Options } from './types'; + +import withObservable from './observable'; + +export default function withObservableParams (observable: rxjs$Observable | rxjs$Subject, options?: Options = {}): HOC { + return withObservable(observable, { + propName: 'params', + // flowlint-next-line inexact-spread:off + ...options + }); +} diff --git a/packages/ui-react-rx/src/with/storage.js b/packages/ui-react-rx/src/with/storage.js new file mode 100644 index 0000000000..94aef7542f --- /dev/null +++ b/packages/ui-react-rx/src/with/storage.js @@ -0,0 +1,156 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +// TODO: Lots of duplicated code between this and withObservable, surely there ois a better way of doing this? + +import type { Storage$Key, Storage$Key$Values } from '@polkadot/storage/types'; +import type { ApiProps, BareProps, ChangeProps, ParamProps, RxProps } from '../types'; +import type { HOC, StorageOptions, DefaultProps, Transform } from './types'; + +import React from 'react'; +import { map } from 'rxjs/operators/map'; + +import storageKey from '@polkadot/storage/key'; + +import intervalSubscribe from '../util/intervalSubscribe'; +import isEqual from '../util/isEqual'; +import triggerChange from '../util/triggerChange'; +import echoTransform from './transform/echo'; +import createTransform from './transform/storage'; +import withApi from './api'; + +type InProps = BareProps & ChangeProps & ParamProps & { + transform?: Transform +}; + +type InApiProps = ApiProps & InProps; + +type OutProps = InApiProps & RxProps; + +type State = RxProps & { + subscriptions: Array; +} + +export default function withStorage>, InputProps: InProps, InputApiProps: InApiProps> (key: Storage$Key, { onChange, params, propName = 'value', transform }: StorageOptions = {}): HOC { + const keyCreator = storageKey(key); + const storageTransform = createTransform(key); + const createKey = (propParams?: Storage$Key$Values): Uint8Array => { + const values = [params, propParams].reduce((result, input = []) => { + if (Array.isArray(input)) { + return result.concat(input); + } + + result.push(input); + return result; + }, []); + + return keyCreator.apply(null, values); + }; + + return (Component: React$ComponentType, defaultProps?: DefaultProps = {}): Class> => { + class WithStorage extends React.Component> { + state: State; + + constructor (props: InputApiProps) { + super(props); + + this.state = { + rxUpdated: false, + rxUpdatedAt: 0, + subscriptions: [], + value: void 0 + }; + } + + componentDidUpdate (prevProps: InputApiProps) { + if (!isEqual(this.props.params, prevProps.params)) { + this.triggerUpdate(); + } + } + + componentDidMount () { + const subscriptions: Array = [ + this.props.api.chain + .newHead() + .subscribe((value) => { + this.triggerUpdate(); + }), + intervalSubscribe(this) + ]; + + this.setState({ + subscriptions + }); + } + + componentWillUnmount () { + this.state.subscriptions.forEach((subscription) => { + subscription.unsubscribe(); + }); + } + + triggerUpdate = async (): Promise => { + // flowlint-next-line unclear-type:off + const mapping = (value: Uint8Array, index: number): any => { + const outer = this.props.transform || echoTransform; + const inner = transform || echoTransform; + + return outer( + inner( + storageTransform(value, index), + index + ), + index + ); + }; + + const key = createKey(this.props.params); + let value; + + try { + value = await this.props.api.state + .getStorage(key) + .pipe(map(mapping)) + .toPromise(); + } catch (error) { + // swallow? + // console.error(error); + } + + if (isEqual(value, this.state.value)) { + return; + } + + triggerChange(value, onChange, this.props.onChange || defaultProps.onChange); + + this.setState({ + rxUpdated: true, + rxUpdatedAt: Date.now(), + value + }); + } + + render () { + const { rxUpdated, rxUpdatedAt, value } = this.state; + // flowlint-next-line inexact-spread:off + const _props = { + ...defaultProps, + ...this.props, + rxUpdated, + rxUpdatedAt, + [propName]: value + }; + + delete _props.onChange; + + return ( + + ); + } + } + + return withApi(WithStorage); + }; +} diff --git a/packages/ui-react-rx/src/with/storageDiv.js b/packages/ui-react-rx/src/with/storageDiv.js new file mode 100644 index 0000000000..e23f7664fb --- /dev/null +++ b/packages/ui-react-rx/src/with/storageDiv.js @@ -0,0 +1,19 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import type { Storage$Key } from '@polkadot/storage/types'; +import type { BaseProps } from '../types'; +import type { ComponentRenderer, DefaultProps, RenderFn, StorageOptions } from './types'; + +import Div from '../Div'; +import withStorage from './storage'; + +export default function withStorageDiv> (key: Storage$Key, options?: StorageOptions): ComponentRenderer { + return (render: RenderFn, defaultProps?: DefaultProps = {}): React$ComponentType => + withStorage(key, options)(Div, { + ...defaultProps, + render + }); +} diff --git a/packages/ui-react-rx/src/with/transform/echo.js b/packages/ui-react-rx/src/with/transform/echo.js new file mode 100644 index 0000000000..63da55ae65 --- /dev/null +++ b/packages/ui-react-rx/src/with/transform/echo.js @@ -0,0 +1,8 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +export default function echoTransform (x: T, index: number): T { + return x; +} diff --git a/packages/ui-react-rx/src/with/transform/storage.js b/packages/ui-react-rx/src/with/transform/storage.js new file mode 100644 index 0000000000..baac101363 --- /dev/null +++ b/packages/ui-react-rx/src/with/transform/storage.js @@ -0,0 +1,21 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import type { Param$Values } from '@polkadot/params/types'; +import type { Storage$Key } from '@polkadot/storage/types'; +import type { StorageTransform } from '../types'; + +import decodeParams from '@polkadot/params/decode'; + +export default function storageTransform ({ type }: Storage$Key): StorageTransform { + return (input?: Uint8Array = new Uint8Array(0), index: number): Param$Values | null => { + try { + return decodeParams(type, input).value; + } catch (error) { + console.error('Decoding', type, '::', error); + return null; + } + }; +} diff --git a/packages/ui-react-rx/src/with/types.js b/packages/ui-react-rx/src/with/types.js new file mode 100644 index 0000000000..d1edca7fbf --- /dev/null +++ b/packages/ui-react-rx/src/with/types.js @@ -0,0 +1,42 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import type { Interface$Method } from '@polkadot/jsonrpc/types'; +import type { Param$Values } from '@polkadot/params/types'; +import type { Storage$Key$Values } from '@polkadot/storage/types'; +import type { OnChangeCb } from '../types'; + +import React from 'react'; + +// flowlint-next-line unclear-type:off +export type Transform = (value: any, index: number) => any; + +export type DefaultProps = { + onChange?: OnChangeCb, + [string]: mixed +}; + +export type Options = { + onChange?: OnChangeCb, + propName?: string, + transform?: Transform +}; + +export type StorageTransform = (value?: Uint8Array, index: number) => Param$Values | null; + +export type StorageOptions = Options & { + params?: Storage$Key$Values; +}; + +// flowlint-next-line unclear-type:off +export type HOC = (Component: React$ComponentType, defaultProps?: DefaultProps) => Class>; + +export type ApiMethod = $Shape; + +// flowlint-next-line unclear-type:off +export type RenderFn = (value?: any) => any; + +// flowlint-next-line unclear-type:off +export type ComponentRenderer = (render: RenderFn, defaultProps?: DefaultProps) => React$ComponentType; diff --git a/packages/ui-react-rx/test/enzyme.js b/packages/ui-react-rx/test/enzyme.js new file mode 100644 index 0000000000..d0e28707f4 --- /dev/null +++ b/packages/ui-react-rx/test/enzyme.js @@ -0,0 +1,12 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. + +const Adapter = require('enzyme-adapter-react-16'); +const Enzyme = require('enzyme'); + +Enzyme.configure({ + adapter: new Adapter() +}); + +module.exports = Enzyme; diff --git a/packages/ui-react-rx/test/observable.js b/packages/ui-react-rx/test/observable.js new file mode 100644 index 0000000000..f375c52bbd --- /dev/null +++ b/packages/ui-react-rx/test/observable.js @@ -0,0 +1,15 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. + +const createObservable = require('@polkadot/api-rx/observable'); + +module.exports = function observable (method) { + const fn = () => Promise.resolve(12345); + + fn.unsubscribe = () => Promise.resolve(true); + + return createObservable(`section_${method}`, method, { + [method]: fn + })(); +}; diff --git a/packages/ui-react-rx/webpack.config.js b/packages/ui-react-rx/webpack.config.js new file mode 100644 index 0000000000..c0200c9d37 --- /dev/null +++ b/packages/ui-react-rx/webpack.config.js @@ -0,0 +1,29 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +module.exports = { + context: __dirname, + entry: './src/demo.js', + mode: 'development', + output: { + path: __dirname, + filename: './demo.js' + }, + resolve: { + alias: {} + }, + module: { + rules: [ + { + test: /\.js$/, + exclude: /(node_modules)/, + use: { + loader: 'babel-loader' + } + } + ] + }, + plugins: [] +}; diff --git a/packages/ui-react/LICENSE b/packages/ui-react/LICENSE new file mode 100644 index 0000000000..3d150a620a --- /dev/null +++ b/packages/ui-react/LICENSE @@ -0,0 +1,15 @@ +ISC License (ISC) + +Copyright 2017-2018 Jaco Greeff + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. diff --git a/packages/ui-react/README.md b/packages/ui-react/README.md new file mode 100644 index 0000000000..3cc45dd65d --- /dev/null +++ b/packages/ui-react/README.md @@ -0,0 +1,13 @@ +[![polkadotjs](https://img.shields.io/badge/polkadot-js-orange.svg?style=flat-square)](https://polkadot.js.org) +![isc](https://img.shields.io/badge/license-ISC-lightgrey.svg?style=flat-square) +[![style](https://img.shields.io/badge/code%20style-semistandard-lightgrey.svg?style=flat-square)](https://github.com/Flet/semistandard) +[![npm](https://img.shields.io/npm/v/@polkadot/ui-react.svg?style=flat-square)](https://www.npmjs.com/package/@polkadot/ui-react) +[![travis](https://img.shields.io/travis/polkadot-js/apps.svg?style=flat-square)](https://travis-ci.org/polkadot-js/ui) +[![maintainability](https://img.shields.io/codeclimate/maintainability/polkadot-js/apps.svg?style=flat-square)](https://codeclimate.com/github/polkadot-js/apps/maintainability) +[![coverage](https://img.shields.io/coveralls/polkadot-js/apps.svg?style=flat-square)](https://coveralls.io/github/polkadot-js/apps?branch=master) +[![dependency](https://david-dm.org/polkadot-js/apps.svg?style=flat-square&path=packages/ui-react)](https://david-dm.org/polkadot-js/apps?path=packages/ui-react) +[![devDependency](https://david-dm.org/polkadot-js/apps/dev-status.svg?style=flat-square&path=packages/ui-react)](https://david-dm.org/polkadot-js/apps?path=packages/ui-react#info=devDependencies) + +# @polkadot/ui-react + +A lightweight collection of React components used for construction of applications. See [src/demo.js](src/demo.js) for a sample utilising all components. diff --git a/packages/ui-react/index.html b/packages/ui-react/index.html new file mode 100644 index 0000000000..fb30a73837 --- /dev/null +++ b/packages/ui-react/index.html @@ -0,0 +1,9 @@ + + + + + +
+ + + diff --git a/packages/ui-react/package.json b/packages/ui-react/package.json new file mode 100644 index 0000000000..d41842913e --- /dev/null +++ b/packages/ui-react/package.json @@ -0,0 +1,35 @@ +{ + "name": "@polkadot/ui-react", + "version": "0.17.0", + "description": "A collection of base React components", + "main": "index.js", + "scripts": { + "build": "polkadot-dev-build-babel", + "check": "stylelint 'src/**/*.css' && eslint src && flow check", + "test": "echo \"Tests only available from root wrapper\"" + }, + "author": "Jaco Greeff ", + "license": "ISC", + "dependencies": { + "@babel/runtime": "^7.0.0-beta.47", + "@polkadot/ui-identicon": "^0.17.0" + }, + "devDependencies": { + "@polkadot/util-keyring": "^0.22.2", + "babel-loader": "^8.0.0-beta.2", + "css-loader": "0.28.9", + "empty": "^0.10.1", + "enzyme": "^3.3.0", + "enzyme-adapter-react-16": "^1.1.1", + "postcss": "^6.0.22", + "postcss-loader": "2.0.10", + "postcss-sass": "^0.3.0", + "react": "^16.4.0", + "react-dom": "^16.4.0", + "style-loader": "^0.19.1" + }, + "peerDependencies": { + "react": "^16.4.0", + "react-router-dom": "^4.2.2" + } +} diff --git a/packages/ui-react/postcss.config.js b/packages/ui-react/postcss.config.js new file mode 100644 index 0000000000..f053ebf797 --- /dev/null +++ b/packages/ui-react/postcss.config.js @@ -0,0 +1 @@ +module.exports = {}; diff --git a/packages/ui-react/src/Container/Container.css b/packages/ui-react/src/Container/Container.css new file mode 100644 index 0000000000..a1adaed4dd --- /dev/null +++ b/packages/ui-react/src/Container/Container.css @@ -0,0 +1,18 @@ +/* Copyright 2017-2018 Jaco Greeff +/* This software may be modified and distributed under the terms +/* of the ISC license. See the LICENSE file for details. */ + +.ui--Container { + padding: 0.5em; +} + +.ui--DemoContainer { + padding: 0.5em 0.5em 1.5em 5em; +} + +.ui--DemoHeader { + background: #f5f5f5; + font-size: 0.75em; + margin: 0 0 1em -6em; + padding: 0.5em; +} diff --git a/packages/ui-react/src/Container/Demo.js b/packages/ui-react/src/Container/Demo.js new file mode 100644 index 0000000000..c2c9118e71 --- /dev/null +++ b/packages/ui-react/src/Container/Demo.js @@ -0,0 +1,39 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import React from 'react'; + +import Container from './index'; + +type DemoProps = { + attrs: { [string]: mixed }, + attrsReq: Array, + children: React$Node, + component: string +}; + +export default function Demo ({ attrs, attrsReq, children, component }: DemoProps): React$Node { + const allAttrs = Object.assign({ className: '"..."', style: '{...}' }, attrs); + const attrsText = Object + .keys(allAttrs) + .map((attr) => { + const value = allAttrs[attr]; + const bool = value === 'bool'; + const opt = bool || !attrsReq.includes(attr); + + return `${opt ? '[' : ''}${attr}${bool ? '' : '='}${bool ? '' : value}${opt ? ']' : ''}`; + }) + .sort() + .join('\n '); + + return ( + +
+        {`<${component}\n  ${attrsText}\n/>`}
+      
+ {children} +
+ ); +} diff --git a/packages/ui-react/src/Container/index.js b/packages/ui-react/src/Container/index.js new file mode 100644 index 0000000000..65b0142436 --- /dev/null +++ b/packages/ui-react/src/Container/index.js @@ -0,0 +1,25 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import type { BaseProps } from '../types'; + +import './Container.css'; + +import React from 'react'; + +type Props = BaseProps & { + children: React$Node +}; + +export default function Container ({ children, className, style }: Props): React$Node { + return ( +
+ {children} +
+ ); +} diff --git a/packages/ui-react/src/Container/index.spec.js b/packages/ui-react/src/Container/index.spec.js new file mode 100644 index 0000000000..e89811bd8d --- /dev/null +++ b/packages/ui-react/src/Container/index.spec.js @@ -0,0 +1,26 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. + +import { shallow } from '../../test/enzyme'; + +import React from 'react'; + +import { Container } from '../index'; + +describe('Container', () => { + let wrapper; + + beforeEach(() => { + wrapper = shallow( + , + {} + ); + }); + + it('creates the element', () => { + expect( + wrapper + ).toBeDefined(); + }); +}); diff --git a/packages/ui-react/src/IdentityIcon/Demo.js b/packages/ui-react/src/IdentityIcon/Demo.js new file mode 100644 index 0000000000..d3ede939f7 --- /dev/null +++ b/packages/ui-react/src/IdentityIcon/Demo.js @@ -0,0 +1,43 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import React from 'react'; +import randomAsU8a from '@polkadot/util-crypto/random/asU8a'; +import addressEncode from '@polkadot/util-keyring/address/encode'; + +import Container from '../Container/Demo'; +import IdentityIcon from './index'; + +export default function Demo () { + const identities = [new Uint8Array(32)]; + + while (identities.length !== 10) { + identities.push( + addressEncode(randomAsU8a(32)) + ); + } + + return ( + ', + value: '{...}' + }} + attrsReq={[ + 'value' + ]} + > + { + identities.map((value) => ( + + )) + } + + ); +} diff --git a/packages/ui-react/src/IdentityIcon/IdentityIcon.css b/packages/ui-react/src/IdentityIcon/IdentityIcon.css new file mode 100644 index 0000000000..ad513732f7 --- /dev/null +++ b/packages/ui-react/src/IdentityIcon/IdentityIcon.css @@ -0,0 +1,7 @@ +/* Copyright 2017-2018 Jaco Greeff +/* This software may be modified and distributed under the terms +/* of the ISC license. See the LICENSE file for details. */ + +.ui--IdentityIcon { + display: inline-block; +} diff --git a/packages/ui-react/src/IdentityIcon/appendIcon.js b/packages/ui-react/src/IdentityIcon/appendIcon.js new file mode 100644 index 0000000000..21dcea0cf3 --- /dev/null +++ b/packages/ui-react/src/IdentityIcon/appendIcon.js @@ -0,0 +1,22 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import identicon from '@polkadot/ui-identicon'; + +type NodeRefSet = (node: ?Element) => void; + +export default function appendIcon (address: string | Uint8Array, size: number): NodeRefSet { + return (node: ?Element): void => { + if (node) { + while (node.firstChild) { + node.removeChild(node.firstChild); + } + + node.appendChild( + identicon(address, size) + ); + } + }; +} diff --git a/packages/ui-react/src/IdentityIcon/appendIcon.spec.js b/packages/ui-react/src/IdentityIcon/appendIcon.spec.js new file mode 100644 index 0000000000..e8cffbcda6 --- /dev/null +++ b/packages/ui-react/src/IdentityIcon/appendIcon.spec.js @@ -0,0 +1,33 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. + +describe('autoFocus', () => { + let appendIcon; + let mockIdenticon; + let node; + + beforeEach(() => { + node = { + appendChild: jest.fn() + }; + + mockIdenticon = jest.fn(() => 'genIdentitycon'); + jest.mock('@polkadot/ui-identicon', () => mockIdenticon); + + appendIcon = require('./appendIcon').default; + }); + + it('generates & appends icon to the node', () => { + appendIcon('testAddr', 'testSize')(node); + + expect(node.appendChild).toHaveBeenCalledWith('genIdentitycon'); + expect(mockIdenticon).toHaveBeenCalledWith('testAddr', 'testSize'); + }); + + it('does not call identicon when no node', () => { + appendIcon('testAddr', 'testSize')(); + + expect(mockIdenticon).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/ui-react/src/IdentityIcon/index.js b/packages/ui-react/src/IdentityIcon/index.js new file mode 100644 index 0000000000..ebe4a83572 --- /dev/null +++ b/packages/ui-react/src/IdentityIcon/index.js @@ -0,0 +1,27 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import type { BaseProps } from '../types'; + +import './IdentityIcon.css'; + +import React from 'react'; + +import appendIcon from './appendIcon'; + +type Props = BaseProps & { + size?: number, + value: string | Uint8Array +}; + +export default function IdentityIcon ({ className, size = 64, style, value }: Props) { + return ( +
+ ); +} diff --git a/packages/ui-react/src/IdentityIcon/index.spec.js b/packages/ui-react/src/IdentityIcon/index.spec.js new file mode 100644 index 0000000000..b5852afa44 --- /dev/null +++ b/packages/ui-react/src/IdentityIcon/index.spec.js @@ -0,0 +1,26 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. + +import { shallow } from '../../test/enzyme'; + +import React from 'react'; + +import { IdentityIcon } from '../index'; + +describe('IdentityIcon', () => { + let wrapper; + + beforeEach(() => { + wrapper = shallow( + , + {} + ); + }); + + it('creates the element', () => { + expect( + wrapper + ).toBeDefined(); + }); +}); diff --git a/packages/ui-react/src/demo.css b/packages/ui-react/src/demo.css new file mode 100644 index 0000000000..fd9b896b32 --- /dev/null +++ b/packages/ui-react/src/demo.css @@ -0,0 +1,8 @@ +/* Copyright 2017-2018 Jaco Greeff +/* This software may be modified and distributed under the terms +/* of the ISC license. See the LICENSE file for details. */ + +.ui--Demo { + margin: 0; + padding: 0.5em; +} diff --git a/packages/ui-react/src/demo.js b/packages/ui-react/src/demo.js new file mode 100644 index 0000000000..5d15a1521c --- /dev/null +++ b/packages/ui-react/src/demo.js @@ -0,0 +1,24 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import './demo.css'; + +import React from 'react'; +import ReactDOM from 'react-dom'; + +import IdentityIcon from './IdentityIcon/Demo'; + +const element = document.getElementById('demo'); + +if (!element) { + throw new Error('Unable to find #demo element'); +} + +ReactDOM.render( +
+ +
, + element +); diff --git a/packages/ui-react/src/index.js b/packages/ui-react/src/index.js new file mode 100644 index 0000000000..51e337b81f --- /dev/null +++ b/packages/ui-react/src/index.js @@ -0,0 +1,12 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +import Container from './Container'; +import IdentityIcon from './IdentityIcon'; + +export { + Container, + IdentityIcon +}; diff --git a/packages/ui-react/src/types.js b/packages/ui-react/src/types.js new file mode 100644 index 0000000000..744f420419 --- /dev/null +++ b/packages/ui-react/src/types.js @@ -0,0 +1,11 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +export type BaseProps = { + className?: string, + style?: { + [string]: mixed + } +}; diff --git a/packages/ui-react/test/enzyme.js b/packages/ui-react/test/enzyme.js new file mode 100644 index 0000000000..d0e28707f4 --- /dev/null +++ b/packages/ui-react/test/enzyme.js @@ -0,0 +1,12 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. + +const Adapter = require('enzyme-adapter-react-16'); +const Enzyme = require('enzyme'); + +Enzyme.configure({ + adapter: new Adapter() +}); + +module.exports = Enzyme; diff --git a/packages/ui-react/webpack.config.js b/packages/ui-react/webpack.config.js new file mode 100644 index 0000000000..ee1a123d60 --- /dev/null +++ b/packages/ui-react/webpack.config.js @@ -0,0 +1,47 @@ +// Copyright 2017-2018 Jaco Greeff +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. +// @flow + +const path = require('path'); + +module.exports = { + context: __dirname, + entry: './src/demo.js', + mode: 'development', + output: { + path: __dirname, + filename: './demo.js' + }, + resolve: { + alias: { + '@polkadot/ui-identicon': path.resolve(__dirname, '../ui-identicon/src/index.js') + } + }, + module: { + rules: [ + { + test: /\.css$/, + exclude: /(node_modules)/, + use: [ + 'style-loader', + { + loader: 'css-loader', + options: { + importLoaders: 1 + } + }, + 'postcss-loader' + ] + }, + { + test: /\.js$/, + exclude: /(node_modules)/, + use: { + loader: 'babel-loader' + } + } + ] + }, + plugins: [] +}; diff --git a/packages/ui-signer/package.json b/packages/ui-signer/package.json index dfe9a3e2e6..3c0ab60ef4 100644 --- a/packages/ui-signer/package.json +++ b/packages/ui-signer/package.json @@ -1,6 +1,6 @@ { "name": "@polkadot/ui-signer", - "version": "0.7.2", + "version": "0.17.0", "main": "index.js", "repository": "https://github.com/polkadot-js/apps.git", "author": "Jaco Greeff ", @@ -13,8 +13,8 @@ "dependencies": { "@babel/runtime": "^7.0.0-beta.47", "@polkadot/extrinsics": "^0.17.2", - "@polkadot/ui-app": "^0.7.2", - "@polkadot/ui-keyring": "^0.7.2" + "@polkadot/ui-app": "^0.17.0", + "@polkadot/ui-keyring": "^0.17.0" }, "peerDependencies": { "react": "^16.4.0", diff --git a/yarn.lock b/yarn.lock index bfaf16b4cc..6cd35a5908 100644 --- a/yarn.lock +++ b/yarn.lock @@ -910,30 +910,6 @@ "@polkadot/util" "^0.22.2" "@polkadot/util-keyring" "^0.22.2" -"@polkadot/ui-identicon@^0.14.3": - version "0.14.3" - resolved "https://registry.yarnpkg.com/@polkadot/ui-identicon/-/ui-identicon-0.14.3.tgz#b9cd977e92458748dd67a5321422734f4a820750" - dependencies: - "@babel/runtime" "^7.0.0-beta.47" - color "^3.0.0" - -"@polkadot/ui-react-rx@^0.14.3": - version "0.14.3" - resolved "https://registry.yarnpkg.com/@polkadot/ui-react-rx/-/ui-react-rx-0.14.3.tgz#408cb03e6ae9d3eaca096b4c653afe0618904f42" - dependencies: - "@babel/runtime" "^7.0.0-beta.47" - "@polkadot/api-rx" "^0.13.2" - "@polkadot/extrinsics-codec" "^0.17.2" - "@polkadot/storage" "^0.17.2" - semver "^5.5.0" - -"@polkadot/ui-react@^0.14.3": - version "0.14.3" - resolved "https://registry.yarnpkg.com/@polkadot/ui-react/-/ui-react-0.14.3.tgz#4455cd8a285dc690f546cd1a614361b6dbdb1c0b" - dependencies: - "@babel/runtime" "^7.0.0-beta.47" - "@polkadot/ui-identicon" "^0.14.3" - "@polkadot/util-crypto@^0.22.2": version "0.22.2" resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-0.22.2.tgz#068b567b2a1945fac5d0cf699f6226b21297a0ee" @@ -999,6 +975,10 @@ version "0.7.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" +"@types/node@*": + version "10.1.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.1.3.tgz#5c16980936c4e3c83ce64e8ed71fb37bd7aea135" + "@webassemblyjs/ast@1.4.3": version "1.4.3" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.4.3.tgz#3b3f6fced944d8660273347533e6d4d315b5934a" @@ -2590,6 +2570,17 @@ chardet@^0.4.0: version "0.4.2" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2" +cheerio@^1.0.0-rc.2: + version "1.0.0-rc.2" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.2.tgz#4b9f53a81b27e4d5dac31c0ffd0cfa03cc6830db" + dependencies: + css-select "~1.2.0" + dom-serializer "~0.1.0" + entities "~1.1.1" + htmlparser2 "^3.9.1" + lodash "^4.15.0" + parse5 "^3.0.1" + chokidar@^2.0.0, chokidar@^2.0.2, chokidar@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.3.tgz#dcbd4f6cbb2a55b4799ba8a840ac527e5f4b1176" @@ -2852,6 +2843,10 @@ colormin@^1.0.5: css-color-names "0.0.4" has "^1.0.1" +colors@0.5.x: + version "0.5.1" + resolved "https://registry.yarnpkg.com/colors/-/colors-0.5.1.tgz#7d0023eaeb154e8ee9fce75dcb923d0ed1667774" + colors@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" @@ -3316,6 +3311,25 @@ css-color-names@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" +css-loader@0.28.9: + version "0.28.9" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.28.9.tgz#68064b85f4e271d7ce4c48a58300928e535d1c95" + dependencies: + babel-code-frame "^6.26.0" + css-selector-tokenizer "^0.7.0" + cssnano "^3.10.0" + icss-utils "^2.1.0" + loader-utils "^1.0.2" + lodash.camelcase "^4.3.0" + object-assign "^4.1.1" + postcss "^5.0.6" + postcss-modules-extract-imports "^1.2.0" + postcss-modules-local-by-default "^1.2.0" + postcss-modules-scope "^1.1.0" + postcss-modules-values "^1.3.0" + postcss-value-parser "^3.3.0" + source-list-map "^2.0.0" + css-loader@^0.28.9: version "0.28.11" resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.28.11.tgz#c3f9864a700be2711bb5a2462b2389b1a392dab7" @@ -3335,7 +3349,7 @@ css-loader@^0.28.9: postcss-value-parser "^3.3.0" source-list-map "^2.0.0" -css-select@^1.1.0: +css-select@^1.1.0, css-select@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" dependencies: @@ -3651,6 +3665,10 @@ dir-glob@^2.0.0: arrify "^1.0.1" path-type "^3.0.0" +discontinuous-range@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/discontinuous-range/-/discontinuous-range-1.0.0.tgz#e38331f0844bba49b9a9cb71c771585aab1bc65a" + docopt@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/docopt/-/docopt-0.6.2.tgz#b28e9e2220da5ec49f7ea5bb24a47787405eeb11" @@ -3674,7 +3692,7 @@ dom-converter@~0.1: dependencies: utila "~0.3" -dom-serializer@0: +dom-serializer@0, dom-serializer@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" dependencies: @@ -3855,6 +3873,47 @@ envinfo@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-4.4.2.tgz#472c49f3a8b9bca73962641ce7cb692bf623cd1c" +enzyme-adapter-react-16@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.1.1.tgz#a8f4278b47e082fbca14f5bfb1ee50ee650717b4" + dependencies: + enzyme-adapter-utils "^1.3.0" + lodash "^4.17.4" + object.assign "^4.0.4" + object.values "^1.0.4" + prop-types "^15.6.0" + react-reconciler "^0.7.0" + react-test-renderer "^16.0.0-0" + +enzyme-adapter-utils@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.3.0.tgz#d6c85756826c257a8544d362cc7a67e97ea698c7" + dependencies: + lodash "^4.17.4" + object.assign "^4.0.4" + prop-types "^15.6.0" + +enzyme@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.3.0.tgz#0971abd167f2d4bf3f5bd508229e1c4b6dc50479" + dependencies: + cheerio "^1.0.0-rc.2" + function.prototype.name "^1.0.3" + has "^1.0.1" + is-boolean-object "^1.0.0" + is-callable "^1.1.3" + is-number-object "^1.0.3" + is-string "^1.0.4" + is-subset "^0.1.1" + lodash "^4.17.4" + object-inspect "^1.5.0" + object-is "^1.0.1" + object.assign "^4.1.0" + object.entries "^1.0.4" + object.values "^1.0.4" + raf "^3.4.0" + rst-selector-parser "^2.2.3" + errno@^0.1.3, errno@~0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" @@ -3878,7 +3937,7 @@ error@^7.0.2: string-template "~0.2.1" xtend "~4.0.0" -es-abstract@^1.5.1, es-abstract@^1.7.0: +es-abstract@^1.5.1, es-abstract@^1.6.1, es-abstract@^1.7.0: version "1.11.0" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.11.0.tgz#cce87d518f0496893b1a30cd8461835535480681" dependencies: @@ -4538,10 +4597,18 @@ fsevents@^1.1.2, fsevents@^1.2.3: nan "^2.9.2" node-pre-gyp "^0.10.0" -function-bind@^1.0.2, function-bind@^1.1.1: +function-bind@^1.0.2, function-bind@^1.1.0, function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" +function.prototype.name@^1.0.3: + version "1.1.0" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.0.tgz#8bd763cc0af860a859cc5d49384d74b932cd2327" + dependencies: + define-properties "^1.1.2" + function-bind "^1.1.1" + is-callable "^1.1.3" + functional-red-black-tree@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" @@ -5114,7 +5181,7 @@ html-webpack-plugin@^3.2.0: toposort "^1.0.0" util.promisify "1.0.0" -htmlparser2@^3.9.2: +htmlparser2@^3.9.1, htmlparser2@^3.9.2: version "3.9.2" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338" dependencies: @@ -5387,6 +5454,10 @@ is-binary-path@^1.0.0: dependencies: binary-extensions "^1.0.0" +is-boolean-object@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.0.tgz#98f8b28030684219a95f375cfbd88ce3405dff93" + is-buffer@^1.1.4, is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" @@ -5546,6 +5617,10 @@ is-npm@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4" +is-number-object@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.3.tgz#f265ab89a9f445034ef6aff15a8f00b00f551799" + is-number@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" @@ -5656,6 +5731,10 @@ is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" +is-string@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.4.tgz#cc3a9b69857d621e963725a24caeec873b826e64" + is-subset@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-subset/-/is-subset-0.1.1.tgz#8a59117d932de1de00f245fcdd39ce43f1e939a6" @@ -6598,6 +6677,10 @@ lodash.camelcase@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" +lodash.flattendeep@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" + lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" @@ -7154,6 +7237,15 @@ natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" +nearley@^2.7.10: + version "2.13.0" + resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.13.0.tgz#6e7b0f4e68bfc3e74c99eaef2eda39e513143439" + dependencies: + nomnom "~1.6.2" + railroad-diagrams "^1.0.0" + randexp "0.4.6" + semver "^5.4.1" + needle@^2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.1.tgz#b5e325bd3aae8c2678902fa296f729455d1d3a7d" @@ -7262,6 +7354,13 @@ nomnom@^1.8.1: chalk "~0.4.0" underscore "~1.6.0" +nomnom@~1.6.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/nomnom/-/nomnom-1.6.2.tgz#84a66a260174408fc5b77a18f888eccc44fb6971" + dependencies: + colors "0.5.x" + underscore "~1.4.4" + nopt@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" @@ -7373,6 +7472,14 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" +object-inspect@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.6.0.tgz#c70b6cbf72f274aab4c34c0c82f5167bf82cf15b" + +object-is@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.1.tgz#0aa60ec9989a0b3ed795cf4d06f62cf1ad6539b6" + object-keys@^1.0.11, object-keys@^1.0.8: version "1.0.11" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d" @@ -7383,7 +7490,7 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" -object.assign@^4.1.0: +object.assign@^4.0.4, object.assign@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" dependencies: @@ -7392,6 +7499,15 @@ object.assign@^4.1.0: has-symbols "^1.0.0" object-keys "^1.0.11" +object.entries@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.0.4.tgz#1bf9a4dd2288f5b33f3a993d257661f05d161a5f" + dependencies: + define-properties "^1.1.2" + es-abstract "^1.6.1" + function-bind "^1.1.0" + has "^1.0.1" + object.getownpropertydescriptors@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" @@ -7412,6 +7528,15 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" +object.values@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.0.4.tgz#e524da09b4f66ff05df457546ec72ac99f13069a" + dependencies: + define-properties "^1.1.2" + es-abstract "^1.6.1" + function-bind "^1.1.0" + has "^1.0.1" + on-finished@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" @@ -7654,6 +7779,12 @@ parse5@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" +parse5@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.3.tgz#042f792ffdd36851551cf4e9e066b3874ab45b5c" + dependencies: + "@types/node" "*" + parseurl@^1.3.0: version "1.3.2" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" @@ -7898,6 +8029,15 @@ postcss-load-plugins@^2.3.0: cosmiconfig "^2.1.1" object-assign "^4.1.0" +postcss-loader@2.0.10: + version "2.0.10" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-2.0.10.tgz#090db0540140bd56a7a7f717c41bc29aeef4c674" + dependencies: + loader-utils "^1.1.0" + postcss "^6.0.0" + postcss-load-config "^1.2.0" + schema-utils "^0.3.0" + postcss-loader@^2.1.5: version "2.1.5" resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-2.1.5.tgz#3c6336ee641c8f95138172533ae461a83595e788" @@ -8296,6 +8436,23 @@ quick-lru@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8" +raf@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.0.tgz#a28876881b4bc2ca9117d4138163ddb80f781575" + dependencies: + performance-now "^2.1.0" + +railroad-diagrams@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz#eb7e6267548ddedfb899c1b90e57374559cddb7e" + +randexp@0.4.6: + version "0.4.6" + resolved "https://registry.yarnpkg.com/randexp/-/randexp-0.4.6.tgz#e986ad5e5e31dae13ddd6f7b3019aa7c87f60ca3" + dependencies: + discontinuous-range "1.0.0" + ret "~0.1.10" + randomatic@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.0.0.tgz#d35490030eb4f7578de292ce6dfb04a91a128923" @@ -8372,10 +8529,23 @@ react-i18next@^7.5.1: html-parse-stringify2 "2.0.1" prop-types "^15.6.0" +react-is@^16.4.0: + version "16.4.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.4.0.tgz#cc9fdc855ac34d2e7d9d2eb7059bbc240d35ffcf" + react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" +react-reconciler@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/react-reconciler/-/react-reconciler-0.7.0.tgz#9614894103e5f138deeeb5eabaf3ee80eb1d026d" + dependencies: + fbjs "^0.8.16" + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.0" + react-router-dom@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-4.2.2.tgz#c8a81df3adc58bba8a76782e946cbd4eae649b8d" @@ -8399,6 +8569,15 @@ react-router@^4.2.0: prop-types "^15.5.4" warning "^3.0.0" +react-test-renderer@^16.0.0-0: + version "16.4.0" + resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.4.0.tgz#0dbe0e24263e94e1830c7afb1f403707fad313a3" + dependencies: + fbjs "^0.8.16" + object-assign "^4.1.1" + prop-types "^15.6.0" + react-is "^16.4.0" + react@^16.4.0: version "16.4.0" resolved "https://registry.yarnpkg.com/react/-/react-16.4.0.tgz#402c2db83335336fba1962c08b98c6272617d585" @@ -8945,6 +9124,13 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" +rst-selector-parser@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz#81b230ea2fcc6066c89e3472de794285d9b03d91" + dependencies: + lodash.flattendeep "^4.4.0" + nearley "^2.7.10" + rsvp@^3.3.3: version "3.6.2" resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-3.6.2.tgz#2e96491599a96cde1b515d5674a8f7a91452926a" @@ -9969,6 +10155,10 @@ underscore@^1.8.3: version "1.9.0" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.0.tgz#31dbb314cfcc88f169cd3692d9149d81a00a73e4" +underscore@~1.4.4: + version "1.4.4" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.4.4.tgz#61a6a32010622afa07963bf325203cf12239d604" + underscore@~1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.6.0.tgz#8b38b10cacdef63337b8b24e4ff86d45aea529a8" @@ -10645,6 +10835,10 @@ xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" +xmlserializer@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/xmlserializer/-/xmlserializer-0.6.1.tgz#14099f60272e16cb608be3758992f998070cc29a" + xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0, xtend@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"