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

experiment with universal channel #22939

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
1 change: 0 additions & 1 deletion code/builders/builder-vite/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
},
"dependencies": {
"@storybook/channel-postmessage": "7.1.0-alpha.29",
"@storybook/channel-websocket": "7.1.0-alpha.29",
"@storybook/client-logger": "7.1.0-alpha.29",
"@storybook/core-common": "7.1.0-alpha.29",
"@storybook/csf-plugin": "7.1.0-alpha.29",
Expand Down
5 changes: 1 addition & 4 deletions code/builders/builder-vite/src/codegen-set-addon-channel.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
export async function generateAddonSetupCode() {
return `
import { createChannel as createPostMessageChannel } from '@storybook/channel-postmessage';
import { createChannel as createWebSocketChannel } from '@storybook/channel-websocket';
import { addons } from '@storybook/preview-api';

const channel = createPostMessageChannel({ page: 'preview' });
addons.setChannel(channel);
window.__STORYBOOK_ADDONS_CHANNEL__ = channel;

if (window.CONFIG_TYPE === 'DEVELOPMENT'){
const serverChannel = createWebSocketChannel({});
addons.setServerChannel(serverChannel);
window.__STORYBOOK_SERVER_CHANNEL__ = serverChannel;
window.__STORYBOOK_SERVER_CHANNEL__ = channel;
Copy link
Member

Choose a reason for hiding this comment

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

It feels weird that both channels come from '@storybook/channel-postmessage'. Should instead we make a new '@storybook/channel-duplex' or something?

Copy link
Member Author

Choose a reason for hiding this comment

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

@tmeasday perhaps in 8.0 we can simply have a @storybook/channel that can do any, with some config passed in, no need for separate packages?

I did not want to add another package.

Copy link
Member

Choose a reason for hiding this comment

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

Appreciate that. Maybe we should rename the package then (would that be bad too?) -- or at least indicate somewhere that 'channel-postmessage' is a bit of a misnomer as this now includes 2 channels.

}
`.trim();
}
1 change: 0 additions & 1 deletion code/builders/builder-webpack5/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@
"@storybook/addons": "7.1.0-alpha.29",
"@storybook/api": "7.1.0-alpha.29",
"@storybook/channel-postmessage": "7.1.0-alpha.29",
"@storybook/channel-websocket": "7.1.0-alpha.29",
"@storybook/channels": "7.1.0-alpha.29",
"@storybook/client-api": "7.1.0-alpha.29",
"@storybook/client-logger": "7.1.0-alpha.29",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { global } from '@storybook/global';

import { ClientApi, PreviewWeb, addons, composeConfigs } from '@storybook/preview-api';
import { createChannel as createPostMessageChannel } from '@storybook/channel-postmessage';
import { createChannel as createWebSocketChannel } from '@storybook/channel-websocket';

import { importFn } from './{{storiesFilename}}';

Expand All @@ -13,9 +12,7 @@ const channel = createPostMessageChannel({ page: 'preview' });
addons.setChannel(channel);

if (global.CONFIG_TYPE === 'DEVELOPMENT'){
const serverChannel = createWebSocketChannel({});
addons.setServerChannel(serverChannel);
window.__STORYBOOK_SERVER_CHANNEL__ = serverChannel;
window.__STORYBOOK_SERVER_CHANNEL__ = channel;
}

const preview = new PreviewWeb();
Expand Down
1 change: 0 additions & 1 deletion code/frameworks/html-vite/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
"@storybook/addons": "7.1.0-alpha.29",
"@storybook/builder-vite": "7.1.0-alpha.29",
"@storybook/channel-postmessage": "7.1.0-alpha.29",
"@storybook/channel-websocket": "7.1.0-alpha.29",
"@storybook/client-api": "7.1.0-alpha.29",
"@storybook/core-server": "7.1.0-alpha.29",
"@storybook/html": "7.1.0-alpha.29",
Expand Down
1 change: 1 addition & 0 deletions code/lib/channel-postmessage/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"prep": "../../../scripts/prepare/bundle.ts"
},
"dependencies": {
"@storybook/channel-websocket": "7.1.0-alpha.29",
"@storybook/channels": "7.1.0-alpha.29",
"@storybook/client-logger": "7.1.0-alpha.29",
"@storybook/core-events": "7.1.0-alpha.29",
Expand Down
16 changes: 13 additions & 3 deletions code/lib/channel-postmessage/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import { global } from '@storybook/global';
import * as EVENTS from '@storybook/core-events';
import { Channel } from '@storybook/channels';
import type { ChannelHandler, ChannelEvent, ChannelTransport } from '@storybook/channels';
import { WebsocketTransport } from '@storybook/channel-websocket';
import { logger, pretty } from '@storybook/client-logger';
import { isJSON, parse, stringify } from 'telejson';
import qs from 'qs';
import invariant from 'tiny-invariant';

const { document, location } = global;
const { document, location, CONFIG_TYPE } = global;

interface Config {
page: 'manager' | 'preview';
Expand Down Expand Up @@ -289,8 +290,17 @@ const getEventSourceUrl = (event: MessageEvent) => {
* Creates a channel which communicates with an iframe or child window.
*/
export function createChannel({ page }: Config): Channel {
const transport = new PostmsgTransport({ page });
return new Channel({ transport });
const transports: ChannelTransport[] = [new PostmsgTransport({ page })];

if (CONFIG_TYPE === 'DEVELOPMENT') {
const protocol = window.location.protocol === 'http:' ? 'ws' : 'wss';
const { hostname, port } = window.location;
const channelUrl = `${protocol}://${hostname}:${port}/storybook-server-channel`;

transports.push(new WebsocketTransport({ url: channelUrl, onError: () => {} }));
}

return new Channel({ transports });
}

// backwards compat with builder-vite
Expand Down
1 change: 1 addition & 0 deletions code/lib/channel-postmessage/src/typings.d.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
declare var CHANNEL_OPTIONS: any;
declare var CONFIG_TYPE: 'DEVELOPMENT' | 'PRODUCTION';
11 changes: 7 additions & 4 deletions code/lib/channels/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe('Channel', () => {
});

it('should not set transport if not passed as an argument', () => {
channel = new Channel();
channel = new Channel({});
expect(channel.hasTransport).toBeFalsy();
});

Expand All @@ -29,7 +29,7 @@ describe('Channel', () => {
});

it('should set isAsync to false as default value', () => {
channel = new Channel();
channel = new Channel({});
expect(channel.isAsync).toBeFalsy();
});

Expand Down Expand Up @@ -104,8 +104,11 @@ describe('Channel', () => {
listenerOutputData = data;
});
const sendSpy = jest.fn();
// @ts-expect-error (Converted from ts-ignore)
channel.transport.send = sendSpy;
// @ts-expect-error (access private property for testing purposes)
channel.transports.forEach((t) => {
// eslint-disable-next-line no-param-reassign
t.send = sendSpy;
});
channel.emit(eventName, ...listenerInputData);
expect(listenerOutputData).toEqual(listenerInputData);
expect(sendSpy.mock.calls[0][1]).toEqual({ depth: 1 });
Expand Down
44 changes: 33 additions & 11 deletions code/lib/channels/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,20 @@ interface EventsKeyValue {
[key: string]: Listener[];
}

interface ChannelArgs {
type ChannelArgs = ChannelArgsSingle | ChannelArgsMulti;
interface ChannelArgsSingle {
transport?: ChannelTransport;
async?: boolean;
}
interface ChannelArgsMulti {
transports: ChannelTransport[];
async?: boolean;
}

const isMulti = (args: ChannelArgs): args is ChannelArgsMulti => {
// @ts-expect-error (we guard against this right here)
return args.transports !== undefined;
};

const generateRandomId = () => {
// generates a random 13 character string
Expand All @@ -40,18 +50,30 @@ export class Channel {

private data: Record<string, any> = {};

private readonly transport: ChannelTransport | undefined = undefined;
private readonly transports: ChannelTransport[] = [];

constructor(input: ChannelArgsMulti);
constructor(input: ChannelArgsSingle);
constructor(input: ChannelArgs = {}) {
this.isAsync = input.async || false;

constructor({ transport, async = false }: ChannelArgs = {}) {
this.isAsync = async;
if (transport) {
this.transport = transport;
this.transport.setHandler((event) => this.handleEvent(event));
if (isMulti(input)) {
this.transports = input.transports || [];

this.transports.forEach((t) => {
t.setHandler((event) => this.handleEvent(event));
});
} else {
this.transports = input.transport ? [input.transport] : [];
}

this.transports.forEach((t) => {
t.setHandler((event) => this.handleEvent(event));
});
}

get hasTransport() {
return !!this.transport;
return this.transports.length > 0;
}

addListener(eventName: string, listener: Listener) {
Expand All @@ -67,9 +89,9 @@ export class Channel {
}

const handler = () => {
if (this.transport) {
this.transport.send(event, options);
}
this.transports.forEach((t) => {
t.send(event, options);
});
this.handleEvent(event);
};

Expand Down
11 changes: 0 additions & 11 deletions code/lib/manager-api/src/modules/channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,8 @@ import type { API, ModuleFn } from '../index';
export interface SubAPI {
getChannel: () => API_Provider<API>['channel'];
on: (type: string, cb: Listener) => () => void;
experimental_onServer: (type: string, cb: Listener) => () => void;
off: (type: string, cb: Listener) => void;
emit: (type: string, ...args: any[]) => void;
experimental_emitServer: (type: string, ...args: any[]) => void;
once: (type: string, cb: Listener) => void;
collapseAll: () => void;
expandAll: () => void;
Expand All @@ -27,11 +25,6 @@ export const init: ModuleFn<SubAPI, SubState> = ({ provider }) => {

return () => provider.channel.removeListener(type, cb);
},
experimental_onServer: (type, cb) => {
provider.serverChannel.addListener(type, cb);

return () => provider.serverChannel.removeListener(type, cb);
},
off: (type, cb) => provider.channel.removeListener(type, cb),
once: (type, cb) => provider.channel.once(type, cb),
emit: (type, data, ...args) => {
Expand All @@ -47,10 +40,6 @@ export const init: ModuleFn<SubAPI, SubState> = ({ provider }) => {
}
provider.channel.emit(type, data, ...args);
},
experimental_emitServer: (type, data, ...args) => {
provider.serverChannel.emit(type, data, ...args);
},

collapseAll: () => {
provider.channel.emit(STORIES_COLLAPSE_ALL, {});
},
Expand Down
2 changes: 1 addition & 1 deletion code/lib/manager-api/src/modules/stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,7 @@ export const init: ModuleFn<SubAPI, SubState, true> = ({
});

if (FEATURES?.storyStoreV7) {
fullAPI.experimental_onServer(STORY_INDEX_INVALIDATED, () => fullAPI.fetchIndex());
fullAPI.on(STORY_INDEX_INVALIDATED, () => fullAPI.fetchIndex());
await fullAPI.fetchIndex();
}
};
Expand Down
Loading