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

Vue $root component data access or setup #6548

Closed
linasmatakas opened this issue Apr 17, 2019 · 18 comments
Closed

Vue $root component data access or setup #6548

linasmatakas opened this issue Apr 17, 2019 · 18 comments

Comments

@linasmatakas
Copy link

If Vue project's $root component has a reactive variable (in data or computed) f.e. isMobile, so it can be used in app's components as $root.isMobile, then there is no way to define it in config.js or anywhere globally for storybook usage.

It would be nice to have some option to define $root component's data, computed and methods.

Alternative solution that I use now, but it has annoying performance and usability cases:
Add variable as Vue.prototype.isMobile

const localEventBus = new Vue();

Vue.mixin({
    mounted() {
        localEventBus.$on('storybook-rerender', this.storybookForceUpdate);
    },
    beforeDestroy() {
        localEventBus.$off('storybook-rerender', this.storybookForceUpdate);
    },
    methods: {
        storybookForceUpdate() {
            this.$forceUpdate();
        },
    },
});

window.addEventListener('resize', () => {
    localEventBus.$emit('storybook-rerender');
});

Does anyone have alternative solutions? or perhaps there is a way to add data or computed to root component in storybook?

@stale
Copy link

stale bot commented May 8, 2019

Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!

@stale stale bot added the inactive label May 8, 2019
@stale
Copy link

stale bot commented Jun 7, 2019

Hey there, it's me again! I am going close this issue to help our maintainers focus on the current development roadmap instead. If the issue mentioned is still a concern, please open a new ticket and mention this old one. Cheers and thanks for using Storybook!

@stale stale bot closed this as completed Jun 7, 2019
@graup
Copy link
Contributor

graup commented Jun 20, 2019

@shilman can we reopen this? I have the same question.

@graup
Copy link
Contributor

graup commented Jun 21, 2019

Here's a sort of ugly workaround for this. Just add a decorator that wraps every story and manually assign $root in there. Something along the lines of this:

addDecorator({
  i18n,
  beforeCreate: function() {
    this.$root._i18n = this.$i18n;
  },
  template: '<div><slot /></div>',
})

@backbone87
Copy link
Contributor

using $root is considered bad practice exactly for this reason, because your component can only be used in an environment (root) you have configuration control over. its better to use provide/inject (in your app, your root comp can provide and in storybook a decorator could provide) or a vuex store

@gilesbutler
Copy link

@backbone87 any ideas on how you use provide in a decorator?

Thanks

@shilman shilman reopened this Sep 13, 2019
@stale stale bot removed the inactive label Sep 13, 2019
@shilman
Copy link
Member

shilman commented Sep 13, 2019

Sorry @graup , missed your comment back in June. Still an issue?

@stale
Copy link

stale bot commented Oct 4, 2019

Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!

@stale stale bot added the inactive label Oct 4, 2019
@graup
Copy link
Contributor

graup commented Oct 8, 2019

@shilman yes, I believe this is still an issue, albeit a low priority one since there are possible (hacky) workarounds. Looks like nobody has a need for this strong enough to whip up a PR.

@stale stale bot removed the inactive label Oct 8, 2019
@naimlatifi5
Copy link

naimlatifi5 commented Oct 25, 2019

I have the same issue and when I use this.$root.isMobile in my components for my app and inject those components in storybook which is in another context it doesn't work. @Stale I actually have a need for that :)

@naimlatifi5
Copy link

@graup I tried your workaround by doesn't work for me. Could you please give me info on that with some more code examples ? :) I would appreciate it.

@graup
Copy link
Contributor

graup commented Oct 28, 2019

@naimlatifi5 I only tested it with vue-i18n, but I don't have any complete example right now. If you could post a link to a test repo, I can try to take a look there.

@naimlatifi5
Copy link

naimlatifi5 commented Oct 30, 2019

Hi @graup,
Yes and please find a code example with storybook here https://github.com/naimlatifi5/storybookwithissueroot . In the root vue instance I have isMobile property with a method that later on component I am referencing as this.$root.methodName and you will see that storybook under Button -> text will generate an error

@linasmatakas
Copy link
Author

@naimlatifi5
I cannot offer a fix for the original issue, but if you are interested in a bit more elegant solution of how to use global variables, which also easily works in storybook as well then here is a quick try at that:

  1. Example of how to create global variables. Note that this service does more than it needs for isMobile to work, but I think that is self-sufficient, so it should be easy to try it out. https://gist.github.com/linasmatakas/94a515f78228f023f250a1f959e073b0
  2. main.js:
    2.1. import WindowService from '@/services/window-service';
    2.2. beforeMount() { WindowService.init(); },
  3. Usage in template $screen.isMobile
  4. .storybook/config.js: import and WindowService.init(); anywhere

@stale
Copy link

stale bot commented Nov 21, 2019

Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!

@stale stale bot added the inactive label Nov 21, 2019
@stale
Copy link

stale bot commented Dec 21, 2019

Hey there, it's me again! I am going close this issue to help our maintainers focus on the current development roadmap instead. If the issue mentioned is still a concern, please open a new ticket and mention this old one. Cheers and thanks for using Storybook!

@stale stale bot closed this as completed Dec 21, 2019
@Juice10
Copy link

Juice10 commented May 10, 2022

@backbone87 any ideas on how you use provide in a decorator?

Thanks

@gilesbutler I was able to create a global decorator to add support for inject/provide using a decorator

In your preview.js add the following:

export const decorators = [
  (story, { args, parameters }) => {
    const valueMap = new Map();
    const newArgs = { ...args };
    parameters.inject?.forEach((attribute) => {
      valueMap.set(attribute, args[attribute]);
      delete newArgs[attribute];
    });

    const WrapperComponent = defineComponent({
      setup() {
        valueMap.forEach((value, key) => {
          provide(key, value);
        });
      },
      template: "<slot />",
    });
    story({ args: newArgs });
    return {
      components: { story, WrapperComponent },
      template: "<WrapperComponent><story /></WrapperComponent>",
    };
  },
];

Then in your story add what you'd like to inject with the 'inject' parameter:

export default {
  title: "MyComponent",
  component: MyComponent,
  parameters: {
    inject: ["isPlaying"],
  },
};

And add the value you'd like to pass in, in the stories args:

export const Default = Template.bind({});

Default.args = {
  isPlaying: false,
}

@gilesbutler
Copy link

Nice one, thanks @Juice10

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

7 participants