Created
May 2, 2020 04:39
-
-
Save f1yn/af7cd96051ed111cf0c2e4c5d7242093 to your computer and use it in GitHub Desktop.
Demo Knob injection in storybook (extracted sample)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import React, { useContext } from 'react'; | |
| import { DocsContext } from '@storybook/addon-docs/blocks'; | |
| import { Source } from '@storybook/components'; | |
| /** | |
| * Adapted from https://github.com/storybookjs/storybook/blob/master/addons/docs/src/blocks/Source.tsx | |
| */ | |
| const extractLines = (source, location) => { | |
| const { startBody: start, endBody: end } = location; | |
| const lines = source.split('\n'); | |
| if (start.line === end.line) { | |
| return lines[start.line - 1].substring(start.col, end.col); | |
| } | |
| // NOTE: storysource locations are 1-based not 0-based! | |
| const startLine = lines[start.line - 1]; | |
| const endLine = lines[end.line - 1]; | |
| return [ | |
| startLine.substring(start.col), | |
| ...lines.slice(start.line, end.line - 1), | |
| endLine.substring(0, end.col), | |
| ]; | |
| }; | |
| /** | |
| * Joins the original source for various | |
| * @param originalLines | |
| * @return {*} | |
| */ | |
| function applyKnobDefaults(originalLines) { | |
| const joinedSource = originalLines.join('\n'); | |
| // Special cases for knobs for resolving index. Most knob calls use [1] as the defaultValue index | |
| const specialTypeToParamIndex = { | |
| select: 2, | |
| radios: 2, | |
| files: 2, | |
| }; | |
| // safer and slightly eval for converting object into statement | |
| // see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval#Do_not_ever_use_eval! | |
| const evaluateObject = (obj) => Function('"use strict";return (' + obj + ')')(); | |
| // Special sauce regular expression, will extract the block group representing a common knob helper | |
| // This also works for multiline knobs | |
| return joinedSource | |
| .replace(/(color|boolean|text|array|select|object|radios|files)\(((\s|\S)*?)\)/g, (fullMatch, knobType, rawParams) => { | |
| // Super hack: Evaluate the parameter list as a native object. Note this won't respect the closure of | |
| // the original source. That would require a full VM OR to evaluate the whole script in isolation | |
| try { | |
| const parsedParams = evaluateObject(`[${rawParams}]`); | |
| // Determine which index to use | |
| const indexForExtraction = specialTypeToParamIndex[knobType] || 1; | |
| return parsedParams[indexForExtraction]; | |
| } catch (error) { | |
| console.error('Failed to automatically resolve knob value', error); | |
| return fullMatch; | |
| } | |
| }) | |
| .split('\n'); | |
| } | |
| export function RaiselySource({ | |
| id: customId | |
| }) { | |
| const context = useContext(DocsContext); | |
| const data = customId ? | |
| context.storyStore.fromId(customId) : | |
| context; | |
| const { source: rawSource } = data.parameters.storySource; | |
| const location = data.parameters.storySource.locationsMap[data.id]; | |
| // Extract the source code for the contextual (or provided) story | |
| let lines = applyKnobDefaults(extractLines(rawSource, location)); | |
| return ( | |
| <Source | |
| code={lines.join('\n')} | |
| language="jsx" | |
| format={false} | |
| /> | |
| ) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment