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

Tech/api package #5402

Merged
merged 92 commits into from
Mar 19, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
92 commits
Select commit Hold shift + click to select a range
accb7ff
REFACTOR context into it's own package && REFACTOR into typescript
ndelangen Jan 29, 2019
5380276
FIX tests & linting
ndelangen Jan 29, 2019
c60b06d
CLEANUP
ndelangen Jan 29, 2019
0c3791d
SYNC prettier setting for line-length && FIX linting
ndelangen Jan 29, 2019
b1c791d
Merge branch 'next' into tech/api-package
ndelangen Jan 29, 2019
0a0e4f2
CHANGE the usages of <Consumer> && FIX static-build issue with initia…
ndelangen Jan 29, 2019
1b716bb
IMPROVE treeview search && FIX an issue for build storybook search no…
ndelangen Jan 29, 2019
ed44fd0
FIX tests && FIX snapshots && IMPROVE official-example config.js to h…
ndelangen Jan 29, 2019
4fb6264
FIX parameters story && FIX HMR in story format
ndelangen Jan 29, 2019
f893431
Merge branch 'next' into tech/api-package
ndelangen Mar 6, 2019
7a1fdc7
Merge branch 'next' into tech/api-package
ndelangen Mar 6, 2019
710025d
UPDATE version.ts
ndelangen Mar 6, 2019
61f25fd
Merge branch 'next' into tech/api-package
ndelangen Mar 7, 2019
f230c41
FIX shortcuts story
ndelangen Mar 7, 2019
d287d1f
FIX an error of storyDataFromString not coping with empty path
ndelangen Mar 7, 2019
c924ae0
FIX issue with SideBar staying in loading state
ndelangen Mar 7, 2019
5e24692
FIX snapshots
ndelangen Mar 7, 2019
f20eecf
FIX some tests
ndelangen Mar 7, 2019
7412d7b
Merge branch 'next' into tech/api-package
ndelangen Mar 7, 2019
2b2e794
FIX preact acceptance example
ndelangen Mar 8, 2019
3f6d16d
FIX version
ndelangen Mar 8, 2019
d22ac67
Merge branch 'next' into tech/api-package
ndelangen Mar 8, 2019
fd96884
FIX after merge
ndelangen Mar 8, 2019
36e4cfa
FIX tests && FIX bootstrap errors
ndelangen Mar 8, 2019
6d2a260
Merge branch 'next' into tech/api-package
ndelangen Mar 8, 2019
2a3d7b0
IMPROVE type coverage of api
ndelangen Mar 9, 2019
b4c2fbc
CLEANUP && FIX linting
ndelangen Mar 9, 2019
8eec272
Merge branch 'next' into tech/api-package
ndelangen Mar 9, 2019
9c9dffe
ADD a hook for getting the storybook global state
ndelangen Mar 9, 2019
de5e55a
Merge branch 'next' into tech/api-package
ndelangen Mar 9, 2019
b63280d
FIX an issue persisted state is empty
ndelangen Mar 9, 2019
bbb56b2
Merge branch 'next' into tech/api-package
ndelangen Mar 11, 2019
ed60fbe
REVERT changes to config in official example to support HMR
ndelangen Mar 11, 2019
ab2b73f
FIX snaphots
ndelangen Mar 11, 2019
781bd3f
Update preact-kitchen-sink
ndelangen Mar 11, 2019
4b194b1
REMOVE the module from example format stories
ndelangen Mar 11, 2019
39c4b0f
CLEANUP
ndelangen Mar 11, 2019
efddb56
CLEANUP
ndelangen Mar 11, 2019
cc668e5
Merge branch 'next' into tech/api-package
ndelangen Mar 11, 2019
0216f71
CLEANUP
ndelangen Mar 11, 2019
c04fbe5
CLEANUP
ndelangen Mar 11, 2019
9a1c01a
FIX the out of memory issue
ndelangen Mar 11, 2019
80c0842
Merge branch 'next' into tech/api-package
ndelangen Mar 11, 2019
eac8313
FIX version
ndelangen Mar 11, 2019
f0383ce
Merge branch 'next' into tech/api-package
ndelangen Mar 11, 2019
c84b49e
IMPROVE typing of lib/addon
ndelangen Mar 12, 2019
3a92016
CLEANUP
ndelangen Mar 12, 2019
5690954
connect addons/a11y to the API type
ndelangen Mar 12, 2019
7928db0
connect addons/cssresurces to the API type
ndelangen Mar 12, 2019
415663e
connect addons/actions to Theme & API types
ndelangen Mar 12, 2019
3a0ae06
connect addons/background to API type
ndelangen Mar 12, 2019
a6c11a9
WIP
ndelangen Mar 12, 2019
930e9e2
CLEANUP
ndelangen Mar 13, 2019
0994524
Merge branch 'next' into tech/api-package
ndelangen Mar 13, 2019
cb73f0f
FIX provider
ndelangen Mar 13, 2019
51a9108
FIX broken provider
ndelangen Mar 13, 2019
d19c4c7
FIX an issue with background tool && MIGRATE it to use the new Consum…
ndelangen Mar 13, 2019
39b93fa
CLEANUP types for addon-background
ndelangen Mar 13, 2019
a7cd8c3
DISABLE teamcity running of unit-tests
ndelangen Mar 14, 2019
28ae982
Merge branch 'next' into tech/api-package
ndelangen Mar 14, 2019
7fb28ec
use the filter in all consumers where applicable
ndelangen Mar 14, 2019
7e12272
FIX merge conflict
ndelangen Mar 14, 2019
61c89d6
Revert "DISABLE teamcity running of unit-tests"
ndelangen Mar 14, 2019
9f1ae25
FIX teamcity speed?
ndelangen Mar 14, 2019
dba15dc
Merge branch 'next' into tech/api-package
ndelangen Mar 14, 2019
843ee3c
CLEANUP addon-notes && use Consumer & filter
ndelangen Mar 14, 2019
223ea4d
ADD the version module and url module to the api type
ndelangen Mar 14, 2019
1a3607c
CHANGE addon-events to use API instead of channel
ndelangen Mar 14, 2019
3bdaa9e
FIX missing Version export type
ndelangen Mar 14, 2019
2c5bf08
CLEANUP addon-notes
ndelangen Mar 14, 2019
d689cdc
Merge branch 'next' into tech/api-package
ndelangen Mar 15, 2019
11e8a43
Merge branch 'next' into tech/api-package
ndelangen Mar 15, 2019
5592373
FIX background transparent on Preview causing the addons to be visisb…
ndelangen Mar 15, 2019
0b6528b
Update tsconfig.json
ndelangen Mar 15, 2019
b3e111b
Update Vue "manual setup" steps
breadadams Mar 15, 2019
d0281d0
Merge pull request #6046 from thekogmo/patch-3
shilman Mar 14, 2019
bf252cc
Merge pull request #6084 from raybooysen/patch-3
shilman Mar 14, 2019
9b6905c
Merge pull request #6105 from danielsogl/patch-3
shilman Mar 15, 2019
9744edb
REMOVE the pure functionality as it's not really usable && ADD overlo…
ndelangen Mar 15, 2019
1a7841e
Merge branch 'next' into tech/api-package
ndelangen Mar 16, 2019
77cfc5b
FIX issue with backgroundSelector
ndelangen Mar 16, 2019
7c56ae7
Merge branch 'next' into tech/api-package
ndelangen Mar 18, 2019
b18b2c8
Merge branch 'next' into tech/api-package
ndelangen Mar 18, 2019
be2387f
FIX types
ndelangen Mar 18, 2019
b7bc65c
REMOVE much of the need for channel in addon-links
ndelangen Mar 18, 2019
880723a
ADD once functionality to api && FIX types for provider.channel
ndelangen Mar 18, 2019
817c8a1
ADD script for detecting memoryLeaks in tests
ndelangen Mar 18, 2019
29a603a
CLEANUP && FIX tests
ndelangen Mar 18, 2019
9510552
ADD always mock for util-deprecate && REMOVE loglines in tests for de…
ndelangen Mar 18, 2019
62299a6
trying to speed up yarn tests
ndelangen Mar 18, 2019
e73ffa8
Merge branch 'next' into tech/api-package
ndelangen Mar 18, 2019
accf970
FIX yarn.lock file for docs
ndelangen Mar 18, 2019
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
24 changes: 4 additions & 20 deletions addons/cssresources/src/css-resource-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@ import React, { Component, Fragment } from 'react';
import { SyntaxHighlighter } from '@storybook/components';
import events, { STORY_CHANGED } from '@storybook/core-events';
import { EVENTS, PARAM_KEY } from './constants';
import { Channel } from '@storybook/channels';
import { CssResource } from './CssResource';
import { bool, func, shape } from 'prop-types';

interface CssResourcePanelProps {
interface Props {
active: boolean;
channel: Channel;
api: {
emit: (event: any, data: any) => void;
on: (event: events, callback: (data: any) => void) => void;
Expand All @@ -19,12 +17,12 @@ interface CssResourcePanelProps {
};
}

interface CssResourcePanelState {
interface State {
list: CssResource[];
}

export class CssResourcePanel extends Component<CssResourcePanelProps, CssResourcePanelState> {
constructor(props: CssResourcePanelProps) {
export class CssResourcePanel extends Component<Props, State> {
ndelangen marked this conversation as resolved.
Show resolved Hide resolved
constructor(props: Props) {
super(props);

this.state = {
Expand Down Expand Up @@ -90,17 +88,3 @@ export class CssResourcePanel extends Component<CssResourcePanelProps, CssResour
);
}
}

(CssResourcePanel as any).propTypes = {
active: bool.isRequired,
channel: shape({
on: func,
emit: func,
removeListener: func,
}).isRequired,
api: shape({
on: func,
getQueryParam: func,
setQueryParams: func,
}).isRequired,
};
9 changes: 2 additions & 7 deletions addons/cssresources/src/register.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,9 @@ import { ADDON_ID, PANEL_ID } from './constants';
import { CssResourcePanel } from './css-resource-panel';

addons.register(ADDON_ID, api => {
const channel = addons.getChannel();
// Need to cast as any as it's not matching Addon type, to investigate
addons.add(PANEL_ID, {
type: types.PANEL,
title: 'CSS resources',
// eslint-disable-next-line react/prop-types
render: ({ active, key }: any) => (
<CssResourcePanel key={key} channel={channel} api={api} active={active} />
),
} as any);
render: ({ active }) => <CssResourcePanel api={api} active={active} />,
});
});
30 changes: 30 additions & 0 deletions lib/addons/src/experimental.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// import React from 'react';
// import { addons, types } from '@storybook/addons';
// import { Consumer } from '@storybook/api';

// import { ADDON_ID, PANEL_ID } from './constants';

// {
// /*
// <Consumer filter={({ state, api }) => [state.storyId]}>
// {(storyId) => <h1>Current story = {storyId}</h1>}
// </Consumer>
// */
// }

// addons.register(ADDON_ID, api => {
// addons.add(PANEL_ID, {
// type: types.PANEL,
// title: 'My Addon',
// render: ({ active }) => <Consumer>{({ state }) => <h1>Current story = {state.storyId}</h1>}</Consumer>,
// });
// });

// addons.register(ADDON_ID, api => {
// addons.add(PANEL_ID, {
// type: types.PANEL,
// title: 'My Addon',
// connect: ({ state: { storyId }, api }) => ({ storyId }),
// render: ({ active, storyId }) => <h1>Current story = {storyId}</h1>,
// });
// });
9 changes: 4 additions & 5 deletions lib/addons/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { ReactElement } from 'react';
import { Channel } from '@storybook/channels';
import logger from '@storybook/client-logger';
import { types, Types, isSupportedType } from './types';
import deprecate from 'util-deprecate';

export interface RenderOptions {
active: boolean;
Expand All @@ -20,19 +19,19 @@ export interface Addon {
title: string;
type?: Types;
id?: string;
route: (routeOptions: RouteOptions) => string;
match: (matchOptions: MatchOptions) => boolean;
route?: (routeOptions: RouteOptions) => string;
match?: (matchOptions: MatchOptions) => boolean;
render: (renderOptions: RenderOptions) => ReactElement<any>;
}

export type Loader = (callback: (api: any) => void) => void;

export { types, isSupportedType };
export { types, Types, isSupportedType };

interface Loaders {
[key: string]: Loader;
}
interface Collection {
export interface Collection {
[key: string]: Addon;
}
interface Elements {
Expand Down
43 changes: 43 additions & 0 deletions lib/api/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"name": "@storybook/api",
"version": "5.0.0-alpha.10",
"description": "Core Storybook API & Context",
"keywords": [
"storybook"
],
"homepage": "https://github.com/storybooks/storybook/tree/master/lib/api",
"bugs": {
"url": "https://github.com/storybooks/storybook/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/storybooks/storybook.git"
},
"license": "MIT",
"main": "dist/index.js",
"jsnext:main": "src/index.js",
"scripts": {
"prepare": "node ./scripts/generateVersion.js && node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "5.0.0-alpha.10",
"@storybook/core-events": "5.0.0-alpha.10",
"@storybook/theming": "5.0.0-alpha.10",
"@storybook/client-logger": "5.0.0-alpha.10",
"@storybook/router": "5.0.0-alpha.10",
"@types/lodash.isequal": "^4.5.3",
"@types/lodash.mergewith": "^4.6.4",
"@types/lodash.pick": "^4.4.4",
"global": "^4.3.2",
"lodash.isequal": "^4.5.0",
"lodash.mergewith": "^4.6.1",
"lodash.pick": "^4.4.0",
"prop-types": "^15.6.2",
"react": "^16.7.0",
"util-deprecate": "^1.0.2"
},
"devDependencies": {},
"publishConfig": {
"access": "public"
}
}
9 changes: 9 additions & 0 deletions lib/api/scripts/generateVersion.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const fs = require('fs');
ndelangen marked this conversation as resolved.
Show resolved Hide resolved
const path = require('path');

const output = version => `export const version = '${version}';\n`;

const text = fs.readFileSync(path.join(__dirname, '../package.json'), 'utf8');
const json = JSON.parse(text);

fs.writeFileSync(path.join(__dirname, '../src/version.ts'), output(json.version), 'utf8');
211 changes: 211 additions & 0 deletions lib/api/src/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
import React, { Component } from 'react';

import Events from '@storybook/core-events';
import { Collection, Types } from '@storybook/addons';
import initProviderApi from './init-provider-api';

import Store from './store';
import getInitialState from './initial-state';

import initAddons from './modules/addons';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the modules should remain in lib/ui

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At least the modules that are directly relevant to the UI, such as the layout state.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

possibly, do you have a suggestion on how to hook that up?

I feel like there a pros and cons to both approaches. Having the global state in 1 location is not a bad thing?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it depends what you mean. I would expect the state to all be stored in the one "store" but the code for setting/changing the UI's state would live in lib/ui. Similarly, addons set their own state in the same store but their store setting state lives in their codebase.

I guess the question is how to make the APIs that the UI creates available to the addons in a principled/typed way. Would it make sense that if an addon wants to alter the UI's state (e.g. set fullscreen) that it should import something from @storybook/ui? I feel like that would be sensible.

import initChannel from './modules/channel';
import initNotifications, { Notification } from './modules/notifications';
import initStories, { StoriesHash } from './modules/stories';
import initLayout from './modules/layout';
import initShortcuts, { Shortcuts } from './modules/shortcuts';
import initURL, { QueryParams } from './modules/url';
import initVersions from './modules/versions';

const ManagerContext = React.createContext({ api: undefined, state: getInitialState({}) });

const { STORY_CHANGED, SET_STORIES, SELECT_STORY } = Events;

interface RouterData {
location: Location;
path: string;
viewMode: 'story' | 'info' | string | undefined;
storyId: string;
}

export type Module = StoreData & RouterData & ProviderData & Navigate;

interface Theme {
[key: string]: any;
}

export interface State extends RouterData {
ndelangen marked this conversation as resolved.
Show resolved Hide resolved
layout: {
isFullscreen: boolean;
showPanel: boolean;
panelPosition: 'bottom' | 'right';
showNav: boolean;
isToolshown: boolean;
};

ui: {
name: string;
url: string;
enableShortcuts: boolean;
sortStoriesByKind: boolean;
sidebarAnimations: boolean;
theme: Theme;
};

storiesHash: StoriesHash;

shortcuts: Shortcuts;

customQueryParams: QueryParams;

notifications: Notification[];

versions: {
latest: any;
current: any;
};
lastVersionCheck: any;
dismissedVersionNotification: any;

[key: string]: any;
}

export interface API {
ndelangen marked this conversation as resolved.
Show resolved Hide resolved
[key: string]: any;
}
interface Combo {
api: API;
state: State;
}

interface Provider {
handleAPI(api: API): void;
renderPreview(): void;
getElements(type: Types): Collection;
[key: string]: any;
ndelangen marked this conversation as resolved.
Show resolved Hide resolved
}

interface ProviderData {
provider: Provider;
}

interface StoreData {
store: Store;
}

interface Children {
children: React.Component | (({ api, state }: Combo) => React.Component);
}

interface Navigate {
navigate(to: string, options?: { replace: boolean }): void;
}

type Props = Children & RouterData & ProviderData & Navigate;

export class ManagerProvider extends Component<Props, State> {
constructor(props: Props) {
super(props);
const { provider, location, path, viewMode, storyId, navigate } = props;

const store = new Store({
getState: () => this.state,
setState: (a: any, b: any) => this.setState(a, b),
ndelangen marked this conversation as resolved.
Show resolved Hide resolved
});

// Initialize the state to be the initial (persisted) state of the store.
// This gives the modules the chance to read the persisted state, apply their defaults
// and override if necessary
this.state = store.getInitialState();

const apiData = {
navigate,
store,
provider,
location,
path,
viewMode,
storyId,
};

this.modules = [initChannel, initAddons, initLayout, initNotifications, initShortcuts, initStories, initURL, initVersions].map(
initModule => initModule(apiData)
);

// Create our initial state by combining the initial state of all modules, then overlaying any saved state
const state = getInitialState(...this.modules.map(m => m.state));

// Get our API by combining the APIs exported by each module
const combo = Object.assign({ navigate }, ...this.modules.map(m => m.api));

const api = initProviderApi({ provider, store, api: combo });

api.on(STORY_CHANGED, (id: string) => {
const options = api.getParameters(id, 'options');

api.setOptions(options);
});

api.on(SET_STORIES, (data: any) => {
api.setStories(data.stories);
ndelangen marked this conversation as resolved.
Show resolved Hide resolved
});
api.on(SELECT_STORY, ({ kind, story, ...rest }: { [k: string]: any }) => {
api.selectStory(kind, story, rest);
});

this.state = state;
this.api = api;
}

static displayName = 'Manager';
api: API;
modules: any[];

static getDerivedStateFromProps = (props: Props, state: State) => {
if (state.path !== props.path) {
return {
...state,
location: props.location,
path: props.path,
viewMode: props.viewMode,
storyId: props.storyId,
};
}
return null;
};

componentDidMount() {
// Now every module has had a chance to set its API, call init on each module which gives it
// a chance to do things that call other modules' APIs.
this.modules.forEach(({ init }) => {
if (init) {
init({ api: this.api });
}
});
}

shouldComponentUpdate(nextProps: Props, nextState: State) {
const { state: prevState, props: prevProps } = this;

if (prevState !== nextState) {
return true;
}
if (prevProps.path !== nextProps.path) {
return true;
}
return false;
}

render() {
const { children } = this.props;
const value = {
state: this.state,
api: this.api,
};

return <ManagerContext.Provider value={value}>{typeof children === 'function' ? children(value) : children}</ManagerContext.Provider>;
}
}

const ManagerConsumer = ManagerContext.Consumer;

export { ManagerConsumer as Consumer, ManagerProvider as Provider };
Loading