Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -366,8 +366,7 @@ const PyodideRunner = ({ active, outputPanels = ["text", "visual"] }) => {
};

if (!pyodideWorker && active) {
console.warn("PyodideWorker is not initialized");
return;
return null;
}

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,6 @@ const SkulptRunner = ({ active, outputPanels = ["text", "visual"] }) => {
) : (
<Tabs
forceRenderTabPanel={true}
defaultIndex={showVisualOutput ? 0 : 1}
selectedIndex={tabbedViewSelectedIndex}
onSelect={setTabbedViewSelectedIndex}
>
Expand Down
1 change: 1 addition & 0 deletions src/components/Menus/Sidebar/FilePanel/FilePanel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ const FilePanel = ({ isMobile }) => {
? []
: [
<DesignSystemButton
key="new-file"
text={t("filePanel.newFileButton")}
textAways={true}
icon={<PlusIcon />}
Expand Down
14 changes: 13 additions & 1 deletion src/components/ScratchEditor/ScratchIntegrationHOC.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ const ScratchIntegrationHOC = function (WrappedComponent) {
);
return;
}
if (event.data?.type === "webpackOk") {
return;
}

switch (event.data.type) {
case "scratch-gui-download":
Expand Down Expand Up @@ -80,7 +83,16 @@ const ScratchIntegrationHOC = function (WrappedComponent) {
this.props.onClickSave();
}
render() {
const { ...componentProps } = this.props;
const {
loadProject,
localesOnly,
onClickRemix,
onClickSave,
saveProjectSb3,
setStageSize,
...componentProps
} = this.props;

return <WrappedComponent {...componentProps} />;
}
}
Expand Down
12 changes: 12 additions & 0 deletions src/containers/WebComponentLoader.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,17 @@ import {
projectOwnerLoadedEvent,
} from "../events/WebComponentCustomEvents";

const TOAST_CONTAINER_DEFAULTS = {
...(ToastContainer.defaultProps || {}),
};

// react-toastify v8 uses defaultProps on a function component, which React
// warns about in development. We pass the same defaults explicitly instead.
// We should upgrade to version 10 in a different commit, this removes the warning
if (process.env.NODE_ENV === "development") {
ToastContainer.defaultProps = undefined;
}

const WebComponentLoader = (props) => {
const {
assetsIdentifier,
Expand Down Expand Up @@ -222,6 +233,7 @@ const WebComponentLoader = (props) => {
{internalStyles.toString()}
<div id="wc" className={`--${cookies.theme || themeDefault}`}>
<ToastContainer
{...TOAST_CONTAINER_DEFAULTS}
enableMultiContainer
containerId="top-center"
position="top-center"
Expand Down
25 changes: 21 additions & 4 deletions src/hooks/useProject.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { useRef, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { syncProject, setProject } from "../redux/EditorSlice";
import { defaultPythonProject } from "../utils/defaultProjects";
import { createDefaultPythonProject } from "../utils/defaultProjects";
import { useTranslation } from "react-i18next";

export const useProject = ({
Expand Down Expand Up @@ -43,7 +43,13 @@ export const useProject = ({
}, [projectIdentifier]);

useEffect(() => {
if (!loadRemix) {
let didUnmount = false;

const loadProjectData = async () => {
if (loadRemix) {
return;
}

const is_cached_saved_project =
projectIdentifier &&
cachedProject &&
Expand Down Expand Up @@ -105,9 +111,20 @@ export const useProject = ({
return;
}

const data = defaultPythonProject;
const data = await createDefaultPythonProject(effectiveLocale);

if (didUnmount) {
return;
}

dispatch(setProject(data));
}
};

void loadProjectData();

return () => {
didUnmount = true;
};
}, [
code,
projectIdentifier,
Expand Down
9 changes: 8 additions & 1 deletion src/hooks/useProject.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,14 @@ describe("When not embedded", () => {

test("If no identifier uses default python project", () => {
renderHook(() => useProject({}), { wrapper });
expect(setProject).toHaveBeenCalledWith(defaultPythonProject);
return waitFor(() =>
expect(setProject).toHaveBeenCalledWith(
expect.objectContaining({
...defaultPythonProject,
name: "project.untitled",
}),
),
);
});

test("sets project to initialProject if provided", () => {
Expand Down
3 changes: 3 additions & 0 deletions src/scratch.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ import { compose } from "redux";

import GUI, { AppStateHOC } from "@scratch/scratch-gui";
import ScratchIntegrationHOC from "./components/ScratchEditor/ScratchIntegrationHOC.jsx";
import dedupeScratchWarnings from "./utils/dedupeScratchWarnings.js";

import ScratchStyles from "./assets/stylesheets/Scratch.scss";

dedupeScratchWarnings();

const appTarget = document.getElementById("app");
document.getElementById("scratch-loading")?.remove();
GUI.setAppElement(appTarget);
Expand Down
32 changes: 32 additions & 0 deletions src/utils/dedupeDesignSystemWarnings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const designSystemWarningsKey = "__designSystemWarningsDeduped";
const designSystemIconWarning =
"DEPRECATED: icons as React elements will not be supported in future releases";

const dedupeDesignSystemWarnings = () => {
if (
process.env.NODE_ENV !== "development" ||
typeof window !== "object" ||
window[designSystemWarningsKey]
) {
return;
}

window[designSystemWarningsKey] = true;

const originalWarn = console.warn.bind(console);
let didWarnIcon = false;

console.warn = (...args) => {
if (args[0] === designSystemIconWarning) {
if (didWarnIcon) {
return;
}

didWarnIcon = true;
}

originalWarn(...args);
};
};

export default dedupeDesignSystemWarnings;
79 changes: 79 additions & 0 deletions src/utils/dedupeScratchWarnings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
const scratchWarningsKey = "__scratchWarningsDeduped";

const scratchWarningMatchers = {
error: [
[
"Support for defaultProps will be removed",
"React 18 compatibility warnings about function-component defaultProps",
],
[
"React does not recognize the `%s` prop on a DOM element",
"React warnings about scratch-editor props leaking onto DOM nodes",
],
[
"Unknown event handler property `%s`",
"React warnings about unsupported event handler props",
],
[
"findDOMNode is deprecated",
"React warnings about deprecated findDOMNode usage",
],
],
warn: [
[
"componentWillMount has been renamed",
"React warnings about legacy componentWillMount usage",
],
[
"componentWillReceiveProps has been renamed",
"React warnings about legacy componentWillReceiveProps usage",
],
],
};

const dedupeScratchWarnings = () => {
if (
process.env.NODE_ENV !== "development" ||
typeof window !== "object" ||
window[scratchWarningsKey]
) {
return;
}

window[scratchWarningsKey] = true;

const seenSummaries = new Set();
const originalWarn = console.warn.bind(console);

const wrapConsoleMethod = (method) => {
const originalMethod = console[method].bind(console);

return (...args) => {
const message = typeof args[0] === "string" ? args[0] : "";
const summary = scratchWarningMatchers[method]?.find(([needle]) =>
message.includes(needle),
);

if (!summary) {
originalMethod(...args);
return;
}

const [, text] = summary;

if (seenSummaries.has(text)) {
return;
}

seenSummaries.add(text);
originalWarn(
`[scratch-editor] emitted ${text}. Further duplicates suppressed.`,
);
};
};

console.error = wrapConsoleMethod("error");
console.warn = wrapConsoleMethod("warn");
};

export default dedupeScratchWarnings;
42 changes: 40 additions & 2 deletions src/utils/defaultProjects.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import i18n from "./i18n";

const UNTITLED_PROJECT_NAME = "Untitled project";

export const defaultPythonProject = {
project_type: "python",
name: i18n.t("project.untitled"),
name: UNTITLED_PROJECT_NAME,
locale: null,
components: [{ extension: "py", name: "main", content: "", default: true }],
image_list: [],
};

export const defaultHtmlProject = {
project_type: "html",
name: i18n.t("project.untitled"),
name: UNTITLED_PROJECT_NAME,
components: [
{
extension: "html",
Expand All @@ -25,3 +27,39 @@ export const DEFAULT_PROJECTS = {
python: defaultPythonProject,
html: defaultHtmlProject,
};

Copy link
Contributor

Choose a reason for hiding this comment

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

The changes in this file are the only ones that aren't obviously clear to me -

Will this have any user facing behaviour change?
Is there any tests we could add for this file?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is the most complicated change imho.

There is a race condition:

  • If we use i18n.t it causes issues because it's not initialized, solution:
    • Start with a given string, it could be empty but the string is safer.
    • Then find the right i18n value.
    • I did a lot of tests with this one, and i didn't want to spend more time, but i could try cleaning up, this was the version I made to work without not a very invasive change
    • I think an alternative would be to return the const async conditional to have initailized the i18n, maybe add an extra effect in the component.
    • But this felt like smallest change i could think of.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This also fix this state that starts by default:

image

export const createDefaultPythonProject = async (locale = i18n.language) => {
try {
if (locale && i18n.resolvedLanguage !== locale) {
await i18n.changeLanguage?.(locale);
}
} catch {
// Fall back to the default untitled name if locale files fail.
}

return {
...defaultPythonProject,
name: i18n.t("project.untitled", {
lng: locale,
defaultValue: UNTITLED_PROJECT_NAME,
}),
};
};

export const createDefaultHtmlProject = async (locale = i18n.language) => {
try {
if (locale && i18n.resolvedLanguage !== locale) {
await i18n.changeLanguage?.(locale);
}
} catch {
// Fall back to the default untitled name if locale files fail.
}

return {
...defaultHtmlProject,
name: i18n.t("project.untitled", {
lng: locale,
defaultValue: UNTITLED_PROJECT_NAME,
}),
};
};
4 changes: 1 addition & 3 deletions src/web-component.html
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<link rel="icon" href="data:," />
<title>Editor Web component</title>
</head>
<body>
Expand All @@ -76,13 +77,10 @@

// subscribe to the 'codeChanged' custom event which is pushed by the project react component
document.addEventListener("editor-codeChanged", function (e) {
console.log("listener in index html");
const code = webComp.editorCode;
console.log(code);
});

document.addEventListener("editor-runCompleted", (e) => {
console.log(e.detail);
document.getElementById("results").innerText = JSON.stringify(e.detail);
});

Expand Down
3 changes: 3 additions & 0 deletions src/web-component.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import camelCase from "camelcase";
import { stopCodeRun, stopDraw, triggerCodeRun } from "./redux/EditorSlice";
import { BrowserRouter } from "react-router-dom";
import { resetStore } from "./redux/RootSlice";
import dedupeDesignSystemWarnings from "./utils/dedupeDesignSystemWarnings";

dedupeDesignSystemWarnings();

Sentry.init({
dsn: process.env.REACT_APP_SENTRY_DSN,
Expand Down
Loading