-
-
Notifications
You must be signed in to change notification settings - Fork 9.4k
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
Sorting stories #548
Comments
This is based on the way you load stories. |
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;) |
+1 |
This will be added soon via this PR: |
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 |
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: |
@simon360 After adding the options addon, use this in your config:
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 is an attempt to at least be able to have Storybook order its menu alphabetically, so we can then possibly add a top story which serves as the default and could be a readme. So far not working! See storybookjs/storybook#548 (comment)
This is an attempt to enable Storybook options so we can at least have it display its stories (and sections) in alphabetical order. Then perhaps we can create a default story which will display first and could act as the readme. So far not working! See storybookjs/storybook#548 (comment)
This does not work for me. Does anybody else encounter this issue? |
To support alphabetically reordering, in
to
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 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 #5827 which discusses fixing |
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:
This does not output anything, even when I run 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 [
"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 |
Hoping this helps someone. Here is the code I used to set the order of my stories, using
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 |
heads up, it is possible to use both
|
Unfortunately, the current sorting configuration works only for two-level storybooks:
If you group your components by one more level, the stories will move to the third level and you won't be able to sort them. For example: .
├── Articles
│ ├── Getting Started.mdx
│ └── Versioning.mdx
├── Components
│ └── Header
│ ├── Collapsed.mdx
│ ├── Default.mdx
│ └── Expanded.mdx
└── Elements
├── Button
│ ├── Active.mdx
│ └── Default.mdx
└── Link
├── Active.mdx
└── Default.mdx Let's say, that you want:
storySort: {
order: ['Articles', '*', ['*', ['Default', '*']]]
} I wonder why the sorting wasn't implemented for any grouping depth. I took @mkrause's sorting function and added support for a wildcard key - const hasKey = (obj, key) => Object.hasOwn(obj, key);
const compareAlphabetical = (a, b) => a.localeCompare(b, { numeric: true });
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; try either the key for the head, or the
// wildcard key, otherwise pass `undefined` to sort without an explicit order
return compareStoryPaths(order[path1Head] || order['*'], path1Tail, path2Tail);
}
if (hasKey(order, path1Head) && hasKey(order, path2Head)) {
// 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;
} 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 {
// No explicit order for the path heads was found, try the wildcard key,
// otherwise pass `undefined` to sort without an explicit order
return compareStoryPaths(order['*'], path1, path2);
}
};
const storiesOrder = {
articles: {},
elements: {
'*': { default: null, },
},
components: {
'*': { default: null, },
},
};
export const parameters = {
options: {
storySort: ([, story1], [, 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);
},
},
}; The result of the sorting is: .
├── Articles
│ ├── Getting Started.mdx
│ └── Versioning.mdx
├── Elements
│ ├── Button
│ │ ├── Default.mdx
│ │ └── Active.mdx
│ └── Link
│ ├── Default.mdx
│ └── Active.mdx
└── Components
└── Header
├── Default.mdx
├── Collapsed.mdx
└── Expanded.mdx |
I found it helpful in more than one projects and published the code from the comment above as an NPM package storybook-multilevel-sort: // .storybook/preview.js
import sort from 'storybook-multilevel-sort'
const order = {
articles: null,
elements: {
'*': { default: null }
},
components: {
'*': { default: null }
}
}
export const parameters = {
options: {
storySort: (story1, story2) => sort(order, story1, story2)
}
} |
All that does not seem to work for me, and to be honest, seem way too complicated for such a simple feature like sorting my sub-stories in a specific way. I remember that in a (much) older version of storybook I had something like an "order" parameter (I think) in the (I think) the most natural way of describing the hierarchy storybook is featuring, is an object property hierarchy. That's also what many of the others showed, when they implemented such sorting in a custom function. I would love to see something like that built-in rather than customized by users. This would allow to easily describe patterns of sorted stories, especially when allowing for wildcards. For now, I'm going with renaming the files to create a certain order, but as others mentioned, this can easily break when building it - so, I'm curious what happens ^^ |
Clearly as @prantlf mentioned there's a regression - in Storybook 6 it's not possible anymore to sort stories of a specific component in the source code order, while in Storybook 5 it was possible. |
Hi, the stories are listed in rather random order, is it possible to sort them alphabetically? This is how it looks on my end.
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 ...
The text was updated successfully, but these errors were encountered: