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

feat: support for storybook 6.4/6.5, CSF3 and HMR fix #384

Merged
merged 28 commits into from
Nov 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 5 additions & 5 deletions addons/ondevice-actions/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@storybook/addon-ondevice-actions",
"version": "6.0.1-beta.8",
"version": "6.0.1-canary.3",
"description": "Action Logger addon for react-native storybook",
"keywords": [
"storybook"
Expand All @@ -26,15 +26,15 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "~6.3",
"@storybook/core-events": "~6.3",
"@storybook/addons": "^6.5",
"@storybook/core-events": "^6.5",
"fast-deep-equal": "^2.0.1"
},
"devDependencies": {
"@storybook/addon-actions": "~6.3"
"@storybook/addon-actions": "^6.5"
},
"peerDependencies": {
"@storybook/addon-actions": "~6.3",
"@storybook/addon-actions": "^6.5",
"react": "*",
"react-native": "*"
},
Expand Down
8 changes: 4 additions & 4 deletions addons/ondevice-backgrounds/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@storybook/addon-ondevice-backgrounds",
"version": "6.0.1-beta.8",
"version": "6.0.1-canary.3",
"description": "A react-native storybook addon to show different backgrounds for your preview",
"keywords": [
"addon",
Expand Down Expand Up @@ -31,9 +31,9 @@
"prepare": "node ../../scripts/prepare.js"
},
"dependencies": {
"@storybook/addons": "~6.3",
"@storybook/api": "~6.3",
"@storybook/client-api": "~6.3",
"@storybook/addons": "^6.5",
"@storybook/api": "^6.5",
"@storybook/client-api": "^6.5",
"core-js": "^3.0.1",
"prop-types": "^15.7.2"
},
Expand Down
2 changes: 1 addition & 1 deletion addons/ondevice-backgrounds/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const withBackgrounds = makeDecorator({

return (
<Container initialBackground={background} channel={addons.getChannel()}>
{getStory(context)}
{getStory(context) as React.ReactNode}
</Container>
);
},
Expand Down
12 changes: 6 additions & 6 deletions addons/ondevice-controls/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@storybook/addon-ondevice-controls",
"version": "6.0.1-beta.8",
"version": "6.0.1-canary.3",
"description": "Display storybook controls on your device.",
"keywords": [
"addon",
Expand Down Expand Up @@ -30,9 +30,9 @@
},
"dependencies": {
"@emotion/native": "^10.0.14",
"@storybook/addons": "~6.3",
"@storybook/client-logger": "~6.3",
"@storybook/core-events": "~6.3",
"@storybook/addons": "^6.5",
"@storybook/client-logger": "^6.5",
"@storybook/core-events": "^6.5",
"core-js": "^3.0.1",
"deep-equal": "^1.0.1",
"prop-types": "^15.7.2",
Expand All @@ -41,13 +41,13 @@
"tinycolor2": "^1.4.1"
},
"devDependencies": {
"@storybook/addon-knobs": "~6.3",
"@storybook/addon-controls": "^6.5",
"@types/react-native": "^0.70.4"
},
"peerDependencies": {
"@react-native-community/datetimepicker": "*",
"@react-native-community/slider": "*",
"@storybook/addon-controls": "~6.3",
"@storybook/addon-controls": "^6.5",
"react": "*",
"react-native": "*"
},
Expand Down
26 changes: 4 additions & 22 deletions addons/ondevice-controls/src/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,11 @@
import { Args } from '@storybook/addons';
import { useState, useEffect, useCallback } from 'react';
import Events, { FORCE_RE_RENDER } from '@storybook/core-events';
import useDebouncedCallback from './useDebounceCallback';
import Events from '@storybook/core-events';

export const useArgs = (
storyId: string,
storyStore: any
): [Args, (args: Args) => void, (argNames?: string[]) => void] => {
// TODO: remove need to force re-render
const debouncedReRender = useDebouncedCallback(
() => storyStore._channel.emit(FORCE_RE_RENDER),
100
);

useEffect(() => {
return () => {
debouncedReRender.cancel();
};
}, [debouncedReRender]);

const story = storyStore.fromId(storyId);
if (!story) {
throw new Error(`Unknown story: ${storyId}`);
Expand All @@ -38,18 +25,13 @@ export const useArgs = (

const updateArgs = useCallback(
(newArgs) => {
storyStore.updateStoryArgs(storyId, newArgs);

//TODO: remove this if possible
debouncedReRender();
storyStore._channel.emit(Events.UPDATE_STORY_ARGS, { storyId, updatedArgs: newArgs });
},
[debouncedReRender, storyId, storyStore]
[storyId, storyStore]
);
const resetArgs = useCallback(
(argNames?: string[]) => {
storyStore.resetStoryArgs(storyId, argNames);
//TODO: remove this if possible
storyStore._channel.emit(FORCE_RE_RENDER);
storyStore._channel.emit(Events.RESET_STORY_ARGS, { storyId, argNames });
},
[storyId, storyStore]
);
Expand Down
29 changes: 20 additions & 9 deletions addons/ondevice-controls/src/types/Array.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import styled from '@emotion/native';
import { useResyncValue } from './useResyncValue';

const Input = styled.TextInput(({ theme }) => ({
borderWidth: 1,
Expand All @@ -25,20 +26,30 @@ export interface ArrayProps {
separator: string;
};
onChange: (value: string[]) => void;
isPristine: boolean;
}

const ArrayType = ({
arg: { name, value, separator = ',' },
onChange = () => null,
}: ArrayProps) => (
<Input
testID={name}
underlineColorAndroid="transparent"
autoCapitalize="none"
defaultValue={value.join(separator)}
onChangeText={(e) => onChange(formatArray(e, separator))}
/>
);
isPristine,
}: ArrayProps) => {
const { setCurrentValue, key } = useResyncValue(value, isPristine);
return (
<Input
key={key}
testID={name}
underlineColorAndroid="transparent"
autoCapitalize="none"
defaultValue={value.join(separator)}
onChangeText={(text) => {
const formatted = formatArray(text, separator);
onChange(formatted);
setCurrentValue(formatted);
}}
/>
);
};

ArrayType.serialize = (value) => value;

Expand Down
25 changes: 13 additions & 12 deletions addons/ondevice-controls/src/types/Number.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import styled from '@emotion/native';
import Slider from '@react-native-community/slider';
import React, { useEffect, useState } from 'react';
import React, { useCallback, useState } from 'react';
import { Platform, StyleSheet, View } from 'react-native';
import { useResyncValue } from './useResyncValue';

const Input = styled.TextInput(({ theme }) => ({
borderWidth: 1,
Expand All @@ -25,7 +26,7 @@ const ValueLabelText = styled.Text(({ theme }) => ({
}));

const ValueContainer = styled.View({ flexDirection: 'row' });

// @ts-ignore styled is being weird ;(
const NumberSlider = styled(Slider)(() => ({
marginTop: Platform.OS === 'android' ? 8 : 4,
marginLeft: Platform.OS === 'android' ? -10 : 0,
Expand All @@ -49,25 +50,22 @@ export interface NumberProps {
const NumberType = ({ arg, isPristine, onChange = (value) => value }: NumberProps) => {
const showError = Number.isNaN(arg.value);
const [numStr, setNumStr] = useState(arg.value.toString());
const updateNumstr = useCallback((value) => setNumStr(value.toString()), []);
const { key, setCurrentValue } = useResyncValue(arg.value, isPristine, updateNumstr);

const handleNormalChangeText = (text: string) => {
const commaReplaced = text.trim().replace(/,/, '.');

setNumStr(commaReplaced);
if (commaReplaced === '-') {
onChange(-1);
setCurrentValue(-1);
} else {
onChange(Number(commaReplaced));
setCurrentValue(Number(commaReplaced));
}
};

// handle arg.value and numStr out of sync issue on reset
useEffect(() => {
if (isPristine) {
setNumStr(arg.value.toString());
}
}, [isPristine, arg.value]);

const renderNormal = () => {
return (
<Input
Expand All @@ -83,7 +81,7 @@ const NumberType = ({ arg, isPristine, onChange = (value) => value }: NumberProp

const renderRange = (): React.ReactNode => {
return (
<>
<View key={key}>
<ValueContainer>
<ValueLabelText>Value:</ValueLabelText>
<SliderText>{arg.value}</SliderText>
Expand All @@ -92,9 +90,12 @@ const NumberType = ({ arg, isPristine, onChange = (value) => value }: NumberProp
minimumValue={arg.min}
maximumValue={arg.max}
step={arg.step}
onValueChange={(val) => onChange(val)}
onValueChange={(val) => {
onChange(val);
setCurrentValue(val);
}}
/>
</>
</View>
);
};

Expand Down
7 changes: 6 additions & 1 deletion addons/ondevice-controls/src/types/Object.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import React, { useCallback, useState } from 'react';
import styled from '@emotion/native';
import { ViewStyle } from 'react-native';
import { useResyncValue } from './useResyncValue';

export interface ObjectProps {
arg: {
name: string;
value: Record<string, any> | Array<any>;
};
onChange: (value: any) => void;
isPristine: boolean;
}

const Input = styled.TextInput(({ theme }) => ({
Expand All @@ -21,7 +23,7 @@ const Input = styled.TextInput(({ theme }) => ({
minHeight: 60,
}));

const ObjectType = ({ arg, onChange }: ObjectProps) => {
const ObjectType = ({ arg, onChange, isPristine }: ObjectProps) => {
const getJsonString = useCallback(() => {
try {
return JSON.stringify(arg.value, null, 2);
Expand All @@ -31,6 +33,7 @@ const ObjectType = ({ arg, onChange }: ObjectProps) => {
}, [arg.value]);

const [failed, setFailed] = useState(false);
const { key, setCurrentValue } = useResyncValue(arg.value, isPristine);

const handleChange = (value) => {
const withReplacedQuotes = value
Expand All @@ -42,6 +45,7 @@ const ObjectType = ({ arg, onChange }: ObjectProps) => {
const json = JSON.parse(withReplacedQuotes.trim());

onChange(json);
setCurrentValue(json);
setFailed(false);
} catch (err) {
setFailed(true);
Expand All @@ -58,6 +62,7 @@ const ObjectType = ({ arg, onChange }: ObjectProps) => {

return (
<Input
key={key}
testID={arg.name}
style={extraStyle}
defaultValue={getJsonString()}
Expand Down
1 change: 1 addition & 0 deletions addons/ondevice-controls/src/types/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const Container = styled.View(({ theme }) => ({
borderColor: theme.borderColor || '#e6e6e6',
}));

// @ts-ignore styled is being weird ;(
const WebSelect = styled(Select)(({ theme }) => ({
border: 'none',
color: theme.labelColor || 'black',
Expand Down
11 changes: 9 additions & 2 deletions addons/ondevice-controls/src/types/Text.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import styled from '@emotion/native';
import { useResyncValue } from './useResyncValue';

export interface TextProps {
arg: {
Expand All @@ -8,6 +9,7 @@ export interface TextProps {
type: string;
};
onChange: (value: any) => void;
isPristine: boolean;
}

const Input = styled.TextInput(({ theme }) => ({
Expand All @@ -20,12 +22,17 @@ const Input = styled.TextInput(({ theme }) => ({
color: theme.labelColor || 'black',
}));

const TextType = ({ arg, onChange }: TextProps) => {
const TextType = ({ arg, onChange, isPristine }: TextProps) => {
const { setCurrentValue, key } = useResyncValue(arg.value, isPristine);
return (
<Input
key={key}
testID={arg.name}
defaultValue={arg.value}
onChangeText={onChange}
onChangeText={(text) => {
onChange(text);
setCurrentValue(text);
}}
autoCapitalize="none"
underlineColorAndroid="transparent"
/>
Expand Down
20 changes: 20 additions & 0 deletions addons/ondevice-controls/src/types/useResyncValue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { useEffect, useState } from 'react';

// syncs up the value of a control with the value of a story arg when resetting
// this is used for controls that don't use a controlled input like the slider
export function useResyncValue(
value: any,
isPristine: boolean,
resyncCallback?: (syncValue: any) => void
) {
const [key, setKey] = useState(0);
const [currentValue, setCurrentValue] = useState(value);

useEffect(() => {
if (isPristine && value !== currentValue) {
setKey((cur) => cur + 1);
resyncCallback?.(value);
}
}, [value, currentValue, isPristine, resyncCallback]);
return { key, setCurrentValue };
}