-
-
Notifications
You must be signed in to change notification settings - Fork 9.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #20683 from storybookjs/tom/sb-1152-implement-cont…
…rols-block Docs: Implement Controls block
- Loading branch information
Showing
6 changed files
with
225 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import type { Meta, StoryObj } from '@storybook/react'; | ||
|
||
import { Controls } from './Controls'; | ||
import * as ExampleStories from '../examples/ControlsParameters.stories'; | ||
|
||
const meta: Meta<typeof Controls> = { | ||
component: Controls, | ||
parameters: { | ||
relativeCsfPaths: ['../examples/ControlsParameters.stories'], | ||
}, | ||
}; | ||
export default meta; | ||
|
||
type Story = StoryObj<typeof meta>; | ||
|
||
export const Default: Story = {}; | ||
|
||
export const OfStory: Story = { | ||
args: { | ||
of: ExampleStories.NoParameters, | ||
}, | ||
}; | ||
|
||
// NOTE: this will throw with no of prop | ||
export const OfStoryUnattached: Story = { | ||
parameters: { attached: false }, | ||
args: { | ||
of: ExampleStories.NoParameters, | ||
}, | ||
}; | ||
|
||
export const IncludeProp: Story = { | ||
args: { | ||
of: ExampleStories.NoParameters, | ||
include: ['a'], | ||
}, | ||
}; | ||
|
||
export const IncludeParameter: Story = { | ||
args: { | ||
of: ExampleStories.Include, | ||
}, | ||
}; | ||
|
||
export const ExcludeProp: Story = { | ||
args: { | ||
of: ExampleStories.NoParameters, | ||
exclude: ['a'], | ||
}, | ||
}; | ||
|
||
export const ExcludeParameter: Story = { | ||
args: { | ||
of: ExampleStories.Exclude, | ||
}, | ||
}; | ||
|
||
export const SortProp: Story = { | ||
args: { | ||
of: ExampleStories.NoParameters, | ||
sort: 'alpha', | ||
}, | ||
}; | ||
|
||
export const SortParameter: Story = { | ||
args: { | ||
of: ExampleStories.Sort, | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
/* eslint-disable react/destructuring-assignment */ | ||
import type { Args, Globals, Renderer } from '@storybook/csf'; | ||
import type { DocsContextProps, ModuleExports, PreparedStory } from '@storybook/types'; | ||
import type { FC } from 'react'; | ||
import React, { useCallback, useEffect, useState, useContext } from 'react'; | ||
import type { PropDescriptor } from '@storybook/preview-api'; | ||
import { filterArgTypes } from '@storybook/preview-api'; | ||
import { | ||
STORY_ARGS_UPDATED, | ||
UPDATE_STORY_ARGS, | ||
RESET_STORY_ARGS, | ||
GLOBALS_UPDATED, | ||
} from '@storybook/core-events'; | ||
|
||
import type { SortType } from '../components'; | ||
import { ArgsTable as PureArgsTable } from '../components'; | ||
import { DocsContext } from './DocsContext'; | ||
|
||
type ControlsParameters = { | ||
include?: PropDescriptor; | ||
exclude?: PropDescriptor; | ||
sort?: SortType; | ||
}; | ||
|
||
type ControlsProps = ControlsParameters & { | ||
of?: Renderer['component'] | ModuleExports; | ||
}; | ||
|
||
const useArgs = ( | ||
story: PreparedStory, | ||
context: DocsContextProps | ||
): [Args, (args: Args) => void, (argNames?: string[]) => void] => { | ||
const storyContext = context.getStoryContext(story); | ||
const { id: storyId } = story; | ||
|
||
const [args, setArgs] = useState(storyContext.args); | ||
useEffect(() => { | ||
const onArgsUpdated = (changed: { storyId: string; args: Args }) => { | ||
if (changed.storyId === storyId) { | ||
setArgs(changed.args); | ||
} | ||
}; | ||
context.channel.on(STORY_ARGS_UPDATED, onArgsUpdated); | ||
return () => context.channel.off(STORY_ARGS_UPDATED, onArgsUpdated); | ||
}, [storyId, context.channel]); | ||
const updateArgs = useCallback( | ||
(updatedArgs) => context.channel.emit(UPDATE_STORY_ARGS, { storyId, updatedArgs }), | ||
[storyId, context.channel] | ||
); | ||
const resetArgs = useCallback( | ||
(argNames?: string[]) => context.channel.emit(RESET_STORY_ARGS, { storyId, argNames }), | ||
[storyId, context.channel] | ||
); | ||
return [args, updateArgs, resetArgs]; | ||
}; | ||
|
||
const useGlobals = (story: PreparedStory, context: DocsContextProps): [Globals] => { | ||
const storyContext = context.getStoryContext(story); | ||
|
||
const [globals, setGlobals] = useState(storyContext.globals); | ||
useEffect(() => { | ||
const onGlobalsUpdated = (changed: { globals: Globals }) => { | ||
setGlobals(changed.globals); | ||
}; | ||
context.channel.on(GLOBALS_UPDATED, onGlobalsUpdated); | ||
return () => context.channel.off(GLOBALS_UPDATED, onGlobalsUpdated); | ||
}, [context.channel]); | ||
|
||
return [globals]; | ||
}; | ||
|
||
export const Controls: FC<ControlsProps> = (props) => { | ||
const { of } = props; | ||
const context = useContext(DocsContext); | ||
const { story } = context.resolveOf(of || 'story', ['story']); | ||
const { parameters, argTypes } = story; | ||
const controlsParameters = parameters.docs?.controls || ({} as ControlsParameters); | ||
|
||
const include = props.include ?? controlsParameters.include; | ||
const exclude = props.exclude ?? controlsParameters.exclude; | ||
const sort = props.sort ?? controlsParameters.sort; | ||
|
||
const [args, updateArgs, resetArgs] = useArgs(story, context); | ||
const [globals] = useGlobals(story, context); | ||
|
||
const filteredArgTypes = filterArgTypes(argTypes, include, exclude); | ||
|
||
return ( | ||
<PureArgsTable | ||
rows={filteredArgTypes} | ||
args={args} | ||
globals={globals} | ||
updateArgs={updateArgs} | ||
resetArgs={resetArgs} | ||
sort={sort} | ||
/> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
50 changes: 50 additions & 0 deletions
50
code/ui/blocks/src/examples/ControlsParameters.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import type { Meta, StoryObj } from '@storybook/react'; | ||
import { ControlsParameters } from './ControlsParameters'; | ||
|
||
/** | ||
* Reference stories to be used by the Controls stories | ||
*/ | ||
const meta = { | ||
title: 'Example/Stories for the Controls Block', | ||
component: ControlsParameters, | ||
args: { b: 'b' }, | ||
argTypes: { | ||
// @ts-expect-error Meta type is trying to force us to use real props as args | ||
extraMetaArgType: { | ||
type: { name: 'string' }, | ||
name: 'Extra Meta', | ||
description: 'An extra argtype added at the meta level', | ||
table: { defaultValue: { summary: "'a default value'" } }, | ||
}, | ||
}, | ||
} satisfies Meta<typeof ControlsParameters>; | ||
|
||
export default meta; | ||
type Story = StoryObj<typeof meta>; | ||
|
||
export const NoParameters: Story = { | ||
argTypes: { | ||
// @ts-expect-error Story type is trying to force us to use real props as args | ||
extraStoryArgType: { | ||
type: { name: 'string' }, | ||
name: 'Extra Story', | ||
description: 'An extra argtype added at the story level', | ||
table: { defaultValue: { summary: "'a default value'" } }, | ||
}, | ||
}, | ||
}; | ||
|
||
export const Include = { | ||
...NoParameters, | ||
parameters: { docs: { controls: { include: ['a'] } } }, | ||
}; | ||
|
||
export const Exclude = { | ||
...NoParameters, | ||
parameters: { docs: { controls: { exclude: ['a'] } } }, | ||
}; | ||
|
||
export const Sort = { | ||
...NoParameters, | ||
parameters: { docs: { controls: { sort: 'alpha' } } }, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import React from 'react'; | ||
|
||
type PropTypes = { a?: string; b: string }; | ||
|
||
export const ControlsParameters = ({ a = 'a', b }: PropTypes) => <div>Example story</div>; |