Skip to content

Commit

Permalink
feat(Preview): add startRoute prop to override Provider default
Browse files Browse the repository at this point in the history
  • Loading branch information
SSHari committed Apr 2, 2023
1 parent b60bd89 commit d557f9d
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 16 deletions.
4 changes: 3 additions & 1 deletion sandpack-react/src/components/Navigator/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,21 @@ const inputClassName = css({
export interface NavigatorProps {
clientId: string;
onURLChange?: (newURL: string) => void;
startRoute?: string;
}

export const Navigator = ({
clientId,
onURLChange,
className,
startRoute,
...props
}: NavigatorProps & React.HTMLAttributes<HTMLDivElement>): JSX.Element => {
const [baseUrl, setBaseUrl] = React.useState<string>("");
const { sandpack, dispatch, listen } = useSandpack();

const [relativeUrl, setRelativeUrl] = React.useState<string>(
sandpack.startRoute ?? "/"
startRoute ?? sandpack.startRoute ?? "/"
);

const [backEnabled, setBackEnabled] = React.useState(false);
Expand Down
39 changes: 39 additions & 0 deletions sandpack-react/src/components/Preview/Preview.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,45 @@ export const WithNavigator: React.FC = () => (
</SandpackProvider>
);

export const MultipleRoutePreviews: React.FC = () => {
const multiRoutePreviewCode = `
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
const router = createBrowserRouter([
{ path: "/about", element: <div>About!</div> },
{ path: "/careers", element: <div>Careers!</div> },
]);
export default function MultiRoutePreviews() {
return <RouterProvider router={router} />;
}
`;

return (
<SandpackProvider
options={{ startRoute: "default" }}
files={{
"/App.js": multiRoutePreviewCode,
"package.json": JSON.stringify({
dependencies: {
react: "^18.0.0",
"react-dom": "^18.0.0",
"react-scripts": "^4.0.0",
"react-router-dom": "^6.10.0",
},
main: "/index.js",
}),
}}
template="react"
>
<SandpackLayout>
<SandpackPreview showNavigator startRoute="/about" />
<SandpackPreview showNavigator startRoute="/careers" />
</SandpackLayout>
</SandpackProvider>
);
};

export const AutoResize: React.FC = () => (
<SandpackProvider
files={{
Expand Down
13 changes: 10 additions & 3 deletions sandpack-react/src/components/Preview/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export interface PreviewProps {
showOpenNewtab?: boolean;
actionsChildren?: JSX.Element;
children?: JSX.Element;
startRoute?: string;
}

const previewClassName = css({
Expand Down Expand Up @@ -104,12 +105,14 @@ export const SandpackPreview = React.forwardRef<
actionsChildren = <></>,
children,
className,
startRoute,
...props
},
ref
) => {
const { sandpack, listen, iframe, getClient, clientId } =
useSandpackClient();
const { sandpack, listen, iframe, getClient, clientId } = useSandpackClient(
{ startRoute }
);
const [iframeComputedHeight, setComputedAutoHeight] = React.useState<
number | null
>(null);
Expand Down Expand Up @@ -158,7 +161,11 @@ export const SandpackPreview = React.forwardRef<
{...props}
>
{showNavigator && (
<Navigator clientId={clientId} onURLChange={handleNewURL} />
<Navigator
clientId={clientId}
onURLChange={handleNewURL}
startRoute={startRoute}
/>
)}

<div className={classNames(c("preview-container"), previewClassName)}>
Expand Down
43 changes: 34 additions & 9 deletions sandpack-react/src/contexts/utils/useClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,17 @@ interface SandpackConfigState {
status: SandpackStatus;
}

export type ClientPropsOverride = { startRoute?: string };

export interface UseClientOperations {
clients: Record<string, SandpackClientType>;
initializeSandpackIframe: () => void;
runSandpack: () => Promise<void>;
unregisterBundler: (clientId: string) => void;
registerBundler: (
iframe: HTMLIFrameElement,
clientId: string
clientId: string,
clientPropsOverride?: ClientPropsOverride
) => Promise<void>;
registerReactDevTools: (value: ReactDevToolsMode) => void;
addListener: (
Expand Down Expand Up @@ -85,7 +88,12 @@ export const useClient: UseClient = ({ options, customSetup }, filesState) => {
*/
const intersectionObserver = useRef<IntersectionObserver | null>(null);
const lazyAnchorRef = useRef<HTMLDivElement>(null);
const preregisteredIframes = useRef<Record<string, HTMLIFrameElement>>({});
const preregisteredIframes = useRef<
Record<
string,
{ iframe: HTMLIFrameElement; clientPropsOverride?: ClientPropsOverride }
>
>({});
const clients = useRef<Record<string, SandpackClientType>>({});
const timeoutHook = useRef<NodeJS.Timer | null>(null);
const unsubscribeClientListeners = useRef<
Expand All @@ -106,7 +114,8 @@ export const useClient: UseClient = ({ options, customSetup }, filesState) => {
const createClient = useCallback(
async (
iframe: HTMLIFrameElement,
clientId: string
clientId: string,
clientPropsOverride?: ClientPropsOverride
): Promise<SandpackClientType> => {
options ??= {};
customSetup ??= {};
Expand All @@ -131,7 +140,7 @@ export const useClient: UseClient = ({ options, customSetup }, filesState) => {
{
externalResources: options.externalResources,
bundlerURL: options.bundlerURL,
startRoute: options.startRoute,
startRoute: clientPropsOverride?.startRoute ?? options.startRoute,
fileResolver: options.fileResolver,
skipEval: options.skipEval ?? false,
logLevel: options.logLevel,
Expand Down Expand Up @@ -208,8 +217,13 @@ export const useClient: UseClient = ({ options, customSetup }, filesState) => {
const runSandpack = useCallback(async (): Promise<void> => {
await Promise.all(
Object.keys(preregisteredIframes.current).map(async (clientId) => {
const iframe = preregisteredIframes.current[clientId];
clients.current[clientId] = await createClient(iframe, clientId);
const { iframe, clientPropsOverride = {} } =
preregisteredIframes.current[clientId];
clients.current[clientId] = await createClient(
iframe,
clientId,
clientPropsOverride
);
})
);

Expand Down Expand Up @@ -267,11 +281,22 @@ export const useClient: UseClient = ({ options, customSetup }, filesState) => {
]);

const registerBundler = useCallback(
async (iframe: HTMLIFrameElement, clientId: string): Promise<void> => {
async (
iframe: HTMLIFrameElement,
clientId: string,
clientPropsOverride?: ClientPropsOverride
): Promise<void> => {
if (state.status === "running") {
clients.current[clientId] = await createClient(iframe, clientId);
clients.current[clientId] = await createClient(
iframe,
clientId,
clientPropsOverride
);
} else {
preregisteredIframes.current[clientId] = iframe;
preregisteredIframes.current[clientId] = {
iframe,
clientPropsOverride,
};
}
},
[createClient, state.status]
Expand Down
11 changes: 9 additions & 2 deletions sandpack-react/src/hooks/useSandpackClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import * as React from "react";

import type { SandpackState } from "../types";
import { generateRandomId } from "../utils/stringUtils";
import type { ClientPropsOverride } from "../contexts/utils/useClient";

import { useSandpack } from "./useSandpack";

Expand All @@ -28,7 +29,9 @@ interface UseSandpackClient {
*
* @category Hooks
*/
export const useSandpackClient = (): UseSandpackClient => {
export const useSandpackClient = (
clientPropsOverride?: ClientPropsOverride
): UseSandpackClient => {
const { sandpack, listen, dispatch } = useSandpack();
const iframeRef = React.useRef<HTMLIFrameElement | null>(null);
const clientId = React.useRef<string>(generateRandomId());
Expand All @@ -38,7 +41,11 @@ export const useSandpackClient = (): UseSandpackClient => {
const clientIdValue = clientId.current;

if (iframeElement !== null) {
sandpack.registerBundler(iframeElement, clientIdValue);
sandpack.registerBundler(
iframeElement,
clientIdValue,
clientPropsOverride
);
}

return (): void => sandpack.unregisterBundler(clientIdValue);
Expand Down
5 changes: 4 additions & 1 deletion sandpack-react/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import type { SANDBOX_TEMPLATES } from "./templates";

import type { CodeEditorProps } from ".";

import type { ClientPropsOverride } from "./contexts/utils/useClient";

/**
* ------------------------ Public documentation ------------------------
*
Expand Down Expand Up @@ -541,7 +543,8 @@ export interface SandpackState {
runSandpack: () => Promise<void>;
registerBundler: (
iframe: HTMLIFrameElement,
clientId: string
clientId: string,
clientPropsOverride?: ClientPropsOverride
) => Promise<void>;
unregisterBundler: (clientId: string) => void;
updateFile: (
Expand Down

0 comments on commit d557f9d

Please sign in to comment.