diff --git a/frontend/.eslintrc b/frontend/.eslintrc
index 3f3e372e9e..196241c50d 100644
--- a/frontend/.eslintrc
+++ b/frontend/.eslintrc
@@ -15,5 +15,11 @@
"printWidth": 120
}
]
- }
+ },
+ "overrides": [
+ {
+ "files": ["**/__tests__/*"],
+ "env": { "jest": true }
+ }
+ ]
}
diff --git a/frontend/package.json b/frontend/package.json
index c31ef3c45e..01f565defd 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -50,8 +50,7 @@
"react-mdl": "^1.11.0",
"react-modal": "^3.1.13",
"react-redux": "^5.0.6",
- "react-router": "^3.0.0",
- "react-router-scroll": "^0.4.1",
+ "react-router-dom": "^4.3.1",
"redux": "^3.6.0",
"redux-thunk": "^2.1.0",
"whatwg-fetch": "^2.0.0"
diff --git a/frontend/src/__mocks__/react-mdl.js b/frontend/src/__mocks__/react-mdl.js
index c1ddf6d0c8..7052e6d945 100644
--- a/frontend/src/__mocks__/react-mdl.js
+++ b/frontend/src/__mocks__/react-mdl.js
@@ -5,6 +5,7 @@ module.exports = {
CardText: 'react-mdl-CardText',
CardMenu: 'react-mdl-CardMenu',
DataTable: 'react-mdl-DataTable',
+ Drawer: 'react-mdl-Drawer',
Cell: 'react-mdl-Cell',
Chip: 'react-mdl-Chip',
Grid: 'react-mdl-Grid',
@@ -16,12 +17,14 @@ module.exports = {
ListItemAction: 'react-mdl-ListItemAction',
Menu: 'react-mdl-Menu',
MenuItem: 'react-mdl-MenuItem',
+ Navigation: 'react-mdl-Navigation',
ProgressBar: 'react-mdl-ProgressBar',
Switch: 'react-mdl-Switch',
Tab: 'react-mdl-Tab',
Tabs: 'react-mdl-Tabs',
TableHeader: 'react-mdl-TableHeader',
Textfield: 'react-mdl-Textfield',
+ FooterDropDownSection: 'react-mdl-FooterDropDownSection',
FooterSection: 'react-mdl-FooterSection',
FooterLinkList: 'react-mdl-FooterLinkList',
};
diff --git a/frontend/src/__tests__/.eslintrc b/frontend/src/__tests__/.eslintrc
deleted file mode 100644
index eba2077219..0000000000
--- a/frontend/src/__tests__/.eslintrc
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "env": {
- "jest": true
- }
-}
diff --git a/frontend/src/component/api/__tests__/.eslintrc b/frontend/src/component/api/__tests__/.eslintrc
deleted file mode 100644
index eba2077219..0000000000
--- a/frontend/src/component/api/__tests__/.eslintrc
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "env": {
- "jest": true
- }
-}
diff --git a/frontend/src/component/app.jsx b/frontend/src/component/app.jsx
index 4fe0b65bc0..863f27816d 100644
--- a/frontend/src/component/app.jsx
+++ b/frontend/src/component/app.jsx
@@ -1,43 +1,23 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
-import {
- Layout,
- Drawer,
- Header,
- Navigation,
- Content,
- Footer,
- FooterSection,
- FooterDropDownSection,
- FooterLinkList,
- Grid,
- Cell,
- Icon,
-} from 'react-mdl';
-import { Link } from 'react-router';
+import { Layout, Header, Navigation, Content, Footer, Grid, Cell } from 'react-mdl';
+import { Route, Redirect, Switch } from 'react-router-dom';
import styles from './styles.scss';
import ErrorContainer from './error/error-container';
import AuthenticationContainer from './user/authentication-container';
import ShowUserContainer from './user/show-user-container';
import ShowApiDetailsContainer from './api/show-api-details-container';
-import { ScrollContainer } from 'react-router-scroll';
-
-function replace(input, params) {
- if (!params) {
- return input;
- }
- Object.keys(params).forEach(key => {
- input = input.replace(`:${key}`, params[key]);
- });
- return input;
-}
+import Features from '../page/features';
+import { DrawerMenu } from './menu/drawer';
+import { FooterMenu } from './menu/footer';
+import Breadcrum from './menu/breadcrumb';
+import { routes } from './menu/routes';
export default class App extends Component {
static propTypes = {
location: PropTypes.object.isRequired,
- params: PropTypes.object.isRequired,
- routes: PropTypes.array.isRequired,
+ match: PropTypes.object.isRequired,
};
static contextTypes = {
@@ -58,152 +38,38 @@ export default class App extends Component {
}
}
- getSections() {
- const { routes, params } = this.props;
- const unique = {};
- const result = routes
- .splice(1)
- .map(routeEntry => ({
- name: replace(routeEntry.pageTitle, params),
- link: replace(routeEntry.link || routeEntry.path, params),
- }))
- .filter(entry => {
- if (!unique[entry.link]) {
- unique[entry.link] = true;
- return true;
- }
- return false;
- });
-
- // mutate document.title:
- document.title = result
- .map(e => e.name)
- .reverse()
- .concat('Unleash')
- .join(' – ');
-
- return result;
- }
-
- getTitleWithLinks() {
- const result = this.getSections();
- return (
-
- {result.map((entry, index) => (
- 0 ? 'mdl-layout--large-screen-only' : ''}>
- {index > 0 ? ' › ' : null}
-
- {entry.name}
-
-
- ))}
-
- );
- }
-
render() {
- const shouldUpdateScroll = (prevRouterProps, { location }) => {
- if (prevRouterProps && location.pathname !== prevRouterProps.location.pathname) {
- return location.action === 'POP';
- } else {
- return [0, 0];
- }
- };
- const createListItem = (path, caption, icon, isDrawerNavigation = false) => {
- const linkColor =
- isDrawerNavigation && this.context.router.isActive(path)
- ? 'mdl-color-text--black'
- : 'mdl-color-text--grey-900';
- const iconColor =
- isDrawerNavigation && this.context.router.isActive(path)
- ? 'mdl-color-text--black'
- : 'mdl-color-text--grey-600';
- const renderIcon = (
-
- );
- return (
-
- {icon && renderIcon}
- {caption}
-
- );
- };
-
return (
-
-
-
-
- Unleash
-
-
-
- {createListItem('/features', 'Feature Toggles', 'list', true)}
- {createListItem('/strategies', 'Strategies', 'extension', true)}
- {createListItem('/history', 'Event History', 'history', true)}
- {createListItem('/archive', 'Archived Toggles', 'archive', true)}
- {createListItem('/applications', 'Applications', 'apps', true)}
- {createListItem('logout', 'Sign out', 'exit_to_app', true)}
-
-
-
-
- GitHub
-
-
-
-
-
-
-
- {this.props.children}
-
- |
-
-
-
-
+
+
+
+
+
+ }
+ />
+ {routes.map(route => (
+
+ ))}
+
+
+ |
+
+
+
);
diff --git a/frontend/src/component/application/__tests__/.eslintrc b/frontend/src/component/application/__tests__/.eslintrc
deleted file mode 100644
index eba2077219..0000000000
--- a/frontend/src/component/application/__tests__/.eslintrc
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "env": {
- "jest": true
- }
-}
diff --git a/frontend/src/component/application/application-edit-component.js b/frontend/src/component/application/application-edit-component.js
index 2f8e0cfaa1..5dcc164875 100644
--- a/frontend/src/component/application/application-edit-component.js
+++ b/frontend/src/component/application/application-edit-component.js
@@ -2,7 +2,7 @@
import React, { Component, PureComponent } from 'react';
import PropTypes from 'prop-types';
-import { Link } from 'react-router';
+import { Link } from 'react-router-dom';
import {
Grid,
Cell,
diff --git a/frontend/src/component/client-instance/__tests__/.eslintrc b/frontend/src/component/client-instance/__tests__/.eslintrc
deleted file mode 100644
index eba2077219..0000000000
--- a/frontend/src/component/client-instance/__tests__/.eslintrc
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "env": {
- "jest": true
- }
-}
diff --git a/frontend/src/component/common/__tests__/.eslintrc b/frontend/src/component/common/__tests__/.eslintrc
deleted file mode 100644
index eba2077219..0000000000
--- a/frontend/src/component/common/__tests__/.eslintrc
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "env": {
- "jest": true
- }
-}
diff --git a/frontend/src/component/common/index.js b/frontend/src/component/common/index.js
index 73145d6bc9..6d21dd885c 100644
--- a/frontend/src/component/common/index.js
+++ b/frontend/src/component/common/index.js
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { Link } from 'react-router';
+import { Link } from 'react-router-dom';
import { List, ListItem, ListItemContent, Button, Icon, Switch, MenuItem } from 'react-mdl';
import styles from './common.scss';
@@ -11,10 +11,10 @@ export const shorten = (str, len = 50) => (str && str.length > len ? `${str.subs
export const AppsLinkList = ({ apps }) => (
{apps.length > 0 &&
- apps.map(({ appName, description = '-', icon = 'apps' }) => (
+ apps.map(({ appName, description = '-', icon }) => (
-
+
{appName}
diff --git a/frontend/src/component/feature/__tests__/.eslintrc b/frontend/src/component/feature/__tests__/.eslintrc
deleted file mode 100644
index eba2077219..0000000000
--- a/frontend/src/component/feature/__tests__/.eslintrc
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "env": {
- "jest": true
- }
-}
diff --git a/frontend/src/component/feature/__tests__/__snapshots__/feature-list-item-component-test.jsx.snap b/frontend/src/component/feature/__tests__/__snapshots__/feature-list-item-component-test.jsx.snap
index 96acaef791..07294b161d 100644
--- a/frontend/src/component/feature/__tests__/__snapshots__/feature-list-item-component-test.jsx.snap
+++ b/frontend/src/component/feature/__tests__/__snapshots__/feature-list-item-component-test.jsx.snap
@@ -32,8 +32,8 @@ exports[`renders correctly with one feature 1`] = `
>
Another
{
const featureMetrics = { lastHour: {}, lastMinute: {}, seenApps: {} };
const settings = { sort: 'name' };
const tree = renderer.create(
-
+
+
+
);
expect(tree).toMatchSnapshot();
diff --git a/frontend/src/component/feature/feature-list-item-component.jsx b/frontend/src/component/feature/feature-list-item-component.jsx
index 4db409569e..9ec3d019b5 100644
--- a/frontend/src/component/feature/feature-list-item-component.jsx
+++ b/frontend/src/component/feature/feature-list-item-component.jsx
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { Link } from 'react-router';
+import { Link } from 'react-router-dom';
import { Switch, Chip, ListItem, ListItemAction, Icon } from 'react-mdl';
import Progress from './progress';
import { calc, styles as commonStyles } from '../common';
diff --git a/frontend/src/component/feature/form/form-add-feature-container.jsx b/frontend/src/component/feature/form/form-add-feature-container.jsx
index 8361e1f0ea..6835ffbc52 100644
--- a/frontend/src/component/feature/form/form-add-feature-container.jsx
+++ b/frontend/src/component/feature/form/form-add-feature-container.jsx
@@ -1,5 +1,4 @@
import { connect } from 'react-redux';
-import { hashHistory } from 'react-router';
import { createFeatureToggles, validateName } from './../../../store/feature-actions';
import { createMapper, createActions } from './../../input-helpers';
import AddFeatureComponent from './form-add-feature-component';
@@ -17,7 +16,7 @@ const mapStateToProps = createMapper({
return { name };
},
});
-const prepare = (methods, dispatch) => {
+const prepare = (methods, dispatch, ownProps) => {
methods.onSubmit = input => e => {
e.preventDefault();
@@ -31,13 +30,13 @@ const prepare = (methods, dispatch) => {
createFeatureToggles(input)(dispatch)
.then(() => methods.clear())
- .then(() => hashHistory.push(`/features/strategies/${input.name}`));
+ .then(() => ownProps.history.push(`/features/strategies/${input.name}`));
};
methods.onCancel = evt => {
evt.preventDefault();
methods.clear();
- hashHistory.push('/features');
+ ownProps.history.push('/features');
};
methods.addStrategy = v => {
diff --git a/frontend/src/component/feature/form/form-update-feature-container.jsx b/frontend/src/component/feature/form/form-update-feature-container.jsx
index 8366db1794..d8839142b6 100644
--- a/frontend/src/component/feature/form/form-update-feature-container.jsx
+++ b/frontend/src/component/feature/form/form-update-feature-container.jsx
@@ -1,5 +1,4 @@
import { connect } from 'react-redux';
-import { hashHistory } from 'react-router';
import { requestUpdateFeatureToggle } from '../../../store/feature-actions';
import { createMapper, createActions } from '../../input-helpers';
@@ -25,7 +24,7 @@ const mapStateToProps = createMapper({
},
});
-const prepare = (methods, dispatch) => {
+const prepare = (methods, dispatch, ownProps) => {
methods.onSubmit = (input, features) => e => {
e.preventDefault();
@@ -42,13 +41,13 @@ const prepare = (methods, dispatch) => {
// TODO: should add error handling
requestUpdateFeatureToggle(input)(dispatch)
.then(() => methods.clear())
- .then(() => hashHistory.push(`/features`));
+ .then(() => ownProps.history.push(`/features`));
};
methods.onCancel = evt => {
evt.preventDefault();
methods.clear();
- hashHistory.push(`/features`);
+ ownProps.history.push(`/features`);
};
methods.addStrategy = v => {
diff --git a/frontend/src/component/feature/form/form-view-feature-container.jsx b/frontend/src/component/feature/form/form-view-feature-container.jsx
index 3ed8e9605b..1f7dc4c463 100644
--- a/frontend/src/component/feature/form/form-view-feature-container.jsx
+++ b/frontend/src/component/feature/form/form-view-feature-container.jsx
@@ -1,7 +1,6 @@
import { connect } from 'react-redux';
import { createMapper, createActions } from '../../input-helpers';
import ViewFeatureToggleComponent from './form-view-feature-component';
-import { hashHistory } from 'react-router';
const ID = 'view-feature-toggle';
function getId(props) {
@@ -27,7 +26,7 @@ const prepare = methods => {
methods.onCancel = evt => {
evt.preventDefault();
methods.clear();
- hashHistory.push(`/archive`);
+ this.props.history.push(`/archive`);
};
return methods;
};
diff --git a/frontend/src/component/feature/form/strategies-list.jsx b/frontend/src/component/feature/form/strategies-list.jsx
index b6f8936c2c..dabaf01aa6 100644
--- a/frontend/src/component/feature/form/strategies-list.jsx
+++ b/frontend/src/component/feature/form/strategies-list.jsx
@@ -24,7 +24,7 @@ class StrategiesList extends React.Component {
const blocks = configuredStrategies.map((strategy, i) => (
;
} else if (TABS[activeTab] === TABS.strategies) {
if (this.isFeatureView) {
- return ;
+ return (
+
+ );
}
return ;
} else {
@@ -62,7 +65,7 @@ export default class ViewFeatureToggleComponent extends React.Component {
goToTab(tabName, featureToggleName) {
let view = this.props.fetchFeatureToggles ? 'features' : 'archive';
- hashHistory.push(`/${view}/${tabName}/${featureToggleName}`);
+ this.props.history.push(`/${view}/${tabName}/${featureToggleName}`);
}
render() {
@@ -105,12 +108,12 @@ export default class ViewFeatureToggleComponent extends React.Component {
window.confirm('Are you sure you want to remove this toggle?')
) {
removeFeatureToggle(featureToggle.name);
- hashHistory.push('/features');
+ this.props.history.push('/features');
}
};
const reviveToggle = () => {
revive(featureToggle.name);
- hashHistory.push('/features');
+ this.props.history.push('/features');
};
const updateFeatureToggle = () => {
let feature = { ...featureToggle };
diff --git a/frontend/src/component/menu/__tests__/__snapshots__/breadcrumb-test.jsx.snap b/frontend/src/component/menu/__tests__/__snapshots__/breadcrumb-test.jsx.snap
new file mode 100644
index 0000000000..ee64a666c4
--- /dev/null
+++ b/frontend/src/component/menu/__tests__/__snapshots__/breadcrumb-test.jsx.snap
@@ -0,0 +1,49 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`breadcrumb for /features 1`] = `
+
+
+ Feature Toggles
+
+
+`;
+
+exports[`breadcrumb for /features/view/Demo 1`] = `
+
+
+ Feature Toggles
+
+
+
+ ›
+
+
+ Demo
+
+
+
+`;
+
+exports[`breadcrumb for /strategies 1`] = `
+
+
+ Strategies
+
+
+`;
diff --git a/frontend/src/component/menu/__tests__/__snapshots__/drawer-test.jsx.snap b/frontend/src/component/menu/__tests__/__snapshots__/drawer-test.jsx.snap
new file mode 100644
index 0000000000..ab4ba96f3b
--- /dev/null
+++ b/frontend/src/component/menu/__tests__/__snapshots__/drawer-test.jsx.snap
@@ -0,0 +1,253 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render DrawerMenu 1`] = `
+
+
+
+
+ Unleash
+
+
+
+
+
+
+
+ Feature Toggles
+
+
+
+
+ Strategies
+
+
+
+
+ Event History
+
+
+
+
+ Archived Toggles
+
+
+
+
+ Applications
+
+
+
+
+ Sign out
+
+
+
+
+
+
+ GitHub
+
+
+
+`;
+
+exports[`should render DrawerMenu with "features" selected 1`] = `
+
+
+
+
+ Unleash
+
+
+
+
+
+
+
+ Feature Toggles
+
+
+
+
+ Strategies
+
+
+
+
+ Event History
+
+
+
+
+ Archived Toggles
+
+
+
+
+ Applications
+
+
+
+
+ Sign out
+
+
+
+
+
+
+ GitHub
+
+
+
+`;
diff --git a/frontend/src/component/menu/__tests__/__snapshots__/footer-test.jsx.snap b/frontend/src/component/menu/__tests__/__snapshots__/footer-test.jsx.snap
new file mode 100644
index 0000000000..1fc175925b
--- /dev/null
+++ b/frontend/src/component/menu/__tests__/__snapshots__/footer-test.jsx.snap
@@ -0,0 +1,177 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render DrawerMenu 1`] = `
+
+
+
+
+ Feature Toggles
+
+
+ Strategies
+
+
+ Event History
+
+
+ Archived Toggles
+
+
+ Applications
+
+
+ Sign out
+
+
+
+
+
+
+ Node.js
+
+
+ Java
+
+
+ Go
+
+
+
+
+`;
+
+exports[`should render DrawerMenu with "features" selected 1`] = `
+
+
+
+
+ Feature Toggles
+
+
+ Strategies
+
+
+ Event History
+
+
+ Archived Toggles
+
+
+ Applications
+
+
+ Sign out
+
+
+
+
+
+
+ Node.js
+
+
+ Java
+
+
+ Go
+
+
+
+
+`;
diff --git a/frontend/src/component/menu/__tests__/__snapshots__/routes-test.jsx.snap b/frontend/src/component/menu/__tests__/__snapshots__/routes-test.jsx.snap
new file mode 100644
index 0000000000..0820089161
--- /dev/null
+++ b/frontend/src/component/menu/__tests__/__snapshots__/routes-test.jsx.snap
@@ -0,0 +1,125 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`returns all baseRoutes 1`] = `
+Array [
+ Object {
+ "component": [Function],
+ "icon": "list",
+ "path": "/features",
+ "title": "Feature Toggles",
+ },
+ Object {
+ "component": [Function],
+ "icon": "extension",
+ "path": "/strategies",
+ "title": "Strategies",
+ },
+ Object {
+ "component": [Function],
+ "icon": "history",
+ "path": "/history",
+ "title": "Event History",
+ },
+ Object {
+ "component": [Function],
+ "icon": "archive",
+ "path": "/archive",
+ "title": "Archived Toggles",
+ },
+ Object {
+ "component": [Function],
+ "icon": "apps",
+ "path": "/applications",
+ "title": "Applications",
+ },
+ Object {
+ "component": [Function],
+ "icon": "exit_to_app",
+ "path": "/logout",
+ "title": "Sign out",
+ },
+]
+`;
+
+exports[`returns all defined routes 1`] = `
+Array [
+ Object {
+ "component": [Function],
+ "parent": "/features",
+ "path": "/features/create",
+ "title": "Create",
+ },
+ Object {
+ "component": [Function],
+ "parent": "/features",
+ "path": "/features/:activeTab/:name",
+ "title": ":name",
+ },
+ Object {
+ "component": [Function],
+ "icon": "list",
+ "path": "/features",
+ "title": "Feature Toggles",
+ },
+ Object {
+ "component": [Function],
+ "parent": "/strategies",
+ "path": "/strategies/create",
+ "title": "Create",
+ },
+ Object {
+ "component": [Function],
+ "parent": "/strategies",
+ "path": "/strategies/:activeTab/:strategyName",
+ "title": ":strategyName",
+ },
+ Object {
+ "component": [Function],
+ "icon": "extension",
+ "path": "/strategies",
+ "title": "Strategies",
+ },
+ Object {
+ "component": [Function],
+ "parent": "/history",
+ "path": "/history/:toggleName",
+ "title": ":toggleName",
+ },
+ Object {
+ "component": [Function],
+ "icon": "history",
+ "path": "/history",
+ "title": "Event History",
+ },
+ Object {
+ "component": [Function],
+ "parent": "/archive",
+ "path": "/archive/:activeTab/:name",
+ "title": ":name",
+ },
+ Object {
+ "component": [Function],
+ "icon": "archive",
+ "path": "/archive",
+ "title": "Archived Toggles",
+ },
+ Object {
+ "component": [Function],
+ "parent": "/applications",
+ "path": "/applications/:name",
+ "title": ":name",
+ },
+ Object {
+ "component": [Function],
+ "icon": "apps",
+ "path": "/applications",
+ "title": "Applications",
+ },
+ Object {
+ "component": [Function],
+ "icon": "exit_to_app",
+ "path": "/logout",
+ "title": "Sign out",
+ },
+]
+`;
diff --git a/frontend/src/component/menu/__tests__/breadcrumb-test.jsx b/frontend/src/component/menu/__tests__/breadcrumb-test.jsx
new file mode 100644
index 0000000000..cb0e74b176
--- /dev/null
+++ b/frontend/src/component/menu/__tests__/breadcrumb-test.jsx
@@ -0,0 +1,37 @@
+import React from 'react';
+import renderer from 'react-test-renderer';
+import { MemoryRouter } from 'react-router-dom';
+
+import Breadcrumb from '../breadcrumb';
+
+jest.mock('react-mdl');
+
+test('breadcrumb for /features', () => {
+ const tree = renderer.create(
+
+
+
+ );
+
+ expect(tree).toMatchSnapshot();
+});
+
+test('breadcrumb for /features/view/Demo', () => {
+ const tree = renderer.create(
+
+
+
+ );
+
+ expect(tree).toMatchSnapshot();
+});
+
+test('breadcrumb for /strategies', () => {
+ const tree = renderer.create(
+
+
+
+ );
+
+ expect(tree).toMatchSnapshot();
+});
diff --git a/frontend/src/component/menu/__tests__/drawer-test.jsx b/frontend/src/component/menu/__tests__/drawer-test.jsx
new file mode 100644
index 0000000000..f581522202
--- /dev/null
+++ b/frontend/src/component/menu/__tests__/drawer-test.jsx
@@ -0,0 +1,27 @@
+import React from 'react';
+import renderer from 'react-test-renderer';
+import { MemoryRouter } from 'react-router-dom';
+
+import { DrawerMenu } from '../drawer';
+
+jest.mock('react-mdl');
+
+test('should render DrawerMenu', () => {
+ const tree = renderer.create(
+
+
+
+ );
+
+ expect(tree).toMatchSnapshot();
+});
+
+test('should render DrawerMenu with "features" selected', () => {
+ const tree = renderer.create(
+
+
+
+ );
+
+ expect(tree).toMatchSnapshot();
+});
diff --git a/frontend/src/component/menu/__tests__/footer-test.jsx b/frontend/src/component/menu/__tests__/footer-test.jsx
new file mode 100644
index 0000000000..6993c42fc5
--- /dev/null
+++ b/frontend/src/component/menu/__tests__/footer-test.jsx
@@ -0,0 +1,27 @@
+import React from 'react';
+import renderer from 'react-test-renderer';
+import { MemoryRouter } from 'react-router-dom';
+
+import { FooterMenu } from '../footer';
+
+jest.mock('react-mdl');
+
+test('should render DrawerMenu', () => {
+ const tree = renderer.create(
+
+
+
+ );
+
+ expect(tree).toMatchSnapshot();
+});
+
+test('should render DrawerMenu with "features" selected', () => {
+ const tree = renderer.create(
+
+
+
+ );
+
+ expect(tree).toMatchSnapshot();
+});
diff --git a/frontend/src/component/menu/__tests__/routes-test.jsx b/frontend/src/component/menu/__tests__/routes-test.jsx
new file mode 100644
index 0000000000..20ce8e87a2
--- /dev/null
+++ b/frontend/src/component/menu/__tests__/routes-test.jsx
@@ -0,0 +1,16 @@
+import { routes, baseRoutes, getRoute } from '../routes';
+
+test('returns all defined routes', () => {
+ expect(routes.length).toEqual(13);
+ expect(routes).toMatchSnapshot();
+});
+
+test('returns all baseRoutes', () => {
+ expect(baseRoutes.length).toEqual(6);
+ expect(baseRoutes).toMatchSnapshot();
+});
+
+test('getRoute() returns named route', () => {
+ const featuresRoute = getRoute('/features');
+ expect(featuresRoute.path).toEqual('/features');
+});
diff --git a/frontend/src/component/menu/breadcrumb.jsx b/frontend/src/component/menu/breadcrumb.jsx
new file mode 100644
index 0000000000..ec02bb6ef8
--- /dev/null
+++ b/frontend/src/component/menu/breadcrumb.jsx
@@ -0,0 +1,69 @@
+import React from 'react';
+import { Link, Route, Switch } from 'react-router-dom';
+
+import { routes, getRoute } from './routes';
+
+import styles from '../styles.scss';
+
+const renderDoubleBread = (currentTitle, parentRoute) => {
+ document.title = `${currentTitle} - ${parentRoute.title} - Unleash`;
+ return (
+
+
+ {parentRoute.title}
+
+
+ ›
+
+ {currentTitle}
+
+
+
+ );
+};
+
+const renderBread = route => {
+ document.title = `${route.title} - Unleash`;
+ return (
+
+
+ {route.title}
+
+
+ );
+};
+
+const renderRoute = (params, route) => {
+ if (!route) {
+ return null;
+ }
+ const title = route.title.startsWith(':') ? params[route.title.substring(1)] : route.title;
+ return route.parent ? renderDoubleBread(title, getRoute(route.parent)) : renderBread(route);
+};
+
+/*
+ Render the breadcrumb.
+
+ We only support two levels.
+
+ Examples:
+ - Features
+ - Features > Create
+ - Features > SomeToggle
+ */
+const Breadcrumb = () => (
+
+ {routes.map(route => (
+ renderRoute(params, route)}
+ />
+ ))}
+
+);
+
+export default Breadcrumb;
diff --git a/frontend/src/component/menu/drawer.jsx b/frontend/src/component/menu/drawer.jsx
new file mode 100644
index 0000000000..ac30a33a49
--- /dev/null
+++ b/frontend/src/component/menu/drawer.jsx
@@ -0,0 +1,38 @@
+import React from 'react';
+import { Drawer, Icon, Navigation } from 'react-mdl';
+import { NavLink } from 'react-router-dom';
+import styles from '../styles.scss';
+
+import { baseRoutes as routes } from './routes';
+
+export const DrawerMenu = () => (
+
+
+
+ Unleash
+
+
+
+ {routes.map(item => (
+
+ {item.title}
+
+ ))}
+
+
+
+
+ GitHub
+
+
+
+);
diff --git a/frontend/src/component/menu/footer.jsx b/frontend/src/component/menu/footer.jsx
new file mode 100644
index 0000000000..1f8713745a
--- /dev/null
+++ b/frontend/src/component/menu/footer.jsx
@@ -0,0 +1,26 @@
+import React from 'react';
+import { FooterDropDownSection, FooterLinkList, FooterSection } from 'react-mdl';
+import { NavLink } from 'react-router-dom';
+
+import { baseRoutes as routes } from './routes';
+
+export const FooterMenu = () => (
+
+
+
+ {routes.map(item => (
+
+ {item.title}
+
+ ))}
+
+
+
+
+ Node.js
+ Java
+ Go
+
+
+
+);
diff --git a/frontend/src/component/menu/routes.js b/frontend/src/component/menu/routes.js
new file mode 100644
index 0000000000..4854810352
--- /dev/null
+++ b/frontend/src/component/menu/routes.js
@@ -0,0 +1,48 @@
+import CreateFeatureToggle from '../../page/features/create';
+import ViewFeatureToggle from '../../page/features/show';
+import Features from '../../page/features';
+import CreateStrategies from '../../page/strategies/create';
+import StrategyView from '../../page/strategies/show';
+import Strategies from '../../page/strategies';
+import HistoryPage from '../../page/history';
+import HistoryTogglePage from '../../page/history/toggle';
+import ShowArchive from '../../page/archive/show';
+import Archive from '../../page/archive';
+import Applications from '../../page/applications';
+import ApplicationView from '../../page/applications/view';
+import LogoutFeatures from '../../page/user/logout';
+
+export const routes = [
+ // Features
+ { path: '/features/create', parent: '/features', title: 'Create', component: CreateFeatureToggle },
+ { path: '/features/:activeTab/:name', parent: '/features', title: ':name', component: ViewFeatureToggle },
+ { path: '/features', title: 'Feature Toggles', icon: 'list', component: Features },
+
+ // Strategies
+ { path: '/strategies/create', title: 'Create', parent: '/strategies', component: CreateStrategies },
+ {
+ path: '/strategies/:activeTab/:strategyName',
+ title: ':strategyName',
+ parent: '/strategies',
+ component: StrategyView,
+ },
+ { path: '/strategies', title: 'Strategies', icon: 'extension', component: Strategies },
+
+ // History
+ { path: '/history/:toggleName', title: ':toggleName', parent: '/history', component: HistoryTogglePage },
+ { path: '/history', title: 'Event History', icon: 'history', component: HistoryPage },
+
+ // Archive
+ { path: '/archive/:activeTab/:name', title: ':name', parent: '/archive', component: ShowArchive },
+ { path: '/archive', title: 'Archived Toggles', icon: 'archive', component: Archive },
+
+ // Applications
+ { path: '/applications/:name', title: ':name', parent: '/applications', component: ApplicationView },
+ { path: '/applications', title: 'Applications', icon: 'apps', component: Applications },
+
+ { path: '/logout', title: 'Sign out', icon: 'exit_to_app', component: LogoutFeatures },
+];
+
+export const getRoute = path => routes.find(route => route.path === path);
+
+export const baseRoutes = routes.filter(route => !route.parent);
diff --git a/frontend/src/component/scroll-to-top.jsx b/frontend/src/component/scroll-to-top.jsx
new file mode 100644
index 0000000000..1c5f88c13e
--- /dev/null
+++ b/frontend/src/component/scroll-to-top.jsx
@@ -0,0 +1,21 @@
+import { Component } from 'react';
+import { withRouter } from 'react-router-dom';
+import PropTypes from 'prop-types';
+
+class ScrollToTop extends Component {
+ static propTypes = {
+ location: PropTypes.object.isRequired,
+ };
+
+ componentDidUpdate(prevProps) {
+ if (this.props.location !== prevProps.location) {
+ window.scrollTo(0, 0);
+ }
+ }
+
+ render() {
+ return this.props.children;
+ }
+}
+
+export default withRouter(ScrollToTop);
diff --git a/frontend/src/component/strategies/edit-container.js b/frontend/src/component/strategies/edit-container.js
index 52ea626ed5..bf13af98ed 100644
--- a/frontend/src/component/strategies/edit-container.js
+++ b/frontend/src/component/strategies/edit-container.js
@@ -1,5 +1,4 @@
import { connect } from 'react-redux';
-import { hashHistory } from 'react-router';
import { createMapper, createActions } from '../input-helpers';
import { updateStrategy } from '../../store/strategy/actions';
@@ -41,7 +40,7 @@ const prepare = (methods, dispatch) => {
parameters,
})(dispatch)
.then(() => methods.clear())
- .then(() => hashHistory.push(`/strategies/view/${input.name}`));
+ .then(() => this.props.history.push(`/strategies/view/${input.name}`));
};
methods.onCancel = e => {
diff --git a/frontend/src/component/strategies/list-component.jsx b/frontend/src/component/strategies/list-component.jsx
index 020602a706..a48ef734cf 100644
--- a/frontend/src/component/strategies/list-component.jsx
+++ b/frontend/src/component/strategies/list-component.jsx
@@ -1,6 +1,6 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
-import { Link } from 'react-router';
+import { Link } from 'react-router-dom';
import { List, ListItem, ListItemContent, IconButton, Grid, Cell } from 'react-mdl';
import { HeaderTitle } from '../common';
diff --git a/frontend/src/component/strategies/strategy-details-component.jsx b/frontend/src/component/strategies/strategy-details-component.jsx
index c9762b8e05..9c612cdfb8 100644
--- a/frontend/src/component/strategies/strategy-details-component.jsx
+++ b/frontend/src/component/strategies/strategy-details-component.jsx
@@ -1,6 +1,5 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
-import { hashHistory } from 'react-router';
import { Tabs, Tab, ProgressBar, Grid, Cell } from 'react-mdl';
import ShowStrategy from './show-strategy-component';
import EditStrategy from './edit-container';
@@ -21,6 +20,7 @@ export default class StrategyDetails extends Component {
fetchStrategies: PropTypes.func.isRequired,
fetchApplications: PropTypes.func.isRequired,
fetchFeatureToggles: PropTypes.func.isRequired,
+ history: PropTypes.object.isRequired,
};
componentDidMount() {
@@ -50,7 +50,7 @@ export default class StrategyDetails extends Component {
}
goToTab(tabName) {
- hashHistory.push(`/strategies/${tabName}/${this.props.strategyName}`);
+ this.props.history.push(`/strategies/${tabName}/${this.props.strategyName}`);
}
render() {
diff --git a/frontend/src/component/user/authentication-simple-component.jsx b/frontend/src/component/user/authentication-simple-component.jsx
index 4f3fb51555..d00a5f3959 100644
--- a/frontend/src/component/user/authentication-simple-component.jsx
+++ b/frontend/src/component/user/authentication-simple-component.jsx
@@ -1,6 +1,5 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { hashHistory } from 'react-router';
import { CardActions, Button, Textfield } from 'react-mdl';
class SimpleAuthenticationComponent extends React.Component {
@@ -16,12 +15,7 @@ class SimpleAuthenticationComponent extends React.Component {
const user = { email };
const path = evt.target.action;
- this.props
- .unsecureLogin(path, user)
- .then(this.props.fetchFeatureToggles)
- .then(() => {
- hashHistory.push('/features');
- });
+ this.props.unsecureLogin(path, user).then(this.props.fetchFeatureToggles);
};
render() {
diff --git a/frontend/src/index.jsx b/frontend/src/index.jsx
index 188f331cce..33104de9b9 100644
--- a/frontend/src/index.jsx
+++ b/frontend/src/index.jsx
@@ -4,8 +4,7 @@ import 'react-mdl/extra/material.js';
import React from 'react';
import ReactDOM from 'react-dom';
-import { applyRouterMiddleware, Router, Route, IndexRedirect, hashHistory } from 'react-router';
-import { useScroll } from 'react-router-scroll';
+import { HashRouter, Route } from 'react-router-dom';
import { Provider } from 'react-redux';
import thunkMiddleware from 'redux-thunk';
import { createStore, applyMiddleware, compose } from 'redux';
@@ -13,20 +12,7 @@ import { createStore, applyMiddleware, compose } from 'redux';
import store from './store';
import MetricsPoller from './metrics-poller';
import App from './component/app';
-
-import Features from './page/features';
-import CreateFeatureToggle from './page/features/create';
-import ViewFeatureToggle from './page/features/show';
-import Strategies from './page/strategies';
-import StrategyView from './page/strategies/show';
-import CreateStrategies from './page/strategies/create';
-import HistoryPage from './page/history';
-import HistoryTogglePage from './page/history/toggle';
-import Archive from './page/archive';
-import ShowArchive from './page/archive/show';
-import Applications from './page/applications';
-import ApplicationView from './page/applications/view';
-import LogoutFeatures from './page/user/logout';
+import ScrollToTop from './component/scroll-to-top';
let composeEnhancers;
@@ -40,48 +26,13 @@ const unleashStore = createStore(store, composeEnhancers(applyMiddleware(thunkMi
const metricsPoller = new MetricsPoller(unleashStore);
metricsPoller.start();
-// "pageTitle" and "link" attributes are for internal usage only
-
ReactDOM.render(
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
,
document.getElementById('app')
);
diff --git a/frontend/src/page/applications/view.js b/frontend/src/page/applications/view.js
index 1838eb5abc..13e72c32a6 100644
--- a/frontend/src/page/applications/view.js
+++ b/frontend/src/page/applications/view.js
@@ -2,10 +2,10 @@ import React from 'react';
import PropTypes from 'prop-types';
import ApplicationEditComponent from '../../component/application/application-edit-container';
-const render = ({ params }) => ;
+const render = ({ match: { params } }) => ;
render.propTypes = {
- params: PropTypes.object.isRequired,
+ match: PropTypes.object.isRequired,
};
export default render;
diff --git a/frontend/src/page/archive/index.js b/frontend/src/page/archive/index.js
index bbd37e3600..bac016f71a 100644
--- a/frontend/src/page/archive/index.js
+++ b/frontend/src/page/archive/index.js
@@ -2,9 +2,10 @@ import React from 'react';
import Archive from '../../component/archive/archive-list-container';
import PropTypes from 'prop-types';
-const render = ({ params }) => ;
+const render = ({ match: { params }, history }) => ;
render.propTypes = {
- params: PropTypes.object,
+ match: PropTypes.object,
+ history: PropTypes.object,
};
export default render;
diff --git a/frontend/src/page/archive/show.js b/frontend/src/page/archive/show.js
index 674e303ac0..242a588d71 100644
--- a/frontend/src/page/archive/show.js
+++ b/frontend/src/page/archive/show.js
@@ -4,11 +4,18 @@ import ViewFeatureToggle from './../../component/archive/view-container';
export default class Features extends PureComponent {
static propTypes = {
- params: PropTypes.object.isRequired,
+ match: PropTypes.object.isRequired,
+ history: PropTypes.object.isRequired,
};
render() {
- const { params } = this.props;
- return ;
+ const { match, history } = this.props;
+ return (
+
+ );
}
}
diff --git a/frontend/src/page/features/create.js b/frontend/src/page/features/create.js
index 7e65314b7d..8531c74052 100644
--- a/frontend/src/page/features/create.js
+++ b/frontend/src/page/features/create.js
@@ -1,6 +1,11 @@
import React from 'react';
import AddFeatureToggleForm from '../../component/feature/form/form-add-feature-container';
+import PropTypes from 'prop-types';
-const render = () => ;
+const render = ({ history }) => ;
+
+render.propTypes = {
+ history: PropTypes.object.isRequired,
+};
export default render;
diff --git a/frontend/src/page/features/index.js b/frontend/src/page/features/index.js
index ccd602443e..bf18de3066 100644
--- a/frontend/src/page/features/index.js
+++ b/frontend/src/page/features/index.js
@@ -1,6 +1,11 @@
import React from 'react';
import FeatureListContainer from './../../component/feature/list-container';
+import PropTypes from 'prop-types';
-const render = () => ;
+const render = ({ history }) => ;
+
+render.propTypes = {
+ history: PropTypes.object.isRequired,
+};
export default render;
diff --git a/frontend/src/page/features/show.js b/frontend/src/page/features/show.js
index 4143c1f289..68c4b6da63 100644
--- a/frontend/src/page/features/show.js
+++ b/frontend/src/page/features/show.js
@@ -4,11 +4,12 @@ import ViewFeatureToggle from './../../component/feature/view-container';
export default class Features extends PureComponent {
static propTypes = {
- params: PropTypes.object.isRequired,
+ match: PropTypes.object.isRequired,
+ history: PropTypes.object.isRequired,
};
render() {
- const { params } = this.props;
- return ;
+ const { match: { params }, history } = this.props;
+ return ;
}
}
diff --git a/frontend/src/page/history/toggle.js b/frontend/src/page/history/toggle.js
index 0c464780bb..e0146459ad 100644
--- a/frontend/src/page/history/toggle.js
+++ b/frontend/src/page/history/toggle.js
@@ -2,10 +2,10 @@ import React from 'react';
import PropTypes from 'prop-types';
import HistoryListToggle from '../../component/history/history-list-toggle-container';
-const render = ({ params }) => ;
+const render = ({ match: { params } }) => ;
render.propTypes = {
- params: PropTypes.object.isRequired,
+ match: PropTypes.object.isRequired,
};
export default render;
diff --git a/frontend/src/page/strategies/show.js b/frontend/src/page/strategies/show.js
index a33d5809e6..6f3125935e 100644
--- a/frontend/src/page/strategies/show.js
+++ b/frontend/src/page/strategies/show.js
@@ -2,10 +2,12 @@ import React from 'react';
import PropTypes from 'prop-types';
import ShowStrategy from '../../component/strategies/strategy-details-container';
-const render = ({ params }) => ;
+const render = ({ match: { params } }) => (
+
+);
render.propTypes = {
- params: PropTypes.object.isRequired,
+ match: PropTypes.object.isRequired,
};
export default render;
diff --git a/frontend/src/page/user/logout.js b/frontend/src/page/user/logout.js
index d2b3217548..710ea681cd 100644
--- a/frontend/src/page/user/logout.js
+++ b/frontend/src/page/user/logout.js
@@ -1,6 +1,11 @@
import React from 'react';
import FeatureListContainer from './../../component/feature/list-container';
+import PropTypes from 'prop-types';
-const render = () => ;
+const render = ({ history }) => ;
+
+render.propTypes = {
+ history: PropTypes.object.isRequired,
+};
export default render;
diff --git a/frontend/src/store/api/__tests__/.eslintrc b/frontend/src/store/api/__tests__/.eslintrc
deleted file mode 100644
index eba2077219..0000000000
--- a/frontend/src/store/api/__tests__/.eslintrc
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "env": {
- "jest": true
- }
-}
diff --git a/frontend/yarn.lock b/frontend/yarn.lock
index 37f81fe163..274277cb1d 100644
--- a/frontend/yarn.lock
+++ b/frontend/yarn.lock
@@ -1732,14 +1732,6 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
safe-buffer "^5.0.1"
sha.js "^2.4.8"
-create-react-class@^15.5.1:
- version "15.6.3"
- resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.6.3.tgz#2d73237fb3f970ae6ebe011a9e66f46dbca80036"
- dependencies:
- fbjs "^0.8.9"
- loose-envify "^1.3.1"
- object-assign "^4.1.1"
-
cross-spawn@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982"
@@ -2030,10 +2022,6 @@ doctrine@^2.1.0:
dependencies:
esutils "^2.0.2"
-dom-helpers@^3.2.1:
- version "3.3.1"
- resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.3.1.tgz#fc1a4e15ffdf60ddde03a480a9c0fece821dd4a6"
-
dom-serializer@0, dom-serializer@~0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82"
@@ -2674,7 +2662,7 @@ fbjs@^0.8.1:
setimmediate "^1.0.5"
ua-parser-js "^0.7.18"
-fbjs@^0.8.16, fbjs@^0.8.9:
+fbjs@^0.8.16:
version "0.8.16"
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db"
dependencies:
@@ -3188,13 +3176,14 @@ hawk@~6.0.2:
hoek "4.x.x"
sntp "2.x.x"
-history@^3.0.0:
- version "3.3.0"
- resolved "https://registry.yarnpkg.com/history/-/history-3.3.0.tgz#fcedcce8f12975371545d735461033579a6dae9c"
+history@^4.7.2:
+ version "4.7.2"
+ resolved "https://registry.yarnpkg.com/history/-/history-4.7.2.tgz#22b5c7f31633c5b8021c7f4a8a954ac139ee8d5b"
dependencies:
invariant "^2.2.1"
loose-envify "^1.2.0"
- query-string "^4.2.2"
+ resolve-pathname "^2.2.0"
+ value-equal "^0.4.0"
warning "^3.0.0"
hmac-drbg@^1.0.0:
@@ -3213,10 +3202,6 @@ hoek@4.x.x:
version "4.2.0"
resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.0.tgz#72d9d0754f7fe25ca2d01ad8f8f9a9449a89526d"
-hoist-non-react-statics@^1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz#aa448cf0986d55cc40773b17174b7dd066cb7cfb"
-
hoist-non-react-statics@^2.2.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.3.1.tgz#343db84c6018c650778898240135a1420ee22ce0"
@@ -3699,6 +3684,10 @@ is-wsl@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d"
+isarray@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
+
isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
@@ -5062,6 +5051,12 @@ path-to-regexp@0.1.7:
version "0.1.7"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
+path-to-regexp@^1.7.0:
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d"
+ dependencies:
+ isarray "0.0.1"
+
path-to-regexp@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-2.1.0.tgz#7e30f9f5b134bd6a28ffc2e3ef1e47075ac5259b"
@@ -5238,7 +5233,7 @@ promise@^7.1.1:
dependencies:
asap "~2.0.3"
-prop-types@^15.5.0, prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.7, prop-types@^15.6.0:
+prop-types@^15.5.0, prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.7, prop-types@^15.6.0:
version "15.6.0"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.0.tgz#ceaf083022fc46b4a35f69e13ef75aed0d639856"
dependencies:
@@ -5246,7 +5241,7 @@ prop-types@^15.5.0, prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6,
loose-envify "^1.3.1"
object-assign "^4.1.1"
-prop-types@^15.6.2:
+prop-types@^15.6.1, prop-types@^15.6.2:
version "15.6.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102"
dependencies:
@@ -5302,13 +5297,6 @@ qs@~6.4.0:
version "6.4.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233"
-query-string@^4.2.2:
- version "4.3.4"
- resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb"
- dependencies:
- object-assign "^4.1.0"
- strict-uri-encode "^1.0.0"
-
querystring-es3@^0.2.0:
version "0.2.1"
resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
@@ -5465,25 +5453,28 @@ react-redux@^5.0.6:
loose-envify "^1.1.0"
prop-types "^15.5.10"
-react-router-scroll@^0.4.1:
- version "0.4.4"
- resolved "https://registry.yarnpkg.com/react-router-scroll/-/react-router-scroll-0.4.4.tgz#4d7b71c75b45ff296e4adca1e029a86e898a155d"
+react-router-dom@^4.3.1:
+ version "4.3.1"
+ resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-4.3.1.tgz#4c2619fc24c4fa87c9fd18f4fb4a43fe63fbd5c6"
dependencies:
- prop-types "^15.6.0"
- scroll-behavior "^0.9.5"
- warning "^3.0.0"
+ history "^4.7.2"
+ invariant "^2.2.4"
+ loose-envify "^1.3.1"
+ prop-types "^15.6.1"
+ react-router "^4.3.1"
+ warning "^4.0.1"
-react-router@^3.0.0:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/react-router/-/react-router-3.2.0.tgz#62b6279d589b70b34e265113e4c0a9261a02ed36"
+react-router@^4.3.1:
+ version "4.3.1"
+ resolved "https://registry.yarnpkg.com/react-router/-/react-router-4.3.1.tgz#aada4aef14c809cb2e686b05cee4742234506c4e"
dependencies:
- create-react-class "^15.5.1"
- history "^3.0.0"
- hoist-non-react-statics "^1.2.0"
- invariant "^2.2.1"
- loose-envify "^1.2.0"
- prop-types "^15.5.6"
- warning "^3.0.0"
+ history "^4.7.2"
+ hoist-non-react-statics "^2.5.0"
+ invariant "^2.2.4"
+ loose-envify "^1.3.1"
+ path-to-regexp "^1.7.0"
+ prop-types "^15.6.1"
+ warning "^4.0.1"
react-test-renderer@^16.0.0-0, react-test-renderer@^16.2.0:
version "16.2.0"
@@ -5852,6 +5843,10 @@ resolve-from@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748"
+resolve-pathname@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-2.2.0.tgz#7e9ae21ed815fd63ab189adeee64dc831eefa879"
+
resolve-url@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
@@ -5967,13 +5962,6 @@ schema-utils@^0.4.5:
ajv "^6.1.0"
ajv-keywords "^3.1.0"
-scroll-behavior@^0.9.5:
- version "0.9.9"
- resolved "https://registry.yarnpkg.com/scroll-behavior/-/scroll-behavior-0.9.9.tgz#ebfe0658455b82ad885b66195215416674dacce2"
- dependencies:
- dom-helpers "^3.2.1"
- invariant "^2.2.2"
-
scss-tokenizer@^0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1"
@@ -6351,10 +6339,6 @@ stream-http@^2.7.2:
to-arraybuffer "^1.0.0"
xtend "^4.0.0"
-strict-uri-encode@^1.0.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
-
string-length@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed"
@@ -6795,6 +6779,10 @@ validate-npm-package-license@^3.0.1:
spdx-correct "~1.0.0"
spdx-expression-parse "~1.0.0"
+value-equal@^0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-0.4.0.tgz#c5bdd2f54ee093c04839d71ce2e4758a6890abc7"
+
vary@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
@@ -6831,6 +6819,12 @@ warning@^3.0.0:
dependencies:
loose-envify "^1.0.0"
+warning@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.1.tgz#66ce376b7fbfe8a887c22bdf0e7349d73d397745"
+ dependencies:
+ loose-envify "^1.0.0"
+
watch@~0.18.0:
version "0.18.0"
resolved "https://registry.yarnpkg.com/watch/-/watch-0.18.0.tgz#28095476c6df7c90c963138990c0a5423eb4b986"