From 98bf15bdbf6cabf781d59b6a844f5c57ff267d3f Mon Sep 17 00:00:00 2001 From: Corinne Krych Date: Sat, 3 Mar 2018 21:34:38 +0100 Subject: [PATCH 01/13] feat(archive): reuse Feature --- frontend/src/component/app.jsx | 10 +- .../archive/archive-list-component.jsx | 152 +++++++++++------- .../archive/archive-list-container.js | 73 ++++++++- .../feature/feature-list-item-component.jsx | 32 ++-- .../src/component/feature/list-component.jsx | 2 +- 5 files changed, 194 insertions(+), 75 deletions(-) diff --git a/frontend/src/component/app.jsx b/frontend/src/component/app.jsx index a52af8b78e..a905082e31 100644 --- a/frontend/src/component/app.jsx +++ b/frontend/src/component/app.jsx @@ -192,11 +192,11 @@ 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', '')} Sign out diff --git a/frontend/src/component/archive/archive-list-component.jsx b/frontend/src/component/archive/archive-list-component.jsx index 1757dcd75a..444a2fe5ab 100644 --- a/frontend/src/component/archive/archive-list-component.jsx +++ b/frontend/src/component/archive/archive-list-component.jsx @@ -1,16 +1,21 @@ -import React, { Component } from 'react'; +import React from 'react'; import PropTypes from 'prop-types'; import { Link } from 'react-router'; +import Feature from './../feature/feature-list-item-component'; import { Icon, Card, List, ListItem, ListItemContent, ListItemAction, Chip } from 'react-mdl'; +//import { Textfield, Menu, MenuItem, Card, CardActions, List, Chip, MenuItemWithIcon, DropdownButton } from 'react-mdl'; import { styles as commonStyles } from '../common'; import styles from './archive.scss'; -class ArchiveList extends Component { +class ArchiveList extends React.PureComponent { static propTypes = { name: PropTypes.string, - archive: PropTypes.array, + archive: PropTypes.array.isRequired, fetchArchive: PropTypes.func, - revive: PropTypes.func, + featureMetrics: PropTypes.object.isRequired, + updateSetting: PropTypes.func.isRequired, + settings: PropTypes.object, + revive: PropTypes.func.optional, }; componentDidMount() { @@ -55,8 +60,86 @@ class ArchiveList extends Component { } return display; } + + // render() { + // const { archive, featureMetrics, settings, revive } = this.props; + // archive.forEach(e => { + // e.reviveName = e.name; + // }); + // 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 + // + // + // + //
+ // + // {archive.map((feature, i) => ( + // + // ))} + // + //
+ //
+ // ); + // } + render() { - const { archive, revive } = this.props; + const { archive, featureMetrics, settings, revive } = this.props; + archive.forEach(e => { e.reviveName = e.name; }); @@ -73,57 +156,14 @@ class ArchiveList extends Component {
{archive.map((feature, i) => ( - - - {this.props.name && feature.name === this.props.name ? ( - - ) : ( - - )} - - - {this.props.name && feature.name === this.props.name ? ( - - {this.renderStrategiesInList(feature).map((strategyChip, i) => ( - {strategyChip} - ))} - - {feature.name} -
- {feature.description} -
-
- {this.renderStrategyDetail(feature)} -
- - ) : ( - - {feature.name} - - {this.renderStrategiesInList(feature).map((strategyChip, i) => ( - {strategyChip} - ))} - -
- {feature.description} -
- - )} -
- revive(feature.name)}> - - -
+ ))}
diff --git a/frontend/src/component/archive/archive-list-container.js b/frontend/src/component/archive/archive-list-container.js index 577187c6e5..88e2062fe4 100644 --- a/frontend/src/component/archive/archive-list-container.js +++ b/frontend/src/component/archive/archive-list-container.js @@ -1,15 +1,80 @@ import { connect } from 'react-redux'; -import ListComponent from './archive-list-component'; +import ArchiveList from './archive-list-component'; import { fetchArchive, revive } from './../../store/archive-actions'; +import { updateSettingForGroup } from './../../store/settings/actions'; const mapStateToProps = state => { - const archive = state.archive.get('list').toArray(); + const featureMetrics = state.featureMetrics.toJS(); + const settings = state.settings.toJS().feature || {}; + let features = state.archive.get('list').toArray(); + if (settings.filter) { + try { + const regex = new RegExp(settings.filter, 'i'); + features = features.filter( + feature => + regex.test(feature.name) || + regex.test(feature.description) || + feature.strategies.some(s => s && s.name && regex.test(s.name)) + ); + } catch (e) { + // Invalid filter regex + } + } + + if (!settings.sort) { + settings.sort = 'name'; + } + + if (settings.sort === 'enabled') { + 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)); + } else if (settings.sort === 'name') { + features = features.sort((a, b) => { + 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)); + } else if (settings.sort === 'metrics') { + 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].yes > target[b.name].yes) { + return -1; + } + return 1; + }); + } return { - archive, + archive: features, + featureMetrics, + settings, }; }; -const ArchiveListContainer = connect(mapStateToProps, { fetchArchive, revive })(ListComponent); +const mapDispatchToProps = { + fetchArchive, + revive, + updateSetting: updateSettingForGroup('feature'), +}; + +const ArchiveListContainer = connect(mapStateToProps, mapDispatchToProps)(ArchiveList); export default ArchiveListContainer; diff --git a/frontend/src/component/feature/feature-list-item-component.jsx b/frontend/src/component/feature/feature-list-item-component.jsx index 6d4a831f8a..41dbc2b4a0 100644 --- a/frontend/src/component/feature/feature-list-item-component.jsx +++ b/frontend/src/component/feature/feature-list-item-component.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Link } from 'react-router'; -import { Switch, Chip, ListItem } from 'react-mdl'; +import { Switch, Chip, ListItem, ListItemAction, Icon } from 'react-mdl'; import Progress from './progress'; import { calc, styles as commonStyles } from '../common'; @@ -13,6 +13,7 @@ const Feature = ({ settings, metricsLastHour = { yes: 0, no: 0, isFallback: true }, metricsLastMinute = { yes: 0, no: 0, isFallback: true }, + revive, }) => { const { name, description, enabled, strategies } = feature; @@ -42,14 +43,19 @@ const Feature = ({ - - toggleFeature(name)} - checked={enabled} - /> - + {toggleFeature ? ( // display feature list + + toggleFeature(name)} + checked={enabled} + /> + + ) : ( + // display archive + + )} + {revive ? ( + revive(feature.name)}> + + + ) : ( + + )} ); }; @@ -73,6 +86,7 @@ Feature.propTypes = { settings: PropTypes.object, metricsLastHour: PropTypes.object, metricsLastMinute: PropTypes.object, + revive: PropTypes.func, }; export default Feature; diff --git a/frontend/src/component/feature/list-component.jsx b/frontend/src/component/feature/list-component.jsx index 269b2877a3..7b060cc0f7 100644 --- a/frontend/src/component/feature/list-component.jsx +++ b/frontend/src/component/feature/list-component.jsx @@ -13,7 +13,7 @@ export default class FeatureListComponent extends React.PureComponent { featureMetrics: PropTypes.object.isRequired, fetchFeatureToggles: PropTypes.func.isRequired, updateSetting: PropTypes.func.isRequired, - toggleFeature: PropTypes.func.isRequired, + toggleFeature: PropTypes.func, settings: PropTypes.object, }; From d31d5340c2bbadbe5c2a03af34a8a4db2bae7612 Mon Sep 17 00:00:00 2001 From: Corinne Krych Date: Sat, 3 Mar 2018 21:34:45 +0100 Subject: [PATCH 02/13] feat(archive): reuse Feature --- .../archive/archive-list-component.jsx | 79 +------------------ 1 file changed, 1 insertion(+), 78 deletions(-) diff --git a/frontend/src/component/archive/archive-list-component.jsx b/frontend/src/component/archive/archive-list-component.jsx index 444a2fe5ab..43069ec216 100644 --- a/frontend/src/component/archive/archive-list-component.jsx +++ b/frontend/src/component/archive/archive-list-component.jsx @@ -2,8 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Link } from 'react-router'; import Feature from './../feature/feature-list-item-component'; -import { Icon, Card, List, ListItem, ListItemContent, ListItemAction, Chip } from 'react-mdl'; -//import { Textfield, Menu, MenuItem, Card, CardActions, List, Chip, MenuItemWithIcon, DropdownButton } from 'react-mdl'; +import { Icon, Card, List, ListItem, Chip } from 'react-mdl'; import { styles as commonStyles } from '../common'; import styles from './archive.scss'; @@ -61,82 +60,6 @@ class ArchiveList extends React.PureComponent { return display; } - // render() { - // const { archive, featureMetrics, settings, revive } = this.props; - // archive.forEach(e => { - // e.reviveName = e.name; - // }); - // 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 - // - // - // - //
- // - // {archive.map((feature, i) => ( - // - // ))} - // - //
- //
- // ); - // } - render() { const { archive, featureMetrics, settings, revive } = this.props; From a88f0da8228c4f951e4f4e872dadd089014f9996 Mon Sep 17 00:00:00 2001 From: Corinne Krych Date: Sat, 10 Mar 2018 18:09:20 +0100 Subject: [PATCH 03/13] feat(archive): reuse feature/view-component.jsx to display archive details --- .../archive/archive-list-component.jsx | 6 +- .../src/component/archive/view-container.js | 18 ++++ .../feature/feature-list-item-component.jsx | 32 +++++-- .../src/component/feature/view-component.jsx | 91 +++++++++++++------ frontend/src/index.jsx | 3 +- frontend/src/page/archive/show.js | 14 +++ 6 files changed, 124 insertions(+), 40 deletions(-) create mode 100644 frontend/src/component/archive/view-container.js create mode 100644 frontend/src/page/archive/show.js diff --git a/frontend/src/component/archive/archive-list-component.jsx b/frontend/src/component/archive/archive-list-component.jsx index 43069ec216..74cb4a171e 100644 --- a/frontend/src/component/archive/archive-list-component.jsx +++ b/frontend/src/component/archive/archive-list-component.jsx @@ -11,10 +11,10 @@ class ArchiveList extends React.PureComponent { name: PropTypes.string, archive: PropTypes.array.isRequired, fetchArchive: PropTypes.func, - featureMetrics: PropTypes.object.isRequired, - updateSetting: PropTypes.func.isRequired, + featureMetrics: PropTypes.object, + updateSetting: PropTypes.func, settings: PropTypes.object, - revive: PropTypes.func.optional, + revive: PropTypes.func, }; componentDidMount() { diff --git a/frontend/src/component/archive/view-container.js b/frontend/src/component/archive/view-container.js new file mode 100644 index 0000000000..f7517c9629 --- /dev/null +++ b/frontend/src/component/archive/view-container.js @@ -0,0 +1,18 @@ +import { connect } from 'react-redux'; +import { fetchArchive } from './../../store/archive-actions'; + +import ViewToggleComponent from './../feature/view-component'; + +export default connect( + (state, props) => ({ + features: state.archive.get('list').toArray(), + featureToggle: state.archive + .get('list') + .toArray() + .find(toggle => toggle.name === props.featureToggleName), + activeTab: props.activeTab, + }), + { + fetchArchive, + } +)(ViewToggleComponent); diff --git a/frontend/src/component/feature/feature-list-item-component.jsx b/frontend/src/component/feature/feature-list-item-component.jsx index 41dbc2b4a0..20f2ff0eb4 100644 --- a/frontend/src/component/feature/feature-list-item-component.jsx +++ b/frontend/src/component/feature/feature-list-item-component.jsx @@ -43,7 +43,7 @@ const Feature = ({ - {toggleFeature ? ( // display feature list + {toggleFeature ? ( // display feature list, toggleFeature is available ) : ( - // display archive + // display archive, toggleFeature is undefined )} - - {name} - {description} - + {toggleFeature ? ( // display feature list, toggleFeature is available + + {name} + + {description} + + + ) : ( + + {name} + + {description} + + + )} {strategyChips} diff --git a/frontend/src/component/feature/view-component.jsx b/frontend/src/component/feature/view-component.jsx index 4f2546fe33..06f4b250e6 100644 --- a/frontend/src/component/feature/view-component.jsx +++ b/frontend/src/component/feature/view-component.jsx @@ -15,24 +15,31 @@ const TABS = { }; export default class ViewFeatureToggleComponent extends React.Component { + isFeatureView; constructor(props) { super(props); + this.isFeatureView = !!props.fetchFeatureToggles; } static propTypes = { activeTab: PropTypes.string.isRequired, featureToggleName: PropTypes.string.isRequired, features: PropTypes.array.isRequired, - toggleFeature: PropTypes.func.isRequired, - removeFeatureToggle: PropTypes.func.isRequired, - fetchFeatureToggles: PropTypes.func.isRequired, - editFeatureToggle: PropTypes.func.isRequired, + toggleFeature: PropTypes.func, + removeFeatureToggle: PropTypes.func, + fetchArchive: PropTypes.func, + fetchFeatureToggles: PropTypes.func, + editFeatureToggle: PropTypes.func, featureToggle: PropTypes.object, }; componentWillMount() { if (this.props.features.length === 0) { - this.props.fetchFeatureToggles(); + if (this.isFeatureView) { + this.props.fetchFeatureToggles(); + } else { + this.props.fetchArchive(); + } } } @@ -42,14 +49,16 @@ export default class ViewFeatureToggleComponent extends React.Component { if (TABS[activeTab] === TABS.history) { return ; } else if (TABS[activeTab] === TABS.strategies) { - return ; + return + } else { return ; } } goToTab(tabName, featureToggleName) { - hashHistory.push(`/features/${tabName}/${featureToggleName}`); + let view = this.props.fetchFeatureToggles ? 'features' : 'archive'; + hashHistory.push(`/${view}/${tabName}/${featureToggleName}`); } render() { @@ -113,16 +122,28 @@ export default class ViewFeatureToggleComponent extends React.Component { {featureToggle.name} - setValue('description', v)} - onBlur={updateFeatureToggle} - /> + {this.isFeatureView ? ( + setValue('description', v)} + onBlur={updateFeatureToggle} + /> + ) : ( + + )} - toggleFeature(featureToggle.name)} - > - {featureToggle.enabled ? 'Enabled' : 'Disabled'} - + {this.isFeatureView ? ( + toggleFeature(featureToggle.name)} + > + {featureToggle.enabled ? 'Enabled' : 'Disabled'} + + ) : ( + toggleFeature(featureToggle.name)} + > + {featureToggle.enabled ? 'Enabled' : 'Disabled'} + + )} - + + {this.isFeatureView ? ( + + ) : ( + + )}
- + diff --git a/frontend/src/page/archive/show.js b/frontend/src/page/archive/show.js new file mode 100644 index 0000000000..674e303ac0 --- /dev/null +++ b/frontend/src/page/archive/show.js @@ -0,0 +1,14 @@ +import React, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; +import ViewFeatureToggle from './../../component/archive/view-container'; + +export default class Features extends PureComponent { + static propTypes = { + params: PropTypes.object.isRequired, + }; + + render() { + const { params } = this.props; + return ; + } +} From b3734cde614ec119c958393ba0e9d377deaafe3d Mon Sep 17 00:00:00 2001 From: Corinne Krych Date: Sun, 11 Mar 2018 11:54:45 +0100 Subject: [PATCH 04/13] feat(archive): make read-only view for feature item --- .../form/form-update-feature-container.jsx | 2 +- .../form/form-view-feature-component.jsx | 42 +++++++++++++++++++ .../form/form-view-feature-container.jsx | 40 ++++++++++++++++++ .../component/feature/form/strategies-add.jsx | 2 +- .../feature/form/strategies-list.jsx | 10 ++--- .../feature/form/strategies-section.jsx | 6 +-- .../feature/form/strategy-configure.jsx | 12 ++++-- .../src/component/feature/view-component.jsx | 6 ++- 8 files changed, 105 insertions(+), 15 deletions(-) create mode 100644 frontend/src/component/feature/form/form-view-feature-component.jsx create mode 100644 frontend/src/component/feature/form/form-view-feature-container.jsx diff --git a/frontend/src/component/feature/form/form-update-feature-container.jsx b/frontend/src/component/feature/form/form-update-feature-container.jsx index 58c755487e..f4b0f08637 100644 --- a/frontend/src/component/feature/form/form-update-feature-container.jsx +++ b/frontend/src/component/feature/form/form-update-feature-container.jsx @@ -45,7 +45,7 @@ const prepare = (methods, dispatch) => { methods.onCancel = evt => { evt.preventDefault(); methods.clear(); - window.history.back(); + hashHistory.push(`/features`); }; methods.addStrategy = v => { diff --git a/frontend/src/component/feature/form/form-view-feature-component.jsx b/frontend/src/component/feature/form/form-view-feature-component.jsx new file mode 100644 index 0000000000..bdcaab61e2 --- /dev/null +++ b/frontend/src/component/feature/form/form-view-feature-component.jsx @@ -0,0 +1,42 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import StrategiesSection from './strategies-section-container'; +import { Button, Icon } from 'react-mdl'; + +class ViewFeatureComponent extends Component { + // static displayName = `UpdateFeatureComponent-{getDisplayName(Component)}`; + componentWillMount() { + // TODO unwind this stuff + if (this.props.initCallRequired === true) { + this.props.init(this.props.input); + } + } + + render() { + const { input, onCancel } = this.props; + + const { + name, // eslint-disable-line + } = input; + const configuredStrategies = input.strategies || []; + + return ( +
+ +
+ +
+ ); + } +} + +ViewFeatureComponent.propTypes = { + input: PropTypes.object, + onCancel: PropTypes.func.isRequired, + initCallRequired: PropTypes.bool, + init: PropTypes.func, +}; + +export default ViewFeatureComponent; diff --git a/frontend/src/component/feature/form/form-view-feature-container.jsx b/frontend/src/component/feature/form/form-view-feature-container.jsx new file mode 100644 index 0000000000..3ed8e9605b --- /dev/null +++ b/frontend/src/component/feature/form/form-view-feature-container.jsx @@ -0,0 +1,40 @@ +import { connect } from 'react-redux'; +import { createMapper, createActions } from '../../input-helpers'; +import ViewFeatureToggleComponent from './form-view-feature-component'; +import { hashHistory } from 'react-router'; + +const ID = 'view-feature-toggle'; +function getId(props) { + return [ID, props.featureToggle.name]; +} +// TODO: need to scope to the active featureToggle +// best is to emulate the "input-storage"? +const mapStateToProps = createMapper({ + id: getId, + getDefault: (state, ownProps) => { + ownProps.featureToggle.strategies.forEach((strategy, index) => { + strategy.id = Math.round(Math.random() * 1000000 * (1 + index)); + }); + return ownProps.featureToggle; + }, + prepare: props => { + props.editmode = true; + return props; + }, +}); + +const prepare = methods => { + methods.onCancel = evt => { + evt.preventDefault(); + methods.clear(); + hashHistory.push(`/archive`); + }; + return methods; +}; + +const actions = createActions({ + id: getId, + prepare, +}); + +export default connect(mapStateToProps, actions)(ViewFeatureToggleComponent); diff --git a/frontend/src/component/feature/form/strategies-add.jsx b/frontend/src/component/feature/form/strategies-add.jsx index aac8731fb5..429022e2cf 100644 --- a/frontend/src/component/feature/form/strategies-add.jsx +++ b/frontend/src/component/feature/form/strategies-add.jsx @@ -5,7 +5,7 @@ import { Menu, MenuItem, IconButton } from 'react-mdl'; class AddStrategy extends React.Component { static propTypes = { strategies: PropTypes.array.isRequired, - addStrategy: PropTypes.func.isRequired, + addStrategy: PropTypes.func, fetchStrategies: PropTypes.func.isRequired, }; diff --git a/frontend/src/component/feature/form/strategies-list.jsx b/frontend/src/component/feature/form/strategies-list.jsx index 26a3eecc34..b6f8936c2c 100644 --- a/frontend/src/component/feature/form/strategies-list.jsx +++ b/frontend/src/component/feature/form/strategies-list.jsx @@ -9,9 +9,9 @@ class StrategiesList extends React.Component { static propTypes = { strategies: PropTypes.array.isRequired, configuredStrategies: PropTypes.array.isRequired, - updateStrategy: PropTypes.func.isRequired, - removeStrategy: PropTypes.func.isRequired, - moveStrategy: PropTypes.func.isRequired, + updateStrategy: PropTypes.func, + removeStrategy: PropTypes.func, + moveStrategy: PropTypes.func, }; render() { @@ -27,8 +27,8 @@ class StrategiesList extends React.Component { key={strategy.id} strategy={strategy} moveStrategy={moveStrategy} - removeStrategy={removeStrategy.bind(null, i)} - updateStrategy={updateStrategy.bind(null, i)} + removeStrategy={removeStrategy ? removeStrategy.bind(null, i) : null} + updateStrategy={updateStrategy ? updateStrategy.bind(null, i) : null} strategyDefinition={strategies.find(s => s.name === strategy.name)} /> )); diff --git a/frontend/src/component/feature/form/strategies-section.jsx b/frontend/src/component/feature/form/strategies-section.jsx index e580fc9006..6a2a305098 100644 --- a/frontend/src/component/feature/form/strategies-section.jsx +++ b/frontend/src/component/feature/form/strategies-section.jsx @@ -8,9 +8,9 @@ import { HeaderTitle } from '../../common'; class StrategiesSectionComponent extends React.Component { static propTypes = { strategies: PropTypes.array.isRequired, - addStrategy: PropTypes.func.isRequired, - removeStrategy: PropTypes.func.isRequired, - updateStrategy: PropTypes.func.isRequired, + addStrategy: PropTypes.func, + removeStrategy: PropTypes.func, + updateStrategy: PropTypes.func, fetchStrategies: PropTypes.func.isRequired, }; diff --git a/frontend/src/component/feature/form/strategy-configure.jsx b/frontend/src/component/feature/form/strategy-configure.jsx index 8311121b0e..6853a8d81c 100644 --- a/frontend/src/component/feature/form/strategy-configure.jsx +++ b/frontend/src/component/feature/form/strategy-configure.jsx @@ -47,9 +47,9 @@ class StrategyConfigure extends React.Component { static propTypes = { strategy: PropTypes.object.isRequired, strategyDefinition: PropTypes.object.isRequired, - updateStrategy: PropTypes.func.isRequired, - removeStrategy: PropTypes.func.isRequired, - moveStrategy: PropTypes.func.isRequired, + updateStrategy: PropTypes.func, + removeStrategy: PropTypes.func, + moveStrategy: PropTypes.func, isDragging: PropTypes.bool.isRequired, connectDragPreview: PropTypes.func.isRequired, connectDragSource: PropTypes.func.isRequired, @@ -170,7 +170,11 @@ class StrategyConfigure extends React.Component { - + {this.props.removeStrategy ? ( + + ) : ( + + )} {connectDragSource( diff --git a/frontend/src/component/feature/view-component.jsx b/frontend/src/component/feature/view-component.jsx index 06f4b250e6..6650761240 100644 --- a/frontend/src/component/feature/view-component.jsx +++ b/frontend/src/component/feature/view-component.jsx @@ -6,6 +6,7 @@ import { hashHistory, Link } from 'react-router'; import HistoryComponent from '../history/history-list-toggle-container'; import MetricComponent from './metric-container'; import EditFeatureToggle from './form/form-update-feature-container'; +import ViewFeatureToggle from './form/form-view-feature-container'; import { styles as commonStyles } from '../common'; const TABS = { @@ -49,7 +50,10 @@ export default class ViewFeatureToggleComponent extends React.Component { if (TABS[activeTab] === TABS.history) { return ; } else if (TABS[activeTab] === TABS.strategies) { - return + if (this.isFeatureView) { + return + } + return } else { return ; From e7d70abb0cf571845d4efb2ea54e2acaf72b0520 Mon Sep 17 00:00:00 2001 From: Corinne Krych Date: Sun, 11 Mar 2018 12:36:49 +0100 Subject: [PATCH 05/13] feat(archive): archive view can be sorted as feature view --- .../archive/archive-list-component.jsx | 85 +++++++++++++------ .../feature/form/strategy-configure.jsx | 2 +- 2 files changed, 61 insertions(+), 26 deletions(-) diff --git a/frontend/src/component/archive/archive-list-component.jsx b/frontend/src/component/archive/archive-list-component.jsx index 74cb4a171e..525aae4235 100644 --- a/frontend/src/component/archive/archive-list-component.jsx +++ b/frontend/src/component/archive/archive-list-component.jsx @@ -2,8 +2,8 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Link } from 'react-router'; import Feature from './../feature/feature-list-item-component'; -import { Icon, Card, List, ListItem, Chip } from 'react-mdl'; -import { styles as commonStyles } from '../common'; +import { CardActions, Menu, MenuItem, Icon, Card, List, Chip } from 'react-mdl'; +import { MenuItemWithIcon, DropdownButton, styles as commonStyles } from '../common'; import styles from './archive.scss'; class ArchiveList extends React.PureComponent { @@ -20,6 +20,12 @@ class ArchiveList extends React.PureComponent { componentDidMount() { this.props.fetchArchive(); } + setSort(v) { + this.props.updateSetting('sort', typeof v === 'string' ? v.trim() : ''); + } + toggleMetrics() { + this.props.updateSetting('showLastHour', !this.props.settings.showLastHour); + } renderStrategyDetail(feature) { let strategiesList = ( @@ -68,30 +74,59 @@ class ArchiveList extends React.PureComponent { }); return ( + + + this.toggleMetrics()} style={{ width: '168px' }}> + + + + + this.setSort(e.target.getAttribute('data-target'))} + style={{ width: '168px' }} + > + + Name + + + Enabled + + + Created + + + Strategies + + + Metrics + + + +
{archive && archive.length > 0 ? ( -
-
- - - Toggle name - Revive - -
- - {archive.map((feature, i) => ( - - ))} - -
-
-
+ + {archive.map((feature, i) => ( + + ))} + ) : (
diff --git a/frontend/src/component/feature/form/strategy-configure.jsx b/frontend/src/component/feature/form/strategy-configure.jsx index 6853a8d81c..da816b4ac4 100644 --- a/frontend/src/component/feature/form/strategy-configure.jsx +++ b/frontend/src/component/feature/form/strategy-configure.jsx @@ -46,7 +46,7 @@ class StrategyConfigure extends React.Component { /* eslint-enable */ static propTypes = { strategy: PropTypes.object.isRequired, - strategyDefinition: PropTypes.object.isRequired, + strategyDefinition: PropTypes.object, updateStrategy: PropTypes.func, removeStrategy: PropTypes.func, moveStrategy: PropTypes.func, From 6db1483a4229e0ecbaf7e3d66669f556dfe944fb Mon Sep 17 00:00:00 2001 From: Corinne Krych Date: Sun, 11 Mar 2018 14:53:57 +0100 Subject: [PATCH 06/13] fix(unit-test): update test --- frontend/src/__mocks__/react-mdl.js | 1 + .../archive-list-component-test.jsx.snap | 229 +++++++----------- .../__tests__/archive-list-component-test.jsx | 49 +++- .../archive/archive-list-component.jsx | 12 +- .../feature-list-item-component-test.jsx.snap | 1 + .../src/component/feature/view-component.jsx | 5 +- 6 files changed, 144 insertions(+), 153 deletions(-) diff --git a/frontend/src/__mocks__/react-mdl.js b/frontend/src/__mocks__/react-mdl.js index dde54f173c..c1ddf6d0c8 100644 --- a/frontend/src/__mocks__/react-mdl.js +++ b/frontend/src/__mocks__/react-mdl.js @@ -1,5 +1,6 @@ module.exports = { Card: 'react-mdl-Card', + CardActions: 'react-mdl-CardActions', CardTitle: 'react-mdl-CardTitle', CardText: 'react-mdl-CardText', CardMenu: 'react-mdl-CardMenu', 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 76efcd1391..f9ea853981 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 @@ -1,149 +1,100 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`renders correctly with archived toggles 1`] = ` - - - -`; - exports[`renders correctly with no archived toggles 1`] = ` -
- -
- No archived feature toggles, go see - + + - active toggles here - -
+ + + + + + + + + adin-pay-confirm-disabled + + Disables the confirm-functionality from API + + + + + + default + + + + + + + + + + + + + + adin-pay-platform-sch-payment + + Enables use of schibsted payment from order-payment-management + + + + + + default + + + + +
`; diff --git a/frontend/src/component/archive/__tests__/archive-list-component-test.jsx b/frontend/src/component/archive/__tests__/archive-list-component-test.jsx index 3b38afe2ad..65723fb83f 100644 --- a/frontend/src/component/archive/__tests__/archive-list-component-test.jsx +++ b/frontend/src/component/archive/__tests__/archive-list-component-test.jsx @@ -4,7 +4,8 @@ import ArchiveList from '../archive-list-component'; import renderer from 'react-test-renderer'; jest.mock('react-mdl'); - +// TODO mock DropdownButton +// jest.mock('../../common', () => ({ DropdownButton: 'DropdownButton', styles: {} })); const archive = [ { name: 'adin-pay-confirm-disabled', @@ -24,12 +25,50 @@ const archive = [ }, ]; -test('renders correctly with no archived toggles', () => { - const tree = renderer.create().toJSON(); +xtest('renders correctly with no archived toggles', () => { + const featureMetrics = { lastHour: {}, lastMinute: {}, seenApps: {} }; + const settings = { + feature: { + filter: '', + sort: 'name', + showLastHour: false, + }, + }; + + const tree = renderer + .create( + + ) + .toJSON(); expect(tree).toMatchSnapshot(); }); -test('renders correctly with archived toggles', () => { - const tree = renderer.create().toJSON(); +xtest('renders correctly with archived toggles', () => { + const featureMetrics = { lastHour: {}, lastMinute: {}, seenApps: {} }; + const settings = { + feature: { + filter: '', + sort: 'name', + showLastHour: false, + }, + }; + + const tree = renderer + .create( + + ) + .toJSON(); expect(tree).toMatchSnapshot(); }); diff --git a/frontend/src/component/archive/archive-list-component.jsx b/frontend/src/component/archive/archive-list-component.jsx index 525aae4235..a2954c2f81 100644 --- a/frontend/src/component/archive/archive-list-component.jsx +++ b/frontend/src/component/archive/archive-list-component.jsx @@ -3,8 +3,8 @@ import PropTypes from 'prop-types'; import { Link } from 'react-router'; import Feature from './../feature/feature-list-item-component'; import { CardActions, Menu, MenuItem, Icon, Card, List, Chip } from 'react-mdl'; -import { MenuItemWithIcon, DropdownButton, styles as commonStyles } from '../common'; -import styles from './archive.scss'; +import { MenuItemWithIcon, DropdownButton, styles } from '../common'; +// import styles from './archive.scss'; class ArchiveList extends React.PureComponent { static propTypes = { @@ -49,12 +49,12 @@ class ArchiveList extends React.PureComponent { const strategyChips = feature.strategies && feature.strategies.slice(0, strategiesToShow).map((s, i) => ( - + {s.name} )); const remaining = ( - + +{remainingStrategies} ); @@ -73,7 +73,7 @@ class ArchiveList extends React.PureComponent { e.reviveName = e.name; }); return ( - + this.toggleMetrics()} style={{ width: '168px' }}> @@ -128,7 +128,7 @@ class ArchiveList extends React.PureComponent { ))} ) : ( -
+

No archived feature toggles, go see active toggles here diff --git a/frontend/src/component/feature/__tests__/__snapshots__/feature-list-item-component-test.jsx.snap b/frontend/src/component/feature/__tests__/__snapshots__/feature-list-item-component-test.jsx.snap index 1bc9ad72fc..e4b78e992a 100644 --- a/frontend/src/component/feature/__tests__/__snapshots__/feature-list-item-component-test.jsx.snap +++ b/frontend/src/component/feature/__tests__/__snapshots__/feature-list-item-component-test.jsx.snap @@ -51,5 +51,6 @@ exports[`renders correctly with one feature 1`] = ` gradualRolloutRandom + `; diff --git a/frontend/src/component/feature/view-component.jsx b/frontend/src/component/feature/view-component.jsx index 6650761240..3e5a052407 100644 --- a/frontend/src/component/feature/view-component.jsx +++ b/frontend/src/component/feature/view-component.jsx @@ -51,10 +51,9 @@ export default class ViewFeatureToggleComponent extends React.Component { return ; } else if (TABS[activeTab] === TABS.strategies) { if (this.isFeatureView) { - return + return ; } - return - + return ; } else { return ; } From 6b08647403fb7db23ef8c3587375ef6ea8d1b681 Mon Sep 17 00:00:00 2001 From: Corinne Krych Date: Wed, 14 Mar 2018 09:43:58 +0100 Subject: [PATCH 07/13] fix(archive): do not disaply add strategy in read-only mode --- frontend/src/component/feature/form/strategies-section.jsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend/src/component/feature/form/strategies-section.jsx b/frontend/src/component/feature/form/strategies-section.jsx index 6a2a305098..ed3c24605b 100644 --- a/frontend/src/component/feature/form/strategies-section.jsx +++ b/frontend/src/component/feature/form/strategies-section.jsx @@ -25,7 +25,11 @@ class StrategiesSectionComponent extends React.Component { return (
- } /> + {this.props.addStrategy ? ( + } /> + ) : ( + + )}
); From e962a38f0b2bdc32d41944fb09cb7a4087dd5189 Mon Sep 17 00:00:00 2001 From: Corinne Krych Date: Wed, 14 Mar 2018 10:32:13 +0100 Subject: [PATCH 08/13] feat(archive): make revive available from archive view details --- frontend/src/component/archive/view-container.js | 4 ++-- .../component/feature/feature-list-item-component.jsx | 10 +++++++++- frontend/src/component/feature/view-component.jsx | 11 ++++++++++- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/frontend/src/component/archive/view-container.js b/frontend/src/component/archive/view-container.js index f7517c9629..6457722619 100644 --- a/frontend/src/component/archive/view-container.js +++ b/frontend/src/component/archive/view-container.js @@ -1,6 +1,5 @@ import { connect } from 'react-redux'; -import { fetchArchive } from './../../store/archive-actions'; - +import { fetchArchive, revive } from './../../store/archive-actions'; import ViewToggleComponent from './../feature/view-component'; export default connect( @@ -14,5 +13,6 @@ export default connect( }), { fetchArchive, + revive, } )(ViewToggleComponent); diff --git a/frontend/src/component/feature/feature-list-item-component.jsx b/frontend/src/component/feature/feature-list-item-component.jsx index 20f2ff0eb4..774ad70469 100644 --- a/frontend/src/component/feature/feature-list-item-component.jsx +++ b/frontend/src/component/feature/feature-list-item-component.jsx @@ -54,7 +54,15 @@ const Feature = ({
) : ( // display archive, toggleFeature is undefined - + + toggleFeature(name)} + checked={enabled} + /> + )} {toggleFeature ? ( // display feature list, toggleFeature is available diff --git a/frontend/src/component/feature/view-component.jsx b/frontend/src/component/feature/view-component.jsx index 3e5a052407..31ad0d01d8 100644 --- a/frontend/src/component/feature/view-component.jsx +++ b/frontend/src/component/feature/view-component.jsx @@ -8,6 +8,7 @@ import MetricComponent from './metric-container'; import EditFeatureToggle from './form/form-update-feature-container'; import ViewFeatureToggle from './form/form-view-feature-container'; import { styles as commonStyles } from '../common'; +import {revive} from '../../store/archive-actions'; const TABS = { strategies: 0, @@ -28,6 +29,7 @@ export default class ViewFeatureToggleComponent extends React.Component { features: PropTypes.array.isRequired, toggleFeature: PropTypes.func, removeFeatureToggle: PropTypes.func, + revive: PropTypes.func, fetchArchive: PropTypes.func, fetchFeatureToggles: PropTypes.func, editFeatureToggle: PropTypes.func, @@ -69,6 +71,7 @@ export default class ViewFeatureToggleComponent extends React.Component { featureToggle, features, activeTab, + revive, // setValue, featureToggleName, toggleFeature, @@ -106,6 +109,10 @@ export default class ViewFeatureToggleComponent extends React.Component { hashHistory.push('/features'); } }; + const reviveToggle = () => { + revive(featureToggle.name); + hashHistory.push('/features'); + }; const updateFeatureToggle = () => { let feature = { ...featureToggle }; if (Array.isArray(feature.strategies)) { @@ -183,7 +190,9 @@ export default class ViewFeatureToggleComponent extends React.Component { Archive ) : ( - + )}
From 477768c81db0e8e0934f7fc3ffb9ad630342c0c9 Mon Sep 17 00:00:00 2001 From: Corinne Krych Date: Wed, 14 Mar 2018 11:02:32 +0100 Subject: [PATCH 09/13] clean(archive): reuse list-component from feature to diplay list of archives --- .../src/component/archive/__tests__/.eslintrc | 5 - .../archive-list-component-test.jsx.snap | 100 ------------ .../__tests__/archive-list-component-test.jsx | 74 --------- .../archive/archive-list-component.jsx | 142 ------------------ .../archive/archive-list-container.js | 6 +- .../src/component/feature/list-component.jsx | 20 ++- 6 files changed, 17 insertions(+), 330 deletions(-) delete mode 100644 frontend/src/component/archive/__tests__/.eslintrc delete mode 100644 frontend/src/component/archive/__tests__/__snapshots__/archive-list-component-test.jsx.snap delete mode 100644 frontend/src/component/archive/__tests__/archive-list-component-test.jsx delete mode 100644 frontend/src/component/archive/archive-list-component.jsx diff --git a/frontend/src/component/archive/__tests__/.eslintrc b/frontend/src/component/archive/__tests__/.eslintrc deleted file mode 100644 index eba2077219..0000000000 --- a/frontend/src/component/archive/__tests__/.eslintrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "env": { - "jest": true - } -} 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 deleted file mode 100644 index f9ea853981..0000000000 --- a/frontend/src/component/archive/__tests__/__snapshots__/archive-list-component-test.jsx.snap +++ /dev/null @@ -1,100 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`renders correctly with no archived toggles 1`] = ` - -
- - - - - - - - - - - adin-pay-confirm-disabled - - Disables the confirm-functionality from API - - - - - - default - - - - - - - - - - - - - - adin-pay-platform-sch-payment - - Enables use of schibsted payment from order-payment-management - - - - - - default - - - - - -
-`; diff --git a/frontend/src/component/archive/__tests__/archive-list-component-test.jsx b/frontend/src/component/archive/__tests__/archive-list-component-test.jsx deleted file mode 100644 index 65723fb83f..0000000000 --- a/frontend/src/component/archive/__tests__/archive-list-component-test.jsx +++ /dev/null @@ -1,74 +0,0 @@ -import React from 'react'; - -import ArchiveList from '../archive-list-component'; -import renderer from 'react-test-renderer'; - -jest.mock('react-mdl'); -// TODO mock DropdownButton -// jest.mock('../../common', () => ({ DropdownButton: 'DropdownButton', styles: {} })); -const archive = [ - { - name: 'adin-pay-confirm-disabled', - description: 'Disables the confirm-functionality from API', - enabled: false, - strategies: [{ name: 'default', parameters: {} }], - createdAt: '2016-10-25T15:38:28.573Z', - reviveName: 'adin-pay-confirm-disabled', - }, - { - name: 'adin-pay-platform-sch-payment', - description: 'Enables use of schibsted payment from order-payment-management', - enabled: true, - strategies: [{ name: 'default', parameters: {} }], - createdAt: '2016-08-03T12:41:35.631Z', - reviveName: 'adin-pay-platform-sch-payment', - }, -]; - -xtest('renders correctly with no archived toggles', () => { - const featureMetrics = { lastHour: {}, lastMinute: {}, seenApps: {} }; - const settings = { - feature: { - filter: '', - sort: 'name', - showLastHour: false, - }, - }; - - const tree = renderer - .create( - - ) - .toJSON(); - expect(tree).toMatchSnapshot(); -}); - -xtest('renders correctly with archived toggles', () => { - const featureMetrics = { lastHour: {}, lastMinute: {}, seenApps: {} }; - const settings = { - feature: { - filter: '', - sort: 'name', - showLastHour: false, - }, - }; - - const tree = renderer - .create( - - ) - .toJSON(); - expect(tree).toMatchSnapshot(); -}); diff --git a/frontend/src/component/archive/archive-list-component.jsx b/frontend/src/component/archive/archive-list-component.jsx deleted file mode 100644 index a2954c2f81..0000000000 --- a/frontend/src/component/archive/archive-list-component.jsx +++ /dev/null @@ -1,142 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { Link } from 'react-router'; -import Feature from './../feature/feature-list-item-component'; -import { CardActions, Menu, MenuItem, Icon, Card, List, Chip } from 'react-mdl'; -import { MenuItemWithIcon, DropdownButton, styles } from '../common'; -// import styles from './archive.scss'; - -class ArchiveList extends React.PureComponent { - static propTypes = { - name: PropTypes.string, - archive: PropTypes.array.isRequired, - fetchArchive: PropTypes.func, - featureMetrics: PropTypes.object, - updateSetting: PropTypes.func, - settings: PropTypes.object, - revive: PropTypes.func, - }; - - componentDidMount() { - this.props.fetchArchive(); - } - setSort(v) { - this.props.updateSetting('sort', typeof v === 'string' ? v.trim() : ''); - } - toggleMetrics() { - this.props.updateSetting('showLastHour', !this.props.settings.showLastHour); - } - renderStrategyDetail(feature) { - let strategiesList = ( - - {feature.strategies.map((s, i) => ( - - {s.name} - {Object.keys(s.parameters).map((p, j) => {s.parameters[p]})} - - ))} - - ); - - return strategiesList; - } - renderStrategiesInList(feature) { - let display = []; - if (feature.strategies && feature.strategies.length > 0) { - const strategiesToShow = Math.min(feature.strategies.length, 3); - const remainingStrategies = feature.strategies.length - strategiesToShow; - - const strategyChips = - feature.strategies && - feature.strategies.slice(0, strategiesToShow).map((s, i) => ( - - {s.name} - - )); - const remaining = ( - - +{remainingStrategies} - - ); - if (remainingStrategies > 0) { - display.push(remaining); - } - display.push(strategyChips); - } - return display; - } - - render() { - const { archive, featureMetrics, settings, revive } = this.props; - - archive.forEach(e => { - e.reviveName = e.name; - }); - return ( - - - - this.toggleMetrics()} style={{ width: '168px' }}> - - - - - this.setSort(e.target.getAttribute('data-target'))} - style={{ width: '168px' }} - > - - Name - - - Enabled - - - Created - - - Strategies - - - Metrics - - - -
- {archive && archive.length > 0 ? ( - - {archive.map((feature, i) => ( - - ))} - - ) : ( -
- -
- No archived feature toggles, go see active toggles here -
- )} -
- ); - } -} - -export default ArchiveList; diff --git a/frontend/src/component/archive/archive-list-container.js b/frontend/src/component/archive/archive-list-container.js index 88e2062fe4..b1c884d258 100644 --- a/frontend/src/component/archive/archive-list-container.js +++ b/frontend/src/component/archive/archive-list-container.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux'; -import ArchiveList from './archive-list-component'; +import FeatureListComponent from './../feature/list-component'; import { fetchArchive, revive } from './../../store/archive-actions'; import { updateSettingForGroup } from './../../store/settings/actions'; @@ -63,7 +63,7 @@ const mapStateToProps = state => { } return { - archive: features, + features, featureMetrics, settings, }; @@ -75,6 +75,6 @@ const mapDispatchToProps = { updateSetting: updateSettingForGroup('feature'), }; -const ArchiveListContainer = connect(mapStateToProps, mapDispatchToProps)(ArchiveList); +const ArchiveListContainer = connect(mapStateToProps, mapDispatchToProps)(FeatureListComponent); export default ArchiveListContainer; diff --git a/frontend/src/component/feature/list-component.jsx b/frontend/src/component/feature/list-component.jsx index 7b060cc0f7..118260df96 100644 --- a/frontend/src/component/feature/list-component.jsx +++ b/frontend/src/component/feature/list-component.jsx @@ -7,11 +7,13 @@ import { Icon, FABButton, Textfield, Menu, MenuItem, Card, CardActions, List } f import { MenuItemWithIcon, DropdownButton, styles as commonStyles } from '../common'; import styles from './feature.scss'; -export default class FeatureListComponent extends React.PureComponent { +export default class FeatureListComponent extends React.Component { static propTypes = { features: PropTypes.array.isRequired, featureMetrics: PropTypes.object.isRequired, - fetchFeatureToggles: PropTypes.func.isRequired, + fetchFeatureToggles: PropTypes.func, + fetchArchive: PropTypes.func, + revive: PropTypes.func, updateSetting: PropTypes.func.isRequired, toggleFeature: PropTypes.func, settings: PropTypes.object, @@ -22,7 +24,11 @@ export default class FeatureListComponent extends React.PureComponent { }; componentDidMount() { - this.props.fetchFeatureToggles(); + if (this.props.fetchFeatureToggles) { + this.props.fetchFeatureToggles(); + } else { + this.props.fetchArchive(); + } } toggleMetrics() { @@ -38,8 +44,10 @@ export default class FeatureListComponent extends React.PureComponent { } render() { - const { features, toggleFeature, featureMetrics, settings } = this.props; - + const { features, toggleFeature, featureMetrics, settings, revive } = this.props; + features.forEach(e => { + e.reviveName = e.name; + }); return (
@@ -107,7 +115,7 @@ export default class FeatureListComponent extends React.PureComponent { metricsLastHour={featureMetrics.lastHour[feature.name]} metricsLastMinute={featureMetrics.lastMinute[feature.name]} feature={feature} - toggleFeature={toggleFeature} + revive={revive} /> ))} From 10fd027eae4a3c732655b43d76f5012fb62633e9 Mon Sep 17 00:00:00 2001 From: Corinne Krych Date: Wed, 14 Mar 2018 11:23:13 +0100 Subject: [PATCH 10/13] clean(archive): reuse part of list-container from feature to diplay list of archives --- .../archive/archive-list-container.js | 68 +------------------ .../src/component/feature/list-component.jsx | 1 + .../src/component/feature/list-container.jsx | 6 +- 3 files changed, 6 insertions(+), 69 deletions(-) diff --git a/frontend/src/component/archive/archive-list-container.js b/frontend/src/component/archive/archive-list-container.js index b1c884d258..5c12f58e06 100644 --- a/frontend/src/component/archive/archive-list-container.js +++ b/frontend/src/component/archive/archive-list-container.js @@ -2,73 +2,9 @@ import { connect } from 'react-redux'; import FeatureListComponent from './../feature/list-component'; import { fetchArchive, revive } from './../../store/archive-actions'; import { updateSettingForGroup } from './../../store/settings/actions'; +import { mapStateToPropsConfigurable } from '../feature/list-container'; -const mapStateToProps = state => { - const featureMetrics = state.featureMetrics.toJS(); - const settings = state.settings.toJS().feature || {}; - let features = state.archive.get('list').toArray(); - if (settings.filter) { - try { - const regex = new RegExp(settings.filter, 'i'); - features = features.filter( - feature => - regex.test(feature.name) || - regex.test(feature.description) || - feature.strategies.some(s => s && s.name && regex.test(s.name)) - ); - } catch (e) { - // Invalid filter regex - } - } - - if (!settings.sort) { - settings.sort = 'name'; - } - - if (settings.sort === 'enabled') { - 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)); - } else if (settings.sort === 'name') { - features = features.sort((a, b) => { - 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)); - } else if (settings.sort === 'metrics') { - 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].yes > target[b.name].yes) { - return -1; - } - return 1; - }); - } - - return { - features, - featureMetrics, - settings, - }; -}; - +const mapStateToProps = mapStateToPropsConfigurable(false); const mapDispatchToProps = { fetchArchive, revive, diff --git a/frontend/src/component/feature/list-component.jsx b/frontend/src/component/feature/list-component.jsx index 118260df96..3b38518ddc 100644 --- a/frontend/src/component/feature/list-component.jsx +++ b/frontend/src/component/feature/list-component.jsx @@ -115,6 +115,7 @@ export default class FeatureListComponent extends React.Component { metricsLastHour={featureMetrics.lastHour[feature.name]} metricsLastMinute={featureMetrics.lastMinute[feature.name]} feature={feature} + toggleFeature={toggleFeature} revive={revive} /> ))} diff --git a/frontend/src/component/feature/list-container.jsx b/frontend/src/component/feature/list-container.jsx index 412b50376c..3ca68741f1 100644 --- a/frontend/src/component/feature/list-container.jsx +++ b/frontend/src/component/feature/list-container.jsx @@ -4,10 +4,10 @@ import { updateSettingForGroup } from '../../store/settings/actions'; import FeatureListComponent from './list-component'; -const mapStateToProps = state => { +export const mapStateToPropsConfigurable = isFeature => state => { const featureMetrics = state.featureMetrics.toJS(); const settings = state.settings.toJS().feature || {}; - let features = state.features.toJS(); + let features = isFeature ? state.features.toJS() : state.archive.get('list').toArray(); if (settings.filter) { try { const regex = new RegExp(settings.filter, 'i'); @@ -69,7 +69,7 @@ const mapStateToProps = state => { settings, }; }; - +const mapStateToProps = mapStateToPropsConfigurable(true); const mapDispatchToProps = { toggleFeature, fetchFeatureToggles, From 8d28ba7d2196aa0f57a8e778b8b06c657b95053d Mon Sep 17 00:00:00 2001 From: Corinne Krych Date: Wed, 14 Mar 2018 11:30:48 +0100 Subject: [PATCH 11/13] fix(travis): make CI happy --- frontend/src/component/feature/view-component.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/component/feature/view-component.jsx b/frontend/src/component/feature/view-component.jsx index 31ad0d01d8..c000eaf720 100644 --- a/frontend/src/component/feature/view-component.jsx +++ b/frontend/src/component/feature/view-component.jsx @@ -8,7 +8,6 @@ import MetricComponent from './metric-container'; import EditFeatureToggle from './form/form-update-feature-container'; import ViewFeatureToggle from './form/form-view-feature-container'; import { styles as commonStyles } from '../common'; -import {revive} from '../../store/archive-actions'; const TABS = { strategies: 0, From aaf4243b24912ba02eaaf7d8e2706a2e622cdea5 Mon Sep 17 00:00:00 2001 From: Corinne Krych Date: Tue, 20 Mar 2018 18:53:59 +0100 Subject: [PATCH 12/13] clean(feature-form): remove unused code --- .../form/form-view-feature-component.jsx | 12 --------- .../src/component/feature/view-component.jsx | 26 ++++++------------- 2 files changed, 8 insertions(+), 30 deletions(-) diff --git a/frontend/src/component/feature/form/form-view-feature-component.jsx b/frontend/src/component/feature/form/form-view-feature-component.jsx index bdcaab61e2..3fa862c5a6 100644 --- a/frontend/src/component/feature/form/form-view-feature-component.jsx +++ b/frontend/src/component/feature/form/form-view-feature-component.jsx @@ -4,20 +4,8 @@ import StrategiesSection from './strategies-section-container'; import { Button, Icon } from 'react-mdl'; class ViewFeatureComponent extends Component { - // static displayName = `UpdateFeatureComponent-{getDisplayName(Component)}`; - componentWillMount() { - // TODO unwind this stuff - if (this.props.initCallRequired === true) { - this.props.init(this.props.input); - } - } - render() { const { input, onCancel } = this.props; - - const { - name, // eslint-disable-line - } = input; const configuredStrategies = input.strategies || []; return ( diff --git a/frontend/src/component/feature/view-component.jsx b/frontend/src/component/feature/view-component.jsx index c000eaf720..2d287456fd 100644 --- a/frontend/src/component/feature/view-component.jsx +++ b/frontend/src/component/feature/view-component.jsx @@ -164,24 +164,14 @@ export default class ViewFeatureToggleComponent extends React.Component { }} > - {this.isFeatureView ? ( - toggleFeature(featureToggle.name)} - > - {featureToggle.enabled ? 'Enabled' : 'Disabled'} - - ) : ( - toggleFeature(featureToggle.name)} - > - {featureToggle.enabled ? 'Enabled' : 'Disabled'} - - )} + toggleFeature(featureToggle.name)} + > + {featureToggle.enabled ? 'Enabled' : 'Disabled'} + {this.isFeatureView ? ( From a7a7201590140a2504e2446bf182f443df98ff17 Mon Sep 17 00:00:00 2001 From: Corinne Krych Date: Tue, 20 Mar 2018 19:07:17 +0100 Subject: [PATCH 13/13] clean(feature-list): remove ternary if in jsx --- .../feature-list-item-component-test.jsx.snap | 1 + .../feature/feature-list-item-component.jsx | 60 +++++-------------- 2 files changed, 15 insertions(+), 46 deletions(-) diff --git a/frontend/src/component/feature/__tests__/__snapshots__/feature-list-item-component-test.jsx.snap b/frontend/src/component/feature/__tests__/__snapshots__/feature-list-item-component-test.jsx.snap index e4b78e992a..fe9c8e6a76 100644 --- a/frontend/src/component/feature/__tests__/__snapshots__/feature-list-item-component-test.jsx.snap +++ b/frontend/src/component/feature/__tests__/__snapshots__/feature-list-item-component-test.jsx.snap @@ -22,6 +22,7 @@ exports[`renders correctly with one feature 1`] = ` > diff --git a/frontend/src/component/feature/feature-list-item-component.jsx b/frontend/src/component/feature/feature-list-item-component.jsx index 774ad70469..47221a1428 100644 --- a/frontend/src/component/feature/feature-list-item-component.jsx +++ b/frontend/src/component/feature/feature-list-item-component.jsx @@ -16,10 +16,8 @@ const Feature = ({ revive, }) => { const { name, description, enabled, strategies } = feature; - const { showLastHour = false } = settings; const isStale = showLastHour ? metricsLastHour.isFallback : metricsLastMinute.isFallback; - const percent = 1 * (showLastHour @@ -28,7 +26,6 @@ const Feature = ({ const strategiesToShow = Math.min(strategies.length, 3); const remainingStrategies = strategies.length - strategiesToShow; - const strategyChips = strategies && strategies.slice(0, strategiesToShow).map((s, i) => ( @@ -37,55 +34,26 @@ const Feature = ({ )); const summaryChip = remainingStrategies > 0 && +{remainingStrategies}; - + const featureUrl = toggleFeature === undefined ? `/archive/strategies/${name}` : `/features/strategies/${name}`; return ( - {toggleFeature ? ( // display feature list, toggleFeature is available - - toggleFeature(name)} - checked={enabled} - /> - - ) : ( - // display archive, toggleFeature is undefined - - toggleFeature(name)} - checked={enabled} - /> - - )} + + toggleFeature(name)} + checked={enabled} + /> + - {toggleFeature ? ( // display feature list, toggleFeature is available - - {name} - - {description} - - - ) : ( - - {name} - - {description} - - - )} + + {name} + {description} + {strategyChips}