Skip to content
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

Links: Update LinkTo component #21569

Merged
merged 4 commits into from
Mar 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 8 additions & 6 deletions code/addons/links/src/react/components/link.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,21 @@ describe('LinkTo', () => {
mockAddons.getChannel.mockReturnValue(channel);

const { container } = render(
<LinkTo kind="foo" story="bar">
// eslint-disable-next-line jsx-a11y/anchor-is-valid
<LinkTo title="foo" name="bar">
link
</LinkTo>
);

await waitFor(() => {
expect(screen.getByText('link')).toHaveAttribute(
'href',
'originpathname?search=&id=foo--bar'
'originpathname?path=/story/foo--bar'
);
});
expect(container.firstChild).toMatchInlineSnapshot(`
<a
href="originpathname?search=&id=foo--bar"
href="originpathname?path=/story/foo--bar"
>
link
</a>
Expand All @@ -68,7 +69,8 @@ describe('LinkTo', () => {
mockAddons.getChannel.mockReturnValue(channel);

render(
<LinkTo kind="foo" story="bar">
// eslint-disable-next-line jsx-a11y/anchor-is-valid
<LinkTo title="foo" name="bar">
link
</LinkTo>
);
Expand All @@ -77,8 +79,8 @@ describe('LinkTo', () => {
expect(channel.emit).toHaveBeenLastCalledWith(
SELECT_STORY,
expect.objectContaining({
kind: 'foo',
story: 'bar',
title: 'foo',
name: 'bar',
})
);
});
Expand Down
34 changes: 20 additions & 14 deletions code/addons/links/src/react/components/link.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { ComponentTitle, StoryKind, StoryName } from '@storybook/types';
import type { MouseEvent, ReactNode } from 'react';
import React, { PureComponent } from 'react';

Expand All @@ -20,8 +21,10 @@ const cancelled = (e: MouseEvent<HTMLAnchorElement>, cb = (_e: any) => {}) => {
};

interface Props {
kind: string | null;
story: string | null;
kind?: StoryKind;
title?: ComponentTitle;
story?: StoryName;
name?: StoryName;
children: ReactNode;
}

Expand All @@ -31,8 +34,6 @@ interface State {

export default class LinkTo extends PureComponent<Props, State> {
static defaultProps: Props = {
kind: null,
story: null,
children: undefined,
};

Expand All @@ -45,30 +46,35 @@ export default class LinkTo extends PureComponent<Props, State> {
}

componentDidUpdate(prevProps: Props) {
const { kind, story } = this.props;

if (prevProps.kind !== kind || prevProps.story !== story) {
const { kind, title, story, name } = this.props;

if (
prevProps.kind !== kind ||
prevProps.story !== story ||
prevProps.title !== title ||
prevProps.name !== name
) {
this.updateHref();
}
}

updateHref = async () => {
const { kind, story } = this.props;
if (kind && story) {
const href = await hrefTo(kind, story);
const { kind, title = kind, story, name = story } = this.props;
if (title && name) {
const href = await hrefTo(title, name);
this.setState({ href });
}
};

handleClick = () => {
const { kind, story } = this.props;
if (kind && story) {
navigate({ kind, story });
const { kind, title = kind, story, name = story } = this.props;
if (title && name) {
navigate({ title, name });
}
};

render() {
const { kind, story, children, ...rest } = this.props;
const { kind, title = kind, story, name = story, children, ...rest } = this.props;
const { href } = this.state;

return (
Expand Down
2 changes: 1 addition & 1 deletion code/addons/links/src/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ describe('preview', () => {
describe('hrefTo()', () => {
it('should return promise resolved with story href', async () => {
const href = await hrefTo('title', 'name');
expect(href).toContain('?id=title--name');
expect(href).toContain('?path=/story/title--name');
});
});
});
12 changes: 9 additions & 3 deletions code/addons/links/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { global } from '@storybook/global';
import { addons, makeDecorator } from '@storybook/preview-api';
import { STORY_CHANGED, SELECT_STORY } from '@storybook/core-events';
import type { StoryId, StoryName, ComponentTitle } from '@storybook/types';
import type { StoryId, StoryName, ComponentTitle, StoryKind } from '@storybook/types';
import { toId } from '@storybook/csf';
import { PARAM_KEY } from './constants';

Expand All @@ -11,8 +11,10 @@ interface ParamsId {
storyId: StoryId;
}
interface ParamsCombo {
kind?: ComponentTitle;
kind?: StoryKind;
title?: ComponentTitle;
story?: StoryName;
name?: StoryName;
}

function parseQuery(queryString: string) {
Expand Down Expand Up @@ -41,7 +43,11 @@ export const hrefTo = (title: ComponentTitle, name: StoryName): Promise<string>
// @ts-expect-error (Converted from ts-ignore)
const titleToLink = title || existingId.split('--', 2)[0];
const id = toId(titleToLink, name);
const url = `${location.origin + location.pathname}?${Object.entries({ ...query, id })
const path = `/story/${id}`;

// Drop the `iframe.html` from the preview path
const sbPath = location.pathname.replace(/iframe\.html$/, '');
const url = `${location.origin + sbPath}?${Object.entries({ path })
.map((item) => `${item[0]}=${item[1]}`)
.join('&')}`;

Expand Down
41 changes: 24 additions & 17 deletions code/lib/manager-api/src/modules/stories.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
import { global } from '@storybook/global';
import { toId, sanitize } from '@storybook/csf';
import type {
StoryKind,
ComponentTitle,
StoryName,
StoryId,
Args,
API_ComposedRef,
API_HashEntry,
API_LeafEntry,
API_PreparedStoryIndex,
SetStoriesPayload,
API_StoryEntry,
StoryIndex,
API_LoadedRefData,
API_IndexHash,
} from '@storybook/types';
import {
PRELOAD_ENTRIES,
STORY_PREPARED,
Expand All @@ -18,19 +34,6 @@ import {
} from '@storybook/core-events';
import { logger } from '@storybook/client-logger';

import type {
StoryId,
Args,
API_ComposedRef,
API_HashEntry,
API_LeafEntry,
API_PreparedStoryIndex,
SetStoriesPayload,
API_StoryEntry,
StoryIndex,
API_LoadedRefData,
API_IndexHash,
} from '@storybook/types';
// eslint-disable-next-line import/no-cycle
import { getEventMetadata } from '../lib/events';

Expand Down Expand Up @@ -480,21 +483,25 @@ export const init: ModuleFn<SubAPI, SubState, true> = ({
SELECT_STORY,
function handler({
kind,
title = kind,
story,
name = story,
storyId,
...rest
}: {
kind: string;
story: string;
kind?: StoryKind;
title?: ComponentTitle;
story?: StoryName;
name?: StoryName;
storyId: string;
viewMode: ViewMode;
}) {
const { ref } = getEventMetadata(this, fullAPI);

if (!ref) {
fullAPI.selectStory(storyId || kind, story, rest);
fullAPI.selectStory(storyId || title, name, rest);
} else {
fullAPI.selectStory(storyId || kind, story, { ...rest, ref: ref.id });
fullAPI.selectStory(storyId || title, name, { ...rest, ref: ref.id });
}
}
);
Expand Down