mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-25 00:07:47 +01:00
cleans up the filter and sorting toolbar + fab for new feature toggle… (#61)
* cleans up the filter and sorting toolbar + fab for new feature toggle + makes name default sorting
This commit is contained in:
parent
92eaed85d1
commit
5242e2aa0d
@ -27,6 +27,17 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.listLink {
|
||||||
|
color: #212121;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: normal;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.listLink:hover {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 920px) {
|
@media (max-width: 920px) {
|
||||||
.hideLt920 {
|
.hideLt920 {
|
||||||
display: none;
|
display: none;
|
||||||
@ -74,4 +85,9 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
min-height: 200px;
|
min-height: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dropdownButton {
|
||||||
|
text-transform: none;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
@ -5,7 +5,7 @@ import styles from './common.scss';
|
|||||||
const {
|
const {
|
||||||
List, ListItem, ListItemContent,
|
List, ListItem, ListItemContent,
|
||||||
Button, Icon,
|
Button, Icon,
|
||||||
Switch,
|
Switch, MenuItem,
|
||||||
} = require('react-mdl');
|
} = require('react-mdl');
|
||||||
const { Link } = require('react-router');
|
const { Link } = require('react-router');
|
||||||
|
|
||||||
@ -112,6 +112,20 @@ export const ExternalIconLink = ({ url, children }) => (
|
|||||||
</IconLink>
|
</IconLink>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const DropdownButton = ({ label, id }) => (
|
||||||
|
<Button id={id} className={styles.dropdownButton}>
|
||||||
|
{label}
|
||||||
|
<Icon name="arrow_drop_down" className="mdl-color-text--grey-600"/>
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const MenuItemWithIcon = ({ icon, label, disabled, ...menuItemProps }) => (
|
||||||
|
<MenuItem disabled={disabled} style={{ display: 'flex', alignItems: 'center' }} {...menuItemProps}>
|
||||||
|
<Icon name={icon} style={{ paddingRight: '16px' }}/>
|
||||||
|
{label}
|
||||||
|
</MenuItem>
|
||||||
|
);
|
||||||
|
|
||||||
const badNumbers = [NaN, Infinity, -Infinity];
|
const badNumbers = [NaN, Infinity, -Infinity];
|
||||||
export function calc (value, total, decimal) {
|
export function calc (value, total, decimal) {
|
||||||
if (typeof value !== 'number' ||
|
if (typeof value !== 'number' ||
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import { Link } from 'react-router';
|
import { Link } from 'react-router';
|
||||||
import { Switch, Icon, Chip } from 'react-mdl';
|
import { Switch, Icon, Chip, ListItem } from 'react-mdl';
|
||||||
import Progress from './progress';
|
import Progress from './progress';
|
||||||
import { calc, styles as commonStyles } from '../common';
|
import { calc, styles as commonStyles } from '../common';
|
||||||
|
|
||||||
@ -27,13 +27,13 @@ const Feature = ({
|
|||||||
const remainingStrategies = strategies.length - strategiesToShow;
|
const remainingStrategies = strategies.length - strategiesToShow;
|
||||||
|
|
||||||
const strategyChips = strategies && strategies.slice(0, strategiesToShow).map((s, i) =>
|
const strategyChips = strategies && strategies.slice(0, strategiesToShow).map((s, i) =>
|
||||||
<Chip className={styles.iconListItemChip} key={i}>{s.name}</Chip>);
|
<Chip className={styles.strategyChip} key={i}>{s.name}</Chip>);
|
||||||
const summaryChip = remainingStrategies > 0 &&
|
const summaryChip = remainingStrategies > 0 &&
|
||||||
<Chip className={styles.iconListItemChip}>+{remainingStrategies}</Chip>;
|
<Chip className={styles.strategyChip}>+{remainingStrategies}</Chip>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li key={name} className="mdl-list__item mdl-list__item--two-line">
|
<ListItem twoLine>
|
||||||
<span className={styles.iconListItemProgress}>
|
<span className={styles.listItemMetric}>
|
||||||
<div style={{ width: '40px', textAlign: 'center' }}>
|
<div style={{ width: '40px', textAlign: 'center' }}>
|
||||||
{
|
{
|
||||||
isStale ?
|
isStale ?
|
||||||
@ -46,26 +46,29 @@ const Feature = ({
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
<span className={styles.iconListItemToggle} style={{ flexShrink: 0 }}>
|
<span className={styles.listItemToggle}>
|
||||||
<Switch title={`Toggle ${name}`} key="left-actions" onChange={() => toggleFeature(name)} checked={enabled} />
|
<Switch title={`Toggle ${name}`} key="left-actions" onChange={() => toggleFeature(name)} checked={enabled} />
|
||||||
</span>
|
</span>
|
||||||
<span className="mdl-list__item-primary-content" style={{ minWidth: 0 }}>
|
<span className={['mdl-list__item-primary-content', styles.listItemLink].join(' ')}>
|
||||||
<Link to={`/features/view/${name}`} className={[styles.link, commonStyles.truncate].join(' ')}>
|
<Link to={`/features/view/${name}`} className={[commonStyles.listLink, commonStyles.truncate].join(' ')}>
|
||||||
{name}
|
{name}
|
||||||
<span className={['mdl-list__item-sub-title', commonStyles.truncate].join(' ')}>{description}</span>
|
<span className={['mdl-list__item-sub-title', commonStyles.truncate].join(' ')}>{description}</span>
|
||||||
</Link>
|
</Link>
|
||||||
</span>
|
</span>
|
||||||
<span className={commonStyles.hideLt920} style={{ flexShrink: 0 }}>
|
<span className={[styles.listItemStrategies, commonStyles.hideLt920].join(' ')}>
|
||||||
{strategyChips}
|
{strategyChips}
|
||||||
{summaryChip}
|
{summaryChip}
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</ListItem>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Feature.propTypes = {
|
Feature.propTypes = {
|
||||||
feature: PropTypes.object,
|
feature: PropTypes.object,
|
||||||
toggleFeature: PropTypes.func,
|
toggleFeature: PropTypes.func,
|
||||||
|
settings: PropTypes.object,
|
||||||
|
metricsLastHour: PropTypes.object,
|
||||||
|
metricsLastMinute: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Feature;
|
export default Feature;
|
||||||
|
@ -1,56 +1,34 @@
|
|||||||
.action {
|
.toolbar {
|
||||||
color: #aaa !important;
|
position: relative;
|
||||||
cursor: pointer;
|
padding: 0 104px 16px 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.yes {
|
.toolbarButton {
|
||||||
color: green;
|
position: absolute;
|
||||||
|
top: 56px;
|
||||||
|
right: 24px;
|
||||||
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.no {
|
.listItemMetric {
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
|
|
||||||
.link {
|
|
||||||
color: #212121;
|
|
||||||
text-decoration: none;
|
|
||||||
font-weight: normal;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.link:hover {
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.iconListItemProgress {
|
|
||||||
float: left;
|
float: left;
|
||||||
margin-right: 16px;
|
margin-right: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.iconListItemToggle {
|
.listItemToggle {
|
||||||
float: left;
|
|
||||||
margin-right: 16px;
|
margin-right: 16px;
|
||||||
width: 40px;
|
width: 40px;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.iconListItemChip {
|
.listItemLink {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.listItemStrategies {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.strategyChip {
|
||||||
margin-left: 8px !important;
|
margin-left: 8px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.topList {
|
|
||||||
display: flex;
|
|
||||||
margin: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.topListItem0 {
|
|
||||||
flex: 1;
|
|
||||||
flex-grow: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.topListItem {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.topListItem2 {
|
|
||||||
flex: 2;
|
|
||||||
}
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import Feature from './feature-list-item-component';
|
import Feature from './feature-list-item-component';
|
||||||
import { Link } from 'react-router';
|
import { Link } from 'react-router';
|
||||||
import { Icon, Chip, ChipContact, IconButton, FABButton, Textfield, Menu, MenuItem, Grid, Cell } from 'react-mdl';
|
import { Icon, FABButton, Textfield, Menu, MenuItem, Card, CardActions, List } from 'react-mdl';
|
||||||
|
|
||||||
import { styles as commonStyles } from '../common';
|
import { MenuItemWithIcon, DropdownButton, styles as commonStyles } from '../common';
|
||||||
import styles from './feature.scss';
|
import styles from './feature.scss';
|
||||||
|
|
||||||
export default class FeatureListComponent extends React.PureComponent {
|
export default class FeatureListComponent extends React.PureComponent {
|
||||||
@ -15,6 +15,8 @@ export default class FeatureListComponent extends React.PureComponent {
|
|||||||
featureMetrics: PropTypes.object.isRequired,
|
featureMetrics: PropTypes.object.isRequired,
|
||||||
fetchFeatureToggles: PropTypes.func.isRequired,
|
fetchFeatureToggles: PropTypes.func.isRequired,
|
||||||
fetchFeatureMetrics: PropTypes.func.isRequired,
|
fetchFeatureMetrics: PropTypes.func.isRequired,
|
||||||
|
updateSetting: PropTypes.func.isRequired,
|
||||||
|
settings: React.PropTypes.object,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,73 +51,54 @@ export default class FeatureListComponent extends React.PureComponent {
|
|||||||
render () {
|
render () {
|
||||||
const { features, toggleFeature, featureMetrics, settings } = this.props;
|
const { features, toggleFeature, featureMetrics, settings } = this.props;
|
||||||
|
|
||||||
return (
|
return (<div>
|
||||||
<Grid className="mdl-color--white">
|
<div className={styles.toolbar}>
|
||||||
<Cell col={12}>
|
<Textfield
|
||||||
<div className={styles.topList}>
|
floatingLabel
|
||||||
<Chip onClick={() => this.toggleMetrics()} className={styles.topListItem0}>
|
value={settings.filter}
|
||||||
{ settings.showLastHour &&
|
onChange={(e) => { this.setFilter(e.target.value); }}
|
||||||
<ChipContact className="mdl-color--teal mdl-color-text--white">
|
label="Search"
|
||||||
<Icon name="hourglass_full" style={{ fontSize: '16px' }} />
|
style={{ width: '100%' }}
|
||||||
</ChipContact> }
|
/>
|
||||||
{ '1 hour' }
|
<Link to="/features/create" className={styles.toolbarButton}>
|
||||||
</Chip>
|
<FABButton accent title="Create feature toggle">
|
||||||
|
<Icon name="add"/>
|
||||||
<Chip onClick={() => this.toggleMetrics()} className={styles.topListItem0}>
|
</FABButton>
|
||||||
{ !settings.showLastHour &&
|
</Link>
|
||||||
<ChipContact className="mdl-color--teal mdl-color-text--white">
|
</div>
|
||||||
<Icon name="hourglass_empty" style={{ fontSize: '16px' }} />
|
<Card shadow={0} className={commonStyles.fullwidth}>
|
||||||
</ChipContact> }
|
<CardActions>
|
||||||
{ '1 minute' }
|
<DropdownButton id="metric" label={`Last ${settings.showLastHour ? 'hour' : 'minute'}`}/>
|
||||||
</Chip>
|
<Menu target="metric" onClick={() => this.toggleMetrics()}
|
||||||
|
style={{ width: '168px' }}>
|
||||||
<div className={styles.topListItem2} style={{ margin: '-10px 10px 0 10px' }}>
|
<MenuItemWithIcon icon="hourglass_empty" disabled={!settings.showLastHour} data-target="minute"
|
||||||
<Textfield
|
label="Last minute"/>
|
||||||
floatingLabel
|
<MenuItemWithIcon icon="hourglass_full" disabled={settings.showLastHour} data-target="hour"
|
||||||
value={settings.filter}
|
label="Last hour"/>
|
||||||
onChange={(e) => { this.setFilter(e.target.value); }}
|
</Menu>
|
||||||
label="Filter toggles"
|
<DropdownButton id="sorting" label={`By ${settings.sort}`}/>
|
||||||
style={{ width: '100%' }}
|
<Menu target="sorting" onClick={(e) => this.setSort(e.target.getAttribute('data-target'))}
|
||||||
/>
|
style={{ width: '168px' }}>
|
||||||
</div>
|
<MenuItem disabled={!settings.sort || settings.sort === 'name'} data-target="name">Name</MenuItem>
|
||||||
|
<MenuItem disabled={settings.sort === 'enabled'} data-target="enabled">Enabled</MenuItem>
|
||||||
<div style={{ position: 'relative' }} className={styles.topListItem0}>
|
<MenuItem disabled={settings.sort === 'created'} data-target="created">Created</MenuItem>
|
||||||
<IconButton name="sort" id="demo-menu-top-right" colored title="Sort" />
|
<MenuItem disabled={settings.sort === 'strategies'} data-target="strategies">Strategies</MenuItem>
|
||||||
<Menu target="demo-menu-top-right" valign="bottom" align="right" ripple onClick={
|
<MenuItem disabled={settings.sort === 'metrics'} data-target="metrics">Metrics</MenuItem>
|
||||||
(e) => this.setSort(e.target.getAttribute('data-target'))}>
|
</Menu>
|
||||||
<MenuItem disabled>Filter by:</MenuItem>
|
</CardActions>
|
||||||
<MenuItem disabled={!settings.sort || settings.sort === 'nosort'} data-target="nosort">Default</MenuItem>
|
<hr className={commonStyles.divider}/>
|
||||||
<MenuItem disabled={settings.sort === 'name'} data-target="name">Name</MenuItem>
|
<List className={commonStyles.list}>
|
||||||
<MenuItem disabled={settings.sort === 'enabled'} data-target="enabled">Enabled</MenuItem>
|
{features.map((feature, i) =>
|
||||||
<MenuItem disabled={settings.sort === 'appName'} data-target="appName">Application name</MenuItem>
|
<Feature key={i}
|
||||||
<MenuItem disabled={settings.sort === 'created'} data-target="created">Created</MenuItem>
|
settings={settings}
|
||||||
<MenuItem disabled={settings.sort === 'strategies'} data-target="strategies">Strategies</MenuItem>
|
metricsLastHour={featureMetrics.lastHour[feature.name]}
|
||||||
<MenuItem disabled={settings.sort === 'metrics'} data-target="metrics">Metrics</MenuItem>
|
metricsLastMinute={featureMetrics.lastMinute[feature.name]}
|
||||||
</Menu>
|
feature={feature}
|
||||||
</div>
|
toggleFeature={toggleFeature}/>
|
||||||
<Link to="/features/create" className={styles.topListItem0}>
|
)}
|
||||||
<IconButton ripple raised name="add" component="span" style={{ color: 'black' }}/>
|
</List>
|
||||||
</Link>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul className={['mdl-list', commonStyles.list].join(' ')}>
|
|
||||||
{features.map((feature, i) =>
|
|
||||||
<Feature key={i}
|
|
||||||
settings={settings}
|
|
||||||
metricsLastHour={featureMetrics.lastHour[feature.name]}
|
|
||||||
metricsLastMinute={featureMetrics.lastMinute[feature.name]}
|
|
||||||
feature={feature}
|
|
||||||
toggleFeature={toggleFeature}/>
|
|
||||||
)}
|
|
||||||
</ul>
|
|
||||||
<hr />
|
|
||||||
<Link to="/features/create" className={styles.topListItem0}>
|
|
||||||
<FABButton ripple component="span" mini>
|
|
||||||
<Icon name="add" />
|
|
||||||
</FABButton>
|
|
||||||
</Link>
|
|
||||||
</Cell>
|
|
||||||
</Grid>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,13 +27,6 @@ const mapStateToProps = (state) => {
|
|||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
a.enabled === b.enabled ? 0 : a.enabled ? -1 : 1
|
a.enabled === b.enabled ? 0 : a.enabled ? -1 : 1
|
||||||
));
|
));
|
||||||
} else if (settings.sort === 'appName') {
|
|
||||||
// AppName
|
|
||||||
// features = features.sort((a, b) => {
|
|
||||||
// if (a.appName < b.appName) { return -1; }
|
|
||||||
// if (a.appName > b.appName) { return 1; }
|
|
||||||
// return 0;
|
|
||||||
// });
|
|
||||||
} else if (settings.sort === 'created') {
|
} else if (settings.sort === 'created') {
|
||||||
features = features.sort((a, b) => (
|
features = features.sort((a, b) => (
|
||||||
new Date(a.createdAt) > new Date(b.createdAt) ? -1 : 1
|
new Date(a.createdAt) > new Date(b.createdAt) ? -1 : 1
|
||||||
|
Loading…
Reference in New Issue
Block a user