feat(joyent-cp-frontend): create dg initial implementation

This commit is contained in:
Sérgio Ramos 2017-06-09 05:26:25 +01:00 committed by Judit Greskovits
parent b5a8a8e23d
commit 1cc2330b22
46 changed files with 8542 additions and 1096 deletions

View File

@ -34,8 +34,6 @@ Gruntfile.js
.flowconfig .flowconfig
.documentup.json .documentup.json
.yarn-metadata.json .yarn-metadata.json
.*.yml
*.yml
# misc # misc
*.gz *.gz

View File

@ -2,6 +2,8 @@
"lerna": "2.0.0-rc.4", "lerna": "2.0.0-rc.4",
"version": "independent", "version": "independent",
"npmClient": "yarn", "npmClient": "yarn",
"hoist": true,
"nohoist": ["graphi"],
"packages": [ "packages": [
"packages/*" "packages/*"
] ]

View File

@ -46,7 +46,7 @@
"conventional-changelog-lint": "^1.1.9", "conventional-changelog-lint": "^1.1.9",
"conventional-changelog-lint-config-angular": "^0.4.1", "conventional-changelog-lint-config-angular": "^0.4.1",
"conventional-changelog-lint-config-lerna-scopes": "^1.0.0", "conventional-changelog-lint-config-lerna-scopes": "^1.0.0",
"cross-env": "^5.0.0", "cross-env": "^5.0.1",
"dotenv": "^4.0.0", "dotenv": "^4.0.0",
"eslint": "^3.19.0", "eslint": "^3.19.0",
"eslint-config-prettier": "^2.1.1", "eslint-config-prettier": "^2.1.1",
@ -58,11 +58,11 @@
"eslint-plugin-prettier": "^2.1.1", "eslint-plugin-prettier": "^2.1.1",
"eslint-plugin-react": "^7.0.1", "eslint-plugin-react": "^7.0.1",
"eslint-tap": "^2.0.1", "eslint-tap": "^2.0.1",
"execa": "^0.6.3", "execa": "^0.7.0",
"figures": "^2.0.0", "figures": "^2.0.0",
"force-array": "^3.1.0", "force-array": "^3.1.0",
"husky": "^0.13.4", "husky": "^0.13.4",
"inquirer": "^3.0.6", "inquirer": "^3.1.0",
"lerna": "^2.0.0-rc.5", "lerna": "^2.0.0-rc.5",
"lerna-wizard": "ramitos/lerna-wizard#7bcdc11", "lerna-wizard": "ramitos/lerna-wizard#7bcdc11",
"license-to-fail": "^2.2.0", "license-to-fail": "^2.2.0",
@ -71,9 +71,7 @@
"lodash.isstring": "^4.0.1", "lodash.isstring": "^4.0.1",
"lodash.uniq": "^4.5.0", "lodash.uniq": "^4.5.0",
"lodash.uniqby": "^4.7.0", "lodash.uniqby": "^4.7.0",
"npm-check-updates": "^2.11.2", "prettier": "1.4.4",
"per-env": "^1.0.2",
"prettier": "1.3.1",
"quality-docs": "^3.3.0", "quality-docs": "^3.3.0",
"read-pkg": "^2.0.0", "read-pkg": "^2.0.0",
"redrun": "^5.9.14", "redrun": "^5.9.14",

View File

@ -15,11 +15,11 @@
"dependencies": { "dependencies": {
"bunyan": "^1.8.10", "bunyan": "^1.8.10",
"dotenv": "^4.0.0", "dotenv": "^4.0.0",
"express": "^4.15.2", "express": "^4.15.3",
"express-graphql": "^0.6.4", "express-graphql": "^0.6.6",
"got": "^6.7.1", "got": "^7.0.0",
"graphql": "^0.9.3", "graphql": "^0.10.1",
"smartdc-auth": "^2.5.2", "smartdc-auth": "^2.5.5",
"triton": "^5.2.0" "triton": "^5.2.0"
}, },
"devDependencies": { "devDependencies": {

View File

@ -20,37 +20,44 @@
}, },
"dependencies": { "dependencies": {
"apollo": "^0.2.2", "apollo": "^0.2.2",
"apr-intercept": "^1.0.4",
"constant-case": "^2.0.0", "constant-case": "^2.0.0",
"graphql-tag": "^2.0.0", "graphql-tag": "^2.2.2",
"joyent-manifest-editor": "^1.0.0",
"joyent-ui-toolkit": "^1.1.0", "joyent-ui-toolkit": "^1.1.0",
"lodash.isstring": "^4.0.1", "lodash.isstring": "^4.0.1",
"normalized-styled-components": "^1.0.5", "normalized-styled-components": "^1.0.8",
"param-case": "^2.1.1",
"prop-types": "^15.5.10", "prop-types": "^15.5.10",
"react": "^15.5.4", "react": "^15.5.4",
"react-apollo": "^1.2.0", "react-apollo": "^1.4.2",
"react-bundle": "^1.0.3",
"react-codemirror": "^1.0.0",
"react-dom": "^15.5.4", "react-dom": "^15.5.4",
"react-redux": "^5.0.4", "react-redux": "^5.0.5",
"react-router": "^4.1.1", "react-router": "^4.1.1",
"react-router-dom": "^4.1.1", "react-router-dom": "^4.1.1",
"react-styled-flexboxgrid": "^1.1.2", "react-styled-flexboxgrid": "^2.0.0",
"redux": "^3.6.0", "redux": "^3.6.0",
"redux-actions": "^2.0.3", "redux-actions": "^2.0.3",
"redux-batched-actions": "^0.2.0", "redux-batched-actions": "^0.2.0",
"redux-form": "^6.7.0", "redux-form": "^6.8.0",
"remcalc": "^1.0.5", "remcalc": "^1.0.8",
"reselect": "^3.0.1", "reselect": "^3.0.1",
"simple-statistics": "^4.1.0", "simple-statistics": "^4.1.0",
"styled-components": "^2.0.0", "styled-components": "^2.0.1",
"styled-is": "^1.0.7", "styled-is": "^1.0.11",
"unitcalc": "^1.0.5" "styled-text-spinners": "^1.0.1",
"unitcalc": "^1.0.8"
}, },
"devDependencies": { "devDependencies": {
"apr-find": "^1.0.5",
"apr-for-each": "^1.0.6", "apr-for-each": "^1.0.6",
"apr-main": "^1.0.7", "apr-main": "^1.0.7",
"babel-plugin-inline-react-svg": "^0.4.0", "babel-plugin-inline-react-svg": "^0.4.0",
"babel-plugin-styled-components": "^1.1.4", "babel-plugin-styled-components": "^1.1.4",
"babel-preset-joyent-portal": "^1.0.0", "babel-preset-joyent-portal": "^1.0.3",
"cross-env": "^5.0.0", "cross-env": "^5.0.1",
"eslint": "^3.19.0", "eslint": "^3.19.0",
"eslint-config-joyent-portal": "1.0.0", "eslint-config-joyent-portal": "1.0.0",
"jest": "^20.0.4", "jest": "^20.0.4",
@ -62,12 +69,12 @@
"jest-snapshot": "^20.0.3", "jest-snapshot": "^20.0.3",
"jest-styled-components": "^3.0.0-2", "jest-styled-components": "^3.0.0-2",
"mz": "^2.6.0", "mz": "^2.6.0",
"react-scripts": "^1.0.0", "react-scripts": "^1.0.7",
"react-test-renderer": "^15.5.4", "react-test-renderer": "^15.5.4",
"redrun": "^5.9.14", "redrun": "^5.9.14",
"stylelint": "^7.10.1", "stylelint": "^7.11.0",
"stylelint-config-primer": "^1.4.0", "stylelint-config-primer": "^1.4.0",
"stylelint-config-standard": "^16.0.0", "stylelint-config-standard": "^16.0.0",
"stylelint-processor-styled-components": "^0.1.0" "stylelint-processor-styled-components": "styled-components/stylelint-processor-styled-components#68b4c4f"
} }
} }

View File

@ -5,6 +5,16 @@
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico"> <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<title>React App</title> <title>React App</title>
<style>
.CodeMirror, .ReactCodeMirror {
height: 100% !important;
}
.CodeMirror {
border: solid 1px #d8d8d8;
margin-bottom: 8px;
}
</style>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>

View File

@ -1,10 +1,9 @@
const { readFile, writeFile, exists } = require('mz/fs'); const { readFile, writeFile, exists } = require('mz/fs');
const main = require('apr-main'); const main = require('apr-main');
const forEach = require('apr-for-each'); const forEach = require('apr-for-each');
const find = require('apr-find');
const path = require('path'); const path = require('path');
const ROOT = path.join(__dirname, '../node_modules/react-scripts/config');
const configs = ['webpack.config.dev', 'webpack.config.prod']; const configs = ['webpack.config.dev', 'webpack.config.prod'];
const toCopy = [ const toCopy = [
@ -13,6 +12,8 @@ const toCopy = [
'webpack.config.prod' 'webpack.config.prod'
]; ];
let ROOT;
const backup = async file => { const backup = async file => {
const backupPath = path.join(ROOT, `${file}.original.js`); const backupPath = path.join(ROOT, `${file}.original.js`);
const backupExists = await exists(backupPath); const backupExists = await exists(backupPath);
@ -36,6 +37,15 @@ const copy = async file => {
main( main(
(async () => { (async () => {
ROOT = await find([
path.join(__dirname, '../node_modules/react-scripts/config'),
path.join(__dirname, '../../../node_modules/react-scripts/config')
], exists);
if (!ROOT) {
throw new Error('react-scripts not found');
}
await forEach(configs, backup); await forEach(configs, backup);
await forEach(toCopy, copy); await forEach(toCopy, copy);
})() })()

View File

@ -0,0 +1,53 @@
import React from 'react';
import { FormGroup, FormMeta, Input, Button } from 'joyent-ui-toolkit';
import { Field } from 'redux-form';
import { Row, Col } from 'react-styled-flexboxgrid';
import { Dots2 } from 'styled-text-spinners';
import Bundle from 'react-bundle';
const Editor = ManifestEditor => ({ input }) =>
<ManifestEditor mode="yaml" {...input} />;
export const Name = ({ handleSubmit, onCancel, dirty }) =>
<form onSubmit={handleSubmit}>
<Row>
<Col xs={12} md={3} lg={3}>
<FormGroup name="name" reduxForm>
<FormMeta left />
<Input type="text" />
</FormGroup>
</Col>
</Row>
<Row>
<Button onClick={onCancel} secondary>Cancel</Button>
<Button type="submit" disabled={!dirty}>Next</Button>
</Row>
</form>;
export const Manifest = ({ handleSubmit, onCancel, dirty, mode }) =>
<form onSubmit={handleSubmit}>
<Bundle load={() => import('joyent-manifest-editor')}>
{ManifestEditor =>
ManifestEditor
? <Field name="manifest" component={Editor(ManifestEditor)} />
: <Dots2 />}
</Bundle>
<Row>
<Button onClick={onCancel} secondary>Cancel</Button>
<Button type="submit" disabled={!dirty}>Review</Button>
</Row>
</form>;
export const Review = ({ handleSubmit, onCancel, dirty, ...state }) =>
<form onSubmit={handleSubmit}>
<pre>{state.deploymentGroupName}</pre>
<pre>{state.manifest}</pre>
<Row>
<Button onClick={onCancel} disabled={state.loading} secondary>
Cancel
</Button>
<Button disabled={state.loading} type="submit">
{state.loading ? <Dots2 /> : 'Provision'}
</Button>
</Row>
</form>;

View File

@ -2,10 +2,9 @@ import React from 'react';
import { Col, Row } from 'react-styled-flexboxgrid'; import { Col, Row } from 'react-styled-flexboxgrid';
export default () => ( export default () =>
<Row> <Row>
<Col xs={12}> <Col xs={12}>
<p>you don't have any deployment groups</p> <p>you don't have any deployment groups</p>
</Col> </Col>
</Row> </Row>;
);

View File

@ -3,10 +3,9 @@ import React from 'react';
import { Col, Row } from 'react-styled-flexboxgrid'; import { Col, Row } from 'react-styled-flexboxgrid';
import { P } from 'joyent-ui-toolkit'; import { P } from 'joyent-ui-toolkit';
export default () => ( export default () =>
<Row> <Row>
<Col xs={12}> <Col xs={12}>
<P>You don't have any instances</P> <P>You don't have any instances</P>
</Col> </Col>
</Row> </Row>;
);

View File

@ -13,7 +13,7 @@ const InstanceCard = ({
instance = {}, instance = {},
onOptionsClick = () => null, onOptionsClick = () => null,
toggleCollapsed = () => null toggleCollapsed = () => null
}) => ( }) =>
<Card collapsed={true} key={instance.uuid}> <Card collapsed={true} key={instance.uuid}>
<CardView> <CardView>
<CardMeta onClick={toggleCollapsed}> <CardMeta onClick={toggleCollapsed}>
@ -21,8 +21,7 @@ const InstanceCard = ({
</CardMeta> </CardMeta>
</CardView> </CardView>
<CardOptions onClick={onOptionsClick} /> <CardOptions onClick={onOptionsClick} />
</Card> </Card>;
);
InstanceCard.propTypes = { InstanceCard.propTypes = {
instance: PropTypes.object, instance: PropTypes.object,

View File

@ -47,7 +47,7 @@ function getBreadcrumbLinks(links) {
return null; return null;
} }
const Breadcrumb = ({ links = [] }) => ( const Breadcrumb = ({ links = [] }) =>
<Grid> <Grid>
<Row> <Row>
<Col xs={12}> <Col xs={12}>
@ -58,8 +58,7 @@ const Breadcrumb = ({ links = [] }) => (
</StyledDiv> </StyledDiv>
</Col> </Col>
</Row> </Row>
</Grid> </Grid>;
);
Breadcrumb.propTypes = { Breadcrumb.propTypes = {
links: PropTypes.arrayOf( links: PropTypes.arrayOf(

View File

@ -15,7 +15,7 @@ const StyledLogo = Img.extend`
height: ${remcalc(25)}; height: ${remcalc(25)};
`; `;
const NavHeader = ({ datacenter, username }) => ( const NavHeader = ({ datacenter, username }) =>
<Header> <Header>
<HeaderBrand> <HeaderBrand>
<Link to="/"> <Link to="/">
@ -24,8 +24,7 @@ const NavHeader = ({ datacenter, username }) => (
</HeaderBrand> </HeaderBrand>
<HeaderItem>{datacenter}</HeaderItem> <HeaderItem>{datacenter}</HeaderItem>
<HeaderItem>{username}</HeaderItem> <HeaderItem>{username}</HeaderItem>
</Header> </Header>;
);
NavHeader.propTypes = { NavHeader.propTypes = {
datacenter: PropTypes.string, datacenter: PropTypes.string,

View File

@ -16,7 +16,7 @@ const StyledBox = styled.div`
} }
`; `;
export default () => ( export default () =>
<LayoutContainer> <LayoutContainer>
<Row> <Row>
<Col> <Col>
@ -28,7 +28,8 @@ export default () => (
<Col md={10}> <Col md={10}>
<H3>Import your services</H3> <H3>Import your services</H3>
<P> <P>
You can import your services from a Git repository hosting service. Learn more. You can import your services from a Git repository hosting
service. Learn more.
</P> </P>
<Button secondary>from GitHub</Button> <Button secondary>from GitHub</Button>
<Button secondary>from GitLab</Button> <Button secondary>from GitLab</Button>
@ -42,7 +43,9 @@ export default () => (
<Col md={9}> <Col md={9}>
<H3>Alternatively, you can upload or edit manifest file.</H3> <H3>Alternatively, you can upload or edit manifest file.</H3>
<P> <P>
Manifest is a file describing your services. It is similar to Docker Compose file. You can upload a file from you local machine or edit it manually. Learn more. Manifest is a file describing your services. It is similar
to Docker Compose file. You can upload a file from you local
machine or edit it manually. Learn more.
</P> </P>
<Button secondary>Upload manifest</Button> <Button secondary>Upload manifest</Button>
<Button secondary>Edit manifest</Button> <Button secondary>Edit manifest</Button>
@ -53,5 +56,4 @@ export default () => (
</Row> </Row>
</Col> </Col>
</Row> </Row>
</LayoutContainer> </LayoutContainer>;
);

View File

@ -44,13 +44,13 @@ const ServiceListItem = ({
const isChild = Boolean(service.parent); const isChild = Boolean(service.parent);
const children = service.children const children = service.children
? service.children.map(service => ( ? service.children.map(service =>
<ServiceListItem <ServiceListItem
key={service.id} key={service.id}
deploymentGroup={deploymentGroup} deploymentGroup={deploymentGroup}
service={service} service={service}
/> />
)) )
: null; : null;
const to = `/deployment-groups/${deploymentGroup}/services/${service.slug}`; const to = `/deployment-groups/${deploymentGroup}/services/${service.slug}`;

View File

@ -0,0 +1,231 @@
import React, { Component } from 'react';
import { reduxForm } from 'redux-form';
import { compose, graphql } from 'react-apollo';
import { Redirect } from 'react-router-dom';
import intercept from 'apr-intercept';
import PropTypes from 'prop-types';
import paramCase from 'param-case';
import DeploymentGroupBySlug from '@graphql/DeploymentGroupBySlug.gql';
import DeploymentGroupCreateMutation from '@graphql/DeploymentGroupCreate.gql';
import DeploymentGroupProvisionMutation from '@graphql/DeploymentGroupProvision.gql';
import { client } from '@state/store';
import { LayoutContainer } from '@components/layout';
import { Name, Manifest, Review } from '@components/deployment-groups/create';
import { H2 } from 'joyent-ui-toolkit';
const Title = H2.extend`
margin-top: 0;
`;
const validateName = async ({ name = '' }) => {
const { data } = await client.query({
fetchPolicy: 'network-only',
query: DeploymentGroupBySlug,
variables: {
slug: paramCase(name.trim())
}
});
if (data.deploymentGroups.length) {
throw { name: `"${name}" already exists!` };
}
};
const NameForm = reduxForm({
form: 'create-deployment-group',
destroyOnUnmount: true,
forceUnregisterOnUnmount: true,
asyncValidate: validateName
})(Name);
const ManifestForm = reduxForm({
form: 'create-deployment-group',
destroyOnUnmount: true,
forceUnregisterOnUnmount: true
})(Manifest);
const ReviewForm = reduxForm({
form: 'create-deployment-group',
destroyOnUnmount: true,
forceUnregisterOnUnmount: true
})(Review);
// TODO: move state to redux. why: because in redux we can cache transactional
// state between refreshes
class DeploymentGroupCreate extends Component {
state = {
deploymentGroupName: '',
manifest: '',
loading: false,
error: null
};
constructor() {
super();
this.stages = {
name: this.renderNameForm.bind(this),
manifest: this.renderManifestEditor.bind(this),
review: this.renderReview.bind(this)
};
this.handleNameSubmit = this.handleNameSubmit.bind(this);
this.handleManifestSubmit = this.handleManifestSubmit.bind(this);
this.handleReviewSubmit = this.handleReviewSubmit.bind(this);
this.handleCancel = this.handleCancel.bind(this);
}
handleNameSubmit({ name = '' }) {
this.setState({ deploymentGroupName: name }, () =>
this.redirect({ stage: 'manifest', prog: true })
);
}
handleManifestSubmit({ manifest = '' }) {
this.setState({ manifest }, () =>
this.redirect({ stage: 'review', prog: true })
);
}
handleReviewSubmit() {
const { history } = this.props;
const submit = async () => {
const { id, slug } = await this.createDeploymentGroup();
if (!id) {
return;
}
const manifest = await this.provision(id);
if (!manifest) {
return;
}
history.push(`/deployment-groups/${slug}`);
};
this.setState({ loading: true }, submit);
}
createDeploymentGroup = async () => {
const { deploymentGroupName } = this.state;
const { createDeploymentGroup } = this.props;
const [err, res] = await intercept(
createDeploymentGroup({ name: deploymentGroupName })
);
if (err) {
this.setState({
error: err.message
});
}
return err ? null : res.data.createDeploymentGroup;
};
provision = async deploymentGroupId => {
const { manifest } = this.state;
const { provisionManifest } = this.props;
const [err, { data }] = await intercept(
provisionManifest({
deploymentGroupId,
type: 'COMPOSE',
format: 'YAML',
raw: manifest
})
);
if (err) {
this.setState({
error: err.message
});
}
return err ? null : true;
};
renderNameForm() {
return (
<NameForm onSubmit={this.handleNameSubmit} onCancel={this.handleCancel} />
);
}
renderManifestEditor() {
return (
<ManifestForm
onSubmit={this.handleManifestSubmit}
onCancel={this.handleCancel}
/>
);
}
renderReview() {
return (
<ReviewForm
onSubmit={this.handleReviewSubmit}
onCancel={this.handleCancel}
{...this.state}
/>
);
}
handleCancel() {
const { history } = this.props;
history.push('/');
}
redirect({ stage = 'name', prog = false }) {
const { match, history } = this.props;
const pathname = match.url.replace(/~create(.*)/, '~create');
const to = `${pathname}/${stage}`;
if (!prog) {
return <Redirect to={to} />;
}
history.push(to);
}
render() {
const { match } = this.props;
const stage = match.params.stage;
if (!stage) {
return this.redirect({ stage: 'name' });
}
if (!this.stages[stage]) {
return this.redirect({ stage: 'name' });
}
const view = this.stages[stage]();
return (
<LayoutContainer>
<Title>Creating deployment group</Title>
{view}
</LayoutContainer>
);
}
}
export default compose(
graphql(DeploymentGroupCreateMutation, {
props: ({ mutate }) => ({
createDeploymentGroup: variables => mutate({ variables })
})
}),
graphql(DeploymentGroupProvisionMutation, {
props: ({ mutate }) => ({
provisionManifest: variables => mutate({ variables })
})
})
)(DeploymentGroupCreate);

View File

@ -1 +1,2 @@
export { default as DeploymentGroupList } from './list'; export { default as DeploymentGroupList } from './list';
export { default as DeploymentGroupCreate } from './create';

View File

@ -28,13 +28,13 @@ class InstanceList extends Component {
} }
const instanceList = instances const instanceList = instances
? instances.map((instance, index) => ( ? instances.map((instance, index) =>
<InstanceListItem <InstanceListItem
instance={instance} instance={instance}
key={instance.id} key={instance.id}
toggleCollapsed={() => null} toggleCollapsed={() => null}
/> />
)) )
: <EmptyInstances />; : <EmptyInstances />;
return ( return (

View File

@ -14,12 +14,11 @@ const Header = ({
}, },
loading, loading,
error error
}) => ( }) =>
<HeaderComponent <HeaderComponent
datacenter={portal.datacenter.region} datacenter={portal.datacenter.region}
username={portal.user.firstName} username={portal.user.firstName}
/> />;
);
const HeaderWithData = graphql(PortalQuery, { const HeaderWithData = graphql(PortalQuery, {
props: ({ data: { portal, loading, error } }) => ({ props: ({ data: { portal, loading, error } }) => ({

View File

@ -2,24 +2,25 @@ import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Menu as MenuComponent } from '@components/navigation'; import { Menu as MenuComponent } from '@components/navigation';
const Menu = ({ sections, matchUrl }) => { const Menu = ({ sections }) =>
return <MenuComponent links={sections} />; sections && sections.length ? <MenuComponent links={sections} /> : null;
};
const ConnectedMenu = connect( const ConnectedMenu = connect(
(state, ownProps) => { (state, ownProps) => {
const params = ownProps.match.params; const params = ownProps.match.params;
const matchUrl = ownProps.match.url;
const deploymentGroupSlug = params.deploymentGroup; const deploymentGroupSlug = params.deploymentGroup;
const serviceSlug = params.service; const serviceSlug = params.service;
if ((deploymentGroupSlug || '').match(/^\~/)) {
return {};
}
const sections = serviceSlug const sections = serviceSlug
? state.ui.sections.services ? state.ui.sections.services
: deploymentGroupSlug ? state.ui.sections.deploymentGroups : null; : deploymentGroupSlug ? state.ui.sections.deploymentGroups : null;
return { return {
sections, sections
matchUrl
}; };
}, },
dispatch => ({}) dispatch => ({})

View File

@ -48,7 +48,7 @@ class ServiceList extends Component {
toggleServicesQuickActions(o); toggleServicesQuickActions(o);
}; };
const serviceList = services.map(service => ( const serviceList = services.map(service =>
<ServiceListItem <ServiceListItem
key={service.id} key={service.id}
deploymentGroup={deploymentGroup.slug} deploymentGroup={deploymentGroup.slug}
@ -60,7 +60,7 @@ class ServiceList extends Component {
onQuickActionsClick={handleQuickActionsClick} onQuickActionsClick={handleQuickActionsClick}
onQuickActionsBlur={handleQuickActionsBlur} onQuickActionsBlur={handleQuickActionsBlur}
/> />
)); );
return ( return (
<LayoutContainer> <LayoutContainer>

View File

@ -0,0 +1,5 @@
query DeploymentGroupBySlug($slug: String!) {
deploymentGroups(slug: $slug) {
id
}
}

View File

@ -0,0 +1,7 @@
#import "./DeploymentGroupInfo.gql"
mutation createDeploymentGroup($name: String!) {
createDeploymentGroup(name: $name) {
...DeploymentGroupInfo
}
}

View File

@ -0,0 +1,9 @@
mutation provisionManifest($deploymentGroupId: ID!, $type: ManifestType!, $format: ManifestFormat!, $raw: String!) {
provisionManifest(deploymentGroupId: $deploymentGroupId, type: $type, format: $format, raw: $raw) {
id
created
type
format
obj
}
}

View File

@ -2,28 +2,31 @@ import React from 'react';
import { BrowserRouter, Redirect, Route, Switch } from 'react-router-dom'; import { BrowserRouter, Redirect, Route, Switch } from 'react-router-dom';
import { Header, Breadcrumb, Menu } from '@containers/navigation'; import { Header, Breadcrumb, Menu } from '@containers/navigation';
import { InstanceList } from '@containers/instances';
import {
DeploymentGroupList,
DeploymentGroupCreate
} from '@containers/deployment-groups';
import { DeploymentGroupList } from '@containers/deployment-groups';
import { import {
ServiceList, ServiceList,
ServicesTopology, ServicesTopology,
ServicesMenu ServicesMenu
} from '@containers/services'; } from '@containers/services';
import { InstanceList } from '@containers/instances';
const rootRedirect = p => <Redirect to="/deployment-groups" />; const rootRedirect = p => <Redirect to="/deployment-groups" />;
const deploymentGroupRedirect = p => ( const deploymentGroupRedirect = p =>
<Redirect <Redirect
to={`/deployment-groups/${p.match.params.deploymentGroup}/services-list`} to={`/deployment-groups/${p.match.params.deploymentGroup}/services-list`}
/> />;
);
const serviceRedirect = p => ( const serviceRedirect = p =>
<Redirect <Redirect
to={`/deployment-groups/${p.match.params.deploymentGroup}/services/${p.match.params.service}/instances`} to={`/deployment-groups/${p.match.params.deploymentGroup}/services/${p.match
/> .params.service}/instances`}
); />;
const Router = ( const Router = (
<BrowserRouter> <BrowserRouter>
@ -53,6 +56,12 @@ const Router = (
<Route path="/" exact component={rootRedirect} /> <Route path="/" exact component={rootRedirect} />
<Route path="/deployment-groups" exact component={DeploymentGroupList} /> <Route path="/deployment-groups" exact component={DeploymentGroupList} />
<Switch>
<Route
path="/deployment-groups/~create/:stage?"
exact
component={DeploymentGroupCreate}
/>
<Route <Route
path="/deployment-groups/:deploymentGroup" path="/deployment-groups/:deploymentGroup"
exact exact
@ -63,6 +72,8 @@ const Router = (
exact exact
component={deploymentGroupRedirect} component={deploymentGroupRedirect}
/> />
</Switch>
<Route <Route
path="/deployment-groups/:deploymentGroup/instances" path="/deployment-groups/:deploymentGroup/instances"
exact exact

View File

@ -1,4 +1,5 @@
import { createStore, combineReducers, applyMiddleware, compose } from 'redux'; import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
import { reducer as formReducer } from 'redux-form';
// import { enableBatching } from 'redux-batched-actions'; // import { enableBatching } from 'redux-batched-actions';
import { ApolloClient, createNetworkInterface } from 'react-apollo'; import { ApolloClient, createNetworkInterface } from 'react-apollo';
import state from './state'; import state from './state';
@ -36,7 +37,8 @@ export const client = new ApolloClient({
export const store = createStore( export const store = createStore(
combineReducers({ combineReducers({
ui, ui,
apollo: client.reducer() apollo: client.reducer(),
form: formReducer
}), }),
state, // Initial state state, // Initial state
compose( compose(

View File

@ -2390,10 +2390,6 @@ eslint-plugin-import@2.2.0:
minimatch "^3.0.3" minimatch "^3.0.3"
pkg-up "^1.0.0" pkg-up "^1.0.0"
eslint-plugin-jest@^20.0.3:
version "20.0.3"
resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-20.0.3.tgz#ec15eba6ac0ab44a67ebf6e02672ca9d7e7cba29"
eslint-plugin-jsx-a11y@5.0.3: eslint-plugin-jsx-a11y@5.0.3:
version "5.0.3" version "5.0.3"
resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-5.0.3.tgz#4a939f76ec125010528823331bf948cc573380b6" resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-5.0.3.tgz#4a939f76ec125010528823331bf948cc573380b6"
@ -3886,9 +3882,9 @@ jest-snapshot@^20.0.3:
natural-compare "^1.4.0" natural-compare "^1.4.0"
pretty-format "^20.0.3" pretty-format "^20.0.3"
jest-styled-components@^2.2.0: jest-styled-components@^3.0.0-2:
version "2.2.0" version "3.0.0-2"
resolved "https://registry.yarnpkg.com/jest-styled-components/-/jest-styled-components-2.2.0.tgz#7c0748c07979b090ede10220ffa67ab9057b4e8e" resolved "https://registry.yarnpkg.com/jest-styled-components/-/jest-styled-components-3.0.0-2.tgz#147fd618f3642d0fd7724285bdc08a8b3a91361b"
dependencies: dependencies:
css "^2.2.1" css "^2.2.1"
@ -4607,6 +4603,10 @@ npmlog@^4.0.2:
gauge "~2.7.3" gauge "~2.7.3"
set-blocking "~2.0.0" set-blocking "~2.0.0"
nprogress@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/nprogress/-/nprogress-0.2.0.tgz#cb8f34c53213d895723fcbab907e9422adbcafb1"
nth-check@~1.0.1: nth-check@~1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.1.tgz#9929acdf628fc2c41098deab82ac580cf149aae4" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.1.tgz#9929acdf628fc2c41098deab82ac580cf149aae4"

View File

@ -14,13 +14,17 @@
"dev": "nodemon src/index.js" "dev": "nodemon src/index.js"
}, },
"dependencies": { "dependencies": {
"camel-case": "^3.0.0",
"good": "^7.2.0", "good": "^7.2.0",
"good-console": "^6.4.0", "good-console": "^6.4.0",
"good-squeeze": "^5.0.2", "good-squeeze": "^5.0.2",
"graphi": "^2.0.0", "graphi": "^2.2.1",
"hapi": "^16.1.1", "hapi": "^16.4.3",
"joi": "^10.5.0", "joi": "^10.5.2",
"joyent-cp-gql-schema": "^1.0.4" "joyent-cp-gql-schema": "^1.0.4",
"js-yaml": "^3.8.4",
"param-case": "^2.1.1",
"uuid": "^3.0.1"
}, },
"devDependencies": { "devDependencies": {
"eslint": "^3.19.0", "eslint": "^3.19.0",

View File

@ -15,7 +15,7 @@
"deploymentGroups": [ "deploymentGroups": [
{ {
"id": "e0ea0c02-55cc-45fe-8064-3e5176a59401", "id": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
"slug": "forest-foundation-dev", "slug": "warp-records-blog",
"name": "WarpRecords Blog" "name": "WarpRecords Blog"
}, },
{ {

View File

@ -1,3 +1,8 @@
const { v4: uuid } = require('uuid');
const paramCase = require('param-case');
const camelCase = require('camel-case');
const yaml = require('js-yaml');
const { const {
datacenter, datacenter,
portal, portal,
@ -58,6 +63,43 @@ const getPortal = () =>
}) })
); );
const createDeploymentGroup = ({ name }) => {
const dg = {
id: uuid(),
slug: paramCase(name),
name
};
deploymentGroups.push(dg);
return Promise.resolve(dg);
};
const createServicesFromManifest = ({ deploymentGroupId, raw }) => {
const manifest = yaml.safeLoad(raw);
Object.keys(manifest).forEach(name => {
const service = {
id: uuid(),
deploymentGroup: deploymentGroupId,
slug: paramCase(name),
name
};
const instance = {
id: uuid(),
name: camelCase(`${service.slug}_01`),
service: service.id,
deploymentGroup: deploymentGroupId
};
services.push(service);
instances.push(instance);
});
return Promise.resolve(undefined);
};
module.exports = { module.exports = {
portal: getPortal, portal: getPortal,
deploymentGroups: getDeploymentGroups, deploymentGroups: getDeploymentGroups,
@ -65,5 +107,12 @@ module.exports = {
services: getServices, services: getServices,
service: query => getServices(query).then(services => services.shift()), service: query => getServices(query).then(services => services.shift()),
instances: getInstances, instances: getInstances,
instance: query => getInstances(query).then(instances => instances.shift()) instance: query => getInstances(query).then(instances => instances.shift()),
createDeploymentGroup,
provisionManifest: options =>
createServicesFromManifest(options).then(() => ({
id: uuid(),
type: options.type,
format: options.format
}))
}; };

View File

@ -14,7 +14,7 @@
"zerorpc": "^0.9.7" "zerorpc": "^0.9.7"
}, },
"devDependencies": { "devDependencies": {
"code": "^4.0.0", "code": "^4.1.0",
"eslint": "^3.19.0", "eslint": "^3.19.0",
"eslint-config-joyent-portal": "1.0.0", "eslint-config-joyent-portal": "1.0.0",
"js-yaml": "^3.8.4", "js-yaml": "^3.8.4",

View File

@ -13,13 +13,13 @@
"peerDependencies": { "peerDependencies": {
"babel-eslint": "^7.2.3", "babel-eslint": "^7.2.3",
"eslint": "^3.19.0", "eslint": "^3.19.0",
"eslint-config-prettier": "^2.1.0", "eslint-config-prettier": "^2.1.1",
"eslint-config-react-app": "^0.6.2", "eslint-config-react-app": "^0.6.2",
"eslint-config-xo-space": "^0.16.0", "eslint-config-xo-space": "^0.16.0",
"eslint-plugin-flowtype": "^2.33.0", "eslint-plugin-flowtype": "^2.33.0",
"eslint-plugin-import": "^2.2.0", "eslint-plugin-import": "^2.2.0",
"eslint-plugin-jsx-a11y": "^5.0.3", "eslint-plugin-jsx-a11y": "^5.0.3",
"eslint-plugin-prettier": "^2.0.1", "eslint-plugin-prettier": "^2.1.1",
"eslint-plugin-react": "^7.0.1" "eslint-plugin-react": "^7.0.1"
} }
} }

View File

@ -23,21 +23,21 @@
"prepublish": "redrun build" "prepublish": "redrun build"
}, },
"dependencies": { "dependencies": {
"prop-types": "^15.5.10" "prop-types": "^15.5.10",
"react-codemirror": "^1.0.0"
}, },
"devDependencies": { "devDependencies": {
"babel-preset-react-app": "^3.0.0", "babel-preset-react-app": "^3.0.0",
"bup": "^1.0.9", "bup": "^1.0.9",
"eslint": "^3.19.0", "eslint": "^3.19.0",
"eslint-config-joyent-portal": "1.0.3", "eslint-config-joyent-portal": "1.0.0",
"jest": "^20.0.4", "jest": "^20.0.4",
"react": "^15.5.4", "react": "^15.5.4",
"react-test-renderer": "^15.5.4", "react-test-renderer": "^15.5.4",
"redrun": "^5.9.14" "redrun": "^5.9.14"
}, },
"peerDependencies": { "peerDependencies": {
"react": "*", "react": "*"
"react-codemirror": "*"
}, },
"jest": { "jest": {
"testEnvironment": "jsdom", "testEnvironment": "jsdom",

View File

@ -1,3 +1,20 @@
import 'codemirror/lib/codemirror.css';
import 'codemirror/theme/eclipse.css';
import 'codemirror/addon/fold/foldgutter.css';
import 'codemirror/addon/lint/lint.css';
import 'codemirror/mode/yaml/yaml';
import 'codemirror/mode/javascript/javascript';
import 'codemirror/addon/edit/closebrackets';
import 'codemirror/addon/edit/matchbrackets';
import 'codemirror/addon/fold/foldcode';
import 'codemirror/addon/fold/foldgutter';
import 'codemirror/addon/fold/brace-fold';
import 'codemirror/addon/fold/indent-fold';
import 'codemirror/addon/fold/comment-fold';
import 'codemirror/addon/hint/show-hint';
import 'codemirror/addon/selection/active-line';
import 'codemirror/addon/edit/closetag';
import React, { Component } from 'react'; import React, { Component } from 'react';
import ReactCodeMirror from 'react-codemirror'; import ReactCodeMirror from 'react-codemirror';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
@ -11,7 +28,6 @@ const options = {
electricChars: true, electricChars: true,
lineNumbers: true, lineNumbers: true,
inputStyle: 'contenteditable', inputStyle: 'contenteditable',
readOnly: false,
lint: true, lint: true,
autoCloseBrackets: true, autoCloseBrackets: true,
styleActiveLine: true, styleActiveLine: true,
@ -38,7 +54,7 @@ class ManifestEditor extends Component {
this._refs[name] = ref; this._refs[name] = ref;
}; };
} }
options({ mode }) { options({ mode, readOnly }) {
const modes = { const modes = {
json: { json: {
name: 'javascript', name: 'javascript',
@ -48,7 +64,8 @@ class ManifestEditor extends Component {
}; };
return Object.assign({}, options, { return Object.assign({}, options, {
mode: modes[mode.toLowerCase()] mode: modes[mode.toLowerCase()],
readOnly
}); });
} }
render() { render() {
@ -72,7 +89,8 @@ ManifestEditor.defaultProps = {
onChange: () => null, onChange: () => null,
onFocusChange: () => null, onFocusChange: () => null,
autoSave: true, autoSave: true,
preserveScrollPosition: true preserveScrollPosition: true,
readOnly: false
}; };
ManifestEditor.propTypes = { ManifestEditor.propTypes = {
@ -81,7 +99,8 @@ ManifestEditor.propTypes = {
onChange: PropTypes.func, onChange: PropTypes.func,
onFocusChange: PropTypes.func, onFocusChange: PropTypes.func,
autoSave: PropTypes.bool, autoSave: PropTypes.bool,
preserveScrollPosition: PropTypes.bool preserveScrollPosition: PropTypes.bool,
readOnly: PropTypes.bool
}; };
export default ManifestEditor; export default ManifestEditor;

View File

@ -30,12 +30,12 @@
"prepublish": "redrun build" "prepublish": "redrun build"
}, },
"dependencies": { "dependencies": {
"remcalc": "^1.0.5" "remcalc": "^1.0.8"
}, },
"devDependencies": { "devDependencies": {
"babel-plugin-styled-components": "^1.1.4", "babel-plugin-styled-components": "^1.1.4",
"babel-preset-react-app": "^3.0.0", "babel-preset-react-app": "^3.0.0",
"bup": "^1.0.7", "bup": "^1.0.9",
"chalk": "^1.1.3", "chalk": "^1.1.3",
"eslint": "^3.19.0", "eslint": "^3.19.0",
"eslint-config-joyent-portal": "1.0.0", "eslint-config-joyent-portal": "1.0.0",
@ -44,13 +44,13 @@
"jest-junit": "^1.5.1", "jest-junit": "^1.5.1",
"jest-matcher-utils": "^20.0.3", "jest-matcher-utils": "^20.0.3",
"jest-snapshot": "^20.0.3", "jest-snapshot": "^20.0.3",
"jest-styled-components": "^3.0.0-1", "jest-styled-components": "^3.0.0-2",
"react": "^15.5.4", "react": "^15.5.4",
"react-test-renderer": "^15.5.4", "react-test-renderer": "^15.5.4",
"redrun": "^5.9.14", "redrun": "^5.9.14",
"strip-ansi": "^3.0.1", "strip-ansi": "^3.0.1",
"styled-components": "^2.0.0", "styled-components": "^2.0.1",
"stylelint": "^7.10.1", "stylelint": "^7.11.0",
"stylelint-config-primer": "^1.4.0", "stylelint-config-primer": "^1.4.0",
"stylelint-config-standard": "^16.0.0", "stylelint-config-standard": "^16.0.0",
"stylelint-processor-styled-components": "styled-components/stylelint-processor-styled-components#68b4c4f" "stylelint-processor-styled-components": "styled-components/stylelint-processor-styled-components#68b4c4f"

View File

@ -18,19 +18,19 @@
"license": "MPL-2.0", "license": "MPL-2.0",
"devDependencies": { "devDependencies": {
"belly-button": "^3.1.0", "belly-button": "^3.1.0",
"code": "^4.0.0", "code": "^4.1.0",
"hapi": "^16.1.1", "hapi": "^16.4.3",
"hapi-swagger": "^7.7.0", "hapi-swagger": "^7.7.0",
"inert": "^4.2.0", "inert": "^4.2.0",
"lab": "^13.0.2", "lab": "^13.1.0",
"vision": "^4.1.1" "vision": "^4.1.1"
}, },
"dependencies": { "dependencies": {
"boom": "^4.3.1", "boom": "^5.1.0",
"graphi": "^2.0.0", "graphi": "^2.2.1",
"hoek": "^4.1.1", "hoek": "^4.1.1",
"joi": "^10.4.1", "joi": "^10.5.2",
"joyent-cp-gql-schema": "^1.0.4", "joyent-cp-gql-schema": "^1.0.4",
"portal-data": "^1.0.0" "portal-data": "^1.1.0"
} }
} }

View File

@ -19,13 +19,13 @@
"docker-compose-client": "^1.0.7", "docker-compose-client": "^1.0.7",
"dockerode": "^2.4.3", "dockerode": "^2.4.3",
"hoek": "^4.1.1", "hoek": "^4.1.1",
"penseur": "^7.8.1", "penseur": "^7.12.3",
"vasync": "^1.6.4", "vasync": "^1.6.4",
"yamljs": "^0.2.10" "yamljs": "^0.2.10"
}, },
"devDependencies": { "devDependencies": {
"belly-button": "^3.1.0", "belly-button": "^3.1.0",
"code": "^4.0.0", "code": "^4.1.0",
"lab": "^13.0.4" "lab": "^13.1.0"
} }
} }

View File

@ -39,15 +39,14 @@
}, },
"devDependencies": { "devDependencies": {
"ava": "0.19.1", "ava": "0.19.1",
"babel-plugin-istanbul": "^4.1.3", "babel-plugin-istanbul": "^4.1.4",
"babel-preset-env": "^1.5.1", "babel-preset-env": "^1.5.2",
"babel-register": "^6.24.1", "babel-register": "^6.24.1",
"bup": "^1.0.7", "bup": "^1.0.9",
"cross-env": "^5.0.0", "cross-env": "^5.0.1",
"eslint": "^3.19.0", "eslint": "^3.19.0",
"eslint-config-joyent-portal": "1.0.0", "eslint-config-joyent-portal": "1.0.0",
"nyc": "^10.3.2", "nyc": "^11.0.2",
"prettier": "^1.3.1",
"redrun": "^5.9.14", "redrun": "^5.9.14",
"tap-xunit": "^1.7.0" "tap-xunit": "^1.7.0"
}, },

View File

@ -37,19 +37,18 @@
"has-own-prop": "^1.0.0", "has-own-prop": "^1.0.0",
"lodash.isnull": "^3.0.0", "lodash.isnull": "^3.0.0",
"lodash.isundefined": "^3.0.1", "lodash.isundefined": "^3.0.1",
"yaml-ast-parser": "0.0.32" "yaml-ast-parser": "0.0.33"
}, },
"devDependencies": { "devDependencies": {
"ava": "0.19.1", "ava": "0.19.1",
"babel-plugin-istanbul": "^4.1.3", "babel-plugin-istanbul": "^4.1.4",
"babel-preset-env": "^1.5.1", "babel-preset-env": "^1.5.2",
"babel-register": "^6.24.1", "babel-register": "^6.24.1",
"bup": "^1.0.7", "bup": "^1.0.9",
"cross-env": "^5.0.0", "cross-env": "^5.0.1",
"eslint": "^3.19.0", "eslint": "^3.19.0",
"eslint-config-joyent-portal": "1.0.0", "eslint-config-joyent-portal": "1.0.0",
"nyc": "^10.3.2", "nyc": "^11.0.2",
"prettier": "^1.3.1",
"redrun": "^5.9.14", "redrun": "^5.9.14",
"tap-xunit": "^1.7.0" "tap-xunit": "^1.7.0"
}, },

View File

@ -29,17 +29,17 @@
}, },
"devDependencies": { "devDependencies": {
"ava": "0.19.1", "ava": "0.19.1",
"babel-plugin-istanbul": "^4.1.3", "babel-plugin-istanbul": "^4.1.4",
"babel-plugin-transform-es2015-arrow-functions": "^6.22.0", "babel-plugin-transform-es2015-arrow-functions": "^6.22.0",
"babel-plugin-transform-es2015-parameters": "^6.24.1", "babel-plugin-transform-es2015-parameters": "^6.24.1",
"babel-plugin-transform-es2015-spread": "^6.22.0", "babel-plugin-transform-es2015-spread": "^6.22.0",
"babel-plugin-transform-es2015-template-literals": "^6.22.0", "babel-plugin-transform-es2015-template-literals": "^6.22.0",
"babel-register": "^6.24.1", "babel-register": "^6.24.1",
"bup": "^1.0.8", "bup": "^1.0.9",
"cross-env": "^5.0.0", "cross-env": "^5.0.1",
"eslint": "^3.19.0", "eslint": "^3.19.0",
"eslint-config-joyent-portal": "1.0.0", "eslint-config-joyent-portal": "1.0.0",
"nyc": "^10.3.2", "nyc": "^11.0.2",
"tap-xunit": "^1.7.0" "tap-xunit": "^1.7.0"
}, },
"nyc": { "nyc": {

View File

@ -25,16 +25,16 @@
}, },
"devDependencies": { "devDependencies": {
"ava": "0.19.1", "ava": "0.19.1",
"babel-plugin-istanbul": "^4.1.3", "babel-plugin-istanbul": "^4.1.4",
"babel-plugin-transform-es2015-arrow-functions": "^6.22.0", "babel-plugin-transform-es2015-arrow-functions": "^6.22.0",
"babel-plugin-transform-es2015-template-literals": "^6.22.0", "babel-plugin-transform-es2015-template-literals": "^6.22.0",
"babel-register": "^6.24.1", "babel-register": "^6.24.1",
"bup": "^1.0.8", "bup": "^1.0.9",
"cross-env": "^5.0.0", "cross-env": "^5.0.1",
"eslint": "^3.19.0", "eslint": "^3.19.0",
"eslint-config-joyent-portal": "1.0.0", "eslint-config-joyent-portal": "1.0.0",
"lodash.uniq": "^4.5.0", "lodash.uniq": "^4.5.0",
"nyc": "^10.3.2", "nyc": "^11.0.2",
"tap-xunit": "^1.7.0" "tap-xunit": "^1.7.0"
}, },
"nyc": { "nyc": {

View File

@ -28,20 +28,20 @@
}, },
"devDependencies": { "devDependencies": {
"ava": "0.19.1", "ava": "0.19.1",
"babel-plugin-istanbul": "^4.1.3", "babel-plugin-istanbul": "^4.1.4",
"babel-plugin-transform-es2015-arrow-functions": "^6.22.0", "babel-plugin-transform-es2015-arrow-functions": "^6.22.0",
"babel-plugin-transform-es2015-parameters": "^6.24.1", "babel-plugin-transform-es2015-parameters": "^6.24.1",
"babel-plugin-transform-es2015-spread": "^6.22.0", "babel-plugin-transform-es2015-spread": "^6.22.0",
"babel-plugin-transform-es2015-template-literals": "^6.22.0", "babel-plugin-transform-es2015-template-literals": "^6.22.0",
"babel-register": "^6.24.1", "babel-register": "^6.24.1",
"bup": "^1.0.7", "bup": "^1.0.9",
"cross-env": "^5.0.0", "cross-env": "^5.0.1",
"eslint": "^3.19.0", "eslint": "^3.19.0",
"eslint-config-joyent-portal": "1.0.0", "eslint-config-joyent-portal": "1.0.0",
"nyc": "^10.3.2", "nyc": "^11.0.2",
"react": "^15.5.4", "react": "^15.5.4",
"redrun": "^5.9.14", "redrun": "^5.9.14",
"styled-components": "^2.0.0", "styled-components": "^2.0.1",
"tap-xunit": "^1.7.0" "tap-xunit": "^1.7.0"
}, },
"peerDependencies": { "peerDependencies": {

View File

@ -32,48 +32,54 @@
"d3": "^4.9.1", "d3": "^4.9.1",
"lodash.isstring": "^4.0.1", "lodash.isstring": "^4.0.1",
"normalized-styled-components": "^1.0.5", "normalized-styled-components": "^1.0.5",
"polished": "^1.1.2", "polished": "^1.1.3",
"prop-types": "^15.5.10", "prop-types": "^15.5.10",
"react": "^15.5.4",
"react-broadcast": "^0.1.2", "react-broadcast": "^0.1.2",
"react-dom": "^15.5.4", "react-styled-flexboxgrid": "^2.0.0",
"react-router-dom": "^4.1.1", "remcalc": "^1.0.8",
"react-styled-flexboxgrid": "^1.1.2", "rnd-id": "^1.0.8",
"redux-form": "^6.7.0", "styled-components": "^2.0.1",
"remcalc": "^1.0.5", "styled-is": "^1.0.11",
"rnd-id": "^1.0.5", "unitcalc": "^1.0.8"
"styled-components": "^2.0.0",
"styled-is": "^1.0.7",
"unitcalc": "^1.0.5"
}, },
"devDependencies": { "devDependencies": {
"babel-cli": "^6.24.1", "babel-cli": "^6.24.1",
"babel-plugin-inline-react-svg": "^0.4.0", "babel-plugin-inline-react-svg": "^0.4.0",
"babel-plugin-styled-components": "^1.1.4", "babel-plugin-styled-components": "^1.1.4",
"babel-preset-joyent-portal": "^1.0.0", "babel-preset-joyent-portal": "^1.0.0",
"cross-env": "^5.0.0", "cross-env": "^5.0.1",
"eslint": "^3.19.0", "eslint": "^3.19.0",
"eslint-config-joyent-portal": "1.0.0", "eslint-config-joyent-portal": "1.0.0",
"jest": "^20.0.3", "jest": "^20.0.4",
"jest-diff": "^20.0.3", "jest-diff": "^20.0.3",
"jest-matcher-utils": "^20.0.3", "jest-matcher-utils": "^20.0.3",
"jest-snapshot": "^20.0.3", "jest-snapshot": "^20.0.3",
"jest-styled-components": "^2.0.0", "jest-styled-components": "^3.0.0-2",
"react": "^15.5.4",
"react-docgen": "^2.15.0", "react-docgen": "^2.15.0",
"react-docgen-displayname-handler": "^1.0.0", "react-docgen-displayname-handler": "^1.0.0",
"react-dom": "^15.5.4",
"react-redux": "^5.0.5", "react-redux": "^5.0.5",
"react-scripts": "^1.0.0", "react-router-dom": "^4.1.1",
"react-styleguidist": "^5.2.1", "react-scripts": "^1.0.7",
"react-styleguidist": "^5.4.2",
"react-test-renderer": "^15.5.4", "react-test-renderer": "^15.5.4",
"redrun": "^5.9.14", "redrun": "^5.9.14",
"redux": "^3.6.0", "redux": "^3.6.0",
"redux-form": "^6.8.0",
"snapguidist": "^1.1.2", "snapguidist": "^1.1.2",
"stylelint": "^7.10.1", "stylelint": "^7.11.0",
"stylelint-config-primer": "^1.4.0", "stylelint-config-primer": "^1.4.0",
"stylelint-config-standard": "^16.0.0", "stylelint-config-standard": "^16.0.0",
"stylelint-processor-styled-components": "ramitos/stylelint-processor-styled-components#e81e1d0", "stylelint-processor-styled-components": "styled-components/stylelint-processor-styled-components#68b4c4f",
"tinycolor2": "^1.4.1", "tinycolor2": "^1.4.1",
"title-case": "^2.1.1", "title-case": "^2.1.1",
"webpack": "^2.5.1" "webpack": "^2.6.1"
},
"peerDependencies": {
"react": "^15.5.4",
"react-dom": "^15.5.4",
"react-router-dom": "^4.1.1",
"redux-form": "^6.8.0"
} }
} }

View File

@ -36,9 +36,8 @@ class FormGroup extends Component {
render() { render() {
const { const {
name = rndId(), name = rndId(),
defaultValue, reduxForm = false,
normalize, ...rest
reduxForm = false
} = this.props; } = this.props;
if (!reduxForm) { if (!reduxForm) {
@ -48,9 +47,8 @@ class FormGroup extends Component {
return ( return (
<Field <Field
name={name} name={name}
defaultValue={defaultValue}
component={this.renderGroup} component={this.renderGroup}
normalize={normalize} {...rest}
/> />
); );
} }

View File

@ -28,20 +28,20 @@
}, },
"dependencies": { "dependencies": {
"lodash.flatten": "^4.4.0", "lodash.flatten": "^4.4.0",
"remcalc": "^1.0.5" "remcalc": "^1.0.8"
}, },
"devDependencies": { "devDependencies": {
"ava": "0.19.1", "ava": "0.19.1",
"babel-plugin-istanbul": "^4.1.3", "babel-plugin-istanbul": "^4.1.4",
"babel-plugin-transform-es2015-arrow-functions": "^6.22.0", "babel-plugin-transform-es2015-arrow-functions": "^6.22.0",
"babel-plugin-transform-es2015-parameters": "^6.24.1", "babel-plugin-transform-es2015-parameters": "^6.24.1",
"babel-plugin-transform-es2015-spread": "^6.22.0", "babel-plugin-transform-es2015-spread": "^6.22.0",
"babel-register": "^6.24.1", "babel-register": "^6.24.1",
"bup": "^1.0.8", "bup": "^1.0.9",
"cross-env": "^5.0.0", "cross-env": "^5.0.1",
"eslint": "^3.19.0", "eslint": "^3.19.0",
"eslint-config-joyent-portal": "1.0.0", "eslint-config-joyent-portal": "1.0.0",
"nyc": "^10.3.2", "nyc": "^11.0.2",
"tap-xunit": "^1.7.0" "tap-xunit": "^1.7.0"
}, },
"nyc": { "nyc": {

8849
yarn.lock

File diff suppressed because it is too large Load Diff