feat(my-joy-beta): create packages in create instance page

This commit is contained in:
Sara Vieira 2018-01-11 12:02:38 +00:00
parent 6768bdd948
commit 5bb6b2ab5f
22 changed files with 3596 additions and 291 deletions

36
packages/icons/src/cpu.js Normal file
View File

@ -0,0 +1,36 @@
import React from 'react';
import Colors from './colors';
import Rotate from './rotate';
import calcFill from './fill';
export default ({
fill = null,
light = false,
disabled = false,
direction = 'down',
style = {},
...rest
}) => (
<Colors white text grey>
{colors => (
<Rotate direction={direction}>
{({ style: rotateStyle }) => (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
style={{ ...style, ...rotateStyle }}
{...rest}
>
<path
fill={calcFill({ fill, disabled, light, colors })}
d="M20,18a2,2,0,0,1-2,2H6a2,2,0,0,1-2-2V6A2,2,0,0,1,6,4H18a2,2,0,0,1,2,2Zm3-5a1,1,0,0,0,0-2H22V10h1a1,1,0,0,0,0-2H22V6a4,4,0,0,0-4-4H16V1a1,1,0,0,0-2,0V2H13V1a1,1,0,0,0-2,0V2H10V1A1,1,0,0,0,8,1V2H6A4,4,0,0,0,2,6V8H1a1,1,0,0,0,0,2H2v1H1a1,1,0,0,0,0,2H2v1H1a1,1,0,0,0,0,2H2v2a4,4,0,0,0,4,4H8v1a1,1,0,0,0,2,0V22h1v1a1,1,0,0,0,2,0V22h1v1a1,1,0,0,0,2,0V22h2a4,4,0,0,0,4-4V16h1a1,1,0,0,0,0-2H22V13ZM7,7V17H17V7Zm9,9H8V8h8Z"
/>
</svg>
)}
</Rotate>
)}
</Colors>
);

View File

@ -0,0 +1,36 @@
import React from 'react';
import Colors from './colors';
import Rotate from './rotate';
import calcFill from './fill';
export default ({
fill = null,
light = false,
disabled = false,
direction = 'down',
style = {},
...rest
}) => (
<Colors white text grey>
{colors => (
<Rotate direction={direction}>
{({ style: rotateStyle }) => (
<svg
width="20"
height="20"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
style={{ ...style, ...rotateStyle }}
{...rest}
>
<path
fill={calcFill({ fill, disabled, light, colors })}
d="M18,17a1,1,0,0,1-1,1H3a1,1,0,0,1-1-1V3A1,1,0,0,1,3,2H17a1,1,0,0,1,1,1ZM17,0H3A3,3,0,0,0,0,3V17a3,3,0,0,0,3,3H17a3,3,0,0,0,3-3V3A3,3,0,0,0,17,0ZM10,15a5,5,0,1,1,5-5A5,5,0,0,1,10,15ZM10,4a6,6,0,1,0,6,6A6,6,0,0,0,10,4ZM9,10a1,1,0,1,0,1-1A1,1,0,0,0,9,10Z"
/>
</svg>
)}
</Rotate>
)}
</Colors>
);

View File

@ -36,3 +36,7 @@ export { default as User } from './user';
export { default as Randomize } from './randomize';
export { default as Name } from './name';
export { default as Fabric } from './fabric';
export { default as Cpu } from './cpu';
export { default as Memory } from './memory';
export { default as Storage } from './storage';
export { default as General } from './general';

View File

@ -0,0 +1,36 @@
import React from 'react';
import Colors from './colors';
import Rotate from './rotate';
import calcFill from './fill';
export default ({
fill = null,
light = false,
disabled = false,
direction = 'down',
style = {},
...rest
}) => (
<Colors white text grey>
{colors => (
<Rotate direction={direction}>
{({ style: rotateStyle }) => (
<svg
width="21.68"
height="21.68"
viewBox="0 0 21.68 21.68"
xmlns="http://www.w3.org/2000/svg"
style={{ ...style, ...rotateStyle }}
{...rest}
>
<path
fill={calcFill({ fill, disabled, light, colors })}
d="M6.24,11.9,8.36,14l1.42-1.41L7.66,10.49ZM4.12,14l2.12,2.12,1.42-1.41L5.54,12.6ZM8.36,9.78l2.13,2.12,1.41-1.41L9.78,8.36Zm2.13-2.12,2.12,2.12L14,8.36,11.9,6.24Zm2.12-2.13,2.12,2.13,1.41-1.42L14,4.12Zm8.48,0L16.14.59a2,2,0,0,0-2.83,0L.59,13.31a2,2,0,0,0,0,2.83l5,4.95a2,2,0,0,0,2.82,0l1.42-1.41.71.7A1,1,0,0,0,11.9,19l-.71-.71.71-.7.71.7A1,1,0,0,0,14,16.85l-.71-.71.71-.71.71.71a1,1,0,0,0,1.41-1.41L15.43,14l.71-.71.71.71a1,1,0,0,0,1.41-1.41l-.7-.71.7-.71.71.71a1,1,0,0,0,1.41-1.41l-.7-.71,1.41-1.42A2,2,0,0,0,21.09,5.54ZM7,19.68,2,14.73,14.73,2l4.95,5Z"
/>
</svg>
)}
</Rotate>
)}
</Colors>
);

View File

@ -0,0 +1,36 @@
import React from 'react';
import Colors from './colors';
import Rotate from './rotate';
import calcFill from './fill';
export default ({
fill = null,
light = false,
disabled = false,
direction = 'down',
style = {},
...rest
}) => (
<Colors white text grey>
{colors => (
<Rotate direction={direction}>
{({ style: rotateStyle }) => (
<svg
width="22"
height="22"
viewBox="0 0 22 22"
xmlns="http://www.w3.org/2000/svg"
style={{ ...style, ...rotateStyle }}
{...rest}
>
<path
fill={calcFill({ fill, disabled, light, colors })}
d="M17,16a1,1,0,1,1,1,1A1,1,0,0,1,17,16ZM7,17H8V15H7ZM5,17H6V15H5ZM3,17H4V15H3Zm14-7a1,1,0,1,1,1,1A1,1,0,0,1,17,10ZM7,11H8V9H7ZM5,11H6V9H5ZM3,11H4V9H3ZM19,4a1,1,0,1,1-1-1A1,1,0,0,1,19,4ZM7,5H8V3H7ZM5,5H6V3H5ZM3,5H4V3H3ZM22,2a2,2,0,0,0-2-2H2A2,2,0,0,0,0,2V6A1.91,1.91,0,0,0,.28,7,1.88,1.88,0,0,0,0,8v4a1.91,1.91,0,0,0,.28,1A1.88,1.88,0,0,0,0,14v4a2,2,0,0,0,2,2H20a2,2,0,0,0,2-2V14a1.88,1.88,0,0,0-.28-1A1.91,1.91,0,0,0,22,12V8a1.88,1.88,0,0,0-.28-1A1.91,1.91,0,0,0,22,6ZM20,18H2V14H20Zm0-6H2V8H20Zm0-6H2V2H20Z"
/>
</svg>
)}
</Rotate>
)}
</Colors>
);

View File

@ -23,6 +23,7 @@
"apollo": "^0.2.2",
"apr-intercept": "^1.0.4",
"clipboard-copy": "^1.2.0",
"constant-case": "^2.0.0",
"date-fns": "^1.29.0",
"declarative-redux-form": "^2.0.8",
"joyent-manifest-editor": "^1.4.0",

View File

@ -354,7 +354,8 @@ exports[`renders <Images expanded /> without throwing 1`] = `
<p
className="c1"
>
Hardware virtual machines are generally used for non-containerized applications. Infrastructure containers are generally for running any Linux image on secure, bare metal containers.
Hardware virtual machines are generally used for non-containerized applications. Infrastructure containers are generally for running any Linux image on secure, bare metal containers.
<a
href="https://docs.joyent.com/private-cloud/images"
rel="noopener noreferrer"

View File

@ -1,9 +1,174 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders <Name /> without throwing 1`] = `
.c0 {
margin-top: 1rem;
margin-bottom: 0.5rem;
}
.c4 {
font-family: sans-serif;
font-size: 100%;
line-height: 1.15;
margin: 0;
overflow: visible;
text-transform: none;
-webkit-appearance: button;
-moz-appearance: button;
appearance: button;
min-width: 7.5rem;
}
.c4::-moz-focus-inner,
.c4[type='button']::-moz-focus-inner,
.c4[type='reset']::-moz-focus-inner,
.c4[type='submit']::-moz-focus-inner {
border-style: none;
padding: 0;
}
.c4:-moz-focusring,
.c4[type='button']:-moz-focusring,
.c4[type='reset']:-moz-focusring,
.c4[type='submit']:-moz-focusring {
outline: 0.0625rem dotted ButtonText;
}
.c4 + button {
margin-left: 0.375rem;
}
.c3 {
display: inline-block;
}
.c2 {
box-sizing: border-box;
display: inline-block;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
min-height: 3rem;
height: 3rem;
min-width: 7.5rem;
margin-bottom: 0.5rem;
margin-top: 0.5rem;
padding: 0.9375rem 1.125rem;
position: relative;
font-size: 0.9375rem;
text-align: center;
font-style: normal;
font-stretch: normal;
line-height: normal;
-webkit-letter-spacing: normal;
-moz-letter-spacing: normal;
-ms-letter-spacing: normal;
letter-spacing: normal;
text-decoration: none;
white-space: nowrap;
vertical-align: middle;
touch-action: manipulation;
cursor: pointer;
color: rgb(255,255,255);
-webkit-text-fill-color: currentcolor;
background-image: none;
background-color: rgb(59,70,204);
border-radius: 0.25rem;
border: solid 0.0625rem rgb(45,56,132);
color: rgb(70,70,70);
-webkit-text-fill-color: currentcolor;
background-color: rgb(255,255,255);
border-color: rgb(216,216,216);
}
.c2:focus {
outline: 0;
text-decoration: none;
background-color: rgb(59,70,204);
border-color: rgb(45,56,132);
}
.c2:hover {
background-color: rgb(72,83,217);
border: solid 0.0625rem rgb(45,56,132);
}
.c2:active,
.c2:active:hover,
.c2:active:focus {
background-image: none;
outline: 0;
background-color: rgb(45,56,132);
border-color: rgb(45,56,132);
}
.c2[disabled] {
cursor: not-allowed;
pointer-events: none;
}
.c2:focus {
background-color: rgb(255,255,255);
border-color: rgb(216,216,216);
}
.c2:hover {
background-color: rgb(247,247,247);
border-color: rgb(216,216,216);
}
.c2:active,
.c2:active:hover,
.c2:active:focus {
background-color: rgb(230,230,230);
border-color: rgb(216,216,216);
}
.c1 {
color: rgba(73,73,73,1);
font-weight: normal;
line-height: 1.625rem;
font-size: 1.3125rem;
margin: 0;
}
.c1 + p,
.c1 + small,
.c1 + h1,
.c1 + h2,
.c1 + label,
.c1 + h3,
.c1 + h4,
.c1 + h5,
.c1 + div,
.c1 + span {
margin-top: 1.5rem;
}
<form
onSubmit={undefined}
/>
>
<div
className="c0"
>
<h3
className="c1"
/>
</div>
<button
className="c2 c3 c4"
href=""
onClick={undefined}
type="button"
>
Edit
</button>
</form>
`;
exports[`renders <Name expanded /> without throwing 1`] = `
@ -455,7 +620,172 @@ exports[`renders <Name name="test" /> without throwing 1`] = `
`;
exports[`renders <Name pristine={false} /> without throwing 1`] = `
.c0 {
margin-top: 1rem;
margin-bottom: 0.5rem;
}
.c4 {
font-family: sans-serif;
font-size: 100%;
line-height: 1.15;
margin: 0;
overflow: visible;
text-transform: none;
-webkit-appearance: button;
-moz-appearance: button;
appearance: button;
min-width: 7.5rem;
}
.c4::-moz-focus-inner,
.c4[type='button']::-moz-focus-inner,
.c4[type='reset']::-moz-focus-inner,
.c4[type='submit']::-moz-focus-inner {
border-style: none;
padding: 0;
}
.c4:-moz-focusring,
.c4[type='button']:-moz-focusring,
.c4[type='reset']:-moz-focusring,
.c4[type='submit']:-moz-focusring {
outline: 0.0625rem dotted ButtonText;
}
.c4 + button {
margin-left: 0.375rem;
}
.c3 {
display: inline-block;
}
.c2 {
box-sizing: border-box;
display: inline-block;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
min-height: 3rem;
height: 3rem;
min-width: 7.5rem;
margin-bottom: 0.5rem;
margin-top: 0.5rem;
padding: 0.9375rem 1.125rem;
position: relative;
font-size: 0.9375rem;
text-align: center;
font-style: normal;
font-stretch: normal;
line-height: normal;
-webkit-letter-spacing: normal;
-moz-letter-spacing: normal;
-ms-letter-spacing: normal;
letter-spacing: normal;
text-decoration: none;
white-space: nowrap;
vertical-align: middle;
touch-action: manipulation;
cursor: pointer;
color: rgb(255,255,255);
-webkit-text-fill-color: currentcolor;
background-image: none;
background-color: rgb(59,70,204);
border-radius: 0.25rem;
border: solid 0.0625rem rgb(45,56,132);
color: rgb(70,70,70);
-webkit-text-fill-color: currentcolor;
background-color: rgb(255,255,255);
border-color: rgb(216,216,216);
}
.c2:focus {
outline: 0;
text-decoration: none;
background-color: rgb(59,70,204);
border-color: rgb(45,56,132);
}
.c2:hover {
background-color: rgb(72,83,217);
border: solid 0.0625rem rgb(45,56,132);
}
.c2:active,
.c2:active:hover,
.c2:active:focus {
background-image: none;
outline: 0;
background-color: rgb(45,56,132);
border-color: rgb(45,56,132);
}
.c2[disabled] {
cursor: not-allowed;
pointer-events: none;
}
.c2:focus {
background-color: rgb(255,255,255);
border-color: rgb(216,216,216);
}
.c2:hover {
background-color: rgb(247,247,247);
border-color: rgb(216,216,216);
}
.c2:active,
.c2:active:hover,
.c2:active:focus {
background-color: rgb(230,230,230);
border-color: rgb(216,216,216);
}
.c1 {
color: rgba(73,73,73,1);
font-weight: normal;
line-height: 1.625rem;
font-size: 1.3125rem;
margin: 0;
}
.c1 + p,
.c1 + small,
.c1 + h1,
.c1 + h2,
.c1 + label,
.c1 + h3,
.c1 + h4,
.c1 + h5,
.c1 + div,
.c1 + span {
margin-top: 1.5rem;
}
<form
onSubmit={undefined}
/>
>
<div
className="c0"
>
<h3
className="c1"
/>
</div>
<button
className="c2 c3 c4"
href=""
onClick={undefined}
type="button"
>
Edit
</button>
</form>
`;

View File

@ -2,15 +2,74 @@ import React from 'react';
import renderer from 'react-test-renderer';
import 'jest-styled-components';
import Packages from '../package';
import { Filters, Packages, Package, Overview } from '../package';
import Theme from '@mocks/theme';
it('renders <Packages /> without throwing', () => {
it('renders <Overview hasVms/> without throwing', () => {
expect(
renderer
.create(
<Theme>
<Packages />
<Overview
name="test"
price="1"
memory="1"
vcpus="1"
hasVms
ssd
disk="1"
onCancel={() => {}}
/>
</Theme>
)
.toJSON()
).toMatchSnapshot();
});
it('renders <Overview /> without throwing', () => {
expect(
renderer
.create(
<Theme>
<Overview
name="test"
price="1"
memory="1"
vcpus="1"
disk="1"
onCancel={() => {}}
/>
</Theme>
)
.toJSON()
).toMatchSnapshot();
});
it('renders <Package /> without throwing', () => {
expect(
renderer
.create(
<Theme>
<Package
name="test"
price="1"
memory="1"
vcpus="1"
disk="1"
onCancel={() => {}}
/>
</Theme>
)
.toJSON()
).toMatchSnapshot();
});
it('renders <Filters /> without throwing', () => {
expect(
renderer
.create(
<Theme>
<Filters />
</Theme>
)
.toJSON()

View File

@ -131,7 +131,7 @@ export default ({
</Margin>
<Row>
{images &&
images.map(image => (
images.filter(i => i.isVm === isVmSelected).map(image => (
<Col md={2} sm={3}>
<Card
selected={

View File

@ -8,7 +8,7 @@ import pretty from 'prettysize';
import {
H2,
P,
H4,
FormGroup,
Button,
TableTh,
@ -18,9 +18,21 @@ import {
Table,
TableTd,
Radio,
H4
Checkbox,
Label,
GeneralIcon,
StorageIcon,
CpuIcon,
MemoryIcon
} from 'joyent-ui-toolkit';
const GroupIcons = {
MEMORY: <MemoryIcon fill="#32ABCF" />,
STORAGE: <StorageIcon fill="#A88A83" />,
GENERAL: <GeneralIcon fill="#E08A0E" />,
COMPUTE: <CpuIcon fill="#8043DC" />
};
const VerticalDivider = styled.div`
width: ${remcalc(1)};
background: ${props => props.theme.grey};
@ -47,155 +59,213 @@ const Badge = styled.div`
margin-left: ${remcalc(6)};
`;
export default ({
export const Filters = () => (
<Margin top={4} bottom={3}>
<H4>Filters</H4>
<Flex alignCenter justifyBetween>
<FormGroup type="checkbox" name="compute-optimized" field={Field}>
<Checkbox>
<Label>
<Flex alignCenter>
{GroupIcons.COMPUTE}
<Margin right={1} left={1}>
Compute optimized
</Margin>
</Flex>
</Label>
</Checkbox>
</FormGroup>
<FormGroup type="checkbox" name="memory-optimized" field={Field}>
<Checkbox>
<Label>
<Flex alignCenter>
{GroupIcons.MEMORY}
<Margin left={1} right={2}>
Memory optimized
</Margin>
</Flex>
</Label>
</Checkbox>
</FormGroup>
<FormGroup type="checkbox" name="general-purpose" field={Field}>
<Checkbox>
<Label>
<Flex alignCenter>
{GroupIcons.GENERAL}
<Margin left={1} right={2}>
General Purpose
</Margin>
</Flex>
</Label>
</Checkbox>
</FormGroup>
<FormGroup type="checkbox" name="storage-optimized" field={Field}>
<Checkbox>
<Label>
<Flex alignCenter>
{GroupIcons.STORAGE}
<Margin left={1} right={2}>
Storage optimized
</Margin>
</Flex>
</Label>
</Checkbox>
</FormGroup>
<FormGroup type="checkbox" name="ssd" field={Field}>
<Checkbox>
<Label>SSD</Label>
</Checkbox>
</FormGroup>
</Flex>
</Margin>
);
export const Package = ({
selected = false,
id,
name,
group,
memory,
price,
vcpus,
disk,
ssd,
hasVms
}) => (
<TableTr>
<TableTd selected={selected}>
<FormGroup name="package" value={id} type="radio" field={Field} fluid>
<Radio noMargin>
<Flex alignCenter>
{GroupIcons[group]}
<Margin left={1} right={2}>
<Label>{name}</Label>
</Margin>
</Flex>
</Radio>
</FormGroup>
</TableTd>
<TableTd selected={selected}>{pretty(memory)}</TableTd>
<TableTd selected={selected}>
{pretty(disk)}
{ssd && <Badge>SSD</Badge>}
</TableTd>
{hasVms && <TableTd selected={selected}>{vcpus}</TableTd>}
<TableTd selected={selected}>{price}</TableTd>
</TableTr>
);
export const Packages = ({
handleSubmit,
pristine,
expanded,
sortBy = 'name',
sortOrder = 'desc',
onSortBy = () => null,
onCancel,
packages,
pkg,
vmSelected
hasVms,
children
}) => (
<form onSubmit={handleSubmit}>
{expanded ? (
<Fragment>
<Margin bottom={3}>
<P>
A package defines the specs of your instance. On Triton, packages
can only increase in size. Read the docs
</P>
</Margin>
<Table>
<TableThead>
<TableTr>
<TableTh
onClick={() => onSortBy('name', sortOrder)}
sortOrder={sortOrder}
showSort={sortBy === 'name'}
left
middle
xs="200"
actionable
>
<span>Name </span>
</TableTh>
<TableTh
xs="100"
onClick={() => onSortBy('ram', sortOrder)}
sortOrder={sortOrder}
showSort={sortBy === 'ram'}
left
middle
actionable
>
<span>RAM </span>
</TableTh>
<TableTh
xs="100"
onClick={() => onSortBy('disk', sortOrder)}
sortOrder={sortOrder}
showSort={sortBy === 'disk'}
left
middle
actionable
>
<span>Disk </span>
</TableTh>
{vmSelected && (
<TableTh
xs="100"
onClick={() => onSortBy('vcpu', sortOrder)}
sortOrder={sortOrder}
showSort={sortBy === 'vcpu'}
left
middle
actionable
>
<span>vCPU</span>
</TableTh>
)}
<TableTh
xs="100"
onClick={() => onSortBy('price', sortOrder)}
sortOrder={sortOrder}
showSort={sortBy === 'price'}
left
middle
actionable
>
<span>$/hour</span>
</TableTh>
</TableTr>
</TableThead>
<TableTbody>
{packages.filter(p => p.vm === vmSelected).map(p => (
<TableTr>
<TableTd selected={pkg === p.id}>
<FormGroup
name="package"
value={p.id}
type="radio"
field={Field}
fluid
>
<Radio noMargin>
<H4>{p.name}</H4>
</Radio>
</FormGroup>
</TableTd>
<TableTd selected={pkg === p.id}>{pretty(p.memory)}</TableTd>
<TableTd selected={pkg === p.id}>
{pretty(p.disk)}
{p.ssd && <Badge>SSD</Badge>}
</TableTd>
{vmSelected && (
<TableTd selected={pkg === p.id}>{p.vcpus}</TableTd>
)}
<TableTd selected={pkg === p.id}>{p.price}</TableTd>
</TableTr>
))}
</TableTbody>
</Table>
<Button type="submit" disabled={pristine}>
Next
</Button>
</Fragment>
) : (
<Fragment>
<Margin bottom={2} top={3}>
{packages.map(p => {
if (p.id === pkg) {
return (
<Fragment>
<H2>{p.name}</H2>
<Flex alignCenter>
<span>{p.price} $</span>
<VerticalDivider />
<span>{pretty(p.memory)}</span>
{vmSelected && (
<Fragment>
<VerticalDivider />
<span>{p.vcpus} vCPUS</span>
</Fragment>
)}
<VerticalDivider />
<span>{pretty(p.disk)} disk</span>
<VerticalDivider />
{p.ssd && <span>SSD</span>}
</Flex>
</Fragment>
);
}
})}
</Margin>
{pkg && <Button type="button" secondary onClick={onCancel}>
Edit
</Button>}
</Fragment>
)}
<Table>
<TableThead>
<TableTr>
<TableTh
onClick={() => onSortBy('name', sortOrder)}
sortOrder={sortOrder}
showSort={sortBy === 'name'}
left
middle
xs="200"
actionable
>
<span>Name </span>
</TableTh>
<TableTh
xs="100"
onClick={() => onSortBy('ram', sortOrder)}
sortOrder={sortOrder}
showSort={sortBy === 'ram'}
left
middle
actionable
>
<span>RAM </span>
</TableTh>
<TableTh
xs="100"
onClick={() => onSortBy('disk', sortOrder)}
sortOrder={sortOrder}
showSort={sortBy === 'disk'}
left
middle
actionable
>
<span>Disk </span>
</TableTh>
{hasVms && (
<TableTh
xs="100"
onClick={() => onSortBy('vcpu', sortOrder)}
sortOrder={sortOrder}
showSort={sortBy === 'vcpu'}
left
middle
actionable
>
<span>vCPU</span>
</TableTh>
)}
<TableTh
xs="100"
onClick={() => onSortBy('price', sortOrder)}
sortOrder={sortOrder}
showSort={sortBy === 'price'}
left
middle
actionable
>
<span>$/hour</span>
</TableTh>
</TableTr>
</TableThead>
<TableTbody>{children}</TableTbody>
</Table>
<Button type="submit" disabled={pristine}>
Next
</Button>
</form>
);
export const Overview = ({
name,
price,
memory,
vcpus,
hasVms,
ssd,
disk,
onCancel
}) => (
<Fragment>
<Margin bottom={2} top={3}>
<H2>{name}</H2>
<Flex alignCenter>
<span>{price} $</span>
<VerticalDivider />
<span>{pretty(memory)}</span>
{hasVms && (
<Fragment>
<VerticalDivider />
<span>{vcpus} vCPUS</span>
</Fragment>
)}
<VerticalDivider />
<span>{pretty(disk)} disk</span>
<VerticalDivider />
{ssd && <span>SSD</span>}
</Flex>
</Margin>
<Button type="button" secondary onClick={onCancel}>
Edit
</Button>
</Fragment>
);

View File

@ -230,6 +230,7 @@ exports[`renders <InstanceList /> without throwing 1`] = `
color: rgb(189,189,189);
font-weight: 500;
color: rgba(73,73,73,1);
font-weight: bold;
}
.c13:not(:first-child) {
@ -773,6 +774,7 @@ exports[`renders <InstanceList allSelected /> without throwing 1`] = `
color: rgb(189,189,189);
font-weight: 500;
color: rgba(73,73,73,1);
font-weight: bold;
}
.c13:not(:first-child) {
@ -1433,6 +1435,7 @@ exports[`renders <InstanceList sortBy /> without throwing 1`] = `
color: rgb(189,189,189);
font-weight: 500;
color: rgba(73,73,73,1);
font-weight: bold;
}
.c14:not(:first-child) {
@ -1976,6 +1979,7 @@ exports[`renders <InstanceList sortBy sortOrder /> without throwing 1`] = `
color: rgb(189,189,189);
font-weight: 500;
color: rgba(73,73,73,1);
font-weight: bold;
}
.c14:not(:first-child) {
@ -2402,6 +2406,7 @@ exports[`renders <InstanceList submitting /> without throwing 1`] = `
color: rgb(189,189,189);
font-weight: 500;
color: rgba(73,73,73,1);
font-weight: bold;
}
.c13:not(:first-child) {
@ -2945,6 +2950,7 @@ exports[`renders <InstanceList>{children}</InstanceList> without throwing 1`] =
color: rgb(189,189,189);
font-weight: 500;
color: rgba(73,73,73,1);
font-weight: bold;
}
.c13:not(:first-child) {
@ -3482,6 +3488,7 @@ exports[`renders <Item /> without throwing 1`] = `
height: 3.75rem;
vertical-align: middle;
text-align: left;
border-bottom-width: 0;
}
.c4:not(:first-child) {
@ -3505,6 +3512,7 @@ exports[`renders <Item /> without throwing 1`] = `
display: none;
vertical-align: middle;
text-align: left;
border-bottom-width: 0;
}
.c16:not(:first-child) {
@ -3528,6 +3536,7 @@ exports[`renders <Item /> without throwing 1`] = `
display: none;
vertical-align: middle;
text-align: left;
border-bottom-width: 0;
}
.c17:not(:first-child) {
@ -3547,6 +3556,7 @@ exports[`renders <Item /> without throwing 1`] = `
box-sizing: border-box;
padding: 0 1.5rem;
height: 3.75rem;
border-bottom-width: 0;
border-left-width: 0.0625rem !important;
}
@ -3980,6 +3990,7 @@ exports[`renders <Item {...item} /> without throwing 1`] = `
height: 3.75rem;
vertical-align: middle;
text-align: left;
border-bottom-width: 0;
}
.c4:not(:first-child) {
@ -4003,6 +4014,7 @@ exports[`renders <Item {...item} /> without throwing 1`] = `
display: none;
vertical-align: middle;
text-align: left;
border-bottom-width: 0;
}
.c16:not(:first-child) {
@ -4026,6 +4038,7 @@ exports[`renders <Item {...item} /> without throwing 1`] = `
display: none;
vertical-align: middle;
text-align: left;
border-bottom-width: 0;
}
.c17:not(:first-child) {
@ -4045,6 +4058,7 @@ exports[`renders <Item {...item} /> without throwing 1`] = `
box-sizing: border-box;
padding: 0 1.5rem;
height: 3.75rem;
border-bottom-width: 0;
border-left-width: 0.0625rem !important;
}
@ -4480,6 +4494,7 @@ exports[`renders <Item allowedActions /> without throwing 1`] = `
height: 3.75rem;
vertical-align: middle;
text-align: left;
border-bottom-width: 0;
}
.c4:not(:first-child) {
@ -4503,6 +4518,7 @@ exports[`renders <Item allowedActions /> without throwing 1`] = `
display: none;
vertical-align: middle;
text-align: left;
border-bottom-width: 0;
}
.c16:not(:first-child) {
@ -4526,6 +4542,7 @@ exports[`renders <Item allowedActions /> without throwing 1`] = `
display: none;
vertical-align: middle;
text-align: left;
border-bottom-width: 0;
}
.c17:not(:first-child) {
@ -4545,6 +4562,7 @@ exports[`renders <Item allowedActions /> without throwing 1`] = `
box-sizing: border-box;
padding: 0 1.5rem;
height: 3.75rem;
border-bottom-width: 0;
border-left-width: 0.0625rem !important;
}
@ -5002,6 +5020,7 @@ exports[`renders <Item mutating /> without throwing 1`] = `
height: 3.75rem;
vertical-align: middle;
text-align: left;
border-bottom-width: 0;
}
.c4:not(:first-child) {
@ -5025,6 +5044,7 @@ exports[`renders <Item mutating /> without throwing 1`] = `
display: none;
vertical-align: middle;
text-align: left;
border-bottom-width: 0;
}
.c19:not(:first-child) {
@ -5048,6 +5068,7 @@ exports[`renders <Item mutating /> without throwing 1`] = `
display: none;
vertical-align: middle;
text-align: left;
border-bottom-width: 0;
}
.c20:not(:first-child) {
@ -5069,6 +5090,7 @@ exports[`renders <Item mutating /> without throwing 1`] = `
height: 3.75rem;
vertical-align: middle;
text-align: center;
border-bottom-width: 0;
border-left-width: 0.0625rem !important;
}

View File

@ -230,6 +230,7 @@ exports[`renders <Actions /> without throwing 1`] = `
color: rgb(189,189,189);
font-weight: 500;
color: rgba(73,73,73,1);
font-weight: bold;
}
.c13:not(:first-child) {
@ -691,6 +692,7 @@ exports[`renders <Item /> without throwing 1`] = `
height: 3.75rem;
vertical-align: middle;
text-align: left;
border-bottom-width: 0;
}
.c2:not(:first-child) {
@ -714,6 +716,7 @@ exports[`renders <Item /> without throwing 1`] = `
display: none;
vertical-align: middle;
text-align: left;
border-bottom-width: 0;
}
.c12:not(:first-child) {
@ -733,6 +736,7 @@ exports[`renders <Item /> without throwing 1`] = `
box-sizing: border-box;
padding: 0 1.5rem;
height: 3.75rem;
border-bottom-width: 0;
border-left-width: 0.0625rem !important;
}
@ -1107,6 +1111,7 @@ exports[`renders <Item {...item} /> without throwing 1`] = `
height: 3.75rem;
vertical-align: middle;
text-align: left;
border-bottom-width: 0;
}
.c2:not(:first-child) {
@ -1130,6 +1135,7 @@ exports[`renders <Item {...item} /> without throwing 1`] = `
display: none;
vertical-align: middle;
text-align: left;
border-bottom-width: 0;
}
.c12:not(:first-child) {
@ -1149,6 +1155,7 @@ exports[`renders <Item {...item} /> without throwing 1`] = `
box-sizing: border-box;
padding: 0 1.5rem;
height: 3.75rem;
border-bottom-width: 0;
border-left-width: 0.0625rem !important;
}
@ -1446,6 +1453,7 @@ exports[`renders <Item mutating /> without throwing 1`] = `
box-sizing: border-box;
padding: 0 1.5rem;
height: 3.75rem;
border-bottom-width: 0;
}
.c3:not(:first-child) {
@ -1765,6 +1773,7 @@ exports[`renders <SnapshotList /> without throwing 1`] = `
color: rgb(189,189,189);
font-weight: 500;
color: rgba(73,73,73,1);
font-weight: bold;
}
.c13:not(:first-child) {
@ -2268,6 +2277,7 @@ exports[`renders <SnapshotList allSelected /> without throwing 1`] = `
color: rgb(189,189,189);
font-weight: 500;
color: rgba(73,73,73,1);
font-weight: bold;
}
.c13:not(:first-child) {
@ -2857,6 +2867,7 @@ exports[`renders <SnapshotList sortBy /> without throwing 1`] = `
color: rgb(189,189,189);
font-weight: 500;
color: rgba(73,73,73,1);
font-weight: bold;
}
.c14:not(:first-child) {
@ -3360,6 +3371,7 @@ exports[`renders <SnapshotList sortBy sortOrder /> without throwing 1`] = `
color: rgb(189,189,189);
font-weight: 500;
color: rgba(73,73,73,1);
font-weight: bold;
}
.c14:not(:first-child) {
@ -3777,6 +3789,7 @@ exports[`renders <SnapshotList submitting /> without throwing 1`] = `
color: rgb(189,189,189);
font-weight: 500;
color: rgba(73,73,73,1);
font-weight: bold;
}
.c13:not(:first-child) {

View File

@ -21,7 +21,7 @@ export default ({ step, ...props }) => (
<Image {...props} expanded={step === 'image'} />
</Margin>
<Margin bottom={4}>
<Package expanded={step === 'package'} />
<Package {...props} expanded={step === 'package'} />
</Margin>
<Margin bottom={4}>
<Tags {...props} expanded={step === 'tags'} />

View File

@ -1,5 +1,5 @@
import React, { Fragment } from 'react';
import { withRouter } from 'react-router';
import { Margin } from 'styled-components-spacing';
import { compose, graphql } from 'react-apollo';
import ReduxForm from 'declarative-redux-form';
import { connect } from 'react-redux';
@ -7,49 +7,95 @@ import titleCase from 'title-case';
import get from 'lodash.get';
import { set } from 'react-redux-values';
import sortBy from 'lodash.sortby';
import find from 'lodash.find';
import constantCase from 'constant-case';
import { PackageIcon, StatusLoader } from 'joyent-ui-toolkit';
import Package from '@components/create-instance/package';
import { PackageIcon, StatusLoader, P } from 'joyent-ui-toolkit';
import {
Filters,
Packages,
Package,
Overview
} from '@components/create-instance/package';
import Title from '@components/create-instance/title';
import priceData from '../../data/prices.json';
import getPackages from '../../graphql/get-packages.gql';
const FORM_NAME = 'create-instance-package';
const PackageContainer = ({
expanded,
vmSelected,
hasVms,
handleSubmit,
handleCancel,
loading,
packages,
pkg,
selected = {},
sortOrder,
handleSortBy,
sortBy
}) => (
<Fragment>
<Title icon={<PackageIcon />}>Package</Title>
{expanded ? (
<Margin bottom={3}>
<P>
A package defines the specs of your instance. On Triton, packages can
only increase in size.{' '}
<a
href="https://docs.joyent.com/private-cloud/packages"
target="_blank"
rel="noopener noreferrer"
>
Read the docs
</a>
</P>
</Margin>
) : null}
{!loading && expanded ? (
<ReduxForm
form={`${FORM_NAME}-filters`}
destroyOnUnmount={false}
forceUnregisterOnUnmount={true}
>
{props => <Filters {...props} />}
</ReduxForm>
) : null}
{loading && expanded ? (
<StatusLoader />
) : (
<ReduxForm
form="create-instance-package"
form={FORM_NAME}
destroyOnUnmount={false}
forceUnregisterOnUnmount={true}
onSubmit={handleSubmit}
>
{props => (
<Package
{...props}
pkg={pkg}
vmSelected={vmSelected}
packages={packages}
expanded={expanded}
sortBy={sortBy}
onCancel={handleCancel}
sortOrder={sortOrder}
onSortBy={handleSortBy}
/>
<Fragment>
{expanded ? (
<Packages
{...props}
hasVms={hasVms}
sortBy={sortBy}
sortOrder={sortOrder}
onSortBy={handleSortBy}
>
{packages.map(({ id, ...pkg }) => (
<Package
key={id}
id={id}
selected={selected.id === id}
hasVms={hasVms}
{...pkg}
/>
))}
</Packages>
) : null}
{!expanded && selected.id ? (
<Overview {...selected} hasVms={hasVms} onCancel={handleCancel} />
) : null}
</Fragment>
)}
</ReduxForm>
)}
@ -57,12 +103,8 @@ const PackageContainer = ({
);
export default compose(
withRouter,
graphql(getPackages, {
props: ({
ownProps: { vmSelected = false },
data: { loading, packages = [] }
}) => ({
props: ({ data: { loading, packages = [] } }) => ({
loading,
packages: packages.map(pkg => {
const packagePrice = priceData.filter(p => p.name === pkg.name)[0];
@ -76,31 +118,75 @@ export default compose(
disk: pkg.disk * 1000000,
price: packagePrice.cost || 0,
name: titleCase(packageName.replace(/-/g, ' ')),
group: pkg.group.replace(/KVM/g, '').trim()
group: constantCase(
pkg.group.replace(/optimized|purpose|KVM/gi, '').trim()
)
};
})
})
}),
connect(
(state, { packages, ...ownProps }) => {
const _sortBy = get(state, 'values.packages-list-sort-by', 'name');
const _sortOrder = get(state, 'values.packages-list-sort-order', 'asc');
({ form, values }, { packages, ...ownProps }) => {
const _sortBy = get(values, 'packages-list-sort-by', 'price');
const _sortOrder = get(values, 'packages-list-sort-order', 'asc');
const sortedPackages = sortBy(packages, [_sortBy]);
const ssdOnly = get(form, `${FORM_NAME}-filters.values.ssd`, false);
const computeOptimized = get(
form,
`${FORM_NAME}-filters.values.compute-optimized`,
false
);
const generalPurpose = get(
form,
`${FORM_NAME}-filters.values.general-purpose`,
false
);
const storageOptimized = get(
form,
`${FORM_NAME}-filters.values.storage-optimized`,
false
);
const memoryOptimized = get(
form,
`${FORM_NAME}-filters.values.memory-optimized`,
false
);
const vmSelected = get(form, 'create-instance-image.values.vms', false);
const pkgSelected = get(form, `${FORM_NAME}.values.package`, null);
const sorted = sortBy(packages, [_sortBy]);
let filtered = sorted
.filter(p => (ssdOnly ? p.ssd === ssdOnly : true))
.filter(p => p.vm === vmSelected);
if (
computeOptimized ||
generalPurpose ||
storageOptimized ||
memoryOptimized
) {
filtered = filtered.filter(
p =>
(memoryOptimized && p.group === 'MEMORY') ||
(storageOptimized && p.group === 'STORAGE') ||
(generalPurpose && p.group === 'GENERAL') ||
(computeOptimized && p.group === 'COMPUTE')
);
}
return {
...ownProps,
sortBy: _sortBy,
sortOrder: _sortOrder,
packages:
_sortOrder === 'asc' ? sortedPackages : sortedPackages.reverse(),
vmSelected: get(state, 'form.create-instance-image.values.vms', false),
pkg: get(state, 'form.create-instance-package.values.package', null)
packages: _sortOrder === 'asc' ? filtered : filtered.reverse(),
hasVms: vmSelected,
selected: find(packages, ['id', pkgSelected])
};
},
(dispatch, { history }) => ({
handleSubmit: () => history.push(`/instances/~create/tags`),
handleCancel: () => history.push(`/instances/~create/image`),
handleSubmit: () => history.push('/instances/~create/tags'),
handleCancel: () => history.push('/instances/~create/package'),
handleSortBy: (newSortBy, sortOrder) => {
dispatch([
set({

View File

@ -532,6 +532,7 @@ exports[`renders <List /> without throwing 1`] = `
color: rgb(189,189,189);
font-weight: 500;
color: rgba(73,73,73,1);
font-weight: bold;
}
.c26:not(:first-child) {
@ -1579,6 +1580,7 @@ exports[`renders <List error /> without throwing 1`] = `
color: rgb(189,189,189);
font-weight: 500;
color: rgba(73,73,73,1);
font-weight: bold;
}
.c32:not(:first-child) {
@ -2637,6 +2639,7 @@ exports[`renders <List instances /> without throwing 1`] = `
color: rgb(189,189,189);
font-weight: 500;
color: rgba(73,73,73,1);
font-weight: bold;
}
.c26:not(:first-child) {
@ -2775,6 +2778,7 @@ exports[`renders <List instances /> without throwing 1`] = `
height: 3.75rem;
vertical-align: middle;
text-align: left;
border-bottom-width: 0;
}
.c36:not(:first-child) {
@ -2798,6 +2802,7 @@ exports[`renders <List instances /> without throwing 1`] = `
display: none;
vertical-align: middle;
text-align: left;
border-bottom-width: 0;
}
.c40:not(:first-child) {
@ -2821,6 +2826,7 @@ exports[`renders <List instances /> without throwing 1`] = `
display: none;
vertical-align: middle;
text-align: left;
border-bottom-width: 0;
}
.c41:not(:first-child) {
@ -2840,6 +2846,7 @@ exports[`renders <List instances /> without throwing 1`] = `
box-sizing: border-box;
padding: 0 1.5rem;
height: 3.75rem;
border-bottom-width: 0;
border-left-width: 0.0625rem !important;
}
@ -4733,6 +4740,7 @@ exports[`renders <List instances selected /> without throwing 1`] = `
color: rgb(189,189,189);
font-weight: 500;
color: rgba(73,73,73,1);
font-weight: bold;
}
.c26:not(:first-child) {
@ -4871,6 +4879,7 @@ exports[`renders <List instances selected /> without throwing 1`] = `
height: 3.75rem;
vertical-align: middle;
text-align: left;
border-bottom-width: 0;
}
.c36:not(:first-child) {
@ -4894,6 +4903,7 @@ exports[`renders <List instances selected /> without throwing 1`] = `
display: none;
vertical-align: middle;
text-align: left;
border-bottom-width: 0;
}
.c40:not(:first-child) {
@ -4917,6 +4927,7 @@ exports[`renders <List instances selected /> without throwing 1`] = `
display: none;
vertical-align: middle;
text-align: left;
border-bottom-width: 0;
}
.c41:not(:first-child) {
@ -4936,6 +4947,7 @@ exports[`renders <List instances selected /> without throwing 1`] = `
box-sizing: border-box;
padding: 0 1.5rem;
height: 3.75rem;
border-bottom-width: 0;
border-left-width: 0.0625rem !important;
}
@ -7372,6 +7384,7 @@ exports[`renders <List instances selected=all /> without throwing 1`] = `
color: rgb(189,189,189);
font-weight: 500;
color: rgba(73,73,73,1);
font-weight: bold;
}
.c26:not(:first-child) {
@ -7510,6 +7523,7 @@ exports[`renders <List instances selected=all /> without throwing 1`] = `
height: 3.75rem;
vertical-align: middle;
text-align: left;
border-bottom-width: 0;
}
.c36:not(:first-child) {
@ -7533,6 +7547,7 @@ exports[`renders <List instances selected=all /> without throwing 1`] = `
display: none;
vertical-align: middle;
text-align: left;
border-bottom-width: 0;
}
.c40:not(:first-child) {
@ -7556,6 +7571,7 @@ exports[`renders <List instances selected=all /> without throwing 1`] = `
display: none;
vertical-align: middle;
text-align: left;
border-bottom-width: 0;
}
.c41:not(:first-child) {
@ -7575,6 +7591,7 @@ exports[`renders <List instances selected=all /> without throwing 1`] = `
box-sizing: border-box;
padding: 0 1.5rem;
height: 3.75rem;
border-bottom-width: 0;
border-left-width: 0.0625rem !important;
}
@ -10220,6 +10237,7 @@ exports[`renders <List instances selected=all allowedActions /> without throwing
color: rgb(189,189,189);
font-weight: 500;
color: rgba(73,73,73,1);
font-weight: bold;
}
.c26:not(:first-child) {
@ -10358,6 +10376,7 @@ exports[`renders <List instances selected=all allowedActions /> without throwing
height: 3.75rem;
vertical-align: middle;
text-align: left;
border-bottom-width: 0;
}
.c36:not(:first-child) {
@ -10381,6 +10400,7 @@ exports[`renders <List instances selected=all allowedActions /> without throwing
display: none;
vertical-align: middle;
text-align: left;
border-bottom-width: 0;
}
.c40:not(:first-child) {
@ -10404,6 +10424,7 @@ exports[`renders <List instances selected=all allowedActions /> without throwing
display: none;
vertical-align: middle;
text-align: left;
border-bottom-width: 0;
}
.c41:not(:first-child) {
@ -10423,6 +10444,7 @@ exports[`renders <List instances selected=all allowedActions /> without throwing
box-sizing: border-box;
padding: 0 1.5rem;
height: 3.75rem;
border-bottom-width: 0;
border-left-width: 0.0625rem !important;
}

View File

@ -115,14 +115,18 @@ const InnerContainer = styled.div`
height: ${remcalc(18)};
position: relative;
cursor: pointer;
${isNot('noMargin')`
margin-bottom: ${remcalc(12)};
`};
`;
const Container = styled.div`
margin-bottom: ${remcalc(12)};
margin-left: ${remcalc(12)};
${isNot('noMargin')`
margin-bottom: ${remcalc(12)};
`};
`;
const ToggleBase = ({ container = null, type = 'radio' }) =>
@ -175,9 +179,9 @@ const ToggleBase = ({ container = null, type = 'radio' }) =>
);
const el = OuterContainer ? (
<OuterContainer>
<OuterContainer {...rest}>
{toggle}
<Container>{children}</Container>
<Container {...rest}>{children}</Container>
</OuterContainer>
) : (
toggle

View File

@ -38,9 +38,17 @@ import {
User as BaseUser,
Fabric as BaseFabric,
Name as BaseName,
Randomize as BaseRandomize
Randomize as BaseRandomize,
Cpu as BaseCpu,
Memory as BaseMemory,
Storage as BaseStorage,
General as BaseGeneral
} from 'joyent-icons';
export const General = Baseline(BaseGeneral);
export const Storage = Baseline(BaseStorage);
export const Cpu = Baseline(BaseCpu);
export const Memory = Baseline(BaseMemory);
export const Fabric = Baseline(BaseFabric);
export const Name = Baseline(BaseName);
export const Randomize = Baseline(BaseRandomize);

View File

@ -103,7 +103,11 @@ export {
Tags as TagsIcon,
Triton as TritonIcon,
User as UserIcon,
Name as NameIcon
Name as NameIcon,
General as GeneralIcon,
Storage as StorageIcon,
Cpu as CpuIcon,
Memory as MemoryIcon
} from './icons';
export {

View File

@ -2,7 +2,7 @@ import React from 'react';
import { Broadcast, Subscriber } from 'joy-react-broadcast';
import isBoolean from 'lodash.isboolean';
import styled, { css } from 'styled-components';
import is from 'styled-is';
import is, { isOr } from 'styled-is';
import remcalc from 'remcalc';
import Baseline from '../baseline';
@ -174,7 +174,7 @@ const BaseTh = styled.th`
color: ${props => props.theme.greyLight};
font-weight: 500;
${is('selected')`
${isOr('selected', 'showSort')`
color: ${props => props.theme.text};
font-weight: bold;
`};
@ -308,7 +308,7 @@ export const Tr = Baseline(({ children, ...rest }) => (
export const Th = Baseline(({ children, ...rest }) => (
<Propagate {...rest}>
{({ showSort, sortOrder, header, ...value }) => (
<BaseTh {...value} header={header} name="th">
<BaseTh {...value} showSort={showSort} header={header} name="th">
{children}
{!showSort || !header ? null : (
<ArrowIcon

View File

@ -2502,6 +2502,13 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
constant-case@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-2.0.0.tgz#4175764d389d3fa9c8ecd29186ed6005243b6a46"
dependencies:
snake-case "^2.1.0"
upper-case "^1.1.1"
constants-browserify@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75"
@ -9570,6 +9577,12 @@ smog-formula@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/smog-formula/-/smog-formula-1.0.2.tgz#25753f3bc7c0a7d10e0767a56e716ebd3bdd326e"
snake-case@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-2.1.0.tgz#41bdb1b73f30ec66a04d4e2cad1b76387d4d6d9f"
dependencies:
no-case "^2.2.0"
snapdragon-node@^2.0.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b"