diff --git a/frontend/src/component/common/index.js b/frontend/src/component/common/index.js index 8493bc2567..1daa5a6bfc 100644 --- a/frontend/src/component/common/index.js +++ b/frontend/src/component/common/index.js @@ -140,8 +140,8 @@ IconLink.propTypes = { icon: PropTypes.string, }; -export const DropdownButton = ({ label, id }) => ( - 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 9e510db3ea..719d267548 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 @@ -47,7 +47,7 @@ exports[`renders correctly with one feature 1`] = ` className="listItemStrategies hideLt920" > @@ -100,7 +100,7 @@ exports[`renders correctly with one feature without permission 1`] = ` className="listItemStrategies hideLt920" > diff --git a/frontend/src/component/feature/__tests__/__snapshots__/view-component-test.jsx.snap b/frontend/src/component/feature/__tests__/__snapshots__/view-component-test.jsx.snap index effe2d0ec1..75e93cb14f 100644 --- a/frontend/src/component/feature/__tests__/__snapshots__/view-component-test.jsx.snap +++ b/frontend/src/component/feature/__tests__/__snapshots__/view-component-test.jsx.snap @@ -10,6 +10,19 @@ exports[`renders correctly with one feature 1`] = ` } } > + + Active +
+ + + Status + + + + + Set toggle Active + + + Mark toggle as Stale + + + { - const { name, description, enabled, type } = feature; + const { name, description, enabled, type, stale } = feature; const { showLastHour = false } = settings; const isStale = showLastHour ? metricsLastHour.isFallback : metricsLastMinute.isFallback; const percent = @@ -25,7 +26,6 @@ const Feature = ({ (showLastHour ? calc(metricsLastHour.yes, metricsLastHour.yes + metricsLastHour.no, 0) : calc(metricsLastMinute.yes, metricsLastMinute.yes + metricsLastMinute.no, 0)); - const typeChip = {type}; const featureUrl = toggleFeature === undefined ? `/archive/strategies/${name}` : `/features/strategies/${name}`; return ( @@ -51,7 +51,10 @@ const Feature = ({ {description} - {typeChip} + + + {type} + {revive && hasPermission(UPDATE_FEATURE) ? ( revive(feature.name)}> diff --git a/frontend/src/component/feature/feature.scss b/frontend/src/component/feature/feature.scss index bba65140ce..a8e127f020 100644 --- a/frontend/src/component/feature/feature.scss +++ b/frontend/src/component/feature/feature.scss @@ -35,6 +35,7 @@ } .typeChip { - margin-left: 8px !important; - background: #d3c1ff; + margin: 0 8px !important; + box-shadow: 0 2px 2px 0 rgba(0,0,0,.14), 0 3px 1px -2px rgba(0,0,0,.2), 0 1px 5px 0 rgba(0,0,0,.12); + background-color: #cfd8dc !important; } \ No newline at end of file diff --git a/frontend/src/component/feature/status-component.jsx b/frontend/src/component/feature/status-component.jsx new file mode 100644 index 0000000000..75afb8f3a8 --- /dev/null +++ b/frontend/src/component/feature/status-component.jsx @@ -0,0 +1,28 @@ +import React from 'react'; +import { Chip } from 'react-mdl'; +import PropTypes from 'prop-types'; + +export default function StatusComponent({ stale, style, showActive = true }) { + if (!stale && !showActive) { + return null; + } + + const className = stale + ? 'mdl-color--red mdl-color-text--white mdl-shadow--2dp' + : 'mdl-color--light-green-500 mdl-color-text--white mdl-shadow--2dp'; + + const title = stale ? 'Feature toggle is deprecated.' : 'Feature toggle is active.'; + const value = stale ? 'Stale' : 'Active'; + + return ( + + {value} + + ); +} + +StatusComponent.propTypes = { + stale: PropTypes.bool.isRequired, + style: PropTypes.object, + showActive: PropTypes.bool, +}; diff --git a/frontend/src/component/feature/status-update-component.jsx b/frontend/src/component/feature/status-update-component.jsx new file mode 100644 index 0000000000..f5dfbbea8e --- /dev/null +++ b/frontend/src/component/feature/status-update-component.jsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { Menu, MenuItem } from 'react-mdl'; +import { DropdownButton } from '../common'; +import PropTypes from 'prop-types'; + +export default function StatusUpdateComponent({ stale, updateStale }) { + function setStatus(field) { + if (field === 'active') { + updateStale(false); + } else { + updateStale(true); + } + } + + return ( + + + setStatus(e.target.getAttribute('data-target'))} + style={{ width: '168px' }} + > + + Set toggle Active + + + Mark toggle as Stale + + + + ); +} + +StatusUpdateComponent.propTypes = { + stale: PropTypes.bool.isRequired, + updateStale: PropTypes.func.isRequired, +}; diff --git a/frontend/src/component/feature/view-component.jsx b/frontend/src/component/feature/view-component.jsx index 37ac1cf42d..f8f5ecfeda 100644 --- a/frontend/src/component/feature/view-component.jsx +++ b/frontend/src/component/feature/view-component.jsx @@ -12,6 +12,8 @@ import FeatureTypeSelect from './form/feature-type-select-container'; import UpdateDescriptionComponent from './form/update-description-component'; import { styles as commonStyles } from '../common'; import { CREATE_FEATURE, DELETE_FEATURE, UPDATE_FEATURE } from '../../permissions'; +import StatusComponent from './status-component'; +import StatusUpdateComponent from './status-update-component'; const TABS = { strategies: 0, @@ -32,6 +34,7 @@ export default class ViewFeatureToggleComponent extends React.Component { featureToggleName: PropTypes.string.isRequired, features: PropTypes.array.isRequired, toggleFeature: PropTypes.func, + setStale: PropTypes.func, removeFeatureToggle: PropTypes.func, revive: PropTypes.func, fetchArchive: PropTypes.func, @@ -159,8 +162,20 @@ export default class ViewFeatureToggleComponent extends React.Component { this.props.editFeatureToggle(feature); }; + const updateStale = stale => { + this.props.setStale(stale, featureToggleName); + }; + return ( + {featureToggle.name} + response.json()); +} + function remove(featureToggleName) { return fetch(`${URI}/${featureToggleName}`, { method: 'DELETE', @@ -75,5 +86,6 @@ export default { validate, update, toggle, + setStale, remove, }; diff --git a/frontend/src/store/feature-actions.js b/frontend/src/store/feature-actions.js index 5ad8d57bd6..39d978e273 100644 --- a/frontend/src/store/feature-actions.js +++ b/frontend/src/store/feature-actions.js @@ -26,6 +26,13 @@ export function toggleFeature(enable, name) { }; } +export function setStale(stale, name) { + debug('Set stale property on feature toggle ', name); + return dispatch => { + dispatch(requestSetStaleFeatureToggle(stale, name)); + }; +} + export function editFeatureToggle(featureToggle) { debug('Update feature toggle ', featureToggle); return dispatch => { @@ -76,6 +83,21 @@ export function requestToggleFeatureToggle(enable, name) { }; } +export function requestSetStaleFeatureToggle(stale, name) { + return dispatch => { + dispatch({ type: START_UPDATE_FEATURE_TOGGLE }); + + return api + .setStale(stale, name) + .then(featureToggle => { + const info = `${name} marked as ${stale ? 'Stale' : 'Active'}.`; + setTimeout(() => dispatch({ type: MUTE_ERROR, error: info }), 1000); + dispatch({ type: UPDATE_FEATURE_TOGGLE, featureToggle, info }); + }) + .catch(dispatchAndThrow(dispatch, ERROR_UPDATE_FEATURE_TOGGLE)); + }; +} + export function requestUpdateFeatureToggle(featureToggle) { return dispatch => { dispatch({ type: START_UPDATE_FEATURE_TOGGLE });