Storybook: Sorting stories

Created on 17 Oct 2016  路  26Comments  路  Source: storybookjs/storybook

Hi, the stories are listed in rather random order, is it possible to sort them alphabetically? This is how it looks on my end.

screen shot 2016-10-17 at 10 45 11 am

Also, in my project I have quite a few stories by now, would it be possible to add also more hierarchy into stories? Component > View 1 > View 1 Mutation ...

stories feature request in progress ui

Most helpful comment

For reference, here's the recommended setup for v5.2 onwards:

addParameters({
  options: {
    storySort: (a, b) =>
      a[1].kind === b[1].kind ? 0 : a[1].id.localeCompare(b[1].id, { numeric: true }),
  },
});

This will sort your story kinds (files) naturally, while stories within one file remain sorted as defined in the source code. Pretty much what you'd expect. Probably this will become the default at some point.

See also https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#storysort-option

All 26 comments

This is based on the way you load stories.
@mnmtanish how about providing a global API to sort stories. (Or sth like that?)

I'm sure we'll have to open some way for developers to customize the story list soon (notifications, etc.). I guess we can make sure users can also sort the list when we do that

If you do, throw in also support for deeper hierarchies;)
A
-- b
-- c
--- d
--- e
-- f

+1

This will be added soon via this PR:
https://github.com/storybooks/react-storybook/pull/705

So this is taking quite long, because we're all having some reservations about the apis and keeping our api surface as small as possible.

@tomitrescak That is going to get released with 3.2.0 #1383

Looks like this has been added. Please reopen if there's still issues.

@danielduan I can't find anything in the code or the docs to allow for ordering - should this issue still be open?

It's part of the options addon:
https://github.com/storybooks/storybook/tree/master/addons/options#getting-started
@simon360

@simon360 After adding the options addon, use this in your config:

setOptions({
  sortStoriesByKind: true
});

This will sort your stories alphabetically no matter in what order you loaded them.

@simon360 @robbertvancaem Would love a PR on our docs to improve to add this 馃檱

This does not work for me. Does anybody else encounter this issue?

To support alphabetically reordering, in .storybook/config.js, just rewrite this line from

  req.keys().forEach(filename => req(filename));

to

  req.keys().sort().forEach(filename => req(filename));

So, if you want Component > View 1 > View 1 Mutation, you could rename them to 1.Component > 2.View 1 > 3.View 1 Mutation

Thanks @SunHuawei

I followed @danielduan's link but it's pointed to master while mine downloaded v3.4.11. I had to update the tag to view the right instructions and it's working with setOptions as described by @robbertvancaem. There was an update in v4 that changed setOptions to withOptions and required the decorator, thus the master documentation is now misleading for the latest since v4 isn't released yet.

v3.4.11 ReadMe: https://github.com/storybooks/storybook/tree/v3.4.11/addons/options

Hi, I check all solution in this issues comment, but those solutions couldn't sort the secondary menu, so how can I sort the secondary menu?

@yukun-kooboo Have you tried the example from master? I haven't tried it, but guessing it might work the same. Looks like you add parameters like so:

storiesOf('Addons|Knobs/Hello', module)
  // If you want to set the option for all stories in of this kind
  .addParameters({ options: { sortStoriesByKind: true } })

Not sure if there's a way to do that globally though instead of manually for each story.

@timbomckay Thanks, but we still use old version storybook, so we need to update it, and then I can add parameters as you say

For the v5 users, see https://github.com/storybooks/storybook/issues/5827 which discusses fixing sortStoriesByKind in v5.2.

For reference, here's the recommended setup for v5.2 onwards:

addParameters({
  options: {
    storySort: (a, b) =>
      a[1].kind === b[1].kind ? 0 : a[1].id.localeCompare(b[1].id, { numeric: true }),
  },
});

This will sort your story kinds (files) naturally, while stories within one file remain sorted as defined in the source code. Pretty much what you'd expect. Probably this will become the default at some point.

See also https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#storysort-option

@ghengeveld Do you know how we can debug those objects that are being sorted? I tried it like this:

    storySort: (a, b) => {
            console.log(a);
            return a[1].kind === b[1].kind ? 0 : a[1].id.compare(b[1].id)
    },

This does not output anything, even when I run start-storybook -p 6006 -c .storybook -s src/static --debug-webpack.

What I'm trying is to get after which properties I can sort, since this doesn't seem documented.

You can see the console.log output in the browser console, not the terminal. Took me a while to figure out too. Here's what you'll get (for a and b):

[
  "components-shapes--square",
  {
    "id": "components-shapes--square",
    "kind": "components|Shapes",
    "name": "Square",
    "story": "Square",
    "parameters": {
      "fileName": "./stories/Shapes.stories.js",
      "options": {
        "hierarchyRootSeparator": "|",
        "hierarchySeparator": {}
      },
      "docs": {},
      "framework": "react"
    }
    // plus some react specific stuff you probably shouldn't touch
  }
]

This is for the story right here.

Don't try to sort by parameters.filename.
After building the storybook the cleartext (./stories/Shapes.stories.js in the above example`) is replaced with a hash of some sort.

Hoping this helps someone. Here is the code I used to set the order of my stories, using v5.3. I wanted to achieve this order:

  • Overview

    • Introduction

    • Any other stories

  • ReactJS

    • Introduction

    • Quickstart

    • Examples

  • Components

    • (component stories can appear in whatever order)

This is the custom sorting function

    storySort: (a, b) => {
      // Sort function to sort stories based on a config file
      // Can handle sorting up to one level deep
      // Any stories that aren't expliciply listed will appear at the end
      // Based off https://github.com/storybookjs/storybook/issues/6327#issuecomment-613122487

      // The order in which we want stories to appear
      const config = [
        {
          category: 'Overview',
          order: ['Introduction'],
        },
        {
          category: 'ReactJS',
          order: [
            'Introduction',
            'Quickstart',
            'Examples',
          ]
        },
        {
          category: 'Components',
        },
      ];

      // assuming storybook uses / to separate everything
      // e.g. Component/Alert
      const story1 = a[1].kind.split('/');
      const story2 = b[1].kind.split('/');

      // util function for getting number
      // given array haystack, returns index for given needle
      // or 9999 so it goes at the end of the list
      function getOrderNumber(needle, haystack) {
        let order = 9999;
        if (Array.isArray(haystack)) {
          order = haystack.findIndex(h => h.toLowerCase() === needle.toLowerCase());
          if (order === -1) order = 9999;
        }
        return order;
      }

      // generate array of top-level categories from config array
      const topLevelOrderArray = config.map(h => h.category);

      const topLevelOrder1 = getOrderNumber(story1[0], topLevelOrderArray);
      const topLevelOrder2 = getOrderNumber(story2[0], topLevelOrderArray);

      // if top-level category is different
      // order based on config array
      // e.g. have Overview|Introduction come before Module|Header
      if (story1[0] !== story2[0]) {
        return topLevelOrder1 - topLevelOrder2;
      }

      // sorting one level deep now
      // if second-level category is different
      // e.g. have ReactJS|Introduction come before ReactJS|Examples
      if (story1[1] !== story2[1]) {
        return getOrderNumber(story1[1], config[topLevelOrder1] && config[topLevelOrder1].order) - getOrderNumber(story2[1], config[topLevelOrder2] && config[topLevelOrder2].order)
      }

      return 0;
    },

I wrote a sorting function similar to what @hanilim wrote above, but with support for any amount of nesting.

const hasKey = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key);
const compareAlphabetical = (a, b) => a.localeCompare(b, { numeric: true });

export const compareStoryPaths = (order, path1, path2) => {
  if (path1.length === 0 && path2.length === 0) {
    return 0;
  }  else if (path1.length === 0 && path2.length > 0) {
    // Path1 must be an ancestor of path2
    return -1;
  } else if (path1.length > 0 && path2.length === 0) {
    // Path2 must be an ancestor of path1
    return 1;
  }

  const [path1Head, ...path1Tail] = path1;
  const [path2Head, ...path2Tail] = path2;

  if (!order) {
    // No reference order, so just sort alphabetically
    const comp = compareAlphabetical(path1Head, path2Head);
    if (comp === 0) {
      return compareStoryPaths(null, path1Tail, path2Tail);
    } else {
      return comp;
    }
  }

  if (path1Head === path2Head) {
    // The two paths share the same head
    const key = path1Head;

    if (hasKey(order, key)) {
      return compareStoryPaths(order[key], path1Tail, path2Tail);
    } else {
      return compareStoryPaths(null, path1Tail, path2Tail);
    }
  }

  if (!hasKey(order, path1Head) && !hasKey(order, path2Head)) {
    return compareStoryPaths(null, path1, path2);
  } else if (hasKey(order, path1Head) && !hasKey(order, path2Head)) {
    return -1; // Give preference to path1, since it is included in the reference order
  } else if (!hasKey(order, path1Head) && hasKey(order, path2Head)) {
    return 1; // Give preference to path2, since it is included in the reference order
  } else {
    // If both heads are in the reference order, use the ordering of the keys in the reference order
    const orderKeys = Object.keys(order);

    return orderKeys.indexOf(path1Head) < orderKeys.indexOf(path2Head) ? -1 : 1;
  }
};

// Example of a reference order
// Note: keys must be in all lowercase
const storiesOrder = {
  'docs': {
    'design guide': {
      'getting started': null,
      'overview': null,
      'color': null,
      'iconography': null,
      'accessibility': null,
      'faq': null,
    },
  },
  'components': {
    'typography': null,
    'layout': null,
    'buttons': null,
    'forms': null,
    'overlays': null,
    'tables': null,
  },
};

addParameters({
  options: {
    storySort: ([story1Id, story1], [story2Id, story2]) => {
      const story1Path = [...story1.kind.split('/'), story1.name].map(key => key.toLowerCase());
      const story2Path = [...story2.kind.split('/'), story2.name].map(key => key.toLowerCase());

      return compareStoryPaths(storiesOrder, story1Path, story2Path);
    },
  },
});

Adding one last update in case people reach this via Google - Storybook 6.0 has a new feature that lets you easily sort stories. https://storybook.js.org/docs/react/writing-stories/naming-components-and-hierarchy#sorting-stories

Copied directly from those docs:

To get alphabetical sorting:

// .storybook/preview.js

export const parameters = {
  options: {
    storySort: {
      method: 'alphabetical',
    },
  },
};

To provide an array of titles:

// .storybook/preview.js

export const parameters = {
  options: {
    storySort: {
      order: ['Intro', 'Pages', ['Home', 'Login', 'Admin'], 'Components'],
    },
  },
};

Full docs: https://storybook.js.org/docs/react/writing-stories/naming-components-and-hierarchy#sorting-stories

Was this page helpful?
0 / 5 - 0 ratings