Skip to content

Commit

Permalink
Merge pull request #21840 from agriffis/fix/auto-title-dotted-component
Browse files Browse the repository at this point in the history
CSF: Autotitle bug fix and enhancement
  • Loading branch information
shilman authored Nov 28, 2023
2 parents 06fb59f + e7b3993 commit ca659bd
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 33 deletions.
17 changes: 15 additions & 2 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
- [From version 7.x to 8.0.0](#from-version-7x-to-800)
- [Implicit actions can not be used during rendering (for example in the play function)](#implicit-actions-can-not-be-used-during-rendering-for-example-in-the-play-function)
- [Core changes](#core-changes)
- [Autotitle breaking fixes](#autotitle-breaking-fixes)
- [React v18 in the manager UI (including addons)](#react-v18-in-the-manager-ui-including-addons)
- [Storyshots has been removed](#storyshots-has-been-removed)
- [Storyshots has been removed](#storyshots-has-been-removed)
- [UI layout state has changed shape](#ui-layout-state-has-changed-shape)
- [New UI and props for Button and IconButton components](#new-ui-and-props-for-button-and-iconbutton-components)
- [Icons is deprecated](#icons-is-deprecated)
Expand Down Expand Up @@ -373,14 +374,26 @@ To summarize:

### Core changes

#### Autotitle breaking fixes

In Storybook 7, the file name `path/to/foo.bar.stories.js` would result in the [autotitle](https://storybook.js.org/docs/react/configure/overview#configure-story-loading) `path/to/foo`. In 8.0, this has been changed to generate `path/to/foo.bar`. We consider this a bugfix but it is also a breaking change if you depended on the old behavior. To get the old titles, you can manually specify the desired title in the default export of your story file. For example:

```js
export default {
title: 'path/to/foo',
}
```

Alternatively, if you need to achieve a different behavior for a large number of files, you can provide a [custom indexer](https://storybook.js.org/docs/7.0/vue/configure/sidebar-and-urls#processing-custom-titles) to generate the titles dynamically.

#### React v18 in the manager UI (including addons)

Storybook 7 used React 16 in the manager. In Storybook 8 this is upgraded to react v18.
Addons that inject UI into panels, tools, etc. are possibly affected by this.

Addon authors are advised to upgrade to react v18.

##### Storyshots has been removed
#### Storyshots has been removed

Storyshots was an addon for storybook which allowed users to turn their stories into automated snapshot-tests.

Expand Down
61 changes: 61 additions & 0 deletions code/lib/preview-api/src/modules/store/autoTitle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,39 @@ describe('userOrAutoTitleFromSpecifier', () => {
).toMatchInlineSnapshot(`to/button`);
});

it('match with trailing stories', () => {
expect(
userOrAuto(
'./path/to/button/stories.js',
normalizeStoriesEntry({ directory: './path', files: '**/?(*.)stories.*' }, options),
undefined
)
).toMatchInlineSnapshot(`to/button`);
});

it('match with trailing stories (windows path)', () => {
expect(
userOrAuto(
'./path/to/button/stories.js',
normalizeStoriesEntry(
{ directory: '.\\path\\', files: '**/?(*.)stories.*' },
winOptions
),
undefined
)
).toMatchInlineSnapshot(`to/button`);
});

it('match with dotted component', () => {
expect(
userOrAuto(
'./path/to/button/button.group.stories.js',
normalizeStoriesEntry({ directory: './path' }, options),
undefined
)
).toMatchInlineSnapshot(`to/button/button.group`);
});

it('match with hyphen path', () => {
expect(
userOrAuto(
Expand All @@ -207,6 +240,18 @@ describe('userOrAutoTitleFromSpecifier', () => {
).toMatchInlineSnapshot(`to_my/file`);
});

it('match with short path', () => {
// Make sure "stories" isn't trimmed as redundant when there won't be
// anything left.
expect(
userOrAuto(
'./path/stories.js',
normalizeStoriesEntry({ directory: './path', files: '**/?(*.)stories.*' }, options),
undefined
)
).toMatchInlineSnapshot(`stories`);
});

it('match with windows path', () => {
expect(
userOrAuto(
Expand Down Expand Up @@ -279,5 +324,21 @@ describe('userOrAutoTitleFromSpecifier', () => {
).toMatchInlineSnapshot(`to_my/MyButton`);
});
});

it('Story.stories.js', () => {
expect(
userOrAuto(
'../blocks/src/Story.stories.tsx',
normalizeStoriesEntry(
{
directory: '../blocks/src',
titlePrefix: '@blocks',
},
options
),
undefined
)
).toMatchInlineSnapshot(`@blocks/Story`);
});
});
});
53 changes: 22 additions & 31 deletions code/lib/preview-api/src/modules/store/autoTitle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,22 @@ import type { NormalizedStoriesSpecifier } from '@storybook/types';
// FIXME: types duplicated type from `core-common', to be
// removed when we remove v6 back-compat.

const stripExtension = (path: string[]) => {
let parts = [...path];
const last = parts[parts.length - 1];
const dotIndex = last.indexOf('.');
const stripped = dotIndex > 0 ? last.substr(0, dotIndex) : last;
parts[parts.length - 1] = stripped;
const [first, ...rest] = parts;
if (first === '') {
parts = rest;
}
return parts;
};
// deal with files like "atoms/button/{button,index}.stories.js"
const sanitize = (parts: string[]) => {
if (parts.length === 0) return parts;

const indexRe = /^index$/i;
const last = parts[parts.length - 1];
const lastStripped = last?.replace(/(?:[.](?:story|stories))?([.][^.]+)$/i, '');
if (parts.length === 1) return [lastStripped];

// deal with files like "atoms/button/{button,index}.stories.js"
const removeRedundantFilename = (paths: string[]) => {
let prevVal: string;
return paths.filter((val, index) => {
if (index === paths.length - 1 && (val === prevVal || indexRe.test(val))) {
return false;
}
prevVal = val;
return true;
});
const nextToLast = parts[parts.length - 2];
return lastStripped &&
nextToLast &&
(lastStripped === nextToLast ||
/^(story|stories)([.][^.]+)$/i.test(last) ||
/^index$/i.test(lastStripped))
? parts.slice(0, -1)
: [...parts.slice(0, -1), lastStripped];
};

/**
Expand All @@ -41,8 +32,10 @@ const removeRedundantFilename = (paths: string[]) => {
* @returns joined path string, with single '/' between parts
*/
function pathJoin(paths: string[]): string {
const slashes = new RegExp('/{1,}', 'g');
return paths.join('/').replace(slashes, '/');
return paths
.flatMap((p) => p.split('/'))
.filter(Boolean)
.join('/');
}

export const userOrAutoTitleFromSpecifier = (
Expand All @@ -67,18 +60,16 @@ export const userOrAutoTitleFromSpecifier = (
if (importPathMatcher.exec(normalizedFileName)) {
if (!userTitle) {
const suffix = normalizedFileName.replace(directory, '');
const titleAndSuffix = slash(pathJoin([titlePrefix, suffix]));
let path = titleAndSuffix.split('/');
path = stripExtension(path);
path = removeRedundantFilename(path);
return path.join('/');
let parts = pathJoin([titlePrefix, suffix]).split('/');
parts = sanitize(parts);
return parts.join('/');
}

if (!titlePrefix) {
return userTitle;
}

return slash(pathJoin([titlePrefix, userTitle]));
return pathJoin([titlePrefix, userTitle]);
}

return undefined;
Expand Down

0 comments on commit ca659bd

Please sign in to comment.