@storybook/react-native 6.0.1-alpha.3 → 6.0.1-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/babel.config.js +3 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -1
- package/dist/preview/Preview.d.ts +26 -22
- package/dist/preview/Preview.js +16 -21
- package/dist/preview/components/OnDeviceUI/OnDeviceUI.js +7 -4
- package/dist/preview/components/OnDeviceUI/animation.d.ts +4 -4
- package/dist/preview/components/OnDeviceUI/animation.js +21 -12
- package/package.json +31 -13
- package/scripts/__snapshots__/loader.test.js.snap +118 -0
- package/scripts/get-stories.js +3 -1
- package/scripts/handle-args.js +18 -0
- package/scripts/loader.js +80 -65
- package/scripts/loader.test.js +133 -0
- package/scripts/mocks/all-config-files/FakeComponent.tsx +1 -0
- package/scripts/mocks/all-config-files/FakeStory.stories.tsx +10 -0
- package/scripts/mocks/all-config-files/main.js +9 -0
- package/scripts/mocks/all-config-files/preview.js +24 -0
- package/scripts/mocks/blank-config/main.js +4 -0
- package/scripts/mocks/exclude-config-files/exclude-components/FakeComponent.tsx +1 -0
- package/scripts/mocks/exclude-config-files/exclude-components/FakeStory.stories.tsx +10 -0
- package/scripts/mocks/exclude-config-files/include-components/FakeComponent.tsx +1 -0
- package/scripts/mocks/exclude-config-files/include-components/FakeStory.stories.tsx +10 -0
- package/scripts/mocks/exclude-config-files/main.js +12 -0
- package/scripts/mocks/exclude-config-files/preview.js +24 -0
- package/scripts/mocks/no-preview/FakeComponent.tsx +1 -0
- package/scripts/mocks/no-preview/FakeStory.stories.tsx +10 -0
- package/scripts/mocks/no-preview/main.js +9 -0
- package/scripts/watcher.js +48 -15
package/babel.config.js
ADDED
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
import { StoryApi } from '@storybook/addons';
|
|
4
4
|
import { ClientApi } from '@storybook/client-api';
|
|
5
5
|
import { ReactNode } from 'react';
|
|
6
|
+
import Preview from './preview';
|
|
7
|
+
export declare const preview: Preview;
|
|
6
8
|
export declare const setAddon: ClientApi['setAddon'];
|
|
7
9
|
export declare const addDecorator: ClientApi['addDecorator'];
|
|
8
10
|
export declare const addParameters: ClientApi['addParameters'];
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Preview from './preview';
|
|
2
|
-
const preview = new Preview();
|
|
2
|
+
export const preview = new Preview();
|
|
3
3
|
const rawStoriesOf = preview.api().storiesOf.bind(preview);
|
|
4
4
|
export const setAddon = preview.api().setAddon.bind(preview);
|
|
5
5
|
export const addDecorator = preview.api().addDecorator.bind(preview);
|
|
@@ -4,27 +4,32 @@ import Channel from '@storybook/channels';
|
|
|
4
4
|
import { ClientApi, ConfigApi, StoryStore } from '@storybook/client-api';
|
|
5
5
|
import { Loadable } from '@storybook/core-client';
|
|
6
6
|
import { theme } from './components/Shared/theme';
|
|
7
|
-
interface
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
interface InitialSelection {
|
|
8
|
+
/**
|
|
9
|
+
* Kind is the default export name or the storiesOf("name") name
|
|
10
|
+
*/
|
|
11
|
+
kind: string;
|
|
12
|
+
/**
|
|
13
|
+
* Name is the named export or the .add("name") name
|
|
14
|
+
*/
|
|
15
|
+
name: string;
|
|
10
16
|
}
|
|
11
17
|
export declare type Params = {
|
|
12
|
-
onDeviceUI
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
keyboardAvoidingViewVerticalOffset: number;
|
|
18
|
+
onDeviceUI?: boolean;
|
|
19
|
+
resetStorybook?: boolean;
|
|
20
|
+
disableWebsockets?: boolean;
|
|
21
|
+
query?: string;
|
|
22
|
+
host?: string;
|
|
23
|
+
port?: number;
|
|
24
|
+
secured?: boolean;
|
|
25
|
+
initialSelection?: InitialSelection;
|
|
26
|
+
shouldPersistSelection?: boolean;
|
|
27
|
+
tabOpen?: number;
|
|
28
|
+
isUIHidden?: boolean;
|
|
29
|
+
shouldDisableKeyboardAvoidingView?: boolean;
|
|
30
|
+
keyboardAvoidingViewVerticalOffset?: number;
|
|
26
31
|
} & {
|
|
27
|
-
theme
|
|
32
|
+
theme?: typeof theme;
|
|
28
33
|
};
|
|
29
34
|
export default class Preview {
|
|
30
35
|
_clientApi: ClientApi;
|
|
@@ -33,18 +38,17 @@ export default class Preview {
|
|
|
33
38
|
_channel: Channel;
|
|
34
39
|
_decorators: any[];
|
|
35
40
|
_asyncStorageStoryId: string;
|
|
36
|
-
_asyncStorage: AsyncStorage | null;
|
|
37
41
|
_configApi: ConfigApi;
|
|
38
42
|
configure: (loadable: Loadable, m: NodeModule, showDeprecationWarning: boolean) => void;
|
|
39
43
|
constructor();
|
|
40
44
|
api: () => ClientApi;
|
|
41
45
|
getStorybookUI: (params?: Partial<Params>) => () => JSX.Element;
|
|
42
|
-
_setInitialStory: (initialSelection
|
|
43
|
-
_getInitialStory: (initialSelection
|
|
46
|
+
_setInitialStory: (initialSelection?: InitialSelection, shouldPersistSelection?: boolean) => Promise<void>;
|
|
47
|
+
_getInitialStory: (initialSelection?: InitialSelection, shouldPersistSelection?: boolean) => Promise<import("@storybook/client-api").PublishedStoreItem>;
|
|
44
48
|
_getStory(storyId: string): import("@storybook/client-api").PublishedStoreItem;
|
|
45
49
|
_selectStoryEvent({ storyId }: {
|
|
46
50
|
storyId: string;
|
|
47
|
-
}): void;
|
|
51
|
+
}, shouldPersistSelection: any): void;
|
|
48
52
|
_selectStory(story: any): void;
|
|
49
53
|
_checkStory(storyId: string): import("@storybook/client-api").PublishedStoreItem;
|
|
50
54
|
}
|
package/dist/preview/Preview.js
CHANGED
|
@@ -7,15 +7,17 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
10
11
|
import { addons } from '@storybook/addons';
|
|
11
12
|
import Channel from '@storybook/channels';
|
|
12
13
|
import { ClientApi, ConfigApi, StoryStore } from '@storybook/client-api';
|
|
13
14
|
import Events from '@storybook/core-events';
|
|
15
|
+
import { toId } from '@storybook/csf';
|
|
14
16
|
import { ThemeProvider } from 'emotion-theming';
|
|
15
17
|
import React from 'react';
|
|
16
|
-
import { loadCsf } from './loadCsf';
|
|
17
18
|
import OnDeviceUI from './components/OnDeviceUI';
|
|
18
19
|
import { theme } from './components/Shared/theme';
|
|
20
|
+
import { loadCsf } from './loadCsf';
|
|
19
21
|
const STORAGE_KEY = 'lastOpenedStory';
|
|
20
22
|
export default class Preview {
|
|
21
23
|
constructor() {
|
|
@@ -23,20 +25,10 @@ export default class Preview {
|
|
|
23
25
|
return this._clientApi;
|
|
24
26
|
};
|
|
25
27
|
this.getStorybookUI = (params = {}) => {
|
|
26
|
-
|
|
27
|
-
console.warn(`
|
|
28
|
-
Starting Storybook v5.3.0, we require you to manually pass an asyncStorage prop. Pass null to disable or use https://github.com/react-native-async-storage/async-storage.
|
|
29
|
-
|
|
30
|
-
More info: https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#react-native-async-storage
|
|
31
|
-
`.trim());
|
|
32
|
-
}
|
|
33
|
-
if (params.asyncStorage) {
|
|
34
|
-
this._asyncStorage = params.asyncStorage;
|
|
35
|
-
}
|
|
36
|
-
const { initialSelection, shouldPersistSelection } = params;
|
|
28
|
+
const { initialSelection, shouldPersistSelection = true } = params;
|
|
37
29
|
this._setInitialStory(initialSelection, shouldPersistSelection);
|
|
38
30
|
this._channel.on(Events.SET_CURRENT_STORY, (d) => {
|
|
39
|
-
this._selectStoryEvent(d);
|
|
31
|
+
this._selectStoryEvent(d, shouldPersistSelection);
|
|
40
32
|
});
|
|
41
33
|
const { _storyStore } = this;
|
|
42
34
|
addons.loadAddons(this._clientApi);
|
|
@@ -52,14 +44,17 @@ export default class Preview {
|
|
|
52
44
|
});
|
|
53
45
|
this._getInitialStory = (initialSelection, shouldPersistSelection = true) => __awaiter(this, void 0, void 0, function* () {
|
|
54
46
|
let story = null;
|
|
55
|
-
|
|
56
|
-
|
|
47
|
+
const initialSelectionId = initialSelection
|
|
48
|
+
? toId(initialSelection.kind, initialSelection.name)
|
|
49
|
+
: undefined;
|
|
50
|
+
if (initialSelection && initialSelectionId && this._checkStory(initialSelectionId)) {
|
|
51
|
+
story = initialSelectionId;
|
|
57
52
|
}
|
|
58
53
|
else if (shouldPersistSelection) {
|
|
59
54
|
try {
|
|
60
55
|
let value = this._asyncStorageStoryId;
|
|
61
|
-
if (!value
|
|
62
|
-
value = JSON.parse(yield
|
|
56
|
+
if (!value) {
|
|
57
|
+
value = JSON.parse(yield AsyncStorage.getItem(STORAGE_KEY));
|
|
63
58
|
this._asyncStorageStoryId = value;
|
|
64
59
|
}
|
|
65
60
|
if (this._checkStory(value)) {
|
|
@@ -96,10 +91,10 @@ export default class Preview {
|
|
|
96
91
|
_getStory(storyId) {
|
|
97
92
|
return this._storyStore.fromId(storyId);
|
|
98
93
|
}
|
|
99
|
-
_selectStoryEvent({ storyId }) {
|
|
94
|
+
_selectStoryEvent({ storyId }, shouldPersistSelection) {
|
|
100
95
|
if (storyId) {
|
|
101
|
-
if (
|
|
102
|
-
|
|
96
|
+
if (shouldPersistSelection) {
|
|
97
|
+
AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(storyId)).catch(() => { });
|
|
103
98
|
}
|
|
104
99
|
const story = this._getStory(storyId);
|
|
105
100
|
this._selectStory(story);
|
|
@@ -114,7 +109,7 @@ export default class Preview {
|
|
|
114
109
|
return null;
|
|
115
110
|
}
|
|
116
111
|
const story = this._getStory(storyId);
|
|
117
|
-
if (story.storyFn === null) {
|
|
112
|
+
if (story === null || story.storyFn === null) {
|
|
118
113
|
return null;
|
|
119
114
|
}
|
|
120
115
|
return story;
|
|
@@ -11,12 +11,14 @@ import { getAddonPanelPosition, getNavigatorPanelPosition, getPreviewPosition, g
|
|
|
11
11
|
import Navigation from './navigation';
|
|
12
12
|
import { PREVIEW, ADDONS } from './navigation/constants';
|
|
13
13
|
import Panel from './Panel';
|
|
14
|
+
import { useWindowDimensions } from 'react-native';
|
|
14
15
|
const ANIMATION_DURATION = 300;
|
|
15
16
|
const IS_IOS = Platform.OS === 'ios';
|
|
16
17
|
// @ts-ignore: Property 'Expo' does not exist on type 'Global'
|
|
17
18
|
const getExpoRoot = () => global.Expo || global.__expo || global.__exponent;
|
|
18
19
|
export const IS_EXPO = getExpoRoot() !== undefined;
|
|
19
20
|
const IS_ANDROID = Platform.OS === 'android';
|
|
21
|
+
const BREAKPOINT = 1024;
|
|
20
22
|
const flex = { flex: 1 };
|
|
21
23
|
const Preview = styled.View(flex, ({ disabled, theme }) => ({
|
|
22
24
|
borderLeftWidth: disabled ? 0 : 1,
|
|
@@ -62,6 +64,7 @@ const OnDeviceUI = ({ storyStore, isUIHidden, shouldDisableKeyboardAvoidingView,
|
|
|
62
64
|
});
|
|
63
65
|
const story = useSelectedStory(storyStore);
|
|
64
66
|
const animatedValue = useRef(new Animated.Value(tabOpen));
|
|
67
|
+
const wide = useWindowDimensions().width >= BREAKPOINT;
|
|
65
68
|
const handleToggleTab = (newTabOpen) => {
|
|
66
69
|
if (newTabOpen === tabOpen) {
|
|
67
70
|
return;
|
|
@@ -81,9 +84,9 @@ const OnDeviceUI = ({ storyStore, isUIHidden, shouldDisableKeyboardAvoidingView,
|
|
|
81
84
|
};
|
|
82
85
|
const previewWrapperStyles = [
|
|
83
86
|
flex,
|
|
84
|
-
getPreviewPosition(animatedValue.current, previewDimensions, slideBetweenAnimation),
|
|
87
|
+
getPreviewPosition(animatedValue.current, previewDimensions, slideBetweenAnimation, wide),
|
|
85
88
|
];
|
|
86
|
-
const previewStyles = [flex, getPreviewScale(animatedValue.current, slideBetweenAnimation)];
|
|
89
|
+
const previewStyles = [flex, getPreviewScale(animatedValue.current, slideBetweenAnimation, wide)];
|
|
87
90
|
return (React.createElement(SafeAreaView, { style: [flex, IS_ANDROID && IS_EXPO && styles.expoAndroidContainer] },
|
|
88
91
|
React.createElement(KeyboardAvoidingView, { enabled: !shouldDisableKeyboardAvoidingView || tabOpen !== PREVIEW, behavior: IS_IOS ? 'padding' : null, keyboardVerticalOffset: keyboardAvoidingViewVerticalOffset, style: flex },
|
|
89
92
|
React.createElement(AbsolutePositionedKeyboardAwareView, { onLayout: setPreviewDimensions, previewDimensions: previewDimensions },
|
|
@@ -92,9 +95,9 @@ const OnDeviceUI = ({ storyStore, isUIHidden, shouldDisableKeyboardAvoidingView,
|
|
|
92
95
|
React.createElement(Preview, { disabled: tabOpen === PREVIEW },
|
|
93
96
|
React.createElement(StoryView, { story: story })),
|
|
94
97
|
tabOpen !== PREVIEW ? (React.createElement(TouchableOpacity, { style: absolutePosition, onPress: () => handleToggleTab(PREVIEW) })) : null)),
|
|
95
|
-
React.createElement(Panel, { style: getNavigatorPanelPosition(animatedValue.current, previewDimensions.width) },
|
|
98
|
+
React.createElement(Panel, { style: getNavigatorPanelPosition(animatedValue.current, previewDimensions.width, wide) },
|
|
96
99
|
React.createElement(StoryListView, { storyStore: storyStore, selectedStory: story })),
|
|
97
|
-
React.createElement(Panel, { style: getAddonPanelPosition(animatedValue.current, previewDimensions.width) },
|
|
100
|
+
React.createElement(Panel, { style: getAddonPanelPosition(animatedValue.current, previewDimensions.width, wide) },
|
|
98
101
|
React.createElement(Addons, { active: tabOpen === ADDONS }))),
|
|
99
102
|
React.createElement(Navigation, { tabOpen: tabOpen, onChangeTab: handleToggleTab, initialUiVisible: !isUIHidden }))));
|
|
100
103
|
};
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { Animated } from 'react-native';
|
|
2
2
|
import { PreviewDimens } from './absolute-positioned-keyboard-aware-view';
|
|
3
|
-
export declare const getNavigatorPanelPosition: (animatedValue: Animated.Value, previewWidth: number) => {
|
|
3
|
+
export declare const getNavigatorPanelPosition: (animatedValue: Animated.Value, previewWidth: number, wide: boolean) => {
|
|
4
4
|
transform: {
|
|
5
5
|
translateX: Animated.AnimatedInterpolation;
|
|
6
6
|
}[];
|
|
7
7
|
width: number;
|
|
8
8
|
}[];
|
|
9
|
-
export declare const getAddonPanelPosition: (animatedValue: Animated.Value, previewWidth: number) => {
|
|
9
|
+
export declare const getAddonPanelPosition: (animatedValue: Animated.Value, previewWidth: number, wide: boolean) => {
|
|
10
10
|
transform: {
|
|
11
11
|
translateX: Animated.AnimatedInterpolation;
|
|
12
12
|
}[];
|
|
13
13
|
width: number;
|
|
14
14
|
}[];
|
|
15
|
-
export declare const getPreviewPosition: (animatedValue: Animated.Value, { width: previewWidth, height: previewHeight }: PreviewDimens, slideBetweenAnimation: boolean) => {
|
|
15
|
+
export declare const getPreviewPosition: (animatedValue: Animated.Value, { width: previewWidth, height: previewHeight }: PreviewDimens, slideBetweenAnimation: boolean, wide: boolean) => {
|
|
16
16
|
transform: ({
|
|
17
17
|
translateX: Animated.AnimatedInterpolation;
|
|
18
18
|
translateY?: undefined;
|
|
@@ -21,7 +21,7 @@ export declare const getPreviewPosition: (animatedValue: Animated.Value, { width
|
|
|
21
21
|
translateX?: undefined;
|
|
22
22
|
})[];
|
|
23
23
|
};
|
|
24
|
-
export declare const getPreviewScale: (animatedValue: Animated.Value, slideBetweenAnimation: boolean) => {
|
|
24
|
+
export declare const getPreviewScale: (animatedValue: Animated.Value, slideBetweenAnimation: boolean, wide: boolean) => {
|
|
25
25
|
transform: {
|
|
26
26
|
scale: Animated.AnimatedInterpolation;
|
|
27
27
|
}[];
|
|
@@ -1,39 +1,47 @@
|
|
|
1
1
|
import { NAVIGATOR, PREVIEW, ADDONS } from './navigation/constants';
|
|
2
2
|
const PREVIEW_SCALE = 0.3;
|
|
3
|
-
const
|
|
4
|
-
|
|
3
|
+
const PREVIEW_WIDE_SCREEN = 0.7;
|
|
4
|
+
const SCALE_OFFSET = 0.025;
|
|
5
|
+
const TRANSLATE_X_OFFSET = 6;
|
|
6
|
+
const TRANSLATE_Y_OFFSET = 12;
|
|
7
|
+
const panelWidth = (width, wide) => {
|
|
8
|
+
const scale = wide ? PREVIEW_WIDE_SCREEN : PREVIEW_SCALE;
|
|
9
|
+
return width * (1 - scale - SCALE_OFFSET);
|
|
10
|
+
};
|
|
11
|
+
export const getNavigatorPanelPosition = (animatedValue, previewWidth, wide) => {
|
|
5
12
|
return [
|
|
6
13
|
{
|
|
7
14
|
transform: [
|
|
8
15
|
{
|
|
9
16
|
translateX: animatedValue.interpolate({
|
|
10
17
|
inputRange: [NAVIGATOR, PREVIEW],
|
|
11
|
-
outputRange: [0, -panelWidth(previewWidth) - 1],
|
|
18
|
+
outputRange: [0, -panelWidth(previewWidth, wide) - 1],
|
|
12
19
|
}),
|
|
13
20
|
},
|
|
14
21
|
],
|
|
15
|
-
width: panelWidth(previewWidth),
|
|
22
|
+
width: panelWidth(previewWidth, wide),
|
|
16
23
|
},
|
|
17
24
|
];
|
|
18
25
|
};
|
|
19
|
-
export const getAddonPanelPosition = (animatedValue, previewWidth) => {
|
|
26
|
+
export const getAddonPanelPosition = (animatedValue, previewWidth, wide) => {
|
|
20
27
|
return [
|
|
21
28
|
{
|
|
22
29
|
transform: [
|
|
23
30
|
{
|
|
24
31
|
translateX: animatedValue.interpolate({
|
|
25
32
|
inputRange: [PREVIEW, ADDONS],
|
|
26
|
-
outputRange: [previewWidth, previewWidth - panelWidth(previewWidth)],
|
|
33
|
+
outputRange: [previewWidth, previewWidth - panelWidth(previewWidth, wide)],
|
|
27
34
|
}),
|
|
28
35
|
},
|
|
29
36
|
],
|
|
30
|
-
width: panelWidth(previewWidth),
|
|
37
|
+
width: panelWidth(previewWidth, wide),
|
|
31
38
|
},
|
|
32
39
|
];
|
|
33
40
|
};
|
|
34
|
-
export const getPreviewPosition = (animatedValue, { width: previewWidth, height: previewHeight }, slideBetweenAnimation) => {
|
|
35
|
-
const
|
|
36
|
-
const
|
|
41
|
+
export const getPreviewPosition = (animatedValue, { width: previewWidth, height: previewHeight }, slideBetweenAnimation, wide) => {
|
|
42
|
+
const scale = wide ? PREVIEW_WIDE_SCREEN : PREVIEW_SCALE;
|
|
43
|
+
const translateX = previewWidth / 2 - (previewWidth * scale) / 2 - TRANSLATE_X_OFFSET;
|
|
44
|
+
const translateY = -(previewHeight / 2 - (previewHeight * scale) / 2 - TRANSLATE_Y_OFFSET);
|
|
37
45
|
return {
|
|
38
46
|
transform: [
|
|
39
47
|
{
|
|
@@ -51,13 +59,14 @@ export const getPreviewPosition = (animatedValue, { width: previewWidth, height:
|
|
|
51
59
|
],
|
|
52
60
|
};
|
|
53
61
|
};
|
|
54
|
-
export const getPreviewScale = (animatedValue, slideBetweenAnimation) => {
|
|
62
|
+
export const getPreviewScale = (animatedValue, slideBetweenAnimation, wide) => {
|
|
63
|
+
const scale = wide ? PREVIEW_WIDE_SCREEN : PREVIEW_SCALE;
|
|
55
64
|
return {
|
|
56
65
|
transform: [
|
|
57
66
|
{
|
|
58
67
|
scale: animatedValue.interpolate({
|
|
59
68
|
inputRange: [NAVIGATOR, PREVIEW, ADDONS],
|
|
60
|
-
outputRange: [
|
|
69
|
+
outputRange: [scale, slideBetweenAnimation ? scale : 1, scale],
|
|
61
70
|
}),
|
|
62
71
|
},
|
|
63
72
|
],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@storybook/react-native",
|
|
3
|
-
"version": "6.0.1-
|
|
3
|
+
"version": "6.0.1-beta.0",
|
|
4
4
|
"description": "A better way to develop React Native Components for your app",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
"license": "MIT",
|
|
20
20
|
"main": "dist/index.js",
|
|
21
21
|
"bin": {
|
|
22
|
-
"
|
|
23
|
-
"
|
|
22
|
+
"sb-rn-get-stories": "./bin/get-stories.js",
|
|
23
|
+
"sb-rn-watcher": "./bin/watcher.js"
|
|
24
24
|
},
|
|
25
25
|
"files": [
|
|
26
26
|
"bin/**/*",
|
|
@@ -32,29 +32,47 @@
|
|
|
32
32
|
],
|
|
33
33
|
"scripts": {
|
|
34
34
|
"preprepare": "rm -rf dist/",
|
|
35
|
-
"prepare": "tsc"
|
|
35
|
+
"prepare": "tsc",
|
|
36
|
+
"test": "jest"
|
|
37
|
+
},
|
|
38
|
+
"jest": {
|
|
39
|
+
"moduleFileExtensions": [
|
|
40
|
+
"ts",
|
|
41
|
+
"tsx",
|
|
42
|
+
"js",
|
|
43
|
+
"jsx",
|
|
44
|
+
"json",
|
|
45
|
+
"node"
|
|
46
|
+
],
|
|
47
|
+
"preset": "react-native"
|
|
36
48
|
},
|
|
37
49
|
"dependencies": {
|
|
38
50
|
"@emotion/core": "^10.0.20",
|
|
39
51
|
"@emotion/native": "^10.0.14",
|
|
40
|
-
"@storybook/addons": "
|
|
41
|
-
"@storybook/channel-websocket": "
|
|
42
|
-
"@storybook/channels": "
|
|
43
|
-
"@storybook/client-api": "
|
|
44
|
-
"@storybook/client-logger": "
|
|
45
|
-
"@storybook/core-client": "
|
|
46
|
-
"@storybook/core-events": "
|
|
52
|
+
"@storybook/addons": "~6.3",
|
|
53
|
+
"@storybook/channel-websocket": "~6.3",
|
|
54
|
+
"@storybook/channels": "~6.3",
|
|
55
|
+
"@storybook/client-api": "~6.3",
|
|
56
|
+
"@storybook/client-logger": "~6.3",
|
|
57
|
+
"@storybook/core-client": "~6.3",
|
|
58
|
+
"@storybook/core-events": "~6.3",
|
|
47
59
|
"@storybook/csf": "0.0.1",
|
|
48
60
|
"chokidar": "^3.5.1",
|
|
61
|
+
"commander": "^8.2.0",
|
|
49
62
|
"emotion-theming": "^10.0.19",
|
|
50
63
|
"glob": "^7.1.7",
|
|
64
|
+
"prettier": "^2.4.1",
|
|
51
65
|
"react-native-swipe-gestures": "^1.0.5",
|
|
52
66
|
"util": "^0.12.4"
|
|
53
67
|
},
|
|
54
68
|
"devDependencies": {
|
|
55
|
-
"@types/react-native": "^0.
|
|
69
|
+
"@types/react-native": "^0.66.15",
|
|
70
|
+
"babel-jest": "^26.6.3",
|
|
71
|
+
"jest": "^26.6.3",
|
|
72
|
+
"react-test-renderer": "17.0.2"
|
|
56
73
|
},
|
|
57
74
|
"peerDependencies": {
|
|
75
|
+
"@react-native-async-storage/async-storage": ">=1",
|
|
58
76
|
"react": "*",
|
|
59
77
|
"react-native": ">=0.57.0"
|
|
60
78
|
},
|
|
@@ -64,5 +82,5 @@
|
|
|
64
82
|
"publishConfig": {
|
|
65
83
|
"access": "public"
|
|
66
84
|
},
|
|
67
|
-
"gitHead": "
|
|
85
|
+
"gitHead": "3490aec85c84210997f38b92afa515b6dc41b8b5"
|
|
68
86
|
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`loader writeRequires when there is a story glob and exclude paths globs writes the story imports 1`] = `
|
|
4
|
+
"
|
|
5
|
+
/* do not change this file, it is auto generated by storybook. */
|
|
6
|
+
|
|
7
|
+
import { configure, addDecorator, addParameters, addArgsEnhancer } from '@storybook/react-native';
|
|
8
|
+
|
|
9
|
+
import \\"@storybook/addon-ondevice-notes/register\\";
|
|
10
|
+
import \\"@storybook/addon-ondevice-controls/register\\";
|
|
11
|
+
import \\"@storybook/addon-ondevice-backgrounds/register\\";
|
|
12
|
+
import \\"@storybook/addon-ondevice-actions/register\\";
|
|
13
|
+
|
|
14
|
+
import { argsEnhancers } from \\"@storybook/addon-actions/dist/modern/preset/addArgs\\"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
import { decorators, parameters } from './preview';
|
|
18
|
+
|
|
19
|
+
if (decorators) {
|
|
20
|
+
decorators.forEach((decorator) => addDecorator(decorator));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (parameters) {
|
|
24
|
+
addParameters(parameters);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
argsEnhancers.forEach(enhancer => addArgsEnhancer(enhancer))
|
|
29
|
+
|
|
30
|
+
const getStories=() => {
|
|
31
|
+
return [require(\\"include-components/FakeStory.stories.tsx\\")];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
configure(getStories, module, false)
|
|
35
|
+
"
|
|
36
|
+
`;
|
|
37
|
+
|
|
38
|
+
exports[`loader writeRequires when there is a story glob writes the story imports 1`] = `
|
|
39
|
+
"
|
|
40
|
+
/* do not change this file, it is auto generated by storybook. */
|
|
41
|
+
|
|
42
|
+
import { configure, addDecorator, addParameters, addArgsEnhancer } from '@storybook/react-native';
|
|
43
|
+
|
|
44
|
+
import \\"@storybook/addon-ondevice-notes/register\\";
|
|
45
|
+
import \\"@storybook/addon-ondevice-controls/register\\";
|
|
46
|
+
import \\"@storybook/addon-ondevice-backgrounds/register\\";
|
|
47
|
+
import \\"@storybook/addon-ondevice-actions/register\\";
|
|
48
|
+
|
|
49
|
+
import { argsEnhancers } from \\"@storybook/addon-actions/dist/modern/preset/addArgs\\"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
import { decorators, parameters } from './preview';
|
|
53
|
+
|
|
54
|
+
if (decorators) {
|
|
55
|
+
decorators.forEach((decorator) => addDecorator(decorator));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (parameters) {
|
|
59
|
+
addParameters(parameters);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
argsEnhancers.forEach(enhancer => addArgsEnhancer(enhancer))
|
|
64
|
+
|
|
65
|
+
const getStories=() => {
|
|
66
|
+
return [require(\\"./FakeStory.stories.tsx\\")];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
configure(getStories, module, false)
|
|
70
|
+
"
|
|
71
|
+
`;
|
|
72
|
+
|
|
73
|
+
exports[`loader writeRequires when there is no preview does not add preview related stuff 1`] = `
|
|
74
|
+
"
|
|
75
|
+
/* do not change this file, it is auto generated by storybook. */
|
|
76
|
+
|
|
77
|
+
import { configure, addDecorator, addParameters, addArgsEnhancer } from '@storybook/react-native';
|
|
78
|
+
|
|
79
|
+
import \\"@storybook/addon-ondevice-notes/register\\";
|
|
80
|
+
import \\"@storybook/addon-ondevice-controls/register\\";
|
|
81
|
+
import \\"@storybook/addon-ondevice-backgrounds/register\\";
|
|
82
|
+
import \\"@storybook/addon-ondevice-actions/register\\";
|
|
83
|
+
|
|
84
|
+
import { argsEnhancers } from \\"@storybook/addon-actions/dist/modern/preset/addArgs\\"
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
argsEnhancers.forEach(enhancer => addArgsEnhancer(enhancer))
|
|
89
|
+
|
|
90
|
+
const getStories=() => {
|
|
91
|
+
return [require(\\"./FakeStory.stories.tsx\\")];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
configure(getStories, module, false)
|
|
95
|
+
"
|
|
96
|
+
`;
|
|
97
|
+
|
|
98
|
+
exports[`loader writeRequires when there is no story glob or addons writes no story imports or addons 1`] = `
|
|
99
|
+
"
|
|
100
|
+
/* do not change this file, it is auto generated by storybook. */
|
|
101
|
+
|
|
102
|
+
import { configure, addDecorator, addParameters, addArgsEnhancer } from '@storybook/react-native';
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
const getStories=() => {
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
configure(getStories, module, false)
|
|
117
|
+
"
|
|
118
|
+
`;
|
package/scripts/get-stories.js
CHANGED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
function getArguments() {
|
|
2
|
+
const { program } = require('commander');
|
|
3
|
+
|
|
4
|
+
program
|
|
5
|
+
.description('Getter and watcher for react native storybook')
|
|
6
|
+
.option(
|
|
7
|
+
'-c, --config-path <path>',
|
|
8
|
+
'The path to your config folder relative to your project-dir',
|
|
9
|
+
'./.storybook'
|
|
10
|
+
)
|
|
11
|
+
.option('-a, --absolute', 'Use absolute paths for story imports');
|
|
12
|
+
|
|
13
|
+
program.parse();
|
|
14
|
+
|
|
15
|
+
return program.opts();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = { getArguments };
|
package/scripts/loader.js
CHANGED
|
@@ -1,98 +1,115 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const glob = require('glob');
|
|
4
|
-
const
|
|
4
|
+
const prettier = require('prettier');
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
delete require.cache[require.resolve(module)];
|
|
8
|
-
return require(module);
|
|
9
|
-
}
|
|
6
|
+
const cwd = process.cwd();
|
|
10
7
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
return path.join(cwd, '.storybook/main.js');
|
|
14
|
-
}
|
|
8
|
+
const previewImports = `
|
|
9
|
+
import { decorators, parameters } from './preview';
|
|
15
10
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
11
|
+
if (decorators) {
|
|
12
|
+
decorators.forEach((decorator) => addDecorator(decorator));
|
|
13
|
+
}
|
|
20
14
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
15
|
+
if (parameters) {
|
|
16
|
+
addParameters(parameters);
|
|
17
|
+
}
|
|
18
|
+
`;
|
|
24
19
|
|
|
25
|
-
function
|
|
26
|
-
//
|
|
27
|
-
|
|
20
|
+
function normalizeExcludePaths(paths) {
|
|
21
|
+
// automatically convert a string to an array of a single string
|
|
22
|
+
if (typeof paths === 'string') {
|
|
23
|
+
return [paths];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ensure the paths is an array and if any items exists, they are strings
|
|
27
|
+
if (Array.isArray(paths) && paths.every((p) => typeof p === 'string')) {
|
|
28
|
+
return paths;
|
|
29
|
+
}
|
|
28
30
|
|
|
29
|
-
return
|
|
31
|
+
// when the paths aren't a string or an (empty) array of strings, return
|
|
32
|
+
return undefined;
|
|
30
33
|
}
|
|
31
34
|
|
|
32
|
-
function
|
|
33
|
-
|
|
35
|
+
function requireUncached(module) {
|
|
36
|
+
delete require.cache[require.resolve(module)];
|
|
37
|
+
return require(module);
|
|
38
|
+
}
|
|
34
39
|
|
|
35
|
-
|
|
40
|
+
function getMain({ configPath }) {
|
|
41
|
+
const mainPath = path.resolve(cwd, configPath, 'main.js');
|
|
42
|
+
return requireUncached(mainPath);
|
|
36
43
|
}
|
|
37
44
|
|
|
38
|
-
function
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return [...acc, ...paths.map((storyPath) => `../${storyPath}`)];
|
|
42
|
-
}, []);
|
|
45
|
+
function getPreviewExists({ configPath }) {
|
|
46
|
+
const previewPath = path.resolve(cwd, configPath, 'preview.js');
|
|
47
|
+
return fs.existsSync(previewPath);
|
|
43
48
|
}
|
|
44
49
|
|
|
45
|
-
function writeRequires() {
|
|
46
|
-
const
|
|
50
|
+
function writeRequires({ configPath, absolute = false }) {
|
|
51
|
+
const storybookRequiresLocation = path.resolve(cwd, configPath, 'storybook.requires.js');
|
|
52
|
+
|
|
53
|
+
const main = getMain({ configPath });
|
|
54
|
+
const reactNativeOptions = main.reactNativeOptions;
|
|
55
|
+
const excludePaths = reactNativeOptions && reactNativeOptions.excludePaths;
|
|
56
|
+
const normalizedExcludePaths = normalizeExcludePaths(excludePaths);
|
|
57
|
+
|
|
58
|
+
const storyPaths = main.stories.reduce((acc, storyGlob) => {
|
|
59
|
+
const paths = glob.sync(storyGlob, {
|
|
60
|
+
cwd: path.resolve(cwd, configPath),
|
|
61
|
+
absolute,
|
|
62
|
+
// default to always ignore (exclude) anything in node_modules
|
|
63
|
+
ignore: normalizedExcludePaths !== undefined
|
|
64
|
+
? normalizedExcludePaths
|
|
65
|
+
: ['**/node_modules'],
|
|
66
|
+
});
|
|
67
|
+
return [...acc, ...paths];
|
|
68
|
+
}, []);
|
|
47
69
|
|
|
48
|
-
|
|
49
|
-
const addons = getAddons();
|
|
70
|
+
fs.writeFileSync(storybookRequiresLocation, '');
|
|
50
71
|
|
|
51
|
-
|
|
72
|
+
const previewExists = getPreviewExists({ configPath });
|
|
52
73
|
|
|
53
|
-
|
|
54
|
-
let previewJs = previewExists
|
|
55
|
-
? `
|
|
56
|
-
import { decorators, parameters } from './preview';
|
|
57
|
-
if (decorators) {
|
|
58
|
-
decorators.forEach((decorator) => addDecorator(decorator));
|
|
59
|
-
}
|
|
60
|
-
if (parameters) {
|
|
61
|
-
addParameters(parameters);
|
|
62
|
-
}`
|
|
63
|
-
: '';
|
|
74
|
+
let previewJs = previewExists ? previewImports : '';
|
|
64
75
|
|
|
65
|
-
const storyRequires = storyPaths.map((storyPath) =>
|
|
66
|
-
const path_array_str = `[
|
|
76
|
+
const storyRequires = storyPaths.map((storyPath) => `require("${storyPath}")`).join(',');
|
|
77
|
+
const path_array_str = `[${storyRequires}]`;
|
|
67
78
|
|
|
68
|
-
const registerAddons = addons.map((addon) => `import "${addon}/register";`).join('\n');
|
|
79
|
+
const registerAddons = main.addons.map((addon) => `import "${addon}/register";`).join('\n');
|
|
80
|
+
let enhancersImport = '';
|
|
69
81
|
let enhancers = '';
|
|
70
82
|
|
|
71
83
|
// TODO: implement presets or something similar
|
|
72
|
-
if (addons.includes('@storybook/addon-ondevice-actions')) {
|
|
73
|
-
|
|
74
|
-
argsEnhancers
|
|
84
|
+
if (main.addons.includes('@storybook/addon-ondevice-actions')) {
|
|
85
|
+
enhancersImport =
|
|
86
|
+
'import { argsEnhancers } from "@storybook/addon-actions/dist/modern/preset/addArgs"';
|
|
87
|
+
enhancers = 'argsEnhancers.forEach(enhancer => addArgsEnhancer(enhancer))';
|
|
75
88
|
}
|
|
76
89
|
|
|
77
90
|
const fileContent = `
|
|
78
|
-
/*
|
|
79
|
-
do not change this file, it is auto generated by storybook.
|
|
80
|
-
*/
|
|
81
|
-
import { configure, addDecorator, addParameters, addArgsEnhancer } from '@storybook/react-native';
|
|
82
|
-
${registerAddons}
|
|
91
|
+
/* do not change this file, it is auto generated by storybook. */
|
|
83
92
|
|
|
84
|
-
|
|
93
|
+
import { configure, addDecorator, addParameters, addArgsEnhancer } from '@storybook/react-native';
|
|
85
94
|
|
|
86
|
-
${
|
|
95
|
+
${registerAddons}
|
|
87
96
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
configure(getStories, module, false)
|
|
97
|
+
${enhancersImport}
|
|
98
|
+
|
|
99
|
+
${previewJs}
|
|
92
100
|
|
|
101
|
+
${enhancers}
|
|
102
|
+
|
|
103
|
+
const getStories=() => {
|
|
104
|
+
return ${path_array_str};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
configure(getStories, module, false)
|
|
93
108
|
`;
|
|
94
109
|
|
|
95
|
-
|
|
110
|
+
const formattedFileContent = prettier.format(fileContent, { parser: 'babel' });
|
|
111
|
+
|
|
112
|
+
fs.writeFileSync(storybookRequiresLocation, formattedFileContent, {
|
|
96
113
|
encoding: 'utf8',
|
|
97
114
|
flag: 'w',
|
|
98
115
|
});
|
|
@@ -100,8 +117,6 @@ configure(getStories, module, false)
|
|
|
100
117
|
|
|
101
118
|
module.exports = {
|
|
102
119
|
writeRequires,
|
|
103
|
-
|
|
104
|
-
getMainPath,
|
|
120
|
+
getMain,
|
|
105
121
|
getPreviewExists,
|
|
106
|
-
getPreviewPath,
|
|
107
122
|
};
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const { writeRequires, getMain, getPreviewExists } = require('./loader');
|
|
3
|
+
const glob = require('glob');
|
|
4
|
+
|
|
5
|
+
let pathMock;
|
|
6
|
+
let fileContentMock;
|
|
7
|
+
|
|
8
|
+
jest.mock('fs', () => ({
|
|
9
|
+
...jest.requireActual('fs'),
|
|
10
|
+
writeFileSync: (filePath, fileContent, opts) => {
|
|
11
|
+
pathMock = filePath;
|
|
12
|
+
fileContentMock = fileContent;
|
|
13
|
+
},
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
jest.mock('prettier', () => ({
|
|
17
|
+
format(s, opts) {
|
|
18
|
+
return s;
|
|
19
|
+
},
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
describe('loader', () => {
|
|
23
|
+
describe('getMain', () => {
|
|
24
|
+
it('should return the main js default export as an object', () => {
|
|
25
|
+
const main = getMain({ configPath: path.resolve(__dirname, 'mocks/all-config-files') });
|
|
26
|
+
expect(main).toEqual({
|
|
27
|
+
stories: ['./FakeStory.stories.tsx'],
|
|
28
|
+
addons: [
|
|
29
|
+
'@storybook/addon-ondevice-notes',
|
|
30
|
+
'@storybook/addon-ondevice-controls',
|
|
31
|
+
'@storybook/addon-ondevice-backgrounds',
|
|
32
|
+
'@storybook/addon-ondevice-actions',
|
|
33
|
+
],
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should also work with relative paths', () => {
|
|
38
|
+
// relative from where the command is run
|
|
39
|
+
const main = getMain({ configPath: './scripts/mocks/all-config-files' });
|
|
40
|
+
expect(main).toEqual({
|
|
41
|
+
stories: ['./FakeStory.stories.tsx'],
|
|
42
|
+
addons: [
|
|
43
|
+
'@storybook/addon-ondevice-notes',
|
|
44
|
+
'@storybook/addon-ondevice-controls',
|
|
45
|
+
'@storybook/addon-ondevice-backgrounds',
|
|
46
|
+
'@storybook/addon-ondevice-actions',
|
|
47
|
+
],
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe('getPreviewExists', () => {
|
|
53
|
+
describe('when using a relative path', () => {
|
|
54
|
+
it('should return true if the preview exists', () => {
|
|
55
|
+
expect(getPreviewExists({ configPath: 'scripts/mocks/all-config-files' })).toBe(true);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should return false if the preview does not exist', () => {
|
|
59
|
+
expect(getPreviewExists({ configPath: './scripts/mocks/no-preview' })).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe('when using an absolute path', () => {
|
|
64
|
+
it('should return true if the preview exists', () => {
|
|
65
|
+
expect(
|
|
66
|
+
getPreviewExists({ configPath: path.resolve(__dirname, 'mocks/all-config-files') })
|
|
67
|
+
).toBe(true);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should return false if the preview does not exist', () => {
|
|
71
|
+
expect(getPreviewExists({ configPath: path.resolve(__dirname, 'mocks/no-preview') })).toBe(
|
|
72
|
+
false
|
|
73
|
+
);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe('writeRequires', () => {
|
|
79
|
+
describe('when there is a story glob', () => {
|
|
80
|
+
it('writes the story imports', () => {
|
|
81
|
+
writeRequires({ configPath: 'scripts/mocks/all-config-files' });
|
|
82
|
+
expect(pathMock).toEqual(
|
|
83
|
+
path.resolve(__dirname, 'mocks/all-config-files/storybook.requires.js')
|
|
84
|
+
);
|
|
85
|
+
expect(fileContentMock).toMatchSnapshot();
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe('when there is a story glob and exclude paths globs', () => {
|
|
90
|
+
it('writes the story imports', () => {
|
|
91
|
+
writeRequires({ configPath: 'scripts/mocks/exclude-config-files' });
|
|
92
|
+
expect(pathMock).toEqual(
|
|
93
|
+
path.resolve(__dirname, 'mocks/exclude-config-files/storybook.requires.js')
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
expect(fileContentMock).toContain('include-components/FakeStory.stories.tsx');
|
|
97
|
+
expect(fileContentMock).not.toContain('exclude-components/FakeStory.stories.tsx');
|
|
98
|
+
|
|
99
|
+
expect(fileContentMock).toMatchSnapshot();
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe('when there is no story glob or addons', () => {
|
|
104
|
+
it('writes no story imports or addons', () => {
|
|
105
|
+
writeRequires({ configPath: 'scripts/mocks/blank-config' });
|
|
106
|
+
expect(pathMock).toEqual(
|
|
107
|
+
path.resolve(__dirname, 'mocks/blank-config/storybook.requires.js')
|
|
108
|
+
);
|
|
109
|
+
expect(fileContentMock).toMatchSnapshot();
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe('when there is no preview', () => {
|
|
114
|
+
it('does not add preview related stuff', () => {
|
|
115
|
+
writeRequires({ configPath: 'scripts/mocks/no-preview' });
|
|
116
|
+
expect(pathMock).toEqual(path.resolve(__dirname, 'mocks/no-preview/storybook.requires.js'));
|
|
117
|
+
expect(fileContentMock).toMatchSnapshot();
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
describe('when the absolute option is true', () => {
|
|
122
|
+
it('should write absolute paths to the requires file', () => {
|
|
123
|
+
writeRequires({ configPath: 'scripts/mocks/all-config-files', absolute: true });
|
|
124
|
+
expect(pathMock).toEqual(
|
|
125
|
+
path.resolve(__dirname, 'mocks/all-config-files/storybook.requires.js')
|
|
126
|
+
);
|
|
127
|
+
expect(fileContentMock).toContain(
|
|
128
|
+
path.resolve(__dirname, 'mocks/all-config-files/FakeStory.stories.tsx')
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const FakeComponent = () => null;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View, StyleSheet } from 'react-native';
|
|
3
|
+
import { withBackgrounds } from '@storybook/addon-ondevice-backgrounds';
|
|
4
|
+
|
|
5
|
+
export const decorators = [
|
|
6
|
+
(StoryFn) => (
|
|
7
|
+
<View style={styles.container}>
|
|
8
|
+
<StoryFn />
|
|
9
|
+
</View>
|
|
10
|
+
),
|
|
11
|
+
withBackgrounds,
|
|
12
|
+
];
|
|
13
|
+
export const parameters = {
|
|
14
|
+
my_param: 'anything',
|
|
15
|
+
backgrounds: [
|
|
16
|
+
{ name: 'plain', value: 'white', default: true },
|
|
17
|
+
{ name: 'warm', value: 'hotpink' },
|
|
18
|
+
{ name: 'cool', value: 'deepskyblue' },
|
|
19
|
+
],
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const styles = StyleSheet.create({
|
|
23
|
+
container: { padding: 8, flex: 1 },
|
|
24
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const FakeComponent = () => null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const FakeComponent = () => null;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
stories: ['**/*.stories.tsx'],
|
|
3
|
+
reactNativeOptions: {
|
|
4
|
+
excludePaths: '**/exclude-components/**',
|
|
5
|
+
},
|
|
6
|
+
addons: [
|
|
7
|
+
'@storybook/addon-ondevice-notes',
|
|
8
|
+
'@storybook/addon-ondevice-controls',
|
|
9
|
+
'@storybook/addon-ondevice-backgrounds',
|
|
10
|
+
'@storybook/addon-ondevice-actions',
|
|
11
|
+
],
|
|
12
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { View, StyleSheet } from 'react-native';
|
|
3
|
+
import { withBackgrounds } from '@storybook/addon-ondevice-backgrounds';
|
|
4
|
+
|
|
5
|
+
export const decorators = [
|
|
6
|
+
(StoryFn) => (
|
|
7
|
+
<View style={styles.container}>
|
|
8
|
+
<StoryFn />
|
|
9
|
+
</View>
|
|
10
|
+
),
|
|
11
|
+
withBackgrounds,
|
|
12
|
+
];
|
|
13
|
+
export const parameters = {
|
|
14
|
+
my_param: 'anything',
|
|
15
|
+
backgrounds: [
|
|
16
|
+
{ name: 'plain', value: 'white', default: true },
|
|
17
|
+
{ name: 'warm', value: 'hotpink' },
|
|
18
|
+
{ name: 'cool', value: 'deepskyblue' },
|
|
19
|
+
],
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const styles = StyleSheet.create({
|
|
23
|
+
container: { padding: 8, flex: 1 },
|
|
24
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const FakeComponent = () => null;
|
package/scripts/watcher.js
CHANGED
|
@@ -1,29 +1,62 @@
|
|
|
1
1
|
const chokidar = require('chokidar');
|
|
2
2
|
const path = require('path');
|
|
3
|
-
const {
|
|
4
|
-
getGlobs,
|
|
5
|
-
writeRequires,
|
|
6
|
-
getMainPath,
|
|
7
|
-
getPreviewPath,
|
|
8
|
-
getPreviewExists,
|
|
9
|
-
} = require('./loader');
|
|
10
3
|
|
|
4
|
+
const { writeRequires, getMain, getPreviewExists } = require('./loader');
|
|
5
|
+
|
|
6
|
+
const { getArguments } = require('./handle-args');
|
|
7
|
+
|
|
8
|
+
const args = getArguments();
|
|
11
9
|
const log = console.log.bind(console);
|
|
12
10
|
|
|
13
|
-
const watchPaths = [
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
const watchPaths = ['./main.js'];
|
|
12
|
+
|
|
13
|
+
if (getPreviewExists(args)) {
|
|
14
|
+
watchPaths.push('./preview.js');
|
|
16
15
|
}
|
|
17
16
|
|
|
18
17
|
const updateRequires = (event, watchPath) => {
|
|
19
18
|
if (typeof watchPath === 'string') {
|
|
20
19
|
log(`event ${event} for file ${path.basename(watchPath)}`);
|
|
21
20
|
}
|
|
22
|
-
writeRequires();
|
|
21
|
+
writeRequires(args);
|
|
23
22
|
};
|
|
24
23
|
|
|
25
|
-
|
|
24
|
+
const globs = getMain(args).stories;
|
|
25
|
+
|
|
26
|
+
chokidar
|
|
27
|
+
.watch(watchPaths, { cwd: args.configPath })
|
|
28
|
+
.on('change', (watchPath) => updateRequires('change', watchPath));
|
|
29
|
+
|
|
30
|
+
let isReady = false;
|
|
31
|
+
|
|
26
32
|
chokidar
|
|
27
|
-
.watch(
|
|
28
|
-
.on('
|
|
29
|
-
|
|
33
|
+
.watch(globs, { cwd: args.configPath })
|
|
34
|
+
.on('ready', () => {
|
|
35
|
+
log('Watcher is ready, performing initial write');
|
|
36
|
+
writeRequires(args);
|
|
37
|
+
log('Waiting for changes, press r to manually re-write');
|
|
38
|
+
isReady = true;
|
|
39
|
+
})
|
|
40
|
+
.on('add', (watchPath) => {
|
|
41
|
+
if (isReady) {
|
|
42
|
+
updateRequires('add', watchPath);
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
.on('unlink', (watchPath) => {
|
|
46
|
+
if (isReady) {
|
|
47
|
+
updateRequires('unlink', watchPath);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const readline = require('readline');
|
|
52
|
+
readline.emitKeypressEvents(process.stdin);
|
|
53
|
+
process.stdin.setRawMode(true);
|
|
54
|
+
process.stdin.on('keypress', (str, key) => {
|
|
55
|
+
if (key.ctrl && key.name === 'c') {
|
|
56
|
+
process.exit();
|
|
57
|
+
}
|
|
58
|
+
if (key.name === 'r') {
|
|
59
|
+
log('Detected "r" keypress, rewriting story imports...');
|
|
60
|
+
writeRequires(args);
|
|
61
|
+
}
|
|
62
|
+
});
|