Skip to content

Commit

Permalink
feat: optimize BlockContext
Browse files Browse the repository at this point in the history
  • Loading branch information
atanasster committed Apr 12, 2020
1 parent 23a5284 commit 95d071b
Show file tree
Hide file tree
Showing 13 changed files with 274 additions and 181 deletions.
45 changes: 35 additions & 10 deletions core/specification/src/stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,21 +219,46 @@ export interface StoriesKind {
[name: string]: any;
}

/**
* list of components used in stories
*/

export interface StoryComponents {
[fileName: string]: StoryComponent;
}

/**
* list of story files, or groups
*/
export interface StoryKinds {
[title: string]: StoriesKind;
}

/**
* list of stories
*/
export interface StoryStories {
[id: string]: Story;
}

/**
* store of stories information in memory after the loader is applied
*/
export interface StoriesStore {
/**
* unique has for a store
* unique hash for a store
*/
hash?: string;
kinds: {
[title: string]: StoriesKind;
};
stories: {
[id: string]: Story;
};
components: {
[fileName: string]: StoryComponent;
};
/**
* list of story files, or groups
*/
kinds: StoryKinds;
/**
* list of stories
*/
stories: StoryStories;
/**
* list of components used in stories
*/
components: StoryComponents;
}
25 changes: 17 additions & 8 deletions core/store/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,20 @@ interface MessageType {
storyId: string;
moduleId: number;
}
class Store implements StoryStore {
export class Store implements StoryStore {
loadedStore: StoriesStore | undefined;
updateLocalStorage: boolean = true;
channel: BroadcastChannel;
observers: StoreObserver[];
moduleId: number;
constructor(store?: StoriesStore) {
constructor(options?: {
store?: StoriesStore;
updateLocalStorage?: boolean;
}) {
const { store, updateLocalStorage = true } = options || {};
this.moduleId = Math.random();
this.loadedStore = store;
this.updateLocalStorage = updateLocalStorage;
this.channel = new BroadcastChannel<MessageType>(UPDATE_STORY_MSG);
this.observers = [];
this.channel.onmessage = ({ storyId, moduleId }: MessageType) => {
Expand Down Expand Up @@ -97,12 +103,15 @@ class Store implements StoryStore {
...this.loadedStore.stories[storyId],
[propName]: newVal,
};
localStorage.setItem(
COMPONENT_CONTROLS_STORAGE,
JSON.stringify(this.loadedStore),
);
const message: MessageType = { storyId, moduleId: this.moduleId };
this.channel.postMessage(message);
if (this.updateLocalStorage) {
localStorage.setItem(
COMPONENT_CONTROLS_STORAGE,
JSON.stringify(this.loadedStore),
);
const message: MessageType = { storyId, moduleId: this.moduleId };
this.channel.postMessage(message);
}
this.notifyObservers(storyId);
}
return this.loadedStore;
};
Expand Down
1 change: 1 addition & 0 deletions ui/blocks/src/BlockContainer/story/StoryBlockContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export const StoryBlockContainer: FC<StoryBlockContainerProps> = ({
title: userTitle,
});
const block = children(context, rest);

return (
<BlockContainer
title={title}
Expand Down
9 changes: 4 additions & 5 deletions ui/blocks/src/ControlsTable/ControlsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
StoryBlockContainerProps,
} from '../BlockContainer/story';

import { useBlockContext, BlockContextProps } from '../context';
import { BlockControlsContext } from '../context';
import { SingleControlsTable } from './SingleControlsTable';

export type ControlsTableProps = Omit<StoryBlockContainerProps, 'children'>;
Expand Down Expand Up @@ -60,10 +60,9 @@ export const ControlsTable: FC<ControlsTableProps> = (
props: ControlsTableProps,
) => {
const [copied, setCopied] = React.useState(false);
const {
setControlValue,
clickControl,
} = useBlockContext() as BlockContextProps;
const { setControlValue, clickControl } = React.useContext(
BlockControlsContext,
);
return (
<StoryBlockContainer {...props}>
{(context, rest) => {
Expand Down
9 changes: 3 additions & 6 deletions ui/blocks/src/PropsTable/PropsTable.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
/* eslint-disable react/display-name */
/** @jsx jsx */
import { jsx, Text, Flex, Styled } from 'theme-ui';
import { FC, useMemo } from 'react';
import { FC, useMemo, useContext } from 'react';
import { getPropertyEditor, PropertyEditor } from '@component-controls/editors';
import { Table, TableProps, Markdown } from '@component-controls/components';
import { Column } from 'react-table';
import {
ComponentsBlockContainer,
ComponentsBlockContainerProps,
} from '../BlockContainer/components/ComponentsBlockContainer';
import { useBlockContext, BlockContextProps } from '../context';
import { BlockControlsContext } from '../context';
import { InvalidType } from '../notifications';

export interface PropsTableOwnProps {
Expand All @@ -30,10 +30,7 @@ export const PropsTable: FC<PropsTableProps> = ({
extraColumns = [],
...props
}) => {
const {
setControlValue,
clickControl,
} = useBlockContext() as BlockContextProps;
const { setControlValue, clickControl } = useContext(BlockControlsContext);

return (
<ComponentsBlockContainer {...props}>
Expand Down
150 changes: 12 additions & 138 deletions ui/blocks/src/context/block/BlockContext.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,8 @@
import React, { useEffect, useState } from 'react';
import { toId, storyNameFromExport } from '@storybook/csf';
import { store as storyStore } from '@component-controls/store';
import {
Story,
StoryComponent,
StoriesKind,
StoriesStore,
SetControlValueFn,
ClickControlFn,
ComponentControlButton,
getComponentName,
} from '@component-controls/specification';
import { mergeControlValues } from '@component-controls/core';
import { store as storyStore, StoryStore } from '@component-controls/store';
import { Story } from '@component-controls/specification';
import { BlockDataContextProvider } from './BlockDataContext';
import { BlockControlsContextProvider } from './BlockControlsContext';

export interface BlockContextInputProps {
/**
Expand All @@ -21,52 +12,14 @@ export interface BlockContextInputProps {
/**
* store mockup when running tests
*/
mockStore?: StoriesStore;
}

export interface Components {
[label: string]: StoryComponent;
mockStore?: StoryStore;
}

export interface BlockContextProps {
/**
* current story
*/
story?: Story;
/**
* generic function to update the values of component controls.
*/
setControlValue?: SetControlValueFn;

/**
* generic function to propagate a click event for component controls.
*/
clickControl?: ClickControlFn;
/**
* returns a story, given a story id
*/
getStory: (storyId?: string) => Story | undefined;

/**
* returns a story and its associated objects (kind, component), given a story id
*/
getStoryData: (
storyId?: string,
) => { story?: Story; kind?: StoriesKind; component?: StoryComponent };

/**
* given an object of components, resolves to name => StoryComponent
*/
getComponents: (
components: { [key: string]: any },
kind: StoriesKind,
) => Components;
/**
*
* find a story id from a story 'name'
* will navigate through the store kinds and look for a matching story id
*/
storyIdFromName: (name: string) => string | undefined;
}
//@ts-ignore
export const BlockContext = React.createContext<BlockContextProps>({});
Expand All @@ -76,7 +29,8 @@ export const BlockContextProvider: React.FC<BlockContextInputProps> = ({
storyId,
mockStore,
}) => {
const store = mockStore || storyStore.getStore();
const storeProvider = mockStore || storyStore;
const store = storeProvider.getStore();
const [story, setStory] = useState<{ story?: Story; id: string }>({
story: store ? store.stories[storyId] : undefined,
id: storyId,
Expand Down Expand Up @@ -106,97 +60,17 @@ export const BlockContextProvider: React.FC<BlockContextInputProps> = ({
}
}, [storyId, store]);

/**
* returns a story and its file, given a story id
*/
const getStoryData = (id?: string) => {
const story: Story | undefined =
store && store.stories && store.stories[id || storyId];

const kind =
store && story && story.kind ? store.kinds[story.kind] : undefined;
const storyComponent: any =
story && kind ? story.component || kind.component : undefined;
const componentName = getComponentName(storyComponent);

const component =
store && componentName && kind && kind.components[componentName]
? store.components[kind.components[componentName]]
: undefined;
return { story, kind, component };
};
const getStory = (id?: string) =>
store && store.stories && store.stories[id || storyId];

const getComponents = (
components: { [key: string]: any },
kind: StoriesKind,
) =>
store && kind && components
? Object.keys(components).reduce((acc, key) => {
const name = getComponentName(components[key]);
const component =
name &&
kind?.components[name] &&
store?.components[kind.components[name]];
if (component) {
return { ...acc, [key]: component };
} else {
return acc;
}
}, {})
: {};

const storyIdFromName = (name: string): string | undefined => {
if (store) {
for (const title in store.kinds) {
const kind = store.kinds[title];
const storyId = toId(title, storyNameFromExport(name));
if (kind.stories && kind.stories.indexOf(storyId) > -1) {
return storyId;
}
}
}
return undefined;
};
const setControlValue: SetControlValueFn = (
storyId: string,
propName: string | undefined,
propValue: any,
) => {
const controls =
store && store.stories[storyId] && store.stories[storyId].controls;
if (store && controls) {
const newValues = mergeControlValues(controls, propName, propValue);
storyStore.updateStoryProp(storyId, 'controls', newValues);
refreshData();
}
};
const clickControl: ClickControlFn = (storyId: string, propName: string) => {
const controls =
store && store.stories[storyId] && store.stories[storyId].controls;
if (controls && controls[propName]) {
const control: ComponentControlButton = controls[
propName
] as ComponentControlButton;
if (control && typeof control.onClick === 'function') {
control.onClick(control);
}
}
};
return (
<BlockContext.Provider
value={{
story: story.story,
setControlValue,
clickControl,
getStory,
getStoryData,
storyIdFromName,
getComponents,
}}
>
{children}
<BlockDataContextProvider store={store}>
<BlockControlsContextProvider store={storeProvider}>
{children}
</BlockControlsContextProvider>
</BlockDataContextProvider>
</BlockContext.Provider>
);
};
Expand Down
Loading

0 comments on commit 95d071b

Please sign in to comment.