Skip to content

Commit

Permalink
Merge pull request #14875 from robhil/single-story-mode
Browse files Browse the repository at this point in the history
Core: Single story option in iframe view
  • Loading branch information
shilman authored May 19, 2021
2 parents 2ccb609 + 69880ed commit b88ab7c
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 3 deletions.
74 changes: 74 additions & 0 deletions lib/client-api/src/story_store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1613,4 +1613,78 @@ describe('preview.story_store', () => {
expect(onStorySpecified).not.toHaveBeenCalled();
});
});

describe('In Single Story mode', () => {
describe('when storySpecifier is story id', () => {
it('adds only one story specified in selection specifier when addStory is called', () => {
const store = new StoryStore({ channel });
store.setSelectionSpecifier({
storySpecifier: toId('kind-1', 'story-1.1'),
viewMode: 'story',
singleStory: true,
});

store.startConfiguring();
addStoryToStore(store, 'kind-1', 'story-1.1', () => 0);
addStoryToStore(store, 'kind-1', 'story-1.2', () => 0);
store.finishConfiguring();

expect(store.fromId(toId('kind-1', 'story-1.1'))).toBeTruthy();
expect(store.fromId(toId('kind-1', 'story-1.2'))).toBeFalsy();
});

it('adds only kind metadata specified in selection specifier when addKindMetadata is called', () => {
const store = new StoryStore({ channel });
store.setSelectionSpecifier({
storySpecifier: toId('kind-1', 'story-1.1'),
viewMode: 'story',
singleStory: true,
});

store.startConfiguring();
store.addKindMetadata('kind-1', {});
store.addKindMetadata('kind-2', {});
store.finishConfiguring();

expect(store._kinds['kind-1']).toBeDefined();
expect(store._kinds['kind-2']).not.toBeDefined();
});
});

describe('when storySpecifier is object', () => {
it('adds only one story specified in selection specifier when addStory is called', () => {
const store = new StoryStore({ channel });
store.setSelectionSpecifier({
storySpecifier: { kind: 'kind-1', name: 'story-1.1' },
viewMode: 'story',
singleStory: true,
});

store.startConfiguring();
addStoryToStore(store, 'kind-1', 'story-1.1', () => 0);
addStoryToStore(store, 'kind-1', 'story-1.2', () => 0);
store.finishConfiguring();

expect(store.fromId(toId('kind-1', 'story-1.1'))).toBeTruthy();
expect(store.fromId(toId('kind-1', 'story-1.2'))).toBeFalsy();
});

it('adds only kind metadata specified in selection specifier when addKindMetadata is called', () => {
const store = new StoryStore({ channel });
store.setSelectionSpecifier({
storySpecifier: { kind: 'kind-1', name: 'story-1.1' },
viewMode: 'story',
singleStory: true,
});

store.startConfiguring();
store.addKindMetadata('kind-1', {});
store.addKindMetadata('kind-2', {});
store.finishConfiguring();

expect(store._kinds['kind-1']).toBeDefined();
expect(store._kinds['kind-2']).not.toBeDefined();
});
});
});
});
50 changes: 50 additions & 0 deletions lib/client-api/src/story_store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import deprecate from 'util-deprecate';
import { Channel } from '@storybook/channels';
import Events from '@storybook/core-events';
import { logger } from '@storybook/client-logger';
import { sanitize, toId } from '@storybook/csf';
import {
Comparator,
Parameters,
Expand All @@ -33,6 +34,7 @@ import {
ArgTypesEnhancer,
StoreSelectionSpecifier,
StoreSelection,
StorySpecifier,
} from './types';
import { combineArgs, mapArgsToTypes, validateOptions } from './args';
import { HooksContext } from './hooks';
Expand All @@ -50,6 +52,22 @@ type KindMetadata = StoryMetadata & { order: number };

const STORAGE_KEY = '@storybook/preview/store';

function extractSanitizedKindNameFromStorySpecifier(storySpecifier: StorySpecifier): string {
if (typeof storySpecifier === 'string') {
return storySpecifier.split('--').shift();
}

return sanitize(storySpecifier.kind);
}

function extractIdFromStorySpecifier(storySpecifier: StorySpecifier): string {
if (typeof storySpecifier === 'string') {
return storySpecifier;
}

return toId(storySpecifier.kind, storySpecifier.name);
}

const isStoryDocsOnly = (parameters?: Parameters) => {
return parameters && parameters.docsOnly;
};
Expand Down Expand Up @@ -313,6 +331,10 @@ export default class StoryStore {
}

addKindMetadata(kind: string, { parameters = {}, decorators = [], loaders = [] }: StoryMetadata) {
if (this.shouldBlockAddingKindMetadata(kind)) {
return;
}

this.ensureKind(kind);
if (parameters) {
checkGlobals(parameters);
Expand Down Expand Up @@ -347,6 +369,21 @@ export default class StoryStore {
);
}

shouldBlockAddingStory(id: string): boolean {
return (
this.isSingleStoryMode() &&
id !== extractIdFromStorySpecifier(this._selectionSpecifier.storySpecifier)
);
}

shouldBlockAddingKindMetadata(kind: string): boolean {
return (
this.isSingleStoryMode() &&
sanitize(kind) !==
extractSanitizedKindNameFromStorySpecifier(this._selectionSpecifier.storySpecifier)
);
}

addStory(
{
id,
Expand All @@ -369,6 +406,10 @@ export default class StoryStore {
'Cannot add a story when not configuring, see https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#story-store-immutable-outside-of-configuration'
);

if (this.shouldBlockAddingStory(id)) {
return;
}

checkGlobals(storyParameters);
checkStorySort(storyParameters);

Expand Down Expand Up @@ -694,6 +735,15 @@ export default class StoryStore {
}
}

isSingleStoryMode(): boolean {
if (!this._selectionSpecifier) {
return false;
}

const { singleStory, storySpecifier } = this._selectionSpecifier;
return storySpecifier && storySpecifier !== '*' && singleStory;
}

getSelection = (): StoreSelection => this._selection;

getDataForManager = () => {
Expand Down
3 changes: 2 additions & 1 deletion lib/client-api/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,12 @@ export interface StoryMetadata {
export type ArgTypesEnhancer = (context: StoryContext) => ArgTypes;
export type ArgsEnhancer = (context: StoryContext) => Args;

type StorySpecifier = StoryId | { name: StoryName; kind: StoryKind } | '*';
export type StorySpecifier = StoryId | { name: StoryName; kind: StoryKind } | '*';

export interface StoreSelectionSpecifier {
storySpecifier: StorySpecifier;
viewMode: ViewMode;
singleStory?: boolean;
args?: Args;
}

Expand Down
12 changes: 12 additions & 0 deletions lib/core-client/src/preview/url.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,29 +73,41 @@ describe('url', () => {
expect(getSelectionSpecifierFromPath()).toEqual({
storySpecifier: 'story--id',
viewMode: 'story',
singleStory: false,
});
});
it('should handle id queries with *', () => {
document.location.search = '?id=*';
expect(getSelectionSpecifierFromPath()).toEqual({
storySpecifier: '*',
viewMode: 'story',
singleStory: false,
});
});
it('should redirect legacy queries', () => {
document.location.search = '?selectedKind=kind&selectedStory=story';
expect(getSelectionSpecifierFromPath()).toEqual({
storySpecifier: { kind: 'kind', name: 'story' },
viewMode: 'story',
singleStory: false,
});
});
it('should parse args', () => {
document.location.search = '?id=story--id&args=obj.key:val';
expect(getSelectionSpecifierFromPath()).toEqual({
storySpecifier: 'story--id',
viewMode: 'story',
singleStory: false,
args: { obj: { key: 'val' } },
});
});
it('should handle singleStory param', () => {
document.location.search = '?id=abc&singleStory=true';
expect(getSelectionSpecifierFromPath()).toEqual({
storySpecifier: 'abc',
viewMode: 'story',
singleStory: true,
});
});
});
});
5 changes: 3 additions & 2 deletions lib/core-client/src/preview/url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,12 @@ export const getSelectionSpecifierFromPath: () => StoreSelectionSpecifier = () =
viewMode = 'story';
}

const singleStory = getFirstString(query.singleStory) === 'true';
const path = getFirstString(query.path);
const storyId = path ? pathToId(path) : getFirstString(query.id);

if (storyId) {
return { storySpecifier: storyId, args, viewMode };
return { storySpecifier: storyId, args, viewMode, singleStory };
}

// Legacy URL format
Expand All @@ -85,7 +86,7 @@ export const getSelectionSpecifierFromPath: () => StoreSelectionSpecifier = () =

if (kind && name) {
deprecatedLegacyQuery();
return { storySpecifier: { kind, name }, args, viewMode };
return { storySpecifier: { kind, name }, args, viewMode, singleStory };
}
return null;
};

0 comments on commit b88ab7c

Please sign in to comment.