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

Core: Add args feature #10014

Merged
merged 43 commits into from
Mar 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
90adc9e
Add very basic story state
tmeasday Feb 11, 2020
8b08dc2
Add STORY_STATE_CHANGED event
tmeasday Feb 11, 2020
a784aa1
Listen to `CHANGE_STORY_STATE` and change it in the store
tmeasday Feb 11, 2020
208b446
Ensure that the state is passed to the story function
tmeasday Feb 11, 2020
d2a7e63
Ensure the story re-renders when the state changes
tmeasday Feb 11, 2020
f070609
Started on README for client_api
tmeasday Feb 13, 2020
7406da4
Add a README with a game plan around args
tmeasday Feb 14, 2020
cc41e48
Change story state => story args
tmeasday Feb 18, 2020
060f0d6
Initialize arg values based on parameters.argTypes
tmeasday Feb 18, 2020
be5ef39
Fix type signature for `.add()`
tmeasday Feb 18, 2020
b386f30
Add parameterEnhancers
tmeasday Feb 18, 2020
1f0de26
Implemented `parameters.passArgsFirst`
tmeasday Feb 18, 2020
4135503
Allow importing addParameters/Decorators from client-api after all
tmeasday Feb 18, 2020
95f713e
Add addParameterEnhancer export to client api
tmeasday Feb 18, 2020
833c13b
Type fixes
tmeasday Feb 18, 2020
c7f7f19
Fixes
tmeasday Feb 18, 2020
e54646c
Update state=>args.stories
tmeasday Feb 18, 2020
a0b5663
Fix up vue decorators
tmeasday Feb 18, 2020
13c7697
Add useArgs
tmeasday Feb 18, 2020
fc9f956
Merge remote-tracking branch 'origin/refactor-client-api' into featur…
tmeasday Feb 19, 2020
cd89d72
Merge remote-tracking branch 'origin/next' into feature/args
tmeasday Feb 27, 2020
d008c50
Added test for useStoryArgs
tmeasday Feb 27, 2020
ddb104b
Update README a little bit.
tmeasday Mar 2, 2020
6633c79
Change behaviour of parameter enhancers to not merge subkeys
tmeasday Mar 2, 2020
3f9223f
Added manager side support for args
tmeasday Mar 2, 2020
44e6f87
Change arg initial values to come from parameters.args
tmeasday Mar 2, 2020
2fd2dc9
Add `useArgs` API on manager side
tmeasday Mar 2, 2020
43cbb08
Args: Fix circular package dependency
tmeasday Mar 2, 2020
315101d
Update treeview mock data with args
tmeasday Mar 2, 2020
6c5b216
Merge remote-tracking branch 'origin/next' into feature/args
tmeasday Mar 2, 2020
cee92e2
Fix merge issue
tmeasday Mar 2, 2020
bb9a88a
Update snapshots
tmeasday Mar 2, 2020
a104c33
Fix issue with types
tmeasday Mar 2, 2020
c64b99d
Fix merge issues that stopped stories rendering
tmeasday Mar 2, 2020
ebc2069
Fix bug with stories.js
tmeasday Mar 2, 2020
c12cf36
Fix tests
tmeasday Mar 2, 2020
2d3e057
Fix story store test
tmeasday Mar 3, 2020
bf3ca9c
Update lib/client-api/README.md
tmeasday Mar 3, 2020
0f0d898
Merge remote-tracking branch 'origin/next' into feature/args
tmeasday Mar 3, 2020
fd77f06
Fix dependency on manager code in preview
tmeasday Mar 3, 2020
3aaba64
Fix tests that got lost in translation
tmeasday Mar 3, 2020
533b1fc
Change story update functions to be `updateX`
tmeasday Mar 4, 2020
16cbd5c
Change event name to use UPDATE too
tmeasday Mar 4, 2020
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: 1 addition & 0 deletions app/vue/src/client/preview/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ const defaultContext: StoryContext = {
name: 'unspecified',
kind: 'unspecified',
parameters: {},
args: {},
};

function decorateStory(
Expand Down
62 changes: 62 additions & 0 deletions examples/official-storybook/stories/core/args.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';

import { useArgs } from '@storybook/client-api';

// eslint-disable-next-line react/prop-types
const ArgUpdater = ({ args, updateArgs }) => {
const [argsInput, updateArgsInput] = useState(JSON.stringify(args));

return (
<div>
<h3>Hooks args:</h3>
<pre>{JSON.stringify(args)}</pre>
<form
onSubmit={e => {
e.preventDefault();
updateArgs(JSON.parse(argsInput));
}}
>
<textarea value={argsInput} onChange={e => updateArgsInput(e.target.value)} />
<br />
<button type="submit">Change</button>
</form>
</div>
);
};

export default {
title: 'Core/Args',
parameters: {
passArgsFirst: true,
},
decorators: [
story => {
const [args, updateArgs] = useArgs();

return (
<>
{story()}
<ArgUpdater args={args} updateArgs={updateArgs} />
</>
);
},
],
};

export const PassedToStory = inputArgs => {
return (
<div>
<h3>Input args:</h3>
<pre>{JSON.stringify(inputArgs)}</pre>
</div>
);
};

PassedToStory.story = {
parameters: { argTypes: { name: { defaultValue: 'initial' } } },
};

PassedToStory.propTypes = {
args: PropTypes.shape({}).isRequired,
};
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ exports[`Storyshots Custom/Decorator for Vue With Data 1`] = `
"framework": "vue",
"__id": "custom-decorator-for-vue--with-data"
},
"args": {},
"customContext": 52
}
</pre>
Expand Down
22 changes: 20 additions & 2 deletions lib/addons/src/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import window from 'global';
import { logger } from '@storybook/client-logger';
import { FORCE_RE_RENDER, STORY_RENDERED, DOCS_RENDERED } from '@storybook/core-events';
import {
FORCE_RE_RENDER,
STORY_RENDERED,
DOCS_RENDERED,
UPDATE_STORY_ARGS,
} from '@storybook/core-events';
import { addons } from './index';
import { StoryGetter, StoryContext } from './types';
import { StoryGetter, StoryContext, Args } from './types';

interface StoryStore {
fromId: (
Expand Down Expand Up @@ -409,3 +414,16 @@ export function useParameter<S>(parameterKey: string, defaultValue?: S): S | und
}
return undefined;
}

/* Returns current value of story args */
export function useArgs(): [Args, (newArgs: Args) => void] {
const channel = addons.getChannel();
const { id: storyId, args } = useStoryContext();

const updateArgs = useCallback(
(newArgs: Args) => channel.emit(UPDATE_STORY_ARGS, storyId, newArgs),
[channel, storyId]
);

return [args, updateArgs];
}
12 changes: 11 additions & 1 deletion lib/addons/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ export interface Parameters {
[key: string]: any;
}

// This is duplicated in @storybook/api because there is no common place to put types (manager/preview)
// We cannot import from @storybook/api here because it will lead to manager code (i.e. emotion) imported in the preview
export interface Args {
[key: string]: any;
}

export interface StoryIdentifier {
id: StoryId;
kind: StoryKind;
Expand All @@ -36,6 +42,7 @@ export interface StoryIdentifier {
export interface StoryContext extends StoryIdentifier {
[key: string]: any;
parameters: Parameters;
args: Args;
hooks?: HooksContext;
}

Expand Down Expand Up @@ -68,7 +75,10 @@ export interface OptionsParameter extends Object {
}

export type StoryGetter = (context: StoryContext) => any;
export type StoryFn<ReturnType = unknown> = (p?: StoryContext) => ReturnType;

export type LegacyStoryFn<ReturnType = unknown> = (p?: StoryContext) => ReturnType;
export type ArgsStoryFn<ReturnType = unknown> = (a?: Args, p?: StoryContext) => ReturnType;
export type StoryFn<ReturnType = unknown> = LegacyStoryFn<ReturnType> | ArgsStoryFn<ReturnType>;

export type StoryWrapper = (
getStory: StoryGetter,
Expand Down
14 changes: 14 additions & 0 deletions lib/api/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ interface Children {
children: ReactNode | ((props: Combo) => ReactNode);
}

export interface Args {
[key: string]: any;
}

type StatePartial = Partial<State>;

export type ManagerProviderProps = Children & RouterData & ProviderData & DocsModeData;
Expand Down Expand Up @@ -423,3 +427,13 @@ export function useStoryState<S>(defaultState?: S) {
const { storyId } = useStorybookState();
return useSharedState<S>(`story-state-${storyId}`, defaultState);
}

export function useArgs(): [Args, (newArgs: Args) => void] {
const {
api: { getCurrentStoryData, updateStoryArgs },
} = useStorybookApi();

const { id, args } = getCurrentStoryData();

return [args, (newArgs: Args) => updateStoryArgs(id, newArgs)];
}
3 changes: 3 additions & 0 deletions lib/api/src/lib/stories.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import deprecate from 'util-deprecate';
import dedent from 'ts-dedent';
import { sanitize, parseKind } from '@storybook/csf';

import { Args } from '../index';
import merge from './merge';
import { Provider } from '../init-provider-api';

Expand Down Expand Up @@ -58,6 +60,7 @@ export interface Story {
docsOnly?: boolean;
[k: string]: any;
};
args: Args;
}

export interface StoryInput {
Expand Down
25 changes: 24 additions & 1 deletion lib/api/src/modules/stories.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { DOCS_MODE } from 'global';
import { toId, sanitize } from '@storybook/csf';
import { UPDATE_STORY_ARGS, STORY_ARGS_UPDATED } from '@storybook/core-events';

import {
transformStoriesRawToStoriesHash,
Expand All @@ -11,7 +12,7 @@ import {
isStory,
} from '../lib/stories';

import { Module } from '../index';
import { Module, API, Args } from '../index';

type Direction = -1 | 1;
type ParameterName = string;
Expand Down Expand Up @@ -46,6 +47,8 @@ const initStoriesApi = ({
storyId: initialStoryId,
viewMode: initialViewMode,
}: Module) => {
let fullApi: API;

const getData = (storyId: StoryId) => {
const { storiesHash } = store.getState();

Expand Down Expand Up @@ -235,6 +238,24 @@ const initStoriesApi = ({
}
};

const storyArgsChanged = (id: StoryId, args: Args) => {
const { storiesHash } = store.getState();
(storiesHash[id] as Story).args = args;
store.setState({ storiesHash });
};

const updateStoryArgs = (id: StoryId, newArgs: Args) => {
if (!fullApi) throw new Error('Cannot set story args until api has been initialized');

fullApi.emit(UPDATE_STORY_ARGS, id, newArgs);
};

function init({ api: inputFullApi }: { api: API }) {
fullApi = inputFullApi;

fullApi.on(STORY_ARGS_UPDATED, (id: StoryId, args: Args) => storyArgsChanged(id, args));
}

return {
api: {
storyId: toId,
Expand All @@ -246,13 +267,15 @@ const initStoriesApi = ({
getData,
getParameters,
getCurrentParameter,
updateStoryArgs,
},
state: {
storiesHash: {},
storyId: initialStoryId,
viewMode: initialViewMode,
storiesConfigured: false,
},
init,
};
};
export default initStoriesApi;
Loading