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

Core: Maintenance changes for NextJS embedding #25086

Merged
merged 12 commits into from
Dec 11, 2023
Merged
3 changes: 2 additions & 1 deletion code/builders/builder-manager/src/utils/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const renderHTML = async (
refs: Promise<Record<string, Ref>>,
logLevel: Promise<string>,
docsOptions: Promise<DocsOptions>,
{ versionCheck, previewUrl, configType }: Options
{ versionCheck, previewUrl, configType, ignorePreview }: Options
) => {
const titleRef = await title;
const templateRef = await template;
Expand All @@ -54,5 +54,6 @@ export const renderHTML = async (
PREVIEW_URL: JSON.stringify(previewUrl, null, 2), // global preview URL
},
head: (await customHead) || '',
ignorePreview,
});
};
2 changes: 2 additions & 0 deletions code/builders/builder-manager/templates/template.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@
import './sb-manager/runtime.js';
</script>

<% if (!ignorePreview) { %>
<link href="./sb-preview/runtime.js" rel="prefetch" as="script" />
<% } %>
</body>
</html>
1 change: 1 addition & 0 deletions code/lib/cli/src/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ command('dev')
)
.option('--force-build-preview', 'Build the preview iframe even if you are using --preview-url')
.option('--docs', 'Build a documentation-only site using addon-docs')
.option('--exact-port', 'Exit early if the desired port is not available')
.option(
'--initial-path [path]',
'URL path to be appended when visiting Storybook for the first time'
Expand Down
2 changes: 1 addition & 1 deletion code/lib/core-server/src/build-dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export async function buildDevStandalone(
);
// updateInfo are cached, so this is typically pretty fast
const [port, versionCheck] = await Promise.all([
getServerPort(options.port),
getServerPort(options.port, { exactPort: options.exactPort }),
versionUpdates
? updateCheck(packageJson.version)
: Promise.resolve({ success: false, cached: false, data: {}, time: Date.now() }),
Expand Down
21 changes: 16 additions & 5 deletions code/lib/core-server/src/utils/server-address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,22 @@ export function getServerAddresses(
};
}

export const getServerPort = (port?: number) =>
detectFreePort(port).catch((error) => {
logger.error(error);
process.exit(-1);
});
interface PortOptions {
exactPort?: boolean;
}

export const getServerPort = (port?: number, { exactPort }: PortOptions = {}) =>
detectFreePort(port)
.then((freePort) => {
if (freePort !== port && exactPort) {
process.exit(-1);
}
return freePort;
})
.catch((error) => {
logger.error(error);
process.exit(-1);
});

export const getServerChannelUrl = (port: number, { https }: { https?: boolean }) => {
return `${https ? 'wss' : 'ws'}://localhost:${port}/storybook-server-channel`;
Expand Down
3 changes: 2 additions & 1 deletion code/lib/preview-api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,5 +85,6 @@ export type { PropDescriptor } from './store';
*/
export { ClientApi } from './client-api';
export { StoryStore } from './store';
export { Preview, PreviewWeb } from './preview-web';
export { Preview, PreviewWeb, PreviewWithSelection, UrlStore, WebView } from './preview-web';
export type { SelectionStore, View } from './preview-web';
export { start } from './core-client';
5 changes: 5 additions & 0 deletions code/lib/preview-api/src/modules/preview-web/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ export { Preview } from './Preview';
export { PreviewWeb } from './PreviewWeb';
export { PreviewWithSelection } from './PreviewWithSelection';

export type { SelectionStore } from './SelectionStore';
export { UrlStore } from './UrlStore';
export type { View } from './View';
export { WebView } from './WebView';

export { simulatePageLoad, simulateDOMContentLoaded } from './simulate-pageload';

export { DocsContext } from './docs-context/DocsContext';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type {
StoryContext,
Parameters,
ComposedStoryFn,
StrictArgTypes,
} from '@storybook/types';

import { HooksContext } from '../../../addons';
Expand Down Expand Up @@ -89,6 +90,7 @@ export function composeStory<TRenderer extends Renderer = Renderer, TArgs extend
args: story.initialArgs as Partial<TArgs>,
play: story.playFunction as ComposedStoryPlayFn<TRenderer, Partial<TArgs>>,
parameters: story.parameters as Parameters,
argTypes: story.argTypes as StrictArgTypes<TArgs>,
id: story.id,
}
);
Expand Down
3 changes: 2 additions & 1 deletion code/lib/types/src/modules/composedStory.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/naming-convention */

import type { Renderer, StoryId } from '@storybook/csf';
import type { Renderer, StoryId, StrictArgTypes } from '@storybook/csf';

import type {
AnnotatedStoryFn,
Expand Down Expand Up @@ -60,6 +60,7 @@ export type ComposedStoryFn<
id: StoryId;
storyName: string;
parameters: Parameters;
argTypes: StrictArgTypes<TArgs>;
};
/**
* Based on a module of stories, it returns all stories within it, filtering non-stories
Expand Down
1 change: 1 addition & 0 deletions code/lib/types/src/modules/core-common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ export interface CLIOptions {
enableCrashReports?: boolean;
host?: string;
initialPath?: string;
exactPort?: boolean;
/**
* @deprecated Use 'staticDirs' Storybook Configuration option instead
*/
Expand Down
3 changes: 2 additions & 1 deletion code/renderers/react/src/entry-preview.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export const parameters: {} = { renderer: 'react' };
export { render, renderToCanvas } from './render';
export { render } from './render';
export { renderToCanvas } from './renderToCanvas';
81 changes: 3 additions & 78 deletions code/renderers/react/src/render.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
import { global } from '@storybook/global';
import type { FC } from 'react';
import React, { Component as ReactComponent, StrictMode, Fragment } from 'react';
import { renderElement, unmountElement } from '@storybook/react-dom-shim';
import React from 'react';

import type { RenderContext, ArgsStoryFn } from '@storybook/types';
import type { ArgsStoryFn } from '@storybook/types';

import type { ReactRenderer, StoryContext } from './types';

const { FRAMEWORK_OPTIONS } = global;
import type { ReactRenderer } from './types';

export const render: ArgsStoryFn<ReactRenderer> = (args, context) => {
const { id, component: Component } = context;
Expand All @@ -19,73 +14,3 @@ export const render: ArgsStoryFn<ReactRenderer> = (args, context) => {

return <Component {...args} />;
};

class ErrorBoundary extends ReactComponent<{
showException: (err: Error) => void;
showMain: () => void;
children?: React.ReactNode;
}> {
state = { hasError: false };

static getDerivedStateFromError() {
return { hasError: true };
}

componentDidMount() {
const { hasError } = this.state;
const { showMain } = this.props;
if (!hasError) {
showMain();
}
}

componentDidCatch(err: Error) {
const { showException } = this.props;
// message partially duplicates stack, strip it
showException(err);
}

render() {
const { hasError } = this.state;
const { children } = this.props;

return hasError ? null : children;
}
}

const Wrapper = FRAMEWORK_OPTIONS?.strictMode ? StrictMode : Fragment;

export async function renderToCanvas(
{
storyContext,
unboundStoryFn,
showMain,
showException,
forceRemount,
}: RenderContext<ReactRenderer>,
canvasElement: ReactRenderer['canvasElement']
) {
const Story = unboundStoryFn as FC<StoryContext<ReactRenderer>>;

const content = (
<ErrorBoundary showMain={showMain} showException={showException}>
<Story {...storyContext} />
</ErrorBoundary>
);

// For React 15, StrictMode & Fragment doesn't exists.
const element = Wrapper ? <Wrapper>{content}</Wrapper> : content;

// In most cases, we need to unmount the existing set of components in the DOM node.
// Otherwise, React may not recreate instances for every story run.
// This could leads to issues like below:
// https://github.com/storybookjs/react-storybook/issues/81
// (This is not the case when we change args or globals to the story however)
if (forceRemount) {
unmountElement(canvasElement);
}

await renderElement(element, canvasElement);

return () => unmountElement(canvasElement);
}
80 changes: 80 additions & 0 deletions code/renderers/react/src/renderToCanvas.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { global } from '@storybook/global';
import type { FC } from 'react';
import React, { Component as ReactComponent, StrictMode, Fragment } from 'react';
import { renderElement, unmountElement } from '@storybook/react-dom-shim';

import type { RenderContext } from '@storybook/types';

import type { ReactRenderer, StoryContext } from './types';

const { FRAMEWORK_OPTIONS } = global;

class ErrorBoundary extends ReactComponent<{
showException: (err: Error) => void;
showMain: () => void;
children?: React.ReactNode;
}> {
state = { hasError: false };

static getDerivedStateFromError() {
return { hasError: true };
}

componentDidMount() {
const { hasError } = this.state;
const { showMain } = this.props;
if (!hasError) {
showMain();
}
}

componentDidCatch(err: Error) {
const { showException } = this.props;
// message partially duplicates stack, strip it
showException(err);
}

render() {
const { hasError } = this.state;
const { children } = this.props;

return hasError ? null : children;
}
}

const Wrapper = FRAMEWORK_OPTIONS?.strictMode ? StrictMode : Fragment;

export async function renderToCanvas(
{
storyContext,
unboundStoryFn,
showMain,
showException,
forceRemount,
}: RenderContext<ReactRenderer>,
canvasElement: ReactRenderer['canvasElement']
) {
const Story = unboundStoryFn as FC<StoryContext<ReactRenderer>>;

const content = (
<ErrorBoundary showMain={showMain} showException={showException}>
<Story {...storyContext} />
</ErrorBoundary>
);

// For React 15, StrictMode & Fragment doesn't exists.
const element = Wrapper ? <Wrapper>{content}</Wrapper> : content;

// In most cases, we need to unmount the existing set of components in the DOM node.
// Otherwise, React may not recreate instances for every story run.
// This could leads to issues like below:
// https://github.com/storybookjs/react-storybook/issues/81
// (This is not the case when we change args or globals to the story however)
if (forceRemount) {
unmountElement(canvasElement);
}

await renderElement(element, canvasElement);

return () => unmountElement(canvasElement);
}
Loading