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

How to use context? #76

Closed
davidpelaez opened this issue Apr 7, 2016 · 27 comments
Closed

How to use context? #76

davidpelaez opened this issue Apr 7, 2016 · 27 comments

Comments

@davidpelaez
Copy link

Currently many UI libraries like material-ui depend on the context for theming. I wasn't able to figure out a way to setup this. Maybe I should have a global container and use it to wrap all my stories? It doesn't feel 100% clean, but I wanted to check if this if the correct approach.

Thanks for react-storybook, this is amazing! We've wanted this for so long in our team, it's currently changing the way we work with new projects. :)

@stewartduffy
Copy link
Contributor

Hi @davidpelaez we are doing this by setting context on each component. Not ideal, but in the context of a component library which we are building - it makes sense, having each component be self containing so they can be used in isolation.

import React from 'react'
import ThemeManager from 'material-ui/lib/styles/theme-manager'
import myTheme from '../theme/my-theme' //path to your custom theme
import AppBar from 'material-ui/lib/app-bar';

const ExampleComponent = React.createClass({
  propTypes: {
    children: React.PropTypes.any.isRequired
  },

  childContextTypes: {
    muiTheme: React.PropTypes.object
  },

  getChildContext () {
    return {
      muiTheme: ThemeManager.getMuiTheme(myTheme)
    }
  },


  render () {
    return (
      <div>
        <AppBar title="Title" iconClassNameRight="muidocs-icon-navigation-expand-more" />
      </div>
    )
  }
})

export default ExampleComponent

@arunoda
Copy link
Member

arunoda commented Apr 7, 2016

@davidpelaez, They way you do it the correct way to do it. But, I think we can introduce a new API to make these things a bit easier.

See:

import { storiesOf } from '@kadira/storybook';
import Context from '../context';
import MyComp from '../my_comp';

storiesOf('MyComp').
  .addDecorator(function(getStory) {
    return (<Context>getStory()</Context>)
  })
  .add('default view', () => (
    <MyComp>Something here</MyComp>
  ))

This is just a nice syntax but it will wrap each story with the context. But, I think that's the way it should be anyway.

@mcbain
Copy link

mcbain commented Apr 15, 2016

The same is needed for material-ui. Currently I wrap every story with a <WithTheme>/<WithTheme> component. +1

@davidpelaez
Copy link
Author

@mcbain exactly I came to this because of material-ui. Thanks for pointing
out that using the wrapper was correct. It would be nice to either have
this is in the docs or have a dedicated helper for it. A slightly closer
integration to small details of React like this would be great. I've seen
many constants update in the last dates and you are doing an amazing job.
Whatever you choose will work great :)

David Peláez Tamayo
Designer & Entrepreneur

On Fri, Apr 15, 2016 at 1:47 AM, mcbain [email protected] wrote:

The same is needed for material-ui. Currently I wrap every story with a
/ component. +1


You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
#76 (comment)

@arunoda
Copy link
Member

arunoda commented Apr 16, 2016

@mnmtanish could you work on the addDecorator() API when you have some time. Make a comment if you are working on this.

If some else could do it before @mnmtanish that's great too :)

@thani-sh
Copy link
Contributor

thani-sh commented Apr 16, 2016

@arunoda just wrote a simple implementation here. Let me know if there are any changes.
It should work but I haven't tested it with a React demo yet. I'll comment here after testing in a while.

@necolas
Copy link
Contributor

necolas commented Apr 20, 2016

Could you not use something like https://github.com/mattzeunert/react-with-context?

@wuzhuzhu
Copy link

I'm tring to use storybook with Material-ui. And the Dropdown menu in material-ui not response for clicking.
After reading above I have tried to use new addDecorator API, but import Context from '../context'; cause new Error:

...
ERROR in ./client/configs/context.js
Module not found: Error: Cannot resolve module 'meteor/reactive-dict' in /Users/walter/WebstormProjects/meteor13test/client/configs
 @ ./client/configs/context.js 25:20-51
...

here is content in context.js:

import * as Collections from '/lib/index';
import { Meteor } from 'meteor/meteor';
import { FlowRouter } from 'meteor/kadira:flow-router-ssr';
import { ReactiveDict } from 'meteor/reactive-dict';
import { Tracker } from 'meteor/tracker';

export default function () {
    return {
        Meteor,
        FlowRouter,
        Collections,
        LocalState: new ReactiveDict(),
        Tracker,
    };
}

@arunoda
Copy link
Member

arunoda commented May 10, 2016

@wuzhuzhu

Storybook can't read meteor/xxx dependencies since they are some special deps managed by Meteor. Seems like you are using Mantra and you should not import context directly. That may fix your issue.

@VirtueMe
Copy link

@wuzhuzhu

I had the same issue with SelectField, the answer is in the material-ui readme.md You need to load the react-tap-event-plugin when storybook loads.

Some components use react-tap-event-plugin to listen for touch events because onClick is not fast enough This dependency is temporary and will eventually go away. Until then, be sure to inject this plugin at the start of your app.

import injectTapEventPlugin from 'react-tap-event-plugin';

// Needed for onTouchTap
// http://stackoverflow.com/a/34015469/988941
injectTapEventPlugin();

@wuzhuzhu
Copy link

Thanks for both of your answers! It's working now.

@kjetilge
Copy link

kjetilge commented Jun 12, 2016

Is it possible get formsy-react to work with storybook / meteor-mantra ? All fields just disappears in storybook.

@ffxsam
Copy link

ffxsam commented Jun 16, 2016

@VirtueMe Where did you wind up putting the injectTapEventPlugin() call? I keep getting errors, because it can only be invoked once. I'm not sure what the best place for it is.

@VirtueMe
Copy link

I added it to the .storybook/config.js file.

@michaltakac
Copy link

Was trying to use Material UI with storybook today and came up with this solution:

/stories/materialize.js:

import React from 'react';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import defaultTheme from '../../../common/app/defaultTheme';


export default function materialize(Component, props) {
  return (
    <MuiThemeProvider muiTheme={defaultTheme}>
      <Component {...props} />
    </MuiThemeProvider>
  );
}

stories/header.js:

import React from 'react';
import Header from '../Header';
import materialize from './materialize';
import { storiesOf, action } from '@kadira/storybook';

storiesOf('Header', module)
  .add('default button view', () => {
    const props = {
      label: "Button"
    }
    return materialize(Header, props);
  })
  .add('primary button view', () => {
    const props = {
      label: "Button",
      primary: true
    }
    return materialize(Header, props);
  });

@ffxsam
Copy link

ffxsam commented Jun 17, 2016

@michaltakac No need for the materialize function. What you're doing is easily done via Storybook's built in addDecorator method:

import React from 'react';
import { action, storiesOf } from '@kadira/storybook';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';

import StarRating from '../common-ui/components/StarRating';

storiesOf('common.StarRating', module)
  .addDecorator(story => (
    <MuiThemeProvider muiTheme={getMuiTheme()}>
      {story()}
    </MuiThemeProvider>
  ))
  .add('summary mode', _ => (
    renderStarRating()
  ))
  .add('details mode', _ => (
    renderStarRating({ mode: 'details' })
  ))
  .add('rate mode', _ => (
    renderStarRating({ mode: 'rate' })
  ));

function renderStarRating(props) {
  const ratings = [
    { name: 'Bob G', rating: 5 },
    { name: 'Ali G', rating: 4 },
    { name: 'Frankenstein', rating: 1 },
    { name: 'Santa Claus', rating: 3 },
    { name: 'Another Person', rating: 4 },
    { name: 'Some Gal', rating: 5 },
    { name: 'Some Critical Dude', rating: 1 },
  ];

  return <div style={rootStyle}>
    <StarRating ratings={ratings} { ...props } />
  </div>
}

const rootStyle = {
  alignItems: 'center',
  display: 'flex',
  justifyContent: 'center',
  marginTop: 200,
};

@michaltakac
Copy link

Thank you for example code, helped!

@vladfr
Copy link

vladfr commented Jul 27, 2016

Shouldn't this be closed now that addDecorator exists?

@arunoda
Copy link
Member

arunoda commented Jul 28, 2016

@vladfr I think yes.

@arunoda arunoda closed this as completed Jul 28, 2016
@Domiii
Copy link

Domiii commented Oct 13, 2016

Hi!

Thank you for finding such an elegant solution to this annoying problem!

However, can the MUI support be generalized in a way that I can add the decorator only once, in the global config, instead of each individual story?

For reference, my .storybook/config.js looks like this:

import { configure } from '@kadira/storybook';
import { setStubbingMode } from 'react-komposer';

// See: https://github.com/kadirahq/react-komposer#stubbing
setStubbingMode(true);

function loadStories() {
  require('../client/modules/core/components/.stories');
  require('../client/modules/comments/components/.stories');
}

configure(loadStories, module);

@usulpro
Copy link
Member

usulpro commented Oct 13, 2016

@Domiii You can add decorator both locally and globally:

import { addDecorator } from '@kadira/storybook';
import { muiTheme } from 'storybook-addon-material-ui';
// You can add decorator globally:
addDecorator(muiTheme());

@Domiii
Copy link

Domiii commented Oct 13, 2016

@usulpro Does this work for you with the latest version?

When visiting the storybook frontend, I get:

ERROR in ./~/react-material-color-picker/dist/ic_done_black_64dp_1x.png
Module parse failed: D:\code\meteor\mantra-sample-blog-app\node_modules\react-material-color-picker\dist\ic_done_black_64dp_1x.png Unexpected character '�' (1:0)
You may need an appropriate loader to handle this file type.
SyntaxError: Unexpected character '�' (1:0)
    at Parser.pp$4.raise (D:\code\meteor\mantra-sample-blog-app\node_modules\acorn\dist\acorn.js:2221:15)
    at Parser.pp$7.getTokenFromCode (D:\code\meteor\mantra-sample-blog-app\node_modules\acorn\dist\acorn.js:2756:10)
    at Parser.pp$7.readToken (D:\code\meteor\mantra-sample-blog-app\node_modules\acorn\dist\acorn.js:2477:17)
    at Parser.pp$7.nextToken (D:\code\meteor\mantra-sample-blog-app\node_modules\acorn\dist\acorn.js:2468:15)
    at Parser.parse (D:\code\meteor\mantra-sample-blog-app\node_modules\acorn\dist\acorn.js:515:10)
    at Object.parse (D:\code\meteor\mantra-sample-blog-app\node_modules\acorn\dist\acorn.js:3098:39)
    at Parser.parse (D:\code\meteor\mantra-sample-blog-app\node_modules\webpack\lib\Parser.js:902:15)
    at DependenciesBlock.<anonymous> (D:\code\meteor\mantra-sample-blog-app\node_modules\webpack\lib\NormalModule.js:104:16)
    at DependenciesBlock.onModuleBuild (D:\code\meteor\mantra-sample-blog-app\node_modules\webpack-core\lib\NormalModuleMixin.js:310:10)
    at nextLoader (D:\code\meteor\mantra-sample-blog-app\node_modules\webpack-core\lib\NormalModuleMixin.js:275:25)
 @ ./~/react-material-color-picker/dist/MaterialColorPicker.js 39:29-67

@arunoda
Copy link
Member

arunoda commented Oct 13, 2016

@Domiii your issue is your webpack config can't understand png files.
Our default setup should support it.
But if you are using a custom webpack loader, try to use a proper loader for png files.

@caub
Copy link

caub commented Mar 9, 2018

I did an horrible:

const story = (...args) => ({
	add(name, fn) {
		storiesOf(...args).add(name, () => <Wrapper>{fn()}</Wrapper>);
		return this;
	},
});

same usage than before:

configure(() => {
	story('StatusChip', module)
		.add('with status', () => <StatusChip status="Rejected" />)
		.add('with progress', () => <StatusChip progress={72} />);
})

It would be great to allow to configure storiesOf, I'll see if I can make a PR later, because passing a wrapper, or an extra function every time is too much (@michaltakac)

@Hypnosphi
Copy link
Member

Hypnosphi commented Mar 18, 2018

@caub this should do effectively the same:

import { addDecorator } from '@storybook/react'

addDecorator(fn => <Wrapper>{fn()}</Wrapper>)

See https://storybook.js.org/basics/writing-stories/#using-decorators

@AlexVotry
Copy link

I'm using "useContext(user)" in my component and use that variable to populate the component. How would I be able to get that data into the storybook? I can't useContext without a functional component, so I don't know how to access that data in the storybook? I looked everywhere to see an example, but couldn't find it.

@Hypnosphi
Copy link
Member

@AlexVotry

I can't useContext without a functional component

Yeah you definitely need one:

const Example = () => {
  const user = useContext(UserContext)
  return <MyComponent user={user} />
}

storiesOf('MyComponent', module).add('default', () => <Example />)

Or, maybe even better, useContext directly in MyComponent

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests