frodare / addon-redux Goto Github PK
View Code? Open in Web Editor NEWStorybook addon that aids in running redux backed components in your stories.
License: MIT License
Storybook addon that aids in running redux backed components in your stories.
License: MIT License
If i want to display multiple stories with addon-docs, i need to create multiple stores.
BUT "redux state" panel action send to FIRST store.
So, i cannot use "redux state" panel.
withRedux.ts
if (!initialized) {
channel.on(events.SET_STATE, state => store.dispatch(setStateAction(state)))
channel.on(events.DISPATCH, action => store.dispatch(action))
}
initialized = true
I am trying to setup addon but store will be correct on first story but as soon as I switch story I get an empty object as store and have to refresh. Changing values on first page works so the store is hooked up correctly but I guess something about the decorator is not.
I am not using same import system:
module.exports = {
stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/preset-create-react-app",
"@chakra-ui/storybook-addon",
"addon-redux",
],
framework: "@storybook/react",
core: {
builder: "webpack5",
},
};
preview.js
import { Provider } from "react-redux";
import { addDecorator } from "@storybook/react";
import { store } from "../src/app/store";
export const decorators = [
(storyFn) => <Provider store={store}>{storyFn()}</Provider>,
];
And the store.ts file (redux toolkit)
import { configureStore, ThunkAction, Action } from "@reduxjs/toolkit";
// import counterReducer from '../features/counter/counterSlice';
import { enhancer } from "addon-redux";
import gameBoardReducer from "../features/GamePage/gameBoardSlice";
export const store = configureStore({
reducer: {
gameBoard: gameBoardReducer,
},
enhancers: [enhancer],
});
export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof store.getState>;
export type AppThunk<ReturnType = void> = ThunkAction<
ReturnType,
RootState,
unknown,
Action<string>
>;
I can even change the store using webextension and that change is visible on all pages (so not cleared) when changing story pages. However the addon bar will just stay "select node" after the first page.
Edit: I don't know if it is a refresh problem of sorts. I tried adding "arg" to a story. Changing the story from that and the"Redux store" tab will refresh and work again (but will reset to empty object if switching story again).
Maybe a redux toolkit problem and manipulating the state outside of the slices? Anyone got react toolkit to work correctly with this?
From the redux developer toolkit the state seem to be correct but still.
I assume this was set during testing and was accidentally committed?
https://github.com/frodare/addon-redux/blob/master/src/redux/enhancer.ts#L28
If this isn't a mistake, probably there should be a comment explaining it
Redux Toolkit exposes its own utility function for creating stores called configureStore. When using this function, you need to pass all enhancers in as an array.
The type that Redux Tookit expects is StoreEnhancer[] | (StoreEnhancer[] => StoreEnhancer[])
This worked well with the v1 version of addon-redux. However, the type of withReduxEnhancer
in v2 now uses
(createStore: StoreCreator) => (reducer: Reducer, state: State, enhancer: StoreEnhancer)
which isn't compatible with Redux Toolkit.
Would be great to see an example of how to setup this library with Redux Toolkit given how prevalent that library is.
warning.js:33 Warning: Failed prop type: Invalid prop date
of type string
supplied to StateChange
, expected object
.
in StateChange (created by HistoryPanel)
in HistoryPanel (created by lifecycle(HistoryPanel))
in lifecycle(HistoryPanel) (created by withHandlers(lifecycle(HistoryPanel)))
in withHandlers(lifecycle(HistoryPanel)) (created by withState(withHandlers(lifecycle(HistoryPanel))))
in withState(withHandlers(lifecycle(HistoryPanel))) (created by withState(withState(withHandlers(lifecycle(HistoryPanel)))))
in withState(withState(withHandlers(lifecycle(HistoryPanel)))) (created by withState(withState(withState(withHandlers(lifecycle(HistoryPanel))))))
in withState(withState(withState(withHandlers(lifecycle(HistoryPanel))))) (created by withState(withState(withState(withState(withHandlers(lifecycle(HistoryPanel)))))))
in withState(withState(withState(withState(withHandlers(lifecycle(HistoryPanel)))))) (created by AddonPanel)
in div (created by AddonPanel)
in div (created by AddonPanel)
in div (created by AddonPanel)
in AddonPanel (created by Container(AddonPanel))
in Container(AddonPanel) (created by Layout)
in div (created by Layout)
in div (created by Pane)
in Pane (created by SplitPane)
in div (created by SplitPane)
in SplitPane (created by Layout)
in div (created by Pane)
in Pane (created by SplitPane)
in div (created by SplitPane)
in SplitPane (created by Layout)
in div (created by Layout)
in Layout (created by Container(Layout))
in Container(Layout)
in div
I created a custom decorator as recommended... but the decorators are all invoked on Storybook load, therefore the story that occurs last overwrites the store for previous stories.
Is there a way to ensure each story has its own state in the Redux store? I don't see a way to do that at the moment, as the addon store appears to be a single instance, and Storybook does not reinitialize Redux on story change.
Thanks in advance.
I 've upgraded recently to v5 and started having some weird warnings and errors in console. I 've dropped addon-redux
and errors went away so probably some things need to be changed in order to support this. Do you have a clue what these might be? I could provide a helping hand if needed ๐
Right now we have two ways of injecting data into Redux
PARAM_REDUX_MERGE_STATE
(can only be static data)ARG_REDUX_PATH
(inject the value of a control to a specific path)Using ARG_REDUX_PATH
works well when your Storybook control maps directly to a value in your Redux store, but it doesn't allow any way to do dependent state
ex: imagine you have a single control that needs to modify multiple parts of your Redux store.
One simple solution would be to allow ARG_REDUX_PATH
to be a function that takes the current value of the control as its argument and then returns a subset of the Redux state to override (similar to PARAM_REDUX_MERGE_STATE
)
theme: {
control: { type: 'boolean' },
[ARG_REDUX_PATH]: isMobile => ({ isMobile, supportsBluetooth: isMobile })
},
However I feel this isn't a great solution because it just pushes the problem down one layer -- what happens if you need to set Redux state based off the combination of multiple Storybook controls?
You could solve this by making that the lambda takes a more complicated argument like
Main.argTypes = {
allowedBluetoothPermission: {
control: { type: 'boolean' },
},
theme: {
control: { type: 'boolean' },
[ARG_REDUX_PATH]: (isMobile, args) => ({ isMobile, supportsBluetooth: isMobile && args.allowedBluetoothPermission })
},
}
I think #14 is the same issue, but I think this was closed even though the issue wasn't fixed.
All stories share a single redux store. That means that if you change the Redux store on one story (say, you use ARG_REDUX_PATH
or PARAM_REDUX_MERGE_STATE
), the state of the store isn't reset by going to a different story.
Probably the solution is to make switching stories dispatch a RESET action that sets the state to undefined
at the root level (setting state to undefined makes Redux reset to the initial state).
In fact, there already exists a very short library that does exactly this (https://github.com/wwayne/redux-reset) but they have some extra complexity to it
Version: 2.0.5
When I interact with the story in any way (even just clicking on empty space) triggers the following error. I don't have any non-serializable types in my Redux state, so not sure what could be causing this.
The error seems to come from the HistoryView calling JSON.parse(diff)
when diff is undefined
. Probably there needs to be code added to handle an undefined diff (or maybe this is never suppose to happen?)
VM749965:1 Uncaught SyntaxError: Unexpected token u in JSON at position 0
at JSON.parse (<anonymous>)
at Row (vendors~main.manager.bundle.js:28869)
at renderWithHooks (vendors~main.manager.bundle.js:91442)
at mountIndeterminateComponent (vendors~main.manager.bundle.js:94121)
at beginWork (vendors~main.manager.bundle.js:95235)
at HTMLUnknownElement.callCallback (vendors~main.manager.bundle.js:76827)
at Object.invokeGuardedCallbackDev (vendors~main.manager.bundle.js:76876)
at invokeGuardedCallback (vendors~main.manager.bundle.js:76931)
at beginWork$1 (vendors~main.manager.bundle.js:99842)
at performUnitOfWork (vendors~main.manager.bundle.js:98796)
vendors~main.manager.bundle.js:96166 The above error occurred in the <Row> component:
in Row (created by HistoryView)
in tbody (created by HistoryView)
in table (created by Context.Consumer)
in Styled(table) (created by HistoryView)
in HistoryView
in div (created by AddonPanel)
in AddonPanel
in div (created by Context.Consumer)
in Styled(div)
in div (created by Context.Consumer)
in Styled(div)
in Unknown
in Unknown
in Unknown
in Unknown (created by ManagerConsumer)
in ManagerConsumer (created by Panel)
in Panel (created by Layout)
in div (created by Context.Consumer)
in Styled(div) (created by Panel)
in Panel (created by Layout)
in div (created by Context.Consumer)
in Styled(div) (created by Main)
in div (created by Context.Consumer)
in Styled(div) (created by Main)
in Main (created by Layout)
in Layout (created by Context.Consumer)
in WithTheme(Layout)
in Unknown
in div (created by Context.Consumer)
in Styled(div)
in Unknown
in Unknown (created by SizeMeRenderer(Component))
in SizeMeReferenceWrapper (created by SizeMeRenderer(Component))
in SizeMeRenderer(Component) (created by SizeMe(Component))
in SizeMe(Component)
in ThemeProvider
in Unknown (created by ManagerConsumer)
in ManagerConsumer (created by Manager)
in EffectOnMount (created by Manager)
in Manager (created by Context.Consumer)
in Location (created by QueryLocation)
in QueryLocation (created by Root)
in LocationProvider (created by Root)
in HelmetProvider (created by Root)
in Root
React will try to recreate this component tree from scratch using the error boundary you provided, LocationProvider
vendors~main.manager.bundle.js:15740 manager received storybook/addon-redux/on_dispatch but was unable to determine the source of the event
This addon works correctly on versions:
"react-redux": "^5.1.1",
"redux": "^3.5.2",
But, on versions:
"react-redux": "^6.0.0",
"redux": "^4.0.1",
I receive error:
Invariant Violation: Could not find "store" in the context of "Connect(Container)". Either wrap the root component in a <Provider>, or pass a custom React context provider to <Provider> and the corresponding React context consumer to Connect(Container) in connect options.
So I noticed that the version you get downloading 2.0.11 from npm does not seem to be matching that tag here.
It can be seen by
#32
should be included in it but it is not. Or I am missing something.
Firstly thank you for putting together this storybook addon.
I've recently upgraded to Storybook 7 and React 18.
The package.json of this package specify the following peer deps
"@storybook/addons": "^6.2.9",
"@storybook/api": "^6.2.9",
"@storybook/components": "^6.2.9",
"@storybook/core-events": "^6.2.9",
"@storybook/theming": "^6.2.9",
"react": "^16.8.0 || ^17.0.0",
"react-dom": "^16.8.0 || ^17.0.0",
NPM generates warnings when trying to install because of the above.
Could this package be upgraded to support React 18 and Storybook 7 please.
warning.js:33 Warning: Each child in an array or iterator should have a unique "key" prop.
Check the render method of ViewMode
. See https://fb.me/react-warning-keys for more information.
in button (created by ViewMode)
in ViewMode (created by StatePanel)
in div (created by StatePanel)
in StatePanel (created by lifecycle(StatePanel))
in lifecycle(StatePanel) (created by withHandlers(lifecycle(StatePanel)))
in withHandlers(lifecycle(StatePanel)) (created by withState(withHandlers(lifecycle(StatePanel))))
in withState(withHandlers(lifecycle(StatePanel))) (created by withState(withState(withHandlers(lifecycle(StatePanel)))))
in withState(withState(withHandlers(lifecycle(StatePanel)))) (created by withState(withState(withState(withHandlers(lifecycle(StatePanel))))))
in withState(withState(withState(withHandlers(lifecycle(StatePanel))))) (created by withState(withState(withState(withState(withHandlers(lifecycle(StatePanel)))))))
in withState(withState(withState(withState(withHandlers(lifecycle(StatePanel)))))) (created by AddonPanel)
in div (created by AddonPanel)
in div (created by AddonPanel)
in div (created by AddonPanel)
in AddonPanel (created by Container(AddonPanel))
in Container(AddonPanel) (created by Layout)
in div (created by Layout)
in div (created by Pane)
in Pane (created by SplitPane)
in div (created by SplitPane)
in SplitPane (created by Layout)
in div (created by Pane)
in Pane (created by SplitPane)
in div (created by SplitPane)
in SplitPane (created by Layout)
in div (created by Layout)
in Layout (created by Container(Layout))
in Container(Layout)
in div
Hi,
I am in need of some help, I've been trying to figure this out for months but can't seem to find any answer or clue to what is going on.
When I am adding the decorator this is the state I am seeing. I can't figure out why this is happening.
I am seeing this in version 1.0.0 and 1.1.0.
my state appears to be withing the computedStates array.
The setup
.storybook/addons.js
.storybook/preview.js
Could anyone please advise and save my sanity?
Hello, everyone. It's great to have an addon for redux in storybooks. I tried to used it to develop some components, still I have following warning in chrome dev instruments console after installing addon-redux (v1.0.0):
I also run the latest stable storybook:
"@storybook/addon-actions": "^5.1.3",
"@storybook/addon-links": "^5.1.3",
"@storybook/cli": "5.1.3",
"@storybook/react": "5.1.3",
"storybook": "1.0.0",
"react": "^16.5.2"
I will play around of this problem and let you know if something may fix it.
Probably due to be registered multiple times and not cleared
I get this warning only when I run storybook using this addon:
You (or an addon) are using the 'config' preset field. This has been replaced by 'previewAnnotations' and will be removed in 8.0
Currently PARAM_REDUX_MERGE_STATE
needs to be a string object (will crash trying to JSON.parse an object if you pass an object in instead)
Given that in Redux, best practice is to only ever use serializable data I feel like a JS object should be allowed as a value here instead of a string. Internally, you could use the typeof
of the variable to decide to call JSON.stingify on the object is used instead if you need that for some reason
This library provides a way to override the default Redux state by setting the PARAM_REDUX_MERGE_STATE
parameter for the story.
This works for basic usage, but it means nothing in your Redux state can leverage controls (the recommended way to provide knobs in Storybook as of Storybook v6)
An example of why this would be useful is I would like all my stories to have a control for the language used, so it has to be part of the component "args". However, "selectedLanguage" is also part of my Redux state so I would like to inject the arg value using this addon.
I asked the Storybook team and they said this is not solvable using parameters as they are meant to be static. This is the solution they proposed:
i think a lot of the issue is that many addons were written before args/globals existed, and we haven't been prescriptive enough to get addon authors to use them as intended.
parameters are great for configuring things but if you want to support dynamic data, you need to use args/globals and many addons use parameters because that was the only configuration mechanism until 6.0 and all of the existing addons use them
Globals doesn't sound like the right solution since this is a per-story config, so probably args need to be used instead.
There seems to be a related discussion here.
Supposed you have the following custom middle ware that simply logs any action that happens
const customMiddleWare = store => {
console.log('store');
return next => {
console.log('next');
return action => {
console.log(`Middleware ran ${JSON.stringify(action)}`);
next(action);
}
}
};
const createMiddlewareEnhancer = () => {
const middleware = []
middleware.push(customMiddleWare);
return applyMiddleware(...middleware)
}
If you call
store.dispatch({
type: 'asdf'
})
It will properly get logged in the custom middleware.
However, if you dispatch an action from a React component while running Storybook with addon-redux,
It will instead print next
but never print the action.
If you're using redux-thunk, your React UI made be triggering actions where the action is a function instead of an object.
Since addon-redux ignores middlewares, the redux-thunk middleware gets ignore and then an error is thrown that Redux found an action that is a function.
See: addon-redux/src/redux/enhancer.ts
const enhanceDispatch: Enhancer<Dispatcher> = dispatch => action => {
const prev = store.getState()
dispatch(action)
const next = store.getState()
if (listener !== null) listener(action, prev, next)
}
This will break for scenarios where the value of the dispatch is read.
In my case when working with redux toolkit - query mutations.
Adding a return statement like below fixed it for me.
const enhanceDispatch: Enhancer<Dispatcher> = dispatch => action => {
const prev = store.getState()
const result = dispatch(action)
const next = store.getState()
if (listener !== null) listener(action, prev, next)
return result;
}
Regards
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.