diff --git a/frontend/.eslintrc b/frontend/.eslintrc index ee0b68514f..56bf2c48ee 100644 --- a/frontend/.eslintrc +++ b/frontend/.eslintrc @@ -1,7 +1,8 @@ { "extends": [ "finn", - "finn/node" + "finn/node", + "finn-prettier" ], "rules": { "no-shadow": 0 diff --git a/frontend/package.json b/frontend/package.json index 2bc09fdc6f..a95340a417 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -67,14 +67,16 @@ "babel-preset-stage-0": "^6.5.0", "babel-preset-stage-2": "^6.13.0", "css-loader": "^0.28.4", - "eslint": "^4.1.1", + "eslint": "^4.5.0", "eslint-config-finn": "^2.0.0", + "eslint-config-finn-prettier": "^3.0.0", "eslint-config-finn-react": "^2.0.0", - "eslint-plugin-react": "^7.1.0", + "eslint-plugin-react": "^7.3.0", "extract-text-webpack-plugin": "^3.0.0", "identity-obj-proxy": "^3.0.0", "jest": "^20.0.4", "node-sass": "^4.5.3", + "prettier": "^1.6.0", "react-test-renderer": "^15.4.2", "redux-devtools": "^3.3.1", "sass-loader": "^6.0.6", diff --git a/frontend/src/.eslintrc b/frontend/src/.eslintrc index 941a7b8694..1fb58d9e15 100644 --- a/frontend/src/.eslintrc +++ b/frontend/src/.eslintrc @@ -3,7 +3,9 @@ "extends": [ "finn", "finn-react", - "finn/es-modules" + "finn/es-modules", + "finn-prettier", + "finn-prettier/react" ], "env": { "browser": true, @@ -16,10 +18,7 @@ "parserOptions": { "ecmaVersion": 7, "ecmaFeatures": { - "experimentalObjectRestSpread": true, - "classes":true, - "spread":true, - "restParams": true + "experimentalObjectRestSpread": true } }, "rules": { diff --git a/frontend/src/component/app.jsx b/frontend/src/component/app.jsx index 5d61a15e2e..0b1daa012e 100644 --- a/frontend/src/component/app.jsx +++ b/frontend/src/component/app.jsx @@ -1,7 +1,17 @@ import React, { Component, PropTypes } from 'react'; -import { Layout, Drawer, Header, Navigation, Content, - Footer, FooterSection, FooterDropDownSection, FooterLinkList, - Grid, Cell, Icon, +import { + Layout, + Drawer, + Header, + Navigation, + Content, + Footer, + FooterSection, + FooterDropDownSection, + FooterLinkList, + Grid, + Cell, + Icon, } from 'react-mdl'; import { Link } from 'react-router'; import styles from './styles.scss'; @@ -11,7 +21,7 @@ import UserContainer from './user/user-container'; import ShowUserContainer from './user/show-user-container'; import { ScrollContainer } from 'react-router-scroll'; -function replace (input, params) { +function replace(input, params) { if (!params) { return input; } @@ -26,13 +36,13 @@ export default class App extends Component { location: PropTypes.object.isRequired, params: PropTypes.object.isRequired, routes: PropTypes.array.isRequired, - } + }; static contextTypes = { router: React.PropTypes.object, - } + }; - componentWillReceiveProps (nextProps) { + componentWillReceiveProps(nextProps) { if (this.props.location.pathname !== nextProps.location.pathname) { clearTimeout(this.timer); this.timer = setTimeout(() => { @@ -46,11 +56,12 @@ export default class App extends Component { } } - getSections () { + getSections() { const { routes, params } = this.props; const unique = {}; - const result = routes.splice(1) - .map((routeEntry) => ({ + const result = routes + .splice(1) + .map(routeEntry => ({ name: replace(routeEntry.pageTitle, params), link: replace(routeEntry.link || routeEntry.path, params), })) @@ -72,14 +83,25 @@ export default class App extends Component { return result; } - getTitleWithLinks () { + getTitleWithLinks() { const result = this.getSections(); return ( {result.map((entry, index) => ( - 0 ? 'mdl-layout--large-screen-only' : ''}> + 0 ? 'mdl-layout--large-screen-only' : '' + } + > {index > 0 ? ' › ' : null} - + {entry.name} @@ -88,24 +110,49 @@ export default class App extends Component { ); } - render () { + render() { const shouldUpdateScroll = (prevRouterProps, { location }) => { - if (prevRouterProps && location.pathname !== prevRouterProps.location.pathname) { + 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 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'; return ( - {icon && }{caption} + className={ + isDrawerNavigation && + [styles.navigationLink, linkColor].join(' ') + } + > + {icon && ( + + )} + {caption} ); }; @@ -120,30 +167,79 @@ export default class App extends Component { - - - Unleash + + + + 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( + '/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 + )} -
+
- - GitHub + + GitHub
- + @@ -155,27 +251,56 @@ export default class App extends Component { - {createListItem('/features', 'Feature Toggles')} - {createListItem('/strategies', 'Strategies')} - {createListItem('/history', 'Event History')} - {createListItem('/archive', 'Archived Toggles')} - {createListItem('/applications', 'Applications')} + {createListItem( + '/features', + 'Feature Toggles' + )} + {createListItem( + '/strategies', + 'Strategies' + )} + {createListItem( + '/history', + 'Event History' + )} + {createListItem( + '/archive', + 'Archived Toggles' + )} + {createListItem( + '/applications', + 'Applications' + )} - Node.js - Java - Go + + Node.js + + + Java + + + Go + - + GitHub - A product by FINN.no + + A product by FINN.no + @@ -185,4 +310,4 @@ export default class App extends Component { ); } -}; +} diff --git a/frontend/src/component/application/__tests__/application-edit-component-test.js b/frontend/src/component/application/__tests__/application-edit-component-test.js index ce5a1f500c..21295c3d91 100644 --- a/frontend/src/component/application/__tests__/application-edit-component-test.js +++ b/frontend/src/component/application/__tests__/application-edit-component-test.js @@ -6,9 +6,9 @@ import renderer from 'react-test-renderer'; jest.mock('react-mdl'); test('renders correctly if no application', () => { - const tree = renderer.create( - - ).toJSON(); + const tree = renderer + .create() + .toJSON(); expect(tree).toMatchSnapshot(); }); diff --git a/frontend/src/component/application/application-edit-component.js b/frontend/src/component/application/application-edit-component.js index 9422088a4c..9c38b7d2de 100644 --- a/frontend/src/component/application/application-edit-component.js +++ b/frontend/src/component/application/application-edit-component.js @@ -3,55 +3,64 @@ import React, { Component, PureComponent } from 'react'; import { Link } from 'react-router'; import { - Grid, Cell, Card, CardTitle, CardText, CardMenu, - List, ListItem, ListItemContent, - Textfield, Icon, ProgressBar, - Tabs, Tab, + Grid, + Cell, + Card, + CardTitle, + CardText, + CardMenu, + List, + ListItem, + ListItemContent, + Textfield, + Icon, + ProgressBar, + Tabs, + Tab, Switch, } from 'react-mdl'; import { IconLink, shorten, styles as commonStyles } from '../common'; import { formatFullDateTime } from '../common/util'; class StatefulTextfield extends Component { - constructor (props) { + constructor(props) { super(props); this.state = { value: props.value }; - this.setValue = function setValue (e) { + this.setValue = function setValue(e) { this.setState({ value: e.target.value }); }.bind(this); } - render () { - return ( + render() { + return ( + ); } } class ClientApplications extends PureComponent { - constructor (props) { + constructor(props) { super(props); this.state = { activeTab: 0 }; } - componentDidMount () { + componentDidMount() { this.props.fetchApplication(this.props.appName); } - render () { + render() { if (!this.props.application) { return ; } - const { - application, - storeApplicationMetaData, - } = this.props; + const { application, storeApplicationMetaData } = this.props; const { appName, instances, @@ -63,108 +72,210 @@ class ClientApplications extends PureComponent { color, } = application; - const content = this.state.activeTab === 0 ? ( - - -
Toggles
-
- - {seenToggles.map(({ name, description, enabled, notFound }, i) => - (notFound ? - - - - {name} - - - : - -
} - subtitle={shorten(description, 60)}> - - {shorten(name, 50)} - - - ) - )} - - - -
Implemented strategies
-
- - {strategies.map(({ name, description, notFound }, i) => ( - notFound ? - - - - {name} - - - : - - - - {shorten(name, 50)} - - - - ))} - -
- -
{instances.length} Instances registered
-
- - {instances.map(({ instanceId, clientIp, lastSeen, sdkVersion }, i) => ( - - {clientIp} last seen at {formatFullDateTime(lastSeen)}
- }> - {instanceId} {sdkVersion ? `(${sdkVersion})` : ''} - - - ))} - - - ) : ( - - -
Edit app meta data
-
- - storeApplicationMetaData(appName, 'url', e.target.value)} />
- storeApplicationMetaData(appName, 'description', e.target.value)} /> -
- - storeApplicationMetaData(appName, 'icon', e.target.value)} /> - storeApplicationMetaData(appName, 'color', e.target.value)} /> - -
); - + const content = + this.state.activeTab === 0 ? ( + + +
Toggles
+
+ + {seenToggles.map( + ({ name, description, enabled, notFound }, i) => + notFound ? ( + + + + {name} + + + + ) : ( + + + + + } + subtitle={shorten( + description, + 60 + )} + > + + {shorten(name, 50)} + + + + ) + )} + +
+ +
Implemented strategies
+
+ + {strategies.map( + ({ name, description, notFound }, i) => + notFound ? ( + + + + {name} + + + + ) : ( + + + + {shorten(name, 50)} + + + + ) + )} + +
+ +
{instances.length} Instances registered
+
+ + {instances.map( + ( + { + instanceId, + clientIp, + lastSeen, + sdkVersion, + }, + i + ) => ( + + + {clientIp} last seen at{' '} + {formatFullDateTime(lastSeen)} + + } + > + {instanceId}{' '} + {sdkVersion ? `(${sdkVersion})` : ''} + + + ) + )} + +
+
+ ) : ( + + +
Edit app meta data
+
+ + + storeApplicationMetaData( + appName, + 'url', + e.target.value + )} + /> +
+ + storeApplicationMetaData( + appName, + 'description', + e.target.value + )} + /> +
+ + + storeApplicationMetaData( + appName, + 'icon', + e.target.value + )} + /> + + storeApplicationMetaData( + appName, + 'color', + e.target.value + )} + /> + +
+ ); return ( - + {appName} - {description && - {description} - } - {url && - - } -
- this.setState({ activeTab: tabId })} ripple - tabBarProps={{ style: { width: '100%' } }} className="mdl-color--grey-100"> + {description && {description}} + {url && ( + + + + )} +
+ this.setState({ activeTab: tabId })} + ripple + tabBarProps={{ style: { width: '100%' } }} + className="mdl-color--grey-100" + > Details Edit @@ -175,5 +286,4 @@ class ClientApplications extends PureComponent { } } - export default ClientApplications; diff --git a/frontend/src/component/application/application-edit-container.js b/frontend/src/component/application/application-edit-container.js index b3b2d66989..cec2829e40 100644 --- a/frontend/src/component/application/application-edit-container.js +++ b/frontend/src/component/application/application-edit-container.js @@ -1,6 +1,9 @@ import { connect } from 'react-redux'; import ApplicationEdit from './application-edit-component'; -import { fetchApplication, storeApplicationMetaData } from '../../store/application/actions'; +import { + fetchApplication, + storeApplicationMetaData, +} from '../../store/application/actions'; const mapStateToProps = (state, props) => { let application = state.applications.getIn(['apps', props.appName]); diff --git a/frontend/src/component/application/application-list-component.js b/frontend/src/component/application/application-list-component.js index 6d77846d9e..3c0f117e40 100644 --- a/frontend/src/component/application/application-list-component.js +++ b/frontend/src/component/application/application-list-component.js @@ -3,14 +3,12 @@ import { ProgressBar, Card } from 'react-mdl'; import { AppsLinkList, styles as commonStyles } from '../common'; class ClientStrategies extends Component { - componentDidMount () { + componentDidMount() { this.props.fetchAll(); } - render () { - const { - applications, - } = this.props; + render() { + const { applications } = this.props; if (!applications) { return ; @@ -23,5 +21,4 @@ class ClientStrategies extends Component { } } - export default ClientStrategies; diff --git a/frontend/src/component/application/application-list-container.js b/frontend/src/component/application/application-list-container.js index 519527955d..270ea9ed8f 100644 --- a/frontend/src/component/application/application-list-container.js +++ b/frontend/src/component/application/application-list-container.js @@ -2,7 +2,9 @@ import { connect } from 'react-redux'; import ApplicationList from './application-list-component'; import { fetchAll } from '../../store/application/actions'; -const mapStateToProps = (state) => ({ applications: state.applications.get('list').toJS() }); +const mapStateToProps = state => ({ + applications: state.applications.get('list').toJS(), +}); const Container = connect(mapStateToProps, { fetchAll })(ApplicationList); diff --git a/frontend/src/component/archive/__tests__/__snapshots__/archive-list-component-test.jsx.snap b/frontend/src/component/archive/__tests__/__snapshots__/archive-list-component-test.jsx.snap index 9a0b363fec..e3bb99e079 100644 --- a/frontend/src/component/archive/__tests__/__snapshots__/archive-list-component-test.jsx.snap +++ b/frontend/src/component/archive/__tests__/__snapshots__/archive-list-component-test.jsx.snap @@ -102,7 +102,8 @@ exports[`renders correctly with no archived toggles 1`] = ` } />
- No archived feature toggles, go see + No archived feature toggles, go see + { - const tree = renderer.create( - - ).toJSON(); + const tree = renderer + .create() + .toJSON(); expect(tree).toMatchSnapshot(); }); test('renders correctly with archived toggles', () => { - const tree = renderer.create( - - ).toJSON(); + const tree = renderer + .create() + .toJSON(); expect(tree).toMatchSnapshot(); }); diff --git a/frontend/src/component/archive/archive-container.js b/frontend/src/component/archive/archive-container.js index a7b589e5d7..748f6dd2bb 100644 --- a/frontend/src/component/archive/archive-container.js +++ b/frontend/src/component/archive/archive-container.js @@ -2,7 +2,7 @@ import { connect } from 'react-redux'; import ListComponent from './archive-list-component'; import { fetchArchive, revive } from '../../store/archive-actions'; -const mapStateToProps = (state) => { +const mapStateToProps = state => { const archive = state.archive.get('list').toArray(); return { @@ -10,6 +10,8 @@ const mapStateToProps = (state) => { }; }; -const ArchiveListContainer = connect(mapStateToProps, { fetchArchive, revive })(ListComponent); +const ArchiveListContainer = connect(mapStateToProps, { fetchArchive, revive })( + ListComponent +); export default ArchiveListContainer; diff --git a/frontend/src/component/archive/archive-list-component.jsx b/frontend/src/component/archive/archive-list-component.jsx index 3f0f881798..a09e6d6da6 100644 --- a/frontend/src/component/archive/archive-list-component.jsx +++ b/frontend/src/component/archive/archive-list-component.jsx @@ -4,42 +4,65 @@ import { DataTable, TableHeader, IconButton, Icon, Card } from 'react-mdl'; import { styles as commonStyles } from '../common'; class ArchiveList extends Component { - componentDidMount () { + componentDidMount() { this.props.fetchArchive(); } - render () { + render() { const { archive, revive } = this.props; archive.forEach(e => { e.reviveName = e.name; }); return ( - { - archive.length > 0 ? -
- - ( - revive(reviveName)} /> - )}>Revive - (v ? 'Yes' : '-')}> - Enabled - Toggle name - Created - -
: -
-
- No archived feature toggles, go see active toggles here -
- } + {archive.length > 0 ? ( +
+ + ( + revive(reviveName)} + /> + )} + > + Revive + + (v ? 'Yes' : '-')} + > + Enabled + + Toggle name + + Created + + +
+ ) : ( +
+ +
+ No archived feature toggles, go see{' '} + active toggles here +
+ )}
); } } - export default ArchiveList; diff --git a/frontend/src/component/client-instance/__tests__/client-instance-component-test.jsx b/frontend/src/component/client-instance/__tests__/client-instance-component-test.jsx index 19b28b4445..a614499750 100644 --- a/frontend/src/component/client-instance/__tests__/client-instance-component-test.jsx +++ b/frontend/src/component/client-instance/__tests__/client-instance-component-test.jsx @@ -6,9 +6,14 @@ import renderer from 'react-test-renderer'; jest.mock('react-mdl'); test('renders correctly with no clientInstances', () => { - const tree = renderer.create( - - ).toJSON(); + const tree = renderer + .create( + + ) + .toJSON(); expect(tree).toMatchSnapshot(); }); diff --git a/frontend/src/component/client-instance/client-instance-component.js b/frontend/src/component/client-instance/client-instance-component.js index 883e402609..a76e476d1a 100644 --- a/frontend/src/component/client-instance/client-instance-component.js +++ b/frontend/src/component/client-instance/client-instance-component.js @@ -5,13 +5,13 @@ class ClientStrategies extends Component { static propTypes = { fetchClientInstances: PropTypes.func.isRequired, clientInstances: PropTypes.array.isRequired, - } + }; - componentDidMount () { + componentDidMount() { this.props.fetchClientInstances(); } - render () { + render() { const source = this.props.clientInstances; return ( @@ -20,18 +20,14 @@ class ClientStrategies extends Component { rows={source} selectable={false} > - - Instance ID Application name IP Created Last Seen - ); } } - export default ClientStrategies; diff --git a/frontend/src/component/client-instance/client-instance-container.js b/frontend/src/component/client-instance/client-instance-container.js index 4c13df8eab..8a6bfb8fca 100644 --- a/frontend/src/component/client-instance/client-instance-container.js +++ b/frontend/src/component/client-instance/client-instance-container.js @@ -2,8 +2,12 @@ import { connect } from 'react-redux'; import ClientInstances from './client-instance-component'; import { fetchClientInstances } from '../../store/client-instance-actions'; -const mapStateToProps = (state) => ({ clientInstances: state.clientInstances.toJS() }); +const mapStateToProps = state => ({ + clientInstances: state.clientInstances.toJS(), +}); -const StrategiesContainer = connect(mapStateToProps, { fetchClientInstances })(ClientInstances); +const StrategiesContainer = connect(mapStateToProps, { fetchClientInstances })( + ClientInstances +); export default StrategiesContainer; diff --git a/frontend/src/component/common/index.js b/frontend/src/component/common/index.js index 984ac78358..cb8ec214c3 100644 --- a/frontend/src/component/common/index.js +++ b/frontend/src/component/common/index.js @@ -1,42 +1,71 @@ const React = require('react'); import styles from './common.scss'; - const { - List, ListItem, ListItemContent, - Button, Icon, - Switch, MenuItem, + List, + ListItem, + ListItemContent, + Button, + Icon, + Switch, + MenuItem, } = require('react-mdl'); const { Link } = require('react-router'); export { styles }; -export const shorten = (str, len = 50) => (str && str.length > len ? `${str.substring(0, len)}...` : str); +export const shorten = (str, len = 50) => + str && str.length > len ? `${str.substring(0, len)}...` : str; export const AppsLinkList = ({ apps }) => ( - {apps.length > 0 && apps.map(({ appName, description = '-', icon = 'apps' }) => ( - - - - - {appName} - {description} - - - - ))} + {apps.length > 0 && + apps.map(({ appName, description = '-', icon = 'apps' }) => ( + + + + + {appName} + + {description} + + + + + ))} ); export const HeaderTitle = ({ title, actions, subtitle }) => ( -
+
{title}
{subtitle && {subtitle}}
- {actions &&
{actions}
} + {actions && ( +
{actions}
+ )}
); @@ -45,11 +74,7 @@ export const DataTableHeader = ({ title, actions }) => (

{title}

- {actions && -
- {actions} -
- } + {actions &&
{actions}
}
); @@ -57,17 +82,27 @@ export const FormButtons = ({ submitText = 'Create', onCancel }) => (
  -
); -export const SwitchWithLabel = ({ onChange, checked, children, ...switchProps }) => ( +export const SwitchWithLabel = ({ + onChange, + checked, + children, + ...switchProps +}) => ( {children} @@ -78,54 +113,75 @@ export const SwitchWithLabel = ({ onChange, checked, children, ...switchProps }) export const TogglesLinkList = ({ toggles }) => ( - {toggles.length > 0 && toggles.map(({ name, description = '-', icon = 'toggle' }) => ( - - - - {name} - - - - ))} + {toggles.length > 0 && + toggles.map(({ name, description = '-', icon = 'toggle' }) => ( + + + + {name} + + + + ))} ); -export function getIcon (type) { +export function getIcon(type) { switch (type) { - case 'feature-updated': return 'autorenew'; - case 'feature-created': return 'add'; - case 'feature-deleted': return 'remove'; - case 'feature-archived': return 'archived'; - default: return 'star'; + case 'feature-updated': + return 'autorenew'; + case 'feature-created': + return 'add'; + case 'feature-deleted': + return 'remove'; + case 'feature-archived': + return 'archived'; + default: + return 'star'; } -}; - +} export const IconLink = ({ url, icon }) => ( -
- + + ); export const DropdownButton = ({ label, id }) => ( ); -export const MenuItemWithIcon = ({ icon, label, disabled, ...menuItemProps }) => ( - - +export const MenuItemWithIcon = ({ + icon, + label, + disabled, + ...menuItemProps +}) => ( + + {label} ); const badNumbers = [NaN, Infinity, -Infinity]; -export function calc (value, total, decimal) { - if (typeof value !== 'number' || +export function calc(value, total, decimal) { + if ( + typeof value !== 'number' || typeof total !== 'number' || - typeof decimal !== 'number') { + typeof decimal !== 'number' + ) { return null; } @@ -133,7 +189,7 @@ export function calc (value, total, decimal) { return 0; } - badNumbers.forEach((number) => { + badNumbers.forEach(number => { if ([value, total, decimal].indexOf(number) > -1) { return number; } diff --git a/frontend/src/component/common/util.js b/frontend/src/component/common/util.js index 22e0da0117..a58f003fc5 100644 --- a/frontend/src/component/common/util.js +++ b/frontend/src/component/common/util.js @@ -1,3 +1,11 @@ -const dateTimeOptions = { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' }; +const dateTimeOptions = { + day: '2-digit', + month: '2-digit', + year: 'numeric', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', +}; -export const formatFullDateTime = v => new Date(v).toLocaleString('nb-NO', dateTimeOptions); +export const formatFullDateTime = v => + new Date(v).toLocaleString('nb-NO', dateTimeOptions); diff --git a/frontend/src/component/error/error-component.jsx b/frontend/src/component/error/error-component.jsx index 8920840315..71b4df9eb1 100644 --- a/frontend/src/component/error/error-component.jsx +++ b/frontend/src/component/error/error-component.jsx @@ -6,9 +6,9 @@ class ErrorComponent extends React.Component { static propTypes = { errors: PropTypes.array.isRequired, muteError: PropTypes.func.isRequired, - } + }; - render () { + render() { const showError = this.props.errors.length > 0; const error = showError ? this.props.errors[0] : undefined; const muteError = () => this.props.muteError(error); diff --git a/frontend/src/component/error/error-container.jsx b/frontend/src/component/error/error-container.jsx index 53452c42fd..566efa4093 100644 --- a/frontend/src/component/error/error-container.jsx +++ b/frontend/src/component/error/error-container.jsx @@ -2,12 +2,11 @@ import { connect } from 'react-redux'; import ErrorComponent from './error-component'; import { muteError } from '../../store/error-actions'; - const mapDispatchToProps = { muteError, }; -const mapStateToProps = (state) => ({ +const mapStateToProps = state => ({ errors: state.error.get('list').toArray(), }); diff --git a/frontend/src/component/feature/feature-list-item-component.jsx b/frontend/src/component/feature/feature-list-item-component.jsx index 97e5476a1e..4201cf3148 100644 --- a/frontend/src/component/feature/feature-list-item-component.jsx +++ b/frontend/src/component/feature/feature-list-item-component.jsx @@ -16,36 +16,85 @@ const Feature = ({ const { name, description, enabled, strategies } = feature; const { showLastHour = false } = settings; - const isStale = showLastHour ? metricsLastHour.isFallback : metricsLastMinute.isFallback; + const isStale = showLastHour + ? metricsLastHour.isFallback + : metricsLastMinute.isFallback; - const percent = 1 * (showLastHour ? - calc(metricsLastHour.yes, metricsLastHour.yes + metricsLastHour.no, 0) : - calc(metricsLastMinute.yes, metricsLastMinute.yes + metricsLastMinute.no, 0) - ); + const percent = + 1 * + (showLastHour + ? calc( + metricsLastHour.yes, + metricsLastHour.yes + metricsLastHour.no, + 0 + ) + : calc( + metricsLastMinute.yes, + metricsLastMinute.yes + metricsLastMinute.no, + 0 + )); const strategiesToShow = Math.min(strategies.length, 3); const remainingStrategies = strategies.length - strategiesToShow; - const strategyChips = strategies && strategies.slice(0, strategiesToShow).map((s, i) => - {s.name}); - const summaryChip = remainingStrategies > 0 && - +{remainingStrategies}; + const strategyChips = + strategies && + strategies.slice(0, strategiesToShow).map((s, i) => ( + + {s.name} + + )); + const summaryChip = remainingStrategies > 0 && ( + +{remainingStrategies} + ); return ( - + - toggleFeature(name)} checked={enabled} /> + toggleFeature(name)} + checked={enabled} + /> - - + + {name} - {description} + + {description} + - + {strategyChips} {summaryChip} diff --git a/frontend/src/component/feature/form-add-component.jsx b/frontend/src/component/feature/form-add-component.jsx index 0e7f731796..39922b3f18 100644 --- a/frontend/src/component/feature/form-add-component.jsx +++ b/frontend/src/component/feature/form-add-component.jsx @@ -7,7 +7,7 @@ import { styles as commonStyles } from '../common'; const FormAddComponent = ({ title, ...formProps }) => ( {title} - + ); diff --git a/frontend/src/component/feature/form-add-container.jsx b/frontend/src/component/feature/form-add-container.jsx index ef1c66191a..065898d605 100644 --- a/frontend/src/component/feature/form-add-container.jsx +++ b/frontend/src/component/feature/form-add-container.jsx @@ -1,13 +1,16 @@ import { connect } from 'react-redux'; import { hashHistory } from 'react-router'; -import { createFeatureToggles, validateName } from '../../store/feature-actions'; +import { + createFeatureToggles, + validateName, +} from '../../store/feature-actions'; import { createMapper, createActions } from '../input-helpers'; import FormAddComponent from './form-add-component'; const ID = 'add-feature-toggle'; const mapStateToProps = createMapper({ id: ID, - getDefault () { + getDefault() { let name; try { [, name] = document.location.hash.match(/name=([a-z0-9-_.]+)/i); @@ -16,30 +19,27 @@ const mapStateToProps = createMapper({ }, }); const prepare = (methods, dispatch) => { - methods.onSubmit = (input) => ( - (e) => { - e.preventDefault(); + methods.onSubmit = input => e => { + e.preventDefault(); - if (Array.isArray(input.strategies)) { - input.strategies.forEach((s) => { - delete s.id; - }); - } - - - createFeatureToggles(input)(dispatch) - .then(() => methods.clear()) - .then(() => hashHistory.push(`/features/edit/${input.name}`)); + if (Array.isArray(input.strategies)) { + input.strategies.forEach(s => { + delete s.id; + }); } - ); - methods.onCancel = (evt) => { + createFeatureToggles(input)(dispatch) + .then(() => methods.clear()) + .then(() => hashHistory.push(`/features/edit/${input.name}`)); + }; + + methods.onCancel = evt => { evt.preventDefault(); methods.clear(); hashHistory.push('/features'); }; - methods.addStrategy = (v) => { + methods.addStrategy = v => { v.id = Math.round(Math.random() * 10000000); methods.pushToList('strategies', v); }; @@ -52,23 +52,20 @@ const prepare = (methods, dispatch) => { methods.moveItem('strategies', index, toIndex); }; - methods.removeStrategy = (index) => { + methods.removeStrategy = index => { methods.removeFromList('strategies', index); }; - methods.validateName = (featureToggleName) => { + methods.validateName = featureToggleName => { validateName(featureToggleName) .then(() => methods.setValue('nameError', undefined)) - .catch((err) => methods.setValue('nameError', err.message)); + .catch(err => methods.setValue('nameError', err.message)); }; return methods; }; const actions = createActions({ id: ID, prepare }); -const FormAddContainer = connect( - mapStateToProps, - actions -)(FormAddComponent); +const FormAddContainer = connect(mapStateToProps, actions)(FormAddComponent); export default FormAddContainer; diff --git a/frontend/src/component/feature/form-edit-container.jsx b/frontend/src/component/feature/form-edit-container.jsx index 4c9b359b79..ee3d9e7e6c 100644 --- a/frontend/src/component/feature/form-edit-container.jsx +++ b/frontend/src/component/feature/form-edit-container.jsx @@ -6,7 +6,7 @@ import { createMapper, createActions } from '../input-helpers'; import FormComponent from './form'; const ID = 'edit-feature-toggle'; -function getId (props) { +function getId(props) { return [ID, props.featureToggle.name]; } // TODO: need to scope to the active featureToggle @@ -19,41 +19,39 @@ const mapStateToProps = createMapper({ }); return ownProps.featureToggle; }, - prepare: (props) => { + prepare: props => { props.editmode = true; return props; }, }); const prepare = (methods, dispatch) => { - methods.onSubmit = (input) => ( - (e) => { - e.preventDefault(); + methods.onSubmit = input => e => { + e.preventDefault(); - if (Array.isArray(input.strategies)) { - input.strategies.forEach((s) => { - delete s.id; - }); - } - // TODO: should add error handling - requestUpdateFeatureToggle(input)(dispatch) - .then(() => methods.clear()) - .then(() => hashHistory.push(`/features/view/${input.name}`)); + if (Array.isArray(input.strategies)) { + input.strategies.forEach(s => { + delete s.id; + }); } - ); + // TODO: should add error handling + requestUpdateFeatureToggle(input)(dispatch) + .then(() => methods.clear()) + .then(() => hashHistory.push(`/features/view/${input.name}`)); + }; - methods.onCancel = (evt) => { + methods.onCancel = evt => { evt.preventDefault(); methods.clear(); window.history.back(); }; - methods.addStrategy = (v) => { + methods.addStrategy = v => { v.id = Math.round(Math.random() * 10000000); methods.pushToList('strategies', v); }; - methods.removeStrategy = (index) => { + methods.removeStrategy = index => { methods.removeFromList('strategies', index); }; diff --git a/frontend/src/component/feature/form/index.jsx b/frontend/src/component/feature/form/index.jsx index e04f40ebcd..b6b564b638 100644 --- a/frontend/src/component/feature/form/index.jsx +++ b/frontend/src/component/feature/form/index.jsx @@ -4,7 +4,7 @@ import StrategiesSection from './strategies-section-container'; import { FormButtons } from '../../common'; -const trim = (value) => { +const trim = value => { if (value && value.trim) { return value.trim(); } else { @@ -13,14 +13,14 @@ const trim = (value) => { }; class AddFeatureToggleComponent extends Component { - componentWillMount () { + componentWillMount() { // TODO unwind this stuff if (this.props.initCallRequired === true) { this.props.init(this.props.input); } } - render () { + render() { const { input, setValue, @@ -53,8 +53,9 @@ class AddFeatureToggleComponent extends Component { required value={name} error={nameError} - onBlur={(v) => validateName(v.target.value)} - onChange={(v) => setValue('name', trim(v.target.value))} /> + onBlur={v => validateName(v.target.value)} + onChange={v => setValue('name', trim(v.target.value))} + />
setValue('description', v.target.value)} /> + onChange={v => setValue('description', v.target.value)} + /> - {!editmode &&
-
- { - setValue('enabled', !enabled); - }}>Enabled -
-
} + {!editmode && ( +
+
+ { + setValue('enabled', !enabled); + }} + > + Enabled + +
+
+ )} + removeStrategy={removeStrategy} + />
); } -}; +} AddFeatureToggleComponent.propTypes = { input: PropTypes.object, diff --git a/frontend/src/component/feature/form/strategies-add.jsx b/frontend/src/component/feature/form/strategies-add.jsx index d03420df24..b83e53a5dc 100644 --- a/frontend/src/component/feature/form/strategies-add.jsx +++ b/frontend/src/component/feature/form/strategies-add.jsx @@ -6,13 +6,17 @@ class AddStrategy extends React.Component { strategies: PropTypes.array.isRequired, addStrategy: PropTypes.func.isRequired, fetchStrategies: PropTypes.func.isRequired, - } + }; - addStrategy = (strategyName) => { - const selectedStrategy = this.props.strategies.find(s => s.name === strategyName); + addStrategy = strategyName => { + const selectedStrategy = this.props.strategies.find( + s => s.name === strategyName + ); const parameters = {}; - selectedStrategy.parameters.forEach(({ name }) => { parameters[name] = ''; }); + selectedStrategy.parameters.forEach(({ name }) => { + parameters[name] = ''; + }); this.props.addStrategy({ name: selectedStrategy.name, @@ -20,30 +24,55 @@ class AddStrategy extends React.Component { }); }; - stopPropagation (e) { + stopPropagation(e) { e.stopPropagation(); e.preventDefault(); } - render () { + render() { const menuStyle = { maxHeight: '300px', overflowY: 'auto', backgroundColor: 'rgb(247, 248, 255)', }; return ( -
- - +
+ + Add Strategy: - {this.props.strategies.map((s) => - this.addStrategy(s.name)}>{s.name}) - } + {this.props.strategies.map(s => ( + this.addStrategy(s.name)} + > + {s.name} + + ))}
); } } - export default AddStrategy; diff --git a/frontend/src/component/feature/form/strategies-list.jsx b/frontend/src/component/feature/form/strategies-list.jsx index 847b90a7b4..bb3e86135e 100644 --- a/frontend/src/component/feature/form/strategies-list.jsx +++ b/frontend/src/component/feature/form/strategies-list.jsx @@ -11,9 +11,9 @@ class StrategiesList extends React.Component { updateStrategy: PropTypes.func.isRequired, removeStrategy: PropTypes.func.isRequired, moveStrategy: PropTypes.func.isRequired, - } + }; - render () { + render() { const { strategies, configuredStrategies, @@ -34,12 +34,13 @@ class StrategiesList extends React.Component { moveStrategy={moveStrategy} removeStrategy={removeStrategy.bind(null, i)} updateStrategy={updateStrategy.bind(null, i)} - strategyDefinition={strategies.find(s => s.name === strategy.name)} /> + strategyDefinition={strategies.find( + s => s.name === strategy.name + )} + /> )); return ( -
- {blocks} -
+
{blocks}
); } } diff --git a/frontend/src/component/feature/form/strategies-section-container.jsx b/frontend/src/component/feature/form/strategies-section-container.jsx index 263f5cbee1..34b4afb225 100644 --- a/frontend/src/component/feature/form/strategies-section-container.jsx +++ b/frontend/src/component/feature/form/strategies-section-container.jsx @@ -2,7 +2,9 @@ import { connect } from 'react-redux'; import StrategiesSection from './strategies-section'; import { fetchStrategies } from '../../../store/strategy/actions'; - -export default connect((state) => ({ - strategies: state.strategies.get('list').toArray(), -}), { fetchStrategies })(StrategiesSection); +export default connect( + state => ({ + strategies: state.strategies.get('list').toArray(), + }), + { fetchStrategies } +)(StrategiesSection); diff --git a/frontend/src/component/feature/form/strategies-section.jsx b/frontend/src/component/feature/form/strategies-section.jsx index ea65b5f98f..5ea9f01ed3 100644 --- a/frontend/src/component/feature/form/strategies-section.jsx +++ b/frontend/src/component/feature/form/strategies-section.jsx @@ -11,20 +11,23 @@ class StrategiesSection extends React.Component { removeStrategy: PropTypes.func.isRequired, updateStrategy: PropTypes.func.isRequired, fetchStrategies: PropTypes.func.isRequired, - } + }; - componentWillMount () { + componentWillMount() { this.props.fetchStrategies(); } - render () { + render() { if (!this.props.strategies || this.props.strategies.length === 0) { return ; } return (
- } /> + } + />
); diff --git a/frontend/src/component/feature/form/strategy-configure.jsx b/frontend/src/component/feature/form/strategy-configure.jsx index f58c21af2f..ad11301a56 100644 --- a/frontend/src/component/feature/form/strategy-configure.jsx +++ b/frontend/src/component/feature/form/strategy-configure.jsx @@ -1,8 +1,14 @@ import React, { PropTypes } from 'react'; import { - Textfield, Button, - Card, CardTitle, CardText, CardActions, CardMenu, - IconButton, Icon, + Textfield, + Button, + Card, + CardTitle, + CardText, + CardActions, + CardMenu, + IconButton, + Icon, } from 'react-mdl'; import { DragSource, DropTarget } from 'react-dnd'; import { Link } from 'react-router'; @@ -11,13 +17,13 @@ import StrategyInputList from './strategy-input-list'; import styles from './strategy.scss'; const dragSource = { - beginDrag (props) { + beginDrag(props) { return { id: props.id, index: props.index, }; }, - endDrag (props, monitor) { + endDrag(props, monitor) { if (!monitor.didDrop()) { return; } @@ -29,22 +35,24 @@ const dragSource = { }; const dragTarget = { - drop (props) { + drop(props) { return { index: props.index, }; }, }; -@DropTarget('strategy', dragTarget, connect => ({ // eslint-disable-line new-cap +/* eslint-disable new-cap */ +@DropTarget('strategy', dragTarget, connect => ({ connectDropTarget: connect.dropTarget(), })) -@DragSource('strategy', dragSource, (connect, monitor) => ({ // eslint-disable-line new-cap +@DragSource('strategy', dragSource, (connect, monitor) => ({ connectDragSource: connect.dragSource(), connectDragPreview: connect.dragPreview(), isDragging: monitor.isDragging(), })) class StrategyConfigure extends React.Component { + /* eslint-enable */ static propTypes = { strategy: PropTypes.object.isRequired, strategyDefinition: PropTypes.object.isRequired, @@ -55,7 +63,7 @@ class StrategyConfigure extends React.Component { connectDragPreview: PropTypes.func.isRequired, connectDragSource: PropTypes.func.isRequired, connectDropTarget: PropTypes.func.isRequired, - } + }; handleConfigChange = (key, e) => { this.setConfig(key, e.target.value); @@ -65,31 +73,42 @@ class StrategyConfigure extends React.Component { const parameters = this.props.strategy.parameters || {}; parameters[key] = value; - const updatedStrategy = Object.assign({}, this.props.strategy, { parameters }); + const updatedStrategy = Object.assign({}, this.props.strategy, { + parameters, + }); this.props.updateStrategy(updatedStrategy); - } + }; - handleRemove = (evt) => { + handleRemove = evt => { evt.preventDefault(); this.props.removeStrategy(); - } + }; - renderInputFields ({ parameters }) { + renderInputFields({ parameters }) { if (parameters && parameters.length > 0) { return parameters.map(({ name, type, description, required }) => { let value = this.props.strategy.parameters[name]; if (type === 'percentage') { - if (value == null || (typeof value === 'string' && value === '')) { + if ( + value == null || + (typeof value === 'string' && value === '') + ) { this.setConfig(name, 50); } return (
- {description &&

{description}

} + onChange={this.handleConfigChange.bind( + this, + name + )} + value={1 * value} + /> + {description && ( +

{description}

+ )}
); } else if (type === 'list') { @@ -102,8 +121,14 @@ class StrategyConfigure extends React.Component { } return (
- - {description &&

{description}

} + + {description && ( +

{description}

+ )}
); } else if (type === 'number') { @@ -117,10 +142,15 @@ class StrategyConfigure extends React.Component { style={{ width: '100%' }} name={name} label={name} - onChange={this.handleConfigChange.bind(this, name)} + onChange={this.handleConfigChange.bind( + this, + name + )} value={value} /> - {description &&

{description}

} + {description && ( +

{description}

+ )}
); } else { @@ -133,10 +163,15 @@ class StrategyConfigure extends React.Component { required={required} name={name} label={name} - onChange={this.handleConfigChange.bind(this, name)} + onChange={this.handleConfigChange.bind( + this, + name + )} value={value} /> - {description &&

{description}

} + {description && ( +

{description}

+ )} ); } @@ -145,37 +180,56 @@ class StrategyConfigure extends React.Component { return null; } - render () { - const { isDragging, connectDragPreview, connectDragSource, connectDropTarget } = this.props; + render() { + const { + isDragging, + connectDragPreview, + connectDragSource, + connectDropTarget, + } = this.props; let item; if (this.props.strategyDefinition) { - const inputFields = this.renderInputFields(this.props.strategyDefinition); + const inputFields = this.renderInputFields( + this.props.strategyDefinition + ); const { name } = this.props.strategy; item = ( - +  {name} {this.props.strategyDefinition.description} - { - inputFields && + {inputFields && ( + {inputFields} - } + )} + className={styles.editLink} + > - + {connectDragSource( - )} + + + + )} ); @@ -186,19 +240,27 @@ class StrategyConfigure extends React.Component { "{name}" deleted? The strategy "{name}" does not exist on this server. - Want to create it now? + + Want to create it now? + - + - ); } - return (connectDropTarget(connectDragPreview( -
{item}
- ))); + return connectDropTarget( + connectDragPreview(
{item}
) + ); } } diff --git a/frontend/src/component/feature/form/strategy-input-list.jsx b/frontend/src/component/feature/form/strategy-input-list.jsx index 6da937290f..f60848723d 100644 --- a/frontend/src/component/feature/form/strategy-input-list.jsx +++ b/frontend/src/component/feature/form/strategy-input-list.jsx @@ -1,37 +1,33 @@ import React, { Component, PropTypes } from 'react'; -import { - Textfield, - IconButton, - Chip, -} from 'react-mdl'; +import { Textfield, IconButton, Chip } from 'react-mdl'; export default class InputList extends Component { static propTypes = { name: PropTypes.string.isRequired, list: PropTypes.array.isRequired, setConfig: PropTypes.func.isRequired, - } + }; - onBlur = (e) => { + onBlur = e => { this.setValue(e); window.removeEventListener('keydown', this.onKeyHandler, false); - } + }; - onFocus = (e) => { + onFocus = e => { e.preventDefault(); e.stopPropagation(); window.addEventListener('keydown', this.onKeyHandler, false); - } + }; - onKeyHandler = (e) => { + onKeyHandler = e => { if (e.key === 'Enter') { this.setValue(); e.preventDefault(); e.stopPropagation(); } - } + }; - setValue = (e) => { + setValue = e => { if (e) { e.preventDefault(); e.stopPropagation(); @@ -44,36 +40,53 @@ export default class InputList extends Component { inputValue.value = ''; setConfig(name, list.join(',')); } - } + }; - onClose (index) { + onClose(index) { const { name, list, setConfig } = this.props; list[index] = null; - setConfig(name, list.length === 1 ? '' : list.filter(Boolean).join(',')); + setConfig( + name, + list.length === 1 ? '' : list.filter(Boolean).join(',') + ); } - render () { + render() { const { name, list } = this.props; - return (
-

{name}

- {list.map((entryValue, index) => ( - this.onClose(index)}>{entryValue} - ))} + return ( +
+

{name}

+ {list.map((entryValue, index) => ( + this.onClose(index)} + > + {entryValue} + + ))} -
- - +
+ + +
- -
); + ); } } diff --git a/frontend/src/component/feature/form/strategy-input-percentage.jsx b/frontend/src/component/feature/form/strategy-input-percentage.jsx index cd12d5b82a..158da010c6 100644 --- a/frontend/src/component/feature/form/strategy-input-percentage.jsx +++ b/frontend/src/component/feature/form/strategy-input-percentage.jsx @@ -10,7 +10,16 @@ const labelStyle = { export default ({ name, value, onChange }) => (
-
{name}: {value}%
- +
+ {name}: {value}% +
+
); diff --git a/frontend/src/component/feature/list-component.jsx b/frontend/src/component/feature/list-component.jsx index bc3d17dbf3..11104aca67 100644 --- a/frontend/src/component/feature/list-component.jsx +++ b/frontend/src/component/feature/list-component.jsx @@ -1,9 +1,22 @@ import React, { PropTypes } from 'react'; import Feature from './feature-list-item-component'; import { Link } from 'react-router'; -import { Icon, FABButton, Textfield, Menu, MenuItem, Card, CardActions, List } from 'react-mdl'; +import { + Icon, + FABButton, + Textfield, + Menu, + MenuItem, + Card, + CardActions, + List, +} from 'react-mdl'; -import { MenuItemWithIcon, DropdownButton, styles as commonStyles } from '../common'; +import { + MenuItemWithIcon, + DropdownButton, + styles as commonStyles, +} from '../common'; import styles from './feature.scss'; export default class FeatureListComponent extends React.PureComponent { @@ -14,13 +27,13 @@ export default class FeatureListComponent extends React.PureComponent { fetchFeatureMetrics: PropTypes.func.isRequired, updateSetting: PropTypes.func.isRequired, settings: React.PropTypes.object, - } + }; static contextTypes = { router: React.PropTypes.object, - } + }; - componentDidMount () { + componentDidMount() { this.props.fetchFeatureToggles(); this.props.fetchFeatureMetrics(); this.timer = setInterval(() => { @@ -28,73 +41,147 @@ export default class FeatureListComponent extends React.PureComponent { }, 5000); } - componentWillUnmount () { + componentWillUnmount() { clearInterval(this.timer); } - toggleMetrics () { - this.props.updateSetting('showLastHour', !this.props.settings.showLastHour); + toggleMetrics() { + this.props.updateSetting( + 'showLastHour', + !this.props.settings.showLastHour + ); } - setFilter (v) { + setFilter(v) { this.props.updateSetting('filter', typeof v === 'string' ? v : ''); } - setSort (v) { + setSort(v) { this.props.updateSetting('sort', typeof v === 'string' ? v.trim() : ''); } - render () { - const { features, toggleFeature, featureMetrics, settings } = this.props; + render() { + const { + features, + toggleFeature, + featureMetrics, + settings, + } = this.props; - return (
-
- { this.setFilter(e.target.value); }} - label="Search" - style={{ width: '100%' }} - /> - - - - - + return ( +
+
+ { + this.setFilter(e.target.value); + }} + label="Search" + style={{ width: '100%' }} + /> + + + + + +
+ + + + this.toggleMetrics()} + style={{ width: '168px' }} + > + + + + + + this.setSort( + e.target.getAttribute('data-target') + )} + style={{ width: '168px' }} + > + + Name + + + Enabled + + + Created + + + Strategies + + + Metrics + + + +
+ + {features.map((feature, i) => ( + + ))} + +
- - - - this.toggleMetrics()} - style={{ width: '168px' }}> - - - - - this.setSort(e.target.getAttribute('data-target'))} - style={{ width: '168px' }}> - Name - Enabled - Created - Strategies - Metrics - - -
- - {features.map((feature, i) => - () - )} - -
-
); } } diff --git a/frontend/src/component/feature/list-container.jsx b/frontend/src/component/feature/list-container.jsx index 84bcbe854b..5c99df31b4 100644 --- a/frontend/src/component/feature/list-container.jsx +++ b/frontend/src/component/feature/list-container.jsx @@ -1,23 +1,24 @@ import { connect } from 'react-redux'; -import { toggleFeature, fetchFeatureToggles } from '../../store/feature-actions'; +import { + toggleFeature, + fetchFeatureToggles, +} from '../../store/feature-actions'; import { fetchFeatureMetrics } from '../../store/feature-metrics-actions'; import { updateSettingForGroup } from '../../store/settings/actions'; - import FeatureListComponent from './list-component'; -const mapStateToProps = (state) => { +const mapStateToProps = state => { const featureMetrics = state.featureMetrics.toJS(); const settings = state.settings.toJS().feature || {}; let features = state.features.toJS(); if (settings.filter) { const regex = new RegExp(settings.filter, 'i'); - features = features.filter(feature => - ( - regex.test(feature.name) || + features = features.filter( + feature => + regex.test(feature.name) || regex.test(feature.description) || feature.strategies.some(s => s && s.name && regex.test(s.name)) - ) ); } @@ -26,30 +27,41 @@ const mapStateToProps = (state) => { } if (settings.sort === 'enabled') { - features = features.sort((a, b) => ( - // eslint-disable-next-line + features = features.sort( + (a, b) => + // eslint-disable-next-line a.enabled === b.enabled ? 0 : a.enabled ? -1 : 1 - )); + ); } else if (settings.sort === 'created') { - features = features.sort((a, b) => ( - new Date(a.createdAt) > new Date(b.createdAt) ? -1 : 1 - )); + features = features.sort( + (a, b) => (new Date(a.createdAt) > new Date(b.createdAt) ? -1 : 1) + ); } else if (settings.sort === 'name') { features = features.sort((a, b) => { - if (a.name < b.name) { return -1; } - if (a.name > b.name) { return 1; } + if (a.name < b.name) { + return -1; + } + if (a.name > b.name) { + return 1; + } return 0; }); } else if (settings.sort === 'strategies') { - features = features.sort((a, b) => ( - a.strategies.length > b.strategies.length ? -1 : 1 - )); + features = features.sort( + (a, b) => (a.strategies.length > b.strategies.length ? -1 : 1) + ); } else if (settings.sort === 'metrics') { - const target = settings.showLastHour ? featureMetrics.lastHour : featureMetrics.lastMinute; + const target = settings.showLastHour + ? featureMetrics.lastHour + : featureMetrics.lastMinute; features = features.sort((a, b) => { - if (!target[a.name]) { return 1; } - if (!target[b.name]) { return -1; } + if (!target[a.name]) { + return 1; + } + if (!target[b.name]) { + return -1; + } if (target[a.name].yes > target[b.name].yes) { return -1; } @@ -71,9 +83,8 @@ const mapDispatchToProps = { updateSetting: updateSettingForGroup('feature'), }; -const FeatureListContainer = connect( - mapStateToProps, - mapDispatchToProps -)(FeatureListComponent); +const FeatureListContainer = connect(mapStateToProps, mapDispatchToProps)( + FeatureListComponent +); export default FeatureListContainer; diff --git a/frontend/src/component/feature/metric-component.jsx b/frontend/src/component/feature/metric-component.jsx index ee5d5fe864..6a461fc7b2 100644 --- a/frontend/src/component/feature/metric-component.jsx +++ b/frontend/src/component/feature/metric-component.jsx @@ -11,15 +11,23 @@ const StrategyChipItem = ({ strategy }) => ( - {strategy.name} + + {strategy.name} + ); // TODO what about "missing" strategies here? const StrategiesList = ({ strategies }) => ( -
With {strategies.length > 1 ? 'strategies' : 'strategy'} { - strategies.map((strategy, i) => ) - }
+
+ With {strategies.length > 1 ? 'strategies' : 'strategy'}{' '} + {strategies.map((strategy, i) => ( + + ))} +
); export default class MetricComponent extends React.Component { @@ -28,9 +36,9 @@ export default class MetricComponent extends React.Component { featureToggle: PropTypes.object.isRequired, fetchSeenApps: PropTypes.func.isRequired, fetchFeatureMetrics: PropTypes.func.isRequired, - } + }; - componentWillMount () { + componentWillMount() { this.props.fetchSeenApps(); this.props.fetchFeatureMetrics(); this.timer = setInterval(() => { @@ -38,11 +46,11 @@ export default class MetricComponent extends React.Component { }, 5000); } - componentWillUnmount () { + componentWillUnmount() { clearInterval(this.timer); } - render () { + render() { const { metrics = {}, featureToggle } = this.props; const { lastHour = { yes: 0, no: 0, isFallback: true }, @@ -50,41 +58,81 @@ export default class MetricComponent extends React.Component { seenApps = [], } = metrics; - const lastHourPercent = 1 * calc(lastHour.yes, lastHour.yes + lastHour.no, 0); - const lastMinutePercent = 1 * calc(lastMinute.yes, lastMinute.yes + lastMinute.no, 0); + const lastHourPercent = + 1 * calc(lastHour.yes, lastHour.yes + lastHour.no, 0); + const lastMinutePercent = + 1 * calc(lastMinute.yes, lastMinute.yes + lastMinute.no, 0); - return (
- - - - {lastMinute.isFallback ? -

No metrics available

: -

Last minute
Yes {lastMinute.yes}, No: {lastMinute.no}

- } -
- - - {lastHour.isFallback ? -

No metrics available

: -

Last hour
Yes {lastHour.yes}, No: {lastHour.no}

- } -
- - {seenApps.length > 0 ? - (
Seen in applications:
) : -
- -
Not used in an app in the last hour. - This might be due to your client implementation is not reporting usage.
-
- } - - Created {formatFullDateTime(featureToggle.createdAt)} -
-
-
- -
); + return ( +
+ + + + {lastMinute.isFallback ? ( +

+ No metrics available +

+ ) : ( +

+ Last minute +
Yes {lastMinute.yes}, No: {lastMinute.no} +

+ )} +
+ + + {lastHour.isFallback ? ( +

+ No metrics available +

+ ) : ( +

+ Last hour +
Yes {lastHour.yes}, No: {lastHour.no} +

+ )} +
+ + {seenApps.length > 0 ? ( +
+ Seen in applications: +
+ ) : ( +
+ +
+ + + Not used in an app in the last hour. + + This might be due to your client + implementation is not reporting usage. + +
+
+ )} + + + Created{' '} + {formatFullDateTime(featureToggle.createdAt)} + +
+
+
+ +
+ ); } } diff --git a/frontend/src/component/feature/metric-container.jsx b/frontend/src/component/feature/metric-container.jsx index 90f8fe6c54..55cef55bea 100644 --- a/frontend/src/component/feature/metric-container.jsx +++ b/frontend/src/component/feature/metric-container.jsx @@ -1,11 +1,13 @@ - import { connect } from 'react-redux'; -import { fetchFeatureMetrics, fetchSeenApps } from '../../store/feature-metrics-actions'; +import { + fetchFeatureMetrics, + fetchSeenApps, +} from '../../store/feature-metrics-actions'; import MatricComponent from './metric-component'; -function getMetricsForToggle (state, toggleName) { +function getMetricsForToggle(state, toggleName) { if (!toggleName) { return; } @@ -16,14 +18,20 @@ function getMetricsForToggle (state, toggleName) { } if (state.featureMetrics.hasIn(['lastHour', toggleName])) { result.lastHour = state.featureMetrics.getIn(['lastHour', toggleName]); - result.lastMinute = state.featureMetrics.getIn(['lastMinute', toggleName]); + result.lastMinute = state.featureMetrics.getIn([ + 'lastMinute', + toggleName, + ]); } return result; } -export default connect((state, props) => ({ - metrics: getMetricsForToggle(state, props.featureToggle.name), -}), { - fetchFeatureMetrics, - fetchSeenApps, -})(MatricComponent); +export default connect( + (state, props) => ({ + metrics: getMetricsForToggle(state, props.featureToggle.name), + }), + { + fetchFeatureMetrics, + fetchSeenApps, + } +)(MatricComponent); diff --git a/frontend/src/component/feature/progress.jsx b/frontend/src/component/feature/progress.jsx index 1de4604d69..bc21444a44 100644 --- a/frontend/src/component/feature/progress.jsx +++ b/frontend/src/component/feature/progress.jsx @@ -2,7 +2,7 @@ import React, { PropTypes, Component } from 'react'; import styles from './progress-styles.scss'; class Progress extends Component { - constructor (props) { + constructor(props) { super(props); this.state = { @@ -11,7 +11,7 @@ class Progress extends Component { }; } - componentDidMount () { + componentDidMount() { if (this.props.initialAnimation) { this.initialTimeout = setTimeout(() => { this.rafTimerInit = window.requestAnimationFrame(() => { @@ -23,7 +23,7 @@ class Progress extends Component { } } - componentWillReceiveProps ({ percentage }) { + componentWillReceiveProps({ percentage }) { if (this.state.percentage !== percentage) { const nextState = { percentage }; if (this.props.animatePercentageText) { @@ -35,12 +35,14 @@ class Progress extends Component { } } - getTarget (target) { + getTarget(target) { const start = this.state.percentageText; const TOTAL_ANIMATION_TIME = 5000; const diff = start > target ? -(start - target) : target - start; const perCycle = TOTAL_ANIMATION_TIME / diff; - const cyclesCounter = Math.round(Math.abs(TOTAL_ANIMATION_TIME / perCycle)); + const cyclesCounter = Math.round( + Math.abs(TOTAL_ANIMATION_TIME / perCycle) + ); const perCycleTime = Math.round(Math.abs(perCycle)); return { @@ -52,7 +54,7 @@ class Progress extends Component { }; } - animateTo (percentage, targetState) { + animateTo(percentage, targetState) { cancelAnimationFrame(this.rafCounterTimer); clearTimeout(this.nextTimer); @@ -73,17 +75,16 @@ class Progress extends Component { }); } - - componentWillUnmount () { + componentWillUnmount() { clearTimeout(this.initialTimeout); clearTimeout(this.nextTimer); window.cancelAnimationFrame(this.rafTimerInit); window.cancelAnimationFrame(this.rafCounterTimer); } - render () { + render() { const { strokeWidth, colorClassName, isFallback } = this.props; - const radius = (50 - strokeWidth / 2); + const radius = 50 - strokeWidth / 2; const pathDescription = ` M 50,50 m 0,-${radius} a ${radius},${radius} 0 1 1 0,${2 * radius} @@ -93,17 +94,27 @@ class Progress extends Component { const diameter = Math.PI * 2 * radius; const progressStyle = { strokeDasharray: `${diameter}px ${diameter}px`, - strokeDashoffset: `${((100 - this.state.percentage) / 100 * diameter)}px`, + strokeDashoffset: `${(100 - this.state.percentage) / + 100 * + diameter}px`, }; - return (isFallback ? - { - // eslint-disable-next-line max-len - } - : + return isFallback ? ( + + { + // eslint-disable-next-line max-len + } + + + ) : ( - {this.state.percentageText}% + + {this.state.percentageText}% + ); } diff --git a/frontend/src/component/feature/view-component.jsx b/frontend/src/component/feature/view-component.jsx index f350d8d48a..a800a90811 100644 --- a/frontend/src/component/feature/view-component.jsx +++ b/frontend/src/component/feature/view-component.jsx @@ -1,5 +1,15 @@ import React, { PropTypes } from 'react'; -import { Tabs, Tab, ProgressBar, Button, Card, CardTitle, CardText, CardActions, Switch } from 'react-mdl'; +import { + Tabs, + Tab, + ProgressBar, + Button, + Card, + CardTitle, + CardText, + CardActions, + Switch, +} from 'react-mdl'; import { hashHistory, Link } from 'react-router'; import HistoryComponent from '../history/history-list-toggle-container'; @@ -14,7 +24,7 @@ const TABS = { }; export default class ViewFeatureToggleComponent extends React.Component { - constructor (props) { + constructor(props) { super(props); } @@ -26,19 +36,16 @@ export default class ViewFeatureToggleComponent extends React.Component { removeFeatureToggle: PropTypes.func.isRequired, fetchFeatureToggles: PropTypes.array.isRequired, featureToggle: PropTypes.object.isRequired, - } + }; - componentWillMount () { + componentWillMount() { if (this.props.features.length === 0) { this.props.fetchFeatureToggles(); } } - getTabContent (activeTab) { - const { - featureToggle, - featureToggleName, - } = this.props; + getTabContent(activeTab) { + const { featureToggle, featureToggleName } = this.props; if (TABS[activeTab] === TABS.history) { return ; @@ -49,11 +56,11 @@ export default class ViewFeatureToggleComponent extends React.Component { } } - goToTab (tabName, featureToggleName) { + goToTab(tabName, featureToggleName) { hashHistory.push(`/features/${tabName}/${featureToggleName}`); } - render () { + render() { const { featureToggle, features, @@ -64,44 +71,95 @@ export default class ViewFeatureToggleComponent extends React.Component { } = this.props; if (!featureToggle) { - if (features.length === 0 ) { + if (features.length === 0) { return ; } return ( - Could not find the toggle - {featureToggleName} + Could not find the toggle{' '} + + {featureToggleName} + ); } - const activeTabId = TABS[this.props.activeTab] ? TABS[this.props.activeTab] : TABS.view; + const activeTabId = TABS[this.props.activeTab] + ? TABS[this.props.activeTab] + : TABS.view; const tabContent = this.getTabContent(activeTab); const removeToggle = () => { - if (window.confirm('Are you sure you want to remove this toggle?')) { // eslint-disable-line no-alert + if ( + // eslint-disable-next-line no-alert + window.confirm('Are you sure you want to remove this toggle?') + ) { removeFeatureToggle(featureToggle.name); hashHistory.push('/features'); } }; return ( - - {featureToggle.name} + + + {featureToggle.name} + {featureToggle.description} - + - toggleFeature(featureToggle.name)}> + toggleFeature(featureToggle.name)} + > {featureToggle.enabled ? 'Enabled' : 'Disabled'} - + -
- - this.goToTab('view', featureToggleName)}>Metrics - this.goToTab('edit', featureToggleName)}>Edit - this.goToTab('history', featureToggleName)}>History +
+ + this.goToTab('view', featureToggleName)} + > + Metrics + + this.goToTab('edit', featureToggleName)} + > + Edit + + + this.goToTab('history', featureToggleName)} + > + History + {tabContent}
diff --git a/frontend/src/component/feature/view-container.jsx b/frontend/src/component/feature/view-container.jsx index 5acbae1714..10fc279335 100644 --- a/frontend/src/component/feature/view-container.jsx +++ b/frontend/src/component/feature/view-container.jsx @@ -1,16 +1,24 @@ - import { connect } from 'react-redux'; -import { fetchFeatureToggles, toggleFeature, removeFeatureToggle } from '../../store/feature-actions'; - -import ViewToggleComponent from './view-component'; - -export default connect((state, props) => ({ - features: state.features.toJS(), - featureToggle: state.features.toJS().find(toggle => toggle.name === props.featureToggleName), - activeTab: props.activeTab, -}), { +import { fetchFeatureToggles, toggleFeature, removeFeatureToggle, -})(ViewToggleComponent); +} from '../../store/feature-actions'; + +import ViewToggleComponent from './view-component'; + +export default connect( + (state, props) => ({ + features: state.features.toJS(), + featureToggle: state.features + .toJS() + .find(toggle => toggle.name === props.featureToggleName), + activeTab: props.activeTab, + }), + { + fetchFeatureToggles, + toggleFeature, + removeFeatureToggle, + } +)(ViewToggleComponent); diff --git a/frontend/src/component/history/history-component.jsx b/frontend/src/component/history/history-component.jsx index a3a85bc6d7..5b0c35882c 100644 --- a/frontend/src/component/history/history-component.jsx +++ b/frontend/src/component/history/history-component.jsx @@ -4,15 +4,15 @@ import HistoryList from './history-list-container'; import { styles as commonStyles } from '../common'; class History extends PureComponent { - componentDidMount () { + componentDidMount() { this.props.fetchHistory(); } - toggleShowDiff () { + toggleShowDiff() { this.setState({ showData: !this.state.showData }); } - render () { + render() { const { history } = this.props; if (history.length < 0) { return; diff --git a/frontend/src/component/history/history-container.js b/frontend/src/component/history/history-container.js index 3b1d7cb8a2..9b7ae8f719 100644 --- a/frontend/src/component/history/history-container.js +++ b/frontend/src/component/history/history-container.js @@ -2,13 +2,15 @@ import { connect } from 'react-redux'; import HistoryComponent from './history-component'; import { fetchHistory } from '../../store/history-actions'; -const mapStateToProps = (state) => { +const mapStateToProps = state => { const history = state.history.get('list').toArray(); return { history, }; }; -const HistoryListContainer = connect(mapStateToProps, { fetchHistory })(HistoryComponent); +const HistoryListContainer = connect(mapStateToProps, { fetchHistory })( + HistoryComponent +); export default HistoryListContainer; diff --git a/frontend/src/component/history/history-item-diff.jsx b/frontend/src/component/history/history-item-diff.jsx index 2be1c27986..2b38b298c0 100644 --- a/frontend/src/component/history/history-item-diff.jsx +++ b/frontend/src/component/history/history-item-diff.jsx @@ -16,18 +16,22 @@ const KLASSES = { N: style.positive, // added }; -function buildItemDiff (diff, key) { +function buildItemDiff(diff, key) { let change; if (diff.lhs !== undefined) { change = (
-
- {key}: {JSON.stringify(diff.lhs)}
+
+ - {key}: {JSON.stringify(diff.lhs)} +
); } else if (diff.rhs !== undefined) { change = (
-
+ {key}: {JSON.stringify(diff.rhs)}
+
+ + {key}: {JSON.stringify(diff.rhs)} +
); } @@ -35,7 +39,7 @@ function buildItemDiff (diff, key) { return change; } -function buildDiff (diff, idx) { +function buildDiff(diff, idx) { let change; const key = diff.path.join('.'); @@ -44,26 +48,34 @@ function buildDiff (diff, idx) { } else if (diff.lhs !== undefined && diff.rhs !== undefined) { change = (
-
- {key}: {JSON.stringify(diff.lhs)}
-
+ {key}: {JSON.stringify(diff.rhs)}
+
+ - {key}: {JSON.stringify(diff.lhs)} +
+
+ + {key}: {JSON.stringify(diff.rhs)} +
); } else { const spadenClass = KLASSES[diff.kind]; const prefix = DIFF_PREFIXES[diff.kind]; - change = (
{prefix} {key}: {JSON.stringify(diff.rhs || diff.item)}
); + change = ( +
+ {prefix} {key}: {JSON.stringify(diff.rhs || diff.item)} +
+ ); } - return (
{change}
); + return
{change}
; } class HistoryItem extends PureComponent { static propTypes = { entry: PropTypes.object, - } + }; - render () { + render() { const entry = this.props.entry; let changes; @@ -71,12 +83,20 @@ class HistoryItem extends PureComponent { changes = entry.diffs.map(buildDiff); } else { // Just show the data if there is no diff yet. - changes =
{JSON.stringify(entry.data, null, 2)}
; + changes = ( +
+ {JSON.stringify(entry.data, null, 2)} +
+ ); } - return (
-            {changes.length === 0 ? '(no changes)' : changes}
-        
); + return ( +
+                
+                    {changes.length === 0 ? '(no changes)' : changes}
+                
+            
+ ); } } diff --git a/frontend/src/component/history/history-item-json.jsx b/frontend/src/component/history/history-item-json.jsx index 8f836df42d..fb82482360 100644 --- a/frontend/src/component/history/history-item-json.jsx +++ b/frontend/src/component/history/history-item-json.jsx @@ -5,9 +5,9 @@ import style from './history.scss'; class HistoryItem extends PureComponent { static propTypes = { entry: PropTypes.object, - } + }; - render () { + render() { const localEventData = JSON.parse(JSON.stringify(this.props.entry)); delete localEventData.description; delete localEventData.name; diff --git a/frontend/src/component/history/history-list-component.jsx b/frontend/src/component/history/history-list-component.jsx index 0be1b2372d..7039b0195b 100644 --- a/frontend/src/component/history/history-list-component.jsx +++ b/frontend/src/component/history/history-list-component.jsx @@ -2,17 +2,21 @@ import React, { Component } from 'react'; import HistoryItemDiff from './history-item-diff'; import HistoryItemJson from './history-item-json'; import { Table, TableHeader } from 'react-mdl'; -import { DataTableHeader, SwitchWithLabel, styles as commonStyles } from '../common'; +import { + DataTableHeader, + SwitchWithLabel, + styles as commonStyles, +} from '../common'; import { formatFullDateTime } from '../common/util'; import styles from './history.scss'; class HistoryList extends Component { - toggleShowDiff () { + toggleShowDiff() { this.props.updateSetting('showData', !this.props.settings.showData); } - render () { + render() { const showData = this.props.settings.showData; const { history } = this.props; if (!history || history.length < 0) { @@ -20,41 +24,84 @@ class HistoryList extends Component { } const truncateTableCell = v => ( - {v} + + {v} + ); let entries; if (showData) { - entries = history.map((entry) => ); + entries = history.map(entry => ( + + )); } else { - entries = ( Object.assign({ - diff: (), - }, entry)) - } - className={commonStyles.fullwidth} - style={{ border: 0, tableLayout: 'fixed', minWidth: '840px' }} - > - Type - User - Diff - Time -
); + entries = ( + + Object.assign( + { + diff: , + }, + entry + ) + )} + className={commonStyles.fullwidth} + style={{ + border: 0, + tableLayout: 'fixed', + minWidth: '840px', + }} + > + + Type + + + User + + Diff + + Time + +
+ ); } return (
- - Full events - - }/> -
- {entries} -
+ + Full events + + } + /> +
{entries}
); } diff --git a/frontend/src/component/history/history-list-container.jsx b/frontend/src/component/history/history-list-container.jsx index b3055e52e3..dafc7057a2 100644 --- a/frontend/src/component/history/history-list-container.jsx +++ b/frontend/src/component/history/history-list-container.jsx @@ -2,7 +2,7 @@ import { connect } from 'react-redux'; import HistoryListToggleComponent from './history-list-component'; import { updateSettingForGroup } from '../../store/settings/actions'; -const mapStateToProps = (state) => { +const mapStateToProps = state => { const settings = state.settings.toJS().history || {}; return { diff --git a/frontend/src/component/history/history-list-toggle-component.jsx b/frontend/src/component/history/history-list-toggle-component.jsx index 3b2f2553b9..c9b6d88c43 100644 --- a/frontend/src/component/history/history-list-toggle-component.jsx +++ b/frontend/src/component/history/history-list-toggle-component.jsx @@ -4,22 +4,18 @@ import HistoryList from './history-list-container'; class HistoryListToggle extends Component { static propTypes = { toggleName: PropTypes.string.isRequired, - } + }; - componentDidMount () { + componentDidMount() { this.props.fetchHistoryForToggle(this.props.toggleName); } - render () { + render() { if (!this.props.history || this.props.history.length === 0) { return fetching..; } const { history } = this.props; - return ( - - ); + return ; } } diff --git a/frontend/src/component/history/history-list-toggle-container.jsx b/frontend/src/component/history/history-list-toggle-container.jsx index e44c4d4cba..d581685a20 100644 --- a/frontend/src/component/history/history-list-toggle-container.jsx +++ b/frontend/src/component/history/history-list-toggle-container.jsx @@ -2,7 +2,7 @@ import { connect } from 'react-redux'; import HistoryListToggleComponent from './history-list-toggle-component'; import { fetchHistoryForToggle } from '../../store/history-actions'; -function getHistoryFromToggle (state, toggleName) { +function getHistoryFromToggle(state, toggleName) { if (!toggleName) { return []; } diff --git a/frontend/src/component/input-helpers.js b/frontend/src/component/input-helpers.js index d3c734422c..e870c03f92 100644 --- a/frontend/src/component/input-helpers.js +++ b/frontend/src/component/input-helpers.js @@ -9,14 +9,14 @@ import { createMove, } from '../store/input-actions'; -function getId (id, ownProps) { +function getId(id, ownProps) { if (typeof id === 'function') { return id(ownProps); // should return array... } return [id]; } -export function createMapper ({ id, getDefault, prepare = (v) => v }) { +export function createMapper({ id, getDefault, prepare = v => v }) { return (state, ownProps) => { let input; let initCallRequired = false; @@ -28,46 +28,75 @@ export function createMapper ({ id, getDefault, prepare = (v) => v }) { input = getDefault ? getDefault(state, ownProps) : {}; } - return prepare({ - initCallRequired, - input, - }, state, ownProps); + return prepare( + { + initCallRequired, + input, + }, + state, + ownProps + ); }; } -export function createActions ({ id, prepare = (v) => v }) { - return (dispatch, ownProps) => (prepare({ +export function createActions({ id, prepare = v => v }) { + return (dispatch, ownProps) => + prepare( + { + clear() { + dispatch(createClear({ id: getId(id, ownProps) })); + }, - clear () { - dispatch(createClear({ id: getId(id, ownProps) })); - }, + init(value) { + dispatch(createInit({ id: getId(id, ownProps), value })); + }, - init (value) { - dispatch(createInit({ id: getId(id, ownProps), value })); - }, + setValue(key, value) { + dispatch( + createSet({ id: getId(id, ownProps), key, value }) + ); + }, - setValue (key, value) { - dispatch(createSet({ id: getId(id, ownProps), key, value })); - }, + pushToList(key, value) { + dispatch( + createPush({ id: getId(id, ownProps), key, value }) + ); + }, - pushToList (key, value) { - dispatch(createPush({ id: getId(id, ownProps), key, value })); - }, + removeFromList(key, index) { + dispatch( + createPop({ id: getId(id, ownProps), key, index }) + ); + }, - removeFromList (key, index) { - dispatch(createPop({ id: getId(id, ownProps), key, index })); - }, + moveItem(key, index, toIndex) { + dispatch( + createMove({ + id: getId(id, ownProps), + key, + index, + toIndex, + }) + ); + }, - moveItem (key, index, toIndex) { - dispatch(createMove({ id: getId(id, ownProps), key, index, toIndex })); - }, + updateInList(key, index, newValue, merge = false) { + dispatch( + createUp({ + id: getId(id, ownProps), + key, + index, + newValue, + merge, + }) + ); + }, - updateInList (key, index, newValue, merge = false) { - dispatch(createUp({ id: getId(id, ownProps), key, index, newValue, merge })); - }, - - incValue (key) { - dispatch(createInc({ id: getId(id, ownProps), key })); - }, - }, dispatch, ownProps)); + incValue(key) { + dispatch(createInc({ id: getId(id, ownProps), key })); + }, + }, + dispatch, + ownProps + ); } diff --git a/frontend/src/component/strategies/add-container.js b/frontend/src/component/strategies/add-container.js index cd718cd0e9..79d5cb3f53 100644 --- a/frontend/src/component/strategies/add-container.js +++ b/frontend/src/component/strategies/add-container.js @@ -8,13 +8,13 @@ import AddStrategy from './add-strategy'; const ID = 'add-strategy'; const prepare = (methods, dispatch) => { - methods.onSubmit = (input) => ( - (e) => { - e.preventDefault(); - // clean - const parameters = (input.parameters || []) - .filter((name) => !!name) - .map(({ + methods.onSubmit = input => e => { + e.preventDefault(); + // clean + const parameters = (input.parameters || []) + .filter(name => !!name) + .map( + ({ name, type = 'string', description = '', @@ -24,27 +24,26 @@ const prepare = (methods, dispatch) => { type, description, required, - })); + }) + ); - createStrategy({ - name: input.name, - description: input.description, - parameters, - })(dispatch) - .then(() => methods.clear()) - // somewhat quickfix / hacky to go back.. - .then(() => window.history.back()); - } - ); + createStrategy({ + name: input.name, + description: input.description, + parameters, + })(dispatch) + .then(() => methods.clear()) + // somewhat quickfix / hacky to go back.. + .then(() => window.history.back()); + }; - methods.onCancel = (e) => { + methods.onCancel = e => { e.preventDefault(); methods.clear(); // somewhat quickfix / hacky to go back.. window.history.back(); }; - return methods; }; @@ -53,13 +52,16 @@ const actions = createActions({ prepare, }); -export default connect(createMapper({ - id: ID, - getDefault () { - let name; - try { - [, name] = document.location.hash.match(/name=([a-z0-9-_.]+)/i); - } catch (e) {} - return { name }; - }, -}), actions)(AddStrategy); +export default connect( + createMapper({ + id: ID, + getDefault() { + let name; + try { + [, name] = document.location.hash.match(/name=([a-z0-9-_.]+)/i); + } catch (e) {} + return { name }; + }, + }), + actions +)(AddStrategy); diff --git a/frontend/src/component/strategies/add-strategy.jsx b/frontend/src/component/strategies/add-strategy.jsx index 84c25d9e4c..3f4f81205b 100644 --- a/frontend/src/component/strategies/add-strategy.jsx +++ b/frontend/src/component/strategies/add-strategy.jsx @@ -1,10 +1,17 @@ import React, { PropTypes, Component } from 'react'; -import { Textfield, IconButton, Menu, MenuItem, Checkbox, Grid, Cell } from 'react-mdl'; +import { + Textfield, + IconButton, + Menu, + MenuItem, + Checkbox, + Grid, + Cell, +} from 'react-mdl'; import { FormButtons } from '../common'; - -const trim = (value) => { +const trim = value => { if (value && value.trim) { return value.trim(); } else { @@ -12,36 +19,57 @@ const trim = (value) => { } }; -function gerArrayWithEntries (num) { +function gerArrayWithEntries(num) { return Array.from(Array(num)); } const Parameter = ({ set, input = {}, index }) => ( -
+
set({ name: target.value }, true)} - value={input.name} /> + value={input.name} + />
- + {input.type || 'string'} - evt.preventDefault()} /> + evt.preventDefault()} + /> - set({ type: 'string' })}>string - set({ type: 'percentage' })}>percentage + set({ type: 'string' })}> + string + + set({ type: 'percentage' })}> + percentage + set({ type: 'list' })}>list - set({ type: 'number' })}>number + set({ type: 'number' })}> + number +
(

Edit strategy

- Be carefull! Changing a strategy definition might also require changes to the - implementation in the clients. + Be carefull! Changing a strategy definition might also require + changes to the implementation in the clients.

); @@ -78,17 +106,18 @@ const CreateHeader = () => (
); - const Parameters = ({ input = [], count = 0, updateInList }) => ( -
{ - gerArrayWithEntries(count) - .map((v, i) => ( + {gerArrayWithEntries(count).map((v, i) => ( + updateInList('parameters', i, v, true)} + set={v => updateInList('parameters', i, v, true)} index={i} input={input[i]} - />)) - }
); + /> + ))} +
+); class AddStrategy extends Component { static propTypes = { @@ -102,20 +131,22 @@ class AddStrategy extends Component { editmode: PropTypes.bool, initCallRequired: PropTypes.bool, init: PropTypes.func, - } + }; - componentWillMount () { + componentWillMount() { // TODO unwind this stuff if (this.props.initCallRequired === true) { this.props.init(this.props.input); if (this.props.input.parameters) { - this.props.setValue('_params', this.props.input.parameters.length); + this.props.setValue( + '_params', + this.props.input.parameters.length + ); } } } - - render () { + render() { const { input, setValue, @@ -131,13 +162,15 @@ class AddStrategy extends Component {
{editmode ? : } - setValue('name', trim(target.value))} + onChange={({ target }) => + setValue('name', trim(target.value))} value={input.name} />
@@ -147,21 +180,27 @@ class AddStrategy extends Component { rows={1} label="Description" name="description" - onChange={({ target }) => setValue('description', target.value)} + onChange={({ target }) => + setValue('description', target.value)} value={input.description} /> - - - - { - e.preventDefault(); - incValue('_params'); - }}/>  Add parameter - - + + { + e.preventDefault(); + incValue('_params'); + }} + />{' '} +  Add parameter

- ownProps.strategy, - prepare: (props) => { + prepare: props => { props.editmode = true; return props; }, }); const prepare = (methods, dispatch) => { - methods.onSubmit = (input) => ( - (e) => { - e.preventDefault(); - // clean - const parameters = (input.parameters || []) - .filter((name) => !!name) - .map(({ + methods.onSubmit = input => e => { + e.preventDefault(); + // clean + const parameters = (input.parameters || []) + .filter(name => !!name) + .map( + ({ name, type = 'string', description = '', @@ -39,26 +39,25 @@ const prepare = (methods, dispatch) => { type, description, required, - })); + }) + ); - updateStrategy({ - name: input.name, - description: input.description, - parameters, - })(dispatch) - .then(() => methods.clear()) - .then(() => hashHistory.push(`/strategies/view/${input.name}`)); - } - ); + updateStrategy({ + name: input.name, + description: input.description, + parameters, + })(dispatch) + .then(() => methods.clear()) + .then(() => hashHistory.push(`/strategies/view/${input.name}`)); + }; - methods.onCancel = (e) => { + methods.onCancel = e => { e.preventDefault(); methods.clear(); // somewhat quickfix / hacky to go back.. window.history.back(); }; - return methods; }; diff --git a/frontend/src/component/strategies/list-component.jsx b/frontend/src/component/strategies/list-component.jsx index 8cd20d985e..c69e23ac6d 100644 --- a/frontend/src/component/strategies/list-component.jsx +++ b/frontend/src/component/strategies/list-component.jsx @@ -1,45 +1,73 @@ import React, { Component } from 'react'; import { Link } from 'react-router'; -import { List, ListItem, ListItemContent, IconButton, Grid, Cell } from 'react-mdl'; +import { + List, + ListItem, + ListItemContent, + IconButton, + Grid, + Cell, +} from 'react-mdl'; import { HeaderTitle } from '../common'; class StrategiesListComponent extends Component { static contextTypes = { router: React.PropTypes.object, - } + }; - componentDidMount () { + componentDidMount() { this.props.fetchStrategies(); } - render () { + render() { const { strategies, removeStrategy } = this.props; return ( - this.context.router.push('/strategies/create')} - title="Add new strategy" />} /> + onClick={() => + this.context.router.push( + '/strategies/create' + )} + title="Add new strategy" + /> + } + /> - {strategies.length > 0 ? strategies.map((strategy, i) => ( - - - - {strategy.name} - - - { - strategy.editable === false ? - '' : - removeStrategy(strategy)} /> - } - - )) : No entries} + {strategies.length > 0 ? ( + strategies.map((strategy, i) => ( + + + + {strategy.name} + + + {strategy.editable === false ? ( + '' + ) : ( + + removeStrategy(strategy)} + /> + )} + + )) + ) : ( + No entries + )} @@ -47,5 +75,4 @@ class StrategiesListComponent extends Component { } } - export default StrategiesListComponent; diff --git a/frontend/src/component/strategies/list-container.jsx b/frontend/src/component/strategies/list-container.jsx index 85a7db034b..ec5c4c6ac0 100644 --- a/frontend/src/component/strategies/list-container.jsx +++ b/frontend/src/component/strategies/list-container.jsx @@ -2,7 +2,7 @@ import { connect } from 'react-redux'; import StrategiesListComponent from './list-component.jsx'; import { fetchStrategies, removeStrategy } from '../../store/strategy/actions'; -const mapStateToProps = (state) => { +const mapStateToProps = state => { const list = state.strategies.get('list').toArray(); return { @@ -10,15 +10,18 @@ const mapStateToProps = (state) => { }; }; -const mapDispatchToProps = (dispatch) => ({ - removeStrategy: (strategy) => { - if (window.confirm('Are you sure you want to remove this strategy?')) { // eslint-disable-line no-alert +const mapDispatchToProps = dispatch => ({ + removeStrategy: strategy => { + // eslint-disable-next-line no-alert + if (window.confirm('Are you sure you want to remove this strategy?')) { removeStrategy(strategy)(dispatch); } }, fetchStrategies: () => fetchStrategies()(dispatch), }); -const StrategiesListContainer = connect(mapStateToProps, mapDispatchToProps)(StrategiesListComponent); +const StrategiesListContainer = connect(mapStateToProps, mapDispatchToProps)( + StrategiesListComponent +); export default StrategiesListContainer; diff --git a/frontend/src/component/strategies/show-strategy-component.js b/frontend/src/component/strategies/show-strategy-component.js index 9073daea01..0072f28f47 100644 --- a/frontend/src/component/strategies/show-strategy-component.js +++ b/frontend/src/component/strategies/show-strategy-component.js @@ -7,13 +7,20 @@ class ShowStrategyComponent extends PureComponent { toggles: PropTypes.array, applications: PropTypes.array, strategy: PropTypes.object.isRequired, - } + }; - renderParameters (params) { + renderParameters(params) { if (params) { return params.map(({ name, type, description, required }, i) => ( - - + + {name} ({type}) @@ -23,27 +30,18 @@ class ShowStrategyComponent extends PureComponent { } } - render () { - const { - strategy, - applications, - toggles, - } = this.props; + render() { + const { strategy, applications, toggles } = this.props; - const { - parameters = [], - } = strategy; + const { parameters = [] } = strategy; return (
- - +
Parameters

- - {this.renderParameters(parameters)} - + {this.renderParameters(parameters)}
@@ -63,5 +61,4 @@ class ShowStrategyComponent extends PureComponent { } } - export default ShowStrategyComponent; diff --git a/frontend/src/component/strategies/strategy-details-component.jsx b/frontend/src/component/strategies/strategy-details-component.jsx index f4768c6645..8723db0939 100644 --- a/frontend/src/component/strategies/strategy-details-component.jsx +++ b/frontend/src/component/strategies/strategy-details-component.jsx @@ -20,12 +20,12 @@ export default class StrategyDetails extends Component { fetchStrategies: PropTypes.func.isRequired, fetchApplications: PropTypes.func.isRequired, fetchFeatureToggles: PropTypes.func.isRequired, - } + }; - componentDidMount () { + componentDidMount() { if (!this.props.strategy) { this.props.fetchStrategies(); - }; + } if (!this.props.applications || this.props.applications.length === 0) { this.props.fetchApplications(); } @@ -34,23 +34,28 @@ export default class StrategyDetails extends Component { } } - getTabContent (activeTabId) { + getTabContent(activeTabId) { if (activeTabId === TABS.edit) { return ; } else { - return (); + return ( + + ); } } - goToTab (tabName) { + goToTab(tabName) { hashHistory.push(`/strategies/${tabName}/${this.props.strategyName}`); } - render () { - const activeTabId = TABS[this.props.activeTab] ? TABS[this.props.activeTab] : TABS.view; + render() { + const activeTabId = TABS[this.props.activeTab] + ? TABS[this.props.activeTab] + : TABS.view; const strategy = this.props.strategy; if (!strategy) { return ; @@ -58,24 +63,29 @@ export default class StrategyDetails extends Component { const tabContent = this.getTabContent(activeTabId); - return ( - - - {strategy.editable === false ? '' : - this.goToTab('view')}> - Details - - this.goToTab('edit')}> - Edit - - } + return ( + + + + {strategy.editable === false ? ( + '' + ) : ( + + this.goToTab('view')}> + Details + + this.goToTab('edit')}>Edit + + )} -
-
- {tabContent} -
-
-
-
); +
+
{tabContent}
+
+
+
+ ); } } diff --git a/frontend/src/component/strategies/strategy-details-container.js b/frontend/src/component/strategies/strategy-details-container.js index 66e82d91d5..08a404c6de 100644 --- a/frontend/src/component/strategies/strategy-details-container.js +++ b/frontend/src/component/strategies/strategy-details-container.js @@ -11,9 +11,12 @@ const mapStateToProps = (state, props) => { const applications = state.applications .get('list') .filter(app => app.strategies.includes(props.strategyName)); - const toggles = state.features - .filter(toggle => - toggle.get('strategies').findIndex(s => s.name === props.strategyName) > -1); + const toggles = state.features.filter( + toggle => + toggle + .get('strategies') + .findIndex(s => s.name === props.strategyName) > -1 + ); return { strategy, diff --git a/frontend/src/component/user/show-user-component.jsx b/frontend/src/component/user/show-user-component.jsx index d69a6e6b09..4c3c3528ea 100644 --- a/frontend/src/component/user/show-user-component.jsx +++ b/frontend/src/component/user/show-user-component.jsx @@ -7,16 +7,20 @@ export default class ShowUserComponent extends React.Component { openEdit: PropTypes.func.isRequired, }; - openEdit = (evt) => { + openEdit = evt => { evt.preventDefault(); this.props.openEdit(); - } + }; - render () { + render() { return ( - + - + ); diff --git a/frontend/src/component/user/show-user-container.jsx b/frontend/src/component/user/show-user-container.jsx index 125773eafa..dfbf283601 100644 --- a/frontend/src/component/user/show-user-container.jsx +++ b/frontend/src/component/user/show-user-container.jsx @@ -2,12 +2,11 @@ import { connect } from 'react-redux'; import ShowUserComponent from './show-user-component'; import { openEdit } from '../../store/user/actions'; - const mapDispatchToProps = { openEdit, }; -const mapStateToProps = (state) => ({ +const mapStateToProps = state => ({ user: state.user.toJS(), }); diff --git a/frontend/src/component/user/user-component.jsx b/frontend/src/component/user/user-component.jsx index 4b5b073315..80f1fb6031 100644 --- a/frontend/src/component/user/user-component.jsx +++ b/frontend/src/component/user/user-component.jsx @@ -28,24 +28,26 @@ class EditUserComponent extends React.Component { user: PropTypes.object.isRequired, updateUserName: PropTypes.func.isRequired, save: PropTypes.func.isRequired, - } + }; - handleSubmit = (evt) => { + handleSubmit = evt => { evt.preventDefault(); this.props.save(); - } + }; - render () { + render() { return (
+ style={customStyles} + >

Action required

- You have to specify a username to use Unleash. This will allow us to track your changes. + You have to specify a username to use Unleash. This + will allow us to track your changes.

this.props.updateUserName(e.target.value)} + onChange={e => + this.props.updateUserName(e.target.value)} />
- +
diff --git a/frontend/src/component/user/user-container.jsx b/frontend/src/component/user/user-container.jsx index 8af4cb632d..ead0259620 100644 --- a/frontend/src/component/user/user-container.jsx +++ b/frontend/src/component/user/user-container.jsx @@ -2,13 +2,12 @@ import { connect } from 'react-redux'; import UserComponent from './user-component'; import { updateUserName, save } from '../../store/user/actions'; - const mapDispatchToProps = { updateUserName, save, }; -const mapStateToProps = (state) => ({ +const mapStateToProps = state => ({ user: state.user.toJS(), }); diff --git a/frontend/src/data/applications-api.js b/frontend/src/data/applications-api.js index d64cba482a..919e3df790 100644 --- a/frontend/src/data/applications-api.js +++ b/frontend/src/data/applications-api.js @@ -2,19 +2,19 @@ import { throwIfNotSuccess, headers } from './helper'; const URI = 'api/admin/metrics/applications'; -function fetchAll () { +function fetchAll() { return fetch(URI, { headers, credentials: 'include' }) .then(throwIfNotSuccess) .then(response => response.json()); } -function fetchApplication (appName) { +function fetchApplication(appName) { return fetch(`${URI}/${appName}`, { headers, credentials: 'include' }) .then(throwIfNotSuccess) .then(response => response.json()); } -function fetchApplicationsWithStrategyName (strategyName) { +function fetchApplicationsWithStrategyName(strategyName) { return fetch(`${URI}?strategyName=${strategyName}`, { headers, credentials: 'include', @@ -23,7 +23,7 @@ function fetchApplicationsWithStrategyName (strategyName) { .then(response => response.json()); } -function storeApplicationMetaData (appName, key, value) { +function storeApplicationMetaData(appName, key, value) { const data = {}; data[key] = value; return fetch(`${URI}/${appName}`, { diff --git a/frontend/src/data/archive-api.js b/frontend/src/data/archive-api.js index a32f8a988b..bcfdd28557 100644 --- a/frontend/src/data/archive-api.js +++ b/frontend/src/data/archive-api.js @@ -2,13 +2,13 @@ import { throwIfNotSuccess, headers } from './helper'; const URI = 'api/admin/archive'; -function fetchAll () { +function fetchAll() { return fetch(`${URI}/features`, { credentials: 'include' }) .then(throwIfNotSuccess) .then(response => response.json()); } -function revive (featureName) { +function revive(featureName) { return fetch(`${URI}/revive/${featureName}`, { method: 'POST', headers, @@ -16,9 +16,7 @@ function revive (featureName) { }).then(throwIfNotSuccess); } - export default { fetchAll, revive, }; - diff --git a/frontend/src/data/client-instance-api.js b/frontend/src/data/client-instance-api.js index 90a69e9917..9cd45fea1d 100644 --- a/frontend/src/data/client-instance-api.js +++ b/frontend/src/data/client-instance-api.js @@ -2,7 +2,7 @@ import { throwIfNotSuccess, headers } from './helper'; const URI = 'api/admin/metrics/instances'; -function fetchAll () { +function fetchAll() { return fetch(URI, { headers, credentials: 'include' }) .then(throwIfNotSuccess) .then(response => response.json()); diff --git a/frontend/src/data/feature-api.js b/frontend/src/data/feature-api.js index fdfe078b38..9a72815727 100644 --- a/frontend/src/data/feature-api.js +++ b/frontend/src/data/feature-api.js @@ -2,9 +2,12 @@ import { throwIfNotSuccess, headers } from './helper'; const URI = 'api/admin/features'; -function validateToggle (featureToggle) { +function validateToggle(featureToggle) { return new Promise((resolve, reject) => { - if (!featureToggle.strategies || featureToggle.strategies.length === 0) { + if ( + !featureToggle.strategies || + featureToggle.strategies.length === 0 + ) { reject(new Error('You must add at least one activation strategy')); } else { resolve(featureToggle); @@ -12,24 +15,26 @@ function validateToggle (featureToggle) { }); } -function fetchAll () { +function fetchAll() { return fetch(URI, { credentials: 'include' }) .then(throwIfNotSuccess) .then(response => response.json()); } -function create (featureToggle) { +function create(featureToggle) { return validateToggle(featureToggle) - .then(() => fetch(URI, { - method: 'POST', - headers, - credentials: 'include', - body: JSON.stringify(featureToggle), - })) + .then(() => + fetch(URI, { + method: 'POST', + headers, + credentials: 'include', + body: JSON.stringify(featureToggle), + }) + ) .then(throwIfNotSuccess); } -function validate (featureToggle) { +function validate(featureToggle) { return fetch(`${URI}/validate`, { method: 'POST', headers, @@ -38,27 +43,28 @@ function validate (featureToggle) { }).then(throwIfNotSuccess); } -function update (featureToggle) { +function update(featureToggle) { return validateToggle(featureToggle) - .then(() => fetch(`${URI}/${featureToggle.name}`, { - method: 'PUT', - headers, - credentials: 'include', - body: JSON.stringify(featureToggle), - })) + .then(() => + fetch(`${URI}/${featureToggle.name}`, { + method: 'PUT', + headers, + credentials: 'include', + body: JSON.stringify(featureToggle), + }) + ) .then(throwIfNotSuccess); } -function toggle (name) { +function toggle(name) { return fetch(`${URI}/${name}/toggle`, { method: 'POST', headers, credentials: 'include', - }) - .then(throwIfNotSuccess); + }).then(throwIfNotSuccess); } -function remove (featureToggleName) { +function remove(featureToggleName) { return fetch(`${URI}/${featureToggleName}`, { method: 'DELETE', credentials: 'include', diff --git a/frontend/src/data/feature-metrics-api.js b/frontend/src/data/feature-metrics-api.js index ddba9b053e..2287e1b2b0 100644 --- a/frontend/src/data/feature-metrics-api.js +++ b/frontend/src/data/feature-metrics-api.js @@ -2,7 +2,7 @@ const { throwIfNotSuccess } = require('./helper'); const URI = 'api/admin/metrics/feature-toggles'; -function fetchFeatureMetrics () { +function fetchFeatureMetrics() { return fetch(URI, { credentials: 'include' }) .then(throwIfNotSuccess) .then(response => response.json()); @@ -10,7 +10,7 @@ function fetchFeatureMetrics () { const seenURI = 'api/admin/metrics/seen-apps'; -function fetchSeenApps () { +function fetchSeenApps() { return fetch(seenURI, { credentials: 'include' }) .then(throwIfNotSuccess) .then(response => response.json()); diff --git a/frontend/src/data/helper.js b/frontend/src/data/helper.js index 0775a699c6..cf44aae22e 100644 --- a/frontend/src/data/helper.js +++ b/frontend/src/data/helper.js @@ -1,18 +1,23 @@ const defaultErrorMessage = 'Unexptected exception when talking to unleash-api'; -function extractJoiMsg (body) { - return body.details.length > 0 ? body.details[0].message : defaultErrorMessage; +function extractJoiMsg(body) { + return body.details.length > 0 + ? body.details[0].message + : defaultErrorMessage; } -function extractLegacyMsg (body) { +function extractLegacyMsg(body) { return body && body.length > 0 ? body[0].msg : defaultErrorMessage; } -export function throwIfNotSuccess (response) { +export function throwIfNotSuccess(response) { if (!response.ok) { if (response.status > 399 && response.status < 404) { return new Promise((resolve, reject) => { response.json().then(body => { - const errorMsg = body && body.isJoi ? extractJoiMsg(body) : extractLegacyMsg(body); + const errorMsg = + body && body.isJoi + ? extractJoiMsg(body) + : extractLegacyMsg(body); let error = new Error(errorMsg); error.statusCode = response.status; reject(error); @@ -23,10 +28,9 @@ export function throwIfNotSuccess (response) { } } return Promise.resolve(response); -}; - +} export const headers = { - 'Accept': 'application/json', + Accept: 'application/json', 'Content-Type': 'application/json', }; diff --git a/frontend/src/data/history-api.js b/frontend/src/data/history-api.js index e76a6284c6..7a72bf09de 100644 --- a/frontend/src/data/history-api.js +++ b/frontend/src/data/history-api.js @@ -2,13 +2,13 @@ import { throwIfNotSuccess } from './helper'; const URI = 'api/admin/events'; -function fetchAll () { +function fetchAll() { return fetch(URI, { credentials: 'include' }) .then(throwIfNotSuccess) .then(response => response.json()); } -function fetchHistoryForToggle (toggleName) { +function fetchHistoryForToggle(toggleName) { return fetch(`${URI}/${toggleName}`, { credentials: 'include' }) .then(throwIfNotSuccess) .then(response => response.json()); diff --git a/frontend/src/data/strategy-api.js b/frontend/src/data/strategy-api.js index eb6c0fae0f..16647ede0d 100644 --- a/frontend/src/data/strategy-api.js +++ b/frontend/src/data/strategy-api.js @@ -2,13 +2,13 @@ import { throwIfNotSuccess, headers } from './helper'; const URI = 'api/admin/strategies'; -function fetchAll () { +function fetchAll() { return fetch(URI, { credentials: 'include' }) .then(throwIfNotSuccess) .then(response => response.json()); } -function create (strategy) { +function create(strategy) { return fetch(URI, { method: 'POST', headers, @@ -17,7 +17,7 @@ function create (strategy) { }).then(throwIfNotSuccess); } -function update (strategy) { +function update(strategy) { return fetch(`${URI}/${strategy.name}`, { method: 'put', headers, @@ -26,7 +26,7 @@ function update (strategy) { }).then(throwIfNotSuccess); } -function remove (strategy) { +function remove(strategy) { return fetch(`${URI}/${strategy.name}`, { method: 'DELETE', headers, diff --git a/frontend/src/index.jsx b/frontend/src/index.jsx index 88aa0bf5ca..3f37ee31bc 100644 --- a/frontend/src/index.jsx +++ b/frontend/src/index.jsx @@ -4,7 +4,13 @@ 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 { + applyRouterMiddleware, + Router, + Route, + IndexRedirect, + hashHistory, +} from 'react-router'; import { useScroll } from 'react-router-scroll'; import { Provider } from 'react-redux'; import thunkMiddleware from 'redux-thunk'; @@ -27,7 +33,10 @@ import ApplicationView from './page/applications/view'; let composeEnhancers; -if (process.env.NODE_ENV !== 'production' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) { +if ( + process.env.NODE_ENV !== 'production' && + window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ +) { composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__; } else { composeEnhancers = compose; @@ -35,42 +44,88 @@ if (process.env.NODE_ENV !== 'production' && window.__REDUX_DEVTOOLS_EXTENSION_C const unleashStore = createStore( store, - composeEnhancers( - applyMiddleware(thunkMiddleware) - ) + composeEnhancers(applyMiddleware(thunkMiddleware)) ); // "pageTitle" and "link" attributes are for internal usage only ReactDOM.render( - + - - - + + + - - - + + + - - + + - + - - + + - - , document.getElementById('app')); + , + document.getElementById('app') +); diff --git a/frontend/src/page/applications/view.js b/frontend/src/page/applications/view.js index 2ecfa22355..e8a6640f54 100644 --- a/frontend/src/page/applications/view.js +++ b/frontend/src/page/applications/view.js @@ -1,7 +1,9 @@ import React, { PropTypes } from 'react'; import ApplicationEditComponent from '../../component/application/application-edit-container'; -const render = ({ params }) => ; +const render = ({ params }) => ( + +); render.propTypes = { params: PropTypes.object.isRequired, diff --git a/frontend/src/page/features/create.js b/frontend/src/page/features/create.js index f621b98def..cab5811e62 100644 --- a/frontend/src/page/features/create.js +++ b/frontend/src/page/features/create.js @@ -1,7 +1,6 @@ import React from 'react'; import AddFeatureToggleForm from '../../component/feature/form-add-container'; - -const render = () => (); +const render = () => ; export default render; diff --git a/frontend/src/page/features/index.js b/frontend/src/page/features/index.js index b632addcd0..764327ced3 100644 --- a/frontend/src/page/features/index.js +++ b/frontend/src/page/features/index.js @@ -1,8 +1,6 @@ import React from 'react'; import FeatureListContainer from '../../component/feature/list-container'; -const render = () => (); +const render = () => ; export default render; - - diff --git a/frontend/src/page/features/show.js b/frontend/src/page/features/show.js index 3678b89567..b403541166 100644 --- a/frontend/src/page/features/show.js +++ b/frontend/src/page/features/show.js @@ -4,12 +4,15 @@ import ViewFeatureToggle from '../../component/feature/view-container'; export default class Features extends PureComponent { static propTypes = { params: PropTypes.object.isRequired, - } + }; - render () { + render() { const { params } = this.props; return ( - + ); } -}; +} diff --git a/frontend/src/page/history/toggle.js b/frontend/src/page/history/toggle.js index 95b4da6ac0..515485b373 100644 --- a/frontend/src/page/history/toggle.js +++ b/frontend/src/page/history/toggle.js @@ -1,7 +1,9 @@ import React, { PropTypes } from 'react'; import HistoryListToggle from '../../component/history/history-list-toggle-container'; -const render = ({ params }) => ; +const render = ({ params }) => ( + +); render.propTypes = { params: PropTypes.object.isRequired, diff --git a/frontend/src/page/strategies/create.js b/frontend/src/page/strategies/create.js index bd94fcf7d1..7fc1f4727e 100644 --- a/frontend/src/page/strategies/create.js +++ b/frontend/src/page/strategies/create.js @@ -1,4 +1,4 @@ import React from 'react'; import AddStrategies from '../../component/strategies/add-container'; -export default () => (); +export default () => ; diff --git a/frontend/src/page/strategies/index.js b/frontend/src/page/strategies/index.js index 8f41038bd0..6485b3c760 100644 --- a/frontend/src/page/strategies/index.js +++ b/frontend/src/page/strategies/index.js @@ -1,4 +1,4 @@ import React from 'react'; import Strategies from '../../component/strategies/list-container'; -export default () => (); +export default () => ; diff --git a/frontend/src/page/strategies/show.js b/frontend/src/page/strategies/show.js index 0dc4fdbc76..0e438de7b5 100644 --- a/frontend/src/page/strategies/show.js +++ b/frontend/src/page/strategies/show.js @@ -1,7 +1,12 @@ import React, { PropTypes } from 'react'; import ShowStrategy from '../../component/strategies/strategy-details-container'; -const render = ({ params }) => ; +const render = ({ params }) => ( + +); render.propTypes = { params: PropTypes.object.isRequired, diff --git a/frontend/src/store/application/actions.js b/frontend/src/store/application/actions.js index 3f295bd4c3..07306a3c4c 100644 --- a/frontend/src/store/application/actions.js +++ b/frontend/src/store/application/actions.js @@ -2,39 +2,55 @@ import api from '../../data/applications-api'; export const RECEIVE_ALL_APPLICATIONS = 'RECEIVE_ALL_APPLICATIONS'; export const ERROR_RECEIVE_ALL_APPLICATIONS = 'ERROR_RECEIVE_ALL_APPLICATIONS'; -export const ERROR_UPDATING_APPLICATION_DATA = 'ERROR_UPDATING_APPLICATION_DATA'; +export const ERROR_UPDATING_APPLICATION_DATA = + 'ERROR_UPDATING_APPLICATION_DATA'; export const RECEIVE_APPLICATION = 'RECEIVE_APPLICATION'; -const recieveAllApplications = (json) => ({ +const recieveAllApplications = json => ({ type: RECEIVE_ALL_APPLICATIONS, value: json, }); -const recieveApplication = (json) => ({ +const recieveApplication = json => ({ type: RECEIVE_APPLICATION, value: json, }); -const errorReceiveApplications = (statusCode, type = ERROR_RECEIVE_ALL_APPLICATIONS) => ({ +const errorReceiveApplications = ( + statusCode, + type = ERROR_RECEIVE_ALL_APPLICATIONS +) => ({ type, statusCode, }); -export function fetchAll () { - return dispatch => api.fetchAll() - .then(json => dispatch(recieveAllApplications(json))) - .catch(error => dispatch(errorReceiveApplications(error))); +export function fetchAll() { + return dispatch => + api + .fetchAll() + .then(json => dispatch(recieveAllApplications(json))) + .catch(error => dispatch(errorReceiveApplications(error))); } -export function storeApplicationMetaData (appName, key, value) { - return dispatch => api.storeApplicationMetaData(appName, key, value) - .catch(error => dispatch(errorReceiveApplications(error, ERROR_UPDATING_APPLICATION_DATA))); +export function storeApplicationMetaData(appName, key, value) { + return dispatch => + api + .storeApplicationMetaData(appName, key, value) + .catch(error => + dispatch( + errorReceiveApplications( + error, + ERROR_UPDATING_APPLICATION_DATA + ) + ) + ); } -export function fetchApplication (appName) { - return dispatch => api.fetchApplication(appName) - .then(json => dispatch(recieveApplication(json))) - .catch(error => dispatch(errorReceiveApplications(error))); +export function fetchApplication(appName) { + return dispatch => + api + .fetchApplication(appName) + .then(json => dispatch(recieveApplication(json))) + .catch(error => dispatch(errorReceiveApplications(error))); } - diff --git a/frontend/src/store/application/index.js b/frontend/src/store/application/index.js index b2daa39146..7ca0b5458f 100644 --- a/frontend/src/store/application/index.js +++ b/frontend/src/store/application/index.js @@ -1,14 +1,17 @@ import { fromJS, List, Map } from 'immutable'; import { RECEIVE_ALL_APPLICATIONS, RECEIVE_APPLICATION } from './actions'; -function getInitState () { +function getInitState() { return fromJS({ list: [], apps: {} }); } const store = (state = getInitState(), action) => { switch (action.type) { case RECEIVE_APPLICATION: - return state.setIn(['apps', action.value.appName], new Map(action.value)); + return state.setIn( + ['apps', action.value.appName], + new Map(action.value) + ); case RECEIVE_ALL_APPLICATIONS: return state.set('list', new List(action.value.applications)); default: diff --git a/frontend/src/store/archive-actions.js b/frontend/src/store/archive-actions.js index ff8795b822..b787c04e49 100644 --- a/frontend/src/store/archive-actions.js +++ b/frontend/src/store/archive-actions.js @@ -4,30 +4,33 @@ export const REVIVE_TOGGLE = 'REVIVE_TOGGLE'; export const RECEIVE_ARCHIVE = 'RECEIVE_ARCHIVE'; export const ERROR_RECEIVE_ARCHIVE = 'ERROR_RECEIVE_ARCHIVE'; -const receiveArchive = (json) => ({ +const receiveArchive = json => ({ type: RECEIVE_ARCHIVE, value: json.features, }); -const reviveToggle = (archiveFeatureToggle) => ({ +const reviveToggle = archiveFeatureToggle => ({ type: REVIVE_TOGGLE, value: archiveFeatureToggle, }); -const errorReceiveArchive = (statusCode) => ({ +const errorReceiveArchive = statusCode => ({ type: ERROR_RECEIVE_ARCHIVE, statusCode, }); -export function revive (featureToggle) { - return dispatch => api.revive(featureToggle) - .then(() => dispatch(reviveToggle(featureToggle))) - .catch(error => dispatch(errorReceiveArchive(error))); +export function revive(featureToggle) { + return dispatch => + api + .revive(featureToggle) + .then(() => dispatch(reviveToggle(featureToggle))) + .catch(error => dispatch(errorReceiveArchive(error))); } - -export function fetchArchive () { - return dispatch => api.fetchAll() - .then(json => dispatch(receiveArchive(json))) - .catch(error => dispatch(errorReceiveArchive(error))); +export function fetchArchive() { + return dispatch => + api + .fetchAll() + .then(json => dispatch(receiveArchive(json))) + .catch(error => dispatch(errorReceiveArchive(error))); } diff --git a/frontend/src/store/archive-store.js b/frontend/src/store/archive-store.js index 674e2733bb..e8caec466a 100644 --- a/frontend/src/store/archive-store.js +++ b/frontend/src/store/archive-store.js @@ -1,14 +1,16 @@ import { List, Map as $Map } from 'immutable'; import { RECEIVE_ARCHIVE, REVIVE_TOGGLE } from './archive-actions'; -function getInitState () { +function getInitState() { return new $Map({ list: new List() }); } const archiveStore = (state = getInitState(), action) => { switch (action.type) { case REVIVE_TOGGLE: - return state.update('list', (list) => list.filter(item => item.name !== action.value)); + return state.update('list', list => + list.filter(item => item.name !== action.value) + ); case RECEIVE_ARCHIVE: return state.set('list', new List(action.value)); default: diff --git a/frontend/src/store/client-instance-actions.js b/frontend/src/store/client-instance-actions.js index 03de9edcf5..de39b88903 100644 --- a/frontend/src/store/client-instance-actions.js +++ b/frontend/src/store/client-instance-actions.js @@ -3,18 +3,20 @@ import api from '../data/client-instance-api'; export const RECEIVE_CLIENT_INSTANCES = 'RECEIVE_CLIENT_INSTANCES'; export const ERROR_RECEIVE_CLIENT_INSTANCES = 'ERROR_RECEIVE_CLIENT_INSTANCES'; -const receiveClientInstances = (json) => ({ +const receiveClientInstances = json => ({ type: RECEIVE_CLIENT_INSTANCES, value: json, }); -const errorReceiveClientInstances = (statusCode) => ({ +const errorReceiveClientInstances = statusCode => ({ type: RECEIVE_CLIENT_INSTANCES, statusCode, }); -export function fetchClientInstances () { - return dispatch => api.fetchAll() - .then(json => dispatch(receiveClientInstances(json))) - .catch(error => dispatch(errorReceiveClientInstances(error))); +export function fetchClientInstances() { + return dispatch => + api + .fetchAll() + .then(json => dispatch(receiveClientInstances(json))) + .catch(error => dispatch(errorReceiveClientInstances(error))); } diff --git a/frontend/src/store/client-instance-store.js b/frontend/src/store/client-instance-store.js index d075fbf841..f7deda4ae6 100644 --- a/frontend/src/store/client-instance-store.js +++ b/frontend/src/store/client-instance-store.js @@ -1,7 +1,7 @@ import { fromJS } from 'immutable'; import { RECEIVE_CLIENT_INSTANCES } from './client-instance-actions'; -function getInitState () { +function getInitState() { return fromJS([]); } diff --git a/frontend/src/store/error-actions.js b/frontend/src/store/error-actions.js index 5b8a3682df..3d378e690e 100644 --- a/frontend/src/store/error-actions.js +++ b/frontend/src/store/error-actions.js @@ -3,6 +3,4 @@ export const MUTE_ERROR = 'MUTE_ERROR'; export const muteErrors = () => ({ type: MUTE_ERRORS }); -export const muteError = (error) => ({ type: MUTE_ERROR, error }); - - +export const muteError = error => ({ type: MUTE_ERROR, error }); diff --git a/frontend/src/store/error-store.js b/frontend/src/store/error-store.js index fd9422ae00..1645795d82 100644 --- a/frontend/src/store/error-store.js +++ b/frontend/src/store/error-store.js @@ -11,21 +11,20 @@ import { ERROR_UPDATING_STRATEGY, ERROR_CREATING_STRATEGY, ERROR_RECEIVE_STRATEGIES, - } from './strategy/actions'; const debug = require('debug')('unleash:error-store'); -function getInitState () { +function getInitState() { return new $Map({ list: new List(), }); } -function addErrorIfNotAlreadyInList (state, error) { +function addErrorIfNotAlreadyInList(state, error) { debug('Got error', error); if (state.get('list').indexOf(error) < 0) { - return state.update('list', (list) => list.push(error)); + return state.update('list', list => list.push(error)); } return state; } @@ -41,7 +40,9 @@ const strategies = (state = getInitState(), action) => { case ERROR_RECEIVE_STRATEGIES: return addErrorIfNotAlreadyInList(state, action.error.message); case MUTE_ERROR: - return state.update('list', (list) => list.remove(list.indexOf(action.error))); + return state.update('list', list => + list.remove(list.indexOf(action.error)) + ); default: return state; } diff --git a/frontend/src/store/feature-actions.js b/frontend/src/store/feature-actions.js index 09d79e08c5..86f6421e0f 100644 --- a/frontend/src/store/feature-actions.js +++ b/frontend/src/store/feature-actions.js @@ -15,22 +15,21 @@ export const ERROR_CREATING_FEATURE_TOGGLE = 'ERROR_CREATING_FEATURE_TOGGLE'; export const ERROR_UPDATE_FEATURE_TOGGLE = 'ERROR_UPDATE_FEATURE_TOGGLE'; export const ERROR_REMOVE_FEATURE_TOGGLE = 'ERROR_REMOVE_FEATURE_TOGGLE'; -export function toggleFeature (name) { +export function toggleFeature(name) { debug('Toggle feature toggle ', name); return dispatch => { dispatch(requestToggleFeatureToggle(name)); }; -}; +} -export function editFeatureToggle (featureToggle) { +export function editFeatureToggle(featureToggle) { debug('Update feature toggle ', featureToggle); return dispatch => { dispatch(requestUpdateFeatureToggle(featureToggle)); }; -}; +} - -function receiveFeatureToggles (json) { +function receiveFeatureToggles(json) { debug('reviced feature toggles', json); return { type: RECEIVE_FEATURE_TOGGLES, @@ -39,64 +38,73 @@ function receiveFeatureToggles (json) { }; } -function dispatchAndThrow (dispatch, type) { - return (error) => { +function dispatchAndThrow(dispatch, type) { + return error => { dispatch({ type, error, receivedAt: Date.now() }); throw error; }; } -export function fetchFeatureToggles () { +export function fetchFeatureToggles() { debug('Start fetching feature toggles'); return dispatch => { dispatch({ type: START_FETCH_FEATURE_TOGGLES }); - return api.fetchAll() + return api + .fetchAll() .then(json => dispatch(receiveFeatureToggles(json))) .catch(dispatchAndThrow(dispatch, ERROR_FETCH_FEATURE_TOGGLES)); }; } -export function createFeatureToggles (featureToggle) { +export function createFeatureToggles(featureToggle) { return dispatch => { dispatch({ type: START_CREATE_FEATURE_TOGGLE }); - return api.create(featureToggle) + return api + .create(featureToggle) .then(() => dispatch({ type: ADD_FEATURE_TOGGLE, featureToggle })) .catch(dispatchAndThrow(dispatch, ERROR_CREATING_FEATURE_TOGGLE)); }; } -export function requestToggleFeatureToggle (name) { +export function requestToggleFeatureToggle(name) { return dispatch => { dispatch({ type: START_UPDATE_FEATURE_TOGGLE }); - return api.toggle(name) + return api + .toggle(name) .then(() => dispatch({ type: TOGGLE_FEATURE_TOGGLE, name })) .catch(dispatchAndThrow(dispatch, ERROR_UPDATE_FEATURE_TOGGLE)); }; } -export function requestUpdateFeatureToggle (featureToggle) { +export function requestUpdateFeatureToggle(featureToggle) { return dispatch => { dispatch({ type: START_UPDATE_FEATURE_TOGGLE }); - return api.update(featureToggle) - .then(() => dispatch({ type: UPDATE_FEATURE_TOGGLE, featureToggle })) + return api + .update(featureToggle) + .then(() => + dispatch({ type: UPDATE_FEATURE_TOGGLE, featureToggle }) + ) .catch(dispatchAndThrow(dispatch, ERROR_UPDATE_FEATURE_TOGGLE)); }; } -export function removeFeatureToggle (featureToggleName) { +export function removeFeatureToggle(featureToggleName) { return dispatch => { dispatch({ type: START_REMOVE_FEATURE_TOGGLE }); - return api.remove(featureToggleName) - .then(() => dispatch({ type: REMOVE_FEATURE_TOGGLE, featureToggleName })) + return api + .remove(featureToggleName) + .then(() => + dispatch({ type: REMOVE_FEATURE_TOGGLE, featureToggleName }) + ) .catch(dispatchAndThrow(dispatch, ERROR_REMOVE_FEATURE_TOGGLE)); }; } -export function validateName (featureToggleName) { +export function validateName(featureToggleName) { return api.validate({ name: featureToggleName }); } diff --git a/frontend/src/store/feature-metrics-actions.js b/frontend/src/store/feature-metrics-actions.js index c9914cd541..2c2fb409e4 100644 --- a/frontend/src/store/feature-metrics-actions.js +++ b/frontend/src/store/feature-metrics-actions.js @@ -8,7 +8,7 @@ export const START_FETCH_SEEN_APP = 'START_FETCH_SEEN_APP'; export const RECEIVE_SEEN_APPS = 'RECEIVE_SEEN_APPS'; export const ERROR_FETCH_SEEN_APP = 'ERROR_FETCH_SEEN_APP'; -function receiveFeatureMetrics (json) { +function receiveFeatureMetrics(json) { return { type: RECEIVE_FEATURE_METRICS, value: json, @@ -16,7 +16,7 @@ function receiveFeatureMetrics (json) { }; } -function receiveSeenApps (json) { +function receiveSeenApps(json) { return { type: RECEIVE_SEEN_APPS, value: json, @@ -24,30 +24,31 @@ function receiveSeenApps (json) { }; } -function dispatchAndThrow (dispatch, type) { - return (error) => { +function dispatchAndThrow(dispatch, type) { + return error => { dispatch({ type, error, receivedAt: Date.now() }); throw error; }; } -export function fetchFeatureMetrics () { +export function fetchFeatureMetrics() { return dispatch => { dispatch({ type: START_FETCH_SEEN_APP }); - return api.fetchFeatureMetrics() + return api + .fetchFeatureMetrics() .then(json => dispatch(receiveFeatureMetrics(json))) .catch(dispatchAndThrow(dispatch, ERROR_FETCH_SEEN_APP)); }; } -export function fetchSeenApps () { +export function fetchSeenApps() { return dispatch => { dispatch({ type: START_FETCH_FEATURE_METRICS }); - return api.fetchSeenApps() + return api + .fetchSeenApps() .then(json => dispatch(receiveSeenApps(json))) .catch(dispatchAndThrow(dispatch, ERROR_FETCH_FEATURE_TOGGLES)); }; } - diff --git a/frontend/src/store/feature-metrics-store.js b/frontend/src/store/feature-metrics-store.js index f73387f3a0..2f8291bcce 100644 --- a/frontend/src/store/feature-metrics-store.js +++ b/frontend/src/store/feature-metrics-store.js @@ -5,13 +5,15 @@ import { RECEIVE_SEEN_APPS, } from './feature-metrics-actions'; - -const metrics = (state = fromJS({ lastHour: {}, lastMinute: {}, seenApps: {} }), action) => { +const metrics = ( + state = fromJS({ lastHour: {}, lastMinute: {}, seenApps: {} }), + action +) => { switch (action.type) { case RECEIVE_SEEN_APPS: return state.set('seenApps', new $Map(action.value)); case RECEIVE_FEATURE_METRICS: - return state.withMutations((ctx) => { + return state.withMutations(ctx => { ctx.set('lastHour', new $Map(action.value.lastHour)); ctx.set('lastMinute', new $Map(action.value.lastMinute)); return ctx; diff --git a/frontend/src/store/feature-store.js b/frontend/src/store/feature-store.js index e3a4f4850e..cc10834429 100644 --- a/frontend/src/store/feature-store.js +++ b/frontend/src/store/feature-store.js @@ -1,7 +1,6 @@ import { List, Map as $Map } from 'immutable'; const debug = require('debug')('unleash:feature-store'); - import { ADD_FEATURE_TOGGLE, RECEIVE_FEATURE_TOGGLES, @@ -10,7 +9,6 @@ import { TOGGLE_FEATURE_TOGGLE, } from './feature-actions'; - const features = (state = new List([]), action) => { switch (action.type) { case ADD_FEATURE_TOGGLE: @@ -18,7 +16,9 @@ const features = (state = new List([]), action) => { return state.push(new $Map(action.featureToggle)); case REMOVE_FEATURE_TOGGLE: debug(REMOVE_FEATURE_TOGGLE, action); - return state.filter(toggle => toggle.get('name') !== action.featureToggleName); + return state.filter( + toggle => toggle.get('name') !== action.featureToggleName + ); case TOGGLE_FEATURE_TOGGLE: debug(TOGGLE_FEATURE_TOGGLE, action); return state.map(toggle => { diff --git a/frontend/src/store/history-actions.js b/frontend/src/store/history-actions.js index 51f2b3daef..3367123358 100644 --- a/frontend/src/store/history-actions.js +++ b/frontend/src/store/history-actions.js @@ -5,30 +5,33 @@ export const ERROR_RECEIVE_HISTORY = 'ERROR_RECEIVE_HISTORY'; export const RECEIVE_HISTORY_FOR_TOGGLE = 'RECEIVE_HISTORY_FOR_TOGGLE'; -const receiveHistory = (json) => ({ +const receiveHistory = json => ({ type: RECEIVE_HISTORY, value: json.events, }); -const receiveHistoryforToggle = (json) => ({ +const receiveHistoryforToggle = json => ({ type: RECEIVE_HISTORY_FOR_TOGGLE, value: json, }); -const errorReceiveHistory = (statusCode) => ({ +const errorReceiveHistory = statusCode => ({ type: ERROR_RECEIVE_HISTORY, statusCode, }); -export function fetchHistory () { - return dispatch => api.fetchAll() - .then(json => dispatch(receiveHistory(json))) - .catch(error => dispatch(errorReceiveHistory(error))); +export function fetchHistory() { + return dispatch => + api + .fetchAll() + .then(json => dispatch(receiveHistory(json))) + .catch(error => dispatch(errorReceiveHistory(error))); } - -export function fetchHistoryForToggle (toggleName) { - return dispatch => api.fetchHistoryForToggle(toggleName) - .then(json => dispatch(receiveHistoryforToggle(json))) - .catch(error => dispatch(errorReceiveHistory(error))); +export function fetchHistoryForToggle(toggleName) { + return dispatch => + api + .fetchHistoryForToggle(toggleName) + .then(json => dispatch(receiveHistoryforToggle(json))) + .catch(error => dispatch(errorReceiveHistory(error))); } diff --git a/frontend/src/store/history-store.js b/frontend/src/store/history-store.js index 7ec098235a..0e6a435094 100644 --- a/frontend/src/store/history-store.js +++ b/frontend/src/store/history-store.js @@ -1,14 +1,17 @@ import { List, Map as $Map } from 'immutable'; import { RECEIVE_HISTORY, RECEIVE_HISTORY_FOR_TOGGLE } from './history-actions'; -function getInitState () { +function getInitState() { return new $Map({ list: new List(), toggles: new $Map() }); } const historyStore = (state = getInitState(), action) => { switch (action.type) { case RECEIVE_HISTORY_FOR_TOGGLE: - return state.setIn(['toggles', action.value.toggleName], new List(action.value.events)); + return state.setIn( + ['toggles', action.value.toggleName], + new List(action.value.events) + ); case RECEIVE_HISTORY: return state.set('list', new List(action.value)); default: diff --git a/frontend/src/store/input-actions.js b/frontend/src/store/input-actions.js index 39dbd3d707..c59354d3c5 100644 --- a/frontend/src/store/input-actions.js +++ b/frontend/src/store/input-actions.js @@ -9,13 +9,49 @@ export const actions = { MOVE: 'MOVE', }; -export const createInit = ({ id, value }) => ({ type: actions.INIT, id, value }); -export const createInc = ({ id, key }) => ({ type: actions.INCREMENT_VALUE, id, key }); -export const createSet = ({ id, key, value }) => ({ type: actions.SET_VALUE, id, key, value }); -export const createPush = ({ id, key, value }) => ({ type: actions.LIST_PUSH, id, key, value }); -export const createPop = ({ id, key, index }) => ({ type: actions.LIST_POP, id, key, index }); -export const createMove = ({ id, key, index, toIndex }) => ({ type: actions.MOVE, id, key, index, toIndex }); -export const createUp = ({ id, key, index, newValue, merge }) => ({ type: actions.LIST_UP, id, key, index, newValue, merge }); +export const createInit = ({ id, value }) => ({ + type: actions.INIT, + id, + value, +}); +export const createInc = ({ id, key }) => ({ + type: actions.INCREMENT_VALUE, + id, + key, +}); +export const createSet = ({ id, key, value }) => ({ + type: actions.SET_VALUE, + id, + key, + value, +}); +export const createPush = ({ id, key, value }) => ({ + type: actions.LIST_PUSH, + id, + key, + value, +}); +export const createPop = ({ id, key, index }) => ({ + type: actions.LIST_POP, + id, + key, + index, +}); +export const createMove = ({ id, key, index, toIndex }) => ({ + type: actions.MOVE, + id, + key, + index, + toIndex, +}); +export const createUp = ({ id, key, index, newValue, merge }) => ({ + type: actions.LIST_UP, + id, + key, + index, + newValue, + merge, +}); export const createClear = ({ id }) => ({ type: actions.CLEAR, id }); export default actions; diff --git a/frontend/src/store/input-store.js b/frontend/src/store/input-store.js index c3559bf44f..f93cabcad7 100644 --- a/frontend/src/store/input-store.js +++ b/frontend/src/store/input-store.js @@ -1,75 +1,75 @@ import { Map as $Map, List, fromJS } from 'immutable'; import actions from './input-actions'; -function getInitState () { +function getInitState() { return new $Map(); } -function init (state, { id, value }) { +function init(state, { id, value }) { state = assertId(state, id); return state.setIn(id, fromJS(value)); } -function assertId (state, id) { +function assertId(state, id) { if (!state.hasIn(id)) { return state.setIn(id, new $Map({ inputId: id })); } return state; } -function assertList (state, id, key) { +function assertList(state, id, key) { if (!state.getIn(id).has(key)) { return state.setIn(id.concat([key]), new List()); } return state; } -function setKeyValue (state, { id, key, value }) { +function setKeyValue(state, { id, key, value }) { state = assertId(state, id); return state.setIn(id.concat([key]), value); } -function increment (state, { id, key }) { +function increment(state, { id, key }) { state = assertId(state, id); return state.updateIn(id.concat([key]), (value = 0) => value + 1); } -function clear (state, { id }) { +function clear(state, { id }) { if (state.hasIn(id)) { return state.removeIn(id); } return state; } -function addToList (state, { id, key, value }) { +function addToList(state, { id, key, value }) { state = assertId(state, id); state = assertList(state, id, key); - return state.updateIn(id.concat([key]), (list) => list.push(value)); + return state.updateIn(id.concat([key]), list => list.push(value)); } -function updateInList (state, { id, key, index, newValue, merge }) { +function updateInList(state, { id, key, index, newValue, merge }) { state = assertId(state, id); state = assertList(state, id, key); - return state.updateIn(id.concat([key]), (list) => { + return state.updateIn(id.concat([key]), list => { if (merge && list.has(index)) { newValue = list.get(index).merge(new $Map(newValue)); - } else if (typeof newValue !== 'string' ) { + } else if (typeof newValue !== 'string') { newValue = fromJS(newValue); } return list.set(index, newValue); }); } -function removeFromList (state, { id, key, index }) { +function removeFromList(state, { id, key, index }) { state = assertId(state, id); state = assertList(state, id, key); - return state.updateIn(id.concat([key]), (list) => list.remove(index)); + return state.updateIn(id.concat([key]), list => list.remove(index)); } -function move (state, { id, key, index, toIndex }) { +function move(state, { id, key, index, toIndex }) { return state.updateIn(id.concat([key]), list => { const olditem = list.get(index); return list.delete(index).insert(toIndex, olditem); diff --git a/frontend/src/store/settings/actions.js b/frontend/src/store/settings/actions.js index 5aaa47345d..d1b6ffda4a 100644 --- a/frontend/src/store/settings/actions.js +++ b/frontend/src/store/settings/actions.js @@ -7,4 +7,5 @@ export const updateSetting = (group, field, value) => ({ value, }); -export const updateSettingForGroup = (group) => (field, value) => updateSetting(group, field, value); +export const updateSettingForGroup = group => (field, value) => + updateSetting(group, field, value); diff --git a/frontend/src/store/settings/index.js b/frontend/src/store/settings/index.js index 12d883b154..719da261cb 100644 --- a/frontend/src/store/settings/index.js +++ b/frontend/src/store/settings/index.js @@ -5,7 +5,7 @@ import { UPDATE_SETTING } from './actions'; const localStorage = window.localStorage || {}; const SETTINGS = 'settings'; -function getInitState () { +function getInitState() { try { const state = JSON.parse(localStorage.getItem(SETTINGS)); return state ? fromJS(state) : new $Map(); @@ -14,8 +14,11 @@ function getInitState () { } } -function updateSetting (state, action) { - const newState = state.updateIn([action.group, action.field], () => action.value); +function updateSetting(state, action) { + const newState = state.updateIn( + [action.group, action.field], + () => action.value + ); localStorage.setItem(SETTINGS, JSON.stringify(newState.toJSON())); return newState; diff --git a/frontend/src/store/strategy/actions.js b/frontend/src/store/strategy/actions.js index 84525dd291..6e7bc16781 100644 --- a/frontend/src/store/strategy/actions.js +++ b/frontend/src/store/strategy/actions.js @@ -12,77 +12,79 @@ export const ERROR_RECEIVE_STRATEGIES = 'ERROR_RECEIVE_STRATEGIES'; export const ERROR_CREATING_STRATEGY = 'ERROR_CREATING_STRATEGY'; export const ERROR_UPDATING_STRATEGY = 'ERROR_UPDATING_STRATEGY'; -const addStrategy = (strategy) => ({ type: ADD_STRATEGY, strategy }); -const createRemoveStrategy = (strategy) => ({ type: REMOVE_STRATEGY, strategy }); -const updatedStrategy = (strategy) => ({ type: UPDATE_STRATEGY, strategy }); +const addStrategy = strategy => ({ type: ADD_STRATEGY, strategy }); +const createRemoveStrategy = strategy => ({ type: REMOVE_STRATEGY, strategy }); +const updatedStrategy = strategy => ({ type: UPDATE_STRATEGY, strategy }); -const errorCreatingStrategy = (statusCode) => ({ +const errorCreatingStrategy = statusCode => ({ type: ERROR_CREATING_STRATEGY, statusCode, }); const startRequest = () => ({ type: REQUEST_STRATEGIES }); - -const receiveStrategies = (json) => ({ +const receiveStrategies = json => ({ type: RECEIVE_STRATEGIES, value: json.strategies, }); const startCreate = () => ({ type: START_CREATE_STRATEGY }); -const errorReceiveStrategies = (statusCode) => ({ +const errorReceiveStrategies = statusCode => ({ type: ERROR_RECEIVE_STRATEGIES, statusCode, }); const startUpdate = () => ({ type: START_UPDATE_STRATEGY }); -function dispatchAndThrow (dispatch, type) { - return (error) => { +function dispatchAndThrow(dispatch, type) { + return error => { dispatch({ type, error, receivedAt: Date.now() }); throw error; }; } -export function fetchStrategies () { +export function fetchStrategies() { return dispatch => { dispatch(startRequest()); - return api.fetchAll() + return api + .fetchAll() .then(json => dispatch(receiveStrategies(json))) .catch(error => dispatch(errorReceiveStrategies(error))); }; } -export function createStrategy (strategy) { +export function createStrategy(strategy) { return dispatch => { dispatch(startCreate()); - return api.create(strategy) + return api + .create(strategy) .then(() => dispatch(addStrategy(strategy))) .catch(error => dispatch(errorCreatingStrategy(error))); }; } -export function updateStrategy (strategy) { +export function updateStrategy(strategy) { return dispatch => { dispatch(startUpdate()); - return api.update(strategy) + return api + .update(strategy) .then(() => dispatch(updatedStrategy(strategy))) .catch(dispatchAndThrow(dispatch, ERROR_UPDATING_STRATEGY)); }; } - -export function removeStrategy (strategy) { - return dispatch => api.remove(strategy) - .then(() => dispatch(createRemoveStrategy(strategy))) - .catch(error => dispatch(errorCreatingStrategy(error))); +export function removeStrategy(strategy) { + return dispatch => + api + .remove(strategy) + .then(() => dispatch(createRemoveStrategy(strategy))) + .catch(error => dispatch(errorCreatingStrategy(error))); } -export function getApplicationsWithStrategy (strategyName) { +export function getApplicationsWithStrategy(strategyName) { return applicationApi.fetchApplicationsWithStrategyName(strategyName); } - diff --git a/frontend/src/store/strategy/index.js b/frontend/src/store/strategy/index.js index 125f3fc530..c17eb2e8dc 100644 --- a/frontend/src/store/strategy/index.js +++ b/frontend/src/store/strategy/index.js @@ -1,26 +1,33 @@ import { List, Map as $Map } from 'immutable'; -import { RECEIVE_STRATEGIES, REMOVE_STRATEGY, ADD_STRATEGY, UPDATE_STRATEGY } from './actions'; +import { + RECEIVE_STRATEGIES, + REMOVE_STRATEGY, + ADD_STRATEGY, + UPDATE_STRATEGY, +} from './actions'; -function getInitState () { +function getInitState() { return new $Map({ list: new List() }); } -function removeStrategy (state, action) { +function removeStrategy(state, action) { const indexToRemove = state.get('list').indexOf(action.strategy); if (indexToRemove !== -1) { - return state.update('list', (list) => list.remove(indexToRemove)); + return state.update('list', list => list.remove(indexToRemove)); } return state; } -function updateStrategy (state, action) { - return state.update('list', (list) => list.map(strategy => { - if (strategy.name === action.strategy.name) { - return action.strategy; - } else { - return strategy; - } - })); +function updateStrategy(state, action) { + return state.update('list', list => + list.map(strategy => { + if (strategy.name === action.strategy.name) { + return action.strategy; + } else { + return strategy; + } + }) + ); } const strategies = (state = getInitState(), action) => { @@ -30,7 +37,7 @@ const strategies = (state = getInitState(), action) => { case REMOVE_STRATEGY: return removeStrategy(state, action); case ADD_STRATEGY: - return state.update('list', (list) => list.push(action.strategy)); + return state.update('list', list => list.push(action.strategy)); case UPDATE_STRATEGY: return updateStrategy(state, action); default: diff --git a/frontend/src/store/user/actions.js b/frontend/src/store/user/actions.js index 69818de1e9..502590ec32 100644 --- a/frontend/src/store/user/actions.js +++ b/frontend/src/store/user/actions.js @@ -2,7 +2,7 @@ export const USER_UPDATE_USERNAME = 'USER_UPDATE_USERNAME'; export const USER_SAVE = 'USER_SAVE'; export const USER_EDIT = 'USER_EDIT'; -export const updateUserName = (value) => ({ +export const updateUserName = value => ({ type: USER_UPDATE_USERNAME, value, }); diff --git a/frontend/src/store/user/index.js b/frontend/src/store/user/index.js index a5d7ae2bc8..3adb7f289c 100644 --- a/frontend/src/store/user/index.js +++ b/frontend/src/store/user/index.js @@ -4,12 +4,13 @@ import { USER_UPDATE_USERNAME, USER_SAVE, USER_EDIT } from './actions'; const COOKIE_NAME = 'username'; // Ref: http://stackoverflow.com/questions/10730362/get-cookie-by-name -function readCookie () { +function readCookie() { const nameEQ = `${COOKIE_NAME}=`; const ca = document.cookie.split(';'); - for (let i = 0;i < ca.length;i++) { + for (let i = 0; i < ca.length; i++) { let c = ca[i]; - while (c.charAt(0) == ' ') { // eslint-disable-line eqeqeq + // eslint-disable-next-line eqeqeq + while (c.charAt(0) == ' ') { c = c.substring(1, c.length); } if (c.indexOf(nameEQ) === 0) { @@ -18,22 +19,23 @@ function readCookie () { } } -function writeCookie (userName) { - document.cookie = `${COOKIE_NAME}=${encodeURIComponent(userName)}; expires=Thu, 18 Dec 2099 12:00:00 UTC`; +function writeCookie(userName) { + document.cookie = `${COOKIE_NAME}=${encodeURIComponent( + userName + )}; expires=Thu, 18 Dec 2099 12:00:00 UTC`; } - -function getInitState () { +function getInitState() { const userName = decodeURIComponent(readCookie(COOKIE_NAME)); const showDialog = !userName; return new $Map({ userName, showDialog }); } -function updateUserName (state, action) { +function updateUserName(state, action) { return state.set('userName', action.value); } -function save (state) { +function save(state) { const userName = state.get('userName'); if (userName) { writeCookie(userName); diff --git a/frontend/webpack.config.js b/frontend/webpack.config.js index db8ffb84bc..dc19020889 100644 --- a/frontend/webpack.config.js +++ b/frontend/webpack.config.js @@ -54,14 +54,17 @@ module.exports = { sourceMap: true, modules: true, importLoaders: 1, - localIdentName: '[name]__[local]___[hash:base64:5]', + localIdentName: + '[name]__[local]___[hash:base64:5]', }, }, { loader: 'sass-loader', options: { // data: '@import "theme/_config.scss";', - includePaths: [path.resolve(__dirname, './src')], + includePaths: [ + path.resolve(__dirname, './src'), + ], }, }, ], @@ -69,7 +72,10 @@ module.exports = { }, { test: /\.css$/, - loader: ExtractTextPlugin.extract({ fallback: 'style-loader', use: 'css-loader' }), + loader: ExtractTextPlugin.extract({ + fallback: 'style-loader', + use: 'css-loader', + }), }, ], }, diff --git a/frontend/yarn.lock b/frontend/yarn.lock index b231965333..f1e3580bb5 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -43,7 +43,7 @@ acorn@^4.0.3, acorn@^4.0.4: version "4.0.13" resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" -acorn@^5.0.0, acorn@^5.0.1: +acorn@^5.0.0, acorn@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.1.1.tgz#53fe161111f912ab999ee887a90a0bc52822fd75" @@ -173,6 +173,13 @@ array-flatten@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.1.tgz#426bb9da84090c1838d812c8150af20a8331e296" +array-includes@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.0.3.tgz#184b48f62d92d7452bb31b323165c7f8bd02266d" + dependencies: + define-properties "^1.1.2" + es-abstract "^1.7.0" + array-union@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" @@ -1250,9 +1257,9 @@ chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.0.1.tgz#dbec49436d2ae15f536114e76d14656cdbc0f44d" +chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.1.0.tgz#ac5becf14fa21b99c6c92ca7a7d7cfd5b17e743e" dependencies: ansi-styles "^3.1.0" escape-string-regexp "^1.0.5" @@ -1526,6 +1533,14 @@ cross-spawn@^3.0.0: lru-cache "^4.0.1" which "^1.2.9" +cross-spawn@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" + dependencies: + lru-cache "^4.0.1" + shebang-command "^1.2.0" + which "^1.2.9" + cryptiles@2.x.x: version "2.0.5" resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" @@ -1698,6 +1713,13 @@ default-require-extensions@^1.0.0: dependencies: strip-bom "^2.0.0" +define-properties@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94" + dependencies: + foreach "^2.0.5" + object-keys "^1.0.8" + defined@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" @@ -1890,6 +1912,24 @@ error-stack-parser@^1.3.6: dependencies: stackframe "^0.3.1" +es-abstract@^1.7.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.8.0.tgz#3b00385e85729932beffa9163bbea1234e932914" + dependencies: + es-to-primitive "^1.1.1" + function-bind "^1.1.0" + has "^1.0.1" + is-callable "^1.1.3" + is-regex "^1.0.4" + +es-to-primitive@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.1.1.tgz#45355248a88979034b6792e19bb81f2b7975dd0d" + dependencies: + is-callable "^1.1.1" + is-date-object "^1.0.1" + is-symbol "^1.0.1" + es5-ext@^0.10.14, es5-ext@^0.10.9, es5-ext@~0.10.14: version "0.10.24" resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.24.tgz#a55877c9924bc0c8d9bd3c2cbe17495ac1709b14" @@ -1970,6 +2010,13 @@ escope@^3.6.0: esrecurse "^4.1.0" estraverse "^4.1.1" +eslint-config-finn-prettier@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eslint-config-finn-prettier/-/eslint-config-finn-prettier-3.0.0.tgz#01122c53dc0ae022988f1c69b50e59a00bf238c6" + dependencies: + eslint-config-prettier "^2.0.0" + eslint-plugin-prettier "^2.0.1" + eslint-config-finn-react@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/eslint-config-finn-react/-/eslint-config-finn-react-2.0.0.tgz#94d11891168eeb110aedd39285c31700e9f09bc2" @@ -1982,6 +2029,12 @@ eslint-config-finn@^2.0.0: dependencies: eslint-config-schibsted "^4.0.0" +eslint-config-prettier@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-2.3.0.tgz#b75b1eabea0c8b97b34403647ee25db349b9d8a0" + dependencies: + get-stdin "^5.0.1" + eslint-config-schibsted-react@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/eslint-config-schibsted-react/-/eslint-config-schibsted-react-4.0.1.tgz#c1fca494bcb142b32cc413885b93eff5ff541e2a" @@ -1990,13 +2043,21 @@ eslint-config-schibsted@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/eslint-config-schibsted/-/eslint-config-schibsted-4.0.0.tgz#7c0dacdc18d56a2dac006923917ef40df29438ed" -eslint-plugin-react@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.1.0.tgz#27770acf39f5fd49cd0af4083ce58104eb390d4c" +eslint-plugin-prettier@^2.0.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-2.2.0.tgz#f2837ad063903d73c621e7188fb3d41486434088" + dependencies: + fast-diff "^1.1.1" + jest-docblock "^20.0.1" + +eslint-plugin-react@^7.3.0: + version "7.3.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.3.0.tgz#ca9368da36f733fbdc05718ae4e91f778f38e344" dependencies: doctrine "^2.0.0" has "^1.0.1" - jsx-ast-utils "^1.4.1" + jsx-ast-utils "^2.0.0" + prop-types "^15.5.10" eslint-scope@^3.7.1: version "3.7.1" @@ -2005,29 +2066,31 @@ eslint-scope@^3.7.1: esrecurse "^4.1.0" estraverse "^4.1.1" -eslint@^4.1.1: - version "4.2.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.2.0.tgz#a2b3184111b198e02e9c7f3cca625a5e01c56b3d" +eslint@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.5.0.tgz#bb75d3b8bde97fb5e13efcd539744677feb019c3" dependencies: ajv "^5.2.0" babel-code-frame "^6.22.0" - chalk "^1.1.3" + chalk "^2.1.0" concat-stream "^1.6.0" + cross-spawn "^5.1.0" debug "^2.6.8" doctrine "^2.0.0" eslint-scope "^3.7.1" - espree "^3.4.3" + espree "^3.5.0" esquery "^1.0.0" estraverse "^4.2.0" esutils "^2.0.2" file-entry-cache "^2.0.0" + functional-red-black-tree "^1.0.1" glob "^7.1.2" globals "^9.17.0" ignore "^3.3.3" imurmurhash "^0.1.4" inquirer "^3.0.6" is-resolvable "^1.0.0" - js-yaml "^3.8.4" + js-yaml "^3.9.1" json-stable-stringify "^1.0.1" levn "^0.3.0" lodash "^4.17.4" @@ -2039,15 +2102,17 @@ eslint@^4.1.1: pluralize "^4.0.0" progress "^2.0.0" require-uncached "^1.0.3" + semver "^5.3.0" + strip-ansi "^4.0.0" strip-json-comments "~2.0.1" table "^4.0.1" text-table "~0.2.0" -espree@^3.4.3: - version "3.4.3" - resolved "https://registry.yarnpkg.com/espree/-/espree-3.4.3.tgz#2910b5ccd49ce893c2ffffaab4fd8b3a31b82374" +espree@^3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.0.tgz#98358625bdd055861ea27e2867ea729faf463d8d" dependencies: - acorn "^5.0.1" + acorn "^5.1.1" acorn-jsx "^3.0.0" esprima@^2.6.0, esprima@^2.7.1: @@ -2204,6 +2269,10 @@ fast-deep-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff" +fast-diff@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.1.1.tgz#0aea0e4e605b6a2189f0e936d4b7fbaf1b7cfd9b" + fast-levenshtein@~2.0.4: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" @@ -2348,6 +2417,10 @@ for-own@^1.0.0: dependencies: for-in "^1.0.1" +foreach@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -2396,10 +2469,14 @@ fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2: mkdirp ">=0.5 0" rimraf "2" -function-bind@^1.0.2: +function-bind@^1.0.2, function-bind@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771" +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + gauge@~2.7.3: version "2.7.4" resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" @@ -2427,6 +2504,10 @@ get-stdin@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" +get-stdin@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398" + getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -2834,12 +2915,20 @@ is-builtin-module@^1.0.0: dependencies: builtin-modules "^1.0.0" +is-callable@^1.1.1, is-callable@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.3.tgz#86eb75392805ddc33af71c92a0eedf74ee7604b2" + is-ci@^1.0.10: version "1.0.10" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.0.10.tgz#f739336b2632365061a9d48270cd56ae3369318e" dependencies: ci-info "^1.0.0" +is-date-object@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" + is-dotfile@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" @@ -2940,6 +3029,12 @@ is-promise@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" +is-regex@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" + dependencies: + has "^1.0.1" + is-resolvable@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.0.0.tgz#8df57c61ea2e3c501408d100fb013cf8d6e0cc62" @@ -2956,6 +3051,10 @@ is-svg@^2.0.0: dependencies: html-comment-regex "^1.1.0" +is-symbol@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572" + is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" @@ -3119,7 +3218,7 @@ jest-diff@^20.0.3: jest-matcher-utils "^20.0.3" pretty-format "^20.0.3" -jest-docblock@^20.0.3: +jest-docblock@^20.0.1, jest-docblock@^20.0.3: version "20.0.3" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-20.0.3.tgz#17bea984342cc33d83c50fbe1545ea0efaa44712" @@ -3275,9 +3374,9 @@ js-tokens@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" -js-yaml@^3.7.0, js-yaml@^3.8.4: - version "3.9.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.9.0.tgz#4ffbbf25c2ac963b8299dc74da7e3740de1c18ce" +js-yaml@^3.7.0, js-yaml@^3.9.1: + version "3.9.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.9.1.tgz#08775cebdfdd359209f0d2acd383c8f86a6904a0" dependencies: argparse "^1.0.7" esprima "^4.0.0" @@ -3372,9 +3471,11 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.3.6" -jsx-ast-utils@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz#3867213e8dd79bf1e8f2300c0cfc1efb182c0df1" +jsx-ast-utils@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.0.0.tgz#ec06a3d60cf307e5e119dac7bad81e89f096f0f8" + dependencies: + array-includes "^3.0.3" kind-of@^2.0.1: version "2.0.1" @@ -3890,6 +3991,10 @@ object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" +object-keys@^1.0.8: + version "1.0.11" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d" + object.omit@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" @@ -4376,6 +4481,10 @@ preserve@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" +prettier@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.6.0.tgz#23e9c68251f440feb847f558821bead21765919a" + pretty-format@^20.0.3: version "20.0.3" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-20.0.3.tgz#020e350a560a1fe1a98dc3beb6ccffb386de8b14" @@ -5048,6 +5157,16 @@ shallow-clone@^0.1.2: lazy-cache "^0.2.3" mixin-object "^2.0.1" +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + shellwords@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.0.tgz#66afd47b6a12932d9071cbfd98a52e785cd0ba14"