Compare commits

..

1 Commits

Author SHA1 Message Date
johnytiago
ba9980dae2 feat(instances): QA bug fixing. closes #1416 2018-05-18 17:16:00 +01:00
384 changed files with 22756 additions and 30322 deletions

View File

@ -1,9 +1,4 @@
packages/*/**
prototypes/*/**
artifacts
reports
.nyc_output
coverage
dist
styleguide
build
consoles/*/lib/app
node_modules

View File

@ -1,10 +1,8 @@
{
"extends": "joyent-portal",
"rules": {
"no-console": 1,
"new-cap": 0,
"jsx-a11y/href-no-hash": 0,
"no-negated-condition": 1,
"camelcase": 1
"new-cap": 0,
"no-console": 0
}
}

View File

View File

@ -9,23 +9,24 @@
"build:lib": "echo 0",
"build:bundle": "echo 0",
"prepublish": "echo 0",
"lint": "echo 0",
"lint:ci": "echo 0",
"test": "echo 0",
"test:ci": "echo 0"
},
"dependencies": {
"apr-main": "^4.0.3",
"cloudapi-gql": "^8.0.0",
"brule": "^3.1.0",
"cloudapi-gql": "^7.1.4",
"execa": "^0.10.0",
"graphi": "^5.7.0",
"h2o2": "^8.1.2",
"hapi": "^17.5.0",
"hapi-triton-auth": "^3.0.0",
"hapi-webconsole-nav": "^2.1.0",
"h2o2": "^8.0.1",
"hapi": "^17.3.1",
"hapi-triton-auth": "^2.0.1",
"hapi-webconsole-nav": "^1.2.0",
"inert": "^5.1.0",
"my-joy-images": "*",
"my-joy-instances": "*",
"my-joy-navigation": "*",
"my-joy-service-groups": "*",
"my-joy-templates": "*",
"tsg-graphql": "^1.0.0"
"rollover": "^1.0.0"
}
}

View File

@ -1,8 +1,5 @@
require('../.env.js');
const Main = require('apr-main');
const CloudApiGql = require('cloudapi-gql');
const Graphi = require('graphi');
const Url = require('url');
const Server = require('./server');
@ -31,17 +28,6 @@ Main(async () => {
});
await server.register([
{
plugin: Graphi,
options: {
graphqlPath: '/graphql',
graphiqlPath: '/graphiql',
authStrategy: 'sso'
},
routes: {
prefix: `/${PREFIX}`
}
},
{
plugin: CloudApiGql,
options: {

View File

@ -1,8 +1,5 @@
require('../.env.js');
const Main = require('apr-main');
const CloudApiGql = require('cloudapi-gql');
const Graphi = require('graphi');
const Url = require('url');
const Server = require('./server');
@ -31,17 +28,6 @@ Main(async () => {
});
await server.register([
{
plugin: Graphi,
options: {
graphqlPath: '/graphql',
graphiqlPath: '/graphiql',
authStrategy: 'sso'
},
routes: {
prefix: `/${PREFIX}`
}
},
{
plugin: CloudApiGql,
options: {

View File

@ -1,8 +1,5 @@
require('../.env.js');
const Main = require('apr-main');
const Nav = require('hapi-webconsole-nav');
const Graphi = require('graphi');
const Url = require('url');
const Server = require('./server');
@ -36,17 +33,6 @@ Main(async () => {
});
await server.register([
{
plugin: Graphi,
options: {
graphqlPath: '/graphql',
graphiqlPath: '/graphiql',
authStrategy: 'sso'
},
routes: {
prefix: `/${PREFIX}`
}
},
{
plugin: Nav,
options: {

View File

@ -1,7 +1,6 @@
require('../.env.js');
const Hapi = require('hapi');
const Sso = require('hapi-triton-auth');
const Url = require('url');
const {
COOKIE_PASSWORD,
@ -9,10 +8,12 @@ const {
SDC_KEY_PATH,
SDC_ACCOUNT,
SDC_KEY_ID,
SDC_URL
SDC_URL,
DC_NAME
} = process.env;
module.exports = async ({ PORT, BASE_URL }) => {
const dcName = DC_NAME || Url.parse(SDC_URL).host.split('.')[0];
const keyPath = SDC_KEY_PATH;
const keyId = `/${SDC_ACCOUNT}/keys/${SDC_KEY_ID}`;
const apiBaseUrl = SDC_URL;
@ -49,7 +50,6 @@ module.exports = async ({ PORT, BASE_URL }) => {
server.events.on('log', (event, tags) => {
if (tags.error) {
// eslint-disable-next-line no-console
console.log(event);
}
});
@ -58,7 +58,6 @@ module.exports = async ({ PORT, BASE_URL }) => {
const { tags } = event;
if (tags.includes('error') && event.data && event.data.errors) {
event.data.errors.forEach(error => {
// eslint-disable-next-line no-console
console.log(error);
});
}

View File

@ -1,78 +0,0 @@
require('../.env.js');
const Main = require('apr-main');
const CloudApiGql = require('cloudapi-gql');
const Tsg = require('tsg-graphql');
const Graphi = require('graphi');
const Url = require('url');
const Server = require('./server');
const Ui = require('my-joy-service-groups');
const {
PORT = 4004,
BASE_URL = `http://0.0.0.0:${PORT}`,
PREFIX = 'service-groups',
DC_NAME,
TSG_URL = 'http://0.0.0.0:3000',
SDC_URL,
SDC_KEY_PATH,
SDC_ACCOUNT,
SDC_KEY_ID
} = process.env;
const dcName = DC_NAME || Url.parse(SDC_URL).host.split('.')[0];
const keyPath = SDC_KEY_PATH;
const keyId = `/${SDC_ACCOUNT}/keys/${SDC_KEY_ID}`;
Main(async () => {
const server = await Server({
PORT,
BASE_URL
});
await server.register([
{
plugin: Graphi,
options: {
graphqlPath: '/graphql',
graphiqlPath: '/graphiql',
authStrategy: 'sso'
},
routes: {
prefix: `/${PREFIX}`
}
},
{
plugin: Tsg,
options: {
authStrategy: 'sso',
keyPath,
keyId,
apiBaseUrl: TSG_URL,
dcName
},
routes: {
prefix: `/${PREFIX}`
}
},
{
plugin: CloudApiGql,
options: {
authStrategy: 'sso',
keyPath,
keyId,
apiBaseUrl: SDC_URL,
dcName
},
routes: {
prefix: `/${PREFIX}`
}
},
{
plugin: Ui
}
]);
await server.start();
});

View File

@ -1,78 +0,0 @@
require('../.env.js');
const Main = require('apr-main');
const CloudApiGql = require('cloudapi-gql');
const Tsg = require('tsg-graphql');
const Graphi = require('graphi');
const Url = require('url');
const Server = require('./server');
const Ui = require('my-joy-templates');
const {
PORT = 4005,
BASE_URL = `http://0.0.0.0:${PORT}`,
PREFIX = 'templates',
DC_NAME,
TSG_URL = 'http://0.0.0.0:3000',
SDC_URL,
SDC_KEY_PATH,
SDC_ACCOUNT,
SDC_KEY_ID
} = process.env;
const dcName = DC_NAME || Url.parse(SDC_URL).host.split('.')[0];
const keyPath = SDC_KEY_PATH;
const keyId = `/${SDC_ACCOUNT}/keys/${SDC_KEY_ID}`;
Main(async () => {
const server = await Server({
PORT,
BASE_URL
});
await server.register([
{
plugin: Graphi,
options: {
graphqlPath: '/graphql',
graphiqlPath: '/graphiql',
authStrategy: 'sso'
},
routes: {
prefix: `/${PREFIX}`
}
},
{
plugin: Tsg,
options: {
authStrategy: 'sso',
keyPath,
keyId,
apiBaseUrl: TSG_URL,
dcName
},
routes: {
prefix: `/${PREFIX}`
}
},
{
plugin: CloudApiGql,
options: {
authStrategy: 'sso',
keyPath,
keyId,
apiBaseUrl: SDC_URL,
dcName
},
routes: {
prefix: `/${PREFIX}`
}
},
{
plugin: Ui
}
]);
await server.start();
});

View File

@ -4,16 +4,7 @@ module.exports = {
'scope-enum': [
2,
'always',
[
'ui-toolkit',
'icons',
'instances',
'navigation',
'bundle',
'images',
'sg',
'templates'
]
['ui-toolkit', 'icons', 'instances', 'navigation', 'bundle', 'images']
]
}
};

View File

@ -0,0 +1,10 @@
{
"extends": "joyent-portal",
"rules": {
"no-console": 0,
"new-cap": 0,
"camelcase": 1,
"jsx-a11y/href-no-hash": 0,
"no-negated-condition": 0
}
}

View File

@ -16,7 +16,6 @@ exports.register = async server => {
if (NODE_ENV === 'production') {
throw err;
} else {
// eslint-disable-next-line no-console
console.error(err);
}
}

View File

@ -6,11 +6,13 @@
"repository": "github:yldio/joyent-portal",
"main": "lib/index.js",
"scripts": {
"dev": "REACT_APP_DEV=1 NAMESPACE=images NODE_ENV=development REACT_APP_GQL_PORT=4000 PORT=3070 joyent-react-scripts start",
"dev": "NAMESPACE=images NODE_ENV=development REACT_APP_GQL_PORT=4000 PORT=3070 joyent-react-scripts start",
"build:test": "echo 0",
"build:lib": "echo 0",
"build:bundle": "NAMESPACE=images NODE_ENV=production redrun -p build:frontend build:ssr",
"prepublish": "NODE_ENV=production redrun build:bundle",
"lint": "redrun lint:ci -- --fix",
"lint:ci": "NODE_ENV=test eslint . --ext .js --ext .md",
"test": "echo 0",
"test:ci": "echo 0",
"build:frontend": "joyent-react-scripts build",
@ -19,24 +21,23 @@
"dependencies": {
"@manaflair/redux-batch": "^0.1.0",
"apollo": "^0.2.2",
"apollo-cache-inmemory": "^1.2.2",
"apollo-client": "^2.3.2",
"apollo-link-http": "^1.5.4",
"apollo-cache-inmemory": "^1.1.12",
"apollo-client": "^2.2.8",
"apollo-link-http": "^1.5.3",
"apr-intercept": "^3.0.3",
"apr-reduce": "^3.0.3",
"boom": "^7.2.0",
"cross-fetch": "^2.2.0",
"cross-fetch": "^2.1.0",
"date-fns": "^1.29.0",
"declarative-redux-form": "^2.0.8",
"exenv": "^1.2.2",
"force-array": "^3.1.0",
"fuse.js": "^3.2.0",
"hapi-render-react": "^2.5.2",
"hapi-render-react-joyent-document": "^7.2.0",
"hapi-render-react-joyent-document": "^7.1.0",
"inert": "^5.1.0",
"joyent-logo-assets": "^1.1.0",
"joyent-react-styled-flexboxgrid": "^3.1.0",
"joyent-ui-resource-widgets": "^1.0.0",
"joyent-react-styled-flexboxgrid": "^2.2.3",
"joyent-ui-toolkit": "^6.0.0",
"lodash.assign": "^4.2.0",
"lodash.find": "^4.6.0",
@ -46,37 +47,37 @@
"lodash.keys": "^4.2.0",
"lodash.omit": "^4.5.0",
"lodash.uniqby": "^4.7.0",
"lunr": "^2.2.1",
"lunr": "^2.1.6",
"mz": "^2.7.0",
"param-case": "^2.1.1",
"react": "^16.4.0",
"react-apollo": "^2.1.4",
"react-dom": "^16.4.0",
"react-helmet-async": "0.1.0",
"react": "^16.3.1",
"react-apollo": "^2.1.2",
"react-dom": "^16.3.1",
"react-helmet-async": "0.0.5",
"react-redux": "^5.0.7",
"react-redux-values": "^1.1.2",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
"redux": "^4.0.0",
"redux": "^3.7.2",
"redux-form": "^7.3.0",
"remcalc": "^1.0.10",
"styled-components": "^3.3.0",
"styled-components-spacing": "^3.0.0",
"styled-components": "^3.2.5",
"styled-components-spacing": "^2.1.3",
"styled-flex-component": "^2.2.2",
"styled-is": "^1.1.3",
"styled-is": "^1.1.2",
"title-case": "^2.1.1",
"yup": "^0.25.1"
"yup": "^0.24.1"
},
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-preset-joyent-portal": "^7.0.1",
"eslint": "^4.19.1",
"eslint-config-joyent-portal": "^3.3.1",
"jest-image-snapshot": "^2.4.2",
"jest-image-snapshot": "^2.4.0",
"jest-styled-components": "^5.0.1",
"joyent-react-scripts": "^8.2.1",
"joyent-react-scripts": "^8.2.0",
"react-screenshot-renderer": "^1.1.2",
"react-test-renderer": "^16.4.0",
"redrun": "^6.0.4"
"react-test-renderer": "^16.3.1",
"redrun": "^6.0.2"
}
}

View File

@ -2,9 +2,11 @@ import React from 'react';
import { Field } from 'redux-form';
import { Margin } from 'styled-components-spacing';
import Flex, { FlexItem } from 'styled-flex-component';
import remcalc from 'remcalc';
import { Row, Col } from 'joyent-react-styled-flexboxgrid';
import {
Divider,
FormGroup,
FormLabel,
Input,
@ -20,16 +22,18 @@ export default ({ placeholderName, randomizing, onRandomize }) => (
<FlexItem flex>
<FormGroup name="name" fluid field={Field}>
<FormLabel>Image name</FormLabel>
<Margin top="0.5">
<Margin top={0.5}>
<Input placeholder={placeholderName} onBlur={null} required />
</Margin>
<FormMeta />
</FormGroup>
</FlexItem>
<FlexItem>
<Margin left="1">
<Divider height={remcalc(13)} transparent />
<Margin left={1}>
<Button
type="button"
marginTop={remcalc(8)}
onClick={onRandomize}
loading={randomizing}
marginless
@ -42,21 +46,21 @@ export default ({ placeholderName, randomizing, onRandomize }) => (
</Margin>
</FlexItem>
</Flex>
<Margin top="3">
<Margin top={3}>
<FormGroup name="version" fluid field={Field}>
<FormLabel>Version</FormLabel>
<Margin top="0.5">
<Margin top={0.5}>
<Input placeholder="Example: v1.0" onBlur={null} required />
</Margin>
<FormMeta />
</FormGroup>
</Margin>
<Row>
<Col xs="12" sm="8">
<Margin top="3">
<Col xs={12} sm={8}>
<Margin top={3}>
<FormGroup name="description" fluid field={Field}>
<FormLabel>Description</FormLabel>
<Margin top="0.5">
<Margin top={0.5}>
<Textarea
placeholder="Example: JarJarBinks, Anakin Skywalker, Obi Wan Kenobi, Qui-Gon Jinn, Han Solo, Wookies"
fluid

View File

@ -16,14 +16,14 @@ const Container = styled.div`
export default ({ icon, children, collapsed = true, ...rest }) => (
<Container {...rest}>
<Flex>
<Margin right="1">
<Margin right={1}>
<Flex alignCenter full>
{icon}
</Flex>
</Margin>
<Small noMargin>{children}</Small>
</Flex>
<Margin top="1" bottom={collapsed ? 7 : 3}>
<Margin top={1} bottom={collapsed ? 7 : 3}>
<Divider height={remcalc(1)} />
</Margin>
</Container>

View File

@ -5,8 +5,8 @@ import { P } from 'joyent-ui-toolkit';
export default ({ children }) => (
<Row>
<Col xs="12" sm="8">
<Margin bottom="3">
<Col xs={12} sm={8}>
<Margin bottom={3}>
<P>{children}</P>
</Margin>
</Col>

View File

@ -17,9 +17,9 @@ const FullWidthCard = styled(Card)`
export default ({ children }) => (
<FullWidthCard>
<Padding all="6">
<Padding all={6}>
<Flex alignCenter justifyCenter column>
<Margin bottom="2">
<Margin bottom={2}>
<EmptyState />
</Margin>
<NoPackagesTitle>{children}</NoPackagesTitle>

View File

@ -84,20 +84,20 @@ export const Image = ({
onRemove,
onCreateInstance
}) => (
<Margin bottom="3">
<Margin bottom={3}>
<CardAnchor to={`/images/${id}`} component={Link}>
<Card radius>
{removing ? (
<Padding all="2">
<Padding all={2}>
<StatusLoader />
</Padding>
) : (
<Fragment>
<CardHeader white radius>
<Padding left="2" right="2">
<Padding left={2} right={2}>
<Flex full alignCenter>
<FlexItem>
<Margin right="2">
<Margin right={2}>
{React.createElement(OS[os], {
width: '24',
height: '24'
@ -113,7 +113,7 @@ export const Image = ({
</Padding>
</CardHeader>
<Flex justifyBetween>
<Content left="2" top="2" bottom="2">
<Content left={2} top={2} bottom={2}>
<Max justifyBetween>
<Max alignCenter>
<Flex>{version}</Flex>
@ -130,7 +130,7 @@ export const Image = ({
</ActionsWrapper>
</PopoverTarget>
<Popover noPadding placement="bottom">
<Padding horizontal="3" vertical="2">
<Padding horizontal={3} vertical={2}>
<PopoverItem disabled={false} onClick={onCreateInstance}>
<ItemAnchor
href={`${
@ -144,7 +144,7 @@ export const Image = ({
</PopoverItem>
</Padding>
<PopoverDivider />
<Padding horizontal="3" vertical="2">
<Padding horizontal={3} vertical={2}>
<PopoverItem disabled={removing} onClick={onRemove}>
Remove
</PopoverItem>
@ -165,7 +165,7 @@ export const Filters = ({ selected }) => (
<FormGroup name="image-type" value="all" field={Field} type="radio">
<Radio>
<Flex alignCenter>
<Margin horizontal="2">
<Margin horizontal={2}>
<FormLabel big normal={selected !== 'all'}>
All
</FormLabel>
@ -181,7 +181,7 @@ export const Filters = ({ selected }) => (
>
<Radio noMargin>
<Flex alignCenter>
<Margin horizontal="2">
<Margin horizontal={2}>
<FormLabel big normal={selected !== 'hardware-virtual-machine'}>
Virtual machines
</FormLabel>
@ -197,7 +197,7 @@ export const Filters = ({ selected }) => (
>
<Radio noMargin>
<Flex alignCenter>
<Margin horizontal="2">
<Margin horizontal={2}>
<FormLabel big normal={selected !== 'infrastructure-container'}>
Infrastructure container
</FormLabel>

View File

@ -1,12 +1,11 @@
import React, { Fragment } from 'react';
import { Row, Col } from 'joyent-react-styled-flexboxgrid';
import { Margin, Padding } from 'styled-components-spacing';
import styled from 'styled-components';
import styled, { withTheme } from 'styled-components';
import Flex, { FlexItem } from 'styled-flex-component';
import distanceInWordsToNow from 'date-fns/distance_in_words_to_now';
import titleCase from 'title-case';
import remcalc from 'remcalc';
import { ValueBreakpoints as breakpoints } from 'joyent-ui-toolkit';
import {
Card,
@ -38,7 +37,7 @@ const VerticalDivider = styled.div`
align-self: flex-end;
margin: 0 ${remcalc(18)};
@media (max-width: ${remcalc(breakpoints.small.upper)}) {
@media (max-width: ${remcalc(767)}) {
display: none;
}
`;
@ -76,7 +75,7 @@ export const Meta = ({ name, version, type, published_at, state, os }) => (
<H2 bold>{name}</H2>
</FlexItem>
</Flex>
<Margin top="2" bottom="3">
<Margin top={2} bottom={3}>
<Flex>
<Label>{version}</Label>
<VerticalDivider />
@ -104,15 +103,15 @@ export const Meta = ({ name, version, type, published_at, state, os }) => (
</Fragment>
);
export default ({ theme = {}, onRemove, removing, ...image }) => (
export default withTheme(({ theme = {}, onRemove, removing, ...image }) => (
<Row>
<Col xs="12" sm="12" md="9">
<Col xs={12} sm={12} md={9}>
<Card>
<CardOutlet>
<Padding all="5">
<Padding all={5}>
<Meta {...image} />
<Row between="xs">
<Col xs="9">
<Col xs={9}>
<SmallOnly>
<Button type="button" small icon>
<DuplicateIcon light />
@ -133,10 +132,10 @@ export default ({ theme = {}, onRemove, removing, ...image }) => (
</Button>
</Medium>
</Col>
<Col xs="3">
<Col xs={3}>
<SmallOnly>
<Button type="button" small icon error right>
<DeleteIcon fill="red" />
<DeleteIcon fill={theme.red} />
</Button>
</SmallOnly>
<Medium>
@ -150,7 +149,7 @@ export default ({ theme = {}, onRemove, removing, ...image }) => (
right
>
<Margin right="1">
<DeleteIcon fill="red" />
<DeleteIcon fill={theme.red} />
</Margin>
<span>Delete</span>
</Button>
@ -163,15 +162,15 @@ export default ({ theme = {}, onRemove, removing, ...image }) => (
<Margin bottom="2">
<P>{image.description}</P>
</Margin>
<Margin bottom="3">
<Margin bottom={3}>
<CopiableField text={(image.id || '').split('-')[0]} label="ID" />
</Margin>
<Margin bottom="3">
<Margin bottom={3}>
<CopiableField text={image.id} label="UUID" />
</Margin>
<Row>
<Col xs="12" md="7">
<Margin bottom="3">
<Col xs={12} md={7}>
<Margin bottom={3}>
<FormLabel>Operating system</FormLabel>
<Input
monospace
@ -187,4 +186,4 @@ export default ({ theme = {}, onRemove, removing, ...image }) => (
</Card>
</Col>
</Row>
);
));

View File

@ -1,8 +1,7 @@
import React from 'react';
import { Margin } from 'styled-components-spacing';
import { TagItem } from 'joyent-ui-toolkit';
import { KeyValue } from 'joyent-ui-resource-widgets';
import { TagItem, KeyValue } from 'joyent-ui-toolkit';
export const AddForm = props => (
<KeyValue {...props} method="add" input="input" type="tag" expanded />
@ -14,8 +13,8 @@ export const EditForm = props => (
export default ({ norMargin, name, value, onClick, onRemoveClick, active }) => (
<Margin
right={norMargin ? '0' : '1'}
bottom={norMargin ? '0' : '1'}
right={norMargin ? 0 : 1}
bottom={norMargin ? 0 : 1}
key={`${name}-${value}`}
>
<TagItem onClick={onClick} active={active} onRemoveClick={onRemoveClick}>

View File

@ -17,7 +17,7 @@ export const Toolbar = ({
<Flex justifyBetween alignEnd>
<FormGroup name="filter" field={Field}>
<FormLabel>{searchLabel}</FormLabel>
<Margin top="0.5">
<Margin top={0.5}>
<Input placeholder={searchPlaceholder} disabled={!searchable} />
</Margin>
</FormGroup>

View File

@ -44,7 +44,7 @@ export default ({ match }) => {
.filter(Boolean)
.map(({ name, pathname }) => (
<BreadcrumbItem key={name} to={pathname} component={Link}>
<Margin horizontal="1" vertical="3">
<Margin horizontal={1} vertical={3}>
{name}
</Margin>
</BreadcrumbItem>

View File

@ -67,12 +67,12 @@ const NameContainer = ({
onRandomize={handleRandomize}
/>
) : name ? (
<Margin top="3">
<Margin top={3}>
<H3 bold noMargin>
{name}
</H3>
{version ? (
<Margin top="2">
<Margin top={2}>
<H4 bold noMargin>
{version}
</H4>
@ -80,8 +80,8 @@ const NameContainer = ({
) : null}
{description ? (
<Row>
<Col xs="12" sm="8">
<Margin top="1">
<Col xs={12} sm={8}>
<Margin top={1}>
<P>{description}</P>
</Margin>
</Col>
@ -92,13 +92,13 @@ const NameContainer = ({
}
</ReduxForm>
{expanded ? (
<Margin top="4" bottom="7">
<Margin top={4} bottom={7}>
<Button type="button" disabled={!name} onClick={handleNext}>
Next
</Button>
</Margin>
) : proceeded ? (
<Margin top="4" bottom="7">
<Margin top={4} bottom={7}>
<Button type="button" onClick={handleEdit} secondary>
Edit
</Button>
@ -166,7 +166,6 @@ export default compose(
dispatch(set({ name: 'create-image-name-randomizing', value: false }));
if (err) {
// eslint-disable-next-line no-console
console.error(err);
return;
}

View File

@ -6,10 +6,17 @@ import { destroy, reset } from 'redux-form';
import ReduxForm from 'declarative-redux-form';
import { connect } from 'react-redux';
import get from 'lodash.get';
import remcalc from 'remcalc';
import Flex from 'styled-flex-component';
import { TagsIcon, Button, H3, TagList } from 'joyent-ui-toolkit';
import { KeyValue } from 'joyent-ui-resource-widgets';
import {
TagsIcon,
Button,
H3,
TagList,
Divider,
KeyValue
} from 'joyent-ui-toolkit';
import Title from '@components/create-image/title';
import Description from '@components/description';
@ -59,7 +66,7 @@ export const Tags = ({
) : null}
{proceeded || expanded ? (
<Fragment>
<Margin bottom="5">
<Margin bottom={5}>
<H3>
{tags.length} Tag{tags.length === 1 ? '' : 's'}
</H3>
@ -95,11 +102,12 @@ export const Tags = ({
expanded
onCancel={() => handleChangeAddOpen(false)}
/>
<Divider height={remcalc(18)} transparent />
</Fragment>
) : null
}
</ReduxForm>
<Margin top="1">
<Margin top={1}>
<Flex alignCenter>
{expanded ? (
<Button
@ -110,11 +118,11 @@ export const Tags = ({
Add Tag
</Button>
) : null}
<Margin left="1">{children}</Margin>
<Margin left={1}>{children}</Margin>
</Flex>
</Margin>
{proceeded ? (
<Margin top="1">
<Margin top={1}>
<Button type="button" onClick={handleEdit} secondary>
Edit
</Button>

View File

@ -1,3 +1,5 @@
/* eslint-disable camelcase */
import React from 'react';
import { Margin } from 'styled-components-spacing';
import ReduxForm from 'declarative-redux-form';
@ -39,12 +41,12 @@ const Create = ({
}) => (
<ViewContainer>
{loading ? (
<Margin top="4">
<Margin top={4}>
<StatusLoader />
</Margin>
) : null}
{loadingError ? (
<Margin top="4">
<Margin top={4}>
<Message error>
<MessageTitle>Ooops!</MessageTitle>
<MessageDescription>{loadingError}</MessageDescription>
@ -52,7 +54,7 @@ const Create = ({
</Margin>
) : null}
{!loading && !loadingError ? (
<Margin top="4" bottom="5">
<Margin top={4} bottom={5}>
<H2>Create Image</H2>
</Margin>
) : null}
@ -76,7 +78,7 @@ const Create = ({
{({ handleSubmit, submitting }) =>
!loading && !loadingError ? (
<form onSubmit={handleSubmit}>
<Margin top={step === 'tag' ? '7' : '4'}>
<Margin top={step === 'tag' ? 7 : 4}>
<Button disabled={disabled} loading={submitting}>
Create Image
</Button>

View File

@ -2,6 +2,7 @@ import React, { Fragment } from 'react';
import { compose, graphql } from 'react-apollo';
import ReduxForm from 'declarative-redux-form';
import { Margin } from 'styled-components-spacing';
import remcalc from 'remcalc';
import { Row, Col } from 'joyent-react-styled-flexboxgrid';
import { connect } from 'react-redux';
import get from 'lodash.get';
@ -40,21 +41,22 @@ export const List = ({
handleRemove
}) => (
<ViewContainer main>
<Margin top="4">
<Margin top={4}>
<ReduxForm form={LIST_TOOLBAR_FORM}>
{props => <ToolbarForm {...props} actionable={!loading} />}
</ReduxForm>
</Margin>
<Margin vertical="4">
<Margin vertical={4}>
<Divider />
</Margin>
{loading && !images.length ? (
<Fragment>
<Divider height={remcalc(30)} transparent />
<StatusLoader />
</Fragment>
) : null}
{error && !images.length && !loading ? (
<Margin bottom="5">
<Margin bottom={5}>
<Message error>
<MessageTitle>Ooops!</MessageTitle>
<MessageDescription>
@ -64,7 +66,7 @@ export const List = ({
</Margin>
) : null}
<Fragment>
<Margin bottom="4">
<Margin bottom={4}>
<ReduxForm
form={LIST_TOGGLE_TYPE_FORM}
initialValues={{ 'image-type': 'all' }}
@ -78,7 +80,7 @@ export const List = ({
</Margin>
<Row>
{images.map(image => (
<Col sm="4">
<Col sm={4}>
<Image
{...image}
onCreateInstance={() => handleCreateInstance(image)}

View File

@ -10,7 +10,7 @@ const SECTIONS = [
export default ({ match }) => {
const imageId = get(match, 'params.image');
const sections = imageId === '~create' ? [] : SECTIONS;
const sections = imageId !== '~create' ? SECTIONS : [];
const links = sections.map(({ name, pathname }) => ({
name,

View File

@ -30,7 +30,7 @@ export const Summary = ({
<ViewContainer main>
{loading && !image ? <StatusLoader /> : null}
{error && !loading && !image ? (
<Margin bottom="5">
<Margin bottom={5}>
<Message error>
<MessageTitle>Ooops!</MessageTitle>
<MessageDescription>
@ -40,7 +40,7 @@ export const Summary = ({
</Margin>
) : null}
{mutationError ? (
<Margin bottom="5">
<Margin bottom={5}>
<Message error>
<MessageTitle>Ooops!</MessageTitle>
<MessageDescription>

View File

@ -14,11 +14,11 @@ import {
H3,
ViewContainer,
StatusLoader,
Divider,
Message,
MessageTitle,
MessageDescription,
TagList,
Divider
TagList
} from 'joyent-ui-toolkit';
import { Forms } from '@root/constants';
@ -63,7 +63,7 @@ export const Tags = ({
)}
</ReduxForm>
{error && !loading && !tags.length ? (
<Margin bottom="5">
<Margin bottom={5}>
<Message error>
<MessageTitle>Ooops!</MessageTitle>
<MessageDescription>
@ -73,7 +73,7 @@ export const Tags = ({
</Margin>
) : null}
{mutationError ? (
<Margin bottom="5">
<Margin bottom={5}>
<Message error>
<MessageTitle>Ooops!</MessageTitle>
<MessageDescription>{mutationError}</MessageDescription>
@ -88,7 +88,7 @@ export const Tags = ({
>
{props =>
addOpen ? (
<Margin bottom="5">
<Margin bottom={5}>
<AddForm
{...props}
onToggleExpanded={() => handleToggleAddOpen(!addOpen)}
@ -98,13 +98,13 @@ export const Tags = ({
) : null
}
</ReduxForm>
{loading ? null : (
<Margin bottom="5">
{!loading ? (
<Margin bottom={5}>
<H3>
{tags.length} tag{tags.length === 1 ? '' : 's'}
</H3>
</Margin>
)}
) : null}
{loading && !tags.length ? <StatusLoader /> : null}
<TagList>
{tags.map(({ id, name, value }) => (

View File

@ -20,7 +20,7 @@ module.exports = ({
</head>
<body {...bodyAttrs}>
<div id="header" />
{children ? null : <div id="root" />}
{!children ? <div id="root" /> : null}
{children}
<script src="/navigation/static/main.js" />
</body>

View File

@ -1,4 +1,4 @@
import React, { Fragment } from 'react';
import React from 'react';
import { Route, Switch, Redirect } from 'react-router-dom';
import get from 'lodash.get';
@ -19,8 +19,6 @@ import Create from '@containers/create';
import Tags from '@containers/tags';
import { Route as ServerError } from '@root/server-error';
const { REACT_APP_DEV = false } = process.env;
export default () => (
<PageContainer>
{/* Breadcrumb */}
@ -72,41 +70,6 @@ export default () => (
<Route path="/" exact component={() => <Redirect to="/images" />} />
{REACT_APP_DEV ? (
<Fragment>
<Route
path="/instances"
component={({ location }) =>
window.location.replace(
`${window.location.protocol}//${window.location.hostname}:3069${
location.pathname
}${location.search}`
)
}
/>
<Route
path="/templates"
component={({ location }) =>
window.location.replace(
`${window.location.protocol}//${window.location.hostname}:3071${
location.pathname
}${location.search}`
)
}
/>
<Route
path="/service-groups"
component={({ location }) =>
window.location.replace(
`${window.location.protocol}//${window.location.hostname}:3072${
location.pathname
}${location.search}`
)
}
/>
</Fragment>
) : null}
<noscript>
<ViewContainer main>
<Message warning>

View File

@ -17,7 +17,7 @@ import Breadcrumb from '@containers/breadcrumb';
export const Route = () => (
<ViewContainer main>
<Divider height={remcalc(30)} transparent />
<Margin bottom="5">
<Margin bottom={5}>
<Message error>
<MessageTitle>Ooops!</MessageTitle>
<MessageDescription>

View File

@ -2,7 +2,6 @@ import { ApolloClient } from 'apollo-client';
import { HttpLink } from 'apollo-link-http';
import { InMemoryCache } from 'apollo-cache-inmemory';
import fetch from 'cross-fetch';
import get from 'lodash.get';
import global from './global';
@ -15,9 +14,7 @@ const {
const PORT = REACT_APP_GQL_PORT ? `:${REACT_APP_GQL_PORT}` : '';
const URI = `${REACT_APP_GQL_PROTOCOL}://${REACT_APP_GQL_HOSTNAME}${PORT}/images/graphql`;
export default (opts = {}, request = {}) => {
const host = get(request, 'raw.req.headers.host', '');
export default (opts = {}) => {
let cache = new InMemoryCache();
if (global.__APOLLO_STATE__) {
@ -27,7 +24,7 @@ export default (opts = {}, request = {}) => {
return new ApolloClient({
cache,
link: new HttpLink({
uri: host ? `${REACT_APP_GQL_PROTOCOL}//${host}/images/graphql` : URI,
uri: URI,
credentials: 'same-origin',
fetch,
headers: {

View File

@ -1,12 +1,8 @@
import { canUseDOM } from 'exenv';
import queryString from 'query-string';
const { NODE_ENV = 'development' } = process.env;
export const Global = () => {
export default (() => {
if (!canUseDOM) {
return {
protocol: NODE_ENV === 'development' ? 'http:' : 'https:',
cookie: ''
};
}
@ -15,15 +11,10 @@ export const Global = () => {
port: window.location.port,
protocol: window.location.protocol.replace(/:$/, ''),
hostname: window.location.hostname,
pathname: window.location.pathname,
origin: window.location.origin,
cookie: document.cookie || '',
search: window.location.search,
query: queryString.parse(window.location.search || ''),
__REDUX_DEVTOOLS_EXTENSION__: window.__REDUX_DEVTOOLS_EXTENSION__,
__APOLLO_STATE__: window.__APOLLO_STATE__,
__REDUX_STATE__: window.__REDUX_STATE__
};
};
export default Global();
})();

View File

@ -2,7 +2,7 @@ import intercept from 'apr-intercept';
import keys from 'lodash.keys';
import reduce from 'apr-reduce';
import assign from 'lodash.assign';
import * as yup from 'yup';
import yup from 'yup';
/*****************************************************************************/
@ -16,7 +16,7 @@ const validateSchema = async (schema, value) => {
keys(schema),
async (errors, name) => {
const msg = await validateField(schema[name], value[name]);
return msg ? assign(errors, { [name]: msg }) : errors;
return !msg ? errors : assign(errors, { [name]: msg });
},
{}
);
@ -66,4 +66,4 @@ const Schemas = {
export const addTag = tag => validateSchema(Schemas.tag, tag);
export const instanceName = ({ name }) =>
name ? validateSchema(Schemas.instanceName, { name }) : null;
!name ? null : validateSchema(Schemas.instanceName, { name });

View File

@ -1,4 +1,5 @@
.nyc_output
coverage
dist
styleguide
build
lib/app

View File

@ -0,0 +1,10 @@
{
"extends": "joyent-portal",
"rules": {
"no-console": 0,
"new-cap": 0,
"camelcase": 1,
"jsx-a11y/href-no-hash": 0,
"no-negated-condition": 0
}
}

View File

@ -16,7 +16,6 @@ exports.register = async server => {
if (NODE_ENV === 'production') {
throw err;
} else {
// eslint-disable-next-line no-console
console.error(err);
}
}

View File

@ -6,11 +6,13 @@
"repository": "github:yldio/joyent-portal",
"main": "lib/index.js",
"scripts": {
"dev": "REACT_APP_DEV=1 NAMESPACE=instances NODE_ENV=development REACT_APP_GQL_PORT=4000 PORT=3069 joyent-react-scripts start",
"dev": "NAMESPACE=instances NODE_ENV=development REACT_APP_GQL_PORT=4000 PORT=3069 joyent-react-scripts start",
"build:test": "echo 0",
"build:lib": "echo 0",
"build:bundle": "NAMESPACE=instances NODE_ENV=production redrun -p build:frontend build:ssr",
"prepublish": "NODE_ENV=production redrun build:bundle",
"lint": "redrun lint:ci -- --fix",
"lint:ci": "NODE_ENV=test eslint . --ext .js --ext .md",
"test": "DEFAULT_TIMEOUT_INTERVAL=100000 NODE_ENV=test joyent-react-scripts test --env=jsdom --testPathIgnorePatterns='.ui.js'",
"test:ci": "NODE_ENV=test joyent-react-scripts test --env=jsdom --testPathIgnorePatterns='.ui.js'",
"build:frontend": "joyent-react-scripts build",
@ -18,26 +20,27 @@
},
"dependencies": {
"@manaflair/redux-batch": "^0.1.0",
"apollo-cache-inmemory": "^1.2.2",
"apollo-client": "^2.3.2",
"apollo-link-http": "^1.5.4",
"apollo-cache-inmemory": "^1.1.12",
"apollo-client": "^2.2.8",
"apollo-link-http": "^1.5.3",
"apr-intercept": "^3.0.3",
"apr-reduce": "^3.0.3",
"boom": "^7.2.0",
"bytes": "^3.0.0",
"clipboard-copy": "^2.0.0",
"cross-fetch": "^2.2.0",
"constant-case": "^2.0.0",
"cross-fetch": "^2.1.0",
"date-fns": "^1.29.0",
"declarative-redux-form": "^2.0.8",
"exenv": "^1.2.2",
"fuse.js": "^3.2.0",
"hapi-render-react": "^2.5.2",
"hapi-render-react-joyent-document": "^7.2.0",
"hapi-render-react-joyent-document": "^7.1.0",
"inert": "^5.1.0",
"joyent-logo-assets": "^1.1.0",
"joyent-ui-resource-step": "^1.0.0",
"joyent-manifest-editor": "^1.4.0",
"joyent-react-styled-flexboxgrid": "^3.1.0",
"joyent-react-styled-flexboxgrid": "^2.2.3",
"joyent-ui-toolkit": "^6.0.0",
"lodash.find": "^4.6.0",
"lodash.findindex": "^4.6.0",
@ -51,41 +54,42 @@
"lodash.isfunction": "^3.0.9",
"lodash.isinteger": "^4.0.4",
"lodash.isnan": "^3.0.2",
"lodash.omit": "^4.5.0",
"lodash.reduce": "^4.6.0",
"lodash.reverse": "^4.0.1",
"lodash.some": "^4.6.0",
"lodash.sortby": "^4.7.0",
"lodash.uniqby": "^4.7.0",
"lodash.values": "^4.3.0",
"mz": "^2.7.0",
"param-case": "^2.1.1",
"query-string": "^6.1.0",
"react": "^16.4.0",
"react-apollo": "^2.1.4",
"react-dom": "^16.4.0",
"react-helmet-async": "0.1.0",
"query-string": "^6.0.0",
"react": "^16.3.1",
"react-apollo": "^2.1.2",
"react-dom": "^16.3.1",
"react-helmet-async": "0.0.5",
"react-redux": "^5.0.7",
"react-redux-values": "^1.1.2",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
"redux": "^4.0.0",
"redux": "^3.7.2",
"redux-form": "^7.3.0",
"remcalc": "^1.0.10",
"styled-components": "^3.3.0",
"styled-components-spacing": "^3.0.0",
"styled-components": "^3.2.5",
"styled-components-spacing": "^2.1.3",
"styled-flex-component": "^2.2.2",
"title-case": "^2.1.1",
"yup": "^0.25.1"
"yup": "^0.24.1"
},
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-preset-joyent-portal": "^7.0.1",
"eslint": "^4.19.1",
"eslint-config-joyent-portal": "^3.3.1",
"jest-image-snapshot": "^2.4.2",
"jest-image-snapshot": "^2.4.0",
"jest-styled-components": "^5.0.1",
"joyent-react-scripts": "^8.2.1",
"joyent-react-scripts": "^8.2.0",
"react-screenshot-renderer": "^1.1.2",
"react-test-renderer": "^16.4.0",
"redrun": "^6.0.4"
"react-test-renderer": "^16.3.1",
"redrun": "^6.0.2"
}
}

View File

@ -21,9 +21,15 @@ exports[`renders <Menu links /> without throwing 1`] = `
.c6 {
color: rgb(59,70,204);
-webkit-text-fill-color: currentcolor;
cursor: pointer;
-webkit-text-decoration: underline;
text-decoration: underline;
}
.c6:hover {
-webkit-text-decoration: none;
text-decoration: none;
}
.c6 a:hover {
color: rgb(45,56,132);
}
.c1 {
@ -34,7 +40,7 @@ exports[`renders <Menu links /> without throwing 1`] = `
.c0 {
box-sizing: border-box;
width: 100%;
max-width: 78.75rem;
max-width: 62.5rem;
}
.c4 {
@ -55,10 +61,10 @@ exports[`renders <Menu links /> without throwing 1`] = `
position: relative;
}
.c5:after {
.c5.active:after {
width: 100%;
height: 0.0625rem;
background: rgb(216,216,216);
background: rgb(73,73,73);
content: '';
position: absolute;
bottom: 0;
@ -66,14 +72,6 @@ exports[`renders <Menu links /> without throwing 1`] = `
z-index: 2;
}
.c5.active:after {
background: rgb(73,73,73);
}
.c5.active {
font-weight: 600;
}
.c3 {
background: rgb(250,250,250);
list-style-type: none;
@ -119,24 +117,10 @@ exports[`renders <Menu links /> without throwing 1`] = `
}
}
@media only screen and (max-width:37.4375rem) {
@media only screen and (max-width:47.9375rem) {
.c0 {
padding-left: 0.75rem;
padding-right: 0.75rem;
}
}
@media only screen and (min-width:37.5rem) and (max-width:62.4375rem) {
.c0 {
padding-left: 1.875rem;
padding-right: 1.875rem;
}
}
@media only screen and (min-width:62.5rem) and (max-width:87.4375rem) {
.c0 {
padding-left: 4.375rem;
padding-right: 4.375rem;
padding-left: 0.375rem;
padding-right: 0.375rem;
}
}

View File

@ -231,13 +231,19 @@ exports[`renders <Summary /> without throwing 1`] = `
.c36 {
color: rgb(59,70,204);
-webkit-text-fill-color: currentcolor;
cursor: pointer;
-webkit-text-decoration: underline;
text-decoration: underline;
color: rgb(255,255,255);
-webkit-text-fill-color: currentcolor;
}
.c36:hover {
-webkit-text-decoration: none;
text-decoration: none;
}
.c36 a:hover {
color: rgb(45,56,132);
}
.c29 {
min-width: 7.5rem;
}
@ -313,7 +319,7 @@ exports[`renders <Summary /> without throwing 1`] = `
.c27:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c27:active,
@ -417,7 +423,7 @@ exports[`renders <Summary /> without throwing 1`] = `
.c33:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c33:active,
@ -513,7 +519,7 @@ exports[`renders <Summary /> without throwing 1`] = `
.c35:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c35:active,
@ -623,7 +629,7 @@ exports[`renders <Summary /> without throwing 1`] = `
.c40:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c40:active,
@ -765,7 +771,7 @@ exports[`renders <Summary /> without throwing 1`] = `
.c41:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c41:active,
@ -883,7 +889,7 @@ exports[`renders <Summary /> without throwing 1`] = `
.c9 {
margin: 0;
color: rgb(73,73,73);
font-weight: 400;
font-weight: normal;
line-height: 1.875rem;
font-size: 1.5rem;
}
@ -891,7 +897,6 @@ exports[`renders <Summary /> without throwing 1`] = `
.c43 {
height: 1px;
background-color: rgb(216,216,216);
height: 1;
}
.c2 {
@ -950,7 +955,6 @@ exports[`renders <Summary /> without throwing 1`] = `
-moz-appearance: none;
appearance: none;
outline: 0;
white-space: pre-wrap;
font-family: "Roboto Mono",monospace;
width: 100%;
min-width: 100%;
@ -1030,7 +1034,6 @@ exports[`renders <Summary /> without throwing 1`] = `
-moz-appearance: none;
appearance: none;
outline: 0;
white-space: pre-wrap;
font-family: "Roboto Mono",monospace;
width: 100%;
min-width: 100%;
@ -1248,7 +1251,7 @@ exports[`renders <Summary /> without throwing 1`] = `
}
}
@media (max-width:37.4375rem) {
@media (max-width:47.9375rem) {
.c17 {
display: none;
}
@ -1294,11 +1297,6 @@ exports[`renders <Summary /> without throwing 1`] = `
>
<svg
height="17.07"
style={
Object {
"transform": "rotate(0deg)",
}
}
viewBox="0 0 17.07 17.07"
width="17.07"
xmlns="http://www.w3.org/2000/svg"
@ -1515,7 +1513,7 @@ exports[`renders <Summary /> without throwing 1`] = `
>
<span
name="react-responsive-mock"
query="only screen and (max-width: 37.4375rem)"
query="only screen and (max-width: 47.9375rem)"
>
<button
className="c40 c28 c29 c30"
@ -1550,7 +1548,7 @@ exports[`renders <Summary /> without throwing 1`] = `
</span>
<span
name="react-responsive-mock"
query="only screen and (min-width: 37.5rem)"
query="only screen and (min-width: 48rem)"
>
<button
className="c41 c28 c29 c30"
@ -2087,13 +2085,19 @@ exports[`renders <Summary instance /> without throwing 1`] = `
.c35 {
color: rgb(59,70,204);
-webkit-text-fill-color: currentcolor;
cursor: pointer;
-webkit-text-decoration: underline;
text-decoration: underline;
color: rgb(255,255,255);
-webkit-text-fill-color: currentcolor;
}
.c35:hover {
-webkit-text-decoration: none;
text-decoration: none;
}
.c35 a:hover {
color: rgb(45,56,132);
}
.c29 {
min-width: 7.5rem;
}
@ -2155,7 +2159,7 @@ exports[`renders <Summary instance /> without throwing 1`] = `
.c33:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c33:active,
@ -2251,7 +2255,7 @@ exports[`renders <Summary instance /> without throwing 1`] = `
.c27:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c27:active,
@ -2356,7 +2360,7 @@ exports[`renders <Summary instance /> without throwing 1`] = `
.c39:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c39:active,
@ -2476,7 +2480,7 @@ exports[`renders <Summary instance /> without throwing 1`] = `
.c40:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c40:active,
@ -2578,7 +2582,7 @@ exports[`renders <Summary instance /> without throwing 1`] = `
.c9 {
margin: 0;
color: rgb(73,73,73);
font-weight: 400;
font-weight: normal;
line-height: 1.875rem;
font-size: 1.5rem;
}
@ -2586,7 +2590,6 @@ exports[`renders <Summary instance /> without throwing 1`] = `
.c42 {
height: 1px;
background-color: rgb(216,216,216);
height: 1;
}
.c2 {
@ -2645,7 +2648,6 @@ exports[`renders <Summary instance /> without throwing 1`] = `
-moz-appearance: none;
appearance: none;
outline: 0;
white-space: pre-wrap;
font-family: "Roboto Mono",monospace;
width: 100%;
min-width: 100%;
@ -2863,7 +2865,7 @@ exports[`renders <Summary instance /> without throwing 1`] = `
}
}
@media (max-width:37.4375rem) {
@media (max-width:47.9375rem) {
.c17 {
display: none;
}
@ -2909,11 +2911,6 @@ exports[`renders <Summary instance /> without throwing 1`] = `
>
<svg
height="17.07"
style={
Object {
"transform": "rotate(0deg)",
}
}
viewBox="0 0 17.07 17.07"
width="17.07"
xmlns="http://www.w3.org/2000/svg"
@ -3132,7 +3129,7 @@ exports[`renders <Summary instance /> without throwing 1`] = `
>
<span
name="react-responsive-mock"
query="only screen and (max-width: 37.4375rem)"
query="only screen and (max-width: 47.9375rem)"
>
<button
className="c39 c28 c29 c30"
@ -3167,7 +3164,7 @@ exports[`renders <Summary instance /> without throwing 1`] = `
</span>
<span
name="react-responsive-mock"
query="only screen and (min-width: 37.5rem)"
query="only screen and (min-width: 48rem)"
>
<button
className="c40 c28 c29 c30"
@ -4019,13 +4016,19 @@ exports[`renders <Summary instance /> without throwing 2`] = `
.c35 {
color: rgb(59,70,204);
-webkit-text-fill-color: currentcolor;
cursor: pointer;
-webkit-text-decoration: underline;
text-decoration: underline;
color: rgb(255,255,255);
-webkit-text-fill-color: currentcolor;
}
.c35:hover {
-webkit-text-decoration: none;
text-decoration: none;
}
.c35 a:hover {
color: rgb(45,56,132);
}
.c29 {
min-width: 7.5rem;
}
@ -4087,7 +4090,7 @@ exports[`renders <Summary instance /> without throwing 2`] = `
.c33:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c33:active,
@ -4183,7 +4186,7 @@ exports[`renders <Summary instance /> without throwing 2`] = `
.c27:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c27:active,
@ -4288,7 +4291,7 @@ exports[`renders <Summary instance /> without throwing 2`] = `
.c39:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c39:active,
@ -4408,7 +4411,7 @@ exports[`renders <Summary instance /> without throwing 2`] = `
.c40:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c40:active,
@ -4510,7 +4513,7 @@ exports[`renders <Summary instance /> without throwing 2`] = `
.c9 {
margin: 0;
color: rgb(73,73,73);
font-weight: 400;
font-weight: normal;
line-height: 1.875rem;
font-size: 1.5rem;
}
@ -4518,7 +4521,6 @@ exports[`renders <Summary instance /> without throwing 2`] = `
.c42 {
height: 1px;
background-color: rgb(216,216,216);
height: 1;
}
.c2 {
@ -4577,7 +4579,6 @@ exports[`renders <Summary instance /> without throwing 2`] = `
-moz-appearance: none;
appearance: none;
outline: 0;
white-space: pre-wrap;
font-family: "Roboto Mono",monospace;
width: 100%;
min-width: 100%;
@ -4795,7 +4796,7 @@ exports[`renders <Summary instance /> without throwing 2`] = `
}
}
@media (max-width:37.4375rem) {
@media (max-width:47.9375rem) {
.c17 {
display: none;
}
@ -4841,11 +4842,6 @@ exports[`renders <Summary instance /> without throwing 2`] = `
>
<svg
height="17.07"
style={
Object {
"transform": "rotate(0deg)",
}
}
viewBox="0 0 17.07 17.07"
width="17.07"
xmlns="http://www.w3.org/2000/svg"
@ -5064,7 +5060,7 @@ exports[`renders <Summary instance /> without throwing 2`] = `
>
<span
name="react-responsive-mock"
query="only screen and (max-width: 37.4375rem)"
query="only screen and (max-width: 47.9375rem)"
>
<button
className="c39 c28 c29 c30"
@ -5099,7 +5095,7 @@ exports[`renders <Summary instance /> without throwing 2`] = `
</span>
<span
name="react-responsive-mock"
query="only screen and (min-width: 37.5rem)"
query="only screen and (min-width: 48rem)"
>
<button
className="c40 c28 c29 c30"
@ -5947,13 +5943,19 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
.c39 {
color: rgb(59,70,204);
-webkit-text-fill-color: currentcolor;
cursor: pointer;
-webkit-text-decoration: underline;
text-decoration: underline;
color: rgb(255,255,255);
-webkit-text-fill-color: currentcolor;
}
.c39:hover {
-webkit-text-decoration: none;
text-decoration: none;
}
.c39 a:hover {
color: rgb(45,56,132);
}
.c32 {
fill: rgb(59,70,204);
stroke: rgb(59,70,204);
@ -6074,7 +6076,7 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
.c27:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c27:active,
@ -6178,7 +6180,7 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
.c36:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c36:active,
@ -6274,7 +6276,7 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
.c38:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c38:active,
@ -6384,7 +6386,7 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
.c43:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c43:active,
@ -6526,7 +6528,7 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
.c44:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c44:active,
@ -6644,7 +6646,7 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
.c9 {
margin: 0;
color: rgb(73,73,73);
font-weight: 400;
font-weight: normal;
line-height: 1.875rem;
font-size: 1.5rem;
}
@ -6652,7 +6654,6 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
.c46 {
height: 1px;
background-color: rgb(216,216,216);
height: 1;
}
.c2 {
@ -6711,7 +6712,6 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
-moz-appearance: none;
appearance: none;
outline: 0;
white-space: pre-wrap;
font-family: "Roboto Mono",monospace;
width: 100%;
min-width: 100%;
@ -6791,7 +6791,6 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
-moz-appearance: none;
appearance: none;
outline: 0;
white-space: pre-wrap;
font-family: "Roboto Mono",monospace;
width: 100%;
min-width: 100%;
@ -7009,7 +7008,7 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
}
}
@media (max-width:37.4375rem) {
@media (max-width:47.9375rem) {
.c17 {
display: none;
}
@ -7055,11 +7054,6 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
>
<svg
height="17.07"
style={
Object {
"transform": "rotate(0deg)",
}
}
viewBox="0 0 17.07 17.07"
width="17.07"
xmlns="http://www.w3.org/2000/svg"
@ -7283,7 +7277,7 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
>
<span
name="react-responsive-mock"
query="only screen and (max-width: 37.4375rem)"
query="only screen and (max-width: 47.9375rem)"
>
<button
className="c43 c28 c29 c30"
@ -7328,7 +7322,7 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
</span>
<span
name="react-responsive-mock"
query="only screen and (min-width: 37.5rem)"
query="only screen and (min-width: 48rem)"
>
<button
className="c44 c28 c29 c30"
@ -7872,13 +7866,19 @@ exports[`renders <Summary state /> without throwing 1`] = `
.c35 {
color: rgb(59,70,204);
-webkit-text-fill-color: currentcolor;
cursor: pointer;
-webkit-text-decoration: underline;
text-decoration: underline;
color: rgb(255,255,255);
-webkit-text-fill-color: currentcolor;
}
.c35:hover {
-webkit-text-decoration: none;
text-decoration: none;
}
.c35 a:hover {
color: rgb(45,56,132);
}
.c29 {
min-width: 7.5rem;
}
@ -7940,7 +7940,7 @@ exports[`renders <Summary state /> without throwing 1`] = `
.c33:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c33:active,
@ -8036,7 +8036,7 @@ exports[`renders <Summary state /> without throwing 1`] = `
.c27:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c27:active,
@ -8141,7 +8141,7 @@ exports[`renders <Summary state /> without throwing 1`] = `
.c39:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c39:active,
@ -8261,7 +8261,7 @@ exports[`renders <Summary state /> without throwing 1`] = `
.c40:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c40:active,
@ -8363,7 +8363,7 @@ exports[`renders <Summary state /> without throwing 1`] = `
.c9 {
margin: 0;
color: rgb(73,73,73);
font-weight: 400;
font-weight: normal;
line-height: 1.875rem;
font-size: 1.5rem;
}
@ -8371,7 +8371,6 @@ exports[`renders <Summary state /> without throwing 1`] = `
.c42 {
height: 1px;
background-color: rgb(216,216,216);
height: 1;
}
.c2 {
@ -8430,7 +8429,6 @@ exports[`renders <Summary state /> without throwing 1`] = `
-moz-appearance: none;
appearance: none;
outline: 0;
white-space: pre-wrap;
font-family: "Roboto Mono",monospace;
width: 100%;
min-width: 100%;
@ -8510,7 +8508,6 @@ exports[`renders <Summary state /> without throwing 1`] = `
-moz-appearance: none;
appearance: none;
outline: 0;
white-space: pre-wrap;
font-family: "Roboto Mono",monospace;
width: 100%;
min-width: 100%;
@ -8728,7 +8725,7 @@ exports[`renders <Summary state /> without throwing 1`] = `
}
}
@media (max-width:37.4375rem) {
@media (max-width:47.9375rem) {
.c17 {
display: none;
}
@ -8774,11 +8771,6 @@ exports[`renders <Summary state /> without throwing 1`] = `
>
<svg
height="17.07"
style={
Object {
"transform": "rotate(0deg)",
}
}
viewBox="0 0 17.07 17.07"
width="17.07"
xmlns="http://www.w3.org/2000/svg"
@ -8995,7 +8987,7 @@ exports[`renders <Summary state /> without throwing 1`] = `
>
<span
name="react-responsive-mock"
query="only screen and (max-width: 37.4375rem)"
query="only screen and (max-width: 47.9375rem)"
>
<button
className="c39 c28 c29 c30"
@ -9030,7 +9022,7 @@ exports[`renders <Summary state /> without throwing 1`] = `
</span>
<span
name="react-responsive-mock"
query="only screen and (min-width: 37.5rem)"
query="only screen and (min-width: 48rem)"
>
<button
className="c40 c28 c29 c30"
@ -9567,13 +9559,19 @@ exports[`renders <Summary state /> without throwing 2`] = `
.c35 {
color: rgb(59,70,204);
-webkit-text-fill-color: currentcolor;
cursor: pointer;
-webkit-text-decoration: underline;
text-decoration: underline;
color: rgb(255,255,255);
-webkit-text-fill-color: currentcolor;
}
.c35:hover {
-webkit-text-decoration: none;
text-decoration: none;
}
.c35 a:hover {
color: rgb(45,56,132);
}
.c29 {
min-width: 7.5rem;
}
@ -9635,7 +9633,7 @@ exports[`renders <Summary state /> without throwing 2`] = `
.c33:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c33:active,
@ -9731,7 +9729,7 @@ exports[`renders <Summary state /> without throwing 2`] = `
.c27:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c27:active,
@ -9836,7 +9834,7 @@ exports[`renders <Summary state /> without throwing 2`] = `
.c39:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c39:active,
@ -9956,7 +9954,7 @@ exports[`renders <Summary state /> without throwing 2`] = `
.c40:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c40:active,
@ -10058,7 +10056,7 @@ exports[`renders <Summary state /> without throwing 2`] = `
.c9 {
margin: 0;
color: rgb(73,73,73);
font-weight: 400;
font-weight: normal;
line-height: 1.875rem;
font-size: 1.5rem;
}
@ -10066,7 +10064,6 @@ exports[`renders <Summary state /> without throwing 2`] = `
.c42 {
height: 1px;
background-color: rgb(216,216,216);
height: 1;
}
.c2 {
@ -10125,7 +10122,6 @@ exports[`renders <Summary state /> without throwing 2`] = `
-moz-appearance: none;
appearance: none;
outline: 0;
white-space: pre-wrap;
font-family: "Roboto Mono",monospace;
width: 100%;
min-width: 100%;
@ -10205,7 +10201,6 @@ exports[`renders <Summary state /> without throwing 2`] = `
-moz-appearance: none;
appearance: none;
outline: 0;
white-space: pre-wrap;
font-family: "Roboto Mono",monospace;
width: 100%;
min-width: 100%;
@ -10423,7 +10418,7 @@ exports[`renders <Summary state /> without throwing 2`] = `
}
}
@media (max-width:37.4375rem) {
@media (max-width:47.9375rem) {
.c17 {
display: none;
}
@ -10469,11 +10464,6 @@ exports[`renders <Summary state /> without throwing 2`] = `
>
<svg
height="17.07"
style={
Object {
"transform": "rotate(0deg)",
}
}
viewBox="0 0 17.07 17.07"
width="17.07"
xmlns="http://www.w3.org/2000/svg"
@ -10690,7 +10680,7 @@ exports[`renders <Summary state /> without throwing 2`] = `
>
<span
name="react-responsive-mock"
query="only screen and (max-width: 37.4375rem)"
query="only screen and (max-width: 47.9375rem)"
>
<button
className="c39 c28 c29 c30"
@ -10725,7 +10715,7 @@ exports[`renders <Summary state /> without throwing 2`] = `
</span>
<span
name="react-responsive-mock"
query="only screen and (min-width: 37.5rem)"
query="only screen and (min-width: 48rem)"
>
<button
className="c40 c28 c29 c30"
@ -11262,13 +11252,19 @@ exports[`renders <Summary state /> without throwing 3`] = `
.c36 {
color: rgb(59,70,204);
-webkit-text-fill-color: currentcolor;
cursor: pointer;
-webkit-text-decoration: underline;
text-decoration: underline;
color: rgb(255,255,255);
-webkit-text-fill-color: currentcolor;
}
.c36:hover {
-webkit-text-decoration: none;
text-decoration: none;
}
.c36 a:hover {
color: rgb(45,56,132);
}
.c29 {
min-width: 7.5rem;
}
@ -11344,7 +11340,7 @@ exports[`renders <Summary state /> without throwing 3`] = `
.c27:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c27:active,
@ -11448,7 +11444,7 @@ exports[`renders <Summary state /> without throwing 3`] = `
.c33:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c33:active,
@ -11544,7 +11540,7 @@ exports[`renders <Summary state /> without throwing 3`] = `
.c35:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c35:active,
@ -11654,7 +11650,7 @@ exports[`renders <Summary state /> without throwing 3`] = `
.c40:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c40:active,
@ -11796,7 +11792,7 @@ exports[`renders <Summary state /> without throwing 3`] = `
.c41:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c41:active,
@ -11915,7 +11911,7 @@ exports[`renders <Summary state /> without throwing 3`] = `
.c9 {
margin: 0;
color: rgb(73,73,73);
font-weight: 400;
font-weight: normal;
line-height: 1.875rem;
font-size: 1.5rem;
}
@ -11923,7 +11919,6 @@ exports[`renders <Summary state /> without throwing 3`] = `
.c43 {
height: 1px;
background-color: rgb(216,216,216);
height: 1;
}
.c2 {
@ -11982,7 +11977,6 @@ exports[`renders <Summary state /> without throwing 3`] = `
-moz-appearance: none;
appearance: none;
outline: 0;
white-space: pre-wrap;
font-family: "Roboto Mono",monospace;
width: 100%;
min-width: 100%;
@ -12062,7 +12056,6 @@ exports[`renders <Summary state /> without throwing 3`] = `
-moz-appearance: none;
appearance: none;
outline: 0;
white-space: pre-wrap;
font-family: "Roboto Mono",monospace;
width: 100%;
min-width: 100%;
@ -12280,7 +12273,7 @@ exports[`renders <Summary state /> without throwing 3`] = `
}
}
@media (max-width:37.4375rem) {
@media (max-width:47.9375rem) {
.c17 {
display: none;
}
@ -12326,11 +12319,6 @@ exports[`renders <Summary state /> without throwing 3`] = `
>
<svg
height="17.07"
style={
Object {
"transform": "rotate(0deg)",
}
}
viewBox="0 0 17.07 17.07"
width="17.07"
xmlns="http://www.w3.org/2000/svg"
@ -12547,7 +12535,7 @@ exports[`renders <Summary state /> without throwing 3`] = `
>
<span
name="react-responsive-mock"
query="only screen and (max-width: 37.4375rem)"
query="only screen and (max-width: 47.9375rem)"
>
<button
className="c40 c28 c29 c30"
@ -12582,7 +12570,7 @@ exports[`renders <Summary state /> without throwing 3`] = `
</span>
<span
name="react-responsive-mock"
query="only screen and (min-width: 37.5rem)"
query="only screen and (min-width: 48rem)"
>
<button
className="c41 c28 c29 c30"

View File

@ -127,7 +127,7 @@ exports[`renders <Toolbar /> without throwing 1`] = `
.c7:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c7:active,
@ -421,7 +421,7 @@ exports[`renders <Toolbar actionLabel /> without throwing 1`] = `
.c7:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c7:active,
@ -720,7 +720,7 @@ exports[`renders <Toolbar actionable /> without throwing 1`] = `
.c7:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c7:active,
@ -1031,7 +1031,7 @@ exports[`renders <Toolbar onActionClick /> without throwing 1`] = `
.c7:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c7:active,
@ -1325,7 +1325,7 @@ exports[`renders <Toolbar searchLabel /> without throwing 1`] = `
.c7:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c7:active,
@ -1619,7 +1619,7 @@ exports[`renders <Toolbar searchPlaceholder /> without throwing 1`] = `
.c7:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c7:active,
@ -1913,7 +1913,7 @@ exports[`renders <Toolbar searchable /> without throwing 1`] = `
.c7:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c7:active,

View File

@ -2,6 +2,8 @@ import React from 'react';
import { P as BaseP } from 'joyent-ui-toolkit';
import { Row, Col } from 'joyent-react-styled-flexboxgrid';
import styled from 'styled-components';
import { Anchor } from 'joyent-ui-toolkit';
import { Link } from 'react-router-dom';
const P = styled(BaseP)`
font-weight: 200;
@ -9,13 +11,15 @@ const P = styled(BaseP)`
export default ({ href = '', children }) => (
<Row>
<Col xs="12" sm="7">
<Col xs={12} sm={7}>
<P>
{children}{' '}
{href ? (
<a target="__blank" href={href} rel="noopener noreferrer">
Read the docs
</a>
<Anchor>
<Link target="__blank" to={href} rel="noopener noreferrer">
Read the docs
</Link>
</Anchor>
) : null}
</P>
</Col>

View File

@ -1,5 +1,6 @@
import React from 'react';
import { Row, Col } from 'joyent-react-styled-flexboxgrid';
import { withTheme } from 'styled-components';
import { Margin } from 'styled-components-spacing';
import Flex from 'styled-flex-component';
@ -15,159 +16,172 @@ import {
const { SmallOnly, Medium } = QueryBreakpoints;
export default ({
submitting = false,
statuses = {},
allowedActions = {},
onStart,
onStop,
onReboot,
onRemove
}) => (
<StickyFooter fill="disabled" fixed bottom>
<Row between="xs" middle="xs">
<Col xs="7">
<Flex>
{onStart && [
<SmallOnly key="small-only">
<Button
type="button"
onClick={onStart}
disabled={submitting || !allowedActions.start}
loading={submitting && statuses.starting}
secondary
small
icon
>
<StartIcon disabled={submitting || !allowedActions.start} />
</Button>
</SmallOnly>,
<Margin right="1">
<Medium key="medium">
export default withTheme(
({
submitting = false,
statuses = {},
allowedActions = {},
onStart,
onStop,
onReboot,
onRemove,
theme = {}
}) => (
<StickyFooter fixed bottom fill="#FAFAFA">
<Row between="xs" middle="xs">
<Col xs={7}>
<Flex>
{onStart && [
<SmallOnly key="small-only">
<Button
type="button"
onClick={onStart}
disabled={submitting || !allowedActions.start}
loading={submitting && statuses.starting}
secondary
small
icon
>
<Margin right="1">
<StartIcon disabled={submitting || !allowedActions.start} />
</Margin>
<span>Start</span>
<StartIcon disabled={submitting || !allowedActions.start} />
</Button>
</Medium>
</Margin>
]}
{onStop && [
<SmallOnly key="small-only">
<Button
type="button"
onClick={onStop}
disabled={submitting || !allowedActions.stop}
loading={submitting && statuses.stopping}
secondary
small
icon
>
<StopIcon disabled={submitting || !allowedActions.stop} />
</Button>
</SmallOnly>,
<Margin right="1">
<Medium key="medium">
</SmallOnly>,
<Margin right={1}>
<Medium key="medium">
<Button
type="button"
onClick={onStart}
disabled={submitting || !allowedActions.start}
loading={submitting && statuses.starting}
secondary
icon
>
<Margin right={1}>
<StartIcon
disabled={submitting || !allowedActions.start}
/>
</Margin>
<span>Start</span>
</Button>
</Medium>
</Margin>
]}
{onStop && [
<SmallOnly key="small-only">
<Button
type="button"
onClick={onStop}
disabled={submitting || !allowedActions.stop}
loading={submitting && statuses.stopping}
secondary
small
icon
>
<Margin right="1">
<StopIcon disabled={submitting || !allowedActions.stop} />
</Margin>
<span>Stop</span>
<StopIcon disabled={submitting || !allowedActions.stop} />
</Button>
</Medium>
</Margin>
]}
{onReboot && [
<SmallOnly key="small-only">
<Button
type="button"
onClick={onReboot}
disabled={submitting || !allowedActions.reboot}
loading={submitting && statuses.rebooting}
secondary
small
icon
>
<ResetIcon disabled={submitting || !allowedActions.reboot} />
</Button>
</SmallOnly>,
<Margin right="1">
<Medium key="medium">
</SmallOnly>,
<Margin right={1}>
<Medium key="medium">
<Button
type="button"
onClick={onStop}
disabled={submitting || !allowedActions.stop}
loading={submitting && statuses.stopping}
secondary
icon
>
<Margin right={1}>
<StopIcon disabled={submitting || !allowedActions.stop} />
</Margin>
<span>Stop</span>
</Button>
</Medium>
</Margin>
]}
{onReboot && [
<SmallOnly key="small-only">
<Button
type="button"
onClick={onReboot}
disabled={submitting || !allowedActions.reboot}
loading={submitting && statuses.rebooting}
secondary
small
icon
>
<ResetIcon disabled={submitting || !allowedActions.reboot} />
<span>Reboot</span>
</Button>
</Medium>
</Margin>
]}
</Flex>
</Col>
{onRemove && (
<Col xs="5">
<SmallOnly key="small-only">
<Button
type="button"
onClick={onRemove}
disabled={submitting || !allowedActions.remove}
loading={submitting && statuses.removing}
secondary
error
right
small
icon
>
<DeleteIcon
disabled={submitting}
fill={submitting || !allowedActions.remove ? undefined : 'red'}
/>
</Button>
</SmallOnly>
<Medium key="medium">
<Button
type="button"
onClick={onRemove}
disabled={submitting || !allowedActions.remove}
loading={submitting && statuses.removing}
error
secondary
right
icon
>
<Margin right="1">
</SmallOnly>,
<Margin right={1}>
<Medium key="medium">
<Button
type="button"
onClick={onReboot}
disabled={submitting || !allowedActions.reboot}
loading={submitting && statuses.rebooting}
secondary
icon
>
<ResetIcon
disabled={submitting || !allowedActions.reboot}
/>
<span>Reboot</span>
</Button>
</Medium>
</Margin>
]}
</Flex>
</Col>
{onRemove && (
<Col xs={5}>
<SmallOnly key="small-only">
<Button
type="button"
onClick={onRemove}
disabled={submitting || !allowedActions.remove}
loading={submitting && statuses.removing}
secondary
error
right
small
icon
>
<DeleteIcon
disabled={submitting || !allowedActions.remove}
disabled={submitting}
fill={
submitting || !allowedActions.remove ? undefined : 'red'
!(submitting || !allowedActions.remove)
? theme.red
: undefined
}
/>
</Margin>
<span>Remove</span>
</Button>
</Medium>
</Col>
)}
</Row>
</StickyFooter>
</Button>
</SmallOnly>
<Medium key="medium">
<Button
type="button"
onClick={onRemove}
disabled={submitting || !allowedActions.remove}
loading={submitting && statuses.removing}
error
secondary
right
icon
>
<Margin right={1}>
<DeleteIcon
disabled={submitting || !allowedActions.remove}
fill={
!(submitting || !allowedActions.remove)
? theme.red
: undefined
}
/>
</Margin>
<span>Remove</span>
</Button>
</Medium>
</Col>
)}
</Row>
</StickyFooter>
)
);

View File

@ -64,7 +64,7 @@ const MarginalPaginationItem = styled(PaginationItem)`
`;
const Actions = styled(Flex)`
height: 100%;
height: ${remcalc(48)};
`;
const PopoverItem = styled(BasePopoverItem)`
@ -98,8 +98,8 @@ export const Item = ({
onClick
}) => (
<TableTr>
<TableTd middle left>
<FormGroup name={id} field={Field}>
<TableTd padding="0" paddingLeft={remcalc(12)} middle left>
<FormGroup name={id} paddingTop={remcalc(4)} field={Field}>
<Checkbox noMargin />
</FormGroup>
</TableTd>
@ -124,13 +124,9 @@ export const Item = ({
<TableTd xs="0" sm="130" middle left>
<code>{id.substring(0, 7)}</code>
</TableTd>
{mutating ? (
<TableTd hasBorder="left" center middle>
<ActionsIcon disabled />
</TableTd>
) : (
{!mutating ? (
<PopoverContainer clickable>
<TableTd hasBorder="left">
<TableTd padding="0" hasBorder="left">
<PopoverTarget box>
<Actions alignCenter justifyCenter>
<ActionsIcon />
@ -149,7 +145,7 @@ export const Item = ({
<PopoverItem disabled={!allowedActions.remove} onClick={onRemove}>
Delete
</PopoverItem>
<Padding bottom="2">
<Padding bottom={2}>
<PopoverDivider />
</Padding>
<PopoverItem disabled={false} onClick={onCreateImage}>
@ -164,6 +160,10 @@ export const Item = ({
</Popover>
</TableTd>
</PopoverContainer>
) : (
<TableTd padding="0" hasBorder="left" center middle>
<ActionsIcon disabled />
</TableTd>
)}
</TableTr>
);
@ -191,8 +191,8 @@ export default ({
<Table>
<TableThead>
<TableTr>
<TableTh xs="32" middle left>
<FormGroup>
<TableTh xs="32" padding="0" paddingLeft={remcalc(12)} middle left>
<FormGroup paddingTop={remcalc(4)}>
<Checkbox
checked={allSelected}
disabled={submitting || noInstances}
@ -246,11 +246,11 @@ export default ({
>
<span>Short ID </span>
</TableTh>
<TableTh xs="60" />
<TableTh xs="60" padding="0" />
</TableTr>
</TableThead>
<TableTbody>{children}</TableTbody>
{noInstances ? null : (
{!noInstances ? (
<PaginationTableFoot colSpan="6">
<PaginationItem
to={`${Global().pathname}?${queryString.stringify({
@ -336,7 +336,7 @@ export default ({
Next
</PaginationItem>
</PaginationTableFoot>
)}
) : null}
</Table>
</form>
);

View File

@ -1,6 +1,6 @@
import React from 'react';
import { KeyValue } from 'joyent-ui-resource-widgets';
import { KeyValue } from 'joyent-ui-toolkit';
import Editor from 'joyent-ui-toolkit/dist/es/editor';
export const AddForm = props => (

View File

@ -15,6 +15,7 @@ import {
TableTbody,
TableTd,
Checkbox,
KeyValue,
Popover,
PopoverContainer,
PopoverTarget,
@ -23,7 +24,7 @@ import {
DotIcon
} from 'joyent-ui-toolkit';
import { Empty, KeyValue } from 'joyent-ui-resource-widgets';
import { Empty } from 'joyent-ui-resource-widgets';
const stateColor = {
QUEUED: 'primary',
@ -37,14 +38,10 @@ const loadingState = {
export const Item = ({ name, state, created, onStart, onRemove, mutating }) => (
<TableTr>
{mutating ? (
<TableTd colSpan="5">
<StatusLoader msg={loadingState[mutating]} />
</TableTd>
) : (
{!mutating ? (
<Fragment>
<TableTd middle left>
<FormGroup name={name} field={Field}>
<TableTd padding="0" paddingLeft={remcalc(12)} middle left>
<FormGroup paddingTop={remcalc(4)} name={name} field={Field}>
<Checkbox noMargin />
</FormGroup>
</TableTd>
@ -59,21 +56,25 @@ export const Item = ({ name, state, created, onStart, onRemove, mutating }) => (
{distanceInWordsToNow(created)}
</TableTd>
<PopoverContainer clickable>
<TableTd hasBorder="left">
<TableTd padding="0" hasBorder="left">
<PopoverTarget box>
<ActionsIcon />
</PopoverTarget>
<Popover placement="top">
<Margin vertical="2" horizontal="3">
<Margin vertical={2} horizontal={3}>
<PopoverItem onClick={onStart}>Start</PopoverItem>
</Margin>
<Margin vertical="2" horizontal="3">
<Margin vertical={2} horizontal={3}>
<PopoverItem onClick={onRemove}>Remove</PopoverItem>
</Margin>
</Popover>
</TableTd>
</PopoverContainer>
</Fragment>
) : (
<TableTd colSpan="5">
<StatusLoader msg={loadingState[mutating]} />
</TableTd>
)}
</TableTr>
);
@ -111,8 +112,8 @@ export default ({
<Table>
<TableThead>
<TableTr>
<TableTh xs="32" middle left>
<FormGroup>
<TableTh xs="32" padding="0" paddingLeft={remcalc(12)} middle left>
<FormGroup paddingTop={remcalc(4)}>
<Checkbox
checked={allSelected}
disabled={submitting}
@ -154,7 +155,7 @@ export default ({
>
<span>Created </span>
</TableTh>
<TableTh xs="60" />
<TableTh xs="60" padding="0" />
</TableTr>
</TableThead>
<TableTbody>
@ -170,6 +171,6 @@ export default ({
: null}
</TableTbody>
</Table>
{snapshots.length ? null : <Empty borderTop>You have no Snapshots</Empty>}
{!snapshots.length ? <Empty borderTop>You have no Snapshots</Empty> : null}
</Fragment>
);

View File

@ -1,14 +1,13 @@
import React from 'react';
import distanceInWordsToNow from 'date-fns/distance_in_words_to_now';
import { Row, Col } from 'joyent-react-styled-flexboxgrid';
import Flex, { FlexItem } from 'styled-flex-component';
import styled from 'styled-components';
import { default as Flex, FlexItem } from 'styled-flex-component';
import styled, { withTheme } from 'styled-components';
import { Margin, Padding } from 'styled-components-spacing';
import titleCase from 'title-case';
import get from 'lodash.get';
import remcalc from 'remcalc';
import { Field } from 'redux-form';
import { ValueBreakpoints as breakpoints } from 'joyent-ui-toolkit';
import {
Card,
@ -72,7 +71,7 @@ const VerticalDivider = styled.div`
align-self: flex-end;
margin: 0 ${remcalc(12)};
@media (max-width: ${remcalc(breakpoints.small.upper)}) {
@media (max-width: ${remcalc(767)}) {
display: none;
}
`;
@ -99,8 +98,8 @@ export const Meta = ({
...instance
}) => [
<Row middle="xs">
<Col xs="12">
<Margin bottom="1">
<Col xs={12}>
<Margin bottom={1}>
<H2>
{editingName ? (
<form onSubmit={handleSubmit}>
@ -114,7 +113,7 @@ export const Meta = ({
/>
<FormMeta />
</FormGroup>
<Margin left="1">
<Margin left={1}>
<Button
type="submit"
disabled={submitting}
@ -129,7 +128,7 @@ export const Meta = ({
) : (
<Flex>
{instance.name}
<Actionable left="2" onClick={editName}>
<Actionable left={2} onClick={editName}>
<EditIcon />
</Actionable>
</Flex>
@ -138,7 +137,7 @@ export const Meta = ({
</Margin>
</Col>
</Row>,
<Margin vertical="1">
<Margin vertical={1}>
<Flex>
<TrimedLabel>
{image && image.name ? titleCase(image.name) : 'Custom Image'}
@ -161,7 +160,7 @@ export const Meta = ({
{titleCase(state)}
</Flex>
</Flex>
<Margin top="1">
<Margin top={1}>
<Flex>
<Flex>
<GreyLabel>Created: </GreyLabel>
@ -177,218 +176,227 @@ export const Meta = ({
</Margin>
];
export default ({
instance = {},
starting = false,
stopping = false,
rebooting = false,
removing = false,
onAction,
...props
}) => (
<Row>
<Col xs="12" sm="12" md="9">
<Card>
<CardOutlet>
<Padding all="5">
<Meta {...instance} {...props} />
<Margin top="3">
<Row between="xs">
<Col xs="9">
<Flex>
<FlexItem>
<Margin right="1">
<ButtonGroup>
{instance.state === 'STOPPED' ? (
<Button
type="button"
loading={starting}
disabled={instance.state !== 'STOPPED'}
onClick={() => onAction('start')}
secondary
bold
icon
>
<Margin right="2">
<StartIcon
disabled={instance.state !== 'STOPPED'}
/>
</Margin>
<span>Start</span>
</Button>
) : (
<Button
type="button"
loading={stopping}
disabled={instance.state !== 'RUNNING'}
onClick={() => onAction('stop')}
secondary
bold
icon
>
<Margin right="2">
<StopIcon
disabled={instance.state !== 'RUNNING'}
/>
</Margin>
<span>Stop</span>
</Button>
)}
<PopoverButton secondary>
<PopoverItem
disabled={instance.state === 'RUNNING'}
onClick={() =>
instance.state === 'RUNNING'
? null
: onAction('start')
}
>
Start
</PopoverItem>
<PopoverItem
disabled={instance.state === 'STOPPED'}
onClick={() =>
instance.state === 'STOPPED'
? null
: onAction('reboot')
}
>
Restart
</PopoverItem>
<PopoverItem
disabled={instance.state === 'STOPPED'}
onClick={() =>
instance.state === 'STOPPED'
? null
: onAction('stop')
}
>
Stop
</PopoverItem>
</PopoverButton>
</ButtonGroup>
</Margin>
</FlexItem>
<FlexItem>
export default withTheme(
({
instance = {},
starting = false,
stopping = false,
rebooting = false,
removing = false,
onAction,
theme = {},
...props
}) => (
<Row>
<Col xs={12} sm={12} md={9}>
<Card>
<CardOutlet>
<Padding all={5}>
<Meta {...instance} {...props} />
<Margin top={3}>
<Row between="xs">
<Col xs={9}>
<Flex>
<FlexItem>
<Margin right={1}>
<ButtonGroup>
{instance.state === 'STOPPED' ? (
<Button
type="button"
loading={starting}
disabled={instance.state !== 'STOPPED'}
onClick={() => onAction('start')}
secondary
bold
icon
>
<Margin right={2}>
<StartIcon
disabled={instance.state !== 'STOPPED'}
/>
</Margin>
<span>Start</span>
</Button>
) : (
<Button
type="button"
loading={stopping}
disabled={instance.state !== 'RUNNING'}
onClick={() => onAction('stop')}
secondary
bold
icon
>
<Margin right={2}>
<StopIcon
disabled={instance.state !== 'RUNNING'}
/>
</Margin>
<span>Stop</span>
</Button>
)}
<PopoverButton secondary>
<PopoverItem
disabled={instance.state === 'RUNNING'}
onClick={() =>
instance.state === 'RUNNING'
? null
: onAction('start')
}
>
Start
</PopoverItem>
<PopoverItem
disabled={instance.state === 'STOPPED'}
onClick={() =>
instance.state === 'STOPPED'
? null
: onAction('reboot')
}
>
Restart
</PopoverItem>
<PopoverItem
disabled={instance.state === 'STOPPED'}
onClick={() =>
instance.state === 'STOPPED'
? null
: onAction('stop')
}
>
Stop
</PopoverItem>
</PopoverButton>
</ButtonGroup>
</Margin>
</FlexItem>
<FlexItem>
<Button
href={`${GLOBAL.origin}/images/~create/${
instance.id
}`}
target="__blank"
rel="noopener noreferrer"
secondary
bold
icon
>
Create Image
</Button>
</FlexItem>
</Flex>
</Col>
<Col xs={3}>
<SmallOnly>
<Button
href={`${GLOBAL.origin}/images/~create/${instance.id}`}
target="__blank"
rel="noopener noreferrer"
type="button"
loading={removing}
disabled={
['RUNNING', 'STOPPED'].indexOf(instance.state) < 0
}
onClick={() => onAction('remove')}
secondary
small
right
icon
error
>
<Margin right={2}>
<DeleteIcon
fill={theme.red}
disabled={
['RUNNING', 'STOPPED'].indexOf(instance.state) < 0
}
/>
</Margin>
</Button>
</SmallOnly>
<Medium>
<Button
type="button"
loading={removing}
disabled={
['RUNNING', 'STOPPED'].indexOf(instance.state) < 0
}
onClick={() => onAction('remove')}
secondary
bold
right
icon
error
>
Create Image
<Margin right={2}>
<DeleteIcon
fill={
['RUNNING', 'STOPPED'].indexOf(instance.state) >=
0
? theme.red
: undefined
}
disabled={
['RUNNING', 'STOPPED'].indexOf(instance.state) < 0
}
/>
</Margin>
<span>Delete</span>
</Button>
</FlexItem>
</Flex>
</Col>
<Col xs="3">
<SmallOnly>
<Button
type="button"
loading={removing}
disabled={
['RUNNING', 'STOPPED'].indexOf(instance.state) < 0
}
onClick={() => onAction('remove')}
secondary
small
right
icon
error
>
<Margin right="2">
<DeleteIcon
fill="red"
disabled={
['RUNNING', 'STOPPED'].indexOf(instance.state) < 0
}
/>
</Margin>
</Button>
</SmallOnly>
<Medium>
<Button
type="button"
loading={removing}
disabled={
['RUNNING', 'STOPPED'].indexOf(instance.state) < 0
}
onClick={() => onAction('remove')}
secondary
bold
right
icon
error
>
<Margin right="2">
<DeleteIcon
fill={
['RUNNING', 'STOPPED'].indexOf(instance.state) >= 0
? 'red'
: undefined
}
disabled={
['RUNNING', 'STOPPED'].indexOf(instance.state) < 0
}
/>
</Margin>
<span>Delete</span>
</Button>
</Medium>
</Col>
</Row>
</Margin>
<Margin bottom="5" top="3">
<Divider height={1} />
</Margin>
<Margin bottom="3">
<CopiableField
text={(instance.id || '').split('-')[0]}
label="Short ID"
/>
</Margin>
<Margin bottom="3">
<CopiableField text={instance.id} label="ID" />
</Margin>
<Margin bottom="3">
<CopiableField text={instance.compute_node} label="CN UUID" />
</Margin>
{instance.image &&
instance.image.id && (
<Margin bottom="3">
<CopiableField text={instance.image.id} label="Image UUID" />
</Medium>
</Col>
</Row>
</Margin>
<Margin bottom={5} top={3}>
<Divider height={1} />
</Margin>
<Margin bottom={3}>
<CopiableField
text={(instance.id || '').split('-')[0]}
label="Short ID"
/>
</Margin>
<Margin bottom={3}>
<CopiableField text={instance.id} label="ID" />
</Margin>
<Margin bottom={3}>
<CopiableField text={instance.compute_node} label="CN UUID" />
</Margin>
{instance.image &&
instance.image.id && (
<Margin bottom={3}>
<CopiableField
text={instance.image.id}
label="Image UUID"
/>
</Margin>
)}
<Margin bottom={3}>
<CopiableField
text={`ssh root@${instance.primary_ip}`}
label="Login"
/>
</Margin>
{get(instance, 'ips.public', []).map((ip, i, ips) => (
<Margin bottom={3}>
<CopiableField
key={`public-${i}`}
label={`Public IP address ${ips.length > 1 ? i + 1 : ''}`}
text={ip}
/>
</Margin>
)}
<Margin bottom="3">
<CopiableField
text={`ssh root@${instance.primary_ip}`}
label="Login"
/>
</Margin>
{get(instance, 'ips.public', []).map((ip, i, ips) => (
<Margin bottom="3">
<CopiableField
key={`public-${i}`}
label={`Public IP address ${ips.length > 1 ? i + 1 : ''}`}
text={ip}
/>
</Margin>
))}
{get(instance, 'ips.private', []).map((ip, i, ips) => (
<Margin bottom="3">
<CopiableField
key={`private-${i}`}
noMargin={i === ips.length - 1}
label={`Private IP address ${ips.length > 1 ? i + 1 : ''}`}
text={ip}
/>
</Margin>
))}
</Padding>
</CardOutlet>
</Card>
</Col>
</Row>
))}
{get(instance, 'ips.private', []).map((ip, i, ips) => (
<Margin bottom={3}>
<CopiableField
key={`private-${i}`}
noMargin={i === ips.length - 1}
label={`Private IP address ${ips.length > 1 ? i + 1 : ''}`}
text={ip}
/>
</Margin>
))}
</Padding>
</CardOutlet>
</Card>
</Col>
</Row>
)
);

View File

@ -18,7 +18,7 @@ export const Toolbar = ({
<Flex justifyBetween alignEnd>
<FormGroup name="filter" field={Field}>
<FormLabel>{searchLabel}</FormLabel>
<Margin top="0.5">
<Margin top={0.5}>
<Input placeholder={searchPlaceholder} disabled={!searchable} />
</Margin>
</FormGroup>

View File

@ -18,7 +18,6 @@ export const Forms = {
};
export const Values = {
IC_SHOW_CLI: 'INSTANCE_CREATION_SHOW_CLI',
IC_AFF_V_ADD_OPEN: 'INSTANCE_CREATION_AFFINITY_VALUE_ADD_OPEN',
IC_AFF_V_EDIT_OPEN: 'INSTANCE_CREATION_AFFINITY_VALUE_EDIT_OPEN',
IC_AFF_V_AFF: 'INSTANCE_CREATION_AFFINITY_VALUE_AFFINITY',

View File

@ -1,14 +1,21 @@
import React, { Component, Fragment } from 'react';
import React, { Component } from 'react';
import { Margin, Padding } from 'styled-components-spacing';
import { graphql, compose } from 'react-apollo';
import ReduxForm from 'declarative-redux-form';
import { connect } from 'react-redux';
import { SubmissionError, destroy } from 'redux-form';
import { set, destroyAll } from 'react-redux-values';
import Flex, { FlexItem } from 'styled-flex-component';
import intercept from 'apr-intercept';
import get from 'lodash.get';
import styled from 'styled-components';
import omit from 'lodash.omit';
import uniqBy from 'lodash.uniqby';
import constantCase from 'constant-case';
import { H3, ViewContainer, Button } from 'joyent-ui-toolkit';
import { Forms } from '../constants';
import { Provider as ResourceSteps } from 'joyent-ui-resource-step';
import parseError from '../state/parse-error';
import CreateInstanceMutation from '../graphql/create-instance.gql';
import {
Name,
@ -20,21 +27,10 @@ import {
UserScript,
Firewall,
CNS,
Affinity,
generatePayload,
Footer
Affinity
} from 'joyent-ui-instance-steps';
import { Provider as ResourceSteps } from 'joyent-ui-resource-step';
import { Anchor, H3, ViewContainer, Button } from 'joyent-ui-toolkit';
import { Forms, Values } from '@root/constants';
import parseError from '@state/parse-error';
import CreateInstanceMutation from '@graphql/create-instance.gql';
const { IC_F } = Forms;
const { IC_SHOW_CLI } = Values;
const names = {
name: 'IC_NAME',
image: 'IC_IMAGE',
@ -91,23 +87,14 @@ class CreateInstance extends Component {
};
render() {
const {
match,
steps,
showCli = false,
handleDefocus,
handleToggleShowCli,
handleSubmit,
disabled
} = this.props;
const { match, steps, handleDefocus, handleSubmit, disabled } = this.props;
const { params } = match;
const { step } = params;
const {
name,
image,
package: pkg,
package: packageResult,
networks,
tags,
metadata,
@ -116,19 +103,9 @@ class CreateInstance extends Component {
affinity
} = steps;
const Mask = styled.div`
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.25);
position: absolute;
display: ${showCli ? 'block' : 'none'};
`;
return (
<ViewContainer main>
<Margin top="5">
<Margin top={5}>
<H3>Create Instance</H3>
</Margin>
<Padding top="5">
@ -161,7 +138,7 @@ class CreateInstance extends Component {
next="networks"
saved={steps.package}
onDefocus={handleDefocus('package')}
preview={pkg}
preview={packageResult}
/>
</Margin>
<Margin bottom="5">
@ -241,44 +218,21 @@ class CreateInstance extends Component {
/>
</Margin>
</ResourceSteps>
<Margin bottom="5">
<Flex alignCenter>
<FlexItem>
<ReduxForm form={IC_F} onSubmit={handleSubmit}>
{({ handleSubmit, submitting }) => (
<form onSubmit={handleSubmit}>
<Button
disabled={disabled || !this.isFormValid()}
loading={submitting}
>
Deploy
</Button>
</form>
)}
</ReduxForm>
</FlexItem>
<FlexItem>
<Margin left={3}>
<Anchor
<Margin bottom={5}>
<ReduxForm form={IC_F} onSubmit={handleSubmit}>
{({ handleSubmit, submitting }) => (
<form onSubmit={handleSubmit}>
<Button
disabled={disabled || !this.isFormValid()}
onClick={() => handleToggleShowCli(!showCli)}
loading={submitting}
>
View CLI Details
</Anchor>
</Margin>
</FlexItem>
</Flex>
Deploy
</Button>
</form>
)}
</ReduxForm>
</Margin>
</Padding>
{showCli ? (
<Fragment>
<Footer
getData={() => generatePayload(steps)}
onCloseCli={() => handleToggleShowCli(!showCli)}
/>
<Mask onClick={() => handleToggleShowCli(!showCli)} />
</Fragment>
) : null}
</ViewContainer>
);
}
@ -301,7 +255,6 @@ export default compose(
};
const error = get(form, `${IC_F}.error`, null);
const showCli = Boolean(values[IC_SHOW_CLI]);
// Maybe re-use saved to only write the rule once
const disabled = !(
@ -318,24 +271,100 @@ export default compose(
return {
disabled,
showCli,
forms: Object.keys(form), // improve this
error,
steps
};
}),
connect(null, (dispatch, { steps = {}, forms, history, createInstance }) => {
const parseAffRule = ({
conditional,
placement,
type: identity,
name,
pattern,
value
}) => {
const type = constantCase(
`${conditional}_${placement === 'same' ? 'equal' : 'not_equal'}`
);
const patterns = {
equalling: value => value,
starting: value => `/^${value}/`
};
const _name = identity === 'name' ? 'instance' : name;
const _value = patterns[pattern](type === 'name' ? name : value);
return {
type,
key: _name,
value: _value
};
};
return {
handleToggleShowCli: value => {
return dispatch(set({ name: IC_SHOW_CLI, value }));
},
handleDefocus: name => value => {
return dispatch(set({ name: names[name], value }));
},
handleSubmit: async () => {
const _affinity = steps.affinity ? parseAffRule(steps.affinity) : null;
const _name = steps.name && steps.name.name.toLowerCase();
const _metadata =
(steps.metadata && steps.metadata.map(a => omit(a, 'open'))) || [];
const _tags =
(steps.tags &&
uniqBy(steps.tags.map(a => omit(a, 'expanded')), 'name').map(a =>
omit(a, 'expanded')
)) ||
[];
const _networks = steps.networks && steps.networks.map(({ id }) => id);
if (steps['user-script'] && steps['user-script'].lines) {
_metadata.push({
name: 'user-script',
value: steps['user-script'].script
});
}
if (steps.cns) {
_tags.push({
name: 'triton.cns.disable',
value: !steps.cns.cnsEnabled
});
}
if (
steps.cns &&
(steps.cns.serviceNames &&
steps.cns.serviceNames.length &&
steps.cns.cnsEnabled)
) {
_tags.push({
name: 'triton.cns.services',
value: steps.cns.serviceNames.join(',')
});
}
const [err, res] = await intercept(
createInstance({
variables: generatePayload(steps)
variables: {
name: _name,
package: steps.package.id,
image: steps.image.id,
affinity: _affinity ? [_affinity] : [],
metadata: _metadata,
tags: _tags,
firewall_enabled: steps.firewall
? steps.firewall.enabled
: undefined,
networks: _networks && _networks.length ? _networks : undefined
}
})
);

View File

@ -23,7 +23,6 @@ exports[`renders <Summary /> without throwing 1`] = `
margin: 0;
line-height: 1.5rem;
font-size: 0.9375rem;
margin: 0;
}
.c2 {
@ -72,7 +71,7 @@ exports[`renders <Summary /> without throwing 1`] = `
.c0 {
box-sizing: border-box;
width: 100%;
max-width: 78.75rem;
max-width: 62.5rem;
padding-bottom: 1.125rem;
display: -webkit-box;
display: -webkit-flex;
@ -115,24 +114,10 @@ exports[`renders <Summary /> without throwing 1`] = `
}
}
@media only screen and (max-width:37.4375rem) {
@media only screen and (max-width:47.9375rem) {
.c0 {
padding-left: 0.75rem;
padding-right: 0.75rem;
}
}
@media only screen and (min-width:37.5rem) and (max-width:62.4375rem) {
.c0 {
padding-left: 1.875rem;
padding-right: 1.875rem;
}
}
@media only screen and (min-width:62.5rem) and (max-width:87.4375rem) {
.c0 {
padding-left: 4.375rem;
padding-right: 4.375rem;
padding-left: 0.375rem;
padding-right: 0.375rem;
}
}
@ -200,7 +185,6 @@ exports[`renders <Summary loading /> without throwing 1`] = `
margin: 0;
line-height: 1.5rem;
font-size: 0.9375rem;
margin: 0;
}
.c2 {
@ -249,7 +233,7 @@ exports[`renders <Summary loading /> without throwing 1`] = `
.c0 {
box-sizing: border-box;
width: 100%;
max-width: 78.75rem;
max-width: 62.5rem;
padding-bottom: 1.125rem;
display: -webkit-box;
display: -webkit-flex;
@ -292,24 +276,10 @@ exports[`renders <Summary loading /> without throwing 1`] = `
}
}
@media only screen and (max-width:37.4375rem) {
@media only screen and (max-width:47.9375rem) {
.c0 {
padding-left: 0.75rem;
padding-right: 0.75rem;
}
}
@media only screen and (min-width:37.5rem) and (max-width:62.4375rem) {
.c0 {
padding-left: 1.875rem;
padding-right: 1.875rem;
}
}
@media only screen and (min-width:62.5rem) and (max-width:87.4375rem) {
.c0 {
padding-left: 4.375rem;
padding-right: 4.375rem;
padding-left: 0.375rem;
padding-right: 0.375rem;
}
}
@ -364,7 +334,6 @@ exports[`renders <Summary loadingError /> without throwing 1`] = `
margin: 0;
line-height: 1.5rem;
font-size: 0.9375rem;
margin: 0;
}
.c1 {
@ -375,14 +344,14 @@ exports[`renders <Summary loadingError /> without throwing 1`] = `
.c0 {
box-sizing: border-box;
width: 100%;
max-width: 78.75rem;
max-width: 62.5rem;
padding-bottom: 1.125rem;
}
.c6 {
margin: 0;
color: rgb(73,73,73);
font-weight: 400;
font-weight: 600;
line-height: 1.5rem;
font-size: 0.9375rem;
}
@ -434,24 +403,10 @@ exports[`renders <Summary loadingError /> without throwing 1`] = `
}
}
@media only screen and (max-width:37.4375rem) {
@media only screen and (max-width:47.9375rem) {
.c0 {
padding-left: 0.75rem;
padding-right: 0.75rem;
}
}
@media only screen and (min-width:37.5rem) and (max-width:62.4375rem) {
.c0 {
padding-left: 1.875rem;
padding-right: 1.875rem;
}
}
@media only screen and (min-width:62.5rem) and (max-width:87.4375rem) {
.c0 {
padding-left: 4.375rem;
padding-right: 4.375rem;
padding-left: 0.375rem;
padding-right: 0.375rem;
}
}
@ -509,7 +464,6 @@ exports[`renders <Summary mutationError /> without throwing 1`] = `
margin: 0;
line-height: 1.5rem;
font-size: 0.9375rem;
margin: 0;
}
.c2 {
@ -558,7 +512,7 @@ exports[`renders <Summary mutationError /> without throwing 1`] = `
.c0 {
box-sizing: border-box;
width: 100%;
max-width: 78.75rem;
max-width: 62.5rem;
padding-bottom: 1.125rem;
display: -webkit-box;
display: -webkit-flex;
@ -586,7 +540,7 @@ exports[`renders <Summary mutationError /> without throwing 1`] = `
.c11 {
margin: 0;
color: rgb(73,73,73);
font-weight: 400;
font-weight: 600;
line-height: 1.5rem;
font-size: 0.9375rem;
}
@ -638,24 +592,10 @@ exports[`renders <Summary mutationError /> without throwing 1`] = `
}
}
@media only screen and (max-width:37.4375rem) {
@media only screen and (max-width:47.9375rem) {
.c0 {
padding-left: 0.75rem;
padding-right: 0.75rem;
}
}
@media only screen and (min-width:37.5rem) and (max-width:62.4375rem) {
.c0 {
padding-left: 1.875rem;
padding-right: 1.875rem;
}
}
@media only screen and (min-width:62.5rem) and (max-width:87.4375rem) {
.c0 {
padding-left: 4.375rem;
padding-right: 4.375rem;
padding-left: 0.375rem;
padding-right: 0.375rem;
}
}
@ -744,7 +684,6 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
margin: 0;
line-height: 1.5rem;
font-size: 0.9375rem;
margin: 0;
}
.c2 {
@ -793,7 +732,7 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
.c0 {
box-sizing: border-box;
width: 100%;
max-width: 78.75rem;
max-width: 62.5rem;
padding-bottom: 1.125rem;
display: -webkit-box;
display: -webkit-flex;
@ -836,24 +775,10 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
}
}
@media only screen and (max-width:37.4375rem) {
@media only screen and (max-width:47.9375rem) {
.c0 {
padding-left: 0.75rem;
padding-right: 0.75rem;
}
}
@media only screen and (min-width:37.5rem) and (max-width:62.4375rem) {
.c0 {
padding-left: 1.875rem;
padding-right: 1.875rem;
}
}
@media only screen and (min-width:62.5rem) and (max-width:87.4375rem) {
.c0 {
padding-left: 4.375rem;
padding-right: 4.375rem;
padding-left: 0.375rem;
padding-right: 0.375rem;
}
}
@ -985,13 +910,19 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
.c37 {
color: rgb(59,70,204);
-webkit-text-fill-color: currentcolor;
cursor: pointer;
-webkit-text-decoration: underline;
text-decoration: underline;
color: rgb(255,255,255);
-webkit-text-fill-color: currentcolor;
}
.c37:hover {
-webkit-text-decoration: none;
text-decoration: none;
}
.c37 a:hover {
color: rgb(45,56,132);
}
.c31 {
min-width: 7.5rem;
}
@ -1062,7 +993,7 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
.c29:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c29:active,
@ -1149,7 +1080,7 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
.c35:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c35:active,
@ -1254,7 +1185,7 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
.c41:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c41:active,
@ -1374,7 +1305,7 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
.c42:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c42:active,
@ -1588,14 +1519,14 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
.c0 {
box-sizing: border-box;
width: 100%;
max-width: 78.75rem;
max-width: 62.5rem;
padding-bottom: 1.125rem;
}
.c11 {
margin: 0;
color: rgb(73,73,73);
font-weight: 400;
font-weight: normal;
line-height: 1.875rem;
font-size: 1.5rem;
}
@ -1603,7 +1534,43 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
.c44 {
height: 1px;
background-color: rgb(216,216,216);
height: 1;
}
.c12 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-flex-wrap: nowrap;
-ms-flex-wrap: nowrap;
flex-wrap: nowrap;
-webkit-box-pack: start;
-webkit-justify-content: flex-start;
-ms-flex-pack: start;
justify-content: flex-start;
-webkit-align-content: stretch;
-ms-flex-line-pack: stretch;
align-content: stretch;
}
.c26 {
-webkit-order: 0;
-ms-flex-order: 0;
order: 0;
-webkit-flex-basis: auto;
-ms-flex-preferred-size: auto;
flex-basis: auto;
-webkit-box-flex: 0;
-webkit-flex-grow: 0;
-ms-flex-positive: 0;
flex-grow: 0;
-webkit-flex-shrink: 1;
-ms-flex-negative: 1;
flex-shrink: 1;
display: block;
}
.c4 {
@ -1644,43 +1611,6 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
background-color: transparent;
}
.c12 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-flex-wrap: nowrap;
-ms-flex-wrap: nowrap;
flex-wrap: nowrap;
-webkit-box-pack: start;
-webkit-justify-content: flex-start;
-ms-flex-pack: start;
justify-content: flex-start;
-webkit-align-content: stretch;
-ms-flex-line-pack: stretch;
align-content: stretch;
}
.c26 {
-webkit-order: 0;
-ms-flex-order: 0;
order: 0;
-webkit-flex-basis: auto;
-ms-flex-preferred-size: auto;
flex-basis: auto;
-webkit-box-flex: 0;
-webkit-flex-grow: 0;
-ms-flex-positive: 0;
flex-grow: 0;
-webkit-flex-shrink: 1;
-ms-flex-negative: 1;
flex-shrink: 1;
display: block;
}
.c50 {
box-sizing: border-box;
width: 18.75rem;
@ -1699,7 +1629,6 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
-moz-appearance: none;
appearance: none;
outline: 0;
white-space: pre-wrap;
font-family: "Roboto Mono",monospace;
width: 100%;
min-width: 100%;
@ -1935,28 +1864,14 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
}
}
@media only screen and (max-width:37.4375rem) {
@media only screen and (max-width:47.9375rem) {
.c0 {
padding-left: 0.75rem;
padding-right: 0.75rem;
padding-left: 0.375rem;
padding-right: 0.375rem;
}
}
@media only screen and (min-width:37.5rem) and (max-width:62.4375rem) {
.c0 {
padding-left: 1.875rem;
padding-right: 1.875rem;
}
}
@media only screen and (min-width:62.5rem) and (max-width:87.4375rem) {
.c0 {
padding-left: 4.375rem;
padding-right: 4.375rem;
}
}
@media (max-width:37.4375rem) {
@media (max-width:47.9375rem) {
.c19 {
display: none;
}
@ -2005,11 +1920,6 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
>
<svg
height="17.07"
style={
Object {
"transform": "rotate(0deg)",
}
}
viewBox="0 0 17.07 17.07"
width="17.07"
xmlns="http://www.w3.org/2000/svg"
@ -2228,7 +2138,7 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
>
<span
name="react-responsive-mock"
query="only screen and (max-width: 37.4375rem)"
query="only screen and (max-width: 47.9375rem)"
>
<button
className="c41 c30 c31 c32"
@ -2263,7 +2173,7 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
</span>
<span
name="react-responsive-mock"
query="only screen and (min-width: 37.5rem)"
query="only screen and (min-width: 48rem)"
>
<button
className="c42 c30 c31 c32"
@ -2972,13 +2882,19 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
.c37 {
color: rgb(59,70,204);
-webkit-text-fill-color: currentcolor;
cursor: pointer;
-webkit-text-decoration: underline;
text-decoration: underline;
color: rgb(255,255,255);
-webkit-text-fill-color: currentcolor;
}
.c37:hover {
-webkit-text-decoration: none;
text-decoration: none;
}
.c37 a:hover {
color: rgb(45,56,132);
}
.c31 {
min-width: 7.5rem;
}
@ -3049,7 +2965,7 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
.c29:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c29:active,
@ -3136,7 +3052,7 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
.c35:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c35:active,
@ -3241,7 +3157,7 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
.c41:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c41:active,
@ -3361,7 +3277,7 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
.c42:hover {
background-color: rgb(72,83,217);
border-color: rgb(45,56,132);
border: solid 0.0625rem rgb(45,56,132);
}
.c42:active,
@ -3575,14 +3491,14 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
.c0 {
box-sizing: border-box;
width: 100%;
max-width: 78.75rem;
max-width: 62.5rem;
padding-bottom: 1.125rem;
}
.c11 {
margin: 0;
color: rgb(73,73,73);
font-weight: 400;
font-weight: normal;
line-height: 1.875rem;
font-size: 1.5rem;
}
@ -3590,7 +3506,43 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
.c44 {
height: 1px;
background-color: rgb(216,216,216);
height: 1;
}
.c12 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-flex-wrap: nowrap;
-ms-flex-wrap: nowrap;
flex-wrap: nowrap;
-webkit-box-pack: start;
-webkit-justify-content: flex-start;
-ms-flex-pack: start;
justify-content: flex-start;
-webkit-align-content: stretch;
-ms-flex-line-pack: stretch;
align-content: stretch;
}
.c26 {
-webkit-order: 0;
-ms-flex-order: 0;
order: 0;
-webkit-flex-basis: auto;
-ms-flex-preferred-size: auto;
flex-basis: auto;
-webkit-box-flex: 0;
-webkit-flex-grow: 0;
-ms-flex-positive: 0;
flex-grow: 0;
-webkit-flex-shrink: 1;
-ms-flex-negative: 1;
flex-shrink: 1;
display: block;
}
.c4 {
@ -3631,43 +3583,6 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
background-color: transparent;
}
.c12 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-flex-wrap: nowrap;
-ms-flex-wrap: nowrap;
flex-wrap: nowrap;
-webkit-box-pack: start;
-webkit-justify-content: flex-start;
-ms-flex-pack: start;
justify-content: flex-start;
-webkit-align-content: stretch;
-ms-flex-line-pack: stretch;
align-content: stretch;
}
.c26 {
-webkit-order: 0;
-ms-flex-order: 0;
order: 0;
-webkit-flex-basis: auto;
-ms-flex-preferred-size: auto;
flex-basis: auto;
-webkit-box-flex: 0;
-webkit-flex-grow: 0;
-ms-flex-positive: 0;
flex-grow: 0;
-webkit-flex-shrink: 1;
-ms-flex-negative: 1;
flex-shrink: 1;
display: block;
}
.c50 {
box-sizing: border-box;
width: 18.75rem;
@ -3686,7 +3601,6 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
-moz-appearance: none;
appearance: none;
outline: 0;
white-space: pre-wrap;
font-family: "Roboto Mono",monospace;
width: 100%;
min-width: 100%;
@ -3922,28 +3836,14 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
}
}
@media only screen and (max-width:37.4375rem) {
@media only screen and (max-width:47.9375rem) {
.c0 {
padding-left: 0.75rem;
padding-right: 0.75rem;
padding-left: 0.375rem;
padding-right: 0.375rem;
}
}
@media only screen and (min-width:37.5rem) and (max-width:62.4375rem) {
.c0 {
padding-left: 1.875rem;
padding-right: 1.875rem;
}
}
@media only screen and (min-width:62.5rem) and (max-width:87.4375rem) {
.c0 {
padding-left: 4.375rem;
padding-right: 4.375rem;
}
}
@media (max-width:37.4375rem) {
@media (max-width:47.9375rem) {
.c19 {
display: none;
}
@ -3992,11 +3892,6 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
>
<svg
height="17.07"
style={
Object {
"transform": "rotate(0deg)",
}
}
viewBox="0 0 17.07 17.07"
width="17.07"
xmlns="http://www.w3.org/2000/svg"
@ -4215,7 +4110,7 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
>
<span
name="react-responsive-mock"
query="only screen and (max-width: 37.4375rem)"
query="only screen and (max-width: 47.9375rem)"
>
<button
className="c41 c30 c31 c32"
@ -4250,7 +4145,7 @@ exports[`renders <Summary starting stopping rebooting removing /> without throwi
</span>
<span
name="react-responsive-mock"
query="only screen and (min-width: 37.5rem)"
query="only screen and (min-width: 48rem)"
>
<button
className="c42 c30 c31 c32"

View File

@ -1,3 +1,4 @@
/* eslint-disable camelcase */
import React, { Fragment } from 'react';
import renderer from 'react-test-renderer';
import 'jest-styled-components';

View File

@ -1,3 +1,4 @@
/* eslint-disable camelcase */
import React, { Fragment } from 'react';
import { toMatchImageSnapshot } from 'jest-image-snapshot';
import screenshot from 'react-screenshot-renderer';

View File

@ -1,3 +1,4 @@
/* eslint-disable camelcase */
import React from 'react';
import renderer from 'react-test-renderer';
import 'jest-styled-components';

View File

@ -1,3 +1,4 @@
/* eslint-disable camelcase */
import React from 'react';
import { toMatchImageSnapshot } from 'jest-image-snapshot';
import screenshot from 'react-screenshot-renderer';

View File

@ -22,13 +22,12 @@ import {
MessageDescription
} from 'joyent-ui-toolkit';
import Description from '@components/instances/description';
import {
Cns,
CnsFooter as Footer,
CnsAddServiceForm as AddServiceForm
} from 'joyent-ui-resource-widgets';
import Description from '@components/instances/description';
import GetAccount from '@graphql/get-account.gql';
import DeleteTag from '@graphql/delete-tag.gql';
import UpdateTags from '@graphql/update-tags.gql';
@ -53,7 +52,7 @@ const CnsContainer = ({
handleAsyncValidate
}) => (
<ViewContainer main>
<Margin bottom="3">
<Margin bottom={3}>
<Description href="https://docs.joyent.com/private-cloud/install/cns">
Triton CNS is used to automatically update hostnames for your instances.
You can serve multiple instances (with multiple IP addresses) under the
@ -61,13 +60,13 @@ const CnsContainer = ({
</Description>
</Margin>
<Row>
<Col xs="12" sm="12" md="9">
<Col xs={12} sm={12} md={9}>
<Card>
<CardOutlet>
<Padding all="5">
<Padding all={5}>
{loading ? <StatusLoader /> : null}
{!loading && loadingError ? (
<Margin bottom="5">
<Margin bottom={5}>
<Message error>
<MessageTitle>Ooops!</MessageTitle>
<MessageDescription>
@ -77,7 +76,7 @@ const CnsContainer = ({
</Margin>
) : null}
{!loading && mutationError ? (
<Margin bottom="5">
<Margin bottom={5}>
<Message error>
<MessageTitle>Ooops!</MessageTitle>
<MessageDescription>{mutationError}</MessageDescription>
@ -109,7 +108,7 @@ const CnsContainer = ({
</CardOutlet>
</Card>
{!loading && !loadingError ? (
<Margin top="5">
<Margin top={5}>
<Footer
enabled={!disabled}
submitting={mutating}
@ -279,8 +278,14 @@ export default compose(
]);
const newValue = value.join(',');
const mutation = newValue.length
? updateTags({
const mutation = !newValue.length
? deleteTag({
variables: {
id: instance.id,
name: 'triton.cns.services'
}
})
: updateTags({
variables: {
id: instance.id,
tags: [
@ -290,12 +295,6 @@ export default compose(
}
]
}
})
: deleteTag({
variables: {
id: instance.id,
name: 'triton.cns.services'
}
});
const [err] = await intercept(mutation);

View File

@ -1,3 +1,4 @@
/* eslint-disable camelcase */
import React from 'react';
import intercept from 'apr-intercept';
import { connect } from 'react-redux';
@ -42,7 +43,7 @@ export const Firewall = ({
handleEnabledToggle
}) => (
<ViewContainer main>
<Margin bottom="3">
<Margin bottom={3}>
<Description href="https://docs.joyent.com/private-cloud/install/cns">
Cloud Firewall rules control traffic across instances. Enabling the
firewall adds a default set of rules and rules defined by your chosen
@ -51,7 +52,7 @@ export const Firewall = ({
</Margin>
{loading ? <StatusLoader /> : null}
{!loading && loadingError ? (
<Margin bottom="5">
<Margin bottom={5}>
<Message error>
<MessageTitle>Ooops!</MessageTitle>
<MessageDescription>
@ -61,7 +62,7 @@ export const Firewall = ({
</Margin>
) : null}
{!loading && mutationError ? (
<Margin bottom="5">
<Margin bottom={5}>
<Message error>
<MessageTitle>Ooops!</MessageTitle>
<MessageDescription>{mutationError}</MessageDescription>
@ -79,7 +80,7 @@ export const Firewall = ({
>
{props =>
loading ? null : (
<Margin right="5">
<Margin right={5}>
<ToggleFirewallForm {...props} submitOnChange />
</Margin>
)
@ -100,19 +101,19 @@ export const Firewall = ({
</FlexItem>
</Flex>
{!loading && !defaultRules.length && !tagRules.length ? (
<Margin top="5">
<Margin top={5}>
<Empty borderTop>
Sorry, but we werent able to find any firewall rules.
</Empty>
</Margin>
) : null}
{!loading && enabled && defaultRules.length ? (
<Margin top="5">
<Margin top={5}>
<DefaultRules rules={defaultRules} />
</Margin>
) : null}
{!loading && enabled && tagRules.length ? (
<Margin top="5">
<Margin top={5}>
<TagRules rules={tagRules} />
</Margin>
) : null}

View File

@ -83,7 +83,7 @@ export const List = ({
const _error =
error && !_instances.length && !_loading && !filter.length ? (
<Margin bottom="5">
<Margin bottom={5}>
<Message error>
<MessageTitle>Ooops!</MessageTitle>
<MessageDescription>
@ -94,7 +94,7 @@ export const List = ({
) : null;
const _mutationError = mutationError && (
<Margin bottom="5">
<Margin bottom={5}>
<Message error>
<MessageTitle>Ooops!</MessageTitle>
<MessageDescription>{mutationError}</MessageDescription>
@ -107,7 +107,7 @@ export const List = ({
const handleReboot = selected => handleAction({ name: 'reboot', selected });
const handleRemove = selected => handleAction({ name: 'remove', selected });
const _table = _loading ? null : (
const _table = !_loading ? (
<ReduxForm form={TABLE_FORM_NAME}>
{props => (
<InstanceList
@ -123,7 +123,7 @@ export const List = ({
total={total}
>
{fetching ? <InstanceListFetchingItem /> : null}
{(fetching ? [] : _instances).map(instance => (
{(!fetching ? _instances : []).map(instance => (
<InstanceListItem
key={instance.id}
{...instance}
@ -137,7 +137,7 @@ export const List = ({
</InstanceList>
)}
</ReduxForm>
);
) : null;
const _empty =
!_loading && !_instances.length ? (
@ -164,10 +164,10 @@ export const List = ({
return (
<ViewContainer main>
<Margin top="5">
<Margin top={5}>
<H3>Instances</H3>
</Margin>
<Margin top="3" bottom="5">
<Margin top={3} bottom={5}>
<ReduxForm form={MENU_FORM_NAME}>
{props => (
<ToolbarForm
@ -180,7 +180,7 @@ export const List = ({
)}
</ReduxForm>
</Margin>
{_mutationError ? null : _error}
{!_mutationError ? _error : null}
{_mutationError}
{_loading}
{_table}
@ -260,10 +260,7 @@ export default compose(
const sortOrder = get(values, 'instance-list-sort-order', 'asc');
// if user is searching something, get items that match that query
const filtered =
filter && index.list.length
? index.list.filter(i => i.name.includes(filter))
: instances;
const filtered = filter ? index.search(filter) : instances;
// from filtered instances, sort asc
// set's mutating flag

View File

@ -1,4 +1,4 @@
import React, { Fragment } from 'react';
import React from 'react';
import paramCase from 'param-case';
import { Margin } from 'styled-components-spacing';
import { set } from 'react-redux-values';
@ -53,10 +53,10 @@ export const Metadata = ({
handleUpdate,
handleRemove
}) => {
const _loading = loading && !metadata.length ? <StatusLoader /> : null;
const _loading = !(loading && !metadata.length) ? null : <StatusLoader />;
const _add = addOpen ? (
<Margin bottom="3">
<Margin bottom={3}>
<ReduxForm
form={ADD_FORM_NAME}
onSubmit={handleCreate}
@ -73,25 +73,25 @@ export const Metadata = ({
</Margin>
) : null;
const _line = _loading ? null : (
<Fragment>
<Divider key="line" height={remcalc(1)} />
<Divider key="after-line-space" height={remcalc(24)} transparent />
</Fragment>
);
const _line = !_loading
? [
<Divider key="line" height={remcalc(1)} />,
<Divider key="after-line-space" height={remcalc(24)} transparent />
]
: null;
const _count = _loading ? null : (
<Margin bottom="3" top="5">
const _count = !_loading ? (
<Margin bottom={3} top={5}>
<H3>
{metadata.length} key-value pair{metadata.length === 1 ? '' : 's'}
</H3>
</Margin>
);
) : null;
const _metadata =
!_loading &&
metadata.map(({ form, initialValues, expanded, removing }) => (
<Margin bottom="2">
<Margin bottom={2}>
<ReduxForm
form={form}
key={form}
@ -119,7 +119,7 @@ export const Metadata = ({
const _error =
error && !_metadata.length && !_loading ? (
<Margin bottom="5">
<Margin bottom={5}>
<Message error>
<MessageTitle>Ooops!</MessageTitle>
<MessageDescription>
@ -131,7 +131,7 @@ export const Metadata = ({
return (
<ViewContainer main>
<Margin bottom="5">
<Margin bottom={5}>
<ReduxForm form={MENU_FORM_NAME}>
{props => (
<ToolbarForm

View File

@ -30,7 +30,7 @@ export const Networks = ({
setInfoExpanded
}) => (
<ViewContainer main>
<Margin bottom="3">
<Margin bottom={3}>
<Description href="https://docs.joyent.com/public-cloud/network/sdn">
Use predefined or customized fabric networks which can be public-facing
or private. All fabrics are isolated from other customers giving you
@ -42,7 +42,7 @@ export const Networks = ({
</Margin>
{loading ? <StatusLoader /> : null}
{!loading && error && !networks.length ? (
<Margin bottom="5">
<Margin bottom={5}>
<Message error>
<MessageTitle>Ooops!</MessageTitle>
<MessageDescription>
@ -81,6 +81,7 @@ export default compose(
}
}),
props: ({ data }) => {
console.log(data);
const { loading, error, variables } = data;
const { id } = variables;

View File

@ -70,7 +70,7 @@ const Snapshots = ({
const _error = error &&
!_loading &&
!_values.length && (
<Margin bottom="5">
<Margin bottom={5}>
<Message error>
<MessageTitle>Ooops!</MessageTitle>
<MessageDescription>
@ -82,7 +82,7 @@ const Snapshots = ({
const _createSnapshot =
!loading && createSnapshotOpen ? (
<Margin bottom="5">
<Margin bottom={5}>
<ReduxForm
form={CREATE_FORM_NAME}
shouldAsyncValidate={shouldAsyncValidate}
@ -111,7 +111,7 @@ const Snapshots = ({
) : null;
const _mutationError = mutationError ? (
<Margin bottom="5">
<Margin bottom={5}>
<Message error>
<MessageTitle>Ooops!</MessageTitle>
<MessageDescription>{mutationError}</MessageDescription>
@ -119,7 +119,7 @@ const Snapshots = ({
</Margin>
) : null;
const _items = _loading ? null : (
const _items = !_loading ? (
<ReduxForm form={TABLE_FORM_NAME}>
{props => (
<SnapshotsList
@ -135,11 +135,11 @@ const Snapshots = ({
/>
)}
</ReduxForm>
);
) : null;
return (
<ViewContainer main>
<Margin bottom="5">
<Margin bottom={5}>
<ReduxForm form={MENU_FORM_NAME}>
{props => (
<ToolbarForm

View File

@ -79,7 +79,7 @@ export const Summary = ({
const _error = loadingError &&
!_loading &&
!instance && (
<Margin bottom="5">
<Margin bottom={5}>
<Message error>
<MessageTitle>Ooops!</MessageTitle>
<MessageDescription>

View File

@ -18,11 +18,10 @@ import {
Divider,
H3,
TagList,
TagItem
TagItem,
KeyValue
} from 'joyent-ui-toolkit';
import { KeyValue } from 'joyent-ui-resource-widgets';
import ToolbarForm from '@components/instances/toolbar';
import GetTags from '@graphql/list-tags.gql';
import UpdateTags from '@graphql/update-tags.gql';
@ -38,13 +37,11 @@ const EDIT_FORM_KEY = field => `instance-tags-${paramCase(field)}`;
const TagsAddForm = props => (
<KeyValue {...props} method="add" input="input" type="tag" expanded />
);
const TagsEditForm = props => (
<KeyValue {...props} method="edit" input="input" type="tag" expanded />
);
const Tag = ({ name, value, onRemoveClick, onClick }) => (
<Margin right="1" bottom="1" key={`${name}-${value}`}>
<Margin right={1} bottom={1} key={`${name}-${value}`}>
<TagItem onClick={onClick} onRemoveClick={onRemoveClick}>
{name ? `${name}: ${value}` : value}
</TagItem>
@ -66,7 +63,7 @@ export const Tags = ({
shouldAsyncValidate,
handleAsyncValidate
}) => {
const _loading = loading && !tags.length ? <StatusLoader /> : null;
const _loading = !(loading && !tags.length) ? null : <StatusLoader />;
const _add = addOpen ? (
<ReduxForm
@ -88,15 +85,15 @@ export const Tags = ({
]
: null;
const _count = _loading ? null : (
<Margin bottom="3" top="5">
const _count = !_loading ? (
<Margin bottom={3} top={5}>
<H3>
{tags.length} tag{tags.length === 1 ? '' : 's'}
</H3>
</Margin>
);
) : null;
const _tags = _loading ? null : (
const _tags = !_loading ? (
<TagList>
{tags.map(({ id, name, value }) => (
<Tag
@ -108,7 +105,7 @@ export const Tags = ({
/>
))}
</TagList>
);
) : null;
const _edit = editing ? (
<ReduxForm
@ -134,7 +131,7 @@ export const Tags = ({
return (
<ViewContainer main>
<Margin bottom="3">
<Margin bottom={3}>
<ReduxForm form={MENU_FORM_NAME}>
{props => (
<ToolbarForm

View File

@ -18,14 +18,14 @@ import GetMetadata from '@graphql/list-metadata.gql';
export const UserScript = ({ metadata, loading = false, error = null }) => (
<ViewContainer main>
<Margin bottom="3">
<Margin bottom={3}>
<Description href="https://docs.joyent.com/private-cloud/instances/using-mdata#UsingtheMetadataAPI-ListofMetadataKeys">
User script can be used to inject a custom boot script.
</Description>
</Margin>
{loading ? <StatusLoader /> : null}
{!loading && error ? (
<Margin bottom="5">
<Margin bottom={5}>
<Message error>
<MessageTitle>Ooops!</MessageTitle>
<MessageDescription>

View File

@ -64,13 +64,13 @@ exports[`renders <Breadcrumb /> without throwing 1`] = `
.c4 {
box-sizing: border-box;
width: 100%;
max-width: 78.75rem;
max-width: 62.5rem;
}
.c10 {
margin: 0;
color: rgb(73,73,73);
font-weight: 400;
font-weight: 600;
line-height: 1.5rem;
font-size: 0.9375rem;
}
@ -138,24 +138,10 @@ exports[`renders <Breadcrumb /> without throwing 1`] = `
}
}
@media only screen and (max-width:37.4375rem) {
@media only screen and (max-width:47.9375rem) {
.c4 {
padding-left: 0.75rem;
padding-right: 0.75rem;
}
}
@media only screen and (min-width:37.5rem) and (max-width:62.4375rem) {
.c4 {
padding-left: 1.875rem;
padding-right: 1.875rem;
}
}
@media only screen and (min-width:62.5rem) and (max-width:87.4375rem) {
.c4 {
padding-left: 4.375rem;
padding-right: 4.375rem;
padding-left: 0.375rem;
padding-right: 0.375rem;
}
}
@ -316,13 +302,13 @@ exports[`renders <Breadcrumb match /> without throwing 1`] = `
.c4 {
box-sizing: border-box;
width: 100%;
max-width: 78.75rem;
max-width: 62.5rem;
}
.c10 {
margin: 0;
color: rgb(73,73,73);
font-weight: 400;
font-weight: 600;
line-height: 1.5rem;
font-size: 0.9375rem;
}
@ -390,24 +376,10 @@ exports[`renders <Breadcrumb match /> without throwing 1`] = `
}
}
@media only screen and (max-width:37.4375rem) {
@media only screen and (max-width:47.9375rem) {
.c4 {
padding-left: 0.75rem;
padding-right: 0.75rem;
}
}
@media only screen and (min-width:37.5rem) and (max-width:62.4375rem) {
.c4 {
padding-left: 1.875rem;
padding-right: 1.875rem;
}
}
@media only screen and (min-width:62.5rem) and (max-width:87.4375rem) {
.c4 {
padding-left: 4.375rem;
padding-right: 4.375rem;
padding-left: 0.375rem;
padding-right: 0.375rem;
}
}

View File

@ -30,7 +30,7 @@ export default ({ match }) => {
.filter(Boolean)
.map(({ name, pathname }) => (
<BreadcrumbItem key={name} to={pathname} component={Link}>
<Margin horizontal="1" vertical="3">
<Margin horizontal={1} vertical={3}>
{name}
</Margin>
</BreadcrumbItem>

View File

@ -20,7 +20,7 @@ module.exports = ({
</head>
<body {...bodyAttrs}>
<div id="header" />
{children ? null : <div id="root" />}
{!children ? <div id="root" /> : null}
{children}
<script src="/navigation/static/main.js" />
</body>

View File

@ -1,14 +1,15 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { HelmetProvider } from 'react-helmet-async';
import ThemeProvider from 'joyent-theme-resizer';
import { ThemeProvider } from 'styled-components';
import { Provider as ReduxProvider } from 'react-redux';
import { ApolloProvider } from 'react-apollo';
import { BrowserRouter } from 'react-router-dom';
import isFunction from 'lodash.isfunction';
import isFinite from 'lodash.isfinite';
import theme from '@state/theme';
import { theme } from 'joyent-ui-toolkit';
import createStore from '@state/redux-store';
import createClient from '@state/apollo-client';
import App from './app';

View File

@ -1,4 +1,4 @@
import React, { Fragment } from 'react';
import React from 'react';
import { Redirect, Route, Switch } from 'react-router-dom';
import get from 'lodash.get';
@ -27,8 +27,6 @@ import {
UserScript as InstanceUserScript
} from '@containers/instances';
const { REACT_APP_DEV = false } = process.env;
export default () => (
<PageContainer>
{/* Breadcrumb */}
@ -104,41 +102,6 @@ export default () => (
<Route path="/" exact component={() => <Redirect to="/instances" />} />
{REACT_APP_DEV ? (
<Fragment>
<Route
path="/images"
component={({ location }) =>
window.location.replace(
`${window.location.protocol}//${window.location.hostname}:3070${
location.pathname
}${location.search}`
)
}
/>
<Route
path="/templates"
component={({ location }) =>
window.location.replace(
`${window.location.protocol}//${window.location.hostname}:3071${
location.pathname
}${location.search}`
)
}
/>
<Route
path="/service-groups"
component={({ location }) =>
window.location.replace(
`${window.location.protocol}//${window.location.hostname}:3072${
location.pathname
}${location.search}`
)
}
/>
</Fragment>
) : null}
<noscript>
<ViewContainer main>
<Message warning>

View File

@ -1,5 +1,6 @@
import React from 'react';
import { Margin } from 'styled-components-spacing';
import remcalc from 'remcalc';
import {
RootContainer,
@ -7,14 +8,16 @@ import {
ViewContainer,
Message,
MessageDescription,
MessageTitle
MessageTitle,
Divider
} from 'joyent-ui-toolkit';
import Breadcrumb from '@containers/navigation/breadcrumb';
export const Route = () => (
<ViewContainer main>
<Margin bottom="4">
<Divider height={remcalc(24)} transparent />
<Margin bottom={5}>
<Message error>
<MessageTitle>Ooops!</MessageTitle>
<MessageDescription>

View File

@ -2,7 +2,6 @@ import { ApolloClient } from 'apollo-client';
import { HttpLink } from 'apollo-link-http';
import { InMemoryCache } from 'apollo-cache-inmemory';
import fetch from 'cross-fetch';
import get from 'lodash.get';
import global from './global';
@ -15,9 +14,7 @@ const {
const PORT = REACT_APP_GQL_PORT ? `:${REACT_APP_GQL_PORT}` : '';
const URI = `${REACT_APP_GQL_PROTOCOL}://${REACT_APP_GQL_HOSTNAME}${PORT}/instances/graphql`;
export default (opts = {}, request = {}) => {
const host = get(request, 'raw.req.headers.host', '');
export default (opts = {}) => {
let cache = new InMemoryCache();
if (global.__APOLLO_STATE__) {
@ -27,7 +24,7 @@ export default (opts = {}, request = {}) => {
return new ApolloClient({
cache,
link: new HttpLink({
uri: host ? `${REACT_APP_GQL_PROTOCOL}//${host}/instances/graphql` : URI,
uri: URI,
credentials: 'same-origin',
fetch,
headers: {

View File

@ -1,12 +1,9 @@
import { canUseDOM } from 'exenv';
import queryString from 'query-string';
const { NODE_ENV = 'development' } = process.env;
export const Global = () => {
if (!canUseDOM) {
return {
protocol: NODE_ENV === 'development' ? 'http:' : 'https:',
cookie: ''
};
}

View File

@ -2,7 +2,7 @@ import intercept from 'apr-intercept';
import keys from 'lodash.keys';
import reduce from 'apr-reduce';
import assign from 'lodash.assign';
import * as yup from 'yup';
import yup from 'yup';
/*****************************************************************************/
@ -16,7 +16,7 @@ const validateSchema = async (schema, value) => {
keys(schema),
async (errors, name) => {
const msg = await validateField(schema[name], value[name]);
return msg ? assign(errors, { [name]: msg }) : errors;
return !msg ? errors : assign(errors, { [name]: msg });
},
{}
);
@ -109,6 +109,6 @@ export const addMetadata = metadata =>
validateSchema(Schemas.metadata, metadata);
export const instanceName = ({ name }) =>
name ? validateSchema(Schemas.instanceName, { name }) : null;
!name ? null : validateSchema(Schemas.instanceName, { name });
export const editMetadata = addMetadata;

View File

@ -0,0 +1,2 @@
build
dist

View File

@ -0,0 +1,13 @@
{
"extends": "joyent-portal",
"rules": {
"no-console": 0,
"new-cap": 0,
"camelcase": 1,
// temp
"no-undef": 1,
"no-debugger": 1,
"no-negated-condition": 0,
"jsx-a11y/href-no-hash": 0
}
}

View File

@ -15,7 +15,6 @@ exports.register = async server => {
if (NODE_ENV === 'production') {
throw err;
} else {
// eslint-disable-next-line no-console
console.error(err);
}
}

View File

@ -10,21 +10,23 @@
"build:lib": "echo 0",
"build:bundle": "NAMESPACE=navigation NODE_ENV=production redrun build",
"prepublish": "NAMESPACE=navigation NODE_ENV=production redrun build",
"lint": "redrun lint:ci -- --fix",
"lint:ci": "NODE_ENV=test eslint . --ext .js --ext .md",
"test": "echo 0",
"test:ci": "redrun test",
"build": "PREACT=1 joyent-react-scripts build"
},
"dependencies": {
"apollo-cache-inmemory": "^1.2.2",
"apollo-client": "^2.3.2",
"apollo-link": "^1.2.2",
"apollo-link-http": "^1.5.4",
"apollo-cache-inmemory": "^1.1.12",
"apollo-client": "^2.2.8",
"apollo-link": "^1.2.1",
"apollo-link-http": "^1.5.3",
"apollo-link-state": "^0.4.1",
"apr-intercept": "^3.0.3",
"boom": "^7.2.0",
"emotion": "^9.1.3",
"emotion-theming": "^9.1.2",
"graphql-tag": "^2.9.2",
"emotion": "^9.1.1",
"emotion-theming": "^9.0.0",
"graphql-tag": "^2.8.0",
"inert": "^5.1.0",
"joyent-icons": "^5.1.0",
"joyent-ui-toolkit": "^6.0.0",
@ -34,20 +36,20 @@
"mz": "^2.7.0",
"outy": "^0.1.2",
"pascal-case": "^2.0.1",
"preact": "^8.2.9",
"preact": "^8.2.7",
"preact-compat": "^3.18.0",
"preact-emotion": "^9.1.3",
"preact-emotion": "^9.1.1",
"preact-emotion-flexboxgrid": "^2.0.1",
"react-apollo": "^2.1.4",
"react-apollo": "^2.1.2",
"remcalc": "^1.0.10",
"stickybits": "^3.3.2"
"stickybits": "^3.2.0"
},
"devDependencies": {
"babel-eslint": "^8.2.3",
"babel-eslint": "^8.2.2",
"babel-preset-joyent-portal": "^7.0.1",
"eslint": "^4.19.1",
"eslint-config-joyent-portal": "^3.3.1",
"joyent-react-scripts": "^8.2.1",
"redrun": "^6.0.4"
"joyent-react-scripts": "^8.2.0",
"redrun": "^6.0.2"
}
}

View File

@ -4,7 +4,7 @@ import emotion from 'preact-emotion';
import remcalc from 'remcalc';
import outy from 'outy';
import { ValueBreakpoints as breakpoints } from 'joyent-ui-toolkit';
import { breakpoints } from 'joyent-ui-toolkit/dist/es/breakpoints/screens';
export const Item = emotion('div')`
order: 0;

View File

@ -24,7 +24,7 @@ const GetAccountServices = gql`
`;
const Account = ({ expanded, services = [] }) => {
return expanded ? (
return !expanded ? null : (
<Popover>
<Ul>
{services.map(({ name, url }) => (
@ -34,7 +34,7 @@ const Account = ({ expanded, services = [] }) => {
))}
</Ul>
</Popover>
) : null;
);
};
export default compose(

View File

@ -69,7 +69,7 @@ const Datacenters = ({ expanded, regions = [] }) =>
<RegionContainer>
<Row>
{regions[region].map(({ name, datacenters }) => (
<Col key={name} xs="12" md="6" lg="3">
<Col key={name} xs={12} md={6} lg={3}>
<DatacenterPlace>{name}</DatacenterPlace>
{datacenters.map(({ name, url }) => (
<Datacenter key={name}>

View File

@ -64,7 +64,7 @@ const Services = ({ expanded = false, categories = [], loading }) =>
<Row>
{!loading &&
categories.map(({ name, services }) => (
<CategoryWrapper xs="12" sm="6" md="4">
<CategoryWrapper xs={12} sm={6} md={4}>
<ServiceCategory>{name}</ServiceCategory>
{services.map(({ name, description, url, tags }) => (
<Service>

View File

@ -1,12 +0,0 @@
{
"ignore": ["_document.js", "_aliases.js"],
"presets": [
[
"joyent-portal",
{
"aliases": true,
"autoAliases": true
}
]
]
}

View File

@ -1,25 +0,0 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
/node_modules
# testing
/coverage
# production
/build
# misc
.DS_Store
.env
npm-debug.log*
yarn-debug.log*
yarn-error.log*
## Image Snapshots Diff
**/__diff_output__
lib/app

View File

@ -1,8 +0,0 @@
{
"setup": {
"compile": "npm run build",
"start": "serve -s build --port 3069 --single",
"href": "http://0.0.0.0:3069"
},
"extends": "lighthouse:default"
}

View File

@ -1,27 +0,0 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
/node_modules
# testing
/coverage
# production
/build
# misc
.DS_Store
.env
npm-debug.log*
yarn-debug.log*
yarn-error.log*
## Image Snapshots Diff
**/__diff_output__
!lib/app
!dist
!build

View File

@ -1,4 +0,0 @@
{
"test": ["./src/**/*.js"],
"extends": ["stylelint-config-joyent-portal"]
}

View File

@ -1,13 +0,0 @@
{
"libs": ["ecmascript", "browser"],
"plugins": {
"doc_comment": true,
"local-scope": true,
"jsx": true,
"node": true,
"webpack": {
"configPath":
"../../node_modules/joyent-react-scripts/src/webpack.config.dev.js"
}
}
}

Some files were not shown because too many files have changed in this diff Show More