Thursday, January 28, 2021

human-readable sorting of contents in storybook

Storybook slightly undersells its sorting capability... the array you pass to the exported variable parameters.options.storySort.order can be a mix of strings and arrays of [strings or more such arrays], and empircally it seems to work many levels deep. So for example a hierarchy like 

Documentation
	Overview
	Design System Workflow
	Getting Started Guide
		Intro
		Designer Guide
		Developer Guide
Components

can be described as the following:

[
 "Documentation",
 [
  "Overview",
  "Design System Workflow",
  "Getting Started Guide",
  ["Intro","Designer Guide","Developer Guide"]
],
 "Components"
]

But I admit, I didn't quite like that format - the way the contents of a section (like ["Intro, Designer,Developer Guide"]) were at the same level of its "parent" ("Getting Started Guide") seems odd to me, and I thought maybe it would be easier if the Table of Contents were human readable...

So I don't know if I over-engineered it, but I came up with this:

const toc = `
Documentation
 Overview
 Design System Workflow
 Getting Started Guide
  Intro
  Designer Guide
  Developer Guide
Components`;

const tocParse = (lines, ptr, currentDepth, arrayStack) => {
  if(ptr >= lines.length) return;
  const line = lines[ptr];
  const lineDepth = line.search(/\S/); // how many spaces before nonwhite space?
  if(lineDepth === -1) { // if -1 is empty line, skip and move to next
    return tocParse(lines, ptr + 1, currentDepth, arrayStack);
  }
  const title = line.trimLeft().trimRight();
  const currentArray = arrayStack[currentDepth];
  if(lineDepth === currentDepth) { // at this level, add and look to next one
    currentArray.push(title);
    tocParse(lines, ptr + 1, currentDepth, arrayStack);
  } else {
    if(lineDepth > currentDepth) { // deeper, make child array and re-parse line
      const subArray = [];
      currentArray.push(subArray);
      arrayStack.push(subArray);
      tocParse(lines, ptr, currentDepth + 1, arrayStack);
    } else { // more shallow, go up a level and re-parse line
      arrayStack.pop();
      tocParse(lines, ptr, currentDepth - 1, arrayStack );
    }
  }
};

const order = [];
const tocLines = toc.split('\n');
tocParse(tocLines, 0, 0, [order]);

The toc uses spaces not tabs, and so isn't super robust, but still, I think it does a good job having a string that looks like the end result drive the whole process.

Also, as I was building up that little recursive thing, I wanted to test my work - a convenient way of making sure two arrays (containing strings and other nested arrays only) are equal is to just do JSON.stringify() on 'em - unlike objects you don't have keys that might be in a weird order to worry about.

No comments:

Post a Comment