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

fix: Add support for React 18 #265

Merged
merged 3 commits into from
Dec 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 8 additions & 4 deletions cypress/integration/smoke.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { getFirstFrame, loadPlayroom } from '../support/utils';
import {
assertPreviewContains,
getFirstFrame,
loadPlayroom,
} from '../support/utils';

describe('Smoke', () => {
it('frames are interactive', () => {
Expand All @@ -9,8 +13,8 @@ describe('Smoke', () => {
it('preview mode loads correctly', () => {
cy.visit(
'http://localhost:9000/preview#?code=N4Igxg9gJgpiBcIA8AxCEB8r1YEIEMAnAei2LUyXJxAF8g'
)
.get('body')
.then((el) => expect(el.get(0).innerText).to.eq('Foo\nFoo\nBar'));
);

assertPreviewContains('Foo\nFoo\nBar');
});
});
11 changes: 8 additions & 3 deletions cypress/support/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,14 @@ export const assertFramesMatch = (matches) =>
});

export const assertPreviewContains = (text) =>
cy.get('body').then((el) => {
expect(el.get(0).innerText).to.eq(text);
});
cy
.then(() => {
cy.get('[data-testid="splashscreen"]').should('not.be.visible');
})
.get('body')
.then((el) => {
expect(el.get(0).innerText).to.eq(text);
});

export const loadPlayroom = () =>
cy
Expand Down
27 changes: 27 additions & 0 deletions lib/makeWebpackConfig.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const fs = require('fs');
const path = require('path');
const webpack = require('webpack');
const { merge } = require('webpack-merge');
Expand All @@ -18,6 +19,22 @@ module.exports = async (playroomConfig, options) => {
const relativeResolve = (requirePath) =>
require.resolve(requirePath, { paths: [playroomConfig.cwd] });

let isLegacyReact = true;

try {
// eslint-disable-next-line no-sync
const pkgContents = fs.readFileSync(
relativeResolve('react-dom/package.json'),
{
encoding: 'utf-8',
}
);
const { version } = JSON.parse(pkgContents);
isLegacyReact = !(version.startsWith('18') || version.startsWith('0.0.0'));
} catch (e) {
throw new Error('Unable to read `react-dom` package json');
}

const staticTypes = await getStaticTypes(playroomConfig);

const ourConfig = {
Expand Down Expand Up @@ -158,6 +175,16 @@ module.exports = async (playroomConfig, options) => {
new VanillaExtractPlugin(),
new MiniCssExtractPlugin({ ignoreOrder: true }),
...(options.production ? [] : [new FriendlyErrorsWebpackPlugin()]),
// If using a version of React earlier than 18, ignore the
// react-dom/client import. This hack can be removed when
// support for older versions of React is removed.
...(isLegacyReact
? [
new webpack.IgnorePlugin({
resourceRegExp: /react-dom\/client$/,
}),
]
: []),
],
devtool: !options.production && 'eval-source-map',
};
Expand Down
17 changes: 8 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@
"@types/lodash": "^4.14.168",
"@types/lz-string": "^1.3.34",
"@types/prettier": "^2.2.3",
"@types/react": "^17.0.3",
"@types/react-dom": "^17.0.2",
"@types/react": "^18.0.9",
"@types/react-dom": "^18.0.4",
"@vanilla-extract/babel-plugin": "^1.1.5",
"@vanilla-extract/css": "^1.7.0",
"@vanilla-extract/css-utils": "^0.1.2",
Expand Down Expand Up @@ -108,10 +108,9 @@
"prettier": "^2.2.1",
"prop-types": "^15.7.2",
"query-string": "^6.14.1",
"re-resizable": "^6.9.0",
"react-codemirror2": "^7.2.1",
"re-resizable": "^6.9.6",
"react-docgen-typescript": "^2.1.0",
"react-use": "^17.2.1",
"react-use": "^17.4.0",
"read-pkg-up": "^7.0.1",
"scope-eval": "^1.0.0",
"typescript": "^4.3.2",
Expand All @@ -135,16 +134,16 @@
"husky": "^4.3.8",
"jest": "^26.6.3",
"lint-staged": "^10.5.4",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react": "^18.0.1",
"react-dom": "^18.0.1",
"semantic-release": "^17.4.2",
"serve": "^11.3.2",
"start-server-and-test": "^1.12.0",
"surge": "^0.22.1"
},
"peerDependencies": {
"react": "^16.8 || ^17.0",
"react-dom": "^16.8 || ^17.0"
"react": "^16.8 || ^17 || ^18",
"react-dom": "^16.8 || ^17 || ^18"
},
"volta": {
"node": "12.22.12",
Expand Down
28 changes: 8 additions & 20 deletions src/Playroom/CodeEditor/CodeEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useRef, useContext, useEffect, useCallback } from 'react';
import { useDebouncedCallback } from 'use-debounce';
import { Editor } from 'codemirror';
import CodeMirror, { Editor } from 'codemirror';
import 'codemirror/lib/codemirror.css';
import 'codemirror/theme/neo.css';

Expand All @@ -10,7 +10,7 @@ import { compileJsx } from '../../utils/compileJsx';

import * as styles from './CodeEditor.css';

import { UnControlled as ReactCodeMirror } from 'react-codemirror2';
import { UnControlled as ReactCodeMirror } from './CodeMirror2';
import 'codemirror/mode/jsx/jsx';
import 'codemirror/addon/edit/closetag';
import 'codemirror/addon/edit/closebrackets';
Expand All @@ -21,36 +21,27 @@ import 'codemirror/addon/fold/foldcode';
import 'codemirror/addon/fold/foldgutter';
import 'codemirror/addon/fold/brace-fold';

const completeAfter = (cm: Editor, predicate: () => boolean) => {
const CodeMirror = cm.constructor;
const completeAfter = (cm: Editor, predicate?: () => boolean) => {
if (!predicate || predicate()) {
setTimeout(() => {
if (!cm.state.completionActive) {
// @ts-ignore
cm.showHint({ completeSingle: false });
}
}, 100);
}

// @ts-ignore
return CodeMirror.Pass;
};

const completeIfAfterLt = (cm: Editor) => {
const CodeMirror = cm.constructor;

return completeAfter(cm, () => {
const completeIfAfterLt = (cm: Editor) =>
completeAfter(cm, () => {
const cur = cm.getCursor();
// @ts-ignore
// eslint-disable-next-line new-cap
return cm.getRange(CodeMirror.Pos(cur.line, cur.ch - 1), cur) === '<';
});
};

const completeIfInTag = (cm: Editor) => {
const CodeMirror = cm.constructor;

return completeAfter(cm, () => {
const completeIfInTag = (cm: Editor) =>
completeAfter(cm, () => {
const tok = cm.getTokenAt(cm.getCursor());
if (
tok.type === 'string' &&
Expand All @@ -59,11 +50,9 @@ const completeIfInTag = (cm: Editor) => {
) {
return false;
}
// @ts-ignore
const inner = CodeMirror.innerMode(cm.getMode(), tok.state).state;
return inner.tagName;
});
};

const validateCode = (editorInstance: Editor, code: string) => {
editorInstance.clearGutter('errorGutter');
Expand Down Expand Up @@ -228,7 +217,6 @@ export const CodeEditor = ({ code, onChange, previewCode, hints }: Props) => {
}, [highlightLineNumber]);

return (
// @ts-ignore
<ReactCodeMirror
editorDidMount={(editorInstance) => {
editorInstanceRef.current = editorInstance;
Expand Down Expand Up @@ -259,6 +247,7 @@ export const CodeEditor = ({ code, onChange, previewCode, hints }: Props) => {
autoCloseBrackets: true,
theme: 'neo',
gutters: ['errorGutter', 'CodeMirror-linenumbers', styles.foldGutter],
// @ts-expect-error
hintOptions: { schemaInfo: hints },
viewportMargin: 50,
lineNumbers: true,
Expand All @@ -275,7 +264,6 @@ export const CodeEditor = ({ code, onChange, previewCode, hints }: Props) => {
extraKeys: {
Tab: (cm) => {
if (cm.somethingSelected()) {
// @ts-ignore
cm.indentSelection('add');
} else {
const indent = cm.getOption('indentUnit') as number;
Expand Down
Loading