Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
valentinpalkovic committed Apr 19, 2024
1 parent e7817b0 commit a007256
Show file tree
Hide file tree
Showing 16 changed files with 235 additions and 205 deletions.
4 changes: 1 addition & 3 deletions code/lib/core-events/src/data/argtypes-info.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import type { ArgTypes } from '@storybook/csf';

export interface ArgTypesRequestPayload {
storyId: string;
}
export interface ArgTypesRequestPayload {}

export interface ArgTypesResponsePayload {
argTypes: ArgTypes;
Expand Down
14 changes: 5 additions & 9 deletions code/lib/core-events/src/data/create-new-story.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export interface CreateNewStoryPayload {
export interface CreateNewStoryRequestPayload {
// The filepath of the component for which the Story should be generated for (relative to the project root)
componentFilePath: string;
// The name of the exported component
Expand All @@ -7,12 +7,8 @@ export interface CreateNewStoryPayload {
componentIsDefaultExport: boolean;
}

export interface CreateNewStoryResult {
success: true | false;
result: null | {
storyId: string;
storyFilePath: string;
exportedStoryName: string;
};
error: null | string;
export interface CreateNewStoryResponsePayload {
storyId: string;
storyFilePath: string;
exportedStoryName: string;
}
37 changes: 14 additions & 23 deletions code/lib/core-events/src/data/file-component-search.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,17 @@
export interface FileComponentSearchPayload {
// A regular string or a glob pattern
searchQuery?: string;
}
export interface FileComponentSearchRequestPayload {}

export interface FileComponentSearchResult {
success: true | false;
result: null | {
// The search query - Helps to identify the event on the frontend
searchQuery: string;
files: Array<{
// The filepath relative to the project root
filepath: string;
// Whether a corresponding story file exists
storyFileExists: boolean;
// A list of exported components
exportedComponents: Array<{
// the name of the exported component
name: string;
// True, if the exported component is a default export
default: boolean;
}> | null;
export interface FileComponentSearchResponsePayload {
files: Array<{
// The filepath relative to the project root
filepath: string;
// Whether a corresponding story file exists
storyFileExists: boolean;
// A list of exported components
exportedComponents: Array<{
// the name of the exported component
name: string;
// True, if the exported component is a default export
default: boolean;
}> | null;
};
error: null | string;
}> | null;
}
4 changes: 2 additions & 2 deletions code/lib/core-events/src/data/request-response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ export type RequestData<Payload = void> = {
};

export type ResponseData<Payload = void> =
| { id: string; success: true; payload: Payload }
| { id: string; success: false; error?: string; payload?: Payload };
| { id: string; success: true; error: null; payload: Payload }
| { id: string; success: false; error: string; payload: null };
32 changes: 12 additions & 20 deletions code/lib/core-events/src/data/save-story.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,14 @@
export interface SaveStoryRequest {
id: string;
payload: {
csfId: string;
importPath: string;
args: Record<string, any>;
name?: string;
};
export interface SaveStoryRequestPayload {
args: string | undefined;
csfId: string;
importPath: string;
name?: string;
}

export type SaveStoryResponse = (
| { id: string; success: true }
| { id: string; success: false; error: string }
) & {
payload: {
csfId: string;
newStoryId?: string;
newStoryName?: string;
sourceFileName?: string;
sourceStoryName?: string;
};
};
export interface SaveStoryResponsePayload {
csfId: string;
newStoryId?: string;
newStoryName?: string;
sourceFileName?: string;
sourceStoryName?: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { initCreateNewStoryChannel } from './create-new-story-channel';
import path from 'path';
import type { ChannelTransport } from '@storybook/channels';
import { Channel } from '@storybook/channels';
import type { CreateNewStoryRequestPayload, RequestData } from '@storybook/core-events';
import {
CREATE_NEW_STORYFILE_REQUEST,
CREATE_NEW_STORYFILE_RESPONSE,
Expand Down Expand Up @@ -63,9 +64,12 @@ describe('createNewStoryChannel', () => {
} as any);

mockChannel.emit(CREATE_NEW_STORYFILE_REQUEST, {
componentFilePath: 'src/components/Page.jsx',
componentExportName: 'Page',
componentIsDefaultExport: true,
id: 'components-page--default',
payload: {
componentFilePath: 'src/components/Page.jsx',
componentExportName: 'Page',
componentIsDefaultExport: true,
},
});

await vi.waitFor(() => {
Expand All @@ -74,7 +78,8 @@ describe('createNewStoryChannel', () => {

expect(createNewStoryFileEventListener).toHaveBeenCalledWith({
error: null,
result: {
id: 'components-page--default',
payload: {
storyId: 'components-page--default',
storyFilePath: './src/components/Page.stories.jsx',
exportedStoryName: 'Default',
Expand Down Expand Up @@ -106,18 +111,22 @@ describe('createNewStoryChannel', () => {
} as any);

mockChannel.emit(CREATE_NEW_STORYFILE_REQUEST, {
componentFilePath: 'src/components/Page.jsx',
componentExportName: 'Page',
componentIsDefaultExport: true,
});
id: 'components-page--default',
payload: {
componentFilePath: 'src/components/Page.jsx',
componentExportName: 'Page',
componentIsDefaultExport: true,
},
} satisfies RequestData<CreateNewStoryRequestPayload>);

await vi.waitFor(() => {
expect(createNewStoryFileEventListener).toHaveBeenCalled();
});

expect(createNewStoryFileEventListener).toHaveBeenCalledWith({
error: 'Failed to write file',
result: null,
payload: null,
id: 'components-page--default',
success: false,
});
});
Expand Down
68 changes: 39 additions & 29 deletions code/lib/core-server/src/server-channel/create-new-story-channel.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import type { Options } from '@storybook/types';
import type { Channel } from '@storybook/channels';
import type { CreateNewStoryPayload, CreateNewStoryResult } from '@storybook/core-events';
import type {
CreateNewStoryRequestPayload,
CreateNewStoryResponsePayload,
RequestData,
ResponseData,
} from '@storybook/core-events';
import {
CREATE_NEW_STORYFILE_REQUEST,
CREATE_NEW_STORYFILE_RESPONSE,
Expand All @@ -15,40 +20,45 @@ export function initCreateNewStoryChannel(channel: Channel, options: Options) {
/**
* Listens for events to create a new storyfile
*/
channel.on(CREATE_NEW_STORYFILE_REQUEST, async (data: CreateNewStoryPayload) => {
try {
const { storyFilePath, exportedStoryName, storyFileContent } = await getNewStoryFile(
data,
options
);
channel.on(
CREATE_NEW_STORYFILE_REQUEST,
async (data: RequestData<CreateNewStoryRequestPayload>) => {
try {
const { storyFilePath, exportedStoryName, storyFileContent } = await getNewStoryFile(
data.payload,
options
);

const relativeStoryFilePath = path.relative(process.cwd(), storyFilePath);
const relativeStoryFilePath = path.relative(process.cwd(), storyFilePath);

if (existsSync(storyFilePath)) {
throw new Error(`Story file already exists at .${path.sep}${relativeStoryFilePath}`);
}
if (existsSync(storyFilePath)) {
throw new Error(`Story file already exists at .${path.sep}${relativeStoryFilePath}`);
}

await fs.writeFile(storyFilePath, storyFileContent, 'utf-8');
await fs.writeFile(storyFilePath, storyFileContent, 'utf-8');

const storyId = await getStoryId({ storyFilePath, exportedStoryName }, options);
const storyId = await getStoryId({ storyFilePath, exportedStoryName }, options);

channel.emit(CREATE_NEW_STORYFILE_RESPONSE, {
success: true,
result: {
storyId,
storyFilePath: `./${relativeStoryFilePath}`,
exportedStoryName,
},
error: null,
} satisfies CreateNewStoryResult);
} catch (e: any) {
channel.emit(CREATE_NEW_STORYFILE_RESPONSE, {
success: false,
result: null,
error: e?.message,
} satisfies CreateNewStoryResult);
channel.emit(CREATE_NEW_STORYFILE_RESPONSE, {
success: true,
id: storyId,
payload: {
storyId,
storyFilePath: `./${relativeStoryFilePath}`,
exportedStoryName,
},
error: null,
} satisfies ResponseData<CreateNewStoryResponsePayload>);
} catch (e: any) {
channel.emit(CREATE_NEW_STORYFILE_RESPONSE, {
success: false,
id: data.id,
payload: null,
error: e?.message,
} satisfies ResponseData<CreateNewStoryResponsePayload>);
}
}
});
);

return channel;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { ChannelTransport } from '@storybook/channels';
import { Channel } from '@storybook/channels';
import type { RequestData, FileComponentSearchRequestPayload } from '@storybook/core-events';
import {
FILE_COMPONENT_SEARCH_RESPONSE,
FILE_COMPONENT_SEARCH_REQUEST,
Expand Down Expand Up @@ -51,7 +52,10 @@ describe('file-search-channel', () => {
initFileSearchChannel(mockChannel, mockOptions as any);

mockChannel.addListener(FILE_COMPONENT_SEARCH_RESPONSE, searchResultChannelListener);
mockChannel.emit(FILE_COMPONENT_SEARCH_REQUEST, data);
mockChannel.emit(FILE_COMPONENT_SEARCH_REQUEST, {
id: data.searchQuery,
payload: {},
} satisfies RequestData<FileComponentSearchRequestPayload>);

mocks.searchFiles.mockImplementation(async (...args) => {
// @ts-expect-error Ignore type issue
Expand All @@ -66,8 +70,9 @@ describe('file-search-channel', () => {
);

expect(searchResultChannelListener).toHaveBeenCalledWith({
id: data.searchQuery,
error: null,
result: {
payload: {
files: [
{
exportedComponents: [
Expand Down Expand Up @@ -100,7 +105,6 @@ describe('file-search-channel', () => {
storyFileExists: false,
},
],
searchQuery: 'es-module',
},
success: true,
});
Expand All @@ -113,7 +117,10 @@ describe('file-search-channel', () => {
initFileSearchChannel(mockChannel, mockOptions as any);

mockChannel.addListener(FILE_COMPONENT_SEARCH_RESPONSE, searchResultChannelListener);
mockChannel.emit(FILE_COMPONENT_SEARCH_REQUEST, data);
mockChannel.emit(FILE_COMPONENT_SEARCH_REQUEST, {
id: data.searchQuery,
payload: {},
} satisfies RequestData<FileComponentSearchRequestPayload>);

mocks.searchFiles.mockImplementation(async (...args) => {
// @ts-expect-error Ignore type issue
Expand All @@ -128,10 +135,10 @@ describe('file-search-channel', () => {
);

expect(searchResultChannelListener).toHaveBeenCalledWith({
id: data.searchQuery,
error: null,
result: {
payload: {
files: [],
searchQuery: 'no-file-for-search-query',
},
success: true,
});
Expand All @@ -145,7 +152,10 @@ describe('file-search-channel', () => {

mockChannel.addListener(FILE_COMPONENT_SEARCH_RESPONSE, searchResultChannelListener);

mockChannel.emit(FILE_COMPONENT_SEARCH_REQUEST, data);
mockChannel.emit(FILE_COMPONENT_SEARCH_REQUEST, {
id: data.searchQuery,
payload: {},
} satisfies RequestData<FileComponentSearchRequestPayload>);

mocks.searchFiles.mockRejectedValue(new Error('ENOENT: no such file or directory'));

Expand All @@ -154,9 +164,10 @@ describe('file-search-channel', () => {
});

expect(searchResultChannelListener).toHaveBeenCalledWith({
id: data.searchQuery,
payload: null,
error:
'An error occurred while searching for components in the project.\nENOENT: no such file or directory',
result: null,
success: false,
});
});
Expand Down
Loading

0 comments on commit a007256

Please sign in to comment.