Import .stories file into another .stories file with Storybook? - storybook

Can you import one .stories file into another .stories with Storybook?
Eg I have
/component1/component1.tsx
/component1/component1.stories.tsx
/component2/component2.tsx
/component2/component2.stories.tsx
I would like to also have a story for all of my components:
In /all-components/all-components.stories.tsx
import * as React from 'react';
import Component1Story from '../component1/component1.stories.tsx';
import Component2Story from '../component2/component2.stories.tsx';
export const Test = () => {
return (
<div>
<Component1Story />
<Component2Story />
</div>
);
};
export default {
title: 'Components',
};
I get this error:
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.
Check the render method of storyFn.

this should be doable as your stories are just React components. Your problem is happening because you're trying to import the default from your module, which is actually just an object:
export default {
title: 'Components',
};
All stories are named exports, and you should import them with destructuring:
import { Component1Story } from '../component1/component1.stories';
import { Component2Story } from '../component2/component2.stories';
I created an example for you which shows a working scenario here.
p.s. It's interesting to know that starting with Storybook 6 there's a new mechanism to simplify the creation and reuse of stories so stay tuned! It's called Args.

Related

nextjs " Failed to compile pages/_app.js " how to fix it [duplicate]

This question already has answers here:
Next.js Global CSS cannot be imported from files other than your Custom <App>
(11 answers)
Closed 1 year ago.
How to fix it, it happens when compiling
This is the official document for your problem from the Next.js team:
https://nextjs.org/docs/basic-features/built-in-css-support
You need to import global CSS in the _app.js file to use it globally in your application.
For example, use Tailwind CSS as global like this:
// your _app.js file
// import your CSS file you want to use globally here
import 'tailwindcss/tailwind.css';
const CustomApp = ({ Component, pageProps }) => (
<Component {...pageProps} />
);
export default CustomApp;
If you just want to use CSS for specific components, please use module CSS. It will be helpful to code splitting your bundle files when you build the production version.
Example to use modular CSS just only for your component:
import styles from './index.module.scss';
const YourComponent = () => {
return <div className={styles.example}>...</div>;
};
export default YourComponent;
For special cases that you need to import CSS files from the library inside your node_modules. You can import that CSS file from node_modules inside your specific component.
For example:
import { FC } from 'react';
import Slider from 'react-slick';
import 'react-slick/css/slick.css';
import 'react-slick/css/slick-theme.css';
const SlickSlider: FC<any> = ({ children, ...restProps }) => {
return <Slider {...restProps}>{children}</Slider>;
};
export default SlickSlider;

How to add a global decorator in Storybook

In ReactJs project you can use .storybook/preview.js file to add global decorators and parameters. How to achieve this same behaviour with #storybook/react-native?
What I need is to wrap all my stories with ThemeProvider but the unique way that I found is to wrap individual stories with .addDecorator().
Edit storybook/index.js, by using addDecorator on it.
Example:
import React from 'react'
import { getStorybookUI, configure, addDecorator } from '#storybook/react-native'
import Decorator from './Decorator'
addDecorator(storyFn => (
<Decorator>
{storyFn()}
</Decorator>
))
// import stories
configure(() => {
require('../stories')
}, module)
const StorybookUI = getStorybookUI({ onDeviceUI: true })
export default StorybookUI;;
Found an updated answer in Storybook's own documentation.
// .storybook/preview.js
import React from 'react';
export const decorators = [
(Story) => (
<div style={{ margin: '3em' }}>
<Story />
</div>
),
];
As of June 2021, using storybook v5.3.25, the above answer does not work. However I have managed to figure out a solution.
Decorators must be added to the storybook/index.js file in the following format:
import { ThemeDecorator } from './storybook/ThemeDecorator';
addDecorator(withKnobs); // inbuilt storybook addon decorator
addDecorator(ThemeDecorator);// custom decorator
configure(() => {
loadStories();
}, module);
in this instance, ThemeDecorator.js is a simple wrapper component that renders your story, it would look something like this:
import React from 'react';
import { Provider } from 'theme-provider';
export const ThemeDecorator = (getStory) => (
<Provider>{getStory()}</Provider>
);
Importantly, the addDecorator function expects a React component (not a wrapper function as other examples claim), that it will render, with its props being a reference to an individual story at runtime.

Dynamically load .css based on condition in reactJS application

I have a reactJS application that I want to make available to multiple clients. Each clients has unique color schemes. I need to be able to import the .css file that corresponds to the specific client.
For example, if client 1 logs into the application, I want to import client1.css. if client 2 logs into the application, I want to import client2.css. I will know the client number once I have validated the login information.
The application contains multiple .js files. Every .js file contains the following at the top of the file
import React from 'react';
import { Redirect } from 'react-router-dom';
import {mqRequest} from '../functions/commonFunctions.js';
import '../styles/app.css';
Is there a way to import .css files dynamically for this scenario as opposed to specifying the .css file in the above import statement?
Thank you
Easy - i've delt with similar before.
componentWillMount() {
if(this.props.css1 === true) {
require('style1.css');
} else {
require('style2.css');
}
}
Consider using a cssInJs solution. Popular libraries are: emotion and styled-components but there are others as well.
I generally recommend a cssInJs solution, but for what you are trying to do it is especially useful.
In Emotion for example they have a tool specifically build for this purpose - the contextTheme.
What cssInJs basically means is that instead of using different static css files, use all the power of Javascript, to generate the needed css rules from your javascript code.
A bit late to the party, I want to expand on #Harmenx answer.
require works in development environments only, once it goes to production you're likely to get errors or not see the css file at all. Here are some options if you, or others, encounter this:
Option 1: Using css modules, assign a variable of styles with the response from the import based on the condition.
let styles;
if(this.props.css1 === true) {
//require('style1.css');
import("./style1.module.css").then((res) => { styles = res;});
} else {
//require('style2.css');
import("./style2.module.css").then((res) => { styles = res;});
}
...
<div className={styles.divClass}>...</div>
...
Option 2: using Suspend and lazy load from react
// STEP 1: create components for each stylesheet
// styles1.js
import React from "react";
import "./styles1.css";
export const Style1Variables = (React.FC = () => <></>);
export default Style1Variables ;
// styles2.js
import React from "react";
import "./styles2.css";
export const Style2Variables = (React.FC = () => <></>);
export default Style2Variables ;
// STEP 2: setup your conditional rendering component
import React, {lazy, Suspense} from "react";
const Styles1= lazy(() => import("./styles1"));
const Styles2= lazy(() => import("./styles2"));
export const ThemeSelector = ({ children }) => {
return (
<>
<Suspense fallback={null} />}>
{isClient1() ? <Styles1 /> : <Styles2/>}
</Suspense>
{children}
</>
);
};
// STEP 3: Wrap your app
ReactDOM.render(
<ThemeSelector>
<App />
</ThemeSelector>,
document.getElementById('root')
);
Option 3: Use React Helm which will include a link to the stylesheet in the header based on a conditional
class App extends Component {
render() {
<>
<Helmet>
<link
type="text/css"
rel="stylesheet"
href={isClient1() ? "./styles1.css" : "./styles2.css"}
/>
</Helmet>
...
</>
}
}
Personally, I like option 2 because you can set a variable whichClientIsThis() then modify the code to:
import React, {lazy, Suspense} from "react";
let clientID = whichClientIsThis();
const Styles= lazy(() => import("./`${clientID}`.css")); // change your variable filenames to match the client id.
export const ThemeSelector = ({ children }) => {
return (
<>
<Suspense fallback={null} />}>
<Styles />
</Suspense>
{children}
</>
);
};
ReactDOM.render(
<ThemeSelector>
<App />
</ThemeSelector>,
document.getElementById('root')
);
This way you don't need any conditionals. I'd still recommend lazy loading and suspending so the app has time to get the id and make the "decision" on which stylesheet to bring in.

JssProvider in Material-UI isn't applying my custom production prefix to CSS

I've built a fairly simple React app based on create-react-app which uses the Material-UI for its interface components. It also depends on one of my own packages which also uses Material-UI (same version) for a couple of shared components.
Things were looking good locally until I ran a production build and deployed it. Some of the styles were behaving oddly, for example the Material-UI grid was much narrower than when running locally.
I did some reading and found a few instances of people discussing colliding class names under my scenario. This took me to some official Material-UI documentation which provides the following example code to use a custom class name prefix:
import JssProvider from 'react-jss/lib/JssProvider';
import { createGenerateClassName } from '#material-ui/core/styles';
const generateClassName = createGenerateClassName({
dangerouslyUseGlobalCSS: true,
productionPrefix: 'c',
});
function App() {
return (
<JssProvider generateClassName={generateClassName}>
...
</JssProvider>
);
}
export default App;
Before applying this fix when inspecting my production app's source code I could see the outermost DIV using the CSS class jss2 jss24.
After applying this fix my production app actually visually renders the same layout as my development version and so would appear to be fixed. However, examining the source shows the outermost DIV to have the class MuiGrid-container-2 MuiGrid-spacing-xs-8-24 which suggests to me something isn't right. I could leave it like this but it does mean I'm running with unoptimised code.
Am I doing something wrong here? Or is there an alternative resolution? I'm using current latest version of #material-ui/core (3.3.2) and the full contents of my App.js are:
import React, { Component } from 'react';
import { Provider } from "react-redux";
import { OidcProvider } from 'redux-oidc';
import JssProvider from 'react-jss/lib/JssProvider';
import Routes from './routes';
import store from './store';
import userManager from './utils/userManager';
import {
CustomUiTheme as Theme,
CustomUiLayout as Layout,
CustomUiSnackbar as Snackbar,
CustomUiModalAlert as Alert
} from 'custom-ui';
import Loading from './components/loading';
import { createGenerateClassName } from '#material-ui/core/styles';
const generateClassName = createGenerateClassName({
dangerouslyUseGlobalCSS: true,
productionPrefix: 'tw',
});
class App extends Component {
render() {
return (
<JssProvider generateClassName={generateClassName}>
<Provider store={store}>
<OidcProvider store={store} userManager={userManager}>
<Theme>
<Loading />
<Layout variant="xmas">
<Alert />
<Routes />
<Snackbar />
</Layout>
</Theme>
</OidcProvider>
</Provider>
</JssProvider>
);
}
}
export default App;

what is the proper way to use createContainer() in Meteor + Blaze + React?

i have a working component where i'm doing this:
import React, {Component, PropTypes} from 'react';
import {createContainer} from 'meteor/react-meteor-data';
export default class Foo extends Component {
}
export default createContainer(() => {
}, Foo);
import Foo from '/imports/ui/components/Foo';
i am using Blaze to wrap the React components, like this:
import Foo from '/imports/ui/components/Foo';
Template.registerHelper("Foo", function() {
return Foo;
);
<div>
{{> React component=Foo}}
</div>
i realize that i shouldn't be doing multiple default exports in a single file, but it does work. note that this is with these versions: Meteor v1.4.1.1, Meteor npm v3.10.6, Meteor node v4.5.0.
i now have a test harness, with Meteor v1.4.2.3, Meteor npm v3.10.9 and Meteor node v4.6.2, where this has stopped working. not surprisingly, in my server console:
While building for web.browser:
imports/ui/components/Foo.jsx:58: Only one default export allowed
per module. (58:0)
so now i'm looking for a way to get this back working, and in the proper way.
what i've tried:
first, keeping the component and the create container in the same file, i did proper ES6 exporting:
const Foo = class Foo extends Component {
const FooContainer = createContainer(() => {
export {Foo, FooContainer};
... and imported Foo.
result: Foo loaded in the app, but the container code never ran.
second, i put the component and the create container in two different files, and reverted to exporting defaults:
// Foo.jsx
export default class Foo extends Component {
// FooContainer.jsx
export default createContainer(() => {
... and used Foo:
import Foo from '/imports/ui/components/Foo';
Template.registerHelper("Foo", function() {
return Foo;
});
<div>
{{> React component=Foo}}
</div>
result: Foo loaded in the app, but the container code never ran.
third, similar to the above, but i instead tried putting FooContainer on the page:
import FooContainer from '/imports/ui/components/FooContainer';
Template.registerHelper("Foo", function() {
return FooContainer;
});
<div>
{{> React component=Foo}}
</div>
result: big error message from React that basically i wasn't doing it right.
any idea on the proper way to get this to work?
update:
attempts 4 and 5
put both back into same class, like this:
export class Foo extends Component {
export default createContainer(() => {
... with 2 different ways of importing it:
import Foo from '/imports/ui/components/Foo';
that ran createContainer() but did not put my component on the page.
import {Foo} from '/imports/ui/components/Foo';
that did the opposite: did not run createContainer(), but i did see the component.
got it working, in 1 jsx file:
export class Foo extends Component {
export default createContainer(() => {
in the helper, relying on the default export:
import Foo from '/imports/ui/components/Foo';
the actual problem was i had incorrectly imported a server file to publish, and that caused a chain reaction which caused the component to not render. doh.

Resources