Skip to content

Commit

Permalink
Add support for React 18 (#7012)
Browse files Browse the repository at this point in the history
Co-authored-by: Cee Chen <[email protected]>
Co-authored-by: Cee Chen <[email protected]>
  • Loading branch information
3 people committed Aug 1, 2023
1 parent 90909b6 commit d3e8e0b
Show file tree
Hide file tree
Showing 176 changed files with 5,099 additions and 2,437 deletions.
4 changes: 2 additions & 2 deletions .babelrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ module.exports = {
"corejs": !process.env.NO_COREJS_POLYFILL ? '3.6' : undefined,
"modules": process.env.BABEL_MODULES ? process.env.BABEL_MODULES === 'false' ? false : process.env.BABEL_MODULES : "commonjs" // babel's default is commonjs
}],
["@babel/typescript", { isTSX: true, allExtensions: true }],
"@babel/react",
["@babel/react", { runtime: 'classic' }],
["@babel/typescript", { isTSX: true, allExtensions: true, allowDeclareFields: true }],
[
"@emotion/babel-preset-css-prop",
{
Expand Down
12 changes: 4 additions & 8 deletions cypress/support/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import type { ReactNode } from 'react';
import { mount } from 'cypress/react';
import { ContextObject, Result, RunOptions } from 'axe-core';
import { realPress } from 'cypress-real-events/commands/realPress';
import type { EuiProviderProps } from '../../src/components/provider';
import type { mountCommand } from './setup/mount';
import type { realMountCommand } from './setup/realMount';

type KeyOrShortcut = Parameters<typeof realPress>[0];
type RealPressOptions = Parameters<typeof realPress>[1];
Expand Down Expand Up @@ -32,16 +31,13 @@ declare global {
/**
* Mounts components with a basic `EuiProvider` wrapper
*/
mount: <T = {}>(
children: ReactNode,
options?: { providerProps?: Partial<EuiProviderProps<T>> }
) => ReturnType<typeof mount>;
mount: mountCommand;

/**
* This ensures the correct testing window has focus when using Cypress Real Events.
* @see https://github.com/dmtrKovalenko/cypress-real-events/issues/196
*/
realMount: typeof mount;
realMount: realMountCommand;

/**
* Repeat the Real Events `realPress()` method 2 or more times
Expand Down
16 changes: 0 additions & 16 deletions cypress/support/setup/mount.js

This file was deleted.

38 changes: 38 additions & 0 deletions cypress/support/setup/mount.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import React, { ReactNode } from 'react';
import { EuiProvider, EuiProviderProps } from '../../../src';
import type { mount } from '@cypress/react18';

// Pick cypress mount function based on which React version is currently being
// tested. It has to be directly compared against process.env.REACT_VERSION
// for tree-shaking to work and not throw an error because of a missing import.
let cypressMount: typeof mount;
if (process.env.REACT_VERSION === '18') {
cypressMount = require('@cypress/react18').mount;
} else {
cypressMount = require('@cypress/react').mount;
}

export interface MountOptions {
providerProps?: Partial<EuiProviderProps<any>>;
}

const mountCommand = (
children: ReactNode,
options: MountOptions = {}
): ReturnType<typeof mount> => {
const { providerProps } = options;
return cypressMount(<EuiProvider {...providerProps}>{children}</EuiProvider>);
};

// Export only the type to not confuse code-completion tools
export type mountCommand = typeof mountCommand;

Cypress.Commands.add('mount', mountCommand);
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,26 @@
* Side Public License, v 1.
*/

import { React, Fragment } from 'react';
import React, { ReactNode } from 'react';
import './mount';

Cypress.Commands.add('realMount', (children) => {
const realMountCommand = (children: ReactNode) => {
cy.mount(
<Fragment>
<>
<div
data-test-subj="cypress-real-event-target"
style={{ height: '1px', width: '1px' }}
/>
{children}
</Fragment>
</>
).then(() => {
cy.get('[data-test-subj="cypress-real-event-target"]').realClick({
position: 'topLeft',
});
});
});
};

// Export only the type to not confuse code-completion tools
export type realMountCommand = typeof realMountCommand;

Cypress.Commands.add('realMount', realMountCommand);
17 changes: 14 additions & 3 deletions cypress/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ const { ProvidePlugin, DefinePlugin } = require('webpack');

const THEME_IMPORT = `'../../dist/eui_theme_${process.env.THEME}.css'`;

const alias = {};
const reactVersion = process.env.REACT_VERSION || '18';

// Setup module aliasing when we're testing an older React version
if (['16', '17'].includes(reactVersion)) {
alias.react = `react-${reactVersion}`;
alias['react-dom'] = `react-dom-${reactVersion}`;
}

module.exports = {
mode: 'development',

Expand All @@ -26,6 +35,7 @@ module.exports = {
os: false,
process: require.resolve('process/browser'),
},
alias,
},

module: {
Expand All @@ -46,9 +56,9 @@ module.exports = {
loader: 'style-loader',
options: {
insert: 'meta[name="css-styles"]',
}
},
},
'css-loader'
'css-loader',
],
exclude: /node_modules/,
},
Expand All @@ -62,7 +72,8 @@ module.exports = {
}),

new DefinePlugin({
THEME_IMPORT, // allow cypress/suport/index.js to require the correct css file
THEME_IMPORT, // allow cypress/support/component.tsx to require the correct css file
'process.env.REACT_VERSION': JSON.stringify(reactVersion),
}),
],
};
37 changes: 22 additions & 15 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"lint-sass": "yarn stylelint \"**/*.scss\" --quiet-deprecation-warnings",
"test": "yarn lint && yarn test-unit",
"test-ci": "yarn test && yarn test-cypress",
"test-unit": "cross-env NODE_ENV=test jest --config ./scripts/jest/config.json",
"test-unit": "cross-env NODE_ENV=test jest --config ./scripts/jest/config.js",
"test-a11y": "node ./scripts/a11y-testing",
"test-staged": "yarn lint && node scripts/test-staged.js",
"test-cypress": "node ./scripts/cypress",
Expand All @@ -56,17 +56,18 @@
},
"resolutions": {
"**/prismjs": "1.27.0",
"**/react": "^17.0.0",
"**/react": "^18",
"**/@types/react": "^18",
"react-focus-lock": "^2.9.5"
},
"pre-commit": [
"test-staged"
],
"dependencies": {
"@hello-pangea/dnd": "^16.2.0",
"@types/chroma-js": "^2.0.0",
"@types/lodash": "^4.14.194",
"@types/numeral": "^0.0.28",
"@types/react-beautiful-dnd": "^13.1.2",
"@types/react-input-autosize": "^2.2.1",
"@types/react-virtualized-auto-sizer": "^1.0.1",
"@types/react-window": "^1.8.5",
Expand All @@ -79,7 +80,6 @@
"mdast-util-to-hast": "^10.0.0",
"numeral": "^2.0.6",
"prop-types": "^15.6.0",
"react-beautiful-dnd": "^13.1.0",
"react-dropzone": "^11.5.3",
"react-element-to-jsx-string": "^14.3.4",
"react-focus-on": "^3.9.1",
Expand Down Expand Up @@ -116,8 +116,10 @@
"@babel/preset-react": "^7.18.6",
"@babel/preset-typescript": "^7.21.5",
"@babel/template": "^7.21.9",
"@cfaester/enzyme-adapter-react-18": "^0.7.0",
"@cypress/code-coverage": "^3.10.0",
"@cypress/react": "^5.10.3",
"@cypress/react": "^7.0.3",
"@cypress/react18": "^2.0.0",
"@cypress/webpack-dev-server": "^1.7.0",
"@elastic/charts": "^53.1.1",
"@elastic/datemath": "^5.0.3",
Expand All @@ -139,17 +141,18 @@
"@svgr/core": "8.0.0",
"@svgr/plugin-jsx": "^8.0.1",
"@svgr/plugin-svgo": "^8.0.1",
"@testing-library/dom": "^8.12.0",
"@testing-library/jest-dom": "^5.16.3",
"@testing-library/react": "^12.1.5",
"@testing-library/react": "^14.0.0",
"@testing-library/react-16-17": "npm:@testing-library/react@^12.1.5",
"@testing-library/react-hooks": "^7.0.2",
"@testing-library/user-event": "^13.5.0",
"@types/cheerio": "^0.22.31",
"@types/classnames": "^2.2.10",
"@types/enzyme": "^3.10.5",
"@types/jest": "^24.0.6",
"@types/node": "^10.17.5",
"@types/react": "^17.0.38",
"@types/react-dom": "^17.0.11",
"@types/react": "^18.2.14",
"@types/react-dom": "^18.2.6",
"@types/react-is": "^17.0.3",
"@types/react-router-dom": "^5.3.3",
"@types/tabbable": "^3.1.2",
Expand Down Expand Up @@ -225,9 +228,13 @@
"prop-types": "^15.6.0",
"puppeteer": "^5.5.0",
"raw-loader": "^4.0.1",
"react": "^17.0.2",
"react": "^18.2.0",
"react-16": "npm:react@^16.14.0",
"react-17": "npm:react@^17.0.2",
"react-docgen-typescript": "^2.2.2",
"react-dom": "^17.0.2",
"react-dom": "^18.2.0",
"react-dom-16": "npm:react-dom@^16.14.0",
"react-dom-17": "npm:react-dom@^17.0.2",
"react-helmet": "^6.1.0",
"react-redux": "^7.2.1",
"react-refresh": "^0.11.0",
Expand Down Expand Up @@ -261,12 +268,12 @@
"@elastic/datemath": "^5.0.2",
"@emotion/css": "11.x",
"@emotion/react": "11.x",
"@types/react": "^16.9 || ^17.0",
"@types/react-dom": "^16.9 || ^17.0",
"@types/react": "^16.9 || ^17.0 || ^18.0",
"@types/react-dom": "^16.9 || ^17.0 || ^18.0",
"moment": "^2.13.0",
"prop-types": "^15.5.0",
"react": "^16.12 || ^17.0",
"react-dom": "^16.12 || ^17.0",
"react": "^16.12 || ^17.0 || ^18.0",
"react-dom": "^16.12 || ^17.0 || ^18.0",
"typescript": "~4.5.3"
},
"files": [
Expand Down
2 changes: 1 addition & 1 deletion scripts/babel/proptypes-from-ts-props/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ function stripTypeScript(filename, ast) {
return babelCore.transform(babelCore.transformFromAst(ast).code, {
filename: filename,
babelrc: false,
presets: ['@babel/typescript'],
presets: [['@babel/typescript', { allowDeclareFields: true }]],
}).code;
}

Expand Down
8 changes: 8 additions & 0 deletions scripts/cypress.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,18 @@ const argv = yargs(hideBin(process.argv))
dev: { type: 'boolean' },
theme: { type: 'string', default: 'light', choices: ['light', 'dark'] },
a11y: { type: 'boolean' },
'react-version': {
type: 'number',
default: 18,
choices: [16, 17, 18],
},
}).argv;

const isDev = argv.hasOwnProperty('dev');
const isA11y = argv.hasOwnProperty('a11y');
const skipScss = argv.hasOwnProperty('skip-css');
const theme = argv.theme;
const reactVersion = argv['react-version'];

const info = chalk.white;
const log = chalk.grey;
Expand Down Expand Up @@ -54,11 +60,13 @@ const cypressCommandParts = [
`THEME=${theme}`, // pass the theme
'BABEL_MODULES=false', // let webpack receive ES Module code
'NODE_ENV=cypress_test', // enable code coverage checks
`REACT_VERSION=${reactVersion}`, // set react version to test
`cypress ${testParams}`,
...argv._, // pass any extra options given to this script
];
const cypressCommand = cypressCommandParts.join(' ');

console.log(info(`Running tests on React v${reactVersion}`));
console.log(info(`${isDev ? 'Opening' : 'Running'} cypress`));
console.log(log(cypressCommand));
execSync(cypressCommand, {
Expand Down
70 changes: 70 additions & 0 deletions scripts/jest/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
const getCacheDirectory =
require('jest-config/build/getCacheDirectory').default;

// Set REACT_VERSION env variable to latest if empty or invalid
if (!['16', '17', '18'].includes(process.env.REACT_VERSION)) {
process.env.REACT_VERSION = '18';
}

const reactVersion = process.env.REACT_VERSION;

console.log(`Running tests on React v${reactVersion}`);

/** @type {import('jest').Config} */
const config = {
rootDir: '../../',
roots: [
'<rootDir>/src/',
'<rootDir>/src-docs/src/components',
'<rootDir>/scripts/babel',
'<rootDir>/scripts/tests',
'<rootDir>/scripts/eslint-plugin',
],
collectCoverageFrom: [
'src/{components,services,global_styling}/**/*.{ts,tsx,js,jsx}',
'!src/{components,services,global_styling}/**/*.{testenv,spec,a11y,stories}.{ts,tsx,js,jsx}',
'!src/{components,services,global_styling}/index.ts',
'!src/{components,services,global_styling}/**/*/index.ts',
'!src/components/date_picker/react-datepicker/**/*.{js,jsx}',
],
moduleNameMapper: {
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
'<rootDir>/scripts/jest/mocks/file_mock.js',
'\\.(css|less|scss)$': '<rootDir>/scripts/jest/mocks/style_mock.js',
},
setupFiles: [
'<rootDir>/scripts/jest/setup/enzyme.js',
'<rootDir>/scripts/jest/setup/throw_on_console_error.js',
'<rootDir>/scripts/jest/setup/mocks.js',
],
setupFilesAfterEnv: [
'<rootDir>/scripts/jest/setup/polyfills.js',
'<rootDir>/scripts/jest/setup/unmount_enzyme.js',
],
coverageDirectory: '<rootDir>/reports/jest-coverage',
coverageReporters: ['json', 'html'],
moduleFileExtensions: ['ts', 'tsx', 'js', 'json'],
testMatch: ['**/*.test.js', '**/*.test.ts', '**/*.test.tsx'],
transform: {
'^.+\\.(js|tsx?)$': 'babel-jest',
},
snapshotSerializers: [
'<rootDir>/node_modules/enzyme-to-json/serializer',
'<rootDir>/scripts/jest/setup/emotion',
],
// react version and user permissions aware cache directory
cacheDirectory: `${getCacheDirectory()}_react-${reactVersion}`,
};

if (['16', '17'].includes(reactVersion)) {
config.moduleNameMapper[
'^@testing-library/react((\\\\/.*)?)$'
] = `@testing-library/react-16-17$1`;
config.moduleNameMapper['^react((\\/.*)?)$'] = `react-${reactVersion}$1`;

config.moduleNameMapper[
'^react-dom((\\/.*)?)$'
] = `react-dom-${reactVersion}$1`;
}

module.exports = config;
Loading

0 comments on commit d3e8e0b

Please sign in to comment.