Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for React 18 #7012

Merged
merged 24 commits into from
Aug 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
35b3358
feat(DragAndDrop): Switch from react-beautiful-dnd to its fork @hello…
tkajtoch Jun 23, 2023
1c672b1
Add basic React 18 support (#6827)
tkajtoch Jun 29, 2023
160001d
build: Cypress React 18 support and React version switcher (#6933)
tkajtoch Jul 12, 2023
d8e93f1
build: adjust enzyme, RTL and jest to run in React 18 runtime (#6940)
tkajtoch Jul 13, 2023
4551c0e
test: React 18 snapshot adjustments (#6944)
tkajtoch Jul 13, 2023
e36d4cc
test: React 18 act() adjustments (#6943)
tkajtoch Jul 14, 2023
ab7f6cc
feature: EuiDataGrid type improvements to support React 18 (#6958)
tkajtoch Jul 25, 2023
f6e1afe
[React 18] Fix remaining TypeScript errors (#6988)
tkajtoch Jul 25, 2023
1b9ed70
[React 18] Fix cross-version compatibility and unit test errors (#7002)
tkajtoch Jul 26, 2023
bf42924
[React 18] Fix flaky cypress tests (#7003)
tkajtoch Jul 27, 2023
496d1ae
[React 18] Add full <StrictMode> support (#7007)
tkajtoch Jul 28, 2023
6931692
docs: add changelog
tkajtoch Jul 31, 2023
d92103c
feat: add <StrictMode> switcher flag and default to false in docs app
tkajtoch Jul 31, 2023
f601d1f
docs: update changelog to remove <StrictMode> note
tkajtoch Jul 31, 2023
48a2563
Fix crashing popover demo
cee-chen Jul 31, 2023
85a71e5
Fix `EuiWrappingPopover` demo to use React 18
cee-chen Jul 31, 2023
9c220c9
Update CodeSandbox to use React 18 APIs
cee-chen Jul 31, 2023
5b30513
:facepalm: fix CodeSandbox
cee-chen Jul 31, 2023
2230fed
refactor: move react version test utilities to src/test/internal
tkajtoch Aug 1, 2023
c6b40d4
fix: remove code that's been moved to another file and incorrectly re…
tkajtoch Aug 1, 2023
7e214eb
fix: remove unused TextEncoder jest polyfill
tkajtoch Aug 1, 2023
f879d52
Update upcoming_changelogs/7012.md
tkajtoch Aug 1, 2023
2fa936e
[PR feedback] Minor cleanup
cee-chen Aug 1, 2023
95beab1
[EuiInlineEdit] Types cleanup
cee-chen Aug 1, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI, it looks like a lot of the act() issues that came up during your feature branch were the result of a RTL bug that is going to be shortly fixed in another release: testing-library/react-testing-library#1216

Apparently only upgrading to v13.4.0 also would have resolved the issue 🤷

"@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