Skip to content

Commit

Permalink
feat: support for decaorators
Browse files Browse the repository at this point in the history
  • Loading branch information
atanasster committed Apr 25, 2020
1 parent d5b3cc6 commit 686564f
Show file tree
Hide file tree
Showing 16 changed files with 114 additions and 34 deletions.
1 change: 1 addition & 0 deletions core/specification/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
},
"license": "MIT",
"dependencies": {
"deepmerge": "^4.2.2",
"typescript": "^3.8.3"
},
"devDependencies": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ const concatMerge = (dest: any[], src: any[]) => [...dest, ...src];

export const deepMerge = (a: any, b: any) =>
merge(a, b, { arrayMerge: concatMerge });

export { merge };
1 change: 1 addition & 0 deletions core/specification/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './components';
export * from './controls';
export * from './deepMerge';
export * from './propsInfo';
export * from './stories';
export * from './utility';
Expand Down
10 changes: 10 additions & 0 deletions core/specification/src/stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@ export interface Story {
* object of key/value pairs specifying the controls for the story
*/
controls?: ComponentControls;

/**
* story decorators (or wrappers)
*/
decorators?: StoryRenderFn[];
}

/**
Expand Down Expand Up @@ -178,6 +183,11 @@ export interface StoriesKind {
*/
subcomponents?: string[] | object[];

/**
* story decorators (or wrappers)
*/
decorators?: StoryRenderFn[];

/**
* file name of the file of stories
*/
Expand Down
1 change: 0 additions & 1 deletion core/store/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
"@component-controls/specification": "^0.7.1",
"@storybook/csf": "^0.0.1",
"broadcast-channel": "^3.1.0",
"deepmerge": "^4.2.2",
"typescript": "^3.8.3"
},
"devDependencies": {
Expand Down
8 changes: 5 additions & 3 deletions core/store/src/serialization/load-store.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable @typescript-eslint/camelcase */
import { StoriesStore, Story } from '@component-controls/specification';
import {
StoriesStore,
Story,
deepMerge,
} from '@component-controls/specification';
import { toId, storyNameFromExport } from '@storybook/csf';
import store from '@component-controls/loader/story-store-data';
import { addSmartControls } from './smart-controls';
import { deepMerge } from './deepMerge';

let storyStore: StoriesStore | undefined = undefined;

Expand Down
7 changes: 6 additions & 1 deletion examples/storybook-6-no-docs/.storybook/preview.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { addParameters } from '@storybook/react';
import React from 'react';
import { addParameters, addDecorator } from '@storybook/react';
import { ThemeProvider } from '@component-controls/storybook';

addDecorator(story => (
<ThemeProvider><div id='my-story-wrapper'>{story()}</div></ThemeProvider>
));
const categories = ['Storybook', 'Blocks', 'Editors', 'Components']
addParameters({
dependencies: { hideEmpty: true },
Expand Down
7 changes: 6 additions & 1 deletion examples/storybook-6/.storybook/preview.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { addParameters } from '@storybook/react';
import React from 'react';
import { addParameters, addDecorator } from '@storybook/react';
import { ThemeProvider } from '@component-controls/storybook';

addDecorator(story => (
<ThemeProvider>{story()}</ThemeProvider>
));
const categories = ['Storybook', 'Blocks', 'Editors', 'Components']
addParameters({
dependencies: { hideEmpty: true },
Expand Down
7 changes: 6 additions & 1 deletion examples/storybook-custom-docs-pages/.storybook/preview.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { addParameters } from '@storybook/react';
import React from 'react';
import { addParameters, addDecorator } from '@storybook/react';
import { ThemeProvider } from '@component-controls/storybook';

addDecorator(story => (
<ThemeProvider>{story()}</ThemeProvider>
));
const categories = ['Storybook', 'Blocks', 'Editors', 'Components']
addParameters({
dependencies: { hideEmpty: true },
Expand Down
5 changes: 1 addition & 4 deletions integrations/storybook/src/config.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
/* eslint-disable react/display-name */
import React from 'react';
import { addDecorator } from '@storybook/client-api';
import addons, { makeDecorator } from '@storybook/addons';
import { FORCE_RE_RENDER } from '@storybook/core-events';

import { store } from '@component-controls/store';
import { getControlValues } from '@component-controls/core';
import { ThemeProvider } from './context/ThemeProvider';

store.addObserver(() => {
addons.getChannel().emit(FORCE_RE_RENDER);
Expand All @@ -21,8 +19,7 @@ addDecorator(
const values =
story && story.controls ? getControlValues(story.controls) : undefined;
//@ts-ignore
const render = values ? storyFn(values, context) : storyFn(context);
return <ThemeProvider>{render}</ThemeProvider>;
return values ? storyFn(values, context) : storyFn(context);
},
}),
);
8 changes: 6 additions & 2 deletions integrations/storybook/src/docs-page/DocsContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@ import {
PageContainer as BlockPageContainer,
PageContainerProps,
} from '@component-controls/blocks';
import { useStoryId } from '@component-controls/storybook-custom-docs';
import {
useStoryId,
getGlobalOptions,
} from '@component-controls/storybook-custom-docs';
import { useIsDark } from '../context/useIsDark';

export const PageContextContainer: FC<PageContainerProps> = ({ children }) => {
const options = React.useMemo(() => getGlobalOptions(), []);
const storyId = useStoryId();
const isDark = useIsDark();
return (
<BlockPageContainer dark={isDark} storyId={storyId}>
<BlockPageContainer dark={isDark} storyId={storyId} options={options}>
{children}
</BlockPageContainer>
);
Expand Down
22 changes: 20 additions & 2 deletions misc/storybook-custom-docs/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,34 @@ export const getContext = () => {
};
};

/**
* function returning the global options
* parameters and decorators
*/
export const getGlobalOptions = (): any => {
const store =
window &&
//@ts-ignore
window.__STORYBOOK_CLIENT_API__ &&
//@ts-ignore
window.__STORYBOOK_CLIENT_API__.store();
//@ts-ignore
return store._globalMetadata;
};

/**
* function returning the current story id
*/
export const getCurrentStoryId = (): string | undefined => {
const selection =
const store =
window &&
//@ts-ignore
window.__STORYBOOK_CLIENT_API__ &&
//@ts-ignore
window.__STORYBOOK_CLIENT_API__.store().getSelection();
window.__STORYBOOK_CLIENT_API__.store();

const selection = store.getSelection();
//@ts-ignore
return selection ? selection.storyId : undefined;
};

Expand Down
14 changes: 13 additions & 1 deletion ui/blocks/src/PageContainer/PageContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ export interface PageContainerProps {
* dark/light theme for the page
*/
dark?: boolean;

/**
* global options passed from container
* those are global parameters as well as decorators
*/
options?: any;

/**
* components to customize the markdown display.
*/
Expand Down Expand Up @@ -47,6 +54,7 @@ export const PageContainer: FC<PageContainerProps> = ({
storyId,
mockStore,
theme,
options,
components = {},
}) => {
return storyId ? (
Expand All @@ -62,7 +70,11 @@ export const PageContainer: FC<PageContainerProps> = ({
}}
>
<Box sx={{ maxWidth: '1000px', width: '100%' }}>
<BlockContextProvider storyId={storyId} mockStore={mockStore}>
<BlockContextProvider
storyId={storyId}
mockStore={mockStore}
options={options}
>
<StoryContextConsumer id={storyId}>
{({ kind }) => {
const { MDXPage } = kind || {};
Expand Down
33 changes: 16 additions & 17 deletions ui/blocks/src/Story/Story.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/** @jsx jsx */
import { createElement, FC } from 'react';
import { FC, createElement } from 'react';
import { jsx, Box } from 'theme-ui';
import { deepMerge, StoryRenderFn } from '@component-controls/specification';
import { getControlValues } from '@component-controls/core';
import {
StoryBlockContainer,
Expand All @@ -9,29 +10,27 @@ import {

export type StoryProps = StoryBlockContainerProps;

interface RenderStoryProps {
renderFn: (controlValues: { [key: string]: any }, context: any) => any;
values: { [key: string]: any };
context: any;
}

const RenderStory: FC<RenderStoryProps> = ({ renderFn, values, context }) =>
createElement('div', null, renderFn(values, { context }));

export const Story: FC<StoryProps> = (props: StoryProps) => (
<StoryBlockContainer {...props}>
{(context, rest) => {
const { story } = context;
if (story?.renderFn) {
const { story, options = {} } = context;
if (story && story.renderFn) {
try {
const values = story.controls ? getControlValues(story.controls) : {};
const { decorators_XXX: globalDecorators = [] } = options;
const { decorators: storyDecorators = [] } = story;
const decorators = deepMerge(globalDecorators, storyDecorators);
const renderFn = decorators.reduce(
(acc: StoryRenderFn, item: StoryRenderFn) => () =>
item(acc, { context }),
//@ts-ignore
() => story.renderFn(values, { context }),
);
return (
<Box id={story.id} sx={{ px: 3 }} {...rest}>
<RenderStory
renderFn={story.renderFn}
values={values}
context={context}
/>
<div style={{ all: 'unset' }}>
{createElement('div', null, renderFn())}
</div>
</Box>
);
} catch (e) {
Expand Down
12 changes: 12 additions & 0 deletions ui/blocks/src/context/block/BlockContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ export interface BlockContextInputProps {
* store mockup when running tests
*/
mockStore?: StoryStore;
/**
* global options passed from container
* those are global parameters as well as decorators
*/
options?: any;
}

export interface BlockContextProps {
Expand All @@ -23,6 +28,11 @@ export interface BlockContextProps {
* store interface
*/
storeProvider: StoryStore;
/**
* global options passed from container
* those are global parameters as well as decorators
*/
options?: any;
}
//@ts-ignore
export const BlockContext = React.createContext<BlockContextProps>({});
Expand All @@ -31,13 +41,15 @@ export const BlockContextProvider: React.FC<BlockContextInputProps> = ({
children,
storyId,
mockStore,
options,
}) => {
const storeProvider = mockStore || storyStore;
return (
<BlockContext.Provider
value={{
storyId,
storeProvider,
options,
}}
>
<BlockDataContextProvider store={storeProvider} storyId={storyId}>
Expand Down
10 changes: 9 additions & 1 deletion ui/blocks/src/context/story/StoryContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ export interface StoryContextProps {
* current story's/document's component
*/
component?: StoryComponent;
/**
* global options passed from container
* those are global parameters as well as decorators
*/
options?: any;
}

/**
Expand All @@ -45,7 +50,9 @@ export const useStoryContext = ({
id = CURRENT_STORY,
name,
}: StoryInputProps): StoryContextProps => {
const { storyId: currentId, storeProvider } = React.useContext(BlockContext);
const { storyId: currentId, storeProvider, options } = React.useContext(
BlockContext,
);
const { getStoryData, storyIdFromName } = React.useContext(BlockDataContext);
const storyId = name
? storyIdFromName(name)
Expand Down Expand Up @@ -83,6 +90,7 @@ export const useStoryContext = ({
story: data.story,
kind: data.kind,
component: data.component,
options,
};
};

Expand Down

0 comments on commit 686564f

Please sign in to comment.