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

Ability to exclude some directories/files from stories #11181

Closed
pelotom opened this issue Jun 15, 2020 · 39 comments
Closed

Ability to exclude some directories/files from stories #11181

pelotom opened this issue Jun 15, 2020 · 39 comments

Comments

@pelotom
Copy link
Contributor

pelotom commented Jun 15, 2020

The new main.js format for specifying stories requires specifying an array of globs, e.g.

module.exports = {
  stories: ['../../src/**/*.@(story|stories).tsx'],
};

With this approach it's not clear how I can exclude certain things from being included. When the stories were configured within the browser environment I was able to use require.context with a regex, e.g.

configure(
  require.context('../../src', true, /(?<!\/some\/forbidden\/directory.*)\.(story|stories)\.tsx?$/),
  module,
);

I could walk the directory structure in node and just provide a large array of fully resolved file paths, but it'd be nice to just be able to provide a regex instead of a glob. Or is there a way to do this with globs that I'm missing?

@shilman
Copy link
Member

shilman commented Jun 15, 2020

Haven't tried this, but possible solution here:
prettier/prettier#1358 (comment)

@pelotom
Copy link
Contributor Author

pelotom commented Jun 16, 2020

I can't seem to make that work, it ends up excluding all files.

@stale
Copy link

stale bot commented Jul 7, 2020

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 Jul 7, 2020
@DHedgecock
Copy link

DHedgecock commented Jul 13, 2020

Is there anyway to pass options for glob in main.js? From the glob docs:

Previously, this module let you mark a pattern as a "comment" if it started with a # character, or a "negated" pattern if it started with a ! character.

These options were deprecated in version 5, and removed in version 6.

To specify things that should not match, use the ignore option.

@stale stale bot removed the inactive label Jul 13, 2020
@shilman
Copy link
Member

shilman commented Jul 14, 2020

cc @yannbf @ndelangen

@yannbf
Copy link
Member

yannbf commented Jul 14, 2020

I don't think there's a mechanism in place for that, and I tried tweaking a little here but it seems like passing a ignore option to micromatch doesn't alter the resulting regex. Would be nice to have an exclude property like tsconfig has for instance, but unfortunately we don't have that.

@pelotom I'm not sure about your use case, but the use case I see about using exclude would be maybe if I'm developing CheckoutFeature and I only want Storybook to bring those stories because maybe the project has like 500 stories. But in this scenario, rather than excluding, I would make my glob more specific and I would achieve that with the existing implementation of Storybook. Does that help somehow?

@pelotom
Copy link
Contributor Author

pelotom commented Jul 14, 2020

@pelotom I'm not sure about your use case, but the use case I see about using exclude would be maybe if I'm developing CheckoutFeature and I only want Storybook to bring those stories because maybe the project has like 500 stories. But in this scenario, rather than excluding, I would make my glob more specific and I would achieve that with the existing implementation of Storybook. Does that help somehow?

My use case is that I have one particular directory of stories nested deep within my repo which I want to exclude during visual regression testing in CI. It's not practical to positively specify all the things I do want to include while omitting this one particular deeply-nested directory, if that makes sense. My short term workaround is just to delete the directory in the CI script before building the storybook, but it'd be nice to have an exclude option.

@shilman
Copy link
Member

shilman commented Jul 15, 2020

@pelotom One workaround might be to specify the disable parameter for the components in that directory. This is available for both Storyshots and Chromatic, and may be available for whatever tool you're using.

export default {
  title: 'path/to/MyComponent',
  component: MyComponent,
  parameters: {
    chromatic: { disable: true },
  }
}
// stories here ...

@pelotom
Copy link
Contributor Author

pelotom commented Jul 15, 2020

Ah, I am using Chromatic, thanks for the tip!

@artaommahe
Copy link

Faced same issue. We have a big library with a lot of submodules and their stories. Some submodules have their own storybook configuration due to specific code/environment. We need to exclude these submodules stories from the main storybook and cant find a way to do it with the new pattern format 😕

@shilman
Copy link
Member

shilman commented Jul 20, 2020

@artaommahe have you looked at storybook composition? https://medium.com/storybookjs/storybook-composition-af0da9084fba

@artaommahe
Copy link

how does it exclude dirs from specific storybook? e.x. this folders structure

.storybook/
layout/
lesson/
user/
widgets/
  chat/
    .storybook/
  switcher/
    .storybook/

we need layout/lesson/user stories, but not widgets one for the main storybook

@shilman
Copy link
Member

shilman commented Jul 20, 2020

you could just include layout, lesson, user and then compose the storybooks from chat & switcher if you wanted a unified view including storybooks with different configurations

@artaommahe
Copy link

hm, yu're right, just list all folders.. so there is no other way, no excluding in current glob implementation?

@stale
Copy link

stale bot commented Aug 16, 2020

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 Aug 16, 2020
@ndelangen
Copy link
Member

You're not required to specify globs!
You can specify an array of files.

module.exports = {
  stories: async (list) => [...list, ...findFilesUsingWhateverMethodYouLikeAndReturnAnArray()],
};

@stale
Copy link

stale bot commented Sep 20, 2020

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 Sep 20, 2020
@doublejosh
Copy link
Contributor

doublejosh commented Dec 22, 2020

Same need for excluding SCSS files from rebuilding Storybook... when using a parallel build and injecting compiled css via .preview.js. Otherwise, storybook rebuilds BEFORE the freshly compiled css is ready.

This seems like a common workflow (it's mentioned in docs)... is there a workaround/solution for that already?

@berci-i
Copy link

berci-i commented Feb 3, 2021

I also kind of need this. Is way harder to keep everything in sync by just adding all the new folders everytime. Is kind of redundant and hard to do. This option would be extremely useful or at least a way to do it with regex.

avaly added a commit to avaly/storybook that referenced this issue Apr 2, 2021
gabiseabra pushed a commit to gabiseabra/storybook that referenced this issue Apr 3, 2021
@Domiii
Copy link

Domiii commented Apr 3, 2021

For anyone still struggling: You can in fact exclude stories without custom story resolver functions.

First of all, these are the relevant resources (as I also explained in #10153):

  1. The official documentation on this is now here
  2. As explained on that page, the glob syntax is that of micromatch (and not that of glob).
  3. Here is the storybook code that parses and resolves stories: https://github.com/storybookjs/storybook/blob/master/lib/core-common/src/utils/to-require-context.ts#L31

Since micromatch is used, you can simply add negative patterns using !(dont want this).

Be aware of the other rules, to make sure, you are not accidentally misusing it. E.g. make sure, to keep all /**/ patterns.

Example: stories!(/components)/**/*.js

@kelly-tock
Copy link

I am still not getting this to work, here's my current config:

../stories/**/*.stories.@(js|jsx|ts|tsx)

I basically want to exclude one folder inside of stories to keep it in source code for a while. I could move it out but don't really want to do that.

I tried this:

'../stories!(/deprecated)/**/*.stories.tsx',

@Domiii
Copy link

Domiii commented May 3, 2021

@kelly-tock I would recommend installing micromatch locally to test your patterns. Yours looks good to me, but might be an issue with how they match things.

Mine works fine: ../packages/mystuff-*/stories!(/components)/**/*?(.stories).@(js|jsx|ts|tsx|mdx)

@dannyhw
Copy link
Member

dannyhw commented Sep 3, 2021

In case it's useful for anyone:

Using node's glob with the ignore option worked well for me.

const glob = require('glob');
const path = require('path');
const appDirectory = path.resolve(__dirname, '../');
const getStories = () =>
  glob.sync(`${appDirectory}/stories/**/*.stories.@(js|jsx|ts|tsx|mdx)`, {
    ignore: `${appDirectory}/stories/**/*.native.stories.@(js|jsx|ts|tsx|mdx)`,
  });
module.exports = {
  stories: async list => [...list, ...getStories()],
}

@sneko
Copy link

sneko commented Jan 5, 2023

On my side, @dannyhw the disadvantage is you have to stop/restart your Storybook to take in account new story files. While developing it's a bit annoying.

I recommend using in the array the standard way of Storybook with wildcards, and for a folder where you need to exclude something, you calculate fixed paths with glob.sync for example.

It's not perfect until Storybook will eventually manage excluding paths natively.

@dannyhw
Copy link
Member

dannyhw commented Jan 5, 2023

@sneko yeah you're right it doesn't work for new files, its more of a workaround. Though with the way require.context is used for importing stories I wasn't able to find another way to solve this at the time.

@sneko
Copy link

sneko commented Jan 5, 2023

@dannyhw still no other good way same 1 year later. In my case I have pnpm in a monorepo, and storybook looks into node_modules, but since pnpm works natively with symlinks, it's like an infinite loop hell.

glob helped during a time, but I was tired of restarting Storybook at each new file, so I just moved all my "logic" code in big "src/" folders in each of my monorepo packages, like that I tell Storybook to not target /package1 but instead /package1/src, so no more circular loops with node_modules folders.

@D1no
Copy link

D1no commented Mar 6, 2023

In a pnpm workspace with storybook on vite, the !(/node_modules) ignore glob doesn't work.

const config: StorybookConfig = {
  stories: [
  // (...)
    "../ui/**/*.stories.@(js|jsx|ts|tsx)",
  • recursively sources stories from the ui library when components within cross pnpm symlink each other deep down
    "../ui/**!(/node_modules)/**/*.stories.@(js|jsx|ts|tsx)",
  • trying to exclude any node_modules folder down the tree = no luck, even after many attempts and reading micromatch docs.

Probably related to
#15913 (need for ignore options)

@andrejilderda
Copy link
Contributor

@D1no I had a very similar problem within a similar project setup which I managed to fix by:

  stories: [
    "../../**!(node_modules)/**!(node_modules)/*.mdx",
    "../../**!(node_modules)/**!(node_modules)/*.stories.@(js|jsx|ts|tsx)",
  ],

See: andrejilderda/desktop-ui@0993a07

Also note that Storybook is not using micromatch but globby as a globbing library (see here).

D1no added a commit to D1no/reproduction-storybook-symlinks-pnpm that referenced this issue Mar 18, 2023
@D1no
Copy link

D1no commented Mar 18, 2023

Thank you very much @andrejilderda ! Due to your insight I was able to add it to the following issue: #21399

Seems like it is flaky / not really user friendly but it works! Reference

  case "4_proper_workspace_stories_trying_to_avoid_recursion_with_glob":
    configStories = [
      /**
       * Since storybook uses globby under the hood, trying to add a negative
       * glob for nested node_modules via "**!(node_modules)/**!(node_modules)".
       * -> Insight from https://github.com/storybookjs/storybook/issues/11181#issuecomment-1474347928
       *    though sometimes just "**!(node_modules)" seems to work, when there
       *    are stories directly on next level folder. The following wont work:
       *    "../node_modules/@repo/views/**!(node_modules)/**!(node_modules)/*.stories.@(js|jsx|ts|tsx)"
       *    This does:
       *    "../node_modules/@repo/views/**!(node_modules)/*.stories.@(js|jsx|ts|tsx)"
       */
      "../node_modules/@repo/**!(node_modules)/**!(node_modules)/*.stories.@(js|jsx|ts|tsx)",
    ];
    break;

@astriskit
Copy link

just to add the point - tried the above steps of glob pattern - **(!) including !<dir> too; but couldn't get to intended ignore; hence went old-school by having two stories array, one for the build-time and one for the dev-time. And the stories, which are to be separated out are configured aptly in both stories. Basically one of the array has all the story-file extensions included, including the build-time-non-inclusive files (used *.d-stories.tsx) and the other array has all except the .d-stories.tsx files.

Btw, the use-case was to skip some stories while building. And the storybook setup is 7-rc3.

@oemer-aran
Copy link

oemer-aran commented May 4, 2023

As stated before, storybook is using globby, but adjust it's behaviour internally. My wild guess is, that storybook prepends .storybook/ to every glob pattern?

With a workaround, you can use globby just as it is documented: https://www.npmjs.com/package/globby

Here is how I include all **/*.stories.@(js|jsx|ts|tsx), excluding node_modules:

import type { StorybookConfig } from "@storybook/vue3-vite"
// This is already installed by storybook
import globby from "globby"

const config: StorybookConfig = {
  // globby is working from the root, where "yarn storybook" was called. 
  // To make it work with storybooks internal logic, we have to change the cwd 
  stories: globby.sync([`../**/*.stories.@(js|jsx|ts|tsx)`, "!../**/node_modules/**/*"], , { cwd: "./.storybook" }),
  // ...
}
export default config

None of the other workarounds worked for me.

@Haschtl
Copy link

Haschtl commented May 13, 2023

As stated before, storybook is using globby, but adjust it's behaviour internally. My wild guess is, that storybook prepends .storybook/ to every glob pattern?

With a workaround, you can use globby just as it is documented: https://www.npmjs.com/package/globby

Here is how I include all **/*.stories.@(js|jsx|ts|tsx), excluding node_modules:

import type { StorybookConfig } from "@storybook/vue3-vite"
// This is already installed by storybook
import globby from "globby"

const config: StorybookConfig = {
  // globby is working from the root, where "yarn storybook" was called. 
  // To make it work with storybooks internal logic, we have to change the cwd 
  stories: globby.sync([`../**/*.stories.@(js|jsx|ts|tsx)`, "!../**/node_modules/**/*"], , { cwd: "./.storybook" }),
  // ...
}
export default config

None of the other workarounds worked for me.

Thanks for this solution!
Since globby@12 the globby.sync was replaced with globbySync.
Here is the snippet for globby@12

import type { StorybookConfig } from "@storybook/vue3-vite"
// This is already installed by storybook
import { globbySync } from "globby"

const config: StorybookConfig = {
  // globby is working from the root, where "yarn storybook" was called. 
  // To make it work with storybooks internal logic, we have to change the cwd 
  stories: globbySync([`../**/*.stories.@(js|jsx|ts|tsx)`, "!../**/node_modules/**/*"], , { cwd: "./.storybook" }),
  // ...
}
export default config

@oemer-aran
Copy link

oemer-aran commented May 22, 2023

Thanks for this solution! Since globby@12 the globby.sync was replaced with globbySync. Here is the snippet for globby@12

import type { StorybookConfig } from "@storybook/vue3-vite"
// This is already installed by storybook
import { globbySync } from "globby"

const config: StorybookConfig = {
  // globby is working from the root, where "yarn storybook" was called. 
  // To make it work with storybooks internal logic, we have to change the cwd 
  stories: globbySync([`../**/*.stories.@(js|jsx|ts|tsx)`, "!../**/node_modules/**/*"], , { cwd: "./.storybook" }),
  // ...
}
export default config

Unfortunately, this breaks HMR for new files.. If you add a new .stories.ts it will not be recognized until restart. This makes sense because globbySync/globby.sync just returns an array of all the file paths at the time, when you start storybook . The new file will never be added to this array after it already started.

This issue has to be solved internally by the Storybook team to support globby patterns as expected.

@ctjhoa
Copy link

ctjhoa commented May 24, 2023

For the record, in my case, I have a specific folder to ignore and here is what I come up with:

import { readdirSync } from 'fs';

const config: StorybookConfig = {
  stories: [
    ...readdirSync(__dirname + '/../../', { withFileTypes: true })
      .filter((dirent) => dirent.isDirectory())
      .filter((dirent) => dirent.name !== 'to_ignore')
      .map(
        (dirent) =>
          `../../${dirent.name}/**/src/lib/**/*.stories.@(js|jsx|ts|tsx|mdx)`
      ),
  ],

@sneko
Copy link

sneko commented Jun 9, 2023

I'm in a monorepo and some internal imports are done for modules, so I need to mix both worlds. The easiest solution for me was:

// .storybook/main.ts

const environmentVariables: Record<string, string> = {
  LOL: 'true',
};

export const config: StorybookConfig = {
    ...,
    env: (config) => ({
        ...config,
        ...environmentVariables,
    }),
    viteFinal: {
        define: {
            'process.env': environmentVariables,
        },
    },
};

@pm0u
Copy link

pm0u commented Jun 28, 2023

Just want to mention that it seems (according to the docs) Storybook now uses picomatch which may be missing some features from micromatch (why i was left scratching my head)

@prashanth-cn
Copy link

As stated before, storybook is using globby, but adjust it's behaviour internally. My wild guess is, that storybook prepends .storybook/ to every glob pattern?

With a workaround, you can use globby just as it is documented: https://www.npmjs.com/package/globby

Here is how I include all **/*.stories.@(js|jsx|ts|tsx), excluding node_modules:

import type { StorybookConfig } from "@storybook/vue3-vite"
// This is already installed by storybook
import globby from "globby"

const config: StorybookConfig = {
  // globby is working from the root, where "yarn storybook" was called. 
  // To make it work with storybooks internal logic, we have to change the cwd 
  stories: globby.sync([`../**/*.stories.@(js|jsx|ts|tsx)`, "!../**/node_modules/**/*"], , { cwd: "./.storybook" }),
  // ...
}
export default config

None of the other workarounds worked for me.

core: { builder: '@storybook/builder-webpack5', },

pointing to @storybook/builder-webpack5 in main.js fixed the HMR for me

@eportet
Copy link

eportet commented Feb 27, 2024

Just want to mention that it seems (according to the docs) Storybook now uses picomatch which may be missing some features from micromatch (why i was left scratching my head)

Thanks to @pm0u. Reading the documentation for picomatch I saw that their negation operator works this way:

!(pattern): Match anything but pattern

So ended up writing my storybook config like so:

// main.ts
const config: StorybookConfig = {
  // ...
  stories: ['../src/!(ignoreFolder)/**/*.stories.{ts,tsx}', '../src/**/*.mdx'],
  // ...
}

@ingvaldlorentzen
Copy link

Can be kinda tricky to get this right, so thought I would share my solution with anyone coming across this.

stories: [
    '../apps/**/!(tests)/*.mdx',
    '../apps/**/!(tests)/*.stories.@(ts|tsx)',
    '../packages/**/!(tests)/*.mdx',
    '../packages/**/!(tests)/*.stories.@(ts|tsx)',
  ],

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