Unverified Commit b0874e05 authored by Andrey Azov's avatar Andrey Azov Committed by GitHub
Browse files

Update to React18 (#736)

- Make sure Storybook works
- Use promise api of new useEvent
- Fix tests
- Switch React 18 to concurrent mode and fix some bugs in genome browser
- Update react-spring to fix a react18-associated bug
- Replace react-ga with custom implementation
parent 170509b2
Pipeline #273594 passed with stages
in 4 minutes and 52 seconds
......@@ -6,6 +6,9 @@ module.exports = {
},
stories: ['../stories/**/*.stories.tsx'],
webpackFinal: (config) => webpackConfig(config),
features: {
babelModeV7: true,
},
addons: [
'@storybook/addon-essentials'
]
......
This diff is collapsed.
......@@ -30,32 +30,30 @@
"coverage": "jest --coverage"
},
"dependencies": {
"@apollo/client": "3.5.10",
"@ensembl/ensembl-genome-browser": "0.2.1",
"@loadable/component": "5.15.2",
"@loadable/server": "5.15.2",
"@react-spring/web": "9.4.4",
"@react-spring/web": "9.4.5-beta.1",
"@reduxjs/toolkit": "1.8.1",
"@sentry/browser": "6.19.6",
"@sentry/browser": "6.19.7",
"classnames": "2.3.1",
"comlink": "4.3.1",
"core-js": "3.22.2",
"core-js": "3.22.4",
"cross-fetch": "3.1.5",
"d3": "6.7.0",
"express": "4.17.3",
"express": "4.18.1",
"filesize": "8.0.7",
"graphql": "16.3.0",
"graphql": "16.4.0",
"graphql-request": "4.2.0",
"http-proxy-middleware": "2.0.6",
"idb": "7.0.1",
"lodash": "4.17.21",
"query-string": "7.1.1",
"react": "17.0.2",
"react": "18.1.0",
"react-cookie": "4.1.1",
"react-dom": "17.0.2",
"react-ga": "3.3.0",
"react-dom": "18.1.0",
"react-helmet-async": "1.3.0",
"react-redux": "7.2.6",
"react-redux": "8.0.1",
"react-router": "6.3.0",
"react-router-dom": "6.3.0",
"redux": "4.2.0",
......@@ -66,36 +64,35 @@
"what-input": "5.2.11"
},
"devDependencies": {
"@babel/core": "7.17.9",
"@babel/core": "7.17.10",
"@babel/plugin-transform-modules-commonjs": "7.17.9",
"@babel/preset-env": "7.16.11",
"@babel/preset-env": "7.17.10",
"@babel/preset-react": "7.16.7",
"@babel/preset-typescript": "7.16.7",
"@faker-js/faker": "6.2.0",
"@faker-js/faker": "6.3.1",
"@loadable/babel-plugin": "5.13.2",
"@loadable/webpack-plugin": "5.15.2",
"@storybook/addon-essentials": "6.4.22",
"@storybook/addons": "6.4.22",
"@storybook/builder-webpack5": "6.4.22",
"@storybook/manager-webpack5": "6.4.22",
"@storybook/react": "6.4.22",
"@storybook/theming": "6.4.22",
"@storybook/addon-essentials": "6.5.0-beta.5",
"@storybook/addons": "6.5.0-beta.5",
"@storybook/builder-webpack5": "6.5.0-beta.5",
"@storybook/manager-webpack5": "6.5.0-beta.5",
"@storybook/react": "6.5.0-beta.5",
"@storybook/theming": "6.5.0-beta.5",
"@svgr/webpack": "6.2.1",
"@testing-library/jest-dom": "5.16.4",
"@testing-library/react": "12.1.3",
"@testing-library/react-hooks": "7.0.2",
"@testing-library/user-event": "13.5.0",
"@testing-library/react": "13.2.0",
"@testing-library/user-event": "14.1.1",
"@types/d3": "7.1.0",
"@types/express": "4.17.13",
"@types/jest": "27.4.1",
"@types/jest": "27.5.0",
"@types/loadable__component": "5.13.4",
"@types/loadable__server": "5.12.6",
"@types/loadable__webpack-plugin": "5.7.3",
"@types/lodash": "4.14.182",
"@types/node": "17.0.25",
"@types/node": "17.0.31",
"@types/prettier": "2.6.0",
"@types/react": "17.0.39",
"@types/react-dom": "17.0.13",
"@types/react": "18.0.8",
"@types/react-dom": "18.0.3",
"@types/react-redux": "7.1.24",
"@types/react-router-dom": "5.3.3",
"@types/react-router-hash-link": "2.4.5",
......@@ -104,54 +101,55 @@
"@types/webpack-hot-middleware": "2.25.6",
"@types/webpack-node-externals": "2.5.3",
"@types/workbox-webpack-plugin": "5.1.8",
"@typescript-eslint/eslint-plugin": "5.20.0",
"@typescript-eslint/parser": "5.20.0",
"@typescript-eslint/eslint-plugin": "5.22.0",
"@typescript-eslint/parser": "5.22.0",
"babel-core": "7.0.0-bridge.0",
"babel-jest": "27.5.1",
"babel-jest": "28.0.3",
"babel-loader": "8.2.5",
"babel-plugin-react-remove-properties": "0.3.0",
"compression-webpack-plugin": "9.2.0",
"copy-webpack-plugin": "10.2.4",
"css-loader": "6.7.1",
"css-minimizer-webpack-plugin": "3.4.1",
"eslint": "8.13.0",
"eslint": "8.14.0",
"eslint-config-prettier": "8.5.0",
"eslint-plugin-jest": "26.1.4",
"eslint-plugin-jest": "26.1.5",
"eslint-plugin-prettier": "4.0.0",
"eslint-plugin-react": "7.29.4",
"eslint-plugin-react-hooks": "4.4.0",
"fork-ts-checker-webpack-plugin": "7.2.6",
"eslint-plugin-react-hooks": "4.5.0",
"fork-ts-checker-webpack-plugin": "7.2.11",
"gh-pages": "3.2.3",
"html-webpack-plugin": "5.5.0",
"husky": "7.0.4",
"identity-obj-proxy": "3.0.0",
"image-webpack-loader": "8.1.0",
"jest": "27.5.1",
"jest": "28.0.3",
"jest-environment-jsdom": "28.0.2",
"jest-fetch-mock": "3.0.3",
"jest-transform-stub": "2.0.0",
"licence-manager": "git+https://github.com/Ensembl/ensembl-licence-manager.git#36b534d28ff26d345b4ba4da0cbc18e1e0f7a6b7",
"lint-staged": "12.4.0",
"lint-staged": "12.4.1",
"mini-css-extract-plugin": "2.6.0",
"msw": "0.39.2",
"nodemon": "2.0.15",
"postcss": "8.4.12",
"nodemon": "2.0.16",
"postcss": "8.4.13",
"postcss-loader": "6.2.1",
"postcss-preset-env": "7.4.3",
"postcss-preset-env": "7.5.0",
"prettier": "2.6.2",
"redux-mock-store": "1.5.4",
"rimraf": "3.0.2",
"sass": "1.50.1",
"sass": "1.51.0",
"sass-loader": "12.6.0",
"source-map-loader": "3.0.1",
"style-loader": "3.3.1",
"stylelint": "14.7.1",
"stylelint": "14.8.2",
"stylelint-config-recommended-scss": "6.0.0",
"stylelint-scss": "4.2.0",
"stylelint-webpack-plugin": "3.2.0",
"terser-webpack-plugin": "5.3.1",
"ts-multipick": "1.0.0",
"ts-node": "10.7.0",
"typescript": "4.6.3",
"typescript": "4.6.4",
"webpack": "5.72.0",
"webpack-bundle-analyzer": "4.5.0",
"webpack-cli": "4.9.2",
......
......@@ -18,7 +18,6 @@ export type AnalyticsOptions = {
category: string;
action: string;
label?: string;
nonInteraction?: boolean;
value?: number;
species?: string;
app?: string;
......
......@@ -103,7 +103,7 @@ describe('<CheckboxGrid />', () => {
'.checkboxDefault'
) as HTMLElement;
userEvent.click(firstCheckbox);
await userEvent.click(firstCheckbox);
const checkedStatus = defaultOptions[0].isChecked;
......
......@@ -72,28 +72,28 @@ describe('<CheckboxWithRadios />', () => {
expect(container.querySelector('.radio')).toBeFalsy();
});
it('displays all the radios when the checkbox is checked', () => {
it('displays all the radios when the checkbox is checked', async () => {
const { container } = renderCheckboxWithRadios();
const checkboxElement = container.querySelector('.checkboxDefault');
userEvent.click(checkboxElement as HTMLElement);
await userEvent.click(checkboxElement as HTMLElement);
expect(container.querySelectorAll('.radio').length).toBe(
defaultProps.options.length
);
});
it('calls the onChange when the radio is changed with the selected option', () => {
it('calls the onChange when the radio is changed with the selected option', async () => {
const { container } = renderCheckboxWithRadios();
const checkboxElement = container.querySelector('.checkboxDefault');
userEvent.click(checkboxElement as HTMLInputElement);
await userEvent.click(checkboxElement as HTMLInputElement);
const firstRadioInput = container.querySelector('.radio');
userEvent.click(firstRadioInput as HTMLInputElement);
await userEvent.click(firstRadioInput as HTMLInputElement);
expect(onChange).toHaveBeenCalledWith(defaultProps.options[0].value);
});
......
......@@ -74,12 +74,12 @@ describe('<CheckboxWithSelects />', () => {
expect(container.querySelectorAll('.select').length).toBe(1);
});
it('displays one Select when the checkbox is checked', () => {
it('displays one Select when the checkbox is checked', async () => {
const { container } = renderCheckboxWithSelects();
const checkboxElement = container.querySelector('.checkboxDefault');
userEvent.click(checkboxElement as HTMLElement);
await userEvent.click(checkboxElement as HTMLElement);
expect(container.querySelectorAll('.select').length).toBe(1);
});
......@@ -95,12 +95,12 @@ describe('<CheckboxWithSelects />', () => {
).toBeTruthy();
});
it('does not display the remove button next to the Select if no option is selected ', () => {
it('does not display the remove button next to the Select if no option is selected ', async () => {
const { container } = renderCheckboxWithSelects();
const checkboxElement = container.querySelector('.checkboxDefault');
userEvent.click(checkboxElement as HTMLElement);
await userEvent.click(checkboxElement as HTMLElement);
expect(container.querySelectorAll('.select').length).toBe(1);
......@@ -123,7 +123,7 @@ describe('<CheckboxWithSelects />', () => {
expect(container.querySelectorAll('.addIconHolder').length).toBe(1);
});
it('displays another select when the plus button is clicked', () => {
it('displays another select when the plus button is clicked', async () => {
const { container } = renderCheckboxWithSelects({
selectedOptions: [defaultProps.options[0].value]
});
......@@ -132,12 +132,12 @@ describe('<CheckboxWithSelects />', () => {
const addIcon = container.querySelector('.addIconHolder .imageButton');
userEvent.click(addIcon as HTMLElement);
await userEvent.click(addIcon as HTMLElement);
expect(container.querySelectorAll('.select').length).toBe(2);
});
it('displays all the options when no options are selected', () => {
it('displays all the options when no options are selected', async () => {
const { container } = renderCheckboxWithSelects();
const selectElement = container.querySelector(
......@@ -146,27 +146,27 @@ describe('<CheckboxWithSelects />', () => {
const selectControl = selectElement.querySelector('.selectControl');
userEvent.click(selectControl as HTMLElement);
await userEvent.click(selectControl as HTMLElement);
expect(selectElement.querySelectorAll('.option')).toHaveLength(
defaultProps.options.length
);
});
it('hides the options that are already selected within the new Select', () => {
it('hides the options that are already selected within the new Select', async () => {
const { container } = renderCheckboxWithSelects({
selectedOptions: [defaultProps.options[0].value]
});
const addIcon = container.querySelector('.addIconHolder .imageButton');
userEvent.click(addIcon as HTMLElement);
await userEvent.click(addIcon as HTMLElement);
const allSelects = container.querySelectorAll('.select');
const lastSelect = allSelects[allSelects.length - 1];
const lastSelectControl = lastSelect.querySelector('.selectControl');
userEvent.click(lastSelectControl as HTMLElement);
await userEvent.click(lastSelectControl as HTMLElement);
expect(lastSelect.querySelectorAll('.option')).toHaveLength(
defaultProps.options.length - 1
......@@ -185,25 +185,25 @@ describe('<CheckboxWithSelects />', () => {
expect(container.querySelector('.addIconHolder')).toBeFalsy();
});
it('calls the onChange function when an option is selected', () => {
it('calls the onChange function when an option is selected', async () => {
const { container } = renderCheckboxWithSelects({
selectedOptions: [defaultProps.options[0].value]
});
const addIcon = container.querySelector('.addIconHolder .imageButton');
userEvent.click(addIcon as HTMLElement);
await userEvent.click(addIcon as HTMLElement);
const allSelects = container.querySelectorAll('.select');
const lastSelect = allSelects[allSelects.length - 1];
const lastSelectControl = lastSelect.querySelector('.selectControl');
userEvent.click(lastSelectControl as HTMLElement);
await userEvent.click(lastSelectControl as HTMLElement);
const lastSelectOptionsPanel = lastSelect.querySelector('.optionsPanel');
const targetOption = lastSelectOptionsPanel?.querySelector('li');
userEvent.click(targetOption as HTMLElement);
await userEvent.click(targetOption as HTMLElement);
expect(onChange).toHaveBeenCalledWith([
defaultProps.options[0].value,
......@@ -211,7 +211,7 @@ describe('<CheckboxWithSelects />', () => {
]);
});
it('calls the onChange function when an option is removed', () => {
it('calls the onChange function when an option is removed', async () => {
const selectedOptions = [
defaultProps.options[0].value,
defaultProps.options[1].value
......@@ -223,7 +223,7 @@ describe('<CheckboxWithSelects />', () => {
const lastRemoveIconButton = lastRemoveIcon.querySelector('.imageButton');
userEvent.click(lastRemoveIconButton as HTMLElement);
await userEvent.click(lastRemoveIconButton as HTMLElement);
expect(onChange).toHaveBeenCalledWith([defaultProps.options[0].value]);
});
......
......@@ -99,11 +99,11 @@ describe('<CheckboxWithTextfields />', () => {
).toBeTruthy();
});
it('calls the onReset prop when the checkbox is unchecked', () => {
it('calls the onReset prop when the checkbox is unchecked', async () => {
const { getByTestId } = renderCheckboxWithTextfields({ textValue: 'foo' });
const checkboxLabelElement = getByTestId('checkbox-label-grid');
userEvent.click(checkboxLabelElement as HTMLElement);
await userEvent.click(checkboxLabelElement as HTMLElement);
expect(onReset).toBeCalled();
});
......@@ -124,7 +124,7 @@ describe('<CheckboxWithTextfields />', () => {
.querySelectorAll('.removeFileIcon')
[randomNumber].querySelector('.imageButton');
userEvent.click(removeIcon as HTMLElement);
await userEvent.click(removeIcon as HTMLElement);
expect(onFilesChange).toBeCalled();
});
......
......@@ -45,7 +45,7 @@ export const setAttributes = createAsyncAction(
)<undefined, Attributes, Error>();
export const fetchAttributes: ActionCreator<
ThunkAction<void, any, null, Action<string>>
ThunkAction<void, any, void, Action<string>>
> = () => (dispatch, getState: () => RootState) => {
const activeGenomeId = getCustomDownloadActiveGenomeId(getState());
......@@ -74,7 +74,7 @@ export const fetchAttributes: ActionCreator<
};
export const updateSelectedAttributes: ActionCreator<
ThunkAction<void, any, null, Action<string>>
ThunkAction<void, any, void, Action<string>>
> = (selectedAttributes: JSONValue) => (dispatch, getState: () => RootState) => {
const activeGenomeId = getCustomDownloadActiveGenomeId(getState());
......@@ -95,7 +95,7 @@ export const updateSelectedAttributes: ActionCreator<
};
export const resetSelectedAttributes: ActionCreator<
ThunkAction<void, any, null, Action<string>>
ThunkAction<void, any, void, Action<string>>
> = () => (dispatch, getState: () => RootState) => {
const activeGenomeId = getCustomDownloadActiveGenomeId(getState());
......@@ -116,7 +116,7 @@ export const resetSelectedAttributes: ActionCreator<
};
export const updateUi: ActionCreator<
ThunkAction<void, any, null, Action<string>>
ThunkAction<void, any, void, Action<string>>
> = (attributesUi: JSONValue) => (dispatch, getState: () => RootState) => {
const activeGenomeId = getCustomDownloadActiveGenomeId(getState());
......@@ -137,7 +137,7 @@ export const updateUi: ActionCreator<
};
export const setOrthologueAttributes: ActionCreator<
ThunkAction<void, any, null, Action<string>>
ThunkAction<void, any, void, Action<string>>
> =
(orthologues: { [key: string]: AttributeWithOptions }) =>
(dispatch, getState: () => RootState) => {
......@@ -160,7 +160,7 @@ export const setOrthologueAttributes: ActionCreator<
};
export const setOrthologueShowBestMatches: ActionCreator<
ThunkAction<void, any, null, Action<string>>
ThunkAction<void, any, void, Action<string>>
> = (showBestMatches: boolean) => (dispatch, getState: () => RootState) => {
const activeGenomeId = getCustomDownloadActiveGenomeId(getState());
......@@ -181,7 +181,7 @@ export const setOrthologueShowBestMatches: ActionCreator<
};
export const setOrthologueShowAll: ActionCreator<
ThunkAction<void, any, null, Action<string>>
ThunkAction<void, any, void, Action<string>>
> = (showAll: boolean) => (dispatch, getState: () => RootState) => {
const activeGenomeId = getCustomDownloadActiveGenomeId(getState());
......@@ -202,7 +202,7 @@ export const setOrthologueShowAll: ActionCreator<
};
export const setOrthologueApplyToAllSpecies: ActionCreator<
ThunkAction<void, any, null, Action<string>>
ThunkAction<void, any, void, Action<string>>
> = (applyToAllSpecies: boolean) => (dispatch, getState: () => RootState) => {
const activeGenomeId = getCustomDownloadActiveGenomeId(getState());
......@@ -223,7 +223,7 @@ export const setOrthologueApplyToAllSpecies: ActionCreator<
};
export const setOrthologueSearchTerm: ActionCreator<
ThunkAction<void, any, null, Action<string>>
ThunkAction<void, any, void, Action<string>>
> = (searchTerm: string) => (dispatch, getState: () => RootState) => {
const activeGenomeId = getCustomDownloadActiveGenomeId(getState());
......@@ -250,7 +250,7 @@ export const setOrthologueSpecies = createAsyncAction(
)<{ searchTerm: string }, void, Error>();
export const updateOrthologueSpecies: ActionCreator<
ThunkAction<void, any, null, Action<string>>
ThunkAction<void, any, void, Action<string>>
> =
(orthologueSpecies: CheckboxGridOption[]) =>
(dispatch, getState: () => RootState) => {
......@@ -273,7 +273,7 @@ export const updateOrthologueSpecies: ActionCreator<
};
export const fetchOrthologueSpecies: ActionCreator<
ThunkAction<void, any, null, Action<string>>
ThunkAction<void, any, void, Action<string>>
> =
(searchTerm: string, orthologueSpecies: CheckboxGridOption[]) =>
(dispatch, getState: () => RootState) => {
......@@ -335,7 +335,7 @@ export const fetchOrthologueSpecies: ActionCreator<
};
export const setAttributesAccordionExpandedPanel: ActionCreator<
ThunkAction<void, any, null, Action<string>>
ThunkAction<void, any, void, Action<string>>
> = (expandedPanels: string[]) => (dispatch, getState: () => RootState) => {
const activeGenomeId = getCustomDownloadActiveGenomeId(getState());
......
......@@ -45,7 +45,7 @@ export const updateActiveGenomeId = createAction(
)<string | null>();
export const setActiveGenomeId: ActionCreator<
ThunkAction<void, any, null, Action<string>>
ThunkAction<void, any, void, Action<string>>
> = (activeGenomeId: string) => (dispatch, getState: () => RootState) => {
dispatch(updateActiveGenomeId(activeGenomeId));
......@@ -72,7 +72,7 @@ export const updateActiveConfigurationForGenome = createAction(
)();
export const updateSelectedPreFilter: ActionCreator<
ThunkAction<void, any, null, Action<string>>
ThunkAction<void, any, void, Action<string>>
> = (selectedPreFilter: boolean) => (dispatch, getState: () => RootState) => {
const activeGenomeId = getCustomDownloadActiveGenomeId(getState());
......@@ -93,7 +93,7 @@ export const updateSelectedPreFilter: ActionCreator<
};
export const togglePreFiltersPanel: ActionCreator<
ThunkAction<void, any, null, Action<string>>
ThunkAction<void, any, void, Action<string>>
> = (showPreFiltersPanel: boolean) => (dispatch, getState: () => RootState) => {
const activeGenomeId = getCustomDownloadActiveGenomeId(getState());
......@@ -120,7 +120,7 @@ export const setPreviewResult = createAsyncAction(
)<undefined, JSONValue, Error>();
export const fetchPreviewResult: ActionCreator<
ThunkAction<void, any, null, Action<string>>
ThunkAction<void, any, void, Action<string>>
> = (endpointURL: string) => (dispatch) => {
try {
apiService
......@@ -143,7 +143,7 @@ export const setIsLoadingResult = createAction(
)<boolean>();
export const setShowPreview: ActionCreator<
ThunkAction<void, any, null, Action<string>>
ThunkAction<void, any, void, Action<string>>
> = (showSummary: boolean) => (dispatch, getState: () => RootState) => {
const activeGenomeId = getCustomDownloadActiveGenomeId(getState());
......@@ -164,7 +164,7 @@ export const setShowPreview: ActionCreator<
};
export const setShowExampleData: ActionCreator<
ThunkAction<void, any, null, Action<string>>
ThunkAction<void, any, void, Action<string>>
> = (showExampleData: boolean) => (dispatch, getState: () => RootState) => {
const activeGenomeId = getCustomDownloadActiveGenomeId(getState());
......@@ -185,7 +185,7 @@ export const setShowExampleData: ActionCreator<
};
export const setDownloadType: ActionCreator<
ThunkAction<void, any, null, Action<string>>
ThunkAction<void, any, void, Action<string>>
> = (downloadType: string) => (dispatch, getState: () => RootState) => {
const activeGenomeId = getCustomDownloadActiveGenomeId(getState());
......