diff --git a/.browserslistrc b/.browserslistrc
new file mode 100644
index 000000000..19eb536a1
--- /dev/null
+++ b/.browserslistrc
@@ -0,0 +1,13 @@
+# https://github.com/browserslist/browserslist
+
+# include browsers based on global coverage
+> 1%
+
+# exclude all Internet Explorer versions
+not ie > 0
+
+# include browsers we are committed to support
+last 1 Chrome version
+last 1 Firefox version
+last 1 Edge version
+last 1 Safari version
diff --git a/.eslintrc.js b/.eslintrc.js
deleted file mode 100644
index d49c26d61..000000000
--- a/.eslintrc.js
+++ /dev/null
@@ -1,17 +0,0 @@
-const path = require('path');
-
-module.exports = {
- root: true,
- extends: ['plugin:patternfly-react/recommended'],
- rules: {
- 'import/first': 'off'
- },
- overrides: [
- {
- files: ['**/__mocks__/**', '**/Stories/**', '*.stories.js', '*.test.js'],
- rules: {
- 'import/no-extraneous-dependencies': 'off'
- }
- }
- ],
-};
diff --git a/.gitignore b/.gitignore
index 096ed519c..49c06cdfb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,24 +1,15 @@
-# See https://help.github.com/ignore-files/ for more about ignoring files.
+# https://git-scm.com/docs/gitignore
-# dependencies
-/node_modules
+# files generated by package manager
+/node_modules/
+/yarn-debug.log
+/yarn-error.log
-# testing
-/coverage
-
-# production
-/dist
-
-# misc
-.DS_Store
-.env.local
-.env.development.local
-.env.test.local
-.env.production.local
-
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
-
-.idea
+# build artifacts and other generated files
+/dist/
+/coverage/
+/cosmos/
+# editor files
+/.idea/
+/.vscode/
diff --git a/.npmignore b/.npmignore
deleted file mode 100644
index 99c723616..000000000
--- a/.npmignore
+++ /dev/null
@@ -1,18 +0,0 @@
-coverage/
-scripts/
-config/
-src/
-storybook/
-
-README-create-react-app.md
-.travis.yml
-
-.idea
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
-.DS_Store
-.env.local
-.env.development.local
-.env.test.local
-.env.production.local
diff --git a/.prettierrc b/.prettierrc
index 026543951..81b259b61 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -2,7 +2,7 @@
"semi": true,
"singleQuote": true,
"tabWidth": 2,
- "trailingComma": "none",
"useTabs": false,
+ "trailingComma": "none",
"printWidth": 120
}
diff --git a/.stylelintrc b/.stylelintrc
deleted file mode 100644
index f5d14f0a6..000000000
--- a/.stylelintrc
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "defaultSeverity": "warning",
- "extends": ["stylelint-config-standard"]
-}
diff --git a/.travis.yml b/.travis.yml
index fe3e64623..d0844ff45 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,9 +1,11 @@
language: node_js
node_js:
- - '10'
+ - 'lts/*'
cache: yarn
+before_install:
+ - curl -o- -L https://yarnpkg.com/install.sh | bash
+ - export PATH="$HOME/.yarn/bin:$PATH"
+ - yarn install
script:
-- yarn test
-- yarn coveralls
-- yarn dist
-
+ - yarn build
+ - yarn coveralls
diff --git a/README.md b/README.md
index 4bcafe03a..e27905a06 100644
--- a/README.md
+++ b/README.md
@@ -3,4 +3,3 @@
# web-ui-components
Set of reusable components identified during [kubevirt/web-ui](https://github.com/kubevirt/web-ui) development.
-
diff --git a/config/babel.config.js b/config/babel.config.js
new file mode 100644
index 000000000..387aec93f
--- /dev/null
+++ b/config/babel.config.js
@@ -0,0 +1,6 @@
+// https://babeljs.io/docs/en/next/options
+
+module.exports = {
+ presets: ['@babel/preset-env', '@babel/preset-react'],
+ plugins: ['@babel/plugin-proposal-class-properties']
+};
diff --git a/config/cosmos.config.js b/config/cosmos.config.js
new file mode 100644
index 000000000..c9e461ba0
--- /dev/null
+++ b/config/cosmos.config.js
@@ -0,0 +1,24 @@
+// https://github.com/react-cosmos/react-cosmos#config
+
+const paths = require('./paths');
+
+module.exports = {
+ rootPath: paths.projectRoot,
+
+ // by convention, all fixtures use the '.fixture.js' suffix
+ fileMatch: '**/*.fixture.js',
+
+ // additional modules to load along with every component
+ globalImports: ['@babel/polyfill'],
+
+ // path to Cosmos proxies
+ proxiesPath: `${paths.src}/cosmos/proxies.js`,
+
+ // webpack server settings
+ webpackConfigPath: `${paths.config}/cosmos.webpack.config.js`,
+ watchDirs: [paths.src],
+ port: 9000,
+
+ // directory where cosmos-export tool generates the static application
+ outputPath: paths.cosmosExport
+};
diff --git a/config/cosmos.webpack.config.js b/config/cosmos.webpack.config.js
new file mode 100644
index 000000000..e860838ed
--- /dev/null
+++ b/config/cosmos.webpack.config.js
@@ -0,0 +1,23 @@
+// https://webpack.js.org/configuration/
+
+const chalk = require('chalk');
+const paths = require('./paths');
+const babelOptions = require('./babel.config');
+
+const webpackMode = process.env.NODE_ENV === 'production' ? 'production' : 'development';
+console.log(chalk`Running webpack in {white ${webpackMode}} mode.`);
+
+module.exports = {
+ mode: webpackMode,
+
+ module: {
+ rules: [
+ {
+ test: /\.js$/,
+ include: paths.src,
+ loader: 'babel-loader',
+ options: babelOptions
+ }
+ ]
+ }
+};
diff --git a/config/env.js b/config/env.js
deleted file mode 100644
index 30a6c7f1b..000000000
--- a/config/env.js
+++ /dev/null
@@ -1,93 +0,0 @@
-'use strict';
-
-const fs = require('fs');
-const path = require('path');
-const paths = require('./paths');
-
-// Make sure that including paths.js after env.js will read .env variables.
-delete require.cache[require.resolve('./paths')];
-
-const NODE_ENV = process.env.NODE_ENV;
-if (!NODE_ENV) {
- throw new Error(
- 'The NODE_ENV environment variable is required but was not specified.'
- );
-}
-
-// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
-var dotenvFiles = [
- `${paths.dotenv}.${NODE_ENV}.local`,
- `${paths.dotenv}.${NODE_ENV}`,
- // Don't include `.env.local` for `test` environment
- // since normally you expect tests to produce the same
- // results for everyone
- NODE_ENV !== 'test' && `${paths.dotenv}.local`,
- paths.dotenv,
-].filter(Boolean);
-
-// Load environment variables from .env* files. Suppress warnings using silent
-// if this file is missing. dotenv will never modify any environment variables
-// that have already been set. Variable expansion is supported in .env files.
-// https://github.com/motdotla/dotenv
-// https://github.com/motdotla/dotenv-expand
-dotenvFiles.forEach(dotenvFile => {
- if (fs.existsSync(dotenvFile)) {
- require('dotenv-expand')(
- require('dotenv').config({
- path: dotenvFile,
- })
- );
- }
-});
-
-// We support resolving modules according to `NODE_PATH`.
-// This lets you use absolute paths in imports inside large monorepos:
-// https://github.com/facebookincubator/create-react-app/issues/253.
-// It works similar to `NODE_PATH` in Node itself:
-// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders
-// Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.
-// Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims.
-// https://github.com/facebookincubator/create-react-app/issues/1023#issuecomment-265344421
-// We also resolve them to make sure all tools using them work consistently.
-const appDirectory = fs.realpathSync(process.cwd());
-process.env.NODE_PATH = (process.env.NODE_PATH || '')
- .split(path.delimiter)
- .filter(folder => folder && !path.isAbsolute(folder))
- .map(folder => path.resolve(appDirectory, folder))
- .join(path.delimiter);
-
-// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
-// injected into the application via DefinePlugin in Webpack configuration.
-const REACT_APP = /^REACT_APP_/i;
-
-function getClientEnvironment(publicUrl) {
- const raw = Object.keys(process.env)
- .filter(key => REACT_APP.test(key))
- .reduce(
- (env, key) => {
- env[key] = process.env[key];
- return env;
- },
- {
- // Useful for determining whether we’re running in production mode.
- // Most importantly, it switches React into the correct mode.
- NODE_ENV: process.env.NODE_ENV || 'development',
- // Useful for resolving the correct path to static assets in `public`.
- // For example, .
- // This should only be used as an escape hatch. Normally you would put
- // images into the `src` and `import` them in code to get their paths.
- PUBLIC_URL: publicUrl,
- }
- );
- // Stringify all values so we can feed into Webpack DefinePlugin
- const stringified = {
- 'process.env': Object.keys(raw).reduce((env, key) => {
- env[key] = JSON.stringify(raw[key]);
- return env;
- }, {}),
- };
-
- return { raw, stringified };
-}
-
-module.exports = getClientEnvironment;
diff --git a/config/eslint.browser.js b/config/eslint.browser.js
new file mode 100644
index 000000000..46c67c200
--- /dev/null
+++ b/config/eslint.browser.js
@@ -0,0 +1,55 @@
+// https://eslint.org/docs/user-guide/configuring
+// This configuration covers code meant to execute in browser or browser-like environment.
+
+const chalk = require('chalk');
+const paths = require('./paths');
+const pkg = require(paths.packageJson);
+
+// resolve React version for use with eslint-plugin-react
+const reactVersion = /^\D*(\d+).*$/.exec(pkg.peerDependencies.react)[1];
+console.log(chalk`Using React {white ${reactVersion}} linting rules.`);
+
+const commonRules = require('./eslint.rules.common');
+const reactRules = require('./eslint.rules.react');
+
+module.exports = {
+ extends: [
+ 'standard',
+ 'standard-react',
+ 'airbnb',
+ 'plugin:import/errors',
+ 'plugin:import/warnings',
+ 'plugin:promise/recommended',
+ 'plugin:jest/recommended',
+ // Prettier configuration comes last
+ 'plugin:prettier/recommended',
+ 'prettier/react',
+ 'prettier/standard'
+ ],
+
+ env: {
+ es6: true,
+ browser: true,
+ jest: true
+ },
+
+ // use babel-eslint parser to ensure parity with Babel-supported syntax
+ parser: 'babel-eslint',
+
+ settings: {
+ react: {
+ version: reactVersion
+ }
+ },
+
+ rules: {
+ ...commonRules,
+ ...reactRules,
+ 'react/destructuring-assignment': [
+ 'error',
+ {
+ ignoreClassFields: true
+ }
+ ]
+ }
+};
diff --git a/config/eslint.node.js b/config/eslint.node.js
new file mode 100644
index 000000000..5eb541bfb
--- /dev/null
+++ b/config/eslint.node.js
@@ -0,0 +1,35 @@
+// https://eslint.org/docs/user-guide/configuring
+// This configuration covers code meant to execute in Node.js environment.
+
+const commonRules = require('./eslint.rules.common');
+
+module.exports = {
+ extends: [
+ 'standard',
+ 'airbnb',
+ 'plugin:node/recommended',
+ 'plugin:jest/recommended',
+ // Prettier configuration comes last
+ 'plugin:prettier/recommended',
+ 'prettier/standard'
+ ],
+
+ env: {
+ es6: true,
+ node: true,
+ jest: true
+ },
+
+ parserOptions: {
+ // Node.js loads scripts as CommonJS modules
+ sourceType: 'script'
+ },
+
+ rules: {
+ ...commonRules,
+ 'no-console': 'off',
+ 'global-require': 'off',
+ 'import/no-dynamic-require': 'off',
+ 'import/newline-after-import': 'off'
+ }
+};
diff --git a/config/eslint.rules.common.js b/config/eslint.rules.common.js
new file mode 100644
index 000000000..79e14d5dc
--- /dev/null
+++ b/config/eslint.rules.common.js
@@ -0,0 +1,33 @@
+// Common ESLint rule configuration extracted from patternfly-react/recommended.
+
+module.exports = {
+ 'import/no-extraneous-dependencies': [
+ 'error',
+ {
+ devDependencies: true
+ }
+ ],
+ 'import/no-named-default': 'off',
+ 'import/prefer-default-export': 'off',
+ 'no-param-reassign': 'off',
+ 'no-plusplus': 'off',
+ 'no-prototype-builtins': 'off',
+ 'no-restricted-syntax': 'off',
+ 'no-underscore-dangle': 'off',
+ 'no-unused-expressions': [
+ 'error',
+ {
+ allowShortCircuit: true,
+ allowTernary: true
+ }
+ ],
+ 'no-unused-vars': [
+ 'error',
+ {
+ vars: 'all',
+ args: 'none',
+ ignoreRestSiblings: true
+ }
+ ],
+ 'no-use-before-define': 'off'
+};
diff --git a/config/eslint.rules.react.js b/config/eslint.rules.react.js
new file mode 100644
index 000000000..f48e880a0
--- /dev/null
+++ b/config/eslint.rules.react.js
@@ -0,0 +1,16 @@
+// React-specific ESLint rule configuration extracted from patternfly-react/recommended.
+
+module.exports = {
+ 'jsx-a11y/anchor-has-content': 'off',
+ 'jsx-a11y/anchor-is-valid': 'off',
+ 'jsx-a11y/label-has-for': 'off',
+ 'jsx-a11y/click-events-have-key-events': 'off',
+ 'jsx-a11y/no-static-element-interactions': 'off',
+ 'jsx-a11y/no-noninteractive-element-interactions': 'off',
+ 'react/no-array-index-key': 'off',
+ 'react/forbid-prop-types': 'off',
+ 'react/jsx-filename-extension': 'off',
+ 'react/jsx-uses-vars': 'error',
+ 'react/no-danger': 'off',
+ 'react/sort-comp': 'off'
+};
diff --git a/config/jest.config.js b/config/jest.config.js
new file mode 100644
index 000000000..e186d235a
--- /dev/null
+++ b/config/jest.config.js
@@ -0,0 +1,51 @@
+// https://jestjs.io/docs/en/configuration
+// We use a single Jest configuration to cover all types of test environments.
+
+const paths = require('./paths');
+const testWatch = process.argv.slice(2).includes('--watch');
+
+const commonProjectConfig = {
+ rootDir: paths.projectRoot,
+
+ // by convention, all tests use the '.test.js' suffix
+ testMatch: ['**/*.test.js']
+};
+
+module.exports = {
+ // each project represents a separate test configuration
+ projects: [
+ {
+ displayName: 'jsdom-env',
+ ...commonProjectConfig,
+ roots: [paths.src],
+ testEnvironment: 'jsdom',
+ transform: {
+ '\\.js$': `${paths.config}/jest.transform.babel.js`
+ },
+ setupFiles: [`${paths.src}/jest/setupTest.js`],
+ snapshotSerializers: ['enzyme-to-json/serializer']
+ },
+
+ {
+ displayName: 'node-env',
+ ...commonProjectConfig,
+ roots: [paths.tools],
+ testEnvironment: 'node'
+ }
+ ],
+
+ // don't collect coverage while in watch mode
+ collectCoverage: !testWatch,
+
+ // files for which to collect coverage information
+ collectCoverageFrom: [
+ 'src/components/**/*.js',
+ '!src/components/**/fixtures/**/*.js',
+ '!src/components/**/index.js',
+ 'tools/validations/*.js'
+ ],
+
+ // coverage output settings
+ coverageDirectory: `${paths.coverage}`,
+ coverageReporters: ['lcov']
+};
diff --git a/config/jest.transform.babel.js b/config/jest.transform.babel.js
new file mode 100644
index 000000000..b2636bfc7
--- /dev/null
+++ b/config/jest.transform.babel.js
@@ -0,0 +1,4 @@
+// Custom Jest transformer that wraps babel-jest and applies project-specific
+// Babel options, avoiding the need to rely on .babelrc lookup semantics.
+
+module.exports = require('babel-jest').createTransformer(require('./babel.config'));
diff --git a/config/jest/cssTransform.js b/config/jest/cssTransform.js
deleted file mode 100644
index 8f6511481..000000000
--- a/config/jest/cssTransform.js
+++ /dev/null
@@ -1,14 +0,0 @@
-'use strict';
-
-// This is a custom Jest transformer turning style imports into empty objects.
-// http://facebook.github.io/jest/docs/en/webpack.html
-
-module.exports = {
- process() {
- return 'module.exports = {};';
- },
- getCacheKey() {
- // The output is always the same.
- return 'cssTransform';
- },
-};
diff --git a/config/jest/fileTransform.js b/config/jest/fileTransform.js
deleted file mode 100644
index 9e4047d35..000000000
--- a/config/jest/fileTransform.js
+++ /dev/null
@@ -1,12 +0,0 @@
-'use strict';
-
-const path = require('path');
-
-// This is a custom Jest transformer turning file imports into filenames.
-// http://facebook.github.io/jest/docs/en/webpack.html
-
-module.exports = {
- process(src, filename) {
- return `module.exports = ${JSON.stringify(path.basename(filename))};`;
- },
-};
diff --git a/config/paths.js b/config/paths.js
index 6d16efc99..f14068eeb 100644
--- a/config/paths.js
+++ b/config/paths.js
@@ -1,55 +1,14 @@
-'use strict';
-
const path = require('path');
-const fs = require('fs');
-const url = require('url');
-
-// Make sure any symlinks in the project folder are resolved:
-// https://github.com/facebookincubator/create-react-app/issues/637
-const appDirectory = fs.realpathSync(process.cwd());
-const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
-
-const envPublicUrl = process.env.PUBLIC_URL;
-
-function ensureSlash(path, needsSlash) {
- const hasSlash = path.endsWith('/');
- if (hasSlash && !needsSlash) {
- return path.substr(path, path.length - 1);
- } else if (!hasSlash && needsSlash) {
- return `${path}/`;
- } else {
- return path;
- }
-}
-
-const getPublicUrl = appPackageJson =>
- envPublicUrl || require(appPackageJson).homepage;
-
-// We use `PUBLIC_URL` environment variable or "homepage" field to infer
-// "public path" at which the app is served.
-// Webpack needs to know it to put the right