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

UI: Add external links to navigation #8047

Open
shilman opened this issue Sep 10, 2019 · 9 comments
Open

UI: Add external links to navigation #8047

shilman opened this issue Sep 10, 2019 · 9 comments

Comments

@shilman
Copy link
Member

shilman commented Sep 10, 2019

The left-hand navigation of storybook consists of links to stories, arranged hierarchically. The Storybook logo link is also configurable (as well as visually).

In 5.2, we added the ability to link to "documentation-only stories" which is markdown-based documentation that can be interspersed within your stories.

Let's also allow the user to embed links to other external resources in the same navigation tree.

One possible implementation

In storiesOf:

storiesOf(...)
  .add('storybook website', 'http://storybook.js.org', { isLink: true })

In CSF:

export default { ... }
export const storybookWebsite = 'http://storybook.js.org'
storybookWebsite.story {
  isLink: true,
}

In MDX:

<Navigation name='storybook website' href='http://storybook.js.org' />
@stale
Copy link

stale bot commented Oct 1, 2019

Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!

@stale stale bot added the inactive label Oct 1, 2019
@shilman shilman added the todo label Oct 1, 2019
@stale stale bot removed the inactive label Oct 1, 2019
@trevoreyre
Copy link
Contributor

Is there a workaround to achieve this functionality now? Like hooking into the Storybook API somehow to redirect to an external page when a specific story is selected?

@fabian-hiller
Copy link
Member

This feature is especially important for design systems and documentation. Has the implementation already started @shilman?

@shilman
Copy link
Member Author

shilman commented Apr 14, 2020

Nope, @fabian-hiller want to give it a try?

@fabian-hiller
Copy link
Member

Nope, @fabian-hiller want to give it a try?

Yes I want to take a look at it. Can you give me a hint in which files to search?

@shilman
Copy link
Member Author

shilman commented Apr 15, 2020

@daveconnis
Copy link

Hello frens!

@shilman Did this ever get any action? Looking to do this currently in our design system.

@dimitrieh
Copy link

Looking for a way to do this ideally

@shilman shilman removed the todo label Jun 20, 2023
@Sidnioulz
Copy link
Contributor

Until someone finds the time to build this properly into Storybook, here is a HACK that works with Storybook 8 and React installed (regardless of the actual framework).

Warning

This is NOT a sustainable approach to the problem. It relies on CSS escape hatches that will likely break repeatedly and require maintenance as Storybook evolves. Please understand also that in the example below, CSS was hardcoded to match the default dark Storybook theme and must be adjusted.

image

General idea

  • We create a special type of MDX page, with a naming pattern in the title/id
  • We use sidebar.renderLabel to hack the output when we find this pattern
  • The hack involves a lot of CSS massaging to circumvent the default link
  • For safety reasons, we also redirect to the previous page when a sidebar link is clicked, so that the fake MDX page doesn't get shown to the end user
  • The MDX pages acting as links must follow a strict naming pattern

There are ways to do this better. https://storybook.js.org/docs/api/main-config/main-config-indexers will give us a much better way to create stories with much cleaner data for use in the sidebar label renderer. And of course the label renderer could be replaced by a proper implementation like the ones shilman suggested, should anyone have free time!

Tip

Make this work better: contribute a PR.

manager-head.html

Lots of ugly hacks in there. You'll need to adjust for your theme. I did not test this in mobile layouts.

<style>
  .sidebar-link {
    /* Take up the whole button's click area. */
    position: absolute;
    inset: 0;
    display: flex;
    align-items: start;

    /* Copied from SB. For left, we do 22px (padding) + 14px (icon) + 6px (gap). */
    padding-left: 42px;
    padding-top: 7px;
    padding-bottom: 4px;

    /* Copied from SB. Typography. */
    font-size: 14px;
    text-decoration: none;
    overflow-wrap: break-word;
    word-wrap: break-word;
    word-break: break-word;

    /* Copied from SB. Interaction. */
    cursor: pointer;
    color: inherit;
  }

  .sidebar-link svg {
    position: absolute;
    left: 21px;
    top: 6px;
    height: 16px;
    width: 16px;
    background-color: #222425;
    color: #c3ff5a;
  }
  .sidebar-link:hover svg {
    background-color: #202c34;
  }

  .invisible {
    /* This one is just so that the parent div reserves enough space for overflowing text. */
    visibility: hidden;
    font-size: 14px;
    overflow-wrap: break-word;
    word-wrap: break-word;
    word-break: break-word;
    padding-top: 2px;
  }
</style>

manager.tsx

import { addons } from '@storybook/manager-api';
import SidebarLabelWrapper from './components/SidebarLabelWrapper';

export const sidebar = {
  showRoots: true,
  renderLabel: (item) => SidebarLabelWrapper({ item })
};

addons.setConfig({
  sidebar
});

components/SidebarLabelWrapper.tsx

This component appears on top of the normal sidebar link. Note we also render a visibility: hidden version of the text, on top of the absolutely positioned HTML link, to ensure Storybook reserves enough space for the link. Yes, this is messy.

import React from 'react';

import IconSidebarExternal from './IconSidebarExternal';

const SidebarLabelWrapper = ({ item }) => {
  if (item.type === 'docs' && item.id.startsWith('url-')) {
    const [, title, encodedURI] = item.title.split('@');

    const onClick = (e) => {
      e.preventDefault();
      window.open(decodeURIComponent(encodedURI));
    };

    return (
      <>
        <a
          className='sidebar-link'
          onClick={onClick}
          href={decodeURIComponent(encodedURI)}
          target="_blank"
        >
          <IconSidebarExternal />
          <span className="sidebar-link-label">{title}</span>
        </a>
        <span className="invisible" aria-hidden>{title}</span>
      </>
    )
  }
};

export default SidebarLabelWrapper;

components/IconSidebarExternal.tsx

Make this one yourself 😄

  • Just a basic SVG you want to use, wrapped in a React shell
  • Make sure to use currentColor as a fill attribute

components/RedirectBack.tsx

This one is for quality of life. It ensures we don't actually load the fake MDX pages and stay on them.

import React, { useEffect } from 'react';

const RedirectBack = () => {
  useEffect(() => {
    window.history.back();
  });

  return <p>You will be redirected.</p>
};

export default RedirectBack;

stories/OneFakeStoryPerLink.mdx

Here we load the RedirectBack component and we write a title that's legal and matches the following parts:

  • url@ prefix
  • Then the actual title, and another @ separator
  • Then an encoded URL
import { Meta } from "@storybook/blocks";
import RedirectBack from "../../.storybook/components/RedirectBack";

<Meta title="url@Accessibility Overlays@https%3A%2F%2Fshouldiuseanaccessibilityoverlay.com%2F" />

<RedirectBack />

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants