Move code to this repo

This commit is contained in:
Alexey Kontsevoy
2019-09-12 19:37:15 -04:00
parent bdc3984503
commit 5e0a948f19
997 changed files with 92406 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
node_modules
scripts
**/node_modules/
**/dist/
.gitignore
Dockerfile
Makefile
*/**/.git
*/**/*.log
+2
View File
@@ -0,0 +1,2 @@
const eslint = require('./packages/build/.eslintrc');
module.exports = eslint;
+4
View File
@@ -0,0 +1,4 @@
node_modules
dist
*.log
.vscode
+3
View File
@@ -0,0 +1,3 @@
[submodule "packages/webapps"]
path = packages/webapps
url = [email protected]:gravitational/teleport.git
+10
View File
@@ -0,0 +1,10 @@
{
"arrowParens": "avoid",
"printWidth": 80,
"bracketSpacing": true,
"jsxBracketSameLine": false,
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5"
}
+2
View File
@@ -0,0 +1,2 @@
{ "extends": "./../packages/build/.babelrc" }
+37
View File
@@ -0,0 +1,37 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import { configure } from '@storybook/react';
import { addDecorator } from '@storybook/react';
import theme from './../packages/design/src/theme';
import ThemeProvider from './../packages/design/src/ThemeProvider';
import Box from './../packages/design/src/Box';
const ThemeDecorator = storyFn => (
<ThemeProvider theme={theme}>
<Box p={3}>{storyFn()}</Box>
</ThemeProvider>
);
addDecorator(ThemeDecorator);
const reqs = require.context('./../packages/', true, /\.story.js$/);
const loadStories = () => {
reqs.keys().forEach(reqs);
};
configure(loadStories, module);
+29
View File
@@ -0,0 +1,29 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
const path = require("path");
const createConfig = require('@gravitational/build/webpack/webpack.base');
const webpackCfg = createConfig();
module.exports = ({ config }) => {
config.devtool = false;
config.resolve = {
...config.resolve,
...webpackCfg.resolve,
}
return config;
};
+21
View File
@@ -0,0 +1,21 @@
FROM node:12-slim
RUN apt-get update && apt-get install git -y
# copy only package.json files first
RUN mkdir -p web-apps
COPY yarn.lock web-apps/
COPY package.json web-apps/
# copy the build folder so yarn can install build/bin files in other packages
COPY packages/build/ web-apps/packages/build/
# copy just the package.json files to yarn install results
COPY packages/design/package.json web-apps/packages/design/
COPY packages/gravity/package.json web-apps/packages/gravity/
COPY packages/shared/package.json web-apps/packages/shared/
COPY packages/teleport/package.json web-apps/packages/teleport/
COPY packages/e/gravity/package.json web-apps/packages/e/gravity/
COPY packages/e/teleport/package.json web-apps/packages/e/teleport/
WORKDIR web-apps
RUN yarn install
COPY . .
ARG NPM_SCRIPT
RUN yarn run $NPM_SCRIPT
+57
View File
@@ -0,0 +1,57 @@
IMAGE_NAME := web-apps:1.0.0
REPO_VER_TAG := $(shell git describe --tags --abbrev=8)
CONTAINER_NAME := web-apps-container-$(REPO_VER_TAG)-$(shell bash -c 'echo $$RANDOM')
HOME_DIR = $(shell pwd)
.PHONY:gravity
gravity:
$(MAKE) docker-build PACKAGE_PATH=packages/gravity NPM_CMD=build-gravity
.PHONY:teleport
teleport:
$(MAKE) docker-build PACKAGE_PATH=packages/teleport NPM_CMD=build-teleport
.PHONY:docker-enter
docker-enter:
docker run -ti --rm=true -t $(IMAGE_NAME) /bin/bash
.PHONY: docker-clean
docker-clean:
docker rmi --force $(IMAGE_NAME)
docker-build:
rm -rf $(HOME_DIR)/$(PACKAGE_PATH)/dist
docker build --force-rm=true --build-arg NPM_SCRIPT=$(NPM_CMD) -t $(IMAGE_NAME) .
docker create --name $(CONTAINER_NAME) -t $(IMAGE_NAME) && \
docker cp $(CONTAINER_NAME):/web-apps/$(PACKAGE_PATH)/dist $(HOME_DIR)/$(PACKAGE_PATH)/
docker rm -f $(CONTAINER_NAME)
.PHONY:clean
clean:
find . -name "node_modules" -type d -prune -exec rm -rf '{}' +
find . -name "dist" -type d -prune -exec rm -rf '{}' +
.PHONY:install
install:
bash -c "./scripts/install.sh"
# Use git submodule status to figure out if submodule is not initialized
# (- at the beginning) or somehow modified/outdated (+ at the beginning).
# or the folder has been deleted ([null])
# .PHONY: ensure-submodules
# ensure-submodules:
# @if git submodule status | egrep -q '(^[-]|^[+])|[null]' ; then \
# git submodule update --init; \
# fi
.PHONY: init
init:
git config core.hooksPath $(HOME_DIR)/scripts/githooks/oss
.PHONY: init-enterprise
init-enterprise:
git config core.hooksPath $(HOME_DIR)/scripts/githooks/enterpise
#ensure-submodules
#$(shell test -d $(FMC_DRV) || git submodule update --init)
+5
View File
@@ -0,0 +1,5 @@
const baseCfg = require('@gravitational/build/.babelrc');
module.exports = function (api) {
api.cache(true);
return baseCfg
}
+11
View File
@@ -0,0 +1,11 @@
module.exports = {
moduleNameMapper: {
'shared/(.*)$': '<rootDir>/packages/shared/$1',
'design/(.*)$': '<rootDir>/packages/design/src/$1',
'gravity/(.*)$': '<rootDir>/packages/gravity/src/$1',
'teleport/(.*)$': '<rootDir>/packages/teleport/src/$1',
'e-teleport/(.*)$': '<rootDir>/packages/e/teleport/src/$1',
'e-shared/(.*)$': '<rootDir>/packages/e/shared/$1',
'e-gravity/(.*)$': '<rootDir>/packages/e/gravity/$1',
},
};
+21
View File
@@ -0,0 +1,21 @@
{
"compilerOptions": {
"jsx": "react",
"allowSyntheticDefaultImports": true,
"baseUrl": ".",
"paths": {
"shared/*": ["packages/shared/*"],
"design/*": ["packages/design/src/*"],
"design": ["packages/design/src/"],
"teleport/*": ["packages/teleport/src/*"],
"gravity/*": ["packages/gravity/src/*"],
"e-gravity/*": ["packages/e/gravity/src/*"],
"e-teleport/*": ["packages/e/teleport/src/*"],
"e-shared/*": ["packages/e/shared/src/*"],
}
},
"exclude": [
"node_modules",
"dist"
]
}
+17
View File
@@ -0,0 +1,17 @@
{
"name": "namespaces",
"version": "1.0.0",
"main": "index.js",
"license": "Apache-2.0",
"scripts": {
"storybook": "start-storybook -p 9002 -c .storybook",
"test": "jest",
"tdd": "jest . --watch",
"build-gravity": "yarn workspace @gravitational/gravity run build",
"build-teleport": "yarn workspace @gravitational/teleport run build"
},
"private": true,
"workspaces": [
"packages/*/**"
]
}
+27
View File
@@ -0,0 +1,27 @@
const presets = ['@babel/preset-env', '@babel/preset-react'];
const plugins = [
'@babel/plugin-proposal-class-properties',
'@babel/plugin-proposal-object-rest-spread',
'@babel/plugin-syntax-dynamic-import',
];
module.exports = {
env: {
test: {
presets: [
['@babel/preset-env', { targets: { node: 'current' } }],
'@babel/preset-react',
],
},
development: {
plugins: [
'react-hot-loader/babel',
...plugins,
'babel-plugin-styled-components',
],
},
},
presets,
plugins,
};
+69
View File
@@ -0,0 +1,69 @@
const path = require('path');
const createConfig = require('./webpack/webpack.base');
module.exports = {
parser: 'babel-eslint',
parserOptions: {
ecmaVersion: 6,
sourceType: 'module',
ecmaFeatures: {
jsx: true,
experimentalObjectRestSpread: true,
},
},
env: {
es6: true,
browser: true,
node: true,
mocha: true,
},
globals: {
expect: true,
jest: true,
},
extends: [
'eslint:recommended',
'plugin:import/errors',
'plugin:import/warnings',
],
plugins: ['react', 'babel', 'import'],
rules: {
'comma-dangle': 0,
'no-mixed-spaces-and-tabs': 0,
'no-alert': 0,
'import/no-named-as-default': 0,
'import/default': 2,
'import/named': 2,
'import/no-unresolved': 2,
'no-underscore-dangle': 0,
'no-case-declarations': 0,
'strict': 0,
'no-console': 1,
'no-trailing-spaces': 2,
'react/display-name': 0,
'react/jsx-no-undef': 2,
'react/jsx-pascal-case': 2,
'react/no-danger': 2,
'react/jsx-no-duplicate-props': 2,
'react/jsx-sort-prop-types': 0,
'react/jsx-sort-props': 0,
'react/jsx-uses-react': 1,
'react/jsx-uses-vars': 1,
'react/no-did-mount-set-state': 1,
'react/no-did-update-set-state': 1,
'react/no-unknown-property': 1,
'react/prop-types': 0,
'react/react-in-jsx-scope': 1,
'react/self-closing-comp': 0,
'react/sort-comp': 0,
'react/jsx-wrap-multilines': 1,
},
settings: {
'import/resolver': {
webpack: {
config: createConfig()
},
},
},
};
+18
View File
@@ -0,0 +1,18 @@
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const defaultConfig = process.argv.some(arg => arg.startsWith('--dev'))
? 'webpack.dev.config.js'
: 'webpack.prod.config.js';
if (!process.argv.some(arg => arg.startsWith('--config'))) {
let webpackConfig = path.join(process.cwd(), 'webpack.config.js');
if (!fs.existsSync(webpackConfig)) {
webpackConfig = path.join(__dirname, '../webpack', defaultConfig);
}
process.argv.push('--config', webpackConfig);
}
require('webpack-cli');
+30
View File
@@ -0,0 +1,30 @@
#!/usr/bin/env node
process.on('unhandledRejection', err => {
throw err;
});
const spawn = require('cross-spawn');
const path = require('path');
const args = process.argv.slice(2);
const devServerPath = path.join(__dirname, '../devServer.js');
const nodeArgs = [devServerPath].concat(args);
const result = spawn.sync('node', nodeArgs, { stdio: 'inherit' });
if (result.signal) {
if (result.signal === 'SIGKILL') {
console.log(
'The build failed because the process exited too early. ' +
'This probably means the system ran out of memory or someone called ' +
'`kill -9` on the process.'
);
} else if (result.signal === 'SIGTERM') {
console.log(
'The build failed because the process exited too early. ' +
'Someone might have called `kill` or `killall`, or the system could ' +
'be shutting down.'
);
}
process.exit(1);
}
process.exit(result.status);
+144
View File
@@ -0,0 +1,144 @@
const fs = require('fs');
const path = require('path');
const uri = require('url');
const WebpackDevServer = require('webpack-dev-server');
const webpack = require('webpack');
const proxy = require('http-proxy').createProxyServer();
const changeProxyResponse = require('./devServerUtils');
const webpackConfig = require('./webpack/webpack.dev.config');
// parse target URL
const argv = require('optimist')
.usage('Usage: $0 -proxy [url]')
.demand(['proxy']).argv;
const urlObj = uri.parse(argv.proxy);
if (!urlObj.host) {
console.error('invalid URL: ' + argv.proxy);
return;
}
const PROXY_TARGET = urlObj.host;
const ROOT = '/web';
const PORT = '8080';
const WEBPACK_CLIENT_ENTRY =
'webpack-dev-server/client?https://0.0.0.0:' + PORT;
const WEBPACK_SRV_ENTRY = 'webpack/hot/dev-server';
for (var prop in webpackConfig.entry) {
webpackConfig.entry[prop].unshift('react-hot-loader/patch');
webpackConfig.entry[prop].unshift(WEBPACK_CLIENT_ENTRY, WEBPACK_SRV_ENTRY);
}
function getTargetOptions() {
return {
target: 'https://' + PROXY_TARGET,
secure: false,
changeOrigin: true,
xfwd: true,
};
}
const compiler = webpack(webpackConfig);
const server = new WebpackDevServer(compiler, {
proxy: {
'/web/grafana/*': getTargetOptions(),
'/web/config.*': getTargetOptions(),
'/pack/v1/*': getTargetOptions(),
'/portalapi/*': getTargetOptions(),
'/portal*': getTargetOptions(),
'/proxy/*': getTargetOptions(),
'/v1/*': getTargetOptions(),
'/app/*': getTargetOptions(),
'/sites/v1/*': getTargetOptions(),
},
publicPath: ROOT + '/app',
hot: true,
disableHostCheck: true,
https: true,
inline: true,
headers: { 'X-Custom-Header': 'yes' },
//stats: { colors: true },
stats: 'errors-only',
});
// tell webpack dev server to proxy below sockets requests to actual server
server.listeningApp.on('upgrade', function(req, socket) {
console.log('proxying ws', req.url);
proxy.ws(req, socket, {
target: 'wss://' + PROXY_TARGET,
secure: false,
});
});
const indexHtmlPath = path.join(process.cwd(), '//dist//index.html');
const htmlToSend = fs.readFileSync(indexHtmlPath, 'utf8');
// to enable Hot Module Reload we need to serve local index.html.
// since local index.html has no embeded TOKEN, we need to:
// 1) make a proxy request
// 2) modify proxy response by replacing server index.html with the local
// 3) insert embeded by server token into the local
server.app.use(
changeProxyResponse(
(req, res) => {
// return true if you want to modify the response later
var contentType = res.getHeader('Content-Type');
if (contentType && contentType.startsWith('text/html')) {
return true;
}
return false;
},
(req, res, body) => {
// body is a Buffer with the current response; return Buffer or string with the modified response
// can also return a Promise.
var str = body.toString();
res.set({
'content-security-policy': '',
});
if (req.path.endsWith('/complete/')) {
return body;
}
var htmlWithTokens = htmlToSend;
htmlWithTokens = replaceToken(
new RegExp(/<meta name="grv_csrf_token" .*\>/),
str,
htmlWithTokens
);
htmlWithTokens = replaceToken(
new RegExp(/<meta name="grv_bearer_token" .*\>/),
str,
htmlWithTokens
);
return htmlWithTokens;
}
)
);
function serveHTML() {
return function(req, res) {
proxy.web(req, res, getTargetOptions());
};
}
server.app.get(ROOT + '/*', serveHTML());
server.app.get(ROOT, serveHTML());
server.listen(PORT, '0.0.0.0', function() {
console.log(
'Dev Server is up and running: https://localhost:' + PORT + '/web/'
);
});
function replaceToken(regex, takeFrom, insertTo) {
var value = takeFrom.match(regex);
if (value) {
return insertTo.replace(regex, value[0]);
}
return insertTo;
}
+98
View File
@@ -0,0 +1,98 @@
//
// taken and modified from https://github.com/mo22/express-modify-response
//
module.exports = function expressModifyResponse(checkCallback, modifyCallback) {
return function expressModifyResponse(req, res, next) {
var _end = res.end;
var _write = res.write;
var checked = false;
var buffers = [];
var addBuffer = (chunk, encoding) => {
if (chunk === undefined)
return;
if (typeof chunk === 'string') {
chunk = new Buffer(chunk, encoding);
}
buffers.push(chunk);
};
var _writeHead = res.writeHead;
res.writeHead = function () {
// writeHead supports (statusCode, headers) as well as (statusCode,
// statusMessage, headers)
var headers = (arguments.length > 2)
? arguments[2]
: arguments[1];
var contentType = this.getHeader('content-type');
if ((typeof contentType != 'undefined') && (contentType.indexOf('text/html') == 0)) {
res.isHtml = true;
// Strip off the content length since it will change.
res.removeHeader('Content-Length');
if (headers) {
delete headers['content-length'];
}
}
_writeHead.apply(res, arguments);
};
res.write = function write(chunk, encoding) {
if (!checked) {
checked = true;
var hook = checkCallback(req, res);
if (!hook) {
res.end = _end;
res.write = _write;
return res.write(chunk, encoding);
} else {
addBuffer(chunk, encoding);
}
} else {
addBuffer(chunk, encoding);
}
};
res.end = function end(chunk, encoding) {
if (!checked) {
checked = true;
var hook = checkCallback(req, res);
if (!hook) {
res.end = _end;
res.write = _write;
return res.end(chunk, encoding);
} else {
addBuffer(chunk, encoding);
}
} else {
addBuffer(chunk, encoding);
}
var buffer = Buffer.concat(buffers);
Promise
.resolve(modifyCallback(req, res, buffer))
.then((result) => {
if (typeof result === 'string') {
result = new Buffer(result, 'utf-8');
}
if (res.getHeader('Content-Length')) {
res.setHeader('Content-Length', String(result.length));
}
res.end = _end;
res.write = _write;
res.write(result);
res.end();
})
.catch((e) => {
// handle?
next(e);
});
};
next();
}
}
+16
View File
@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="referrer" content="origin" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="grv_csrf_token" content="{{ .XCSRF }}" />
<meta name="grv_bearer_token" content="{{ .Session }}" />
<title><%= htmlWebpackPlugin.options.title %></title>
<script src="/web/config.js"></script>
</head>
<body>
<div id="app"></div>
</body>
</html>
+62
View File
@@ -0,0 +1,62 @@
{
"name": "@gravitational/build",
"version": "1.0.1",
"description": "build scripts",
"main": "index.js",
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "https://github.com/gravitational/webapps/webapps.git",
"directory": "packages/build"
},
"bin": {
"gravity-start": "./bin/start.js",
"gravity-build": "./bin/build.js"
},
"dependencies": {
"@babel/core": "^7.3.4",
"@babel/plugin-proposal-class-properties": "^7.2.1",
"@babel/plugin-proposal-object-rest-spread": "^7.2.0",
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
"@babel/plugin-transform-classes": "^7.3.4",
"@babel/preset-env": "^7.3.4",
"@babel/preset-react": "^7.0.0",
"@hot-loader/react-dom": "^16.8.6",
"@storybook/addon-info": "^5.1.9",
"@storybook/react": "^5.1.9",
"babel-eslint": "10.0.1",
"babel-loader": "8.0.6",
"body-parser": "1.18.3",
"core-js": "^3",
"cross-env": "5.0.5",
"css-loader": "1.0.1",
"eslint": "4.19.1",
"eslint-import-resolver-webpack": "0.11.0",
"eslint-loader": "2.1.1",
"eslint-plugin-babel": "4.1.2",
"eslint-plugin-import": "2.16.0",
"eslint-plugin-react": "7.11.1",
"events": "1.0.2",
"expect": "1.20.2",
"express": "4.16.3",
"file-loader": "2.0.0",
"html-webpack-plugin": "3.2.0",
"jest": "^24.8.0",
"jest-styled-components": "^6.3.1",
"optimist": "^0.6.1",
"raw-loader": "0.5.1",
"react": "^16.8.4",
"react-dom": "^16.8.4",
"react-hot-loader": "4.11.0",
"serve-static": "1.13.2",
"style-loader": "0.19.1",
"styled-components": "4.1.2",
"svg-url-loader": "^2.3.2",
"url-loader": "1.1.2",
"webpack": "4.33.0",
"webpack-bundle-analyzer": "^3.3.2",
"webpack-cli": "3.3.2",
"webpack-dev-server": "3.1.11",
"cross-spawn": "6.0.5"
}
}
+161
View File
@@ -0,0 +1,161 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
const fs = require('fs');
const path = require('path');
const HtmlWebPackPlugin = require('html-webpack-plugin');
const appDirectory = fs.realpathSync(process.cwd());
const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
module.exports = function createConfig() {
return {
optimization: {
splitChunks: {
cacheGroups: {
vendors: {
chunks: 'all',
name: 'vendor',
test: /([\\/]node_modules[\\/])/,
priority: -10,
},
},
},
},
entry: {
app: ['./src/boot.js'],
},
output: {
// used by loaders to generate various URLs within CSS, JS based off publicPath
publicPath: '/web/app/',
path: resolveApp('dist/app'),
/*
* format of the output file names. [name] stands for 'entry' keys
* defined in the 'entry' section
**/
filename: '[name].[hash].js',
// chunk file name format
chunkFilename: '[name].[chunkhash].js',
},
resolve: {
// some vendor libraries expect below globals to be defined
alias: {
jQuery: 'jquery',
teleport: path.join(__dirname, '/../../teleport/src'),
'e-teleport': path.join(__dirname, '/../../e/teleport/src'),
'e-shared': path.join(__dirname, '/../../e/shared/src'),
'e-gravity': path.join(__dirname, '/../../e/gravity/src'),
design: path.join(__dirname, '/../../design/src'),
shared: path.join(__dirname, '/../../shared'),
gravity: path.join(__dirname, '/../../gravity/src'),
},
/*
* root path to resolve js our modules, enables us to use absolute path.
* For ex: require('./../../../config') can be replaced with require('app/config')
**/
modules: ['node_modules', 'src'],
extensions: ['.js', '.jsx'],
},
noParse: function(content) {
return /xterm.js$/.test(content);
},
rules: {
fonts: {
test: /fonts\/(.)+\.(woff|woff2|ttf|eot|svg)/,
loader: 'url-loader',
options: {
limit: 102400, // 100kb
name: '/assets/fonts/[name].[ext]',
},
},
svg: {
test: /\.svg$/,
loader: 'svg-url-loader',
options: {
noquotes: true,
},
exclude: /node_modules/,
},
css() {
return {
test: /\.(css)$/,
use: ['style-loader', 'css-loader'],
};
},
images: {
test: /\.(png|jpg|gif)$/,
loader: 'url-loader',
options: {
limit: 10000,
name: '/assets/img/img-[hash:6].[ext]',
},
},
jsx(args) {
args = args || {};
var emitWarning = false;
if (args.withHot) {
emitWarning = true;
}
return {
test: /\.(js|jsx)$/,
exclude: /(node_modules)|(assets)/,
use: [
{
loader: 'babel-loader',
},
{
loader: 'eslint-loader',
options: {
emitWarning,
},
},
],
};
},
},
plugins: {
// builds index html page, the main entry point for application
createIndexHtml(options) {
return createHtmlPluginInstance({
filename: '../index.html',
title: '',
inject: true,
template: path.join(__dirname, '/../index.ejs'),
...options,
});
},
},
};
};
function createHtmlPluginInstance(cfg) {
cfg.inject = true;
return new HtmlWebPackPlugin(cfg);
}
@@ -0,0 +1,64 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
const webpack = require('webpack');
const createConfig = require('./webpack.base');
process.env.BABEL_ENV = 'development';
process.env.NODE_ENV = 'development';
const baseCfg = createConfig();
var cfg = {
entry: baseCfg.entry,
output: {
...baseCfg.output,
filename: '[name].js',
chunkFilename: '[name].js',
},
resolve: {
...baseCfg.resolve,
alias: {
...baseCfg.resolve.alias,
'react-dom': '@hot-loader/react-dom',
},
},
devtool: false,
mode: 'development',
optimization: baseCfg.optimization,
module: {
noParse: baseCfg.noParse,
strictExportPresence: true,
rules: [
baseCfg.rules.fonts,
baseCfg.rules.svg,
baseCfg.rules.images,
baseCfg.rules.jsx({ withHot: true }),
baseCfg.rules.css(),
],
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
baseCfg.plugins.createIndexHtml(),
],
};
module.exports = cfg;
@@ -0,0 +1,58 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
const webpack = require('webpack');
const createConfig = require('./webpack.base');
const baseCfg = createConfig();
process.env.BABEL_ENV = 'production';
process.env.NODE_ENV = 'production';
//const BundleAnalyzerPlugin = require('webpack-bundle-analyzer')
// .BundleAnalyzerPlugin;
var cfg = {
entry: baseCfg.entry,
output: baseCfg.output,
resolve: baseCfg.resolve,
mode: 'production',
optimization: {
...baseCfg.optimization,
minimize: false,
},
module: {
noParse: baseCfg.noParse,
strictExportPresence: true,
rules: [
baseCfg.rules.fonts,
baseCfg.rules.svg,
baseCfg.rules.images,
baseCfg.rules.jsx(),
baseCfg.rules.css(),
],
},
plugins: [
// new BundleAnalyzerPlugin(),
new webpack.HashedModuleIdsPlugin(),
baseCfg.plugins.createIndexHtml(),
],
};
module.exports = cfg;
File diff suppressed because it is too large Load Diff
+22
View File
@@ -0,0 +1,22 @@
{
"name": "@gravitational/design",
"version": "1.0.0",
"description": "Gravitatinal design system",
"main": "index.js",
"license": "Apache-2.0",
"scripts": {
"storybook": "start-storybook -p 9002 -c .storybook",
"test": "jest",
"tdd": "jest . --watch"
},
"repository": {
"type" : "git",
"url" : "https://github.com/gravitational/webapps/webapps.git",
"directory": "packages/design"
},
"dependencies": {
"cross-env": "5.0.5",
"styled-system": "^3.1.11",
"@gravitational/build": "^1.0.0"
}
}
@@ -0,0 +1,42 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import { configure } from '@storybook/react';
import { addDecorator } from '@storybook/react';
import theme from './../../theme';
import ThemeProvider from '../ThemeProvider';
import Box from './../Box';
const ThemeDecorator = storyFn => (
<ThemeProvider theme={theme}>
<Box p={3}>
{storyFn()}
</Box>
</ThemeProvider>
)
addDecorator(ThemeDecorator);
const sharedReq = require.context('./../../', true, /\.story.js$/);
const srcReq = require.context('./../../../src', true, /\.story.js$/);
const loadStories = () => {
sharedReq.keys().forEach(sharedReq);
srcReq.keys().forEach(srcReq);
}
configure(loadStories, module);
@@ -0,0 +1,28 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
const path = require('path');
const webpackBaseCfg = require('@gravitational/build/webpack/webpack.base');
module.exports = ({ config }) => {
config.devtool = false;
config.resolve = {
...config.resolve,
...webpackBaseCfg.resolve,
};
return config;
};
+95
View File
@@ -0,0 +1,95 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import styled from 'styled-components';
import PropTypes from 'prop-types';
import { space, color, width } from 'design/system';
import defaultTheme from 'design/theme'
const kindColors = props => {
const { kindColor, theme } = props;
switch (kindColor) {
case 'danger':
return {
background: theme.colors.danger,
color: theme.colors.primary.contrastText
}
case 'info':
return {
background: theme.colors.info,
color: theme.colors.primary.contrastText
}
case 'warning':
return {
background: theme.colors.warning,
color: theme.colors.primary.contrastText
}
case 'success':
return {
background: theme.colors.success,
color: theme.colors.primary.contrastText
}
default:
return {
background: theme.colors.danger,
color: theme.colors.primary.contrastText
}
}
}
const Alert = styled.div`
display: flex;
align-items: center;
justify-content: center;
border-radius: 8px;
box-sizing: border-box;
box-shadow: 0 0 2px rgba(0, 0, 0, .12), 0 2px 2px rgba(0, 0, 0, .24);
font-weight: ${({theme}) => theme.fontWeights.regular};
font-size: 16px;
margin: 0 0 16px 0;
min-height: 48px;
padding: 8px 16px;
overflow: auto;
word-break: break-all;
${space}
${kindColors}
${width}
a {
color: ${({theme}) => theme.colors.light};
}
`;
Alert.propTypes = {
kind: PropTypes.oneOf(['danger', 'info', 'warning', 'success']),
...color.propTypes,
...space.propTypes,
...width.propTypes,
};
Alert.defaultProps = {
kindColor: 'danger',
theme: defaultTheme,
};
Alert.displayName = 'Alert';
export default Alert;
export const Danger = props => <Alert kindColor="danger" {...props} />
export const Info = props => <Alert kindColor="info" {...props} />
export const Warning = props => <Alert kindColor="warning" {...props} />
export const Success = props => <Alert kindColor="success" {...props} />
@@ -0,0 +1,48 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import { storiesOf } from '@storybook/react';
import { withInfo } from '@storybook/addon-info';
import Alert from './index';
storiesOf('Desigin/Alert', module)
.addDecorator(withInfo)
.add('Alert Component', () => {
return (
<Alert>This is an error message</Alert>
);
})
.add('Danger Alert', () => {
return (
<Alert kindColor="danger">This is an error message</Alert>
);
})
.add('Warning Alert', () => {
return (
<Alert kindColor="warning">This is a warning message</Alert>
);
})
.add('Info Alert', () => {
return (
<Alert kindColor="info">This is a informational message</Alert>
);
})
.add('Success Alert', () => {
return (
<Alert kindColor="success">This is a success message</Alert>
);
});
+24
View File
@@ -0,0 +1,24 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Alert, { Danger, Info, Warning, Success } from './Alert';
export default Alert;
export {
Danger, Info, Warning, Success
}
+74
View File
@@ -0,0 +1,74 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import styled from 'styled-components';
import defaultTheme from './../theme';
import {
overflow,
borders,
borderRadius,
flex,
height,
maxWidth,
minHeight,
maxHeight,
minWidth,
alignSelf,
justifySelf,
space,
width,
color,
textAlign,
} from '../system';
const Box = styled.div`
box-sizing: border-box;
${maxWidth}
${minWidth}
${space}
${height}
${minHeight}
${maxHeight}
${width}
${color}
${textAlign}
${flex}
${alignSelf}
${justifySelf}
${borders}
${borderRadius}
${overflow}
`;
Box.displayName = 'Box';
Box.defaultProps = {
theme: defaultTheme,
};
Box.propTypes = {
...space.propTypes,
...height.propTypes,
...width.propTypes,
...color.propTypes,
...textAlign.propTypes,
...flex.propTypes,
...alignSelf.propTypes,
...justifySelf.propTypes,
...borders.propTypes,
...overflow.propTypes,
};
export default Box;
+124
View File
@@ -0,0 +1,124 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import { storiesOf } from '@storybook/react'
import { withInfo } from '@storybook/addon-info'
import Box from './Box';
storiesOf('Desigin/Box', module)
.addDecorator(withInfo)
.add('Layout component', () => (
<Box color="white" bg="blue">
this is a simple div
</Box>
))
.add('Padding', () => (
<Box bg="blue" p={3}>
Hello
</Box>
))
.add('Margin', () => (
<Box bg="blue" m={4}>
Hello
</Box>
))
.add('Color', () => (
<Box bg="blue" p={3} color='white'>
Hello
</Box>
))
.add('Background Color', () => (
<Box bg="blue" p={3} color='white'>
Hello
</Box>
))
.add('Border Radius', () => (
<div>
<Box
borderRadius="16px"
color='white'
p={5}
bg='blue'>
16 Pixel Border Radius
</Box>
<Box
mt={4}
borderBottomRightRadius={3}
borderBottomLeftRadius={3}
color='white'
p={5}
bg='blue'>
Border Radius on Bottom Left & Right
</Box>
<Box
mt={4}
borderTopLeftRadius={16}
borderTopRightRadius={16}
color='white'
p={5}
bg='blue'>
Border Radius on Top Left & Right
</Box>
</div>
))
.add('Width', () => (
<Box
p={3}
width={1 / 2}
color='white'
bg='blue'>
Half Width
</Box>
))
.add('Pixel Width', () => (
<Box
p={3}
width={256}
color='white'
bg='blue'>
256px width
</Box>
))
.add('VW Width', () => (
<Box
p={3}
width='50vw'
color='white'
bg='blue'>
50vw width
</Box>
))
.add('Directional Padding', () => (
<Box p={3}>
<Box m={2} pt={3} color='white' bg='blue'>Padding Top</Box>
<Box m={2} pr={4} color='white' bg='blue'>Padding Right</Box>
<Box m={2} pb={3} color='white' bg='blue'>Padding Bottom</Box>
<Box m={2} pl={4} color='white' bg='blue'>Padding Left</Box>
<Box m={2} px={4} color='white' bg='blue'>Padding X-Axis</Box>
<Box m={2} py={4} color='white' bg='blue'>Padding Y-Axis</Box>
</Box>
))
.add('Directional Margin', () => (
<Box p={3}>
<Box mt={5} color='white' bg='blue'>Margin Top</Box>
<Box mr={3} color='white' bg='blue'>Margin Right</Box>
<Box mb={4} color='white' bg='blue'>Margin Bottom</Box>
<Box ml={5} color='white' bg='blue'>Margin Left</Box>
<Box mx={5} color='white' bg='blue'>Margin X-Axis</Box>
<Box my={5} color='white' bg='blue'>Margin Y-Axis</Box>
</Box>
));
+22
View File
@@ -0,0 +1,22 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Box from './Box';
export default Box;
+160
View File
@@ -0,0 +1,160 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import styled from 'styled-components'
import PropTypes from 'prop-types'
import { space, width, height } from 'design/system';
import defaultTheme from 'design/theme';
const Button = ({ children, setRef, ...props}) => {
return (
<StyledButton {...props} ref={setRef}>
{children}
</StyledButton>
)
}
const size = props => {
switch (props.size) {
case 'small':
return {
fontSize: '10px',
minHeight: '24px',
padding: '0px 16px'
}
case 'large':
return {
minHeight: '48px',
fontSize: '14px',
padding: '0px 48px'
}
default:
// medium
return {
minHeight: '40px',
fontSize: `12px`,
padding: '0px 32px'
}
}
}
const themedStyles = props => {
const { colors } = props.theme;
const style = {
color: colors.secondary.contrastText,
'&:disabled': {
background: colors.action.disabledBackground,
color: colors.action.disabled
},
}
return {
...kinds(props),
...style,
...size(props),
...space(props),
...width(props),
...block(props),
...height(props)
}
}
const kinds = props => {
const { kind, theme } = props;
switch(kind){
case 'secondary':
return {
background: theme.colors.primary.light,
'&:hover, &:focus': {
background: theme.colors.primary.lighter,
}
};
case 'warning':
return {
background: theme.colors.error.dark,
'&:hover, &:focus': {
background: theme.colors.error.main
}
};
case 'primary':
default:
return {
background: theme.colors.secondary.main,
'&:hover, &:focus': {
background: theme.colors.secondary.light
},
'&:active': {
background: theme.colors.secondary.dark,
},
}
}
}
const block = props => (props.block ? {
width: '100%'
} : null)
const StyledButton = styled.button`
line-height: 1.5;
margin: 0;
display: inline-flex;
justify-content: center;
align-items: center;
box-sizing: border-box;
border: none;
border-radius: 4px;
cursor: pointer;
font-family: inherit;
font-weight: 600;
outline: none;
position: relative;
text-align: center;
text-decoration: none;
text-transform: uppercase;
transition: all .3s;
-webkit-font-smoothing: antialiased;
&:active {
opacity: .56;
}
// remove dotted Firefox outline
::-moz-focus-inner {
border: 0;
}
${themedStyles}
`
Button.propTypes = {
block: PropTypes.bool,
secondary: PropTypes.bool,
...space.propTypes,
...height.propTypes
}
Button.defaultProps = {
size: 'medium',
theme: defaultTheme
}
Button.displayName = 'Button'
export default Button;
export const ButtonPrimary = props => <Button kind="primary" {...props} />
export const ButtonSecondary = props => <Button kind="secondary" {...props} />
export const ButtonWarning = props => <Button kind="warning" {...props} />
@@ -0,0 +1,73 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react'
import { storiesOf } from '@storybook/react'
import { withInfo } from '@storybook/addon-info'
import Button, { ButtonPrimary, ButtonSecondary, ButtonWarning } from './Button';
import ButtonOutlinedDefault, * as ButtonOutlined from './../ButtonOutlined';
import ButtonIcon from './../ButtonIcon';
import ButtonLink from './../ButtonLink';
import { AddUsers, Trash, Ellipsis } from './../Icon';
import styled from 'styled-components';
storiesOf('Desigin/Button', module)
.addDecorator(withInfo)
.add('kinds', () => (
<StyledBox>
<ButtonPrimary>Primary</ButtonPrimary>
<ButtonSecondary>Secondary</ButtonSecondary>
<ButtonWarning >Warning</ButtonWarning>
<ButtonLink href="" >Link Button</ButtonLink>
<ButtonIcon> <AddUsers/></ButtonIcon>
<ButtonIcon> <Ellipsis /></ButtonIcon>
<ButtonIcon> <Trash /></ButtonIcon>
<ButtonIcon> <Trash /></ButtonIcon>
<ButtonOutlinedDefault>Default</ButtonOutlinedDefault>
<ButtonOutlined.OutlinedPrimary>Primary</ButtonOutlined.OutlinedPrimary>
</StyledBox>
))
.add('sizes', () => (
<div>
<StyledBox>
<Button size="large" mr={3}>Large</Button>
<Button size="medium" mr={3}>Medium</Button>
<Button size="small" mr={3}>Small</Button>
</StyledBox>
<StyledBox>
<ButtonIcon size={2}> <AddUsers/></ButtonIcon>
<ButtonIcon size={1}> <Ellipsis /></ButtonIcon>
<ButtonIcon size={0}> <Trash /></ButtonIcon>
</StyledBox>
</div>
))
.add('block', () => <Button block>Block Button</Button>)
.add('states', () => (
<StyledBox>
<Button mr={3} disabled>Disabled</Button>
<Button autoFocus> Focused</Button>
</StyledBox>
));
const StyledBox = styled.div`
display: flex;
flex-wrap: wrap;
align-items: center;
> * {
margin-left: 20px;
margin-bottom: 20px;
}
`
+19
View File
@@ -0,0 +1,19 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Button, { ButtonPrimary, ButtonWarning, ButtonSecondary } from './Button';
export default Button;
export { ButtonPrimary, ButtonWarning, ButtonSecondary }
@@ -0,0 +1,92 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import styled from 'styled-components'
import Icon from '../Icon';
import { space } from 'design/system';
const sizeMap = {
0: {
fontSize: '12px',
height: '24px',
width: '24px',
},
1: {
fontSize: '16px',
height: '32px',
width: '32px',
},
2: {
fontSize: '24px',
height: '48px',
width: '48px',
}
}
const defaultSize = sizeMap[1]
const size = props => {
return sizeMap[props.size] || defaultSize;
}
const fromProps = props => {
const { theme } = props;
return {
'&:disabled': {
color: theme.colors.action.disabled
},
'&:hover, &:focus': {
background: theme.colors.action.hover,
}
}
}
const ButtonIcon = ({children, setRef, ...rest}) => {
return (
<StyledButtonIcon ref={setRef} {...rest}>{children}</StyledButtonIcon>
)
}
const StyledButtonIcon = styled.button`
align-items: center;
border: none;
cursor: pointer;
display: flex;
outline: none;
border-radius: 50%;
overflow: visible;
justify-content: center;
text-align: center;
flex: 0 0 auto;
background: transparent;
color: inherit;
transition: all .3s;
-webkit-font-smoothing: antialiased;
${Icon}{
color: inherit;
}
&:disabled {
color: ${({ theme }) => theme.colors.action.disabled };
}
${fromProps}
${size}
${space}
`
export default ButtonIcon;
@@ -0,0 +1,18 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import ButtonIcon from './ButtonIcon';
export default ButtonIcon;
@@ -0,0 +1,51 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import styled from 'styled-components'
import defaultTheme from 'design/theme';
import Button from './../Button/Button';
function ButtonLink({ ...props }){
return (
<Button as={StyledButtonLink} {...props} />
)
}
ButtonLink.propTypes = {
...Button.propTypes
}
ButtonLink.defaultProps = {
size: 'medium',
theme: defaultTheme
}
ButtonLink.displayName = 'ButtonLink'
const StyledButtonLink = styled.a`
color: ${({ theme }) => theme.colors.link};
font-weight: normal;
background: none;
text-decoration: underline;
text-transform: none;
&:hover, &:focus {
background: ${ ({theme}) => theme.colors.primary.light}
}
`
export default ButtonLink;
@@ -0,0 +1,18 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import ButtonLink from './ButtonLink';
export default ButtonLink;
@@ -0,0 +1,151 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import styled from 'styled-components'
import { space, width } from 'design/system';
import defaultTheme from 'design/theme';
const ButtonOutlined = ({ children, setRef, ...props}) => {
return (
<StyledButton {...props} ref={setRef}>
<span>{children}</span>
</StyledButton>
)
}
const size = props => {
switch (props.size) {
case 'small':
return {
fontSize: '10px',
padding: '8px 8px'
}
case 'medium':
return {
fontSize: `12px`,
padding: '12px 32px'
}
case 'large':
return {
fontSize: '14px',
padding: '20px 40px'
}
default:
return {
fontSize: `10px`,
padding: '12px 24px'
}
}
}
const themedStyles = props => {
const { colors } = props.theme;
const style = {
color: colors.secondary.contrastText,
'&:disabled': {
background: colors.action.disabledBackground,
color: colors.action.disabled
},
}
return {
...kinds(props),
...style,
...size(props),
...space(props),
...width(props),
...block(props)
}
}
const kinds = props => {
const { kind, theme } = props;
switch(kind){
case 'primary':
return {
borderColor: theme.colors.secondary.main,
color: theme.colors.secondary.light,
'&:hover, &:focus': {
borderColor: theme.colors.secondary.light
},
'&:active': {
borderColor: theme.colors.secondary.dark,
},
}
default:
return {
borderColor: theme.colors.text.primary,
color: theme.colors.text.primary,
'&:hover, &:focus': {
borderColor: theme.colors.light,
color: theme.colors.light
}
}
}
}
const block = props => (props.block ? {
width: '100%'
} : null)
const StyledButton = styled.button`
border-radius: 4px;
display: inline-flex;
justify-content: center;
align-items: center;
border: 1px solid;
box-sizing: border-box;
background-color: transparent;
cursor: pointer;
font-family: inherit;
font-weight: bold;
outline: none;
position: relative;
text-align: center;
text-decoration: none;
text-transform: uppercase;
transition: all .3s;
-webkit-font-smoothing: antialiased;
&:active {
opacity: .56;
}
> span {
display: flex;
align-items: center;
justify-content: center;
}
${themedStyles}
${kinds}
${block}
`
ButtonOutlined.propTypes = {
...space.propTypes,
}
ButtonOutlined.defaultProps = {
size: 'medium',
theme: defaultTheme
}
ButtonOutlined.displayName = 'ButtonOutlined'
export default ButtonOutlined;
export const OutlinedPrimary = props => <ButtonOutlined kind="primary" {...props} />
@@ -0,0 +1,21 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Button, { OutlinedPrimary } from './ButtonOutlined';
export default Button;
export {
OutlinedPrimary
}
+33
View File
@@ -0,0 +1,33 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import styled from 'styled-components';
import Box from './../Box';
import theme from './../theme';
const Card = styled(Box)`
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.24);
border-radius: 8px;
`;
Card.defaultProps = {
theme: theme,
bg: 'primary.light',
};
Card.displayName = 'Card';
export default Card;
@@ -0,0 +1,27 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react'
import { storiesOf } from '@storybook/react'
import { withInfo } from '@storybook/addon-info'
import Card from './Card'
storiesOf('Desigin/Card', module)
.addDecorator(withInfo)
.add('Card', () => (
<Card width="500px" height="200px" m="0 auto">
</Card>
))
+21
View File
@@ -0,0 +1,21 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Card from './Card';
export default Card;
@@ -0,0 +1,97 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import styled from 'styled-components'
import { Text, Alert, Card } from 'design';
export default function CardError({children}) {
return (
<Card
color='text.onLight'
bg='light'
width='540px'
mx='auto'
my={6}
p={5}
children={children}
/>
)
}
const Header = props => (
<Text typography="h1" mb={3} textAlign="center" children={props.children}/>
)
const Content = ({ message='', desc }) => {
const $desc = desc ? <Text typography="body2">{desc}</Text> : null;
const $errMessage = message ? <Alert mt={2} mb={4}>{ message }</Alert> : null;
return (
<div>
{$errMessage} {$desc}
</div>
);
}
export const NotFound = ({ message }) => (
<CardError>
<Header>404 Not Found</Header>
<Content message={message}/>
</CardError>
)
export const AccessDenied = ({ message}) => (
<CardError>
<Header>Access denied</Header>
<Content message={message}/>
</CardError>
)
export const Failed = ({message}) => (
<CardError>
<Header>Internal Error</Header>
<Content message={message}/>
</CardError>
)
export const Offline = ({message, title}) => (
<CardError>
<Header>{title}</Header>
<Content
desc={(
<Text typography="paragraph" textAlign="center">
{message}
</Text>
)}/>
</CardError>
)
export const LoginFailed = ({ message, loginUrl }) => (
<CardError>
<Header>Login unsuccessful</Header>
<Content
message={message}
desc={(
<Text typography="paragraph">
<HyperLink href={loginUrl}>Please try again</HyperLink>, if the problem persists, contact your system administrator.
</Text>
)}/>
</CardError>
)
const HyperLink = styled.a`
color: ${({ theme }) => theme.colors.link};
`
@@ -0,0 +1,40 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react'
import { storiesOf } from '@storybook/react'
import { withInfo } from '@storybook/addon-info'
import { Offline, NotFound, AccessDenied, Failed, LoginFailed } from './CardError';
const message = 'some error message';
storiesOf('Desigin/CardError', module)
.addDecorator(withInfo)
.add('NotFound', () => (
<NotFound message={message} />
))
.add('AccessDenied', () => (
<AccessDenied message={message} />
))
.add('Failed', () => (
<Failed message={message} />
))
.add('LoginFailed', () => (
<LoginFailed message={message} loginUrl="https://localhost" />
))
.add('Offline', () => (
<Offline title={"This cluster is not available from Gravity."} message={"To access this cluster, please use its local endpoint"} />
));
@@ -0,0 +1,23 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import CardError, { Failed, Offline, LoginFailed, AccessDenied, NotFound } from './CardError';
export default CardError;
export {
Failed, LoginFailed, AccessDenied, NotFound, Offline
}
@@ -0,0 +1,42 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import { Card, Text } from 'design';
import * as Icons from 'design/Icon';
export default function CardSuccess({ title, children }) {
return (
<Card width="540px" p={7} my={4} mx="auto" textAlign="center">
<Icons.CircleCheck mb={3} fontSize={64} color="success"/>
<Text typography="h1" mb="3">
{title}
</Text>
<Text typography="paragraph">
{children}
</Text>
</Card>
)
}
export function CardSuccessLogin() {
return (
<CardSuccess title="Login Successful">
You have successfully signed into your account.
You can close this window and continue using the product.
</CardSuccess>
)
}
@@ -0,0 +1,21 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import CardSuccess, { CardSuccessLogin } from './CardSuccess';
export default CardSuccess;
export {
CardSuccessLogin
}
@@ -0,0 +1,67 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import styled from 'styled-components';
import { borderRadius } from 'design/system';
import { Table } from './../Table';
import Pager from './Pager';
import usePages from './usePages';
export default function PagedTable(props) {
const { pageSize, data, pagerPosition, ...rest } = props;
const pagedState = usePages({ pageSize, data });
const tableProps = {
...rest,
data: pagedState.data,
};
const showTopPager =
pagedState.hasPages && (!pagerPosition || pagerPosition === 'top');
const showBottomPager =
pagedState.hasPages && (!pagerPosition || pagerPosition === 'bottom');
if (showBottomPager) {
tableProps.borderBottomRightRadius = '0';
tableProps.borderBottomLeftRadius = '0';
}
return (
<div>
{showTopPager && (
<StyledPanel borderTopRightRadius="3" borderTopLeftRadius="3">
<Pager {...pagedState} />
</StyledPanel>
)}
<Table {...tableProps} />
{showBottomPager && (
<StyledPanel borderBottomRightRadius="3" borderBottomLeftRadius="3">
<Pager {...pagedState} />
</StyledPanel>
)}
</div>
);
}
export const StyledPanel = styled.nav`
padding: 16px;
display: flex;
height: 24px;
align-items: center;
justify-content: space-between;
background: ${props => props.theme.colors.primary.light};
${borderRadius}
`;
@@ -0,0 +1,76 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import styled from 'styled-components';
import Icon, {
CircleArrowLeft,
CircleArrowRight,
} from 'design/Icon';
import { Text } from 'design';
export default function Pager(props) {
const { startFrom, endAt, totalRows, onPrev, onNext } = props;
const isPrevDisabled = startFrom === 0;
const isNextDisabled = endAt === totalRows;
return (
<>
<Text typography="body2" color="primary.contrastText">
SHOWING <strong>{startFrom + 1}</strong> to <strong>{endAt}</strong> of{' '}
<strong>{totalRows}</strong>
</Text>
<StyledButtons>
<button
onClick={onPrev}
title="Previous Page"
disabled={isPrevDisabled}
>
<CircleArrowLeft fontSize="3" />
</button>
<button onClick={onNext} title="Next Page" disabled={isNextDisabled}>
<CircleArrowRight fontSize="3" />
</button>
</StyledButtons>
</>
);
}
export const StyledButtons = styled.div`
button {
background: none;
border: none;
border-radius: 200px;
cursor: pointer;
height: 24px;
padding: 0;
margin: 0 2px;
min-width: 24px;
outline: none;
transition: all 0.3s;
&:hover {
background: ${props => props.theme.colors.primary.main};
${Icon} {
opacity: 1;
}
}
${Icon} {
opacity: 0.56;
transition: all 0.3s;
}
}
`;
@@ -0,0 +1,19 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Pager, { StyledButtons } from './Pager';
export default Pager;
export { StyledButtons };
@@ -0,0 +1,22 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import PagedTable from './Paged';
import Pager from './Pager';
import usePages from './usePages';
export default PagedTable;
export { usePages, Pager };
@@ -0,0 +1,67 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
export default function usePages({ pageSize = 7, data }){
const [ startFrom, setFrom ] = React.useState(0);
// set current page to 0 when data source length changes
React.useEffect(() => {
setFrom(0);
}, [data.length]);
function onPrev(){
let prevPage = startFrom - pageSize;
if(prevPage < 0){
prevPage = 0;
}
setFrom(prevPage);
}
function onNext(){
let nextPage = startFrom + pageSize;
if(nextPage < data.length){
nextPage = startFrom + pageSize;
setFrom(nextPage);
}
}
const totalRows = data.length;
let endAt = 0;
let pagedData = data;
if (data.length > 0){
endAt = startFrom + (pageSize > data.length ? data.length : pageSize);
if(endAt > data.length){
endAt = data.length;
}
pagedData = data.slice(startFrom, endAt);
}
return {
pageSize,
startFrom,
endAt,
totalRows,
data: pagedData,
hasPages: totalRows > pageSize,
onNext,
onPrev
}
}
@@ -0,0 +1,72 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import styled from 'styled-components';
import Icon from '../Icon';
import { space, borderRadius } from 'design/system';
export const StyledTable = styled.table`
background: ${props => props.theme.colors.primary.light };
box-shadow: 0 8px 32px rgba(0, 0, 0, .24);
border-collapse: collapse;
border-spacing: 0;
border-radius: 8px;
font-size: 12px;
width: 100%;
& > thead > tr > th,
& > tbody > tr > th,
& > tfoot > tr > th,
& > thead > tr > td,
& > tbody > tr > td,
& > tfoot > tr > td {
padding: 16px;
vertical-align: middle;
}
& > thead > tr > th {
background: ${props => props.theme.colors.primary.main };
color: rgba(255, 255, 255, .56);
cursor: pointer;
font-size: 10px;
font-weight: 600;
padding: 4px 16px;
text-align: left;
text-transform: uppercase;
${Icon} {
font-weight: bold;
margin-left: 8px;
}
}
${space}
${borderRadius}
`;
export const StyledEmptyIndicator = styled.div`
background: ${props => props.theme.colors.primary.main };
border-radius: 4px;
box-sizing: border-box;
margin: 48px auto;
max-width: 720px;
padding: 48px 32px;
text-align: center;
a {
color: ${props => props.theme.colors.link };
}
`
+219
View File
@@ -0,0 +1,219 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import Text from '../Text';
import { StyledTable, StyledEmptyIndicator } from './StyledTable';
import * as Icons from './../Icon/Icon';
/**
* Sort indicator used by SortHeaderCell
*/
const SortTypes = {
ASC: 'ASC',
DESC: 'DESC'
};
class Table extends React.Component {
renderHeader(children) {
const { data } = this.props;
const cells = children.map((item, index)=>{
return this.renderCell(
item.props.header,
{
index,
key: index,
isHeader: true,
data,
...item.props
});
})
return (
<thead>
<tr>{cells}</tr>
</thead>
)
}
renderBody(children) {
const { data } = this.props;
const rows = [];
for (let i = 0; i < data.length; i++){
let cells = children.map((item, index)=>{
return this.renderCell(
item.props.cell,
{
rowIndex: i,
key: index,
isHeader: false,
data,
...item.props
}
);
})
rows.push(<tr key={i}>{cells}</tr>);
}
if(rows.length) {
return <tbody>{rows}</tbody>;
}
return (
<tbody>
<tr>
<td align="center" colSpan={children ? children.length:0}>
<Text typography="paragraph" m="6" color="text.primary">
NO DATA AVAILABLE
</Text>
</td>
</tr>
</tbody>
);
}
renderCell(cell, cellProps){
if (React.isValidElement(cell)) {
return React.cloneElement(cell, cellProps);
}
return null;
}
render() {
/* eslint-disable no-unused-vars */
const { data, children, ...rest } = this.props;
/* eslint-enable no-unused-vars */
const columns = [];
React.Children.forEach(children, child => {
if (child == null) {
return;
}
if(!child.props._isColumn){
throw 'Should be Column';
}
columns.push(child);
});
return (
<StyledTable {...rest}>
{this.renderHeader(columns)}
{this.renderBody(columns)}
</StyledTable>
);
}
}
const SortIndicator = ({sortDir})=>{
if(sortDir === SortTypes.DESC){
return <Icons.SortDesc />;
}
if( sortDir === SortTypes.ASC){
return <Icons.SortAsc />;
}
return <Icons.Sort />;
};
class Column extends React.Component {
static defaultProps = {
_isColumn: true
};
render() {
throw new Error("Component Column should never render");
}
}
const Cell = props => {
const { isHeader, children, align, style, className } = props;
const childProps = {
children, align, style, className
}
if(isHeader) {
return <th {...childProps} />;
}
return <td {...childProps} />;
}
const TextCell = ({rowIndex, data, columnKey, ...props}) => (
<Cell {...props}>
{data[rowIndex][columnKey]}
</Cell>
);
class SortHeaderCell extends React.Component {
onSortChange = e => {
e.preventDefault();
if (!this.props.onSortChange) {
return;
}
const { sortDir, columnKey } = this.props;
// default
let newDir = SortTypes.DESC;
if(sortDir){
newDir = sortDir === SortTypes.DESC ? SortTypes.ASC : SortTypes.DESC;
}
this.props.onSortChange(columnKey, newDir);
}
render() {
const { sortDir, title, ...props } = this.props;
return (
<Cell {...props}>
<a onClick={this.onSortChange}>{title}</a>
<SortIndicator sortDir={sortDir}/>
</Cell>
);
}
}
class EmptyIndicator extends React.Component {
render() {
const {children, title} = this.props;
const noResults = title || "No Results Found";
return (
<StyledEmptyIndicator>
<Text typography="h1" color="text.primary">{noResults}</Text>
<Text typography="paragraph" mt="3" color="text.primary">{children}</Text>
</StyledEmptyIndicator>
);
}
}
export {
Column,
Table,
Cell,
TextCell,
SortHeaderCell,
SortIndicator,
SortTypes,
EmptyIndicator
};
@@ -0,0 +1,165 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react'
import { sortBy } from 'lodash';
import { storiesOf } from '@storybook/react'
import match from './../utils/match';
import {
Table,
TablePaged,
Column,
TextCell,
SortHeaderCell,
SortTypes,
EmptyIndicator
} from './../DataTable';
storiesOf('Desigin/DataTable', module)
.add('DataTable', () => (
<TableSample TableComponent={Table} data={data}/>
))
.add('Empty', () => (
<TableSample TableComponent={Table} data={[]}/>
))
.add('Nothing found', () => (
<TableSample TableComponent={Table} data={data} filter="no_results"/>
))
storiesOf('Desigin/DataTable/Paged', module)
.add('Page size 3', () => (
<TableSample TableComponent={TablePaged} tableProps={ { pageSize: 3 }} data={data}/>
))
class TableSample extends React.Component {
searchableProps = ['addr', 'hostname', 'tags'];
constructor(props) {
super(props);
this.state = {
filter: props.filter || '',
colSortDirs: {
hostname: SortTypes.DESC
}
};
}
onSortChange = (columnKey, sortDir) => {
this.state.colSortDirs = { [columnKey]: sortDir };
this.setState(this.state);
}
onFilterChange = value => {
this.state.filter = value;
this.setState(this.state);
}
searchAndFilterCb(targetValue, searchValue, propName){
if(propName === 'tags'){
return targetValue.some((item) => {
const { name, value } = item;
return name.toLocaleUpperCase().indexOf(searchValue) !==-1 ||
value.toLocaleUpperCase().indexOf(searchValue) !==-1;
});
}
}
sortAndFilter(data) {
const { colSortDirs } = this.state;
const filtered = data
.filter(obj => match(obj, this.state.filter, {
searchableProps: this.searchableProps,
cb: this.searchAndFilterCb
}));
const columnKey = Object.getOwnPropertyNames(colSortDirs)[0];
const sortDir = colSortDirs[columnKey];
let sorted = sortBy(filtered, columnKey);
if(sortDir === SortTypes.ASC){
sorted = sorted.reverse();
}
return sorted;
}
render() {
let { data, TableComponent, tableProps } = this.props;
data = this.sortAndFilter(data);
const nothingFound = data.length === 0 && this.state.filter.length > 0;
if (nothingFound) {
return (
<EmptyIndicator title='No Results Found for "X458AAZ"'>
For tips on getting better search results, please read <a href="https://gravitational.com/teleport/docs">our documentation</a>
</EmptyIndicator>
)
}
const props = {
data: data,
...tableProps
}
return (
<TableComponent {...props} >
<Column
columnKey="hostname"
header={
<SortHeaderCell
sortDir={this.state.colSortDirs.hostname}
onSortChange={this.onSortChange}
title="Hostname"
/>
}
cell={<TextCell /> }
/>
<Column
columnKey="addr"
header={
<SortHeaderCell
sortDir={this.state.colSortDirs.addr}
onSortChange={this.onSortChange}
title="Address"
/>
}
cell={<TextCell /> }
/>
</TableComponent>
)
}
}
const data = [{
hostname: <strong>host-a</strong>,
addr: '192.168.7.1'
},
{
hostname: <strong>host-b</strong>,
addr: '192.168.7.2'
},
{
hostname: <strong>host-c</strong>,
addr: '192.168.7.3'
},
{
hostname: <strong>host-d</strong>,
addr: '192.168.7.4'
},
{
hostname: <strong>host-3</strong>,
addr: '192.168.7.4'
}];
@@ -0,0 +1,40 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import {
Column,
Table,
Cell,
TextCell,
SortHeaderCell,
SortIndicator,
SortTypes,
EmptyIndicator
} from './Table';
import TablePaged from './Paged';
export {
Column,
Table,
TablePaged,
Cell,
TextCell,
SortHeaderCell,
SortIndicator,
SortTypes,
EmptyIndicator
}
+107
View File
@@ -0,0 +1,107 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import Modal from './../Modal';
import styled from 'styled-components';
class Dialog extends React.Component {
handleBackdropClick = event => {
if (event.target !== event.currentTarget) {
return;
}
if (this.props.onBackdropClick) {
this.props.onBackdropClick(event);
}
if (!this.props.disableBackdropClick && this.props.onClose) {
this.props.onClose(event, 'backdropClick');
}
};
render() {
const {
children,
disableBackdropClick,
disableEscapeKeyDown,
onBackdropClick,
onClose,
onEscapeKeyDown,
open,
dialogCss,
...other
} = this.props;
return (
<Modal
disableBackdropClick={disableBackdropClick}
disableEscapeKeyDown={disableEscapeKeyDown}
onBackdropClick={onBackdropClick}
onEscapeKeyDown={onEscapeKeyDown}
onClose={onClose}
open={open}
role="dialog"
{...other}
>
<ModalBox>
<DialogBox dialogCss={dialogCss}>
{children}
</DialogBox>
</ModalBox>
</Modal>
);
}
}
Dialog.defaultProps = {
disableBackdropClick: false,
disableEscapeKeyDown: false,
fullScreen: false,
fullWidth: false,
};
const ModalBox = styled.div`
height: 100%;
outline: none;
color: black;
display: flex;
align-items: center;
justify-content: center;
opacity: 1;
will-change: opacity;
transition: opacity 225ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
`
const DialogBox = styled.div`
padding: 32px;
padding-top: 24px;
background: ${props => props.theme.colors.primary.main };
color: ${props => props.theme.colors.text.primary};
border-radius: 8px;
box-shadow: 0 8px 32px rgba(0, 0, 0, .24);
display: flex;
flex-direction: column;
position: relative;
overflow-y: auto;
max-height: calc(100% - 96px);
${props => props.dialogCss && props.dialogCss(props) }
`
export default Dialog;
@@ -0,0 +1,41 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react'
import { storiesOf } from '@storybook/react'
import Dialog, { DialogHeader, DialogContent, DialogFooter, DialogTitle } from './index';
import { ButtonPrimary, Input, LabelInput } from './..';
storiesOf('Desigin/Dialog', module)
.add('Simple Dialog', () => (
<Dialog open={true}>
<DialogHeader>
<DialogTitle>
Hello
</DialogTitle>
</DialogHeader>
<DialogContent width="400px">
<LabelInput>Name</LabelInput>
<Input />
</DialogContent>
<DialogFooter>
<ButtonPrimary onClick={ () => null} >
Save And Close
</ButtonPrimary>
</DialogFooter>
</Dialog>
)
);
@@ -0,0 +1,29 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import styled from 'styled-components';
import { Flex } from 'design';
const DialogContent = styled(Flex)`
`
DialogContent.defaultProps = {
...Flex.defaultProps,
mb: "5",
flex: "1",
flexDirection: "column"
}
export default DialogContent;
@@ -0,0 +1,26 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import styled from 'styled-components';
import { Box } from 'design';
const DialogFooter = styled(Box)``
DialogFooter.defaultProps = {
...Box.defaultProps,
}
export default DialogFooter;
@@ -0,0 +1,30 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import Flex from './../Flex';
import styled from 'styled-components';
import { typography } from 'design/system';
export default function DialogHeader(props){
return (
<StyledDialogHeader minHeight="32px" mb="3" alignItems="center" {...props} />
)
}
const StyledDialogHeader = styled(Flex)`
${typography}
`
@@ -0,0 +1,24 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import Text from './../Text';
export default function DialogTitle(props){
return (
<Text typography="h2" color="text.primary" caps {...props} />
)
}
+29
View File
@@ -0,0 +1,29 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Dialog from './Dialog';
import DialogTitle from './DialogTitle';
import DialogHeader from './DialogHeader';
import DialogContent from './DialogContent';
import DialogFooter from './DialogFooter';
export default Dialog;
export {
DialogTitle,
DialogContent,
DialogFooter,
DialogHeader
}
@@ -0,0 +1,34 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import Dialog from 'design/Dialog';
function DialogConfirmation(props){
const { children, open, onClose, dialogCss } = props;
return (
<Dialog
dialogCss={dialogCss}
disableEscapeKeyDown={false}
onClose={onClose}
open={open}
>
{children}
</Dialog>
)
}
export default DialogConfirmation;
@@ -0,0 +1,39 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react'
import { storiesOf } from '@storybook/react'
import DialogConfirmation, { DialogHeader, DialogContent, DialogFooter, DialogTitle } from './index';
import { ButtonPrimary } from './../Button';
storiesOf('Desigin/DialogConfirmation', module)
.add('Simple DialogConfirmation', () => (
<DialogConfirmation open={true} >
<DialogHeader>
<DialogTitle>
Hello
</DialogTitle>
</DialogHeader>
<DialogContent width="400px">
DIALOG CONTENT AREA
</DialogContent>
<DialogFooter>
<ButtonPrimary>
Save And Close
</ButtonPrimary>
</DialogFooter>
</DialogConfirmation>
));
@@ -0,0 +1,26 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import DialogConfirmation from './DialogConfirmation';
import { DialogTitle, DialogContent, DialogFooter, DialogHeader } from './../Dialog';
export default DialogConfirmation;
export {
DialogTitle,
DialogContent,
DialogFooter,
DialogHeader
}
@@ -0,0 +1,64 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
export default class DocumentTitle extends React.Component {
componentDidUpdate(prevProps) {
if (prevProps.title !== this.props.title) {
this.setTitle(this.props.title);
}
}
componentDidMount() {
this.setTitle(this.props.title);
}
getTitle() {
return document.title;
}
setTitle(title) {
document.title = title;
}
render() {
return this.props.children;
}
}
export const withDocTitle = (title, component) => {
return class WithWindowTitle extends React.Component{
static displayName = `withDocTitleWrapper`
componentDidMount() {
this.setTitle(title);
}
setTitle(title) {
document.title = title;
}
render() {
return React.createElement(component, {
...this.props,
});
}
}
}
@@ -0,0 +1,21 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import DocumentTitle, { withDocTitle } from './DocumentTitle';
export default DocumentTitle;
export {
withDocTitle
}
+41
View File
@@ -0,0 +1,41 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import styled from 'styled-components';
import { alignItems, justifyContent, flexWrap, flexDirection, propTypes } from 'design/system';
import theme from 'design/theme';
import Box from '../Box';
const Flex = styled(Box)`
display: flex;
${alignItems} ${justifyContent} ${flexWrap} ${flexDirection};
`
Flex.defaultProps = {
theme
}
Flex.propTypes = {
...propTypes.Box,
...propTypes.alignItems,
...propTypes.justifyContent,
...propTypes.flexWrap,
...propTypes.flexDirection
}
Flex.displayName = 'Flex'
export default Flex;
@@ -0,0 +1,42 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import { storiesOf } from '@storybook/react'
import { withInfo } from '@storybook/addon-info'
import Flex from './Flex';
import Box from './../Box';
storiesOf('Desigin/Flex', module)
.addDecorator(withInfo)
.add('Basic', () => (
<Flex align='center'>
<Box width={1 / 2} p={3} m={4} color='white' bg='blue'>Flex</Box>
<Box width={1 / 2} p={1} m={2} color='white' bg='green'>Box</Box>
</Flex>
))
.add('Wrap', () => (
<Flex flexWrap="wrap">
<Box width={[1, 1 / 2]} p={3} color='white' bg='blue'>Flex</Box>
<Box width={[1, 1 / 2]} p={1} color='white' bg='green'>Wrap</Box>
</Flex>
))
.add('Justify', () => (
<Flex justifyContent='space-around'>
<Box width={1 / 3} p={2} color='white' bg='blue'>Flex</Box>
<Box width={1 / 3} p={2} color='white' bg='green'>Justify</Box>
</Flex>
));
+18
View File
@@ -0,0 +1,18 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Flex from './Flex';
export default Flex;
+244
View File
@@ -0,0 +1,244 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import styled from 'styled-components';
import { space, fontSize, width, color } from 'styled-system';
import './../assets/icomoon/style.css';
const Icon = styled.span`
display: inline-block;
transition: color .3s;
${space} ${width} ${color} ${fontSize}
`;
Icon.displayName = `Icon`;
Icon.defaultProps = {
color: 'light',
};
function makeFontIcon(name, iconClassName) {
const iconClass = `icon ${iconClassName}`;
return function({ className = '', ...rest }) {
const classes = `${iconClass} ${className}`;
return <Icon className={classes} {...rest} />;
};
}
export const AddUsers = makeFontIcon('AddUsers', 'icon-users-plus');
export const Amex = makeFontIcon('Amex', 'icon-cc-amex');
export const Apartment = makeFontIcon('Apartment', 'icon-apartment');
export const AppInstalled = makeFontIcon('AppInstalled', 'icon-app-installed');
export const Apple = makeFontIcon('Apple', 'icon-apple');
export const AppRollback = makeFontIcon('AppRollback', 'icon-app-rollback');
export const Archive = makeFontIcon('Archive', 'icon-archive2');
export const ArrowDown = makeFontIcon('ArrowDown', 'icon-chevron-down');
export const ArrowLeft = makeFontIcon('ArrowLeft', 'icon-chevron-left');
export const ArrowRight = makeFontIcon('ArrowRight', 'icon-chevron-right');
export const ArrowsVertical = makeFontIcon(
'ArrowsVertical',
'icon-chevrons-expand-vertical'
);
export const ArrowUp = makeFontIcon('ArrowUp', 'icon-chevron-up');
export const BitBucket = makeFontIcon('Bitbucket', 'icon-bitbucket');
export const Bubble = makeFontIcon('Bubble', 'icon-bubble');
export const Calendar = makeFontIcon('Calendar', 'icon-calendar-empty');
export const Camera = makeFontIcon('Camera', 'icon-camera');
export const CardView = makeFontIcon('CardView', 'icon-th-large');
export const CardViewSmall = makeFontIcon('CardViewSmall', 'icon-th');
export const CaretLeft = makeFontIcon('CaretLeft', 'icon-caret-left');
export const CaretRight = makeFontIcon('CaretRight', 'icon-caret-right');
export const CarrotDown = makeFontIcon('CarrotDown', 'icon-caret-down');
export const CarrotLeft = makeFontIcon('CarrotLeft', 'icon-caret-left');
export const CarrotRight = makeFontIcon('CarrotRight', 'icon-caret-right');
export const CarrotSort = makeFontIcon('CarrotSort', 'icon-sort');
export const CarrotUp = makeFontIcon('CarrotUp', 'icon-caret-up');
export const Cash = makeFontIcon('Cash', 'icon-cash-dollar');
export const ChevronCircleDown = makeFontIcon(
'ChevronCircleDown',
'icon-chevron-down-circle'
);
export const ChevronCircleLeft = makeFontIcon(
'ChevronCircleLeft',
'icon-chevron-left-circle'
);
export const ChevronCircleRight = makeFontIcon(
'ChevronCircleRight',
'icon-chevron-right-circle'
);
export const ChevronCircleUp = makeFontIcon(
'ChevronCircleUp',
'icon-chevron-up-circle'
);
export const CircleArrowLeft = makeFontIcon(
'CircleArrowLeft',
'icon-arrow-left-circle'
);
export const CircleArrowRight = makeFontIcon(
'CircleArrowRight',
'icon-arrow-right-circle'
);
export const CircleCheck = makeFontIcon('CircleCheck', 'icon-checkmark-circle');
export const CircleCross = makeFontIcon('CircleCross', 'icon-cross-circle');
export const CirclePause = makeFontIcon('CirclePause', 'icon-pause-circle');
export const CirclePlay = makeFontIcon('CirclePlay', 'icon-play-circle');
export const CircleStop = makeFontIcon('CircleStop', 'icon-stop-circle');
export const Cli = makeFontIcon('Cli', 'icon-terminal');
export const Clipboard = makeFontIcon('Clipboard', 'icon-clipboard-text');
export const ClipboardUser = makeFontIcon(
'ClipboardUser',
'icon-clipboard-user'
);
export const Close = makeFontIcon('Close', 'icon-close');
export const Cloud = makeFontIcon('Cloud', 'icon-cloud');
export const Cluster = makeFontIcon('Cluster', 'icon-site-map');
export const ClusterAdded = makeFontIcon('ClusterAdded', 'icon-cluster-added');
export const ClusterAuth = makeFontIcon('ClusterAuth', 'icon-cluster-auth');
export const Code = makeFontIcon('Code', 'icon-code');
export const Cog = makeFontIcon('Cog', 'icon-cog');
export const Config = makeFontIcon('Config', 'icon-config');
export const Contract = makeFontIcon('Contract', 'icon-frame-contract');
export const CreditCard = makeFontIcon('CreditCard', 'icon-credit-card1');
export const CreditCardAlt = makeFontIcon(
'CreditCardAlt',
'icon-credit-card-alt'
);
export const CreditCardAlt2 = makeFontIcon(
'CreditCardAlt2',
'icon-credit-card'
);
export const Cross = makeFontIcon('Cross', 'icon-cross');
export const Database = makeFontIcon('Database', 'icon-database');
export const Discover = makeFontIcon('Discover', 'icon-cc-discover');
export const Download = makeFontIcon('Download', 'icon-get_app');
export const Earth = makeFontIcon('Earth', 'icon-earth');
export const Edit = makeFontIcon('Edit', 'icon-pencil4');
export const Ellipsis = makeFontIcon('Ellipsis', 'icon-ellipsis');
export const EmailSolid = makeFontIcon('EmailSolid', 'icon-email-solid');
export const Equalizer = makeFontIcon('Equalizer', 'icon-equalizer');
export const Expand = makeFontIcon('Expand', 'icon-frame-expand');
export const Facebook = makeFontIcon('Facebook', 'icon-facebook');
export const FacebookSquare = makeFontIcon('FacebookSquare', 'icon-facebook2');
export const FileCode = makeFontIcon('Youtube', 'icon-file-code');
export const ForwarderAdded = makeFontIcon(
'ForwarderAdded',
'icon-add-fowarder'
);
export const Github = makeFontIcon('Github', 'icon-github');
export const Google = makeFontIcon('Google', 'icon-google-plus');
export const Graph = makeFontIcon('Graph', 'icon-graph');
export const Home = makeFontIcon('Home', 'icon-home3');
export const Keypair = makeFontIcon('Keypair', 'icon-keypair');
export const Kubernetes = makeFontIcon('Kubernetes', 'icon-kubernetes');
export const Label = makeFontIcon('Label', 'icon-label');
export const Lan = makeFontIcon('Lan', 'icon-lan');
export const LanAlt = makeFontIcon('LanAlt', 'icon-lan2');
export const Layers = makeFontIcon('Layers', 'icon-layers');
export const Layers1 = makeFontIcon('Layers1', 'icon-layers1');
export const License = makeFontIcon('License', 'icon-license2');
export const Link = makeFontIcon('Link', 'icon-link');
export const Linkedin = makeFontIcon('Linkedin', 'icon-linkedin');
export const Linux = makeFontIcon('Linux', 'icon-linux');
export const List = makeFontIcon('List', 'icon-list');
export const ListAddCheck = makeFontIcon(
'ListAddCheck',
'icon-playlist_add_check'
);
export const ListBullet = makeFontIcon('ListBullet', 'icon-list4');
export const ListCheck = makeFontIcon('ListCheck', 'icon-list3');
export const ListView = makeFontIcon('ListView', 'icon-th-list');
export const LocalPlay = makeFontIcon('LocalPlay', 'icon-local_play');
export const Lock = makeFontIcon('Lock', 'icon-lock');
export const Magnifier = makeFontIcon('Magnifier', 'icon-magnifier');
export const MapMarker = makeFontIcon('MapMarker', 'icon-map-marker');
export const MasterCard = makeFontIcon('MasterCard', 'icon-cc-mastercard');
export const Memory = makeFontIcon('Memory', 'icon-memory');
export const MoreHoriz = makeFontIcon('MoreHoriz', 'icon-more_horiz');
export const MoreVert = makeFontIcon('MoreVert', 'icon-more_vert');
export const Mute = makeFontIcon('Mute', 'icon-mute');
export const NoteAdded = makeFontIcon('NoteAdded', 'icon-note_add');
export const NotificationsActive = makeFontIcon(
'NotificationsActive',
'icon-notifications_active'
);
export const OpenID = makeFontIcon('OpenID', 'icon-openid');
export const Paypal = makeFontIcon('Paypal', 'icon-cc-paypal');
export const Pencil = makeFontIcon('Pencil', 'icon-pencil');
export const Person = makeFontIcon('Person', 'icon-person');
export const PersonAdd = makeFontIcon('PersonAdd', 'icon-person_add');
export const Phone = makeFontIcon('Phone', 'icon-telephone');
export const PhonelinkErase = makeFontIcon(
'PhonelinkErase',
'icon-phonelink_erase'
);
export const PhonelinkSetup = makeFontIcon(
'PhonelinkSetup',
'icon-phonelink_setup'
);
export const Planet = makeFontIcon('Planet', 'icon-planet');
export const Play = makeFontIcon('Play', 'icon-play');
export const Power = makeFontIcon('Power', 'icon-power');
export const Profile = makeFontIcon('Profile', 'icon-profile');
export const Restore = makeFontIcon('Restore', 'icon-restore');
export const Server = makeFontIcon('Server', 'icon-server');
export const SettingsInputComposite = makeFontIcon(
'SettingsInputComposite',
'icon-settings_input_composite'
);
export const SettingsOverscan = makeFontIcon(
'SettingsOverscan',
'icon-settings_overscan'
);
export const Shart = makeFontIcon('Shart', 'icon-chart-bars');
export const ShieldCheck = makeFontIcon('ShieldCheck', 'icon-shield-check');
export const Shrink = makeFontIcon('Shrink', 'icon-shrink');
export const Signal = makeFontIcon('Signal', 'icon-signal');
export const SmallArrowDown = makeFontIcon(
'SmallArrowDown',
'icon-arrow_drop_down'
);
export const SmallArrowUp = makeFontIcon(
'SmallArrowDown',
'icon-arrow_drop_up'
);
export const SmartPhone = makeFontIcon('SmartPhone', 'icon-smartphone-embed');
export const Sort = makeFontIcon('Sort', 'icon-chevrons-expand-vertical');
export const SortAsc = makeFontIcon('SortAsc', 'icon-chevron-up');
export const SortDesc = makeFontIcon('SortDesc', 'icon-chevron-down');
export const Speed = makeFontIcon('Speed', 'icon-speed-fast');
export const Spinner = makeFontIcon('Spinner', 'icon-spinner8');
export const Stars = makeFontIcon('Stars', 'icon-stars');
export const Stripe = makeFontIcon('Stripe', 'icon-cc-stripe');
export const Tablet = makeFontIcon('Tablet', 'icon-tablet2');
export const Trash = makeFontIcon('Trash', 'icon-trash2');
export const Twitter = makeFontIcon('Twitter', 'icon-twitter');
export const Unarchive = makeFontIcon('Unarchive', 'icon-unarchive');
export const Unlock = makeFontIcon('Unlock', 'icon-unlock');
export const Upload = makeFontIcon('Upload', 'icon-file_upload');
export const User = makeFontIcon('User', 'icon-user');
export const UserCreated = makeFontIcon('UserCreated', 'icon-user-created');
export const Users = makeFontIcon('Users', 'icon-users2');
export const VideoGame = makeFontIcon('VideoGame', 'icon-videogame_asset');
export const Visa = makeFontIcon('Visa', 'icon-cc-visa');
export const VolumeUp = makeFontIcon('VolumeUp', 'icon-volume-high');
export const VpnKey = makeFontIcon('VpnKey', 'icon-vpn_key');
export const Warning = makeFontIcon('Warning', 'icon-warning');
export const Wifi = makeFontIcon('Wifi', 'icon-wifi');
export const Window = makeFontIcon('Window', 'icon-window');
export const Windows = makeFontIcon('Windows', 'icon-windows');
export const Youtube = makeFontIcon('Youtube', 'icon-youtube');
export default Icon;
+197
View File
@@ -0,0 +1,197 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import { storiesOf } from '@storybook/react';
import styled from 'styled-components'
import * as Icon from '../Icon';
storiesOf('Desigin/Icon', module)
.add('Icon components', () => (
<Container>
<IconBox IconCmpt={Icon.AddUsers} text="AddUsers"/>
<IconBox IconCmpt={Icon.Amex} text="Amex"/>
<IconBox IconCmpt={Icon.Apartment} text="Apartment"/>
<IconBox IconCmpt={Icon.AppInstalled} text="AppInstalled"/>
<IconBox IconCmpt={Icon.Apple} text="Apple"/>
<IconBox IconCmpt={Icon.AppRollback} text="AppRollback"/>
<IconBox IconCmpt={Icon.Archive} text="Archive"/>
<IconBox IconCmpt={Icon.ArrowDown} text="ArrowDown"/>
<IconBox IconCmpt={Icon.ArrowLeft} text="ArrowLeft"/>
<IconBox IconCmpt={Icon.ArrowRight} text="ArrowRight"/>
<IconBox IconCmpt={Icon.ArrowsVertical} text="ArrowsVertical"/>
<IconBox IconCmpt={Icon.ArrowUp} text="ArrowUp"/>
<IconBox IconCmpt={Icon.Bubble} text="Bubble"/>
<IconBox IconCmpt={Icon.Calendar} text="Calendar"/>
<IconBox IconCmpt={Icon.Camera} text="Camera"/>
<IconBox IconCmpt={Icon.CardView} text="CardView"/>
<IconBox IconCmpt={Icon.CardViewSmall} text="CardViewSmall"/>
<IconBox IconCmpt={Icon.CaretLeft} text="CaretLeft"/>
<IconBox IconCmpt={Icon.CaretRight} text="CaretRight"/>
<IconBox IconCmpt={Icon.CarrotDown} text="CarrotDown"/>
<IconBox IconCmpt={Icon.CarrotLeft} text="CarrotLeft"/>
<IconBox IconCmpt={Icon.CarrotRight} text="CarrotRight"/>
<IconBox IconCmpt={Icon.CarrotSort} text="CarrotSort"/>
<IconBox IconCmpt={Icon.CarrotUp} text="CarrotUp"/>
<IconBox IconCmpt={Icon.Cash} text="Cash"/>
<IconBox IconCmpt={Icon.ChevronCircleDown} text="ChevronCircleDown"/>
<IconBox IconCmpt={Icon.ChevronCircleLeft} text="ChevronCircleLeft"/>
<IconBox IconCmpt={Icon.ChevronCircleRight} text="ChevronCircleRight"/>
<IconBox IconCmpt={Icon.ChevronCircleUp} text="ChevronCircleUp"/>
<IconBox IconCmpt={Icon.CircleArrowLeft} text="CircleArrowLeft"/>
<IconBox IconCmpt={Icon.CircleArrowRight} text="CircleArrowRight"/>
<IconBox IconCmpt={Icon.CircleCheck} text="CircleCheck"/>
<IconBox IconCmpt={Icon.CircleCross} text="CircleCross"/>
<IconBox IconCmpt={Icon.CirclePause} text="CirclePause"/>
<IconBox IconCmpt={Icon.CirclePlay} text="CirclePlay"/>
<IconBox IconCmpt={Icon.CircleStop} text="CircleStop"/>
<IconBox IconCmpt={Icon.Cli} text="Cli"/>
<IconBox IconCmpt={Icon.Clipboard} text="Clipboard"/>
<IconBox IconCmpt={Icon.ClipboardUser} text="ClipboardUser"/>
<IconBox IconCmpt={Icon.Close} text="Close"/>
<IconBox IconCmpt={Icon.Cloud} text="Cloud"/>
<IconBox IconCmpt={Icon.Cluster} text="Cluster"/>
<IconBox IconCmpt={Icon.ClusterAdded} text="ClusterAdded"/>
<IconBox IconCmpt={Icon.ClusterAuth} text="ClusterAuth"/>
<IconBox IconCmpt={Icon.Code} text="Code"/>
<IconBox IconCmpt={Icon.Cog} text="Cog"/>
<IconBox IconCmpt={Icon.Config} text="Config"/>
<IconBox IconCmpt={Icon.Contract} text="Contract"/>
<IconBox IconCmpt={Icon.CreditCard} text="CreditCard"/>
<IconBox IconCmpt={Icon.CreditCardAlt} text="CreditCardAlt"/>
<IconBox IconCmpt={Icon.CreditCardAlt2} text="CreditCardAlt2"/>
<IconBox IconCmpt={Icon.Cross} text="Cross"/>
<IconBox IconCmpt={Icon.Database} text="Database"/>
<IconBox IconCmpt={Icon.Discover} text="Discover"/>
<IconBox IconCmpt={Icon.Download} text="Download"/>
<IconBox IconCmpt={Icon.Earth} text="Earth"/>
<IconBox IconCmpt={Icon.Edit} text="Edit"/>
<IconBox IconCmpt={Icon.Ellipsis} text="Ellipsis"/>
<IconBox IconCmpt={Icon.EmailSolid} text="EmailSolid"/>
<IconBox IconCmpt={Icon.Equalizer} text="Equalizer"/>
<IconBox IconCmpt={Icon.Expand} text="Expand"/>
<IconBox IconCmpt={Icon.Facebook} text="Facebook"/>
<IconBox IconCmpt={Icon.FacebookSquare} text="FacebookSquare"/>
<IconBox IconCmpt={Icon.ForwarderAdded} text="ForwarderAdded"/>
<IconBox IconCmpt={Icon.Github} text="Github"/>
<IconBox IconCmpt={Icon.Google} text="Google"/>
<IconBox IconCmpt={Icon.Graph} text="Graph"/>
<IconBox IconCmpt={Icon.Home} text="Home"/>
<IconBox IconCmpt={Icon.Keypair} text="Keypair"/>
<IconBox IconCmpt={Icon.Label} text="Label"/>
<IconBox IconCmpt={Icon.Lan} text="Lan"/>
<IconBox IconCmpt={Icon.LanAlt} text="LanAlt"/>
<IconBox IconCmpt={Icon.Layers} text="Layers"/>
<IconBox IconCmpt={Icon.Layers1} text="Layers1"/>
<IconBox IconCmpt={Icon.License} text="License"/>
<IconBox IconCmpt={Icon.Link} text="Link"/>
<IconBox IconCmpt={Icon.Linkedin} text="Linkedin"/>
<IconBox IconCmpt={Icon.Linux} text="Linux"/>
<IconBox IconCmpt={Icon.List} text="List"/>
<IconBox IconCmpt={Icon.ListAddCheck} text="ListAddCheck"/>
<IconBox IconCmpt={Icon.ListBullet} text="ListBullet"/>
<IconBox IconCmpt={Icon.ListCheck} text="ListCheck"/>
<IconBox IconCmpt={Icon.ListView} text="ListView"/>
<IconBox IconCmpt={Icon.LocalPlay} text="LocalPlay"/>
<IconBox IconCmpt={Icon.Lock} text="Lock"/>
<IconBox IconCmpt={Icon.Magnifier} text="Magnifier"/>
<IconBox IconCmpt={Icon.MapMarker} text="MapMarker"/>
<IconBox IconCmpt={Icon.MasterCard} text="MasterCard"/>
<IconBox IconCmpt={Icon.Memory} text="Memory" />
<IconBox IconCmpt={Icon.MoreHoriz} text="MoreHoriz"/>
<IconBox IconCmpt={Icon.MoreVert} text="MoreVert"/>
<IconBox IconCmpt={Icon.Mute} text="Mute"/>
<IconBox IconCmpt={Icon.NoteAdded} text="NoteAdded"/>
<IconBox IconCmpt={Icon.NotificationsActive} text="NotificationsActive"/>
<IconBox IconCmpt={Icon.Paypal} text="Paypal"/>
<IconBox IconCmpt={Icon.Pencil} text="Pencil"/>
<IconBox IconCmpt={Icon.Person} text="Person"/>
<IconBox IconCmpt={Icon.PersonAdd} text="PersonAdd"/>
<IconBox IconCmpt={Icon.Phone} text="Phone"/>
<IconBox IconCmpt={Icon.PhonelinkErase} text="PhonelinkErase"/>
<IconBox IconCmpt={Icon.PhonelinkSetup} text="PhonelinkSetup"/>
<IconBox IconCmpt={Icon.Planet} text="Planet"/>
<IconBox IconCmpt={Icon.Play} text="Play"/>
<IconBox IconCmpt={Icon.Power} text="Power"/>
<IconBox IconCmpt={Icon.Profile} text="Profile"/>
<IconBox IconCmpt={Icon.Restore} text="Restore"/>
<IconBox IconCmpt={Icon.Server} text="Server"/>
<IconBox IconCmpt={Icon.SettingsInputComposite} text="SettingsInputComposite"/>
<IconBox IconCmpt={Icon.SettingsOverscan} text="SettingsOverscan"/>
<IconBox IconCmpt={Icon.Shart} text="Shart"/>
<IconBox IconCmpt={Icon.ShieldCheck} text="ShieldCheck"/>
<IconBox IconCmpt={Icon.Shrink} text="Shrink"/>
<IconBox IconCmpt={Icon.Signal} text="Signal"/>
<IconBox IconCmpt={Icon.SmallArrowDown} text="SmallArrowDown"/>
<IconBox IconCmpt={Icon.SmallArrowUp} text="SmallArrowUp"/>
<IconBox IconCmpt={Icon.SmartPhone} text="SmartPhone"/>
<IconBox IconCmpt={Icon.Sort} text="Sort"/>
<IconBox IconCmpt={Icon.SortAsc} text="SortAsc"/>
<IconBox IconCmpt={Icon.SortDesc} text="SortDesc"/>
<IconBox IconCmpt={Icon.Speed} text="Speed"/>
<IconBox IconCmpt={Icon.Spinner} text="Spinner"/>
<IconBox IconCmpt={Icon.Stars} text="Stars"/>
<IconBox IconCmpt={Icon.Stripe} text="Stripe"/>
<IconBox IconCmpt={Icon.Tablet} text="Tablet"/>
<IconBox IconCmpt={Icon.Trash} text="Trash"/>
<IconBox IconCmpt={Icon.Twitter} text="Twitter"/>
<IconBox IconCmpt={Icon.Unarchive} text="Unarchive" />
<IconBox IconCmpt={Icon.Unlock} text="Unlock"/>
<IconBox IconCmpt={Icon.Upload} text="Upload"/>
<IconBox IconCmpt={Icon.User} text="User"/>
<IconBox IconCmpt={Icon.UserCreated} text="UserCreated"/>
<IconBox IconCmpt={Icon.Users} text="Users"/>
<IconBox IconCmpt={Icon.VideoGame} text="VideoGame"/>
<IconBox IconCmpt={Icon.Visa} text="Visa"/>
<IconBox IconCmpt={Icon.VolumeUp} text="VolumeUp"/>
<IconBox IconCmpt={Icon.VpnKey} text="VpnKey"/>
<IconBox IconCmpt={Icon.Warning} text="Warning"/>
<IconBox IconCmpt={Icon.Wifi} text="Wifi"/>
<IconBox IconCmpt={Icon.Window} text="Window"/>
<IconBox IconCmpt={Icon.Windows} text="Windows"/>
<IconBox IconCmpt={Icon.Youtube} text="Youtube"/>
<IconBox IconCmpt={Icon.Kubernetes} text="Kubernetes"/>
</Container>
));
const Container = styled.div`
font-size: 24px;
display: flex;
flex-direction: row;
flex-wrap: wrap;
`
const IconBox = ({IconCmpt, text}) => (
<Box>
<IconCmpt/>
{text}
</Box>
)
const Box = styled.div`
display: flex;
margin: 10px 10px;
width: 300px;
font-size: 14px;
.icon {
display: inline-block;
margin: 0 10px;
font-size: 16px;
}
`
+312
View File
@@ -0,0 +1,312 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Icon, {
AddUsers,
Amex,
Apartment,
AppInstalled,
Apple,
AppRollback,
Archive,
ArrowDown,
ArrowLeft,
ArrowRight,
ArrowsVertical,
ArrowUp,
BitBucket,
Bubble,
Calendar,
Camera,
CardView,
CardViewSmall,
CaretLeft,
CaretRight,
CarrotDown,
CarrotLeft,
CarrotRight,
CarrotSort,
CarrotUp,
Cash,
ChevronCircleDown,
ChevronCircleLeft,
ChevronCircleRight,
ChevronCircleUp,
CircleArrowLeft,
CircleArrowRight,
CircleCheck,
CircleCross,
CirclePause,
CirclePlay,
CircleStop,
Cli,
Clipboard,
ClipboardUser,
Close,
Cloud,
Cluster,
ClusterAdded,
ClusterAuth,
Code,
Cog,
Config,
Contract,
CreditCard,
CreditCardAlt,
CreditCardAlt2,
Cross,
Database,
Discover,
Download,
Earth,
Edit,
Ellipsis,
EmailSolid,
Equalizer,
Expand,
Facebook,
FacebookSquare,
FileCode,
ForwarderAdded,
Github,
Google,
Graph,
Home,
Keypair,
Kubernetes,
Label,
Lan,
LanAlt,
Layers,
Layers1,
License,
Link,
Linkedin,
Linux,
List,
ListAddCheck,
ListBullet,
ListCheck,
ListView,
LocalPlay,
Lock,
Magnifier,
MapMarker,
MasterCard,
Memory,
MoreHoriz,
MoreVert,
Mute,
NoteAdded,
NotificationsActive,
OpenID,
Paypal,
Pencil,
Person,
PersonAdd,
Phone,
PhonelinkErase,
PhonelinkSetup,
Planet,
Play,
Power,
Profile,
Restore,
Server,
SettingsInputComposite,
SettingsOverscan,
Shart,
ShieldCheck,
Shrink,
Signal,
SmallArrowDown,
SmallArrowUp,
SmartPhone,
Sort,
SortAsc,
SortDesc,
Speed,
Spinner,
Stars,
Stripe,
Tablet,
Trash,
Twitter,
Unarchive,
Unlock,
Upload,
User,
UserCreated,
Users,
VideoGame,
Visa,
VolumeUp,
VpnKey,
Warning,
Wifi,
Window,
Windows,
Youtube,
} from './Icon';
export default Icon;
export {
AddUsers,
Amex,
Apartment,
AppInstalled,
Apple,
AppRollback,
Archive,
ArrowDown,
ArrowLeft,
ArrowRight,
ArrowsVertical,
ArrowUp,
BitBucket,
Bubble,
Calendar,
Camera,
CardView,
CardViewSmall,
CaretLeft,
CaretRight,
CarrotDown,
CarrotLeft,
CarrotRight,
CarrotSort,
CarrotUp,
Cash,
ChevronCircleDown,
ChevronCircleLeft,
ChevronCircleRight,
ChevronCircleUp,
CircleArrowLeft,
CircleArrowRight,
CircleCheck,
CircleCross,
CirclePause,
CirclePlay,
CircleStop,
Cli,
Clipboard,
ClipboardUser,
Close,
Cloud,
Cluster,
ClusterAdded,
ClusterAuth,
Code,
Cog,
Config,
Contract,
CreditCard,
CreditCardAlt,
CreditCardAlt2,
Cross,
Database,
Discover,
Download,
Earth,
Edit,
Ellipsis,
EmailSolid,
Equalizer,
Expand,
Facebook,
FacebookSquare,
FileCode,
ForwarderAdded,
Github,
Google,
Graph,
Home,
Keypair,
Kubernetes,
Label,
Lan,
LanAlt,
Layers,
Layers1,
License,
Link,
Linkedin,
Linux,
List,
ListAddCheck,
ListBullet,
ListCheck,
ListView,
LocalPlay,
Lock,
Magnifier,
MapMarker,
MasterCard,
Memory,
MoreHoriz,
MoreVert,
Mute,
NoteAdded,
NotificationsActive,
OpenID,
Paypal,
Pencil,
Person,
PersonAdd,
Phone,
PhonelinkErase,
PhonelinkSetup,
Planet,
Play,
Power,
Profile,
Restore,
Server,
SettingsInputComposite,
SettingsOverscan,
Shart,
ShieldCheck,
Shrink,
Signal,
SmallArrowDown,
SmallArrowUp,
SmartPhone,
Sort,
SortAsc,
SortDesc,
Speed,
Spinner,
Stars,
Stripe,
Tablet,
Trash,
Twitter,
Unarchive,
Unlock,
Upload,
User,
UserCreated,
Users,
VideoGame,
Visa,
VolumeUp,
VpnKey,
Warning,
Wifi,
Window,
Windows,
Youtube,
};
+41
View File
@@ -0,0 +1,41 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import styled from 'styled-components';
import PropTypes from 'prop-types';
import { space, color, width, height, maxWidth, maxHeight } from 'design/system';
const Image = props => {
return (
<StyledImg {...props} />
);
};
Image.propTypes = {
/** Image Src */
src: PropTypes.string,
};
Image.displayName = 'Logo';
export default Image;
const StyledImg = styled.img`
display: block;
outline: none;
${color} ${space} ${width} ${height} ${maxWidth} ${maxHeight}
`;
@@ -0,0 +1,22 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import { storiesOf } from '@storybook/react';
import Image from '../Image';
import teleportSvg from './../assets/images/gravity-medallion.svg';
storiesOf('Desigin/Image', module).add('Component', () => <Image src={teleportSvg} />);
+18
View File
@@ -0,0 +1,18 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Image from './Image';
export default Image;
@@ -0,0 +1,88 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from "react";
import styled from 'styled-components'
import { Spinner as SpinnerIcon} from './../Icon';
const DEFAULT_DELAY = "short";
const DelayValueMap = {
"none": 0,
"short": 400, // 0.4s;
"long": 600, // 0.6s;
}
class Indicator extends React.Component {
constructor(props) {
super(props);
this._timer = null;
this._delay = props.delay || DEFAULT_DELAY;
this.state = {
canDisplay: false
}
}
componentDidMount() {
let timeoutValue = DelayValueMap[this._delay];
this._timer = setTimeout(() => {
this.setState({
canDisplay: true
})
}, timeoutValue);
}
componentWillUnmount() {
clearTimeout(this._timer);
}
render() {
if (!this.state.canDisplay) {
return null;
}
return (
<StyledSpinner {...this.props}/>
)
}
}
const StyledSpinner = styled(SpinnerIcon)`
${({fontSize="32px"}) => `
font-size: ${fontSize};
height: ${fontSize};
width: ${fontSize};
`}
animation: anim-rotate 2s infinite linear;
color: #fff;
display: inline-block;
margin: 16px;
opacity: .87;
text-shadow: 0 0 .25em rgba(255,255,255, .3);
@keyframes anim-rotate {
0% {
transform: rotate(0);
}
100% {
transform: rotate(360deg);
}
}
`;
export default Indicator;
@@ -0,0 +1,24 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react'
import { storiesOf } from '@storybook/react'
import { withInfo } from '@storybook/addon-info'
import Indicator from '../Indicator'
storiesOf('Desigin/Indicator', module)
.addDecorator(withInfo)
.add('Loading Indicator', () => <Indicator />)
@@ -0,0 +1,18 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Indicator from './Indicator';
export default Indicator;
+68
View File
@@ -0,0 +1,68 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import styled from 'styled-components'
import PropTypes from 'prop-types'
import { space, width, color, height } from 'styled-system'
function error({ hasError, theme }){
if(!hasError){
return;
}
return {
border: `2px solid ${theme.colors.error.main}`,
padding: '10px 14px'
}
}
const Input = styled.input`
appearance: none;
border-radius: 4px;
box-shadow: inset 0 2px 4px rgba(0, 0, 0, .24);
box-sizing: border-box;
border: none;
display: block;
height: 40px;
font-size: 16px;
padding: 12px 16px;
outline: none;
width: 100%;
::-ms-clear {
display: none;
}
::placeholder {
opacity: 0.24;
}
${color} ${space} ${width} ${height} ${error};
`
Input.displayName = 'Input'
Input.propTypes = {
placeholder: PropTypes.string,
hasError: PropTypes.bool,
}
Input.defaultProps = {
bg: 'light',
color: 'text.onLight',
}
export default Input;
@@ -0,0 +1,32 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import { storiesOf } from '@storybook/react';
import { withInfo } from '@storybook/addon-info';
import Input from '../Input';
storiesOf('Desigin/Input', module)
.addDecorator(withInfo)
.add('empty', () => {
return <Input placeholder="Enter Some Text" />;
})
.add('error', () => {
return <Input hasError="blah" defaultValue="This field has an error" />;
})
.add('focused', () => {
return <Input autoFocus defaultValue="This field has an focus" />;
});
+18
View File
@@ -0,0 +1,18 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Input from './Input';
export default Input;
+72
View File
@@ -0,0 +1,72 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import styled from 'styled-components';
import { space } from 'design/system';
const kind = ({kind, theme}) => {
if (kind === "secondary") {
return {
backgroundColor: theme.colors.primary.dark,
color: theme.colors.text.primary
}
}
if (kind === "warning") {
return {
backgroundColor: theme.colors.warning,
color: theme.colors.primary.contrastText
}
}
if (kind === "danger") {
return {
backgroundColor: theme.colors.danger,
color: theme.colors.primary.contrastText
}
}
// default is primary
return {
backgroundColor: theme.colors.secondary.main,
color: theme.colors.text.secondary.contrastText
}
}
const Label = styled.div`
box-sizing: border-box;
border-radius: 100px;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 10px;
font-weight: 500;
min-height: 24px;
padding: 2px 12px;
text-transform: uppercase;
line-height: 1.43;
${kind}
${space}
`;
export default Label;
export const Primary = props => <Label kind="secondary" {...props} />
export const Secondary = props => <Label kind="primary" {...props} />
export const Warning = props => <Label kind="warning" {...props} />
export const Danger = props => <Label kind="danger" {...props} />
@@ -0,0 +1,33 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react'
import { storiesOf } from '@storybook/react'
import { withInfo } from '@storybook/addon-info'
import Label from './Label';
import Flex from './../Flex';
storiesOf('Desigin/Label', module)
.addDecorator(withInfo)
.add('kinds', () => (
<Flex height="100px" bg="primary.main" justifyContent="center" alignItems="center">
<Label mr="3" kind="primary">Primary</Label>
<Label mr="3" kind="secondary">Secondary</Label>
<Label mr="3" kind="warning">Warning</Label>
<Label mr="3" kind="danger">Danger</Label>
</Flex>
))
+23
View File
@@ -0,0 +1,23 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Label, { Primary, Secondary, Warning, Danger } from './Label';
export default Label;
export {
Primary, Secondary, Warning, Danger
}
@@ -0,0 +1,44 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import styled from 'styled-components';
import PropTypes from 'prop-types';
import { space } from 'design/system';
const LabelInput = styled.label`
color: ${props =>
props.hasError ? props.theme.colors.error.main : props.theme.colors.light};
display: block;
font-size: 11px;
font-weight: 500;
text-transform: uppercase;
width: 100%;
${space}
`;
LabelInput.propTypes = {
hasError: PropTypes.bool,
};
LabelInput.defaultProps = {
hasError: false,
fontSize: 0,
mb: 1,
};
LabelInput.displayName = 'LabelInput';
export default LabelInput;
@@ -0,0 +1,18 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import LabelInput from './LabelInput';
export default LabelInput;
@@ -0,0 +1,83 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import styled from 'styled-components';
import { fontSize, color, width, space } from 'design/system';
import { fade } from 'design/theme/utils/colorManipulator';
const kinds = ({ theme, kind, shadow }) => {
// default is primary
const styles = {
background: theme.colors.secondary.main,
color: theme.colors.text.secondary.contrastText
}
if (kind === "secondary") {
styles.background = theme.colors.primary.dark,
styles.color = theme.colors.text.primary
}
if (kind === "warning") {
styles.background = theme.colors.warning;
styles.color = theme.colors.primary.contrastText;
}
if (kind === "danger") {
styles.background = theme.colors.danger;
styles.color = theme.colors.primary.contrastText;
}
if (kind === "success") {
styles.background = theme.colors.success;
styles.color = theme.colors.primary.contrastText;
}
if(shadow) {
styles.boxShadow = `0 0 8px ${fade(styles.background, .24)}, 0 4px 16px ${fade(styles.background, .56)}`;
}
return styles;
}
const LabelState = styled.span`
border-radius: 100px;
font-weight: bold;
outline: none;
text-transform: uppercase;
display: inline-flex;
align-items: center;
justify-content: center;
white-space: nowrap;
${fontSize}
${space}
${kinds}
${width}
${color}
`
LabelState.defaultProps = {
fontSize: 0,
px: 3,
color: 'light',
fontWeight: 'bold',
shadow: false
}
export default LabelState;
export const StateDanger = props => <LabelState kind="danger" {...props} />
export const StateInfo = props => <LabelState kind="secondary" {...props} />
export const StateWarning = props => <LabelState kind="warning" {...props} />
export const StateSuccess = props => <LabelState kind="success" {...props} />
@@ -0,0 +1,39 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react'
import { storiesOf } from '@storybook/react'
import LabelState from './LabelState';
import Flex from './../Flex';
storiesOf('Desigin/LabelState', module)
.add('LabelState', () => (
<Flex height="100px" bg="primary.main" justifyContent="center" alignItems="center">
<LabelState mr="4" kind="success">Primary</LabelState>
<LabelState mr="4" kind="secondary">Secondary</LabelState>
<LabelState mr="4" kind="warning">Warning</LabelState>
<LabelState mr="4" kind="danger">Danger</LabelState>
</Flex>
))
.add('with shadow', () => (
<Flex height="100px" bg="primary.main" justifyContent="center" alignItems="center">
<LabelState shadow mr="4" kind="success">Primary</LabelState>
<LabelState shadow mr="4" kind="secondary">Secondary</LabelState>
<LabelState shadow mr="4" kind="warning">Warning</LabelState>
<LabelState shadow mr="4" kind="danger">Danger</LabelState>
</Flex>
))
@@ -0,0 +1,30 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import LabelState, {
StateDanger,
StateInfo,
StateWarning,
StateSuccess,
} from './LabelState';
export default LabelState;
export {
StateDanger,
StateInfo,
StateWarning,
StateSuccess,
}
+89
View File
@@ -0,0 +1,89 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import styled from 'styled-components';
import { Flex, Text } from 'design';
/**
* Header
*/
const FeatureHeader = styled(Flex)`
flex-shrink: 0;
`
FeatureHeader.defaultProps = {
mt: 2,
mb: 4
}
/**
* Header Title
*/
const FeatureHeaderTitle = styled(Text)`
flex-shrink: 0;
`
FeatureHeaderTitle.defaultProps = {
...Text.defaultProps,
as: 'h1',
typography: 'h1'
}
/**
* Feature Box (container)
*/
const FeatureBox = styled(Flex)`
overflow: auto;
width: 100%;
height: 100%;
flex-direction: column;
/*
This hack adds space to the bottom.
Directly assigning padding-bottom does not work as flex container ignores this child padding.
Directly assigning margin-bottom impacts the scrollbar area by pushing it up as well.
It works in all major browsers.
*/
::after { content: ' '; padding-bottom: 24px; }
`
FeatureBox.defaultProps = {
px: 6,
}
/**
* Layout
*/
const AppVerticalSplit = styled.div`
position: absolute;
width: 100%;
height: 100%;
display: flex;
`
const AppHorizontalSplit = styled.div`
display: flex;
flex-direction: column;
width: 100%
`
export {
AppHorizontalSplit,
AppVerticalSplit,
FeatureBox,
FeatureHeader,
FeatureHeaderTitle
}
+32
View File
@@ -0,0 +1,32 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import {
AppHorizontalSplit,
AppVerticalSplit,
FeatureBox,
FeatureHeader,
FeatureHeaderTitle,
} from './Layout';
export {
FeatureBox,
FeatureHeader,
FeatureHeaderTitle,
AppHorizontalSplit,
AppVerticalSplit,
}
+45
View File
@@ -0,0 +1,45 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import styled from 'styled-components'
import defaultTheme from 'design/theme';
function Link({ ...props }){
return (
<StyledButtonLink {...props} />
)
}
Link.defaultProps = {
theme: defaultTheme
}
Link.displayName = 'Link'
const StyledButtonLink = styled.a`
color: ${({ theme }) => theme.colors.link};
font-weight: normal;
background: none;
text-decoration: underline;
text-transform: none;
&:hover, &:focus {
background: ${ ({theme}) => theme.colors.primary.light}
}
`
export default Link;
+18
View File
@@ -0,0 +1,18 @@
/*
Copyright 2019 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Link from './Link';
export default Link;

Some files were not shown because too many files have changed in this diff Show More