Skip to content

Instantly share code, notes, and snippets.

@devonChurch
Created August 19, 2019 22:00
Show Gist options
  • Save devonChurch/de5efc2701cc63456b4193da151e1590 to your computer and use it in GitHub Desktop.
Save devonChurch/de5efc2701cc63456b4193da151e1590 to your computer and use it in GitHub Desktop.
Get path ID for every Story in sidebar
// Reveals ALL of the Story links in the Storybook Manager Sidebar. From there we
// return a list of all of the paths associated to getting each Story to open in
// an iFrame.
//
// The problem here is that Storybook will only inject the Story links associated
// under a Story group when it's been triggered by the user. In that regard we
// cannot scrape the DOM initially and instead need to programatically open all
// Story groups and then take an inventory of their revealed Story links.
// The "option" links (not links that trigger a story but links that open up the
// hierarchical group with the stories inside).
const OPTIONS = "a:not(#explorerstyle):not([href]):not([data-cypress-clicked])"; // prettier-ignore
// The links accosted to triggering an individual Story to render in an iFrame.
const LINKS = 'a:not(#explorerstyle) ~ div a[href*="/?path=/story/"]';
// NOTE: ☝️ The above selectors are not fetching Stories associated to our CSS
// style guide and instead only focus on our component architecture.
const getStoryPathIds = () => {
const menuNode = document.querySelector("nav.sidebar-container");
// Our recursive function that triggers all unopened Story options. Remember
// that new child options can be injected when parent options are triggered,
// this is why we must recurse until all options are exhausted.
const openAvailableStoryOptions = () => {
const optionNodes = menuNode.querySelectorAll(OPTIONS);
optionNodes.forEach(optionNode => {
const { id, parentNode } = optionNode;
// When the content from a triggered option is injected into the DOM, it is
// added in as a sibling `<div />` (with no discernible hook). Because
// vanilla JS does not have a $.next() we bounce back to the parent node
// then back down to the options next sibling and test if it is a `<div />`
// (which means it is open with its on injected content).
const isOpen = Boolean(parentNode.querySelector(`#${id} ~ div`));
// Regardless if this link was already opened my Storybook (on page load)
// or we are able to open it ourselves, we all this open flag to keep track
// of every open option (when recursing).
optionNode.setAttribute("data-cypress-clicked", "true");
if (!isOpen) {
// Open the options then run the recursive function again to investigate
// the newly injected content.
optionNode.click();
openAvailableStoryOptions();
};
});
};
// Begin recursion...
openAvailableStoryOptions();
const linkNodes = menuNode.querySelectorAll(LINKS);
// EXAMPLE: for a simple button Story href of [href="/?path=/story/button-primary--colors"]
// we extract the path ID of "button-primary--colors" which can be used to target
// a dedicated iFrame of "/iframe.html?id=button-primary--colors"
return [...linkNodes].map(linkNode =>
linkNode.getAttribute("href").replace("/?path=/story/", "")
);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment