feat(create-instance): revised feedback changes

This commit is contained in:
Sara Vieira 2017-11-20 11:33:50 +00:00 committed by Sérgio Ramos
parent 42b4c6230e
commit 0be8553e29
12 changed files with 383 additions and 175 deletions

View File

@ -173,7 +173,7 @@ const style = css`
`};
${is('bold')`
font-weight: bold;
font-weight: 500;
`};
`;

View File

@ -134,6 +134,7 @@ export {
export {
default as Table,
Thead as TableThead,
ThFooter as TableThFooter,
Tr as TableTr,
Th as TableTh,
Tbody as TableTbody,

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, { isNot } from 'styled-is';
import remcalc from 'remcalc';
import Baseline from '../baseline';
@ -45,7 +45,7 @@ const Column = css`
white-space: nowrap;
box-sizing: border-box;
padding: 0 ${remcalc(18)};
padding: 0 ${remcalc(24)};
height: ${remcalc(60)};
${handleBreakpoint('xs')};
@ -124,22 +124,53 @@ const BaseTable = styled.table`
max-width: 100%;
`;
const BaseThead = styled.thead`
const BaseThFooter = styled.tfoot`
width: 100%;
th:first-child {
border-top-left-radius: ${remcalc(4)};
border-bottom-left-radius: ${remcalc(4)};
}
th:last-child {
border-top-right-radius: ${remcalc(4)};
border-bottom-right-radius: ${remcalc(4)};
}
th {
border-bottom-width: 0;
border-top-width: 0;
}
`;
const BaseThead = styled.thead`
width: 100%;
${is('footer')`
th:first-child {
border-bottom-left-radius: ${remcalc(4)};
}
th:last-child {
border-bottom-right-radius: ${remcalc(4)};
}
th {
border-top-width: 0;
}
`};
${isNot('footer')`
th:first-child {
border-top-left-radius: ${remcalc(4)};
}
th:last-child {
border-top-right-radius: ${remcalc(4)};
}
th {
border-bottom-width: 0;
}
`};
`;
const BaseTbody = styled.tbody`
width: 100%;
@ -167,6 +198,7 @@ const BaseTh = styled.th`
${is('selected')`
color: ${props => props.theme.text};
font-weight: bold;
`};
&:not(:first-child) {
@ -186,7 +218,6 @@ const BaseTh = styled.th`
const BaseTd = styled.td`
${Column};
transition: all 200ms ease;
border-bottom-width: 0;
vertical-align: middle;
@ -279,6 +310,16 @@ export const Thead = Baseline(({ children, ...rest }) => (
</Propagate>
));
export const ThFooter = Baseline(({ children, ...rest }) => (
<Propagate {...rest} header={true}>
{value => (
<BaseThFooter {...value} name="thfoot">
{children}
</BaseThFooter>
)}
</Propagate>
));
export const Tr = Baseline(({ children, ...rest }) => (
<Propagate {...rest}>
{value => (

View File

@ -104,7 +104,7 @@ class Affinity extends Component {
<P>
Affinity rules control the location of instances, to help reduce
traffic across networks and keep the workload balanced. With strict
rules, instances are only provisioned when the criteria is met.{' '}
rules, instances are only provisioned when the criteria is met. {' '}
<a href="https://apidocs.joyent.com/docker/features/placement ">
Read the docs
</a>
@ -122,85 +122,82 @@ class Affinity extends Component {
</ViewContainer>,
<Row>
<Col xs={12}>
{this.state.showRuleCreation ? (
<Margin top={2}>
<Card shadow>
<CardHeader secondary={false} transparent={false}>
<CardHeaderMeta>
<Row between="xs" middle="xs">
<Col xs={12}>
<H4>Create an affinity rule</H4>
</Col>
</Row>
</CardHeaderMeta>
</CardHeader>
<CardOutlet>
<div>
<H4>The instance</H4>
</div>
<div>
<Select fluid onChange={this.instanceChange}>
<option>must</option>
<option>should</option>
{this.state.showRuleCreation ? (
<Margin top={2}>
<Card shadow>
<CardHeader secondary={false} transparent={false}>
<CardHeaderMeta>
<Row between="xs" middle="xs">
<Col xs={12}>
<H4>Create an affinity rule</H4>
</Col>
</Row>
</CardHeaderMeta>
</CardHeader>
<CardOutlet>
<div>
<H4>The instance</H4>
</div>
<div>
<Select fluid onChange={this.instanceChange}>
<option>must</option>
<option>should</option>
</Select>
</div>
<div>
<H4>be on</H4>
</div>
<div>
<Select fluid onChange={this.beChange}>
<option>the same</option>
<option>a different</option>
</Select>
</div>
<div>
<H4>node as the instance(s) identified by the</H4>
</div>
<div>
<MarginInline right={1}>
<Select fluid onChange={this.typeChange}>
<option>instance name</option>
<option>tag name</option>
</Select>
</div>
<div>
<H4>be on</H4>
</div>
<div>
<Select fluid onChange={this.beChange}>
<option>the same</option>
<option>a different</option>
</MarginInline>
<MarginInline right={1}>
<Select fluid onChange={this.typeChange}>
<option>equalling</option>
<option>not equalling</option>
<option>containing</option>
<option>starting with</option>
<option>ending with</option>
</Select>
</div>
<div>
<H4>node as the instance(s) identified by the</H4>
</div>
<div>
<MarginInline right={1}>
<Select fluid onChange={this.typeChange}>
<option>instance name</option>
<option>tag name</option>
</Select>
</MarginInline>
<MarginInline right={1}>
<Select fluid onChange={this.typeChange}>
<option>equalling</option>
<option>not equalling</option>
<option>containing</option>
<option>starting with</option>
<option>ending with</option>
</Select>
</MarginInline>
<Input
type="text"
onChange={this.valueChange}
required
value={this.state.rule.value}
placeholder="Example instance name: nginx"
/>
</div>
<div>
<Button secondary onClick={this.toggleForm}>
Cancel
</Button>
<Button
onClick={this.submit}
disabled={!this.state.rule.value}
>
Create
</Button>
</div>
</CardOutlet>
</Card>
</Margin>
) : (
<Margin top={2}>
<Button secondary bold onClick={this.toggleForm}>
Create affinity rule
</Button>
</Margin>
)}
</MarginInline>
<Input
type="text"
onChange={this.valueChange}
required
value={this.state.rule.value}
placeholder="Example instance name: nginx"
/>
</div>
<div>
<Button secondary onClick={this.toggleForm}>
Cancel
</Button>
<Button onClick={this.submit} disabled={!this.state.rule.value}>
Create
</Button>
</div>
</CardOutlet>
</Card>
</Margin>
) : (
<Margin top={2}>
<Button secondary bold onClick={this.toggleForm}>
Create affinity rule
</Button>
</Margin>
)}
</Col>
</Row>
];

View File

@ -17,8 +17,7 @@ import {
Label,
H2,
H4,
P,
ViewContainer
P
} from 'joyent-ui-toolkit';
const FullWidth = styled(Margin)`
@ -38,7 +37,8 @@ class Filters extends Component {
ram,
cpu,
disk,
cost
cost,
reset: 0
};
}
@ -56,7 +56,8 @@ class Filters extends Component {
ram,
cpu,
disk,
cost
cost,
reset: this.state.reset + 1
});
};
@ -122,7 +123,8 @@ class Filters extends Component {
costChange={value => costChange(value)}
filters={filters}
disabled={isEqual(filters, defaultState.filters)}
onClick={this.handleResetClick}
onResetClick={this.handleResetClick}
reset={this.state.reset}
/>
</FullWidth>
];

View File

@ -1,5 +1,6 @@
import React, { Component } from 'react';
import styled from 'styled-components';
import remcalc from 'remcalc';
import {
FormGroup,
FormLabel,
@ -9,52 +10,92 @@ import {
InputDropdown
} from 'joyent-ui-toolkit';
import { Row, Col } from 'react-styled-flexboxgrid';
import { Padding } from 'styled-components-spacing';
import { Padding, Margin } from 'styled-components-spacing';
const RowFullWidth = styled(Row)`
width: 100%;
`;
const InputDropdownBorder = styled(InputDropdown)`
border-right: 1px solid ${props => props.theme.grey};
margin-right: 24px;
padding-right: 24px;
const Divider = styled.div`
width: ${remcalc(1)};
background: ${props => props.theme.grey};
height: ${remcalc(66)};
margin: 0 ${remcalc(14)};
margin-bottom: ${remcalc(9)};
display: flex;
align-self: flex-end;
`;
const valuesToSend = (changed, target, value, name) => ({
const isToBeMultiplied = (name, state, target) =>
(name === 'ram' && state[name][`${target}Selected`] === 'MB') ||
(name === 'disk' && state[name][`${target}Selected`] === 'GB');
const valuesToSend = (changed, target, value) => ({
min: !isNaN(parseFloat(changed.min)) ? parseFloat(changed.min) : 0,
max: !isNaN(parseFloat(changed.max)) ? parseFloat(changed.max) : 0,
[target]: parseFloat(value)
});
const ramLogic = ram => ({
min: ram.min > 1 ? ram.min : ram.min * 1000,
minSelected: 'MB',
max: ram.max > 1 ? ram.max : ram.max * 1000,
maxSelected: 'GB'
});
const diskLogic = disk => ({
min: disk.min > 1 ? disk.min : disk.min * 1000,
minSelected: 'GB',
max: disk.max > 1 ? disk.max : disk.max * 1000,
maxSelected: 'TB'
});
class Inputs extends Component {
constructor(props) {
super(props);
const { filters: { cpu, cost, ram, disk } } = this.props;
const { filters: { cpu, cost, ram, disk }, reset } = this.props;
this.state = {
ram: {
min: ram.min > 1 ? ram.min : ram.min * 1000,
minSelected: 'MB',
max: ram.max > 1 ? ram.max : ram.max * 1000,
maxSelected: 'GB'
},
ram: ramLogic(ram),
cpu,
disk: {
min: disk.min > 1 ? disk.min : disk.min * 1000,
minSelected: 'GB',
max: disk.max > 1 ? disk.max : disk.max * 1000,
maxSelected: 'TB'
},
cost
disk: diskLogic(disk),
cost,
reset
};
}
componentWillReceiveProps = nextProps => {
const { filters: { cpu, cost, ram, disk }, reset } = nextProps;
if (reset !== this.state.reset) {
this.setState({
ram: ramLogic(ram),
cpu,
disk: diskLogic(disk),
cost,
reset
});
}
};
handleChange = (e, name, target) => {
const changed = this.state[name];
const value = (e.target || {}).value;
setTimeout(() => {
this.props[`${name}Change`](valuesToSend(changed, target, value, name));
this.props[`${name}Change`](
valuesToSend(
{
min: isToBeMultiplied(name, this.state, 'min')
? changed.min / 1000
: changed.min,
max: isToBeMultiplied(name, this.state, 'max')
? changed.max / 1000
: changed.max
},
target,
isToBeMultiplied(name, this.state, target) ? value / 1000 : value
)
);
}, 1000);
this.setState({
@ -68,6 +109,8 @@ class Inputs extends Component {
handleSelectChange = (e, name, target, valueTarget) => {
const value = (e.target || {}).value;
const isToBeMultiplied =
(name === 'ram' && value === 'MB') || (name === 'disk' && value === 'GB');
this.setState({
...this.state,
[name]: {
@ -75,13 +118,36 @@ class Inputs extends Component {
[target]: value
}
});
this.props[`${name}Change`](
valuesToSend(
this.state[name],
valueTarget,
isToBeMultiplied
? this.state[name][valueTarget] / 1000
: this.state[name][valueTarget]
)
);
};
handleBlur = (e, name, target) => {
const changed = this.state[name];
const value = (e.target || {}).value;
this.props[`${name}Change`](valuesToSend(changed, target, value, name));
this.props[`${name}Change`](
valuesToSend(
{
min: isToBeMultiplied(name, this.state, 'min')
? changed.min / 1000
: changed.min,
max: isToBeMultiplied(name, this.state, 'max')
? changed.max / 1000
: changed.max
},
target,
isToBeMultiplied(name, this.state, target) ? value / 1000 : value
)
);
this.setState({
...this.state,
@ -94,7 +160,7 @@ class Inputs extends Component {
render() {
const { cpu, cost, ram, disk } = this.state;
const { onClick, disabled } = this.props;
const { onResetClick, disabled } = this.props;
return [
<Row bottom="xs">
@ -124,7 +190,7 @@ class Inputs extends Component {
</Select>
</InputDropdown>
<Padding horizontal={2}>to</Padding>
<InputDropdownBorder>
<InputDropdown>
<Input
wrapped
small
@ -143,9 +209,10 @@ class Inputs extends Component {
<option value="MB">MB</option>
<option value="GB">GB</option>
</Select>
</InputDropdownBorder>
</InputDropdown>
</FormGroup>
</Col>
<Divider />
<Col>
<Padding top={1}>
<FormLabel>Disk</FormLabel>
@ -218,6 +285,7 @@ class Inputs extends Component {
/>
</FormGroup>
</Col>
<Divider />
<Col>
<Padding top={1}>
<FormLabel>$/hour</FormLabel>
@ -243,9 +311,17 @@ class Inputs extends Component {
</RowFullWidth>,
<Row>
<Col xs={12}>
<Button disabled={disabled} secondary small bold onClick={onClick}>
Reset All Filters
</Button>
<Margin vertical={2}>
<Button
disabled={disabled}
secondary
small
bold
onClick={onResetClick}
>
Reset All Filters
</Button>
</Margin>
</Col>
</Row>
];

View File

@ -9,7 +9,9 @@ import {
BreadcrumbItem,
Anchor,
Button,
Divider
Divider,
MessageTitle,
MessageDescription
} from 'joyent-ui-toolkit';
class Home extends Component {
@ -47,7 +49,6 @@ class Home extends Component {
selected: values[key] ? values[key] : false
}));
console.table(groups);
onFilterChange({
...filters,
groups
@ -60,9 +61,12 @@ class Home extends Component {
const { filters, onFilterReset, packages } = this.props;
const _msg = showMessage ? (
<Message onCloseClick={this.closeMessage}>
Not all data centres have all configurations of instances available.
Make sure that you choose the data center that suits your requirements.{' '}
<Anchor href="#">Learn More</Anchor>
<MessageTitle>Choosing deployment data center</MessageTitle>
<MessageDescription>
Not all data centres have all configurations of instances available.
Make sure that you choose the data center that suits your
requirements. <Anchor href="#">Learn More</Anchor>
</MessageDescription>
</Message>
) : null;
@ -103,10 +107,12 @@ class Home extends Component {
</Margin>,
<Row end="xs">
<Col xs={12}>
<Button>Next</Button>
<Margin top={5}>
<Button>Next</Button>
</Margin>
</Col>
</Row>,
<Margin top={2}>
<Margin top={5}>
<AffinityHOC />
</Margin>
];

View File

@ -40,7 +40,7 @@ export const returnIcon = group => {
<IconWrapper>
<TooltipContainer hoverable>
<TooltipTarget>
<Margin right={1}>
<Margin horizontal={1}>
<Flex>{icon}</Flex>
</Margin>
</TooltipTarget>

View File

@ -1,5 +1,7 @@
import React from 'react';
import { Link } from 'react-router-dom';
import styled from 'styled-components';
import remcalc from 'remcalc';
import {
Header,
@ -11,11 +13,15 @@ import {
UserIconLight
} from 'joyent-ui-toolkit';
const HeaderBrandStyled = styled(HeaderBrand)`
margin-top: ${remcalc(6)};
`;
const NavHeader = () => (
<Header>
<HeaderBrand beta>
<HeaderBrandStyled>
<TritonBetaIcon />
</HeaderBrand>
</HeaderBrandStyled>
<HeaderNav>
<li>
<Link className="active" to="/">

View File

@ -1,10 +1,18 @@
import React from 'react';
import { reduxForm } from 'redux-form';
import styled from 'styled-components';
import remcalc from 'remcalc';
import { returnIcon } from '../icons';
import { TableTr, TableTd, H4, Radio, FormGroup } from 'joyent-ui-toolkit';
const FormGroupStyled = styled(FormGroup)`
float: left;
margin-right: ${remcalc(24)};
margin-top: ${remcalc(3)};
`;
const Package = ({
pack: { price, memory, vcpus, disk, group, ssd, name },
selected,
@ -12,20 +20,18 @@ const Package = ({
}) => (
<TableTr selected={selected}>
<TableTd>
<FormGroup name={name}>
<FormGroupStyled name={name}>
<Radio onClick={onClick} name={name} value={name} checked={selected} />
</FormGroup>
</TableTd>
<TableTd>
</FormGroupStyled>
{returnIcon(group)}
<H4>{name}</H4>
</TableTd>
<TableTd>
<TableTd right>
{memory > 1 ? `${parseInt(memory, 10)} GB` : `${memory * 1000} MB`}
</TableTd>
<TableTd>{disk > 1 ? `${disk} TB` : `${disk * 1000} GB`}</TableTd>
<TableTd>{vcpus}</TableTd>
<TableTd>{price.toFixed(3)}</TableTd>
<TableTd right>{disk > 1 ? `${disk} TB` : `${disk * 1000} GB`}</TableTd>
<TableTd right>{vcpus}</TableTd>
<TableTd right>{price.toFixed(3)}</TableTd>
</TableTr>
);

View File

@ -12,6 +12,7 @@ import {
Table,
TableThead,
TableTbody,
TableThFooter,
TableTr,
TableTh,
ArrowIcon
@ -21,18 +22,15 @@ const ArrowIconStyled = styled(ArrowIcon)`
margin-left: ${remcalc(6)};
cursor: pointer;
position: relative;
top: ${remcalc(-1)};
top: ${remcalc(-3)};
transition: transform 200ms ease;
path {
fill: ${props => props.theme.grey};
fill: ${props => props.theme.text};
}
${is('selected', 'down')`
transform: rotate(180deg);
path {
fill: ${props => props.theme.text};
}
`};
`;
@ -46,8 +44,10 @@ class Packages extends Component {
super(props);
this.state = {
packages: props.packages,
selected: null
packages: props.packages.sort((a, b) => (a.price > b.price ? 1 : -1)),
selected: null,
price: true,
ordered: 'price'
};
}
@ -84,56 +84,65 @@ class Packages extends Component {
<Table>
<TableThead>
<TableTr>
<TableTh xs="40" />
<TableTh selected={ordered === 'name'}>
<Span role="button" onClick={() => this.order('name')}>
Name{' '}
</Span>
<ArrowIconStyled
selected={ordered === 'name'}
down={this.state.name}
onClick={() => this.order('name')}
/>
{ordered === 'name' && (
<ArrowIconStyled
selected
down={this.state.name}
onClick={() => this.order('name')}
/>
)}
</TableTh>
<TableTh right xs="100" selected={ordered === 'memory'}>
<Span role="button" onClick={() => this.order('memory')}>
RAM{' '}
</Span>
<ArrowIconStyled
selected={ordered === 'memory'}
down={this.state.memory}
onClick={() => this.order('memory')}
/>
{ordered === 'memory' && (
<ArrowIconStyled
selected
down={this.state.memory}
onClick={() => this.order('memory')}
/>
)}
</TableTh>
<TableTh right xs="100" selected={ordered === 'disk'}>
<Span role="button" onClick={() => this.order('disk')}>
Disk{' '}
</Span>
<ArrowIconStyled
selected={ordered === 'disk'}
down={this.state.disk}
onClick={() => this.order('disk')}
/>
{ordered === 'disk' && (
<ArrowIconStyled
selected
down={this.state.disk}
onClick={() => this.order('disk')}
/>
)}
</TableTh>
<TableTh right xs="100" selected={ordered === 'vcpus'}>
<Span role="button" onClick={() => this.order('vcpus')}>
vCPU{' '}
</Span>
<ArrowIconStyled
selected={ordered === 'vcpus'}
down={this.state.vcpus}
onClick={() => this.order('vcpus')}
/>
{ordered === 'vcpus' && (
<ArrowIconStyled
selected
down={this.state.vcpus}
onClick={() => this.order('vcpus')}
/>
)}
</TableTh>
<TableTh right xs="100" selected={ordered === 'price'}>
<Span role="button" onClick={() => this.order('price')}>
$/hour{' '}
</Span>
<ArrowIconStyled
selected={ordered === 'price'}
down={this.state.price}
onClick={() => this.order('price')}
/>
{ordered === 'price' && (
<ArrowIconStyled
selected={ordered === 'price'}
down={this.state.price}
onClick={() => this.order('price')}
/>
)}
</TableTh>
</TableTr>
</TableThead>
@ -147,12 +156,76 @@ class Packages extends Component {
/>
))}
</TableTbody>
<TableThFooter>
<TableTr>
<TableTh selected={ordered === 'name'}>
<Span role="button" onClick={() => this.order('name')}>
Name{' '}
</Span>
{ordered === 'name' && (
<ArrowIconStyled
selected
down={this.state.name}
onClick={() => this.order('name')}
/>
)}
</TableTh>
<TableTh right xs="100" selected={ordered === 'memory'}>
<Span role="button" onClick={() => this.order('memory')}>
RAM{' '}
</Span>
{ordered === 'memory' && (
<ArrowIconStyled
selected
down={this.state.memory}
onClick={() => this.order('memory')}
/>
)}
</TableTh>
<TableTh right xs="100" selected={ordered === 'disk'}>
<Span role="button" onClick={() => this.order('disk')}>
Disk{' '}
</Span>
{ordered === 'disk' && (
<ArrowIconStyled
selected
down={this.state.disk}
onClick={() => this.order('disk')}
/>
)}
</TableTh>
<TableTh right xs="100" selected={ordered === 'vcpus'}>
<Span role="button" onClick={() => this.order('vcpus')}>
vCPU{' '}
</Span>
{ordered === 'vcpus' && (
<ArrowIconStyled
selected
down={this.state.vcpus}
onClick={() => this.order('vcpus')}
/>
)}
</TableTh>
<TableTh right xs="100" selected={ordered === 'price'}>
<Span role="button" onClick={() => this.order('price')}>
$/hour{' '}
</Span>
{ordered === 'price' && (
<ArrowIconStyled
selected={ordered === 'price'}
down={this.state.price}
onClick={() => this.order('price')}
/>
)}
</TableTh>
</TableTr>
</TableThFooter>
</Table>
</Col>
</Row>
) : (
<Row>
<Col>
<Col xs={12}>
<Empty />
</Col>
</Row>

View File

@ -50,11 +50,11 @@ export const store = createStore(
}),
state, // Initial state
compose(
applyMiddleware(client.middleware())
applyMiddleware(client.middleware()),
// If you are using the devToolsExtension, you can add it here also
// eslint-disable-next-line no-negated-condition
// typeof GLOBAL.__REDUX_DEVTOOLS_EXTENSION__ !== 'undefined'
// ? GLOBAL.__REDUX_DEVTOOLS_EXTENSION__()
// : f => f
typeof GLOBAL.__REDUX_DEVTOOLS_EXTENSION__ !== 'undefined'
? GLOBAL.__REDUX_DEVTOOLS_EXTENSION__()
: f => f
)
);