fix(my-joy-beta): simplify create-instance images implementation

fixes #1027
fixes #1028
This commit is contained in:
Sérgio Ramos 2018-01-29 23:20:22 +00:00 committed by Sérgio Ramos
parent 26894e25d2
commit 0ce87d3edd
30 changed files with 237 additions and 2578 deletions

View File

@ -12,7 +12,7 @@
"lint-ci": "eslint . --ext .js --ext .md", "lint-ci": "eslint . --ext .js --ext .md",
"lint": "eslint . --fix --ext .js --ext .md", "lint": "eslint . --fix --ext .js --ext .md",
"test-ci": "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'",
"test": "DEFAULT_TIMEOUT_INTERVAL=50000 NODE_ENV=test joyent-react-scripts test --env=jsdom", "test": "DEFAULT_TIMEOUT_INTERVAL=80000 NODE_ENV=test joyent-react-scripts test --env=jsdom",
"prepublish": "echo 0" "prepublish": "echo 0"
}, },
"dependencies": { "dependencies": {
@ -27,6 +27,7 @@
"joyent-manifest-editor": "3.0.1", "joyent-manifest-editor": "3.0.1",
"joyent-ui-toolkit": "^4.5.0", "joyent-ui-toolkit": "^4.5.0",
"lodash.find": "^4.6.0", "lodash.find": "^4.6.0",
"lodash.findindex": "^4.6.0",
"lodash.get": "^4.4.2", "lodash.get": "^4.4.2",
"lodash.includes": "^4.3.0", "lodash.includes": "^4.3.0",
"lodash.isarray": "^4.0.0", "lodash.isarray": "^4.0.0",
@ -34,6 +35,7 @@
"lodash.isfunction": "^3.0.8", "lodash.isfunction": "^3.0.8",
"lodash.isstring": "^4.0.1", "lodash.isstring": "^4.0.1",
"lodash.omit": "^4.5.0", "lodash.omit": "^4.5.0",
"lodash.reverse": "^4.0.1",
"lodash.sortby": "^4.7.0", "lodash.sortby": "^4.7.0",
"lodash.uniqby": "^4.7.0", "lodash.uniqby": "^4.7.0",
"lunr": "^2.1.5", "lunr": "^2.1.5",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@ -6,6 +6,7 @@ import Flex from 'styled-flex-component';
import remcalc from 'remcalc'; import remcalc from 'remcalc';
import { Row, Col } from 'react-styled-flexboxgrid'; import { Row, Col } from 'react-styled-flexboxgrid';
import titleCase from 'title-case'; import titleCase from 'title-case';
import includes from 'lodash.includes';
import { import {
H3, H3,
@ -47,47 +48,54 @@ const getImage = name => {
} }
}; };
const getImageByID = (id, images) => { export const Preview = ({ name, version, isVm }) => (
const image = images
.map(image => ({
...image,
versions: (image.versions || []).filter(version => version.id === id)
}))
.filter(e => e.versions.length)[0];
return image
? {
imageName: image.imageName,
name: image.versions[0].name,
version: image.versions[0].version
}
: {};
};
export const Preview = ({ imageID, images, isVmSelected, onEdit }) => (
<Fragment> <Fragment>
<Margin bottom={2} top={3}> <Margin bottom={2} top={3}>
<H3 bold> <H3 bold>
{titleCase(getImageByID(imageID, images).name)} -{' '} {name} - {version}
{getImageByID(imageID, images).version}
</H3> </H3>
<P> <P>{isVm ? 'Hardware Virtual Machine' : 'Infrastructure Container'} </P>
{isVmSelected ? 'Hardware Virtual Machine' : 'Infrastructure Container'}{' '}
</P>
</Margin> </Margin>
<Button type="button" secondary onClick={onEdit}>
Edit
</Button>
</Fragment> </Fragment>
); );
export default ({ const Image = ({ onClick, active, ...image }) => {
handleSubmit, const { imageName = '', versions = [] } = image;
pristine,
imageID, const ids = [`image-card-${imageName}`, `image-img-${imageName}`];
images = [], const handleClick = ev =>
isVmSelected includes(ids, ev.target.id) ? onClick(image) : null;
}) => (
<form onSubmit={handleSubmit}> return (
<Col md={2} sm={3}>
<Card id={ids[0]} onClick={handleClick} active={active} preview>
<img
id={ids[1]}
src={getImage(imageName).url}
width={getImage(imageName).size}
height={getImage(imageName).size}
style={{ marginBottom: getImage(imageName).bottom }}
alt={imageName}
/>
<H4>{titleCase(imageName)}</H4>
<FormGroup name="image" field={Field}>
<Version onBlur={null}>
<option selected>Version</option>
{versions.map(({ name, version, id }) => (
<option
key={`${name} - ${version}`}
value={id}
>{`${name} - ${version}`}</option>
))}
</Version>
</FormGroup>
</Card>
</Col>
);
};
export const ImageType = () => (
<form>
<Margin bottom={4}> <Margin bottom={4}>
<FormGroup name="vms" field={Field}> <FormGroup name="vms" field={Field}>
<Flex alignCenter> <Flex alignCenter>
@ -96,46 +104,20 @@ export default ({
</Flex> </Flex>
</FormGroup> </FormGroup>
</Margin> </Margin>
<Row> </form>
{images && );
images.filter(i => i.isVm === isVmSelected).map(image => (
<Col md={2} sm={3}> export default ({ images = [], onSelectLatest }) => (
<Card <form>
active={ <Row>
image.imageName === getImageByID(imageID, images).imageName {images.map(({ imageName, ...image }) => (
} <Image
preview {...image}
> key={imageName}
<img imageName={imageName}
src={getImage(image.imageName).url} onClick={onSelectLatest}
width={getImage(image.imageName).size} />
height={getImage(image.imageName).size} ))}
style={{ </Row>
marginBottom: getImage(image.imageName).bottom
}}
alt={image.imageName}
/>
<H4>{titleCase(image.imageName)}</H4>
<FormGroup name="image" field={Field}>
<Version onBlur={null}>
<option selected>Version</option>
{image.versions &&
image.versions.map(version => (
<option
key={`${version.name} - ${version.version}`}
value={version.id}
>{`${version.name} - ${version.version}`}</option>
))}
</Version>
</FormGroup>
</Card>
</Col>
))}
</Row>
<Margin top={4}>
<Button type="submit" disabled={pristine || !imageID}>
Next
</Button>
</Margin>
</form> </form>
); );

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

View File

@ -1,31 +1,40 @@
import React, { Fragment } from 'react'; import React, { Fragment } from 'react';
import { compose, graphql } from 'react-apollo'; import { compose, graphql } from 'react-apollo';
import ReduxForm from 'declarative-redux-form'; import ReduxForm from 'declarative-redux-form';
import { change } from 'redux-form';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { set } from 'react-redux-values'; import { set } from 'react-redux-values';
import { Margin } from 'styled-components-spacing';
import includes from 'lodash.includes'; import includes from 'lodash.includes';
import sortBy from 'lodash.sortby';
import findIndex from 'lodash.findindex';
import find from 'lodash.find';
import reverse from 'lodash.reverse';
import get from 'lodash.get'; import get from 'lodash.get';
import { InstanceTypeIcon, StatusLoader } from 'joyent-ui-toolkit'; import { InstanceTypeIcon, StatusLoader, Button } from 'joyent-ui-toolkit';
import Description from '@components/description'; import Description from '@components/description';
import Image, { Preview } from '@components/create-instance/image'; import Image, { Preview, ImageType } from '@components/create-instance/image';
import Title from '@components/create-instance/title'; import Title from '@components/create-instance/title';
import imageData from '@data/images-map.json'; import imageData from '@data/images-map.json';
import getImages from '@graphql/get-images.gql'; import GetImages from '@graphql/get-images.gql';
const ImageContainer = ({ const ImageContainer = ({
expanded, expanded,
image, proceeded,
image = {},
handleNext, handleNext,
handleEdit, handleEdit,
handleSelectLatest,
loading, loading,
images, images,
vms vms
}) => ( }) => (
<Fragment> <Fragment>
{console.log({ image, vms })}
<Title <Title
onClick={!expanded && !image && handleEdit} onClick={!expanded && !image.id && handleEdit}
icon={<InstanceTypeIcon />} icon={<InstanceTypeIcon />}
> >
Instance type and image Instance type and image
@ -44,12 +53,19 @@ const ImageContainer = ({
</a> </a>
</Description> </Description>
) : null} ) : null}
<ReduxForm
form="create-instance-vms"
destroyOnUnmount={false}
forceUnregisterOnUnmount={true}
initialValues={{ vms: true }}
>
{props => (loading || !expanded ? null : <ImageType {...props} />)}
</ReduxForm>
<ReduxForm <ReduxForm
form="create-instance-image" form="create-instance-image"
destroyOnUnmount={false} destroyOnUnmount={false}
forceUnregisterOnUnmount={true} forceUnregisterOnUnmount={true}
initialValues={{ vms: true }} initialValues={{ vms: true }}
onSubmit={handleNext}
> >
{props => {props =>
loading && expanded ? ( loading && expanded ? (
@ -57,30 +73,44 @@ const ImageContainer = ({
) : expanded ? ( ) : expanded ? (
<Image <Image
{...props} {...props}
isVmSelected={vms} images={images.filter(i => i.isVm === vms)}
imageID={image} onSelectLatest={handleSelectLatest}
images={images}
/>
) : image ? (
<Preview
isVmSelected={vms}
imageID={image}
images={images}
onEdit={handleEdit}
/> />
) : image.id ? (
<Preview {...image} />
) : null ) : null
} }
</ReduxForm> </ReduxForm>
<Fragment>
{expanded ? (
<Margin bottom={4}>
<Button
type="button"
onClick={handleNext}
disabled={!image.id || vms !== image.isVm}
>
Next
</Button>
</Margin>
) : proceeded ? (
<Margin bottom={4}>
<Button type="button" onClick={handleEdit} secondary>
Edit
</Button>
</Margin>
) : null}
</Fragment>
</Fragment> </Fragment>
); );
export default compose( export default compose(
connect( connect(
(state, ownProps) => { ({ form, values }, ownProps) => {
return { return {
...ownProps, ...ownProps,
vms: get(state, 'form.create-instance-image.values.vms', false), proceeded: get(values, 'create-instance-image-proceeded', false),
image: get(state, 'form.create-instance-image.values.image', null) vms: get(form, 'create-instance-vms.values.vms', false),
image: get(form, 'create-instance-image.values.image', null)
}; };
}, },
(dispatch, { history }) => ({ (dispatch, { history }) => ({
@ -89,58 +119,76 @@ export default compose(
return history.push(`/instances/~create/package`); return history.push(`/instances/~create/package`);
}, },
handleEdit: () => history.push(`/instances/~create/image`) handleEdit: () => history.push(`/instances/~create/image`),
handleSelectLatest: ({ versions }) => {
const id = versions[versions.length - 1].id;
return dispatch(change('create-instance-image', 'image', id));
}
}) })
), ),
graphql(getImages, { graphql(GetImages, {
props: ({ ownProps: { vms = false }, data: { loading, images = [] } }) => ({ props: ({ ownProps, data }) => {
loading, const { image = '' } = ownProps;
images: images.reduce((accumulator, image) => { const { loading = false, images = [] } = data;
const isVm = !includes(image.type, 'DATASET');
if (isVm && !vms) { const values = images
return accumulator; .reduce((acc, img) => {
} const isVm = !includes(img.type, 'DATASET');
const name = const imageName =
imageData[ imageData[
image.name img.name
.split('-')[0] .split('-')[0]
.split(' ')[0] .split(' ')[0]
.toLowerCase() .toLowerCase()
]; ];
const exists = Boolean( const exists = Boolean(find(acc, { imageName, isVm }));
accumulator.filter(e => e.imageName === name && isVm === e.isVm)
.length
);
if (!exists) { const version = {
return accumulator.concat([ name: img.name,
{ version: img.version,
imageName: name, id: img.id
versions: [ };
{
name: image.name,
version: image.version,
id: image.id
}
],
isVm
}
]);
}
return accumulator.map(({ versions, ...rest }) => ({ if (!exists) {
...rest, return acc.concat([
versions: {
rest.imageName === name && rest.isVm === isVm isVm,
? versions.concat([ imageName,
{ name: image.name, version: image.version, id: image.id } versions: [version]
]) }
: versions ]);
}
const index = findIndex(acc, {
imageName,
isVm
});
acc[index] = {
...acc[index],
versions: acc[index].versions.concat([version])
};
return acc;
}, [])
.map(({ versions, ...img }) => ({
...img,
active: Boolean(find(versions, ['id', image])),
versions: reverse(sortBy(versions, ['name']))
})); }));
}, [])
}) const selected = find(images, ['id', image]) || {};
return {
loading,
images: values,
image: {
...selected,
isVm: !includes(selected.type || '', 'DATASET')
}
};
}
}) })
)(ImageContainer); )(ImageContainer);

View File

@ -8,6 +8,7 @@ import { set } from 'react-redux-values';
import sortBy from 'lodash.sortby'; import sortBy from 'lodash.sortby';
import find from 'lodash.find'; import find from 'lodash.find';
import includes from 'lodash.includes'; import includes from 'lodash.includes';
import reverse from 'lodash.reverse';
import constantCase from 'constant-case'; import constantCase from 'constant-case';
import { reset } from 'redux-form'; import { reset } from 'redux-form';
@ -192,7 +193,7 @@ export default compose(
...ownProps, ...ownProps,
sortBy: _sortBy, sortBy: _sortBy,
sortOrder: _sortOrder, sortOrder: _sortOrder,
packages: _sortOrder === 'asc' ? filtered : filtered.reverse(), packages: _sortOrder === 'asc' ? filtered : reverse(filtered),
hasVms: vmSelected, hasVms: vmSelected,
selected: find(packages, ['id', pkgSelected]) selected: find(packages, ['id', pkgSelected])
}; };

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -8,6 +8,7 @@ import forceArray from 'force-array';
import get from 'lodash.get'; import get from 'lodash.get';
import intercept from 'apr-intercept'; import intercept from 'apr-intercept';
import find from 'lodash.find'; import find from 'lodash.find';
import reverse from 'lodash.reverse';
import sort from 'lodash.sortby'; import sort from 'lodash.sortby';
import remcalc from 'remcalc'; import remcalc from 'remcalc';
@ -231,7 +232,7 @@ export default compose(
return { return {
// is sortOrder !== asc, reverse order // is sortOrder !== asc, reverse order
instances: sortOrder === 'asc' ? ascSorted : ascSorted.reverse(), instances: sortOrder === 'asc' ? ascSorted : reverse(ascSorted),
allowedActions, allowedActions,
selected, selected,
statuses, statuses,

View File

@ -5,6 +5,7 @@ import { set } from 'react-redux-values';
import forceArray from 'force-array'; import forceArray from 'force-array';
import { Margin } from 'styled-components-spacing'; import { Margin } from 'styled-components-spacing';
import find from 'lodash.find'; import find from 'lodash.find';
import reverse from 'lodash.reverse';
import sortBy from 'lodash.sortby'; import sortBy from 'lodash.sortby';
import get from 'lodash.get'; import get from 'lodash.get';
@ -82,7 +83,7 @@ export default compose(
const instance = find(forceArray(machines), ['name', name]); const instance = find(forceArray(machines), ['name', name]);
const values = get(instance, 'networks', []); const values = get(instance, 'networks', []);
const networks = sortBy(values, 'public').reverse(); const networks = reverse(sortBy(values, 'public'));
return { return {
networks, networks,

View File

@ -4,6 +4,7 @@ import { connect } from 'react-redux';
import { stopSubmit, startSubmit, change, reset } from 'redux-form'; import { stopSubmit, startSubmit, change, reset } from 'redux-form';
import { compose, graphql } from 'react-apollo'; import { compose, graphql } from 'react-apollo';
import find from 'lodash.find'; import find from 'lodash.find';
import reverse from 'lodash.reverse';
import get from 'lodash.get'; import get from 'lodash.get';
import sort from 'lodash.sortby'; import sort from 'lodash.sortby';
import { set } from 'react-redux-values'; import { set } from 'react-redux-values';
@ -223,7 +224,7 @@ export default compose(
return { return {
...rest, ...rest,
snapshots: sortOrder === 'asc' ? ascSorted : ascSorted.reverse(), snapshots: sortOrder === 'asc' ? ascSorted : reverse(ascSorted),
selected, selected,
sortBy, sortBy,
sortOrder, sortOrder,

View File

@ -107,6 +107,10 @@ const Preview = styled.div`
margin-bottom: ${remcalc(20)}; margin-bottom: ${remcalc(20)};
animation: ${fadeIn} 0.2s ease-in-out; animation: ${fadeIn} 0.2s ease-in-out;
${is('onClick')`
cursor: pointer;
`};
${is('active')` ${is('active')`
border: ${remcalc(1)} solid ${props => props.theme.primaryActive}; border: ${remcalc(1)} solid ${props => props.theme.primaryActive};

View File

@ -6662,6 +6662,10 @@ lodash.find@^4.6.0:
version "4.6.0" version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.find/-/lodash.find-4.6.0.tgz#cb0704d47ab71789ffa0de8b97dd926fb88b13b1" resolved "https://registry.yarnpkg.com/lodash.find/-/lodash.find-4.6.0.tgz#cb0704d47ab71789ffa0de8b97dd926fb88b13b1"
lodash.findindex@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.findindex/-/lodash.findindex-4.6.0.tgz#a3245dee61fb9b6e0624b535125624bb69c11106"
lodash.flatten@^4.4.0: lodash.flatten@^4.4.0:
version "4.4.0" version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f"
@ -6746,6 +6750,10 @@ lodash.pick@^4.4.0:
version "4.4.0" version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3"
lodash.reverse@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/lodash.reverse/-/lodash.reverse-4.0.1.tgz#1f2afedace2e16e660f3aa7c59d3300a6f25d13c"
lodash.some@^4.6.0: lodash.some@^4.6.0:
version "4.6.0" version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d" resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d"