implement table components and integrate them (#822)

* feat: implement table components and integrate them

* fix(ui-toolkit): styleguide css

* fix(ui-toolkit): merge
This commit is contained in:
Sérgio Ramos 2017-10-31 10:29:15 +00:00 committed by Sara Vieira
parent d84f972fbe
commit 75e5454b92
23 changed files with 656 additions and 676 deletions

View File

@ -41,6 +41,7 @@
"triton": "^5.4.0"
},
"devDependencies": {
"graphql-faker": "^1.5.0",
"eslint": "^4.9.0",
"eslint-config-joyent-portal": "^3.2.0",
"eslint-plugin-graphql": "^1.4.0-1",

View File

@ -9,8 +9,10 @@
"dev": "REACT_APP_GQL_PORT=4000 PORT=3069 REACT_APP_GQL_PROTOCOL=http joyent-react-scripts start",
"start": "PORT=3069 joyent-react-scripts start",
"build": "NODE_ENV=production joyent-react-scripts build",
"lint-ci": "eslint . --ext .js --ext .md && echo 0 `# stylelint './src/**/*.js'`",
"lint": "eslint . --fix --ext .js --ext .md && echo 0 `# stylelint './src/**/*.js'`",
"lint-ci":
"eslint . --ext .js --ext .md && echo 0 `# stylelint './src/**/*.js'`",
"lint":
"eslint . --fix --ext .js --ext .md && echo 0 `# stylelint './src/**/*.js'`",
"test-ci": "echo 0 `# NODE_ENV=test ./test/run --env=jsdom --coverage`",
"test": "NODE_ENV=test ./test/run --env=jsdom",
"prepublish": "echo 0"

View File

@ -1,27 +1,15 @@
import React from 'react';
import forceArray from 'force-array';
import {
Card,
CardMeta,
CardTitle,
CardLabel,
CardView
TableTr,
TableTd
} from 'joyent-ui-toolkit';
export default ({
rule = '',
global = false,
enabled = false,
first,
last
}) => (
<Card collapsed flat={!last} topMargin={first} bottomless={!last} gapless>
<CardView>
<CardMeta>
<CardTitle>{rule}</CardTitle>
<CardLabel icon={global && String.fromCodePoint(0x1f30d)} />
<CardLabel color={enabled ? 'green' : 'red'} />
</CardMeta>
</CardView>
</Card>
export default ({ rule = '', global = false, enabled = false }) => (
<TableTr>
<TableTd><code>{rule}</code></TableTd>
<TableTd center middle><code>{JSON.stringify(global)}</code></TableTd>
<TableTd center middle><code>{JSON.stringify(enabled)}</code></TableTd>
</TableTr>
);

View File

@ -1,4 +1,3 @@
export { default as Item } from './item';
export { default as List } from './list';
export { default as KeyValue } from './key-value';
export { default as Network } from './network';
@ -6,4 +5,3 @@ export { default as FirewallRule } from './firewall-rule';
export { default as Resize } from './resize';
export { default as CreateSnapshot } from './create-snapshot';
export { default as Snapshots } from './snapshots';
export { default as Snapshot } from './snapshot';

View File

@ -2,6 +2,8 @@ import React from 'react';
import { Row, Col } from 'react-styled-flexboxgrid';
import forceArray from 'force-array';
import find from 'lodash.find';
import remcalc from 'remcalc';
import titleCase from 'title-case';
import {
FormGroup,
@ -14,12 +16,105 @@ import {
MessageTitle,
MessageDescription,
Button,
QueryBreakpoints
QueryBreakpoints,
Table,
TableThead,
TableTr,
TableTh,
TableTbody,
TableTd,
Checkbox,
P,
DotIcon,
IconActions,
PopoverContainer,
PopoverTarget,
Popover,
PopoverItem,
PopoverDivider,
Anchor
} from 'joyent-ui-toolkit';
import Item from './item';
const { SmallOnly, Small, Medium } = QueryBreakpoints;
const { SmallOnly, Medium } = QueryBreakpoints;
const stateColor = {
PROVISIONING: 'primary',
RUNNING: 'green',
STOPPING: 'grey',
STOPPED: 'grey',
DELETED: 'secondaryActive',
FAILED: 'red'
};
const Item = ({
id,
name,
state,
allowedActions,
onStop,
onStart,
onReboot,
onResize,
onEnableFw,
onDisableFw,
onCreateSnap,
onStartSnap
}) => (
<TableTr>
<TableTd center middle>
<FormGroup name={name} reduxForm>
<Checkbox />
</FormGroup>
</TableTd>
<TableTd>
<code>{id.substring(0, 7)}</code>
</TableTd>
<TableTd>
<Anchor to={`/instances/${name}`}>{name}</Anchor>
</TableTd>
<TableTd middle>
<DotIcon color={stateColor[state]} /> {titleCase(state)}
</TableTd>
<TableTd border="left" middle center actionable>
<PopoverContainer clickable>
<PopoverTarget>
<IconActions />
</PopoverTarget>
<Popover placement="right-start">
{!allowedActions.stop ? null : (
<PopoverItem onClick={onStop}>Stop</PopoverItem>
)}
{!allowedActions.start ? null : (
<PopoverItem onClick={onStart}>Start</PopoverItem>
)}
{!allowedActions.reboot ? null : (
<PopoverItem onClick={onReboot}>Reboot</PopoverItem>
)}
{!allowedActions.enableFw ? null : (
<PopoverItem onClick={onEnableFw}>Enable Firewall</PopoverItem>
)}
{!allowedActions.disableFw ? null : (
<PopoverItem onClick={onDisableFw}>Disable Firewall</PopoverItem>
)}
{!allowedActions.disableFw ? null : (
<PopoverItem onClick={onDisableFw}>Disable Firewall</PopoverItem>
)}
<PopoverDivider />
{!allowedActions.resize ? null : (
<PopoverItem onClick={onResize}>Resize</PopoverItem>
)}
{!allowedActions.createSnap ? null : (
<PopoverItem onClick={onCreateSnap}>Create Snapshot</PopoverItem>
)}
{!allowedActions.startSnap ? null : (
<PopoverItem onClick={onStartSnap}>Start from Snapshot</PopoverItem>
)}
<PopoverItem>Delete</PopoverItem>
</Popover>
</PopoverContainer>
</TableTd>
</TableTr>
);
export default ({
instances = [],
@ -59,23 +154,39 @@ export default ({
});
};
const _instances = forceArray(instances);
const items = _instances.map((instance, i, all) => {
const { id } = instance;
const items = forceArray(instances).map(instance => {
// eslint-disable-next-line camelcase
const { id, name, state, firewall_enabled, snapshots, brand } = instance;
const isSelected = Boolean(find(selected, ['id', id]));
const isSubmitting = isSelected && submitting;
return (
<Item
key={id}
{...instance}
last={all.length - 1 === i}
first={!i}
loading={isSubmitting}
/>
);
const allowedActions = {
stop: state === 'RUNNING',
start: state !== 'RUNNING',
reboot: true,
resize: brand === 'KVM',
// eslint-disable-next-line camelcase
enableFw: !firewall_enabled,
// eslint-disable-next-line camelcase
disableFw: firewall_enabled,
createSnap: true,
startSnap: Boolean(snapshots.length)
};
return {
...instance,
isSubmitting,
isSelected,
allowedActions,
onStop: () => onAction({ name: 'stop', items: [instance] }),
onStart: () => onAction({ name: 'start', items: [instance] }),
onReboot: () => onAction({ name: 'reboot', items: [instance] }),
onResize: () => onAction({ name: 'resize', items: [instance] }),
onEnableFw: () => onAction({ name: 'enableFw', items: [instance] }),
onDisableFw: () => onAction({ name: 'disableFw', items: [instance] }),
onCreateSnap: () => onAction({ name: 'createSnap', items: [instance] }),
onStartSnap: () => onAction({ name: 'startSnap', items: [instance] })
};
});
const _loading =
@ -93,6 +204,27 @@ export default ({
</Message>
);
const _table = !items.length ? null : (
<Table>
<TableThead>
<TableTr>
<TableTh xs="48" />
<TableTh xs="80" left bottom>
<P>Id</P>
</TableTh>
<TableTh left bottom>
<P>Name</P>
</TableTh>
<TableTh xs="105" left bottom>
<P>Status</P>
</TableTh>
<TableTh xs="48" />
</TableTr>
</TableThead>
<TableTbody>{items.map(instance => <Item key={instance.id} {...instance} />)}</TableTbody>
</Table>
);
return (
<form>
<Row between="xs">
@ -196,9 +328,9 @@ export default ({
</Row>
</Col>
</Row>
{_loading}
{_error}
{items}
{_loading}
{_table}
</form>
);
};

View File

@ -2,22 +2,15 @@ import React from 'react';
import forceArray from 'force-array';
import {
Card,
CardMeta,
CardTitle,
CardLabel,
CardView
TableTr,
TableTd
} from 'joyent-ui-toolkit';
export default ({ name, gateway, subnet, resolvers = [], first, last }) => (
<Card collapsed flat={!last} topMargin={first} bottomless={!last} gapless>
<CardView>
<CardMeta>
<CardTitle>{name}</CardTitle>
<CardLabel>{gateway}</CardLabel>
<CardLabel>{subnet}</CardLabel>
<CardLabel>{forceArray(resolvers).join('\u00B7')}</CardLabel>
</CardMeta>
</CardView>
</Card>
export default ({ name, gateway, subnet, resolvers = [] }) => (
<TableTr>
<TableTd>{name}</TableTd>
<TableTd>{gateway}</TableTd>
<TableTd>{subnet}</TableTd>
<TableTd>{forceArray(resolvers).join('\u00B7')}</TableTd>
</TableTr>
);

View File

@ -1,56 +0,0 @@
import React from 'react';
import titleCase from 'title-case';
import moment from 'moment';
import {
Card,
CardMeta,
CardAction,
CardTitle,
CardLabel,
CardView,
Checkbox,
FormGroup,
QueryBreakpoints,
StatusLoader
} from 'joyent-ui-toolkit';
const { SmallOnly, Small } = QueryBreakpoints;
const stateColor = {
QUEUED: 'blue',
CANCELED: 'grey',
FAILED: 'red',
CREATED: 'green'
};
export default ({ name, state, created, loading, last, first }) => (
<Card collapsed flat={!last} topMargin={first} bottomless={!last} gapless>
<CardView>
<CardMeta>
<CardAction>
<FormGroup name={name} reduxForm>
<Checkbox />
</FormGroup>
</CardAction>
<CardTitle>{name}</CardTitle>
<CardLabel>{moment.unix(created).fromNow()}</CardLabel>
{loading && (
<CardLabel>
<StatusLoader small />
</CardLabel>
)}
{!loading && (
<Small>
<CardLabel color={stateColor[state]}>{titleCase(state)}</CardLabel>
</Small>
)}
{!loading && (
<SmallOnly>
<CardLabel color={stateColor[state]} />
</SmallOnly>
)}
</CardMeta>
</CardView>
</Card>
);

View File

@ -2,6 +2,7 @@ import React from 'react';
import { Row, Col } from 'react-styled-flexboxgrid';
import forceArray from 'force-array';
import find from 'lodash.find';
import moment from 'moment';
import {
FormGroup,
@ -14,13 +15,31 @@ import {
MessageTitle,
MessageDescription,
Button,
QueryBreakpoints
QueryBreakpoints,
Table,
TableThead,
TableTr,
TableTh,
TableTbody,
TableTd,
Checkbox,
P
} from 'joyent-ui-toolkit';
import Item from './snapshot';
const { SmallOnly, Medium } = QueryBreakpoints;
const Item = ({ name, state, created }) => (
<TableTr>
<TableTd center middle>
<FormGroup name={name} reduxForm>
<Checkbox />
</FormGroup>
</TableTd>
<TableTd>{name}</TableTd>
<TableTd>{moment.unix(created).fromNow()}</TableTd>
</TableTr>
);
export default ({
snapshots = [],
selected = [],
@ -57,21 +76,16 @@ export default ({
</ViewContainer>
);
const items = _snapshots.map((snapshot, i, all) => {
const items = _snapshots.map(snapshot => {
const { name } = snapshot;
const isSelected = Boolean(find(selected, ['name', name]));
const isSubmitting = isSelected && submitting;
return (
<Item
key={name}
{...snapshot}
last={all.length - 1 === i}
first={!i}
loading={isSubmitting}
/>
);
return {
...snapshot,
isSubmitting,
isSelected
};
});
const _error = error &&
@ -82,6 +96,23 @@ export default ({
</Message>
);
const _table = !items.length ? null : (
<Table>
<TableThead>
<TableTr>
<TableTh xs="48" />
<TableTh left bottom>
<P>Name</P>
</TableTh>
<TableTh xs="120" left bottom>
<P>Created</P>
</TableTh>
</TableTr>
</TableThead>
<TableTbody>{items.map(snapshot => <Item key={snapshot.name} {...snapshot} />)}</TableTbody>
</Table>
);
return (
<form
onChange={() => handleSubmit(ctx => handleChange(ctx))}
@ -156,7 +187,7 @@ export default ({
</Row>
{_loading}
{_error}
{items}
{_table}
</form>
);
};

View File

@ -11,7 +11,13 @@ import {
StatusLoader,
Message,
MessageDescription,
MessageTitle
MessageTitle,
Table,
TableThead,
TableTr,
TableTh,
TableTbody,
P
} from 'joyent-ui-toolkit';
import GetFirewallRules from '@graphql/list-firewall-rules.gql';
@ -28,16 +34,31 @@ const Firewall = ({
const _title = <Title>Firewall</Title>;
const _loading = !(loading && !values.length) ? null : <StatusLoader />;
const _firewall =
!_loading &&
values.map((rule, i, all) => (
<InstanceFirewallRule
key={rule.id}
{...rule}
last={all.length - 1 === i}
first={!i}
/>
));
const _firewall = (_loading && !values.length) ? null : (
<Table>
<TableThead>
<TableTr>
<TableTh left bottom>
<P>Rule</P>
</TableTh>
<TableTh xs="63" center bottom>
<P>Global</P>
</TableTh>
<TableTh xs="75" center bottom>
<P>Enabled</P>
</TableTh>
</TableTr>
</TableThead>
<TableTbody>{
values.map((network) => (
<InstanceFirewallRule
key={network.id}
{...network}
/>
))}
</TableTbody>
</Table>
);
const _error =
error && !values.length && !_loading ? (

View File

@ -11,7 +11,13 @@ import {
StatusLoader,
Message,
MessageDescription,
MessageTitle
MessageTitle,
Table,
TableThead,
TableTr,
TableTh,
TableTbody,
P
} from 'joyent-ui-toolkit';
import GetNetworks from '@graphql/list-networks.gql';
@ -22,16 +28,34 @@ const Networks = ({ networks = [], loading, error }) => {
const _title = <Title>Networks</Title>;
const _loading = !(loading && !values.length) ? null : <StatusLoader />;
const _networks =
!_loading &&
values.map((network, i, all) => (
<InstanceNetwork
key={network.id}
{...network}
last={all.length - 1 === i}
first={!i}
/>
));
const _networks = (_loading && !values.length) ? null : (
<Table>
<TableThead>
<TableTr>
<TableTh left bottom>
<P>Name</P>
</TableTh>
<TableTh xs="90" left bottom>
<P>Gateway</P>
</TableTh>
<TableTh xs="90" left bottom>
<P>Subnet</P>
</TableTh>
<TableTh xs="90" left bottom>
<P>Resolvers</P>
</TableTh>
</TableTr>
</TableThead>
<TableTbody>{
values.map((network) => (
<InstanceNetwork
key={network.id}
{...network}
/>
))}
</TableTbody>
</Table>
);
const _error =
error && !values.length && !_loading ? (

View File

@ -75,6 +75,7 @@
"jest-snapshot": "^21.2.1",
"jest-styled-components": "^4.9.0",
"joyent-react-scripts": "^2.6.0",
"lodash.isboolean": "^3.0.3",
"navalia": "^1.2.0",
"react": "^16.0.0",
"react-docgen": "^3.0.0-beta8",

View File

@ -1,6 +1,6 @@
```jsx
const React = require('react');
const { Card } = require('.');
const { default: Card } = require('.');
<Card />;
```
@ -9,7 +9,7 @@ const { Card } = require('.');
```jsx
const React = require('react');
const { Card } = require('.');
const { default: Card } = require('.');
<Card shadow />;
```
@ -18,7 +18,7 @@ const { Card } = require('.');
```jsx
const React = require('react');
const { Card, Header, HeaderBox, HeaderMeta } = require('.');
const { default: Card, Header, HeaderBox, HeaderMeta } = require('.');
const { Row, Col } = require('react-styled-flexboxgrid');
const { H4, P } = require('../text');
@ -31,10 +31,10 @@ const {
<Header>
<HeaderMeta>
<Row between="xs" middle="xs">
<Col xs={4} sm={9} md={10}>
<Col xs={4} sm={8} md={9}>
<H4>Nginx</H4>
</Col>
<Col xs={8} sm={3} md={2}>
<Col xs={8} sm={4} md={3}>
<P>
<InstancesIconLight marginRight="0.5" /> 4 of 4 instances
</P>
@ -52,7 +52,7 @@ const {
```jsx
const React = require('react');
const { Card, Header, HeaderBox, HeaderMeta, Outlet } = require('.');
const { default: Card, Header, HeaderBox, HeaderMeta, Outlet } = require('.');
const { Row, Col } = require('react-styled-flexboxgrid');
const { H4, P } = require('../text');
@ -66,10 +66,10 @@ const {
<Header>
<HeaderMeta>
<Row between="xs" middle="xs">
<Col xs={4} sm={9} md={10}>
<Col xs={4} sm={8} md={9}>
<H4>Nginx</H4>
</Col>
<Col xs={8} sm={3} md={2}>
<Col xs={8} sm={4} md={3}>
<P>
<InstancesIconLight marginRight="0.5" /> 4 of 4 instances
</P>
@ -104,7 +104,7 @@ const {
```jsx
const React = require('react');
const { Card, Header, HeaderBox, HeaderMeta, Outlet } = require('.');
const { default: Card, Header, HeaderBox, HeaderMeta, Outlet } = require('.');
const { Row, Col } = require('react-styled-flexboxgrid');
const { H4, P, Small } = require('../text');
@ -117,10 +117,10 @@ const {
<Header>
<HeaderMeta>
<Row between="xs" middle="xs">
<Col xs={4} sm={9} md={10}>
<Col xs={4} sm={8} md={9}>
<H4>Nginx</H4>
</Col>
<Col xs={8} sm={3} md={2}>
<Col xs={8} sm={4} md={3}>
<P>
<InstancesIconLight marginRight="0.5" /> 4 of 4 instances
</P>
@ -143,7 +143,7 @@ const {
```jsx
const React = require('react');
const { Card, Header, HeaderBox, HeaderMeta, Outlet } = require('.');
const { default: Card, Header, HeaderBox, HeaderMeta, Outlet } = require('.');
const { Row, Col } = require('react-styled-flexboxgrid');
const { H4, P } = require('../text');
const { default: StatusLoader } = require('../status-loader');
@ -184,10 +184,10 @@ const {
<Header>
<HeaderMeta>
<Row between="xs" middle="xs">
<Col xs={4} sm={9} md={10}>
<Col xs={4} sm={8} md={9}>
<H4>Nginx</H4>
</Col>
<Col xs={8} sm={3} md={2}>
<Col xs={8} sm={4} md={3}>
<P>
<InstancesIconLight marginRight="0.5" /> 4 of 4 instances
</P>
@ -233,7 +233,7 @@ const {
```jsx
const React = require('react');
const { Card, Header, HeaderBox, HeaderMeta, Outlet } = require('.');
const { default: Card, Header, HeaderBox, HeaderMeta, Outlet } = require('.');
const { Row, Col } = require('react-styled-flexboxgrid');
const { H4, P } = require('../text');
const { IconActions } = require('../icons');
@ -245,7 +245,7 @@ const { IconActions } = require('../icons');
<Col xs={2} sm={9} md={10}>
<H4>Nginx</H4>
</Col>
<Col xs={5} sm={2} md={1}>
<Col xs={5} sm={2} md={2}>
<P>1 Instance</P>
</Col>
</Row>
@ -262,7 +262,7 @@ const { IconActions } = require('../icons');
```jsx
const React = require('react');
const { Card, Outlet } = require('.');
const { default: Card, Outlet } = require('.');
const { H4, P } = require('../text');
const { HealthyIcon } = require('../icons');
@ -294,7 +294,7 @@ const { HealthyIcon } = require('../icons');
```jsx
const React = require('react');
const { Card, Outlet } = require('.');
const { default: Card, Outlet } = require('.');
const { H4, P } = require('../text');
const { HealthyIcon } = require('../icons');
@ -326,7 +326,7 @@ const { HealthyIcon } = require('../icons');
```jsx
const React = require('react');
const { Card, Outlet } = require('.');
const { default: Card, Outlet } = require('.');
const { H4, P } = require('../text');
const { HealthyIcon } = require('../icons');
@ -404,7 +404,7 @@ const { HealthyIcon } = require('../icons');
```jsx
const React = require('react');
const { Card, Header, HeaderBox, HeaderMeta } = require('.');
const { default: Card, Header, HeaderBox, HeaderMeta } = require('.');
const { Row, Col } = require('react-styled-flexboxgrid');
const { H4, P } = require('../text');
const { HealthyIcon, IconActions, DataCenterIcon } = require('../icons');
@ -414,15 +414,15 @@ const { HealthyIcon, IconActions, DataCenterIcon } = require('../icons');
<Header transparent>
<HeaderMeta>
<Row between="xs" middle="xs">
<Col xs={2} sm={9} md={10}>
<Col xs={2} sm={9} md={8}>
<H4>Nginx</H4>
</Col>
<Col xs={5} sm={2} md={1}>
<Col xs={5} sm={2} md={2}>
<P>
<HealthyIcon /> Healthy
</P>
</Col>
<Col xs={5} sm={2} md={1}>
<Col xs={5} sm={2} md={2}>
<P>
<DataCenterIcon /> eu-ams-1
</P>

View File

@ -1,4 +1,4 @@
export { default as Card } from './card';
export { default } from './card';
export { default as Outlet } from './outlet';
export {

View File

@ -2,7 +2,7 @@
```jsx
const React = require('react');
const { Card } = require('.');
const { default: Card } = require('.');
const { Row, Col } = require('react-styled-flexboxgrid');
[
@ -36,7 +36,7 @@ const { Row, Col } = require('react-styled-flexboxgrid');
```jsx
const React = require('react');
const { Card } = require('.');
const { default: Card } = require('.');
const { Row, Col } = require('react-styled-flexboxgrid');
[
@ -70,7 +70,7 @@ const { Row, Col } = require('react-styled-flexboxgrid');
```jsx
const React = require('react');
const { Card } = require('.');
const { default: Card } = require('.');
const { Row, Col } = require('react-styled-flexboxgrid');
[
@ -128,7 +128,7 @@ const { Row, Col } = require('react-styled-flexboxgrid');
```jsx
const React = require('react');
const { Card } = require('.');
const { default: Card } = require('.');
const { Row, Col } = require('react-styled-flexboxgrid');
[
@ -186,7 +186,7 @@ const { Row, Col } = require('react-styled-flexboxgrid');
```jsx
const React = require('react');
const { Card } = require('.');
const { default: Card } = require('.');
const { Row, Col } = require('react-styled-flexboxgrid');
[
@ -244,7 +244,7 @@ const { Row, Col } = require('react-styled-flexboxgrid');
```jsx
const React = require('react');
const { Card, Header } = require('.');
const { default: Card, Header } = require('.');
const { Row, Col } = require('react-styled-flexboxgrid');
[
@ -317,7 +317,7 @@ const { Row, Col } = require('react-styled-flexboxgrid');
```jsx
const React = require('react');
const { Card, Header } = require('.');
const { default: Card, Header } = require('.');
const { Row, Col } = require('react-styled-flexboxgrid');
[
@ -390,7 +390,7 @@ const { Row, Col } = require('react-styled-flexboxgrid');
```jsx
const React = require('react');
const { Card, Header } = require('.');
const { default: Card, Header } = require('.');
const { Row, Col } = require('react-styled-flexboxgrid');
[
@ -463,7 +463,7 @@ const { Row, Col } = require('react-styled-flexboxgrid');
```jsx
const React = require('react');
const { Card, Header, HeaderBox, HeaderMeta } = require('.');
const { default: Card, Header, HeaderBox, HeaderMeta } = require('.');
const { Row, Col } = require('react-styled-flexboxgrid');
[
@ -590,7 +590,7 @@ const { Row, Col } = require('react-styled-flexboxgrid');
```jsx
const React = require('react');
const { Card, Header, HeaderBox, HeaderMeta } = require('.');
const { default: Card, Header, HeaderBox, HeaderMeta } = require('.');
const { Row, Col } = require('react-styled-flexboxgrid');
[
@ -729,7 +729,7 @@ const { Row, Col } = require('react-styled-flexboxgrid');
```jsx
const React = require('react');
const { Card, Header, HeaderBox, HeaderMeta, Outlet } = require('.');
const { default: Card, Header, HeaderBox, HeaderMeta, Outlet } = require('.');
const { Row, Col } = require('react-styled-flexboxgrid');
[
@ -889,7 +889,7 @@ const { Row, Col } = require('react-styled-flexboxgrid');
```jsx
const React = require('react');
const { Card, Header, HeaderBox, HeaderMeta, Outlet } = require('.');
const { default: Card, Header, HeaderBox, HeaderMeta, Outlet } = require('.');
const { Row, Col } = require('react-styled-flexboxgrid');
[
@ -1094,7 +1094,7 @@ const { Row, Col } = require('react-styled-flexboxgrid');
```jsx
const React = require('react');
const { Card, Header, HeaderBox, HeaderMeta, Outlet } = require('.');
const { default: Card, Header, HeaderBox, HeaderMeta, Outlet } = require('.');
const { Row, Col } = require('react-styled-flexboxgrid');
[

View File

@ -1,486 +0,0 @@
#### Card > Headed > Collapsed
```jsx
const {
CardDescription,
CardHeader,
CardMeta,
CardOptions,
CardOutlet,
CardSubTitle,
CardTitle,
CardView,
CardInfo,
CardInfoLabel,
CardInfoIconContainer
} = require('./');
const { InstancesIconLight } = require('../icons');
<Card collapsed headed>
<CardHeader>
<CardMeta>
<CardTitle>Nginx</CardTitle>
<CardDescription>
<CardInfo>
<CardInfoIconContainer>
<InstancesIconLight />
</CardInfoIconContainer>
<CardInfoLabel left light>
4 of 4 instances
</CardInfoLabel>
</CardInfo>
</CardDescription>
</CardMeta>
<CardOptions />
</CardHeader>
</Card>;
```
#### Card > Headed
```jsx
const {
CardDescription,
CardHeader,
CardMeta,
CardOptions,
CardOutlet,
CardSubTitle,
CardTitle,
CardView,
CardInfo,
CardInfoLabel,
CardInfoIconContainer
} = require('./');
const { InstancesIconLight, HealthyIcon } = require('../icons');
<Card headed>
<CardHeader>
<CardMeta>
<CardTitle>Nginx</CardTitle>
<CardDescription>
<CardInfo>
<CardInfoIconContainer>
<InstancesIconLight />
</CardInfoIconContainer>
<CardInfoLabel left light>
4 of 4 instances
</CardInfoLabel>
</CardInfo>
</CardDescription>
</CardMeta>
<CardOptions />
</CardHeader>
<CardView>
<CardDescription>
<CardInfo
icon={<HealthyIcon healthy="HEALTHY" />}
iconPosition="left"
label="Healthy"
color="dark"
/>
</CardDescription>
</CardView>
</Card>;
```
#### Card > Single state
```jsx
const {
CardDescription,
CardHeader,
CardMeta,
CardOptions,
CardOutlet,
CardSubTitle,
CardTitle,
CardView,
CardInfo,
CardInfoLabel,
CardInfoIconContainer
} = require('./');
const { InstancesIconLight, HealthyIcon } = require('../icons');
<Card headed>
<CardHeader>
<CardMeta>
<CardTitle>Nginx</CardTitle>
<CardDescription>
<CardInfo>
<CardInfoIconContainer>
<InstancesIconLight />
</CardInfoIconContainer>
<CardInfoLabel left light>
4 of 4 instances
</CardInfoLabel>
</CardInfo>
</CardDescription>
</CardMeta>
<CardOptions />
</CardHeader>
<CardView>
<CardDescription>
1 instance paused <br />
1 instances stopped <br />
1 instance not responding <br />
</CardDescription>
</CardView>
</Card>;
```
#### Card > Provisioning
```jsx
const {
CardDescription,
CardHeader,
CardMeta,
CardOptions,
CardOutlet,
CardSubTitle,
CardTitle,
CardView,
CardInfo,
CardInfoLabel,
CardInfoIconContainer
} = require('./');
const StatusLoader = require('../status-loader').default;
const { InstancesIconLight, HealthyIcon } = require('../icons');
<Card collapsed headed disabled>
<CardHeader disabled>
<CardMeta>
<CardTitle disabled>
<span>Nginx</span>
<StatusLoader inline row msg="Provisioning" />
</CardTitle>
</CardMeta>
<CardOptions disabled />
</CardHeader>
</Card>;
```
```jsx
const {
CardDescription,
CardHeader,
CardMeta,
CardOptions,
CardOutlet,
CardSubTitle,
CardTitle,
CardView,
CardInfo,
CardInfoLabel,
CardInfoIconContainer
} = require('./');
const StatusLoader = require('../status-loader').default;
const { InstancesIconLight, HealthyIcon } = require('../icons');
<Card headed>
<CardHeader>
<CardMeta>
<CardTitle>Nginx</CardTitle>
<CardDescription>
<CardInfo>
<CardInfoIconContainer>
<InstancesIconLight />
</CardInfoIconContainer>
<CardInfoLabel left light>
4 of 4 instances
</CardInfoLabel>
</CardInfo>
</CardDescription>
</CardMeta>
<CardOptions />
</CardHeader>
<CardView>
<CardDescription>
<StatusLoader inline row msg="Provisioning 3 instances" />
<br />
<br />
<br />
<CardInfo>
<CardInfoIconContainer>
<HealthyIcon />
</CardInfoIconContainer>
<CardInfoLabel left>Healthy</CardInfoLabel>
</CardInfo>
</CardDescription>
</CardView>
</Card>;
```
#### Card > Disabled
```jsx
const {
CardDescription,
CardHeader,
CardMeta,
CardOptions,
CardOutlet,
CardSubTitle,
CardTitle,
CardView,
CardInfo,
CardInfoLabel,
CardInfoIconContainer
} = require('./');
const { InstancesIcon, HealthyIcon } = require('../icons');
const StatusLoader = require('../status-loader').default;
<Card headed disabled>
<CardHeader disabled>
<CardMeta>
<CardTitle disabled>Nginx</CardTitle>
<CardDescription disabled>
<CardInfo>
<CardInfoIconContainer>
<InstancesIcon />
</CardInfoIconContainer>
<CardInfoLabel left>4 of 4 instances</CardInfoLabel>
</CardInfo>
</CardDescription>
</CardMeta>
<CardOptions disabled />
</CardHeader>
<CardView>
<CardDescription>
<StatusLoader row msg="Provisioning 3 instances" />
</CardDescription>
</CardView>
</Card>;
```
#### Card > Instance
```jsx
const {
CardDescription,
CardOutlet,
CardTitle,
CardView,
CardInfo,
CardInfoLabel,
CardInfoIconContainer
} = require('./');
const { Row } = require('react-styled-flexboxgrid');
const { InstancesIconLight, HealthyIcon } = require('../icons');
<Card>
<CardView>
<CardDescription>
<b>percona_primary</b>
<CardInfo>
<CardInfoIconContainer>
<HealthyIcon />
</CardInfoIconContainer>
<CardInfoLabel left>Healthy</CardInfoLabel>
</CardInfo>
</CardDescription>
</CardView>
</Card>;
```
#### Card > Instance > Stacked
```jsx
const {
CardDescription,
CardOutlet,
CardTitle,
CardView,
CardInfo,
CardInfoLabel,
CardInfoIconContainer
} = require('./');
const { Row } = require('react-styled-flexboxgrid');
const { InstancesIconLight, HealthyIcon } = require('../icons');
<Card stacked>
<CardView>
<CardDescription>
<b>percona_primary</b>
<span>4 instances</span>
<CardInfo>
<CardInfoIconContainer>
<HealthyIcon />
</CardInfoIconContainer>
<CardInfoLabel left>Healthy</CardInfoLabel>
</CardInfo>
</CardDescription>
</CardView>
</Card>;
```
#### Card > Instance > Group
```jsx
const {
CardDescription,
CardOutlet,
CardTitle,
CardView,
CardInfo,
CardInfoLabel,
CardInfoIconContainer
} = require('./');
const { Row } = require('react-styled-flexboxgrid');
const { InstancesIconLight, HealthyIcon } = require('../icons');
<div>
<Card>
<CardView>
<CardDescription>
<b>percona_primary</b>
<CardInfo>
<CardInfoIconContainer>
<HealthyIcon />
</CardInfoIconContainer>
<CardInfoLabel left>Healthy</CardInfoLabel>
</CardInfo>
</CardDescription>
</CardView>
</Card>
<Card>
<CardView>
<CardDescription>
<b>percona_primary</b>
<CardInfo>
<CardInfoIconContainer>
<HealthyIcon />
</CardInfoIconContainer>
<CardInfoLabel left>Healthy</CardInfoLabel>
</CardInfo>
</CardDescription>
</CardView>
</Card>
<Card stacked>
<CardView>
<CardDescription>
<b>percona_primary</b>
<span>4 instances</span>
<CardInfo>
<CardInfoIconContainer>
<HealthyIcon />
</CardInfoIconContainer>
<CardInfoLabel left>Healthy</CardInfoLabel>
</CardInfo>
</CardDescription>
</CardView>
</Card>
</div>;
```
#### Card > Instance > List
```jsx
const {
Card,
CardInfo,
CardInfoLabel,
CardInfoIconContainer,
CardView,
CardTitle,
CardDescription,
CardOptions
} = require('./');
const { HealthyIcon, DataCenterIcon } = require('../icons');
<Card collapsed>
<CardView>
<CardTitle>WordPress_01</CardTitle>
<CardDescription>
<CardInfo>
<CardInfoIconContainer>
<HealthyIcon />
</CardInfoIconContainer>
<CardInfoLabel left>Healthy</CardInfoLabel>
</CardInfo>
</CardDescription>
<CardDescription>
<CardInfo>
<CardInfoIconContainer>
<HealthyIcon />
</CardInfoIconContainer>
<CardInfoLabel left>Healthy</CardInfoLabel>
</CardInfo>
</CardDescription>
<CardOptions />
</CardView>
</Card>;
```
#### Card > Secondary
```jsx
const {
CardDescription,
CardHeader,
CardMeta,
CardOptions,
CardOutlet,
CardSubTitle,
CardTitle,
CardView,
CardGroupView,
CardFooter
} = require('./');
<Card transparent>
<CardView>
<CardMeta>
<CardTitle>$0.016 per hour</CardTitle>
<CardSubTitle>0.256 GB RAM</CardSubTitle>
<CardSubTitle>0.25 vCPUs</CardSubTitle>
<CardSubTitle>0.01 TB disk</CardSubTitle>
<CardSubTitle>SSD</CardSubTitle>
<CardFooter>Compute Optimise</CardFooter>
</CardMeta>
</CardView>
</Card>;
```
#### Card > Secondary > Active
```jsx
const {
CardDescription,
CardHeader,
CardMeta,
CardOptions,
CardOutlet,
CardSubTitle,
CardTitle,
CardView,
CardGroupView,
CardFooter
} = require('./');
<Card transparent selected>
<CardView>
<CardMeta>
<CardTitle selected>$0.016 per hour</CardTitle>
<CardSubTitle selected>0.256 GB RAM</CardSubTitle>
<CardSubTitle selected>0.25 vCPUs</CardSubTitle>
<CardSubTitle selected>0.01 TB disk</CardSubTitle>
<CardSubTitle selected>SSD</CardSubTitle>
<CardFooter selected>Compute Optimise</CardFooter>
</CardMeta>
</CardView>
</Card>;
```

View File

@ -0,0 +1,12 @@
import remcalc from 'remcalc';
import styled from 'styled-components';
import Baseline from '../baseline';
export default Baseline(styled.span`
width: ${remcalc(6)};
height: ${remcalc(6)};
border-radius: ${remcalc(3)};
background-color: ${props => props.theme[props.color]};
display: inline-block;
`);

View File

@ -15,6 +15,7 @@ export { default as UserIcon } from './user';
export { default as UserIconLight } from './user-light';
export { default as DataCenterIcon } from './data-center';
export { default as DataCenterIconLight } from './data-center-light';
export { default as DotIcon } from './dot';
export { default as ChevronIcon } from './chevron';
export { default as TritonIcon } from './triton';
export { default as TritonBetaIcon } from './triton-beta'

View File

@ -114,6 +114,7 @@ export {
UserIcon,
DataCenterIcon,
DataCenterIconLight,
DotIcon,
ChevronIcon,
TritonIcon,
UserIconLight,
@ -138,3 +139,12 @@ export {
Divider as PopoverDivider,
default as Popover
} from './popover';
export {
default as Table,
Thead as TableThead,
Tr as TableTr,
Th as TableTh,
Tbody as TableTbody,
Td as TableTd
} from './table';

View File

@ -1,4 +1,5 @@
import remcalc from 'remcalc';
import { H4 } from '../text/headings';
export default H4.extend`

View File

@ -0,0 +1,262 @@
import React from 'react';
import { Broadcast, Subscriber } from 'joy-react-broadcast';
import isBoolean from 'lodash.isboolean';
import styled, { css } from 'styled-components';
import is from 'styled-is';
import remcalc from 'remcalc';
import Baseline from '../baseline';
import { bottomShadow } from '../boxes';
import * as breakpoints from '../breakpoints';
const { styled: query } = breakpoints;
const handleBreakpoint = bp => props => {
const hidden =
(isBoolean(props[bp]) && !props[bp]) || Number(props[bp]) === 0;
const width = remcalc(props[bp]);
return `
width: ${width};
${hidden &&
`
display: none;
`};
`;
};
const ColumnBorder = css`
${is('border')`
border-${props => props.border}-width: ${remcalc(1)};
`};
`;
const Column = css`
border-width: ${remcalc(1)};
border-style: solid;
border-color: ${props => props.theme.grey};
border-spacing: 0;
${is('disabled')`
border-color: ${props => props.theme.grey};
`};
white-space: nowrap;
box-sizing: border-box;
padding: 0 ${remcalc(8)} 0 ${remcalc(8)};
height: ${remcalc(48)};
${handleBreakpoint('xs')};
${query.small`
${handleBreakpoint('sm')};
`};
${query.medium`
${handleBreakpoint('md')};
`};
${query.xlargeUp`
${handleBreakpoint('lg')};
`};
${is('actionable')`
cursor: pointer;
&:hover {
background-color: ${props => props.theme.whiteHover};
}
&:active {
background-color: ${props => props.theme.whiteActive};
}
`};
${is('baseline')`
vertical-align: baseline;
`};
${is('sub')`
vertical-align: sub;
`};
${is('text-top')`
vertical-align: text-top;
`};
${is('text-bottom')`
vertical-align: text-bottom;
`};
${is('middle')`
vertical-align: middle;
`};
${is('top')`
vertical-align: top;
`};
${is('bottom')`
vertical-align: bottom;
`};
${is('center')`
text-align: center;
`};
${is('left')`
text-align: left;
`};
${is('right')`
text-align: right;
`};
`;
const BaseTable = styled.table`
border-collapse: collapse;
table-layout: fixed;
width: 100%;
`;
const BaseThead = styled.thead`
width: 100%;
`;
const BaseTbody = styled.tbody`
width: 100%;
border-width: ${remcalc(1)};
border-style: solid;
border-color: ${props => props.theme.grey};
${is('shadow')`
box-shadow: ${bottomShadow};
`};
${is('actionable')`
cursor: pointer;
`};
${is('disabled')`
border-color: ${props => props.theme.grey};
`};
`;
const BaseTh = styled.th`
${Column};
border-left-width: 0;
border-right-width: 0;
border-top-width: 0;
padding-top: 0;
padding-bottom: ${remcalc(5)};
${ColumnBorder};
`;
const BaseTd = styled.td`
${Column};
border-left-width: 0;
border-right-width: 0;
${ColumnBorder};
`;
const BaseTr = styled.tr`
display: table-row;
color: ${props => props.theme.text};
background-color: ${props => props.theme.white};
${is('actionable')`
cursor: pointer;
`};
${is('disabled')`
background-color: ${props => props.theme.disabled};
color: ${props => props.theme.text};
cursor: default;
`};
/* override background when thead > tr */
${is('header')`
color: ${props => props.theme.text};
background-color: transparent;
`};
`;
/**
* @example ./usage.md
*/
export default Baseline(({ children, ...rest }) => (
<Broadcast channel="almost-responsive-table" value={rest}>
<BaseTable {...rest}>{children}</BaseTable>
</Broadcast>
));
const Propagate = ({ children, ...rest }) => (
<Subscriber channel="almost-responsive-table">
{({ disabled, header }) => (
<Broadcast
channel="almost-responsive-table"
value={{ disabled, header, ...rest }}
>
{children({ disabled, header, ...rest })}
</Broadcast>
)}
</Subscriber>
);
export const Thead = Baseline(({ children, ...rest }) => (
<Propagate {...rest} header={true}>
{value => (
<BaseThead {...value} name="thdead">
{children}
</BaseThead>
)}
</Propagate>
));
export const Tr = Baseline(({ children, ...rest }) => (
<Propagate {...rest}>
{value => (
<BaseTr {...value} name="tr">
{children}
</BaseTr>
)}
</Propagate>
));
export const Th = Baseline(({ children, ...rest }) => (
<Propagate {...rest}>
{value => (
<BaseTh {...value} name="th">
{children}
</BaseTh>
)}
</Propagate>
));
export const Tbody = Baseline(({ children, ...rest }) => (
<Propagate {...rest}>
{value => (
<BaseTbody {...value} name="tbody">
{children}
</BaseTbody>
)}
</Propagate>
));
export const Td = Baseline(({ children, ...rest }) => (
<Propagate {...rest}>
{value => (
<BaseTd {...value} name="td">
{children}
</BaseTd>
)}
</Propagate>
));

View File

@ -0,0 +1,40 @@
```jsx
const React = require('react');
const { default: Table, Thead, Tr, Th, Tbody, Td } = require('./');
const { H4, P } = require('../text');
<Table>
<Thead>
<Tr>
<Th xs="48" />
<Th>Name</Th>
<Th xs="150">Status</Th>
<Th xs="150">Short ID</Th>
<Th xs="48" />
</Tr>
</Thead>
<Tbody>
<Tr actionable>
<Td border="right" middle center>HB</Td>
<Td><H4>percona_high-ram-32_1</H4></Td>
<Td>Provisioning</Td>
<Td>2252839a</Td>
<Td>HB</Td>
</Tr>
<Tr>
<Td>HB</Td>
<Td>percona_high-ram-32_2</Td>
<Td>Provisioning</Td>
<Td>2252839b</Td>
<Td xs="48">HB</Td>
</Tr>
<Tr>
<Td>HB</Td>
<Td>percona_high-ram-32_3</Td>
<Td>Provisioning</Td>
<Td>2252839b</Td>
<Td>HB</Td>
</Tr>
</Tbody>
</Table>
```

View File

@ -61,6 +61,7 @@ module.exports = {
name: 'Components',
components: () => [
'src/card/card.js',
'src/table/index.js',
'src/breadcrumb/index.js',
'src/button/index.js',
'src/form/checkbox.js',

View File

@ -7466,6 +7466,10 @@ lodash.isarraylike@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/lodash.isarraylike/-/lodash.isarraylike-4.2.0.tgz#4623310ab318804b667ddc3619058137559400c4"
lodash.isboolean@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6"
lodash.isempty@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.isempty/-/lodash.isempty-4.4.0.tgz#6f86cbedd8be4ec987be9aaf33c9684db1b31e7e"