Skip to content

Commit

Permalink
fix(updatefile): support multiple files (#453)
Browse files Browse the repository at this point in the history
  • Loading branch information
danilowoz authored Apr 29, 2022
1 parent b3a607c commit a05faac
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 27 deletions.
42 changes: 42 additions & 0 deletions sandpack-react/src/contexts/sandpackContenxt.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from "react";
import { SandpackProvider } from "./sandpackContext";

import { create } from "react-test-renderer";
describe(SandpackProvider, () => {
it("updates a file", () => {
const root = create(<SandpackProvider template="react" />);
const instance = root.getInstance();

expect(instance.state.files["/App.js"]).toEqual({
code: `export default function App() {
return <h1>Hello World</h1>
}
`,
});

instance.updateFile("/App.js", "Foo");

expect(instance.state.files["/App.js"]).toEqual({ code: `Foo` });
});

it("updates multiples files", () => {
const root = create(<SandpackProvider template="react" />);
const instance = root.getInstance();

instance.updateFile({ "/App.js": "Foo", "/index.js": "Baz" });

expect(instance.state.files["/App.js"]).toEqual({ code: `Foo` });
expect(instance.state.files["/index.js"]).toEqual({ code: `Baz` });
});

it("updates multiples files in a row", () => {
const root = create(<SandpackProvider template="react" />);
const instance = root.getInstance();

instance.updateFile("/App.js", "Foo");
instance.updateFile("/index.js", "Baz");

expect(instance.state.files["/App.js"]).toEqual({ code: `Foo` });
expect(instance.state.files["/index.js"]).toEqual({ code: `Baz` });
});
});
33 changes: 20 additions & 13 deletions sandpack-react/src/contexts/sandpackContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
} from "@codesandbox/sandpack-client";
import isEqual from "lodash.isequal";
import * as React from "react";
import { SandpackFiles } from "..";

import type {
SandpackContext,
Expand All @@ -25,7 +26,10 @@ import type {
SandpackSetup,
SandpackInitMode,
} from "../types";
import { getSandpackStateFromProps } from "../utils/sandpackUtils";
import {
convertedFilesToBundlerFiles,
getSandpackStateFromProps,
} from "../utils/sandpackUtils";
import { generateRandomId } from "../utils/stringUtils";

/**
Expand Down Expand Up @@ -199,25 +203,27 @@ class SandpackProvider extends React.PureComponent<
/**
* @hidden
*/
updateCurrentFile = (newCode: string): void => {
this.updateFile(this.state.activePath, newCode);
updateCurrentFile = (code: string): void => {
this.updateFile(this.state.activePath, code);
};

/**
* @hidden
*/
updateFile = (path: string, newCode: string): void => {
if (newCode === this.state.files[this.state.activePath]?.code) {
return;
}
updateFile = (pathOrFiles: string | SandpackFiles, code?: string): void => {
let files = this.state.files;

const { files } = this.state;
const newFiles = {
...files,
[path]: { code: newCode },
};
if (typeof pathOrFiles === "string" && code) {
if (code === this.state.files[pathOrFiles]?.code) {
return;
}

files = { ...files, [pathOrFiles]: { code: code } };
} else if (typeof pathOrFiles === "object") {
files = { ...files, ...convertedFilesToBundlerFiles(pathOrFiles) };
}

this.setState({ files: newFiles }, this.updateClients);
this.setState({ files });
};

/**
Expand All @@ -226,6 +232,7 @@ class SandpackProvider extends React.PureComponent<
updateClients = (): void => {
const { files, sandpackStatus } = this.state;
const { recompileMode, recompileDelay } = this.props;

if (sandpackStatus !== "running") {
return;
}
Expand Down
2 changes: 1 addition & 1 deletion sandpack-react/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export interface SandpackState {
runSandpack: () => void;
registerBundler: (iframe: HTMLIFrameElement, clientId: string) => void;
unregisterBundler: (clientId: string) => void;
updateFile: (path: string, newCode: string) => void;
updateFile: (pathOrFiles: string | SandpackFiles, code?: string) => void;
updateCurrentFile: (newCode: string) => void;
openFile: (path: string) => void;
closeFile: (path: string) => void;
Expand Down
17 changes: 17 additions & 0 deletions sandpack-react/src/utils/sandpackUtils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { REACT_TEMPLATE } from "../templates/react";
import {
getSandpackStateFromProps,
createSetupFromUserInput,
convertedFilesToBundlerFiles,
} from "./sandpackUtils";

describe(getSandpackStateFromProps, () => {
Expand Down Expand Up @@ -248,3 +249,19 @@ describe(createSetupFromUserInput, () => {
});
});
});

describe(convertedFilesToBundlerFiles, () => {
it("converts regular files to bundler files", () => {
expect(convertedFilesToBundlerFiles({ name: "code" })).toEqual({
name: { code: "code" },
});
});

it("keeps bundler files original", () => {
expect(
convertedFilesToBundlerFiles({ name: { code: "code", hidden: true } })
).toEqual({
name: { code: "code", hidden: true },
});
});
});
28 changes: 16 additions & 12 deletions sandpack-react/src/utils/sandpackUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { SANDBOX_TEMPLATES } from "../templates";
import type {
SandboxEnvironment,
SandboxTemplate,
SandpackFiles,
SandpackPredefinedTemplate,
SandpackSetup,
} from "../types";
Expand Down Expand Up @@ -148,6 +149,20 @@ export const getSetup = (
};
};

export const convertedFilesToBundlerFiles = (
files: SandpackFiles
): SandpackBundlerFiles => {
return Object.keys(files).reduce((acc: SandpackBundlerFiles, key) => {
if (typeof files[key] === "string") {
acc[key] = { code: files[key] as string };
} else {
acc[key] = files[key] as SandpackBundlerFile;
}

return acc;
}, {});
};

export const createSetupFromUserInput = (
setup?: SandpackSetup
): Partial<SandboxTemplate> | null => {
Expand All @@ -161,18 +176,7 @@ export const createSetupFromUserInput = (

const { files } = setup;

const convertedFiles = Object.keys(files).reduce(
(acc: SandpackBundlerFiles, key) => {
if (typeof files[key] === "string") {
acc[key] = { code: files[key] as string };
} else {
acc[key] = files[key] as SandpackBundlerFile;
}

return acc;
},
{}
);
const convertedFiles = convertedFilesToBundlerFiles(files);

return {
...setup,
Expand Down
3 changes: 2 additions & 1 deletion website/docs/docs/advanced-usage/hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ Plus, `useSandpack` exposes a bunch of methods that you can use to manage the cu
| `resetAllFiles` | Reset all files for all paths to the original state |
| `resetFile` | Reset the code for a given path |
| `setActiveFile` | Set a specific file as active in a given path |
| `updateFile` | Update the content of a file in a given path |
| `updateFile` | Update the content of a file in a given path or multiple files |
| `updateCurrentFile` | Update the content of the current file |

## useSandpackNavigation

Expand Down

0 comments on commit a05faac

Please sign in to comment.