feat(my-joy-beta): imc networks

fixes #1074
This commit is contained in:
Sérgio Ramos 2018-01-23 12:20:36 +00:00 committed by Sérgio Ramos
parent bbfa427a92
commit 8d7455c07c
44 changed files with 4089 additions and 1005 deletions

View File

@ -14,7 +14,13 @@ export default ({
}) => ( }) => (
<Rotate direction={direction}> <Rotate direction={direction}>
{({ style: rotateStyle }) => ( {({ style: rotateStyle }) => (
<svg width="20.25" height="14" viewBox="0 0 24 16" style={{ ...style, ...rotateStyle }} {...rest}> <svg
width="20.25"
height="14"
viewBox="0 0 24 16"
style={{ ...style, ...rotateStyle }}
{...rest}
>
<path <path
fill={calcFill({ fill, disabled, light, colors })} fill={calcFill({ fill, disabled, light, colors })}
d="M13.25 0a7 7 0 0 0-3.13.74A7 7 0 1 0 7 14a7 7 0 0 0 3.12-.74A7 7 0 1 0 13.25 0zM12 7a5 5 0 0 1-1.88 3.87 4.94 4.94 0 0 1 0-7.74A5 5 0 0 1 12 7zM2 7a5 5 0 0 1 5-5 5.26 5.26 0 0 1 1.2.16 7 7 0 0 0 0 9.68A5.26 5.26 0 0 1 7 12a5 5 0 0 1-5-5zm11.25 5a5.26 5.26 0 0 1-1.2-.16 7 7 0 0 0 0-9.68 5.26 5.26 0 0 1 1.2-.16 5 5 0 0 1 0 10z" d="M13.25 0a7 7 0 0 0-3.13.74A7 7 0 1 0 7 14a7 7 0 0 0 3.12-.74A7 7 0 1 0 13.25 0zM12 7a5 5 0 0 1-1.88 3.87 4.94 4.94 0 0 1 0-7.74A5 5 0 0 1 12 7zM2 7a5 5 0 0 1 5-5 5.26 5.26 0 0 1 1.2.16 7 7 0 0 0 0 9.68A5.26 5.26 0 0 1 7 12a5 5 0 0 1-5-5zm11.25 5a5.26 5.26 0 0 1-1.2-.16 7 7 0 0 0 0-9.68 5.26 5.26 0 0 1 1.2-.16 5 5 0 0 1 0 10z"
@ -22,4 +28,4 @@ export default ({
</svg> </svg>
)} )}
</Rotate> </Rotate>
); );

View File

@ -15,5 +15,6 @@
</noscript> </noscript>
<div id="header"></div> <div id="header"></div>
<div id="root"></div> <div id="root"></div>
<script src="/nav-static/main.js"></script>
</body> </body>
</html> </html>

View File

@ -4,8 +4,7 @@ import { Margin, Padding } from 'styled-components-spacing';
import Flex from 'styled-flex-component'; import Flex from 'styled-flex-component';
import { H3, Card } from 'joyent-ui-toolkit'; import { H3, Card } from 'joyent-ui-toolkit';
import NoPackagesImage from '@assets/no-packages.svg';
import NoPackagesImage from '../../assets/no-packages.svg';
const NoPackagesTitle = styled(H3)` const NoPackagesTitle = styled(H3)`
color: ${props => props.theme.greyDark}; color: ${props => props.theme.greyDark};

View File

@ -1,5 +1,3 @@
/* eslint-disable camelcase */
import React, { Fragment } from 'react'; import React, { Fragment } from 'react';
import { Field } from 'redux-form'; import { Field } from 'redux-form';
import { Margin, Padding } from 'styled-components-spacing'; import { Margin, Padding } from 'styled-components-spacing';
@ -18,7 +16,7 @@ import {
P P
} from 'joyent-ui-toolkit'; } from 'joyent-ui-toolkit';
import Tag from '@components/instances/tags'; import Tag from '@components/tags';
import Empty from './empty'; import Empty from './empty';
const capitalizeFirstLetter = string => const capitalizeFirstLetter = string =>

View File

@ -69,13 +69,13 @@ const Version = styled(Select)`
const getImage = name => { const getImage = name => {
try { try {
return { return {
url: require(`../../assets/${name}.svg`), url: require(`@assets/${name}.svg`),
size: 42, size: 42,
bottom: 0 bottom: 0
}; };
} catch (e) { } catch (e) {
return { return {
url: require(`../../assets/placeholder.svg`), url: require('@assets/placeholder.svg'),
size: 36, size: 36,
bottom: 6 bottom: 6
}; };

View File

@ -1,71 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders <AddForm /> without throwing 1`] = ` exports[`renders <AddForm /> without throwing 1`] = `
.c3 {
padding-right: 1rem;
padding-left: 1rem;
}
.c7 {
padding: 1rem;
}
.c8 {
box-sizing: border-box;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex: 0 1 auto;
-ms-flex: 0 1 auto;
flex: 0 1 auto;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-flex-wrap: wrap;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
margin-right: -0.5rem;
margin-left: -0.5rem;
}
.c17 {
box-sizing: border-box;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex: 0 1 auto;
-ms-flex: 0 1 auto;
flex: 0 1 auto;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-flex-wrap: wrap;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
margin-right: -0.5rem;
margin-left: -0.5rem;
}
.c9 {
box-sizing: border-box;
-webkit-flex: 0 0 auto;
-ms-flex: 0 0 auto;
flex: 0 0 auto;
padding-right: 0.5rem;
padding-left: 0.5rem;
}
.c22 {
box-sizing: border-box;
-webkit-flex: 0 0 auto;
-ms-flex: 0 0 auto;
flex: 0 0 auto;
padding-right: 0.5rem;
padding-left: 0.5rem;
}
.c20 { .c20 {
font-family: sans-serif; font-family: sans-serif;
font-size: 100%; font-size: 100%;
@ -446,6 +381,62 @@ exports[`renders <AddForm /> without throwing 1`] = `
margin-left: 1.75rem; margin-left: 1.75rem;
} }
.c8 {
box-sizing: border-box;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex: 0 1 auto;
-ms-flex: 0 1 auto;
flex: 0 1 auto;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-flex-wrap: wrap;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
margin-right: -0.5rem;
margin-left: -0.5rem;
}
.c17 {
box-sizing: border-box;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex: 0 1 auto;
-ms-flex: 0 1 auto;
flex: 0 1 auto;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-flex-wrap: wrap;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
margin-right: -0.5rem;
margin-left: -0.5rem;
}
.c9 {
box-sizing: border-box;
-webkit-flex: 0 0 auto;
-ms-flex: 0 0 auto;
flex: 0 0 auto;
padding-right: 0.5rem;
padding-left: 0.5rem;
}
.c22 {
box-sizing: border-box;
-webkit-flex: 0 0 auto;
-ms-flex: 0 0 auto;
flex: 0 0 auto;
padding-right: 0.5rem;
padding-left: 0.5rem;
}
.c5 { .c5 {
color: rgba(73,73,73,1); color: rgba(73,73,73,1);
font-weight: 600; font-weight: 600;
@ -789,6 +780,15 @@ exports[`renders <AddForm /> without throwing 1`] = `
outline: 0; outline: 0;
} }
.c3 {
padding-right: 1rem;
padding-left: 1rem;
}
.c7 {
padding: 1rem;
}
.c2 { .c2 {
word-wrap: break-word; word-wrap: break-word;
overflow-wrap: break-word; overflow-wrap: break-word;
@ -1011,80 +1011,6 @@ exports[`renders <AddForm /> without throwing 1`] = `
`; `;
exports[`renders <EditForm /> without throwing 1`] = ` exports[`renders <EditForm /> without throwing 1`] = `
.c3 {
padding-right: 1rem;
padding-left: 1rem;
}
.c7 {
padding: 1rem;
}
.c8 {
box-sizing: border-box;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex: 0 1 auto;
-ms-flex: 0 1 auto;
flex: 0 1 auto;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-flex-wrap: wrap;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
margin-right: -0.5rem;
margin-left: -0.5rem;
}
.c17 {
box-sizing: border-box;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex: 0 1 auto;
-ms-flex: 0 1 auto;
flex: 0 1 auto;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-flex-wrap: wrap;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
margin-right: -0.5rem;
margin-left: -0.5rem;
}
.c9 {
box-sizing: border-box;
-webkit-flex: 0 0 auto;
-ms-flex: 0 0 auto;
flex: 0 0 auto;
padding-right: 0.5rem;
padding-left: 0.5rem;
}
.c18 {
box-sizing: border-box;
-webkit-flex: 0 0 auto;
-ms-flex: 0 0 auto;
flex: 0 0 auto;
padding-right: 0.5rem;
padding-left: 0.5rem;
}
.c23 {
box-sizing: border-box;
-webkit-flex: 0 0 auto;
-ms-flex: 0 0 auto;
flex: 0 0 auto;
padding-right: 0.5rem;
padding-left: 0.5rem;
}
.c21 { .c21 {
font-family: sans-serif; font-family: sans-serif;
font-size: 100%; font-size: 100%;
@ -1465,6 +1391,71 @@ exports[`renders <EditForm /> without throwing 1`] = `
margin-left: 1.75rem; margin-left: 1.75rem;
} }
.c8 {
box-sizing: border-box;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex: 0 1 auto;
-ms-flex: 0 1 auto;
flex: 0 1 auto;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-flex-wrap: wrap;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
margin-right: -0.5rem;
margin-left: -0.5rem;
}
.c17 {
box-sizing: border-box;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex: 0 1 auto;
-ms-flex: 0 1 auto;
flex: 0 1 auto;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-flex-wrap: wrap;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
margin-right: -0.5rem;
margin-left: -0.5rem;
}
.c9 {
box-sizing: border-box;
-webkit-flex: 0 0 auto;
-ms-flex: 0 0 auto;
flex: 0 0 auto;
padding-right: 0.5rem;
padding-left: 0.5rem;
}
.c18 {
box-sizing: border-box;
-webkit-flex: 0 0 auto;
-ms-flex: 0 0 auto;
flex: 0 0 auto;
padding-right: 0.5rem;
padding-left: 0.5rem;
}
.c23 {
box-sizing: border-box;
-webkit-flex: 0 0 auto;
-ms-flex: 0 0 auto;
flex: 0 0 auto;
padding-right: 0.5rem;
padding-left: 0.5rem;
}
.c14 { .c14 {
background-color: rgb(216,216,216); background-color: rgb(216,216,216);
margin: 0; margin: 0;
@ -1789,6 +1780,15 @@ exports[`renders <EditForm /> without throwing 1`] = `
outline: 0; outline: 0;
} }
.c3 {
padding-right: 1rem;
padding-left: 1rem;
}
.c7 {
padding: 1rem;
}
.c5 { .c5 {
word-break: break-all; word-break: break-all;
line-height: 1.5; line-height: 1.5;

View File

@ -1232,7 +1232,7 @@ exports[`renders <Item {...item} /> without throwing 1`] = `
name="td" name="td"
selected={undefined} selected={undefined}
> >
about 1 month about 2 months
</td> </td>
<td <td
className="c11" className="c11"
@ -1240,7 +1240,7 @@ exports[`renders <Item {...item} /> without throwing 1`] = `
name="td" name="td"
selected={undefined} selected={undefined}
> >
about 1 month about 2 months
</td> </td>
<td <td
className="c12 c13" className="c12 c13"

View File

@ -1,17 +0,0 @@
import React from 'react';
import { TableTr, TableTd } from 'joyent-ui-toolkit';
export default ({ rule = '', global = false, enabled = false }) => (
<TableTr>
<TableTd>
<code>{rule}</code>
</TableTd>
<TableTd center middle>
<code>{JSON.stringify(global)}</code>
</TableTd>
<TableTd center middle>
<code>{JSON.stringify(enabled)}</code>
</TableTd>
</TableTr>
);

View File

@ -1,6 +1,3 @@
export { default as List } from './list'; export { default as List } from './list';
export { default as KeyValue } from './key-value';
export { default as Network } from './network';
export { default as FirewallRule } from './firewall-rule';
export { default as Resize } from './resize'; export { default as Resize } from './resize';
export { default as Snapshots } from './snapshots'; export { default as Snapshots } from './snapshots';

View File

@ -1,6 +1,5 @@
import React from 'react'; import React from 'react';
import { Field } from 'redux-form'; import { Field } from 'redux-form';
import KeyValue from './key-value';
import { import {
Row, Row,
@ -11,6 +10,8 @@ import {
Button Button
} from 'joyent-ui-toolkit'; } from 'joyent-ui-toolkit';
import KeyValue from '@components/key-value';
export const MenuForm = ({ searchable, onAdd }) => ( export const MenuForm = ({ searchable, onAdd }) => (
<form> <form>
<Row> <Row>

View File

@ -1,13 +0,0 @@
import React from 'react';
import forceArray from 'force-array';
import { TableTr, TableTd } from 'joyent-ui-toolkit';
export default ({ name, gateway, subnet, resolvers = [] }) => (
<TableTr>
<TableTd>{name}</TableTd>
<TableTd>{gateway}</TableTd>
<TableTd>{subnet}</TableTd>
<TableTd>{forceArray(resolvers).join('\u00B7')}</TableTd>
</TableTr>
);

View File

@ -3,7 +3,6 @@ import { Field } from 'redux-form';
import titleCase from 'title-case'; import titleCase from 'title-case';
import distanceInWordsToNow from 'date-fns/distance_in_words_to_now'; import distanceInWordsToNow from 'date-fns/distance_in_words_to_now';
import remcalc from 'remcalc'; import remcalc from 'remcalc';
import { KeyValue } from '@components/instances';
import { import {
FormGroup, FormGroup,
@ -23,6 +22,8 @@ import {
DotIcon DotIcon
} from 'joyent-ui-toolkit'; } from 'joyent-ui-toolkit';
import KeyValue from '@components/key-value';
const stateColor = { const stateColor = {
QUEUED: 'primary', QUEUED: 'primary',
CREATED: 'green' CREATED: 'green'

View File

@ -0,0 +1,320 @@
import React, { Fragment } from 'react';
import { Field } from 'redux-form';
import { Margin, Padding } from 'styled-components-spacing';
import styled from 'styled-components';
import Flex, { FlexItem } from 'styled-flex-component';
import remcalc from 'remcalc';
import { Row, Col } from 'react-styled-flexboxgrid';
import {
H4,
P,
Small,
DotIcon,
Card,
CardHeader,
CardHeaderMeta,
CardHeaderBox,
CardOutlet,
FormGroup,
FormLabel,
Input,
Checkbox,
Divider,
FabricIcon,
InstanceCountIcon,
DataCenterIcon,
PublicIcon,
PrivateIcon,
ArrowIcon
} from 'joyent-ui-toolkit';
const Box = styled.div`
display: inline-block;
background-color: ${props => props.theme.white};
border: ${remcalc(1)} solid ${props => props.theme.grey};
border-radius: ${remcalc(4)};
min-width: ${remcalc(300)};
`;
export const Collapsed = ({ name, fabric, ...network }) => (
<Row>
<Col xs={12} sm={8}>
<Margin inline right={3} top={3}>
<Box>
<Flex column>
<FlexItem>
<Margin left={3} right={3} top={2} bottom={2}>
<P>{name}</P>
</Margin>
</FlexItem>
<FlexItem>
<Divider height={remcalc(1)} />
</FlexItem>
<FlexItem>
<Margin left={3} right={3} top={2} bottom={2}>
<Flex>
<Margin right={4}>
<FlexItem>
<Flex alignCenter>
<FlexItem>
<Margin right={1}>
{network.public ? <PublicIcon /> : <PrivateIcon />}
</Margin>
</FlexItem>
<FlexItem>
<P>{network.public ? 'Public' : 'Private'}</P>
</FlexItem>
</Flex>
</FlexItem>
</Margin>
<Margin>
<FlexItem>
<Flex alignCenter>
<FlexItem>
<Margin right={1}>
{fabric ? <FabricIcon /> : <DataCenterIcon />}
</Margin>
</FlexItem>
<FlexItem>
<P>
{fabric ? 'Fabric network' : 'Data center network'}
</P>
</FlexItem>
</Flex>
</FlexItem>
</Margin>
</Flex>
</Margin>
</FlexItem>
</Flex>
</Box>
</Margin>
</Col>
</Row>
);
export const Expanded = ({
id,
name,
fabric,
subnet,
description,
machines = [],
provision_start_ip,
provision_end_ip,
selected = false,
infoExpanded = false,
machinesExpanded = false,
readOnly = false,
onInfoClick,
onMachinesClick,
...network
}) => (
<Row>
<Col xs={12} sm={8}>
<Margin bottom={4}>
<Card shadow>
<CardHeader secondary={selected}>
{!readOnly ? (
<CardHeaderBox>
<FormGroup name={id} field={Field}>
<Checkbox noMargin checked={selected} />
</FormGroup>
</CardHeaderBox>
) : null}
<CardHeaderMeta>
<Padding left={readOnly ? 3 : 0} right={readOnly ? 3 : 0}>
<H4 white={selected}>{name}</H4>
</Padding>
</CardHeaderMeta>
</CardHeader>
<CardOutlet>
<Padding all={4}>
{description && (
<Margin bottom={3}>
<P>{description}</P>
</Margin>
)}
<Flex>
<Margin right={4}>
<FlexItem>
<Flex alignCenter>
<FlexItem>
<Margin right={1}>
{network.public ? <PublicIcon /> : <PrivateIcon />}
</Margin>
</FlexItem>
<FlexItem>
<P>{network.public ? 'Public' : 'Private'}</P>
</FlexItem>
</Flex>
</FlexItem>
</Margin>
<Margin right={4}>
<FlexItem>
<Flex alignCenter>
<FlexItem>
<Margin right={1}>
{fabric ? <FabricIcon /> : <DataCenterIcon />}
</Margin>
</FlexItem>
<FlexItem>
<P>
{fabric ? 'Fabric network' : 'Data center network'}
</P>
</FlexItem>
</Flex>
</FlexItem>
</Margin>
{fabric ? (
<Margin right={4}>
<FlexItem>
<Flex alignCenter>
<FlexItem>
<Margin right={1}>
<InstanceCountIcon />
</Margin>
</FlexItem>
<FlexItem>
<P>
{`${machines.length} instance${
machines.length === 1 ? '' : 's'
}`}
</P>
</FlexItem>
</Flex>
</FlexItem>
</Margin>
) : null}
</Flex>
{fabric ? (
<Fragment>
<Margin top={3}>
<Card collapsed={!infoExpanded} actionable={!infoExpanded}>
<CardHeader
secondary={false}
transparent={false}
onClick={onInfoClick}
>
<CardHeaderMeta>
<Padding left={3} right={3}>
<P>Network information</P>
</Padding>
</CardHeaderMeta>
<CardHeaderBox>
<ArrowIcon direction={infoExpanded ? 'up' : 'down'} />
</CardHeaderBox>
</CardHeader>
{infoExpanded ? (
<CardOutlet>
<Padding all={3}>
<Flex column>
{network.internet_nat ? (
<FlexItem>
<Margin bottom={3}>
<Flex alignCenter>
<Margin right={1}>
<DotIcon
width={remcalc(12)}
height={remcalc(12)}
color="green"
/>
</Margin>
<Small bold noMargin>
Outbound internet access enabled
</Small>
</Flex>
</Margin>
</FlexItem>
) : null}
<FlexItem>
<FormGroup name="id">
<FormLabel>ID</FormLabel>
<Input
onBlur={null}
big
monospace
type="text"
value={id}
/>
</FormGroup>
</FlexItem>
<FlexItem>
<FormGroup name="subnet">
<FormLabel>Subnet</FormLabel>
<Input
onBlur={null}
big
monospace
type="text"
value={subnet}
/>
</FormGroup>
</FlexItem>
<FlexItem>
<FormGroup name="ip-range">
<FormLabel>IP range</FormLabel>
<Input
onBlur={null}
big
monospace
type="text"
value={`${provision_start_ip} - ${provision_end_ip}`}
/>
</FormGroup>
</FlexItem>
</Flex>
</Padding>
</CardOutlet>
) : null}
</Card>
</Margin>
<Margin top={3}>
<Card
collapsed={!machinesExpanded}
actionable={!machinesExpanded}
>
<CardHeader
secondary={false}
transparent={false}
onClick={onMachinesClick}
>
<CardHeaderMeta>
<Padding left={3} right={3}>
<P>Instances on network</P>
</Padding>
</CardHeaderMeta>
<CardHeaderBox>
<ArrowIcon
direction={machinesExpanded ? 'up' : 'down'}
/>
</CardHeaderBox>
</CardHeader>
{machinesExpanded ? (
<CardOutlet>
<Padding all={3}>
<Flex column>
{machines.map(({ name }) => (
<FlexItem>
<P>{name}</P>
</FlexItem>
))}
</Flex>
</Padding>
</CardOutlet>
) : null}
</Card>
</Margin>
</Fragment>
) : null}
</Padding>
</CardOutlet>
</Card>
</Margin>
</Col>
</Row>
);
export default ({ small = false, ...rest }) =>
small ? <Collapsed {...rest} /> : <Expanded {...rest} />;

View File

@ -1,8 +1,8 @@
import React from 'react'; import React from 'react';
import { Margin } from 'styled-components-spacing'; import { Margin } from 'styled-components-spacing';
import { KeyValue } from '@components/instances';
import { TagItem } from 'joyent-ui-toolkit'; import { TagItem } from 'joyent-ui-toolkit';
import KeyValue from '@components/key-value';
export const AddForm = props => ( export const AddForm = props => (
<KeyValue {...props} method="add" input="input" type="tag" expanded /> <KeyValue {...props} method="add" input="input" type="tag" expanded />

View File

@ -1606,23 +1606,23 @@ Array [
cursor: not-allowed; cursor: not-allowed;
} }
.c16 { .c17 {
color: rgba(73,73,73,1); color: rgba(73,73,73,1);
line-height: 1.5rem; line-height: 1.5rem;
font-size: 0.9375rem; font-size: 0.9375rem;
margin: 0; margin: 0;
} }
.c16 + p, .c17 + p,
.c16 + small, .c17 + small,
.c16 + h1, .c17 + h1,
.c16 + h2, .c17 + h2,
.c16 + label, .c17 + label,
.c16 + h3, .c17 + h3,
.c16 + h4, .c17 + h4,
.c16 + h5, .c17 + h5,
.c16 + div, .c17 + div,
.c16 + span { .c17 + span {
padding-bottom: 2.25rem; padding-bottom: 2.25rem;
} }
@ -1654,7 +1654,7 @@ Array [
padding-left: 0.5rem; padding-left: 0.5rem;
} }
.c12 { .c13 {
color: rgba(73,73,73,1); color: rgba(73,73,73,1);
font-weight: 600; font-weight: 600;
line-height: 1.5rem; line-height: 1.5rem;
@ -1662,20 +1662,20 @@ Array [
margin: 0; margin: 0;
} }
.c12 + p, .c13 + p,
.c12 + small, .c13 + small,
.c12 + h1, .c13 + h1,
.c12 + h2, .c13 + h2,
.c12 + label, .c13 + label,
.c12 + h3, .c13 + h3,
.c12 + h4, .c13 + h4,
.c12 + h5, .c13 + h5,
.c12 + div, .c13 + div,
.c12 + span { .c13 + span {
margin-top: 0.75rem; margin-top: 0.75rem;
} }
.c30 { .c31 {
color: rgba(73,73,73,1); color: rgba(73,73,73,1);
font-weight: 600; font-weight: 600;
line-height: 1.5rem; line-height: 1.5rem;
@ -1685,16 +1685,16 @@ Array [
color: rgb(255,255,255); color: rgb(255,255,255);
} }
.c30 + p, .c31 + p,
.c30 + small, .c31 + small,
.c30 + h1, .c31 + h1,
.c30 + h2, .c31 + h2,
.c30 + label, .c31 + label,
.c30 + h3, .c31 + h3,
.c30 + h4, .c31 + h4,
.c30 + h5, .c31 + h5,
.c30 + div, .c31 + div,
.c30 + span { .c31 + span {
margin-top: 0.75rem; margin-top: 0.75rem;
} }
@ -1872,7 +1872,7 @@ Array [
color: inherit; color: inherit;
} }
.c13 { .c14 {
box-sizing: content-box; box-sizing: content-box;
display: -webkit-box; display: -webkit-box;
display: -webkit-flex; display: -webkit-flex;
@ -1908,15 +1908,15 @@ Array [
background-color: transparent; background-color: transparent;
} }
.c13 > [name='card']:not(:last-child) { .c14 > [name='card']:not(:last-child) {
margin-bottom: 0.8125rem; margin-bottom: 0.8125rem;
} }
.c13 > [name='card']:last-child { .c14 > [name='card']:last-child {
margin-bottom: 0.4375rem; margin-bottom: 0.4375rem;
} }
.c23 { .c24 {
box-sizing: content-box; box-sizing: content-box;
display: -webkit-box; display: -webkit-box;
display: -webkit-flex; display: -webkit-flex;
@ -1944,7 +1944,7 @@ Array [
flex: 0 0 2.875rem; flex: 0 0 2.875rem;
} }
.c24 { .c25 {
box-sizing: content-box; box-sizing: content-box;
display: -webkit-box; display: -webkit-box;
display: -webkit-flex; display: -webkit-flex;
@ -1982,12 +1982,12 @@ Array [
box-shadow: none; box-shadow: none;
} }
.c24 button { .c25 button {
margin-bottom: 0; margin-bottom: 0;
margin-top: 0; margin-top: 0;
} }
.c25 { .c26 {
box-sizing: content-box; box-sizing: content-box;
display: -webkit-box; display: -webkit-box;
display: -webkit-flex; display: -webkit-flex;
@ -2046,7 +2046,7 @@ Array [
color: inherit; color: inherit;
} }
.c27 { .c28 {
box-sizing: content-box; box-sizing: content-box;
display: -webkit-box; display: -webkit-box;
display: -webkit-flex; display: -webkit-flex;
@ -2084,12 +2084,12 @@ Array [
margin: -0.0625rem -0.0625rem 0 -0.0625rem; margin: -0.0625rem -0.0625rem 0 -0.0625rem;
} }
.c27 button { .c28 button {
margin-bottom: 0; margin-bottom: 0;
margin-top: 0; margin-top: 0;
} }
.c28 { .c29 {
box-sizing: content-box; box-sizing: content-box;
display: -webkit-box; display: -webkit-box;
display: -webkit-flex; display: -webkit-flex;
@ -2145,7 +2145,7 @@ Array [
box-shadow: none; box-shadow: none;
} }
.c29 { .c30 {
box-sizing: content-box; box-sizing: content-box;
display: -webkit-box; display: -webkit-box;
display: -webkit-flex; display: -webkit-flex;
@ -2281,7 +2281,7 @@ Array [
-webkit-padding-after: 0; -webkit-padding-after: 0;
} }
.c19 { .c20 {
-webkit-order: 0; -webkit-order: 0;
-ms-flex-order: 0; -ms-flex-order: 0;
order: 0; order: 0;
@ -2297,7 +2297,7 @@ Array [
flex-shrink: 1; flex-shrink: 1;
} }
.c21 { .c22 {
margin-right: 0.25rem; margin-right: 0.25rem;
} }
@ -2305,28 +2305,33 @@ Array [
margin-bottom: 2rem; margin-bottom: 2rem;
} }
.c15 { .c16 {
margin-bottom: 1rem; margin-bottom: 1rem;
} }
.c18 { .c19 {
margin-right: 2rem; margin-right: 2rem;
} }
.c22 { .c23 {
margin-top: 1rem; margin-top: 1rem;
} }
.c14 { .c12 {
padding-right: 0;
padding-left: 0;
}
.c15 {
padding: 2rem; padding: 2rem;
} }
.c26 { .c27 {
padding-right: 1rem; padding-right: 1rem;
padding-left: 1rem; padding-left: 1rem;
} }
.c17 { .c18 {
display: -webkit-box; display: -webkit-box;
display: -webkit-flex; display: -webkit-flex;
display: -ms-flexbox; display: -ms-flexbox;
@ -2346,7 +2351,7 @@ Array [
align-content: stretch; align-content: stretch;
} }
.c20 { .c21 {
display: -webkit-box; display: -webkit-box;
display: -webkit-flex; display: -webkit-flex;
display: -ms-flexbox; display: -ms-flexbox;
@ -2453,46 +2458,50 @@ Array [
disabled={false} disabled={false}
name="card-header-meta" name="card-header-meta"
> >
<h4 <div
className="c12" className="c12"
> >
name <h4
</h4> className="c13"
>
name
</h4>
</div>
</div> </div>
</div> </div>
<div <div
className="c13" className="c14"
name="card-outlet" name="card-outlet"
> >
<div <div
className="c14" className="c15"
> >
<div <div
className="c15" className="c16"
> >
<p <p
className="c16" className="c17"
> >
description description
</p> </p>
</div> </div>
<div <div
className="c17" className="c18"
> >
<div <div
className="c18" className="c19"
> >
<div <div
className="c19" className="c20"
> >
<div <div
className="c20" className="c21"
> >
<div <div
className="c19" className="c20"
> >
<div <div
className="c21" className="c22"
> >
<svg <svg
className="" className=""
@ -2515,10 +2524,10 @@ Array [
</div> </div>
</div> </div>
<div <div
className="c19" className="c20"
> >
<p <p
className="c16" className="c17"
> >
Private Private
</p> </p>
@ -2527,19 +2536,19 @@ Array [
</div> </div>
</div> </div>
<div <div
className="c18" className="c19"
> >
<div <div
className="c19" className="c20"
> >
<div <div
className="c20" className="c21"
> >
<div <div
className="c19" className="c20"
> >
<div <div
className="c21" className="c22"
> >
<svg <svg
height="16.2" height="16.2"
@ -2555,10 +2564,10 @@ Array [
</div> </div>
</div> </div>
<div <div
className="c19" className="c20"
> >
<p <p
className="c16" className="c17"
> >
Fabric network Fabric network
</p> </p>
@ -2566,31 +2575,165 @@ Array [
</div> </div>
</div> </div>
</div> </div>
<div
className="c19"
>
<div
className="c20"
>
<div
className="c21"
>
<div
className="c20"
>
<div
className="c22"
>
<svg
className=""
height="17"
innerRef={undefined}
style={
Object {
"transform": "rotate(0deg)",
}
}
version="1.1"
viewBox="0 0 17 17"
width="17"
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
>
<g
transform="translate(-1105 974)"
>
<g>
<g>
<use
fill="rgba(73, 73, 73, 1)"
transform="translate(1105 -974)"
xlinkHref="#c"
/>
</g>
<g>
<use
fill="rgba(73, 73, 73, 1)"
transform="translate(1111 -974)"
xlinkHref="#c"
/>
</g>
<g>
<use
fill="rgba(73, 73, 73, 1)"
transform="translate(1117 -974)"
xlinkHref="#c"
/>
</g>
<g>
<use
fill="rgba(73, 73, 73, 1)"
transform="translate(1105 -968)"
xlinkHref="#c"
/>
</g>
<g>
<g>
<use
fill="rgba(73, 73, 73, 1)"
transform="translate(1111 -968)"
xlinkHref="#d"
/>
</g>
</g>
<g>
<g>
<use
fill="rgba(73, 73, 73, 1)"
transform="translate(1117 -968)"
xlinkHref="#d"
/>
</g>
</g>
<g>
<g>
<use
fill="rgba(73, 73, 73, 1)"
transform="translate(1105 -962)"
xlinkHref="#d"
/>
</g>
</g>
<g>
<g>
<use
fill="rgba(73, 73, 73, 1)"
transform="translate(1111 -962)"
xlinkHref="#d"
/>
</g>
</g>
<g>
<g>
<use
fill="rgba(73, 73, 73, 1)"
transform="translate(1117 -962)"
xlinkHref="#d"
/>
</g>
</g>
</g>
</g>
<defs>
<path
d="M 2.5 5C 3.88071 5 5 3.88071 5 2.5C 5 1.11929 3.88071 0 2.5 0C 1.11929 0 0 1.11929 0 2.5C 0 3.88071 1.11929 5 2.5 5Z"
id="c"
/>
<path
d="M 2.5 1C 3.3 1 4 1.7 4 2.5C 4 3.3 3.3 4 2.5 4C 1.7 4 1 3.3 1 2.5C 1 1.7 1.7 1 2.5 1ZM 2.5 0C 1.1 0 0 1.1 0 2.5C 0 3.9 1.1 5 2.5 5C 3.9 5 5 3.9 5 2.5C 5 1.1 3.9 0 2.5 0Z"
id="d"
/>
</defs>
</svg>
</div>
</div>
<div
className="c20"
>
<p
className="c17"
>
0 instances
</p>
</div>
</div>
</div>
</div>
</div> </div>
<div <div
className="c22" className="c23"
> >
<div <div
className="c23" className="c24"
disabled={false} disabled={false}
name="card" name="card"
> >
<div <div
className="c24" className="c25"
disabled={false} disabled={false}
name="card-header" name="card-header"
onClick={[Function]} onClick={[Function]}
> >
<div <div
className="c25" className="c26"
disabled={false} disabled={false}
name="card-header-meta" name="card-header-meta"
> >
<div <div
className="c26" className="c27"
> >
<p <p
className="c16" className="c17"
> >
Network information Network information
</p> </p>
@ -2623,6 +2766,62 @@ Array [
</div> </div>
</div> </div>
</div> </div>
<div
className="c23"
>
<div
className="c24"
disabled={false}
name="card"
>
<div
className="c25"
disabled={false}
name="card-header"
onClick={[Function]}
>
<div
className="c26"
disabled={false}
name="card-header-meta"
>
<div
className="c27"
>
<p
className="c17"
>
Instances on network
</p>
</div>
</div>
<div
className="c5"
disabled={false}
name="card-header-box"
>
<svg
className=""
height="6"
innerRef={undefined}
style={
Object {
"transform": "rotate(0deg)",
}
}
viewBox="0 0 9.6 6"
width="9.6"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M9.6,1.12,8.24,0,4.8,3.5,1.36,0,0,1.12,4.8,6Z"
fill="rgba(73, 73, 73, 1)"
/>
</svg>
</div>
</div>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -2644,12 +2843,12 @@ Array [
name="card" name="card"
> >
<div <div
className="c27" className="c28"
disabled={false} disabled={false}
name="card-header" name="card-header"
> >
<div <div
className="c28" className="c29"
disabled={false} disabled={false}
name="card-header-box" name="card-header-box"
> >
@ -2687,50 +2886,54 @@ Array [
</div> </div>
</div> </div>
<div <div
className="c29" className="c30"
disabled={false} disabled={false}
name="card-header-meta" name="card-header-meta"
> >
<h4 <div
className="c30" className="c12"
> >
name2 <h4
</h4> className="c31"
>
name2
</h4>
</div>
</div> </div>
</div> </div>
<div <div
className="c13" className="c14"
name="card-outlet" name="card-outlet"
> >
<div <div
className="c14" className="c15"
> >
<div <div
className="c15" className="c16"
> >
<p <p
className="c16" className="c17"
> >
description2 description2
</p> </p>
</div> </div>
<div <div
className="c17" className="c18"
> >
<div <div
className="c18" className="c19"
> >
<div <div
className="c19" className="c20"
> >
<div <div
className="c20" className="c21"
> >
<div <div
className="c19" className="c20"
> >
<div <div
className="c21" className="c22"
> >
<svg <svg
className="" className=""
@ -2753,10 +2956,10 @@ Array [
</div> </div>
</div> </div>
<div <div
className="c19" className="c20"
> >
<p <p
className="c16" className="c17"
> >
Private Private
</p> </p>
@ -2765,19 +2968,19 @@ Array [
</div> </div>
</div> </div>
<div <div
className="c18" className="c19"
> >
<div <div
className="c19" className="c20"
> >
<div <div
className="c20" className="c21"
> >
<div <div
className="c19" className="c20"
> >
<div <div
className="c21" className="c22"
> >
<svg <svg
height="13" height="13"
@ -2793,10 +2996,10 @@ Array [
</div> </div>
</div> </div>
<div <div
className="c19" className="c20"
> >
<p <p
className="c16" className="c17"
> >
Data center network Data center network
</p> </p>
@ -2887,6 +3090,11 @@ Array [
background-color: rgb(59,70,204); background-color: rgb(59,70,204);
border-radius: 0.25rem; border-radius: 0.25rem;
border: solid 0.0625rem rgb(45,56,132); border: solid 0.0625rem rgb(45,56,132);
cursor: not-allowed;
pointer-events: none;
color: rgb(216,216,216);
background-color: rgb(250,250,250);
border-color: rgb(216,216,216);
} }
.c1:focus { .c1:focus {
@ -2915,6 +3123,23 @@ Array [
pointer-events: none; pointer-events: none;
} }
.c1:focus {
background-color: rgb(250,250,250);
border-color: rgb(216,216,216);
}
.c1:hover {
background-color: rgb(250,250,250);
border-color: rgb(250,250,250);
}
.c1:active,
.c1:active:hover,
.c1:active:focus {
background-color: rgb(250,250,250);
border-color: rgb(250,250,250);
}
.c0 { .c0 {
margin-bottom: 2rem; margin-bottom: 2rem;
} }
@ -2924,7 +3149,7 @@ Array [
> >
<button <button
className="c1 c2 c3" className="c1 c2 c3"
disabled={false} disabled={true}
href="" href=""
onClick={undefined} onClick={undefined}
type="button" type="button"
@ -3105,9 +3330,9 @@ Array [
<h3 <h3
className="c0" className="c0"
> >
1 0
network network
s
added added
</h3>, </h3>,
.c7 { .c7 {

View File

@ -7776,35 +7776,6 @@ Array [
border-color: rgb(216,216,216); border-color: rgb(216,216,216);
} }
.c5 {
box-sizing: border-box;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex: 0 1 auto;
-ms-flex: 0 1 auto;
flex: 0 1 auto;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-flex-wrap: wrap;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
margin-right: -0.5rem;
margin-left: -0.5rem;
}
.c4 {
background-color: rgb(216,216,216);
margin: 0;
height: 0.0625rem;
}
.c3 {
margin-top: 2rem;
}
<div> <div>
<button <button
className="c0 c1 c2" className="c0 c1 c2"
@ -7814,14 +7785,6 @@ Array [
> >
Edit Edit
</button> </button>
<div
className="c3"
>
<div
className="c4 c5"
height="0.0625rem"
/>
</div>
</div>, </div>,
] ]
`; `;

View File

@ -84,7 +84,7 @@ it('renders <UserScript expanded edit formOpen script /> without throwing', () =
renderer renderer
.create( .create(
<Theme> <Theme>
<UserScript expanded edit formOpen script={{ value: 'hey' }}/> <UserScript expanded edit formOpen script={{ value: 'hey' }} />
</Theme> </Theme>
) )
.toJSON() .toJSON()

View File

@ -12,8 +12,8 @@ import { AffinityIcon, Button, H3, Divider } from 'joyent-ui-toolkit';
import Title from '@components/create-instance/title'; import Title from '@components/create-instance/title';
import { Rule, Header } from '@components/create-instance/affinity'; import { Rule, Header } from '@components/create-instance/affinity';
import KeyValue from '@components/instances/key-value'; import Description from '@components/description';
import Description from '@components/create-instance/description'; import KeyValue from '@components/key-value';
const FORM_NAME_CREATE = 'CREATE-INSTANCE-AFFINITY-ADD'; const FORM_NAME_CREATE = 'CREATE-INSTANCE-AFFINITY-ADD';
const FORM_NAME_EDIT = i => `CREATE-INSTANCE-AFFINITY-EDIT-${i}`; const FORM_NAME_EDIT = i => `CREATE-INSTANCE-AFFINITY-EDIT-${i}`;

View File

@ -24,8 +24,6 @@ import {
StatusLoader StatusLoader
} from 'joyent-ui-toolkit'; } from 'joyent-ui-toolkit';
import getAccount from '../../graphql/get-account.gql';
import { import {
Hostname, Hostname,
Header, Header,
@ -33,9 +31,10 @@ import {
HostnamesHeader HostnamesHeader
} from '@components/create-instance/cns'; } from '@components/create-instance/cns';
import Tag from '@components/tags';
import Title from '@components/create-instance/title'; import Title from '@components/create-instance/title';
import Tag from '@components/instances/tags'; import Description from '@components/description';
import Description from '@components/create-instance/description'; import getAccount from '@graphql/get-account.gql';
const CNS_FORM = 'create-instance-cns'; const CNS_FORM = 'create-instance-cns';

View File

@ -9,13 +9,13 @@ import { connect } from 'react-redux';
import get from 'lodash.get'; import get from 'lodash.get';
import forceArray from 'force-array'; import forceArray from 'force-array';
import { StatusLoader, FirewallIcon, H3, Button } from 'joyent-ui-toolkit';
import Title from '@components/create-instance/title'; import Title from '@components/create-instance/title';
import Description from '@components/create-instance/description'; import Description from '@components/description';
import FirewallForm from '@components/create-instance/firewall'; import FirewallForm from '@components/create-instance/firewall';
import ListFwRules from '@graphql/list-fw-rules.gql'; import ListFwRules from '@graphql/list-fw-rules.gql';
import { StatusLoader, FirewallIcon, H3, Button } from 'joyent-ui-toolkit';
const FORM_NAME = 'CREATE-INSTANCE-FIREWALL'; const FORM_NAME = 'CREATE-INSTANCE-FIREWALL';
const Firewall = ({ const Firewall = ({

View File

@ -6,12 +6,12 @@ import { set } from 'react-redux-values';
import get from 'lodash.get'; import get from 'lodash.get';
import { InstanceTypeIcon, StatusLoader } from 'joyent-ui-toolkit'; import { InstanceTypeIcon, StatusLoader } from 'joyent-ui-toolkit';
import Description from '@components/description';
import Image, { Preview } from '@components/create-instance/image'; import Image, { Preview } from '@components/create-instance/image';
import Title from '@components/create-instance/title'; import Title from '@components/create-instance/title';
import Description from '@components/create-instance/description'; import imageData from '@data/images-map.json';
import imageData from '../../data/images-map.json'; import getImages from '@graphql/get-images.gql';
import getImages from '../../graphql/get-images.gql';
const ImageContainer = ({ const ImageContainer = ({
expanded, expanded,

View File

@ -10,8 +10,8 @@ import get from 'lodash.get';
import { MetadataIcon, Button, H3 } from 'joyent-ui-toolkit'; import { MetadataIcon, Button, H3 } from 'joyent-ui-toolkit';
import Title from '@components/create-instance/title'; import Title from '@components/create-instance/title';
import Description from '@components/create-instance/description'; import Description from '@components/description';
import KeyValue from '@components/instances/key-value'; import KeyValue from '@components/key-value';
const FORM_NAME_CREATE = 'CREATE-INSTANCE-METADATA-ADD'; const FORM_NAME_CREATE = 'CREATE-INSTANCE-METADATA-ADD';
const FORM_NAME_EDIT = i => `CREATE-INSTANCE-METADATA-EDIT-${i}`; const FORM_NAME_EDIT = i => `CREATE-INSTANCE-METADATA-EDIT-${i}`;
@ -75,7 +75,8 @@ export const Metadata = ({
input="textarea" input="textarea"
type="metadata" type="metadata"
onToggleExpanded={() => onToggleExpanded={() =>
expanded ? handleToggleExpanded(index) : null} expanded ? handleToggleExpanded(index) : null
}
onCancel={() => handleCancelEdit(index)} onCancel={() => handleCancelEdit(index)}
onRemove={() => handleRemoveMetadata(index)} onRemove={() => handleRemoveMetadata(index)}
/> />
@ -138,7 +139,7 @@ export default compose(
set({ name: 'create-instance-metadata-proceeded', value: true }) set({ name: 'create-instance-metadata-proceeded', value: true })
); );
return history.push(`/instances/~create/user-script`); return history.push('/instances/~create/user-script');
}, },
handleEdit: () => { handleEdit: () => {
return history.push(`/instances/~create/metadata`); return history.push(`/instances/~create/metadata`);

View File

@ -12,7 +12,7 @@ import punycode from 'punycode';
import { NameIcon, H3, Button } from 'joyent-ui-toolkit'; import { NameIcon, H3, Button } from 'joyent-ui-toolkit';
import Name from '@components/create-instance/name'; import Name from '@components/create-instance/name';
import Description from '@components/create-instance/description'; import Description from '@components/description';
import Title from '@components/create-instance/title'; import Title from '@components/create-instance/title';
import GetInstance from '@graphql/get-instance-small.gql'; import GetInstance from '@graphql/get-instance-small.gql';
import GetRandomName from '@graphql/get-random-name.gql'; import GetRandomName from '@graphql/get-random-name.gql';

View File

@ -9,97 +9,94 @@ import forceArray from 'force-array';
import { NetworkIcon, Button, H3, StatusLoader } from 'joyent-ui-toolkit'; import { NetworkIcon, Button, H3, StatusLoader } from 'joyent-ui-toolkit';
import Description from '@components/create-instance/description'; import Network from '@components/network';
import Description from '@components/description';
import Title from '@components/create-instance/title'; import Title from '@components/create-instance/title';
import Network from '@components/create-instance/network';
import ListNetworks from '@graphql/list-networks.gql'; import ListNetworks from '@graphql/list-networks.gql';
const FORM_NAME = 'CREATE-INSTANCE-NETWORKS'; const FORM_NAME = 'CREATE-INSTANCE-NETWORKS';
export const Networks = ({ export const Networks = ({
networks = [], networks = [],
selected = [],
expanded = false, expanded = false,
proceeded = false, proceeded = false,
loading = false, loading = false,
setInfoExpanded, setInfoExpanded,
setMachinesExpanded,
handleNext, handleNext,
handleEdit handleEdit
}) => { }) => (
const selected = networks.filter(({ selected }) => selected); <Fragment>
<Title
return ( onClick={!expanded && !proceeded && handleEdit}
<Fragment> icon={<NetworkIcon />}
<Title >
onClick={!expanded && !proceeded && handleEdit} Networks
icon={<NetworkIcon />} </Title>
> {expanded ? (
Networks <Description>
</Title> Instances are automatically connected to a private fabric network, which
{expanded ? ( is the best choice for internal communication within your application.
<Description> Data center networks are the best choice for exposing your application
Instances are automatically connected to a private fabric network, to the public internet (if the data center network is a public network).{' '}
which is the best choice for internal communication within your <a
application. Data center networks are the best choice for exposing target="__blank"
your application to the public internet (if the data center network is href="https://docs.joyent.com/public-cloud/network/sdn"
a public network).{' '}
<a
target="__blank"
href="https://docs.joyent.com/public-cloud/network/sdn"
>
Read more
</a>
</Description>
) : null}
{proceeded && !expanded ? (
<H3>
{selected.length} network{selected.length === 1 ? '' : 's'} added
</H3>
) : null}
{loading && expanded ? <StatusLoader /> : null}
{!loading ? (
<ReduxForm
form={FORM_NAME}
destroyOnUnmount={false}
forceUnregisterOnUnmount={true}
> >
{props => ( Read more
<form> </a>
{networks.map( </Description>
({ id, selected, infoExpanded, ...network }) => ) : null}
!expanded && !selected ? null : ( {proceeded && !expanded ? (
<Network <H3>
key={id} {selected.length} network{selected.length === 1 ? '' : 's'} added
id={id} </H3>
selected={selected} ) : null}
infoExpanded={infoExpanded} {loading && expanded ? <StatusLoader /> : null}
small={!expanded && selected} {!loading ? (
onInfoClick={() => setInfoExpanded(id, !infoExpanded)} <ReduxForm
{...network} form={FORM_NAME}
/> destroyOnUnmount={false}
) forceUnregisterOnUnmount={true}
)} >
</form> {props => (
)} <form>
</ReduxForm> {networks.map(
({ id, selected, infoExpanded, machinesExpanded, ...network }) =>
!expanded && !selected ? null : (
<Network
key={id}
id={id}
selected={selected}
infoExpanded={infoExpanded}
machinesExpanded={machinesExpanded}
small={!expanded && selected}
onInfoClick={() => setInfoExpanded(id, !infoExpanded)}
onMachinesClick={() =>
setMachinesExpanded(id, !machinesExpanded)
}
{...network}
/>
)
)}
</form>
)}
</ReduxForm>
) : null}
<Margin bottom={4}>
{expanded ? (
<Button type="button" disabled={!selected.length} onClick={handleNext}>
Next
</Button>
) : proceeded ? (
<Button type="button" onClick={handleEdit} secondary>
Edit
</Button>
) : null} ) : null}
<Margin bottom={4}> </Margin>
{expanded ? ( </Fragment>
<Button );
type="button"
disabled={!selected.length}
onClick={handleNext}
>
Next
</Button>
) : proceeded ? (
<Button type="button" onClick={handleEdit} secondary>
Edit
</Button>
) : null}
</Margin>
</Fragment>
);
};
export default compose( export default compose(
graphql(ListNetworks, { graphql(ListNetworks, {
@ -119,24 +116,32 @@ export default compose(
const selected = get(form, `${FORM_NAME}.values`, {}); const selected = get(form, `${FORM_NAME}.values`, {});
const empty = id => !Object.keys(selected).includes(id); const empty = id => !Object.keys(selected).includes(id);
const _networks = networks
.map(({ id, name, ...network }) => ({
...network,
name,
selected:
empty(id) && name === 'Joyent-SDC-Public'
? true
: Boolean(selected[id]),
infoExpanded: get(
values,
`create-instance-networks-${id}-info-expanded`,
false
),
machinesExpanded: get(
values,
`create-instance-networks-${id}-machines-expanded`,
false
),
id
}))
.sort((a, b) => a.name < b.name);
return { return {
proceeded: get(values, 'create-instance-networks-proceeded', false), proceeded: get(values, 'create-instance-networks-proceeded', false),
networks: networks selected: _networks.filter(({ selected }) => selected),
.map(({ id, name, ...network }) => ({ networks: _networks
...network,
name,
selected:
empty(id) && name === 'Joyent-SDC-Public'
? true
: Boolean(selected[id]),
infoExpanded: get(
values,
`create-instance-networks-${id}-expanded`,
false
),
id
}))
.sort((a, b) => a.name < b.name)
}; };
}, },
(dispatch, { history }) => ({ (dispatch, { history }) => ({
@ -153,7 +158,15 @@ export default compose(
setInfoExpanded: (id, expanded) => { setInfoExpanded: (id, expanded) => {
return dispatch( return dispatch(
set({ set({
name: `create-instance-networks-${id}-expanded`, name: `create-instance-networks-${id}-info-expanded`,
value: expanded
})
);
},
setMachinesExpanded: (id, expanded) => {
return dispatch(
set({
name: `create-instance-networks-${id}-machines-expanded`,
value: expanded value: expanded
}) })
); );

View File

@ -11,17 +11,18 @@ import constantCase from 'constant-case';
import { reset } from 'redux-form'; import { reset } from 'redux-form';
import { PackageIcon, StatusLoader } from 'joyent-ui-toolkit'; import { PackageIcon, StatusLoader } from 'joyent-ui-toolkit';
import { import {
Filters, Filters,
Packages, Packages,
Package, Package,
Overview Overview
} from '@components/create-instance/package'; } from '@components/create-instance/package';
import Title from '@components/create-instance/title';
import Description from '@components/create-instance/description';
import priceData from '../../data/prices.json';
import getPackages from '../../graphql/get-packages.gql'; import Title from '@components/create-instance/title';
import Description from '@components/description';
import getPackages from '@graphql/get-packages.gql';
import priceData from '@data/prices.json';
const FORM_NAME = 'create-instance-package'; const FORM_NAME = 'create-instance-package';
const FILTERS = 'create-instance-package-filters'; const FILTERS = 'create-instance-package-filters';

View File

@ -10,9 +10,9 @@ import get from 'lodash.get';
import { TagsIcon, Button, H3, TagList } from 'joyent-ui-toolkit'; import { TagsIcon, Button, H3, TagList } from 'joyent-ui-toolkit';
import Title from '@components/create-instance/title'; import Title from '@components/create-instance/title';
import Tag from '@components/instances/tags'; import KeyValue from '@components/key-value';
import KeyValue from '@components/instances/key-value'; import Description from '@components/description';
import Description from '@components/create-instance/description'; import Tag from '@components/tags';
const FORM_NAME_CREATE = 'CREATE-INSTANCE-TAGS-ADD'; const FORM_NAME_CREATE = 'CREATE-INSTANCE-TAGS-ADD';
const FORM_NAME_EDIT = i => `CREATE-INSTANCE-TAGS-EDIT-${i}`; const FORM_NAME_EDIT = i => `CREATE-INSTANCE-TAGS-EDIT-${i}`;

View File

@ -6,12 +6,11 @@ import { destroy } from 'redux-form';
import { Margin } from 'styled-components-spacing'; import { Margin } from 'styled-components-spacing';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import get from 'lodash.get'; import get from 'lodash.get';
import remcalc from 'remcalc';
import { ScriptIcon, Button, Divider } from 'joyent-ui-toolkit'; import { ScriptIcon, Button } from 'joyent-ui-toolkit';
import KeyValue from '@components/instances/key-value'; import KeyValue from '@components/key-value';
import Description from '@components/create-instance/description'; import Description from '@components/description';
import Title from '@components/create-instance/title'; import Title from '@components/create-instance/title';
const FORM_NAME = 'create-instance-user-script'; const FORM_NAME = 'create-instance-user-script';
@ -84,9 +83,6 @@ export const UserScript = ({
<Button type="button" onClick={handleEdit} secondary> <Button type="button" onClick={handleEdit} secondary>
Edit Edit
</Button> </Button>
<Margin top={4}>
<Divider height={remcalc(1)} />
</Margin>
</Fragment> </Fragment>
) : null} ) : null}
</div> </div>

View File

@ -0,0 +1,76 @@
import React from 'react';
import renderer from 'react-test-renderer';
import 'jest-styled-components';
import { Networks } from '../networks';
import Theme from '@mocks/theme';
it('renders <Networks /> without throwing', () => {
expect(
renderer
.create(
<Theme>
<Networks />
</Theme>
)
.toJSON()
).toMatchSnapshot();
});
it('renders <Networks loading /> without throwing', () => {
expect(
renderer
.create(
<Theme>
<Networks loading />
</Theme>
)
.toJSON()
).toMatchSnapshot();
});
it('renders <Networks error /> without throwing', () => {
expect(
renderer
.create(
<Theme>
<Networks error />
</Theme>
)
.toJSON()
).toMatchSnapshot();
});
it('renders <Networks networks /> without throwing', () => {
const networks = [
{
id: '1',
name: 'name',
description: 'description',
fabric: true,
subnet: '255.255.255.0',
provision_start_ip: '192.168.1.2',
provision_end_ip: '192.168.1.253',
machines: [{ name: 'hello' }, { name: 'hello2' }]
},
{
id: '2',
name: 'name2',
description: 'description2',
fabric: false,
subnet: '255.255.255.0',
provision_start_ip: '192.168.1.2',
provision_end_ip: '192.168.1.253',
}
];
expect(
renderer
.create(
<Theme>
<Networks networks={networks} />
</Theme>
)
.toJSON()
).toMatchSnapshot();
});

View File

@ -1,100 +1,104 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types';
import forceArray from 'force-array';
import { compose, graphql } from 'react-apollo';
import find from 'lodash.find';
import get from 'lodash.get';
import { export default () => <p>firewalls</p>;
ViewContainer,
StatusLoader,
Message,
MessageDescription,
MessageTitle,
Table,
TableThead,
TableTr,
TableTh,
TableTbody,
P
} from 'joyent-ui-toolkit';
import GetFirewallRules from '@graphql/list-firewall-rules.gql'; //
import { FirewallRule as InstanceFirewallRule } from '@components/instances'; // import PropTypes from 'prop-types';
// import forceArray from 'force-array';
const Firewall = ({ // import { compose, graphql } from 'react-apollo';
// eslint-disable-next-line camelcase // import find from 'lodash.find';
firewallEnabled = false, // import get from 'lodash.get';
firewallRules = [], //
loading, // import {
error // ViewContainer,
}) => { // StatusLoader,
const values = forceArray(firewallRules); // Message,
const _loading = !(loading && !values.length) ? null : <StatusLoader />; // MessageDescription,
// MessageTitle,
const _firewall = // Table,
_loading && !values.length ? null : ( // TableThead,
<Table> // TableTr,
<TableThead> // TableTh,
<TableTr> // TableTbody,
<TableTh left bottom> // P
<P>Rule</P> // } from 'joyent-ui-toolkit';
</TableTh> //
<TableTh xs="63" center bottom> // import GetFirewallRules from '@graphql/list-firewall-rules.gql';
<P>Global</P> // import { FirewallRule as InstanceFirewallRule } from '@components/instances';
</TableTh> //
<TableTh xs="75" center bottom> // const Firewall = ({
<P>Enabled</P> // // eslint-disable-next-line camelcase
</TableTh> // firewallEnabled = false,
</TableTr> // firewallRules = [],
</TableThead> // loading,
<TableTbody> // error
{values.map(network => ( // }) => {
<InstanceFirewallRule key={network.id} {...network} /> // const values = forceArray(firewallRules);
))} // const _loading = !(loading && !values.length) ? null : <StatusLoader />;
</TableTbody> //
</Table> // const _firewall =
); // _loading && !values.length ? null : (
// <Table>
const _error = // <TableThead>
error && !values.length && !_loading ? ( // <TableTr>
<Message error> // <TableTh left bottom>
<MessageTitle>Ooops!</MessageTitle> // <P>Rule</P>
<MessageDescription> // </TableTh>
An error occurred while loading your instance firewall rules // <TableTh xs="63" center bottom>
</MessageDescription> // <P>Global</P>
</Message> // </TableTh>
) : null; // <TableTh xs="75" center bottom>
// <P>Enabled</P>
return ( // </TableTh>
<ViewContainer center={Boolean(_loading)} main> // </TableTr>
{_loading} // </TableThead>
{_error} // <TableTbody>
{_firewall} // {values.map(network => (
</ViewContainer> // <InstanceFirewallRule key={network.id} {...network} />
); // ))}
}; // </TableTbody>
// </Table>
Firewall.propTypes = { // );
loading: PropTypes.bool //
}; // const _error =
// error && !values.length && !_loading ? (
export default compose( // <Message error>
graphql(GetFirewallRules, { // <MessageTitle>Ooops!</MessageTitle>
options: ({ match }) => ({ // <MessageDescription>
pollInterval: 1000, // An error occurred while loading your instance firewall rules
variables: { // </MessageDescription>
name: get(match, 'params.instance') // </Message>
} // ) : null;
}), //
props: ({ data: { loading, error, variables, ...rest } }) => { // return (
const machine = find(get(rest, 'machines', []), ['name', variables.name]); // <ViewContainer center={Boolean(_loading)} main>
// eslint-disable-next-line camelcase // {_loading}
const firewallEnabled = get(machine, 'firewall_enabled', false); // {_error}
const firewallRules = get(machine, 'firewall_rules', []); // {_firewall}
// </ViewContainer>
// eslint-disable-next-line camelcase // );
return { firewallEnabled, firewallRules, loading, error }; // };
} //
}) // Firewall.propTypes = {
)(Firewall); // loading: PropTypes.bool
// };
//
// export default compose(
// graphql(GetFirewallRules, {
// options: ({ match }) => ({
// pollInterval: 1000,
// variables: {
// name: get(match, 'params.instance')
// }
// }),
// props: ({ data: { loading, error, variables, ...rest } }) => {
// const machine = find(get(rest, 'machines', []), ['name', variables.name]);
// // eslint-disable-next-line camelcase
// const firewallEnabled = get(machine, 'firewall_enabled', false);
// const firewallRules = get(machine, 'firewall_rules', []);
//
// // eslint-disable-next-line camelcase
// return { firewallEnabled, firewallRules, loading, error };
// }
// })
// )(Firewall);

View File

@ -1,97 +1,121 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types';
import forceArray from 'force-array';
import { compose, graphql } from 'react-apollo'; import { compose, graphql } from 'react-apollo';
import { connect } from 'react-redux';
import { set } from 'react-redux-values';
import forceArray from 'force-array';
import { Margin } from 'styled-components-spacing';
import find from 'lodash.find'; import find from 'lodash.find';
import sortBy from 'lodash.sortby';
import get from 'lodash.get'; import get from 'lodash.get';
import { import {
ViewContainer, ViewContainer,
StatusLoader, StatusLoader,
Message, Message,
MessageDescription,
MessageTitle, MessageTitle,
Table, MessageDescription,
TableThead, H3
TableTr,
TableTh,
TableTbody,
P
} from 'joyent-ui-toolkit'; } from 'joyent-ui-toolkit';
import GetNetworks from '@graphql/list-networks.gql'; import Network from '@components/network';
import { Network as InstanceNetwork } from '@components/instances'; import Description from '@components/description';
import GetNetworks from '@graphql/list-instance-networks.gql';
const Networks = ({ networks = [], loading, error }) => { export const Networks = ({
const values = forceArray(networks); networks = [],
const _loading = !(loading && !values.length) ? null : <StatusLoader />; loading = false,
error = null,
const _networks = setMachinesExpanded,
_loading && !values.length ? null : ( setInfoExpanded
<Table> }) => (
<TableThead> <ViewContainer main>
<TableTr> <Margin bottom={1}>
<TableTh left bottom> <Description>
<P>Name</P> Use predefined or customized fabric networks which can be public-facing
</TableTh> or private. All fabrics are isolated from other customers giving you
<TableTh xs="90" left bottom> complete control over the network environment. Read more on fabrics.{' '}
<P>Gateway</P> <a
</TableTh> target="__blank"
<TableTh xs="90" left bottom> href="https://docs.joyent.com/public-cloud/network/sdn"
<P>Subnet</P> >
</TableTh> Read more
<TableTh xs="90" left bottom> </a>
<P>Resolvers</P> </Description>
</TableTh> </Margin>
</TableTr> <H3>Networks attached to this instance</H3>
</TableThead> {loading ? <StatusLoader /> : null}
<TableTbody> {!loading && error ? (
{values.map(network => (
<InstanceNetwork key={network.id} {...network} />
))}
</TableTbody>
</Table>
);
const _error =
error && !values.length && !_loading ? (
<Message error> <Message error>
<MessageTitle>Ooops!</MessageTitle> <MessageTitle>Ooops!</MessageTitle>
<MessageDescription> <MessageDescription>
An error occurred while loading your instance networks An error occurred while loading your networks
</MessageDescription> </MessageDescription>
</Message> </Message>
) : null; ) : null}
{!loading &&
return ( networks.map(({ id, infoExpanded, machinesExpanded, ...network }) => (
<ViewContainer center={Boolean(_loading)} main> <Network
{_loading} {...network}
{_error} key={id}
{_networks} id={id}
</ViewContainer> infoExpanded={infoExpanded}
); machinesExpanded={machinesExpanded}
}; onInfoClick={() => setInfoExpanded(id, !infoExpanded)}
onMachinesClick={() => setMachinesExpanded(id, !machinesExpanded)}
Networks.propTypes = { selected
loading: PropTypes.bool readOnly
}; />
))}
</ViewContainer>
);
export default compose( export default compose(
graphql(GetNetworks, { graphql(GetNetworks, {
options: ({ match }) => ({ options: ({ match }) => ({
pollInterval: 1000,
variables: { variables: {
name: get(match, 'params.instance') name: get(match, 'params.instance')
} }
}), }),
props: ({ data: { loading, error, variables, ...rest } }) => ({ props: ({ data }) => {
networks: get( const { machines, loading, error, variables } = data;
find(get(rest, 'machines', []), ['name', variables.name]), const { name } = variables;
'networks',
[] const instance = find(forceArray(machines), ['name', name]);
), const values = get(instance, 'networks', []);
loading, const networks = sortBy(values, 'public').reverse();
error
return {
networks,
instance,
loading,
error
};
}
}),
connect(
({ values }, { networks }) => ({
networks: networks.map(({ id, ...network }) => ({
...network,
infoExpanded: get(values, `networks-${id}-info-expanded`, false),
machinesExpanded: get(
values,
`networks-${id}-machines-expanded`,
false
),
id
}))
}),
dispatch => ({
setMachinesExpanded: (id, expanded) => {
return dispatch(
set({ name: `networks-${id}-machines-expanded`, value: expanded })
);
},
setInfoExpanded: (id, expanded) => {
return dispatch(
set({ name: `networks-${id}-info-expanded`, value: expanded })
);
}
}) })
}) )
)(Networks); )(Networks);

View File

@ -18,20 +18,20 @@ import {
StatusLoader StatusLoader
} from 'joyent-ui-toolkit'; } from 'joyent-ui-toolkit';
import GetSnapshots from '@graphql/list-snapshots.gql';
import StartSnapshot from '@graphql/start-from-snapshot.gql';
import RemoveSnapshot from '@graphql/remove-snapshot.gql';
import CreateSnapshotMutation from '@graphql/create-snapshot.gql';
import GenIndex from '@state/gen-index';
import ToolbarForm from '@components/instances/toolbar';
import SnapshotsListActions from '@components/instances/footer';
import parseError from '@state/parse-error';
import { import {
default as SnapshotsList, default as SnapshotsList,
AddForm as SnapshotAddForm AddForm as SnapshotAddForm
} from '@components/instances/snapshots'; } from '@components/instances/snapshots';
import GetSnapshots from '@graphql/list-snapshots.gql';
import StartSnapshot from '@graphql/start-from-snapshot.gql';
import RemoveSnapshot from '@graphql/remove-snapshot.gql';
import CreateSnapshotMutation from '@graphql/create-snapshot.gql';
import ToolbarForm from '@components/instances/toolbar';
import SnapshotsListActions from '@components/instances/footer';
import parseError from '@state/parse-error';
import GenIndex from '@state/gen-index';
const MENU_FORM_NAME = 'snapshot-list-menu'; const MENU_FORM_NAME = 'snapshot-list-menu';
const TABLE_FORM_NAME = 'snapshot-list-table'; const TABLE_FORM_NAME = 'snapshot-list-table';
const CREATE_FORM_NAME = 'create-snapshot-form'; const CREATE_FORM_NAME = 'create-snapshot-form';

View File

@ -23,14 +23,14 @@ import {
default as Tag, default as Tag,
AddForm as TagsAddForm, AddForm as TagsAddForm,
EditForm as TagsEditForm EditForm as TagsEditForm
} from '@components/instances/tags'; } from '@components/tags';
import ToolbarForm from '@components/instances/toolbar';
import GetTags from '@graphql/list-tags.gql'; import GetTags from '@graphql/list-tags.gql';
import UpdateTags from '@graphql/update-tags.gql'; import UpdateTags from '@graphql/update-tags.gql';
import DeleteTag from '@graphql/delete-tag.gql'; import DeleteTag from '@graphql/delete-tag.gql';
import Index from '@state/gen-index'; import Index from '@state/gen-index';
import parseError from '@state/parse-error'; import parseError from '@state/parse-error';
import ToolbarForm from '@components/instances/toolbar';
const MENU_FORM_NAME = 'instance-tags-list-menu'; const MENU_FORM_NAME = 'instance-tags-list-menu';
const ADD_FORM_NAME = 'instance-tags-add-new'; const ADD_FORM_NAME = 'instance-tags-add-new';

View File

@ -0,0 +1,23 @@
query instance($name: String!) {
machines(name: $name) {
id
name
networks {
id
name
public
fabric
description
subnet
provision_start_ip
provision_end_ip
gateway
resolvers
internet_nat
machines {
id
name
}
}
}
}

View File

@ -11,5 +11,9 @@ query {
gateway gateway
resolvers resolvers
internet_nat internet_nat
machines {
id
name
}
} }
} }

View File

@ -22,7 +22,7 @@ export const client = new ApolloClient({
const initialState = { const initialState = {
ui: { ui: {
sections: { sections: {
instances: ['summary', 'tags', 'metadata', 'snapshots'] instances: ['summary', 'tags', 'metadata', 'networks', 'snapshots']
} }
} }
}; };