mirror of
https://github.com/yldio/copilot.git
synced 2024-11-28 14:10:04 +02:00
feat(my-joyent): fetch packages and implement filters
This commit is contained in:
parent
3b427871cf
commit
884db125e0
@ -7,7 +7,8 @@
|
|||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "eslint . --fix",
|
"lint": "eslint . --fix",
|
||||||
"lint-ci": "eslint . --format junit --output-file $CIRCLE_TEST_REPORTS/lint/cloudapi-gql.xml",
|
"lint-ci":
|
||||||
|
"eslint . --format junit --output-file $CIRCLE_TEST_REPORTS/lint/cloudapi-gql.xml",
|
||||||
"test": "echo 0",
|
"test": "echo 0",
|
||||||
"test-ci": "echo 0",
|
"test-ci": "echo 0",
|
||||||
"start": "node src/index.js",
|
"start": "node src/index.js",
|
||||||
@ -15,6 +16,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bunyan": "^1.8.12",
|
"bunyan": "^1.8.12",
|
||||||
|
"cors": "^2.8.4",
|
||||||
"dotenv": "^4.0.0",
|
"dotenv": "^4.0.0",
|
||||||
"express": "^4.15.4",
|
"express": "^4.15.4",
|
||||||
"express-graphql": "^0.6.11",
|
"express-graphql": "^0.6.11",
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
const express = require('express');
|
const express = require('express');
|
||||||
|
const cors = require('cors');
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
|
app.use(cors());
|
||||||
|
app.options('*', cors());
|
||||||
|
|
||||||
app.use('/graphql', require('./endpoint'));
|
app.use('/graphql', require('./endpoint'));
|
||||||
|
|
||||||
const server = app.listen(4000, err => {
|
const server = app.listen(4000, err => {
|
||||||
|
0
packages/my-joyent/.yarnclean
Normal file
0
packages/my-joyent/.yarnclean
Normal file
@ -6,7 +6,7 @@
|
|||||||
"main": "build/",
|
"main": "build/",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev":
|
"dev":
|
||||||
"REACT_APP_GQL_PORT=3000 PORT=3069 REACT_APP_GQL_PROTOCOL=http react-scripts start",
|
"REACT_APP_GQL_PORT=4000 PORT=3069 REACT_APP_GQL_PROTOCOL=http react-scripts start",
|
||||||
"start": "PORT=3069 react-scripts start",
|
"start": "PORT=3069 react-scripts start",
|
||||||
"build": "NODE_ENV=production react-scripts build",
|
"build": "NODE_ENV=production react-scripts build",
|
||||||
"lint:css": "stylelint './src/**/*.js'",
|
"lint:css": "stylelint './src/**/*.js'",
|
||||||
@ -18,7 +18,8 @@
|
|||||||
"lint-ci": "redrun -p lint-ci:*",
|
"lint-ci": "redrun -p lint-ci:*",
|
||||||
"test": "NODE_ENV=test ./test/run --env=jsdom",
|
"test": "NODE_ENV=test ./test/run --env=jsdom",
|
||||||
"test-ci":
|
"test-ci":
|
||||||
"echo 0 `# NODE_ENV=test JEST_JUNIT_OUTPUT=$CIRCLE_TEST_REPORTS/test/cp-frontend.xml ./test/run --env=jsdom --coverage --coverageDirectory=$CIRCLE_ARTIFACTS/cp-frontend --testResultsProcessor=$(node -e \"console.log(require.resolve('jest-junit'))\")`"
|
"echo 0 `# NODE_ENV=test JEST_JUNIT_OUTPUT=$CIRCLE_TEST_REPORTS/test/cp-frontend.xml ./test/run --env=jsdom --coverage --coverageDirectory=$CIRCLE_ARTIFACTS/cp-frontend --testResultsProcessor=$(node -e \"console.log(require.resolve('jest-junit'))\")`",
|
||||||
|
"prepublish": "node scripts/postinstall"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"apollo": "^0.2.2",
|
"apollo": "^0.2.2",
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* @jest-environment jsdom
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import renderer from 'react-test-renderer';
|
||||||
|
import 'jest-styled-components';
|
||||||
|
|
||||||
|
import { Router, FiltersMock } from '@mocks/';
|
||||||
|
import Filters from '../filters';
|
||||||
|
|
||||||
|
it('renders <Filters /> without throwing', () => {
|
||||||
|
const tree = renderer
|
||||||
|
.create(
|
||||||
|
<Router>
|
||||||
|
<Filters filters={FiltersMock} />
|
||||||
|
</Router>
|
||||||
|
)
|
||||||
|
.toJSON();
|
||||||
|
expect(tree).toMatchSnapshot();
|
||||||
|
});
|
72
packages/my-joyent/src/components/filters/filters.js
Normal file
72
packages/my-joyent/src/components/filters/filters.js
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Col } from 'react-styled-flexboxgrid';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import remcalc from 'remcalc';
|
||||||
|
import { Slider, FormLabel } from 'joyent-ui-toolkit';
|
||||||
|
|
||||||
|
const FilterWrapper = styled.section`
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
margin-right: ${remcalc(36)};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Filters = ({
|
||||||
|
filters: { cpu, cost, ram, disk },
|
||||||
|
ramSliderChange,
|
||||||
|
cpuSliderChange,
|
||||||
|
diskSliderChange,
|
||||||
|
costSliderChange
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<Col xs={12}>
|
||||||
|
<FormLabel>Choose a package</FormLabel>
|
||||||
|
<FilterWrapper>
|
||||||
|
<Slider
|
||||||
|
minValue={ram.min}
|
||||||
|
maxValue={ram.max}
|
||||||
|
step={0.256}
|
||||||
|
value={ram}
|
||||||
|
onChangeComplete={value => ramSliderChange(value)}
|
||||||
|
>
|
||||||
|
GB RAM
|
||||||
|
</Slider>
|
||||||
|
<Slider
|
||||||
|
minValue={cpu.min}
|
||||||
|
maxValue={cpu.max}
|
||||||
|
step={0.25}
|
||||||
|
value={cpu}
|
||||||
|
onChangeComplete={value => cpuSliderChange(value)}
|
||||||
|
>
|
||||||
|
vCPUs
|
||||||
|
</Slider>
|
||||||
|
<Slider
|
||||||
|
minValue={disk.min}
|
||||||
|
maxValue={disk.max}
|
||||||
|
step={0.01}
|
||||||
|
value={disk}
|
||||||
|
onChangeComplete={value => diskSliderChange(value)}
|
||||||
|
>
|
||||||
|
TB Disk
|
||||||
|
</Slider>
|
||||||
|
<Slider
|
||||||
|
minValue={cost.min}
|
||||||
|
maxValue={cost.max}
|
||||||
|
step={0.02}
|
||||||
|
value={cost}
|
||||||
|
onChangeComplete={value => costSliderChange(value)}
|
||||||
|
>
|
||||||
|
$/hr
|
||||||
|
</Slider>
|
||||||
|
</FilterWrapper>
|
||||||
|
</Col>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Filters;
|
1
packages/my-joyent/src/components/filters/index.js
Normal file
1
packages/my-joyent/src/components/filters/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default as Filters } from './filters';
|
File diff suppressed because it is too large
Load Diff
@ -3,19 +3,19 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import renderer from 'react-test-renderer';
|
import ShallowRenderer from 'react-test-renderer/shallow';
|
||||||
import 'jest-styled-components';
|
import 'jest-styled-components';
|
||||||
|
|
||||||
import { Router } from '@mocks/';
|
import { Router, FiltersMock } from '@mocks/';
|
||||||
import Home from '../home';
|
import Home from '../home';
|
||||||
|
|
||||||
it('renders <Home /> without throwing', () => {
|
it('renders <Home /> without throwing', () => {
|
||||||
const tree = renderer
|
const renderer = new ShallowRenderer();
|
||||||
.create(
|
renderer.render(
|
||||||
<Router>
|
<Router>
|
||||||
<Home />
|
<Home filters={FiltersMock} />
|
||||||
</Router>
|
</Router>
|
||||||
)
|
);
|
||||||
.toJSON();
|
const tree = renderer.getRenderOutput();
|
||||||
expect(tree).toMatchSnapshot();
|
expect(tree).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { Row } from 'react-styled-flexboxgrid';
|
import { Row } from 'react-styled-flexboxgrid';
|
||||||
import { SectionNav } from '@components/navigation';
|
import { SectionNav } from '@components/navigation';
|
||||||
|
import { Filters } from '@components/filters';
|
||||||
|
import PackagesHOC from '@containers/packages';
|
||||||
import { Message, Breadcrumb, BreadcrumbItem, Anchor } from 'joyent-ui-toolkit';
|
import { Message, Breadcrumb, BreadcrumbItem, Anchor } from 'joyent-ui-toolkit';
|
||||||
|
|
||||||
class Home extends Component {
|
class Home extends Component {
|
||||||
@ -12,6 +14,7 @@ class Home extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.closeMessage = this.closeMessage.bind(this);
|
this.closeMessage = this.closeMessage.bind(this);
|
||||||
|
this.changeValue = this.changeValue.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
closeMessage() {
|
closeMessage() {
|
||||||
@ -20,8 +23,18 @@ class Home extends Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
changeValue(key, value) {
|
||||||
|
const filters = this.props.filters;
|
||||||
|
this.props.onFilterChange({
|
||||||
|
...filters,
|
||||||
|
[key]: value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const _msg = this.state.showMessage ? (
|
const { showMessage } = this.state;
|
||||||
|
const { filters } = this.props;
|
||||||
|
const _msg = showMessage ? (
|
||||||
<Message
|
<Message
|
||||||
title="Choosing deployement data center"
|
title="Choosing deployement data center"
|
||||||
onCloseClick={this.closeMessage}
|
onCloseClick={this.closeMessage}
|
||||||
@ -33,6 +46,7 @@ class Home extends Component {
|
|||||||
</p>
|
</p>
|
||||||
</Message>
|
</Message>
|
||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main>
|
<main>
|
||||||
<SectionNav />
|
<SectionNav />
|
||||||
@ -41,6 +55,18 @@ class Home extends Component {
|
|||||||
<BreadcrumbItem>Create Instance</BreadcrumbItem>
|
<BreadcrumbItem>Create Instance</BreadcrumbItem>
|
||||||
</Breadcrumb>
|
</Breadcrumb>
|
||||||
<Row>{_msg}</Row>
|
<Row>{_msg}</Row>
|
||||||
|
<Row>
|
||||||
|
<Filters
|
||||||
|
filters={filters}
|
||||||
|
ramSliderChange={value => this.changeValue('ram', value)}
|
||||||
|
cpuSliderChange={value => this.changeValue('cpu', value)}
|
||||||
|
diskSliderChange={value => this.changeValue('disk', value)}
|
||||||
|
costSliderChange={value => this.changeValue('cost', value)}
|
||||||
|
/>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<PackagesHOC />
|
||||||
|
</Row>
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* @jest-environment jsdom
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import renderer from 'react-test-renderer';
|
||||||
|
import 'jest-styled-components';
|
||||||
|
|
||||||
|
import { Router, PackagesMock, FiltersMock } from '@mocks/';
|
||||||
|
import { Packages } from '../';
|
||||||
|
|
||||||
|
it('renders <Packages /> without throwing', () => {
|
||||||
|
const tree = renderer
|
||||||
|
.create(
|
||||||
|
<Router>
|
||||||
|
<Packages packages={PackagesMock} filters={FiltersMock} />
|
||||||
|
</Router>
|
||||||
|
)
|
||||||
|
.toJSON();
|
||||||
|
expect(tree).toMatchSnapshot();
|
||||||
|
});
|
1
packages/my-joyent/src/components/packages/index.js
Normal file
1
packages/my-joyent/src/components/packages/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default as Packages } from './list';
|
80
packages/my-joyent/src/components/packages/list.js
Normal file
80
packages/my-joyent/src/components/packages/list.js
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import remcalc from 'remcalc';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardSubTitle,
|
||||||
|
CardTitle,
|
||||||
|
CardView,
|
||||||
|
CardFooter,
|
||||||
|
CardMeta
|
||||||
|
} from 'joyent-ui-toolkit';
|
||||||
|
|
||||||
|
const ListStyled = styled.ul`
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-top: ${remcalc(36)};
|
||||||
|
`;
|
||||||
|
class Packages extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
const { filters: { ram, cpu, cost, disk }, packages } = props;
|
||||||
|
this.state = {
|
||||||
|
ram,
|
||||||
|
cpu,
|
||||||
|
cost,
|
||||||
|
disk,
|
||||||
|
packages
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
const { filters: { ram, cpu, cost, disk }, packages } = nextProps;
|
||||||
|
this.setState({
|
||||||
|
ram,
|
||||||
|
cpu,
|
||||||
|
cost,
|
||||||
|
disk,
|
||||||
|
packages: packages
|
||||||
|
.filter(pack => pack.memory >= ram.min && pack.memory <= ram.max)
|
||||||
|
.filter(
|
||||||
|
pack => pack.disk / 1000 >= disk.min && pack.disk / 1000 <= disk.max
|
||||||
|
)
|
||||||
|
.filter(pack => pack.vcpus >= cpu.min && pack.vcpus <= cpu.max)
|
||||||
|
.filter(pack => pack.price >= cost.min && pack.price <= cost.max)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_packages() {
|
||||||
|
return (
|
||||||
|
<ListStyled>
|
||||||
|
{this.state.packages.map(pack => (
|
||||||
|
<li>
|
||||||
|
<Card transparent>
|
||||||
|
<CardView>
|
||||||
|
<CardMeta>
|
||||||
|
<CardTitle>${pack.price} per hour</CardTitle>
|
||||||
|
<CardSubTitle>{pack.memory} GB RAM</CardSubTitle>
|
||||||
|
<CardSubTitle>{pack.vcpus} vCPUs</CardSubTitle>
|
||||||
|
<CardSubTitle>{pack.disk / 100} TB disk</CardSubTitle>
|
||||||
|
<CardSubTitle>SSD</CardSubTitle>
|
||||||
|
|
||||||
|
<CardFooter>{pack.group}</CardFooter>
|
||||||
|
</CardMeta>
|
||||||
|
</CardView>
|
||||||
|
</Card>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ListStyled>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
return this._packages();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Packages;
|
File diff suppressed because it is too large
Load Diff
@ -7,13 +7,15 @@ import renderer from 'react-test-renderer';
|
|||||||
import 'jest-styled-components';
|
import 'jest-styled-components';
|
||||||
|
|
||||||
import HomeHOC from '../';
|
import HomeHOC from '../';
|
||||||
import { Router } from '@mocks/';
|
import { Router, Store } from '@mocks/';
|
||||||
|
|
||||||
it('renders <HomeHOC /> without throwing', () => {
|
it('renders <HomeHOC /> without throwing', () => {
|
||||||
const tree = renderer
|
const tree = renderer
|
||||||
.create(
|
.create(
|
||||||
<Router>
|
<Router>
|
||||||
|
<Store>
|
||||||
<HomeHOC />
|
<HomeHOC />
|
||||||
|
</Store>
|
||||||
</Router>
|
</Router>
|
||||||
)
|
)
|
||||||
.toJSON();
|
.toJSON();
|
||||||
|
@ -1,11 +1,27 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import changeFilters from '../../state/actions';
|
||||||
import { LayoutContainer } from '@components/layout';
|
import { LayoutContainer } from '@components/layout';
|
||||||
import { Home } from '@components/home';
|
import { Home } from '@components/home';
|
||||||
|
|
||||||
const HomeHOC = () => (
|
const HomeHOC = ({ filters, onFilterChange }) => (
|
||||||
<LayoutContainer>
|
<LayoutContainer>
|
||||||
<Home />
|
<Home filters={filters} onFilterChange={onFilterChange} />
|
||||||
</LayoutContainer>
|
</LayoutContainer>
|
||||||
);
|
);
|
||||||
|
|
||||||
export default HomeHOC;
|
const mapStateToProps = state => {
|
||||||
|
return {
|
||||||
|
filters: state.filters
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => {
|
||||||
|
return {
|
||||||
|
onFilterChange: filters => {
|
||||||
|
dispatch(changeFilters(filters));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(HomeHOC);
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* @jest-environment jsdom
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import renderer from 'react-test-renderer';
|
||||||
|
import 'jest-styled-components';
|
||||||
|
|
||||||
|
import PackagesHOC from '../';
|
||||||
|
import { Router, Store } from '@mocks/';
|
||||||
|
|
||||||
|
it('renders <PackagesHOC /> without throwing', () => {
|
||||||
|
const tree = renderer
|
||||||
|
.create(
|
||||||
|
<Router>
|
||||||
|
<Store>
|
||||||
|
<PackagesHOC />
|
||||||
|
</Store>
|
||||||
|
</Router>
|
||||||
|
)
|
||||||
|
.toJSON();
|
||||||
|
expect(tree).toMatchSnapshot();
|
||||||
|
});
|
25
packages/my-joyent/src/containers/packages/index.js
Normal file
25
packages/my-joyent/src/containers/packages/index.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
// import { graphql } from 'react-apollo';
|
||||||
|
import { Packages } from '@components/packages';
|
||||||
|
// import packagesQuery from '@graphql/packages.gql';
|
||||||
|
import data from '../../data/packages';
|
||||||
|
|
||||||
|
const PackagesHOC = ({ packages, filters }) => (
|
||||||
|
<Packages packages={data} filters={filters} />
|
||||||
|
);
|
||||||
|
|
||||||
|
// const PackagesHOCWithData = graphql(packagesQuery, {
|
||||||
|
// props: ({ data: { packages = [], loading = true } }) => ({
|
||||||
|
// packages,
|
||||||
|
// loading
|
||||||
|
// })
|
||||||
|
// })(PackagesHOC);
|
||||||
|
|
||||||
|
const mapStateToProps = state => {
|
||||||
|
return {
|
||||||
|
filters: state.filters
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(mapStateToProps)(PackagesHOC);
|
146
packages/my-joyent/src/data/packages.json
Normal file
146
packages/my-joyent/src/data/packages.json
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "High CPU 0.25",
|
||||||
|
"vcpus": 0.25,
|
||||||
|
"memory": 0.256,
|
||||||
|
"disk": 10,
|
||||||
|
"group": "Compute Optimized",
|
||||||
|
"price": "0.016"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "High CPU 0.75",
|
||||||
|
"vcpus": 0.5,
|
||||||
|
"memory": 0.768,
|
||||||
|
"disk": 25,
|
||||||
|
"group": "Compute Optimized",
|
||||||
|
"price": "0.033"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "High CPU 1.75",
|
||||||
|
"vcpus": 1,
|
||||||
|
"memory": 1.8,
|
||||||
|
"disk": 50,
|
||||||
|
"group": "Compute Optimized",
|
||||||
|
"price": "0.066"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "High CPU 3.75",
|
||||||
|
"vcpus": 2,
|
||||||
|
"memory": 3.8,
|
||||||
|
"disk": 100,
|
||||||
|
"group": "Compute Optimized",
|
||||||
|
"price": "0.131"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "High CPU 7.75",
|
||||||
|
"vcpus": 4,
|
||||||
|
"memory": 7.8,
|
||||||
|
"disk": 200,
|
||||||
|
"group": "Compute Optimized",
|
||||||
|
"price": "0.263"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "High CPU 15.75",
|
||||||
|
"vcpus": 8,
|
||||||
|
"memory": 15.8,
|
||||||
|
"disk": 400,
|
||||||
|
"group": "Compute Optimized",
|
||||||
|
"price": "0.525"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "General 3.75",
|
||||||
|
"vcpus": 1,
|
||||||
|
"memory": 3.8,
|
||||||
|
"disk": 50,
|
||||||
|
"group": "General Purpose",
|
||||||
|
"price": "0.084"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "General 7.75",
|
||||||
|
"vcpus": 2,
|
||||||
|
"memory": 7.8,
|
||||||
|
"disk": 100,
|
||||||
|
"group": "General Purpose",
|
||||||
|
"price": "0.166"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "General 15.75",
|
||||||
|
"vcpus": 4,
|
||||||
|
"memory": 15.8,
|
||||||
|
"disk": 200,
|
||||||
|
"group": "General Purpose",
|
||||||
|
"price": "0.333"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "General 31.75",
|
||||||
|
"vcpus": 8,
|
||||||
|
"memory": 31.8,
|
||||||
|
"disk": 400,
|
||||||
|
"group": "General Purpose",
|
||||||
|
"price": "0.665"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "High RAM 15.75",
|
||||||
|
"vcpus": 2,
|
||||||
|
"memory": 15.8,
|
||||||
|
"disk": 100,
|
||||||
|
"group": "Memory Optimized",
|
||||||
|
"price": "0.259"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "High RAM 31.75",
|
||||||
|
"vcpus": 4,
|
||||||
|
"memory": 31.8,
|
||||||
|
"disk": 200,
|
||||||
|
"group": "Memory Optimized",
|
||||||
|
"price": "0.52"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "High RAM 63.75",
|
||||||
|
"vcpus": 8,
|
||||||
|
"memory": 63.8,
|
||||||
|
"disk": 400,
|
||||||
|
"group": "Memory Optimized",
|
||||||
|
"price": "1.039"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Fast Disk 31.75",
|
||||||
|
"vcpus": 4,
|
||||||
|
"memory": 31.8,
|
||||||
|
"disk": 800,
|
||||||
|
"group": "Storage Optimized",
|
||||||
|
"price": "1.066"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Fast Disk 63.75",
|
||||||
|
"vcpus": 8,
|
||||||
|
"memory": 63.8,
|
||||||
|
"disk": 1600,
|
||||||
|
"group": "Storage Optimized",
|
||||||
|
"price": "2.31"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Big Disk 15.75",
|
||||||
|
"vcpus": 2,
|
||||||
|
"memory": 15.8,
|
||||||
|
"disk": 1200,
|
||||||
|
"group": "Storage Optimized",
|
||||||
|
"price": "0.413"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Big Disk 31.75",
|
||||||
|
"vcpus": 4,
|
||||||
|
"memory": 31.8,
|
||||||
|
"disk": 2400,
|
||||||
|
"group": "Storage Optimized",
|
||||||
|
"price": "0.825"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Big Disk 63.75",
|
||||||
|
"vcpus": 8,
|
||||||
|
"memory": 63.8,
|
||||||
|
"disk": 4900,
|
||||||
|
"group": "Storage Optimized",
|
||||||
|
"price": "1.75"
|
||||||
|
}
|
||||||
|
]
|
5
packages/my-joyent/src/graphql/data-centers.gql
Normal file
5
packages/my-joyent/src/graphql/data-centers.gql
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
query Portal {
|
||||||
|
datacenters {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
10
packages/my-joyent/src/graphql/packages.gql
Normal file
10
packages/my-joyent/src/graphql/packages.gql
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
query Portal {
|
||||||
|
packages {
|
||||||
|
name
|
||||||
|
vcpus
|
||||||
|
memory
|
||||||
|
disk
|
||||||
|
version
|
||||||
|
group
|
||||||
|
}
|
||||||
|
}
|
8
packages/my-joyent/src/state/actions.js
Normal file
8
packages/my-joyent/src/state/actions.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
const changeFilters = filters => {
|
||||||
|
return {
|
||||||
|
type: 'CHANGE_FILTERS',
|
||||||
|
filters
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default changeFilters;
|
13
packages/my-joyent/src/state/filterReducer.js
Normal file
13
packages/my-joyent/src/state/filterReducer.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
const filterReducer = (state = [], action) => {
|
||||||
|
switch (action.type) {
|
||||||
|
case 'CHANGE_FILTERS':
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
...action.filters
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default filterReducer;
|
@ -1,3 +1,10 @@
|
|||||||
const state = {};
|
const state = {
|
||||||
|
filters: {
|
||||||
|
cpu: { min: 0.25, max: 8 },
|
||||||
|
cost: { min: 0.016, max: 2.131 },
|
||||||
|
ram: { min: 0.256, max: 63.8 },
|
||||||
|
disk: { min: 0.01, max: 4.9 }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export default state;
|
export default state;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
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 filterReducer from './filterReducer';
|
||||||
import state from './state';
|
import state from './state';
|
||||||
|
|
||||||
const GLOBAL =
|
const GLOBAL =
|
||||||
@ -37,14 +38,15 @@ export const client = new ApolloClient({
|
|||||||
return `${o.__typename}:${id}`;
|
return `${o.__typename}:${id}`;
|
||||||
},
|
},
|
||||||
networkInterface: createNetworkInterface({
|
networkInterface: createNetworkInterface({
|
||||||
uri: `${GQL_PROTOCOL}://${GQL_HOSTNAME}:${GQL_PORT}/api/graphql`
|
uri: `${GQL_PROTOCOL}://${GQL_HOSTNAME}:${GQL_PORT}/graphql`
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
export const store = createStore(
|
export const store = createStore(
|
||||||
combineReducers({
|
combineReducers({
|
||||||
apollo: client.reducer(),
|
apollo: client.reducer(),
|
||||||
form: formReducer
|
form: formReducer,
|
||||||
|
filters: filterReducer
|
||||||
}),
|
}),
|
||||||
state, // Initial state
|
state, // Initial state
|
||||||
compose(
|
compose(
|
||||||
|
18
packages/my-joyent/test/mocks/filters.js
Normal file
18
packages/my-joyent/test/mocks/filters.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
export default {
|
||||||
|
cpu: {
|
||||||
|
min: 0.25,
|
||||||
|
max: 3.25
|
||||||
|
},
|
||||||
|
cost: {
|
||||||
|
min: 0.016,
|
||||||
|
max: 0.525
|
||||||
|
},
|
||||||
|
ram: {
|
||||||
|
min: 0.256,
|
||||||
|
max: 50.688
|
||||||
|
},
|
||||||
|
disk: {
|
||||||
|
min: 0.01,
|
||||||
|
max: 107.26
|
||||||
|
}
|
||||||
|
};
|
@ -1,3 +1,5 @@
|
|||||||
export { default as Router } from './router';
|
export { default as Router } from './router';
|
||||||
export { default as Store } from './store';
|
export { default as Store } from './store';
|
||||||
|
export { default as PackagesMock } from './packages';
|
||||||
|
export { default as FiltersMock } from './filters';
|
||||||
export { default as Theme } from './theme';
|
export { default as Theme } from './theme';
|
||||||
|
50
packages/my-joyent/test/mocks/packages.js
Normal file
50
packages/my-joyent/test/mocks/packages.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
export default [
|
||||||
|
{
|
||||||
|
name: 'High CPU 0.25',
|
||||||
|
vcpus: 0.25,
|
||||||
|
memory: 0.256,
|
||||||
|
disk: 10,
|
||||||
|
group: 'Compute Optimized',
|
||||||
|
price: '0.016'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'High CPU 0.75',
|
||||||
|
vcpus: 0.5,
|
||||||
|
memory: 0.768,
|
||||||
|
disk: 25,
|
||||||
|
group: 'Compute Optimized',
|
||||||
|
price: '0.033'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'High CPU 1.75',
|
||||||
|
vcpus: 1,
|
||||||
|
memory: 1.8,
|
||||||
|
disk: 50,
|
||||||
|
group: 'Compute Optimized',
|
||||||
|
price: '0.066'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'High CPU 3.75',
|
||||||
|
vcpus: 2,
|
||||||
|
memory: 3.8,
|
||||||
|
disk: 100,
|
||||||
|
group: 'Compute Optimized',
|
||||||
|
price: '0.131'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'High CPU 7.75',
|
||||||
|
vcpus: 4,
|
||||||
|
memory: 7.8,
|
||||||
|
disk: 200,
|
||||||
|
group: 'Compute Optimized',
|
||||||
|
price: '0.263'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'High CPU 15.75',
|
||||||
|
vcpus: 8,
|
||||||
|
memory: 15.8,
|
||||||
|
disk: 400,
|
||||||
|
group: 'Compute Optimized',
|
||||||
|
price: '0.525'
|
||||||
|
}
|
||||||
|
];
|
@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"extends": "joyent-portal",
|
"extends": "joyent-portal",
|
||||||
"rules": {
|
"rules": {
|
||||||
"new-cap": 0
|
"new-cap": 0,
|
||||||
|
"jsx-a11y/href-no-hash": 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
0
packages/ui-toolkit/.yarnclean
Normal file
0
packages/ui-toolkit/.yarnclean
Normal file
@ -10,16 +10,22 @@
|
|||||||
"lint:css": "echo 0",
|
"lint:css": "echo 0",
|
||||||
"lint-ci:css": "echo 0",
|
"lint-ci:css": "echo 0",
|
||||||
"lint:js": "eslint . --fix",
|
"lint:js": "eslint . --fix",
|
||||||
"lint-ci:js": "eslint . --format junit --output-file $CIRCLE_TEST_REPORTS/lint/ui-toolkit.xml",
|
"lint-ci:js":
|
||||||
|
"eslint . --format junit --output-file $CIRCLE_TEST_REPORTS/lint/ui-toolkit.xml",
|
||||||
"lint": "redrun -s lint:*",
|
"lint": "redrun -s lint:*",
|
||||||
"lint-ci": "redrun -s lint-ci:*",
|
"lint-ci": "redrun -s lint-ci:*",
|
||||||
"test": "echo 0",
|
"test": "echo 0",
|
||||||
"test-ci": "echo 0",
|
"test-ci": "echo 0",
|
||||||
"copy-fonts": "rm -rf dist; mkdir -p dist/es/typography; mkdir -p dist/umd/typography; cp -r src/typography/libre-franklin dist/es/typography; cp -r src/typography/libre-franklin dist/umd/typography",
|
"copy-fonts":
|
||||||
"compile-watch:es": "NODE_ENV=development babel src --out-dir dist/es --source-maps inline --watch",
|
"rm -rf dist; mkdir -p dist/es/typography; mkdir -p dist/umd/typography; cp -r src/typography/libre-franklin dist/es/typography; cp -r src/typography/libre-franklin dist/umd/typography",
|
||||||
"compile:es": "NODE_ENV=development babel src --out-dir dist/es --source-maps inline",
|
"compile-watch:es":
|
||||||
"compile:umd": "cross-env NODE_ENV=test babel src --out-dir dist/umd --source-maps inline",
|
"NODE_ENV=development babel src --out-dir dist/es --source-maps inline --watch",
|
||||||
"compile-watch:umd": "cross-env NODE_ENV=test babel src --out-dir dist/umd --source-maps inline --watch",
|
"compile:es":
|
||||||
|
"NODE_ENV=development babel src --out-dir dist/es --source-maps inline",
|
||||||
|
"compile:umd":
|
||||||
|
"cross-env NODE_ENV=test babel src --out-dir dist/umd --source-maps inline",
|
||||||
|
"compile-watch:umd":
|
||||||
|
"cross-env NODE_ENV=test babel src --out-dir dist/umd --source-maps inline --watch",
|
||||||
"compile": "redrun -p compile:*",
|
"compile": "redrun -p compile:*",
|
||||||
"watch": "redrun copy-fonts && redrun -p compile-watch:*",
|
"watch": "redrun copy-fonts && redrun -p compile-watch:*",
|
||||||
"styleguide:build": "cross-env NODE_ENV=production styleguidist build",
|
"styleguide:build": "cross-env NODE_ENV=production styleguidist build",
|
||||||
@ -47,6 +53,7 @@
|
|||||||
"polished": "^1.6.1",
|
"polished": "^1.6.1",
|
||||||
"prop-types": "^15.5.10",
|
"prop-types": "^15.5.10",
|
||||||
"react-broadcast": "^0.1.2",
|
"react-broadcast": "^0.1.2",
|
||||||
|
"react-input-range": "^1.2.1",
|
||||||
"react-styled-flexboxgrid": "^2.0.3",
|
"react-styled-flexboxgrid": "^2.0.3",
|
||||||
"redrun": "^5.9.16",
|
"redrun": "^5.9.16",
|
||||||
"reduce-css-calc": "^2.0.5",
|
"reduce-css-calc": "^2.0.5",
|
||||||
@ -81,7 +88,8 @@
|
|||||||
"stylelint": "^8.0.0",
|
"stylelint": "^8.0.0",
|
||||||
"stylelint-config-primer": "^2.0.1",
|
"stylelint-config-primer": "^2.0.1",
|
||||||
"stylelint-config-standard": "^17.0.0",
|
"stylelint-config-standard": "^17.0.0",
|
||||||
"stylelint-processor-styled-components": "styled-components/stylelint-processor-styled-components#2a33b5f",
|
"stylelint-processor-styled-components":
|
||||||
|
"styled-components/stylelint-processor-styled-components#2a33b5f",
|
||||||
"svgo": "^0.7.2",
|
"svgo": "^0.7.2",
|
||||||
"tinycolor2": "^1.4.1",
|
"tinycolor2": "^1.4.1",
|
||||||
"title-case": "^2.1.1",
|
"title-case": "^2.1.1",
|
||||||
|
@ -35,6 +35,16 @@ const StyledCard = Row.extend`
|
|||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
`};
|
`};
|
||||||
|
|
||||||
|
${is('transparent')`
|
||||||
|
border-radius: ${remcalc(4)}
|
||||||
|
background:
|
||||||
|
border: 1px solid ${props => props.theme.grey};
|
||||||
|
background: ${props => props.theme.background};
|
||||||
|
box-shadow: 0px 2px 0px rgba(0, 0, 0, 0.05);
|
||||||
|
min-height: ${remcalc(185)};
|
||||||
|
min-width: 200px;
|
||||||
|
`};
|
||||||
|
|
||||||
${is('stacked')`
|
${is('stacked')`
|
||||||
${paperEffect}
|
${paperEffect}
|
||||||
`};
|
`};
|
||||||
@ -85,7 +95,8 @@ Card.propTypes = {
|
|||||||
collapsed: PropTypes.bool,
|
collapsed: PropTypes.bool,
|
||||||
headed: PropTypes.bool,
|
headed: PropTypes.bool,
|
||||||
flat: PropTypes.bool,
|
flat: PropTypes.bool,
|
||||||
stacked: PropTypes.bool
|
stacked: PropTypes.bool,
|
||||||
|
transparent: PropTypes.bool
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Baseline(Card);
|
export default Baseline(Card);
|
||||||
|
44
packages/ui-toolkit/src/card/footer.js
Normal file
44
packages/ui-toolkit/src/card/footer.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { Subscriber } from 'react-broadcast';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import Baseline from '../baseline';
|
||||||
|
import typography from '../typography';
|
||||||
|
import remcalc from 'remcalc';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import Title from './title';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const StyledTitle = Title.extend`
|
||||||
|
${typography.fontFamily};
|
||||||
|
${typography.normal};
|
||||||
|
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-basis: ${remcalc(90)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Span = styled.span`
|
||||||
|
display: inline-block;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
${typography.fontFamily};
|
||||||
|
${typography.normal};
|
||||||
|
font-size: ${remcalc(13)};
|
||||||
|
font-weight: 500;
|
||||||
|
text-transform: uppercase;
|
||||||
|
|
||||||
|
color: rgba(73, 73, 73, 0.5);
|
||||||
|
`;
|
||||||
|
const Footer = ({ children }) => {
|
||||||
|
const render = () => (
|
||||||
|
<StyledTitle name="card-footer">
|
||||||
|
<Span>{children}</Span>
|
||||||
|
</StyledTitle>
|
||||||
|
);
|
||||||
|
|
||||||
|
return <Subscriber channel="card">{render}</Subscriber>;
|
||||||
|
};
|
||||||
|
|
||||||
|
Footer.propTypes = {
|
||||||
|
children: PropTypes.node
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Baseline(Footer);
|
@ -9,3 +9,4 @@ export { default as CardSubTitle } from './subtitle.js';
|
|||||||
export { default as CardTitle } from './title.js';
|
export { default as CardTitle } from './title.js';
|
||||||
export { default as CardView } from './view.js';
|
export { default as CardView } from './view.js';
|
||||||
export { default as CardInfo } from './info.js';
|
export { default as CardInfo } from './info.js';
|
||||||
|
export { default as CardFooter } from './footer.js';
|
||||||
|
@ -52,6 +52,36 @@ const {
|
|||||||
</Card>
|
</Card>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### `transparent`
|
||||||
|
```
|
||||||
|
const {
|
||||||
|
CardDescription,
|
||||||
|
CardHeader,
|
||||||
|
CardMeta,
|
||||||
|
CardOptions,
|
||||||
|
CardOutlet,
|
||||||
|
CardSubTitle,
|
||||||
|
CardTitle,
|
||||||
|
CardView,
|
||||||
|
CardGroupView,
|
||||||
|
CardFooter
|
||||||
|
} = require('./');
|
||||||
|
|
||||||
|
<Card transparent>
|
||||||
|
<CardView>
|
||||||
|
<CardMeta>
|
||||||
|
<CardTitle>$0.016 per hour</CardTitle>
|
||||||
|
<CardSubTitle>0.256 GB RAM</CardSubTitle>
|
||||||
|
<CardSubTitle>0.25 vCPUs</CardSubTitle>
|
||||||
|
<CardSubTitle>0.01 TB disk</CardSubTitle>
|
||||||
|
<CardSubTitle>SSD</CardSubTitle>
|
||||||
|
|
||||||
|
<CardFooter>Compute Optimise</CardFooter>
|
||||||
|
</CardMeta>
|
||||||
|
</CardView>
|
||||||
|
</Card>
|
||||||
|
```
|
||||||
|
|
||||||
#### `headed`
|
#### `headed`
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -27,6 +27,7 @@ export {
|
|||||||
export { Dropdown } from './dropdown';
|
export { Dropdown } from './dropdown';
|
||||||
export { default as StatusLoader } from './status-loader';
|
export { default as StatusLoader } from './status-loader';
|
||||||
export { default as Message } from './message';
|
export { default as Message } from './message';
|
||||||
|
export { default as Slider } from './slider';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
default as Progressbar,
|
default as Progressbar,
|
||||||
@ -69,7 +70,8 @@ export {
|
|||||||
CardSubTitle,
|
CardSubTitle,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
CardView,
|
CardView,
|
||||||
CardInfo
|
CardInfo,
|
||||||
|
CardFooter
|
||||||
} from './card';
|
} from './card';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
@ -72,7 +72,7 @@ const Message = ({ title, message, onCloseClick, children, ...type }) => {
|
|||||||
|
|
||||||
Message.propTypes = {
|
Message.propTypes = {
|
||||||
title: PropTypes.string,
|
title: PropTypes.string,
|
||||||
message: PropTypes.string.isRequired,
|
message: PropTypes.string,
|
||||||
onCloseClick: PropTypes.func,
|
onCloseClick: PropTypes.func,
|
||||||
error: PropTypes.boolean,
|
error: PropTypes.boolean,
|
||||||
warning: PropTypes.boolean,
|
warning: PropTypes.boolean,
|
||||||
|
25
packages/ui-toolkit/src/slider/Readme.md
Normal file
25
packages/ui-toolkit/src/slider/Readme.md
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
### Double Range Slider
|
||||||
|
|
||||||
|
```
|
||||||
|
<Slider
|
||||||
|
minValue={0.25}
|
||||||
|
maxValue={8}
|
||||||
|
step={0.25}
|
||||||
|
value={{ min: 0.25, max: 8 }}
|
||||||
|
onChangeComplete={value => console.log(value)}
|
||||||
|
onChange={value => console.log(value)}
|
||||||
|
>vCPUs</Slider>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Normal Slider
|
||||||
|
|
||||||
|
```
|
||||||
|
<Slider
|
||||||
|
minValue={10}
|
||||||
|
maxValue={100}
|
||||||
|
step={5}
|
||||||
|
value={0}
|
||||||
|
onChangeComplete={value => console.log(value)}
|
||||||
|
onChange={value => console.log(value)}
|
||||||
|
>Price</Slider>
|
||||||
|
```
|
147
packages/ui-toolkit/src/slider/index.js
Normal file
147
packages/ui-toolkit/src/slider/index.js
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import InputRange from 'react-input-range';
|
||||||
|
import remcalc from 'remcalc';
|
||||||
|
|
||||||
|
import theme from '../theme';
|
||||||
|
import FormLabel from '../form/label';
|
||||||
|
import {
|
||||||
|
sliderStyles,
|
||||||
|
disabledStyles,
|
||||||
|
trackStyles,
|
||||||
|
activeStyles,
|
||||||
|
rangeStyles
|
||||||
|
} from './inputStyles';
|
||||||
|
|
||||||
|
const SliderStyled = styled.div`
|
||||||
|
.input-range {
|
||||||
|
${rangeStyles};
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider {
|
||||||
|
${sliderStyles};
|
||||||
|
}
|
||||||
|
.disabled {
|
||||||
|
${disabledStyles};
|
||||||
|
}
|
||||||
|
|
||||||
|
.min,
|
||||||
|
.max {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
top: ${remcalc(8)};
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
.label-container {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: ${remcalc(10)};
|
||||||
|
color: ${theme.secondary};
|
||||||
|
left: -50%;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.track {
|
||||||
|
${trackStyles};
|
||||||
|
}
|
||||||
|
|
||||||
|
.active-track {
|
||||||
|
${activeStyles};
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Label = styled(FormLabel)`
|
||||||
|
margin-bottom: ${remcalc(10)};
|
||||||
|
margin-top: ${remcalc(12)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
class Slider extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
minValue: this.props.minValue,
|
||||||
|
maxValue: this.props.maxValue,
|
||||||
|
value: this.props.value
|
||||||
|
};
|
||||||
|
|
||||||
|
this.changeValue = this.changeValue.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
changeValue(value) {
|
||||||
|
this.setState({ value }, () => this.props.onChange(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { minValue, maxValue, value } = this.state;
|
||||||
|
const { children, ...rest } = this.props;
|
||||||
|
return (
|
||||||
|
<SliderStyled>
|
||||||
|
<Label>{children}</Label>
|
||||||
|
<InputRange
|
||||||
|
{...rest}
|
||||||
|
minValue={minValue}
|
||||||
|
maxValue={maxValue}
|
||||||
|
value={value}
|
||||||
|
onChange={value => this.changeValue(value)}
|
||||||
|
/>
|
||||||
|
</SliderStyled>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Slider.propTypes = {
|
||||||
|
minValue: PropTypes.number,
|
||||||
|
maxValue: PropTypes.number,
|
||||||
|
step: PropTypes.number,
|
||||||
|
value: PropTypes.oneOfType([PropTypes.number, PropTypes.shape()]),
|
||||||
|
onChangeComplete: PropTypes.func,
|
||||||
|
onChange: PropTypes.func,
|
||||||
|
formatLabel: PropTypes.func,
|
||||||
|
ariaLabelledby: PropTypes.string,
|
||||||
|
ariaControls: PropTypes.string,
|
||||||
|
classNames: PropTypes.shape({
|
||||||
|
activeTrack: PropTypes.string,
|
||||||
|
disabledInputRange: PropTypes.string,
|
||||||
|
inputRange: PropTypes.string,
|
||||||
|
labelContainer: PropTypes.string,
|
||||||
|
maxLabel: PropTypes.string,
|
||||||
|
minLabel: PropTypes.string,
|
||||||
|
slider: PropTypes.string,
|
||||||
|
sliderContainer: PropTypes.string,
|
||||||
|
track: PropTypes.string,
|
||||||
|
valueLabel: PropTypes.string
|
||||||
|
}),
|
||||||
|
disabled: PropTypes.bool,
|
||||||
|
draggableTrack: PropTypes.bool,
|
||||||
|
onChangeStart: PropTypes.func,
|
||||||
|
children: PropTypes.node
|
||||||
|
};
|
||||||
|
|
||||||
|
Slider.defaultProps = {
|
||||||
|
onChangeComplete: () => {},
|
||||||
|
onChange: () => {},
|
||||||
|
formatLabel: value =>
|
||||||
|
(value.toString().split('.')[1] || []).length > 3
|
||||||
|
? Math.round(value).toFixed(3)
|
||||||
|
: value,
|
||||||
|
onChangeStart: () => {},
|
||||||
|
step: 1,
|
||||||
|
classNames: {
|
||||||
|
activeTrack: 'active-track',
|
||||||
|
disabledInputRange: 'disabled-range',
|
||||||
|
inputRange: 'input-range',
|
||||||
|
labelContainer: 'label-container',
|
||||||
|
maxLabel: 'max',
|
||||||
|
minLabel: 'min',
|
||||||
|
sliderContainer: 'slider-container',
|
||||||
|
track: 'track',
|
||||||
|
valueLabel: 'value',
|
||||||
|
slider: 'slider'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Slider;
|
61
packages/ui-toolkit/src/slider/inputStyles.js
Normal file
61
packages/ui-toolkit/src/slider/inputStyles.js
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import { css } from 'styled-components';
|
||||||
|
import remcalc from 'remcalc';
|
||||||
|
|
||||||
|
import theme from '../theme';
|
||||||
|
|
||||||
|
export const sliderStyles = css`
|
||||||
|
appearance: none;
|
||||||
|
background: ${theme.white};
|
||||||
|
border: 2px solid ${theme.greyLight};
|
||||||
|
border-radius: 50%;
|
||||||
|
cursor: pointer;
|
||||||
|
display: block;
|
||||||
|
height: ${remcalc(14)};
|
||||||
|
width: ${remcalc(14)};
|
||||||
|
transform: translateY(-50%) translateX(-50%);
|
||||||
|
outline: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
margin-top: ${remcalc(2)};
|
||||||
|
|
||||||
|
&::active {
|
||||||
|
transform: scale(1.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::focus {
|
||||||
|
box-shadow: 0 0 0 5px rgba(63, 81, 181, 0.2);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const disabledStyles = css`
|
||||||
|
.track {
|
||||||
|
background: ${theme.grey};
|
||||||
|
}
|
||||||
|
.slider {
|
||||||
|
background: ${theme.greyDark};
|
||||||
|
border: 1px solid ${theme.greyDark};
|
||||||
|
box-shadow: none;
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const trackStyles = css`
|
||||||
|
background: ${theme.grey};
|
||||||
|
cursor: pointer;
|
||||||
|
display: block;
|
||||||
|
height: ${remcalc(4)};
|
||||||
|
position: relative;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const activeStyles = css`
|
||||||
|
background: ${theme.blue};
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const rangeStyles = css`
|
||||||
|
position: relative;
|
||||||
|
width: calc(100% - 18px);
|
||||||
|
margin: auto;
|
||||||
|
min-height: ${remcalc(10)};
|
||||||
|
`;
|
@ -50,6 +50,8 @@ export const base = {
|
|||||||
...tertiary,
|
...tertiary,
|
||||||
text: '#494949', // used
|
text: '#494949', // used
|
||||||
grey: '#D8D8D8', // used
|
grey: '#D8D8D8', // used
|
||||||
|
greyDark: '#CCC',
|
||||||
|
greyLight: '#bdbdbd', // used
|
||||||
disabled: '#FAFAFA', // used
|
disabled: '#FAFAFA', // used
|
||||||
background: '#FAFAFA', // used
|
background: '#FAFAFA', // used
|
||||||
green: '#00AF66', // used
|
green: '#00AF66', // used
|
||||||
@ -57,7 +59,8 @@ export const base = {
|
|||||||
orange: '#E38200', // used
|
orange: '#E38200', // used
|
||||||
orangeDark: '#CB7400', // not used - BORDER
|
orangeDark: '#CB7400', // not used - BORDER
|
||||||
red: '#DA4B42', // used
|
red: '#DA4B42', // used
|
||||||
redDark: '#CD251B' // not used - BORDER
|
redDark: '#CD251B', // not used - BORDER
|
||||||
|
blue: '#364ACD'
|
||||||
};
|
};
|
||||||
|
|
||||||
/** ********************************** HEADER ********************************** */
|
/** ********************************** HEADER ********************************** */
|
||||||
|
@ -70,7 +70,8 @@ module.exports = snapguidist({
|
|||||||
'src/tooltip/tooltip.js',
|
'src/tooltip/tooltip.js',
|
||||||
'src/close-button/index.js',
|
'src/close-button/index.js',
|
||||||
'src/icon-button/index.js',
|
'src/icon-button/index.js',
|
||||||
'src/message/index.js'
|
'src/message/index.js',
|
||||||
|
'src/slider/index.js'
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
20
yarn.lock
20
yarn.lock
@ -684,6 +684,10 @@ auto-bind@^1.1.0:
|
|||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/auto-bind/-/auto-bind-1.1.0.tgz#93b864dc7ee01a326281775d5c75ca0a751e5961"
|
resolved "https://registry.yarnpkg.com/auto-bind/-/auto-bind-1.1.0.tgz#93b864dc7ee01a326281775d5c75ca0a751e5961"
|
||||||
|
|
||||||
|
autobind-decorator@^1.3.4:
|
||||||
|
version "1.4.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/autobind-decorator/-/autobind-decorator-1.4.3.tgz#4c96ffa77b10622ede24f110f5dbbf56691417d1"
|
||||||
|
|
||||||
automated-readability@^1.0.0:
|
automated-readability@^1.0.0:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/automated-readability/-/automated-readability-1.0.1.tgz#5cf9111efcead6fe13b8c954d425b0068d59949d"
|
resolved "https://registry.yarnpkg.com/automated-readability/-/automated-readability-1.0.1.tgz#5cf9111efcead6fe13b8c954d425b0068d59949d"
|
||||||
@ -2960,6 +2964,13 @@ cors@2.8.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
vary "^1"
|
vary "^1"
|
||||||
|
|
||||||
|
cors@^2.8.4:
|
||||||
|
version "2.8.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.4.tgz#2bd381f2eb201020105cd50ea59da63090694686"
|
||||||
|
dependencies:
|
||||||
|
object-assign "^4"
|
||||||
|
vary "^1"
|
||||||
|
|
||||||
cosmiconfig@^2.1.0, cosmiconfig@^2.1.1, cosmiconfig@^2.1.3:
|
cosmiconfig@^2.1.0, cosmiconfig@^2.1.1, cosmiconfig@^2.1.3:
|
||||||
version "2.2.2"
|
version "2.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-2.2.2.tgz#6173cebd56fac042c1f4390edf7af6c07c7cb892"
|
resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-2.2.2.tgz#6173cebd56fac042c1f4390edf7af6c07c7cb892"
|
||||||
@ -8565,7 +8576,7 @@ oauth-sign@~0.8.1:
|
|||||||
version "0.8.2"
|
version "0.8.2"
|
||||||
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
|
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
|
||||||
|
|
||||||
object-assign@4.1.1, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
|
object-assign@4.1.1, object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
|
||||||
version "4.1.1"
|
version "4.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||||
|
|
||||||
@ -9934,6 +9945,13 @@ react-icons@^2.2.5:
|
|||||||
dependencies:
|
dependencies:
|
||||||
react-icon-base "2.0.7"
|
react-icon-base "2.0.7"
|
||||||
|
|
||||||
|
react-input-range@^1.2.1:
|
||||||
|
version "1.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-input-range/-/react-input-range-1.2.1.tgz#10ff5fc1ec6ab9d95e15cddebe6f6879db2c3386"
|
||||||
|
dependencies:
|
||||||
|
autobind-decorator "^1.3.4"
|
||||||
|
prop-types "^15.5.8"
|
||||||
|
|
||||||
react-redux@^5.0.6:
|
react-redux@^5.0.6:
|
||||||
version "5.0.6"
|
version "5.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.0.6.tgz#23ed3a4f986359d68b5212eaaa681e60d6574946"
|
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.0.6.tgz#23ed3a4f986359d68b5212eaaa681e60d6574946"
|
||||||
|
Loading…
Reference in New Issue
Block a user