Skip to content

Commit d3e8e0b

Browse files
tkajtochcee-chen
andauthored
Add support for React 18 (#7012)
Co-authored-by: Cee Chen <[email protected]> Co-authored-by: Cee Chen <[email protected]>
1 parent 90909b6 commit d3e8e0b

File tree

176 files changed

+5099
-2437
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

176 files changed

+5099
-2437
lines changed

.babelrc.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ module.exports = {
1111
"corejs": !process.env.NO_COREJS_POLYFILL ? '3.6' : undefined,
1212
"modules": process.env.BABEL_MODULES ? process.env.BABEL_MODULES === 'false' ? false : process.env.BABEL_MODULES : "commonjs" // babel's default is commonjs
1313
}],
14-
["@babel/typescript", { isTSX: true, allExtensions: true }],
15-
"@babel/react",
14+
["@babel/react", { runtime: 'classic' }],
15+
["@babel/typescript", { isTSX: true, allExtensions: true, allowDeclareFields: true }],
1616
[
1717
"@emotion/babel-preset-css-prop",
1818
{

cypress/support/index.d.ts

+4-8
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import type { ReactNode } from 'react';
2-
import { mount } from 'cypress/react';
31
import { ContextObject, Result, RunOptions } from 'axe-core';
42
import { realPress } from 'cypress-real-events/commands/realPress';
5-
import type { EuiProviderProps } from '../../src/components/provider';
3+
import type { mountCommand } from './setup/mount';
4+
import type { realMountCommand } from './setup/realMount';
65

76
type KeyOrShortcut = Parameters<typeof realPress>[0];
87
type RealPressOptions = Parameters<typeof realPress>[1];
@@ -32,16 +31,13 @@ declare global {
3231
/**
3332
* Mounts components with a basic `EuiProvider` wrapper
3433
*/
35-
mount: <T = {}>(
36-
children: ReactNode,
37-
options?: { providerProps?: Partial<EuiProviderProps<T>> }
38-
) => ReturnType<typeof mount>;
34+
mount: mountCommand;
3935

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

4642
/**
4743
* Repeat the Real Events `realPress()` method 2 or more times

cypress/support/setup/mount.js

-16
This file was deleted.

cypress/support/setup/mount.tsx

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
5+
* in compliance with, at your election, the Elastic License 2.0 or the Server
6+
* Side Public License, v 1.
7+
*/
8+
9+
import React, { ReactNode } from 'react';
10+
import { EuiProvider, EuiProviderProps } from '../../../src';
11+
import type { mount } from '@cypress/react18';
12+
13+
// Pick cypress mount function based on which React version is currently being
14+
// tested. It has to be directly compared against process.env.REACT_VERSION
15+
// for tree-shaking to work and not throw an error because of a missing import.
16+
let cypressMount: typeof mount;
17+
if (process.env.REACT_VERSION === '18') {
18+
cypressMount = require('@cypress/react18').mount;
19+
} else {
20+
cypressMount = require('@cypress/react').mount;
21+
}
22+
23+
export interface MountOptions {
24+
providerProps?: Partial<EuiProviderProps<any>>;
25+
}
26+
27+
const mountCommand = (
28+
children: ReactNode,
29+
options: MountOptions = {}
30+
): ReturnType<typeof mount> => {
31+
const { providerProps } = options;
32+
return cypressMount(<EuiProvider {...providerProps}>{children}</EuiProvider>);
33+
};
34+
35+
// Export only the type to not confuse code-completion tools
36+
export type mountCommand = typeof mountCommand;
37+
38+
Cypress.Commands.add('mount', mountCommand);

cypress/support/setup/realMount.js cypress/support/setup/realMount.tsx

+10-5
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,26 @@
66
* Side Public License, v 1.
77
*/
88

9-
import { React, Fragment } from 'react';
9+
import React, { ReactNode } from 'react';
1010
import './mount';
1111

12-
Cypress.Commands.add('realMount', (children) => {
12+
const realMountCommand = (children: ReactNode) => {
1313
cy.mount(
14-
<Fragment>
14+
<>
1515
<div
1616
data-test-subj="cypress-real-event-target"
1717
style={{ height: '1px', width: '1px' }}
1818
/>
1919
{children}
20-
</Fragment>
20+
</>
2121
).then(() => {
2222
cy.get('[data-test-subj="cypress-real-event-target"]').realClick({
2323
position: 'topLeft',
2424
});
2525
});
26-
});
26+
};
27+
28+
// Export only the type to not confuse code-completion tools
29+
export type realMountCommand = typeof realMountCommand;
30+
31+
Cypress.Commands.add('realMount', realMountCommand);

cypress/webpack.config.js

+14-3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,15 @@ const { ProvidePlugin, DefinePlugin } = require('webpack');
1212

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

15+
const alias = {};
16+
const reactVersion = process.env.REACT_VERSION || '18';
17+
18+
// Setup module aliasing when we're testing an older React version
19+
if (['16', '17'].includes(reactVersion)) {
20+
alias.react = `react-${reactVersion}`;
21+
alias['react-dom'] = `react-dom-${reactVersion}`;
22+
}
23+
1524
module.exports = {
1625
mode: 'development',
1726

@@ -26,6 +35,7 @@ module.exports = {
2635
os: false,
2736
process: require.resolve('process/browser'),
2837
},
38+
alias,
2939
},
3040

3141
module: {
@@ -46,9 +56,9 @@ module.exports = {
4656
loader: 'style-loader',
4757
options: {
4858
insert: 'meta[name="css-styles"]',
49-
}
59+
},
5060
},
51-
'css-loader'
61+
'css-loader',
5262
],
5363
exclude: /node_modules/,
5464
},
@@ -62,7 +72,8 @@ module.exports = {
6272
}),
6373

6474
new DefinePlugin({
65-
THEME_IMPORT, // allow cypress/suport/index.js to require the correct css file
75+
THEME_IMPORT, // allow cypress/support/component.tsx to require the correct css file
76+
'process.env.REACT_VERSION': JSON.stringify(reactVersion),
6677
}),
6778
],
6879
};

package.json

+22-15
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
"lint-sass": "yarn stylelint \"**/*.scss\" --quiet-deprecation-warnings",
3333
"test": "yarn lint && yarn test-unit",
3434
"test-ci": "yarn test && yarn test-cypress",
35-
"test-unit": "cross-env NODE_ENV=test jest --config ./scripts/jest/config.json",
35+
"test-unit": "cross-env NODE_ENV=test jest --config ./scripts/jest/config.js",
3636
"test-a11y": "node ./scripts/a11y-testing",
3737
"test-staged": "yarn lint && node scripts/test-staged.js",
3838
"test-cypress": "node ./scripts/cypress",
@@ -56,17 +56,18 @@
5656
},
5757
"resolutions": {
5858
"**/prismjs": "1.27.0",
59-
"**/react": "^17.0.0",
59+
"**/react": "^18",
60+
"**/@types/react": "^18",
6061
"react-focus-lock": "^2.9.5"
6162
},
6263
"pre-commit": [
6364
"test-staged"
6465
],
6566
"dependencies": {
67+
"@hello-pangea/dnd": "^16.2.0",
6668
"@types/chroma-js": "^2.0.0",
6769
"@types/lodash": "^4.14.194",
6870
"@types/numeral": "^0.0.28",
69-
"@types/react-beautiful-dnd": "^13.1.2",
7071
"@types/react-input-autosize": "^2.2.1",
7172
"@types/react-virtualized-auto-sizer": "^1.0.1",
7273
"@types/react-window": "^1.8.5",
@@ -79,7 +80,6 @@
7980
"mdast-util-to-hast": "^10.0.0",
8081
"numeral": "^2.0.6",
8182
"prop-types": "^15.6.0",
82-
"react-beautiful-dnd": "^13.1.0",
8383
"react-dropzone": "^11.5.3",
8484
"react-element-to-jsx-string": "^14.3.4",
8585
"react-focus-on": "^3.9.1",
@@ -116,8 +116,10 @@
116116
"@babel/preset-react": "^7.18.6",
117117
"@babel/preset-typescript": "^7.21.5",
118118
"@babel/template": "^7.21.9",
119+
"@cfaester/enzyme-adapter-react-18": "^0.7.0",
119120
"@cypress/code-coverage": "^3.10.0",
120-
"@cypress/react": "^5.10.3",
121+
"@cypress/react": "^7.0.3",
122+
"@cypress/react18": "^2.0.0",
121123
"@cypress/webpack-dev-server": "^1.7.0",
122124
"@elastic/charts": "^53.1.1",
123125
"@elastic/datemath": "^5.0.3",
@@ -139,17 +141,18 @@
139141
"@svgr/core": "8.0.0",
140142
"@svgr/plugin-jsx": "^8.0.1",
141143
"@svgr/plugin-svgo": "^8.0.1",
142-
"@testing-library/dom": "^8.12.0",
143144
"@testing-library/jest-dom": "^5.16.3",
144-
"@testing-library/react": "^12.1.5",
145+
"@testing-library/react": "^14.0.0",
146+
"@testing-library/react-16-17": "npm:@testing-library/react@^12.1.5",
145147
"@testing-library/react-hooks": "^7.0.2",
146148
"@testing-library/user-event": "^13.5.0",
149+
"@types/cheerio": "^0.22.31",
147150
"@types/classnames": "^2.2.10",
148151
"@types/enzyme": "^3.10.5",
149152
"@types/jest": "^24.0.6",
150153
"@types/node": "^10.17.5",
151-
"@types/react": "^17.0.38",
152-
"@types/react-dom": "^17.0.11",
154+
"@types/react": "^18.2.14",
155+
"@types/react-dom": "^18.2.6",
153156
"@types/react-is": "^17.0.3",
154157
"@types/react-router-dom": "^5.3.3",
155158
"@types/tabbable": "^3.1.2",
@@ -225,9 +228,13 @@
225228
"prop-types": "^15.6.0",
226229
"puppeteer": "^5.5.0",
227230
"raw-loader": "^4.0.1",
228-
"react": "^17.0.2",
231+
"react": "^18.2.0",
232+
"react-16": "npm:react@^16.14.0",
233+
"react-17": "npm:react@^17.0.2",
229234
"react-docgen-typescript": "^2.2.2",
230-
"react-dom": "^17.0.2",
235+
"react-dom": "^18.2.0",
236+
"react-dom-16": "npm:react-dom@^16.14.0",
237+
"react-dom-17": "npm:react-dom@^17.0.2",
231238
"react-helmet": "^6.1.0",
232239
"react-redux": "^7.2.1",
233240
"react-refresh": "^0.11.0",
@@ -261,12 +268,12 @@
261268
"@elastic/datemath": "^5.0.2",
262269
"@emotion/css": "11.x",
263270
"@emotion/react": "11.x",
264-
"@types/react": "^16.9 || ^17.0",
265-
"@types/react-dom": "^16.9 || ^17.0",
271+
"@types/react": "^16.9 || ^17.0 || ^18.0",
272+
"@types/react-dom": "^16.9 || ^17.0 || ^18.0",
266273
"moment": "^2.13.0",
267274
"prop-types": "^15.5.0",
268-
"react": "^16.12 || ^17.0",
269-
"react-dom": "^16.12 || ^17.0",
275+
"react": "^16.12 || ^17.0 || ^18.0",
276+
"react-dom": "^16.12 || ^17.0 || ^18.0",
270277
"typescript": "~4.5.3"
271278
},
272279
"files": [

scripts/babel/proptypes-from-ts-props/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ function stripTypeScript(filename, ast) {
1010
return babelCore.transform(babelCore.transformFromAst(ast).code, {
1111
filename: filename,
1212
babelrc: false,
13-
presets: ['@babel/typescript'],
13+
presets: [['@babel/typescript', { allowDeclareFields: true }]],
1414
}).code;
1515
}
1616

scripts/cypress.js

+8
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,18 @@ const argv = yargs(hideBin(process.argv))
2121
dev: { type: 'boolean' },
2222
theme: { type: 'string', default: 'light', choices: ['light', 'dark'] },
2323
a11y: { type: 'boolean' },
24+
'react-version': {
25+
type: 'number',
26+
default: 18,
27+
choices: [16, 17, 18],
28+
},
2429
}).argv;
2530

2631
const isDev = argv.hasOwnProperty('dev');
2732
const isA11y = argv.hasOwnProperty('a11y');
2833
const skipScss = argv.hasOwnProperty('skip-css');
2934
const theme = argv.theme;
35+
const reactVersion = argv['react-version'];
3036

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

69+
console.log(info(`Running tests on React v${reactVersion}`));
6270
console.log(info(`${isDev ? 'Opening' : 'Running'} cypress`));
6371
console.log(log(cypressCommand));
6472
execSync(cypressCommand, {

scripts/jest/config.js

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
const getCacheDirectory =
2+
require('jest-config/build/getCacheDirectory').default;
3+
4+
// Set REACT_VERSION env variable to latest if empty or invalid
5+
if (!['16', '17', '18'].includes(process.env.REACT_VERSION)) {
6+
process.env.REACT_VERSION = '18';
7+
}
8+
9+
const reactVersion = process.env.REACT_VERSION;
10+
11+
console.log(`Running tests on React v${reactVersion}`);
12+
13+
/** @type {import('jest').Config} */
14+
const config = {
15+
rootDir: '../../',
16+
roots: [
17+
'<rootDir>/src/',
18+
'<rootDir>/src-docs/src/components',
19+
'<rootDir>/scripts/babel',
20+
'<rootDir>/scripts/tests',
21+
'<rootDir>/scripts/eslint-plugin',
22+
],
23+
collectCoverageFrom: [
24+
'src/{components,services,global_styling}/**/*.{ts,tsx,js,jsx}',
25+
'!src/{components,services,global_styling}/**/*.{testenv,spec,a11y,stories}.{ts,tsx,js,jsx}',
26+
'!src/{components,services,global_styling}/index.ts',
27+
'!src/{components,services,global_styling}/**/*/index.ts',
28+
'!src/components/date_picker/react-datepicker/**/*.{js,jsx}',
29+
],
30+
moduleNameMapper: {
31+
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
32+
'<rootDir>/scripts/jest/mocks/file_mock.js',
33+
'\\.(css|less|scss)$': '<rootDir>/scripts/jest/mocks/style_mock.js',
34+
},
35+
setupFiles: [
36+
'<rootDir>/scripts/jest/setup/enzyme.js',
37+
'<rootDir>/scripts/jest/setup/throw_on_console_error.js',
38+
'<rootDir>/scripts/jest/setup/mocks.js',
39+
],
40+
setupFilesAfterEnv: [
41+
'<rootDir>/scripts/jest/setup/polyfills.js',
42+
'<rootDir>/scripts/jest/setup/unmount_enzyme.js',
43+
],
44+
coverageDirectory: '<rootDir>/reports/jest-coverage',
45+
coverageReporters: ['json', 'html'],
46+
moduleFileExtensions: ['ts', 'tsx', 'js', 'json'],
47+
testMatch: ['**/*.test.js', '**/*.test.ts', '**/*.test.tsx'],
48+
transform: {
49+
'^.+\\.(js|tsx?)$': 'babel-jest',
50+
},
51+
snapshotSerializers: [
52+
'<rootDir>/node_modules/enzyme-to-json/serializer',
53+
'<rootDir>/scripts/jest/setup/emotion',
54+
],
55+
// react version and user permissions aware cache directory
56+
cacheDirectory: `${getCacheDirectory()}_react-${reactVersion}`,
57+
};
58+
59+
if (['16', '17'].includes(reactVersion)) {
60+
config.moduleNameMapper[
61+
'^@testing-library/react((\\\\/.*)?)$'
62+
] = `@testing-library/react-16-17$1`;
63+
config.moduleNameMapper['^react((\\/.*)?)$'] = `react-${reactVersion}$1`;
64+
65+
config.moduleNameMapper[
66+
'^react-dom((\\/.*)?)$'
67+
] = `react-dom-${reactVersion}$1`;
68+
}
69+
70+
module.exports = config;

0 commit comments

Comments
 (0)