1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-07-26 13:48:33 +02:00

Merge branch 'master' into add_sdk_version

This commit is contained in:
Ivar Conradi Østhus 2017-06-29 11:27:54 +02:00 committed by GitHub
commit 73a0ffccee
44 changed files with 1623 additions and 1661 deletions

View File

@ -67,13 +67,13 @@
"babel-preset-stage-0": "^6.5.0", "babel-preset-stage-0": "^6.5.0",
"babel-preset-stage-2": "^6.13.0", "babel-preset-stage-2": "^6.13.0",
"css-loader": "^0.25.0", "css-loader": "^0.25.0",
"eslint": "^3.4.0", "eslint": "^4.1.1",
"eslint-config-finn": "1.0.0-alpha.11", "eslint-config-finn": "^2.0.0",
"eslint-config-finn-react": "^1.0.0-alpha.2", "eslint-config-finn-react": "^2.0.0",
"eslint-plugin-react": "^6.2.0", "eslint-plugin-react": "^7.1.0",
"extract-text-webpack-plugin": "^1.0.1", "extract-text-webpack-plugin": "^1.0.1",
"identity-obj-proxy": "^3.0.0", "identity-obj-proxy": "^3.0.0",
"jest": "^19.0.2", "jest": "^20.0.4",
"node-sass": "~3.12.1", "node-sass": "~3.12.1",
"postcss-loader": "^0.13.0", "postcss-loader": "^0.13.0",
"react-test-renderer": "^15.4.2", "react-test-renderer": "^15.4.2",

View File

@ -146,42 +146,42 @@ export default class App extends Component {
</Navigation> </Navigation>
</Drawer> </Drawer>
<ScrollContainer scrollKey="container" shouldUpdateScroll={shouldUpdateScroll}> <ScrollContainer scrollKey="container" shouldUpdateScroll={shouldUpdateScroll}>
<Content className="mdl-color--grey-50"> <Content className="mdl-color--grey-50">
<Grid noSpacing className={styles.content}> <Grid noSpacing className={styles.content}>
<Cell col={12}> <Cell col={12}>
{this.props.children} {this.props.children}
<ErrorContainer /> <ErrorContainer />
</Cell> </Cell>
</Grid> </Grid>
<Footer size="mega"> <Footer size="mega">
<FooterSection type="middle"> <FooterSection type="middle">
<FooterDropDownSection title="Menu"> <FooterDropDownSection title="Menu">
<FooterLinkList>
{createListItem('/features', 'Feature Toggles')}
{createListItem('/strategies', 'Strategies')}
{createListItem('/history', 'Event History')}
{createListItem('/archive', 'Archived Toggles')}
{createListItem('/applications', 'Applications')}
</FooterLinkList>
</FooterDropDownSection>
<FooterDropDownSection title="Clients">
<FooterLinkList>
<a href="https://github.com/Unleash/unleash-client-node/">Node.js</a>
<a href="https://github.com/Unleash/unleash-client-java/">Java</a>
<a href="https://github.com/Unleash/unleash-client-go/">Go</a>
</FooterLinkList>
</FooterDropDownSection>
</FooterSection>
<FooterSection type="bottom" logo="Unleash">
<FooterLinkList> <FooterLinkList>
{createListItem('/features', 'Feature Toggles')} <a href="https://github.com/Unleash/unleash/" target="_blank">
{createListItem('/strategies', 'Strategies')}
{createListItem('/history', 'Event History')}
{createListItem('/archive', 'Archived Toggles')}
{createListItem('/applications', 'Applications')}
</FooterLinkList>
</FooterDropDownSection>
<FooterDropDownSection title="Clients">
<FooterLinkList>
<a href="https://github.com/Unleash/unleash-client-node/">Node.js</a>
<a href="https://github.com/Unleash/unleash-client-java/">Java</a>
<a href="https://github.com/Unleash/unleash-client-go/">Go</a>
</FooterLinkList>
</FooterDropDownSection>
</FooterSection>
<FooterSection type="bottom" logo="Unleash">
<FooterLinkList>
<a href="https://github.com/Unleash/unleash/" target="_blank">
GitHub GitHub
</a> </a>
<a href="https://finn.no" target="_blank"><small>A product by</small> FINN.no</a> <a href="https://finn.no" target="_blank"><small>A product by</small> FINN.no</a>
</FooterLinkList> </FooterLinkList>
</FooterSection> </FooterSection>
</Footer> </Footer>
</Content> </Content>
</ScrollContainer> </ScrollContainer>
</Layout> </Layout>
</div> </div>

View File

@ -69,25 +69,25 @@ class ClientApplications extends PureComponent {
<h6> Toggles</h6> <h6> Toggles</h6>
<hr /> <hr />
<List> <List>
{seenToggles.map(({ name, description, enabled, notFound }, i) => {seenToggles.map(({ name, description, enabled, notFound }, i) =>
(notFound ? (notFound ?
<ListItem twoLine key={i}> <ListItem twoLine key={i}>
<ListItemContent icon={'report'} subtitle={'Missing, want to create?'}> <ListItemContent icon={'report'} subtitle={'Missing, want to create?'}>
<Link to={`/features/create?name=${name}`}> <Link to={`/features/create?name=${name}`}>
{name} {name}
</Link> </Link>
</ListItemContent> </ListItemContent>
</ListItem> : </ListItem> :
<ListItem twoLine key={i}> <ListItem twoLine key={i}>
<ListItemContent <ListItemContent
icon={<span><Switch disabled checked={!!enabled} /></span>} icon={<span><Switch disabled checked={!!enabled} /></span>}
subtitle={shorten(description, 60)}> subtitle={shorten(description, 60)}>
<Link to={`/features/view/${name}`}> <Link to={`/features/view/${name}`}>
{shorten(name, 50)} {shorten(name, 50)}
</Link> </Link>
</ListItemContent> </ListItemContent>
</ListItem>) </ListItem>)
)} )}
</List> </List>
</Cell> </Cell>
<Cell col={6} tablet={4} phone={12}> <Cell col={6} tablet={4} phone={12}>
@ -96,20 +96,20 @@ class ClientApplications extends PureComponent {
<List> <List>
{strategies.map(({ name, description, notFound }, i) => ( {strategies.map(({ name, description, notFound }, i) => (
notFound ? notFound ?
<ListItem twoLine key={`${name}-${i}`}> <ListItem twoLine key={`${name}-${i}`}>
<ListItemContent icon={'report'} subtitle={'Missing, want to create?'}> <ListItemContent icon={'report'} subtitle={'Missing, want to create?'}>
<Link to={`/strategies/create?name=${name}`}> <Link to={`/strategies/create?name=${name}`}>
{name} {name}
</Link> </Link>
</ListItemContent> </ListItemContent>
</ListItem> : </ListItem> :
<ListItem twoLine key={`${name}-${i}`}> <ListItem twoLine key={`${name}-${i}`}>
<ListItemContent icon={'extension'} subtitle={shorten(description, 60)}> <ListItemContent icon={'extension'} subtitle={shorten(description, 60)}>
<Link to={`/strategies/view/${name}`}> <Link to={`/strategies/view/${name}`}>
{shorten(name, 50)} {shorten(name, 50)}
</Link> </Link>
</ListItemContent> </ListItemContent>
</ListItem> </ListItem>
))} ))}
</List> </List>
</Cell> </Cell>
@ -117,17 +117,17 @@ class ClientApplications extends PureComponent {
<h6>{instances.length} Instances registered</h6> <h6>{instances.length} Instances registered</h6>
<hr /> <hr />
<List> <List>
{instances.map(({ instanceId, clientIp, lastSeen, sdkVersion }, i) => ( {instances.map(({ instanceId, clientIp, lastSeen, sdkVersion }, i) => (
<ListItem key={i} twoLine> <ListItem key={i} twoLine>
<ListItemContent <ListItemContent
icon="timeline" icon="timeline"
subtitle={ subtitle={
<span>{clientIp} last seen at <small>{formatFullDateTime(lastSeen)}</small></span> <span>{clientIp} last seen at <small>{formatFullDateTime(lastSeen)}</small></span>
}> }>
{instanceId} {sdkVersion ? `(${sdkVersion})` : ''} {instanceId} {sdkVersion ? `(${sdkVersion})` : ''}
</ListItemContent> </ListItemContent>
</ListItem> </ListItem>
))} ))}
</List> </List>
</Cell> </Cell>
</Grid>) : ( </Grid>) : (

View File

@ -3,7 +3,6 @@ import { ProgressBar, Card } from 'react-mdl';
import { AppsLinkList, styles as commonStyles } from '../common'; import { AppsLinkList, styles as commonStyles } from '../common';
class ClientStrategies extends Component { class ClientStrategies extends Component {
componentDidMount () { componentDidMount () {
this.props.fetchAll(); this.props.fetchAll();
} }

View File

@ -17,24 +17,24 @@ class ArchiveList extends Component {
<Card shadow={0} className={commonStyles.fullwidth}> <Card shadow={0} className={commonStyles.fullwidth}>
{ {
archive.length > 0 ? archive.length > 0 ?
<div className={commonStyles.horisontalScroll}> <div className={commonStyles.horisontalScroll}>
<DataTable <DataTable
rows={archive} rows={archive}
className={commonStyles.fullwidth} className={commonStyles.fullwidth}
style={{ border: 0 }}> style={{ border: 0 }}>
<TableHeader style={{ width: '25px' }} name="reviveName" cellFormatter={(reviveName) => ( <TableHeader style={{ width: '25px' }} name="reviveName" cellFormatter={(reviveName) => (
<IconButton colored name="undo" onClick={() => revive(reviveName)} /> <IconButton colored name="undo" onClick={() => revive(reviveName)} />
)}>Revive</TableHeader> )}>Revive</TableHeader>
<TableHeader style={{ width: '25px' }} name="enabled" cellFormatter={(v) => (v ? 'Yes' : '-')}> <TableHeader style={{ width: '25px' }} name="enabled" cellFormatter={(v) => (v ? 'Yes' : '-')}>
Enabled</TableHeader> Enabled</TableHeader>
<TableHeader name="name">Toggle name</TableHeader> <TableHeader name="name">Toggle name</TableHeader>
<TableHeader numeric name="createdAt">Created</TableHeader> <TableHeader numeric name="createdAt">Created</TableHeader>
</DataTable> </DataTable>
</div> : </div> :
<div className={commonStyles.emptyState}> <div className={commonStyles.emptyState}>
<Icon name="archive" className="mdl-color-text--grey-300" style={{ fontSize: '56px' }}/><br /> <Icon name="archive" className="mdl-color-text--grey-300" style={{ fontSize: '56px' }}/><br />
No archived feature toggles, go see <Link to="/features">active toggles here</Link> No archived feature toggles, go see <Link to="/features">active toggles here</Link>
</div> </div>
} }
</Card> </Card>
); );

View File

@ -1,5 +1,5 @@
import React, { Component, PropTypes } from 'react'; import React, { Component, PropTypes } from 'react';
import { DataTable, TableHeader } from 'react-mdl'; import { DataTable, TableHeader } from 'react-mdl';
class ClientStrategies extends Component { class ClientStrategies extends Component {
static propTypes () { static propTypes () {

View File

@ -15,28 +15,28 @@ export const shorten = (str, len = 50) => (str && str.length > len ? `${str.subs
export const AppsLinkList = ({ apps }) => ( export const AppsLinkList = ({ apps }) => (
<List> <List>
{apps.length > 0 && apps.map(({ appName, description = '-', icon = 'apps' }) => ( {apps.length > 0 && apps.map(({ appName, description = '-', icon = 'apps' }) => (
<ListItem twoLine key={appName}> <ListItem twoLine key={appName}>
<span className="mdl-list__item-primary-content" style={{ minWidth: 0 }}> <span className="mdl-list__item-primary-content" style={{ minWidth: 0 }}>
<Icon name={icon} className="mdl-list__item-avatar"/> <Icon name={icon} className="mdl-list__item-avatar"/>
<Link to={`/applications/${appName}`} className={[styles.listLink, styles.truncate].join(' ')}> <Link to={`/applications/${appName}`} className={[styles.listLink, styles.truncate].join(' ')}>
{appName} {appName}
<span className={['mdl-list__item-sub-title', styles.truncate].join(' ')}>{description}</span> <span className={['mdl-list__item-sub-title', styles.truncate].join(' ')}>{description}</span>
</Link> </Link>
</span> </span>
</ListItem> </ListItem>
))} ))}
</List> </List>
); );
export const HeaderTitle = ({ title, actions, subtitle }) => ( export const HeaderTitle = ({ title, actions, subtitle }) => (
<div style={{ display: 'flex', borderBottom: '1px solid #f1f1f1', marginBottom: '10px', padding: '16px 20px ' }}> <div style={{ display: 'flex', borderBottom: '1px solid #f1f1f1', marginBottom: '10px', padding: '16px 20px ' }}>
<div style={{ flex: '2' }}> <div style={{ flex: '2' }}>
<h6 style={{ margin: 0 }}>{title}</h6> <h6 style={{ margin: 0 }}>{title}</h6>
{subtitle && <small>{subtitle}</small>} {subtitle && <small>{subtitle}</small>}
</div> </div>
{actions && <div style={{ flex: '1', textAlign: 'right' }}>{actions}</div>} {actions && <div style={{ flex: '1', textAlign: 'right' }}>{actions}</div>}
</div> </div>
); );
@ -78,15 +78,15 @@ export const SwitchWithLabel = ({ onChange, checked, children, ...switchProps })
export const TogglesLinkList = ({ toggles }) => ( export const TogglesLinkList = ({ toggles }) => (
<List style={{ textAlign: 'left' }} className={styles.truncate}> <List style={{ textAlign: 'left' }} className={styles.truncate}>
{toggles.length > 0 && toggles.map(({ name, description = '-', icon = 'toggle' }) => ( {toggles.length > 0 && toggles.map(({ name, description = '-', icon = 'toggle' }) => (
<ListItem twoLine key={name}> <ListItem twoLine key={name}>
<ListItemContent avatar={icon} subtitle={description}> <ListItemContent avatar={icon} subtitle={description}>
<Link key={name} to={`/features/view/${name}`}> <Link key={name} to={`/features/view/${name}`}>
{name} {name}
</Link> </Link>
</ListItemContent> </ListItemContent>
</ListItem> </ListItem>
))} ))}
</List> </List>
); );
@ -103,7 +103,7 @@ export function getIcon (type) {
export const IconLink = ({ url, icon }) => ( export const IconLink = ({ url, icon }) => (
<a href={url} target="_blank" rel="noopener" className="mdl-color-text--grey-600"> <a href={url} target="_blank" rel="noopener" className="mdl-color-text--grey-600">
<Icon name={icon}/> <Icon name={icon}/>
</a> </a>
); );

View File

@ -1,6 +1,6 @@
import React, { PropTypes } from 'react'; import React, { PropTypes } from 'react';
import { Snackbar, Icon } from 'react-mdl'; import { Snackbar, Icon } from 'react-mdl';
class ErrorComponent extends React.Component { class ErrorComponent extends React.Component {
static propTypes () { static propTypes () {

View File

@ -7,7 +7,7 @@ const mapDispatchToProps = {
muteError, muteError,
}; };
const mapStateToProps = (state) => ({ const mapStateToProps = (state) => ({
errors: state.error.get('list').toArray(), errors: state.error.get('list').toArray(),
}); });

View File

@ -27,7 +27,7 @@ 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.strategyChip} 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.strategyChip}>+{remainingStrategies}</Chip>; <Chip className={styles.strategyChip}>+{remainingStrategies}</Chip>;

View File

@ -58,7 +58,7 @@ const prepare = (methods, dispatch) => {
methods.validateName = (featureToggleName) => { methods.validateName = (featureToggleName) => {
validateName(featureToggleName) validateName(featureToggleName)
.then(() => methods.setValue('nameError', undefined)) .then(() => methods.setValue('nameError', undefined))
.catch((err) => methods.setValue('nameError', err.message)); .catch((err) => methods.setValue('nameError', err.message));
}; };

View File

@ -25,7 +25,7 @@ const mapStateToProps = createMapper({
}, },
}); });
const prepare = (methods, dispatch) => { const prepare = (methods, dispatch) => {
methods.onSubmit = (input) => ( methods.onSubmit = (input) => (
(e) => { (e) => {
e.preventDefault(); e.preventDefault();
@ -35,7 +35,7 @@ const prepare = (methods, dispatch) => {
delete s.id; delete s.id;
}); });
} }
// TODO: should add error handling // TODO: should add error handling
requestUpdateFeatureToggle(input)(dispatch) requestUpdateFeatureToggle(input)(dispatch)
.then(() => methods.clear()) .then(() => methods.clear())
.then(() => hashHistory.push(`/features/view/${input.name}`)); .then(() => hashHistory.push(`/features/view/${input.name}`));

View File

@ -13,7 +13,6 @@ const trim = (value) => {
}; };
class AddFeatureToggleComponent extends Component { class AddFeatureToggleComponent extends Component {
componentWillMount () { componentWillMount () {
// TODO unwind this stuff // TODO unwind this stuff
if (this.props.initCallRequired === true) { if (this.props.initCallRequired === true) {
@ -69,10 +68,10 @@ class AddFeatureToggleComponent extends Component {
{!editmode && <div> {!editmode && <div>
<br /> <br />
<Switch <Switch
checked={enabled} checked={enabled}
onChange={() => { onChange={() => {
setValue('enabled', !enabled); setValue('enabled', !enabled);
}}>Enabled</Switch> }}>Enabled</Switch>
<hr /> <hr />
</div>} </div>}
@ -92,7 +91,6 @@ class AddFeatureToggleComponent extends Component {
</form> </form>
); );
} }
}; };
AddFeatureToggleComponent.propTypes = { AddFeatureToggleComponent.propTypes = {

View File

@ -1,8 +1,7 @@
import React, { PropTypes } from 'react'; import React, { PropTypes } from 'react';
import { Menu, MenuItem, IconButton } from 'react-mdl'; import { Menu, MenuItem, IconButton } from 'react-mdl';
class AddStrategy extends React.Component { class AddStrategy extends React.Component {
static propTypes () { static propTypes () {
return { return {
strategies: PropTypes.array.isRequired, strategies: PropTypes.array.isRequired,

View File

@ -5,7 +5,6 @@ import HTML5Backend from 'react-dnd-html5-backend';
@DragDropContext(HTML5Backend) // eslint-disable-line new-cap @DragDropContext(HTML5Backend) // eslint-disable-line new-cap
class StrategiesList extends React.Component { class StrategiesList extends React.Component {
static propTypes () { static propTypes () {
return { return {
strategies: PropTypes.array.isRequired, strategies: PropTypes.array.isRequired,

View File

@ -5,7 +5,6 @@ import AddStrategy from './strategies-add';
import { HeaderTitle } from '../../common'; import { HeaderTitle } from '../../common';
class StrategiesSection extends React.Component { class StrategiesSection extends React.Component {
static propTypes () { static propTypes () {
return { return {
strategies: PropTypes.array.isRequired, strategies: PropTypes.array.isRequired,

View File

@ -45,7 +45,6 @@ const dragTarget = {
isDragging: monitor.isDragging(), isDragging: monitor.isDragging(),
})) }))
class StrategyConfigure extends React.Component { class StrategyConfigure extends React.Component {
static propTypes () { static propTypes () {
return { return {
strategy: PropTypes.object.isRequired, strategy: PropTypes.object.isRequired,
@ -122,7 +121,7 @@ class StrategyConfigure extends React.Component {
label={name} label={name}
onChange={this.handleConfigChange.bind(this, name)} onChange={this.handleConfigChange.bind(this, name)}
value={value} value={value}
/> />
{description && <p className={styles.helpText}>{description}</p>} {description && <p className={styles.helpText}>{description}</p>}
</div> </div>
); );
@ -138,7 +137,7 @@ class StrategyConfigure extends React.Component {
label={name} label={name}
onChange={this.handleConfigChange.bind(this, name)} onChange={this.handleConfigChange.bind(this, name)}
value={value} value={value}
/> />
{description && <p className={styles.helpText}>{description}</p>} {description && <p className={styles.helpText}>{description}</p>}
</div> </div>
); );
@ -165,8 +164,8 @@ class StrategyConfigure extends React.Component {
</CardText> </CardText>
{ {
inputFields && <CardActions border style={{ padding: '20px' }}> inputFields && <CardActions border style={{ padding: '20px' }}>
{inputFields} {inputFields}
</CardActions> </CardActions>
} }
<CardMenu className="mdl-color-text--white"> <CardMenu className="mdl-color-text--white">

View File

@ -3,10 +3,9 @@ import {
Textfield, Textfield,
IconButton, IconButton,
Chip, Chip,
} from 'react-mdl'; } from 'react-mdl';
export default class InputList extends Component { export default class InputList extends Component {
static propTypes = { static propTypes = {
name: PropTypes.string.isRequired, name: PropTypes.string.isRequired,
list: PropTypes.array.isRequired, list: PropTypes.array.isRequired,
@ -18,7 +17,7 @@ export default class InputList extends Component {
window.removeEventListener('keydown', this.onKeyHandler, false); window.removeEventListener('keydown', this.onKeyHandler, false);
} }
onFocus = (e) => { onFocus = (e) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
window.addEventListener('keydown', this.onKeyHandler, false); window.addEventListener('keydown', this.onKeyHandler, false);

View File

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { Slider } from 'react-mdl'; import { Slider } from 'react-mdl';
const labelStyle = { const labelStyle = {
margin: '20px 0', margin: '20px 0',

View File

@ -7,7 +7,6 @@ import { MenuItemWithIcon, DropdownButton, styles as commonStyles } from '../com
import styles from './feature.scss'; import styles from './feature.scss';
export default class FeatureListComponent extends React.PureComponent { export default class FeatureListComponent extends React.PureComponent {
static propTypes () { static propTypes () {
return { return {
toggleFeature: PropTypes.func.isRequired, toggleFeature: PropTypes.func.isRequired,
@ -89,16 +88,16 @@ export default class FeatureListComponent extends React.PureComponent {
<hr/> <hr/>
<List> <List>
{features.map((feature, i) => {features.map((feature, i) =>
<Feature key={i} (<Feature key={i}
settings={settings} settings={settings}
metricsLastHour={featureMetrics.lastHour[feature.name]} metricsLastHour={featureMetrics.lastHour[feature.name]}
metricsLastMinute={featureMetrics.lastMinute[feature.name]} metricsLastMinute={featureMetrics.lastMinute[feature.name]}
feature={feature} feature={feature}
toggleFeature={toggleFeature}/> toggleFeature={toggleFeature}/>)
)} )}
</List> </List>
</Card> </Card>
</div> </div>
); );
} }
} }

View File

@ -14,7 +14,7 @@ const mapStateToProps = (state) => {
const regex = new RegExp(settings.filter, 'i'); const regex = new RegExp(settings.filter, 'i');
features = features.filter(feature => features = features.filter(feature =>
( (
regex.test(feature.name) || regex.test(feature.name) ||
regex.test(feature.description) || regex.test(feature.description) ||
feature.strategies.some(s => s && s.name && regex.test(s.name)) feature.strategies.some(s => s && s.name && regex.test(s.name))
) )
@ -72,8 +72,8 @@ const mapDispatchToProps = {
}; };
const FeatureListContainer = connect( const FeatureListContainer = connect(
mapStateToProps, mapStateToProps,
mapDispatchToProps mapDispatchToProps
)(FeatureListComponent); )(FeatureListComponent);
export default FeatureListContainer; export default FeatureListContainer;

View File

@ -18,7 +18,7 @@ const StrategyChipItem = ({ strategy }) => (
// TODO what about "missing" strategies here? // TODO what about "missing" strategies here?
const StrategiesList = ({ strategies }) => ( const StrategiesList = ({ strategies }) => (
<div style={{ verticalAlign: 'middle', paddingTop: '14px' }}>With {strategies.length > 1 ? 'strategies' : 'strategy'} { <div style={{ verticalAlign: 'middle', paddingTop: '14px' }}>With {strategies.length > 1 ? 'strategies' : 'strategy'} {
strategies.map((strategy, i) => <StrategyChipItem key={i} strategy={strategy} />) strategies.map((strategy, i) => <StrategyChipItem key={i} strategy={strategy} />)
}</div> }</div>
); );

View File

@ -58,7 +58,7 @@ class Progress extends Component {
const current = this.state.percentageText; const current = this.state.percentageText;
targetState.cyclesCounter --; targetState.cyclesCounter--;
if (targetState.cyclesCounter <= 0) { if (targetState.cyclesCounter <= 0) {
this.setState({ percentageText: targetState.target }); this.setState({ percentageText: targetState.target });
return; return;
@ -99,7 +99,7 @@ class Progress extends Component {
return (isFallback ? return (isFallback ?
<svg viewBox="0 0 24 24" className="mdl-color-text--grey-300">{ <svg viewBox="0 0 24 24" className="mdl-color-text--grey-300">{
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
}<path fill="currentColor" d="M17.3,18C19,16.5 20,14.4 20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12C4,14.4 5,16.5 6.7,18C8.2,16.7 10,16 12,16C14,16 15.9,16.7 17.3,18M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M7,9A1,1 0 0,1 8,10A1,1 0 0,1 7,11A1,1 0 0,1 6,10A1,1 0 0,1 7,9M10,6A1,1 0 0,1 11,7A1,1 0 0,1 10,8A1,1 0 0,1 9,7A1,1 0 0,1 10,6M17,9A1,1 0 0,1 18,10A1,1 0 0,1 17,11A1,1 0 0,1 16,10A1,1 0 0,1 17,9M14.4,6.1C14.9,6.3 15.1,6.9 15,7.4L13.6,10.8C13.8,11.1 14,11.5 14,12A2,2 0 0,1 12,14A2,2 0 0,1 10,12C10,11 10.7,10.1 11.7,10L13.1,6.7C13.3,6.1 13.9,5.9 14.4,6.1Z" /> }<path fill="currentColor" d="M17.3,18C19,16.5 20,14.4 20,12A8,8 0 0,0 12,4A8,8 0 0,0 4,12C4,14.4 5,16.5 6.7,18C8.2,16.7 10,16 12,16C14,16 15.9,16.7 17.3,18M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M7,9A1,1 0 0,1 8,10A1,1 0 0,1 7,11A1,1 0 0,1 6,10A1,1 0 0,1 7,9M10,6A1,1 0 0,1 11,7A1,1 0 0,1 10,8A1,1 0 0,1 9,7A1,1 0 0,1 10,6M17,9A1,1 0 0,1 18,10A1,1 0 0,1 17,11A1,1 0 0,1 16,10A1,1 0 0,1 17,9M14.4,6.1C14.9,6.3 15.1,6.9 15,7.4L13.6,10.8C13.8,11.1 14,11.5 14,12A2,2 0 0,1 12,14A2,2 0 0,1 10,12C10,11 10.7,10.1 11.7,10L13.1,6.7C13.3,6.1 13.9,5.9 14.4,6.1Z" />
</svg> : </svg> :
<svg viewBox="0 0 100 100"> <svg viewBox="0 0 100 100">
<path <path
@ -107,7 +107,7 @@ class Progress extends Component {
d={pathDescription} d={pathDescription}
strokeWidth={strokeWidth} strokeWidth={strokeWidth}
fillOpacity={0} fillOpacity={0}
/> />
<path <path
className={[styles.path, colorClassName].join(' ')} className={[styles.path, colorClassName].join(' ')}
@ -115,13 +115,13 @@ class Progress extends Component {
strokeWidth={strokeWidth} strokeWidth={strokeWidth}
fillOpacity={0} fillOpacity={0}
style={progressStyle} style={progressStyle}
/> />
<text <text
className={styles.text} className={styles.text}
x={50} x={50}
y={50} y={50}
>{this.state.percentageText}%</text> >{this.state.percentageText}%</text>
</svg> </svg>
); );
} }

View File

@ -14,7 +14,6 @@ const TABS = {
}; };
export default class ViewFeatureToggleComponent extends React.Component { export default class ViewFeatureToggleComponent extends React.Component {
constructor (props) { constructor (props) {
super(props); super(props);
} }
@ -73,7 +72,7 @@ export default class ViewFeatureToggleComponent extends React.Component {
return ( return (
<span> <span>
Could not find the toggle <Link to={{ pathname: '/features/create', query: { name: featureToggleName } }}> Could not find the toggle <Link to={{ pathname: '/features/create', query: { name: featureToggleName } }}>
{featureToggleName}</Link> {featureToggleName}</Link>
</span> </span>
); );
} }
@ -82,7 +81,7 @@ export default class ViewFeatureToggleComponent extends React.Component {
const tabContent = this.getTabContent(activeTab); const tabContent = this.getTabContent(activeTab);
const removeToggle = () => { const removeToggle = () => {
if (window.confirm('Are you sure you want to remove this toggle?')) { // eslint-disable-line no-alert if (window.confirm('Are you sure you want to remove this toggle?')) { // eslint-disable-line no-alert
removeFeatureToggle(featureToggle.name); removeFeatureToggle(featureToggle.name);
hashHistory.push('/features'); hashHistory.push('/features');
} }

View File

@ -4,7 +4,6 @@ import HistoryList from './history-list-container';
import { styles as commonStyles } from '../common'; import { styles as commonStyles } from '../common';
class History extends PureComponent { class History extends PureComponent {
componentDidMount () { componentDidMount () {
this.props.fetchHistory(); this.props.fetchHistory();
} }

View File

@ -50,7 +50,7 @@ function buildDiff (diff, idx) {
); );
} else { } else {
const spadenClass = KLASSES[diff.kind]; const spadenClass = KLASSES[diff.kind];
const prefix = DIFF_PREFIXES[diff.kind]; const prefix = DIFF_PREFIXES[diff.kind];
change = (<div className={spadenClass}>{prefix} {key}: {JSON.stringify(diff.rhs || diff.item)}</div>); change = (<div className={spadenClass}>{prefix} {key}: {JSON.stringify(diff.rhs || diff.item)}</div>);
} }
@ -59,7 +59,6 @@ function buildDiff (diff, idx) {
} }
class HistoryItem extends PureComponent { class HistoryItem extends PureComponent {
static propTypes () { static propTypes () {
return { return {
entry: PropTypes.object, entry: PropTypes.object,

View File

@ -3,7 +3,6 @@ import React, { PropTypes, PureComponent } from 'react';
import style from './history.scss'; import style from './history.scss';
class HistoryItem extends PureComponent { class HistoryItem extends PureComponent {
static propTypes () { static propTypes () {
return { return {
entry: PropTypes.object, entry: PropTypes.object,

View File

@ -8,7 +8,6 @@ import { formatFullDateTime } from '../common/util';
import styles from './history.scss'; import styles from './history.scss';
class HistoryList extends Component { class HistoryList extends Component {
toggleShowDiff () { toggleShowDiff () {
this.props.updateSetting('showData', !this.props.settings.showData); this.props.updateSetting('showData', !this.props.settings.showData);
} }
@ -27,18 +26,18 @@ class HistoryList extends Component {
let entries; let entries;
if (showData) { if (showData) {
entries = history.map((entry) => <HistoryItemJson key={`log${entry.id}`} entry={entry} />); entries = history.map((entry) => <HistoryItemJson key={`log${entry.id}`} entry={entry} />);
} else { } else {
entries = (<Table entries = (<Table
sortable sortable
rows={ rows={
history.map((entry) => Object.assign({ history.map((entry) => Object.assign({
diff: (<HistoryItemDiff entry={entry} />), diff: (<HistoryItemDiff entry={entry} />),
}, entry)) }, entry))
} }
className={commonStyles.fullwidth} className={commonStyles.fullwidth}
style={{ border: 0, tableLayout: 'fixed', minWidth: '840px' }} style={{ border: 0, tableLayout: 'fixed', minWidth: '840px' }}
> >
<TableHeader name="type" cellFormatter={truncateTableCell} style={{ width: '136px' }}>Type</TableHeader> <TableHeader name="type" cellFormatter={truncateTableCell} style={{ width: '136px' }}>Type</TableHeader>
<TableHeader name="createdBy" cellFormatter={truncateTableCell} style={{ width: '115px' }}>User</TableHeader> <TableHeader name="createdBy" cellFormatter={truncateTableCell} style={{ width: '115px' }}>User</TableHeader>
<TableHeader name="diff">Diff</TableHeader> <TableHeader name="diff">Diff</TableHeader>

View File

@ -2,7 +2,6 @@ import React, { Component, PropTypes } from 'react';
import HistoryList from './history-list-container'; import HistoryList from './history-list-container';
class HistoryListToggle extends Component { class HistoryListToggle extends Component {
static propTypes () { static propTypes () {
return { return {
toggleName: PropTypes.string.isRequired, toggleName: PropTypes.string.isRequired,
@ -19,9 +18,9 @@ class HistoryListToggle extends Component {
} }
const { history } = this.props; const { history } = this.props;
return ( return (
<HistoryList <HistoryList
history={history} history={history}
title="Change log"/> title="Change log"/>
); );
} }
} }

View File

@ -17,7 +17,7 @@ function gerArrayWithEntries (num) {
} }
const Parameter = ({ set, input = {}, index }) => ( const Parameter = ({ set, input = {}, index }) => (
<div style={{ background: '#f1f1f1', padding: '16px 20px', marginBottom: '20px' }}> <div style={{ background: '#f1f1f1', padding: '16px 20px', marginBottom: '20px' }}>
<Textfield <Textfield
style={{ width: '50%' }} style={{ width: '50%' }}
floatingLabel floatingLabel
@ -65,7 +65,7 @@ const Parameter = ({ set, input = {}, index }) => (
const EditHeader = () => ( const EditHeader = () => (
<div> <div>
<h4 style={{ marginTop: '16px' }}>Edit strategy</h4> <h4 style={{ marginTop: '16px' }}>Edit strategy</h4>
<p style={{ background: '#ffb7b7', padding: '16px 20px' }}> <p style={{ background: '#ffb7b7', padding: '16px 20px' }}>
Be carefull! Changing a strategy definition might also require changes to the Be carefull! Changing a strategy definition might also require changes to the
implementation in the clients. implementation in the clients.
</p> </p>
@ -80,18 +80,17 @@ const CreateHeader = () => (
const Parameters = ({ input = [], count = 0, updateInList }) => ( const Parameters = ({ input = [], count = 0, updateInList }) => (
<div>{ <div>{
gerArrayWithEntries(count) gerArrayWithEntries(count)
.map((v, i) => <Parameter .map((v, i) => (<Parameter
key={i} key={i}
set={(v) => updateInList('parameters', i, v, true)} set={(v) => updateInList('parameters', i, v, true)}
index={i} index={i}
input={input[i]} input={input[i]}
/>) />))
}</div>); }</div>);
class AddStrategy extends Component { class AddStrategy extends Component {
static propTypes () { static propTypes () {
return { return {
input: PropTypes.object, input: PropTypes.object,
@ -142,7 +141,7 @@ class AddStrategy extends Component {
pattern="^[0-9a-zA-Z\.\-]+$" pattern="^[0-9a-zA-Z\.\-]+$"
onChange={({ target }) => setValue('name', trim(target.value))} onChange={({ target }) => setValue('name', trim(target.value))}
value={input.name} value={input.name}
/> />
<br /> <br />
<Textfield <Textfield
floatingLabel floatingLabel
@ -152,7 +151,7 @@ class AddStrategy extends Component {
name="description" name="description"
onChange={({ target }) => setValue('description', target.value)} onChange={({ target }) => setValue('description', target.value)}
value={input.description} value={input.description}
/> />
<Parameters input={input.parameters} count={input._params} updateInList={updateInList} /> <Parameters input={input.parameters} count={input._params} updateInList={updateInList} />

View File

@ -5,7 +5,6 @@ import { List, ListItem, ListItemContent, IconButton, Grid, Cell } from 'react-m
import { HeaderTitle } from '../common'; import { HeaderTitle } from '../common';
class StrategiesListComponent extends Component { class StrategiesListComponent extends Component {
static contextTypes = { static contextTypes = {
router: React.PropTypes.object, router: React.PropTypes.object,
} }
@ -28,15 +27,19 @@ class StrategiesListComponent extends Component {
title="Add new strategy" />} /> title="Add new strategy" />} />
<List> <List>
{strategies.length > 0 ? strategies.map((strategy, i) => ( {strategies.length > 0 ? strategies.map((strategy, i) => (
<ListItem key={i} twoLine> <ListItem key={i} twoLine>
<ListItemContent icon="extension" subtitle={strategy.description}> <ListItemContent icon="extension" subtitle={strategy.description}>
<Link to={`/strategies/view/${strategy.name}`}> <Link to={`/strategies/view/${strategy.name}`}>
<strong>{strategy.name}</strong> <strong>{strategy.name}</strong>
</Link> </Link>
</ListItemContent> </ListItemContent>
<IconButton name="delete" onClick={() => removeStrategy(strategy)} /> {
</ListItem> strategy.editable === false ?
)) : <ListItem>No entries</ListItem>} '' :
<IconButton name="delete" onClick={() => removeStrategy(strategy)} />
}
</ListItem>
)) : <ListItem>No entries</ListItem>}
</List> </List>
</Cell> </Cell>
</Grid> </Grid>

View File

@ -12,7 +12,7 @@ const mapStateToProps = (state) => {
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
removeStrategy: (strategy) => { removeStrategy: (strategy) => {
if (window.confirm('Are you sure you want to remove this strategy?')) { // eslint-disable-line no-alert if (window.confirm('Are you sure you want to remove this strategy?')) { // eslint-disable-line no-alert
removeStrategy(strategy)(dispatch); removeStrategy(strategy)(dispatch);
} }
}, },

View File

@ -60,21 +60,24 @@ export default class StrategyDetails extends Component {
const tabContent = this.getTabContent(activeTabId); const tabContent = this.getTabContent(activeTabId);
return ( return (<Grid className="mdl-color--white">
<Grid className="mdl-color--white"> <Cell col={12}>
<Cell col={12}> <HeaderTitle title={strategy.name} subtitle={strategy.description} />
<HeaderTitle title={strategy.name} subtitle={strategy.description} /> {strategy.editable === false ? '' : <Tabs activeTab={activeTabId} ripple>
<Tabs activeTab={activeTabId} ripple> <Tab onClick={() => this.goToTab('view')}>
<Tab onClick={() => this.goToTab('view')}>Details</Tab> Details
<Tab onClick={() => this.goToTab('edit')}>Edit</Tab> </Tab>
</Tabs> <Tab onClick={() => this.goToTab('edit')}>
<section> Edit
<div className="content"> </Tab>
{tabContent} </Tabs>}
</div>
</section> <section>
</Cell> <div className="content">
</Grid> {tabContent}
); </div>
</section>
</Cell>
</Grid>);
} }
} }

View File

@ -7,7 +7,7 @@ const mapDispatchToProps = {
openEdit, openEdit,
}; };
const mapStateToProps = (state) => ({ const mapStateToProps = (state) => ({
user: state.user.toJS(), user: state.user.toJS(),
}); });

View File

@ -8,7 +8,7 @@ const mapDispatchToProps = {
save, save,
}; };
const mapStateToProps = (state) => ({ const mapStateToProps = (state) => ({
user: state.user.toJS(), user: state.user.toJS(),
}); });

View File

@ -55,7 +55,7 @@ function toggle (name) {
headers, headers,
credentials: 'include', credentials: 'include',
}) })
.then(throwIfNotSuccess); .then(throwIfNotSuccess);
} }
function remove (featureToggleName) { function remove (featureToggleName) {

View File

@ -61,7 +61,7 @@ ReactDOM.render(
</Route> </Route>
<Route pageTitle="Event History" link="/history"> <Route pageTitle="Event History" link="/history">
<Route pageTitle="Event history" path="/history" component={HistoryPage} /> <Route pageTitle="Event history" path="/history" component={HistoryPage} />
<Route pageTitle=":toggleName" path="/history/:toggleName" component={HistoryTogglePage} /> <Route pageTitle=":toggleName" path="/history/:toggleName" component={HistoryTogglePage} />
</Route> </Route>

View File

@ -1,8 +1,8 @@
import api from '../data/archive-api'; import api from '../data/archive-api';
export const REVIVE_TOGGLE = 'REVIVE_TOGGLE'; export const REVIVE_TOGGLE = 'REVIVE_TOGGLE';
export const RECEIVE_ARCHIVE = 'RECEIVE_ARCHIVE'; export const RECEIVE_ARCHIVE = 'RECEIVE_ARCHIVE';
export const ERROR_RECEIVE_ARCHIVE = 'ERROR_RECEIVE_ARCHIVE'; export const ERROR_RECEIVE_ARCHIVE = 'ERROR_RECEIVE_ARCHIVE';
const receiveArchive = (json) => ({ const receiveArchive = (json) => ({
type: RECEIVE_ARCHIVE, type: RECEIVE_ARCHIVE,

View File

@ -1,19 +1,19 @@
import api from '../data/feature-api'; import api from '../data/feature-api';
const debug = require('debug')('unleash:feature-actions'); const debug = require('debug')('unleash:feature-actions');
export const ADD_FEATURE_TOGGLE = 'ADD_FEATURE_TOGGLE'; export const ADD_FEATURE_TOGGLE = 'ADD_FEATURE_TOGGLE';
export const REMOVE_FEATURE_TOGGLE = 'REMOVE_FEATURE_TOGGLE'; export const REMOVE_FEATURE_TOGGLE = 'REMOVE_FEATURE_TOGGLE';
export const UPDATE_FEATURE_TOGGLE = 'UPDATE_FEATURE_TOGGLE'; export const UPDATE_FEATURE_TOGGLE = 'UPDATE_FEATURE_TOGGLE';
export const TOGGLE_FEATURE_TOGGLE = 'TOGGLE_FEATURE_TOGGLE'; export const TOGGLE_FEATURE_TOGGLE = 'TOGGLE_FEATURE_TOGGLE';
export const START_FETCH_FEATURE_TOGGLES = 'START_FETCH_FEATURE_TOGGLES'; export const START_FETCH_FEATURE_TOGGLES = 'START_FETCH_FEATURE_TOGGLES';
export const START_UPDATE_FEATURE_TOGGLE = 'START_UPDATE_FEATURE_TOGGLE'; export const START_UPDATE_FEATURE_TOGGLE = 'START_UPDATE_FEATURE_TOGGLE';
export const START_CREATE_FEATURE_TOGGLE = 'START_CREATE_FEATURE_TOGGLE'; export const START_CREATE_FEATURE_TOGGLE = 'START_CREATE_FEATURE_TOGGLE';
export const START_REMOVE_FEATURE_TOGGLE = 'START_REMOVE_FEATURE_TOGGLE'; export const START_REMOVE_FEATURE_TOGGLE = 'START_REMOVE_FEATURE_TOGGLE';
export const RECEIVE_FEATURE_TOGGLES = 'RECEIVE_FEATURE_TOGGLES'; export const RECEIVE_FEATURE_TOGGLES = 'RECEIVE_FEATURE_TOGGLES';
export const ERROR_FETCH_FEATURE_TOGGLES = 'ERROR_FETCH_FEATURE_TOGGLES'; export const ERROR_FETCH_FEATURE_TOGGLES = 'ERROR_FETCH_FEATURE_TOGGLES';
export const ERROR_CREATING_FEATURE_TOGGLE = 'ERROR_CREATING_FEATURE_TOGGLE'; export const ERROR_CREATING_FEATURE_TOGGLE = 'ERROR_CREATING_FEATURE_TOGGLE';
export const ERROR_UPDATE_FEATURE_TOGGLE = 'ERROR_UPDATE_FEATURE_TOGGLE'; export const ERROR_UPDATE_FEATURE_TOGGLE = 'ERROR_UPDATE_FEATURE_TOGGLE';
export const ERROR_REMOVE_FEATURE_TOGGLE = 'ERROR_REMOVE_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); debug('Toggle feature toggle ', name);

View File

@ -1,12 +1,12 @@
import api from '../data/feature-metrics-api'; import api from '../data/feature-metrics-api';
export const START_FETCH_FEATURE_METRICS = 'START_FETCH_FEATURE_METRICS'; export const START_FETCH_FEATURE_METRICS = 'START_FETCH_FEATURE_METRICS';
export const RECEIVE_FEATURE_METRICS = 'RECEIVE_FEATURE_METRICS'; export const RECEIVE_FEATURE_METRICS = 'RECEIVE_FEATURE_METRICS';
export const ERROR_FETCH_FEATURE_TOGGLES = 'ERROR_FETCH_FEATURE_TOGGLES'; export const ERROR_FETCH_FEATURE_TOGGLES = 'ERROR_FETCH_FEATURE_TOGGLES';
export const START_FETCH_SEEN_APP = 'START_FETCH_SEEN_APP'; export const START_FETCH_SEEN_APP = 'START_FETCH_SEEN_APP';
export const RECEIVE_SEEN_APPS = 'RECEIVE_SEEN_APPS'; export const RECEIVE_SEEN_APPS = 'RECEIVE_SEEN_APPS';
export const ERROR_FETCH_SEEN_APP = 'ERROR_FETCH_SEEN_APP'; export const ERROR_FETCH_SEEN_APP = 'ERROR_FETCH_SEEN_APP';
function receiveFeatureMetrics (json) { function receiveFeatureMetrics (json) {
return { return {

View File

@ -1,9 +1,9 @@
import api from '../data/history-api'; import api from '../data/history-api';
export const RECEIVE_HISTORY = 'RECEIVE_HISTORY'; export const RECEIVE_HISTORY = 'RECEIVE_HISTORY';
export const ERROR_RECEIVE_HISTORY = 'ERROR_RECEIVE_HISTORY'; export const ERROR_RECEIVE_HISTORY = 'ERROR_RECEIVE_HISTORY';
export const RECEIVE_HISTORY_FOR_TOGGLE = 'RECEIVE_HISTORY_FOR_TOGGLE'; export const RECEIVE_HISTORY_FOR_TOGGLE = 'RECEIVE_HISTORY_FOR_TOGGLE';
const receiveHistory = (json) => ({ const receiveHistory = (json) => ({
type: RECEIVE_HISTORY, type: RECEIVE_HISTORY,

View File

@ -31,7 +31,7 @@ function setKeyValue (state, { id, key, value }) {
function increment (state, { id, key }) { function increment (state, { id, key }) {
state = assertId(state, id); state = assertId(state, id);
return state.updateIn(id.concat([key]), (value = 0) => value + 1); return state.updateIn(id.concat([key]), (value = 0) => value + 1);
} }
function clear (state, { id }) { function clear (state, { id }) {

View File

@ -78,8 +78,8 @@ export function updateStrategy (strategy) {
export function removeStrategy (strategy) { export function removeStrategy (strategy) {
return dispatch => api.remove(strategy) return dispatch => api.remove(strategy)
.then(() => dispatch(createRemoveStrategy(strategy))) .then(() => dispatch(createRemoveStrategy(strategy)))
.catch(error => dispatch(errorCreatingStrategy(error))); .catch(error => dispatch(errorCreatingStrategy(error)));
} }
export function getApplicationsWithStrategy (strategyName) { export function getApplicationsWithStrategy (strategyName) {

File diff suppressed because it is too large Load Diff