Storybook: Question: Looking for nice storySort function

Created on 11 Jan 2020  路  3Comments  路  Source: storybookjs/storybook

does anyone have a nice storySort function?
e.g. I would like to give orderings for each "level"

I have the following stories according to file system input

Buttons/Button/Default
Buttons/Intro
Buttons/Switch/Default
Forms/Input/Default
Forms/Intro
Forms/System
Forms/System/Overview
Forms/System/Registration
Forms/System/Rational
Forms/Textarea/Default
Overlays/Dialog/Default
Overlays/Intro
Overlays/System/Overview
Overlays/Tooltip/Default

What I would like to have is

== Forms ==
  - Intro
  - Input
    - Default
  - Textarea
    - Default
  - System
    - Overview
    - Registration
    - Rational
== Buttons ==
  - Intro
  - Button
    - Default
  - Switch
    - Default
== Overlays ==
  - Intro
  - Dialog
    - Default
  - ToolTip
    - Default
  - System
    - Overview

e.g. I would like to be in control for the first and second level
a possible configuration could look like so

addParameters({
  options: {
    showRoots: true,
    storySort: storySort([
      ['Forms', 'Buttons', 'Overlays', '...'] // first level - ordered like this rest afterwards alphabetically
      ['Intro', '...', 'System'] // second level - Intro first, System last inbetween alphabetically
      // third+ levels - "don't touch" => e.g. same as in code/file system order
   ]);
  },
});

PS: posted as an issue here as I don't know a better place to discuss possible bigger code snippets - I hope that is ok 馃

question / support

Most helpful comment

I found https://gist.github.com/JohnAlbin/83ef5282d07c6aa447ff39719429843e and adapted it to my use-case 馃挭

thx @JohnAlbin 馃

/**
 * Sorts each depth level according to the provided array order.
 * 
 * @example
 * addParameters({
 *   options: {
 *     showRoots: true,
 *     storySort: sortEachDepth([
 *       ['Intro', 'Forms', 'Buttons', '...'] // 1. level - ordered like this rest default order
 *       ['Intro', '...', 'System'], // 2. level - Intro first, System last in between default order
 *       ['Overview', '...'] // 3. level - Intro first rest default order
 *     ]),   
 * });
 * 
 * @param {array} orderPerDepth array of arrays giving the order of each level
 */
function sortEachDepth(orderPerDepth) {
  return (a, b) => {
    // If the two stories have the same story kind, then use the default
    // ordering, which is the order they are defined in the story file.
    if (a[1].kind === b[1].kind) {
      return 0;
    }
    const storyKindA = a[1].kind.split('/');
    const storyKindB = b[1].kind.split('/');
    let depth = 0;
    let nameA, nameB, indexA, indexB;
    let ordering = orderPerDepth[0] || [];
    while (true) {
      nameA = storyKindA[depth] ? storyKindA[depth] : '';
      nameB = storyKindB[depth] ? storyKindB[depth] : '';

      if (nameA === nameB) {
        // We'll need to look at the next part of the name.
        depth++;
        ordering = orderPerDepth[depth] || [];
        continue;
      } else {
        // Look for the names in the given `ordering` array.
        indexA = ordering.indexOf(nameA);
        indexB = ordering.indexOf(nameB);

        // If at least one of the names is found, sort by the `ordering` array.
        if (indexA !== -1 || indexB !== -1) {
          // If one of the names is not found in `ordering`, list it at the place of '...' or last.
          if (indexA === -1) {
            indexA = ordering.indexOf('...') || ordering.length;
          }
          if (indexB === -1) {
            indexB = ordering.indexOf('...') || ordering.length;
          }
          return indexA - indexB;
        }
      }
      // Otherwise, use alphabetical order.
      return nameA.localeCompare(nameB);
    }
  };
};

All 3 comments

I found https://gist.github.com/JohnAlbin/83ef5282d07c6aa447ff39719429843e and adapted it to my use-case 馃挭

thx @JohnAlbin 馃

/**
 * Sorts each depth level according to the provided array order.
 * 
 * @example
 * addParameters({
 *   options: {
 *     showRoots: true,
 *     storySort: sortEachDepth([
 *       ['Intro', 'Forms', 'Buttons', '...'] // 1. level - ordered like this rest default order
 *       ['Intro', '...', 'System'], // 2. level - Intro first, System last in between default order
 *       ['Overview', '...'] // 3. level - Intro first rest default order
 *     ]),   
 * });
 * 
 * @param {array} orderPerDepth array of arrays giving the order of each level
 */
function sortEachDepth(orderPerDepth) {
  return (a, b) => {
    // If the two stories have the same story kind, then use the default
    // ordering, which is the order they are defined in the story file.
    if (a[1].kind === b[1].kind) {
      return 0;
    }
    const storyKindA = a[1].kind.split('/');
    const storyKindB = b[1].kind.split('/');
    let depth = 0;
    let nameA, nameB, indexA, indexB;
    let ordering = orderPerDepth[0] || [];
    while (true) {
      nameA = storyKindA[depth] ? storyKindA[depth] : '';
      nameB = storyKindB[depth] ? storyKindB[depth] : '';

      if (nameA === nameB) {
        // We'll need to look at the next part of the name.
        depth++;
        ordering = orderPerDepth[depth] || [];
        continue;
      } else {
        // Look for the names in the given `ordering` array.
        indexA = ordering.indexOf(nameA);
        indexB = ordering.indexOf(nameB);

        // If at least one of the names is found, sort by the `ordering` array.
        if (indexA !== -1 || indexB !== -1) {
          // If one of the names is not found in `ordering`, list it at the place of '...' or last.
          if (indexA === -1) {
            indexA = ordering.indexOf('...') || ordering.length;
          }
          if (indexB === -1) {
            indexB = ordering.indexOf('...') || ordering.length;
          }
          return indexA - indexB;
        }
      }
      // Otherwise, use alphabetical order.
      return nameA.localeCompare(nameB);
    }
  };
};

@daKmoR FYI @JohnAlbin also put together this great PR to make it easier https://github.com/storybookjs/storybook/pull/9188

@shilman
unfortunately, the solution in the PR does not work for my use-case as we want to have the same rules/structure for each 2nd, 3rd level - e.g. it would mean repeating (all nesting rules) ourselves for each 1st level... 馃檲

also we want to define certain things first, last and decide if in between is alphabetically or source order...

doing that with a custom storySort works quite nice now 馃

As the configuration options are incompatible to the existing solution we can not really combine it 馃檲
It would need to be a totally separate configuration? maybe sortByLevel and leave sort as is? also, it would need to throw an error if both are provided...

So I am not sure if such special "needs" would make sense globally for storybook?

the only thing I can think of would be to distribute multiple storySort functions? so I would do

import { sortByLevel, sortWithNesting } from '...';

addParameters({
  options: {
    showRoots: true,
    storySort: sortByLevel(...)

then every sort functionality could bring their own options - that would leave the options parameter cleaner and you would not need to check for invalid options configurations

Was this page helpful?
0 / 5 - 0 ratings

Related issues

bpeab picture bpeab  路  70Comments

tycho01 picture tycho01  路  76Comments

enagy27 picture enagy27  路  69Comments

aericson picture aericson  路  97Comments

moimikey picture moimikey  路  67Comments