feat: tags and metadata new ui and mutations
This commit is contained in:
parent
34db52d541
commit
c47333ed1b
@ -19,6 +19,9 @@
|
|||||||
"test-ci": "lerna run test-ci",
|
"test-ci": "lerna run test-ci",
|
||||||
"test": "lerna run test",
|
"test": "lerna run test",
|
||||||
"clean": "lerna clean --yes",
|
"clean": "lerna clean --yes",
|
||||||
|
"dev": "redrun -p dev:*",
|
||||||
|
"dev:ui": "lerna run watch --scope joyent-ui-toolkit --stream",
|
||||||
|
"dev:mjb": "lerna run dev --scope my-joy-beta --stream",
|
||||||
"commitmsg": "commitlint -e",
|
"commitmsg": "commitlint -e",
|
||||||
"precommit": "cross-env CI=1 redrun -s lint-staged format-staged",
|
"precommit": "cross-env CI=1 redrun -s lint-staged format-staged",
|
||||||
"postinstall": "lerna run prepublish",
|
"postinstall": "lerna run prepublish",
|
||||||
@ -69,12 +72,11 @@
|
|||||||
"lodash.keys": "4.2.0",
|
"lodash.keys": "4.2.0",
|
||||||
"lodash.defaults": "4.2.0",
|
"lodash.defaults": "4.2.0",
|
||||||
"lodash.assign": "4.2.0",
|
"lodash.assign": "4.2.0",
|
||||||
"graphql": "0.11.7",
|
|
||||||
"isarray": "'2.0.2",
|
"isarray": "'2.0.2",
|
||||||
"moment": "2.19.1",
|
"moment": "2.19.1",
|
||||||
"codemirror": "5.30.0",
|
"codemirror": "5.30.0",
|
||||||
"react": "16.0.0",
|
"react": "16.1.1",
|
||||||
"react-dom": "16.0.0",
|
"react-dom": "16.1.1",
|
||||||
"react-modal": "2.4.1",
|
"react-modal": "2.4.1",
|
||||||
"hoist-non-react-statics": "2.3.1"
|
"hoist-non-react-statics": "2.3.1"
|
||||||
}
|
}
|
||||||
|
@ -6,48 +6,51 @@
|
|||||||
"repository": "github:yldio/joyent-portal",
|
"repository": "github:yldio/joyent-portal",
|
||||||
"main": "build/",
|
"main": "build/",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "REACT_APP_GQL_PORT=4000 PORT=3069 REACT_APP_GQL_PROTOCOL=http joyent-react-scripts start",
|
"dev":
|
||||||
|
"REACT_APP_GQL_PORT=4000 PORT=3069 REACT_APP_GQL_PROTOCOL=http joyent-react-scripts start",
|
||||||
"start": "PORT=3069 joyent-react-scripts start",
|
"start": "PORT=3069 joyent-react-scripts start",
|
||||||
"build": "NODE_ENV=production joyent-react-scripts build",
|
"build": "NODE_ENV=production joyent-react-scripts build",
|
||||||
"lint-ci":
|
"lint-ci":
|
||||||
"eslint . --ext .js --ext .md && echo 0 `# stylelint './src/**/*.js'`",
|
"eslint . --ext .js --ext .md && echo 0 `# stylelint './src/**/*.js'`",
|
||||||
"lint":
|
"lint":
|
||||||
"eslint . --fix --ext .js --ext .md && echo 0 `# stylelint './src/**/*.js'`",
|
"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 joyent-react-scripts test --env=jsdom",
|
||||||
"test": "NODE_ENV=test ./test/run --env=jsdom",
|
"test-ci": "redrun test",
|
||||||
"prepublish": "echo 0"
|
"prepublish": "echo 0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@manaflair/redux-batch": "^0.1.0",
|
"@manaflair/redux-batch": "^0.1.0",
|
||||||
"apollo": "^0.2.2",
|
"apollo": "^0.2.2",
|
||||||
|
"declarative-redux-form": "^1.0.3",
|
||||||
"joyent-ui-toolkit": "^2.0.1",
|
"joyent-ui-toolkit": "^2.0.1",
|
||||||
"lodash.find": "^4.6.0",
|
"lodash.find": "^4.6.0",
|
||||||
"lodash.get": "^4.4.2",
|
"lodash.get": "^4.4.2",
|
||||||
"lodash.isstring": "^4.0.1",
|
"lodash.isstring": "^4.0.1",
|
||||||
"lodash.sortby": "^4.7.0",
|
"lodash.sortby": "^4.7.0",
|
||||||
"lunr": "^2.1.4",
|
"lunr": "^2.1.4",
|
||||||
"moment": "^2.19.1",
|
"moment": "^2.19.2",
|
||||||
"normalized-styled-components": "^1.0.17",
|
"normalized-styled-components": "^1.0.17",
|
||||||
"param-case": "^2.1.1",
|
"param-case": "^2.1.1",
|
||||||
"prop-types": "^15.6.0",
|
"prop-types": "^15.6.0",
|
||||||
"react": "^16.0.0",
|
"react": "^16.1.1",
|
||||||
"react-apollo": "^1.4.16",
|
"react-apollo": "^1.4.16",
|
||||||
"react-dom": "^16.0.0",
|
"react-dom": "^16.1.1",
|
||||||
"react-json-view": "^1.13.1",
|
"react-json-view": "^1.13.3",
|
||||||
"react-redux": "^5.0.6",
|
"react-redux": "^5.0.6",
|
||||||
|
"react-redux-values": "^1.0.2",
|
||||||
"react-router": "^4.2.0",
|
"react-router": "^4.2.0",
|
||||||
"react-router-dom": "^4.2.2",
|
"react-router-dom": "^4.2.2",
|
||||||
"redux": "^3.7.2",
|
"redux": "^3.7.2",
|
||||||
"redux-actions": "^2.2.1",
|
"redux-actions": "^2.2.1",
|
||||||
"redux-form": "^7.1.1",
|
"redux-form": "^7.1.2",
|
||||||
"remcalc": "^1.0.9",
|
"remcalc": "^1.0.9",
|
||||||
"styled-components": "^2.2.2",
|
"styled-components": "^2.2.3",
|
||||||
"title-case": "^2.1.1"
|
"title-case": "^2.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"babel-plugin-inline-react-svg": "^0.4.0",
|
"babel-plugin-inline-react-svg": "^0.4.0",
|
||||||
"babel-preset-joyent-portal": "^3.3.3",
|
"babel-preset-joyent-portal": "^3.3.3",
|
||||||
"eslint": "^4.9.0",
|
"eslint": "^4.11.0",
|
||||||
"eslint-config-joyent-portal": "^3.2.0",
|
"eslint-config-joyent-portal": "^3.2.0",
|
||||||
"jest": "^21.2.1",
|
"jest": "^21.2.1",
|
||||||
"jest-alias-preprocessor": "^1.1.1",
|
"jest-alias-preprocessor": "^1.1.1",
|
||||||
@ -58,10 +61,10 @@
|
|||||||
"jest-snapshot": "^21.2.1",
|
"jest-snapshot": "^21.2.1",
|
||||||
"jest-styled-components": "^4.9.0",
|
"jest-styled-components": "^4.9.0",
|
||||||
"jest-transform-graphql": "^2.1.0",
|
"jest-transform-graphql": "^2.1.0",
|
||||||
"joyent-react-scripts": "^2.6.0",
|
"joyent-react-scripts": "^3.1.0",
|
||||||
"react-test-renderer": "^16.0.0",
|
"react-test-renderer": "^16.1.1",
|
||||||
"redrun": "^5.9.18",
|
"redrun": "^5.10.0",
|
||||||
"serve": "^6.3.1",
|
"serve": "^6.4.1",
|
||||||
"stylelint": "^8.2.0",
|
"stylelint": "^8.2.0",
|
||||||
"stylelint-config-joyent-portal": "^2.0.1"
|
"stylelint-config-joyent-portal": "^2.0.1"
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,86 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import renderer from 'react-test-renderer';
|
||||||
|
import { reduxForm } from 'redux-form';
|
||||||
|
import Store from '@mocks/store';
|
||||||
|
import 'jest-styled-components';
|
||||||
|
|
||||||
|
import KeyValue from '../key-value';
|
||||||
|
|
||||||
|
const KeyValueForm = reduxForm()(KeyValue);
|
||||||
|
|
||||||
|
it('renders <KeyValue /> without throwing', () => {
|
||||||
|
const tree = renderer
|
||||||
|
.create(
|
||||||
|
<Store>
|
||||||
|
<KeyValueForm />
|
||||||
|
</Store>
|
||||||
|
)
|
||||||
|
.toJSON();
|
||||||
|
expect(tree).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders <KeyValue textarea /> with textareas', () => {
|
||||||
|
const tree = renderer
|
||||||
|
.create(
|
||||||
|
<Store>
|
||||||
|
<KeyValueForm textarea />
|
||||||
|
</Store>
|
||||||
|
)
|
||||||
|
.toJSON();
|
||||||
|
expect(tree).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders <KeyValue expanded /> expanded', () => {
|
||||||
|
const tree = renderer
|
||||||
|
.create(
|
||||||
|
<Store>
|
||||||
|
<KeyValueForm expanded />
|
||||||
|
</Store>
|
||||||
|
)
|
||||||
|
.toJSON();
|
||||||
|
expect(tree).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders <KeyValue submitting /> with loader', () => {
|
||||||
|
const tree = renderer
|
||||||
|
.create(
|
||||||
|
<Store>
|
||||||
|
<KeyValueForm submitting />
|
||||||
|
</Store>
|
||||||
|
)
|
||||||
|
.toJSON();
|
||||||
|
expect(tree).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders <KeyValue first /> without top margin', () => {
|
||||||
|
const tree = renderer
|
||||||
|
.create(
|
||||||
|
<Store>
|
||||||
|
<KeyValueForm first />
|
||||||
|
</Store>
|
||||||
|
)
|
||||||
|
.toJSON();
|
||||||
|
expect(tree).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders <KeyValue last /> with bottom border', () => {
|
||||||
|
const tree = renderer
|
||||||
|
.create(
|
||||||
|
<Store>
|
||||||
|
<KeyValueForm last />
|
||||||
|
</Store>
|
||||||
|
)
|
||||||
|
.toJSON();
|
||||||
|
expect(tree).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders <KeyValue label /> with proper label', () => {
|
||||||
|
const tree = renderer
|
||||||
|
.create(
|
||||||
|
<Store>
|
||||||
|
<KeyValueForm label="Label" />
|
||||||
|
</Store>
|
||||||
|
)
|
||||||
|
.toJSON();
|
||||||
|
expect(tree).toMatchSnapshot();
|
||||||
|
});
|
@ -1,106 +1,192 @@
|
|||||||
import React from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import { Row, Col } from 'react-styled-flexboxgrid';
|
import { Row, Col } from 'react-styled-flexboxgrid';
|
||||||
|
import Value from 'react-redux-values';
|
||||||
import { Field } from 'redux-form';
|
import { Field } from 'redux-form';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import remcalc from 'remcalc';
|
||||||
|
import titleCase from 'title-case';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
Message,
|
||||||
|
MessageDescription,
|
||||||
|
MessageTitle,
|
||||||
|
Card,
|
||||||
|
CardHeader,
|
||||||
|
CardHeaderMeta,
|
||||||
|
CardHeaderBox,
|
||||||
|
CardOutlet,
|
||||||
|
ChevronIcon,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
|
Label,
|
||||||
Input,
|
Input,
|
||||||
|
FormMeta,
|
||||||
Button,
|
Button,
|
||||||
BinIcon,
|
Textarea,
|
||||||
QueryBreakpoints,
|
Editor,
|
||||||
Divider,
|
Divider,
|
||||||
Editor
|
P
|
||||||
} from 'joyent-ui-toolkit';
|
} from 'joyent-ui-toolkit';
|
||||||
|
|
||||||
const { SmallOnly } = QueryBreakpoints;
|
const CollapsedKeyValue = styled.span`
|
||||||
|
word-break: break-all;
|
||||||
|
line-height: 1.5;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: block;
|
||||||
|
`;
|
||||||
|
|
||||||
const TextareaKeyValue = ({
|
class ValueTextareaField extends PureComponent {
|
||||||
name,
|
render() {
|
||||||
formName,
|
const { input, submitting } = this.props;
|
||||||
formValue,
|
|
||||||
handleSubmit,
|
return input.value === 'user-script' ? (
|
||||||
|
<Field name="value" component={Editor} />
|
||||||
|
) : (
|
||||||
|
<Textarea resize="vertical" disabled={submitting} fluid />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const KeyValue = ({
|
||||||
|
id,
|
||||||
|
label = '',
|
||||||
|
textarea,
|
||||||
|
create,
|
||||||
|
last,
|
||||||
|
first,
|
||||||
|
expanded,
|
||||||
|
removing,
|
||||||
|
pristine,
|
||||||
|
error,
|
||||||
|
submitting,
|
||||||
onRemove,
|
onRemove,
|
||||||
textarea
|
onToggleExpanded,
|
||||||
}) => (
|
|
||||||
<form onSubmit={handleSubmit}>
|
|
||||||
<Row>
|
|
||||||
<Col xs={8} sm={10}>
|
|
||||||
<FormGroup name={formName} reduxForm>
|
|
||||||
<Input fluid mono marginless />
|
|
||||||
</FormGroup>
|
|
||||||
</Col>
|
|
||||||
<Col xs={2} sm={1}>
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
onClick={() => onRemove(name)}
|
|
||||||
secondary
|
|
||||||
small
|
|
||||||
icon
|
|
||||||
fluid
|
|
||||||
marginless
|
|
||||||
>
|
|
||||||
<BinIcon />
|
|
||||||
</Button>
|
|
||||||
</Col>
|
|
||||||
<Col xs={2} sm={1}>
|
|
||||||
<Button type="submit" secondary small icon fluid marginless>
|
|
||||||
S
|
|
||||||
</Button>
|
|
||||||
</Col>
|
|
||||||
<Col xs={12} sm={12}>
|
|
||||||
<FormGroup name={formValue} reduxForm>
|
|
||||||
<Field name={formValue} component={Editor} mode="sh" />
|
|
||||||
</FormGroup>
|
|
||||||
</Col>
|
|
||||||
<Divider height="4" width="100%" transparent />
|
|
||||||
</Row>
|
|
||||||
</form>
|
|
||||||
);
|
|
||||||
|
|
||||||
const InputKeyValue = ({
|
|
||||||
name,
|
|
||||||
formName,
|
|
||||||
formValue,
|
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
onRemove,
|
onClear
|
||||||
textarea
|
}) => {
|
||||||
}) => (
|
const _error = error &&
|
||||||
<form onSubmit={handleSubmit}>
|
!submitting && (
|
||||||
<Row>
|
<Message error>
|
||||||
<Col xs={12} sm={5}>
|
<MessageTitle>Ooops!</MessageTitle>
|
||||||
<FormGroup name={formName} reduxForm>
|
<MessageDescription>{error}</MessageDescription>
|
||||||
<Input fluid mono marginless />
|
</Message>
|
||||||
</FormGroup>
|
);
|
||||||
</Col>
|
|
||||||
<Col xs={12} sm={5}>
|
|
||||||
<FormGroup name={formValue} reduxForm>
|
|
||||||
<Input fluid mono marginless />
|
|
||||||
</FormGroup>
|
|
||||||
</Col>
|
|
||||||
<Col xs={6} sm={1}>
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
onClick={() => onRemove(name)}
|
|
||||||
secondary
|
|
||||||
small
|
|
||||||
icon
|
|
||||||
fluid
|
|
||||||
marginless
|
|
||||||
>
|
|
||||||
<BinIcon />
|
|
||||||
</Button>
|
|
||||||
</Col>
|
|
||||||
<Col xs={6} sm={1}>
|
|
||||||
<Button type="submit" secondary small icon fluid marginless>
|
|
||||||
S
|
|
||||||
</Button>
|
|
||||||
</Col>
|
|
||||||
<SmallOnly>
|
|
||||||
<Divider height="4" width="100%" transparent />
|
|
||||||
</SmallOnly>
|
|
||||||
</Row>
|
|
||||||
</form>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default ({ textarea, ...rest }) =>
|
const _meta = expanded ? (
|
||||||
textarea ? <TextareaKeyValue {...rest} /> : <InputKeyValue {...rest} />;
|
<P>{create ? `Create ${label}` : `Edit ${label}`}</P>
|
||||||
|
) : (
|
||||||
|
<CollapsedKeyValue>
|
||||||
|
<Field
|
||||||
|
name="name"
|
||||||
|
type="text"
|
||||||
|
component={({ input }) => <b>{`${input.value}: `}</b>}
|
||||||
|
/>
|
||||||
|
<Field name="value" type="text" component={({ input }) => input.value} />
|
||||||
|
</CollapsedKeyValue>
|
||||||
|
);
|
||||||
|
|
||||||
|
const chevronToggle = create ? null : (
|
||||||
|
<CardHeaderBox onClick={onToggleExpanded} actionable={expanded}>
|
||||||
|
<ChevronIcon />
|
||||||
|
</CardHeaderBox>
|
||||||
|
);
|
||||||
|
|
||||||
|
const _valueField = textarea ? (
|
||||||
|
<Field name="name" component={ValueTextareaField} props={{ submitting }} />
|
||||||
|
) : (
|
||||||
|
<Input disabled={submitting} />
|
||||||
|
);
|
||||||
|
|
||||||
|
const _cancel = (
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
key="cancel"
|
||||||
|
onClick={
|
||||||
|
create
|
||||||
|
? pristine ? onToggleExpanded : onClear
|
||||||
|
: pristine ? onRemove : onClear
|
||||||
|
}
|
||||||
|
disabled={submitting}
|
||||||
|
loading={submitting && removing}
|
||||||
|
secondary
|
||||||
|
marginless
|
||||||
|
>
|
||||||
|
{create ? (pristine ? 'Cancel' : 'Clear') : pristine ? 'Remove' : 'Clear'}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
|
||||||
|
const _submit = (
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
key="submit"
|
||||||
|
disabled={pristine || submitting}
|
||||||
|
loading={submitting && !removing}
|
||||||
|
secondary
|
||||||
|
marginless
|
||||||
|
>
|
||||||
|
{create ? 'Create' : 'Update'}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<Divider
|
||||||
|
transparent
|
||||||
|
marginBottom={!first && expanded ? remcalc(13) : 0}
|
||||||
|
/>
|
||||||
|
<Card
|
||||||
|
collapsed={!expanded}
|
||||||
|
actionable={!expanded}
|
||||||
|
bottomless={!last && !expanded}
|
||||||
|
>
|
||||||
|
<CardHeader
|
||||||
|
secondary={false}
|
||||||
|
transparent={false}
|
||||||
|
onClick={onToggleExpanded}
|
||||||
|
actionable
|
||||||
|
>
|
||||||
|
<CardHeaderMeta>{_meta}</CardHeaderMeta>
|
||||||
|
{chevronToggle}
|
||||||
|
</CardHeader>
|
||||||
|
<CardOutlet>
|
||||||
|
<Row>
|
||||||
|
<Col xs={12}>{_error}</Col>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<Col xs={12}>
|
||||||
|
<FormGroup name="name" reduxForm>
|
||||||
|
<Label>{titleCase(label)} key</Label>
|
||||||
|
<Input type="text" disabled={submitting} />
|
||||||
|
<FormMeta />
|
||||||
|
</FormGroup>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<Col xs={12}>
|
||||||
|
<FormGroup name="value" reduxForm>
|
||||||
|
<Label>{titleCase(label)} value</Label>
|
||||||
|
{_valueField}
|
||||||
|
</FormGroup>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<Col xs={12}>
|
||||||
|
{_cancel}
|
||||||
|
{_submit}
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</CardOutlet>
|
||||||
|
</Card>
|
||||||
|
<Divider transparent marginBottom={last || expanded ? remcalc(13) : 0} />
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ({ id, ...rest }) => (
|
||||||
|
<Value name={`${id}-removing`}>
|
||||||
|
{({ value: removing }) => (
|
||||||
|
<KeyValue {...rest} removing={removing} id={id} />
|
||||||
|
)}
|
||||||
|
</Value>
|
||||||
|
);
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import remcalc from 'remcalc';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Header,
|
Header,
|
||||||
@ -7,28 +9,40 @@ import {
|
|||||||
DataCenterIconLight,
|
DataCenterIconLight,
|
||||||
UserIconLight,
|
UserIconLight,
|
||||||
HeaderNav,
|
HeaderNav,
|
||||||
HeaderNavAnchor,
|
HeaderAnchor,
|
||||||
HeaderItem
|
HeaderItem
|
||||||
} from 'joyent-ui-toolkit';
|
} from 'joyent-ui-toolkit';
|
||||||
|
|
||||||
|
const Logo = styled(TritonBetaIcon)`
|
||||||
|
padding-top: ${remcalc(11)};
|
||||||
|
`;
|
||||||
|
|
||||||
export default () => (
|
export default () => (
|
||||||
<Header>
|
<Header>
|
||||||
<HeaderBrand beta>
|
<HeaderBrand beta>
|
||||||
<HeaderNavAnchor to="/">
|
<HeaderAnchor to="/">
|
||||||
<TritonBetaIcon alt="Triton" />
|
<Logo alt="Triton" />
|
||||||
</HeaderNavAnchor>
|
</HeaderAnchor>
|
||||||
</HeaderBrand>
|
</HeaderBrand>
|
||||||
<HeaderNav>
|
<HeaderNav>
|
||||||
<li>
|
<li>
|
||||||
<HeaderNavAnchor to="/">Compute</HeaderNavAnchor>
|
<HeaderAnchor to="/">Compute</HeaderAnchor>
|
||||||
</li>
|
</li>
|
||||||
</HeaderNav>
|
</HeaderNav>
|
||||||
<HeaderItem>Return to existing portal</HeaderItem>
|
|
||||||
<HeaderItem>
|
<HeaderItem>
|
||||||
<DataCenterIconLight />eu-east-1
|
<HeaderAnchor href="https://my.joyent.com">
|
||||||
|
Return to existing portal
|
||||||
|
</HeaderAnchor>
|
||||||
</HeaderItem>
|
</HeaderItem>
|
||||||
<HeaderItem>
|
<HeaderItem>
|
||||||
<UserIconLight />Nicola
|
<HeaderAnchor>
|
||||||
|
<DataCenterIconLight />eu-east-1
|
||||||
|
</HeaderAnchor>
|
||||||
|
</HeaderItem>
|
||||||
|
<HeaderItem>
|
||||||
|
<HeaderAnchor>
|
||||||
|
<UserIconLight />Nicola
|
||||||
|
</HeaderAnchor>
|
||||||
</HeaderItem>
|
</HeaderItem>
|
||||||
</Header>
|
</Header>
|
||||||
);
|
);
|
||||||
|
@ -0,0 +1,204 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`renders <Metadata /> without throwing 1`] = `
|
||||||
|
.c3 {
|
||||||
|
-webkit-animation: iCqDak 1.5s ease-out 0s infinite;
|
||||||
|
animation: iCqDak 1.5s ease-out 0s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c4 {
|
||||||
|
-webkit-animation: iCqDak 1.5s ease-out 0s infinite;
|
||||||
|
animation: iCqDak 1.5s ease-out 0s infinite;
|
||||||
|
-webkit-animation-delay: 0.5s;
|
||||||
|
animation-delay: 0.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c5 {
|
||||||
|
-webkit-animation: iCqDak 1.5s ease-out 0s infinite;
|
||||||
|
animation: iCqDak 1.5s ease-out 0s infinite;
|
||||||
|
-webkit-animation-delay: 1s;
|
||||||
|
animation-delay: 1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c2 {
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -webkit-flex;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
-webkit-flex-direction: column;
|
||||||
|
-ms-flex-direction: column;
|
||||||
|
flex-direction: column;
|
||||||
|
-webkit-flex-wrap: nowrap;
|
||||||
|
-ms-flex-wrap: nowrap;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
-webkit-box-pack: center;
|
||||||
|
-webkit-justify-content: center;
|
||||||
|
-ms-flex-pack: center;
|
||||||
|
justify-content: center;
|
||||||
|
-webkit-align-content: center;
|
||||||
|
-ms-flex-line-pack: center;
|
||||||
|
align-content: center;
|
||||||
|
-webkit-align-items: center;
|
||||||
|
-webkit-box-align: center;
|
||||||
|
-ms-flex-align: center;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 1.25rem;
|
||||||
|
-webkit-flex: 1 0 auto;
|
||||||
|
-ms-flex: 1 0 auto;
|
||||||
|
flex: 1 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c6 {
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.5rem;
|
||||||
|
font-size: 0.9375rem;
|
||||||
|
margin: 0;
|
||||||
|
-webkit-flex: 0 0 auto;
|
||||||
|
-ms-flex: 0 0 auto;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
-webkit-align-self: stretch;
|
||||||
|
-ms-flex-item-align: stretch;
|
||||||
|
align-self: stretch;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 0;
|
||||||
|
margin-left: 0.375rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c6 + p,
|
||||||
|
.c6 + small,
|
||||||
|
.c6 + h1,
|
||||||
|
.c6 + h2,
|
||||||
|
.c6 + label,
|
||||||
|
.c6 + h3,
|
||||||
|
.c6 + h4,
|
||||||
|
.c6 + h5,
|
||||||
|
.c6 + div,
|
||||||
|
.c6 + span {
|
||||||
|
padding-bottom: 2.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c0 {
|
||||||
|
margin-right: auto;
|
||||||
|
margin-left: auto;
|
||||||
|
padding-bottom: 1.125rem;
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -webkit-flex;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
-webkit-flex-direction: column;
|
||||||
|
-ms-flex-direction: column;
|
||||||
|
flex-direction: column;
|
||||||
|
-webkit-flex-wrap: nowrap;
|
||||||
|
-ms-flex-wrap: nowrap;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
-webkit-box-pack: center;
|
||||||
|
-webkit-justify-content: center;
|
||||||
|
-ms-flex-pack: center;
|
||||||
|
justify-content: center;
|
||||||
|
-webkit-align-content: center;
|
||||||
|
-ms-flex-line-pack: center;
|
||||||
|
align-content: center;
|
||||||
|
-webkit-align-items: center;
|
||||||
|
-webkit-box-align: center;
|
||||||
|
-ms-flex-align: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c1 {
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.875rem;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
margin-top: 1.1875rem;
|
||||||
|
margin-bottom: 1.8125rem;
|
||||||
|
-webkit-flex: 0 0 auto;
|
||||||
|
-ms-flex: 0 0 auto;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
-webkit-align-self: stretch;
|
||||||
|
-ms-flex-item-align: stretch;
|
||||||
|
align-self: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.c1 + p,
|
||||||
|
.c1 + small,
|
||||||
|
.c1 + h1,
|
||||||
|
.c1 + h2,
|
||||||
|
.c1 + label,
|
||||||
|
.c1 + h3,
|
||||||
|
.c1 + h4,
|
||||||
|
.c1 + h5,
|
||||||
|
.c1 + div,
|
||||||
|
.c1 + span {
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (min-width:48em) {
|
||||||
|
.c0 {
|
||||||
|
width: 46rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (min-width:64em) {
|
||||||
|
.c0 {
|
||||||
|
width: 61rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (min-width:75em) {
|
||||||
|
.c0 {
|
||||||
|
width: 76rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width:47.9375rem) {
|
||||||
|
.c0 {
|
||||||
|
padding-left: 0.375rem;
|
||||||
|
padding-right: 0.375rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<div
|
||||||
|
className="c0"
|
||||||
|
>
|
||||||
|
<h2
|
||||||
|
className="c1"
|
||||||
|
>
|
||||||
|
Metadata
|
||||||
|
</h2>
|
||||||
|
<div
|
||||||
|
className="c2"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
height="10"
|
||||||
|
width="28"
|
||||||
|
>
|
||||||
|
<rect
|
||||||
|
className="c3"
|
||||||
|
height="6"
|
||||||
|
width="6"
|
||||||
|
x="2"
|
||||||
|
y="2"
|
||||||
|
/>
|
||||||
|
<rect
|
||||||
|
className="c4"
|
||||||
|
height="6"
|
||||||
|
width="6"
|
||||||
|
x="11"
|
||||||
|
y="2"
|
||||||
|
/>
|
||||||
|
<rect
|
||||||
|
className="c5"
|
||||||
|
height="6"
|
||||||
|
width="6"
|
||||||
|
x="20"
|
||||||
|
y="2"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<p
|
||||||
|
className="c6"
|
||||||
|
>
|
||||||
|
Loading...
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
@ -0,0 +1,18 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import renderer from 'react-test-renderer';
|
||||||
|
import { reduxForm } from 'redux-form';
|
||||||
|
import Store from '@mocks/store';
|
||||||
|
import 'jest-styled-components';
|
||||||
|
|
||||||
|
import Metadata from '../metadata';
|
||||||
|
|
||||||
|
it('renders <Metadata /> without throwing', () => {
|
||||||
|
const tree = renderer
|
||||||
|
.create(
|
||||||
|
<Store>
|
||||||
|
<Metadata />
|
||||||
|
</Store>
|
||||||
|
)
|
||||||
|
.toJSON();
|
||||||
|
expect(tree).toMatchSnapshot();
|
||||||
|
});
|
@ -23,15 +23,15 @@ const DNS = ({ instance, loading, error }) => {
|
|||||||
const _summary = !_loading && instance && <ReactJson src={dns_names} />;
|
const _summary = !_loading && instance && <ReactJson src={dns_names} />;
|
||||||
|
|
||||||
const _error = error &&
|
const _error = error &&
|
||||||
!_loading &&
|
!_loading &&
|
||||||
!instance && (
|
!instance && (
|
||||||
<Message error>
|
<Message error>
|
||||||
<MessageTitle>Ooops!</MessageTitle>
|
<MessageTitle>Ooops!</MessageTitle>
|
||||||
<MessageDescription>
|
<MessageDescription>
|
||||||
An error occurred while loading your instance DNS
|
An error occurred while loading your instance DNS
|
||||||
</MessageDescription>
|
</MessageDescription>
|
||||||
</Message>
|
</Message>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewContainer center={Boolean(_loading)} main>
|
<ViewContainer center={Boolean(_loading)} main>
|
||||||
|
@ -34,31 +34,29 @@ const Firewall = ({
|
|||||||
const _title = <Title>Firewall</Title>;
|
const _title = <Title>Firewall</Title>;
|
||||||
const _loading = !(loading && !values.length) ? null : <StatusLoader />;
|
const _loading = !(loading && !values.length) ? null : <StatusLoader />;
|
||||||
|
|
||||||
const _firewall = (_loading && !values.length) ? null : (
|
const _firewall =
|
||||||
<Table>
|
_loading && !values.length ? null : (
|
||||||
<TableThead>
|
<Table>
|
||||||
<TableTr>
|
<TableThead>
|
||||||
<TableTh left bottom>
|
<TableTr>
|
||||||
<P>Rule</P>
|
<TableTh left bottom>
|
||||||
</TableTh>
|
<P>Rule</P>
|
||||||
<TableTh xs="63" center bottom>
|
</TableTh>
|
||||||
<P>Global</P>
|
<TableTh xs="63" center bottom>
|
||||||
</TableTh>
|
<P>Global</P>
|
||||||
<TableTh xs="75" center bottom>
|
</TableTh>
|
||||||
<P>Enabled</P>
|
<TableTh xs="75" center bottom>
|
||||||
</TableTh>
|
<P>Enabled</P>
|
||||||
</TableTr>
|
</TableTh>
|
||||||
</TableThead>
|
</TableTr>
|
||||||
<TableTbody>{
|
</TableThead>
|
||||||
values.map((network) => (
|
<TableTbody>
|
||||||
<InstanceFirewallRule
|
{values.map(network => (
|
||||||
key={network.id}
|
<InstanceFirewallRule key={network.id} {...network} />
|
||||||
{...network}
|
))}
|
||||||
/>
|
</TableTbody>
|
||||||
))}
|
</Table>
|
||||||
</TableTbody>
|
);
|
||||||
</Table>
|
|
||||||
);
|
|
||||||
|
|
||||||
const _error =
|
const _error =
|
||||||
error && !values.length && !_loading ? (
|
error && !values.length && !_loading ? (
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import paramCase from 'param-case';
|
import paramCase from 'param-case';
|
||||||
import forceArray from 'force-array';
|
import Value, { set } from 'react-redux-values';
|
||||||
import { compose, graphql } from 'react-apollo';
|
import { compose, graphql } from 'react-apollo';
|
||||||
import { reduxForm } from 'redux-form';
|
import { connect } from 'react-redux';
|
||||||
|
import { SubmissionError, reset, startSubmit, stopSubmit } from 'redux-form';
|
||||||
|
import ReduxForm from 'declarative-redux-form';
|
||||||
import find from 'lodash.find';
|
import find from 'lodash.find';
|
||||||
|
import sortBy from 'lodash.sortby';
|
||||||
import get from 'lodash.get';
|
import get from 'lodash.get';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -13,42 +15,93 @@ import {
|
|||||||
StatusLoader,
|
StatusLoader,
|
||||||
Message,
|
Message,
|
||||||
MessageDescription,
|
MessageDescription,
|
||||||
MessageTitle
|
MessageTitle,
|
||||||
|
Button
|
||||||
} from 'joyent-ui-toolkit';
|
} from 'joyent-ui-toolkit';
|
||||||
|
|
||||||
import GetMetadata from '@graphql/list-metadata.gql';
|
import GetMetadata from '@graphql/list-metadata.gql';
|
||||||
|
import UpdateMetadata from '@graphql/update-metadata.gql';
|
||||||
|
import DeleteMetadata from '@graphql/delete-metadata.gql';
|
||||||
import { KeyValue } from '@components/instances';
|
import { KeyValue } from '@components/instances';
|
||||||
|
|
||||||
const MetadataForms = (metadata = []) =>
|
const METADATA_FORM_KEY = (name, field) => `instance-metadata-${name}-${field}`;
|
||||||
metadata.map(({ key, formName, formValue, value, name }) => {
|
const CREATE_METADATA_FORM_KEY = name => `instance-create-metadata-${name}`;
|
||||||
const MetadataForm = reduxForm({
|
|
||||||
form: `instance-metadata-${key}`,
|
|
||||||
initialValues: {
|
|
||||||
[formName]: name,
|
|
||||||
[formValue]: value
|
|
||||||
}
|
|
||||||
})(KeyValue);
|
|
||||||
|
|
||||||
return (
|
const Metadata = ({
|
||||||
<MetadataForm
|
instance,
|
||||||
key={key}
|
values = [],
|
||||||
formName={formName}
|
loading,
|
||||||
formValue={formValue}
|
error,
|
||||||
name={key}
|
handleRemove,
|
||||||
onSubmit={val => console.log(key, val)}
|
handleClear,
|
||||||
onRemove={key => console.log('remove', key)}
|
handleUpdate,
|
||||||
textarea
|
handleCreate
|
||||||
/>
|
}) => {
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const Metadata = ({ metadata = [], loading, error }) => {
|
|
||||||
const values = forceArray(metadata);
|
|
||||||
const _title = <Title>Metadata</Title>;
|
const _title = <Title>Metadata</Title>;
|
||||||
const _loading = !(loading && !values.length) ? null : <StatusLoader />;
|
const _loading = !(loading && !values.length) ? null : <StatusLoader />;
|
||||||
|
|
||||||
const _metadata = !_loading && MetadataForms(values);
|
// metadata items forms
|
||||||
|
const _metadata =
|
||||||
|
!_loading &&
|
||||||
|
values.map(({ form, initialValues }, i) => (
|
||||||
|
<Value name={`${form}-expanded`} key={form}>
|
||||||
|
{({ value: expanded, onValueChange }) => (
|
||||||
|
<ReduxForm
|
||||||
|
form={form}
|
||||||
|
initialValues={initialValues}
|
||||||
|
onSubmit={newValues => handleUpdate(newValues, form)}
|
||||||
|
destroyOnUnmount
|
||||||
|
id={form}
|
||||||
|
onClear={() => handleClear(form)}
|
||||||
|
onToggleExpanded={() => onValueChange(!expanded)}
|
||||||
|
onRemove={() => handleRemove(form)}
|
||||||
|
label="metadata"
|
||||||
|
last={values.length - 1 === i}
|
||||||
|
first={i === 0}
|
||||||
|
expanded={expanded}
|
||||||
|
textarea
|
||||||
|
>
|
||||||
|
{KeyValue}
|
||||||
|
</ReduxForm>
|
||||||
|
)}
|
||||||
|
</Value>
|
||||||
|
));
|
||||||
|
|
||||||
|
// create metadata form
|
||||||
|
const _addKey = instance && CREATE_METADATA_FORM_KEY(instance.name);
|
||||||
|
const _add = _metadata &&
|
||||||
|
_addKey && (
|
||||||
|
<Value name={`${_addKey}-expanded`}>
|
||||||
|
{({ value: expanded, onValueChange }) =>
|
||||||
|
!expanded ? (
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
onClick={() => onValueChange(!expanded)}
|
||||||
|
secondary
|
||||||
|
>
|
||||||
|
Add metadata
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<ReduxForm
|
||||||
|
form={_addKey}
|
||||||
|
onSubmit={handleCreate}
|
||||||
|
id={_addKey}
|
||||||
|
onClear={() => handleClear(_addKey)}
|
||||||
|
onToggleExpanded={() => onValueChange(!expanded)}
|
||||||
|
onRemove={() => handleRemove(_addKey)}
|
||||||
|
expanded={expanded}
|
||||||
|
label="metadata"
|
||||||
|
create
|
||||||
|
textarea
|
||||||
|
>
|
||||||
|
{KeyValue}
|
||||||
|
</ReduxForm>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</Value>
|
||||||
|
);
|
||||||
|
|
||||||
|
// fetching error
|
||||||
const _error =
|
const _error =
|
||||||
error && !values.length && !_loading ? (
|
error && !values.length && !_loading ? (
|
||||||
<Message error>
|
<Message error>
|
||||||
@ -65,15 +118,14 @@ const Metadata = ({ metadata = [], loading, error }) => {
|
|||||||
{_loading}
|
{_loading}
|
||||||
{_error}
|
{_error}
|
||||||
{_metadata}
|
{_metadata}
|
||||||
|
{_add}
|
||||||
</ViewContainer>
|
</ViewContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Metadata.propTypes = {
|
|
||||||
loading: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
|
graphql(UpdateMetadata, { name: 'updateMetadata' }),
|
||||||
|
graphql(DeleteMetadata, { name: 'deleteMetadata' }),
|
||||||
graphql(GetMetadata, {
|
graphql(GetMetadata, {
|
||||||
options: ({ match }) => ({
|
options: ({ match }) => ({
|
||||||
pollInterval: 1000,
|
pollInterval: 1000,
|
||||||
@ -81,29 +133,125 @@ export default compose(
|
|||||||
name: get(match, 'params.instance')
|
name: get(match, 'params.instance')
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
props: ({ data: { loading, error, variables, ...rest } }) => {
|
props: ({ data: { loading, error, variables, refetch, ...rest } }) => {
|
||||||
const values = get(
|
const { name } = variables;
|
||||||
find(get(rest, 'machines', []), ['name', variables.name]),
|
|
||||||
'metadata',
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
const metadata = values.reduce((all, { name, value }) => {
|
const instance = find(get(rest, 'machines', []), ['name', name]);
|
||||||
const key = paramCase(name);
|
const metadata = get(instance, 'metadata', []);
|
||||||
|
|
||||||
|
const values = sortBy(metadata, 'name').map(({ name, value }) => {
|
||||||
|
const field = paramCase(name);
|
||||||
|
const form = METADATA_FORM_KEY(name, field);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...all,
|
form,
|
||||||
[key]: {
|
initialValues: {
|
||||||
key,
|
name,
|
||||||
formName: `${key}-name`,
|
value
|
||||||
formValue: `${key}-value`,
|
|
||||||
value,
|
|
||||||
name
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, {});
|
});
|
||||||
|
|
||||||
return { metadata: Object.values(metadata), loading, error };
|
return {
|
||||||
|
values,
|
||||||
|
instance,
|
||||||
|
loading,
|
||||||
|
error,
|
||||||
|
refetch
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
}),
|
||||||
|
connect(null, (dispatch, ownProps) => {
|
||||||
|
const {
|
||||||
|
instance,
|
||||||
|
values,
|
||||||
|
refetch,
|
||||||
|
updateMetadata,
|
||||||
|
deleteMetadata
|
||||||
|
} = ownProps;
|
||||||
|
|
||||||
|
return {
|
||||||
|
// reset sets values to initialValues
|
||||||
|
handleClear: form => dispatch(reset(form)),
|
||||||
|
handleRemove: form =>
|
||||||
|
Promise.resolve(
|
||||||
|
// set removing=true (so that we can have a specific removing spinner)
|
||||||
|
// because remove button is not a submit button, we have to manually flip that flag
|
||||||
|
dispatch([
|
||||||
|
set({ name: `${form}-removing`, value: true }),
|
||||||
|
startSubmit(form)
|
||||||
|
])
|
||||||
|
)
|
||||||
|
.then(() =>
|
||||||
|
// call mutation. get key from values' initialValues
|
||||||
|
deleteMetadata({
|
||||||
|
variables: {
|
||||||
|
id: instance.id,
|
||||||
|
name: get(find(values, ['form', form]), 'initialValues.name')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
// fetch metadata again
|
||||||
|
.then(() => refetch())
|
||||||
|
// we only flip removing and submitting when there is an error.
|
||||||
|
// the reason for that is that metadata is updated asyncronously and
|
||||||
|
// it takes longer to have an efect than the mutation
|
||||||
|
.catch(error =>
|
||||||
|
dispatch([
|
||||||
|
set({ name: `${form}-removing`, value: false }),
|
||||||
|
stopSubmit(form, {
|
||||||
|
_error: error.graphQLErrors
|
||||||
|
.map(({ message }) => message)
|
||||||
|
.join('\n')
|
||||||
|
})
|
||||||
|
])
|
||||||
|
),
|
||||||
|
handleUpdate: ({ name, value }, form) =>
|
||||||
|
// call mutation. delete existing metadata, add new
|
||||||
|
Promise.all([
|
||||||
|
deleteMetadata({
|
||||||
|
variables: {
|
||||||
|
id: instance.id,
|
||||||
|
name: get(find(values, ['form', form]), 'initialValues.name')
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
updateMetadata({
|
||||||
|
variables: {
|
||||||
|
id: instance.id,
|
||||||
|
metadata: [{ name, value }]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
])
|
||||||
|
// fetch metadata again
|
||||||
|
.then(() => refetch())
|
||||||
|
// submit is flipped once the promise is resolved
|
||||||
|
.catch(error => {
|
||||||
|
throw new SubmissionError({
|
||||||
|
_error: error.graphQLErrors
|
||||||
|
.map(({ message }) => message)
|
||||||
|
.join('\n')
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
handleCreate: ({ name, value }) =>
|
||||||
|
// call mutation
|
||||||
|
updateMetadata({
|
||||||
|
variables: {
|
||||||
|
id: instance.id,
|
||||||
|
metadata: [{ name, value }]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// fetch metadata again
|
||||||
|
.then(() => refetch())
|
||||||
|
// reset create new metadata form
|
||||||
|
.then(() => dispatch(reset(CREATE_METADATA_FORM_KEY(instance.name))))
|
||||||
|
// submit is flipped once the promise is resolved
|
||||||
|
.catch(error => {
|
||||||
|
throw new SubmissionError({
|
||||||
|
_error: error.graphQLErrors
|
||||||
|
.map(({ message }) => message)
|
||||||
|
.join('\n')
|
||||||
|
});
|
||||||
|
})
|
||||||
|
};
|
||||||
})
|
})
|
||||||
)(Metadata);
|
)(Metadata);
|
||||||
|
@ -28,34 +28,32 @@ const Networks = ({ networks = [], loading, error }) => {
|
|||||||
const _title = <Title>Networks</Title>;
|
const _title = <Title>Networks</Title>;
|
||||||
const _loading = !(loading && !values.length) ? null : <StatusLoader />;
|
const _loading = !(loading && !values.length) ? null : <StatusLoader />;
|
||||||
|
|
||||||
const _networks = (_loading && !values.length) ? null : (
|
const _networks =
|
||||||
<Table>
|
_loading && !values.length ? null : (
|
||||||
<TableThead>
|
<Table>
|
||||||
<TableTr>
|
<TableThead>
|
||||||
<TableTh left bottom>
|
<TableTr>
|
||||||
<P>Name</P>
|
<TableTh left bottom>
|
||||||
</TableTh>
|
<P>Name</P>
|
||||||
<TableTh xs="90" left bottom>
|
</TableTh>
|
||||||
<P>Gateway</P>
|
<TableTh xs="90" left bottom>
|
||||||
</TableTh>
|
<P>Gateway</P>
|
||||||
<TableTh xs="90" left bottom>
|
</TableTh>
|
||||||
<P>Subnet</P>
|
<TableTh xs="90" left bottom>
|
||||||
</TableTh>
|
<P>Subnet</P>
|
||||||
<TableTh xs="90" left bottom>
|
</TableTh>
|
||||||
<P>Resolvers</P>
|
<TableTh xs="90" left bottom>
|
||||||
</TableTh>
|
<P>Resolvers</P>
|
||||||
</TableTr>
|
</TableTh>
|
||||||
</TableThead>
|
</TableTr>
|
||||||
<TableTbody>{
|
</TableThead>
|
||||||
values.map((network) => (
|
<TableTbody>
|
||||||
<InstanceNetwork
|
{values.map(network => (
|
||||||
key={network.id}
|
<InstanceNetwork key={network.id} {...network} />
|
||||||
{...network}
|
))}
|
||||||
/>
|
</TableTbody>
|
||||||
))}
|
</Table>
|
||||||
</TableTbody>
|
);
|
||||||
</Table>
|
|
||||||
);
|
|
||||||
|
|
||||||
const _error =
|
const _error =
|
||||||
error && !values.length && !_loading ? (
|
error && !values.length && !_loading ? (
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import paramCase from 'param-case';
|
import paramCase from 'param-case';
|
||||||
import forceArray from 'force-array';
|
|
||||||
import { compose, graphql } from 'react-apollo';
|
import { compose, graphql } from 'react-apollo';
|
||||||
import { reduxForm } from 'redux-form';
|
import Value, { set } from 'react-redux-values';
|
||||||
|
import { SubmissionError, reset, startSubmit, stopSubmit } from 'redux-form';
|
||||||
|
import ReduxForm from 'declarative-redux-form';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
import find from 'lodash.find';
|
import find from 'lodash.find';
|
||||||
import get from 'lodash.get';
|
import get from 'lodash.get';
|
||||||
|
|
||||||
@ -13,42 +14,91 @@ import {
|
|||||||
StatusLoader,
|
StatusLoader,
|
||||||
Message,
|
Message,
|
||||||
MessageDescription,
|
MessageDescription,
|
||||||
MessageTitle
|
MessageTitle,
|
||||||
|
Button
|
||||||
} from 'joyent-ui-toolkit';
|
} from 'joyent-ui-toolkit';
|
||||||
|
|
||||||
import { KeyValue } from '@components/instances';
|
|
||||||
import GetTags from '@graphql/list-tags.gql';
|
import GetTags from '@graphql/list-tags.gql';
|
||||||
import PutTags from '@graphql/add-tags.gql';
|
import UpdateTags from '@graphql/update-tags.gql';
|
||||||
|
import DeleteTag from '@graphql/delete-tag.gql';
|
||||||
|
import { KeyValue } from '@components/instances';
|
||||||
|
|
||||||
const TagForms = (tags = []) =>
|
const TAG_FORM_KEY = (name, field) => `instance-tag-${name}-${field}`;
|
||||||
tags.map(({ key, formName, formValue, value, name }) => {
|
const CREATE_TAG_FORM_KEY = name => `instance-create-tag-${name}`;
|
||||||
const TagForm = reduxForm({
|
|
||||||
form: `instance-tags-${key}`,
|
|
||||||
initialValues: {
|
|
||||||
[formName]: name,
|
|
||||||
[formValue]: value
|
|
||||||
}
|
|
||||||
})(KeyValue);
|
|
||||||
|
|
||||||
return (
|
const Tags = ({
|
||||||
<TagForm
|
instance,
|
||||||
key={key}
|
values = [],
|
||||||
formName={formName}
|
loading,
|
||||||
formValue={formValue}
|
error,
|
||||||
name={key}
|
handleRemove,
|
||||||
onSubmit={val => console.log(key, val)}
|
handleClear,
|
||||||
onRemove={key => console.log('remove', key)}
|
handleUpdate,
|
||||||
/>
|
handleCreate
|
||||||
);
|
}) => {
|
||||||
});
|
|
||||||
|
|
||||||
const Tags = ({ tags = [], loading, error }) => {
|
|
||||||
const values = forceArray(tags);
|
|
||||||
const _title = <Title>Tags</Title>;
|
const _title = <Title>Tags</Title>;
|
||||||
const _loading = loading && !values.length ? <StatusLoader /> : null;
|
const _loading = !(loading && !values.length) ? null : <StatusLoader />;
|
||||||
|
|
||||||
const _tags = !_loading && TagForms(tags);
|
// tags items forms
|
||||||
|
const _tags =
|
||||||
|
!_loading &&
|
||||||
|
values.map(({ form, initialValues }, i) => (
|
||||||
|
<Value name={`${form}-expanded`} key={form}>
|
||||||
|
{({ value: expanded, onValueChange }) => (
|
||||||
|
<ReduxForm
|
||||||
|
form={form}
|
||||||
|
initialValues={initialValues}
|
||||||
|
onSubmit={newValues => handleUpdate(newValues, form)}
|
||||||
|
destroyOnUnmount
|
||||||
|
id={form}
|
||||||
|
onClear={() => handleClear(form)}
|
||||||
|
onToggleExpanded={() => onValueChange(!expanded)}
|
||||||
|
onRemove={() => handleRemove(form)}
|
||||||
|
label="tag"
|
||||||
|
last={values.length - 1 === i}
|
||||||
|
first={i === 0}
|
||||||
|
expanded={expanded}
|
||||||
|
>
|
||||||
|
{KeyValue}
|
||||||
|
</ReduxForm>
|
||||||
|
)}
|
||||||
|
</Value>
|
||||||
|
));
|
||||||
|
|
||||||
|
// create tags form
|
||||||
|
const _addKey = instance && CREATE_TAG_FORM_KEY(instance.name);
|
||||||
|
const _add = _tags &&
|
||||||
|
_addKey && (
|
||||||
|
<Value name={`${_addKey}-expanded`}>
|
||||||
|
{({ value: expanded, onValueChange }) =>
|
||||||
|
!expanded ? (
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
onClick={() => onValueChange(!expanded)}
|
||||||
|
secondary
|
||||||
|
>
|
||||||
|
Add tag
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<ReduxForm
|
||||||
|
form={_addKey}
|
||||||
|
onSubmit={handleCreate}
|
||||||
|
id={_addKey}
|
||||||
|
onClear={() => handleClear(_addKey)}
|
||||||
|
onToggleExpanded={() => onValueChange(!expanded)}
|
||||||
|
onRemove={() => handleRemove(_addKey)}
|
||||||
|
expanded={expanded}
|
||||||
|
label="tag"
|
||||||
|
create
|
||||||
|
>
|
||||||
|
{KeyValue}
|
||||||
|
</ReduxForm>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</Value>
|
||||||
|
);
|
||||||
|
|
||||||
|
// fetching error
|
||||||
const _error =
|
const _error =
|
||||||
error && !values.length && !_loading ? (
|
error && !values.length && !_loading ? (
|
||||||
<Message error>
|
<Message error>
|
||||||
@ -65,15 +115,14 @@ const Tags = ({ tags = [], loading, error }) => {
|
|||||||
{_loading}
|
{_loading}
|
||||||
{_error}
|
{_error}
|
||||||
{_tags}
|
{_tags}
|
||||||
|
{_add}
|
||||||
</ViewContainer>
|
</ViewContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Tags.propTypes = {
|
|
||||||
loading: PropTypes.bool
|
|
||||||
};
|
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
|
graphql(UpdateTags, { name: 'updateTags' }),
|
||||||
|
graphql(DeleteTag, { name: 'deleteTag' }),
|
||||||
graphql(GetTags, {
|
graphql(GetTags, {
|
||||||
options: ({ match }) => ({
|
options: ({ match }) => ({
|
||||||
pollInterval: 1000,
|
pollInterval: 1000,
|
||||||
@ -81,37 +130,119 @@ export default compose(
|
|||||||
name: get(match, 'params.instance')
|
name: get(match, 'params.instance')
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
props: ({ data: { loading, error, variables, ...rest } }) => {
|
props: ({ data: { loading, error, variables, refetch, ...rest } }) => {
|
||||||
const values = get(
|
const { name } = variables;
|
||||||
find(get(rest, 'machines', []), ['name', variables.name]),
|
|
||||||
'tags',
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
const tags = values.reduce((all, { name, value }) => {
|
const instance = find(get(rest, 'machines', []), ['name', name]);
|
||||||
const key = paramCase(name);
|
const tags = get(instance, 'tags', []);
|
||||||
|
|
||||||
|
const values = tags.map(({ name, value }) => {
|
||||||
|
const field = paramCase(name);
|
||||||
|
const form = TAG_FORM_KEY(name, field);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...all,
|
form,
|
||||||
[key]: {
|
initialValues: {
|
||||||
key,
|
name,
|
||||||
formName: `${key}-name`,
|
value
|
||||||
formValue: `${key}-value`,
|
|
||||||
value,
|
|
||||||
name
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, {});
|
});
|
||||||
|
|
||||||
return { tags: Object.values(tags), loading, error };
|
return {
|
||||||
|
values,
|
||||||
|
instance,
|
||||||
|
loading,
|
||||||
|
error,
|
||||||
|
refetch
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
graphql(PutTags, {
|
connect(null, (dispatch, ownProps) => {
|
||||||
props: ({ mutate, ownProps }) => ({
|
const { instance, values, refetch, updateTags, deleteTag } = ownProps;
|
||||||
updateTag: (name = '', value = '') =>
|
|
||||||
mutate({
|
return {
|
||||||
variables: { name, value }
|
// reset sets values to initialValues
|
||||||
|
handleClear: form => dispatch(reset(form)),
|
||||||
|
handleRemove: form =>
|
||||||
|
Promise.resolve(
|
||||||
|
// set removing=true (so that we can have a specific removing spinner)
|
||||||
|
// because remove button is not a submit button, we have to manually flip that flag
|
||||||
|
dispatch([
|
||||||
|
set({ name: `${form}-removing`, value: true }),
|
||||||
|
startSubmit(form)
|
||||||
|
])
|
||||||
|
)
|
||||||
|
.then(() =>
|
||||||
|
// call mutation
|
||||||
|
deleteTag({
|
||||||
|
variables: {
|
||||||
|
id: instance.id,
|
||||||
|
name: get(find(values, ['form', form]), 'initialValues.name')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
// fetch tags again
|
||||||
|
.then(() => refetch())
|
||||||
|
// we only flip removing and submitting when there is an error.
|
||||||
|
// the reason for that is that tags is updated asyncronously and
|
||||||
|
// it takes longer to have an efect than the mutation
|
||||||
|
.catch(error =>
|
||||||
|
dispatch([
|
||||||
|
set({ name: `${form}-removing`, value: false }),
|
||||||
|
stopSubmit(form, {
|
||||||
|
_error: error.graphQLErrors
|
||||||
|
.map(({ message }) => message)
|
||||||
|
.join('\n')
|
||||||
|
})
|
||||||
|
])
|
||||||
|
),
|
||||||
|
handleUpdate: ({ name, value }, form) =>
|
||||||
|
// delete old tag and add a new one
|
||||||
|
Promise.all([
|
||||||
|
deleteTag({
|
||||||
|
variables: {
|
||||||
|
id: instance.id,
|
||||||
|
name: get(find(values, ['form', form]), 'initialValues.name')
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
updateTags({
|
||||||
|
variables: {
|
||||||
|
id: instance.id,
|
||||||
|
tags: [{ name, value }]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
])
|
||||||
|
// fetch tags again
|
||||||
|
.then(() => refetch())
|
||||||
|
// submit is flipped once the promise is resolved
|
||||||
|
.catch(error => {
|
||||||
|
throw new SubmissionError({
|
||||||
|
_error: error.graphQLErrors
|
||||||
|
.map(({ message }) => message)
|
||||||
|
.join('\n')
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
handleCreate: ({ name, value }) =>
|
||||||
|
// call mutation
|
||||||
|
updateTags({
|
||||||
|
variables: {
|
||||||
|
id: instance.id,
|
||||||
|
tags: [{ name, value }]
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
// fetch tags again
|
||||||
|
.then(() => refetch())
|
||||||
|
// reset create new tags form
|
||||||
|
.then(() => dispatch(reset(CREATE_TAG_FORM_KEY(instance.name))))
|
||||||
|
// submit is flipped once the promise is resolved
|
||||||
|
.catch(error => {
|
||||||
|
throw new SubmissionError({
|
||||||
|
_error: error.graphQLErrors
|
||||||
|
.map(({ message }) => message)
|
||||||
|
.join('\n')
|
||||||
|
});
|
||||||
|
})
|
||||||
|
};
|
||||||
})
|
})
|
||||||
)(Tags);
|
)(Tags);
|
||||||
|
5
packages/my-joy-beta/src/graphql/delete-metadata.gql
Normal file
5
packages/my-joy-beta/src/graphql/delete-metadata.gql
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
mutation deleteMachineMetadata($id: ID!, $name: String!) {
|
||||||
|
deleteMachineMetadata(id: $id, name: $name) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
5
packages/my-joy-beta/src/graphql/delete-tag.gql
Normal file
5
packages/my-joy-beta/src/graphql/delete-tag.gql
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
mutation deleteMachineTag($id: ID!, $name: String!) {
|
||||||
|
deleteMachineTag(id: $id, name: $name) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
5
packages/my-joy-beta/src/graphql/update-metadata.gql
Normal file
5
packages/my-joy-beta/src/graphql/update-metadata.gql
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
mutation updateMachineMetadata($id: ID!, $metadata: [KeyValueInput]!) {
|
||||||
|
updateMachineMetadata(id: $id, metadata: $metadata) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
5
packages/my-joy-beta/src/graphql/update-tags.gql
Normal file
5
packages/my-joy-beta/src/graphql/update-tags.gql
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
mutation addMachineTags($id: ID!, $tags: [KeyValueInput]!) {
|
||||||
|
addMachineTags(id: $id, tags: $tags) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { client, store } from '@state/store';
|
import { client, store } from '@root/state/store';
|
||||||
import { ApolloProvider } from 'react-apollo';
|
import { ApolloProvider } from 'react-apollo';
|
||||||
|
|
||||||
export default ({ children }) => (
|
export default ({ children }) => (
|
@ -2,6 +2,7 @@ import { reduxBatch } from '@manaflair/redux-batch';
|
|||||||
import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
|
import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
|
||||||
import { reducer as formReducer } from 'redux-form';
|
import { reducer as formReducer } from 'redux-form';
|
||||||
import { ApolloClient, createNetworkInterface } from 'react-apollo';
|
import { ApolloClient, createNetworkInterface } from 'react-apollo';
|
||||||
|
import { reducer as valuesReducer } from 'react-redux-values';
|
||||||
|
|
||||||
import { ui } from './reducers';
|
import { ui } from './reducers';
|
||||||
import state from './state';
|
import state from './state';
|
||||||
@ -47,6 +48,7 @@ export const client = new ApolloClient({
|
|||||||
|
|
||||||
export const store = createStore(
|
export const store = createStore(
|
||||||
combineReducers({
|
combineReducers({
|
||||||
|
values: valuesReducer,
|
||||||
apollo: client.reducer(),
|
apollo: client.reducer(),
|
||||||
form: formReducer,
|
form: formReducer,
|
||||||
ui
|
ui
|
||||||
|
@ -1 +0,0 @@
|
|||||||
module.exports = 'test-file-mock';
|
|
@ -1,66 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
// Do this as the first thing so that any code reading it knows the right env.
|
|
||||||
process.env.BABEL_ENV = 'test';
|
|
||||||
process.env.NODE_ENV = 'test';
|
|
||||||
process.env.PUBLIC_URL = '';
|
|
||||||
|
|
||||||
// Makes the script crash on unhandled rejections instead of silently
|
|
||||||
// ignoring them. In the future, promise rejections that are not handled will
|
|
||||||
// terminate the Node.js process with a non-zero exit code.
|
|
||||||
process.on('unhandledRejection', err => {
|
|
||||||
throw err;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Ensure environment variables are read.
|
|
||||||
require('react-scripts/config/env');
|
|
||||||
|
|
||||||
const jest = require('jest');
|
|
||||||
const argv = process.argv.slice(2);
|
|
||||||
|
|
||||||
// This is not necessary after eject because we embed config into package.json.
|
|
||||||
const createJestConfig = require('react-scripts/scripts/utils/createJestConfig');
|
|
||||||
const path = require('path');
|
|
||||||
const paths = require('react-scripts/config/paths');
|
|
||||||
|
|
||||||
const config = createJestConfig(
|
|
||||||
relativePath =>
|
|
||||||
path.resolve(
|
|
||||||
__dirname,
|
|
||||||
'../../../node_modules/react-scripts',
|
|
||||||
relativePath
|
|
||||||
),
|
|
||||||
path.resolve(__dirname, '../../../'),
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
// patch
|
|
||||||
config.testEnvironment = 'node';
|
|
||||||
config.transform = Object.assign(
|
|
||||||
{},
|
|
||||||
{
|
|
||||||
'\\.(gql|graphql)$': 'jest-transform-graphql'
|
|
||||||
},
|
|
||||||
config.transform
|
|
||||||
);
|
|
||||||
config.testMatch = [
|
|
||||||
'<rootDir>/packages/joyent-boilerplate/src/**/**/__tests__/**/*.js',
|
|
||||||
'<rootDir>/packages/joyent-boilerplate/src/**/**/**/?(*.)(spec|test).js'
|
|
||||||
];
|
|
||||||
config.moduleNameMapper = Object.assign({}, config.moduleNameMapper, {
|
|
||||||
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
|
|
||||||
'<rootDir>/packages/joyent-boilerplate/test/file-mock.js',
|
|
||||||
'^@root/(.*)$': '<rootDir>/packages/joyent-boilerplate/src/$1',
|
|
||||||
'^@mocks/(.*)$': '<rootDir>/packages/joyent-boilerplate/test/mocks$1',
|
|
||||||
'^@components/(.*)$':
|
|
||||||
'<rootDir>/packages/joyent-boilerplate/src/components/$1',
|
|
||||||
'^@containers/(.*)$':
|
|
||||||
'<rootDir>/packages/joyent-boilerplate/src/containers/$1',
|
|
||||||
'^@graphql/(.*)$': '<rootDir>/packages/joyent-boilerplate/src/graphql/$1',
|
|
||||||
'^@assets/(.*)$': '<rootDir>/packages/joyent-boilerplate/src/assets/$1',
|
|
||||||
'^@state/(.*)$': '<rootDir>/packages/joyent-boilerplate/src/state/$1'
|
|
||||||
});
|
|
||||||
|
|
||||||
argv.push('--config', JSON.stringify(config));
|
|
||||||
|
|
||||||
jest.run(argv);
|
|
@ -37,9 +37,10 @@
|
|||||||
"disable-scroll": "^0.3.0",
|
"disable-scroll": "^0.3.0",
|
||||||
"fontfaceobserver": "^2.0.13",
|
"fontfaceobserver": "^2.0.13",
|
||||||
"joy-react-broadcast": "^0.6.9",
|
"joy-react-broadcast": "^0.6.9",
|
||||||
"joyent-manifest-editor": "^1.4.0",
|
"joyent-manifest-editor": "^3.0.1",
|
||||||
|
"lodash.isboolean": "^3.0.3",
|
||||||
"lodash.isstring": "^4.0.1",
|
"lodash.isstring": "^4.0.1",
|
||||||
"moment": "^2.19.1",
|
"moment": "^2.19.2",
|
||||||
"normalized-styled-components": "^1.0.17",
|
"normalized-styled-components": "^1.0.17",
|
||||||
"outy": "^0.1.2",
|
"outy": "^0.1.2",
|
||||||
"pascal-case": "^2.0.1",
|
"pascal-case": "^2.0.1",
|
||||||
@ -48,54 +49,54 @@
|
|||||||
"react-input-range": "^1.2.1",
|
"react-input-range": "^1.2.1",
|
||||||
"react-popper": "^0.7.4",
|
"react-popper": "^0.7.4",
|
||||||
"react-responsive": "^3.0.0",
|
"react-responsive": "^3.0.0",
|
||||||
"react-styled-flexboxgrid": "^2.1.0",
|
"react-styled-flexboxgrid": "^2.1.1",
|
||||||
"redrun": "^5.9.18",
|
"redrun": "^5.10.0",
|
||||||
"remcalc": "^1.0.9",
|
"remcalc": "^1.0.9",
|
||||||
"rnd-id": "^1.1.1",
|
"rnd-id": "^2.0.0",
|
||||||
"styled-components": "^2.2.2",
|
"styled-components": "^2.2.3",
|
||||||
"styled-is": "^1.1.0",
|
"styled-is": "^1.1.0",
|
||||||
"unitcalc": "^1.1.1"
|
"unitcalc": "^1.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"babel-cli": "^6.26.0",
|
"babel-cli": "^6.26.0",
|
||||||
"babel-plugin-inline-react-svg": "^0.4.0",
|
"babel-plugin-inline-react-svg": "^0.4.0",
|
||||||
"babel-plugin-lodash": "^3.2.11",
|
"babel-plugin-lodash": "^3.3.2",
|
||||||
"babel-plugin-transform-es3-member-expression-literals": "^6.22.0",
|
"babel-plugin-transform-es3-member-expression-literals": "^6.22.0",
|
||||||
"babel-plugin-transform-es3-property-literals": "^6.22.0",
|
"babel-plugin-transform-es3-property-literals": "^6.22.0",
|
||||||
"babel-preset-es2015": "^6.24.1",
|
"babel-preset-es2015": "^6.24.1",
|
||||||
"babel-preset-joyent-portal": "^3.3.3",
|
"babel-preset-joyent-portal": "^3.3.3",
|
||||||
"codemirror": "^5.31.0",
|
"codemirror": "^5.31.0",
|
||||||
"eslint": "^4.9.0",
|
"eslint": "^4.11.0",
|
||||||
"eslint-config-joyent-portal": "^3.2.0",
|
"eslint-config-joyent-portal": "^3.2.0",
|
||||||
"http-server": "^0.10.0",
|
"http-server": "^0.10.0",
|
||||||
"jest": "^21.2.1",
|
"jest": "^21.2.1",
|
||||||
"jest-diff": "^21.2.1",
|
"jest-diff": "^21.2.1",
|
||||||
"jest-image-snapshot": "^1.0.1",
|
"jest-image-snapshot": "^2.2.0",
|
||||||
"jest-matcher-utils": "^21.2.1",
|
"jest-matcher-utils": "^21.2.1",
|
||||||
"jest-snapshot": "^21.2.1",
|
"jest-snapshot": "^21.2.1",
|
||||||
"jest-styled-components": "^4.9.0",
|
"jest-styled-components": "^4.9.0",
|
||||||
"joyent-react-scripts": "^2.6.0",
|
"joyent-react-scripts": "^3.1.0",
|
||||||
"lodash.isboolean": "^3.0.3",
|
"lodash.isboolean": "^3.0.3",
|
||||||
"navalia": "^1.2.0",
|
"navalia": "^1.2.0",
|
||||||
"react": "^16.0.0",
|
"react": "^16.1.1",
|
||||||
"react-docgen": "^3.0.0-beta8",
|
"react-docgen": "^3.0.0-beta8",
|
||||||
"react-docgen-displayname-handler": "^1.0.1",
|
"react-docgen-displayname-handler": "^1.0.1",
|
||||||
"react-dom": "^16.0.0",
|
"react-dom": "^16.1.1",
|
||||||
"react-redux": "^5.0.6",
|
"react-redux": "^5.0.6",
|
||||||
"react-router-dom": "^4.2.2",
|
"react-router-dom": "^4.2.2",
|
||||||
"react-styleguidist": "^6.0.31",
|
"react-styleguidist": "^6.0.33",
|
||||||
"react-test-renderer": "^16.0.0",
|
"react-test-renderer": "^16.1.1",
|
||||||
"redux": "^3.7.2",
|
"redux": "^3.7.2",
|
||||||
"redux-form": "^7.1.1",
|
"redux-form": "^7.1.2",
|
||||||
"serve-static": "^1.13.1",
|
"serve-static": "^1.13.1",
|
||||||
"stylelint": "^8.2.0",
|
"stylelint": "^8.2.0",
|
||||||
"stylelint-config-joyent-portal": "^2.0.1",
|
"stylelint-config-joyent-portal": "^2.0.1",
|
||||||
"webpack": "^3.8.1"
|
"webpack": "^3.8.1"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^16.0.0",
|
"react": "^16.1.1",
|
||||||
"react-dom": "^16.0.0",
|
"react-dom": "^16.1.1",
|
||||||
"react-router-dom": "^4.2.2",
|
"react-router-dom": "^4.2.2",
|
||||||
"redux-form": "^7.1.1"
|
"redux-form": "^7.1.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { css } from 'styled-components';
|
import { css } from 'styled-components';
|
||||||
import { loadedFontFamily } from '../typography';
|
import { loadedFontFamily } from '../typography';
|
||||||
|
import remcalc from 'remcalc';
|
||||||
|
|
||||||
export default ({ theme }) => css`
|
export default ({ theme }) => css`
|
||||||
[hidden] {
|
[hidden] {
|
||||||
@ -38,5 +39,6 @@ export default ({ theme }) => css`
|
|||||||
|
|
||||||
.CodeMirror {
|
.CodeMirror {
|
||||||
border: solid 1px ${theme.grey};
|
border: solid 1px ${theme.grey};
|
||||||
|
margin: ${remcalc(8)} 0 ${remcalc(8)} 0;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -15,8 +15,8 @@ on the props passed to it. E.g.:
|
|||||||
|
|
||||||
Is going to translate into a `<Button />` that has `12px` of margin.
|
Is going to translate into a `<Button />` that has `12px` of margin.
|
||||||
|
|
||||||
What enables this is the [`Baseline`
|
What enables this is the
|
||||||
composer](https://github.com/yldio/joyent-portal/blob/a5774063ed8caf2569aff2905af2d7dca7a01a52/ui/src/shared/composers/index.js#L51).
|
[`Baseline` composer](https://github.com/yldio/joyent-portal/blob/a5774063ed8caf2569aff2905af2d7dca7a01a52/ui/src/shared/composers/index.js#L51).
|
||||||
|
|
||||||
The Baseline composer is essentially an
|
The Baseline composer is essentially an
|
||||||
[HOC](https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750):
|
[HOC](https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750):
|
||||||
|
@ -27,13 +27,11 @@ export const BaseCard = styled.div`
|
|||||||
min-height: ${remcalc(125)};
|
min-height: ${remcalc(125)};
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
margin-bottom: 0;
|
|
||||||
|
|
||||||
transition: all 300ms ease;
|
|
||||||
|
|
||||||
border-width: ${remcalc(1)};
|
border-width: ${remcalc(1)};
|
||||||
border-style: solid;
|
border-style: solid;
|
||||||
|
|
||||||
|
transition: all 300ms ease;
|
||||||
|
|
||||||
/* primary */
|
/* primary */
|
||||||
color: ${props => props.theme.text};
|
color: ${props => props.theme.text};
|
||||||
background-color: ${props => props.theme.white};
|
background-color: ${props => props.theme.white};
|
||||||
@ -95,18 +93,46 @@ export const BaseCard = styled.div`
|
|||||||
height: ${remcalc(46)};
|
height: ${remcalc(46)};
|
||||||
flex: 0 0 ${remcalc(46)};
|
flex: 0 0 ${remcalc(46)};
|
||||||
`};
|
`};
|
||||||
|
|
||||||
|
${is('bottomless')`
|
||||||
|
border-bottom-width: 0;
|
||||||
|
`};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @example ./demo.md
|
* @example ./demo.md
|
||||||
*/
|
*/
|
||||||
const Card = ({ children, ...rest }) => (
|
const Card = ({
|
||||||
<Broadcast channel="card" value={rest}>
|
children,
|
||||||
<BaseCard {...rest} name="card">
|
secondary,
|
||||||
{children}
|
tertiary,
|
||||||
</BaseCard>
|
collapsed,
|
||||||
</Broadcast>
|
disabled,
|
||||||
);
|
stacked,
|
||||||
|
active,
|
||||||
|
shadow,
|
||||||
|
actionable,
|
||||||
|
...rest
|
||||||
|
}) => {
|
||||||
|
const newValue = {
|
||||||
|
secondary,
|
||||||
|
tertiary,
|
||||||
|
collapsed,
|
||||||
|
disabled,
|
||||||
|
stacked,
|
||||||
|
active,
|
||||||
|
shadow,
|
||||||
|
actionable
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Broadcast channel="card" value={newValue}>
|
||||||
|
<BaseCard {...rest} {...newValue} name="card">
|
||||||
|
{children}
|
||||||
|
</BaseCard>
|
||||||
|
</Broadcast>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
Card.propTypes = {
|
Card.propTypes = {
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
|
@ -2,6 +2,7 @@ import React from 'react';
|
|||||||
import { Broadcast, Subscriber } from 'joy-react-broadcast';
|
import { Broadcast, Subscriber } from 'joy-react-broadcast';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import is, { isNot, isOr } from 'styled-is';
|
import is, { isNot, isOr } from 'styled-is';
|
||||||
|
import isBoolean from 'lodash.isboolean';
|
||||||
import remcalc from 'remcalc';
|
import remcalc from 'remcalc';
|
||||||
|
|
||||||
import Baseline from '../baseline';
|
import Baseline from '../baseline';
|
||||||
@ -9,6 +10,7 @@ import Card, { BaseCard } from './card';
|
|||||||
|
|
||||||
const BaseHeader = BaseCard.extend`
|
const BaseHeader = BaseCard.extend`
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
margin: ${remcalc(-1)} ${remcalc(-1)} 0 ${remcalc(-1)};
|
margin: ${remcalc(-1)} ${remcalc(-1)} 0 ${remcalc(-1)};
|
||||||
|
|
||||||
@ -47,7 +49,7 @@ const BaseBox = BaseCard.extend`
|
|||||||
min-width: ${remcalc(49)};
|
min-width: ${remcalc(49)};
|
||||||
height: ${remcalc(46)};
|
height: ${remcalc(46)};
|
||||||
|
|
||||||
display: flex;
|
display: inline-flex;
|
||||||
flex: 0 0 ${remcalc(49)};
|
flex: 0 0 ${remcalc(49)};
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@ -82,17 +84,20 @@ const BaseBox = BaseCard.extend`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const BaseMeta = BaseCard.extend`
|
const BaseMeta = BaseCard.extend`
|
||||||
|
box-sizing: border-box;
|
||||||
height: ${remcalc(47)};
|
height: ${remcalc(47)};
|
||||||
width: auto;
|
width: auto;
|
||||||
min-width: auto;
|
min-width: auto;
|
||||||
padding: 0 ${remcalc(12)};
|
padding: ${remcalc(12)};
|
||||||
|
|
||||||
display: flex;
|
display: inline-flex;
|
||||||
flex: 1 0 auto;
|
flex: 1 1 auto;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
align-conent: stretch;
|
align-content: stretch;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
border-width: 0;
|
border-width: 0;
|
||||||
@ -108,8 +113,8 @@ export const Box = ({ children, border, actionable, ...rest }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseBox
|
<BaseBox
|
||||||
{...rest}
|
|
||||||
{...value}
|
{...value}
|
||||||
|
{...rest}
|
||||||
name="card-header-box"
|
name="card-header-box"
|
||||||
border={newBorder}
|
border={newBorder}
|
||||||
secondary={secondary}
|
secondary={secondary}
|
||||||
@ -149,12 +154,15 @@ export const Meta = ({ children, ...rest }) => (
|
|||||||
);
|
);
|
||||||
|
|
||||||
const Header = ({ children, transparent, shadow, ...rest }) => {
|
const Header = ({ children, transparent, shadow, ...rest }) => {
|
||||||
const render = ({ secondary, tertiary, collapsed, ...value }) => {
|
const render = ({ secondary, tertiary, collapsed, actionable, ...value }) => {
|
||||||
const parentPrimary = !secondary && !tertiary;
|
const parentPrimary = !secondary && !tertiary;
|
||||||
|
// if secondary is hardcoded, use that
|
||||||
// if transparent and parent is secondary, keep seconday
|
// if transparent and parent is secondary, keep seconday
|
||||||
// if parent is secondary, keep being secondary or
|
// if parent is secondary, keep being secondary or
|
||||||
// if parent is primary, become secondary
|
// if parent is primary, become secondary
|
||||||
const isSecondary = transparent ? secondary : secondary || parentPrimary;
|
const isSecondary = isBoolean(rest.secondary)
|
||||||
|
? rest.secondary
|
||||||
|
: transparent ? secondary : secondary || parentPrimary;
|
||||||
// if parent is primary, don't become transparent
|
// if parent is primary, don't become transparent
|
||||||
const isTransparent = transparent || secondary || tertiary;
|
const isTransparent = transparent || secondary || tertiary;
|
||||||
|
|
||||||
@ -162,7 +170,8 @@ const Header = ({ children, transparent, shadow, ...rest }) => {
|
|||||||
...value,
|
...value,
|
||||||
parentCollapsed: collapsed,
|
parentCollapsed: collapsed,
|
||||||
secondary: isSecondary,
|
secondary: isSecondary,
|
||||||
tertiary,
|
tertiary: isBoolean(rest.tertiary) ? rest.tertiary : tertiary,
|
||||||
|
actionable: isBoolean(rest.actionable) ? rest.actionable : actionable,
|
||||||
transparent: isTransparent,
|
transparent: isTransparent,
|
||||||
collapsed: true,
|
collapsed: true,
|
||||||
shadow: Boolean(shadow)
|
shadow: Boolean(shadow)
|
||||||
|
@ -10,10 +10,12 @@ import Card, { BaseCard } from './card';
|
|||||||
const BaseOutlet = BaseCard.extend`
|
const BaseOutlet = BaseCard.extend`
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
display: inline-flex;
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
border-width: 0;
|
border-width: 0;
|
||||||
padding: ${remcalc(13)};
|
padding: ${remcalc(13)};
|
||||||
|
margin-bottom: 0;
|
||||||
|
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
|
||||||
@ -21,6 +23,10 @@ const BaseOutlet = BaseCard.extend`
|
|||||||
display: none;
|
display: none;
|
||||||
`};
|
`};
|
||||||
|
|
||||||
|
${is('pullUp')`
|
||||||
|
margin-top: ${remcalc(-48)};
|
||||||
|
`};
|
||||||
|
|
||||||
& > [name='card']:not(:last-child) {
|
& > [name='card']:not(:last-child) {
|
||||||
margin-bottom: ${remcalc(13)};
|
margin-bottom: ${remcalc(13)};
|
||||||
}
|
}
|
||||||
|
@ -725,6 +725,145 @@ const { Row, Col } = require('react-styled-flexboxgrid');
|
|||||||
];
|
];
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Card > Header > Secondary
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
const React = require('react');
|
||||||
|
const { default: Card, Header, HeaderBox, HeaderMeta } = require('.');
|
||||||
|
const { Row, Col } = require('react-styled-flexboxgrid');
|
||||||
|
|
||||||
|
[
|
||||||
|
<Row>
|
||||||
|
<Col xs={12}>
|
||||||
|
<Card shadow>
|
||||||
|
<Header secondary={false}>
|
||||||
|
<HeaderBox border="right">
|
||||||
|
<code>HB</code>
|
||||||
|
</HeaderBox>
|
||||||
|
<HeaderBox>
|
||||||
|
<code>HB</code>
|
||||||
|
</HeaderBox>
|
||||||
|
<HeaderMeta>
|
||||||
|
<code>{`<HeaderMeta />`}</code>
|
||||||
|
</HeaderMeta>
|
||||||
|
<HeaderBox border="left">
|
||||||
|
<code>HB</code>
|
||||||
|
</HeaderBox>
|
||||||
|
</Header>
|
||||||
|
<code>{`<Card shadow />`}</code>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
</Row>,
|
||||||
|
<br />,
|
||||||
|
<Row>
|
||||||
|
<Col xs={12}>
|
||||||
|
<Card secondary={false} shadow>
|
||||||
|
<Header secondary>
|
||||||
|
<HeaderBox border="right">
|
||||||
|
<code>HB</code>
|
||||||
|
</HeaderBox>
|
||||||
|
<HeaderBox>
|
||||||
|
<code>HB</code>
|
||||||
|
</HeaderBox>
|
||||||
|
<HeaderMeta>
|
||||||
|
<code>{`<HeaderMeta />`}</code>
|
||||||
|
</HeaderMeta>
|
||||||
|
<HeaderBox border="left">
|
||||||
|
<code>HB</code>
|
||||||
|
</HeaderBox>
|
||||||
|
</Header>
|
||||||
|
<code>{`<Card secondary shadow />`}</code>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
</Row>,
|
||||||
|
<br />,
|
||||||
|
<Row>
|
||||||
|
<Col xs={12}>
|
||||||
|
<Card tertiary shadow>
|
||||||
|
<Header secondary={false}>
|
||||||
|
<HeaderBox border="right">
|
||||||
|
<code>HB</code>
|
||||||
|
</HeaderBox>
|
||||||
|
<HeaderBox>
|
||||||
|
<code>HB</code>
|
||||||
|
</HeaderBox>
|
||||||
|
<HeaderMeta>
|
||||||
|
<code>{`<HeaderMeta />`}</code>
|
||||||
|
</HeaderMeta>
|
||||||
|
<HeaderBox border="left">
|
||||||
|
<code>HB</code>
|
||||||
|
</HeaderBox>
|
||||||
|
</Header>
|
||||||
|
<code>{`<Card tertiary shadow />`}</code>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
</Row>,
|
||||||
|
<br />,
|
||||||
|
<Row>
|
||||||
|
<Col xs={12}>
|
||||||
|
<Card collapsed shadow>
|
||||||
|
<Header secondary={false}>
|
||||||
|
<HeaderBox border="right">
|
||||||
|
<code>HB</code>
|
||||||
|
</HeaderBox>
|
||||||
|
<HeaderBox>
|
||||||
|
<code>HB</code>
|
||||||
|
</HeaderBox>
|
||||||
|
<HeaderMeta>
|
||||||
|
<code>{`<HeaderMeta />`}</code>
|
||||||
|
</HeaderMeta>
|
||||||
|
<HeaderBox border="left">
|
||||||
|
<code>HB</code>
|
||||||
|
</HeaderBox>
|
||||||
|
</Header>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
</Row>,
|
||||||
|
<br />,
|
||||||
|
<Row>
|
||||||
|
<Col xs={12}>
|
||||||
|
<Card secondary collapsed shadow>
|
||||||
|
<Header secondary={false}>
|
||||||
|
<HeaderBox border="right">
|
||||||
|
<code>HB</code>
|
||||||
|
</HeaderBox>
|
||||||
|
<HeaderBox>
|
||||||
|
<code>HB</code>
|
||||||
|
</HeaderBox>
|
||||||
|
<HeaderMeta>
|
||||||
|
<code>{`<HeaderMeta />`}</code>
|
||||||
|
</HeaderMeta>
|
||||||
|
<HeaderBox border="left">
|
||||||
|
<code>HB</code>
|
||||||
|
</HeaderBox>
|
||||||
|
</Header>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
</Row>,
|
||||||
|
<br />,
|
||||||
|
<Row>
|
||||||
|
<Col xs={12}>
|
||||||
|
<Card tertiary collapsed shadow>
|
||||||
|
<Header secondary={false}>
|
||||||
|
<HeaderBox border="right">
|
||||||
|
<code>HB</code>
|
||||||
|
</HeaderBox>
|
||||||
|
<HeaderBox>
|
||||||
|
<code>HB</code>
|
||||||
|
</HeaderBox>
|
||||||
|
<HeaderMeta>
|
||||||
|
<code>{`<HeaderMeta />`}</code>
|
||||||
|
</HeaderMeta>
|
||||||
|
<HeaderBox border="left">
|
||||||
|
<code>HB</code>
|
||||||
|
</HeaderBox>
|
||||||
|
</Header>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
#### Card > Outlet
|
#### Card > Outlet
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
|
@ -7,13 +7,11 @@ import Baseline from '../baseline';
|
|||||||
|
|
||||||
const Divider = styled(Row)`
|
const Divider = styled(Row)`
|
||||||
background-color: ${props => props.theme.grey};
|
background-color: ${props => props.theme.grey};
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
${is('transparent')`
|
${is('transparent')`
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
`};
|
`};
|
||||||
|
|
||||||
height: ${remcalc(1)};
|
|
||||||
margin: 0;
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export default Baseline(Divider);
|
export default Baseline(Divider);
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
### How was the Joyent UI Toolkit built?
|
### How was the Joyent UI Toolkit built?
|
||||||
|
|
||||||
The toolkit components were built using
|
The toolkit components were built using
|
||||||
[React](https://facebook.github.io/react/) and [Styled
|
[React](https://facebook.github.io/react/) and
|
||||||
Components](http://styled-components.com).
|
[Styled Components](http://styled-components.com).
|
||||||
|
|
||||||
### What is the toolkit's license?
|
### What is the toolkit's license?
|
||||||
|
|
||||||
|
@ -19,7 +19,8 @@ const colorWithDefaultValue = props =>
|
|||||||
const color = props =>
|
const color = props =>
|
||||||
props.defaultValue ? colorWithDefaultValue(props) : colorWithDisabled(props);
|
props.defaultValue ? colorWithDefaultValue(props) : colorWithDisabled(props);
|
||||||
|
|
||||||
const height = props => (props.multiple ? 'auto' : remcalc(48));
|
const height = props =>
|
||||||
|
props.multiple ? 'auto' : props.textarea ? remcalc(96) : remcalc(48);
|
||||||
|
|
||||||
const paddingTop = props => (props.multiple ? remcalc(20) : remcalc(13));
|
const paddingTop = props => (props.multiple ? remcalc(20) : remcalc(13));
|
||||||
|
|
||||||
@ -28,6 +29,7 @@ const style = css`
|
|||||||
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: ${height};
|
height: ${height};
|
||||||
|
min-height: ${height};
|
||||||
|
|
||||||
margin-bottom: ${remcalc(8)};
|
margin-bottom: ${remcalc(8)};
|
||||||
margin-top: ${remcalc(8)};
|
margin-top: ${remcalc(8)};
|
||||||
@ -36,19 +38,45 @@ const style = css`
|
|||||||
border-radius: ${borderRadius};
|
border-radius: ${borderRadius};
|
||||||
background-color: ${props => props.theme.white};
|
background-color: ${props => props.theme.white};
|
||||||
border: ${border.unchecked};
|
border: ${border.unchecked};
|
||||||
|
color: ${color};
|
||||||
|
|
||||||
${is('disabled')`
|
${is('disabled')`
|
||||||
|
background-color: ${props => props.theme.disabled};
|
||||||
|
color: ${props => props.theme.textDisabled};
|
||||||
|
|
||||||
::-webkit-input-placeholder { /* WebKit, Blink, Edge */
|
::-webkit-input-placeholder { /* WebKit, Blink, Edge */
|
||||||
color: ${props => props.theme.placeholder};
|
color: ${props => props.theme.placeholder};
|
||||||
}
|
}
|
||||||
|
|
||||||
::-moz-placeholder { /* Mozilla Firefox 19+ */
|
::-moz-placeholder { /* Mozilla Firefox 19+ */
|
||||||
color: ${props => props.theme.placeholder};
|
color: ${props => props.theme.placeholder};
|
||||||
}
|
}
|
||||||
|
|
||||||
:-ms-input-placeholder { /* Internet Explorer 10-11 */
|
:-ms-input-placeholder { /* Internet Explorer 10-11 */
|
||||||
color: ${props => props.theme.placeholder};
|
color: ${props => props.theme.placeholder};
|
||||||
}
|
}
|
||||||
`};
|
`};
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
background-color: ${props => props.theme.disabled};
|
||||||
|
color: ${props => props.theme.textDisabled};
|
||||||
|
|
||||||
|
::-webkit-input-placeholder {
|
||||||
|
/* WebKit, Blink, Edge */
|
||||||
|
color: ${props => props.theme.placeholder};
|
||||||
|
}
|
||||||
|
|
||||||
|
::-moz-placeholder {
|
||||||
|
/* Mozilla Firefox 19+ */
|
||||||
|
color: ${props => props.theme.placeholder};
|
||||||
|
}
|
||||||
|
|
||||||
|
:-ms-input-placeholder {
|
||||||
|
/* Internet Explorer 10-11 */
|
||||||
|
color: ${props => props.theme.placeholder};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
${is('error')`
|
${is('error')`
|
||||||
border-color: ${props => props.theme.redDark}
|
border-color: ${props => props.theme.redDark}
|
||||||
`};
|
`};
|
||||||
@ -77,13 +105,16 @@ const style = css`
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
`};
|
`};
|
||||||
|
|
||||||
|
${is('resize')`
|
||||||
|
resize: ${props => props.resize};
|
||||||
|
`};
|
||||||
|
|
||||||
font-size: ${remcalc(15)};
|
font-size: ${remcalc(15)};
|
||||||
line-height: normal !important;
|
line-height: normal !important;
|
||||||
|
|
||||||
${typography.normal};
|
${typography.normal};
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-stretch: normal;
|
font-stretch: normal;
|
||||||
color: ${color};
|
|
||||||
|
|
||||||
appearance: none;
|
appearance: none;
|
||||||
outline: 0;
|
outline: 0;
|
||||||
@ -94,7 +125,7 @@ const style = css`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const BaseInput = Component => props => {
|
const BaseInput = Component => ({ resize, type, ...props }) => {
|
||||||
const render = value => {
|
const render = value => {
|
||||||
const _value = value || {};
|
const _value = value || {};
|
||||||
const { input = {}, meta = {}, id = '' } = _value;
|
const { input = {}, meta = {}, id = '' } = _value;
|
||||||
@ -103,6 +134,7 @@ const BaseInput = Component => props => {
|
|||||||
const hasWarning = Boolean(props.warning || _value.warning || meta.warning);
|
const hasWarning = Boolean(props.warning || _value.warning || meta.warning);
|
||||||
const hasSuccess = Boolean(props.success || _value.success || meta.success);
|
const hasSuccess = Boolean(props.success || _value.success || meta.success);
|
||||||
|
|
||||||
|
const textarea = type === 'textarea';
|
||||||
const marginless = Boolean(props.marginless);
|
const marginless = Boolean(props.marginless);
|
||||||
const fluid = Boolean(props.fluid);
|
const fluid = Boolean(props.fluid);
|
||||||
const mono = Boolean(props.mono);
|
const mono = Boolean(props.mono);
|
||||||
@ -118,6 +150,8 @@ const BaseInput = Component => props => {
|
|||||||
fluid={fluid}
|
fluid={fluid}
|
||||||
marginless={marginless}
|
marginless={marginless}
|
||||||
mono={mono}
|
mono={mono}
|
||||||
|
resize={textarea ? resize : null}
|
||||||
|
textarea={textarea}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -127,7 +127,9 @@ const ToggleBase = ({ container = null, type = 'radio' }) =>
|
|||||||
id: rndId()
|
id: rndId()
|
||||||
};
|
};
|
||||||
|
|
||||||
const checked = type === 'checkbox' && rest.value === true;
|
const checked =
|
||||||
|
['checkbox', 'radio'].indexOf(type) >= 0 &&
|
||||||
|
(rest.value === true || rest.checked === true);
|
||||||
|
|
||||||
const toggle = (
|
const toggle = (
|
||||||
<InnerContainer {...types} type={type}>
|
<InnerContainer {...types} type={type}>
|
||||||
|
@ -7,6 +7,8 @@ import rndId from 'rnd-id';
|
|||||||
import Fieldset from './fieldset';
|
import Fieldset from './fieldset';
|
||||||
import Baseline from '../baseline';
|
import Baseline from '../baseline';
|
||||||
|
|
||||||
|
const Noop = ({ children }) => children;
|
||||||
|
|
||||||
class FormGroup extends Component {
|
class FormGroup extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
@ -26,7 +28,7 @@ class FormGroup extends Component {
|
|||||||
return (
|
return (
|
||||||
<Fieldset className={className} style={style}>
|
<Fieldset className={className} style={style}>
|
||||||
<Broadcast channel="input-group" value={value}>
|
<Broadcast channel="input-group" value={value}>
|
||||||
<div>{children}</div>
|
<Noop>{children}</Noop>
|
||||||
</Broadcast>
|
</Broadcast>
|
||||||
</Fieldset>
|
</Fieldset>
|
||||||
);
|
);
|
||||||
|
@ -2,6 +2,7 @@ export { default as Checkbox, CheckboxList } from './checkbox';
|
|||||||
export { default as Fieldset } from './fieldset';
|
export { default as Fieldset } from './fieldset';
|
||||||
export { default as FormGroup } from './group';
|
export { default as FormGroup } from './group';
|
||||||
export { default as Input } from './input';
|
export { default as Input } from './input';
|
||||||
|
export { default as Textarea } from './textarea';
|
||||||
export { default as FormLabel } from './label';
|
export { default as FormLabel } from './label';
|
||||||
export { default as Legend } from './legend';
|
export { default as Legend } from './legend';
|
||||||
export { default as FormMeta } from './meta';
|
export { default as FormMeta } from './meta';
|
||||||
|
42
packages/ui-toolkit/src/form/textarea.js
Normal file
42
packages/ui-toolkit/src/form/textarea.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import is from 'styled-is';
|
||||||
|
import remcalc from 'remcalc';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import Baseline from '../baseline';
|
||||||
|
import BaseInput, { Stylable } from './base/input';
|
||||||
|
|
||||||
|
const TextareaInput = Baseline(BaseInput(Stylable('textarea')));
|
||||||
|
|
||||||
|
const BaseTextarea = TextareaInput.extend`
|
||||||
|
position: relative;
|
||||||
|
display: inline-flex;
|
||||||
|
|
||||||
|
${is('fluid')`
|
||||||
|
flex: 1 1 auto;
|
||||||
|
width: 100%;
|
||||||
|
`};
|
||||||
|
`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @example ./usage-textarea.md
|
||||||
|
*/
|
||||||
|
const Textarea = ({ children, fluid, ...rest }) => (
|
||||||
|
<BaseTextarea {...rest} fluid={fluid} type="textarea">
|
||||||
|
{children}
|
||||||
|
</BaseTextarea>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Textarea;
|
||||||
|
|
||||||
|
Textarea.propTypes = {
|
||||||
|
/**
|
||||||
|
* Is the Textarea disabled ?
|
||||||
|
*/
|
||||||
|
disabled: PropTypes.bool
|
||||||
|
};
|
||||||
|
|
||||||
|
Textarea.defaultProps = {
|
||||||
|
disabled: false
|
||||||
|
};
|
@ -10,11 +10,6 @@ const Brand = H2.extend`
|
|||||||
color: ${props => props.theme.white};
|
color: ${props => props.theme.white};
|
||||||
font-size: ${remcalc(29)};
|
font-size: ${remcalc(29)};
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
${is('beta')`
|
|
||||||
display: inline-block;
|
|
||||||
margin-top: ${remcalc(6)};
|
|
||||||
`};
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Box = styled.div`
|
const Box = styled.div`
|
||||||
|
@ -22,7 +22,6 @@ const Header = styled.div`
|
|||||||
background-color: ${props => props.theme.brandBackground};
|
background-color: ${props => props.theme.brandBackground};
|
||||||
max-height: ${remcalc(53)};
|
max-height: ${remcalc(53)};
|
||||||
min-height: ${remcalc(53)};
|
min-height: ${remcalc(53)};
|
||||||
padding-left: ${remcalc(18)};
|
|
||||||
line-height: ${remcalc(25)};
|
line-height: ${remcalc(25)};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -35,6 +34,6 @@ export default ({ children, ...rest }) => (
|
|||||||
</Header>
|
</Header>
|
||||||
);
|
);
|
||||||
|
|
||||||
export { default as HeaderItem } from './item';
|
export { default as HeaderItem, Anchor as HeaderAnchor } from './item';
|
||||||
export { default as HeaderBrand } from './brand';
|
export { default as HeaderBrand } from './brand';
|
||||||
export { default as HeaderNav, Anchor as HeaderNavAnchor } from './nav';
|
export { default as HeaderNav } from './nav';
|
||||||
|
@ -1,18 +1,35 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled, { css } from 'styled-components';
|
||||||
import remcalc from 'remcalc';
|
import remcalc from 'remcalc';
|
||||||
|
import { Link as BaseLink } from 'react-router-dom';
|
||||||
|
import { A } from 'normalized-styled-components';
|
||||||
|
|
||||||
import P from '../text/p';
|
import P from '../text/p';
|
||||||
|
|
||||||
|
const style = css`
|
||||||
|
padding: ${remcalc(15)};
|
||||||
|
line-height: ${remcalc(24)};
|
||||||
|
font-size: ${remcalc(15)};
|
||||||
|
color: ${props => props.theme.white};
|
||||||
|
text-decoration: none;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: all 200ms ease;
|
||||||
|
max-height: ${remcalc(53)};
|
||||||
|
min-height: ${remcalc(53)};
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&.active {
|
||||||
|
background: rgba(255, 255, 255, 0.15);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
const Text = P.extend`
|
const Text = P.extend`
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: ${props => props.theme.white};
|
color: ${props => props.theme.white};
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
a {
|
|
||||||
color: ${props => props.theme.white};
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const Box = styled.section`
|
const Box = styled.section`
|
||||||
@ -21,7 +38,6 @@ const Box = styled.section`
|
|||||||
order: 0;
|
order: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: ${remcalc(15)};
|
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
margin-right: ${remcalc(6)};
|
margin-right: ${remcalc(6)};
|
||||||
@ -36,6 +52,25 @@ const Box = styled.section`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const StyledAnchor = A.extend`
|
||||||
|
/* trick prettier */
|
||||||
|
${style};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledLink = styled(BaseLink)`
|
||||||
|
/* trick prettier */
|
||||||
|
${style};
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const Anchor = ({ children, ...rest }) => {
|
||||||
|
const { to = '' } = rest;
|
||||||
|
|
||||||
|
const Views = [() => (to ? StyledLink : null), () => StyledAnchor];
|
||||||
|
const View = Views.reduce((sel, view) => (sel ? sel : view()), null);
|
||||||
|
|
||||||
|
return <View {...rest}>{children}</View>;
|
||||||
|
};
|
||||||
|
|
||||||
export default ({ children, ...rest }) => (
|
export default ({ children, ...rest }) => (
|
||||||
<Box {...rest}>
|
<Box {...rest}>
|
||||||
<Text>{children}</Text>
|
<Text>{children}</Text>
|
||||||
|
@ -1,53 +1,8 @@
|
|||||||
import React from 'react';
|
import styled from 'styled-components';
|
||||||
import styled, { css } from 'styled-components';
|
|
||||||
import { Link as BaseLink } from 'react-router-dom';
|
|
||||||
import { A } from 'normalized-styled-components';
|
|
||||||
import remcalc from 'remcalc';
|
|
||||||
|
|
||||||
const Ul = styled.ul`
|
export default styled.ul`
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const style = css`
|
|
||||||
padding: ${remcalc(15)};
|
|
||||||
line-height: ${remcalc(24)};
|
|
||||||
font-size: ${remcalc(15)};
|
|
||||||
color: ${props => props.theme.white};
|
|
||||||
text-decoration: none;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
transition: all 200ms ease;
|
|
||||||
max-height: ${remcalc(53)};
|
|
||||||
min-height: ${remcalc(53)};
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&.active {
|
|
||||||
background: rgba(255, 255, 255, 0.15);
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledAnchor = A.extend`
|
|
||||||
/* trick prettier */
|
|
||||||
${style};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledLink = styled(BaseLink)`
|
|
||||||
/* trick prettier */
|
|
||||||
${style};
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const Anchor = ({ children, ...rest }) => {
|
|
||||||
const { to = '' } = rest;
|
|
||||||
|
|
||||||
const Views = [() => (to ? StyledLink : null), () => StyledAnchor];
|
|
||||||
const View = Views.reduce((sel, view) => (sel ? sel : view()), null);
|
|
||||||
|
|
||||||
return <View {...rest}>{children}</View>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ({ children, ...rest }) => <Ul {...rest}>{children}</Ul>;
|
|
||||||
|
@ -2,13 +2,13 @@
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
const { default: HeaderBrand } = require('./brand.js');
|
const { default: HeaderBrand } = require('./brand.js');
|
||||||
const { default: HeaderItem } = require('./item.js');
|
const { default: HeaderItem } = require('./item.js');
|
||||||
const { default: HeaderNav, HeaderNavAnchor } = require('./nav.js');
|
const { default: HeaderNav, HeaderAnchor } = require('./nav.js');
|
||||||
|
|
||||||
<Header>
|
<Header>
|
||||||
<HeaderBrand beta><TritonBetaIcon/></HeaderBrand>
|
<HeaderBrand beta><TritonBetaIcon/></HeaderBrand>
|
||||||
<HeaderNav>
|
<HeaderNav>
|
||||||
<li><HeaderNavAnchor href="#">Compute</HeaderNavAnchor></li>
|
<li><HeaderAnchor href="#">Compute</HeaderAnchor></li>
|
||||||
<li><HeaderNavAnchor href="#" class="active">Network</HeaderNavAnchor></li>
|
<li><HeaderAnchor href="#" class="active">Network</HeaderAnchor></li>
|
||||||
</HeaderNav>
|
</HeaderNav>
|
||||||
<HeaderItem>Return to existing portal</HeaderItem>
|
<HeaderItem>Return to existing portal</HeaderItem>
|
||||||
<HeaderItem><DataCenterIconLight/>eu-east-1</HeaderItem>
|
<HeaderItem><DataCenterIconLight/>eu-east-1</HeaderItem>
|
||||||
|
@ -42,22 +42,11 @@ export {
|
|||||||
} from './breakpoints';
|
} from './breakpoints';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
CardDescription,
|
default as Card,
|
||||||
CardHeader,
|
Outlet as CardOutlet,
|
||||||
CardGroupView,
|
Header as CardHeader,
|
||||||
Card,
|
HeaderMeta as CardHeaderMeta,
|
||||||
CardMeta,
|
HeaderBox as CardHeaderBox
|
||||||
CardOptions,
|
|
||||||
CardOutlet,
|
|
||||||
CardSubTitle,
|
|
||||||
CardTitle,
|
|
||||||
CardAction,
|
|
||||||
CardView,
|
|
||||||
CardFooter,
|
|
||||||
CardLabel,
|
|
||||||
CardInfo,
|
|
||||||
CardInfoLabel,
|
|
||||||
CardInfoIconContainer
|
|
||||||
} from './card';
|
} from './card';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
@ -66,6 +55,7 @@ export {
|
|||||||
Fieldset,
|
Fieldset,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
Input,
|
Input,
|
||||||
|
Textarea,
|
||||||
FormLabel,
|
FormLabel,
|
||||||
Legend,
|
Legend,
|
||||||
FormMeta,
|
FormMeta,
|
||||||
@ -83,7 +73,7 @@ export {
|
|||||||
HeaderBrand,
|
HeaderBrand,
|
||||||
HeaderItem,
|
HeaderItem,
|
||||||
HeaderNav,
|
HeaderNav,
|
||||||
HeaderNavAnchor
|
HeaderAnchor
|
||||||
} from './header';
|
} from './header';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
@ -54,9 +54,9 @@ export const Message = ({ onCloseClick, children, ...type }) => (
|
|||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const MessageTitle = ({ children }) => <H4>{children}</H4>;
|
export const Title = ({ children }) => <H4>{children}</H4>;
|
||||||
|
|
||||||
export const MessageDescription = ({ children }) => <P>{children}</P>;
|
export const Description = ({ children }) => <P>{children}</P>;
|
||||||
|
|
||||||
Message.propTypes = {
|
Message.propTypes = {
|
||||||
/**
|
/**
|
||||||
|
@ -3,5 +3,5 @@ The Joyent UI Toolkit allows anyone designing and building new
|
|||||||
prototypes that follow a considered and consistent design direction.
|
prototypes that follow a considered and consistent design direction.
|
||||||
|
|
||||||
As any style guide and design system, this toolkit is a work in progress, and
|
As any style guide and design system, this toolkit is a work in progress, and
|
||||||
everyone is encouraged to [contribute and improve
|
everyone is encouraged to
|
||||||
it](https://github.com/yldio/joyent-portal/tree/master/packages/ui-toolkit).
|
[contribute and improve it](https://github.com/yldio/joyent-portal/tree/master/packages/ui-toolkit).
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
Joyent's font is Libre Franklin, which is available to use at [Google
|
Joyent's font is Libre Franklin, which is available to use at
|
||||||
Fonts](https://fonts.google.com/specimen/Libre+Franklin).
|
[Google Fonts](https://fonts.google.com/specimen/Libre+Franklin).
|
||||||
|
|
||||||
The font sizes in the toolkit are based on an [augmented fourth modular
|
The font sizes in the toolkit are based on an
|
||||||
scale](http://www.modularscale.com/?15,24&px&1.414), with base font size of
|
[augmented fourth modular scale](http://www.modularscale.com/?15,24&px&1.414),
|
||||||
**15px**.
|
with base font size of **15px**.
|
||||||
|
|
||||||
### Headings
|
### Headings
|
||||||
|
|
||||||
Headings are available from `h1` through to `h4`. If demand is shown for `h5`
|
Headings are available from `h1` through to `h4`. If demand is shown for `h5`
|
||||||
and `h6`, these will be included in the toolkit.
|
and `h6`, these will be included in the toolkit.
|
||||||
|
|
||||||
To learn more about the correct usage of HTML headings, visit [MDN web
|
To learn more about the correct usage of HTML headings, visit
|
||||||
docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Heading_Elements).
|
[MDN web docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Heading_Elements).
|
||||||
|
|
||||||
#### Heading 1
|
#### Heading 1
|
||||||
|
|
||||||
@ -86,8 +86,8 @@ const Small = require('/').Small;
|
|||||||
The `<label>` element is used for captions in the user interface and information
|
The `<label>` element is used for captions in the user interface and information
|
||||||
labels (i.e. text that is not continuous body text).
|
labels (i.e. text that is not continuous body text).
|
||||||
|
|
||||||
Read more about using the `<label>` element on the [MDN web
|
Read more about using the `<label>` element on the
|
||||||
docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label).
|
[MDN web docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label).
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
@ -102,8 +102,8 @@ const Label = require('/').Label;
|
|||||||
### Anchors
|
### Anchors
|
||||||
|
|
||||||
Links in the toolkit are named `Anchor`. This is to avoid confusion with `Link`,
|
Links in the toolkit are named `Anchor`. This is to avoid confusion with `Link`,
|
||||||
which is a [React Router routing
|
which is a
|
||||||
link](http://knowbody.github.io/react-router-docs/api/Link.html).
|
[React Router routing link](http://knowbody.github.io/react-router-docs/api/Link.html).
|
||||||
|
|
||||||
#### Primary
|
#### Primary
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ const white = {
|
|||||||
|
|
||||||
const grey = {
|
const grey = {
|
||||||
grey: 'rgb(216, 216, 216)',
|
grey: 'rgb(216, 216, 216)',
|
||||||
greyTransparent: 'rgba(73,73,73, 0.8)'
|
greyTransparent: 'rgba(73, 73, 73, 0.8)'
|
||||||
};
|
};
|
||||||
|
|
||||||
const green = {
|
const green = {
|
||||||
@ -62,9 +62,9 @@ export const base = {
|
|||||||
...orange,
|
...orange,
|
||||||
...green,
|
...green,
|
||||||
...grey,
|
...grey,
|
||||||
text: 'rgba(73,73,73, 1)',
|
text: 'rgba(73, 73, 73, 1)',
|
||||||
textDisabled: 'rgba(73,73,73, 0.5)',
|
textDisabled: 'rgba(73, 73, 73, 0.5)',
|
||||||
placeholder: 'rgb(99,99,99)',
|
placeholder: 'rgb(99, 99, 99)',
|
||||||
disabled: 'rgb(250, 250, 250)', // used
|
disabled: 'rgb(250, 250, 250)', // used
|
||||||
background: 'rgb(250, 250, 250)' // used
|
background: 'rgb(250, 250, 250)' // used
|
||||||
};
|
};
|
||||||
|
@ -62,13 +62,15 @@ const generate = name => css`
|
|||||||
font-weight: ${fontFaces[name].weight};
|
font-weight: ${fontFaces[name].weight};
|
||||||
src: url("${fontFaces[name].filenames.eot}");
|
src: url("${fontFaces[name].filenames.eot}");
|
||||||
src:
|
src:
|
||||||
url("${fontFaces[name].filenames
|
url("${
|
||||||
.eot}?#iefix") format("embedded-opentype"),
|
fontFaces[name].filenames.eot
|
||||||
|
}?#iefix") format("embedded-opentype"),
|
||||||
url("${fontFaces[name].filenames.woff}") format("woff"),
|
url("${fontFaces[name].filenames.woff}") format("woff"),
|
||||||
url("${fontFaces[name].filenames.woff2}") format("woff2"),
|
url("${fontFaces[name].filenames.woff2}") format("woff2"),
|
||||||
url("${fontFaces[name].filenames.ttf}") format("truetype"),
|
url("${fontFaces[name].filenames.ttf}") format("truetype"),
|
||||||
url("${fontFaces[name].filenames.svg}#${fontFaces[name]
|
url("${fontFaces[name].filenames.svg}#${
|
||||||
.family}") format("svg");
|
fontFaces[name].family
|
||||||
|
}") format("svg");
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -19,21 +19,21 @@
|
|||||||
"pascal-case": "^2.0.1",
|
"pascal-case": "^2.0.1",
|
||||||
"path-to-regexp": "^2.1.0",
|
"path-to-regexp": "^2.1.0",
|
||||||
"qs": "^6.5.1",
|
"qs": "^6.5.1",
|
||||||
"react": "^16.0.0",
|
"react": "^16.1.1",
|
||||||
"react-dom": "^16.0.0",
|
"react-dom": "^16.1.1",
|
||||||
"react-redux": "^5.0.6",
|
"react-redux": "^5.0.6",
|
||||||
"react-router-dom": "^4.2.2",
|
"react-router-dom": "^4.2.2",
|
||||||
"react-scripts": "1.0.14",
|
"react-scripts": "1.0.17",
|
||||||
"redux": "^3.7.2",
|
"redux": "^3.7.2",
|
||||||
"redux-form": "^7.1.1",
|
"redux-form": "^7.1.2",
|
||||||
"styled-components": "^2.2.2"
|
"styled-components": "^2.2.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"babel-preset-joyent-portal": "^3.3.3",
|
"babel-preset-joyent-portal": "^3.3.3",
|
||||||
"eslint": "^4.9.0",
|
"eslint": "^4.11.0",
|
||||||
"eslint-config-joyent-portal": "^3.2.0",
|
"eslint-config-joyent-portal": "^3.2.0",
|
||||||
"joyent-react-scripts": "^2.6.0",
|
"joyent-react-scripts": "^3.1.0",
|
||||||
"prettier": "^1.7.4",
|
"prettier": "^1.8.2",
|
||||||
"stylelint": "^8.2.0",
|
"stylelint": "^8.2.0",
|
||||||
"stylelint-config-joyent-portal": "^2.0.1"
|
"stylelint-config-joyent-portal": "^2.0.1"
|
||||||
}
|
}
|
||||||
|
@ -24,29 +24,29 @@
|
|||||||
"lodash.isempty": "^4.4.0",
|
"lodash.isempty": "^4.4.0",
|
||||||
"normalized-styled-components": "^1.0.17",
|
"normalized-styled-components": "^1.0.17",
|
||||||
"prop-types": "^15.6.0",
|
"prop-types": "^15.6.0",
|
||||||
"react": "^16.0.0",
|
"react": "^16.1.1",
|
||||||
"react-apollo": "^1.4.16",
|
"react-apollo": "^1.4.16",
|
||||||
"react-dom": "^16.0.0",
|
"react-dom": "^16.1.1",
|
||||||
"react-redux": "^5.0.6",
|
"react-redux": "^5.0.6",
|
||||||
"react-router": "^4.2.0",
|
"react-router": "^4.2.0",
|
||||||
"react-router-dom": "^4.2.2",
|
"react-router-dom": "^4.2.2",
|
||||||
"react-styled-flexboxgrid": "^2.1.0",
|
"react-styled-flexboxgrid": "^2.1.1",
|
||||||
"redux": "^3.7.2",
|
"redux": "^3.7.2",
|
||||||
"redux-form": "^7.1.1",
|
"redux-form": "^7.1.2",
|
||||||
"remcalc": "^1.0.9",
|
"remcalc": "^1.0.9",
|
||||||
"styled-components": "^2.2.2",
|
"styled-components": "^2.2.3",
|
||||||
"styled-is": "^1.1.0",
|
"styled-is": "^1.1.0",
|
||||||
"unitcalc": "^1.1.1"
|
"unitcalc": "^1.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"apr-for-each": "^1.0.6",
|
"apr-for-each": "^1.0.6",
|
||||||
"apr-main": "^1.0.7",
|
"apr-main": "^2.0.2",
|
||||||
"babel-minify-webpack-plugin": "^0.2.0",
|
"babel-minify-webpack-plugin": "^0.2.0",
|
||||||
"babel-plugin-inline-react-svg": "^0.4.0",
|
"babel-plugin-inline-react-svg": "^0.4.0",
|
||||||
"babel-preset-joyent-portal": "^3.3.3",
|
"babel-preset-joyent-portal": "^3.3.3",
|
||||||
"commitizen": "^2.9.6",
|
"commitizen": "^2.9.6",
|
||||||
"cross-env": "^5.1.0",
|
"cross-env": "^5.1.1",
|
||||||
"eslint": "^4.9.0",
|
"eslint": "^4.11.0",
|
||||||
"eslint-config-joyent-portal": "^3.2.0",
|
"eslint-config-joyent-portal": "^3.2.0",
|
||||||
"jest": "^21.2.1",
|
"jest": "^21.2.1",
|
||||||
"jest-alias-preprocessor": "^1.1.1",
|
"jest-alias-preprocessor": "^1.1.1",
|
||||||
@ -57,12 +57,12 @@
|
|||||||
"jest-snapshot": "^21.2.1",
|
"jest-snapshot": "^21.2.1",
|
||||||
"jest-styled-components": "^4.9.0",
|
"jest-styled-components": "^4.9.0",
|
||||||
"jest-transform-graphql": "^2.1.0",
|
"jest-transform-graphql": "^2.1.0",
|
||||||
"joyent-react-scripts": "^2.6.0",
|
"joyent-react-scripts": "^3.1.0",
|
||||||
"lodash.sortby": "^4.7.0",
|
"lodash.sortby": "^4.7.0",
|
||||||
"mz": "^2.7.0",
|
"mz": "^2.7.0",
|
||||||
"react-scripts": "^1.0.14",
|
"react-scripts": "^1.0.17",
|
||||||
"react-test-renderer": "^16.0.0",
|
"react-test-renderer": "^16.1.1",
|
||||||
"redrun": "^5.9.18",
|
"redrun": "^5.10.0",
|
||||||
"stylelint": "^8.2.0",
|
"stylelint": "^8.2.0",
|
||||||
"stylelint-config-joyent-portal": "^2.0.1"
|
"stylelint-config-joyent-portal": "^2.0.1"
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user