Skip to content

Commit

Permalink
rename PRELOAD_STORIES to PRELOAD_ENTRIES event
Browse files Browse the repository at this point in the history
auto map the ref id in lib/api when calling emit
emit preload event when hovering over sidebar items or selecting them with the keyboard
  • Loading branch information
ndelangen committed Jul 27, 2022
1 parent 3db4db6 commit 1aa9f79
Show file tree
Hide file tree
Showing 11 changed files with 101 additions and 29 deletions.
15 changes: 14 additions & 1 deletion code/lib/api/src/modules/channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 26,20 @@ export const init: ModuleFn<SubAPI, SubState> = ({ provider }) => {
},
off: (type, cb) => provider.channel.removeListener(type, cb),
once: (type, cb) => provider.channel.once(type, cb),
emit: (type, ...args) => provider.channel.emit(type, ...args),
emit: (type, data, ...args) => {
if (
data.options.target &&
data.options.target !== 'storybook-preview-iframe' &&
!data.options.target.startsWith('storybook-ref-')
) {
// eslint-disable-next-line no-param-reassign
data.options.target =
data.options.target !== 'storybook_internal'
? `storybook-ref-${data.options.target}`
: 'storybook-preview-iframe';
}
provider.channel.emit(type, data, ...args);
},

collapseAll: () => {
provider.channel.emit(STORIES_COLLAPSE_ALL, {});
Expand Down
17 changes: 8 additions & 9 deletions code/lib/api/src/modules/stories.ts
Original file line number Diff line number Diff line change
@@ -1,7 1,7 @@
import global from 'global';
import { toId, sanitize } from '@storybook/csf';
import {
PRELOAD_STORIES,
PRELOAD_ENTRIES,
STORY_PREPARED,
UPDATE_STORY_ARGS,
RESET_STORY_ARGS,
Expand Down Expand Up @@ -320,19 320,15 @@ export const init: ModuleFn<SubAPI, SubState, true> = ({
fullAPI.emit(UPDATE_STORY_ARGS, {
storyId,
updatedArgs,
options: {
target: refId ? `storybook-ref-${refId}` : 'storybook-preview-iframe',
},
options: { target: refId },
});
},
resetStoryArgs: (story, argNames?: [string]) => {
const { id: storyId, refId } = story;
fullAPI.emit(RESET_STORY_ARGS, {
storyId,
argNames,
options: {
target: refId ? `storybook-ref-${refId}` : 'storybook-preview-iframe',
},
options: { target: refId },
});
},
fetchStoryList: async () => {
Expand Down Expand Up @@ -448,7 444,7 @@ export const init: ModuleFn<SubAPI, SubState, true> = ({
}

if (sourceType === 'local') {
const { storyId, storiesHash } = store.getState();
const { storyId, storiesHash, refId } = store.getState();

// create a list of related stories to be preloaded
const toBePreloaded = Array.from(
Expand All @@ -458,7 454,10 @@ export const init: ModuleFn<SubAPI, SubState, true> = ({
])
).filter(Boolean);

fullAPI.emit(PRELOAD_STORIES, toBePreloaded);
fullAPI.emit(PRELOAD_ENTRIES, {
ids: toBePreloaded,
options: { target: refId },
});
}
});

Expand Down
10 changes: 5 additions & 5 deletions code/lib/api/src/tests/stories.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -623,7 623,7 @@ describe('stories API', () => {
storyId: 'a--1',
updatedArgs: { foo: 'bar' },
options: {
target: 'storybook-preview-iframe',
target: undefined,
},
});

Expand Down Expand Up @@ -654,12 654,12 @@ describe('stories API', () => {
storyId: 'a--1',
updatedArgs: { foo: 'bar' },
options: {
target: 'storybook-ref-refId',
target: 'refId',
},
});
});

it('resetStoryArgs emits RESET_STORY_ARGS to the local frame and does not change anything', () => {
it('refId to the local frame and does not change anything', () => {
const navigate = jest.fn();
const emit = jest.fn();
const on = jest.fn();
Expand All @@ -681,7 681,7 @@ describe('stories API', () => {
storyId: 'a--1',
argNames: ['foo'],
options: {
target: 'storybook-preview-iframe',
target: undefined,
},
});

Expand Down Expand Up @@ -712,7 712,7 @@ describe('stories API', () => {
storyId: 'a--1',
argNames: ['foo'],
options: {
target: 'storybook-ref-refId',
target: 'refId',
},
});
});
Expand Down
4 changes: 2 additions & 2 deletions code/lib/core-events/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 19,7 @@ enum events {
// Force the current story to re-render from scratch, with its initial args
FORCE_REMOUNT = 'forceRemount',
// Request the story has been loaded into the store, ahead of time, before it's actually
PRELOAD_STORIES = 'preloadStories',
PRELOAD_ENTRIES = 'preloadStories',
// The story has been loaded into the store, we have parameters/args/etc
STORY_PREPARED = 'storyPrepared',
// The next 6 events are emitted by the StoryRenderer when rendering the current story
Expand Down Expand Up @@ -78,7 78,7 @@ export const {
STORY_PREPARED,
STORY_CHANGED,
STORY_UNCHANGED,
PRELOAD_STORIES,
PRELOAD_ENTRIES,
STORY_RENDERED,
STORY_MISSING,
STORY_ERRORED,
Expand Down
8 changes: 4 additions & 4 deletions code/lib/preview-web/src/PreviewWeb.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1104,7 1104,7 @@ describe('PreviewWeb', () => {
await waitForRender();

importFn.mockClear();
await preview.onPreloadStories(['component-two--c']);
await preview.onPreloadStories({ ids: ['component-two--c'] });
expect(importFn).toHaveBeenCalledWith('./src/ComponentTwo.stories.js');
});

Expand All @@ -1114,7 1114,7 @@ describe('PreviewWeb', () => {
await waitForRender();

importFn.mockClear();
await preview.onPreloadStories(['component-one--docs']);
await preview.onPreloadStories({ ids: ['component-one--docs'] });
expect(importFn).toHaveBeenCalledWith('./src/ComponentOne.stories.js');
});

Expand All @@ -1124,7 1124,7 @@ describe('PreviewWeb', () => {
await waitForRender();

importFn.mockClear();
await preview.onPreloadStories(['introduction--docs']);
await preview.onPreloadStories({ ids: ['introduction--docs'] });
expect(importFn).toHaveBeenCalledWith('./src/Introduction.mdx');
});
it('loads imports of modern docs entries', async () => {
Expand All @@ -1133,7 1133,7 @@ describe('PreviewWeb', () => {
await waitForRender();

importFn.mockClear();
await preview.onPreloadStories(['introduction--docs']);
await preview.onPreloadStories({ ids: ['introduction--docs'] });
expect(importFn).toHaveBeenCalledWith('./src/ComponentTwo.stories.js');
});
});
Expand Down
14 changes: 10 additions & 4 deletions code/lib/preview-web/src/PreviewWeb.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 4,7 @@ import global from 'global';
import {
CURRENT_STORY_WAS_SET,
IGNORED_EXCEPTION,
PRELOAD_STORIES,
PRELOAD_ENTRIES,
PREVIEW_KEYDOWN,
SET_CURRENT_STORY,
SET_STORIES,
Expand Down Expand Up @@ -93,7 93,7 @@ export class PreviewWeb<TFramework extends AnyFramework> extends Preview<TFramew

this.channel.on(SET_CURRENT_STORY, this.onSetCurrentStory.bind(this));
this.channel.on(UPDATE_QUERY_PARAMS, this.onUpdateQueryParams.bind(this));
this.channel.on(PRELOAD_STORIES, this.onPreloadStories.bind(this));
this.channel.on(PRELOAD_ENTRIES, this.onPreloadStories.bind(this));
}

initializeWithProjectAnnotations(projectAnnotations: WebProjectAnnotations<TFramework>) {
Expand Down Expand Up @@ -240,8 240,14 @@ export class PreviewWeb<TFramework extends AnyFramework> extends Preview<TFramew
super.onUpdateArgs({ storyId, updatedArgs });
}

async onPreloadStories(ids: string[]) {
await Promise.all(ids.map((id) => this.storyStore.loadEntry(id)));
async onPreloadStories({ ids }: { ids: string[] }) {
/**
* It's possible that we're trying to preload a story in a ref we haven't loaded the iframe for yet.
* Because of the way the targeting works, if we can't find the targeted iframe,
* we'll use the currently active iframe which can cause the event to be targeted
* to the wrong iframe, causing an error if the storyId does not exists there.
*/
await Promise.allSettled(ids.map((id) => this.storyStore.loadEntry(id)));
}

// RENDERING
Expand Down
4 changes: 1 addition & 3 deletions code/lib/ui/src/components/preview/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 164,7 @@ const Preview = React.memo<PreviewProps>((props) => {
api.emit(SET_CURRENT_STORY, {
storyId: id,
viewMode,
options: {
target: refId ? `storybook-ref-${refId}` : 'storybook-preview-iframe',
},
options: { target: refId },
});
}
}
Expand Down
31 changes: 31 additions & 0 deletions code/lib/ui/src/components/sidebar/SearchResults.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 4,8 @@ import global from 'global';
import React, { FC, MouseEventHandler, ReactNode, useCallback, useEffect } from 'react';
import { ControllerStateAndHelpers } from 'downshift';

import { useStorybookApi } from '@storybook/api';
import { PRELOAD_ENTRIES } from '@storybook/core-events';
import { ComponentNode, DocumentNode, Path, RootNode, StoryNode } from './TreeNode';
import {
Match,
Expand Down Expand Up @@ -121,6 123,17 @@ const Result: FC<
[onClick]
);

const api = useStorybookApi();
useEffect(() => {
if (api && props.isHighlighted && item.isComponent) {
api.emit(
PRELOAD_ENTRIES,
{ ids: [item.isLeaf ? item.id : item.children[0]] },
{ options: { target: item.refId } }
);
}
}, [props.isHighlighted, item]);

const nameMatch = matches.find((match: Match) => match.key === 'name');
const pathMatches = matches.filter((match: Match) => match.key === 'path');
const label = (
Expand Down Expand Up @@ -175,6 188,7 @@ export const SearchResults: FC<{
isLoading = false,
enableShortcuts = true,
}) => {
const api = useStorybookApi();
useEffect(() => {
const handleEscape = (event: KeyboardEvent) => {
if (!enableShortcuts || isLoading || event.repeat) return;
Expand All @@ -190,6 204,20 @@ export const SearchResults: FC<{
return () => document.removeEventListener('keydown', handleEscape);
}, [enableShortcuts, isLoading]);

const mouseOverHandler = useCallback((event: MouseEvent) => {
const currentTarget = event.currentTarget as HTMLElement;
const storyId = currentTarget.getAttribute('data-id');
const refId = currentTarget.getAttribute('data-refid');
const item = api.getData(storyId, refId === 'storybook_internal' ? undefined : refId);

if (item.isComponent) {
api.emit(PRELOAD_ENTRIES, {
ids: [item.isLeaf ? item.id : item.children[0]],
options: { target: refId },
});
}
}, []);

return (
<ResultsList {...getMenuProps()}>
{results.length > 0 && !query && (
Expand Down Expand Up @@ -255,6 283,9 @@ export const SearchResults: FC<{
{...result}
{...getItemProps({ key, index, item: result })}
isHighlighted={highlightedIndex === index}
data-id={result.item.id}
data-refid={result.item.refId}
onMouseOver={mouseOverHandler}
className="search-result-item"
/>
);
Expand Down
11 changes: 11 additions & 0 deletions code/lib/ui/src/components/sidebar/Tree.tsx
Original file line number Diff line number Diff line change
@@ -1,9 1,11 @@
import { useStorybookApi } from '@storybook/api';
import type { StoriesHash, GroupEntry, ComponentEntry, StoryEntry } from '@storybook/api';
import { styled } from '@storybook/theming';
import { Button, Icons } from '@storybook/components';
import { transparentize } from 'polished';
import React, { MutableRefObject, useCallback, useMemo, useRef } from 'react';

import { PRELOAD_ENTRIES } from '@storybook/core-events';
import {
ComponentNode,
DocumentNode,
Expand Down Expand Up @@ -147,6 149,7 @@ const Node = React.memo<NodeProps>(
setExpanded,
onSelectStoryId,
}) => {
const api = useStorybookApi();
if (!isDisplayed) return null;

const id = createId(item.id, refId);
Expand Down Expand Up @@ -246,6 249,14 @@ const Node = React.memo<NodeProps>(
setExpanded({ ids: [item.id], value: !isExpanded });
if (item.type === 'component' && !isExpanded) onSelectStoryId(item.id);
}}
onMouseEnter={() => {
if (item.isComponent) {
api.emit(PRELOAD_ENTRIES, {
ids: [item.children[0]],
options: { target: refId },
});
}
}}
>
{(item.renderLabel as (i: typeof item) => React.ReactNode)?.(item) || item.name}
</BranchNode>
Expand Down
14 changes: 14 additions & 0 deletions code/lib/ui/src/components/sidebar/useHighlighted.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 8,8 @@ import {
useRef,
useState,
} from 'react';
import { useStorybookApi } from '@storybook/api';
import { PRELOAD_ENTRIES } from '@storybook/core-events';
import { matchesKeyCode, matchesModifiers } from '../../keybinding';

import { CombinedDataset, Highlight, Selection } from './types';
Expand Down Expand Up @@ -40,6 42,7 @@ export const useHighlighted = ({
const initialHighlight = fromSelection(selected);
const highlightedRef = useRef<Highlight>(initialHighlight);
const [highlighted, setHighlighted] = useState<Highlight>(initialHighlight);
const api = useStorybookApi();

const updateHighlighted = useCallback(
(highlight) => {
Expand Down Expand Up @@ -109,6 112,17 @@ export const useHighlighted = ({
const nextIndex = cycle(highlightable, currentIndex, isArrowUp ? -1 : 1);
const didRunAround = isArrowUp ? nextIndex === highlightable.length - 1 : nextIndex === 0;
highlightElement(highlightable[nextIndex], didRunAround);

if (highlightable[nextIndex].getAttribute('data-nodetype') === 'component') {
const { itemId, refId } = highlightedRef.current;
const item = api.getData(itemId, refId === 'storybook_internal' ? undefined : refId);
if (item.isComponent) {
api.emit(PRELOAD_ENTRIES, {
ids: [item.isLeaf ? item.id : item.children[0]],
options: { target: refId },
});
}
}
});
};

Expand Down
2 changes: 1 addition & 1 deletion code/lib/ui/src/globals/exports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 120,7 @@ export default {
'IGNORED_EXCEPTION',
'NAVIGATE_URL',
'PLAY_FUNCTION_THREW_EXCEPTION',
'PRELOAD_STORIES',
'PRELOAD_ENTRIES',
'PREVIEW_KEYDOWN',
'REGISTER_SUBSCRIPTION',
'RESET_STORY_ARGS',
Expand Down

0 comments on commit 1aa9f79

Please sign in to comment.