style: format
This commit is contained in:
parent
d1af5eec1a
commit
cac9453154
@ -16,11 +16,13 @@ module.exports = {
|
||||
const { list, get } = api.users;
|
||||
|
||||
return args.id
|
||||
? get(args).then(user => [user]).then(user =>
|
||||
Object.assign(user, {
|
||||
isUser: true
|
||||
})
|
||||
)
|
||||
? get(args)
|
||||
.then(user => [user])
|
||||
.then(user =>
|
||||
Object.assign(user, {
|
||||
isUser: true
|
||||
})
|
||||
)
|
||||
: list();
|
||||
}
|
||||
};
|
||||
|
@ -17,9 +17,7 @@ class App extends Component {
|
||||
render() {
|
||||
return (
|
||||
<ApolloProvider client={client} store={store}>
|
||||
<ThemeProvider theme={theme}>
|
||||
{Router}
|
||||
</ThemeProvider>
|
||||
<ThemeProvider theme={theme}>{Router}</ThemeProvider>
|
||||
</ApolloProvider>
|
||||
);
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ const DeploymentGroupDelete = ({
|
||||
deploymentGroup,
|
||||
onCancelClick = () => {},
|
||||
onConfirmClick = () => {}
|
||||
}) =>
|
||||
}) => (
|
||||
<div>
|
||||
<ModalHeading>
|
||||
Deleting a deployment group: <br /> {deploymentGroup.name}
|
||||
@ -20,7 +20,8 @@ const DeploymentGroupDelete = ({
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={onConfirmClick}>Delete deployment group</Button>
|
||||
</div>;
|
||||
</div>
|
||||
);
|
||||
|
||||
DeploymentGroupDelete.propTypes = {
|
||||
deploymentGroup: PropTypes.object.isRequired,
|
||||
|
@ -3,9 +3,10 @@ import React from 'react';
|
||||
import { Col, Row } from 'react-styled-flexboxgrid';
|
||||
import { P } from 'joyent-ui-toolkit';
|
||||
|
||||
export default () =>
|
||||
export default () => (
|
||||
<Row>
|
||||
<Col xs={12}>
|
||||
<P>You don't have any instances</P>
|
||||
</Col>
|
||||
</Row>;
|
||||
</Row>
|
||||
);
|
||||
|
@ -65,7 +65,16 @@ const StyledCard = Card.extend`
|
||||
|
||||
background-color: ${props => props.theme.white};
|
||||
|
||||
${isOr('stopping', 'stopped', 'offline', 'destroyed', 'failed', 'deleted', 'incomplete', 'unknown')`
|
||||
${isOr(
|
||||
'stopping',
|
||||
'stopped',
|
||||
'offline',
|
||||
'destroyed',
|
||||
'failed',
|
||||
'deleted',
|
||||
'incomplete',
|
||||
'unknown'
|
||||
)`
|
||||
background-color: ${props => props.theme.background};
|
||||
|
||||
& [name="card-options"] > button {
|
||||
@ -80,7 +89,6 @@ const InstanceCard = ({
|
||||
onStatusMouseOver = () => {},
|
||||
onMouseOut = () => {}
|
||||
}) => {
|
||||
|
||||
const statusProps = STATUSES.reduce(
|
||||
(acc, name) =>
|
||||
Object.assign(acc, {
|
||||
@ -92,42 +100,34 @@ const InstanceCard = ({
|
||||
const label = (instance.healthy || 'UNKNOWN').toLowerCase();
|
||||
const icon = <HealthyIcon healthy={instance.healthy} />;
|
||||
|
||||
const handleHealthMouseOver = (evt) => {
|
||||
const handleHealthMouseOver = evt => {
|
||||
onHealthMouseOver(evt, instance);
|
||||
}
|
||||
};
|
||||
|
||||
const handleStatusMouseOver = (evt) => {
|
||||
const handleStatusMouseOver = evt => {
|
||||
onStatusMouseOver(evt, instance);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseOut = (evt) => {
|
||||
const handleMouseOut = evt => {
|
||||
onMouseOut(evt);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledCard collapsed={true} key={instance.uuid} {...statusProps}>
|
||||
<CardView>
|
||||
<CardTitle>
|
||||
{instance.name}
|
||||
</CardTitle>
|
||||
<CardTitle>{instance.name}</CardTitle>
|
||||
<CardDescription>
|
||||
<div
|
||||
onMouseOver={handleHealthMouseOver}
|
||||
onMouseOut={handleMouseOut}
|
||||
>
|
||||
<div onMouseOver={handleHealthMouseOver} onMouseOut={handleMouseOut}>
|
||||
<CardInfo
|
||||
icon={icon}
|
||||
iconPosition='left'
|
||||
iconPosition="left"
|
||||
label={label}
|
||||
color='dark'
|
||||
color="dark"
|
||||
/>
|
||||
</div>
|
||||
</CardDescription>
|
||||
<CardDescription>
|
||||
<div
|
||||
onMouseOver={handleStatusMouseOver}
|
||||
onMouseOut={handleMouseOut}
|
||||
>
|
||||
<div onMouseOver={handleStatusMouseOver} onMouseOut={handleMouseOut}>
|
||||
<Label>
|
||||
<Dot {...statusProps} />
|
||||
{titleCase(instance.status)}
|
||||
@ -136,7 +136,7 @@ const InstanceCard = ({
|
||||
</CardDescription>
|
||||
</CardView>
|
||||
</StyledCard>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
InstanceCard.propTypes = {
|
||||
|
@ -143,31 +143,29 @@ class ManifestEditorBundle extends Component {
|
||||
const { ManifestEditor } = this.state;
|
||||
const { children, ...rest } = this.props;
|
||||
|
||||
return (
|
||||
<ManifestEditor {...rest}>
|
||||
{children}
|
||||
</ManifestEditor>
|
||||
);
|
||||
return <ManifestEditor {...rest}>{children}</ManifestEditor>;
|
||||
}
|
||||
}
|
||||
|
||||
const MEditor = ({ input, defaultValue, readOnly }) =>
|
||||
const MEditor = ({ input, defaultValue, readOnly }) => (
|
||||
<ManifestEditorBundle
|
||||
mode="yaml"
|
||||
{...input}
|
||||
value={input.value || defaultValue}
|
||||
readOnly={readOnly}
|
||||
/>;
|
||||
/>
|
||||
);
|
||||
|
||||
const EEditor = ({ input, defaultValue, readOnly }) =>
|
||||
const EEditor = ({ input, defaultValue, readOnly }) => (
|
||||
<ManifestEditorBundle
|
||||
mode="ini"
|
||||
{...input}
|
||||
value={input.value || defaultValue}
|
||||
readOnly={readOnly}
|
||||
/>;
|
||||
/>
|
||||
);
|
||||
|
||||
export const Name = ({ handleSubmit, onCancel, dirty }) =>
|
||||
export const Name = ({ handleSubmit, onCancel, dirty }) => (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<Row>
|
||||
<Col xs={12} md={3} lg={3}>
|
||||
@ -185,7 +183,8 @@ export const Name = ({ handleSubmit, onCancel, dirty }) =>
|
||||
Next
|
||||
</Button>
|
||||
</ButtonsRow>
|
||||
</form>;
|
||||
</form>
|
||||
);
|
||||
|
||||
export const Manifest = ({
|
||||
handleSubmit,
|
||||
@ -193,7 +192,7 @@ export const Manifest = ({
|
||||
dirty,
|
||||
defaultValue = '',
|
||||
loading
|
||||
}) =>
|
||||
}) => (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<Field name="manifest" defaultValue={defaultValue} component={MEditor} />
|
||||
<ButtonsRow>
|
||||
@ -208,30 +207,31 @@ export const Manifest = ({
|
||||
Environment
|
||||
</Button>
|
||||
</ButtonsRow>
|
||||
</form>;
|
||||
</form>
|
||||
);
|
||||
|
||||
const File = ({ id, name, value, onRemoveFile, readOnly }) => {
|
||||
const removeButton = !readOnly
|
||||
? <FilenameRemove type="button" onClick={onRemoveFile} secondary>
|
||||
Remove
|
||||
</FilenameRemove>
|
||||
: null;
|
||||
const removeButton = !readOnly ? (
|
||||
<FilenameRemove type="button" onClick={onRemoveFile} secondary>
|
||||
Remove
|
||||
</FilenameRemove>
|
||||
) : null;
|
||||
|
||||
const fileEditor = !readOnly
|
||||
? <Field
|
||||
name={`file-value-${id}`}
|
||||
defaultValue={value}
|
||||
component={EEditor}
|
||||
/>
|
||||
: <EEditor input={{ value }} readOnly />;
|
||||
const fileEditor = !readOnly ? (
|
||||
<Field name={`file-value-${id}`} defaultValue={value} component={EEditor} />
|
||||
) : (
|
||||
<EEditor input={{ value }} readOnly />
|
||||
);
|
||||
|
||||
const input = !readOnly
|
||||
? <FilenameInput type="text" placeholder="Filename including extension…" />
|
||||
: <FilenameInput
|
||||
type="text"
|
||||
placeholder="Filename including extension…"
|
||||
value={name}
|
||||
/>;
|
||||
const input = !readOnly ? (
|
||||
<FilenameInput type="text" placeholder="Filename including extension…" />
|
||||
) : (
|
||||
<FilenameInput
|
||||
type="text"
|
||||
placeholder="Filename including extension…"
|
||||
value={name}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<FileCard>
|
||||
@ -247,15 +247,15 @@ const File = ({ id, name, value, onRemoveFile, readOnly }) => {
|
||||
};
|
||||
|
||||
const Files = ({ files, onAddFile, onRemoveFile, readOnly }) => {
|
||||
const footer = !readOnly
|
||||
? <Button type="button" onClick={onAddFile} secondary>
|
||||
Create new .env file
|
||||
</Button>
|
||||
: null;
|
||||
const footer = !readOnly ? (
|
||||
<Button type="button" onClick={onAddFile} secondary>
|
||||
Create new .env file
|
||||
</Button>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<div>
|
||||
{files.map(({ id, ...rest }) =>
|
||||
{files.map(({ id, ...rest }) => (
|
||||
<File
|
||||
key={id}
|
||||
id={id}
|
||||
@ -263,7 +263,7 @@ const Files = ({ files, onAddFile, onRemoveFile, readOnly }) => {
|
||||
readOnly={readOnly}
|
||||
{...rest}
|
||||
/>
|
||||
)}
|
||||
))}
|
||||
{footer}
|
||||
</div>
|
||||
);
|
||||
@ -280,30 +280,28 @@ export const Environment = ({
|
||||
readOnly = false,
|
||||
loading
|
||||
}) => {
|
||||
const envEditor = !readOnly
|
||||
? <Field
|
||||
name="environment"
|
||||
defaultValue={defaultValue}
|
||||
component={EEditor}
|
||||
/>
|
||||
: <EEditor input={{ value: defaultValue }} readOnly />;
|
||||
const envEditor = !readOnly ? (
|
||||
<Field name="environment" defaultValue={defaultValue} component={EEditor} />
|
||||
) : (
|
||||
<EEditor input={{ value: defaultValue }} readOnly />
|
||||
);
|
||||
|
||||
const footerDivider = !readOnly ? <EnvironmentDivider /> : null;
|
||||
|
||||
const footer = !readOnly
|
||||
? <ButtonsRow>
|
||||
<Button type="button" onClick={onCancel} secondary>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
disabled={!(dirty || !loading || defaultValue.length)}
|
||||
loading={loading}
|
||||
type="submit"
|
||||
>
|
||||
Continue
|
||||
</Button>
|
||||
</ButtonsRow>
|
||||
: null;
|
||||
const footer = !readOnly ? (
|
||||
<ButtonsRow>
|
||||
<Button type="button" onClick={onCancel} secondary>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
disabled={!(dirty || !loading || defaultValue.length)}
|
||||
loading={loading}
|
||||
type="submit"
|
||||
>
|
||||
Continue
|
||||
</Button>
|
||||
</ButtonsRow>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
@ -348,18 +346,18 @@ export const Review = ({
|
||||
environmentToggles,
|
||||
...state
|
||||
}) => {
|
||||
const serviceList = forceArray(state.services).map(({ name, config }) =>
|
||||
const serviceList = forceArray(state.services).map(({ name, config }) => (
|
||||
<ServiceCard key={name}>
|
||||
<ServiceName>
|
||||
{name}
|
||||
</ServiceName>
|
||||
<ServiceName>{name}</ServiceName>
|
||||
<Dl>
|
||||
<dt>
|
||||
<ImageTitle>Image:</ImageTitle> <Image>{config.image}</Image>
|
||||
</dt>
|
||||
</Dl>
|
||||
{config.environment && config.environment.length ? <ServiceDivider /> : null}
|
||||
{config.environment && config.environment.length ?
|
||||
{config.environment && config.environment.length ? (
|
||||
<ServiceDivider />
|
||||
) : null}
|
||||
{config.environment && config.environment.length ? (
|
||||
<ServiceEnvironmentTitle
|
||||
expanded={environmentToggles[name]}
|
||||
onClick={() => onEnvironmentToggle(name)}
|
||||
@ -370,12 +368,14 @@ export const Review = ({
|
||||
up={environmentToggles[name]}
|
||||
/>
|
||||
</ServiceEnvironmentTitle>
|
||||
: null}
|
||||
{config.environment && config.environment.length && environmentToggles[name]
|
||||
? <EnvironmentReview environment={config.environment} />
|
||||
: null}
|
||||
) : null}
|
||||
{config.environment &&
|
||||
config.environment.length &&
|
||||
environmentToggles[name] ? (
|
||||
<EnvironmentReview environment={config.environment} />
|
||||
) : null}
|
||||
</ServiceCard>
|
||||
);
|
||||
));
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
@ -396,18 +396,18 @@ export const Progress = ({ stage, create, edit }) => {
|
||||
const _nameCompleted = stage !== 'name';
|
||||
const _nameActive = stage === 'name';
|
||||
|
||||
const _name = !create
|
||||
? null
|
||||
: <ProgressbarItem>
|
||||
<ProgressbarButton
|
||||
zIndex="10"
|
||||
completed={_nameCompleted}
|
||||
active={_nameActive}
|
||||
first
|
||||
>
|
||||
Name the group
|
||||
</ProgressbarButton>
|
||||
</ProgressbarItem>;
|
||||
const _name = !create ? null : (
|
||||
<ProgressbarItem>
|
||||
<ProgressbarButton
|
||||
zIndex="10"
|
||||
completed={_nameCompleted}
|
||||
active={_nameActive}
|
||||
first
|
||||
>
|
||||
Name the group
|
||||
</ProgressbarButton>
|
||||
</ProgressbarItem>
|
||||
);
|
||||
|
||||
const _manifestCompleted = ['environment', 'review'].indexOf(stage) >= 0;
|
||||
const _manifestActive = create ? stage === 'manifest' : stage === 'edit';
|
||||
|
@ -2,8 +2,9 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Message } from 'joyent-ui-toolkit';
|
||||
|
||||
const ErrorMessage = ({ title, message = "Ooops, there's been an error" }) =>
|
||||
<Message title={title} message={message} type="ERROR" />;
|
||||
const ErrorMessage = ({ title, message = "Ooops, there's been an error" }) => (
|
||||
<Message title={title} message={message} type="ERROR" />
|
||||
);
|
||||
|
||||
ErrorMessage.propTypes = {
|
||||
title: PropTypes.string,
|
||||
|
@ -27,10 +27,9 @@ const Msg = P.extend`
|
||||
margin-bottom: 0;
|
||||
`;
|
||||
|
||||
export default ({ msg }) =>
|
||||
export default ({ msg }) => (
|
||||
<Container>
|
||||
<Loader />
|
||||
<Msg>
|
||||
{msg || 'Loading...'}
|
||||
</Msg>
|
||||
</Container>;
|
||||
<Msg>{msg || 'Loading...'}</Msg>
|
||||
</Container>
|
||||
);
|
||||
|
@ -7,13 +7,15 @@ const StyledHeading = styled(ModalHeading)`
|
||||
color: ${props => props.theme.red};
|
||||
`;
|
||||
|
||||
const ModalErrorMessage = ({ title, message, onCloseClick }) =>
|
||||
const ModalErrorMessage = ({ title, message, onCloseClick }) => (
|
||||
<div>
|
||||
<StyledHeading>{title}</StyledHeading>
|
||||
<ModalText marginBottom="3">{message}
|
||||
</ModalText>
|
||||
<Button onClick={onCloseClick} secondary>Close </Button>
|
||||
</div>;
|
||||
<ModalText marginBottom="3">{message}</ModalText>
|
||||
<Button onClick={onCloseClick} secondary>
|
||||
Close{' '}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
|
||||
ModalErrorMessage.propTypes = {
|
||||
title: PropTypes.string,
|
||||
|
@ -2,8 +2,9 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Message } from 'joyent-ui-toolkit';
|
||||
|
||||
const WarningMessage = ({ title, message }) =>
|
||||
<Message title={title} message={message} type="WARNING" />;
|
||||
const WarningMessage = ({ title, message }) => (
|
||||
<Message title={title} message={message} type="WARNING" />
|
||||
);
|
||||
|
||||
WarningMessage.propTypes = {
|
||||
title: PropTypes.string,
|
||||
|
@ -24,27 +24,22 @@ const BreadcrumbContainer = styled.div`
|
||||
const getBreadcrumbItems = (...links) =>
|
||||
forceArray(links).map(({ pathname, name }, i) => {
|
||||
const item =
|
||||
i + 1 >= links.length
|
||||
? name
|
||||
: <BreadcrumbLink to={pathname}>
|
||||
{name}
|
||||
</BreadcrumbLink>;
|
||||
i + 1 >= links.length ? (
|
||||
name
|
||||
) : (
|
||||
<BreadcrumbLink to={pathname}>{name}</BreadcrumbLink>
|
||||
);
|
||||
|
||||
return (
|
||||
<BreadcrumbItem key={name}>
|
||||
{item}
|
||||
</BreadcrumbItem>
|
||||
);
|
||||
return <BreadcrumbItem key={name}>{item}</BreadcrumbItem>;
|
||||
});
|
||||
|
||||
const NavBreadcrumb = ({ links = [] }) =>
|
||||
const NavBreadcrumb = ({ links = [] }) => (
|
||||
<BreadcrumbContainer>
|
||||
<Grid>
|
||||
<Breadcrumb>
|
||||
{getBreadcrumbItems(...links)}
|
||||
</Breadcrumb>
|
||||
<Breadcrumb>{getBreadcrumbItems(...links)}</Breadcrumb>
|
||||
</Grid>
|
||||
</BreadcrumbContainer>;
|
||||
</BreadcrumbContainer>
|
||||
);
|
||||
|
||||
NavBreadcrumb.propTypes = {
|
||||
links: PropTypes.arrayOf(
|
||||
|
@ -12,20 +12,17 @@ const StyledLogo = Img.extend`
|
||||
height: ${remcalc(25)};
|
||||
`;
|
||||
|
||||
const NavHeader = ({ datacenter, username }) =>
|
||||
const NavHeader = ({ datacenter, username }) => (
|
||||
<Header>
|
||||
<HeaderBrand>
|
||||
<Link to="/">
|
||||
<StyledLogo src={Logo} />
|
||||
</Link>
|
||||
</HeaderBrand>
|
||||
<HeaderItem>
|
||||
{datacenter}
|
||||
</HeaderItem>
|
||||
<HeaderItem>
|
||||
{username}
|
||||
</HeaderItem>
|
||||
</Header>;
|
||||
<HeaderItem>{datacenter}</HeaderItem>
|
||||
<HeaderItem>{username}</HeaderItem>
|
||||
</Header>
|
||||
);
|
||||
|
||||
NavHeader.propTypes = {
|
||||
datacenter: PropTypes.string,
|
||||
|
@ -11,20 +11,19 @@ import {
|
||||
} from 'joyent-ui-toolkit';
|
||||
|
||||
const getMenuItems = (...links) =>
|
||||
forceArray(links).map(({ pathname, name }) =>
|
||||
forceArray(links).map(({ pathname, name }) => (
|
||||
<SectionListItem key={pathname}>
|
||||
<SectionListNavLink activeClassName="active" to={pathname}>
|
||||
{name}
|
||||
</SectionListNavLink>
|
||||
</SectionListItem>
|
||||
);
|
||||
));
|
||||
|
||||
const Menu = ({ links = [] }) =>
|
||||
const Menu = ({ links = [] }) => (
|
||||
<LayoutContainer plain>
|
||||
<SectionList>
|
||||
{getMenuItems(...links)}
|
||||
</SectionList>
|
||||
</LayoutContainer>;
|
||||
<SectionList>{getMenuItems(...links)}</SectionList>
|
||||
</LayoutContainer>
|
||||
);
|
||||
|
||||
Menu.propTypes = {
|
||||
links: PropTypes.arrayOf(
|
||||
|
@ -39,6 +39,6 @@ NotFound.propTypes = {
|
||||
message: PropTypes.string,
|
||||
link: PropTypes.string,
|
||||
to: PropTypes.string
|
||||
}
|
||||
};
|
||||
|
||||
export default NotFound;
|
||||
|
@ -6,7 +6,7 @@ const ServiceDelete = ({
|
||||
service,
|
||||
onCancelClick = () => {},
|
||||
onConfirmClick = () => {}
|
||||
}) =>
|
||||
}) => (
|
||||
<div>
|
||||
<ModalHeading>
|
||||
Deleting a service: <br /> {service.name}
|
||||
@ -19,7 +19,8 @@ const ServiceDelete = ({
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={onConfirmClick}>Delete service</Button>
|
||||
</div>;
|
||||
</div>
|
||||
);
|
||||
|
||||
ServiceDelete.propTypes = {
|
||||
service: PropTypes.object.isRequired,
|
||||
|
@ -1,3 +1,3 @@
|
||||
export { default as ServiceScale } from './scale';
|
||||
export { default as ServiceDelete } from './delete';
|
||||
export { default as ServiceMetrics} from './metrics';
|
||||
export { default as ServiceMetrics } from './metrics';
|
||||
|
@ -2,13 +2,9 @@ import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { MetricGraph } from 'joyent-ui-toolkit';
|
||||
|
||||
const ServiceMetrics = ({
|
||||
metricsData,
|
||||
graphDurationSeconds
|
||||
}) => {
|
||||
|
||||
const ServiceMetrics = ({ metricsData, graphDurationSeconds }) => {
|
||||
// metricsData should prob be an array rather than an object
|
||||
const metricGraphs = Object.keys(metricsData).map((key) => (
|
||||
const metricGraphs = Object.keys(metricsData).map(key => (
|
||||
// should also have a header, w metric name and number of instances (omit everything else from design for copilot)
|
||||
<MetricGraph
|
||||
key={key}
|
||||
@ -17,19 +13,15 @@ const ServiceMetrics = ({
|
||||
height={292}
|
||||
graphDurationSeconds={graphDurationSeconds}
|
||||
/>
|
||||
))
|
||||
));
|
||||
// This needs layout!!!
|
||||
return (
|
||||
<div>
|
||||
{metricGraphs}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return <div>{metricGraphs}</div>;
|
||||
};
|
||||
|
||||
ServiceMetrics.propTypes = {
|
||||
// metricsData should prob be an array rather than an object
|
||||
metricsData: PropTypes.object.isRequired,
|
||||
graphDurationSeconds: PropTypes.number.isRequired
|
||||
}
|
||||
};
|
||||
|
||||
export default ServiceMetrics;
|
||||
|
@ -15,7 +15,7 @@ const ServiceScale = ({
|
||||
onCancelClick = () => {},
|
||||
invalid,
|
||||
pristine
|
||||
}) =>
|
||||
}) => (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<ModalHeading>
|
||||
Scaling a service: <br />
|
||||
@ -38,7 +38,8 @@ const ServiceScale = ({
|
||||
<Button type="submit" disabled={pristine || invalid} secondary>
|
||||
Scale
|
||||
</Button>
|
||||
</form>;
|
||||
</form>
|
||||
);
|
||||
|
||||
ServiceScale.propTypes = {
|
||||
service: PropTypes.object.isRequired,
|
||||
|
@ -16,7 +16,7 @@ const StyledBox = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
export default () =>
|
||||
export default () => (
|
||||
<LayoutContainer>
|
||||
<Row>
|
||||
<Col>
|
||||
@ -56,4 +56,5 @@ export default () =>
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
</LayoutContainer>;
|
||||
</LayoutContainer>
|
||||
);
|
||||
|
@ -57,27 +57,27 @@ const ServiceListItem = ({
|
||||
: service.instances.length;
|
||||
|
||||
const childrenItems = children.length
|
||||
? children.map(service =>
|
||||
<ServiceListItem
|
||||
key={service.id}
|
||||
deploymentGroup={deploymentGroup}
|
||||
service={service}
|
||||
isChild
|
||||
/>
|
||||
) : null;
|
||||
? children.map(service => (
|
||||
<ServiceListItem
|
||||
key={service.id}
|
||||
deploymentGroup={deploymentGroup}
|
||||
service={service}
|
||||
isChild
|
||||
/>
|
||||
))
|
||||
: null;
|
||||
|
||||
|
||||
const title = isChild
|
||||
? <CardTitle>
|
||||
{service.name}
|
||||
</CardTitle>
|
||||
: <CardTitle>
|
||||
<TitleInnerContainer>
|
||||
<StyledAnchor to={to} secondary active={service.instancesActive}>
|
||||
{service.name}
|
||||
</StyledAnchor>
|
||||
</TitleInnerContainer>
|
||||
</CardTitle>;
|
||||
const title = isChild ? (
|
||||
<CardTitle>{service.name}</CardTitle>
|
||||
) : (
|
||||
<CardTitle>
|
||||
<TitleInnerContainer>
|
||||
<StyledAnchor to={to} secondary active={service.instancesActive}>
|
||||
{service.name}
|
||||
</StyledAnchor>
|
||||
</TitleInnerContainer>
|
||||
</CardTitle>
|
||||
);
|
||||
|
||||
const subtitle = (
|
||||
<CardSubTitle>
|
||||
@ -86,52 +86,47 @@ const ServiceListItem = ({
|
||||
</CardSubTitle>
|
||||
);
|
||||
|
||||
const header = !isChild
|
||||
? <StyledCardHeader>
|
||||
{title}
|
||||
<CardDescription>
|
||||
<CardInfo
|
||||
icon={<InstancesIcon />}
|
||||
iconPosition="left"
|
||||
label={`${instancesCount} ${instancesCount > 1
|
||||
? 'instances'
|
||||
: 'instance'}`}
|
||||
color={!service.instancesActive ? 'disabled' : 'light'}
|
||||
/>
|
||||
</CardDescription>
|
||||
<CardOptions onClick={handleCardOptionsClick} />
|
||||
</StyledCardHeader>
|
||||
: null;
|
||||
const header = !isChild ? (
|
||||
<StyledCardHeader>
|
||||
{title}
|
||||
<CardDescription>
|
||||
<CardInfo
|
||||
icon={<InstancesIcon />}
|
||||
iconPosition="left"
|
||||
label={`${instancesCount} ${instancesCount > 1
|
||||
? 'instances'
|
||||
: 'instance'}`}
|
||||
color={!service.instancesActive ? 'disabled' : 'light'}
|
||||
/>
|
||||
</CardDescription>
|
||||
<CardOptions onClick={handleCardOptionsClick} />
|
||||
</StyledCardHeader>
|
||||
) : null;
|
||||
|
||||
let healthyInfo = null;
|
||||
if(service.instancesActive) {
|
||||
if (service.instancesActive) {
|
||||
const { total, healthy } = service.instancesHealthy;
|
||||
const iconHealthy = total === healthy ? 'HEALTHY' : 'NOT HEALTHY';
|
||||
const icon = <HealthyIcon healthy={iconHealthy} />;
|
||||
const label = `${healthy} of ${total} healthy`;
|
||||
|
||||
healthyInfo = (
|
||||
<CardInfo
|
||||
icon={icon}
|
||||
iconPosition='left'
|
||||
label={label}
|
||||
color='dark'
|
||||
/>
|
||||
)
|
||||
<CardInfo icon={icon} iconPosition="left" label={label} color="dark" />
|
||||
);
|
||||
}
|
||||
|
||||
const view = children.length
|
||||
? <CardGroupView>
|
||||
{childrenItems}
|
||||
</CardGroupView>
|
||||
: <CardView>
|
||||
{isChild && title}
|
||||
{isChild && subtitle}
|
||||
<CardDescription>
|
||||
<Status service={service} />
|
||||
{healthyInfo}
|
||||
</CardDescription>
|
||||
</CardView>;
|
||||
const view = children.length ? (
|
||||
<CardGroupView>{childrenItems}</CardGroupView>
|
||||
) : (
|
||||
<CardView>
|
||||
{isChild && title}
|
||||
{isChild && subtitle}
|
||||
<CardDescription>
|
||||
<Status service={service} />
|
||||
{healthyInfo}
|
||||
</CardDescription>
|
||||
</CardView>
|
||||
);
|
||||
|
||||
return (
|
||||
<Card
|
||||
|
@ -1,6 +1,11 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Tooltip, TooltipButton, TooltipDivider, TooltipList } from 'joyent-ui-toolkit';
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipButton,
|
||||
TooltipDivider,
|
||||
TooltipList
|
||||
} from 'joyent-ui-toolkit';
|
||||
|
||||
const ServicesQuickActions = ({
|
||||
show,
|
||||
@ -11,7 +16,7 @@ const ServicesQuickActions = ({
|
||||
onStopClick = () => {},
|
||||
onStartClick = () => {},
|
||||
onScaleClick = () => {},
|
||||
onDeleteClick = () => {},
|
||||
onDeleteClick = () => {}
|
||||
}) => {
|
||||
if (!show) {
|
||||
return null;
|
||||
@ -46,22 +51,22 @@ const ServicesQuickActions = ({
|
||||
}, null);
|
||||
|
||||
const startService =
|
||||
status === 'RUNNING'
|
||||
? null
|
||||
: <li>
|
||||
<TooltipButton onClick={handleStartClick} disabled={disabled}>
|
||||
Start
|
||||
</TooltipButton>
|
||||
</li>;
|
||||
status === 'RUNNING' ? null : (
|
||||
<li>
|
||||
<TooltipButton onClick={handleStartClick} disabled={disabled}>
|
||||
Start
|
||||
</TooltipButton>
|
||||
</li>
|
||||
);
|
||||
|
||||
const stopService =
|
||||
status === 'STOPPED'
|
||||
? null
|
||||
: <li>
|
||||
<TooltipButton onClick={handleStopClick} disabled={disabled}>
|
||||
Stop
|
||||
</TooltipButton>
|
||||
</li>;
|
||||
status === 'STOPPED' ? null : (
|
||||
<li>
|
||||
<TooltipButton onClick={handleStopClick} disabled={disabled}>
|
||||
Stop
|
||||
</TooltipButton>
|
||||
</li>
|
||||
);
|
||||
|
||||
return (
|
||||
<Tooltip {...position} onBlur={onBlur}>
|
||||
|
@ -37,16 +37,18 @@ const ServiceStatus = ({ service }) => {
|
||||
);
|
||||
});
|
||||
|
||||
return service.transitionalStatus
|
||||
? <StyledStatusContainer>
|
||||
<StatusLoader />
|
||||
<StyledTransitionalStatus>
|
||||
{service.status ? service.status.toLowerCase() : ''}
|
||||
</StyledTransitionalStatus>
|
||||
</StyledStatusContainer>
|
||||
: <StyledStatusContainer>
|
||||
{getInstanceStatuses(service.instanceStatuses)}
|
||||
</StyledStatusContainer>;
|
||||
return service.transitionalStatus ? (
|
||||
<StyledStatusContainer>
|
||||
<StatusLoader />
|
||||
<StyledTransitionalStatus>
|
||||
{service.status ? service.status.toLowerCase() : ''}
|
||||
</StyledTransitionalStatus>
|
||||
</StyledStatusContainer>
|
||||
) : (
|
||||
<StyledStatusContainer>
|
||||
{getInstanceStatuses(service.instanceStatuses)}
|
||||
</StyledStatusContainer>
|
||||
);
|
||||
};
|
||||
|
||||
ServiceStatus.propTypes = {
|
||||
|
@ -5,7 +5,7 @@ import DeploymentGroupDeleteMutation from '@graphql/DeploymentGroupDeleteMutatio
|
||||
import DeploymentGroupQuery from '@graphql/DeploymentGroup.gql';
|
||||
import { Loader, ModalErrorMessage } from '@components/messaging';
|
||||
import { DeploymentGroupDelete as DeploymentGroupDeleteComponent } from '@components/deployment-group';
|
||||
import { Modal } from 'joyent-ui-toolkit'
|
||||
import { Modal } from 'joyent-ui-toolkit';
|
||||
import { withNotFound, GqlPaths } from '@containers/navigation';
|
||||
|
||||
export class DeploymentGroupDelete extends Component {
|
||||
@ -21,7 +21,10 @@ export class DeploymentGroupDelete extends Component {
|
||||
const { history, match, loading, error } = this.props;
|
||||
|
||||
const handleCloseClick = evt => {
|
||||
const closeUrl = match.url.split('/').slice(0, -2).join('/');
|
||||
const closeUrl = match.url
|
||||
.split('/')
|
||||
.slice(0, -2)
|
||||
.join('/');
|
||||
history.replace(closeUrl);
|
||||
};
|
||||
|
||||
@ -37,24 +40,21 @@ export class DeploymentGroupDelete extends Component {
|
||||
return (
|
||||
<Modal width={460} onCloseClick={handleCloseClick}>
|
||||
<ModalErrorMessage
|
||||
title='Ooops!'
|
||||
message='An error occurred while loading your deployment group.'
|
||||
title="Ooops!"
|
||||
message="An error occurred while loading your deployment group."
|
||||
onCloseClick={handleCloseClick}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
const {
|
||||
deploymentGroup,
|
||||
deleteDeploymentGroup
|
||||
} = this.props;
|
||||
const { deploymentGroup, deleteDeploymentGroup } = this.props;
|
||||
|
||||
if (this.state.error) {
|
||||
return (
|
||||
<Modal width={460} onCloseClick={handleCloseClick}>
|
||||
<ModalErrorMessage
|
||||
title='Ooops!'
|
||||
title="Ooops!"
|
||||
message={`An error occurred while attempting to delete the ${deploymentGroup.name} deployment group.`}
|
||||
onCloseClick={handleCloseClick}
|
||||
/>
|
||||
@ -117,7 +117,7 @@ const DeploymentGroupGql = graphql(DeploymentGroupQuery, {
|
||||
const DeploymentGroupDeleteWithData = compose(
|
||||
DeleteDeploymentGroupGql,
|
||||
DeploymentGroupGql,
|
||||
withNotFound([ GqlPaths.DEPLOYMENT_GROUP ])
|
||||
withNotFound([GqlPaths.DEPLOYMENT_GROUP])
|
||||
)(DeploymentGroupDelete);
|
||||
|
||||
export default DeploymentGroupDeleteWithData;
|
||||
|
@ -5,9 +5,10 @@ import { Progress } from '@components/manifest/edit-or-create';
|
||||
import { LayoutContainer } from '@components/layout';
|
||||
import { Title } from '@components/navigation';
|
||||
|
||||
export default ({ match }) =>
|
||||
export default ({ match }) => (
|
||||
<LayoutContainer>
|
||||
<Title>Creating deployment group</Title>
|
||||
<Progress stage={match.params.stage} create />
|
||||
<ManifestEditOrCreate create />
|
||||
</LayoutContainer>;
|
||||
</LayoutContainer>
|
||||
);
|
||||
|
@ -140,20 +140,18 @@ export const DeploymentGroupList = ({
|
||||
);
|
||||
}
|
||||
|
||||
const groups = forceArray(deploymentGroups).map(({ slug, name }) =>
|
||||
const groups = forceArray(deploymentGroups).map(({ slug, name }) => (
|
||||
<Col xs={12} sm={4} md={3} lg={3} key={slug}>
|
||||
<Box>
|
||||
<StyledLink to={`${match.path}/${slug}`}>
|
||||
<ServiceTitle>
|
||||
{name}
|
||||
</ServiceTitle>
|
||||
<ServiceTitle>{name}</ServiceTitle>
|
||||
</StyledLink>
|
||||
<StyledIconButton to={`${match.url}/${slug}/delete`}>
|
||||
<BinIcon />
|
||||
</StyledIconButton>
|
||||
</Box>
|
||||
</Col>
|
||||
);
|
||||
));
|
||||
|
||||
const create = [
|
||||
<Col xs={12} sm={4} md={3} lg={3} key="~create">
|
||||
@ -165,18 +163,16 @@ export const DeploymentGroupList = ({
|
||||
</BoxCreate>
|
||||
</Col>
|
||||
].concat(
|
||||
forceArray(importable).map(({ slug, name }) =>
|
||||
forceArray(importable).map(({ slug, name }) => (
|
||||
<Col xs={12} sm={4} md={3} lg={3} key={slug}>
|
||||
<BoxCreate>
|
||||
<StyledCreateLink to={`${match.path}/~import/${slug}`}>
|
||||
<Oval>⤵</Oval>
|
||||
<CreateTitle>
|
||||
{name}
|
||||
</CreateTitle>
|
||||
<CreateTitle>{name}</CreateTitle>
|
||||
</StyledCreateLink>
|
||||
</BoxCreate>
|
||||
</Col>
|
||||
)
|
||||
))
|
||||
);
|
||||
|
||||
return (
|
||||
@ -218,5 +214,5 @@ export default compose(
|
||||
importable: importableDeploymentGroups
|
||||
})
|
||||
}),
|
||||
withNotFound([ GqlPaths.DEPLOYMENT_GROUP ])
|
||||
withNotFound([GqlPaths.DEPLOYMENT_GROUP])
|
||||
)(DeploymentGroupList);
|
||||
|
@ -20,7 +20,6 @@ export const InstanceList = ({
|
||||
instancesTooltip,
|
||||
toggleInstancesTooltip
|
||||
}) => {
|
||||
|
||||
const _title = <Title>Instances</Title>;
|
||||
|
||||
if (loading && !forceArray(instances).length) {
|
||||
@ -62,15 +61,12 @@ export const InstanceList = ({
|
||||
};
|
||||
|
||||
const handleMouseOver = (evt, instance, type) => {
|
||||
|
||||
const label = evt.currentTarget;
|
||||
const labelRect = label.getBoundingClientRect();
|
||||
const offset = type === 'healthy'
|
||||
? 48 : type === 'status' ? 36 : 0;
|
||||
const offset = type === 'healthy' ? 48 : type === 'status' ? 36 : 0;
|
||||
|
||||
const position = {
|
||||
left:
|
||||
`${window.scrollX + labelRect.left + offset}px`,
|
||||
left: `${window.scrollX + labelRect.left + offset}px`,
|
||||
top: `${window.scrollY + labelRect.bottom}px`
|
||||
};
|
||||
|
||||
@ -78,16 +74,16 @@ export const InstanceList = ({
|
||||
instance,
|
||||
position,
|
||||
type
|
||||
}
|
||||
};
|
||||
|
||||
toggleInstancesTooltip(tooltipData);
|
||||
};
|
||||
|
||||
const handleMouseOut = (evt) => {
|
||||
const handleMouseOut = evt => {
|
||||
toggleInstancesTooltip({ show: false });
|
||||
};
|
||||
|
||||
const instanceList = instances.map((instance, index) =>
|
||||
const instanceList = instances.map((instance, index) => (
|
||||
<InstanceListItem
|
||||
instance={instance}
|
||||
key={instance.id}
|
||||
@ -95,7 +91,7 @@ export const InstanceList = ({
|
||||
onStatusMouseOver={handleStatusMouseOver}
|
||||
onMouseOut={handleMouseOut}
|
||||
/>
|
||||
);
|
||||
));
|
||||
|
||||
const _instances = !instanceList.length ? <EmptyInstances /> : instanceList;
|
||||
|
||||
@ -105,7 +101,7 @@ export const InstanceList = ({
|
||||
{_instances}
|
||||
</LayoutContainer>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const mapStateToProps = (state, ownProps) => ({
|
||||
instancesTooltip: state.ui.instances.tooltip
|
||||
@ -131,7 +127,7 @@ const InstanceListGql = graphql(InstancesQuery, {
|
||||
}
|
||||
};
|
||||
},
|
||||
props: ({ data: { deploymentGroup, loading, error }}) => ({
|
||||
props: ({ data: { deploymentGroup, loading, error } }) => ({
|
||||
deploymentGroup,
|
||||
instances: sortBy(
|
||||
forceArray(
|
||||
@ -151,8 +147,5 @@ const InstanceListGql = graphql(InstancesQuery, {
|
||||
export default compose(
|
||||
UiConnect,
|
||||
InstanceListGql,
|
||||
withNotFound([
|
||||
GqlPaths.DEPLOYMENT_GROUP,
|
||||
GqlPaths.SERVICES
|
||||
])
|
||||
withNotFound([GqlPaths.DEPLOYMENT_GROUP, GqlPaths.SERVICES])
|
||||
)(InstanceList);
|
||||
|
@ -12,37 +12,34 @@ const StyledContainer = styled.div`
|
||||
const healthMessages = {
|
||||
healthy: 'Your instance is operating as expected',
|
||||
unhealthy: 'Your instance is not operating as expected',
|
||||
maintenance: 'You\'ve set your instance to this manually, use the Container Pilot CLI to change',
|
||||
unknown: 'We\'ve connected to your instance but we have no health information',
|
||||
unavailable: 'We cannot connect to your instance',
|
||||
maintenance:
|
||||
"You've set your instance to this manually, use the Container Pilot CLI to change",
|
||||
unknown: "We've connected to your instance but we have no health information",
|
||||
unavailable: 'We cannot connect to your instance'
|
||||
};
|
||||
|
||||
const statusMessages = {
|
||||
running: 'Your instance is operating',
|
||||
provisioning: 'Your instance is downloading dependencies and compiling',
|
||||
ready: 'Your instance finished provisioning and is ready to be run, it\'ll be running soon',
|
||||
ready:
|
||||
"Your instance finished provisioning and is ready to be run, it'll be running soon",
|
||||
stopping: 'Your instance is going to be stopped soon',
|
||||
stopped: 'Your instance isn\'t doing anything, you can start it',
|
||||
stopped: "Your instance isn't doing anything, you can start it",
|
||||
offline: 'We have no idea what this means, do you??????',
|
||||
failed: 'Your instance has crashed',
|
||||
unknown: 'We cannot work out what status your instance is in',
|
||||
unknown: 'We cannot work out what status your instance is in'
|
||||
};
|
||||
|
||||
export const InstancesTooltip = ({
|
||||
instancesTooltip
|
||||
}) => {
|
||||
export const InstancesTooltip = ({ instancesTooltip }) => {
|
||||
if (instancesTooltip.show) {
|
||||
const { type, instance } = instancesTooltip;
|
||||
|
||||
if(instancesTooltip.show) {
|
||||
const {
|
||||
type,
|
||||
instance
|
||||
} = instancesTooltip;
|
||||
|
||||
const message = type === 'healthy'
|
||||
? healthMessages[instance.healthy.toLowerCase()]
|
||||
: type === 'status'
|
||||
? statusMessages[instance.status.toLowerCase()]
|
||||
: '';
|
||||
const message =
|
||||
type === 'healthy'
|
||||
? healthMessages[instance.healthy.toLowerCase()]
|
||||
: type === 'status'
|
||||
? statusMessages[instance.status.toLowerCase()]
|
||||
: '';
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
@ -50,7 +47,7 @@ export const InstancesTooltip = ({
|
||||
<TooltipLabel>{message}</TooltipLabel>
|
||||
</Tooltip>
|
||||
</StyledContainer>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -175,7 +175,9 @@ class DeploymentGroupEditOrCreate extends Component {
|
||||
name.replace(/^\$/, '')
|
||||
);
|
||||
|
||||
const vars = uniq(names).map(name => `\n${name}=`).join('');
|
||||
const vars = uniq(names)
|
||||
.map(name => `\n${name}=`)
|
||||
.join('');
|
||||
|
||||
return `# define your interpolatable variables here\n${vars}`;
|
||||
}
|
||||
|
@ -47,12 +47,12 @@ const Manifest = ({
|
||||
}
|
||||
|
||||
const _notice =
|
||||
deploymentGroup && deploymentGroup.imported && !manifest
|
||||
? <WarningMessage
|
||||
title="Be aware"
|
||||
message="Since this Deployment Group was imported, it doesn't have the initial manifest."
|
||||
/>
|
||||
: null;
|
||||
deploymentGroup && deploymentGroup.imported && !manifest ? (
|
||||
<WarningMessage
|
||||
title="Be aware"
|
||||
message="Since this Deployment Group was imported, it doesn't have the initial manifest."
|
||||
/>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<LayoutContainer>
|
||||
|
@ -11,25 +11,16 @@ export const MetricNames = [
|
||||
export const withServiceMetricsPolling = ({
|
||||
pollingInterval = 1000 // in milliseconds
|
||||
}) => {
|
||||
return (WrappedComponent) => {
|
||||
|
||||
return WrappedComponent => {
|
||||
return class extends Component {
|
||||
|
||||
componentDidMount() {
|
||||
|
||||
this._poll = setInterval(() => {
|
||||
const {
|
||||
loading,
|
||||
error,
|
||||
service,
|
||||
fetchMoreMetrics
|
||||
} = this.props;
|
||||
const { loading, error, service, fetchMoreMetrics } = this.props;
|
||||
|
||||
if(!loading && !error && service) {
|
||||
if (!loading && !error && service) {
|
||||
const previousEnd = service.instances[0].metrics[0].end;
|
||||
fetchMoreMetrics(previousEnd);
|
||||
}
|
||||
|
||||
}, pollingInterval); // TODO this is the polling interval - think about amount is the todo I guess...
|
||||
}
|
||||
|
||||
@ -38,24 +29,28 @@ export const withServiceMetricsPolling = ({
|
||||
}
|
||||
|
||||
render() {
|
||||
return <WrappedComponent {...this.props} />
|
||||
return <WrappedComponent {...this.props} />;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
export const withServiceMetricsGql = ({
|
||||
gqlQuery,
|
||||
graphDurationSeconds,
|
||||
updateIntervalSeconds
|
||||
}) => {
|
||||
|
||||
const getPreviousMetrics = (previousResult, serviceId, instanceId, metricName) => {
|
||||
const getPreviousMetrics = (
|
||||
previousResult,
|
||||
serviceId,
|
||||
instanceId,
|
||||
metricName
|
||||
) => {
|
||||
return previousResult.deploymentGroup.services
|
||||
.find(s => s.id === serviceId).instances
|
||||
.find(i => i.id === instanceId).metrics
|
||||
.find(m => m.name === metricName).metrics;
|
||||
}
|
||||
.find(s => s.id === serviceId)
|
||||
.instances.find(i => i.id === instanceId)
|
||||
.metrics.find(m => m.name === metricName).metrics;
|
||||
};
|
||||
|
||||
const getNextResult = (previousResult, fetchNextResult) => {
|
||||
const deploymentGroup = fetchNextResult.deploymentGroup;
|
||||
@ -69,18 +64,18 @@ export const withServiceMetricsGql = ({
|
||||
metrics: instance.metrics.map(metric => ({
|
||||
...metric,
|
||||
metrics: getPreviousMetrics(
|
||||
previousResult,
|
||||
service.id,
|
||||
instance.id,
|
||||
metric.name
|
||||
).concat(metric.metrics)
|
||||
previousResult,
|
||||
service.id,
|
||||
instance.id,
|
||||
metric.name
|
||||
).concat(metric.metrics)
|
||||
}))
|
||||
}))
|
||||
}))
|
||||
}
|
||||
}
|
||||
};
|
||||
return nextResult;
|
||||
}
|
||||
};
|
||||
|
||||
return graphql(gqlQuery, {
|
||||
options(props) {
|
||||
@ -90,7 +85,10 @@ export const withServiceMetricsGql = ({
|
||||
|
||||
// this is potentially prone to overfetching if we already have data within timeframe and we leave the page then come back to it
|
||||
const end = moment();
|
||||
const start = moment(end).subtract(graphDurationSeconds + updateIntervalSeconds, 'seconds'); // TODO initial amount of data we wanna get - should be the same as what we display + 15 secs
|
||||
const start = moment(end).subtract(
|
||||
graphDurationSeconds + updateIntervalSeconds,
|
||||
'seconds'
|
||||
); // TODO initial amount of data we wanna get - should be the same as what we display + 15 secs
|
||||
|
||||
return {
|
||||
variables: {
|
||||
@ -102,27 +100,34 @@ export const withServiceMetricsGql = ({
|
||||
}
|
||||
};
|
||||
},
|
||||
props: ({ data: { deploymentGroup, loading, error, variables, fetchMore }}) => {
|
||||
|
||||
const fetchMoreMetrics = (previousEnd) => {
|
||||
props: ({
|
||||
data: { deploymentGroup, loading, error, variables, fetchMore }
|
||||
}) => {
|
||||
const fetchMoreMetrics = previousEnd => {
|
||||
fetchMore({
|
||||
variables: {
|
||||
...variables,
|
||||
start: previousEnd,
|
||||
end: moment().utc().format()
|
||||
end: moment()
|
||||
.utc()
|
||||
.format()
|
||||
},
|
||||
updateQuery: (previousResult, { fetchMoreResult, queryVariables }) => {
|
||||
updateQuery: (
|
||||
previousResult,
|
||||
{ fetchMoreResult, queryVariables }
|
||||
) => {
|
||||
return getNextResult(previousResult, fetchMoreResult);
|
||||
}
|
||||
});
|
||||
}
|
||||
return ({
|
||||
};
|
||||
return {
|
||||
deploymentGroup,
|
||||
service: !loading && deploymentGroup ? deploymentGroup.services[0] : null,
|
||||
service:
|
||||
!loading && deploymentGroup ? deploymentGroup.services[0] : null,
|
||||
loading,
|
||||
error,
|
||||
fetchMoreMetrics
|
||||
})
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -9,8 +9,11 @@ import {
|
||||
serviceBySlugSelector
|
||||
} from '@root/state/selectors';
|
||||
|
||||
export const Breadcrumb = ({ deploymentGroup, service, location: { pathname }}) => {
|
||||
|
||||
export const Breadcrumb = ({
|
||||
deploymentGroup,
|
||||
service,
|
||||
location: { pathname }
|
||||
}) => {
|
||||
const path = pathname.split('/');
|
||||
|
||||
const links = [
|
||||
@ -41,7 +44,7 @@ Breadcrumb.propTypes = {
|
||||
deploymentGroup: PropTypes.object,
|
||||
service: PropTypes.object,
|
||||
location: PropTypes.object
|
||||
}
|
||||
};
|
||||
|
||||
const connectBreadcrumb = connect(
|
||||
(state, ownProps) => {
|
||||
@ -59,7 +62,4 @@ const connectBreadcrumb = connect(
|
||||
dispatch => ({})
|
||||
);
|
||||
|
||||
export default compose(
|
||||
connectBreadcrumb,
|
||||
withNotFound()
|
||||
)(Breadcrumb);
|
||||
export default compose(connectBreadcrumb, withNotFound())(Breadcrumb);
|
||||
|
@ -5,8 +5,9 @@ import get from 'lodash.get';
|
||||
import PortalQuery from '@graphql/Portal.gql';
|
||||
import { Header as HeaderComponent } from '@components/navigation';
|
||||
|
||||
export const Header = ({ datacenter, username }) =>
|
||||
<HeaderComponent datacenter={datacenter} username={username} />;
|
||||
export const Header = ({ datacenter, username }) => (
|
||||
<HeaderComponent datacenter={datacenter} username={username} />
|
||||
);
|
||||
|
||||
const HeaderWithData = graphql(PortalQuery, {
|
||||
props: ({ data: { portal = {} } }) => ({
|
||||
|
@ -5,7 +5,6 @@ import withNotFound from './not-found-hoc';
|
||||
import { Menu as MenuComponent } from '@components/navigation';
|
||||
|
||||
export const Menu = ({ location, match, sections }) => {
|
||||
|
||||
if (!sections || !sections.length) {
|
||||
return null;
|
||||
}
|
||||
@ -40,7 +39,4 @@ const connectMenu = connect(
|
||||
dispatch => ({})
|
||||
);
|
||||
|
||||
export default compose(
|
||||
connectMenu,
|
||||
withNotFound()
|
||||
)(Menu);
|
||||
export default compose(connectMenu, withNotFound())(Menu);
|
||||
|
@ -4,31 +4,24 @@ import { NotFound } from '@components/navigation';
|
||||
export const GqlPaths = {
|
||||
DEPLOYMENT_GROUP: 'deploymentGroup',
|
||||
SERVICES: 'services'
|
||||
}
|
||||
|
||||
export default (paths) => {
|
||||
return (WrappedComponent) => {
|
||||
};
|
||||
|
||||
export default paths => {
|
||||
return WrappedComponent => {
|
||||
return class extends Component {
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
|
||||
if(paths) {
|
||||
const {
|
||||
error,
|
||||
location,
|
||||
history
|
||||
} = nextProps;
|
||||
if (paths) {
|
||||
const { error, location, history } = nextProps;
|
||||
|
||||
if (
|
||||
error
|
||||
&& (!location.state || !location.state.notFound)
|
||||
&& (error.graphQLErrors && error.graphQLErrors.length)
|
||||
error &&
|
||||
(!location.state || !location.state.notFound) &&
|
||||
(error.graphQLErrors && error.graphQLErrors.length)
|
||||
) {
|
||||
const graphQLError = error.graphQLErrors[0];
|
||||
if(graphQLError.message === 'Not Found') {
|
||||
if (graphQLError.message === 'Not Found') {
|
||||
const notFound = graphQLError.path.pop();
|
||||
if(paths.indexOf(notFound) > -1) {
|
||||
if (paths.indexOf(notFound) > -1) {
|
||||
history.replace(location.pathname, { notFound });
|
||||
}
|
||||
}
|
||||
@ -37,25 +30,22 @@ export default (paths) => {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { error, location, match } = this.props;
|
||||
|
||||
const {
|
||||
error,
|
||||
location,
|
||||
match
|
||||
} = this.props;
|
||||
|
||||
if(location.state && location.state.notFound) {
|
||||
if (location.state && location.state.notFound) {
|
||||
const notFound = location.state.notFound;
|
||||
if(paths && paths.indexOf(notFound) > -1) {
|
||||
if (paths && paths.indexOf(notFound) > -1) {
|
||||
let title;
|
||||
let to;
|
||||
let link;
|
||||
if(notFound === 'services' || notFound === 'service') {
|
||||
if (notFound === 'services' || notFound === 'service') {
|
||||
title = 'This service doesn’t exist';
|
||||
to = match.url.split('/').slice(0, 3).join('/');
|
||||
to = match.url
|
||||
.split('/')
|
||||
.slice(0, 3)
|
||||
.join('/');
|
||||
link = 'Back to services';
|
||||
}
|
||||
else if(notFound === 'deploymentGroup') {
|
||||
} else if (notFound === 'deploymentGroup') {
|
||||
title = 'This deployment group doesn’t exist';
|
||||
to = '/deployment-group';
|
||||
link = 'Back to dashboard';
|
||||
@ -63,17 +53,17 @@ export default (paths) => {
|
||||
return (
|
||||
<NotFound
|
||||
title={title}
|
||||
message='Sorry, but our princess is in another castle.'
|
||||
message="Sorry, but our princess is in another castle."
|
||||
to={to}
|
||||
link={link}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return <WrappedComponent {...this.props} />
|
||||
return <WrappedComponent {...this.props} />;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -21,7 +21,10 @@ export class ServiceDelete extends Component {
|
||||
const { loading, error, match, history } = this.props;
|
||||
|
||||
const handleCloseClick = evt => {
|
||||
const closeUrl = match.url.split('/').slice(0, -2).join('/');
|
||||
const closeUrl = match.url
|
||||
.split('/')
|
||||
.slice(0, -2)
|
||||
.join('/');
|
||||
history.replace(closeUrl);
|
||||
};
|
||||
|
||||
@ -37,8 +40,8 @@ export class ServiceDelete extends Component {
|
||||
return (
|
||||
<Modal width={460} onCloseClick={handleCloseClick}>
|
||||
<ModalErrorMessage
|
||||
title='Ooops!'
|
||||
message='An error occurred while loading your service.'
|
||||
title="Ooops!"
|
||||
message="An error occurred while loading your service."
|
||||
onCloseClick={handleCloseClick}
|
||||
/>
|
||||
</Modal>
|
||||
@ -51,7 +54,7 @@ export class ServiceDelete extends Component {
|
||||
return (
|
||||
<Modal width={460} onCloseClick={handleCloseClick}>
|
||||
<ModalErrorMessage
|
||||
title='Ooops!'
|
||||
title="Ooops!"
|
||||
message={`An error occurred while attempting to delete your ${service.name} service.`}
|
||||
onCloseClick={handleCloseClick}
|
||||
/>
|
||||
@ -60,9 +63,11 @@ export class ServiceDelete extends Component {
|
||||
}
|
||||
|
||||
const handleConfirmClick = evt => {
|
||||
deleteServices(service.id).then(() => handleCloseClick()).catch(err => {
|
||||
this.setState({ error: err });
|
||||
});
|
||||
deleteServices(service.id)
|
||||
.then(() => handleCloseClick())
|
||||
.catch(err => {
|
||||
this.setState({ error: err });
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
@ -98,5 +103,5 @@ const DeleteServicesGql = graphql(ServicesDeleteMutation, {
|
||||
export default compose(
|
||||
DeleteServicesGql,
|
||||
ServiceGql,
|
||||
withNotFound([ GqlPaths.SERVICES ])
|
||||
withNotFound([GqlPaths.SERVICES])
|
||||
)(ServiceDelete);
|
||||
|
@ -10,17 +10,16 @@ import { ServiceMetrics as ServiceMetricsComponent } from '@components/service';
|
||||
import { Button } from 'joyent-ui-toolkit';
|
||||
import { Loader, ErrorMessage } from '@components/messaging';
|
||||
|
||||
import { withServiceMetricsPolling, withServiceMetricsGql } from '@containers/metrics';
|
||||
import {
|
||||
withServiceMetricsPolling,
|
||||
withServiceMetricsGql
|
||||
} from '@containers/metrics';
|
||||
|
||||
// 'width' of graph, i.e. total duration of time it'll display and truncate data to
|
||||
// amount of data we'll need to initially fetch
|
||||
const GraphDurationSeconds = 90;
|
||||
|
||||
const ServiceMetrics = ({
|
||||
service,
|
||||
loading,
|
||||
error
|
||||
}) => {
|
||||
const ServiceMetrics = ({ service, loading, error }) => {
|
||||
if (loading || !service) {
|
||||
return (
|
||||
<LayoutContainer center>
|
||||
@ -43,8 +42,8 @@ const ServiceMetrics = ({
|
||||
// metricsData should prob be an array rather than an object
|
||||
const metricsData = service.instances.reduce((metrics, instance) => {
|
||||
// gather metrics of instances according to type
|
||||
instance.metrics.forEach((instanceMetrics) => {
|
||||
if(!metrics[instanceMetrics.name]) {
|
||||
instance.metrics.forEach(instanceMetrics => {
|
||||
if (!metrics[instanceMetrics.name]) {
|
||||
metrics[instanceMetrics.name] = [];
|
||||
}
|
||||
metrics[instanceMetrics.name].push(instanceMetrics);
|
||||
@ -69,10 +68,7 @@ export default compose(
|
||||
updateIntervalSeconds: 15
|
||||
}),
|
||||
withServiceMetricsPolling({ pollingInterval: 1000 }),
|
||||
withNotFound([
|
||||
GqlPaths.DEPLOYMENT_GROUP,
|
||||
GqlPaths.SERVICES
|
||||
])
|
||||
withNotFound([GqlPaths.DEPLOYMENT_GROUP, GqlPaths.SERVICES])
|
||||
)(ServiceMetrics);
|
||||
|
||||
/*
|
||||
|
@ -22,7 +22,10 @@ export class ServiceScale extends Component {
|
||||
const { loading, error, match, history } = this.props;
|
||||
|
||||
const handleCloseClick = evt => {
|
||||
const closeUrl = match.url.split('/').slice(0, -2).join('/');
|
||||
const closeUrl = match.url
|
||||
.split('/')
|
||||
.slice(0, -2)
|
||||
.join('/');
|
||||
history.replace(closeUrl);
|
||||
};
|
||||
|
||||
@ -38,8 +41,8 @@ export class ServiceScale extends Component {
|
||||
return (
|
||||
<Modal width={460} onCloseClick={handleCloseClick}>
|
||||
<ModalErrorMessage
|
||||
title='Ooops!'
|
||||
message='An error occurred while loading your service.'
|
||||
title="Ooops!"
|
||||
message="An error occurred while loading your service."
|
||||
onCloseClick={handleCloseClick}
|
||||
/>
|
||||
</Modal>
|
||||
@ -52,7 +55,7 @@ export class ServiceScale extends Component {
|
||||
return (
|
||||
<Modal width={460} onCloseClick={handleCloseClick}>
|
||||
<ModalErrorMessage
|
||||
title='Ooops!'
|
||||
title="Ooops!"
|
||||
message={`An error occurred while attempting to scale your ${service.name} service.`}
|
||||
onCloseClick={handleCloseClick}
|
||||
/>
|
||||
@ -70,9 +73,11 @@ export class ServiceScale extends Component {
|
||||
};
|
||||
|
||||
const handleSubmitClick = values => {
|
||||
scale(service.id, values.replicas).then(handleCloseClick).catch(err => {
|
||||
this.setState({ error: err });
|
||||
});
|
||||
scale(service.id, values.replicas)
|
||||
.then(handleCloseClick)
|
||||
.catch(err => {
|
||||
this.setState({ error: err });
|
||||
});
|
||||
};
|
||||
|
||||
if (!service) {
|
||||
@ -126,5 +131,5 @@ const ServiceScaleGql = graphql(ServiceScaleMutation, {
|
||||
export default compose(
|
||||
ServiceScaleGql,
|
||||
ServiceGql,
|
||||
withNotFound([ GqlPaths.SERVICES ])
|
||||
withNotFound([GqlPaths.SERVICES])
|
||||
)(ServiceScale);
|
||||
|
@ -34,7 +34,7 @@ export class ServiceList extends Component {
|
||||
services,
|
||||
loading,
|
||||
error,
|
||||
toggleServicesQuickActions,
|
||||
toggleServicesQuickActions
|
||||
} = this.props;
|
||||
|
||||
if (loading && !forceArray(services).length) {
|
||||
@ -73,8 +73,9 @@ export class ServiceList extends Component {
|
||||
const buttonRect = button.getBoundingClientRect();
|
||||
|
||||
const position = {
|
||||
left:
|
||||
`${buttonRect.left + window.scrollX + (buttonRect.right - buttonRect.left) / 2}px`,
|
||||
left: `${buttonRect.left +
|
||||
window.scrollX +
|
||||
(buttonRect.right - buttonRect.left) / 2}px`,
|
||||
top: `${buttonRect.bottom + window.scrollY}px`
|
||||
};
|
||||
|
||||
@ -120,9 +121,7 @@ export class ServiceList extends Component {
|
||||
return (
|
||||
<LayoutContainer>
|
||||
{renderedError}
|
||||
<StyledContainer>
|
||||
{serviceList}
|
||||
</StyledContainer>
|
||||
<StyledContainer>{serviceList}</StyledContainer>
|
||||
</LayoutContainer>
|
||||
);
|
||||
}
|
||||
@ -134,7 +133,7 @@ ServiceList.propTypes = {
|
||||
loading: PropTypes.bool,
|
||||
error: PropTypes.bool,
|
||||
toggleServicesQuickActions: PropTypes.func
|
||||
}
|
||||
};
|
||||
|
||||
const mapStateToProps = (state, ownProps) => ({});
|
||||
|
||||
@ -153,7 +152,7 @@ const ServicesGql = graphql(ServicesQuery, {
|
||||
}
|
||||
};
|
||||
},
|
||||
props: ({ data: { deploymentGroup, loading, error }}) => ({
|
||||
props: ({ data: { deploymentGroup, loading, error } }) => ({
|
||||
deploymentGroup,
|
||||
services: deploymentGroup
|
||||
? processServices(deploymentGroup.services, null)
|
||||
@ -166,7 +165,7 @@ const ServicesGql = graphql(ServicesQuery, {
|
||||
const ServiceListWithData = compose(
|
||||
ServicesGql,
|
||||
UiConnect,
|
||||
withNotFound([ GqlPaths.DEPLOYMENT_GROUP ])
|
||||
withNotFound([GqlPaths.DEPLOYMENT_GROUP])
|
||||
)(ServiceList);
|
||||
|
||||
export default ServiceListWithData;
|
||||
|
@ -20,11 +20,8 @@ const PaddedRow = Row.extend`
|
||||
margin-bottom: ${remcalc(18)}
|
||||
`;
|
||||
|
||||
export const ServicesMenu = ({
|
||||
location: { pathname },
|
||||
history: { push }
|
||||
}) => {
|
||||
|
||||
export const ServicesMenu = ({ location: { pathname }, history: { push } }) => {
|
||||
const toggleValue = pathname.split('-').pop();
|
||||
|
||||
const handleToggle = evt => {
|
||||
|
@ -17,13 +17,12 @@ const StyledContainer = styled.div`
|
||||
`;
|
||||
|
||||
export class ServicesQuickActions extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
errors: {}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
@ -53,20 +52,20 @@ export class ServicesQuickActions extends Component {
|
||||
? 'An error occurred while attempting to restart your service.'
|
||||
: '';
|
||||
|
||||
errorMessage = (
|
||||
errorMessage = (
|
||||
<LayoutContainer>
|
||||
<ErrorMessage title="Ooops!" message={message} />
|
||||
</LayoutContainer>
|
||||
);
|
||||
}
|
||||
|
||||
if(servicesQuickActions.show) {
|
||||
if (servicesQuickActions.show) {
|
||||
const handleTooltipBlur = evt => {
|
||||
toggleServicesQuickActions({ show: false });
|
||||
};
|
||||
|
||||
const handleRestartClick = (evt, service) => {
|
||||
this.setState({errors: {}});
|
||||
this.setState({ errors: {} });
|
||||
toggleServicesQuickActions({ show: false });
|
||||
restartServices(service.id).catch(err => {
|
||||
this.setState({ errors: { restart: err } });
|
||||
@ -74,7 +73,7 @@ export class ServicesQuickActions extends Component {
|
||||
};
|
||||
|
||||
const handleStopClick = (evt, service) => {
|
||||
this.setState({errors: {}});
|
||||
this.setState({ errors: {} });
|
||||
toggleServicesQuickActions({ show: false });
|
||||
stopServices(service.id).catch(err => {
|
||||
this.setState({ errors: { stop: err } });
|
||||
@ -82,7 +81,7 @@ export class ServicesQuickActions extends Component {
|
||||
};
|
||||
|
||||
const handleStartClick = (evt, service) => {
|
||||
this.setState({errors: {}});
|
||||
this.setState({ errors: {} });
|
||||
toggleServicesQuickActions({ show: false });
|
||||
startServices(service.id).catch(err => {
|
||||
this.setState({ errors: { start: err } });
|
||||
@ -90,13 +89,13 @@ export class ServicesQuickActions extends Component {
|
||||
};
|
||||
|
||||
const handleScaleClick = (evt, service) => {
|
||||
this.setState({errors: {}});
|
||||
this.setState({ errors: {} });
|
||||
toggleServicesQuickActions({ show: false });
|
||||
push(`${url}/${service.slug}/scale`);
|
||||
};
|
||||
|
||||
const handleDeleteClick = (evt, service) => {
|
||||
this.setState({errors: {}});
|
||||
this.setState({ errors: {} });
|
||||
toggleServicesQuickActions({ show: false });
|
||||
push(`${url}/${service.slug}/delete`);
|
||||
};
|
||||
@ -115,16 +114,16 @@ export class ServicesQuickActions extends Component {
|
||||
onDeleteClick={handleDeleteClick}
|
||||
/>
|
||||
</StyledContainer>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if(quickActions || errorMessage) {
|
||||
if (quickActions || errorMessage) {
|
||||
return (
|
||||
<div>
|
||||
{errorMessage}
|
||||
{quickActions}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -47,7 +47,7 @@ export class ServicesTopology extends Component {
|
||||
services,
|
||||
loading,
|
||||
error,
|
||||
toggleServicesQuickActions,
|
||||
toggleServicesQuickActions
|
||||
} = this.props;
|
||||
|
||||
if (loading && !forceArray(services).length) {
|
||||
@ -85,18 +85,27 @@ export class ServicesTopology extends Component {
|
||||
const container = this._refs.container;
|
||||
const containerRect = container.getBoundingClientRect();
|
||||
const position = {
|
||||
top: `${containerRect.top + window.scrollY + tooltipData.position.top}px`,
|
||||
left: `${containerRect.left + window.scrollX + tooltipData.position.left}px`
|
||||
}
|
||||
top: `${containerRect.top +
|
||||
window.scrollY +
|
||||
tooltipData.position.top}px`,
|
||||
left: `${containerRect.left +
|
||||
window.scrollX +
|
||||
tooltipData.position.left}px`
|
||||
};
|
||||
const data = {
|
||||
...tooltipData,
|
||||
position
|
||||
}
|
||||
};
|
||||
toggleServicesQuickActions(data);
|
||||
};
|
||||
|
||||
const handleNodeTitleClick = (evt, { service }) => {
|
||||
push(`${url.split('/').slice(0, 3).join('/')}/services/${service.slug}`);
|
||||
push(
|
||||
`${url
|
||||
.split('/')
|
||||
.slice(0, 3)
|
||||
.join('/')}/services/${service.slug}`
|
||||
);
|
||||
};
|
||||
|
||||
let renderedError = null;
|
||||
@ -173,7 +182,7 @@ const ServicesGql = graphql(ServicesQuery, {
|
||||
const ServicesTopologyWithData = compose(
|
||||
ServicesGql,
|
||||
UiConnect,
|
||||
withNotFound([ GqlPaths.DEPLOYMENT_GROUP ])
|
||||
withNotFound([GqlPaths.DEPLOYMENT_GROUP])
|
||||
)(ServicesTopology);
|
||||
|
||||
export default ServicesTopologyWithData;
|
||||
|
@ -1,8 +1,8 @@
|
||||
query Instances(
|
||||
$deploymentGroupSlug: String!,
|
||||
$serviceSlug: String!,
|
||||
$metricNames: [MetricName]!,
|
||||
$start: String!,
|
||||
$deploymentGroupSlug: String!
|
||||
$serviceSlug: String!
|
||||
$metricNames: [MetricName]!
|
||||
$start: String!
|
||||
$end: String!
|
||||
) {
|
||||
deploymentGroup(slug: $deploymentGroupSlug) {
|
||||
|
@ -25,10 +25,7 @@ import {
|
||||
ServiceMetrics
|
||||
} from '@containers/service';
|
||||
|
||||
import {
|
||||
InstanceList,
|
||||
InstancesTooltip
|
||||
} from '@containers/instances';
|
||||
import { InstanceList, InstancesTooltip } from '@containers/instances';
|
||||
|
||||
import { DeploymentGroupDelete } from '@containers/deployment-group';
|
||||
|
||||
@ -43,23 +40,27 @@ const Container = styled.div`
|
||||
|
||||
const rootRedirect = p => <Redirect to="/deployment-groups" />;
|
||||
|
||||
const servicesListRedirect = p =>
|
||||
const servicesListRedirect = p => (
|
||||
<Redirect
|
||||
to={`/deployment-groups/${p.match.params.deploymentGroup}/services-list`}
|
||||
/>;
|
||||
/>
|
||||
);
|
||||
|
||||
const servicesTopologyRedirect = p =>
|
||||
const servicesTopologyRedirect = p => (
|
||||
<Redirect
|
||||
to={`/deployment-groups/${p.match.params.deploymentGroup}/services-topology`}
|
||||
/>;
|
||||
to={`/deployment-groups/${p.match.params
|
||||
.deploymentGroup}/services-topology`}
|
||||
/>
|
||||
);
|
||||
|
||||
const serviceRedirect = p =>
|
||||
const serviceRedirect = p => (
|
||||
<Redirect
|
||||
to={`/deployment-groups/${p.match.params.deploymentGroup}/services/${p.match
|
||||
.params.service}/instances`}
|
||||
/>;
|
||||
/>
|
||||
);
|
||||
|
||||
const App = p =>
|
||||
const App = p => (
|
||||
<div>
|
||||
<Switch>
|
||||
<Route
|
||||
@ -222,7 +223,8 @@ const App = p =>
|
||||
component={servicesTopologyRedirect}
|
||||
/>
|
||||
</Switch>
|
||||
</div>;
|
||||
</div>
|
||||
);
|
||||
|
||||
const Router = (
|
||||
<BrowserRouter>
|
||||
|
@ -1,5 +1,8 @@
|
||||
import { handleActions } from 'redux-actions';
|
||||
import { toggleServicesQuickActions, toggleInstancesTooltip } from '@state/actions';
|
||||
import {
|
||||
toggleServicesQuickActions,
|
||||
toggleInstancesTooltip
|
||||
} from '@state/actions';
|
||||
|
||||
export const _toggleServicesQuickActions = (state, action) => {
|
||||
const { position, service, show } = action.payload;
|
||||
|
@ -104,10 +104,10 @@ const getInstancesHealthy = instances => {
|
||||
return instances.reduce(
|
||||
(healthy, instance) => ({
|
||||
total: healthy.total + 1,
|
||||
healthy: instance.healthy === 'HEALTHY' ?
|
||||
healthy.healthy + 1 : healthy.healthy
|
||||
healthy:
|
||||
instance.healthy === 'HEALTHY' ? healthy.healthy + 1 : healthy.healthy
|
||||
}),
|
||||
{total: 0, healthy: 0}
|
||||
{ total: 0, healthy: 0 }
|
||||
);
|
||||
};
|
||||
|
||||
@ -174,6 +174,8 @@ const processServicesForTopology = services => {
|
||||
}));
|
||||
};
|
||||
|
||||
/* ,
|
||||
instancesByServiceId */
|
||||
export {
|
||||
deploymentGroupBySlug as deploymentGroupBySlugSelector,
|
||||
serviceBySlug as serviceBySlugSelector,
|
||||
@ -182,6 +184,5 @@ export {
|
||||
getInstancesHealthy,
|
||||
getService,
|
||||
processServices,
|
||||
processServicesForTopology /* ,
|
||||
instancesByServiceId */
|
||||
processServicesForTopology
|
||||
};
|
||||
|
@ -30,8 +30,11 @@ export const client = new ApolloClient({
|
||||
? o.timestamp
|
||||
: o.name && o.instance
|
||||
? `${o.name}-${o.instance}`
|
||||
: o.name ? o.name : o.time && o.value
|
||||
? `${o.time}-${o.value}` : 'apollo-cache-key-not-defined';
|
||||
: o.name
|
||||
? o.name
|
||||
: o.time && o.value
|
||||
? `${o.time}-${o.value}`
|
||||
: 'apollo-cache-key-not-defined';
|
||||
return `${o.__typename}:${id}`;
|
||||
},
|
||||
networkInterface: createNetworkInterface({
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
@ -12,9 +11,7 @@ import { deploymentGroup } from '../../mocks';
|
||||
|
||||
it('renders <DeploymentGroupDelete /> without throwing', () => {
|
||||
const tree = renderer
|
||||
.create(
|
||||
<DeploymentGroupDelete deploymentGroup={deploymentGroup} />
|
||||
)
|
||||
.create(<DeploymentGroupDelete deploymentGroup={deploymentGroup} />)
|
||||
.toJSON();
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
@ -10,10 +9,6 @@ import 'jest-styled-components';
|
||||
import EmtpyInstances from '@components/instances/empty.js';
|
||||
|
||||
it('renders <EmtpyInstances /> without throwing', () => {
|
||||
const tree = renderer
|
||||
.create(
|
||||
<EmtpyInstances />
|
||||
)
|
||||
.toJSON();
|
||||
const tree = renderer.create(<EmtpyInstances />).toJSON();
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
@ -11,10 +10,6 @@ import InstanceCard from '@components/instances/list-item.js';
|
||||
import { instance } from '../../mocks';
|
||||
|
||||
it('renders <InstanceCard /> without throwing', () => {
|
||||
const tree = renderer
|
||||
.create(
|
||||
<InstanceCard instance={instance} />
|
||||
)
|
||||
.toJSON();
|
||||
const tree = renderer.create(<InstanceCard instance={instance} />).toJSON();
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
@ -9,10 +9,6 @@ import 'jest-styled-components';
|
||||
import Container from '@components/layout/container';
|
||||
|
||||
it('renders <Container /> without throwing', () => {
|
||||
const tree = renderer
|
||||
.create(
|
||||
<Container />
|
||||
)
|
||||
.toJSON();
|
||||
const tree = renderer.create(<Container />).toJSON();
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
|
@ -9,10 +9,6 @@ import 'jest-styled-components';
|
||||
import Error from '@components/messaging/error';
|
||||
|
||||
it('renders <Error /> without throwing', () => {
|
||||
const tree = renderer
|
||||
.create(
|
||||
<Error />
|
||||
)
|
||||
.toJSON();
|
||||
const tree = renderer.create(<Error />).toJSON();
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
@ -9,10 +9,6 @@ import 'jest-styled-components';
|
||||
import Loader from '@components/messaging/loader';
|
||||
|
||||
it('renders <Loader /> without throwing', () => {
|
||||
const tree = renderer
|
||||
.create(
|
||||
<Loader />
|
||||
)
|
||||
.toJSON();
|
||||
const tree = renderer.create(<Loader />).toJSON();
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
@ -11,7 +11,7 @@ import ModalError from '@components/messaging/modal-error';
|
||||
it('renders <ModalError /> without throwing', () => {
|
||||
const tree = renderer
|
||||
.create(
|
||||
<ModalError message='Modal error message' onCloseClick={() => {}}/>
|
||||
<ModalError message="Modal error message" onCloseClick={() => {}} />
|
||||
)
|
||||
.toJSON();
|
||||
expect(tree).toMatchSnapshot();
|
||||
|
@ -9,10 +9,6 @@ import 'jest-styled-components';
|
||||
import Warning from '@components/messaging/warning';
|
||||
|
||||
it('renders <Warning /> without throwing', () => {
|
||||
const tree = renderer
|
||||
.create(
|
||||
<Warning message='Warning message'/>
|
||||
)
|
||||
.toJSON();
|
||||
const tree = renderer.create(<Warning message="Warning message" />).toJSON();
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
@ -9,10 +9,6 @@ import 'jest-styled-components';
|
||||
import Title from '@components/navigation/title';
|
||||
|
||||
it('renders <Title /> without throwing', () => {
|
||||
const tree = renderer
|
||||
.create(
|
||||
<Title />
|
||||
)
|
||||
.toJSON();
|
||||
const tree = renderer.create(<Title />).toJSON();
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
@ -10,10 +10,6 @@ import { service } from '../../mocks';
|
||||
import Delete from '@components/service/delete';
|
||||
|
||||
it('renders <Delete /> without throwing', () => {
|
||||
const tree = renderer
|
||||
.create(
|
||||
<Delete service={service} />
|
||||
)
|
||||
.toJSON();
|
||||
const tree = renderer.create(<Delete service={service} />).toJSON();
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
@ -23,9 +22,7 @@ it('renders <ServiceListItem /> without throwing', () => {
|
||||
|
||||
it('renders child <ServiceListItem /> without throwing', () => {
|
||||
const tree = renderer
|
||||
.create(
|
||||
<ServiceListItem service={service} isChild />
|
||||
)
|
||||
.create(<ServiceListItem service={service} isChild />)
|
||||
.toJSON();
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
@ -12,9 +11,7 @@ import { service } from '../../mocks';
|
||||
|
||||
xit('renders <ServiceStatus /> without throwing', () => {
|
||||
const tree = renderer
|
||||
.create(
|
||||
<ServiceStatus instanceStatuses={service.instanceStatuses} />
|
||||
)
|
||||
.create(<ServiceStatus instanceStatuses={service.instanceStatuses} />)
|
||||
.toJSON();
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
@ -17,12 +16,12 @@ it('renders <DeploymentGroupCreate /> without throwing', () => {
|
||||
stage: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
const tree = renderer
|
||||
.create(
|
||||
<Store>
|
||||
<Router>
|
||||
<DeploymentGroupCreate { ...props } />
|
||||
<DeploymentGroupCreate {...props} />
|
||||
</Router>
|
||||
</Store>
|
||||
)
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
@ -15,12 +14,12 @@ it('renders <Breadcrumb /> without throwing', () => {
|
||||
location: {
|
||||
pathname: ''
|
||||
}
|
||||
}
|
||||
};
|
||||
const tree = renderer
|
||||
.create(
|
||||
<Store>
|
||||
<Router>
|
||||
<Breadcrumb { ...props } />
|
||||
<Breadcrumb {...props} />
|
||||
</Router>
|
||||
</Store>
|
||||
)
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
@ -15,10 +14,7 @@ it('renders <ServiceList /> without throwing', () => {
|
||||
.create(
|
||||
<Store>
|
||||
<Router>
|
||||
<ServiceList
|
||||
deploymentGroup={deploymentGroup}
|
||||
services={services}
|
||||
/>
|
||||
<ServiceList deploymentGroup={deploymentGroup} services={services} />
|
||||
</Router>
|
||||
</Store>
|
||||
)
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
@ -18,12 +17,12 @@ it('renders <ServicesMenu /> without throwing', () => {
|
||||
history: {
|
||||
push: () => {}
|
||||
}
|
||||
}
|
||||
};
|
||||
const tree = renderer
|
||||
.create(
|
||||
<Store>
|
||||
<Router>
|
||||
<ServicesMenu { ...props } />
|
||||
<ServicesMenu {...props} />
|
||||
</Router>
|
||||
</Store>
|
||||
)
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
@ -15,9 +14,7 @@ it('renders <ServicesTopology /> without throwing', () => {
|
||||
.create(
|
||||
<Store>
|
||||
<Router>
|
||||
<ServicesTopology
|
||||
services={services}
|
||||
/>
|
||||
<ServicesTopology services={services} />
|
||||
</Router>
|
||||
</Store>
|
||||
)
|
||||
|
@ -1,37 +1,31 @@
|
||||
export const instance = {
|
||||
"id": "309ecd9f-ac03-474b-aff7-4bd2e743296c",
|
||||
"name": "wordpress_01",
|
||||
"serviceId": "be227788-74f1-4e5b-a85f-b5c71cbae8d8",
|
||||
"deploymentGroupId": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
|
||||
"machineId": "011f7479-2d45-442d-99bf-7f6216954cc8",
|
||||
"status": "RUNNING",
|
||||
"healthy": "HEALTHY"
|
||||
id: '309ecd9f-ac03-474b-aff7-4bd2e743296c',
|
||||
name: 'wordpress_01',
|
||||
serviceId: 'be227788-74f1-4e5b-a85f-b5c71cbae8d8',
|
||||
deploymentGroupId: 'e0ea0c02-55cc-45fe-8064-3e5176a59401',
|
||||
machineId: '011f7479-2d45-442d-99bf-7f6216954cc8',
|
||||
status: 'RUNNING',
|
||||
healthy: 'HEALTHY'
|
||||
};
|
||||
|
||||
export const service = {
|
||||
"id": "081a792c-47e0-4439-924b-2efa9788ae9e",
|
||||
"slug": "nginx",
|
||||
"name": "Nginx",
|
||||
"deploymentGroupId": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
|
||||
"connections": ["be227788-74f1-4e5b-a85f-b5c71cbae8d8"],
|
||||
"instances": [instance],
|
||||
"instanceStatuses": [{ status: "RUNNING", count: 1 }]
|
||||
id: '081a792c-47e0-4439-924b-2efa9788ae9e',
|
||||
slug: 'nginx',
|
||||
name: 'Nginx',
|
||||
deploymentGroupId: 'e0ea0c02-55cc-45fe-8064-3e5176a59401',
|
||||
connections: ['be227788-74f1-4e5b-a85f-b5c71cbae8d8'],
|
||||
instances: [instance],
|
||||
instanceStatuses: [{ status: 'RUNNING', count: 1 }]
|
||||
};
|
||||
|
||||
export const deploymentGroup = {
|
||||
"id": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
|
||||
"slug": "wordpress-blog-example",
|
||||
"name": "Wordpress Blog Example"
|
||||
id: 'e0ea0c02-55cc-45fe-8064-3e5176a59401',
|
||||
slug: 'wordpress-blog-example',
|
||||
name: 'Wordpress Blog Example'
|
||||
};
|
||||
|
||||
export const services = [
|
||||
service
|
||||
];
|
||||
export const services = [service];
|
||||
|
||||
export const instances = [
|
||||
instance
|
||||
];
|
||||
export const instances = [instance];
|
||||
|
||||
export const deploymentGroups = [
|
||||
deploymentGroup
|
||||
]
|
||||
export const deploymentGroups = [deploymentGroup];
|
||||
|
@ -1,7 +1,4 @@
|
||||
import React from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
||||
export default ({ children }) =>
|
||||
<MemoryRouter>
|
||||
{children}
|
||||
</MemoryRouter>;
|
||||
export default ({ children }) => <MemoryRouter>{children}</MemoryRouter>;
|
||||
|
@ -2,7 +2,8 @@ import React from 'react';
|
||||
import { client, store } from '@state/store';
|
||||
import { ApolloProvider } from 'react-apollo';
|
||||
|
||||
export default ({ children }) =>
|
||||
export default ({ children }) => (
|
||||
<ApolloProvider client={client} store={store}>
|
||||
{children}
|
||||
</ApolloProvider>;
|
||||
</ApolloProvider>
|
||||
);
|
||||
|
@ -2,7 +2,6 @@ import React from 'react';
|
||||
import { ThemeProvider } from 'styled-components';
|
||||
import { theme } from 'joyent-ui-toolkit';
|
||||
|
||||
export default ({ children }) =>
|
||||
<ThemeProvider theme={theme}>
|
||||
{children}
|
||||
</ThemeProvider>;
|
||||
export default ({ children }) => (
|
||||
<ThemeProvider theme={theme}>{children}</ThemeProvider>
|
||||
);
|
||||
|
@ -5,7 +5,6 @@ import {
|
||||
import state from '@state/state';
|
||||
|
||||
describe('ui reducer', () => {
|
||||
|
||||
it('toggleServicesQuickActions shows correctly', () => {
|
||||
const uiState = state.ui;
|
||||
const expectedUiState = {
|
||||
@ -23,17 +22,19 @@ describe('ui reducer', () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const action = { payload: {
|
||||
show: true,
|
||||
position: {
|
||||
top: 10,
|
||||
left: 10
|
||||
},
|
||||
service: {
|
||||
id: 'service-id'
|
||||
};
|
||||
const action = {
|
||||
payload: {
|
||||
show: true,
|
||||
position: {
|
||||
top: 10,
|
||||
left: 10
|
||||
},
|
||||
service: {
|
||||
id: 'service-id'
|
||||
}
|
||||
}
|
||||
}};
|
||||
};
|
||||
const result = _toggleServicesQuickActions(uiState, action);
|
||||
expect(result).toEqual(expectedUiState);
|
||||
});
|
||||
@ -48,8 +49,8 @@ describe('ui reducer', () => {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
}
|
||||
const action = { payload: { show: false }};
|
||||
};
|
||||
const action = { payload: { show: false } };
|
||||
const result = _toggleServicesQuickActions(uiState, action);
|
||||
expect(result).toEqual(expectedUiState);
|
||||
});
|
||||
@ -72,18 +73,20 @@ describe('ui reducer', () => {
|
||||
type: 'healthy'
|
||||
}
|
||||
}
|
||||
}
|
||||
const action = { payload: {
|
||||
show: true,
|
||||
position: {
|
||||
top: 10,
|
||||
left: 10
|
||||
},
|
||||
instance: {
|
||||
id: 'instance-id'
|
||||
},
|
||||
type: 'healthy'
|
||||
}};
|
||||
};
|
||||
const action = {
|
||||
payload: {
|
||||
show: true,
|
||||
position: {
|
||||
top: 10,
|
||||
left: 10
|
||||
},
|
||||
instance: {
|
||||
id: 'instance-id'
|
||||
},
|
||||
type: 'healthy'
|
||||
}
|
||||
};
|
||||
const result = _toggleInstancesTooltip(uiState, action);
|
||||
expect(result).toEqual(expectedUiState);
|
||||
});
|
||||
@ -98,9 +101,9 @@ describe('ui reducer', () => {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
}
|
||||
const action = { payload: { show: false }};
|
||||
};
|
||||
const action = { payload: { show: false } };
|
||||
const result = _toggleInstancesTooltip(uiState, action);
|
||||
expect(result).toEqual(expectedUiState);
|
||||
});
|
||||
})
|
||||
});
|
||||
|
@ -10,48 +10,45 @@ import {
|
||||
} from '@state/selectors';
|
||||
|
||||
describe('Redux selectors and Apollo helpers', () => {
|
||||
|
||||
describe('getInstanceStatuses', () => {
|
||||
|
||||
it('gathers instance statuses correctly', () => {
|
||||
const service = {
|
||||
instances: [
|
||||
{ status: 'RUNNING' },
|
||||
{ status: 'RUNNING' },
|
||||
{ status: 'READY' },
|
||||
{ status: 'RUNNING' },
|
||||
{ status: 'INCOMPLETE' },
|
||||
{ status: 'READY' },
|
||||
{ status: 'OFFLINE' },
|
||||
{ status: 'STOPPED' },
|
||||
{ status: 'STOPPED' },
|
||||
{ status: 'RUNNING' }
|
||||
]
|
||||
};
|
||||
const expectedResult = [
|
||||
{ status: 'RUNNING', count: 4 },
|
||||
{ status: 'READY', count: 2 },
|
||||
{ status: 'INCOMPLETE', count: 1 },
|
||||
{ status: 'OFFLINE', count: 1 },
|
||||
{ status: 'STOPPED', count: 2 }
|
||||
];
|
||||
const result = getInstanceStatuses(service);
|
||||
expect(result).toEqual(expectedResult);
|
||||
const service = {
|
||||
instances: [
|
||||
{ status: 'RUNNING' },
|
||||
{ status: 'RUNNING' },
|
||||
{ status: 'READY' },
|
||||
{ status: 'RUNNING' },
|
||||
{ status: 'INCOMPLETE' },
|
||||
{ status: 'READY' },
|
||||
{ status: 'OFFLINE' },
|
||||
{ status: 'STOPPED' },
|
||||
{ status: 'STOPPED' },
|
||||
{ status: 'RUNNING' }
|
||||
]
|
||||
};
|
||||
const expectedResult = [
|
||||
{ status: 'RUNNING', count: 4 },
|
||||
{ status: 'READY', count: 2 },
|
||||
{ status: 'INCOMPLETE', count: 1 },
|
||||
{ status: 'OFFLINE', count: 1 },
|
||||
{ status: 'STOPPED', count: 2 }
|
||||
];
|
||||
const result = getInstanceStatuses(service);
|
||||
expect(result).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
it('does not throw a hissy fit if there are no instances', () => {
|
||||
const service = {
|
||||
instances: []
|
||||
};
|
||||
const expectedResult = [];
|
||||
const result = getInstanceStatuses(service);
|
||||
expect(result).toEqual(expectedResult);
|
||||
const service = {
|
||||
instances: []
|
||||
};
|
||||
const expectedResult = [];
|
||||
const result = getInstanceStatuses(service);
|
||||
expect(result).toEqual(expectedResult);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getInstancesActive', () => {
|
||||
|
||||
it('returns true if all instances\' status is active', () => {
|
||||
it("returns true if all instances' status is active", () => {
|
||||
const statuses = [
|
||||
{ status: 'RUNNING' },
|
||||
{ status: 'READY' },
|
||||
@ -63,7 +60,7 @@ describe('Redux selectors and Apollo helpers', () => {
|
||||
expect(result).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
it('returns false if no instances\' status is active', () => {
|
||||
it("returns false if no instances' status is active", () => {
|
||||
const statuses = [
|
||||
{ status: 'STOPPING' },
|
||||
{ status: 'FAILED' },
|
||||
@ -75,7 +72,7 @@ describe('Redux selectors and Apollo helpers', () => {
|
||||
expect(result).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
it('returns true if some instances\' status is active', () => {
|
||||
it("returns true if some instances' status is active", () => {
|
||||
const statuses = [
|
||||
{ status: 'STOPPING' },
|
||||
{ status: 'FAILED' },
|
||||
@ -89,7 +86,6 @@ describe('Redux selectors and Apollo helpers', () => {
|
||||
});
|
||||
|
||||
describe('getInstancesHealthy', () => {
|
||||
|
||||
it('returns the number of healthy instances correctly', () => {
|
||||
const instances = [
|
||||
{ healthy: 'HEALTHY' },
|
||||
@ -106,39 +102,31 @@ describe('Redux selectors and Apollo helpers', () => {
|
||||
});
|
||||
|
||||
describe('getService', () => {
|
||||
|
||||
it('returns the service decorated with details for display correctly', () => {
|
||||
|
||||
const result = getService(nginxService, 0);
|
||||
expect(result).toEqual(nginxExpectedResult);
|
||||
});
|
||||
|
||||
it('returns the consul service decorated with details for display correctly', () => {
|
||||
|
||||
const result = getService(consulService, 1);
|
||||
expect(result).toEqual(consulExpectedResult);
|
||||
});
|
||||
});
|
||||
|
||||
describe('processServices', () => {
|
||||
|
||||
it('returns the services decorated with details for display correctly', () => {
|
||||
const services = [
|
||||
nginxService,
|
||||
consulService
|
||||
];
|
||||
const expectedResult = [
|
||||
nginxExpectedResult,
|
||||
consulExpectedResult
|
||||
];
|
||||
const services = [nginxService, consulService];
|
||||
const expectedResult = [nginxExpectedResult, consulExpectedResult];
|
||||
const result = processServices(services);
|
||||
expect(result).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
it('removes deleted services', () => {
|
||||
const services = [{
|
||||
status: 'DELETED'
|
||||
}];
|
||||
const services = [
|
||||
{
|
||||
status: 'DELETED'
|
||||
}
|
||||
];
|
||||
const expectedResult = [];
|
||||
const result = processServices(services);
|
||||
expect(result).toEqual(expectedResult);
|
||||
@ -146,16 +134,12 @@ describe('Redux selectors and Apollo helpers', () => {
|
||||
});
|
||||
|
||||
describe('processServicesForTopology', () => {
|
||||
|
||||
it('returns the services decorated with details for display correctly', () => {
|
||||
const services = [
|
||||
{
|
||||
...nginxService,
|
||||
id: 'nginx-service-0',
|
||||
connections: [
|
||||
'consul-service-0',
|
||||
'consul-service-1'
|
||||
]
|
||||
connections: ['consul-service-0', 'consul-service-1']
|
||||
},
|
||||
{
|
||||
...nginxService,
|
||||
@ -164,9 +148,7 @@ describe('Redux selectors and Apollo helpers', () => {
|
||||
{
|
||||
...consulService,
|
||||
id: 'consul-service-0',
|
||||
connections: [
|
||||
'consul-service-1'
|
||||
]
|
||||
connections: ['consul-service-1']
|
||||
},
|
||||
{
|
||||
...consulService,
|
||||
@ -177,10 +159,7 @@ describe('Redux selectors and Apollo helpers', () => {
|
||||
{
|
||||
...nginxExpectedResult,
|
||||
id: 'nginx-service-0',
|
||||
connections: [
|
||||
'consul-service-0',
|
||||
'consul-service-1'
|
||||
],
|
||||
connections: ['consul-service-0', 'consul-service-1'],
|
||||
connected: true,
|
||||
index: 0
|
||||
},
|
||||
@ -193,9 +172,7 @@ describe('Redux selectors and Apollo helpers', () => {
|
||||
{
|
||||
...consulExpectedResult,
|
||||
id: 'consul-service-0',
|
||||
connections: [
|
||||
'consul-service-1'
|
||||
],
|
||||
connections: ['consul-service-1'],
|
||||
connected: true,
|
||||
index: 2
|
||||
},
|
||||
|
@ -48,12 +48,8 @@
|
||||
"tap-xunit": "^1.7.0"
|
||||
},
|
||||
"ava": {
|
||||
"files": [
|
||||
"test/*.js"
|
||||
],
|
||||
"source": [
|
||||
"src/*.js"
|
||||
],
|
||||
"files": ["test/*.js"],
|
||||
"source": ["src/*.js"],
|
||||
"failFast": true
|
||||
}
|
||||
}
|
||||
|
@ -268,8 +268,10 @@
|
||||
},
|
||||
{
|
||||
"id": "1ad6dd4e-15bd-4ea5-a0db-bf071d7958c4",
|
||||
"slug": "extra service reported by containerpilot: redbaloonservice-https",
|
||||
"name": "Extra service reported by ContainerPilot: RedBaloonService-HTTPS",
|
||||
"slug":
|
||||
"extra service reported by containerpilot: redbaloonservice-https",
|
||||
"name":
|
||||
"Extra service reported by ContainerPilot: RedBaloonService-HTTPS",
|
||||
"deploymentGroupId": "ba217234-9b1b-41a7-8079-08f9a4aadb0f",
|
||||
"status": "ACTIVE"
|
||||
},
|
||||
@ -578,7 +580,8 @@
|
||||
"deploymentGroupId": "ba217234-9b1b-41a7-8079-08f9a4aadb0f",
|
||||
"serviceId": "31663285-2b58-4f92-b6f5-3ef34591c3a3",
|
||||
"machineId": "c9a49cff-4438-460f-bc46-610bfecbddca",
|
||||
"name": "instance-Extra service reported by ContainerPilot: CartService-HTTPS-0"
|
||||
"name":
|
||||
"instance-Extra service reported by ContainerPilot: CartService-HTTPS-0"
|
||||
},
|
||||
{
|
||||
"id": "instance-31663285-2b58-4f92-b6f5-3ef34591c3a3-1",
|
||||
@ -587,7 +590,8 @@
|
||||
"deploymentGroupId": "ba217234-9b1b-41a7-8079-08f9a4aadb0f",
|
||||
"serviceId": "31663285-2b58-4f92-b6f5-3ef34591c3a3",
|
||||
"machineId": "4252ddb5-e9b4-4d1e-aa53-6e1bdcdeab30",
|
||||
"name": "instance-Extra service reported by ContainerPilot: CartService-HTTPS-1"
|
||||
"name":
|
||||
"instance-Extra service reported by ContainerPilot: CartService-HTTPS-1"
|
||||
},
|
||||
{
|
||||
"id": "instance-31663285-2b58-4f92-b6f5-3ef34591c3a3-2",
|
||||
@ -596,7 +600,8 @@
|
||||
"deploymentGroupId": "ba217234-9b1b-41a7-8079-08f9a4aadb0f",
|
||||
"serviceId": "31663285-2b58-4f92-b6f5-3ef34591c3a3",
|
||||
"machineId": "783c5eb2-2145-4fd2-a22f-274bec3b2ffc",
|
||||
"name": "instance-Extra service reported by ContainerPilot: CartService-HTTPS-2"
|
||||
"name":
|
||||
"instance-Extra service reported by ContainerPilot: CartService-HTTPS-2"
|
||||
},
|
||||
{
|
||||
"id": "instance-31663285-2b58-4f92-b6f5-3ef34591c3a3-3",
|
||||
@ -605,7 +610,8 @@
|
||||
"deploymentGroupId": "ba217234-9b1b-41a7-8079-08f9a4aadb0f",
|
||||
"serviceId": "31663285-2b58-4f92-b6f5-3ef34591c3a3",
|
||||
"machineId": "f8a54a11-93f4-4b0a-9c80-be6542393448",
|
||||
"name": "instance-Extra service reported by ContainerPilot: CartService-HTTPS-3"
|
||||
"name":
|
||||
"instance-Extra service reported by ContainerPilot: CartService-HTTPS-3"
|
||||
},
|
||||
{
|
||||
"id": "instance-3b954132-49fc-405c-9d91-c59b8953d7b8-0",
|
||||
@ -1747,7 +1753,8 @@
|
||||
"healthy": "UNKNOWN",
|
||||
"deploymentGroupId": "ba217234-9b1b-41a7-8079-08f9a4aadb0f",
|
||||
"serviceId": "1ad6dd4e-15bd-4ea5-a0db-bf071d7958c4",
|
||||
"name": "instance-Extra service reported by ContainerPilot: RedBaloonService-HTTPS-0",
|
||||
"name":
|
||||
"instance-Extra service reported by ContainerPilot: RedBaloonService-HTTPS-0",
|
||||
"machineId": "20f5bf63-c7d7-4b80-bbe0-63dd744f1b72"
|
||||
},
|
||||
{
|
||||
@ -1756,7 +1763,8 @@
|
||||
"healthy": "UNKNOWN",
|
||||
"deploymentGroupId": "ba217234-9b1b-41a7-8079-08f9a4aadb0f",
|
||||
"serviceId": "1ad6dd4e-15bd-4ea5-a0db-bf071d7958c4",
|
||||
"name": "instance-Extra service reported by ContainerPilot: RedBaloonService-HTTPS-1",
|
||||
"name":
|
||||
"instance-Extra service reported by ContainerPilot: RedBaloonService-HTTPS-1",
|
||||
"machineId": "bf10d972-17e0-4267-908e-4a8184d7c164"
|
||||
},
|
||||
{
|
||||
@ -1765,7 +1773,8 @@
|
||||
"healthy": "UNKNOWN",
|
||||
"deploymentGroupId": "ba217234-9b1b-41a7-8079-08f9a4aadb0f",
|
||||
"serviceId": "1ad6dd4e-15bd-4ea5-a0db-bf071d7958c4",
|
||||
"name": "instance-Extra service reported by ContainerPilot: RedBaloonService-HTTPS-2",
|
||||
"name":
|
||||
"instance-Extra service reported by ContainerPilot: RedBaloonService-HTTPS-2",
|
||||
"machineId": "d7204a39-5005-4925-a2d5-02afbb2457db"
|
||||
},
|
||||
{
|
||||
@ -1774,7 +1783,8 @@
|
||||
"healthy": "UNKNOWN",
|
||||
"deploymentGroupId": "ba217234-9b1b-41a7-8079-08f9a4aadb0f",
|
||||
"serviceId": "1ad6dd4e-15bd-4ea5-a0db-bf071d7958c4",
|
||||
"name": "instance-Extra service reported by ContainerPilot: RedBaloonService-HTTPS-3",
|
||||
"name":
|
||||
"instance-Extra service reported by ContainerPilot: RedBaloonService-HTTPS-3",
|
||||
"machineId": "80dac6f7-e459-4f42-b824-5abdd6f13d41"
|
||||
},
|
||||
{
|
||||
@ -1783,7 +1793,8 @@
|
||||
"healthy": "UNKNOWN",
|
||||
"deploymentGroupId": "ba217234-9b1b-41a7-8079-08f9a4aadb0f",
|
||||
"serviceId": "1ad6dd4e-15bd-4ea5-a0db-bf071d7958c4",
|
||||
"name": "instance-Extra service reported by ContainerPilot: RedBaloonService-HTTPS-4",
|
||||
"name":
|
||||
"instance-Extra service reported by ContainerPilot: RedBaloonService-HTTPS-4",
|
||||
"machineId": "dbb819d5-9df2-48e1-8412-7cd48ca4c186"
|
||||
},
|
||||
{
|
||||
|
@ -53,4 +53,4 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
||||
|
@ -34466,4 +34466,4 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
||||
|
@ -34490,4 +34490,4 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
||||
|
@ -22993,4 +22993,4 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
||||
|
@ -48,17 +48,11 @@ const cleanQuery = (q = {}) => JSON.parse(JSON.stringify(q));
|
||||
let metricDataIndex = 0;
|
||||
|
||||
const getMetrics = query => {
|
||||
const {
|
||||
names,
|
||||
start,
|
||||
end,
|
||||
instanceId
|
||||
} = query;
|
||||
const { names, start, end, instanceId } = query;
|
||||
|
||||
const metrics = names.reduce((metrics, name) => {
|
||||
|
||||
// pick one of the three metric data jsons, so there's variety
|
||||
const index = metricDataIndex%metricData.length;
|
||||
const index = metricDataIndex % metricData.length;
|
||||
metricDataIndex++;
|
||||
|
||||
const md = metricData[index].find(md => md.name === name);
|
||||
@ -69,17 +63,20 @@ const getMetrics = query => {
|
||||
|
||||
// how many records do we need?
|
||||
const duration = e.diff(s); // duration for which we need data
|
||||
const records = Math.floor(duration/15000); // new metric record every 15 secs
|
||||
const records = Math.floor(duration / 15000); // new metric record every 15 secs
|
||||
|
||||
const requiredMetrics = [];
|
||||
let i = 0;
|
||||
const time = moment(s);
|
||||
// start at a random point within the dataset for variety
|
||||
const randomIndex = Math.round(Math.random() * m.length);
|
||||
while(i < records) {
|
||||
const index = (randomIndex + i)%m.length; // loop if not enough data
|
||||
while (i < records) {
|
||||
const index = (randomIndex + i) % m.length; // loop if not enough data
|
||||
const requiredMetric = m[index];
|
||||
requiredMetric.time = time.add(15, 'seconds').utc().format(); // we should have a new record every 15 secs
|
||||
requiredMetric.time = time
|
||||
.add(15, 'seconds')
|
||||
.utc()
|
||||
.format(); // we should have a new record every 15 secs
|
||||
requiredMetrics.push(requiredMetric);
|
||||
i++;
|
||||
}
|
||||
@ -90,13 +87,13 @@ const getMetrics = query => {
|
||||
start: s.utc().format(),
|
||||
end: time.utc().format(), // this will be used by the frontend for the next fetch
|
||||
metrics: requiredMetrics
|
||||
}
|
||||
};
|
||||
metrics.push(requiredMetricData);
|
||||
return metrics;
|
||||
}, []);
|
||||
}, []);
|
||||
|
||||
return Promise.resolve(metrics);
|
||||
}
|
||||
};
|
||||
|
||||
const getInstances = query => {
|
||||
const metricsResolver = ({ id }) => query =>
|
||||
@ -175,13 +172,12 @@ const getServices = query => {
|
||||
return ret;
|
||||
});
|
||||
|
||||
return Promise.resolve(services)
|
||||
.then((services) => {
|
||||
if(!services || !services.length) {
|
||||
throw Boom.notFound();
|
||||
}
|
||||
return services;
|
||||
});
|
||||
return Promise.resolve(services).then(services => {
|
||||
if (!services || !services.length) {
|
||||
throw Boom.notFound();
|
||||
}
|
||||
return services;
|
||||
});
|
||||
};
|
||||
|
||||
const getDeploymentGroups = query => {
|
||||
@ -199,8 +195,8 @@ const getDeploymentGroups = query => {
|
||||
|
||||
return Promise.resolve(
|
||||
deploymentGroups.filter(find(cleanQuery(query))).map(addNestedResolvers)
|
||||
).then((deploymentGroups) => {
|
||||
if(!deploymentGroups || !deploymentGroups.length) {
|
||||
).then(deploymentGroups => {
|
||||
if (!deploymentGroups || !deploymentGroups.length) {
|
||||
throw Boom.notFound();
|
||||
}
|
||||
return deploymentGroups;
|
||||
@ -406,11 +402,16 @@ const updateServiceAndInstancesStatus = (
|
||||
instancesStatus
|
||||
) => {
|
||||
return Promise.all([
|
||||
getServices({ id: serviceId })/* ,
|
||||
getServices({
|
||||
id: serviceId
|
||||
}) /* ,
|
||||
getServices({ parentId: serviceId }) */
|
||||
])
|
||||
.then(services => {
|
||||
return services.reduce((services, service) => services.concat(service), [])
|
||||
return services.reduce(
|
||||
(services, service) => services.concat(service),
|
||||
[]
|
||||
);
|
||||
})
|
||||
.then(services => {
|
||||
updateServiceStatus(services, serviceStatus);
|
||||
@ -431,7 +432,9 @@ const updateServiceAndInstancesStatus = (
|
||||
})
|
||||
.then(() =>
|
||||
Promise.all([
|
||||
getUnfilteredServices({ id: serviceId })/* ,
|
||||
getUnfilteredServices({
|
||||
id: serviceId
|
||||
}) /* ,
|
||||
getUnfilteredServices({ parentId: serviceId }) */
|
||||
])
|
||||
)
|
||||
@ -536,12 +539,7 @@ const parseEnvVars = (str = '') =>
|
||||
const findEnvInterpolation = (str = '') =>
|
||||
uniq(str.match(INTERPOLATE_REGEX).map(name => name.replace(/^\$/, '')));
|
||||
|
||||
const config = ({
|
||||
environment = '',
|
||||
files = [],
|
||||
raw = '',
|
||||
_plain = false
|
||||
}) => {
|
||||
const config = ({ environment = '', files = [], raw = '', _plain = false }) => {
|
||||
const interpolatableNames = findEnvInterpolation(raw);
|
||||
const interpolatableEnv = parseEnvVars(environment);
|
||||
|
||||
@ -588,11 +586,10 @@ const config = ({
|
||||
config: Object.assign(service.config, {
|
||||
id: hasha(JSON.stringify(service.config)),
|
||||
environment: Object.keys(service.config.environment).map(name => ({
|
||||
name,
|
||||
id: hasha(JSON.stringify(service.config.environment[name])),
|
||||
value: service.config.environment[name]
|
||||
})
|
||||
)
|
||||
name,
|
||||
id: hasha(JSON.stringify(service.config.environment[name])),
|
||||
value: service.config.environment[name]
|
||||
}))
|
||||
})
|
||||
})
|
||||
);
|
||||
|
@ -203,7 +203,6 @@ type Datacenter {
|
||||
region: String!
|
||||
}
|
||||
|
||||
|
||||
# we probably wont use some of these queries or arguments
|
||||
# but this way we expose the entire db through gql
|
||||
type Query {
|
||||
@ -265,7 +264,13 @@ type Query {
|
||||
): [Service]
|
||||
importableDeploymentGroups: [DeploymentGroup]
|
||||
# start and end should be .toISOString() date strings
|
||||
metrics(deploymentGroupId: ID!, names: [MetricName]!, instances: [ID]!, start: String!, end: String!): [InstanceMetric]
|
||||
metrics(
|
||||
deploymentGroupId: ID!
|
||||
names: [MetricName]!
|
||||
instances: [ID]!
|
||||
start: String!
|
||||
end: String!
|
||||
): [InstanceMetric]
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
|
@ -152,17 +152,17 @@ export const Button = styled.button`
|
||||
appearance: button;
|
||||
|
||||
&::-moz-focus-inner,
|
||||
&[type="button"]::-moz-focus-inner,
|
||||
&[type="reset"]::-moz-focus-inner,
|
||||
&[type="submit"]::-moz-focus-inner {
|
||||
&[type='button']::-moz-focus-inner,
|
||||
&[type='reset']::-moz-focus-inner,
|
||||
&[type='submit']::-moz-focus-inner {
|
||||
border-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&:-moz-focusring,
|
||||
&[type="button"]:-moz-focusring,
|
||||
&[type="reset"]:-moz-focusring,
|
||||
&[type="submit"]:-moz-focusring {
|
||||
&[type='button']:-moz-focusring,
|
||||
&[type='reset']:-moz-focusring,
|
||||
&[type='submit']:-moz-focusring {
|
||||
outline: ${remcalc(1)} dotted ButtonText;
|
||||
}
|
||||
`;
|
||||
@ -174,24 +174,24 @@ export const Input = styled.input`
|
||||
margin: 0;
|
||||
overflow: visible;
|
||||
|
||||
&[type="checkbox"],
|
||||
&[type="radio"] {
|
||||
&[type='checkbox'],
|
||||
&[type='radio'] {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&[type="number"]::-webkit-inner-spin-button,
|
||||
&[type="number"]::-webkit-outer-spin-button {
|
||||
&[type='number']::-webkit-inner-spin-button,
|
||||
&[type='number']::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
&[type="search"] {
|
||||
&[type='search'] {
|
||||
appearance: textfield;
|
||||
outline-offset: ${remcalc(-2)};
|
||||
}
|
||||
|
||||
&[type="search"]::-webkit-search-cancel-button,
|
||||
&[type="search"]::-webkit-search-decoration {
|
||||
&[type='search']::-webkit-search-cancel-button,
|
||||
&[type='search']::-webkit-search-decoration {
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
|
@ -36,11 +36,7 @@ const Anchor = ({ children, ...rest }) => {
|
||||
const Views = [() => (to ? StyledLink : null), () => StyledAnchor];
|
||||
const View = Views.reduce((sel, view) => (sel ? sel : view()), null);
|
||||
|
||||
return (
|
||||
<View {...rest}>
|
||||
{children}
|
||||
</View>
|
||||
);
|
||||
return <View {...rest}>{children}</View>;
|
||||
};
|
||||
|
||||
Anchor.propTypes = {
|
||||
|
@ -21,6 +21,6 @@ export default css`
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: ${theme.background};
|
||||
font-family: 'Libre Franklin'
|
||||
font-family: 'Libre Franklin';
|
||||
}
|
||||
`;
|
||||
|
@ -18,4 +18,6 @@ export default Component =>
|
||||
? Component.extend`
|
||||
${alignsFromProps};
|
||||
`
|
||||
: styled(Component)`${alignsFromProps}`;
|
||||
: styled(Component)`
|
||||
${alignsFromProps};
|
||||
`;
|
||||
|
@ -33,4 +33,6 @@ export default Component =>
|
||||
? Component.extend`
|
||||
${unitsFromProps};
|
||||
`
|
||||
: styled(Component)`${unitsFromProps}`;
|
||||
: styled(Component)`
|
||||
${unitsFromProps};
|
||||
`;
|
||||
|
@ -11,9 +11,9 @@ export const tooltipShadow = `0 ${remcalc(2)} ${remcalc(6)} ${remcalc(
|
||||
export const modalShadow = `0 0 ${remcalc(6)} ${remcalc(1)} rgba(0, 0, 0, 0.1)`;
|
||||
|
||||
export const border = {
|
||||
checked: css`${remcalc(1)} solid ${props => props.theme.primary}`,
|
||||
unchecked: css`${remcalc(1)} solid ${props => props.theme.grey}`,
|
||||
confirmed: css`${remcalc(1)} solid ${props => props.theme.grey}`,
|
||||
error: css`${remcalc(1)} solid ${props => props.theme.red}`,
|
||||
secondary: css`${remcalc(1)} solid ${props => props.theme.secondaryActive}`,
|
||||
checked: css`${remcalc(1)} solid ${props => props.theme.primary};`,
|
||||
unchecked: css`${remcalc(1)} solid ${props => props.theme.grey};`,
|
||||
confirmed: css`${remcalc(1)} solid ${props => props.theme.grey};`,
|
||||
error: css`${remcalc(1)} solid ${props => props.theme.red};`,
|
||||
secondary: css`${remcalc(1)} solid ${props => props.theme.secondaryActive};`
|
||||
};
|
||||
|
@ -4,9 +4,10 @@ import { Row } from 'react-styled-flexboxgrid';
|
||||
/**
|
||||
* @example ./usage.md
|
||||
*/
|
||||
export default ({ children, ...rest }) =>
|
||||
export default ({ children, ...rest }) => (
|
||||
<Row name="breadcrum" {...rest}>
|
||||
{children}
|
||||
</Row>;
|
||||
</Row>
|
||||
);
|
||||
|
||||
export { default as Item } from './item';
|
||||
|
@ -21,10 +21,11 @@ const Arrow = styled.div`
|
||||
margin: ${remcalc(3)} ${remcalc(10)} ${remcalc(3)} ${remcalc(10)};
|
||||
`;
|
||||
|
||||
export default ({ children, ...rest }) =>
|
||||
export default ({ children, ...rest }) => (
|
||||
<div>
|
||||
<Name name="breadcrum-item" {...rest}>
|
||||
{children}
|
||||
</Name>
|
||||
<Arrow />
|
||||
</div>;
|
||||
</div>
|
||||
);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user