├── .circleci └── config.yml ├── .gitignore ├── LICENSE ├── README.md ├── babel.config.js ├── misc ├── requestAnimationFrame.js ├── testSetup.js └── zenika.png ├── package.json ├── packages ├── core │ ├── README.md │ ├── build │ │ ├── core.js │ │ └── index.js │ ├── package.json │ └── src │ │ ├── __snapshots__ │ │ └── index.spec.js.snap │ │ ├── core.jsx │ │ ├── index.js │ │ └── index.spec.js ├── examples │ ├── .env │ ├── LICENSE │ ├── package.json │ ├── public │ │ ├── favicon.png │ │ └── index.html │ ├── publish.js │ └── src │ │ ├── components │ │ ├── App.jsx │ │ ├── App.scss │ │ ├── Code │ │ │ ├── Code.jsx │ │ │ ├── Code.scss │ │ │ └── index.js │ │ ├── Example │ │ │ ├── Button │ │ │ │ ├── Button.jsx │ │ │ │ ├── Button.scss │ │ │ │ └── index.js │ │ │ ├── Example.jsx │ │ │ ├── Example.scss │ │ │ └── index.js │ │ ├── Examples │ │ │ ├── Base.jsx │ │ │ ├── DontWait.jsx │ │ │ ├── ErrorIndicator.jsx │ │ │ ├── Examples.jsx │ │ │ ├── Examples.scss │ │ │ ├── LoadingIndicator.jsx │ │ │ ├── OneParam.jsx │ │ │ ├── TwoParams.jsx │ │ │ └── index.js │ │ └── index.js │ │ └── index.js └── full │ ├── README.md │ ├── build │ ├── ErrorCross │ │ ├── ErrorCross.js │ │ └── index.js │ ├── TailSpin │ │ ├── TailSpin.js │ │ └── index.js │ ├── index.js │ └── utils │ │ ├── colorUtil.js │ │ └── index.js │ ├── package.json │ └── src │ ├── ErrorCross │ ├── ErrorCross.jsx │ ├── ErrorCross.spec.js │ └── index.js │ ├── TailSpin │ ├── TailSpin.jsx │ ├── TailSpin.spec.js │ └── index.js │ ├── index.js │ ├── index.spec.js │ └── utils │ ├── colorUtil.js │ └── index.js └── yarn.lock /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | debug: 4 | docker: 5 | - image: circleci/node:latest 6 | steps: 7 | - run: node --version 8 | - run: yarn --version 9 | 10 | build: 11 | docker: 12 | - image: circleci/node:latest 13 | steps: 14 | - checkout 15 | - restore_cache: 16 | keys: 17 | - dependencies-{{ checksum "package.json" }} 18 | - dependencies- 19 | - run: yarn 20 | - save_cache: 21 | paths: 22 | - node_modules 23 | key: dependencies-{{ checksum "package.json" }} 24 | - run: yarn ci 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | .DS_Store 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 uni rakun 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hoc-react-loader 2 | [![CircleCI](https://circleci.com/gh/unirakun/hoc-react-loader.svg?&style=shield)](https://circleci.com/gh/unirakun/hoc-react-loader/tree/master) [![NPM Version](https://badge.fury.io/js/hoc-react-loader.svg)](https://www.npmjs.com/package/hoc-react-loader) [![Coverage Status](https://coveralls.io/repos/github/unirakun/hoc-react-loader/badge.svg?branch=master)](https://coveralls.io/github/unirakun/hoc-react-loader?branch=master) 3 | 4 | This is a [higher order component](https://facebook.github.io/react/docs/higher-order-components.html) ("HOC"). It's an advanced pattern used in React that let you reuse code logic, it can be summarized as a component factory. It improves isolation, interoperability and maintainability of your code base. 5 | 6 | **hoc-react-loader**'s purpose is to call a `load` callback passed through the `props` of a component only once (at `componentWillMount`). This is convenient to load data from a backend for instance. The component shows a loading indicator when it's waiting for the props to be defined. The loading indicator can be changed easily. 7 | 8 | - To see full documentation: [click here](./packages/core/README.md) 9 | - Do you want a default LoadingIndicator and a default ErrorIndicator?, you can use [@hoc-react-loader/full](./packages/full/README.md) 10 | - You want your bundle not being bloated?, you can use [@hoc-react-loader/core](./packages/core/README.md) 11 | 12 | ## Example 13 | ```jsx 14 | import React from 'react' 15 | import loader from '@hoc-react-loader/core' 16 | 17 | const Component = ({ data }) =>
Component {JSON.stringify(data)}
18 | const load = props => console.log(props) 19 | 20 | export default loader({ print: ['data'], load })(Component) 21 | ``` 22 | 23 | In this example `load` will be called at first mount, then the wrapped `Component` will be printed only if `props.data` is truthy. 24 | 25 | `load` function can be the moment you ask for your API data. 26 | 27 | ## Demos 28 | You can test some examples [here](https://unirakun.github.io/hoc-react-loader/). 29 | 30 | # About uni rakun 31 | **uni rakun** is created by two passionate french developers. 32 | 33 | Do you want to contact them ? Go to their [website](https://unirakun.fr) 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 |
Guillaume CRESPELFabien JUIF
44 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | /* 2 | * needed to be this file (babel.config.js) for jest 3 | * because jest doesn't look for .babelrc or package.json.babel ... :( 4 | */ 5 | module.exports = { 6 | presets: [ 7 | '@babel/preset-env', 8 | '@babel/preset-react', 9 | ], 10 | plugins: [ 11 | '@babel/plugin-proposal-class-properties', 12 | ], 13 | } 14 | -------------------------------------------------------------------------------- /misc/requestAnimationFrame.js: -------------------------------------------------------------------------------- 1 | global.requestAnimationFrame = (cb) => { 2 | setTimeout(cb, 0) 3 | } 4 | -------------------------------------------------------------------------------- /misc/testSetup.js: -------------------------------------------------------------------------------- 1 | import './requestAnimationFrame' 2 | import Enzyme from 'enzyme' 3 | import Adapter from 'enzyme-adapter-react-16' 4 | 5 | Enzyme.configure({ adapter: new Adapter() }) 6 | -------------------------------------------------------------------------------- /misc/zenika.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unirakun/hoc-react-loader/6392271292e27f857b20e2d0820a4af050027166/misc/zenika.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hoc-react-loader", 3 | "version": "7.0.0", 4 | "description": "Higher order component to call a load function from props at mount.", 5 | "license": "MIT", 6 | "private": true, 7 | "author": "Fabien JUIF ", 8 | "contributors": [ 9 | "Yvonnick FRIN ", 10 | "Yoann Prot ", 11 | "Benjamin CAVY " 12 | ], 13 | "workspaces": [ 14 | "packages/*" 15 | ], 16 | "scripts": { 17 | "build:core": "yarn --cwd packages/core build", 18 | "build:full": "yarn --cwd packages/full build", 19 | "build": "run-p build:*", 20 | "lint": "eslint . --ext js,jsx --ignore-pattern dist/ --ignore-pattern coverage/ --ignore-pattern node_modules/ --ignore-pattern misc/ --ignore-pattern public/ --ignore-pattern build/", 21 | "test": "jest", 22 | "coveralls": "jest --coverage && cat ./coverage/lcov.info | coveralls", 23 | "ci": "run-p lint coveralls" 24 | }, 25 | "devDependencies": { 26 | "@babel/core": "^7.1.2", 27 | "@babel/plugin-proposal-class-properties": "^7.1.0", 28 | "@babel/preset-env": "^7.1.0", 29 | "@babel/preset-react": "^7.0.0", 30 | "babel-core": "^7.0.0-bridge.0", 31 | "babel-eslint": "^10.0.1", 32 | "babel-jest": "^23.6.0", 33 | "coveralls": "^3.0.2", 34 | "enzyme": "^3.7.0", 35 | "enzyme-adapter-react-16": "^1.6.0", 36 | "eslint": "^5.7.0", 37 | "eslint-config-airbnb": "^17.1.0", 38 | "eslint-plugin-babel": "^5.2.1", 39 | "eslint-plugin-import": "^2.14.0", 40 | "eslint-plugin-jsx-a11y": "^6.1.1", 41 | "eslint-plugin-react": "^7.11.0", 42 | "jest": "^23.6.0", 43 | "npm-run-all": "^4.1.3", 44 | "prop-types": "^15.6.2", 45 | "react": "^16.5.2", 46 | "react-dom": "^16.5.2", 47 | "tinycolor2": "^1.4.1" 48 | }, 49 | "eslintConfig": { 50 | "parser": "babel-eslint", 51 | "extends": [ 52 | "airbnb" 53 | ], 54 | "plugins": [ 55 | "babel" 56 | ], 57 | "rules": { 58 | "max-len": [ 59 | 2, 60 | 200 61 | ], 62 | "semi": [ 63 | 2, 64 | "never" 65 | ], 66 | "react/forbid-prop-types": "off", 67 | "react/no-unescaped-entities": "off" 68 | } 69 | }, 70 | "jest": { 71 | "coveragePathIgnorePatterns": [ 72 | "/node_modules/", 73 | "/dist/", 74 | "/misc/", 75 | "/build/" 76 | ], 77 | "setupFiles": [ 78 | "./misc/testSetup.js" 79 | ] 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /packages/core/README.md: -------------------------------------------------------------------------------- 1 | # @hoc-react-loader/core 2 | [![CircleCI](https://circleci.com/gh/unirakun/hoc-react-loader.svg?&style=shield)](https://circleci.com/gh/unirakun/hoc-react-loader/tree/master) [![NPM Version](https://badge.fury.io/js/hoc-react-loader.svg)](https://www.npmjs.com/package/hoc-react-loader) [![Coverage Status](https://coveralls.io/repos/github/unirakun/hoc-react-loader/badge.svg?branch=master)](https://coveralls.io/github/unirakun/hoc-react-loader?branch=master) 3 | 4 | This is a [higher order component](https://facebook.github.io/react/docs/higher-order-components.html) ("HOC"). It's an advanced pattern used in React that let you reuse code logic, it can be summarized as a component factory. It improves isolation, interoperability and maintainability of your code base. 5 | 6 | **@hoc-react-loader/core**'s purpose is to call a `load` callback passed through the `props` of a component only once (at `componentWillMount`). This is convenient to load data from a backend for instance. The component shows a loading indicator when it's waiting for the props to be defined. The loading indicator can be changed easily. 7 | 8 | ## Demos 9 | You can test some examples [here](https://unirakun.github.io/hoc-react-loader/). 10 | 11 | ## Installation 12 | `yarn add @hoc-react-loader/core` 13 | 14 | ## Usage 15 | ### With `this.props` 16 | ```es6 17 | import loader from '@hoc-react-loader/core' 18 | 19 | const Component = ({ data }) =>
Component {JSON.stringify(data)}
20 | 21 | export default loader({ print: ['data'] })(Component) 22 | ``` 23 | In this case, the loader waits for `this.props.data` to be truthy, then mounts its child component and calls `this.props.load` if it exists. This is useful when the parent has control over the injected data, or when the `Component` is connected with `redux`. `this.props.load` should be injected by the parent component or injected by a `Container` (redux). 24 | 25 | The `print` parameter should be an array of props to waits. All these props should become truthy at some point. 26 | 27 | Since the `LoadingIndicator` is not specified, `null` (nothing) is displayed while waiting for all the props. Here's an exemple with a specified loader: 28 | ```es6 29 | import loader from '@hoc-react-loader/core' 30 | 31 | const MyLoadingIndicator = () =>
Waiting...
32 | const Component = ({ data }) =>
Component {data}
33 | 34 | export default loader({ print: ['data'], LoadingIndicator: MyLoadingIndicator })(Component) 35 | ``` 36 | 37 | The `print` parameter can also be a Promise. The loading indicator is displayed until `print` Promise is resolved or rejected. 38 | 39 | ### Don't wait 40 | ```es6 41 | import loader from '@hoc-react-loader/core' 42 | 43 | const LoadingIndicator = () =>
Waiting...
44 | const Component = ({ data }) =>
Component {JSON.stringify(data)}
45 | 46 | export default loader({ LoadingIndicator })(Component) 47 | ``` 48 | In this example, the loader component doesn't wait for props. `this.props.load` is called once, but the `LoadingIndicator` component isn't displayed. 49 | 50 | ### Load as a function parameter 51 | ```es6 52 | import loader from '@hoc-react-loader/core' 53 | 54 | const LoadingIndicator = () =>
Waiting...
55 | const Component = ({ data }) =>
Component {JSON.stringify(data)}
56 | 57 | export default loader({ LoadingIndicator, load: () => console.log('here') })(Component) 58 | ``` 59 | In this case, the loader calls `this.props.load` if it exists *AND* the `load` parameter, resulting in `here` to be printed. 60 | 61 | The default `print` parameter value is `true`. It means that in this example the `LoadingIndicator` isn't displayed. 62 | 63 | ### Load as a string parameter 64 | ```es6 65 | import loader from '@hoc-react-loader/core' 66 | 67 | const LoadingIndicator = () =>
Waiting...
68 | const Component = ({ data }) =>
Component {JSON.stringify(data)}
69 | 70 | export default loader({ LoadingIndicator, load: 'myLoader' })(Component) 71 | ``` 72 | In this case, the loader calls `this.props.myLoader` if it exists. 73 | 74 | The default `print` parameter value is `true`. It means that in this example the `LoadingIndicator` isn't displayed. 75 | 76 | ### Print as a function 77 | The `print` parameter can also be a function. Then the `props` and `context` are given to it (in this order), and it should return a boolean indicating wether or not the actual component should be displayed. 78 | 79 | ### Error handling 80 | The `error` parameter allows to specify a prop that indicates wether or not a placeholder error component should be displayed in replacement of the real component. 81 | It's usefull when data that are required for the correct display of a component are missing. 82 | 83 | Like for the `print` prop, `error` can be a `boolean`, a `string` (referencing a prop name), an array of `string` (an array of prop names) or a `function` (whose result will be converted to `boolean`). 84 | 85 | ```js 86 | // default error component will be displayed if 'error' prop is truthy 87 | export default loader()(MyComponent) 88 | 89 | // default error component will be displayed (null, meaning nothing) 90 | export default loader({ error: true })(MyComponent) 91 | 92 | // default error component will be displayed if 'errorMessage' prop is truthy 93 | export default loader({ error: 'errorMessage' })(MyComponent) 94 | 95 | // CustomErrorComponent will be displayed if 'error' prop is truthy 96 | export default loader({ ErrorIndicator: CustomErrorComponent })(MyComponent) 97 | ``` 98 | 99 | ### Delay parameter 100 | 101 | When a component loads very quickly, you will see a flash of the loading component. 102 | To avoid this behaviour, you can add a `delay` parameter to the loader with a time in milliseconds. 103 | Then, the loading indicator will be rendered after the delay if the Component can't be rendered before that. 104 | 105 | ```js 106 | // loading indicator will be displayed only after 200ms 107 | export default loader({ print: ['data'], delay: 200 })(MyComponent) 108 | ``` 109 | 110 | By default, no delay is defined. 111 | -------------------------------------------------------------------------------- /packages/core/build/core.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = void 0; 7 | 8 | var _react = _interopRequireWildcard(require("react")); 9 | 10 | function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } 11 | 12 | function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } 13 | 14 | function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; } 15 | 16 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 17 | 18 | function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } 19 | 20 | function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } 21 | 22 | function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } 23 | 24 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } 25 | 26 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 27 | 28 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } 29 | 30 | function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } 31 | 32 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 33 | 34 | // eslint-disable-line import/no-extraneous-dependencies 35 | var getTypeOf = function getTypeOf(something) { 36 | var getType = {}; 37 | return something && getType.toString.call(something); 38 | }; // http://stackoverflow.com/a/7356528 39 | 40 | 41 | var isFunction = function isFunction(functionToCheck) { 42 | var type = getTypeOf(functionToCheck); 43 | return type && type === '[object Function]'; 44 | }; 45 | 46 | var isString = function isString(stringToCheck) { 47 | var type = getTypeOf(stringToCheck); 48 | return type && type === '[object String]'; 49 | }; 50 | 51 | var hasStatus = function hasStatus(prop, propProcessor, defaultProp, defaultValue) { 52 | return function (props, state, context) { 53 | if (prop === undefined) { 54 | var status = props[defaultProp]; 55 | return status === undefined ? defaultValue : !!status; 56 | } 57 | 58 | if (Array.isArray(prop)) { 59 | var boolProps = prop.map(function (p) { 60 | return !!props[p]; 61 | }); 62 | return propProcessor(boolProps); 63 | } 64 | 65 | if (isFunction(prop)) { 66 | return !!prop(props, context); 67 | } 68 | 69 | return !!prop; 70 | }; 71 | }; 72 | 73 | var getDisplayName = function getDisplayName(c) { 74 | return c.displayName || c.name || 'Component'; 75 | }; 76 | 77 | var _default = function _default() { 78 | var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, 79 | LoadingIndicator = _ref.LoadingIndicator, 80 | ErrorIndicator = _ref.ErrorIndicator, 81 | print = _ref.print, 82 | load = _ref.load, 83 | error = _ref.error, 84 | delay = _ref.delay; 85 | 86 | var loadFunctionName = isString(load) ? load : 'load'; 87 | var isLoadFunction = isFunction(load); 88 | var isLoaded = hasStatus(print, function (bs) { 89 | return !bs.includes(false); 90 | }, 'loaded', true); 91 | var isInError = hasStatus(error, function (bs) { 92 | return bs.includes(true); 93 | }, 'error', false); 94 | return function (ComposedComponent) { 95 | var _class, _temp; 96 | 97 | var displayName = "Loader(".concat(getDisplayName(ComposedComponent), ")"); 98 | return _temp = _class = 99 | /*#__PURE__*/ 100 | function (_Component) { 101 | _inherits(_class, _Component); 102 | 103 | _createClass(_class, null, [{ 104 | key: "getDerivedStateFromProps", 105 | value: function getDerivedStateFromProps(props, state) { 106 | var isLoadAFunction = isFunction(props[loadFunctionName]); 107 | 108 | if (isLoadAFunction) { 109 | return _objectSpread({}, state, { 110 | props: _objectSpread({}, props, _defineProperty({}, loadFunctionName, undefined)) 111 | }); 112 | } 113 | 114 | return _objectSpread({}, state, { 115 | props: props 116 | }); 117 | } 118 | }]); 119 | 120 | function _class(props, context) { 121 | var _this; 122 | 123 | _classCallCheck(this, _class); 124 | 125 | _this = _possibleConstructorReturn(this, _getPrototypeOf(_class).call(this, props, context)); 126 | _this.state = { 127 | props: {}, 128 | print: true 129 | }; 130 | return _this; 131 | } 132 | 133 | _createClass(_class, [{ 134 | key: "componentDidMount", 135 | value: function componentDidMount() { 136 | var _this2 = this; 137 | 138 | // Load from hoc argument 139 | if (isLoadFunction) { 140 | load(this.props, this.context); 141 | } // Load from props 142 | 143 | 144 | var loadFunction = this.props[loadFunctionName]; // eslint-disable-line react/destructuring-assignment 145 | 146 | if (isFunction(loadFunction)) { 147 | loadFunction(this.props, this.context); 148 | } // set delay 149 | 150 | 151 | if (delay) { 152 | this.setState(function (state) { 153 | return _objectSpread({}, state, { 154 | print: false 155 | }); 156 | }); 157 | this.timer = setTimeout(function () { 158 | return _this2.setState(function (state) { 159 | return _objectSpread({}, state, { 160 | print: true 161 | }); 162 | }); 163 | }, delay); 164 | } 165 | } 166 | }, { 167 | key: "componentWillUnmount", 168 | value: function componentWillUnmount() { 169 | if (this.timer) { 170 | clearTimeout(this.timer); 171 | } 172 | } 173 | }, { 174 | key: "render", 175 | value: function render() { 176 | var props = this.state.props; 177 | 178 | if (isInError(this.props, this.state, this.context)) { 179 | // react renders nothing if you return null but is not happy if you return undefined 180 | return ErrorIndicator === undefined ? null : _react.default.createElement(ErrorIndicator, props); 181 | } 182 | 183 | if (isLoaded(this.props, this.state, this.context)) { 184 | return _react.default.createElement(ComposedComponent, props); 185 | } 186 | 187 | if (!this.state.print) { 188 | // eslint-disable-line react/destructuring-assignment 189 | return null; 190 | } // react renders nothing if you return null but is not happy if you return undefined 191 | 192 | 193 | return LoadingIndicator === undefined ? null : _react.default.createElement(LoadingIndicator, props); 194 | } 195 | }]); 196 | 197 | return _class; 198 | }(_react.Component), _defineProperty(_class, "displayName", displayName), _temp; 199 | }; 200 | }; 201 | 202 | exports.default = _default; -------------------------------------------------------------------------------- /packages/core/build/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | Object.defineProperty(exports, "default", { 7 | enumerable: true, 8 | get: function get() { 9 | return _core.default; 10 | } 11 | }); 12 | 13 | var _core = _interopRequireDefault(require("./core")); 14 | 15 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } -------------------------------------------------------------------------------- /packages/core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hoc-react-loader/core", 3 | "version": "7.0.0", 4 | "description": "Higher order component to call a load function from props at mount", 5 | "main": "build/index.js", 6 | "repository": "https://github.com/alakarteio/hoc-react-loader", 7 | "author": "Fabien JUIF ", 8 | "contributors": [ 9 | "Yvonnick FRIN ", 10 | "Yoann Prot ", 11 | "Benjamin CAVY " 12 | ], 13 | "keywords": [ 14 | "react", 15 | "loader", 16 | "hoc", 17 | "placeholder" 18 | ], 19 | "license": "MIT", 20 | "private": false, 21 | "scripts": { 22 | "build": "babel --ignore \"**/*.spec.js\" ./src/ --out-dir build" 23 | }, 24 | "devDependencies": { 25 | "@babel/cli": "^7.1.2", 26 | "@babel/core": "^7.1.2", 27 | "@babel/plugin-proposal-class-properties": "^7.1.0", 28 | "@babel/preset-env": "^7.1.0", 29 | "@babel/preset-react": "^7.0.0", 30 | "enzyme": "^3.7.0", 31 | "react": "^16.5.2" 32 | }, 33 | "babel": { 34 | "presets": [ 35 | "@babel/preset-env", 36 | "@babel/preset-react" 37 | ], 38 | "plugins": [ 39 | "@babel/plugin-proposal-class-properties" 40 | ] 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/core/src/__snapshots__/index.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`@hoc-react-loader/core Indicators parameters should print the given ErrorIndicator 1`] = `"
Error
"`; 4 | 5 | exports[`@hoc-react-loader/core Indicators parameters should print the given LoadingIndicator 1`] = `"
Loader
"`; 6 | 7 | exports[`@hoc-react-loader/core delay parameter should render component when loaded when delay not past 1`] = `"
Component
"`; 8 | 9 | exports[`@hoc-react-loader/core delay parameter should render component when loaded when delay past 1`] = `"
Component
"`; 10 | 11 | exports[`@hoc-react-loader/core delay parameter should render loading indicator after delay and component not still loaded 1`] = `"
load
"`; 12 | 13 | exports[`@hoc-react-loader/core delay parameter should render nothing before delay and component not still loaded 1`] = `null`; 14 | 15 | exports[`@hoc-react-loader/core error parameter should print Component -error as a value- 1`] = `"
Component
"`; 16 | 17 | exports[`@hoc-react-loader/core error parameter should print Component -error as an array- 1`] = `"
Component
"`; 18 | 19 | exports[`@hoc-react-loader/core error parameter should print Component -error as an array- 2`] = `"
Component
"`; 20 | 21 | exports[`@hoc-react-loader/core error parameter should print Component -error as an array- 3`] = `"
Component
"`; 22 | 23 | exports[`@hoc-react-loader/core error parameter should print Component -error as function- 1`] = `"
Component
"`; 24 | 25 | exports[`@hoc-react-loader/core error parameter should print Component -error as function- 2`] = ` 26 | Array [ 27 | Array [ 28 | Object { 29 | "some": "props", 30 | }, 31 | Object {}, 32 | ], 33 | ] 34 | `; 35 | 36 | exports[`@hoc-react-loader/core error parameter should print error Component -error as a value- 1`] = `null`; 37 | 38 | exports[`@hoc-react-loader/core error parameter should print error Component -error as an array- 1`] = `null`; 39 | 40 | exports[`@hoc-react-loader/core error parameter should print error Component -error as an array- 2`] = `null`; 41 | 42 | exports[`@hoc-react-loader/core error parameter should print error Component -error as an array- 3`] = `null`; 43 | 44 | exports[`@hoc-react-loader/core error parameter should print error Component -error as an array- 4`] = `null`; 45 | 46 | exports[`@hoc-react-loader/core error parameter should print error Component -error as function- 1`] = `null`; 47 | 48 | exports[`@hoc-react-loader/core error parameter should print error Component -error as function- 2`] = ` 49 | Array [ 50 | Array [ 51 | Object { 52 | "some": "props", 53 | }, 54 | Object {}, 55 | ], 56 | ] 57 | `; 58 | 59 | exports[`@hoc-react-loader/core print parameter should not print Component -loaded props- 1`] = `null`; 60 | 61 | exports[`@hoc-react-loader/core print parameter should not print Component -loaded props- 2`] = `null`; 62 | 63 | exports[`@hoc-react-loader/core print parameter should not print Component -print as a value- 1`] = `null`; 64 | 65 | exports[`@hoc-react-loader/core print parameter should not print Component -print as function- 1`] = `null`; 66 | 67 | exports[`@hoc-react-loader/core print parameter should not print Component -print as function- 2`] = ` 68 | Array [ 69 | Array [ 70 | Object { 71 | "some": "props", 72 | }, 73 | Object {}, 74 | ], 75 | ] 76 | `; 77 | 78 | exports[`@hoc-react-loader/core print parameter should print Component -empty parameters- 1`] = `"
Component
"`; 79 | 80 | exports[`@hoc-react-loader/core print parameter should print Component -loaded props- 1`] = `"
Component
"`; 81 | 82 | exports[`@hoc-react-loader/core print parameter should print Component -loaded props- 2`] = `"
Component
"`; 83 | 84 | exports[`@hoc-react-loader/core print parameter should print Component -print as a value- 1`] = `"
Component
"`; 85 | 86 | exports[`@hoc-react-loader/core print parameter should print Component -print as an array- 1`] = `null`; 87 | 88 | exports[`@hoc-react-loader/core print parameter should print Component -print as an array- 2`] = `null`; 89 | 90 | exports[`@hoc-react-loader/core print parameter should print Component -print as an array- 3`] = `null`; 91 | 92 | exports[`@hoc-react-loader/core print parameter should print Component -print as an array- 4`] = `"
Component
"`; 93 | 94 | exports[`@hoc-react-loader/core print parameter should print Component -print as function- 1`] = `"
Component
"`; 95 | 96 | exports[`@hoc-react-loader/core print parameter should print Component -print as function- 2`] = ` 97 | Array [ 98 | Array [ 99 | Object { 100 | "some": "props", 101 | }, 102 | Object {}, 103 | ], 104 | ] 105 | `; 106 | 107 | exports[`@hoc-react-loader/core print parameter should print Component -undefined parameters- 1`] = `"
Component
"`; 108 | -------------------------------------------------------------------------------- /packages/core/src/core.jsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import React, { Component } from 'react' // eslint-disable-line import/no-extraneous-dependencies 3 | 4 | const getTypeOf = (something) => { 5 | const getType = {} 6 | return something && getType.toString.call(something) 7 | } 8 | 9 | // http://stackoverflow.com/a/7356528 10 | const isFunction = (functionToCheck) => { 11 | const type = getTypeOf(functionToCheck) 12 | return type && type === '[object Function]' 13 | } 14 | 15 | const isString = (stringToCheck) => { 16 | const type = getTypeOf(stringToCheck) 17 | return type && type === '[object String]' 18 | } 19 | 20 | const hasStatus = (prop, propProcessor, defaultProp, defaultValue) => (props, state, context) => { 21 | if (prop === undefined) { 22 | const status = props[defaultProp] 23 | return status === undefined ? defaultValue : !!status 24 | } 25 | 26 | if (Array.isArray(prop)) { 27 | const boolProps = prop.map(p => !!props[p]) 28 | return propProcessor(boolProps) 29 | } 30 | 31 | if (isFunction(prop)) { 32 | return !!prop(props, context) 33 | } 34 | 35 | return !!prop 36 | } 37 | 38 | const getDisplayName = c => c.displayName || c.name || 'Component' 39 | 40 | export default ( 41 | { 42 | LoadingIndicator, 43 | ErrorIndicator, 44 | print, 45 | load, 46 | error, 47 | delay, 48 | } = {}, 49 | ) => { 50 | const loadFunctionName = isString(load) ? load : 'load' 51 | const isLoadFunction = isFunction(load) 52 | 53 | const isLoaded = hasStatus(print, bs => !bs.includes(false), 'loaded', true) 54 | const isInError = hasStatus(error, bs => bs.includes(true), 'error', false) 55 | 56 | return (ComposedComponent) => { 57 | const displayName = `Loader(${getDisplayName(ComposedComponent)})` 58 | 59 | return class extends Component { 60 | static displayName = displayName 61 | 62 | static getDerivedStateFromProps(props, state) { 63 | const isLoadAFunction = isFunction(props[loadFunctionName]) 64 | 65 | if (isLoadAFunction) { 66 | return { 67 | ...state, 68 | props: { 69 | ...props, 70 | [loadFunctionName]: undefined, 71 | }, 72 | } 73 | } 74 | 75 | return { ...state, props } 76 | } 77 | 78 | constructor(props, context) { 79 | super(props, context) 80 | 81 | this.state = { 82 | props: {}, 83 | print: true, 84 | } 85 | } 86 | 87 | componentDidMount() { 88 | // Load from hoc argument 89 | if (isLoadFunction) { 90 | load(this.props, this.context) 91 | } 92 | 93 | // Load from props 94 | const loadFunction = this.props[loadFunctionName] // eslint-disable-line react/destructuring-assignment 95 | if (isFunction(loadFunction)) { 96 | loadFunction(this.props, this.context) 97 | } 98 | 99 | // set delay 100 | if (delay) { 101 | this.setState(state => ({ ...state, print: false })) 102 | this.timer = setTimeout(() => this.setState(state => ({ ...state, print: true })), delay) 103 | } 104 | } 105 | 106 | componentWillUnmount() { 107 | if (this.timer) { 108 | clearTimeout(this.timer) 109 | } 110 | } 111 | 112 | render() { 113 | const { props } = this.state 114 | if (isInError(this.props, this.state, this.context)) { 115 | // react renders nothing if you return null but is not happy if you return undefined 116 | return ErrorIndicator === undefined ? null : 117 | } 118 | 119 | if (isLoaded(this.props, this.state, this.context)) { 120 | return 121 | } 122 | 123 | if (!this.state.print) { // eslint-disable-line react/destructuring-assignment 124 | return null 125 | } 126 | 127 | // react renders nothing if you return null but is not happy if you return undefined 128 | return LoadingIndicator === undefined ? null : 129 | } 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /packages/core/src/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './core' 2 | -------------------------------------------------------------------------------- /packages/core/src/index.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-env jest */ 2 | /* eslint-disable 3 | react/jsx-filename-extension 4 | */ 5 | import React from 'react' 6 | import { mount } from 'enzyme' 7 | import loader from './index' 8 | 9 | const Component = () =>
Component
10 | const Loader = () =>
Loader
11 | const Error = () =>
Error
12 | 13 | describe('@hoc-react-loader/core', () => { 14 | describe('load parameter', () => { 15 | it('should be called at mount', () => { 16 | const load = jest.fn() 17 | const Wrapped = loader({ load })(Component) 18 | 19 | const mounted = mount() 20 | mounted.setProps({ some: 'other props' }) 21 | 22 | expect(load).toHaveBeenCalledTimes(1) 23 | }) 24 | 25 | it('should call the given prop name', () => { 26 | const load = jest.fn() 27 | const call = jest.fn() 28 | const Wrapped = loader({ load: 'call' })(Component) 29 | 30 | const mounted = mount() 31 | mounted.setProps({ some: 'other props' }) 32 | 33 | expect(load).toHaveBeenCalledTimes(0) 34 | expect(call).toHaveBeenCalledTimes(1) 35 | }) 36 | }) 37 | 38 | describe('print parameter', () => { 39 | it('should print Component -empty parameters-', () => { 40 | const Wrapped = loader({ })(Component) 41 | 42 | const mounted = mount() 43 | 44 | expect(mounted.html()).toMatchSnapshot() 45 | }) 46 | 47 | it('should print Component -undefined parameters-', () => { 48 | const Wrapped = loader()(Component) 49 | 50 | const mounted = mount() 51 | 52 | expect(mounted.html()).toMatchSnapshot() 53 | }) 54 | 55 | it('should print Component -loaded props-', () => { 56 | const Wrapped = loader()(Component) 57 | 58 | const mounted = mount() 59 | expect(mounted.html()).toMatchSnapshot() 60 | 61 | mounted.setProps({ loaded: 'value is truthy' }) 62 | expect(mounted.html()).toMatchSnapshot() 63 | }) 64 | 65 | it('should not print Component -loaded props-', () => { 66 | const Wrapped = loader()(Component) 67 | 68 | const mounted = mount() 69 | expect(mounted.html()).toMatchSnapshot() 70 | 71 | mounted.setProps({ loaded: 0 }) 72 | expect(mounted.html()).toMatchSnapshot() 73 | }) 74 | 75 | it('should print Component -print as an array-', () => { 76 | const print = ['first', 'second'] 77 | const Wrapped = loader({ print })(Component) 78 | 79 | const mounted = mount() 80 | expect(mounted.html()).toMatchSnapshot() 81 | 82 | mounted.setProps({ first: false, second: true }) 83 | expect(mounted.html()).toMatchSnapshot() 84 | 85 | mounted.setProps({ first: 'truthy', second: 0 }) 86 | expect(mounted.html()).toMatchSnapshot() 87 | 88 | mounted.setProps({ first: 'truthy', second: 1 }) 89 | expect(mounted.html()).toMatchSnapshot() 90 | }) 91 | 92 | it('should print Component -print as function-', () => { 93 | const print = jest.fn(() => true) 94 | 95 | const Wrapped = loader({ print })(Component) 96 | const mounted = mount() 97 | 98 | expect(mounted.html()).toMatchSnapshot() 99 | expect(print.mock.calls).toMatchSnapshot() 100 | }) 101 | 102 | it('should not print Component -print as function-', () => { 103 | const print = jest.fn(() => false) 104 | 105 | const Wrapped = loader({ print })(Component) 106 | const mounted = mount() 107 | 108 | expect(mounted.html()).toMatchSnapshot() 109 | expect(print.mock.calls).toMatchSnapshot() 110 | }) 111 | 112 | it('should print Component -print as a value-', () => { 113 | const Wrapped = loader({ print: 'truthy' })(Component) 114 | const mounted = mount() 115 | 116 | expect(mounted.html()).toMatchSnapshot() 117 | }) 118 | 119 | it('should not print Component -print as a value-', () => { 120 | const Wrapped = loader({ print: 0 })(Component) 121 | const mounted = mount() 122 | 123 | expect(mounted.html()).toMatchSnapshot() 124 | }) 125 | }) 126 | 127 | describe('error parameter', () => { 128 | it('should print Component -error as a value-', () => { 129 | const error = false 130 | 131 | const Wrapped = loader({ error })(Component) 132 | const mounted = mount() 133 | 134 | expect(mounted.html()).toMatchSnapshot() 135 | }) 136 | 137 | it('should print error Component -error as a value-', () => { 138 | const error = true 139 | 140 | const Wrapped = loader({ error })(Component) 141 | const mounted = mount() 142 | 143 | expect(mounted.html()).toMatchSnapshot() 144 | }) 145 | 146 | it('should print Component -error as function-', () => { 147 | const error = jest.fn(() => false) 148 | 149 | const Wrapped = loader({ error })(Component) 150 | const mounted = mount() 151 | 152 | expect(mounted.html()).toMatchSnapshot() 153 | expect(error.mock.calls).toMatchSnapshot() 154 | }) 155 | 156 | it('should print error Component -error as function-', () => { 157 | const error = jest.fn(() => true) 158 | 159 | const Wrapped = loader({ error })(Component) 160 | const mounted = mount() 161 | 162 | expect(mounted.html()).toMatchSnapshot() 163 | expect(error.mock.calls).toMatchSnapshot() 164 | }) 165 | 166 | it('should print Component -error as an array-', () => { 167 | const error = ['first', 'second'] 168 | const Wrapped = loader({ error })(Component) 169 | 170 | const mounted = mount() 171 | expect(mounted.html()).toMatchSnapshot() 172 | 173 | mounted.setProps({ first: 0, second: false }) 174 | expect(mounted.html()).toMatchSnapshot() 175 | 176 | mounted.setProps({ first: false, second: '' }) 177 | expect(mounted.html()).toMatchSnapshot() 178 | }) 179 | 180 | it('should print error Component -error as an array-', () => { 181 | const error = ['first', 'second'] 182 | const Wrapped = loader({ error })(Component) 183 | 184 | const mounted = mount() 185 | expect(mounted.html()).toMatchSnapshot() 186 | 187 | mounted.setProps({ first: false, second: true }) 188 | expect(mounted.html()).toMatchSnapshot() 189 | 190 | mounted.setProps({ first: false, second: 'An error has occured' }) 191 | expect(mounted.html()).toMatchSnapshot() 192 | 193 | mounted.setProps({ first: 1, second: false }) 194 | expect(mounted.html()).toMatchSnapshot() 195 | }) 196 | }) 197 | 198 | describe('delay parameter', () => { 199 | jest.useFakeTimers() 200 | 201 | it('should render nothing before delay and component not still loaded', () => { 202 | const Wrapped = loader({ delay: 10000 })(Component) 203 | 204 | const mounted = mount() 205 | expect(mounted.html()).toMatchSnapshot() 206 | }) 207 | 208 | it('should render loading indicator after delay and component not still loaded', () => { 209 | const LoadingIndicator = () =>
load
210 | const Wrapped = loader({ delay: 10000, LoadingIndicator })(Component) 211 | 212 | const mounted = mount() 213 | jest.runAllTimers() 214 | expect(mounted.html()).toMatchSnapshot() 215 | }) 216 | 217 | it('should render component when loaded when delay not past', () => { 218 | const Wrapped = loader({ delay: 10000 })(Component) 219 | 220 | const mounted = mount() 221 | expect(mounted.html()).toMatchSnapshot() 222 | }) 223 | 224 | it('should render component when loaded when delay past', () => { 225 | const Wrapped = loader({ delay: 10000 })(Component) 226 | 227 | const mounted = mount() 228 | jest.runAllTimers() 229 | expect(mounted.html()).toMatchSnapshot() 230 | }) 231 | 232 | it('should unregister the timer when component unmounted', () => { 233 | const Wrapped = loader({ delay: 10000 })(Component) 234 | 235 | const mounted = mount() 236 | mounted.unmount() 237 | expect(clearTimeout).toHaveBeenCalledTimes(1) 238 | }) 239 | 240 | it('should unmount correctly when no delay defined', () => { 241 | const Wrapped = loader()(Component) 242 | 243 | const mounted = mount() 244 | mounted.unmount() 245 | expect(mounted.exists()).toBe(false) 246 | }) 247 | }) 248 | 249 | describe('Indicators parameters', () => { 250 | it('should print the given LoadingIndicator', () => { 251 | const Wrapped = loader({ LoadingIndicator: Loader })(Component) 252 | 253 | const mounted = mount() 254 | expect(mounted.html()).toMatchSnapshot() 255 | }) 256 | 257 | it('should print the given ErrorIndicator', () => { 258 | const Wrapped = loader({ ErrorIndicator: Error })(Component) 259 | 260 | const mounted = mount() 261 | expect(mounted.html()).toMatchSnapshot() 262 | }) 263 | }) 264 | }) 265 | -------------------------------------------------------------------------------- /packages/examples/.env: -------------------------------------------------------------------------------- 1 | SKIP_PREFLIGHT_CHECK=true 2 | -------------------------------------------------------------------------------- /packages/examples/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Fabien JUIF 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/examples/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hoc-react-loader/examples", 3 | "version": "7.0.0", 4 | "description": "", 5 | "directories": { 6 | "doc": "doc" 7 | }, 8 | "scripts": { 9 | "start": "react-scripts start", 10 | "build": "react-scripts build", 11 | "publish": "yarn build && node publish" 12 | }, 13 | "author": "Fabien JUIF", 14 | "license": "MIT", 15 | "devDependencies": { 16 | "gh-pages": "^2.0.1", 17 | "node-sass": "^4.9.4", 18 | "react-scripts": "^2.0.5" 19 | }, 20 | "dependencies": { 21 | "@hoc-react-loader/full": "^7.0.0", 22 | "highlight.js": "^9.13.0", 23 | "lodash": "^4.15.0", 24 | "normalize.css": "~4.2.0", 25 | "prop-types": "^15.5.8", 26 | "react": "16.5.2", 27 | "react-dom": "16.5.2", 28 | "react-highlight": "^0.12.0", 29 | "react-ink": "^6.4.0", 30 | "tinycolor2": "^1.4.1" 31 | }, 32 | "browserslist": [ 33 | ">0.2%", 34 | "not dead", 35 | "not ie <= 11", 36 | "not op_mini all" 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /packages/examples/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unirakun/hoc-react-loader/6392271292e27f857b20e2d0820a4af050027166/packages/examples/public/favicon.png -------------------------------------------------------------------------------- /packages/examples/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | hoc-react-loader: examples 7 | 8 | 9 | 10 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 | 23 | -------------------------------------------------------------------------------- /packages/examples/publish.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable 2 | import/no-extraneous-dependencies, 3 | no-console 4 | */ 5 | const ghpages = require('gh-pages') 6 | const path = require('path') 7 | 8 | ghpages.publish(path.join(__dirname, 'public'), (err) => { 9 | if (err) { 10 | console.error(err) 11 | } else { 12 | console.log('Success') 13 | } 14 | }) 15 | -------------------------------------------------------------------------------- /packages/examples/src/components/App.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Examples from './Examples' 3 | import './App.scss' 4 | 5 | const App = () => ( 6 |
7 | presentation 8 | 9 |

10 | hoc-react-loader 11 |

12 | 13 | 26 | 27 |

28 | This is a higher order component ("HOC"). 29 | Its purpose is to call a 30 |

load
31 | callback passed through the 32 |
props
33 | of a component only once (at 34 |
componentWillMount
35 | ). This is convenient to load 36 | data from a backend for instance. The component shows a loading indicator when it's 37 | waiting for the props to be defined. The loading indicator can be changed easily. 38 |

39 | 40 |

41 | Check out the examples below. Use the button to trigger a stubbed loading. 42 |

43 | 44 | 45 |
46 | ) 47 | 48 | export default App 49 | -------------------------------------------------------------------------------- /packages/examples/src/components/App.scss: -------------------------------------------------------------------------------- 1 | .app { 2 | align-items: center; 3 | display: flex; 4 | flex-direction: column; 5 | font-family: 'Roboto', sans-serif; 6 | 7 | .icon { 8 | left: 10px; 9 | position: absolute; 10 | top: 10px; 11 | width: 100px; 12 | } 13 | 14 | .app-description { 15 | margin-bottom: 30px; 16 | width: 50%; 17 | 18 | > pre { 19 | display: inline; 20 | margin: 0 .5em; 21 | } 22 | } 23 | 24 | .github { 25 | position: absolute; 26 | right: 10px; 27 | top: 10px; 28 | } 29 | 30 | h1 { 31 | font-family: 'Roboto Condensed', sans-serif; 32 | } 33 | } 34 | 35 | :global(.github-button) { 36 | display: none; 37 | } 38 | -------------------------------------------------------------------------------- /packages/examples/src/components/Code/Code.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import 'highlight.js/styles/github.css' 4 | import Highlight from 'react-highlight' 5 | import styles from './Code.scss' 6 | 7 | const Code = ({ 8 | style, 9 | className, 10 | children, 11 | }) => ( 12 | 16 | {children} 17 | 18 | ) 19 | 20 | Code.propTypes = { 21 | style: PropTypes.object, 22 | className: PropTypes.string, 23 | children: PropTypes.node.isRequired, 24 | } 25 | 26 | Code.defaultProps = { 27 | style: {}, 28 | className: '', 29 | } 30 | 31 | export default Code 32 | -------------------------------------------------------------------------------- /packages/examples/src/components/Code/Code.scss: -------------------------------------------------------------------------------- 1 | .code { 2 | align-items: center; 3 | background-color: #d1d1d1; 4 | display: flex; 5 | font-size: 80%; 6 | padding: 20px 10px; 7 | position: relative; 8 | } 9 | -------------------------------------------------------------------------------- /packages/examples/src/components/Code/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Code' 2 | -------------------------------------------------------------------------------- /packages/examples/src/components/Example/Button/Button.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import Ink from 'react-ink' 4 | import './Button.scss' 5 | 6 | const Button = ({ 7 | style, 8 | className, 9 | onClick, 10 | toggled, 11 | text, 12 | toggleText, 13 | untoggleText, 14 | }) => ( 15 | 24 | ) 25 | 26 | Button.propTypes = { 27 | style: PropTypes.object, 28 | className: PropTypes.string, 29 | onClick: PropTypes.func.isRequired, 30 | toggled: PropTypes.bool.isRequired, 31 | text: PropTypes.string.isRequired, 32 | toggleText: PropTypes.string, 33 | untoggleText: PropTypes.string, 34 | } 35 | 36 | Button.defaultProps = { 37 | style: {}, 38 | className: '', 39 | toggleText: 'Load', 40 | untoggleText: 'Unload', 41 | } 42 | 43 | export default Button 44 | -------------------------------------------------------------------------------- /packages/examples/src/components/Example/Button/Button.scss: -------------------------------------------------------------------------------- 1 | .button { 2 | background-color: #9577ba; 3 | border: 0; 4 | box-shadow: none; 5 | color: darken(#fff, 10); 6 | cursor: pointer; 7 | flex: 1; 8 | font-family: 'Roboto Condensed', sans-serif; 9 | font-size: 1.2em; 10 | height: 50px; 11 | outline: 0; 12 | padding: 0 2rem; 13 | position: relative; 14 | text-transform: uppercase; 15 | 16 | &:hover { 17 | background-color: darken(#9577ba, 10); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/examples/src/components/Example/Button/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Button' 2 | -------------------------------------------------------------------------------- /packages/examples/src/components/Example/Example.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { PropTypes } from 'prop-types' 3 | import Ink from 'react-ink' 4 | import Code from '../Code' 5 | import Button from './Button' 6 | import './Example.scss' 7 | 8 | const BASE_URL = 'https://github.com/alakarteio/hoc-react-loader/blob/master/packages/examples/src/components/Examples/' 9 | 10 | class Example extends Component { 11 | constructor() { 12 | super() 13 | 14 | this.state = { 15 | loaded: false, 16 | } 17 | } 18 | 19 | onLoad = () => { 20 | this.setState(state => ({ 21 | ...state, 22 | loaded: !state.loaded, 23 | })) 24 | } 25 | 26 | onError = () => { 27 | this.setState(state => ({ 28 | ...state, 29 | error: state.error ? undefined : 'A (fake) error occured !', 30 | })) 31 | } 32 | 33 | onProp = () => { 34 | this.setState(state => ({ 35 | ...state, 36 | prop: state.prop ? undefined : 'Yeah it works !', 37 | })) 38 | } 39 | 40 | onProp2 = () => { 41 | this.setState(state => ({ 42 | ...state, 43 | prop2: state.prop2 ? undefined : 'Yeah it works !', 44 | })) 45 | } 46 | 47 | render() { 48 | const { 49 | style, 50 | className, 51 | code, 52 | children, 53 | example, 54 | link, 55 | buttons = {}, 56 | } = this.props 57 | 58 | const { 59 | loaded, 60 | prop, 61 | prop2, 62 | error, 63 | } = this.state 64 | 65 | return ( 66 |
67 | {children} 68 |
69 | 70 | {code} 71 | 72 | 73 | {React.cloneElement(example, { ...this.state, className: 'result' })} 74 |
75 |

Props values

76 |
 77 |               {buttons['0'] && `loaded: ${loaded ? 'true' : 'false'}\n`}
 78 |               {buttons['1'] && `prop: ${prop || 'undefined'}\n`}
 79 |               {buttons['2'] && `prop2: ${prop2 || 'undefined'}\n`}
 80 |               {buttons['3'] && `error: ${error || 'undefined'}\n`}
 81 |             
82 |
83 |
84 | {buttons['0'] && ( 85 |
103 |
104 |
105 | 106 | ) 107 | } 108 | } 109 | 110 | Example.propTypes = { 111 | style: PropTypes.object, 112 | className: PropTypes.string, 113 | code: PropTypes.string.isRequired, 114 | link: PropTypes.string.isRequired, 115 | buttons: PropTypes.object.isRequired, 116 | children: PropTypes.node, 117 | example: PropTypes.node, 118 | } 119 | 120 | Example.defaultProps = { 121 | style: {}, 122 | className: undefined, 123 | children: undefined, 124 | example: undefined, 125 | } 126 | 127 | export default Example 128 | -------------------------------------------------------------------------------- /packages/examples/src/components/Example/Example.scss: -------------------------------------------------------------------------------- 1 | .sample { 2 | background-color: #fff; 3 | display: flex; 4 | flex-direction: column; 5 | 6 | .render { 7 | display: flex; 8 | } 9 | 10 | .result { 11 | align-items: center; 12 | display: flex; 13 | height: 100px; 14 | justify-content: center; 15 | margin: 10px 0; 16 | min-width: 900px; 17 | } 18 | 19 | .buttons { 20 | display: flex; 21 | 22 | div { 23 | flex: 1; 24 | } 25 | } 26 | 27 | .debug { 28 | background-color: #282828; 29 | color: #fff; 30 | padding-left: 10px; 31 | 32 | .props { 33 | font-size: 80%; 34 | padding-bottom: 10px; 35 | padding-left: 10px; 36 | } 37 | 38 | h2 { 39 | font-size: 90%; 40 | margin-bottom: 5px; 41 | } 42 | } 43 | 44 | a { 45 | flex: 1; 46 | position: relative; 47 | text-decoration: none; 48 | } 49 | 50 | pre { 51 | margin: 0; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /packages/examples/src/components/Example/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Example' 2 | -------------------------------------------------------------------------------- /packages/examples/src/components/Examples/Base.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { PropTypes } from 'prop-types' 3 | import loader from '@hoc-react-loader/full' 4 | 5 | const Base = ({ className }) => ( 6 |
7 | Loaded! 8 |
9 | ) 10 | 11 | Base.propTypes = { 12 | className: PropTypes.string, 13 | } 14 | 15 | Base.defaultProps = { 16 | className: undefined, 17 | } 18 | 19 | export default loader()(Base) 20 | -------------------------------------------------------------------------------- /packages/examples/src/components/Examples/DontWait.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { PropTypes } from 'prop-types' 3 | import loader from '@hoc-react-loader/full' 4 | 5 | const DontWait = ({ className }) => ( 6 |
7 | Loaded without waiting! 8 |
9 | ) 10 | 11 | DontWait.propTypes = { 12 | className: PropTypes.string, 13 | } 14 | 15 | DontWait.defaultProps = { 16 | className: undefined, 17 | } 18 | 19 | export default loader({ print: true })(DontWait) 20 | -------------------------------------------------------------------------------- /packages/examples/src/components/Examples/ErrorIndicator.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import loader from '@hoc-react-loader/full' 3 | 4 | const EmptyComponent = () =>
5 | 6 | const ErrorIndicator = props => ( 7 |
8 | An error occured ! 9 |
10 | ) 11 | 12 | export default loader({ ErrorIndicator })(EmptyComponent) 13 | -------------------------------------------------------------------------------- /packages/examples/src/components/Examples/Examples.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { PropTypes } from 'prop-types' 3 | import Example from '../Example' 4 | import Base from './Base' 5 | import OneParam from './OneParam' 6 | import TwoParams from './TwoParams' 7 | import DontWait from './DontWait' 8 | import LoadingIndicator from './LoadingIndicator' 9 | import ErrorIndicator from './ErrorIndicator' 10 | import './Examples.scss' 11 | 12 | const Examples = ({ style, className }) => ( 13 |
14 | } 20 | > 21 |
22 |

Base: `this.props.loaded` is defined

23 |

24 | In this example, the loader wraps a Component with   25 |

this.props.loaded
26 |  defined. It means that the loader calls 27 |
this.props.load
28 |  if presents, then waits for 29 |
this.props.loaded
30 |  to be truthy, and finally displays the wrapped components. While waiting for 31 |
this.props.loaded
32 |  to be truthy, hoc-react-loader takes care of displaying a spinner based on the background color. 33 |

34 |
35 |
36 | 37 | } 43 | > 44 |
45 |

OneParam: waiting for a given param

46 |

47 | Like in the previous example, the default loading indicator is used (the spinner) and   48 |

this.props.load
49 | is called. But this time, the 50 |
HOC
51 | waits for   52 |
this.props.prop
53 | as specified in the 54 |
print
55 | param. 56 |

57 |
58 |
59 | 60 | } 66 | > 67 |
68 |

TwoParams: wait for multiple props

69 |

70 | If the 71 |

print
72 | parameter is given an array of props instead of just one prop,   73 |
hoc-react-loader
74 | will wait for all of the specified props to be truthy. In this example, these are 75 |
prop
76 | and 77 |
prop2
78 | . 79 |

80 |
81 |
82 | 83 | } 89 | > 90 |
91 |

DontWait: don't show the placeholder

92 |

93 | If the 94 |

print
95 | param is set to 96 |
true
97 | the 98 |
hoc-react-loader
99 |  will not display the loading indicator (default or custom). But 100 |
this.props.load
101 | is still called if presents! 102 |

103 |
104 |
105 | 106 | } 112 | > 113 |
114 |

LoadingIndicator: change the default loading indicator

115 |

116 | In this example, the default loading indicator is replaced with a custom one. 117 | The component waits for the default prop ( 118 |

this.props.loaded
119 | ). 120 |

121 |
122 |
123 | } 129 | > 130 |
131 |

Error: defaults parameters

132 |

133 | In this example, the loader wraps a Component with its default parameters. 134 | The loader will wait fo 135 |

this.props.error
136 | to bre truthy  and will then display the default error component. 137 |

138 |
139 |
140 | } 146 | > 147 |
148 |

ErrorIndicator: change the default error indicator

149 |

150 | In this example, the default error indicator is replaced with a custom one. 151 | The component waits for the default prop ( 152 |

this.props.error
153 | )  before displaying it. 154 |

155 |
156 |
157 |
158 | ) 159 | 160 | Examples.propTypes = { 161 | style: PropTypes.object, 162 | className: PropTypes.string, 163 | } 164 | 165 | Examples.defaultProps = { 166 | style: {}, 167 | className: undefined, 168 | } 169 | 170 | export default Examples 171 | -------------------------------------------------------------------------------- /packages/examples/src/components/Examples/Examples.scss: -------------------------------------------------------------------------------- 1 | .examples { 2 | .example { 3 | border: 1px solid #bebebe; 4 | margin: 0 auto; 5 | margin-bottom: 60px; 6 | min-width: 900px; 7 | position: relative; 8 | width: 40%; 9 | } 10 | 11 | p { 12 | margin-top: 0; 13 | } 14 | 15 | .doc { 16 | margin: 0 auto; 17 | min-width: 950px; 18 | width: 50%; 19 | 20 | .description { 21 | text-align: justify; 22 | 23 | > pre { 24 | display: inline; 25 | margin: 0 .5em; 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/examples/src/components/Examples/LoadingIndicator.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { PropTypes } from 'prop-types' 3 | import loader from '@hoc-react-loader/full' 4 | 5 | const CustomLoadingIndicator = props =>
Wait ...
6 | 7 | const LoadingIndicator = ({ className }) => ( 8 |
9 | Loaded! 10 |
11 | ) 12 | 13 | LoadingIndicator.propTypes = { 14 | className: PropTypes.string, 15 | } 16 | 17 | LoadingIndicator.defaultProps = { 18 | className: undefined, 19 | } 20 | 21 | export default loader({ LoadingIndicator: CustomLoadingIndicator })(LoadingIndicator) 22 | -------------------------------------------------------------------------------- /packages/examples/src/components/Examples/OneParam.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { PropTypes } from 'prop-types' 3 | import loader from '@hoc-react-loader/full' 4 | 5 | const OneParam = ({ className = '', prop }) => ( 6 |
7 | {`Loaded (prop = ${prop})`} 8 |
9 | ) 10 | 11 | OneParam.propTypes = { 12 | className: PropTypes.string, 13 | prop: PropTypes.string.isRequired, 14 | } 15 | 16 | OneParam.defaultProps = { 17 | className: undefined, 18 | } 19 | 20 | export default loader({ print: ['prop'] })(OneParam) 21 | -------------------------------------------------------------------------------- /packages/examples/src/components/Examples/TwoParams.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { PropTypes } from 'prop-types' 3 | import loader from '@hoc-react-loader/full' 4 | 5 | const TwoParams = ({ className, prop, prop2 }) => ( 6 |
7 | {`Loaded (prop = ${prop} || prop2 = ${prop2})`} 8 |
9 | ) 10 | 11 | TwoParams.propTypes = { 12 | className: PropTypes.string, 13 | prop: PropTypes.string.isRequired, 14 | prop2: PropTypes.string.isRequired, 15 | } 16 | 17 | TwoParams.defaultProps = { 18 | className: undefined, 19 | } 20 | 21 | export default loader({ print: ['prop', 'prop2'] })(TwoParams) 22 | -------------------------------------------------------------------------------- /packages/examples/src/components/Examples/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Examples' 2 | -------------------------------------------------------------------------------- /packages/examples/src/components/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './App' 2 | -------------------------------------------------------------------------------- /packages/examples/src/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-env browser */ 2 | import React from 'react' 3 | import { render } from 'react-dom' 4 | import 'normalize.css/normalize.css' 5 | import App from './components' 6 | 7 | render( 8 | React.createElement(App), 9 | document.getElementById('app'), 10 | ) 11 | -------------------------------------------------------------------------------- /packages/full/README.md: -------------------------------------------------------------------------------- 1 | # @hoc-react-loader/full 2 | 3 | This package uses [hoc-react-loader](https://github.com/alakarteio/hoc-react-loader) and add a default LoadingIndicator and a default ErrorIndicator. 4 | 5 | You can see documentation here: [documentation](https://github.com/alakarteio/hoc-react-loader). 6 | 7 | You can test some examples [here](https://alakarteio.github.io/hoc-react-loader/). 8 | 9 | ## Installation 10 | - `yarn add @hoc-react-loader/full` 11 | 12 | `tinycolor2` is a peer dependency of `@hoc-react-loader/full`. It handles color picking for the default loading indicator. You don't have to install it if you use your own loading indicator. 13 | -------------------------------------------------------------------------------- /packages/full/build/ErrorCross/ErrorCross.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = void 0; 7 | 8 | var _react = _interopRequireWildcard(require("react")); 9 | 10 | var _propTypes = _interopRequireDefault(require("prop-types")); 11 | 12 | var _utils = require("../utils"); 13 | 14 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 15 | 16 | function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } 17 | 18 | function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } 19 | 20 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 21 | 22 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } 23 | 24 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 25 | 26 | function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } 27 | 28 | function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } 29 | 30 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } 31 | 32 | function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } 33 | 34 | function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } 35 | 36 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 37 | 38 | var Cross = 39 | /*#__PURE__*/ 40 | function (_Component) { 41 | _inherits(Cross, _Component); 42 | 43 | function Cross(props) { 44 | var _this; 45 | 46 | _classCallCheck(this, Cross); 47 | 48 | _this = _possibleConstructorReturn(this, _getPrototypeOf(Cross).call(this, props)); 49 | 50 | _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "attach", function (div) { 51 | var newColor = (0, _utils.findCorrectColor)(div); 52 | var color = _this.state.color; 53 | 54 | if (color !== newColor) { 55 | _this.setState({ 56 | color: newColor 57 | }); 58 | } 59 | }); 60 | 61 | _this.state = { 62 | color: _utils.INITIAL_COLOR 63 | }; 64 | return _this; 65 | } 66 | 67 | _createClass(Cross, [{ 68 | key: "render", 69 | value: function render() { 70 | var color = this.state.color; 71 | var _this$props = this.props, 72 | style = _this$props.style, 73 | className = _this$props.className, 74 | message = _this$props.message; 75 | return _react.default.createElement("div", { 76 | title: message, 77 | ref: this.attach 78 | }, _react.default.createElement("svg", { 79 | height: "38", 80 | width: "38", 81 | viewBox: "0 0 38 38", 82 | className: className, 83 | style: style, 84 | xmlns: "http://www.w3.org/2000/svg" 85 | }, _react.default.createElement("path", { 86 | stroke: color, 87 | strokeWidth: "3", 88 | strokeLinecap: "round", 89 | id: "path3728", 90 | d: "M 1.5341128,1.5341128 36.465887,36.465887 m 0,-34.9317742 L 1.5341128,36.465887" 91 | }))); 92 | } 93 | }]); 94 | 95 | return Cross; 96 | }(_react.Component); 97 | 98 | var string = _propTypes.default.string, 99 | object = _propTypes.default.object; 100 | Cross.propTypes = { 101 | message: string, 102 | className: string, 103 | style: object 104 | }; 105 | Cross.defaultProps = { 106 | message: 'An error occured', 107 | className: '', 108 | style: {} 109 | }; 110 | var _default = Cross; 111 | exports.default = _default; -------------------------------------------------------------------------------- /packages/full/build/ErrorCross/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | Object.defineProperty(exports, "default", { 7 | enumerable: true, 8 | get: function get() { 9 | return _ErrorCross.default; 10 | } 11 | }); 12 | 13 | var _ErrorCross = _interopRequireDefault(require("./ErrorCross")); 14 | 15 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } -------------------------------------------------------------------------------- /packages/full/build/TailSpin/TailSpin.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = void 0; 7 | 8 | var _react = _interopRequireWildcard(require("react")); 9 | 10 | var _propTypes = _interopRequireDefault(require("prop-types")); 11 | 12 | var _utils = require("../utils"); 13 | 14 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 15 | 16 | function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } 17 | 18 | function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } 19 | 20 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 21 | 22 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } 23 | 24 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } 25 | 26 | function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } 27 | 28 | function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } 29 | 30 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } 31 | 32 | function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } 33 | 34 | function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } 35 | 36 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 37 | 38 | // from https://github.com/SamHerbert/SVG-Loaders 39 | var TailSpin = 40 | /*#__PURE__*/ 41 | function (_Component) { 42 | _inherits(TailSpin, _Component); 43 | 44 | function TailSpin(props) { 45 | var _this; 46 | 47 | _classCallCheck(this, TailSpin); 48 | 49 | _this = _possibleConstructorReturn(this, _getPrototypeOf(TailSpin).call(this, props)); 50 | 51 | _defineProperty(_assertThisInitialized(_assertThisInitialized(_this)), "attach", function (svg) { 52 | var color = _this.state.color; 53 | var newColor = (0, _utils.findCorrectColor)(svg); 54 | 55 | if (color !== newColor) { 56 | _this.setState({ 57 | color: newColor 58 | }); 59 | } 60 | }); 61 | 62 | _this.state = { 63 | color: _utils.INITIAL_COLOR 64 | }; 65 | return _this; 66 | } 67 | 68 | _createClass(TailSpin, [{ 69 | key: "render", 70 | value: function render() { 71 | var color = this.state.color; 72 | var _this$props = this.props, 73 | style = _this$props.style, 74 | className = _this$props.className; 75 | return _react.default.createElement("svg", { 76 | ref: this.attach, 77 | width: "38", 78 | height: "38", 79 | viewBox: "0 0 38 38", 80 | xmlns: "http://www.w3.org/2000/svg", 81 | style: style, 82 | className: className 83 | }, _react.default.createElement("defs", null, _react.default.createElement("linearGradient", { 84 | x1: "8.042%", 85 | y1: "0%", 86 | x2: "65.682%", 87 | y2: "23.865%", 88 | id: "a" 89 | }, _react.default.createElement("stop", { 90 | stopColor: color, 91 | stopOpacity: ".2", 92 | offset: "0%" 93 | }), _react.default.createElement("stop", { 94 | stopColor: color, 95 | stopOpacity: ".631", 96 | offset: "63.146%" 97 | }), _react.default.createElement("stop", { 98 | stopColor: color, 99 | stopOpacity: ".8", 100 | offset: "100%" 101 | }))), _react.default.createElement("g", { 102 | fill: "none", 103 | fillRule: "evenodd" 104 | }, _react.default.createElement("g", { 105 | transform: "translate(1 1)" 106 | }, _react.default.createElement("path", { 107 | d: "M36 18c0-9.94-8.06-18-18-18", 108 | id: "Oval-2", 109 | stroke: "url(#a)", 110 | strokeWidth: "2" 111 | }, _react.default.createElement("animateTransform", { 112 | attributeName: "transform", 113 | type: "rotate", 114 | from: "0 18 18", 115 | to: "360 18 18", 116 | dur: "0.9s", 117 | repeatCount: "indefinite" 118 | })), _react.default.createElement("circle", { 119 | fill: color, 120 | cx: "36", 121 | cy: "18", 122 | r: "1" 123 | }, _react.default.createElement("animateTransform", { 124 | attributeName: "transform", 125 | type: "rotate", 126 | from: "0 18 18", 127 | to: "360 18 18", 128 | dur: "0.9s", 129 | repeatCount: "indefinite" 130 | }))))); 131 | } 132 | }]); 133 | 134 | return TailSpin; 135 | }(_react.Component); 136 | 137 | TailSpin.propTypes = { 138 | className: _propTypes.default.string, 139 | style: _propTypes.default.object 140 | }; 141 | TailSpin.defaultProps = { 142 | className: '', 143 | style: undefined 144 | }; 145 | var _default = TailSpin; 146 | exports.default = _default; -------------------------------------------------------------------------------- /packages/full/build/TailSpin/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | Object.defineProperty(exports, "default", { 7 | enumerable: true, 8 | get: function get() { 9 | return _TailSpin.default; 10 | } 11 | }); 12 | 13 | var _TailSpin = _interopRequireDefault(require("./TailSpin")); 14 | 15 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } -------------------------------------------------------------------------------- /packages/full/build/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = void 0; 7 | 8 | var _core = _interopRequireDefault(require("@hoc-react-loader/core")); 9 | 10 | var _TailSpin = _interopRequireDefault(require("./TailSpin")); 11 | 12 | var _ErrorCross = _interopRequireDefault(require("./ErrorCross")); 13 | 14 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 15 | 16 | function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; } 17 | 18 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 19 | 20 | function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } 21 | 22 | function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } 23 | 24 | var _default = function _default() { 25 | var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}, 26 | _ref$LoadingIndicator = _ref.LoadingIndicator, 27 | LoadingIndicator = _ref$LoadingIndicator === void 0 ? _TailSpin.default : _ref$LoadingIndicator, 28 | _ref$ErrorIndicator = _ref.ErrorIndicator, 29 | ErrorIndicator = _ref$ErrorIndicator === void 0 ? _ErrorCross.default : _ref$ErrorIndicator, 30 | rest = _objectWithoutProperties(_ref, ["LoadingIndicator", "ErrorIndicator"]); 31 | 32 | return (0, _core.default)(_objectSpread({}, rest, { 33 | LoadingIndicator: LoadingIndicator, 34 | ErrorIndicator: ErrorIndicator 35 | })); 36 | }; 37 | 38 | exports.default = _default; -------------------------------------------------------------------------------- /packages/full/build/utils/colorUtil.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.findCorrectColor = exports.INITIAL_COLOR = void 0; 7 | 8 | var _tinycolor = _interopRequireDefault(require("tinycolor2")); 9 | 10 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 11 | 12 | /* global window */ 13 | var getBackgroundColor = function getBackgroundColor(node) { 14 | return window.getComputedStyle(node, null).getPropertyValue('background-color'); 15 | }; 16 | 17 | var INITIAL_COLOR = '#cecece'; 18 | exports.INITIAL_COLOR = INITIAL_COLOR; 19 | 20 | var findCorrectColor = function findCorrectColor(svg) { 21 | var parent = svg && svg.parentNode; 22 | var parentColor = parent ? getBackgroundColor(parent) : undefined; 23 | 24 | while (parent && !parentColor) { 25 | parent = parent.parentNode; 26 | if (parent) parentColor = getBackgroundColor(parent); 27 | } 28 | 29 | if (parentColor) { 30 | var tinyC = (0, _tinycolor.default)(parentColor); 31 | var color = tinyC.isDark() ? tinyC.lighten(20) : tinyC.darken(20); 32 | return color.toHexString(); 33 | } 34 | 35 | return INITIAL_COLOR; 36 | }; 37 | 38 | exports.findCorrectColor = findCorrectColor; -------------------------------------------------------------------------------- /packages/full/build/utils/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | 7 | var _colorUtil = require("./colorUtil"); 8 | 9 | Object.keys(_colorUtil).forEach(function (key) { 10 | if (key === "default" || key === "__esModule") return; 11 | Object.defineProperty(exports, key, { 12 | enumerable: true, 13 | get: function get() { 14 | return _colorUtil[key]; 15 | } 16 | }); 17 | }); -------------------------------------------------------------------------------- /packages/full/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hoc-react-loader/full", 3 | "version": "7.0.0", 4 | "description": "Higher order component to call a load function from props at mount (with default components)", 5 | "main": "build/index.js", 6 | "repository": "https://github.com/unirakun/hoc-react-loader", 7 | "author": "Fabien JUIF ", 8 | "contributors": [ 9 | "Yvonnick FRIN ", 10 | "Yoann Prot ", 11 | "Benjamin CAVY " 12 | ], 13 | "keywords": [ 14 | "react", 15 | "loader", 16 | "hoc", 17 | "placeholder" 18 | ], 19 | "license": "MIT", 20 | "private": false, 21 | "peerDependencies": { 22 | "prop-types": "^15.x", 23 | "react": "^15.x || ^16.x", 24 | "tinycolor2": "^1.x" 25 | }, 26 | "scripts": { 27 | "build": "babel --ignore \"**/*.spec.js\" ./src/ --out-dir build" 28 | }, 29 | "dependencies": { 30 | "@hoc-react-loader/core": "^7.0.0" 31 | }, 32 | "devDependencies": { 33 | "@babel/cli": "^7.1.2", 34 | "@babel/core": "^7.1.2", 35 | "@babel/plugin-proposal-class-properties": "^7.1.0", 36 | "@babel/preset-env": "^7.1.0", 37 | "@babel/preset-react": "^7.0.0", 38 | "blanket": "^1.2.3", 39 | "enzyme": "^3.7.0", 40 | "prop-types": "^15.6.2", 41 | "react": "^16.5.2", 42 | "tinycolor2": "^1.4.1" 43 | }, 44 | "babel": { 45 | "presets": [ 46 | "@babel/preset-env", 47 | "@babel/preset-react" 48 | ], 49 | "plugins": [ 50 | "@babel/plugin-proposal-class-properties" 51 | ] 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /packages/full/src/ErrorCross/ErrorCross.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import PropTypes from 'prop-types' 3 | import { findCorrectColor, INITIAL_COLOR } from '../utils' 4 | 5 | class Cross extends Component { 6 | constructor(props) { 7 | super(props) 8 | 9 | this.state = { 10 | color: INITIAL_COLOR, 11 | } 12 | } 13 | 14 | attach = (div) => { 15 | const newColor = findCorrectColor(div) 16 | const { color } = this.state 17 | 18 | if (color !== newColor) { 19 | this.setState({ color: newColor }) 20 | } 21 | } 22 | 23 | render() { 24 | const { color } = this.state 25 | const { style, className, message } = this.props 26 | 27 | return ( 28 |
32 | 40 | 47 | 48 |
49 | ) 50 | } 51 | } 52 | 53 | const { string, object } = PropTypes 54 | Cross.propTypes = { 55 | message: string, 56 | className: string, 57 | style: object, 58 | } 59 | 60 | Cross.defaultProps = { 61 | message: 'An error occured', 62 | className: '', 63 | style: {}, 64 | } 65 | 66 | export default Cross 67 | -------------------------------------------------------------------------------- /packages/full/src/ErrorCross/ErrorCross.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-env jest */ 2 | 3 | /* eslint-disable 4 | react/jsx-filename-extension, 5 | import/no-extraneous-dependencies 6 | */ 7 | 8 | import React from 'react' 9 | import { mount } from 'enzyme' 10 | import blanket from 'blanket' // eslint-disable-line 11 | import ErrorCross from './ErrorCross' 12 | 13 | const mountWithProps = (props = {}) => mount() 14 | 15 | const testColor = (component, color) => { 16 | expect(component.find('path').prop('stroke')).toEqual(color) 17 | } 18 | 19 | describe('ErrorCross', () => { 20 | it('should have error message in title', () => { 21 | const errorMessage = 'fake-message' 22 | const mounted = mountWithProps({ message: errorMessage }) 23 | const div = mounted.find('div') 24 | 25 | expect(div.prop('title')).toEqual(errorMessage) 26 | }) 27 | 28 | it('should provide a default message', () => { 29 | const mounted = mountWithProps() 30 | const div = mounted.find('div') 31 | 32 | expect(div.prop('title').length).toBeGreaterThan(0) 33 | }) 34 | 35 | it('should use a default color when no background-color is found', () => { 36 | const mounted = mount() 37 | testColor(mounted, '#cecece') 38 | }) 39 | 40 | it('should use the first parent color when accessible (darken)', () => { 41 | const mounted = mount( 42 |
43 | 44 |
, 45 | ) 46 | 47 | testColor(mounted, '#ffe17f') 48 | }) 49 | 50 | it('should go to the first parent with a background color', () => { 51 | const mounted = mount( 52 |
53 |
54 | 55 |
56 |
, 57 | ) 58 | 59 | testColor(mounted, '#76692f') 60 | }) 61 | 62 | it('shouldnt print a warning', () => { 63 | mount( 64 |
65 | 66 |
, 67 | ) 68 | }) 69 | }) 70 | -------------------------------------------------------------------------------- /packages/full/src/ErrorCross/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './ErrorCross' 2 | -------------------------------------------------------------------------------- /packages/full/src/TailSpin/TailSpin.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import PropTypes from 'prop-types' 3 | import { findCorrectColor, INITIAL_COLOR } from '../utils' 4 | 5 | // from https://github.com/SamHerbert/SVG-Loaders 6 | class TailSpin extends Component { 7 | constructor(props) { 8 | super(props) 9 | 10 | this.state = { 11 | color: INITIAL_COLOR, 12 | } 13 | } 14 | 15 | attach = (svg) => { 16 | const { color } = this.state 17 | const newColor = findCorrectColor(svg) 18 | if (color !== newColor) { 19 | this.setState({ color: newColor }) 20 | } 21 | } 22 | 23 | render() { 24 | const { color } = this.state 25 | const { style, className } = this.props 26 | 27 | return ( 28 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 55 | 56 | 57 | 65 | 66 | 67 | 68 | 69 | ) 70 | } 71 | } 72 | 73 | TailSpin.propTypes = { 74 | className: PropTypes.string, 75 | style: PropTypes.object, 76 | } 77 | 78 | TailSpin.defaultProps = { 79 | className: '', 80 | style: undefined, 81 | } 82 | 83 | export default TailSpin 84 | -------------------------------------------------------------------------------- /packages/full/src/TailSpin/TailSpin.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-env jest */ 2 | 3 | /* eslint-disable 4 | react/jsx-filename-extension, 5 | import/no-extraneous-dependencies 6 | */ 7 | 8 | import React from 'react' 9 | import { mount } from 'enzyme' 10 | import TailSpin from './index' 11 | 12 | const testColor = (component, color) => { 13 | expect(component.find('circle').props().fill).toEqual(color) 14 | } 15 | 16 | describe('TailSpin', () => { 17 | it('should use a default color when no background-color is found', () => { 18 | const mounted = mount() 19 | testColor(mounted, '#cecece') 20 | }) 21 | 22 | it('should use the first parent color when accessible (darken)', () => { 23 | const mounted = mount( 24 |
25 | 26 |
, 27 | ) 28 | 29 | testColor(mounted, '#ffe17f') 30 | }) 31 | 32 | it('should use the first parent color when accessible (ligthen)', () => { 33 | const mounted = mount( 34 |
35 | 36 |
, 37 | ) 38 | 39 | testColor(mounted, '#76692f') 40 | }) 41 | 42 | it('should go to the first parent with a background color', () => { 43 | const mounted = mount( 44 |
45 |
46 | 47 |
48 |
, 49 | ) 50 | 51 | testColor(mounted, '#76692f') 52 | }) 53 | 54 | it('shouldnt print a warning', () => { 55 | mount( 56 |
57 | 58 |
, 59 | ) 60 | }) 61 | }) 62 | -------------------------------------------------------------------------------- /packages/full/src/TailSpin/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './TailSpin' 2 | -------------------------------------------------------------------------------- /packages/full/src/index.js: -------------------------------------------------------------------------------- 1 | import core from '@hoc-react-loader/core' 2 | import TailSpin from './TailSpin' 3 | import ErrorCross from './ErrorCross' 4 | 5 | export default ( 6 | { 7 | LoadingIndicator = TailSpin, 8 | ErrorIndicator = ErrorCross, 9 | ...rest 10 | } = {}, 11 | ) => core({ ...rest, LoadingIndicator, ErrorIndicator }) 12 | -------------------------------------------------------------------------------- /packages/full/src/index.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-env jest */ 2 | /* eslint-disable 3 | react/jsx-filename-extension, 4 | */ 5 | import React from 'react' 6 | import { mount } from 'enzyme' 7 | import loader from './index' 8 | import ErrorCross from './ErrorCross' 9 | import TailSpin from './TailSpin' 10 | 11 | const Component = () =>
Component
12 | 13 | describe('@hoc-react-loader/full', () => { 14 | it('should print TailSpin (default LoadingIndicator)', () => { 15 | const Wrapped = loader()(Component) 16 | 17 | const mounted = mount() 18 | expect(mounted.find(TailSpin)).toHaveLength(1) 19 | expect(mounted.find(ErrorCross)).toHaveLength(0) 20 | expect(mounted.find(Component)).toHaveLength(0) 21 | }) 22 | 23 | it('should print ErrorCross (default ErrorIndicator)', () => { 24 | const Wrapped = loader()(Component) 25 | 26 | const mounted = mount() 27 | expect(mounted.find(TailSpin)).toHaveLength(0) 28 | expect(mounted.find(ErrorCross)).toHaveLength(1) 29 | expect(mounted.find(Component)).toHaveLength(0) 30 | }) 31 | 32 | it('should print the Component wrapped', () => { 33 | const Wrapped = loader()(Component) 34 | 35 | const mounted = mount() 36 | expect(mounted.find(TailSpin)).toHaveLength(0) 37 | expect(mounted.find(ErrorCross)).toHaveLength(0) 38 | expect(mounted.find(Component)).toHaveLength(1) 39 | }) 40 | }) 41 | -------------------------------------------------------------------------------- /packages/full/src/utils/colorUtil.js: -------------------------------------------------------------------------------- 1 | import tinycolor from 'tinycolor2' 2 | 3 | /* global window */ 4 | const getBackgroundColor = node => window.getComputedStyle(node, null).getPropertyValue('background-color') 5 | 6 | export const INITIAL_COLOR = '#cecece' 7 | 8 | export const findCorrectColor = (svg) => { 9 | let parent = svg && svg.parentNode 10 | let parentColor = parent ? getBackgroundColor(parent) : undefined 11 | 12 | while (parent && !parentColor) { 13 | parent = parent.parentNode 14 | if (parent) parentColor = getBackgroundColor(parent) 15 | } 16 | 17 | if (parentColor) { 18 | const tinyC = tinycolor(parentColor) 19 | const color = tinyC.isDark() ? tinyC.lighten(20) : tinyC.darken(20) 20 | 21 | return color.toHexString() 22 | } 23 | 24 | return INITIAL_COLOR 25 | } 26 | -------------------------------------------------------------------------------- /packages/full/src/utils/index.js: -------------------------------------------------------------------------------- 1 | export * from './colorUtil' 2 | --------------------------------------------------------------------------------