diff --git a/.babelrc-test-env.js b/.babelrc-test-env.js new file mode 100644 index 00000000000..ebb7be300df --- /dev/null +++ b/.babelrc-test-env.js @@ -0,0 +1,9 @@ +module.exports = { + "extends": "./.babelrc.js", + "plugins": [ + "@babel/plugin-transform-runtime", + "@babel/plugin-transform-async-to-generator", + "@babel/plugin-syntax-dynamic-import", + "dynamic-import-node" + ], +}; diff --git a/.eslintignore b/.eslintignore index e99d1152536..a784b935409 100644 --- a/.eslintignore +++ b/.eslintignore @@ -2,6 +2,7 @@ dist node_modules es lib +test-env types eui.d.ts **/*.snap.js diff --git a/.gitignore b/.gitignore index 3c5d8f63b0b..e6b977f459e 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ tmp/ dist/ lib/ es/ +test-env/ .idea .vscode/ .DS_Store diff --git a/CHANGELOG.md b/CHANGELOG.md index 29411435cdb..56e9735f00f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - Converted `EuiColorPicker` color conversion functions to `chroma-js` methods ([#2805](https://github.com/elastic/eui/pull/2805)) - Added `direction` parameter to `euiPaletteColorBlind()` for specifiying lighter or darker (or both) alternates ([#2822](https://github.com/elastic/eui/pull/2822)) - Converted `EuiSideNav` to TypeScript ([#2818](https://github.com/elastic/eui/issues/2818)) +- Added babel-transformed and partially mocked commonjs build (`test-env/`) to target Kibana's Jest environment ([#2698](https://github.com/elastic/eui/pull/2698)) **Bug fixes** diff --git a/package.json b/package.json index 6b183083310..4e6319a6a82 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,8 @@ "@babel/plugin-proposal-class-properties": "^7.8.3", "@babel/plugin-proposal-object-rest-spread": "^7.8.3", "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-transform-async-to-generator": "^7.8.3", + "@babel/plugin-transform-runtime": "^7.8.3", "@babel/preset-env": "^7.8.3", "@babel/preset-react": "^7.8.3", "@babel/preset-typescript": "^7.8.3", diff --git a/scripts/compile-clean.js b/scripts/compile-clean.js index 8b485fccefd..dd72721f9e1 100755 --- a/scripts/compile-clean.js +++ b/scripts/compile-clean.js @@ -3,3 +3,4 @@ const rimraf = require('rimraf'); rimraf.sync('dist'); rimraf.sync('lib'); rimraf.sync('es'); +rimraf.sync('test-env'); diff --git a/scripts/compile-eui.js b/scripts/compile-eui.js index d092d70502c..ebda61cb841 100755 --- a/scripts/compile-eui.js +++ b/scripts/compile-eui.js @@ -3,6 +3,7 @@ const chalk = require('chalk'); const shell = require('shelljs'); const path = require('path'); const glob = require('glob'); +const fs = require('fs'); const dtsGenerator = require('dts-generator').default; function compileLib() { @@ -13,11 +14,12 @@ function compileLib() { 'lib/test' ); - console.log('Compiling src/ to es/ and lib/'); + console.log('Compiling src/ to es/, lib/, and test-env/'); // Run all code (com|trans)pilation through babel (ESNext JS & TypeScript) + execSync( - 'babel --quiet --out-dir=es --extensions .js,.ts,.tsx --ignore "**/webpack.config.js,**/*.test.js,**/*.d.ts" src', + 'babel --quiet --out-dir=es --extensions .js,.ts,.tsx --ignore "**/webpack.config.js,**/*.test.js,**/*.test.ts,**/*.test.tsx,**/*.d.ts,**/*.testenv.js,**/*.testenv.tsx,**/*.testenv.ts" src', { env: { ...process.env, @@ -26,8 +28,19 @@ function compileLib() { }, } ); + + execSync( + 'babel --quiet --out-dir=lib --extensions .js,.ts,.tsx --ignore "**/webpack.config.js,**/*.test.js,**/*.test.ts,**/*.test.tsx,**/*.d.ts,**/*.testenv.js,**/*.testenv.tsx,**/*.testenv.ts" src', + { + env: { + ...process.env, + NO_COREJS_POLYFILL: true, + }, + } + ); + execSync( - 'babel --quiet --out-dir=lib --extensions .js,.ts,.tsx --ignore "**/webpack.config.js,**/*.test.js,**/*.d.ts" src', + 'babel --quiet --out-dir=test-env --extensions .js,.ts,.tsx --config-file="./.babelrc-test-env.js" --ignore "**/webpack.config.js,**/*.test.js,**/*.test.ts,**/*.test.tsx,**/*.d.ts" src', { env: { ...process.env, @@ -35,6 +48,14 @@ function compileLib() { }, } ); + glob('./test-env/**/*.testenv.js', undefined, (error, files) => { + files.forEach(file => { + const dir = path.dirname(file); + const fileName = path.basename(file, '.js'); + const targetName = fileName.replace('.testenv', ''); + fs.renameSync(file, path.join(dir, `${targetName}.js`)); + }); + }); console.log(chalk.green('✔ Finished compiling src/')); diff --git a/scripts/dtsgenerator.js b/scripts/dtsgenerator.js index ec3cd228e1b..fe03bf5cd98 100644 --- a/scripts/dtsgenerator.js +++ b/scripts/dtsgenerator.js @@ -34,6 +34,8 @@ const generator = dtsGenerator({ 'src-framer/**/*', '**/*.test.ts', '**/*.test.tsx', + '**/*.testenv.ts', + '**/*.testenv.tsx', 'src/themes/charts/*' // A separate d.ts file is generated for the charts theme file ], resolveModuleId(params) { diff --git a/src/components/date_picker/date_picker_range.js b/src/components/date_picker/date_picker_range.js index d8add199ffc..ac8997fb722 100644 --- a/src/components/date_picker/date_picker_range.js +++ b/src/components/date_picker/date_picker_range.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import classNames from 'classnames'; import { EuiText } from '../text'; -import { IconPropType, EuiIcon } from '../icon'; +import { EuiIcon } from '../icon'; export const EuiDatePickerRange = ({ children, @@ -88,7 +88,10 @@ EuiDatePickerRange.propTypes = { /** * Pass either an icon type or set to `false` to remove icon entirely */ - iconType: PropTypes.oneOfType([PropTypes.bool, IconPropType]), + iconType: PropTypes.oneOfType([ + PropTypes.bool, + PropTypes.oneOfType([PropTypes.string, PropTypes.node]), + ]), fullWidth: PropTypes.bool, /** * Won't apply any additional props to start and end date components diff --git a/src/components/icon/icon.testenv.tsx b/src/components/icon/icon.testenv.tsx new file mode 100644 index 00000000000..e441b7a809e --- /dev/null +++ b/src/components/icon/icon.testenv.tsx @@ -0,0 +1,8 @@ +import React from 'react'; +export const EuiIcon = ({ type, ...rest }: any) => ( +
+); + +export const TYPES = []; +export const COLORS = []; +export const SIZES = []; diff --git a/src/components/icon/icon.tsx b/src/components/icon/icon.tsx index 2ddf75b65bf..e3f398625ca 100644 --- a/src/components/icon/icon.tsx +++ b/src/components/icon/icon.tsx @@ -4,7 +4,6 @@ import React, { ReactElement, SVGAttributes, } from 'react'; -import PropTypes from 'prop-types'; import classNames from 'classnames'; import { CommonProps, keysOf } from '../common'; @@ -396,11 +395,6 @@ export type EuiIconType = keyof typeof typeToPathMap; export type IconType = EuiIconType | string | ReactElement; -export const IconPropType = PropTypes.oneOfType([ - PropTypes.string, - PropTypes.node, -]); - const colorToClassMap = { default: null, primary: 'euiIcon--primary', diff --git a/src/components/icon/index.ts b/src/components/icon/index.ts index 486098bc0a0..007020cd543 100644 --- a/src/components/icon/index.ts +++ b/src/components/icon/index.ts @@ -4,7 +4,6 @@ export { IconColor, IconSize, IconType, - IconPropType, TYPES as ICON_TYPES, SIZES as ICON_SIZES, COLORS as ICON_COLORS, diff --git a/wiki/consuming.md b/wiki/consuming.md index dff5e4969ab..67131c777e7 100644 --- a/wiki/consuming.md +++ b/wiki/consuming.md @@ -95,3 +95,23 @@ ReactDOM.render( ### "Module build failed" or "Module parse failed: Unexpected token" error If you get an error when importing a React component, you might need to configure Webpack's `resolve.mainFields` to `['webpack', 'browser', 'main']` to import the components from `lib` intead of `src`. See the [Webpack docs](https://webpack.js.org/configuration/resolve/#resolve-mainfields) for more info. + +## Using the `test-env` build + +EUI provides a separate babel-transformed and partially mocked commonjs build for testing environments in consuming projects. The output is identical to that of `lib/`, but has transformed async functions and dynamic import statements, and also applies some useful mocks. This build mainly targets Kibana's Jest environment, but may be helpful for testing environments in other projects. + +### Mapping to the `test-env` directory + +In Kibana's Jest configuration, the `moduleNameMapper` option is used to resolve standard EUI import statements with `test-env` aliases. + +```js +moduleNameMapper: { + '@elastic/eui$': '/node_modules/@elastic/eui/test-env' +} +``` + +This eliminates the need to polyfill or transform the EUI build for an environment that otherwise has no need for such processing. + +### Mocked component files + +Besides babel transforms, the test environment build consumes mocked component files of the type `src/**/[name].testenv.*`. During the build, files of the type `src/**/[name].*` will be replaced by those with the `testenv` namespace. The purpose of this mocking is to further mitigate the impacts of time- and import-dependent rendering, and simplify environment output such as test snapshots. Information on creating mock component files can be found with [testing documentation](testing). diff --git a/wiki/testing.md b/wiki/testing.md index 5c2c88de49d..113fc760814 100644 --- a/wiki/testing.md +++ b/wiki/testing.md @@ -94,3 +94,17 @@ describe('YourComponent', () => { }); ``` + +## Writing mock component files + +A component file can be mocked for snapshot simplification or to mitigate nondeterministic rendering in test environments. See [`src/components/icon`](../src/components/icon) for a example. + +_Although mock component files are currently only used as part of consuming project test environments, the concept will soon be applied to EUI's own testing environment._ + +### Using the mock namespace + +Component mocking relies on using the `[name].testenv.*` namespace for identification. The mocked module will replace the standard import in the `test-env` build. Both `index` files and individual component files can mocked. + +### Mapping all module exports + +The rendered output of a mocked component is at the author's discretion, however, all public exports from a module must be preserved in the mock file. Note that this does not apply to exported TypeScript types and interfaces, which will always be derived from the original component file. diff --git a/yarn.lock b/yarn.lock index 34ae7da8aef..11a61621c8d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -752,6 +752,16 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" +"@babel/plugin-transform-runtime@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.8.3.tgz#c0153bc0a5375ebc1f1591cb7eea223adea9f169" + integrity sha512-/vqUt5Yh+cgPZXXjmaG9NT8aVfThKk7G4OqkVhrXqwsC5soMn/qTCxs36rZ2QFhpfTJcjw4SNDIZ4RUb8OL4jQ== + dependencies: + "@babel/helper-module-imports" "^7.8.3" + "@babel/helper-plugin-utils" "^7.8.3" + resolve "^1.8.1" + semver "^5.5.1" + "@babel/plugin-transform-shorthand-properties@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.8.3.tgz#28545216e023a832d4d3a1185ed492bcfeac08c8"