mirror of
https://github.com/yldio/copilot.git
synced 2024-12-01 07:30:07 +02:00
feat(ui-toolkit, cp-frontend): Add clear status and health messaging and refactor tooltips use
This commit is contained in:
parent
24bee629e8
commit
bc026b2341
@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, { Component } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import remcalc from 'remcalc';
|
import remcalc from 'remcalc';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
@ -7,10 +7,16 @@ import titleCase from 'title-case';
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
|
CardInfo,
|
||||||
CardView,
|
CardView,
|
||||||
CardMeta,
|
CardMeta,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
CardDescription,
|
CardDescription,
|
||||||
|
HealthyIcon,
|
||||||
|
Tooltip,
|
||||||
|
TooltipLabel,
|
||||||
|
P,
|
||||||
|
Label,
|
||||||
typography
|
typography
|
||||||
} from 'joyent-ui-toolkit';
|
} from 'joyent-ui-toolkit';
|
||||||
|
|
||||||
@ -29,31 +35,18 @@ const STATUSES = [
|
|||||||
'UNKNOWN'
|
'UNKNOWN'
|
||||||
];
|
];
|
||||||
|
|
||||||
const Span = styled.span`
|
const Dot = styled.span`
|
||||||
${typography.fontFamily};
|
|
||||||
${typography.normal};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const Dot = styled.div`
|
|
||||||
margin-right: ${remcalc(6)};
|
margin-right: ${remcalc(6)};
|
||||||
width: ${remcalc(6)};
|
width: ${remcalc(6)};
|
||||||
height: ${remcalc(6)};
|
height: ${remcalc(6)};
|
||||||
border-radius: ${remcalc(3)};
|
border-radius: ${remcalc(3)};
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
||||||
${isOr('provisioning', 'ready', 'active')`
|
${isOr('provisioning', 'ready', 'active', 'running')`
|
||||||
background-color: ${props => props.theme.primary};
|
|
||||||
`};
|
|
||||||
|
|
||||||
${is('running')`
|
|
||||||
background-color: ${props => props.theme.green};
|
background-color: ${props => props.theme.green};
|
||||||
`};
|
`};
|
||||||
|
|
||||||
${is('stopping')`
|
${isOr('stopping', 'stopped')`
|
||||||
background-color: orange;
|
|
||||||
`};
|
|
||||||
|
|
||||||
${is('stopped')`
|
|
||||||
background-color: ${props => props.theme.grey};
|
background-color: ${props => props.theme.grey};
|
||||||
`};
|
`};
|
||||||
|
|
||||||
@ -66,23 +59,6 @@ const Dot = styled.div`
|
|||||||
`};
|
`};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StatusBadge = ({ status }) => {
|
|
||||||
const props = STATUSES.reduce(
|
|
||||||
(acc, name) =>
|
|
||||||
Object.assign(acc, {
|
|
||||||
[name.toLowerCase()]: name === status
|
|
||||||
}),
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Span>
|
|
||||||
<Dot {...props} />
|
|
||||||
{titleCase(status)}
|
|
||||||
</Span>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const StyledCard = Card.extend`
|
const StyledCard = Card.extend`
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|
||||||
@ -92,37 +68,91 @@ const StyledCard = Card.extend`
|
|||||||
border-bottom-width: 0;
|
border-bottom-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:nth-child(odd) {
|
background-color: ${props => props.theme.white};
|
||||||
|
|
||||||
|
${isOr('stopping', 'stopped', 'offline', 'destroyed', 'failed', 'deleted', 'incomplete', 'unknown')`
|
||||||
background-color: ${props => props.theme.background};
|
background-color: ${props => props.theme.background};
|
||||||
|
|
||||||
& [name="card-options"] > button {
|
& [name="card-options"] > button {
|
||||||
background-color: ${props => props.theme.background};
|
background-color: ${props => props.theme.background};
|
||||||
}
|
}`
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const InstanceCard = ({
|
const InstanceCard = ({
|
||||||
instance = {},
|
instance = {},
|
||||||
onOptionsClick = () => null,
|
onOptionsClick = () => null,
|
||||||
toggleCollapsed = () => null
|
toggleCollapsed = () => null,
|
||||||
}) =>
|
onHealthMouseOver,
|
||||||
<StyledCard collapsed={true} key={instance.uuid}>
|
onStatusMouseOver,
|
||||||
|
onMouseOut
|
||||||
|
}) => {
|
||||||
|
|
||||||
|
const statusProps = STATUSES.reduce(
|
||||||
|
(acc, name) =>
|
||||||
|
Object.assign(acc, {
|
||||||
|
[name.toLowerCase()]: name === instance.status
|
||||||
|
}),
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
|
const label = instance.healthy.toLowerCase();
|
||||||
|
const icon = <HealthyIcon healthy={instance.healthy} />;
|
||||||
|
|
||||||
|
const handleHealthMouseOver = (evt) => {
|
||||||
|
onHealthMouseOver(evt, instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleStatusMouseOver = (evt) => {
|
||||||
|
onStatusMouseOver(evt, instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleMouseOut = (evt) => {
|
||||||
|
onMouseOut(evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledCard collapsed={true} key={instance.uuid} {...statusProps}>
|
||||||
<CardView>
|
<CardView>
|
||||||
<CardMeta onClick={toggleCollapsed}>
|
|
||||||
<CardTitle>
|
<CardTitle>
|
||||||
{instance.name}
|
{instance.name}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<CardDescription>
|
<CardDescription>
|
||||||
<StatusBadge status={instance.status} />
|
<div
|
||||||
|
onMouseOver={handleHealthMouseOver}
|
||||||
|
onMouseOut={handleMouseOut}
|
||||||
|
>
|
||||||
|
<CardInfo
|
||||||
|
icon={icon}
|
||||||
|
iconPosition='left'
|
||||||
|
label={label}
|
||||||
|
color='dark'
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</CardDescription>
|
||||||
|
<CardDescription>
|
||||||
|
<div
|
||||||
|
onMouseOver={handleStatusMouseOver}
|
||||||
|
onMouseOut={handleMouseOut}
|
||||||
|
>
|
||||||
|
<Label>
|
||||||
|
<Dot {...statusProps} />
|
||||||
|
{titleCase(instance.status)}
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardMeta>
|
|
||||||
</CardView>
|
</CardView>
|
||||||
</StyledCard>;
|
</StyledCard>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
InstanceCard.propTypes = {
|
InstanceCard.propTypes = {
|
||||||
instance: PropTypes.object,
|
instance: PropTypes.object,
|
||||||
onOptionsClick: PropTypes.func,
|
onOptionsClick: PropTypes.func,
|
||||||
toggleCollapsed: PropTypes.func
|
toggleCollapsed: PropTypes.func,
|
||||||
|
onHealthMouseOver: PropTypes.func,
|
||||||
|
onStatusMouseOver: PropTypes.func,
|
||||||
|
onMouseOut: PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
export default InstanceCard;
|
export default InstanceCard;
|
||||||
|
@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import forceArray from 'force-array';
|
import forceArray from 'force-array';
|
||||||
import sortBy from 'lodash.sortby';
|
import sortBy from 'lodash.sortby';
|
||||||
|
import { isNot } from 'styled-is';
|
||||||
|
|
||||||
import { InstancesIcon, HealthyIcon, UnhealthyIcon } from 'joyent-ui-toolkit';
|
import { InstancesIcon, HealthyIcon, UnhealthyIcon } from 'joyent-ui-toolkit';
|
||||||
import Status from './status';
|
import Status from './status';
|
||||||
@ -27,10 +28,16 @@ const StyledCardHeader = styled(CardHeader)`
|
|||||||
const TitleInnerContainer = styled.div`
|
const TitleInnerContainer = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: center;
|
justify-content: left;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const StyledAnchor = styled(Anchor)`
|
||||||
|
${isNot('active')`
|
||||||
|
color: ${props => props.theme.text}
|
||||||
|
`};
|
||||||
|
`;
|
||||||
|
|
||||||
const ServiceListItem = ({
|
const ServiceListItem = ({
|
||||||
onQuickActionsClick = () => {},
|
onQuickActionsClick = () => {},
|
||||||
deploymentGroup = '',
|
deploymentGroup = '',
|
||||||
@ -42,7 +49,7 @@ const ServiceListItem = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const children = sortBy(forceArray(service.children), ['slug']);
|
const children = sortBy(forceArray(service.children), ['slug']);
|
||||||
const isServiceInactive = service.status && service.status !== 'ACTIVE';
|
// const isServiceInactive = service.status && service.status !== 'ACTIVE';
|
||||||
const to = `/deployment-groups/${deploymentGroup}/services/${service.slug}`;
|
const to = `/deployment-groups/${deploymentGroup}/services/${service.slug}`;
|
||||||
|
|
||||||
const instancesCount = children.length
|
const instancesCount = children.length
|
||||||
@ -64,9 +71,9 @@ const ServiceListItem = ({
|
|||||||
</CardTitle>
|
</CardTitle>
|
||||||
: <CardTitle>
|
: <CardTitle>
|
||||||
<TitleInnerContainer>
|
<TitleInnerContainer>
|
||||||
<Anchor to={to} disabled={isServiceInactive} secondary>
|
<StyledAnchor to={to} secondary active={service.instancesActive}>
|
||||||
{service.name}
|
{service.name}
|
||||||
</Anchor>
|
</StyledAnchor>
|
||||||
</TitleInnerContainer>
|
</TitleInnerContainer>
|
||||||
</CardTitle>;
|
</CardTitle>;
|
||||||
|
|
||||||
@ -87,28 +94,29 @@ const ServiceListItem = ({
|
|||||||
label={`${instancesCount} ${instancesCount > 1
|
label={`${instancesCount} ${instancesCount > 1
|
||||||
? 'instances'
|
? 'instances'
|
||||||
: 'instance'}`}
|
: 'instance'}`}
|
||||||
color={isServiceInactive ? 'disabled' : 'light'}
|
color={!service.instancesActive ? 'disabled' : 'light'}
|
||||||
/>
|
/>
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
<CardOptions onClick={handleCardOptionsClick} />
|
<CardOptions onClick={handleCardOptionsClick} />
|
||||||
</StyledCardHeader>
|
</StyledCardHeader>
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
const healthyInfo = isServiceInactive
|
let healthyInfo = null;
|
||||||
? null
|
if(service.instancesActive) {
|
||||||
: service.instancesHealthy
|
const { total, healthy } = service.instancesHealthy;
|
||||||
? <CardInfo
|
const iconHealthy = total === healthy ? 'HEALTHY' : 'NOT HEALTHY';
|
||||||
icon={<HealthyIcon />}
|
const icon = <HealthyIcon healthy={iconHealthy} />;
|
||||||
iconPosition="left"
|
const label = `${healthy} of ${total} healthy`;
|
||||||
label="Healthy"
|
|
||||||
color="dark"
|
healthyInfo = (
|
||||||
|
<CardInfo
|
||||||
|
icon={icon}
|
||||||
|
iconPosition='left'
|
||||||
|
label={label}
|
||||||
|
color='dark'
|
||||||
/>
|
/>
|
||||||
: <CardInfo
|
)
|
||||||
icon={<UnhealthyIcon />}
|
}
|
||||||
iconPosition="left"
|
|
||||||
label="Unhealthy"
|
|
||||||
color="dark"
|
|
||||||
/>;
|
|
||||||
|
|
||||||
const view = childrenItems.length
|
const view = childrenItems.length
|
||||||
? <CardGroupView>
|
? <CardGroupView>
|
||||||
@ -126,7 +134,7 @@ const ServiceListItem = ({
|
|||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
collapsed={service.collapsed}
|
collapsed={service.collapsed}
|
||||||
disabled={isServiceInactive}
|
active={service.instancesActive}
|
||||||
flat={isChild}
|
flat={isChild}
|
||||||
headed={!isChild}
|
headed={!isChild}
|
||||||
key={service.id}
|
key={service.id}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Tooltip, TooltipButton, TooltipDivider } from 'joyent-ui-toolkit';
|
import { Tooltip, TooltipButton, TooltipDivider, TooltipList } from 'joyent-ui-toolkit';
|
||||||
|
|
||||||
const ServicesQuickActions = ({
|
const ServicesQuickActions = ({
|
||||||
show,
|
show,
|
||||||
@ -18,14 +18,14 @@ const ServicesQuickActions = ({
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const p = Object.keys(position).reduce((p, key) => {
|
/* const p = Object.keys(position).reduce((p, key) => {
|
||||||
if (typeof position[key] === 'number') {
|
if (typeof position[key] === 'number') {
|
||||||
p[key] = `${position[key]}px`;
|
p[key] = `${position[key]}px`;
|
||||||
} else {
|
} else {
|
||||||
p[key] = position[key];
|
p[key] = position[key];
|
||||||
}
|
}
|
||||||
return p;
|
return p;
|
||||||
}, {});
|
}, {}); */
|
||||||
|
|
||||||
const handleRestartClick = evt => {
|
const handleRestartClick = evt => {
|
||||||
onRestartClick(evt, service);
|
onRestartClick(evt, service);
|
||||||
@ -58,31 +58,43 @@ const ServicesQuickActions = ({
|
|||||||
const startService =
|
const startService =
|
||||||
status === 'RUNNING'
|
status === 'RUNNING'
|
||||||
? null
|
? null
|
||||||
: <TooltipButton onClick={handleStartClick} disabled={disabled}>
|
: <li>
|
||||||
|
<TooltipButton onClick={handleStartClick} disabled={disabled}>
|
||||||
Start
|
Start
|
||||||
</TooltipButton>;
|
</TooltipButton>
|
||||||
|
</li>;
|
||||||
|
|
||||||
const stopService =
|
const stopService =
|
||||||
status === 'STOPPED'
|
status === 'STOPPED'
|
||||||
? null
|
? null
|
||||||
: <TooltipButton onClick={handleStopClick} disabled={disabled}>
|
: <li>
|
||||||
|
<TooltipButton onClick={handleStopClick} disabled={disabled}>
|
||||||
Stop
|
Stop
|
||||||
</TooltipButton>;
|
</TooltipButton>
|
||||||
|
</li>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip {...p} onBlur={onBlur}>
|
<Tooltip {...position} onBlur={onBlur}>
|
||||||
|
<TooltipList>
|
||||||
|
<li>
|
||||||
<TooltipButton onClick={handleScaleClick} disabled={disabled}>
|
<TooltipButton onClick={handleScaleClick} disabled={disabled}>
|
||||||
Scale
|
Scale
|
||||||
</TooltipButton>
|
</TooltipButton>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
<TooltipButton onClick={handleRestartClick} disabled={disabled}>
|
<TooltipButton onClick={handleRestartClick} disabled={disabled}>
|
||||||
Restart
|
Restart
|
||||||
</TooltipButton>
|
</TooltipButton>
|
||||||
|
</li>
|
||||||
{startService}
|
{startService}
|
||||||
{stopService}
|
{stopService}
|
||||||
<TooltipDivider />
|
<TooltipDivider />
|
||||||
|
<li>
|
||||||
<TooltipButton onClick={handleDeleteClick} disabled={disabled}>
|
<TooltipButton onClick={handleDeleteClick} disabled={disabled}>
|
||||||
Delete
|
Delete
|
||||||
</TooltipButton>
|
</TooltipButton>
|
||||||
|
</li>
|
||||||
|
</TooltipList>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -6,13 +6,13 @@ import { StatusLoader, P } from 'joyent-ui-toolkit';
|
|||||||
|
|
||||||
const StyledStatusContainer = styled.div`
|
const StyledStatusContainer = styled.div`
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin: 0;
|
margin: 0 0 ${remcalc(15)} 0;
|
||||||
height: ${remcalc(54)};
|
height: ${remcalc(54)};
|
||||||
width: ${remcalc(200)};
|
width: ${remcalc(200)};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledStatus = P.extend`
|
const StyledStatus = P.extend`
|
||||||
margin: 0;
|
margin: 0 0 ${remcalc(6)} 0;
|
||||||
font-size: ${remcalc(13)};
|
font-size: ${remcalc(13)};
|
||||||
line-height: ${remcalc(13)};
|
line-height: ${remcalc(13)};
|
||||||
`;
|
`;
|
||||||
|
@ -1 +1,2 @@
|
|||||||
export { default as InstanceList } from './list';
|
export { default as InstanceList } from './list';
|
||||||
|
export { default as InstancesTooltip } from './tooltip';
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import React from 'react';
|
import React, { Component } from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
import { compose, graphql } from 'react-apollo';
|
import { compose, graphql } from 'react-apollo';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
import InstancesQuery from '@graphql/Instances.gql';
|
import InstancesQuery from '@graphql/Instances.gql';
|
||||||
import forceArray from 'force-array';
|
import forceArray from 'force-array';
|
||||||
import sortBy from 'lodash.sortby';
|
import sortBy from 'lodash.sortby';
|
||||||
@ -8,10 +10,18 @@ import { LayoutContainer } from '@components/layout';
|
|||||||
import { Title } from '@components/navigation';
|
import { Title } from '@components/navigation';
|
||||||
import { Loader, ErrorMessage } from '@components/messaging';
|
import { Loader, ErrorMessage } from '@components/messaging';
|
||||||
import { InstanceListItem, EmptyInstances } from '@components/instances';
|
import { InstanceListItem, EmptyInstances } from '@components/instances';
|
||||||
|
import { toggleInstancesTooltip } from '@root/state/actions';
|
||||||
import { withNotFound, GqlPaths } from '@containers/navigation';
|
import { withNotFound, GqlPaths } from '@containers/navigation';
|
||||||
|
|
||||||
const InstanceList = ({ deploymentGroup, instances = [], loading, error }) => {
|
const InstanceList = ({
|
||||||
|
deploymentGroup,
|
||||||
|
instances = [],
|
||||||
|
loading,
|
||||||
|
error,
|
||||||
|
instancesTooltip,
|
||||||
|
toggleInstancesTooltip
|
||||||
|
}) => {
|
||||||
|
|
||||||
const _title = <Title>Instances</Title>;
|
const _title = <Title>Instances</Title>;
|
||||||
|
|
||||||
if (loading && !forceArray(instances).length) {
|
if (loading && !forceArray(instances).length) {
|
||||||
@ -44,11 +54,48 @@ const InstanceList = ({ deploymentGroup, instances = [], loading, error }) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleHealthMouseOver = (evt, instance) => {
|
||||||
|
handleMouseOver(evt, instance, 'healthy');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleStatusMouseOver = (evt, instance) => {
|
||||||
|
handleMouseOver(evt, instance, 'status');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMouseOver = (evt, instance, type) => {
|
||||||
|
|
||||||
|
const label = evt.currentTarget;
|
||||||
|
const labelRect = label.getBoundingClientRect();
|
||||||
|
const offset = type === 'healthy'
|
||||||
|
? 48 : type === 'status' ? 36 : 0;
|
||||||
|
|
||||||
|
const position = {
|
||||||
|
left:
|
||||||
|
`${window.scrollX + labelRect.left + offset}px`,
|
||||||
|
top: `${window.scrollY + labelRect.bottom}px`
|
||||||
|
};
|
||||||
|
|
||||||
|
const tooltipData = {
|
||||||
|
instance,
|
||||||
|
position,
|
||||||
|
type
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleInstancesTooltip(tooltipData);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMouseOut = (evt) => {
|
||||||
|
toggleInstancesTooltip({ show: false });
|
||||||
|
};
|
||||||
|
|
||||||
const instanceList = instances.map((instance, index) =>
|
const instanceList = instances.map((instance, index) =>
|
||||||
<InstanceListItem
|
<InstanceListItem
|
||||||
instance={instance}
|
instance={instance}
|
||||||
key={instance.id}
|
key={instance.id}
|
||||||
toggleCollapsed={() => null}
|
toggleCollapsed={() => null}
|
||||||
|
onHealthMouseOver={handleHealthMouseOver}
|
||||||
|
onStatusMouseOver={handleStatusMouseOver}
|
||||||
|
onMouseOut={handleMouseOut}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -60,7 +107,17 @@ const InstanceList = ({ deploymentGroup, instances = [], loading, error }) => {
|
|||||||
{_instances}
|
{_instances}
|
||||||
</LayoutContainer>
|
</LayoutContainer>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state, ownProps) => ({
|
||||||
|
instancesTooltip: state.ui.instances.tooltip
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => ({
|
||||||
|
toggleInstancesTooltip: data => dispatch(toggleInstancesTooltip(data))
|
||||||
|
});
|
||||||
|
|
||||||
|
const UiConnect = connect(mapStateToProps, mapDispatchToProps);
|
||||||
|
|
||||||
const InstanceListGql = graphql(InstancesQuery, {
|
const InstanceListGql = graphql(InstancesQuery, {
|
||||||
options(props) {
|
options(props) {
|
||||||
@ -94,6 +151,7 @@ const InstanceListGql = graphql(InstancesQuery, {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
|
UiConnect,
|
||||||
InstanceListGql,
|
InstanceListGql,
|
||||||
withNotFound([
|
withNotFound([
|
||||||
GqlPaths.DEPLOYMENT_GROUP,
|
GqlPaths.DEPLOYMENT_GROUP,
|
||||||
|
68
packages/cp-frontend/src/containers/instances/tooltip.js
Normal file
68
packages/cp-frontend/src/containers/instances/tooltip.js
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { Tooltip, TooltipLabel } from 'joyent-ui-toolkit';
|
||||||
|
import { ServicesQuickActions } from '@components/services';
|
||||||
|
|
||||||
|
const StyledContainer = styled.div`
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
`;
|
||||||
|
|
||||||
|
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',
|
||||||
|
};
|
||||||
|
|
||||||
|
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',
|
||||||
|
stopping: 'Your instance is going to be stopped soon',
|
||||||
|
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',
|
||||||
|
};
|
||||||
|
|
||||||
|
const InstancesTooltip = ({
|
||||||
|
instancesTooltip
|
||||||
|
}) => {
|
||||||
|
|
||||||
|
if(instancesTooltip.show) {
|
||||||
|
const {
|
||||||
|
type,
|
||||||
|
instance
|
||||||
|
} = instancesTooltip;
|
||||||
|
|
||||||
|
const message = type === 'healthy'
|
||||||
|
? healthMessages[instance.healthy.toLowerCase()]
|
||||||
|
: type === 'status'
|
||||||
|
? statusMessages[instance.status.toLowerCase()]
|
||||||
|
: '';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledContainer>
|
||||||
|
<Tooltip {...instancesTooltip.position} secondary>
|
||||||
|
<TooltipLabel>{message}</TooltipLabel>
|
||||||
|
</Tooltip>
|
||||||
|
</StyledContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapStateToProps = (state, ownProps) => ({
|
||||||
|
instancesTooltip: state.ui.instances.tooltip
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => ({});
|
||||||
|
|
||||||
|
const UiConnect = connect(mapStateToProps, mapDispatchToProps);
|
||||||
|
|
||||||
|
export default UiConnect(InstancesTooltip);
|
@ -1,3 +1,4 @@
|
|||||||
export { default as ServiceList } from './list';
|
export { default as ServiceList } from './list';
|
||||||
export { default as ServicesTopology } from './topology';
|
export { default as ServicesTopology } from './topology';
|
||||||
export { default as ServicesMenu } from './menu';
|
export { default as ServicesMenu } from './menu';
|
||||||
|
export { default as ServicesQuickActions } from './quick-actions';
|
||||||
|
@ -6,21 +6,14 @@ import forceArray from 'force-array';
|
|||||||
import sortBy from 'lodash.sortby';
|
import sortBy from 'lodash.sortby';
|
||||||
|
|
||||||
import ServicesQuery from '@graphql/Services.gql';
|
import ServicesQuery from '@graphql/Services.gql';
|
||||||
import ServicesRestartMutation from '@graphql/ServicesRestartMutation.gql';
|
|
||||||
import ServicesStopMutation from '@graphql/ServicesStopMutation.gql';
|
|
||||||
import ServicesStartMutation from '@graphql/ServicesStartMutation.gql';
|
|
||||||
|
|
||||||
import { processServices } from '@root/state/selectors';
|
import { processServices } from '@root/state/selectors';
|
||||||
import { toggleServicesQuickActions } from '@root/state/actions';
|
import { toggleServicesQuickActions } from '@root/state/actions';
|
||||||
|
import { withNotFound, GqlPaths } from '@containers/navigation';
|
||||||
import { LayoutContainer } from '@components/layout';
|
import { LayoutContainer } from '@components/layout';
|
||||||
import { Loader, ErrorMessage } from '@components/messaging';
|
import { Loader, ErrorMessage } from '@components/messaging';
|
||||||
import { ServiceListItem } from '@components/services';
|
import { ServiceListItem } from '@components/services';
|
||||||
|
|
||||||
import { ServicesQuickActions } from '@components/services';
|
|
||||||
|
|
||||||
import { withNotFound, GqlPaths } from '@containers/navigation';
|
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
const StyledContainer = styled.div`
|
||||||
position: relative;
|
position: relative;
|
||||||
`;
|
`;
|
||||||
@ -34,28 +27,13 @@ class ServiceList extends Component {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
ref(name) {
|
|
||||||
this._refs = this._refs || {};
|
|
||||||
|
|
||||||
return el => {
|
|
||||||
this._refs[name] = el;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
deploymentGroup,
|
deploymentGroup,
|
||||||
services,
|
services,
|
||||||
loading,
|
loading,
|
||||||
error,
|
error,
|
||||||
servicesQuickActions,
|
|
||||||
toggleServicesQuickActions,
|
toggleServicesQuickActions,
|
||||||
url,
|
|
||||||
push,
|
|
||||||
restartServices,
|
|
||||||
stopServices,
|
|
||||||
startServices,
|
|
||||||
location
|
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if (loading && !forceArray(services).length) {
|
if (loading && !forceArray(services).length) {
|
||||||
@ -90,17 +68,13 @@ class ServiceList extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleQuickActionsClick = (evt, service) => {
|
const handleQuickActionsClick = (evt, service) => {
|
||||||
const list = this._refs.container;
|
|
||||||
const listRect = list.getBoundingClientRect();
|
|
||||||
const button = evt.currentTarget;
|
const button = evt.currentTarget;
|
||||||
const buttonRect = button.getBoundingClientRect();
|
const buttonRect = button.getBoundingClientRect();
|
||||||
|
|
||||||
const position = {
|
const position = {
|
||||||
left:
|
left:
|
||||||
buttonRect.left -
|
`${buttonRect.left + window.scrollX + (buttonRect.right - buttonRect.left) / 2}px`,
|
||||||
listRect.left +
|
top: `${buttonRect.bottom + window.scrollY}px`
|
||||||
(buttonRect.right - buttonRect.left) / 2,
|
|
||||||
top: buttonRect.bottom - listRect.top
|
|
||||||
};
|
};
|
||||||
|
|
||||||
toggleServicesQuickActions({
|
toggleServicesQuickActions({
|
||||||
@ -109,41 +83,6 @@ class ServiceList extends Component {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRestartClick = (evt, service) => {
|
|
||||||
this.setState({ errors: {} });
|
|
||||||
restartServices(service.id).catch(err => {
|
|
||||||
this.setState({ errors: { restart: err } });
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleStopClick = (evt, service) => {
|
|
||||||
this.setState({ errors: {} });
|
|
||||||
stopServices(service.id).catch(err => {
|
|
||||||
this.setState({ errors: { stop: err } });
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleStartClick = (evt, service) => {
|
|
||||||
this.setState({ errors: {} });
|
|
||||||
startServices(service.id).catch(err => {
|
|
||||||
this.setState({ errors: { start: err } });
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleScaleClick = (evt, service) => {
|
|
||||||
toggleServicesQuickActions({ show: false });
|
|
||||||
push(`${url}/${service.slug}/scale`);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDeleteClick = (evt, service) => {
|
|
||||||
toggleServicesQuickActions({ show: false });
|
|
||||||
push(`${url}/${service.slug}/delete`);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleQuickActionsBlur = o => {
|
|
||||||
toggleServicesQuickActions({ show: false });
|
|
||||||
};
|
|
||||||
|
|
||||||
let renderedError = null;
|
let renderedError = null;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -181,31 +120,14 @@ class ServiceList extends Component {
|
|||||||
<LayoutContainer>
|
<LayoutContainer>
|
||||||
{renderedError}
|
{renderedError}
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
<div ref={this.ref('container')}>
|
|
||||||
{serviceList}
|
{serviceList}
|
||||||
<ServicesQuickActions
|
|
||||||
position={servicesQuickActions.position}
|
|
||||||
service={servicesQuickActions.service}
|
|
||||||
show={servicesQuickActions.show}
|
|
||||||
onBlur={handleQuickActionsBlur}
|
|
||||||
onRestartClick={handleRestartClick}
|
|
||||||
onStopClick={handleStopClick}
|
|
||||||
onStartClick={handleStartClick}
|
|
||||||
onScaleClick={handleScaleClick}
|
|
||||||
onDeleteClick={handleDeleteClick}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
</LayoutContainer>
|
</LayoutContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state, ownProps) => ({
|
const mapStateToProps = (state, ownProps) => ({});
|
||||||
servicesQuickActions: state.ui.services.quickActions,
|
|
||||||
url: ownProps.match.url.replace(/\/$/, ''),
|
|
||||||
push: ownProps.history.push
|
|
||||||
});
|
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = dispatch => ({
|
||||||
toggleServicesQuickActions: data => dispatch(toggleServicesQuickActions(data))
|
toggleServicesQuickActions: data => dispatch(toggleServicesQuickActions(data))
|
||||||
@ -232,29 +154,7 @@ const ServicesGql = graphql(ServicesQuery, {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
const ServicesRestartGql = graphql(ServicesRestartMutation, {
|
|
||||||
props: ({ mutate }) => ({
|
|
||||||
restartServices: serviceId => mutate({ variables: { ids: [serviceId] } })
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
const ServicesStopGql = graphql(ServicesStopMutation, {
|
|
||||||
props: ({ mutate }) => ({
|
|
||||||
stopServices: serviceId => mutate({ variables: { ids: [serviceId] } })
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
const ServicesStartGql = graphql(ServicesStartMutation, {
|
|
||||||
props: ({ mutate }) => ({
|
|
||||||
startServices: serviceId => mutate({ variables: { ids: [serviceId] } })
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
const ServiceListWithData = compose(
|
const ServiceListWithData = compose(
|
||||||
ServicesGql,
|
|
||||||
ServicesRestartGql,
|
|
||||||
ServicesStopGql,
|
|
||||||
ServicesStartGql,
|
|
||||||
ServicesGql,
|
ServicesGql,
|
||||||
UiConnect,
|
UiConnect,
|
||||||
withNotFound([ GqlPaths.DEPLOYMENT_GROUP ])
|
withNotFound([ GqlPaths.DEPLOYMENT_GROUP ])
|
||||||
|
172
packages/cp-frontend/src/containers/services/quick-actions.js
Normal file
172
packages/cp-frontend/src/containers/services/quick-actions.js
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { compose, graphql } from 'react-apollo';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import ServicesRestartMutation from '@graphql/ServicesRestartMutation.gql';
|
||||||
|
import ServicesStopMutation from '@graphql/ServicesStopMutation.gql';
|
||||||
|
import ServicesStartMutation from '@graphql/ServicesStartMutation.gql';
|
||||||
|
import { Tooltip, TooltipLabel } from 'joyent-ui-toolkit';
|
||||||
|
import { toggleServicesQuickActions } from '@root/state/actions';
|
||||||
|
import { ServicesQuickActions as QuickActions } from '@components/services';
|
||||||
|
import { ErrorMessage } from '@components/messaging';
|
||||||
|
import { LayoutContainer } from '@components/layout';
|
||||||
|
|
||||||
|
const StyledContainer = styled.div`
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
`;
|
||||||
|
|
||||||
|
class ServicesQuickActions extends Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
errors: {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
servicesQuickActions,
|
||||||
|
toggleServicesQuickActions,
|
||||||
|
restartServices,
|
||||||
|
stopServices,
|
||||||
|
startServices,
|
||||||
|
url,
|
||||||
|
push
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
let errorMessage = null;
|
||||||
|
let quickActions = null;
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.state.errors.stop ||
|
||||||
|
this.state.errors.start ||
|
||||||
|
this.state.errors.restart
|
||||||
|
) {
|
||||||
|
const message = this.state.errors.stop
|
||||||
|
? 'An error occurred while attempting to stop your service.'
|
||||||
|
: this.state.errors.start
|
||||||
|
? 'An error occurred while attempting to start your service.'
|
||||||
|
: this.state.errors.restart
|
||||||
|
? 'An error occurred while attempting to restart your service.'
|
||||||
|
: '';
|
||||||
|
|
||||||
|
errorMessage = (
|
||||||
|
<LayoutContainer>
|
||||||
|
<ErrorMessage title="Ooops!" message={message} />
|
||||||
|
</LayoutContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(servicesQuickActions.show) {
|
||||||
|
const handleTooltipBlur = evt => {
|
||||||
|
toggleServicesQuickActions({ show: false });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRestartClick = (evt, service) => {
|
||||||
|
this.setState({errors: {}});
|
||||||
|
toggleServicesQuickActions({ show: false });
|
||||||
|
restartServices(service.id).catch(err => {
|
||||||
|
this.setState({ errors: { restart: err } });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleStopClick = (evt, service) => {
|
||||||
|
this.setState({errors: {}});
|
||||||
|
toggleServicesQuickActions({ show: false });
|
||||||
|
stopServices(service.id).catch(err => {
|
||||||
|
this.setState({ errors: { stop: err } });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleStartClick = (evt, service) => {
|
||||||
|
this.setState({errors: {}});
|
||||||
|
toggleServicesQuickActions({ show: false });
|
||||||
|
startServices(service.id).catch(err => {
|
||||||
|
this.setState({ errors: { start: err } });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleScaleClick = (evt, service) => {
|
||||||
|
this.setState({errors: {}});
|
||||||
|
toggleServicesQuickActions({ show: false });
|
||||||
|
push(`${url}/${service.slug}/scale`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteClick = (evt, service) => {
|
||||||
|
this.setState({errors: {}});
|
||||||
|
toggleServicesQuickActions({ show: false });
|
||||||
|
push(`${url}/${service.slug}/delete`);
|
||||||
|
};
|
||||||
|
|
||||||
|
quickActions = (
|
||||||
|
<StyledContainer>
|
||||||
|
<QuickActions
|
||||||
|
service={servicesQuickActions.service}
|
||||||
|
show={servicesQuickActions.show}
|
||||||
|
position={servicesQuickActions.position}
|
||||||
|
onBlur={handleTooltipBlur}
|
||||||
|
onRestartClick={handleRestartClick}
|
||||||
|
onStopClick={handleStopClick}
|
||||||
|
onStartClick={handleStartClick}
|
||||||
|
onScaleClick={handleScaleClick}
|
||||||
|
onDeleteClick={handleDeleteClick}
|
||||||
|
/>
|
||||||
|
</StyledContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(quickActions || errorMessage) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{errorMessage}
|
||||||
|
{quickActions}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = (state, ownProps) => ({
|
||||||
|
servicesQuickActions: state.ui.services.quickActions,
|
||||||
|
url: ownProps.match.url.replace(/\/$/, ''),
|
||||||
|
push: ownProps.history.push
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => ({
|
||||||
|
toggleServicesQuickActions: data => dispatch(toggleServicesQuickActions(data))
|
||||||
|
});
|
||||||
|
|
||||||
|
const UiConnect = connect(mapStateToProps, mapDispatchToProps);
|
||||||
|
|
||||||
|
const ServicesRestartGql = graphql(ServicesRestartMutation, {
|
||||||
|
props: ({ mutate }) => ({
|
||||||
|
restartServices: serviceId => mutate({ variables: { ids: [serviceId] } })
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const ServicesStopGql = graphql(ServicesStopMutation, {
|
||||||
|
props: ({ mutate }) => ({
|
||||||
|
stopServices: serviceId => mutate({ variables: { ids: [serviceId] } })
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const ServicesStartGql = graphql(ServicesStartMutation, {
|
||||||
|
props: ({ mutate }) => ({
|
||||||
|
startServices: serviceId => mutate({ variables: { ids: [serviceId] } })
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const ConnectedServicesQuickActions = compose(
|
||||||
|
ServicesRestartGql,
|
||||||
|
ServicesStopGql,
|
||||||
|
ServicesStartGql,
|
||||||
|
UiConnect
|
||||||
|
)(ServicesQuickActions);
|
||||||
|
|
||||||
|
export default ConnectedServicesQuickActions;
|
@ -3,23 +3,16 @@ import { compose, graphql } from 'react-apollo';
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import forceArray from 'force-array';
|
import forceArray from 'force-array';
|
||||||
import ServicesQuery from '@graphql/Services.gql';
|
|
||||||
import ServicesRestartMutation from '@graphql/ServicesRestartMutation.gql';
|
|
||||||
import ServicesStopMutation from '@graphql/ServicesStopMutation.gql';
|
|
||||||
import ServicesStartMutation from '@graphql/ServicesStartMutation.gql';
|
|
||||||
import unitcalc from 'unitcalc';
|
import unitcalc from 'unitcalc';
|
||||||
|
|
||||||
|
import ServicesQuery from '@graphql/Services.gql';
|
||||||
import { processServicesForTopology } from '@root/state/selectors';
|
import { processServicesForTopology } from '@root/state/selectors';
|
||||||
import { toggleServicesQuickActions } from '@root/state/actions';
|
import { toggleServicesQuickActions } from '@root/state/actions';
|
||||||
|
import { withNotFound, GqlPaths } from '@containers/navigation';
|
||||||
import { LayoutContainer } from '@components/layout';
|
import { LayoutContainer } from '@components/layout';
|
||||||
import { Loader, ErrorMessage } from '@components/messaging';
|
import { Loader, ErrorMessage } from '@components/messaging';
|
||||||
import { ServicesQuickActions } from '@components/services';
|
|
||||||
|
|
||||||
import { Topology } from 'joyent-ui-toolkit';
|
import { Topology } from 'joyent-ui-toolkit';
|
||||||
|
|
||||||
import { withNotFound, GqlPaths } from '@containers/navigation';
|
|
||||||
|
|
||||||
const StyledBackground = styled.div`
|
const StyledBackground = styled.div`
|
||||||
padding: ${unitcalc(4)};
|
padding: ${unitcalc(4)};
|
||||||
background-color: ${props => props.theme.whiteActive};
|
background-color: ${props => props.theme.whiteActive};
|
||||||
@ -38,6 +31,14 @@ class ServicesTopology extends Component {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ref(name) {
|
||||||
|
this._refs = this._refs || {};
|
||||||
|
|
||||||
|
return el => {
|
||||||
|
this._refs[name] = el;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
url,
|
url,
|
||||||
@ -46,12 +47,7 @@ class ServicesTopology extends Component {
|
|||||||
services,
|
services,
|
||||||
loading,
|
loading,
|
||||||
error,
|
error,
|
||||||
servicesQuickActions,
|
|
||||||
toggleServicesQuickActions,
|
toggleServicesQuickActions,
|
||||||
restartServices,
|
|
||||||
stopServices,
|
|
||||||
startServices,
|
|
||||||
location
|
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
if (loading && !forceArray(services).length) {
|
if (loading && !forceArray(services).length) {
|
||||||
@ -86,42 +82,17 @@ class ServicesTopology extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleQuickActionsClick = (evt, tooltipData) => {
|
const handleQuickActionsClick = (evt, tooltipData) => {
|
||||||
toggleServicesQuickActions(tooltipData);
|
const container = this._refs.container;
|
||||||
};
|
const containerRect = container.getBoundingClientRect();
|
||||||
|
const position = {
|
||||||
const handleTooltipBlur = evt => {
|
top: `${containerRect.top + window.scrollY + tooltipData.position.top}px`,
|
||||||
toggleServicesQuickActions({ show: false });
|
left: `${containerRect.left + window.scrollX + tooltipData.position.left}px`
|
||||||
};
|
}
|
||||||
|
const data = {
|
||||||
const handleRestartClick = (evt, service) => {
|
...tooltipData,
|
||||||
this.setState({ errors: {} });
|
position
|
||||||
restartServices(service.id).catch(err => {
|
}
|
||||||
this.setState({ errors: { restart: err } });
|
toggleServicesQuickActions(data);
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleStopClick = (evt, service) => {
|
|
||||||
this.setState({ errors: {} });
|
|
||||||
stopServices(service.id).catch(err => {
|
|
||||||
this.setState({ errors: { stop: err } });
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleStartClick = (evt, service) => {
|
|
||||||
this.setState({ errors: {} });
|
|
||||||
startServices(service.id).catch(err => {
|
|
||||||
this.setState({ errors: { start: err } });
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleScaleClick = (evt, service) => {
|
|
||||||
toggleServicesQuickActions({ show: false });
|
|
||||||
push(`${url}/${service.slug}/scale`);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDeleteClick = (evt, service) => {
|
|
||||||
toggleServicesQuickActions({ show: false });
|
|
||||||
push(`${url}/${service.slug}/delete`);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleNodeTitleClick = (evt, { service }) => {
|
const handleNodeTitleClick = (evt, { service }) => {
|
||||||
@ -155,22 +126,13 @@ class ServicesTopology extends Component {
|
|||||||
{renderedError}
|
{renderedError}
|
||||||
<StyledBackground>
|
<StyledBackground>
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
|
<div ref={this.ref('container')}>
|
||||||
<Topology
|
<Topology
|
||||||
services={services}
|
services={services}
|
||||||
onQuickActionsClick={handleQuickActionsClick}
|
onQuickActionsClick={handleQuickActionsClick}
|
||||||
onNodeTitleClick={handleNodeTitleClick}
|
onNodeTitleClick={handleNodeTitleClick}
|
||||||
/>
|
/>
|
||||||
<ServicesQuickActions
|
</div>
|
||||||
service={servicesQuickActions.service}
|
|
||||||
show={servicesQuickActions.show}
|
|
||||||
position={servicesQuickActions.position}
|
|
||||||
onBlur={handleTooltipBlur}
|
|
||||||
onRestartClick={handleRestartClick}
|
|
||||||
onStopClick={handleStopClick}
|
|
||||||
onStartClick={handleStartClick}
|
|
||||||
onScaleClick={handleScaleClick}
|
|
||||||
onDeleteClick={handleDeleteClick}
|
|
||||||
/>
|
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
</StyledBackground>
|
</StyledBackground>
|
||||||
</div>
|
</div>
|
||||||
@ -179,7 +141,6 @@ class ServicesTopology extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state, ownProps) => ({
|
const mapStateToProps = (state, ownProps) => ({
|
||||||
servicesQuickActions: state.ui.services.quickActions,
|
|
||||||
url: ownProps.match.url.replace(/\/$/, ''),
|
url: ownProps.match.url.replace(/\/$/, ''),
|
||||||
push: ownProps.history.push
|
push: ownProps.history.push
|
||||||
});
|
});
|
||||||
@ -209,28 +170,7 @@ const ServicesGql = graphql(ServicesQuery, {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
const ServicesRestartGql = graphql(ServicesRestartMutation, {
|
|
||||||
props: ({ mutate }) => ({
|
|
||||||
restartServices: serviceId => mutate({ variables: { ids: [serviceId] } })
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
const ServicesStopGql = graphql(ServicesStopMutation, {
|
|
||||||
props: ({ mutate }) => ({
|
|
||||||
stopServices: serviceId => mutate({ variables: { ids: [serviceId] } })
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
const ServicesStartGql = graphql(ServicesStartMutation, {
|
|
||||||
props: ({ mutate }) => ({
|
|
||||||
startServices: serviceId => mutate({ variables: { ids: [serviceId] } })
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
const ServicesTopologyWithData = compose(
|
const ServicesTopologyWithData = compose(
|
||||||
ServicesRestartGql,
|
|
||||||
ServicesStopGql,
|
|
||||||
ServicesStartGql,
|
|
||||||
ServicesGql,
|
ServicesGql,
|
||||||
UiConnect,
|
UiConnect,
|
||||||
withNotFound([ GqlPaths.DEPLOYMENT_GROUP ])
|
withNotFound([ GqlPaths.DEPLOYMENT_GROUP ])
|
||||||
|
@ -4,7 +4,6 @@ import styled from 'styled-components';
|
|||||||
|
|
||||||
import { Header, Breadcrumb, Menu } from '@containers/navigation';
|
import { Header, Breadcrumb, Menu } from '@containers/navigation';
|
||||||
import { ServiceScale, ServiceDelete } from '@containers/service';
|
import { ServiceScale, ServiceDelete } from '@containers/service';
|
||||||
import { InstanceList } from '@containers/instances';
|
|
||||||
import Manifest from '@containers/manifest';
|
import Manifest from '@containers/manifest';
|
||||||
import Environment from '@containers/environment';
|
import Environment from '@containers/environment';
|
||||||
|
|
||||||
@ -17,9 +16,19 @@ import {
|
|||||||
import {
|
import {
|
||||||
ServiceList,
|
ServiceList,
|
||||||
ServicesTopology,
|
ServicesTopology,
|
||||||
ServicesMenu
|
ServicesMenu,
|
||||||
|
ServicesQuickActions
|
||||||
} from '@containers/services';
|
} from '@containers/services';
|
||||||
|
|
||||||
|
import {
|
||||||
|
InstanceList,
|
||||||
|
InstancesTooltip
|
||||||
|
} from '@containers/instances';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Tooltip
|
||||||
|
} from '@containers/tooltip';
|
||||||
|
|
||||||
import { DeploymentGroupDelete } from '@containers/deployment-group';
|
import { DeploymentGroupDelete } from '@containers/deployment-group';
|
||||||
|
|
||||||
import { NotFound } from '@components/navigation';
|
import { NotFound } from '@components/navigation';
|
||||||
@ -88,6 +97,28 @@ const App = p =>
|
|||||||
component={ServicesMenu}
|
component={ServicesMenu}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<Route
|
||||||
|
path="/deployment-groups/:deploymentGroup/services-list"
|
||||||
|
component={ServicesQuickActions}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Route
|
||||||
|
path="/deployment-groups/:deploymentGroup/services-topology"
|
||||||
|
component={ServicesQuickActions}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Route
|
||||||
|
path="/deployment-groups/:deploymentGroup/instances"
|
||||||
|
exact
|
||||||
|
component={InstancesTooltip}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Route
|
||||||
|
path="/deployment-groups/:deploymentGroup/services/:service/instances"
|
||||||
|
exact
|
||||||
|
component={InstancesTooltip}
|
||||||
|
/>
|
||||||
|
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route
|
<Route
|
||||||
path="/deployment-groups/:deploymentGroup/delete"
|
path="/deployment-groups/:deploymentGroup/delete"
|
||||||
|
@ -8,3 +8,7 @@ const APP = constantCase(process.env.APP_NAME);
|
|||||||
export const toggleServicesQuickActions = createAction(
|
export const toggleServicesQuickActions = createAction(
|
||||||
`${APP}/TOGGLE_SERVICES_QUICK_ACTIONS`
|
`${APP}/TOGGLE_SERVICES_QUICK_ACTIONS`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const toggleInstancesTooltip = createAction(
|
||||||
|
`${APP}/TOGGLE_INSTANCES_TOOLTIP`
|
||||||
|
);
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
import { handleActions } from 'redux-actions';
|
import { handleActions } from 'redux-actions';
|
||||||
import { toggleServicesQuickActions } from '@state/actions';
|
import { toggleServicesQuickActions, toggleInstancesTooltip } from '@state/actions';
|
||||||
|
|
||||||
export default handleActions(
|
const _toggleServicesQuickActions = (state, action) => {
|
||||||
{
|
|
||||||
[toggleServicesQuickActions.toString()]: (state, action) => {
|
|
||||||
const { position, service, show } = action.payload;
|
const { position, service, show } = action.payload;
|
||||||
|
|
||||||
const s =
|
const s =
|
||||||
@ -29,7 +27,41 @@ export default handleActions(
|
|||||||
quickActions
|
quickActions
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const _toggleInstancesTooltip = (state, action) => {
|
||||||
|
const { position, instance, show, type } = action.payload;
|
||||||
|
|
||||||
|
const s =
|
||||||
|
show === undefined
|
||||||
|
? !state.instances.tooltip.instance ||
|
||||||
|
instance.id !== state.instances.tooltip.instance.id
|
||||||
|
: show;
|
||||||
|
|
||||||
|
const tooltip = s
|
||||||
|
? {
|
||||||
|
show: true,
|
||||||
|
position,
|
||||||
|
instance,
|
||||||
|
type
|
||||||
}
|
}
|
||||||
|
: {
|
||||||
|
show: false
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
instances: {
|
||||||
|
...state.instances,
|
||||||
|
tooltip
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default handleActions(
|
||||||
|
{
|
||||||
|
[toggleServicesQuickActions.toString()]: _toggleServicesQuickActions,
|
||||||
|
[toggleInstancesTooltip.toString()]: _toggleInstancesTooltip
|
||||||
},
|
},
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
|
@ -61,7 +61,6 @@ const activeInstanceStatuses = [
|
|||||||
'READY',
|
'READY',
|
||||||
'ACTIVE',
|
'ACTIVE',
|
||||||
'RUNNING',
|
'RUNNING',
|
||||||
'STOPPING',
|
|
||||||
'INCOMPLETE'
|
'INCOMPLETE'
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -103,8 +102,12 @@ const getInstancesActive = instanceStatuses => {
|
|||||||
|
|
||||||
const getInstancesHealthy = instances => {
|
const getInstancesHealthy = instances => {
|
||||||
return instances.reduce(
|
return instances.reduce(
|
||||||
(healthy, instance) => (instance.healthy === 'HEALTHY' ? healthy : false),
|
(healthy, instance) => ({
|
||||||
true
|
total: healthy.total + 1,
|
||||||
|
healthy: instance.healthy === 'HEALTHY' ?
|
||||||
|
healthy.healthy + 1 : healthy.healthy
|
||||||
|
}),
|
||||||
|
{total: 0, healthy: 0}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -30,6 +30,11 @@ const state = {
|
|||||||
quickActions: {
|
quickActions: {
|
||||||
show: false
|
show: false
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
instances: {
|
||||||
|
tooltip: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -76,6 +76,7 @@ const getUnfilteredServices = query => {
|
|||||||
|
|
||||||
const getServices = query => {
|
const getServices = query => {
|
||||||
// get all services
|
// get all services
|
||||||
|
|
||||||
const services = getUnfilteredServices(query)
|
const services = getUnfilteredServices(query)
|
||||||
// get all instances
|
// get all instances
|
||||||
.then(services =>
|
.then(services =>
|
||||||
@ -95,9 +96,10 @@ const getServices = query => {
|
|||||||
);
|
);
|
||||||
// get all the serviceIds of the available instances
|
// get all the serviceIds of the available instances
|
||||||
// and then get the servcies with those ids
|
// and then get the servcies with those ids
|
||||||
return uniq(
|
const ret = uniq(
|
||||||
availableInstances.map(({ serviceId }) => serviceId)
|
availableInstances.map(({ serviceId }) => serviceId)
|
||||||
).map(serviceId => lfind(services, ['id', serviceId]));
|
).map(serviceId => lfind(services, ['id', serviceId]));
|
||||||
|
return ret;
|
||||||
});
|
});
|
||||||
|
|
||||||
return Promise.resolve(services)
|
return Promise.resolve(services)
|
||||||
@ -331,12 +333,12 @@ const updateServiceAndInstancesStatus = (
|
|||||||
instancesStatus
|
instancesStatus
|
||||||
) => {
|
) => {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
getServices({ id: serviceId }),
|
getServices({ id: serviceId })/*,
|
||||||
getServices({ parentId: serviceId })
|
getServices({ parentId: serviceId })*/
|
||||||
])
|
])
|
||||||
.then(services =>
|
.then(services => {
|
||||||
services.reduce((services, service) => services.concat(service), [])
|
return services.reduce((services, service) => services.concat(service), [])
|
||||||
)
|
})
|
||||||
.then(services => {
|
.then(services => {
|
||||||
updateServiceStatus(services, serviceStatus);
|
updateServiceStatus(services, serviceStatus);
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
@ -356,8 +358,8 @@ const updateServiceAndInstancesStatus = (
|
|||||||
})
|
})
|
||||||
.then(() =>
|
.then(() =>
|
||||||
Promise.all([
|
Promise.all([
|
||||||
getUnfilteredServices({ id: serviceId }),
|
getUnfilteredServices({ id: serviceId })/*,
|
||||||
getUnfilteredServices({ parentId: serviceId })
|
getUnfilteredServices({ parentId: serviceId })*/
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
.then(services =>
|
.then(services =>
|
||||||
|
@ -114,8 +114,8 @@
|
|||||||
"serviceId": "6d31aff4-de1e-4042-a983-fbd23d5c530c",
|
"serviceId": "6d31aff4-de1e-4042-a983-fbd23d5c530c",
|
||||||
"deploymentGroupId": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
|
"deploymentGroupId": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
|
||||||
"machineId": "8d8a2238-d981-4849-b523-a37456fbe20b",
|
"machineId": "8d8a2238-d981-4849-b523-a37456fbe20b",
|
||||||
"status": "RUNNING",
|
"status": "STOPPING",
|
||||||
"healthy": "HEALTHY"
|
"healthy": "MAINTENANCE"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "68d3046e-8e34-4f5d-a0e5-db3795a250fd",
|
"id": "68d3046e-8e34-4f5d-a0e5-db3795a250fd",
|
||||||
@ -132,8 +132,8 @@
|
|||||||
"serviceId": "6d31aff4-de1e-4042-a983-fbd23d5c530c",
|
"serviceId": "6d31aff4-de1e-4042-a983-fbd23d5c530c",
|
||||||
"deploymentGroupId": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
|
"deploymentGroupId": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
|
||||||
"machineId": "d6871ac4-6433-40c3-89e8-8853ce7f8571",
|
"machineId": "d6871ac4-6433-40c3-89e8-8853ce7f8571",
|
||||||
"status": "RUNNING",
|
"status": "OFFLINE",
|
||||||
"healthy": "HEALTHY"
|
"healthy": "UNAVAILABLE"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "25f6bc62-63b8-4959-908e-1f6d7ff6341d",
|
"id": "25f6bc62-63b8-4959-908e-1f6d7ff6341d",
|
||||||
@ -141,8 +141,8 @@
|
|||||||
"serviceId": "6d31aff4-de1e-4042-a983-fbd23d5c530c",
|
"serviceId": "6d31aff4-de1e-4042-a983-fbd23d5c530c",
|
||||||
"deploymentGroupId": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
|
"deploymentGroupId": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
|
||||||
"machineId": "d89612c8-0578-474a-b45d-98a1dcf6dd18",
|
"machineId": "d89612c8-0578-474a-b45d-98a1dcf6dd18",
|
||||||
"status": "RUNNING",
|
"status": "FAILED",
|
||||||
"healthy": "HEALTHY"
|
"healthy": "UNHEALTHY"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "8be01042-0281-4a77-a357-25979e87bf3d",
|
"id": "8be01042-0281-4a77-a357-25979e87bf3d",
|
||||||
@ -151,7 +151,7 @@
|
|||||||
"deploymentGroupId": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
|
"deploymentGroupId": "e0ea0c02-55cc-45fe-8064-3e5176a59401",
|
||||||
"machineId": "3a9fbaf8-722b-463a-86bd-8d3afe0dd759",
|
"machineId": "3a9fbaf8-722b-463a-86bd-8d3afe0dd759",
|
||||||
"status": "RUNNING",
|
"status": "RUNNING",
|
||||||
"healthy": "HEALTHY"
|
"healthy": "UNKNOWN"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "3d652e9d-73e8-4a6f-8171-84fa83740662",
|
"id": "3d652e9d-73e8-4a6f-8171-84fa83740662",
|
||||||
|
@ -14,5 +14,6 @@ export const border = {
|
|||||||
checked: css`${remcalc(1)} solid ${props => props.theme.primary}`,
|
checked: css`${remcalc(1)} solid ${props => props.theme.primary}`,
|
||||||
unchecked: css`${remcalc(1)} solid ${props => props.theme.grey}`,
|
unchecked: css`${remcalc(1)} solid ${props => props.theme.grey}`,
|
||||||
confirmed: 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}`
|
error: css`${remcalc(1)} solid ${props => props.theme.red}`,
|
||||||
|
secondary: css`${remcalc(1)} solid ${props => props.theme.secondaryActive}`,
|
||||||
};
|
};
|
||||||
|
@ -3,7 +3,7 @@ import Baseline from '../baseline';
|
|||||||
import paperEffect from '../paper-effect';
|
import paperEffect from '../paper-effect';
|
||||||
import { bottomShaddow, bottomShaddowDarker } from '../boxes';
|
import { bottomShaddow, bottomShaddowDarker } from '../boxes';
|
||||||
import remcalc from 'remcalc';
|
import remcalc from 'remcalc';
|
||||||
import is from 'styled-is';
|
import is, { isNot } from 'styled-is';
|
||||||
import { Row } from 'react-styled-flexboxgrid';
|
import { Row } from 'react-styled-flexboxgrid';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
@ -38,6 +38,10 @@ const StyledCard = Row.extend`
|
|||||||
${is('stacked')`
|
${is('stacked')`
|
||||||
${paperEffect}
|
${paperEffect}
|
||||||
`};
|
`};
|
||||||
|
|
||||||
|
${isNot('active')`
|
||||||
|
background-color: ${props => props.theme.disabled};
|
||||||
|
`};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,7 +51,7 @@ const Card = ({
|
|||||||
children,
|
children,
|
||||||
collapsed = false,
|
collapsed = false,
|
||||||
headed = false,
|
headed = false,
|
||||||
disabled = false,
|
active = true,
|
||||||
...rest
|
...rest
|
||||||
}) => {
|
}) => {
|
||||||
const render = value => {
|
const render = value => {
|
||||||
@ -55,14 +59,14 @@ const Card = ({
|
|||||||
fromHeader: (value || {}).fromHeader,
|
fromHeader: (value || {}).fromHeader,
|
||||||
headed,
|
headed,
|
||||||
collapsed,
|
collapsed,
|
||||||
disabled
|
active
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Broadcast channel="card" value={newValue}>
|
<Broadcast channel="card" value={newValue}>
|
||||||
<StyledCard
|
<StyledCard
|
||||||
name="card"
|
name="card"
|
||||||
disabled={disabled}
|
active={active}
|
||||||
collapsed={collapsed}
|
collapsed={collapsed}
|
||||||
headed={headed}
|
headed={headed}
|
||||||
{...rest}
|
{...rest}
|
||||||
|
@ -12,7 +12,8 @@ const StyledTitle = Title.extend`
|
|||||||
${typography.fontFamily};
|
${typography.fontFamily};
|
||||||
${typography.normal};
|
${typography.normal};
|
||||||
|
|
||||||
flex-grow: 2;
|
flex-grow: 1;
|
||||||
|
flex-basis: ${remcalc(90)};
|
||||||
|
|
||||||
${isNot('collapsed')`
|
${isNot('collapsed')`
|
||||||
padding-bottom: ${remcalc(12)};
|
padding-bottom: ${remcalc(12)};
|
||||||
@ -21,11 +22,6 @@ const StyledTitle = Title.extend`
|
|||||||
|
|
||||||
const InnerDescription = styled.div`
|
const InnerDescription = styled.div`
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
|
|
||||||
${is('collapsed')`
|
|
||||||
justify-content: flex-end;
|
|
||||||
margin-left: auto;
|
|
||||||
`};
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Description = ({ children, ...rest }) => {
|
const Description = ({ children, ...rest }) => {
|
||||||
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||||||
import { Broadcast, Subscriber } from 'react-broadcast';
|
import { Broadcast, Subscriber } from 'react-broadcast';
|
||||||
import remcalc from 'remcalc';
|
import remcalc from 'remcalc';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import is from 'styled-is';
|
import is, { isNot } from 'styled-is';
|
||||||
import Baseline from '../baseline';
|
import Baseline from '../baseline';
|
||||||
import Card from './card';
|
import Card from './card';
|
||||||
|
|
||||||
@ -17,16 +17,15 @@ const StyledCard = Card.extend`
|
|||||||
width: calc(100% + ${remcalc(2)});
|
width: calc(100% + ${remcalc(2)});
|
||||||
margin: ${remcalc(-1)} ${remcalc(-1)} 0 ${remcalc(-1)};
|
margin: ${remcalc(-1)} ${remcalc(-1)} 0 ${remcalc(-1)};
|
||||||
|
|
||||||
${is('disabled')`
|
${isNot('active')`
|
||||||
background-color: ${props => props.theme.disabled};
|
background-color: ${props => props.theme.disabled};
|
||||||
border-color: ${props => props.theme.grey};
|
border-color: ${props => props.theme.grey};
|
||||||
color: ${props => props.theme.grey};
|
|
||||||
`};
|
`};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Header = ({ children, ...rest }) => {
|
const Header = ({ children, ...rest }) => {
|
||||||
const render = value => {
|
const render = value => {
|
||||||
const { disabled } = value;
|
const { active } = value;
|
||||||
|
|
||||||
const newValue = {
|
const newValue = {
|
||||||
...value,
|
...value,
|
||||||
@ -37,7 +36,7 @@ const Header = ({ children, ...rest }) => {
|
|||||||
<Broadcast channel="card" value={newValue}>
|
<Broadcast channel="card" value={newValue}>
|
||||||
<StyledCard
|
<StyledCard
|
||||||
name="card-header"
|
name="card-header"
|
||||||
disabled={disabled}
|
active={active}
|
||||||
collapsed
|
collapsed
|
||||||
headed
|
headed
|
||||||
{...rest}
|
{...rest}
|
||||||
|
@ -5,9 +5,13 @@ import remcalc from 'remcalc';
|
|||||||
import Label from '../label';
|
import Label from '../label';
|
||||||
|
|
||||||
const StyledLabel = Label.extend`
|
const StyledLabel = Label.extend`
|
||||||
|
display: inline-block;
|
||||||
${props => (props.color === 'light' ? `color: ${props.theme.white};` : '')};
|
${props => (props.color === 'light' ? `color: ${props.theme.white};` : '')};
|
||||||
${props => (props.color === 'disabled' ? `color: ${props.theme.grey};` : '')};
|
${props => (props.color === 'disabled' ? `color: ${props.theme.text};` : '')};
|
||||||
margin-left: ${props => (props.iconPosition === 'left' ? remcalc(24) : 0)};
|
margin-left: ${props => (props.iconPosition === 'left' ? remcalc(24) : 0)};
|
||||||
|
&::first-letter {
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledIconContainer = styled.div`
|
const StyledIconContainer = styled.div`
|
||||||
@ -15,7 +19,7 @@ const StyledIconContainer = styled.div`
|
|||||||
|
|
||||||
> svg {
|
> svg {
|
||||||
${props => (props.color === 'light' ? `fill: ${props.theme.white};` : '')};
|
${props => (props.color === 'light' ? `fill: ${props.theme.white};` : '')};
|
||||||
${props => (props.color === 'disabled' ? `fill: ${props.theme.grey};` : '')};
|
${props => (props.color === 'disabled' ? `fill: ${props.theme.text};` : '')};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ import styled from 'styled-components';
|
|||||||
import { Nav } from 'normalized-styled-components';
|
import { Nav } from 'normalized-styled-components';
|
||||||
import Baseline from '../baseline';
|
import Baseline from '../baseline';
|
||||||
import remcalc from 'remcalc';
|
import remcalc from 'remcalc';
|
||||||
import is from 'styled-is';
|
import is, { isNot } from 'styled-is';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import Button from '../button';
|
import Button from '../button';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
@ -17,7 +17,7 @@ const StyledNav = Nav.extend`
|
|||||||
border-left-color: ${props => props.theme.primaryDesaturatedActive};
|
border-left-color: ${props => props.theme.primaryDesaturatedActive};
|
||||||
`};
|
`};
|
||||||
|
|
||||||
${is('disabled')`
|
${isNot('active')`
|
||||||
border-left-color: ${props => props.theme.grey};
|
border-left-color: ${props => props.theme.grey};
|
||||||
`};
|
`};
|
||||||
`;
|
`;
|
||||||
@ -54,6 +54,19 @@ const StyledButton = Button.extend`
|
|||||||
&:active:focus {
|
&:active:focus {
|
||||||
border-width: 0;
|
border-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
${isNot('active')`
|
||||||
|
background-color: ${props => props.theme.disabled};
|
||||||
|
border-color: ${props => props.theme.grey};
|
||||||
|
|
||||||
|
&:focus,
|
||||||
|
&:hover,
|
||||||
|
&:active,
|
||||||
|
&:active:hover,
|
||||||
|
&:active:focus {
|
||||||
|
background-color: ${props => props.theme.grey};
|
||||||
|
}
|
||||||
|
`}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
const StyledContainer = styled.div`
|
||||||
@ -73,8 +86,8 @@ const StyledCircle = styled.div`
|
|||||||
background-color: ${props => props.theme.secondary};
|
background-color: ${props => props.theme.secondary};
|
||||||
`};
|
`};
|
||||||
|
|
||||||
${is('disabled')`
|
${isNot('active')`
|
||||||
background-color: ${props => props.theme.grey};
|
background-color: ${props => props.theme.text};
|
||||||
`};
|
`};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -82,20 +95,20 @@ const Options = ({ children, ...rest }) => {
|
|||||||
const render = ({
|
const render = ({
|
||||||
fromHeader = false,
|
fromHeader = false,
|
||||||
collapsed = false,
|
collapsed = false,
|
||||||
disabled = false
|
active = true
|
||||||
}) =>
|
}) =>
|
||||||
<StyledNav disabled={disabled} fromHeader={fromHeader} name="card-options">
|
<StyledNav active={active} fromHeader={fromHeader} name="card-options">
|
||||||
<StyledButton
|
<StyledButton
|
||||||
secondary={!fromHeader}
|
secondary={!fromHeader}
|
||||||
collapsed={collapsed}
|
collapsed={collapsed}
|
||||||
disabled={disabled}
|
active={active}
|
||||||
rect
|
rect
|
||||||
{...rest}
|
{...rest}
|
||||||
>
|
>
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
<StyledCircle disabled={disabled} secondary={!fromHeader} />
|
<StyledCircle active={active} secondary={!fromHeader} />
|
||||||
<StyledCircle disabled={disabled} secondary={!fromHeader} />
|
<StyledCircle active={active} secondary={!fromHeader} />
|
||||||
<StyledCircle disabled={disabled} secondary={!fromHeader} />
|
<StyledCircle active={active} secondary={!fromHeader} />
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
</StyledButton>
|
</StyledButton>
|
||||||
</StyledNav>;
|
</StyledNav>;
|
||||||
|
@ -2,7 +2,7 @@ import { Subscriber } from 'react-broadcast';
|
|||||||
import typography from '../typography';
|
import typography from '../typography';
|
||||||
import Baseline from '../baseline';
|
import Baseline from '../baseline';
|
||||||
import { Col } from 'react-styled-flexboxgrid';
|
import { Col } from 'react-styled-flexboxgrid';
|
||||||
import is from 'styled-is';
|
import is, { isNot } from 'styled-is';
|
||||||
import remcalc from 'remcalc';
|
import remcalc from 'remcalc';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
@ -20,16 +20,16 @@ const StyledCol = Col.extend`
|
|||||||
display: none;
|
display: none;
|
||||||
`};
|
`};
|
||||||
|
|
||||||
${is('disabled')`
|
${isNot('active')`
|
||||||
color: ${props => props.theme.grey};
|
color: ${props => props.theme.grey};
|
||||||
`};
|
`};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Outlet = ({ children, ...rest }) => {
|
const Outlet = ({ children, ...rest }) => {
|
||||||
const render = ({ disabled = false, collapsed = false }) =>
|
const render = ({ active = true, collapsed = false }) =>
|
||||||
<StyledCol
|
<StyledCol
|
||||||
name="card-outlet"
|
name="card-outlet"
|
||||||
disabled={disabled}
|
active={active}
|
||||||
collapsed={collapsed}
|
collapsed={collapsed}
|
||||||
xs={6}
|
xs={6}
|
||||||
{...rest}
|
{...rest}
|
||||||
|
@ -3,7 +3,7 @@ import styled from 'styled-components';
|
|||||||
import Baseline from '../baseline';
|
import Baseline from '../baseline';
|
||||||
import typography from '../typography';
|
import typography from '../typography';
|
||||||
import remcalc from 'remcalc';
|
import remcalc from 'remcalc';
|
||||||
import is from 'styled-is';
|
import is, { isNot } from 'styled-is';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import Title from './title';
|
import Title from './title';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
@ -27,10 +27,6 @@ const Span = styled.span`
|
|||||||
${is('fromHeader')`
|
${is('fromHeader')`
|
||||||
color: ${props => props.theme.white};
|
color: ${props => props.theme.white};
|
||||||
`};
|
`};
|
||||||
|
|
||||||
${is('disabled')`
|
|
||||||
color: ${props => props.theme.grey};
|
|
||||||
`};
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledTitle = Title.extend`
|
const StyledTitle = Title.extend`
|
||||||
@ -48,7 +44,7 @@ const StyledTitle = Title.extend`
|
|||||||
|
|
||||||
const Subtitle = ({ children, ...props }) => {
|
const Subtitle = ({ children, ...props }) => {
|
||||||
const render = ({
|
const render = ({
|
||||||
disabled = false,
|
active = true,
|
||||||
fromHeader = false,
|
fromHeader = false,
|
||||||
collapsed = false
|
collapsed = false
|
||||||
}) =>
|
}) =>
|
||||||
@ -56,7 +52,7 @@ const Subtitle = ({ children, ...props }) => {
|
|||||||
name="card-subtitle"
|
name="card-subtitle"
|
||||||
fromHeader={fromHeader}
|
fromHeader={fromHeader}
|
||||||
collapsed={collapsed}
|
collapsed={collapsed}
|
||||||
disabled={disabled}
|
active={active}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<Span fromHeader={fromHeader} collapsed={collapsed}>
|
<Span fromHeader={fromHeader} collapsed={collapsed}>
|
||||||
|
@ -3,7 +3,7 @@ import isString from 'lodash.isstring';
|
|||||||
import typography from '../typography';
|
import typography from '../typography';
|
||||||
import Baseline from '../baseline';
|
import Baseline from '../baseline';
|
||||||
import remcalc from 'remcalc';
|
import remcalc from 'remcalc';
|
||||||
import is from 'styled-is';
|
import is, { isNot } from 'styled-is';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
@ -21,6 +21,7 @@ const Container = styled.div`
|
|||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
|
|
||||||
flex-grow: 2;
|
flex-grow: 2;
|
||||||
|
flex-basis: ${remcalc(90)};
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
padding: ${remcalc(12)} ${remcalc(18)} 0 ${remcalc(18)};
|
padding: ${remcalc(12)} ${remcalc(18)} 0 ${remcalc(18)};
|
||||||
@ -29,12 +30,8 @@ const Container = styled.div`
|
|||||||
color: ${props => props.theme.white};
|
color: ${props => props.theme.white};
|
||||||
`};
|
`};
|
||||||
|
|
||||||
${is('disabled')`
|
|
||||||
color: ${props => props.theme.grey};
|
|
||||||
`};
|
|
||||||
|
|
||||||
${is('collapsed')`
|
${is('collapsed')`
|
||||||
flex-grow: 0;
|
flex-grow: 6;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: auto;
|
width: auto;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@ -61,16 +58,17 @@ const Title = ({ children, ...rest }) => {
|
|||||||
|
|
||||||
const render = ({
|
const render = ({
|
||||||
collapsed = false,
|
collapsed = false,
|
||||||
disabled = false,
|
active = true,
|
||||||
fromHeader = false
|
fromHeader = false
|
||||||
}) =>
|
}) =>
|
||||||
<Container
|
<Container
|
||||||
collapsed={collapsed}
|
collapsed={collapsed}
|
||||||
fromHeader={fromHeader}
|
fromHeader={fromHeader}
|
||||||
disabled={disabled}
|
active={active}
|
||||||
name="card-title"
|
name="card-title"
|
||||||
xs={collapsed ? 6 : 12}
|
xs={collapsed ? 6 : 12}
|
||||||
{...rest}
|
{...rest}
|
||||||
|
name='container'
|
||||||
>
|
>
|
||||||
{_children}
|
{_children}
|
||||||
</Container>;
|
</Container>;
|
||||||
|
@ -18,6 +18,9 @@ const StyledSelectList = styled(Tooltip)`
|
|||||||
position: relative;
|
position: relative;
|
||||||
display: block;
|
display: block;
|
||||||
left: auto;
|
left: auto;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style-type: none;
|
||||||
}
|
}
|
||||||
ul:after, ul:before {
|
ul:after, ul:before {
|
||||||
left: 97%;
|
left: 97%;
|
||||||
@ -107,7 +110,9 @@ class Dropdown extends Component {
|
|||||||
<StyledArrowIcon onClick={this.toggleDropdown} />
|
<StyledArrowIcon onClick={this.toggleDropdown} />
|
||||||
{this.state.isDroppedDown &&
|
{this.state.isDroppedDown &&
|
||||||
<StyledSelectList>
|
<StyledSelectList>
|
||||||
|
<ul>
|
||||||
{data.map((val, index) =>
|
{data.map((val, index) =>
|
||||||
|
<li>
|
||||||
<DropdownItem
|
<DropdownItem
|
||||||
key={index}
|
key={index}
|
||||||
value={val}
|
value={val}
|
||||||
@ -115,7 +120,9 @@ class Dropdown extends Component {
|
|||||||
>
|
>
|
||||||
{val}
|
{val}
|
||||||
</DropdownItem>
|
</DropdownItem>
|
||||||
|
</li>
|
||||||
)}
|
)}
|
||||||
|
</ul>
|
||||||
</StyledSelectList>}
|
</StyledSelectList>}
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
|
@ -1,7 +1,13 @@
|
|||||||
import Baseline from '../baseline';
|
import Baseline from '../baseline';
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
import HealthyIcon from './svg/icon_healthy.svg';
|
import HealthyIcon from './svg/icon_healthy.svg';
|
||||||
|
|
||||||
export default Baseline(HealthyIcon);
|
const StyledHealthyIcon = styled(HealthyIcon)`
|
||||||
|
fill: ${props => !props.healthy || props.healthy === 'HEALTHY'
|
||||||
|
? props.theme.green : props.theme.orange};
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default Baseline(StyledHealthyIcon);
|
||||||
|
@ -4,12 +4,12 @@
|
|||||||
<title>icon: state</title>
|
<title>icon: state</title>
|
||||||
<desc>Created with Sketch.</desc>
|
<desc>Created with Sketch.</desc>
|
||||||
<defs></defs>
|
<defs></defs>
|
||||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
<g id="Page-1" stroke="none" stroke-width="1" fill-rule="evenodd">
|
||||||
<g id="topology" transform="translate(-489.000000, -441.000000)">
|
<g id="topology" transform="translate(-489.000000, -441.000000)">
|
||||||
<g id="services" transform="translate(0.000000, 348.000000)">
|
<g id="services" transform="translate(0.000000, 348.000000)">
|
||||||
<g id="service:-nginx" transform="translate(476.000000, 36.000000)">
|
<g id="service:-nginx" transform="translate(476.000000, 36.000000)">
|
||||||
<g id="icon:-state" transform="translate(13.000000, 57.000000)">
|
<g id="icon:-state" transform="translate(13.000000, 57.000000)">
|
||||||
<circle id="Oval" fill="#00AF66" cx="9" cy="9" r="9"></circle>
|
<circle id="Oval" cx="9" cy="9" r="9"></circle>
|
||||||
<path d="M9.47745233,6.60270759 L8.95496861,7.04565311 L8.51133742,6.60270759 C7.70841297,5.79909747 6.40563205,5.79909747 5.60270759,6.60270759 C4.79909747,7.40631772 4.79909747,8.70841297 5.60270759,9.5120231 L8.95496861,12.8642841 L12.3668833,9.5120231 C13.1698077,8.70841297 13.2301471,7.40631772 12.4265369,6.60270759 C11.6229268,5.79909747 10.2810625,5.79909747 9.47745233,6.60270759 Z" id="icon:-health" fill="#FFFFFF"></path>
|
<path d="M9.47745233,6.60270759 L8.95496861,7.04565311 L8.51133742,6.60270759 C7.70841297,5.79909747 6.40563205,5.79909747 5.60270759,6.60270759 C4.79909747,7.40631772 4.79909747,8.70841297 5.60270759,9.5120231 L8.95496861,12.8642841 L12.3668833,9.5120231 C13.1698077,8.70841297 13.2301471,7.40631772 12.4265369,6.60270759 C11.6229268,5.79909747 10.2810625,5.79909747 9.47745233,6.60270759 Z" id="icon:-health" fill="#FFFFFF"></path>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.3 KiB |
@ -17,7 +17,7 @@ export { default as Chevron } from './chevron';
|
|||||||
export { default as CloseButton } from './close-button';
|
export { default as CloseButton } from './close-button';
|
||||||
export { default as Divider } from './divider';
|
export { default as Divider } from './divider';
|
||||||
export { default as IconButton } from './icon-button';
|
export { default as IconButton } from './icon-button';
|
||||||
export { Tooltip, TooltipButton, TooltipDivider } from './tooltip';
|
export { Tooltip, TooltipButton, TooltipDivider, TooltipList, TooltipLabel } from './tooltip';
|
||||||
export { Dropdown } from './dropdown';
|
export { Dropdown } from './dropdown';
|
||||||
export { default as StatusLoader } from './status-loader';
|
export { default as StatusLoader } from './status-loader';
|
||||||
export { default as Message } from './message';
|
export { default as Message } from './message';
|
||||||
|
@ -45,8 +45,6 @@ const StyledButton = styled(Button)`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const TooltipButton = props =>
|
const TooltipButton = props =>
|
||||||
<li>
|
<StyledButton {...props} />;
|
||||||
<StyledButton {...props} />
|
|
||||||
</li>;
|
|
||||||
|
|
||||||
export default TooltipButton;
|
export default TooltipButton;
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
export { default as Tooltip } from './tooltip';
|
export { default as Tooltip } from './tooltip';
|
||||||
export { default as TooltipButton } from './button';
|
export { default as TooltipButton } from './button';
|
||||||
export { default as TooltipDivider } from './divider';
|
export { default as TooltipDivider } from './divider';
|
||||||
|
export { default as TooltipList } from './list';
|
||||||
|
export { default as TooltipLabel } from './label';
|
||||||
|
9
packages/ui-toolkit/src/tooltip/label.js
Normal file
9
packages/ui-toolkit/src/tooltip/label.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
import remcalc from 'remcalc';
|
||||||
|
import P from '../text/p';
|
||||||
|
|
||||||
|
export default styled(P)`
|
||||||
|
margin: 0 ${remcalc(18)};
|
||||||
|
color: ${props => props.theme.white};
|
||||||
|
white-space: nowrap;
|
||||||
|
`;
|
7
packages/ui-toolkit/src/tooltip/list.js
Normal file
7
packages/ui-toolkit/src/tooltip/list.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
export default styled.ul`
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style-type: none;
|
||||||
|
`;
|
@ -18,19 +18,18 @@ const StyledContainer = styled.div`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledList = styled.ul`
|
const StyledInnerContainer = styled.div`
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
top: ${remcalc(5)};
|
top: ${remcalc(5)};
|
||||||
left: -50%;
|
left: -50%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: ${unitcalc(2)} 0;
|
padding: ${unitcalc(2)} 0;
|
||||||
list-style-type: none;
|
background-color: ${props => props.secondary ? props.theme.secondary : props.theme.white};
|
||||||
background-color: ${theme.white};
|
border: ${props => props.secondary ? border.secondary : border.unchecked};
|
||||||
border: ${border.unchecked};
|
|
||||||
box-shadow: ${tooltipShadow};
|
box-shadow: ${tooltipShadow};
|
||||||
border-radius: ${borderRadius};
|
border-radius: ${borderRadius};
|
||||||
z-index: 1;
|
z-index: 1000;
|
||||||
|
|
||||||
&:after,
|
&:after,
|
||||||
&:before {
|
&:before {
|
||||||
@ -44,13 +43,13 @@ const StyledList = styled.ul`
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
border-bottom-color: ${theme.white};
|
border-bottom-color: ${props => props.secondary ? props.theme.secondary : theme.white};
|
||||||
border-width: ${remcalc(3)};
|
border-width: ${remcalc(3)};
|
||||||
margin-left: ${remcalc(-3)};
|
margin-left: ${remcalc(-3)};
|
||||||
}
|
}
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
border-bottom-color: ${theme.grey};
|
border-bottom-color: ${props => props.secondary ? props.theme.secondaryActive : theme.grey};
|
||||||
border-width: ${remcalc(5)};
|
border-width: ${remcalc(5)};
|
||||||
margin-left: ${remcalc(-5)};
|
margin-left: ${remcalc(-5)};
|
||||||
}
|
}
|
||||||
@ -84,27 +83,39 @@ class Tooltip extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
let {
|
||||||
children,
|
children,
|
||||||
top = 'auto',
|
top = 'auto',
|
||||||
left = 'auto',
|
left = 'auto',
|
||||||
bottom = 'auto',
|
bottom = 'auto',
|
||||||
right = 'auto',
|
right = 'auto',
|
||||||
className,
|
secondary,
|
||||||
...rest
|
...rest
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
if(typeof top === 'number') {
|
||||||
|
top = `${top}px`
|
||||||
|
}
|
||||||
|
if(typeof left === 'number') {
|
||||||
|
left = `${left}px`
|
||||||
|
}
|
||||||
|
if(typeof bottom === 'number') {
|
||||||
|
bottom = `${bottom}px`
|
||||||
|
}
|
||||||
|
if(typeof right === 'number') {
|
||||||
|
right = `${right}px`
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledContainer
|
<StyledContainer
|
||||||
className={className}
|
|
||||||
top={top}
|
top={top}
|
||||||
left={left}
|
left={left}
|
||||||
bottom={bottom}
|
bottom={bottom}
|
||||||
right={right}
|
right={right}
|
||||||
{...rest}
|
|
||||||
>
|
>
|
||||||
<StyledList>
|
<StyledInnerContainer secondary={secondary}>
|
||||||
{children}
|
{children}
|
||||||
</StyledList>
|
</StyledInnerContainer>
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -116,7 +127,8 @@ Tooltip.propTypes = {
|
|||||||
left: PropTypes.string,
|
left: PropTypes.string,
|
||||||
bottom: PropTypes.string,
|
bottom: PropTypes.string,
|
||||||
right: PropTypes.string,
|
right: PropTypes.string,
|
||||||
onBlur: PropTypes.func
|
onBlur: PropTypes.func,
|
||||||
|
secondary: PropTypes.boolean
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Tooltip;
|
export default Tooltip;
|
||||||
|
Loading…
Reference in New Issue
Block a user