├── .babelrc ├── .eslintrc ├── .gitignore ├── LICENCE ├── README.md ├── package-lock.json ├── package.json ├── public ├── dist │ ├── bundle.css │ └── bundle.js └── index.html ├── src ├── App.js ├── actions │ └── weatherStation.js ├── components │ ├── Dashboard.js │ ├── DetailedInfo.js │ ├── ForecastTiles.js │ └── WeatherForecast.js ├── constants │ ├── ActionTypes.js │ └── generalConstants.js ├── index.js ├── reducers │ ├── index.js │ └── weatherStation.js ├── store.js ├── styles │ └── main.scss └── test │ ├── __tests__ │ ├── dashboard.js │ ├── data │ │ └── forecast.json │ ├── forecastTiles.js │ ├── testAsyncActions.js │ ├── testReducers.js │ └── weatherForecast.js │ └── jestSetup.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["env", {"modules": false}], "react", "es2015", "stage-0"], 3 | "plugins": ["transform-class-properties", "transform-decorators-legacy"], 4 | "env": { 5 | "test": { 6 | "presets": [["env"], "react"], 7 | "plugins": ["transform-class-properties", "transform-decorators-legacy"] 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": false, 4 | "commonjs": true, 5 | "es6": true 6 | }, 7 | "parserOptions": { 8 | "ecmaFeatures": { 9 | "experimentalObjectRestSpread": true, 10 | "jsx": true 11 | }, 12 | "sourceType": "module" 13 | }, 14 | "plugins": [ 15 | "react" 16 | ], 17 | "rules": { 18 | "react/jsx-indent": [ 19 | "error", 20 | 2 21 | ], 22 | "linebreak-style": [ 23 | "error", 24 | "unix" 25 | ], 26 | "quotes": [ 27 | "error", 28 | "double" 29 | ], 30 | "semi": [ 31 | "warn", 32 | "always" 33 | ], 34 | "no-console": 0 35 | } 36 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | public -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Santhosh Sundar 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![logo](https://image.ibb.co/g69ZDx/682111_cloud_512x512.png) 2 | 3 | # 5-Day Weather Forecast 4 | A simple application to display 5-day weather forecast using the OpenWeatherMap API. Built on top of my recently created [React-Redux-Sass Starter Kit](https://github.com/Gigacore/React-Redux-Sass-Starter). 5 | 6 | ### Demo 7 | https://www.gigacore.in/demos/react-weather-forecast/ 8 | 9 | ## Pre-requisites 10 | * Node.js 9.8.0 and above 11 | 12 | ## Run 13 | ``` 14 | git clone 15 | cd react-weather-forecast 16 | npm i 17 | ``` 18 | 19 | 20 | ## Start the dev server 21 | ``` 22 | 23 | npm run start:dev 24 | 25 | ``` 26 | 27 | ## Build 28 | ``` 29 | 30 | npm run build 31 | 32 | ``` 33 | 34 | #### Notes: 35 | * Running the build bundles all your updates to ```bundle.js``` and ```bundle.css``` in dist folder. 36 | * If you insist to automate the build upon appending changes to files, use ```webpack --watch``` 37 | 38 | ## Test 39 | ``` 40 | 41 | npm run test 42 | 43 | ``` 44 | 45 | #### Notes: 46 | * Unit testing can be done manually by executing the above command. 47 | * It will be done automatically prior committing the updates to the repo as a pre-commit hook. 48 | 49 | ### TODOs 50 | - [x] Provide an option for user to choose location of their choice by Name, Lat/Long etc 51 | - [x] Unit testing 52 | - [x] Identify and address edgecases. 53 | - [x] Revisit the code to improve performance. Such as sorting, looping, searching etc. 54 | - [x] Use a proper loading spinner icon on page load 55 | - [x] Detect location automatically 56 | - [x] Display hourly forecasts. 57 | - [ ] Add an option to choose Units in either Metric or Imperial. 58 | - [ ] Display higher-level of weather information such as Wind Speed, Precipitation etc 59 | - [ ] Fix lint issues and config the eslintrc to support "no-vars-used" for Imports 60 | - [ ] Better and more functional UI 61 | - [ ] Prevent fetching new data on every refresh by caching the data for a set duration of session. 62 | 63 | ### Tech Stack 64 | 65 | * React.js 66 | * Redux 67 | * JavaScript (ES6) 68 | * HTML5 69 | * SASS 70 | * Jest + Enzyme 71 | 72 | #### The MIT License (MIT) 73 | MIT © 2018 Santhosh Sundar 74 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "weather-forecast", 3 | "version": "1.0.0", 4 | "description": "webpack init", 5 | "watch": true, 6 | "dependencies": { 7 | "react": "^16.2.0", 8 | "react-dom": "^16.2.0", 9 | "react-redux": "^5.0.7", 10 | "redux": "^3.7.2" 11 | }, 12 | "devDependencies": { 13 | "axios": "^0.17.1", 14 | "babel-cli": "^6.26.0", 15 | "babel-core": "^6.26.0", 16 | "babel-jest": "^22.4.1", 17 | "babel-loader": "^7.1.4", 18 | "babel-plugin-transform-class-properties": "^6.24.1", 19 | "babel-plugin-transform-decorators": "^6.24.1", 20 | "babel-plugin-transform-decorators-legacy": "^1.3.4", 21 | "babel-polyfill": "^6.26.0", 22 | "babel-preset-env": "^1.6.1", 23 | "babel-preset-es2015": "^6.24.1", 24 | "babel-preset-react": "^6.24.1", 25 | "babel-preset-stage-0": "^6.24.1", 26 | "css-loader": "^0.28.10", 27 | "enzyme": "^3.3.0", 28 | "enzyme-adapter-react-16": "^1.1.1", 29 | "enzyme-to-json": "^3.3.1", 30 | "es6-promise": "^4.2.4", 31 | "eslint": "^4.18.2", 32 | "eslint-loader": "^1.9.0", 33 | "eslint-plugin-react": "^7.7.0", 34 | "extract-text-webpack-plugin": "2.1.2", 35 | "fetch-mock": "^6.1.0", 36 | "file-loader": "^0.11.2", 37 | "isomorphic-fetch": "^2.2.1", 38 | "jest": "^22.4.2", 39 | "jest-enzyme": "^4.2.0", 40 | "lodash": "^4.17.5", 41 | "node-sass": "^4.8.2", 42 | "pre-commit": "^1.2.2", 43 | "react-test-renderer": "^16.2.0", 44 | "redux-logger": "^3.0.6", 45 | "redux-mock-store": "^1.5.1", 46 | "redux-thunk": "^2.2.0", 47 | "regenerator-runtime": "^0.11.1", 48 | "sass-loader": "^6.0.7", 49 | "uglifyjs-webpack-plugin": "^1.2.3", 50 | "webpack": "^3.11.0", 51 | "webpack-dev-server": "^2.11.2" 52 | }, 53 | "scripts": { 54 | "start:dev": "webpack-dev-server", 55 | "build": "webpack", 56 | "watch": "webpack --watch", 57 | "lint": "./node_modules/.bin/eslint ./src", 58 | "test": "jest" 59 | }, 60 | "jest": { 61 | "setupFiles": [ 62 | "./src/test/jestSetup.js" 63 | ], 64 | "snapshotSerializers": [ 65 | "enzyme-to-json/serializer" 66 | ], 67 | "setupTestFrameworkScriptFile": "./node_modules/jest-enzyme/lib/index.js", 68 | "unmockedModulePathPatterns": [ 69 | "react", 70 | "enzyme", 71 | "jest-enzyme" 72 | ] 73 | }, 74 | "author": "Santhosh Sundar", 75 | "license": "MIT" 76 | } 77 | -------------------------------------------------------------------------------- /public/dist/bundle.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | html, body, div, span, applet, object, iframe, 6 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 7 | a, abbr, acronym, address, big, cite, code, 8 | del, dfn, em, img, ins, kbd, q, s, samp, 9 | small, strike, strong, sub, sup, tt, var, 10 | b, u, i, center, 11 | dl, dt, dd, ol, ul, li, 12 | fieldset, form, label, legend, 13 | table, caption, tbody, tfoot, thead, tr, th, td, 14 | article, aside, canvas, details, embed, 15 | figure, figcaption, footer, header, hgroup, 16 | menu, nav, output, ruby, section, summary, 17 | time, mark, audio, video { 18 | margin: 0; 19 | padding: 0; 20 | border: 0; 21 | font-size: 100%; 22 | font: inherit; 23 | vertical-align: baseline; } 24 | 25 | /* HTML5 display-role reset for older browsers */ 26 | article, aside, details, figcaption, figure, 27 | footer, header, hgroup, menu, nav, section { 28 | display: block; } 29 | 30 | body { 31 | line-height: 1; } 32 | 33 | ol, ul { 34 | list-style: none; } 35 | 36 | blockquote, q { 37 | quotes: none; } 38 | 39 | blockquote:before, blockquote:after, 40 | q:before, q:after { 41 | content: ''; 42 | content: none; } 43 | 44 | table { 45 | border-collapse: collapse; 46 | border-spacing: 0; } 47 | 48 | hr { 49 | border: 2px solid #666; } 50 | 51 | body { 52 | background: #3f51b5; 53 | color: #fff; 54 | font-family: 'Roboto', sans-serif; } 55 | 56 | .loading { 57 | height: 100%; 58 | width: 100%; 59 | position: absolute; 60 | display: flex; 61 | align-items: center; 62 | justify-content: center; } 63 | .loading .spinner { 64 | background: url("https://www.gigacore.in/demos/react-weather-forecast/assets/loader.gif") no-repeat; 65 | background-size: 100%; 66 | height: 100px; 67 | width: 100px; } 68 | 69 | .weather-forecast-wrapper { 70 | margin: 0 auto; 71 | width: 40%; } 72 | @media only screen and (max-width: 64em) { 73 | .weather-forecast-wrapper { 74 | width: 60%; } } 75 | @media only screen and (max-width: 48em) { 76 | .weather-forecast-wrapper { 77 | width: 100%; } } 78 | .weather-forecast-wrapper header { 79 | text-align: center; } 80 | .weather-forecast-wrapper header .heading { 81 | font-size: 22px; 82 | padding: 25px 0 0 0; 83 | text-transform: uppercase; } 84 | .weather-forecast-wrapper .weather-dashboard { 85 | text-align: center; } 86 | .weather-forecast-wrapper .weather-dashboard .error { 87 | display: none; } 88 | .weather-forecast-wrapper .weather-dashboard section.controls { 89 | display: flex; 90 | align-items: center; 91 | justify-content: center; 92 | height: 100px; } 93 | .weather-forecast-wrapper .weather-dashboard section.controls input { 94 | font-size: 13px; 95 | padding: 10px; 96 | display: inline-block; 97 | background: #283593; 98 | border: none; 99 | color: #fff; 100 | width: 230px; 101 | text-transform: uppercase; } 102 | .weather-forecast-wrapper .weather-dashboard section.controls input::placeholder { 103 | color: #fff; } 104 | .weather-forecast-wrapper .weather-dashboard section.controls input:focus::placeholder { 105 | color: transparent; } 106 | .weather-forecast-wrapper .weather-dashboard section.controls input.search { 107 | width: 30px; 108 | cursor: pointer; } 109 | .weather-forecast-wrapper .weather-dashboard.invalid-city section.controls input { 110 | background: #ff1744; } 111 | .weather-forecast-wrapper .weather-dashboard.invalid-city .error { 112 | display: block; 113 | color: #ff1744; 114 | padding: 0 0 30px 0; } 115 | .weather-forecast-wrapper .forecast-tiles { 116 | width: 100%; } 117 | .weather-forecast-wrapper .forecast-tiles .forecast-tile { 118 | min-height: 100px; 119 | background: #303f9f; 120 | margin: 3px; 121 | cursor: pointer; 122 | overflow: hidden; 123 | position: relative; } 124 | .weather-forecast-wrapper .forecast-tiles .forecast-tile :after { 125 | content: ""; 126 | width: 0; 127 | height: 0; 128 | position: absolute; 129 | right: 10px; 130 | top: 48px; 131 | border-left: 5px solid transparent; 132 | border-right: 5px solid transparent; 133 | border-top: 5px solid #fff; } 134 | .weather-forecast-wrapper .forecast-tiles .forecast-tile .icon { 135 | width: 100px; 136 | height: 100px; 137 | display: flex; 138 | justify-content: center; 139 | align-items: center; 140 | background: #283593; 141 | flex-direction: column; 142 | text-transform: uppercase; 143 | font-size: 12px; } 144 | .weather-forecast-wrapper .forecast-tiles .forecast-tile .weather-info { 145 | padding: 0 0 0 15px; 146 | display: flex; 147 | align-items: left; 148 | flex-direction: column; 149 | justify-content: center; } 150 | .weather-forecast-wrapper .forecast-tiles .forecast-tile .weather-info .min-max { 151 | padding: 10px 10px; 152 | font-size: 20px; 153 | color: #c7cef5; } 154 | .weather-forecast-wrapper .forecast-tiles .forecast-tile .weather-info .min-max strong { 155 | font-weight: bold; 156 | color: #fff; } 157 | .weather-forecast-wrapper .forecast-tiles .forecast-tile .weather-info .more-info { 158 | padding: 0 10px; 159 | font-size: 12px; } 160 | .weather-forecast-wrapper .forecast-tiles .forecast-tile .primary-info { 161 | display: flex; } 162 | .weather-forecast-wrapper .forecast-tiles .forecast-tile.collapsed .detailed-info { 163 | height: 0; } 164 | .weather-forecast-wrapper .forecast-tiles .forecast-tile.expanded .detailed-info { 165 | height: 100px; } 166 | .weather-forecast-wrapper .forecast-tiles .forecast-tile.expanded :after { 167 | border-left: 5px solid transparent; 168 | border-right: 5px solid transparent; 169 | border-bottom: 5px solid #fff; 170 | border-top: none; } 171 | .weather-forecast-wrapper .forecast-tiles .forecast-tile .detailed-info { 172 | width: 100%; 173 | height: 0; 174 | -webkit-transition: height 0.20s ease-in; 175 | -moz-transition: height 0.20s ease-in; 176 | -o-transition: height 0.20s ease-in; 177 | transition: height 0.20s ease-in; 178 | background: #283593; } 179 | .weather-forecast-wrapper .forecast-tiles .forecast-tile .detailed-info .hourly { 180 | display: flex; 181 | padding: 25px 0 20px 10px; } 182 | .weather-forecast-wrapper .forecast-tiles .forecast-tile .detailed-info .hourly .hourly-info { 183 | display: flex; 184 | flex-direction: column; 185 | width: 20%; 186 | align-items: center; 187 | justify-content: center; 188 | overflow-x: scroll; } 189 | .weather-forecast-wrapper .forecast-tiles .forecast-tile .detailed-info .hourly .hourly-info .hour-temperature { 190 | font-size: 16px; 191 | font-weight: bold; } 192 | .weather-forecast-wrapper .forecast-tiles .forecast-tile .detailed-info .hourly .hourly-info .hour-of-the-day { 193 | font-weight: normal; 194 | font-size: 12px; 195 | color: #c5cae9; } 196 | .weather-forecast-wrapper .forecast-tiles .forecast-tile .detailed-info .hourly .hourly-info div { 197 | padding: 5px 0; } 198 | 199 | .fork { 200 | margin: 30px; 201 | text-align: center; 202 | font-size: 12px; 203 | text-transform: uppercase; } 204 | .fork a { 205 | color: #fff; 206 | text-decoration: none; 207 | text-align: center; } 208 | -------------------------------------------------------------------------------- /public/dist/bundle.js: -------------------------------------------------------------------------------- 1 | !function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var n={};t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=28)}([function(e,t,n){"use strict";function r(e){return"[object Array]"===k.call(e)}function o(e){return"[object ArrayBuffer]"===k.call(e)}function a(e){return"undefined"!=typeof FormData&&e instanceof FormData}function i(e){return"undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&e.buffer instanceof ArrayBuffer}function u(e){return"string"==typeof e}function l(e){return"number"==typeof e}function c(e){return void 0===e}function s(e){return null!==e&&"object"==typeof e}function f(e){return"[object Date]"===k.call(e)}function p(e){return"[object File]"===k.call(e)}function d(e){return"[object Blob]"===k.call(e)}function h(e){return"[object Function]"===k.call(e)}function m(e){return s(e)&&h(e.pipe)}function y(e){return"undefined"!=typeof URLSearchParams&&e instanceof URLSearchParams}function v(e){return e.replace(/^\s*/,"").replace(/\s*$/,"")}function g(){return("undefined"==typeof navigator||"ReactNative"!==navigator.product)&&("undefined"!=typeof window&&"undefined"!=typeof document)}function b(e,t){if(null!==e&&void 0!==e)if("object"!=typeof e&&(e=[e]),r(e))for(var n=0,o=e.length;n=200&&e<300}};u.headers={common:{Accept:"application/json, text/plain, */*"}},o.forEach(["delete","get","head"],function(e){u.headers[e]={}}),o.forEach(["post","put","patch"],function(e){u.headers[e]=o.merge(i)}),e.exports=u}).call(t,n(85))},function(e,t,n){"use strict";function r(e){if(null===e||void 0===e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}/* 2 | object-assign 3 | (c) Sindre Sorhus 4 | @license MIT 5 | */ 6 | var o=Object.getOwnPropertySymbols,a=Object.prototype.hasOwnProperty,i=Object.prototype.propertyIsEnumerable;e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map(function(e){return t[e]}).join(""))return!1;var r={};return"abcdefghijklmnopqrst".split("").forEach(function(e){r[e]=e}),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},r)).join("")}catch(e){return!1}}()?Object.assign:function(e,t){for(var n,u,l=r(e),c=1;c=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}function u(){}function l(e,t){var n={run:function(r){try{var o=e(t.getState(),r);(o!==n.props||n.error)&&(n.shouldComponentUpdate=!0,n.props=o,n.error=null)}catch(e){n.shouldComponentUpdate=!0,n.error=e}}};return n}function c(e){var t,n,c=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},s=c.getDisplayName,p=void 0===s?function(e){return"ConnectAdvanced("+e+")"}:s,w=c.methodName,C=void 0===w?"connectAdvanced":w,E=c.renderCountProp,x=void 0===E?void 0:E,k=c.shouldHandleStateChanges,T=void 0===k||k,S=c.storeKey,O=void 0===S?"store":S,P=c.withRef,_=void 0!==P&&P,N=i(c,["getDisplayName","methodName","renderCountProp","shouldHandleStateChanges","storeKey","withRef"]),j=O+"Subscription",D=g++,I=(t={},t[O]=y.a,t[j]=y.b,t),A=(n={},n[j]=y.b,n);return function(t){d()("function"==typeof t,"You must pass a component to the function returned by "+C+". Instead received "+JSON.stringify(t));var n=t.displayName||t.name||"Component",i=p(n),c=v({},N,{getDisplayName:p,methodName:C,renderCountProp:x,shouldHandleStateChanges:T,storeKey:O,withRef:_,displayName:i,wrappedComponentName:n,WrappedComponent:t}),s=function(n){function s(e,t){r(this,s);var a=o(this,n.call(this,e,t));return a.version=D,a.state={},a.renderCount=0,a.store=e[O]||t[O],a.propsMode=Boolean(e[O]),a.setWrappedInstance=a.setWrappedInstance.bind(a),d()(a.store,'Could not find "'+O+'" in either the context or props of "'+i+'". Either wrap the root component in a , or explicitly pass "'+O+'" as a prop to "'+i+'".'),a.initSelector(),a.initSubscription(),a}return a(s,n),s.prototype.getChildContext=function(){var e,t=this.propsMode?null:this.subscription;return e={},e[j]=t||this.context[j],e},s.prototype.componentDidMount=function(){T&&(this.subscription.trySubscribe(),this.selector.run(this.props),this.selector.shouldComponentUpdate&&this.forceUpdate())},s.prototype.componentWillReceiveProps=function(e){this.selector.run(e)},s.prototype.shouldComponentUpdate=function(){return this.selector.shouldComponentUpdate},s.prototype.componentWillUnmount=function(){this.subscription&&this.subscription.tryUnsubscribe(),this.subscription=null,this.notifyNestedSubs=u,this.store=null,this.selector.run=u,this.selector.shouldComponentUpdate=!1},s.prototype.getWrappedInstance=function(){return d()(_,"To access the wrapped instance, you need to specify { withRef: true } in the options argument of the "+C+"() call."),this.wrappedInstance},s.prototype.setWrappedInstance=function(e){this.wrappedInstance=e},s.prototype.initSelector=function(){var t=e(this.store.dispatch,c);this.selector=l(t,this.store),this.selector.run(this.props)},s.prototype.initSubscription=function(){if(T){var e=(this.propsMode?this.props:this.context)[j];this.subscription=new m.a(this.store,e,this.onStateChange.bind(this)),this.notifyNestedSubs=this.subscription.notifyNestedSubs.bind(this.subscription)}},s.prototype.onStateChange=function(){this.selector.run(this.props),this.selector.shouldComponentUpdate?(this.componentDidUpdate=this.notifyNestedSubsOnComponentDidUpdate,this.setState(b)):this.notifyNestedSubs()},s.prototype.notifyNestedSubsOnComponentDidUpdate=function(){this.componentDidUpdate=void 0,this.notifyNestedSubs()},s.prototype.isSubscribed=function(){return Boolean(this.subscription)&&this.subscription.isSubscribed()},s.prototype.addExtraProps=function(e){if(!(_||x||this.propsMode&&this.subscription))return e;var t=v({},e);return _&&(t.ref=this.setWrappedInstance),x&&(t[x]=this.renderCount++),this.propsMode&&this.subscription&&(t[j]=this.subscription),t},s.prototype.render=function(){var e=this.selector;if(e.shouldComponentUpdate=!1,e.error)throw e.error;return Object(h.createElement)(t,this.addExtraProps(e.props))},s}(h.Component);return s.WrappedComponent=t,s.displayName=i,s.childContextTypes=A,s.contextTypes=I,s.propTypes=I,f()(s,t)}}t.a=c;var s=n(45),f=n.n(s),p=n(46),d=n.n(p),h=n(1),m=(n.n(h),n(47)),y=n(11),v=Object.assign||function(e){for(var t=1;tM.length&&M.push(e)}function d(e,t,n,o){var a=typeof e;"undefined"!==a&&"boolean"!==a||(e=null);var i=!1;if(null===e)i=!0;else switch(a){case"string":case"number":i=!0;break;case"object":switch(e.$$typeof){case E:case x:case k:case T:i=!0}}if(i)return n(o,e,""===t?"."+h(e,0):t),1;if(i=0,t=""===t?".":t+":",Array.isArray(e))for(var u=0;uthis.eventPool.length&&this.eventPool.push(e)}function B(e){e.eventPool=[],e.getPooled=H,e.release=z}function V(e,t,n,r){return U.call(this,e,t,n,r)}function q(e,t,n,r){return U.call(this,e,t,n,r)}function K(e,t){switch(e){case"topKeyUp":return-1!==dr.indexOf(t.keyCode);case"topKeyDown":return 229!==t.keyCode;case"topKeyPress":case"topMouseDown":case"topBlur":return!0;default:return!1}}function W(e){return e=e.detail,"object"==typeof e&&"data"in e?e.data:null}function $(e,t){switch(e){case"topCompositionEnd":return W(t);case"topKeyPress":return 32!==t.which?null:(xr=!0,Cr);case"topTextInput":return e=t.data,e===Cr&&xr?null:e;default:return null}}function Q(e,t){if(kr)return"topCompositionEnd"===e||!hr&&K(e,t)?(e=F(),sr._root=null,sr._startText=null,sr._fallbackText=null,kr=!1,e):null;switch(e){case"topPaste":return null;case"topKeyPress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1Br.length&&Br.push(e)}}}function Ie(e,t){var n={};return n[e.toLowerCase()]=t.toLowerCase(),n["Webkit"+e]="webkit"+t,n["Moz"+e]="moz"+t,n["ms"+e]="MS"+t,n["O"+e]="o"+t.toLowerCase(),n}function Ae(e){if($r[e])return $r[e];if(!Wr[e])return e;var t,n=Wr[e];for(t in n)if(n.hasOwnProperty(t)&&t in Qr)return $r[e]=n[t];return""}function Me(e){return Object.prototype.hasOwnProperty.call(e,Jr)||(e[Jr]=Xr++,Yr[e[Jr]]={}),Yr[e[Jr]]}function Re(e){for(;e&&e.firstChild;)e=e.firstChild;return e}function Fe(e,t){var n=Re(e);e=0;for(var r;n;){if(3===n.nodeType){if(r=e+n.textContent.length,e<=t&&r>=t)return{node:n,offset:t-e};e=r}e:{for(;n;){if(n.nextSibling){n=n.nextSibling;break e}n=n.parentNode}n=void 0}n=Re(n)}}function Le(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&("input"===t&&"text"===e.type||"textarea"===t||"true"===e.contentEditable)}function Ue(e,t){if(oo||null==to||to!==kn())return null;var n=to;return"selectionStart"in n&&Le(n)?n={start:n.selectionStart,end:n.selectionEnd}:window.getSelection?(n=window.getSelection(),n={anchorNode:n.anchorNode,anchorOffset:n.anchorOffset,focusNode:n.focusNode,focusOffset:n.focusOffset}):n=void 0,ro&&Tn(ro,n)?null:(ro=n,e=U.getPooled(eo.select,no,e,t),e.type="select",e.target=to,A(e),e)}function He(e,t,n,r){return U.call(this,e,t,n,r)}function ze(e,t,n,r){return U.call(this,e,t,n,r)}function Be(e,t,n,r){return U.call(this,e,t,n,r)}function Ve(e){var t=e.keyCode;return"charCode"in e?0===(e=e.charCode)&&13===t&&(e=13):e=t,32<=e||13===e?e:0}function qe(e,t,n,r){return U.call(this,e,t,n,r)}function Ke(e,t,n,r){return U.call(this,e,t,n,r)}function We(e,t,n,r){return U.call(this,e,t,n,r)}function $e(e,t,n,r){return U.call(this,e,t,n,r)}function Qe(e,t,n,r){return U.call(this,e,t,n,r)}function Ge(e){0>po||(e.current=fo[po],fo[po]=null,po--)}function Ye(e,t){po++,fo[po]=e.current,e.current=t}function Xe(e){return Ze(e)?yo:ho.current}function Je(e,t){var n=e.type.contextTypes;if(!n)return Pn;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var o,a={};for(o in n)a[o]=t[o];return r&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=a),a}function Ze(e){return 2===e.tag&&null!=e.type.childContextTypes}function et(e){Ze(e)&&(Ge(mo,e),Ge(ho,e))}function tt(e,t,n){null!=ho.cursor&&r("168"),Ye(ho,t,e),Ye(mo,n,e)}function nt(e,t){var n=e.stateNode,o=e.type.childContextTypes;if("function"!=typeof n.getChildContext)return t;n=n.getChildContext();for(var a in n)a in o||r("108",Ce(e)||"Unknown",a);return Cn({},t,n)}function rt(e){if(!Ze(e))return!1;var t=e.stateNode;return t=t&&t.__reactInternalMemoizedMergedChildContext||Pn,yo=ho.current,Ye(ho,t,e),Ye(mo,mo.current,e),!0}function ot(e,t){var n=e.stateNode;if(n||r("169"),t){var o=nt(e,yo);n.__reactInternalMemoizedMergedChildContext=o,Ge(mo,e),Ge(ho,e),Ye(ho,o,e)}else Ge(mo,e);Ye(mo,t,e)}function at(e,t,n){this.tag=e,this.key=t,this.stateNode=this.type=null,this.sibling=this.child=this.return=null,this.index=0,this.memoizedState=this.updateQueue=this.memoizedProps=this.pendingProps=this.ref=null,this.internalContextTag=n,this.effectTag=0,this.lastEffect=this.firstEffect=this.nextEffect=null,this.expirationTime=0,this.alternate=null}function it(e,t,n){var r=e.alternate;return null===r?(r=new at(e.tag,e.key,e.internalContextTag),r.type=e.type,r.stateNode=e.stateNode,r.alternate=e,e.alternate=r):(r.effectTag=0,r.nextEffect=null,r.firstEffect=null,r.lastEffect=null),r.expirationTime=n,r.pendingProps=t,r.child=e.child,r.memoizedProps=e.memoizedProps,r.memoizedState=e.memoizedState,r.updateQueue=e.updateQueue,r.sibling=e.sibling,r.index=e.index,r.ref=e.ref,r}function ut(e,t,n){var o=void 0,a=e.type,i=e.key;return"function"==typeof a?(o=a.prototype&&a.prototype.isReactComponent?new at(2,i,t):new at(0,i,t),o.type=a,o.pendingProps=e.props):"string"==typeof a?(o=new at(5,i,t),o.type=a,o.pendingProps=e.props):"object"==typeof a&&null!==a&&"number"==typeof a.tag?(o=a,o.pendingProps=e.props):r("130",null==a?a:typeof a,""),o.expirationTime=n,o}function lt(e,t,n,r){return t=new at(10,r,t),t.pendingProps=e,t.expirationTime=n,t}function ct(e,t,n){return t=new at(6,null,t),t.pendingProps=e,t.expirationTime=n,t}function st(e,t,n){return t=new at(7,e.key,t),t.type=e.handler,t.pendingProps=e,t.expirationTime=n,t}function ft(e,t,n){return e=new at(9,null,t),e.expirationTime=n,e}function pt(e,t,n){return t=new at(4,e.key,t),t.pendingProps=e.children||[],t.expirationTime=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function dt(e){return function(t){try{return e(t)}catch(e){}}}function ht(e){if("undefined"==typeof __REACT_DEVTOOLS_GLOBAL_HOOK__)return!1;var t=__REACT_DEVTOOLS_GLOBAL_HOOK__;if(t.isDisabled||!t.supportsFiber)return!0;try{var n=t.inject(e);vo=dt(function(e){return t.onCommitFiberRoot(n,e)}),go=dt(function(e){return t.onCommitFiberUnmount(n,e)})}catch(e){}return!0}function mt(e){"function"==typeof vo&&vo(e)}function yt(e){"function"==typeof go&&go(e)}function vt(e){return{baseState:e,expirationTime:0,first:null,last:null,callbackList:null,hasForceUpdate:!1,isInitialized:!1}}function gt(e,t){null===e.last?e.first=e.last=t:(e.last.next=t,e.last=t),(0===e.expirationTime||e.expirationTime>t.expirationTime)&&(e.expirationTime=t.expirationTime)}function bt(e,t){var n=e.alternate,r=e.updateQueue;null===r&&(r=e.updateQueue=vt(null)),null!==n?null===(e=n.updateQueue)&&(e=n.updateQueue=vt(null)):e=null,e=e!==r?e:null,null===e?gt(r,t):null===r.last||null===e.last?(gt(r,t),gt(e,t)):(gt(r,t),e.last=t)}function wt(e,t,n,r){return e=e.partialState,"function"==typeof e?e.call(t,n,r):e}function Ct(e,t,n,r,o,a){null!==e&&e.updateQueue===n&&(n=t.updateQueue={baseState:n.baseState,expirationTime:n.expirationTime,first:n.first,last:n.last,isInitialized:n.isInitialized,callbackList:null,hasForceUpdate:!1}),n.expirationTime=0,n.isInitialized?e=n.baseState:(e=n.baseState=t.memoizedState,n.isInitialized=!0);for(var i=!0,u=n.first,l=!1;null!==u;){var c=u.expirationTime;if(c>a){var s=n.expirationTime;(0===s||s>c)&&(n.expirationTime=c),l||(l=!0,n.baseState=e)}else l||(n.first=u.next,null===n.first&&(n.last=null)),u.isReplace?(e=wt(u,r,e,o),i=!0):(c=wt(u,r,e,o))&&(e=i?Cn({},e,c):Cn(e,c),i=!1),u.isForced&&(n.hasForceUpdate=!0),null!==u.callback&&(c=n.callbackList,null===c&&(c=n.callbackList=[]),c.push(u));u=u.next}return null!==n.callbackList?t.effectTag|=32:null!==n.first||n.hasForceUpdate||(t.updateQueue=null),l||(n.baseState=e),e}function Et(e,t){var n=e.callbackList;if(null!==n)for(e.callbackList=null,e=0;ep?(d=f,f=null):d=f.sibling;var v=m(r,f,u[p],l);if(null===v){null===f&&(f=d);break}e&&f&&null===v.alternate&&t(r,f),a=i(v,a,p),null===s?c=v:s.sibling=v,s=v,f=d}if(p===u.length)return n(r,f),c;if(null===f){for(;pd?(v=p,p=null):v=p.sibling;var b=m(a,p,g.value,c);if(null===b){p||(p=v);break}e&&p&&null===b.alternate&&t(a,p),u=i(b,u,d),null===f?s=b:f.sibling=b,f=b,p=v}if(g.done)return n(a,p),s;if(null===p){for(;!g.done;d++,g=l.next())null!==(g=h(a,g.value,c))&&(u=i(g,u,d),null===f?s=g:f.sibling=g,f=g);return s}for(p=o(a,p);!g.done;d++,g=l.next())null!==(g=y(p,a,d,g.value,c))&&(e&&null!==g.alternate&&p.delete(null===g.key?d:g.key),u=i(g,u,d),null===f?s=g:f.sibling=g,f=g);return e&&p.forEach(function(e){return t(a,e)}),s}return function(e,o,i,l){"object"==typeof i&&null!==i&&i.type===ko&&null===i.key&&(i=i.props.children);var c="object"==typeof i&&null!==i;if(c)switch(i.$$typeof){case wo:e:{var s=i.key;for(c=o;null!==c;){if(c.key===s){if(10===c.tag?i.type===ko:c.type===i.type){n(e,c.sibling),o=a(c,i.type===ko?i.props.children:i.props,l),o.ref=Tt(c,i),o.return=e,e=o;break e}n(e,c);break}t(e,c),c=c.sibling}i.type===ko?(o=lt(i.props.children,e.internalContextTag,l,i.key),o.return=e,e=o):(l=ut(i,e.internalContextTag,l),l.ref=Tt(o,i),l.return=e,e=l)}return u(e);case Co:e:{for(c=i.key;null!==o;){if(o.key===c){if(7===o.tag){n(e,o.sibling),o=a(o,i,l),o.return=e,e=o;break e}n(e,o);break}t(e,o),o=o.sibling}o=st(i,e.internalContextTag,l),o.return=e,e=o}return u(e);case Eo:e:{if(null!==o){if(9===o.tag){n(e,o.sibling),o=a(o,null,l),o.type=i.value,o.return=e,e=o;break e}n(e,o)}o=ft(i,e.internalContextTag,l),o.type=i.value,o.return=e,e=o}return u(e);case xo:e:{for(c=i.key;null!==o;){if(o.key===c){if(4===o.tag&&o.stateNode.containerInfo===i.containerInfo&&o.stateNode.implementation===i.implementation){n(e,o.sibling),o=a(o,i.children||[],l),o.return=e,e=o;break e}n(e,o);break}t(e,o),o=o.sibling}o=pt(i,e.internalContextTag,l),o.return=e,e=o}return u(e)}if("string"==typeof i||"number"==typeof i)return i=""+i,null!==o&&6===o.tag?(n(e,o.sibling),o=a(o,i,l)):(n(e,o),o=ct(i,e.internalContextTag,l)),o.return=e,e=o,u(e);if(So(i))return v(e,o,i,l);if(kt(i))return g(e,o,i,l);if(c&&St(e,i),void 0===i)switch(e.tag){case 2:case 1:l=e.type,r("152",l.displayName||l.name||"Component")}return n(e,o)}}function Pt(e,t,n,o,a){function i(e,t,n){var r=t.expirationTime;t.child=null===e?Po(t,null,n,r):Oo(t,e.child,n,r)}function u(e,t){var n=t.ref;null===n||e&&e.ref===n||(t.effectTag|=128)}function l(e,t,n,r){if(u(e,t),!n)return r&&ot(t,!1),s(e,t);n=t.stateNode,zr.current=t;var o=n.render();return t.effectTag|=1,i(e,t,o),t.memoizedState=n.state,t.memoizedProps=n.props,r&&ot(t,!0),t.child}function c(e){var t=e.stateNode;t.pendingContext?tt(e,t.pendingContext,t.pendingContext!==t.context):t.context&&tt(e,t.context,!1),y(e,t.containerInfo)}function s(e,t){if(null!==e&&t.child!==e.child&&r("153"),null!==t.child){e=t.child;var n=it(e,e.pendingProps,e.expirationTime);for(t.child=n,n.return=t;null!==e.sibling;)e=e.sibling,n=n.sibling=it(e,e.pendingProps,e.expirationTime),n.return=t;n.sibling=null}return t.child}function f(e,t){switch(t.tag){case 3:c(t);break;case 2:rt(t);break;case 4:y(t,t.stateNode.containerInfo)}return null}var p=e.shouldSetTextContent,d=e.useSyncScheduling,h=e.shouldDeprioritizeSubtree,m=t.pushHostContext,y=t.pushHostContainer,v=n.enterHydrationState,g=n.resetHydrationState,b=n.tryToClaimNextHydratableInstance;e=xt(o,a,function(e,t){e.memoizedProps=t},function(e,t){e.memoizedState=t});var w=e.adoptClassInstance,C=e.constructClassInstance,E=e.mountClassInstance,x=e.updateClassInstance;return{beginWork:function(e,t,n){if(0===t.expirationTime||t.expirationTime>n)return f(e,t);switch(t.tag){case 0:null!==e&&r("155");var o=t.type,a=t.pendingProps,k=Xe(t);return k=Je(t,k),o=o(a,k),t.effectTag|=1,"object"==typeof o&&null!==o&&"function"==typeof o.render?(t.tag=2,a=rt(t),w(t,o),E(t,n),t=l(e,t,!0,a)):(t.tag=1,i(e,t,o),t.memoizedProps=a,t=t.child),t;case 1:e:{if(a=t.type,n=t.pendingProps,o=t.memoizedProps,mo.current)null===n&&(n=o);else if(null===n||o===n){t=s(e,t);break e}o=Xe(t),o=Je(t,o),a=a(n,o),t.effectTag|=1,i(e,t,a),t.memoizedProps=n,t=t.child}return t;case 2:return a=rt(t),o=void 0,null===e?t.stateNode?r("153"):(C(t,t.pendingProps),E(t,n),o=!0):o=x(e,t,n),l(e,t,o,a);case 3:return c(t),a=t.updateQueue,null!==a?(o=t.memoizedState,a=Ct(e,t,a,null,null,n),o===a?(g(),t=s(e,t)):(o=a.element,k=t.stateNode,(null===e||null===e.child)&&k.hydrate&&v(t)?(t.effectTag|=2,t.child=Po(t,null,o,n)):(g(),i(e,t,o)),t.memoizedState=a,t=t.child)):(g(),t=s(e,t)),t;case 5:m(t),null===e&&b(t),a=t.type;var T=t.memoizedProps;return o=t.pendingProps,null===o&&null===(o=T)&&r("154"),k=null!==e?e.memoizedProps:null,mo.current||null!==o&&T!==o?(T=o.children,p(a,o)?T=null:k&&p(a,k)&&(t.effectTag|=16),u(e,t),2147483647!==n&&!d&&h(a,o)?(t.expirationTime=2147483647,t=null):(i(e,t,T),t.memoizedProps=o,t=t.child)):t=s(e,t),t;case 6:return null===e&&b(t),e=t.pendingProps,null===e&&(e=t.memoizedProps),t.memoizedProps=e,null;case 8:t.tag=7;case 7:return a=t.pendingProps,mo.current?null===a&&null===(a=e&&e.memoizedProps)&&r("154"):null!==a&&t.memoizedProps!==a||(a=t.memoizedProps),o=a.children,t.stateNode=null===e?Po(t,t.stateNode,o,n):Oo(t,t.stateNode,o,n),t.memoizedProps=a,t.stateNode;case 9:return null;case 4:e:{if(y(t,t.stateNode.containerInfo),a=t.pendingProps,mo.current)null===a&&null==(a=e&&e.memoizedProps)&&r("154");else if(null===a||t.memoizedProps===a){t=s(e,t);break e}null===e?t.child=Oo(t,null,a,n):i(e,t,a),t.memoizedProps=a,t=t.child}return t;case 10:e:{if(n=t.pendingProps,mo.current)null===n&&(n=t.memoizedProps);else if(null===n||t.memoizedProps===n){t=s(e,t);break e}i(e,t,n),t.memoizedProps=n,t=t.child}return t;default:r("156")}},beginFailedWork:function(e,t,n){switch(t.tag){case 2:rt(t);break;case 3:c(t);break;default:r("157")}return t.effectTag|=64,null===e?t.child=null:t.child!==e.child&&(t.child=e.child),0===t.expirationTime||t.expirationTime>n?f(e,t):(t.firstEffect=null,t.lastEffect=null,t.child=null===e?Po(t,null,null,n):Oo(t,e.child,null,n),2===t.tag&&(e=t.stateNode,t.memoizedProps=e.props,t.memoizedState=e.state),t.child)}}}function _t(e,t,n){function o(e){e.effectTag|=4}var a=e.createInstance,i=e.createTextInstance,u=e.appendInitialChild,l=e.finalizeInitialChildren,c=e.prepareUpdate,s=e.persistence,f=t.getRootHostContainer,p=t.popHostContext,d=t.getHostContext,h=t.popHostContainer,m=n.prepareToHydrateHostInstance,y=n.prepareToHydrateHostTextInstance,v=n.popHydrationState,g=void 0,b=void 0,w=void 0;return e.mutation?(g=function(){},b=function(e,t,n){(t.updateQueue=n)&&o(t)},w=function(e,t,n,r){n!==r&&o(t)}):r(s?"235":"236"),{completeWork:function(e,t,n){var s=t.pendingProps;switch(null===s?s=t.memoizedProps:2147483647===t.expirationTime&&2147483647!==n||(t.pendingProps=null),t.tag){case 1:return null;case 2:return et(t),null;case 3:return h(t),Ge(mo,t),Ge(ho,t),s=t.stateNode,s.pendingContext&&(s.context=s.pendingContext,s.pendingContext=null),null!==e&&null!==e.child||(v(t),t.effectTag&=-3),g(t),null;case 5:p(t),n=f();var C=t.type;if(null!==e&&null!=t.stateNode){var E=e.memoizedProps,x=t.stateNode,k=d();x=c(x,C,E,s,n,k),b(e,t,x,C,E,s,n),e.ref!==t.ref&&(t.effectTag|=128)}else{if(!s)return null===t.stateNode&&r("166"),null;if(e=d(),v(t))m(t,n,e)&&o(t);else{e=a(C,s,n,e,t);e:for(E=t.child;null!==E;){if(5===E.tag||6===E.tag)u(e,E.stateNode);else if(4!==E.tag&&null!==E.child){E.child.return=E,E=E.child;continue}if(E===t)break;for(;null===E.sibling;){if(null===E.return||E.return===t)break e;E=E.return}E.sibling.return=E.return,E=E.sibling}l(e,C,s,n)&&o(t),t.stateNode=e}null!==t.ref&&(t.effectTag|=128)}return null;case 6:if(e&&null!=t.stateNode)w(e,t,e.memoizedProps,s);else{if("string"!=typeof s)return null===t.stateNode&&r("166"),null;e=f(),n=d(),v(t)?y(t)&&o(t):t.stateNode=i(s,e,n,t)}return null;case 7:(s=t.memoizedProps)||r("165"),t.tag=8,C=[];e:for((E=t.stateNode)&&(E.return=t);null!==E;){if(5===E.tag||6===E.tag||4===E.tag)r("247");else if(9===E.tag)C.push(E.type);else if(null!==E.child){E.child.return=E,E=E.child;continue}for(;null===E.sibling;){if(null===E.return||E.return===t)break e;E=E.return}E.sibling.return=E.return,E=E.sibling}return E=s.handler,s=E(s.props,C),t.child=Oo(t,null!==e?e.child:null,s,n),t.child;case 8:return t.tag=7,null;case 9:case 10:return null;case 4:return h(t),g(t),null;case 0:r("167");default:r("156")}}}}function Nt(e,t){function n(e){var n=e.ref;if(null!==n)try{n(null)}catch(n){t(e,n)}}function o(e){switch("function"==typeof yt&&yt(e),e.tag){case 2:n(e);var r=e.stateNode;if("function"==typeof r.componentWillUnmount)try{r.props=e.memoizedProps,r.state=e.memoizedState,r.componentWillUnmount()}catch(n){t(e,n)}break;case 5:n(e);break;case 7:a(e.stateNode);break;case 4:c&&u(e)}}function a(e){for(var t=e;;)if(o(t),null===t.child||c&&4===t.tag){if(t===e)break;for(;null===t.sibling;){if(null===t.return||t.return===e)return;t=t.return}t.sibling.return=t.return,t=t.sibling}else t.child.return=t,t=t.child}function i(e){return 5===e.tag||3===e.tag||4===e.tag}function u(e){for(var t=e,n=!1,i=void 0,u=void 0;;){if(!n){n=t.return;e:for(;;){switch(null===n&&r("160"),n.tag){case 5:i=n.stateNode,u=!1;break e;case 3:case 4:i=n.stateNode.containerInfo,u=!0;break e}n=n.return}n=!0}if(5===t.tag||6===t.tag)a(t),u?b(i,t.stateNode):g(i,t.stateNode);else if(4===t.tag?i=t.stateNode.containerInfo:o(t),null!==t.child){t.child.return=t,t=t.child;continue}if(t===e)break;for(;null===t.sibling;){if(null===t.return||t.return===e)return;t=t.return,4===t.tag&&(n=!1)}t.sibling.return=t.return,t=t.sibling}}var l=e.getPublicInstance,c=e.mutation;e=e.persistence,c||r(e?"235":"236");var s=c.commitMount,f=c.commitUpdate,p=c.resetTextContent,d=c.commitTextUpdate,h=c.appendChild,m=c.appendChildToContainer,y=c.insertBefore,v=c.insertInContainerBefore,g=c.removeChild,b=c.removeChildFromContainer;return{commitResetTextContent:function(e){p(e.stateNode)},commitPlacement:function(e){e:{for(var t=e.return;null!==t;){if(i(t)){var n=t;break e}t=t.return}r("160"),n=void 0}var o=t=void 0;switch(n.tag){case 5:t=n.stateNode,o=!1;break;case 3:case 4:t=n.stateNode.containerInfo,o=!0;break;default:r("161")}16&n.effectTag&&(p(t),n.effectTag&=-17);e:t:for(n=e;;){for(;null===n.sibling;){if(null===n.return||i(n.return)){n=null;break e}n=n.return}for(n.sibling.return=n.return,n=n.sibling;5!==n.tag&&6!==n.tag;){if(2&n.effectTag)continue t;if(null===n.child||4===n.tag)continue t;n.child.return=n,n=n.child}if(!(2&n.effectTag)){n=n.stateNode;break e}}for(var a=e;;){if(5===a.tag||6===a.tag)n?o?v(t,a.stateNode,n):y(t,a.stateNode,n):o?m(t,a.stateNode):h(t,a.stateNode);else if(4!==a.tag&&null!==a.child){a.child.return=a,a=a.child;continue}if(a===e)break;for(;null===a.sibling;){if(null===a.return||a.return===e)return;a=a.return}a.sibling.return=a.return,a=a.sibling}},commitDeletion:function(e){u(e),e.return=null,e.child=null,e.alternate&&(e.alternate.child=null,e.alternate.return=null)},commitWork:function(e,t){switch(t.tag){case 2:break;case 5:var n=t.stateNode;if(null!=n){var o=t.memoizedProps;e=null!==e?e.memoizedProps:o;var a=t.type,i=t.updateQueue;t.updateQueue=null,null!==i&&f(n,i,a,e,o,t)}break;case 6:null===t.stateNode&&r("162"),n=t.memoizedProps,d(t.stateNode,null!==e?e.memoizedProps:n,n);break;case 3:break;default:r("163")}},commitLifeCycles:function(e,t){switch(t.tag){case 2:var n=t.stateNode;if(4&t.effectTag)if(null===e)n.props=t.memoizedProps,n.state=t.memoizedState,n.componentDidMount();else{var o=e.memoizedProps;e=e.memoizedState,n.props=t.memoizedProps,n.state=t.memoizedState,n.componentDidUpdate(o,e)}t=t.updateQueue,null!==t&&Et(t,n);break;case 3:n=t.updateQueue,null!==n&&Et(n,null!==t.child?t.child.stateNode:null);break;case 5:n=t.stateNode,null===e&&4&t.effectTag&&s(n,t.type,t.memoizedProps,t);break;case 6:case 4:break;default:r("163")}},commitAttachRef:function(e){var t=e.ref;if(null!==t){var n=e.stateNode;switch(e.tag){case 5:t(l(n));break;default:t(n)}}},commitDetachRef:function(e){null!==(e=e.ref)&&e(null)}}}function jt(e){function t(e){return e===_o&&r("174"),e}var n=e.getChildHostContext,o=e.getRootHostContext,a={current:_o},i={current:_o},u={current:_o};return{getHostContext:function(){return t(a.current)},getRootHostContainer:function(){return t(u.current)},popHostContainer:function(e){Ge(a,e),Ge(i,e),Ge(u,e)},popHostContext:function(e){i.current===e&&(Ge(a,e),Ge(i,e))},pushHostContainer:function(e,t){Ye(u,t,e),t=o(t),Ye(i,e,e),Ye(a,t,e)},pushHostContext:function(e){var r=t(u.current),o=t(a.current);r=n(o,e.type,r),o!==r&&(Ye(i,e,e),Ye(a,r,e))},resetHostContainer:function(){a.current=_o,u.current=_o}}}function Dt(e){function t(e,t){var n=new at(5,null,0);n.type="DELETED",n.stateNode=t,n.return=e,n.effectTag=8,null!==e.lastEffect?(e.lastEffect.nextEffect=n,e.lastEffect=n):e.firstEffect=e.lastEffect=n}function n(e,t){switch(e.tag){case 5:return null!==(t=i(t,e.type,e.pendingProps))&&(e.stateNode=t,!0);case 6:return null!==(t=u(t,e.pendingProps))&&(e.stateNode=t,!0);default:return!1}}function o(e){for(e=e.return;null!==e&&5!==e.tag&&3!==e.tag;)e=e.return;p=e}var a=e.shouldSetTextContent;if(!(e=e.hydration))return{enterHydrationState:function(){return!1},resetHydrationState:function(){},tryToClaimNextHydratableInstance:function(){},prepareToHydrateHostInstance:function(){r("175")},prepareToHydrateHostTextInstance:function(){r("176")},popHydrationState:function(){return!1}};var i=e.canHydrateInstance,u=e.canHydrateTextInstance,l=e.getNextHydratableSibling,c=e.getFirstHydratableChild,s=e.hydrateInstance,f=e.hydrateTextInstance,p=null,d=null,h=!1;return{enterHydrationState:function(e){return d=c(e.stateNode.containerInfo),p=e,h=!0},resetHydrationState:function(){d=p=null,h=!1},tryToClaimNextHydratableInstance:function(e){if(h){var r=d;if(r){if(!n(e,r)){if(!(r=l(r))||!n(e,r))return e.effectTag|=2,h=!1,void(p=e);t(p,d)}p=e,d=c(r)}else e.effectTag|=2,h=!1,p=e}},prepareToHydrateHostInstance:function(e,t,n){return t=s(e.stateNode,e.type,e.memoizedProps,t,n,e),e.updateQueue=t,null!==t},prepareToHydrateHostTextInstance:function(e){return f(e.stateNode,e.memoizedProps,e)},popHydrationState:function(e){if(e!==p)return!1;if(!h)return o(e),h=!0,!1;var n=e.type;if(5!==e.tag||"head"!==n&&"body"!==n&&!a(n,e.memoizedProps))for(n=d;n;)t(e,n),n=l(n);return o(e),d=p?l(e.stateNode):null,!0}}}function It(e){function t(e){ae=G=!0;var t=e.stateNode;if(t.current===e&&r("177"),t.isReadyForCommit=!1,zr.current=null,1i.expirationTime)&&(a=i.expirationTime),i=i.sibling;o.expirationTime=a}if(null!==t)return t;if(null!==n&&(null===n.firstEffect&&(n.firstEffect=e.firstEffect),null!==e.lastEffect&&(null!==n.lastEffect&&(n.lastEffect.nextEffect=e.firstEffect),n.lastEffect=e.lastEffect),1e))if(J<=$)for(;null!==Y;)Y=c(Y)?a(Y):o(Y);else for(;null!==Y&&!E();)Y=c(Y)?a(Y):o(Y)}else if(!(0===J||J>e))if(J<=$)for(;null!==Y;)Y=o(Y);else for(;null!==Y&&!E();)Y=o(Y)}function u(e,t){if(G&&r("243"),G=!0,e.isReadyForCommit=!1,e!==X||t!==J||null===Y){for(;-1t)&&(e.expirationTime=t),null!==e.alternate&&(0===e.alternate.expirationTime||e.alternate.expirationTime>t)&&(e.alternate.expirationTime=t),null===e.return){if(3!==e.tag)break;var n=e.stateNode;!G&&n===X&&twe&&r("185"),null===o.nextScheduledRoot)o.remainingExpirationTime=a,null===le?(ue=le=o,o.nextScheduledRoot=o):(le=le.nextScheduledRoot=o,le.nextScheduledRoot=ue);else{var i=o.remainingExpirationTime;(0===i||ace)return;B(se)}var t=H()-W;ce=e,se=z(b,{timeout:10*(e-2)-t})}function g(){var e=0,t=null;if(null!==le)for(var n=le,o=ue;null!==o;){var a=o.remainingExpirationTime;if(0===a){if((null===n||null===le)&&r("244"),o===o.nextScheduledRoot){ue=le=o.nextScheduledRoot=null;break}if(o===ue)ue=a=o.nextScheduledRoot,le.nextScheduledRoot=a,o.nextScheduledRoot=null;else{if(o===le){le=n,le.nextScheduledRoot=ue,o.nextScheduledRoot=null;break}n.nextScheduledRoot=o.nextScheduledRoot,o.nextScheduledRoot=null}o=n.nextScheduledRoot}else{if((0===e||axe)&&(he=!0)}function x(e){null===pe&&r("246"),pe.remainingExpirationTime=0,me||(me=!0,ye=e)}var k=jt(e),T=Dt(e),S=k.popHostContainer,O=k.popHostContext,P=k.resetHostContainer,_=Pt(e,k,T,d,p),N=_.beginWork,j=_.beginFailedWork,D=_t(e,k,T).completeWork;k=Nt(e,l);var I=k.commitResetTextContent,A=k.commitPlacement,M=k.commitDeletion,R=k.commitWork,F=k.commitLifeCycles,L=k.commitAttachRef,U=k.commitDetachRef,H=e.now,z=e.scheduleDeferredCallback,B=e.cancelDeferredCallback,V=e.useSyncScheduling,q=e.prepareForCommit,K=e.resetAfterCommit,W=H(),$=2,Q=0,G=!1,Y=null,X=null,J=0,Z=null,ee=null,te=null,ne=null,re=null,oe=!1,ae=!1,ie=!1,ue=null,le=null,ce=0,se=-1,fe=!1,pe=null,de=0,he=!1,me=!1,ye=null,ve=null,ge=!1,be=!1,we=1e3,Ee=0,xe=1;return{computeAsyncExpiration:f,computeExpirationForFiber:p,scheduleWork:d,batchedUpdates:function(e,t){var n=ge;ge=!0;try{return e(t)}finally{(ge=n)||fe||w(1,null)}},unbatchedUpdates:function(e){if(ge&&!be){be=!0;try{return e()}finally{be=!1}}return e()},flushSync:function(e){var t=ge;ge=!0;try{e:{var n=Q;Q=1;try{var o=e();break e}finally{Q=n}o=void 0}return o}finally{ge=t,fe&&r("187"),w(1,null)}},deferredUpdates:function(e){var t=Q;Q=f();try{return e()}finally{Q=t}}}}function At(e){function t(e){return e=Se(e),null===e?null:e.stateNode}var n=e.getPublicInstance;e=It(e);var o=e.computeAsyncExpiration,a=e.computeExpirationForFiber,i=e.scheduleWork;return{createContainer:function(e,t){var n=new at(3,null,0);return e={current:n,containerInfo:e,pendingChildren:null,remainingExpirationTime:0,isReadyForCommit:!1,finishedWork:null,context:null,pendingContext:null,hydrate:t,nextScheduledRoot:null},n.stateNode=e},updateContainer:function(e,t,n,u){var l=t.current;if(n){n=n._reactInternalFiber;var c;e:{for(2===Ee(n)&&2===n.tag||r("170"),c=n;3!==c.tag;){if(Ze(c)){c=c.stateNode.__reactInternalMemoizedMergedChildContext;break e}(c=c.return)||r("171")}c=c.stateNode.context}n=Ze(n)?nt(n,c):c}else n=Pn;null===t.context?t.context=n:t.pendingContext=n,t=u,t=void 0===t?null:t,u=null!=e&&null!=e.type&&null!=e.type.prototype&&!0===e.type.prototype.unstable_isAsyncReactComponent?o():a(l),bt(l,{expirationTime:u,partialState:{element:e},callback:t,isReplace:!1,isForced:!1,nextCallback:null,next:null}),i(l,u)},batchedUpdates:e.batchedUpdates,unbatchedUpdates:e.unbatchedUpdates,deferredUpdates:e.deferredUpdates,flushSync:e.flushSync,getPublicRootInstance:function(e){if(e=e.current,!e.child)return null;switch(e.child.tag){case 5:return n(e.child.stateNode);default:return e.child.stateNode}},findHostInstance:t,findHostInstanceWithNoPortals:function(e){return e=Oe(e),null===e?null:e.stateNode},injectIntoDevTools:function(e){var n=e.findFiberByHostInstance;return ht(Cn({},e,{findHostInstanceByFiber:function(e){return t(e)},findFiberByHostInstance:function(e){return n?n(e):null}}))}}}function Mt(e,t,n){var r=3n||r.hasOverloadedBooleanValue&&!1===n?Ut(e,t):r.mustUseProperty?e[r.propertyName]=n:(t=r.attributeName,(o=r.attributeNamespace)?e.setAttributeNS(o,t,""+n):r.hasBooleanValue||r.hasOverloadedBooleanValue&&!0===n?e.setAttribute(t,""):e.setAttribute(t,""+n))}else Lt(e,t,a(t,n)?n:null)}function Lt(e,t,n){Rt(t)&&(null==n?e.removeAttribute(t):e.setAttribute(t,""+n))}function Ut(e,t){var n=i(t);n?(t=n.mutationMethod)?t(e,void 0):n.mustUseProperty?e[n.propertyName]=!n.hasBooleanValue&&"":e.removeAttribute(n.attributeName):e.removeAttribute(t)}function Ht(e,t){var n=t.value,r=t.checked;return Cn({type:void 0,step:void 0,min:void 0,max:void 0},t,{defaultChecked:void 0,defaultValue:void 0,value:null!=n?n:e._wrapperState.initialValue,checked:null!=r?r:e._wrapperState.initialChecked})}function zt(e,t){var n=t.defaultValue;e._wrapperState={initialChecked:null!=t.checked?t.checked:t.defaultChecked,initialValue:null!=t.value?t.value:n,controlled:"checkbox"===t.type||"radio"===t.type?null!=t.checked:null!=t.value}}function Bt(e,t){null!=(t=t.checked)&&Ft(e,"checked",t)}function Vt(e,t){Bt(e,t);var n=t.value;null!=n?0===n&&""===e.value?e.value="0":"number"===t.type?(t=parseFloat(e.value)||0,(n!=t||n==t&&e.value!=n)&&(e.value=""+n)):e.value!==""+n&&(e.value=""+n):(null==t.value&&null!=t.defaultValue&&e.defaultValue!==""+t.defaultValue&&(e.defaultValue=""+t.defaultValue),null==t.checked&&null!=t.defaultChecked&&(e.defaultChecked=!!t.defaultChecked))}function qt(e,t){switch(t.type){case"submit":case"reset":break;case"color":case"date":case"datetime":case"datetime-local":case"month":case"time":case"week":e.value="",e.value=e.defaultValue;break;default:e.value=e.value}t=e.name,""!==t&&(e.name=""),e.defaultChecked=!e.defaultChecked,e.defaultChecked=!e.defaultChecked,""!==t&&(e.name=t)}function Kt(e){var t="";return bn.Children.forEach(e,function(e){null==e||"string"!=typeof e&&"number"!=typeof e||(t+=e)}),t}function Wt(e,t){return e=Cn({children:void 0},t),(t=Kt(t.children))&&(e.children=t),e}function $t(e,t,n,r){if(e=e.options,t){t={};for(var o=0;o=t.length||r("93"),t=t[0]),n=""+t),null==n&&(n="")),e._wrapperState={initialValue:""+n}}function Xt(e,t){var n=t.value;null!=n&&(n=""+n,n!==e.value&&(e.value=n),null==t.defaultValue&&(e.defaultValue=n)),null!=t.defaultValue&&(e.defaultValue=t.defaultValue)}function Jt(e){var t=e.textContent;t===e._wrapperState.initialValue&&(e.value=t)}function Zt(e){switch(e){case"svg":return"http://www.w3.org/2000/svg";case"math":return"http://www.w3.org/1998/Math/MathML";default:return"http://www.w3.org/1999/xhtml"}}function en(e,t){return null==e||"http://www.w3.org/1999/xhtml"===e?Zt(t):"http://www.w3.org/2000/svg"===e&&"foreignObject"===t?"http://www.w3.org/1999/xhtml":e}function tn(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&3===n.nodeType)return void(n.nodeValue=t)}e.textContent=t}function nn(e,t){e=e.style;for(var n in t)if(t.hasOwnProperty(n)){var r=0===n.indexOf("--"),o=n,a=t[n];o=null==a||"boolean"==typeof a||""===a?"":r||"number"!=typeof a||0===a||Zo.hasOwnProperty(o)&&Zo[o]?(""+a).trim():a+"px","float"===n&&(n="cssFloat"),r?e.setProperty(n,o):e[n]=o}}function rn(e,t,n){t&&(ta[e]&&(null!=t.children||null!=t.dangerouslySetInnerHTML)&&r("137",e,n()),null!=t.dangerouslySetInnerHTML&&(null!=t.children&&r("60"),"object"==typeof t.dangerouslySetInnerHTML&&"__html"in t.dangerouslySetInnerHTML||r("61")),null!=t.style&&"object"!=typeof t.style&&r("62",n()))}function on(e,t){if(-1===e.indexOf("-"))return"string"==typeof t.is;switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}function an(e,t){e=9===e.nodeType||11===e.nodeType?e:e.ownerDocument;var n=Me(e);t=Yn[t];for(var r=0;r<\/script>",e=e.removeChild(e.firstChild)):e="string"==typeof t.is?n.createElement(e,{is:t.is}):n.createElement(e):e=n.createElementNS(r,e),e}function ln(e,t){return(9===t.nodeType?t:t.ownerDocument).createTextNode(e)}function cn(e,t,n,r){var o=on(t,n);switch(t){case"iframe":case"object":Ne("topLoad","load",e);var a=n;break;case"video":case"audio":for(a in oa)oa.hasOwnProperty(a)&&Ne(a,oa[a],e);a=n;break;case"source":Ne("topError","error",e),a=n;break;case"img":case"image":Ne("topError","error",e),Ne("topLoad","load",e),a=n;break;case"form":Ne("topReset","reset",e),Ne("topSubmit","submit",e),a=n;break;case"details":Ne("topToggle","toggle",e),a=n;break;case"input":zt(e,n),a=Ht(e,n),Ne("topInvalid","invalid",e),an(r,"onChange");break;case"option":a=Wt(e,n);break;case"select":Qt(e,n),a=Cn({},n,{value:void 0}),Ne("topInvalid","invalid",e),an(r,"onChange");break;case"textarea":Yt(e,n),a=Gt(e,n),Ne("topInvalid","invalid",e),an(r,"onChange");break;default:a=n}rn(t,a,ra);var i,u=a;for(i in u)if(u.hasOwnProperty(i)){var l=u[i];"style"===i?nn(e,l,ra):"dangerouslySetInnerHTML"===i?null!=(l=l?l.__html:void 0)&&Jo(e,l):"children"===i?"string"==typeof l?("textarea"!==t||""!==l)&&tn(e,l):"number"==typeof l&&tn(e,""+l):"suppressContentEditableWarning"!==i&&"suppressHydrationWarning"!==i&&"autoFocus"!==i&&(Gn.hasOwnProperty(i)?null!=l&&an(r,i):o?Lt(e,i,l):null!=l&&Ft(e,i,l))}switch(t){case"input":ae(e),qt(e,n);break;case"textarea":ae(e),Jt(e,n);break;case"option":null!=n.value&&e.setAttribute("value",n.value);break;case"select":e.multiple=!!n.multiple,t=n.value,null!=t?$t(e,!!n.multiple,t,!1):null!=n.defaultValue&&$t(e,!!n.multiple,n.defaultValue,!0);break;default:"function"==typeof a.onClick&&(e.onclick=En)}}function sn(e,t,n,r,o){var a=null;switch(t){case"input":n=Ht(e,n),r=Ht(e,r),a=[];break;case"option":n=Wt(e,n),r=Wt(e,r),a=[];break;case"select":n=Cn({},n,{value:void 0}),r=Cn({},r,{value:void 0}),a=[];break;case"textarea":n=Gt(e,n),r=Gt(e,r),a=[];break;default:"function"!=typeof n.onClick&&"function"==typeof r.onClick&&(e.onclick=En)}rn(t,r,ra);var i,u;e=null;for(i in n)if(!r.hasOwnProperty(i)&&n.hasOwnProperty(i)&&null!=n[i])if("style"===i)for(u in t=n[i])t.hasOwnProperty(u)&&(e||(e={}),e[u]="");else"dangerouslySetInnerHTML"!==i&&"children"!==i&&"suppressContentEditableWarning"!==i&&"suppressHydrationWarning"!==i&&"autoFocus"!==i&&(Gn.hasOwnProperty(i)?a||(a=[]):(a=a||[]).push(i,null));for(i in r){var l=r[i];if(t=null!=n?n[i]:void 0,r.hasOwnProperty(i)&&l!==t&&(null!=l||null!=t))if("style"===i)if(t){for(u in t)!t.hasOwnProperty(u)||l&&l.hasOwnProperty(u)||(e||(e={}),e[u]="");for(u in l)l.hasOwnProperty(u)&&t[u]!==l[u]&&(e||(e={}),e[u]=l[u])}else e||(a||(a=[]),a.push(i,e)),e=l;else"dangerouslySetInnerHTML"===i?(l=l?l.__html:void 0,t=t?t.__html:void 0,null!=l&&t!==l&&(a=a||[]).push(i,""+l)):"children"===i?t===l||"string"!=typeof l&&"number"!=typeof l||(a=a||[]).push(i,""+l):"suppressContentEditableWarning"!==i&&"suppressHydrationWarning"!==i&&(Gn.hasOwnProperty(i)?(null!=l&&an(o,i),a||t===l||(a=[])):(a=a||[]).push(i,l))}return e&&(a=a||[]).push("style",e),a}function fn(e,t,n,r,o){"input"===n&&"radio"===o.type&&null!=o.name&&Bt(e,o),on(n,r),r=on(n,o);for(var a=0;a=l.hasBooleanValue+l.hasNumericValue+l.hasOverloadedBooleanValue||r("50",u),i.hasOwnProperty(u)&&(l.attributeName=i[u]),a.hasOwnProperty(u)&&(l.attributeNamespace=a[u]),e.hasOwnProperty(u)&&(l.mutationMethod=e[u]),jn[u]=l}}},jn={},Dn=Nn,In=Dn.MUST_USE_PROPERTY,An=Dn.HAS_BOOLEAN_VALUE,Mn=Dn.HAS_NUMERIC_VALUE,Rn=Dn.HAS_POSITIVE_NUMERIC_VALUE,Fn=Dn.HAS_OVERLOADED_BOOLEAN_VALUE,Ln=Dn.HAS_STRING_BOOLEAN_VALUE,Un={Properties:{allowFullScreen:An,async:An,autoFocus:An,autoPlay:An,capture:Fn,checked:In|An,cols:Rn,contentEditable:Ln,controls:An,default:An,defer:An,disabled:An,download:Fn,draggable:Ln,formNoValidate:An,hidden:An,loop:An,multiple:In|An,muted:In|An,noValidate:An,open:An,playsInline:An,readOnly:An,required:An,reversed:An,rows:Rn,rowSpan:Mn,scoped:An,seamless:An,selected:In|An,size:Rn,start:Mn,span:Rn,spellCheck:Ln,style:0,tabIndex:0,itemScope:An,acceptCharset:0,className:0,htmlFor:0,httpEquiv:0,value:Ln},DOMAttributeNames:{acceptCharset:"accept-charset",className:"class",htmlFor:"for",httpEquiv:"http-equiv"},DOMMutationMethods:{value:function(e,t){if(null==t)return e.removeAttribute("value");"number"!==e.type||!1===e.hasAttribute("value")?e.setAttribute("value",""+t):e.validity&&!e.validity.badInput&&e.ownerDocument.activeElement!==e&&e.setAttribute("value",""+t)}}},Hn=Dn.HAS_STRING_BOOLEAN_VALUE,zn={xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace"},Bn={Properties:{autoReverse:Hn,externalResourcesRequired:Hn,preserveAlpha:Hn},DOMAttributeNames:{autoReverse:"autoReverse",externalResourcesRequired:"externalResourcesRequired",preserveAlpha:"preserveAlpha"},DOMAttributeNamespaces:{xlinkActuate:zn.xlink,xlinkArcrole:zn.xlink,xlinkHref:zn.xlink,xlinkRole:zn.xlink,xlinkShow:zn.xlink,xlinkTitle:zn.xlink,xlinkType:zn.xlink,xmlBase:zn.xml,xmlLang:zn.xml,xmlSpace:zn.xml}},Vn=/[\-\:]([a-z])/g;"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode x-height xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xmlns:xlink xml:lang xml:space".split(" ").forEach(function(e){var t=e.replace(Vn,u);Bn.Properties[t]=0,Bn.DOMAttributeNames[t]=e}),Dn.injectDOMPropertyConfig(Un),Dn.injectDOMPropertyConfig(Bn);var qn={_caughtError:null,_hasCaughtError:!1,_rethrowError:null,_hasRethrowError:!1,injection:{injectErrorUtils:function(e){"function"!=typeof e.invokeGuardedCallback&&r("197"),l=e.invokeGuardedCallback}},invokeGuardedCallback:function(e,t,n,r,o,a,i,u,c){l.apply(qn,arguments)},invokeGuardedCallbackAndCatchFirstError:function(e,t,n,r,o,a,i,u,l){if(qn.invokeGuardedCallback.apply(this,arguments),qn.hasCaughtError()){var c=qn.clearCaughtError();qn._hasRethrowError||(qn._hasRethrowError=!0,qn._rethrowError=c)}},rethrowCaughtError:function(){return c.apply(qn,arguments)},hasCaughtError:function(){return qn._hasCaughtError},clearCaughtError:function(){if(qn._hasCaughtError){var e=qn._caughtError;return qn._caughtError=null,qn._hasCaughtError=!1,e}r("198")}},Kn=null,Wn={},$n=[],Qn={},Gn={},Yn={},Xn=Object.freeze({plugins:$n,eventNameDispatchConfigs:Qn,registrationNameModules:Gn,registrationNameDependencies:Yn,possibleRegistrationNames:null,injectEventPluginOrder:p,injectEventPluginsByName:d}),Jn=null,Zn=null,er=null,tr=null,nr={injectEventPluginOrder:p,injectEventPluginsByName:d},rr=Object.freeze({injection:nr,getListener:w,extractEvents:C,enqueueEvents:E,processEventQueue:x}),or=Math.random().toString(36).slice(2),ar="__reactInternalInstance$"+or,ir="__reactEventHandlers$"+or,ur=Object.freeze({precacheFiberNode:function(e,t){t[ar]=e},getClosestInstanceFromNode:k,getInstanceFromNode:function(e){return e=e[ar],!e||5!==e.tag&&6!==e.tag?null:e},getNodeFromInstance:T,getFiberCurrentPropsFromNode:S,updateFiberProps:function(e,t){e[ir]=t}}),lr=Object.freeze({accumulateTwoPhaseDispatches:A,accumulateTwoPhaseDispatchesSkipTarget:function(e){y(e,j)},accumulateEnterLeaveDispatches:M,accumulateDirectDispatches:function(e){y(e,I)}}),cr=null,sr={_root:null,_startText:null,_fallbackText:null},fr="dispatchConfig _targetInst nativeEvent isDefaultPrevented isPropagationStopped _dispatchListeners _dispatchInstances".split(" "),pr={type:null,target:null,currentTarget:En.thatReturnsNull,eventPhase:null,bubbles:null,cancelable:null,timeStamp:function(e){return e.timeStamp||Date.now()},defaultPrevented:null,isTrusted:null};Cn(U.prototype,{preventDefault:function(){this.defaultPrevented=!0;var e=this.nativeEvent;e&&(e.preventDefault?e.preventDefault():"unknown"!=typeof e.returnValue&&(e.returnValue=!1),this.isDefaultPrevented=En.thatReturnsTrue)},stopPropagation:function(){var e=this.nativeEvent;e&&(e.stopPropagation?e.stopPropagation():"unknown"!=typeof e.cancelBubble&&(e.cancelBubble=!0),this.isPropagationStopped=En.thatReturnsTrue)},persist:function(){this.isPersistent=En.thatReturnsTrue},isPersistent:En.thatReturnsFalse,destructor:function(){var e,t=this.constructor.Interface;for(e in t)this[e]=null;for(t=0;t=parseInt(vr.version(),10))}var gr,br=yr,wr=wn.canUseDOM&&(!hr||mr&&8=mr),Cr=String.fromCharCode(32),Er={beforeInput:{phasedRegistrationNames:{bubbled:"onBeforeInput",captured:"onBeforeInputCapture"},dependencies:["topCompositionEnd","topKeyPress","topTextInput","topPaste"]},compositionEnd:{phasedRegistrationNames:{bubbled:"onCompositionEnd",captured:"onCompositionEndCapture"},dependencies:"topBlur topCompositionEnd topKeyDown topKeyPress topKeyUp topMouseDown".split(" ")},compositionStart:{phasedRegistrationNames:{bubbled:"onCompositionStart",captured:"onCompositionStartCapture"},dependencies:"topBlur topCompositionStart topKeyDown topKeyPress topKeyUp topMouseDown".split(" ")},compositionUpdate:{phasedRegistrationNames:{bubbled:"onCompositionUpdate",captured:"onCompositionUpdateCapture"},dependencies:"topBlur topCompositionUpdate topKeyDown topKeyPress topKeyUp topMouseDown".split(" ")}},xr=!1,kr=!1,Tr={eventTypes:Er,extractEvents:function(e,t,n,r){var o;if(hr)e:{switch(e){case"topCompositionStart":var a=Er.compositionStart;break e;case"topCompositionEnd":a=Er.compositionEnd;break e;case"topCompositionUpdate":a=Er.compositionUpdate;break e}a=void 0}else kr?K(e,n)&&(a=Er.compositionEnd):"topKeyDown"===e&&229===n.keyCode&&(a=Er.compositionStart);return a?(wr&&(kr||a!==Er.compositionStart?a===Er.compositionEnd&&kr&&(o=F()):(sr._root=r,sr._startText=L(),kr=!0)),a=V.getPooled(a,t,n,r),o?a.data=o:null!==(o=W(n))&&(a.data=o),A(a),o=a):o=null,(e=br?$(e,n):Q(e,n))?(t=q.getPooled(Er.beforeInput,t,n,r),t.data=e,A(t)):t=null,[o,t]}},Sr=null,Or=null,Pr=null,_r={injectFiberControlledHostComponent:function(e){Sr=e}},Nr=Object.freeze({injection:_r,enqueueStateRestore:Y,restoreStateIfNeeded:X}),jr=!1,Dr={color:!0,date:!0,datetime:!0,"datetime-local":!0,email:!0,month:!0,number:!0,password:!0,range:!0,search:!0,tel:!0,text:!0,time:!0,url:!0,week:!0};wn.canUseDOM&&(gr=document.implementation&&document.implementation.hasFeature&&!0!==document.implementation.hasFeature("",""));var Ir={change:{phasedRegistrationNames:{bubbled:"onChange",captured:"onChangeCapture"},dependencies:"topBlur topChange topClick topFocus topInput topKeyDown topKeyUp topSelectionChange".split(" ")}},Ar=null,Mr=null,Rr=!1;wn.canUseDOM&&(Rr=ne("input")&&(!document.documentMode||9=document.documentMode,eo={select:{phasedRegistrationNames:{bubbled:"onSelect",captured:"onSelectCapture"},dependencies:"topBlur topContextMenu topFocus topKeyDown topKeyUp topMouseDown topMouseUp topSelectionChange".split(" ")}},to=null,no=null,ro=null,oo=!1,ao={eventTypes:eo,extractEvents:function(e,t,n,r){var o,a=r.window===r?r.document:9===r.nodeType?r:r.ownerDocument;if(!(o=!a)){e:{a=Me(a),o=Yn.onSelect;for(var i=0;i=Bo-e){if(!(-1!==Ho&&Ho<=e))return void(zo||(zo=!0,requestAnimationFrame(Wo)));Fo.didTimeout=!0}else Fo.didTimeout=!1;Ho=-1,e=Lo,Lo=null,null!==e&&e(Fo)}},!1);var Wo=function(e){zo=!1;var t=e-Bo+qo;tt&&(t=8),qo=t"+t+"",t=Xo.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}}),Zo={animationIterationCount:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},ea=["Webkit","ms","Moz","O"];Object.keys(Zo).forEach(function(e){ea.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),Zo[t]=Zo[e]})});var ta=Cn({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0}),na=Yo.html,ra=En.thatReturns(""),oa={topAbort:"abort",topCanPlay:"canplay",topCanPlayThrough:"canplaythrough",topDurationChange:"durationchange",topEmptied:"emptied",topEncrypted:"encrypted",topEnded:"ended",topError:"error",topLoadedData:"loadeddata",topLoadedMetadata:"loadedmetadata",topLoadStart:"loadstart",topPause:"pause",topPlay:"play",topPlaying:"playing",topProgress:"progress",topRateChange:"ratechange",topSeeked:"seeked",topSeeking:"seeking",topStalled:"stalled",topSuspend:"suspend",topTimeUpdate:"timeupdate",topVolumeChange:"volumechange",topWaiting:"waiting"},aa=Object.freeze({createElement:un,createTextNode:ln,setInitialProperties:cn,diffProperties:sn,updateProperties:fn,diffHydratedProperties:pn,diffHydratedText:dn,warnForUnmatchedText:function(){},warnForDeletedHydratableElement:function(){},warnForDeletedHydratableText:function(){},warnForInsertedHydratedElement:function(){},warnForInsertedHydratedText:function(){},restoreControlledState:function(e,t,n){switch(t){case"input":if(Vt(e,n),t=n.name,"radio"===n.type&&null!=t){for(n=e;n.parentNode;)n=n.parentNode;for(n=n.querySelectorAll("input[name="+JSON.stringify(""+t)+'][type="radio"]'),t=0;tr&&(o=r,r=e,e=o),o=Fe(n,e);var a=Fe(n,r);if(o&&a&&(1!==t.rangeCount||t.anchorNode!==o.node||t.anchorOffset!==o.offset||t.focusNode!==a.node||t.focusOffset!==a.offset)){var i=document.createRange();i.setStart(o.node,o.offset),t.removeAllRanges(),e>r?(t.addRange(i),t.extend(a.node,a.offset)):(i.setEnd(a.node,a.offset),t.addRange(i))}}for(t=[],e=n;e=e.parentNode;)1===e.nodeType&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(On(n),n=0;n0&&void 0!==arguments[0]?arguments[0]:"store",n=arguments[1],i=n||t+"Subscription",l=function(e){function n(a,i){r(this,n);var u=o(this,e.call(this,a,i));return u[t]=a.store,u}return a(n,e),n.prototype.getChildContext=function(){var e;return e={},e[t]=this[t],e[i]=null,e},n.prototype.render=function(){return u.Children.only(this.props.children)},n}(u.Component);return l.propTypes={store:s.a.isRequired,children:c.a.element.isRequired},l.childContextTypes=(e={},e[t]=s.a.isRequired,e[i]=s.b,e),l}t.a=i;var u=n(1),l=(n.n(u),n(10)),c=n.n(l),s=n(11);n(5);t.b=i()},function(e,t,n){"use strict";var r=n(2),o=n(43),a=n(44);e.exports=function(){function e(e,t,n,r,i,u){u!==a&&o(!1,"Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types")}function t(){return e}e.isRequired=e;var n={array:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t};return n.checkPropTypes=r,n.PropTypes=n,n}},function(e,t,n){"use strict";function r(e,t,n,r,a,i,u,l){if(o(t),!e){var c;if(void 0===t)c=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var s=[n,r,a,i,u,l],f=0;c=new Error(t.replace(/%s/g,function(){return s[f++]})),c.name="Invariant Violation"}throw c.framesToPop=1,c}}var o=function(e){};e.exports=r},function(e,t,n){"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},function(e,t,n){!function(t,n){e.exports=n()}(0,function(){"use strict";var e={childContextTypes:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,getDerivedStateFromProps:!0,mixins:!0,propTypes:!0,type:!0},t={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},n=Object.defineProperty,r=Object.getOwnPropertyNames,o=Object.getOwnPropertySymbols,a=Object.getOwnPropertyDescriptor,i=Object.getPrototypeOf,u=i&&i(Object);return function l(c,s,f){if("string"!=typeof s){if(u){var p=i(s);p&&p!==u&&l(c,p,f)}var d=r(s);o&&(d=d.concat(o(s)));for(var h=0;h=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}function o(e,t,n){for(var r=t.length-1;r>=0;r--){var o=t[r](e);if(o)return o}return function(t,r){throw new Error("Invalid value of type "+typeof e+" for "+n+" argument when connecting component "+r.wrappedComponentName+".")}}function a(e,t){return e===t}var i=n(12),u=n(49),l=n(50),c=n(75),s=n(76),f=n(77),p=Object.assign||function(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:{},t=e.connectHOC,n=void 0===t?i.a:t,d=e.mapStateToPropsFactories,h=void 0===d?c.a:d,m=e.mapDispatchToPropsFactories,y=void 0===m?l.a:m,v=e.mergePropsFactories,g=void 0===v?s.a:v,b=e.selectorFactory,w=void 0===b?f.a:b;return function(e,t,i){var l=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{},c=l.pure,s=void 0===c||c,f=l.areStatesEqual,d=void 0===f?a:f,m=l.areOwnPropsEqual,v=void 0===m?u.a:m,b=l.areStatePropsEqual,C=void 0===b?u.a:b,E=l.areMergedPropsEqual,x=void 0===E?u.a:E,k=r(l,["pure","areStatesEqual","areOwnPropsEqual","areStatePropsEqual","areMergedPropsEqual"]),T=o(e,h,"mapStateToProps"),S=o(t,y,"mapDispatchToProps"),O=o(i,g,"mergeProps");return n(w,p({methodName:"connect",getDisplayName:function(e){return"Connect("+e+")"},shouldHandleStateChanges:Boolean(e),initMapStateToProps:T,initMapDispatchToProps:S,initMergeProps:O,pure:s,areStatesEqual:d,areOwnPropsEqual:v,areStatePropsEqual:C,areMergedPropsEqual:x},k))}}()},function(e,t,n){"use strict";function r(e,t){return e===t?0!==e||0!==t||1/e==1/t:e!==e&&t!==t}function o(e,t){if(r(e,t))return!0;if("object"!=typeof e||null===e||"object"!=typeof t||null===t)return!1;var n=Object.keys(e),o=Object.keys(t);if(n.length!==o.length)return!1;for(var i=0;i0&&void 0!==arguments[0]?arguments[0]:{},t=arguments[1];if(l)throw l;for(var o=!1,a={},i=0;i=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}function o(e,t,n,r){return function(o,a){return n(e(o,a),t(r,a),a)}}function a(e,t,n,r,o){function a(o,a){return h=o,m=a,y=e(h,m),v=t(r,m),g=n(y,v,m),d=!0,g}function i(){return y=e(h,m),t.dependsOnOwnProps&&(v=t(r,m)),g=n(y,v,m)}function u(){return e.dependsOnOwnProps&&(y=e(h,m)),t.dependsOnOwnProps&&(v=t(r,m)),g=n(y,v,m)}function l(){var t=e(h,m),r=!p(t,y);return y=t,r&&(g=n(y,v,m)),g}function c(e,t){var n=!f(t,m),r=!s(e,h);return h=e,m=t,n&&r?i():n?u():r?l():g}var s=o.areStatesEqual,f=o.areOwnPropsEqual,p=o.areStatePropsEqual,d=!1,h=void 0,m=void 0,y=void 0,v=void 0,g=void 0;return function(e,t){return d?c(e,t):a(e,t)}}function i(e,t){var n=t.initMapStateToProps,i=t.initMapDispatchToProps,u=t.initMergeProps,l=r(t,["initMapStateToProps","initMapDispatchToProps","initMergeProps"]),c=n(e,l),s=i(e,l),f=u(e,l);return(l.pure?a:o)(c,s,f,e,l)}t.a=i;n(78)},function(e,t,n){"use strict";n(5)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function i(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var u,l,c=function(){function e(e,t){for(var n=0;n 26 | * @license MIT 27 | */ 28 | e.exports=function(e){return null!=e&&(n(e)||r(e)||!!e._isBuffer)}},function(e,t,n){"use strict";function r(e){this.defaults=e,this.interceptors={request:new i,response:new i}}var o=n(7),a=n(0),i=n(94),u=n(95);r.prototype.request=function(e){"string"==typeof e&&(e=a.merge({url:arguments[0]},arguments[1])),e=a.merge(o,this.defaults,{method:"get"},e),e.method=e.method.toLowerCase();var t=[u,void 0],n=Promise.resolve(e);for(this.interceptors.request.forEach(function(e){t.unshift(e.fulfilled,e.rejected)}),this.interceptors.response.forEach(function(e){t.push(e.fulfilled,e.rejected)});t.length;)n=n.then(t.shift(),t.shift());return n},a.forEach(["delete","get","head","options"],function(e){r.prototype[e]=function(t,n){return this.request(a.merge(n||{},{method:e,url:t}))}}),a.forEach(["post","put","patch"],function(e){r.prototype[e]=function(t,n,r){return this.request(a.merge(r||{},{method:e,url:t,data:n}))}}),e.exports=r},function(e,t){function n(){throw new Error("setTimeout has not been defined")}function r(){throw new Error("clearTimeout has not been defined")}function o(e){if(s===setTimeout)return setTimeout(e,0);if((s===n||!s)&&setTimeout)return s=setTimeout,setTimeout(e,0);try{return s(e,0)}catch(t){try{return s.call(null,e,0)}catch(t){return s.call(this,e,0)}}}function a(e){if(f===clearTimeout)return clearTimeout(e);if((f===r||!f)&&clearTimeout)return f=clearTimeout,clearTimeout(e);try{return f(e)}catch(t){try{return f.call(null,e)}catch(t){return f.call(this,e)}}}function i(){m&&d&&(m=!1,d.length?h=d.concat(h):y=-1,h.length&&u())}function u(){if(!m){var e=o(i);m=!0;for(var t=h.length;t;){for(d=h,h=[];++y1)for(var n=1;n=0)return;i[t]="set-cookie"===t?(i[t]?i[t]:[]).concat([n]):i[t]?i[t]+", "+n:n}}),i):i}},function(e,t,n){"use strict";var r=n(0);e.exports=r.isStandardBrowserEnv()?function(){function e(e){var t=e;return n&&(o.setAttribute("href",t),t=o.href),o.setAttribute("href",t),{href:o.href,protocol:o.protocol?o.protocol.replace(/:$/,""):"",host:o.host,search:o.search?o.search.replace(/^\?/,""):"",hash:o.hash?o.hash.replace(/^#/,""):"",hostname:o.hostname,port:o.port,pathname:"/"===o.pathname.charAt(0)?o.pathname:"/"+o.pathname}}var t,n=/(msie|trident)/i.test(navigator.userAgent),o=document.createElement("a");return t=e(window.location.href),function(n){var o=r.isString(n)?e(n):n;return o.protocol===t.protocol&&o.host===t.host}}():function(){return function(){return!0}}()},function(e,t,n){"use strict";function r(){this.message="String contains an invalid character"}function o(e){for(var t,n,o=String(e),i="",u=0,l=a;o.charAt(0|u)||(l="=",u%1);i+=l.charAt(63&t>>8-u%1*8)){if((n=o.charCodeAt(u+=.75))>255)throw new r;t=t<<8|n}return i}var a="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";r.prototype=new Error,r.prototype.code=5,r.prototype.name="InvalidCharacterError",e.exports=o},function(e,t,n){"use strict";var r=n(0);e.exports=r.isStandardBrowserEnv()?function(){return{write:function(e,t,n,o,a,i){var u=[];u.push(e+"="+encodeURIComponent(t)),r.isNumber(n)&&u.push("expires="+new Date(n).toGMTString()),r.isString(o)&&u.push("path="+o),r.isString(a)&&u.push("domain="+a),!0===i&&u.push("secure"),document.cookie=u.join("; ")},read:function(e){var t=document.cookie.match(new RegExp("(^|;\\s*)("+e+")=([^;]*)"));return t?decodeURIComponent(t[3]):null},remove:function(e){this.write(e,"",Date.now()-864e5)}}}():function(){return{write:function(){},read:function(){return null},remove:function(){}}}()},function(e,t,n){"use strict";function r(){this.handlers=[]}var o=n(0);r.prototype.use=function(e,t){return this.handlers.push({fulfilled:e,rejected:t}),this.handlers.length-1},r.prototype.eject=function(e){this.handlers[e]&&(this.handlers[e]=null)},r.prototype.forEach=function(e){o.forEach(this.handlers,function(t){null!==t&&e(t)})},e.exports=r},function(e,t,n){"use strict";function r(e){e.cancelToken&&e.cancelToken.throwIfRequested()}var o=n(0),a=n(96),i=n(26),u=n(7),l=n(97),c=n(98);e.exports=function(e){return r(e),e.baseURL&&!l(e.url)&&(e.url=c(e.baseURL,e.url)),e.headers=e.headers||{},e.data=a(e.data,e.headers,e.transformRequest),e.headers=o.merge(e.headers.common||{},e.headers[e.method]||{},e.headers||{}),o.forEach(["delete","get","head","post","put","patch","common"],function(t){delete e.headers[t]}),(e.adapter||u.adapter)(e).then(function(t){return r(e),t.data=a(t.data,t.headers,e.transformResponse),t},function(t){return i(t)||(r(e),t&&t.response&&(t.response.data=a(t.response.data,t.response.headers,e.transformResponse))),Promise.reject(t)})}},function(e,t,n){"use strict";var r=n(0);e.exports=function(e,t,n){return r.forEach(n,function(n){e=n(e,t)}),e}},function(e,t,n){"use strict";e.exports=function(e){return/^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(e)}},function(e,t,n){"use strict";e.exports=function(e,t){return t?e.replace(/\/+$/,"")+"/"+t.replace(/^\/+/,""):e}},function(e,t,n){"use strict";function r(e){if("function"!=typeof e)throw new TypeError("executor must be a function.");var t;this.promise=new Promise(function(e){t=e});var n=this;e(function(e){n.reason||(n.reason=new o(e),t(n.reason))})}var o=n(27);r.prototype.throwIfRequested=function(){if(this.reason)throw this.reason},r.source=function(){var e;return{token:new r(function(t){e=t}),cancel:e}},e.exports=r},function(e,t,n){"use strict";e.exports=function(e){return function(t){return e.apply(null,t)}}},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var o=n(1),a=r(o),i=(n(3),n(102)),u=r(i),l=n(104),c=r(l),s=function(e){var t=e.data,n=t.city,r=t.list,o=n.name;return a.default.createElement("div",{className:"weather-forecast-wrapper"},a.default.createElement(c.default,{city:o}),a.default.createElement(u.default,{forecasts:r}))};t.default=s},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t1&&void 0!==arguments[1]?arguments[1]:[],n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[],r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:[];e.map(function(e){n.push(e.main.temp_max),t.push(e.main.temp_min),r.push(e.main.humidity)});var a={min:Math.round(Math.min.apply(Math,o(t))),max:Math.round(Math.max.apply(Math,o(n)))},i=Math.round(r.reduce(function(e,t){return e+t})/r.length);return s.default.createElement("div",{className:"weather-info"},s.default.createElement("div",{className:"min-max"},s.default.createElement("strong",null,a.max+"°C")," / ",a.min+"°C"),s.default.createElement("div",{className:"more-info"},"Avg. Humidity: "+i+"%"))},r._showMoreInfo=function(e){var t=r.refs["div-"+e],n=document.querySelector(".expanded");t.classList.add("expanded"),null!==n&&n.classList.remove("expanded")},u=n,i(r,u)}return u(t,e),l(t,[{key:"render",value:function(){var e=this,t=this.props.forecasts,n=Object.values(this._groupByDays(t)),r=n.length>5?n.slice(0,5):n;return s.default.createElement("div",{className:"forecast-tiles"},r.map(function(t,n){return s.default.createElement("div",{className:"forecast-tile tile-"+n,key:n,ref:"div-"+n,onClick:function(){e._showMoreInfo(n)}},s.default.createElement("div",{className:"primary-info"},s.default.createElement("div",{className:"icon"},s.default.createElement("img",{src:e._getIcon(t)}),e._getDayInfo(t)),e._getInfo(t)),s.default.createElement("div",{className:"detailed-info",key:n},s.default.createElement(p.default,{data:t})))}))}}]),t}(c.Component);t.default=d},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(1),o=function(e){return e&&e.__esModule?e:{default:e}}(r),a=function(e){var t=e.data,n=function(e,t){return o.default.createElement("div",{className:"hourly-info",key:t},o.default.createElement("div",{className:"hour-temperature"},Math.round(e.main.temp)+"°C"),o.default.createElement("div",{className:"hour-of-the-day"},new Date(1e3*e.dt).getHours()+":00"))},r=function(e){return new Date(e).getHours()},a=function(e){return e?new Date(e).getDate():(new Date).getDate()};return o.default.createElement("div",{className:"hourly"},t.map(function(e,t){return r(1e3*e.dt)>(new Date).getHours()&&a(1e3*e.dt)===a()?n(e,t):r(1e3*e.dt)>=5&&r(1e3*e.dt)<=23?n(e,t):null}))};t.default=a},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function a(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var i,u,l=function(){function e(e,t){for(var n=0;n",className:"search",onClick:this._updateCity,id:"change-city-btn"}))),s.default.createElement("span",{className:"error"},"Please enter valid city name!"))}}]),t}(c.Component))||u;t.default=d},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var o=n(6),a=n(106),i=n(107),u=r(i),l=n(108),c=r(l),s=(0,o.applyMiddleware)(u.default,(0,a.createLogger)()),f=(0,o.compose)(o.createStore)(c.default,s);t.default=f},function(e,t,n){(function(e){!function(e,n){n(t)}(0,function(t){"use strict";function n(e,t){e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}})}function r(e,t){Object.defineProperty(this,"kind",{value:e,enumerable:!0}),t&&t.length&&Object.defineProperty(this,"path",{value:t,enumerable:!0})}function o(e,t,n){o.super_.call(this,"E",e),Object.defineProperty(this,"lhs",{value:t,enumerable:!0}),Object.defineProperty(this,"rhs",{value:n,enumerable:!0})}function a(e,t){a.super_.call(this,"N",e),Object.defineProperty(this,"rhs",{value:t,enumerable:!0})}function i(e,t){i.super_.call(this,"D",e),Object.defineProperty(this,"lhs",{value:t,enumerable:!0})}function u(e,t,n){u.super_.call(this,"A",e),Object.defineProperty(this,"index",{value:t,enumerable:!0}),Object.defineProperty(this,"item",{value:n,enumerable:!0})}function l(e,t,n){var r=e.slice((n||t)+1||e.length);return e.length=t<0?e.length+t:t,e.push.apply(e,r),e}function c(e){var t=void 0===e?"undefined":N(e);return"object"!==t?t:e===Math?"math":null===e?"null":Array.isArray(e)?"array":"[object Date]"===Object.prototype.toString.call(e)?"date":"function"==typeof e.toString&&/^\/.*\//.test(e.toString())?"regexp":"object"}function s(e,t,n,r,f,p,d){f=f||[],d=d||[];var h=f.slice(0);if(void 0!==p){if(r){if("function"==typeof r&&r(h,p))return;if("object"===(void 0===r?"undefined":N(r))){if(r.prefilter&&r.prefilter(h,p))return;if(r.normalize){var m=r.normalize(h,p,e,t);m&&(e=m[0],t=m[1])}}}h.push(p)}"regexp"===c(e)&&"regexp"===c(t)&&(e=e.toString(),t=t.toString());var y=void 0===e?"undefined":N(e),v=void 0===t?"undefined":N(t),g="undefined"!==y||d&&d[d.length-1].lhs&&d[d.length-1].lhs.hasOwnProperty(p),b="undefined"!==v||d&&d[d.length-1].rhs&&d[d.length-1].rhs.hasOwnProperty(p);if(!g&&b)n(new a(h,t));else if(!b&&g)n(new i(h,e));else if(c(e)!==c(t))n(new o(h,e,t));else if("date"===c(e)&&e-t!=0)n(new o(h,e,t));else if("object"===y&&null!==e&&null!==t)if(d.filter(function(t){return t.lhs===e}).length)e!==t&&n(new o(h,e,t));else{if(d.push({lhs:e,rhs:t}),Array.isArray(e)){var w;for(e.length,w=0;w=t.length?n(new u(h,w,new i(void 0,e[w]))):s(e[w],t[w],n,r,h,w,d);for(;w=0?(s(e[o],t[o],n,r,h,o,d),E=l(E,i)):s(e[o],void 0,n,r,h,o,d)}),E.forEach(function(e){s(void 0,t[e],n,r,h,e,d)})}d.length=d.length-1}else e!==t&&("number"===y&&isNaN(e)&&isNaN(t)||n(new o(h,e,t)))}function f(e,t,n,r){return r=r||[],s(e,t,function(e){e&&r.push(e)},n),r.length?r:void 0}function p(e,t,n){if(n.path&&n.path.length){var r,o=e[t],a=n.path.length-1;for(r=0;r0&&void 0!==arguments[0]?arguments[0]:{},t=Object.assign({},A,e),n=t.logger,r=t.stateTransformer,o=t.errorTransformer,a=t.predicate,i=t.logErrors,u=t.diffPredicate;if(void 0===n)return function(){return function(e){return function(t){return e(t)}}};if(e.getState&&e.dispatch)return console.error("[redux-logger] redux-logger not installed. Make sure to pass logger instance as middleware:\n// Logger with default options\nimport { logger } from 'redux-logger'\nconst store = createStore(\n reducer,\n applyMiddleware(logger)\n)\n// Or you can create your own logger with custom options http://bit.ly/redux-logger-options\nimport createLogger from 'redux-logger'\nconst logger = createLogger({\n // ...options\n});\nconst store = createStore(\n reducer,\n applyMiddleware(logger)\n)\n"),function(){return function(e){return function(t){return e(t)}}};var l=[];return function(e){var n=e.getState;return function(e){return function(c){if("function"==typeof a&&!a(n,c))return e(c);var s={};l.push(s),s.started=_.now(),s.startedTime=new Date,s.prevState=r(n()),s.action=c;var f=void 0;if(i)try{f=e(c)}catch(e){s.error=o(e)}else f=e(c);s.took=_.now()-s.started,s.nextState=r(n());var p=t.diff&&"function"==typeof u?u(n,c):t.diff;if(E(l,Object.assign({},t,{diff:p})),l.length=0,s.error)throw s.error;return f}}}}var k,T,S=function(e,t){return new Array(t+1).join(e)},O=function(e,t){return S("0",t-e.toString().length)+e},P=function(e){return O(e.getHours(),2)+":"+O(e.getMinutes(),2)+":"+O(e.getSeconds(),2)+"."+O(e.getMilliseconds(),3)},_="undefined"!=typeof performance&&null!==performance&&"function"==typeof performance.now?performance:Date,N="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},j=function(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t0&&void 0!==arguments[0]?arguments[0]:{},t=e.dispatch,n=e.getState;return"function"==typeof t||"function"==typeof n?x()({dispatch:t,getState:n}):void console.error("\n[redux-logger v3] BREAKING CHANGE\n[redux-logger v3] Since 3.0.0 redux-logger exports by default logger with default settings.\n[redux-logger v3] Change\n[redux-logger v3] import createLogger from 'redux-logger'\n[redux-logger v3] to\n[redux-logger v3] import { createLogger } from 'redux-logger'\n")};t.defaults=A,t.createLogger=x,t.logger=M,t.default=M,Object.defineProperty(t,"__esModule",{value:!0})})}).call(t,n(4))},function(e,t,n){"use strict";function r(e){return function(t){var n=t.dispatch,r=t.getState;return function(t){return function(o){return"function"==typeof o?o(n,r,e):t(o)}}}}t.__esModule=!0;var o=r();o.withExtraArgument=r,t.default=o},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(6),o=n(109),a=function(e){return e&&e.__esModule?e:{default:e}}(o);t.default=(0,r.combineReducers)({weatherStation:a.default})},function(e,t,n){"use strict";function r(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{data:null,status:null},t=arguments[1];switch(t.type){case a.FETCH_DATA_FULFILLED:return o({},e,{data:t.payload,status:"success"});case a.FETCH_DATA_REJECTED:return o({},e,{status:"failed"})}return e}Object.defineProperty(t,"__esModule",{value:!0});var o=Object.assign||function(e){for(var t=1;t 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 5-Days Weather Forecast 12 | 13 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | 3 | import { connect } from "react-redux"; 4 | import { fetchData } from "./actions/weatherStation"; 5 | 6 | import WeatherForecast from './components/WeatherForecast'; 7 | 8 | @connect(store => { 9 | return { 10 | forecast: store.weatherStation.data 11 | } 12 | }) 13 | export default class App extends Component { 14 | 15 | // Fetches data by using geolocation. If the user blocks, or if the browser does not support the API, 16 | // fallsback to default location of London 17 | componentDidMount() { 18 | const detectLocation = new Promise((resolve,reject) => { 19 | if ("geolocation" in navigator) { 20 | navigator.geolocation.getCurrentPosition((position) => { 21 | resolve(position.coords); 22 | }, (error) => { 23 | if(error.code === error.PERMISSION_DENIED) { 24 | console.error("Error detecting location."); 25 | } 26 | }); 27 | } 28 | }); 29 | 30 | detectLocation.then((location) => { 31 | this.props.dispatch(fetchData(location)); 32 | }).catch(() => { 33 | this.props.dispatch(fetchData("london")); 34 | }); 35 | } 36 | 37 | render() { 38 | const { forecast } = this.props; 39 | 40 | return ( 41 | forecast === null ? ( 42 |
43 |
44 |
45 | ) : ( 46 |
47 | 48 |
49 | Fork it on Github 50 |
51 |
52 | ) 53 | ); 54 | } 55 | } -------------------------------------------------------------------------------- /src/actions/weatherStation.js: -------------------------------------------------------------------------------- 1 | import { FETCH_DATA_FULFILLED, FETCH_DATA_REJECTED } from "../constants/ActionTypes"; 2 | import { APP_ID } from "../constants/generalConstants"; 3 | 4 | import axios from "axios"; 5 | 6 | export const fetchData = (region) => (dispatch) => { 7 | const { latitude, longitude } = region || {}; 8 | 9 | const getDataByCity = `https://api.openweathermap.org/data/2.5/forecast?q=${region}&units=metric&appid=${APP_ID}`; 10 | const getDataByCoords = `https://api.openweathermap.org/data/2.5/forecast?lat=${latitude}&lon=${longitude}&units=metric&appid=${APP_ID}`; 11 | 12 | let location = typeof(region) === "object" ? getDataByCoords : getDataByCity; 13 | 14 | return axios.get(location) 15 | .then((response) => { 16 | dispatch({type: FETCH_DATA_FULFILLED, payload: response.data}); 17 | }) 18 | .catch((err) => { 19 | dispatch({type: FETCH_DATA_REJECTED, payload: err}); // Error handling 20 | }); 21 | }; -------------------------------------------------------------------------------- /src/components/Dashboard.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { connect } from "react-redux"; 3 | import { fetchData } from "../actions/weatherStation"; 4 | 5 | @connect((store) => { 6 | return { 7 | status: store.weatherStation.status 8 | } 9 | }) 10 | export default class Dashboard extends Component { 11 | 12 | _updateCity = () => { 13 | const city = this.__cityInput.value; 14 | city.length !== 0 ? this.props.dispatch(fetchData(city)) : null; 15 | } 16 | 17 | _onkeyPress = e => { 18 | e.key === "Enter" ? this._updateCity() : null 19 | } 20 | 21 | render() { 22 | 23 | const { city, status } = this.props; 24 | const wrapperClass = (status === "failed") ? "weather-dashboard invalid-city" : "weather-dashboard"; 25 | 26 | return ( 27 |
28 |
29 |

5-Day Weather Forecast

30 |
31 |
32 |
33 | { 38 | this.__cityInput = input; 39 | return this.__cityInput; 40 | }} 41 | onKeyPress={this._onkeyPress} 42 | placeholder={city} 43 | /> 44 | 51 |
52 |
53 | Please enter valid city name! 54 |
55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/components/DetailedInfo.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const DetailedInfo = ({ data }) => { 4 | 5 | const getHour = time => time ? new Date(time).getHours() : new Date().getHours(); 6 | const getDate = date => date ? new Date(date).getDate() : new Date().getDate(); 7 | 8 | const displayMoreInfo = (item, i) => { 9 | return ( 10 |
11 |
12 | {`${Math.round(item.main.temp)}°C`} 13 |
14 |
15 | {`${getHour(item.dt * 1000)}:00`} 16 |
17 |
18 | ); 19 | }; 20 | 21 | return ( 22 |
23 | {data.map((item, i) => ( 24 | (getHour(item.dt * 1000) > getHour() && getDate(item.dt * 1000) === getDate()) ? ( 25 | displayMoreInfo(item, i) 26 | ) : getHour(item.dt * 1000) >= 5 && getHour(item.dt * 1000) <= 23 ? ( 27 | displayMoreInfo(item, i) 28 | ) : null 29 | ))} 30 |
31 | ); 32 | }; 33 | 34 | export default DetailedInfo; 35 | -------------------------------------------------------------------------------- /src/components/ForecastTiles.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import DetailedInfo from "./DetailedInfo"; 3 | 4 | export default class ForecastTiles extends Component { 5 | 6 | // Filters the data by date and returns an Object containing a list of 5-day forecast. 7 | _groupByDays = data => { 8 | return (data.reduce((list, item) => { 9 | const forecastDate = item.dt_txt.substr(0,10); 10 | list[forecastDate] = list[forecastDate] || []; 11 | list[forecastDate].push(item); 12 | 13 | return list; 14 | }, {})); 15 | }; 16 | 17 | // Returns week of the day 18 | _getDayInfo = data => { 19 | const daysOfWeek = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"]; 20 | return daysOfWeek[new Date(data[0].dt * 1000).getDay()]; 21 | }; 22 | 23 | // Fetches the icon using the icon code available in the forecast data. 24 | _getIcon = data => `https://openweathermap.org/img/w/${data[0].weather[0].icon}.png`; 25 | 26 | // Gets the Minimum, Maximum and Avg Humidity temperatures of the day. 27 | _getInfo = (data, min=[], max=[], humidity=[]) => { 28 | data.map(item => { 29 | max.push(item.main.temp_max); 30 | min.push(item.main.temp_min); 31 | humidity.push(item.main.humidity); 32 | }); 33 | 34 | const minMax = { 35 | min: Math.round(Math.min(...min)), 36 | max: Math.round(Math.max(...max)), 37 | }; 38 | 39 | // Gets the day's average humdity 40 | const avgHumdity = Math.round(humidity.reduce((curr, next) => curr + next) / humidity.length); 41 | 42 | return ( 43 |
44 |
45 | {`${minMax.max}°C`} / {`${minMax.min}°C`} 46 |
47 |
48 | {`Avg. Humidity: ${avgHumdity}%`} 49 |
50 |
51 | ); 52 | }; 53 | 54 | // Toggles accordion to display hourly weather information 55 | _showMoreInfo = (index) => { 56 | const elm = this.refs[`div-${index}`]; 57 | const expandedElment = document.querySelector(".expanded"); 58 | 59 | elm.classList.add("expanded"); 60 | expandedElment !== null && expandedElment.classList.remove("expanded"); 61 | } 62 | 63 | render() { 64 | 65 | const { forecasts } = this.props; 66 | const tiles = Object.values(this._groupByDays(forecasts)); 67 | 68 | // Edge case: 69 | // When the webservice returns data for 6 calendar days during evenings as a result of offset, 70 | // this ensures that we are showing only 5-days of forecast. 71 | const forecastTiles = tiles.length > 5 ? tiles.slice(0, 5) : tiles; 72 | 73 | return ( 74 |
75 | {forecastTiles.map((item, i) => ( 76 |
{this._showMoreInfo(i)}} 81 | > 82 |
83 |
84 | 85 | {this._getDayInfo(item)} 86 |
87 | {this._getInfo(item)} 88 |
89 |
90 | 91 |
92 |
93 | ))} 94 |
95 | ); 96 | } 97 | } 98 | // TODO: Add defaultProps and PropType validations 99 | -------------------------------------------------------------------------------- /src/components/WeatherForecast.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { connect } from "react-redux"; 3 | 4 | import ForecastTiles from "./ForecastTiles"; 5 | import Dashboard from "./Dashboard"; 6 | 7 | const WeatherForecast = ({ data }) => { 8 | 9 | const { city, list } = data; 10 | const { name } = city; 11 | 12 | return ( 13 |
14 | 15 | 16 |
17 | ); 18 | }; 19 | 20 | export default WeatherForecast; -------------------------------------------------------------------------------- /src/constants/ActionTypes.js: -------------------------------------------------------------------------------- 1 | // ActionTypes are defined in constants. 2 | 3 | // They are imported in Actions and Reducers. 4 | // This prevents errors if they are misspelled over here. 5 | 6 | /* eslint-disable */ 7 | 8 | export const FETCH_DATA_FULFILLED = "FETCH_DATA_FULFILLED"; 9 | export const FETCH_DATA_REJECTED = "FETCH_DATA_REJECTED"; 10 | 11 | /* eslint-enable */ -------------------------------------------------------------------------------- /src/constants/generalConstants.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | export const APP_ID = ""; 4 | 5 | /* eslint-enable */ 6 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /* global document */ 2 | 3 | import React from "react"; 4 | import { render } from "react-dom"; 5 | import { Provider } from "react-redux"; 6 | 7 | import App from "./App"; 8 | import store from "./store"; 9 | 10 | render( 11 | 12 | 13 | , document.getElementById("root") 14 | ); -------------------------------------------------------------------------------- /src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from "redux"; 2 | 3 | import weatherStation from "./weatherStation"; 4 | 5 | export default combineReducers({ weatherStation }); 6 | -------------------------------------------------------------------------------- /src/reducers/weatherStation.js: -------------------------------------------------------------------------------- 1 | import { FETCH_DATA_FULFILLED, FETCH_DATA_REJECTED } from "../constants/ActionTypes"; 2 | 3 | export default function reducer(state = { 4 | data: null, 5 | status: null 6 | }, action) { 7 | switch (action.type) { 8 | case FETCH_DATA_FULFILLED: { 9 | return { 10 | ...state, 11 | data: action.payload, 12 | status: "success" 13 | }; 14 | break; 15 | } 16 | case FETCH_DATA_REJECTED: { 17 | return { 18 | ...state, 19 | status: "failed" 20 | }; 21 | 22 | console.error(`Could not fetch the data from webservice. ${action.payload}.`); // eslint-disable-line 23 | break; 24 | } 25 | } 26 | 27 | return state; 28 | } -------------------------------------------------------------------------------- /src/store.js: -------------------------------------------------------------------------------- 1 | import { compose, applyMiddleware, createStore } from "redux"; 2 | import { createLogger } from "redux-logger"; 3 | 4 | import thunk from "redux-thunk"; 5 | import reducer from "./reducers"; 6 | 7 | const middleware = applyMiddleware(thunk, createLogger()); 8 | let store = compose(createStore)(reducer, middleware); 9 | 10 | export default store; -------------------------------------------------------------------------------- /src/styles/main.scss: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | /* HTML5 display-role reset for older browsers */ 27 | article, aside, details, figcaption, figure, 28 | footer, header, hgroup, menu, nav, section { 29 | display: block; 30 | } 31 | body { 32 | line-height: 1; 33 | } 34 | ol, ul { 35 | list-style: none; 36 | } 37 | blockquote, q { 38 | quotes: none; 39 | } 40 | blockquote:before, blockquote:after, 41 | q:before, q:after { 42 | content: ''; 43 | content: none; 44 | } 45 | table { 46 | border-collapse: collapse; 47 | border-spacing: 0; 48 | } 49 | 50 | hr { 51 | border: 2px solid #666; 52 | } 53 | 54 | body { 55 | background: #3f51b5; 56 | color: #fff; 57 | font-family: 'Roboto', sans-serif; 58 | } 59 | 60 | .loading { 61 | height: 100%; 62 | width: 100%; 63 | position: absolute; 64 | display: flex; 65 | align-items: center; 66 | justify-content: center; 67 | 68 | .spinner { 69 | background: url('https://gigacore.in/demos/react-weather-forecast/assets/loader.gif') no-repeat; 70 | background-size: 100%; 71 | height: 100px; 72 | width: 100px; 73 | } 74 | } 75 | 76 | .weather-forecast-wrapper { 77 | margin: 0 auto; 78 | width: 40%; 79 | 80 | @media only screen and (max-width: 64em) { 81 | width: 60%; 82 | } 83 | 84 | @media only screen and (max-width: 48em) { 85 | width: 100%; 86 | } 87 | 88 | header { 89 | text-align: center; 90 | 91 | .heading { 92 | font-size: 22px; 93 | padding: 25px 0 0 0; 94 | text-transform: uppercase; 95 | } 96 | } 97 | 98 | .weather-dashboard { 99 | text-align: center; 100 | 101 | .error { 102 | display: none; 103 | } 104 | 105 | section.controls { 106 | display: flex; 107 | align-items: center; 108 | justify-content: center; 109 | height: 100px; 110 | 111 | input { 112 | font-size: 13px; 113 | padding: 10px; 114 | display: inline-block; 115 | background: #283593; 116 | border: none; 117 | color: #fff; 118 | width: 230px; 119 | text-transform: uppercase; 120 | 121 | &::placeholder { 122 | color: #fff; 123 | } 124 | 125 | &:focus { 126 | &::placeholder { 127 | color: transparent; 128 | } 129 | } 130 | 131 | &.search { 132 | width: 30px; 133 | cursor: pointer; 134 | } 135 | } 136 | } 137 | 138 | &.invalid-city { 139 | section.controls { 140 | input { 141 | background: #ff1744; 142 | } 143 | } 144 | 145 | .error { 146 | display: block; 147 | color: #ff1744; 148 | padding: 0 0 30px 0; 149 | } 150 | } 151 | } 152 | 153 | .forecast-tiles { 154 | width: 100%; 155 | 156 | .forecast-tile { 157 | min-height: 100px; 158 | background: #303f9f; 159 | margin: 3px; 160 | cursor: pointer; 161 | overflow: hidden; 162 | position: relative; 163 | 164 | :after { 165 | content: ""; 166 | width: 0; 167 | height: 0; 168 | position: absolute; 169 | right: 10px; 170 | top: 48px; 171 | border-left: 5px solid transparent; 172 | border-right: 5px solid transparent; 173 | border-top: 5px solid #fff; 174 | } 175 | 176 | .icon { 177 | width: 100px; 178 | height: 100px; 179 | display: flex; 180 | justify-content: center; 181 | align-items: center; 182 | background: #283593; 183 | flex-direction: column; 184 | text-transform: uppercase; 185 | font-size: 12px; 186 | } 187 | 188 | .weather-info { 189 | padding: 0 0 0 15px; 190 | display: flex; 191 | align-items: left; 192 | flex-direction: column; 193 | justify-content: center; 194 | 195 | .min-max { 196 | padding: 10px 10px; 197 | font-size: 20px; 198 | color: #c7cef5; 199 | 200 | strong { 201 | font-weight: bold; 202 | color: #fff; 203 | } 204 | } 205 | 206 | .more-info { 207 | padding: 0 10px; 208 | font-size: 12px; 209 | } 210 | } 211 | 212 | .primary-info { 213 | display: flex; 214 | } 215 | 216 | &.collapsed { 217 | .detailed-info { 218 | height: 0; 219 | } 220 | } 221 | 222 | &.expanded { 223 | .detailed-info { 224 | height: 100px; 225 | } 226 | 227 | :after { 228 | border-left: 5px solid transparent; 229 | border-right: 5px solid transparent; 230 | border-bottom: 5px solid #fff; 231 | border-top: none; 232 | } 233 | } 234 | 235 | .detailed-info { 236 | width: 100%; 237 | height: 0; 238 | -webkit-transition: height 0.20s ease-in; 239 | -moz-transition: height 0.20s ease-in; 240 | -o-transition: height 0.20s ease-in; 241 | transition: height 0.20s ease-in; 242 | background: #283593; 243 | 244 | .hourly { 245 | display: flex; 246 | padding: 25px 0 20px 10px; 247 | 248 | .hourly-info { 249 | display: flex; 250 | flex-direction: column; 251 | width: 20%; 252 | align-items: center; 253 | justify-content: center; 254 | overflow-x: scroll; 255 | 256 | .hour-temperature { 257 | font-size: 16px; 258 | font-weight: bold; 259 | } 260 | 261 | .hour-of-the-day { 262 | font-weight: normal; 263 | font-size: 12px; 264 | color: #c5cae9; 265 | } 266 | 267 | div { 268 | padding: 5px 0; 269 | } 270 | } 271 | } 272 | } 273 | } 274 | } 275 | } 276 | 277 | .fork { 278 | margin: 30px; 279 | text-align: center; 280 | font-size: 12px; 281 | text-transform: uppercase; 282 | 283 | a { 284 | color: #fff; 285 | text-decoration: none; 286 | text-align: center; 287 | } 288 | } 289 | -------------------------------------------------------------------------------- /src/test/__tests__/dashboard.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render, mount, shallow } from "enzyme"; 3 | 4 | import configureStore from "redux-mock-store"; 5 | const mockStore = configureStore(); 6 | 7 | import Dashboard from "../../components/Dashboard"; 8 | const STATUS = "success"; 9 | 10 | describe("", () => { 11 | it("renders an `.weather-dashboard`", () => { 12 | const wrapper = render(); 13 | expect(wrapper.hasClass("weather-dashboard")).toBe(true); 14 | }); 15 | 16 | it("should contain a input field", () => { 17 | const wrapper = render(); 18 | expect(wrapper.find(".city-input")).toHaveLength(1); 19 | }); 20 | 21 | it("should contain a change city button", () => { 22 | const wrapper = render(); 23 | expect(wrapper.find("#change-city-btn")).toHaveLength(1); 24 | }); 25 | 26 | it("should contain app heading", () => { 27 | const wrapper = mount(); 28 | const heading =

5-Day Weather Forecast

; 29 | expect(wrapper.contains(heading)).toEqual(true); 30 | }); 31 | 32 | it("should receive city prop", () => { 33 | const wrapper = shallow(); 34 | expect(wrapper.prop("city")).toBeDefined(); 35 | }); 36 | }); -------------------------------------------------------------------------------- /src/test/__tests__/data/forecast.json: -------------------------------------------------------------------------------- 1 | { 2 | "weatherStation": { 3 | "data": { 4 | "cod": "200", 5 | "message": 0.0193, 6 | "cnt": 40, 7 | "list": [{ 8 | "dt": 1519214400, 9 | "main": { 10 | "temp": 7.46, 11 | "temp_min": 6.06, 12 | "temp_max": 7.46, 13 | "pressure": 1033.16, 14 | "sea_level": 1040.92, 15 | "grnd_level": 1033.16, 16 | "humidity": 84, 17 | "temp_kf": 1.4 18 | }, 19 | "weather": [{ 20 | "id": 800, 21 | "main": "Clear", 22 | "description": "clear sky", 23 | "icon": "02d" 24 | }], 25 | "clouds": { 26 | "all": 8 27 | }, 28 | "wind": { 29 | "speed": 2.86, 30 | "deg": 52.5056 31 | }, 32 | "sys": { 33 | "pod": "d" 34 | }, 35 | "dt_txt": "2018-02-21 12:00:00" 36 | }, { 37 | "dt": 1519225200, 38 | "main": { 39 | "temp": 7.23, 40 | "temp_min": 6.18, 41 | "temp_max": 7.23, 42 | "pressure": 1032.14, 43 | "sea_level": 1039.97, 44 | "grnd_level": 1032.14, 45 | "humidity": 77, 46 | "temp_kf": 1.05 47 | }, 48 | "weather": [{ 49 | "id": 500, 50 | "main": "Rain", 51 | "description": "light rain", 52 | "icon": "10d" 53 | }], 54 | "clouds": { 55 | "all": 80 56 | }, 57 | "wind": { 58 | "speed": 2.87, 59 | "deg": 51.0042 60 | }, 61 | "rain": { 62 | "3h": 0.005 63 | }, 64 | "sys": { 65 | "pod": "d" 66 | }, 67 | "dt_txt": "2018-02-21 15:00:00" 68 | }, { 69 | "dt": 1519236000, 70 | "main": { 71 | "temp": 5.27, 72 | "temp_min": 4.57, 73 | "temp_max": 5.27, 74 | "pressure": 1032.16, 75 | "sea_level": 1039.97, 76 | "grnd_level": 1032.16, 77 | "humidity": 76, 78 | "temp_kf": 0.7 79 | }, 80 | "weather": [{ 81 | "id": 500, 82 | "main": "Rain", 83 | "description": "light rain", 84 | "icon": "10n" 85 | }], 86 | "clouds": { 87 | "all": 76 88 | }, 89 | "wind": { 90 | "speed": 2.63, 91 | "deg": 55.0046 92 | }, 93 | "rain": { 94 | "3h": 0.0975 95 | }, 96 | "sys": { 97 | "pod": "n" 98 | }, 99 | "dt_txt": "2018-02-21 18:00:00" 100 | }, { 101 | "dt": 1519246800, 102 | "main": { 103 | "temp": 3.31, 104 | "temp_min": 2.96, 105 | "temp_max": 3.31, 106 | "pressure": 1032.56, 107 | "sea_level": 1040.49, 108 | "grnd_level": 1032.56, 109 | "humidity": 79, 110 | "temp_kf": 0.35 111 | }, 112 | "weather": [{ 113 | "id": 500, 114 | "main": "Rain", 115 | "description": "light rain", 116 | "icon": "10n" 117 | }], 118 | "clouds": { 119 | "all": 44 120 | }, 121 | "wind": { 122 | "speed": 2.76, 123 | "deg": 65.0004 124 | }, 125 | "rain": { 126 | "3h": 0.0225 127 | }, 128 | "sys": { 129 | "pod": "n" 130 | }, 131 | "dt_txt": "2018-02-21 21:00:00" 132 | }, { 133 | "dt": 1519257600, 134 | "main": { 135 | "temp": 1.5, 136 | "temp_min": 1.5, 137 | "temp_max": 1.5, 138 | "pressure": 1032.5, 139 | "sea_level": 1040.52, 140 | "grnd_level": 1032.5, 141 | "humidity": 87, 142 | "temp_kf": 0 143 | }, 144 | "weather": [{ 145 | "id": 500, 146 | "main": "Rain", 147 | "description": "light rain", 148 | "icon": "10n" 149 | }], 150 | "clouds": { 151 | "all": 56 152 | }, 153 | "wind": { 154 | "speed": 2.56, 155 | "deg": 56.502 156 | }, 157 | "rain": { 158 | "3h": 0.025 159 | }, 160 | "sys": { 161 | "pod": "n" 162 | }, 163 | "dt_txt": "2018-02-22 00:00:00" 164 | }, { 165 | "dt": 1519268400, 166 | "main": { 167 | "temp": 0.95, 168 | "temp_min": 0.95, 169 | "temp_max": 0.95, 170 | "pressure": 1031.73, 171 | "sea_level": 1039.67, 172 | "grnd_level": 1031.73, 173 | "humidity": 91, 174 | "temp_kf": 0 175 | }, 176 | "weather": [{ 177 | "id": 500, 178 | "main": "Rain", 179 | "description": "light rain", 180 | "icon": "10n" 181 | }], 182 | "clouds": { 183 | "all": 44 184 | }, 185 | "wind": { 186 | "speed": 2.86, 187 | "deg": 54.0018 188 | }, 189 | "rain": { 190 | "3h": 0.045 191 | }, 192 | "snow": { 193 | "3h": 0.005 194 | }, 195 | "sys": { 196 | "pod": "n" 197 | }, 198 | "dt_txt": "2018-02-22 03:00:00" 199 | }, { 200 | "dt": 1519279200, 201 | "main": { 202 | "temp": 0.19, 203 | "temp_min": 0.19, 204 | "temp_max": 0.19, 205 | "pressure": 1031.25, 206 | "sea_level": 1039.2, 207 | "grnd_level": 1031.25, 208 | "humidity": 90, 209 | "temp_kf": 0 210 | }, 211 | "weather": [{ 212 | "id": 500, 213 | "main": "Rain", 214 | "description": "light rain", 215 | "icon": "10n" 216 | }], 217 | "clouds": { 218 | "all": 76 219 | }, 220 | "wind": { 221 | "speed": 2.7, 222 | "deg": 51.5021 223 | }, 224 | "rain": { 225 | "3h": 0.005 226 | }, 227 | "snow": {}, 228 | "sys": { 229 | "pod": "n" 230 | }, 231 | "dt_txt": "2018-02-22 06:00:00" 232 | }, { 233 | "dt": 1519290000, 234 | "main": { 235 | "temp": 2.35, 236 | "temp_min": 2.35, 237 | "temp_max": 2.35, 238 | "pressure": 1031.64, 239 | "sea_level": 1039.65, 240 | "grnd_level": 1031.64, 241 | "humidity": 80, 242 | "temp_kf": 0 243 | }, 244 | "weather": [{ 245 | "id": 800, 246 | "main": "Clear", 247 | "description": "clear sky", 248 | "icon": "01d" 249 | }], 250 | "clouds": { 251 | "all": 80 252 | }, 253 | "wind": { 254 | "speed": 2.79, 255 | "deg": 73.5076 256 | }, 257 | "rain": {}, 258 | "snow": { 259 | "3h": 0.0025 260 | }, 261 | "sys": { 262 | "pod": "d" 263 | }, 264 | "dt_txt": "2018-02-22 09:00:00" 265 | }, { 266 | "dt": 1519300800, 267 | "main": { 268 | "temp": 3.89, 269 | "temp_min": 3.89, 270 | "temp_max": 3.89, 271 | "pressure": 1031.73, 272 | "sea_level": 1039.53, 273 | "grnd_level": 1031.73, 274 | "humidity": 76, 275 | "temp_kf": 0 276 | }, 277 | "weather": [{ 278 | "id": 803, 279 | "main": "Clouds", 280 | "description": "broken clouds", 281 | "icon": "04d" 282 | }], 283 | "clouds": { 284 | "all": 76 285 | }, 286 | "wind": { 287 | "speed": 4.05, 288 | "deg": 92.5016 289 | }, 290 | "rain": {}, 291 | "snow": {}, 292 | "sys": { 293 | "pod": "d" 294 | }, 295 | "dt_txt": "2018-02-22 12:00:00" 296 | }, { 297 | "dt": 1519311600, 298 | "main": { 299 | "temp": 3.87, 300 | "temp_min": 3.87, 301 | "temp_max": 3.87, 302 | "pressure": 1030.82, 303 | "sea_level": 1038.65, 304 | "grnd_level": 1030.82, 305 | "humidity": 74, 306 | "temp_kf": 0 307 | }, 308 | "weather": [{ 309 | "id": 500, 310 | "main": "Rain", 311 | "description": "light rain", 312 | "icon": "10d" 313 | }], 314 | "clouds": { 315 | "all": 8 316 | }, 317 | "wind": { 318 | "speed": 4.21, 319 | "deg": 92.002 320 | }, 321 | "rain": { 322 | "3h": 0.005 323 | }, 324 | "snow": {}, 325 | "sys": { 326 | "pod": "d" 327 | }, 328 | "dt_txt": "2018-02-22 15:00:00" 329 | }, { 330 | "dt": 1519322400, 331 | "main": { 332 | "temp": 0.95, 333 | "temp_min": 0.95, 334 | "temp_max": 0.95, 335 | "pressure": 1030.75, 336 | "sea_level": 1038.74, 337 | "grnd_level": 1030.75, 338 | "humidity": 71, 339 | "temp_kf": 0 340 | }, 341 | "weather": [{ 342 | "id": 800, 343 | "main": "Clear", 344 | "description": "clear sky", 345 | "icon": "01n" 346 | }], 347 | "clouds": { 348 | "all": 0 349 | }, 350 | "wind": { 351 | "speed": 3.56, 352 | "deg": 85.5025 353 | }, 354 | "rain": {}, 355 | "snow": {}, 356 | "sys": { 357 | "pod": "n" 358 | }, 359 | "dt_txt": "2018-02-22 18:00:00" 360 | }, { 361 | "dt": 1519333200, 362 | "main": { 363 | "temp": -1.3, 364 | "temp_min": -1.3, 365 | "temp_max": -1.3, 366 | "pressure": 1030.95, 367 | "sea_level": 1038.98, 368 | "grnd_level": 1030.95, 369 | "humidity": 86, 370 | "temp_kf": 0 371 | }, 372 | "weather": [{ 373 | "id": 800, 374 | "main": "Clear", 375 | "description": "clear sky", 376 | "icon": "01n" 377 | }], 378 | "clouds": { 379 | "all": 0 380 | }, 381 | "wind": { 382 | "speed": 2.95, 383 | "deg": 76.5041 384 | }, 385 | "rain": {}, 386 | "snow": {}, 387 | "sys": { 388 | "pod": "n" 389 | }, 390 | "dt_txt": "2018-02-22 21:00:00" 391 | }, { 392 | "dt": 1519344000, 393 | "main": { 394 | "temp": -2.36, 395 | "temp_min": -2.36, 396 | "temp_max": -2.36, 397 | "pressure": 1030.76, 398 | "sea_level": 1038.85, 399 | "grnd_level": 1030.76, 400 | "humidity": 96, 401 | "temp_kf": 0 402 | }, 403 | "weather": [{ 404 | "id": 802, 405 | "main": "Clouds", 406 | "description": "scattered clouds", 407 | "icon": "03n" 408 | }], 409 | "clouds": { 410 | "all": 32 411 | }, 412 | "wind": { 413 | "speed": 2.55, 414 | "deg": 86.5009 415 | }, 416 | "rain": {}, 417 | "snow": {}, 418 | "sys": { 419 | "pod": "n" 420 | }, 421 | "dt_txt": "2018-02-23 00:00:00" 422 | }, { 423 | "dt": 1519354800, 424 | "main": { 425 | "temp": -0.33, 426 | "temp_min": -0.33, 427 | "temp_max": -0.33, 428 | "pressure": 1030.28, 429 | "sea_level": 1038.38, 430 | "grnd_level": 1030.28, 431 | "humidity": 86, 432 | "temp_kf": 0 433 | }, 434 | "weather": [{ 435 | "id": 800, 436 | "main": "Clear", 437 | "description": "clear sky", 438 | "icon": "01n" 439 | }], 440 | "clouds": { 441 | "all": 88 442 | }, 443 | "wind": { 444 | "speed": 2.33, 445 | "deg": 97.5013 446 | }, 447 | "rain": {}, 448 | "snow": { 449 | "3h": 0.005 450 | }, 451 | "sys": { 452 | "pod": "n" 453 | }, 454 | "dt_txt": "2018-02-23 03:00:00" 455 | }, { 456 | "dt": 1519365600, 457 | "main": { 458 | "temp": 0.91, 459 | "temp_min": 0.91, 460 | "temp_max": 0.91, 461 | "pressure": 1029.9, 462 | "sea_level": 1038.04, 463 | "grnd_level": 1029.9, 464 | "humidity": 73, 465 | "temp_kf": 0 466 | }, 467 | "weather": [{ 468 | "id": 800, 469 | "main": "Clear", 470 | "description": "clear sky", 471 | "icon": "01n" 472 | }], 473 | "clouds": { 474 | "all": 88 475 | }, 476 | "wind": { 477 | "speed": 2.86, 478 | "deg": 86.0011 479 | }, 480 | "rain": {}, 481 | "snow": { 482 | "3h": 0.015 483 | }, 484 | "sys": { 485 | "pod": "n" 486 | }, 487 | "dt_txt": "2018-02-23 06:00:00" 488 | }, { 489 | "dt": 1519376400, 490 | "main": { 491 | "temp": 1.4, 492 | "temp_min": 1.4, 493 | "temp_max": 1.4, 494 | "pressure": 1030.12, 495 | "sea_level": 1038.17, 496 | "grnd_level": 1030.12, 497 | "humidity": 71, 498 | "temp_kf": 0 499 | }, 500 | "weather": [{ 501 | "id": 800, 502 | "main": "Clear", 503 | "description": "clear sky", 504 | "icon": "01d" 505 | }], 506 | "clouds": { 507 | "all": 0 508 | }, 509 | "wind": { 510 | "speed": 3.46, 511 | "deg": 88.5013 512 | }, 513 | "rain": {}, 514 | "snow": {}, 515 | "sys": { 516 | "pod": "d" 517 | }, 518 | "dt_txt": "2018-02-23 09:00:00" 519 | }, { 520 | "dt": 1519387200, 521 | "main": { 522 | "temp": 3.62, 523 | "temp_min": 3.62, 524 | "temp_max": 3.62, 525 | "pressure": 1029.39, 526 | "sea_level": 1037.3, 527 | "grnd_level": 1029.39, 528 | "humidity": 71, 529 | "temp_kf": 0 530 | }, 531 | "weather": [{ 532 | "id": 800, 533 | "main": "Clear", 534 | "description": "clear sky", 535 | "icon": "01d" 536 | }], 537 | "clouds": { 538 | "all": 0 539 | }, 540 | "wind": { 541 | "speed": 5.11, 542 | "deg": 90.0027 543 | }, 544 | "rain": {}, 545 | "snow": {}, 546 | "sys": { 547 | "pod": "d" 548 | }, 549 | "dt_txt": "2018-02-23 12:00:00" 550 | }, { 551 | "dt": 1519398000, 552 | "main": { 553 | "temp": 3.27, 554 | "temp_min": 3.27, 555 | "temp_max": 3.27, 556 | "pressure": 1028.27, 557 | "sea_level": 1036.13, 558 | "grnd_level": 1028.27, 559 | "humidity": 66, 560 | "temp_kf": 0 561 | }, 562 | "weather": [{ 563 | "id": 800, 564 | "main": "Clear", 565 | "description": "clear sky", 566 | "icon": "01d" 567 | }], 568 | "clouds": { 569 | "all": 0 570 | }, 571 | "wind": { 572 | "speed": 5.81, 573 | "deg": 84.0005 574 | }, 575 | "rain": {}, 576 | "snow": {}, 577 | "sys": { 578 | "pod": "d" 579 | }, 580 | "dt_txt": "2018-02-23 15:00:00" 581 | }, { 582 | "dt": 1519408800, 583 | "main": { 584 | "temp": 0.9, 585 | "temp_min": 0.9, 586 | "temp_max": 0.9, 587 | "pressure": 1027.53, 588 | "sea_level": 1035.54, 589 | "grnd_level": 1027.53, 590 | "humidity": 63, 591 | "temp_kf": 0 592 | }, 593 | "weather": [{ 594 | "id": 800, 595 | "main": "Clear", 596 | "description": "clear sky", 597 | "icon": "01n" 598 | }], 599 | "clouds": { 600 | "all": 0 601 | }, 602 | "wind": { 603 | "speed": 5.21, 604 | "deg": 78.5019 605 | }, 606 | "rain": {}, 607 | "snow": {}, 608 | "sys": { 609 | "pod": "n" 610 | }, 611 | "dt_txt": "2018-02-23 18:00:00" 612 | }, { 613 | "dt": 1519419600, 614 | "main": { 615 | "temp": 0.16, 616 | "temp_min": 0.16, 617 | "temp_max": 0.16, 618 | "pressure": 1027.36, 619 | "sea_level": 1035.37, 620 | "grnd_level": 1027.36, 621 | "humidity": 72, 622 | "temp_kf": 0 623 | }, 624 | "weather": [{ 625 | "id": 800, 626 | "main": "Clear", 627 | "description": "clear sky", 628 | "icon": "01n" 629 | }], 630 | "clouds": { 631 | "all": 0 632 | }, 633 | "wind": { 634 | "speed": 4.93, 635 | "deg": 82.5017 636 | }, 637 | "rain": {}, 638 | "snow": {}, 639 | "sys": { 640 | "pod": "n" 641 | }, 642 | "dt_txt": "2018-02-23 21:00:00" 643 | }, { 644 | "dt": 1519430400, 645 | "main": { 646 | "temp": -0.4, 647 | "temp_min": -0.4, 648 | "temp_max": -0.4, 649 | "pressure": 1027.56, 650 | "sea_level": 1035.57, 651 | "grnd_level": 1027.56, 652 | "humidity": 72, 653 | "temp_kf": 0 654 | }, 655 | "weather": [{ 656 | "id": 800, 657 | "main": "Clear", 658 | "description": "clear sky", 659 | "icon": "01n" 660 | }], 661 | "clouds": { 662 | "all": 0 663 | }, 664 | "wind": { 665 | "speed": 4.36, 666 | "deg": 96.0003 667 | }, 668 | "rain": {}, 669 | "snow": {}, 670 | "sys": { 671 | "pod": "n" 672 | }, 673 | "dt_txt": "2018-02-24 00:00:00" 674 | }, { 675 | "dt": 1519441200, 676 | "main": { 677 | "temp": -1.43, 678 | "temp_min": -1.43, 679 | "temp_max": -1.43, 680 | "pressure": 1027.78, 681 | "sea_level": 1035.84, 682 | "grnd_level": 1027.78, 683 | "humidity": 78, 684 | "temp_kf": 0 685 | }, 686 | "weather": [{ 687 | "id": 800, 688 | "main": "Clear", 689 | "description": "clear sky", 690 | "icon": "01n" 691 | }], 692 | "clouds": { 693 | "all": 0 694 | }, 695 | "wind": { 696 | "speed": 3.92, 697 | "deg": 93.5006 698 | }, 699 | "rain": {}, 700 | "snow": {}, 701 | "sys": { 702 | "pod": "n" 703 | }, 704 | "dt_txt": "2018-02-24 03:00:00" 705 | }, { 706 | "dt": 1519452000, 707 | "main": { 708 | "temp": -1.89, 709 | "temp_min": -1.89, 710 | "temp_max": -1.89, 711 | "pressure": 1028.41, 712 | "sea_level": 1036.44, 713 | "grnd_level": 1028.41, 714 | "humidity": 83, 715 | "temp_kf": 0 716 | }, 717 | "weather": [{ 718 | "id": 801, 719 | "main": "Clouds", 720 | "description": "few clouds", 721 | "icon": "02n" 722 | }], 723 | "clouds": { 724 | "all": 12 725 | }, 726 | "wind": { 727 | "speed": 4.03, 728 | "deg": 86.0021 729 | }, 730 | "rain": {}, 731 | "snow": {}, 732 | "sys": { 733 | "pod": "n" 734 | }, 735 | "dt_txt": "2018-02-24 06:00:00" 736 | }, { 737 | "dt": 1519462800, 738 | "main": { 739 | "temp": 0.89, 740 | "temp_min": 0.89, 741 | "temp_max": 0.89, 742 | "pressure": 1029.14, 743 | "sea_level": 1037.25, 744 | "grnd_level": 1029.14, 745 | "humidity": 71, 746 | "temp_kf": 0 747 | }, 748 | "weather": [{ 749 | "id": 500, 750 | "main": "Rain", 751 | "description": "light rain", 752 | "icon": "10d" 753 | }], 754 | "clouds": { 755 | "all": 0 756 | }, 757 | "wind": { 758 | "speed": 5.03, 759 | "deg": 87.0009 760 | }, 761 | "rain": { 762 | "3h": 0.005 763 | }, 764 | "snow": {}, 765 | "sys": { 766 | "pod": "d" 767 | }, 768 | "dt_txt": "2018-02-24 09:00:00" 769 | }, { 770 | "dt": 1519473600, 771 | "main": { 772 | "temp": 3.36, 773 | "temp_min": 3.36, 774 | "temp_max": 3.36, 775 | "pressure": 1029.86, 776 | "sea_level": 1037.8, 777 | "grnd_level": 1029.86, 778 | "humidity": 68, 779 | "temp_kf": 0 780 | }, 781 | "weather": [{ 782 | "id": 800, 783 | "main": "Clear", 784 | "description": "clear sky", 785 | "icon": "02d" 786 | }], 787 | "clouds": { 788 | "all": 8 789 | }, 790 | "wind": { 791 | "speed": 6.82, 792 | "deg": 92.001 793 | }, 794 | "rain": {}, 795 | "snow": {}, 796 | "sys": { 797 | "pod": "d" 798 | }, 799 | "dt_txt": "2018-02-24 12:00:00" 800 | }, { 801 | "dt": 1519484400, 802 | "main": { 803 | "temp": 3.42, 804 | "temp_min": 3.42, 805 | "temp_max": 3.42, 806 | "pressure": 1029.61, 807 | "sea_level": 1037.59, 808 | "grnd_level": 1029.61, 809 | "humidity": 61, 810 | "temp_kf": 0 811 | }, 812 | "weather": [{ 813 | "id": 800, 814 | "main": "Clear", 815 | "description": "clear sky", 816 | "icon": "01d" 817 | }], 818 | "clouds": { 819 | "all": 0 820 | }, 821 | "wind": { 822 | "speed": 7.15, 823 | "deg": 86.0011 824 | }, 825 | "rain": {}, 826 | "snow": {}, 827 | "sys": { 828 | "pod": "d" 829 | }, 830 | "dt_txt": "2018-02-24 15:00:00" 831 | }, { 832 | "dt": 1519495200, 833 | "main": { 834 | "temp": 1.66, 835 | "temp_min": 1.66, 836 | "temp_max": 1.66, 837 | "pressure": 1031, 838 | "sea_level": 1038.91, 839 | "grnd_level": 1031, 840 | "humidity": 67, 841 | "temp_kf": 0 842 | }, 843 | "weather": [{ 844 | "id": 800, 845 | "main": "Clear", 846 | "description": "clear sky", 847 | "icon": "01n" 848 | }], 849 | "clouds": { 850 | "all": 0 851 | }, 852 | "wind": { 853 | "speed": 6.78, 854 | "deg": 91.0032 855 | }, 856 | "rain": {}, 857 | "snow": {}, 858 | "sys": { 859 | "pod": "n" 860 | }, 861 | "dt_txt": "2018-02-24 18:00:00" 862 | }, { 863 | "dt": 1519506000, 864 | "main": { 865 | "temp": 1.72, 866 | "temp_min": 1.72, 867 | "temp_max": 1.72, 868 | "pressure": 1031.78, 869 | "sea_level": 1039.79, 870 | "grnd_level": 1031.78, 871 | "humidity": 72, 872 | "temp_kf": 0 873 | }, 874 | "weather": [{ 875 | "id": 800, 876 | "main": "Clear", 877 | "description": "clear sky", 878 | "icon": "02n" 879 | }], 880 | "clouds": { 881 | "all": 8 882 | }, 883 | "wind": { 884 | "speed": 6.16, 885 | "deg": 90.5034 886 | }, 887 | "rain": {}, 888 | "snow": {}, 889 | "sys": { 890 | "pod": "n" 891 | }, 892 | "dt_txt": "2018-02-24 21:00:00" 893 | }, { 894 | "dt": 1519516800, 895 | "main": { 896 | "temp": 1.17, 897 | "temp_min": 1.17, 898 | "temp_max": 1.17, 899 | "pressure": 1032.49, 900 | "sea_level": 1040.47, 901 | "grnd_level": 1032.49, 902 | "humidity": 70, 903 | "temp_kf": 0 904 | }, 905 | "weather": [{ 906 | "id": 800, 907 | "main": "Clear", 908 | "description": "clear sky", 909 | "icon": "01n" 910 | }], 911 | "clouds": { 912 | "all": 0 913 | }, 914 | "wind": { 915 | "speed": 6.05, 916 | "deg": 89.5 917 | }, 918 | "rain": {}, 919 | "snow": {}, 920 | "sys": { 921 | "pod": "n" 922 | }, 923 | "dt_txt": "2018-02-25 00:00:00" 924 | }, { 925 | "dt": 1519527600, 926 | "main": { 927 | "temp": -0.25, 928 | "temp_min": -0.25, 929 | "temp_max": -0.25, 930 | "pressure": 1032.86, 931 | "sea_level": 1040.88, 932 | "grnd_level": 1032.86, 933 | "humidity": 66, 934 | "temp_kf": 0 935 | }, 936 | "weather": [{ 937 | "id": 800, 938 | "main": "Clear", 939 | "description": "clear sky", 940 | "icon": "01n" 941 | }], 942 | "clouds": { 943 | "all": 0 944 | }, 945 | "wind": { 946 | "speed": 5.81, 947 | "deg": 89.0001 948 | }, 949 | "rain": {}, 950 | "snow": {}, 951 | "sys": { 952 | "pod": "n" 953 | }, 954 | "dt_txt": "2018-02-25 03:00:00" 955 | }, { 956 | "dt": 1519538400, 957 | "main": { 958 | "temp": -1.47, 959 | "temp_min": -1.47, 960 | "temp_max": -1.47, 961 | "pressure": 1033.44, 962 | "sea_level": 1041.63, 963 | "grnd_level": 1033.44, 964 | "humidity": 66, 965 | "temp_kf": 0 966 | }, 967 | "weather": [{ 968 | "id": 800, 969 | "main": "Clear", 970 | "description": "clear sky", 971 | "icon": "01n" 972 | }], 973 | "clouds": { 974 | "all": 0 975 | }, 976 | "wind": { 977 | "speed": 5.52, 978 | "deg": 87.0034 979 | }, 980 | "rain": {}, 981 | "snow": {}, 982 | "sys": { 983 | "pod": "n" 984 | }, 985 | "dt_txt": "2018-02-25 06:00:00" 986 | }, { 987 | "dt": 1519549200, 988 | "main": { 989 | "temp": -0.28, 990 | "temp_min": -0.28, 991 | "temp_max": -0.28, 992 | "pressure": 1034.27, 993 | "sea_level": 1042.34, 994 | "grnd_level": 1034.27, 995 | "humidity": 66, 996 | "temp_kf": 0 997 | }, 998 | "weather": [{ 999 | "id": 800, 1000 | "main": "Clear", 1001 | "description": "clear sky", 1002 | "icon": "01d" 1003 | }], 1004 | "clouds": { 1005 | "all": 0 1006 | }, 1007 | "wind": { 1008 | "speed": 5.87, 1009 | "deg": 90.5143 1010 | }, 1011 | "rain": {}, 1012 | "snow": {}, 1013 | "sys": { 1014 | "pod": "d" 1015 | }, 1016 | "dt_txt": "2018-02-25 09:00:00" 1017 | }, { 1018 | "dt": 1519560000, 1019 | "main": { 1020 | "temp": 1.89, 1021 | "temp_min": 1.89, 1022 | "temp_max": 1.89, 1023 | "pressure": 1034.6, 1024 | "sea_level": 1042.56, 1025 | "grnd_level": 1034.6, 1026 | "humidity": 61, 1027 | "temp_kf": 0 1028 | }, 1029 | "weather": [{ 1030 | "id": 800, 1031 | "main": "Clear", 1032 | "description": "clear sky", 1033 | "icon": "01d" 1034 | }], 1035 | "clouds": { 1036 | "all": 0 1037 | }, 1038 | "wind": { 1039 | "speed": 6.61, 1040 | "deg": 90.5034 1041 | }, 1042 | "rain": {}, 1043 | "snow": {}, 1044 | "sys": { 1045 | "pod": "d" 1046 | }, 1047 | "dt_txt": "2018-02-25 12:00:00" 1048 | }, { 1049 | "dt": 1519570800, 1050 | "main": { 1051 | "temp": 2.04, 1052 | "temp_min": 2.04, 1053 | "temp_max": 2.04, 1054 | "pressure": 1034.19, 1055 | "sea_level": 1042.1, 1056 | "grnd_level": 1034.19, 1057 | "humidity": 54, 1058 | "temp_kf": 0 1059 | }, 1060 | "weather": [{ 1061 | "id": 800, 1062 | "main": "Clear", 1063 | "description": "clear sky", 1064 | "icon": "01d" 1065 | }], 1066 | "clouds": { 1067 | "all": 0 1068 | }, 1069 | "wind": { 1070 | "speed": 6.17, 1071 | "deg": 84.5019 1072 | }, 1073 | "rain": {}, 1074 | "snow": {}, 1075 | "sys": { 1076 | "pod": "d" 1077 | }, 1078 | "dt_txt": "2018-02-25 15:00:00" 1079 | }, { 1080 | "dt": 1519581600, 1081 | "main": { 1082 | "temp": -0.39, 1083 | "temp_min": -0.39, 1084 | "temp_max": -0.39, 1085 | "pressure": 1034.33, 1086 | "sea_level": 1042.37, 1087 | "grnd_level": 1034.33, 1088 | "humidity": 59, 1089 | "temp_kf": 0 1090 | }, 1091 | "weather": [{ 1092 | "id": 800, 1093 | "main": "Clear", 1094 | "description": "clear sky", 1095 | "icon": "01n" 1096 | }], 1097 | "clouds": { 1098 | "all": 0 1099 | }, 1100 | "wind": { 1101 | "speed": 5.1, 1102 | "deg": 79.0058 1103 | }, 1104 | "rain": {}, 1105 | "snow": {}, 1106 | "sys": { 1107 | "pod": "n" 1108 | }, 1109 | "dt_txt": "2018-02-25 18:00:00" 1110 | }, { 1111 | "dt": 1519592400, 1112 | "main": { 1113 | "temp": -1.65, 1114 | "temp_min": -1.65, 1115 | "temp_max": -1.65, 1116 | "pressure": 1034.58, 1117 | "sea_level": 1042.65, 1118 | "grnd_level": 1034.58, 1119 | "humidity": 70, 1120 | "temp_kf": 0 1121 | }, 1122 | "weather": [{ 1123 | "id": 800, 1124 | "main": "Clear", 1125 | "description": "clear sky", 1126 | "icon": "01n" 1127 | }], 1128 | "clouds": { 1129 | "all": 0 1130 | }, 1131 | "wind": { 1132 | "speed": 4.06, 1133 | "deg": 80.0013 1134 | }, 1135 | "rain": {}, 1136 | "snow": {}, 1137 | "sys": { 1138 | "pod": "n" 1139 | }, 1140 | "dt_txt": "2018-02-25 21:00:00" 1141 | }, { 1142 | "dt": 1519603200, 1143 | "main": { 1144 | "temp": -2.74, 1145 | "temp_min": -2.74, 1146 | "temp_max": -2.74, 1147 | "pressure": 1034.46, 1148 | "sea_level": 1042.58, 1149 | "grnd_level": 1034.46, 1150 | "humidity": 80, 1151 | "temp_kf": 0 1152 | }, 1153 | "weather": [{ 1154 | "id": 800, 1155 | "main": "Clear", 1156 | "description": "clear sky", 1157 | "icon": "01n" 1158 | }], 1159 | "clouds": { 1160 | "all": 0 1161 | }, 1162 | "wind": { 1163 | "speed": 3.18, 1164 | "deg": 90.0024 1165 | }, 1166 | "rain": {}, 1167 | "snow": {}, 1168 | "sys": { 1169 | "pod": "n" 1170 | }, 1171 | "dt_txt": "2018-02-26 00:00:00" 1172 | }, { 1173 | "dt": 1519614000, 1174 | "main": { 1175 | "temp": -4.39, 1176 | "temp_min": -4.39, 1177 | "temp_max": -4.39, 1178 | "pressure": 1033.95, 1179 | "sea_level": 1042.1, 1180 | "grnd_level": 1033.95, 1181 | "humidity": 86, 1182 | "temp_kf": 0 1183 | }, 1184 | "weather": [{ 1185 | "id": 801, 1186 | "main": "Clouds", 1187 | "description": "few clouds", 1188 | "icon": "02n" 1189 | }], 1190 | "clouds": { 1191 | "all": 20 1192 | }, 1193 | "wind": { 1194 | "speed": 2.41, 1195 | "deg": 82.5104 1196 | }, 1197 | "rain": {}, 1198 | "snow": {}, 1199 | "sys": { 1200 | "pod": "n" 1201 | }, 1202 | "dt_txt": "2018-02-26 03:00:00" 1203 | }, { 1204 | "dt": 1519624800, 1205 | "main": { 1206 | "temp": -4.46, 1207 | "temp_min": -4.46, 1208 | "temp_max": -4.46, 1209 | "pressure": 1033.72, 1210 | "sea_level": 1041.95, 1211 | "grnd_level": 1033.72, 1212 | "humidity": 94, 1213 | "temp_kf": 0 1214 | }, 1215 | "weather": [{ 1216 | "id": 802, 1217 | "main": "Clouds", 1218 | "description": "scattered clouds", 1219 | "icon": "03n" 1220 | }], 1221 | "clouds": { 1222 | "all": 48 1223 | }, 1224 | "wind": { 1225 | "speed": 2.71, 1226 | "deg": 61.0005 1227 | }, 1228 | "rain": {}, 1229 | "snow": {}, 1230 | "sys": { 1231 | "pod": "n" 1232 | }, 1233 | "dt_txt": "2018-02-26 06:00:00" 1234 | }, { 1235 | "dt": 1519635600, 1236 | "main": { 1237 | "temp": -0.89, 1238 | "temp_min": -0.89, 1239 | "temp_max": -0.89, 1240 | "pressure": 1033.92, 1241 | "sea_level": 1041.98, 1242 | "grnd_level": 1033.92, 1243 | "humidity": 75, 1244 | "temp_kf": 0 1245 | }, 1246 | "weather": [{ 1247 | "id": 800, 1248 | "main": "Clear", 1249 | "description": "clear sky", 1250 | "icon": "01d" 1251 | }], 1252 | "clouds": { 1253 | "all": 0 1254 | }, 1255 | "wind": { 1256 | "speed": 3.12, 1257 | "deg": 65.0093 1258 | }, 1259 | "rain": {}, 1260 | "snow": {}, 1261 | "sys": { 1262 | "pod": "d" 1263 | }, 1264 | "dt_txt": "2018-02-26 09:00:00" 1265 | }], 1266 | "city": { 1267 | "id": 2643743, 1268 | "name": "London", 1269 | "coord": { 1270 | "lat": 51.5073, 1271 | "lon": -0.1277 1272 | }, 1273 | "country": "GB", 1274 | "population": 1000000 1275 | } 1276 | }, 1277 | "status": "success" 1278 | } 1279 | } -------------------------------------------------------------------------------- /src/test/__tests__/forecastTiles.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { render } from "enzyme"; 3 | 4 | import configureStore from "redux-mock-store"; 5 | const mockStore = configureStore(); 6 | 7 | import data from "./data/forecast.json"; 8 | const { list } = data.weatherStation.data; 9 | 10 | import ForecastTiles from "../../components/ForecastTiles"; 11 | 12 | describe("", () => { 13 | it("should render a forecast-tiles container div", () => { 14 | const wrapper = render(); 15 | expect(wrapper.hasClass("forecast-tiles")).toBe(true); 16 | }); 17 | 18 | it("should render five forecast tiles", () => { 19 | const wrapper = render(); 20 | expect(wrapper.children().length).toBe(5); 21 | }); 22 | }); -------------------------------------------------------------------------------- /src/test/__tests__/testAsyncActions.js: -------------------------------------------------------------------------------- 1 | import configureStore from "redux-mock-store"; 2 | 3 | import { FETCH_DATA_FULFILLED, FETCH_DATA_REJECTED } from "../../constants/ActionTypes"; 4 | 5 | import thunk from "redux-thunk"; 6 | require("es6-promise").polyfill(); 7 | require("isomorphic-fetch"); 8 | 9 | import { fetchData } from "../../actions/weatherStation"; 10 | 11 | const middlewares = [thunk]; 12 | const mockStore = configureStore(middlewares); 13 | 14 | it("should execute fetchData and return the required action type", () => { 15 | const store = mockStore({}); 16 | return store.dispatch(fetchData("london")) 17 | .then(() => { 18 | const actions = store.getActions(); 19 | 20 | // Expected action type from the action creator 21 | expect(actions[0].type).toEqual(FETCH_DATA_FULFILLED); 22 | }); 23 | }); 24 | 25 | it("should reject the request if no city is being passed", () => { 26 | const store = mockStore({}); 27 | return store.dispatch(fetchData(null)) 28 | .then(() => { 29 | const actions = store.getActions(); 30 | 31 | // Expected action type from the action creator 32 | expect(actions[0].type).toEqual(FETCH_DATA_REJECTED); 33 | }); 34 | }); -------------------------------------------------------------------------------- /src/test/__tests__/testReducers.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import * as actions from "../../actions/weatherStation"; 4 | import reducer from "../../reducers"; 5 | 6 | import { FETCH_DATA_FULFILLED, FETCH_DATA_REJECTED } from "../../constants/ActionTypes"; 7 | 8 | import mockData from "./data/forecast.json"; 9 | 10 | describe("data reducer", () => { 11 | 12 | it("should return initial state", () => { 13 | expect(reducer(undefined, {})).toEqual({"weatherStation": {"data": null, "status": null}}); 14 | }); 15 | 16 | it("should handle FETCH_DATA_FULFILLED", () => { 17 | const startFetch = { 18 | type: FETCH_DATA_FULFILLED, 19 | payload: mockData.weatherStation.data 20 | }; 21 | 22 | expect(reducer({}, startFetch)).toEqual(mockData); 23 | }); 24 | 25 | it("should handle FETCH_DATA_REJECTED", () => { 26 | const startFetch = { 27 | type: FETCH_DATA_REJECTED, 28 | payload: {} 29 | }; 30 | 31 | expect(reducer({}, startFetch)).toEqual({"weatherStation": {"data": null, "status": "failed"}}); 32 | }); 33 | }); 34 | 35 | -------------------------------------------------------------------------------- /src/test/__tests__/weatherForecast.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { shallow } from "enzyme"; 3 | 4 | import configureStore from "redux-mock-store"; 5 | const mockStore = configureStore(); 6 | 7 | import WeatherForecast from "../../components/WeatherForecast"; 8 | import Dashboard from "../../components/Dashboard"; 9 | 10 | import data from "./data/forecast.json"; 11 | 12 | describe("", () => { 13 | it("should render a div with `.weather-forecast-wrapper` class", () => { 14 | const wrapper = shallow(); 15 | expect(wrapper.hasClass("weather-forecast-wrapper")).toBe(true); 16 | }); 17 | 18 | it("should contain a dashboard", () => { 19 | const wrapper = shallow(); 20 | expect(wrapper.find(Dashboard)).toHaveLength(1); 21 | }); 22 | }); -------------------------------------------------------------------------------- /src/test/jestSetup.js: -------------------------------------------------------------------------------- 1 | import "babel-polyfill"; 2 | import Enzyme from "enzyme"; 3 | import Adapter from "enzyme-adapter-react-16"; 4 | 5 | Enzyme.configure({ adapter: new Adapter() }); -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const ExtractTextPlugin = require("extract-text-webpack-plugin"); 3 | const webpack = require("webpack"); 4 | 5 | module.exports = { 6 | entry: ["./src/index.js", "./src/styles/main.scss"], 7 | output: { 8 | filename: "bundle.js", 9 | path: path.resolve(__dirname, "./public/dist") 10 | }, 11 | module: { 12 | rules: [ 13 | { 14 | test: /\.scss/, 15 | loader: ExtractTextPlugin.extract(["css-loader", "sass-loader"]) 16 | }, 17 | { 18 | test: /\.js$/, 19 | exclude: /node_modules/, 20 | loaders: "eslint-loader", 21 | }, 22 | { 23 | test: /\.js$/, 24 | exclude: /node_modules/, 25 | loaders: "babel-loader" 26 | } 27 | ] 28 | }, 29 | devServer: { 30 | contentBase: "./public/", 31 | watchContentBase: true 32 | }, 33 | plugins: [ 34 | new ExtractTextPlugin("bundle.css"), 35 | new webpack.DefinePlugin({ 36 | "process.env.NODE_ENV": JSON.stringify("production") 37 | }), 38 | new webpack.optimize.UglifyJsPlugin() 39 | ] 40 | }; 41 | --------------------------------------------------------------------------------