Skip to content

Commit

Permalink
feat: add Subtitle block
Browse files Browse the repository at this point in the history
  • Loading branch information
atanasster committed Mar 6, 2020
1 parent bfacd04 commit 6486d6c
Show file tree
Hide file tree
Showing 19 changed files with 187 additions and 136 deletions.
30 changes: 12 additions & 18 deletions core/instrument/src/babel/csf-stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ import {
StoriesStore,
Story,
Stories,
StoryArgument,
} from '@component-controls/specification';
import traverse from '@babel/traverse';
import { extractFunctionParameters } from './get-function-parameters';
import { extractProperties } from './extract-properties';
import { extractAttributes } from './extract-attributes';
import { sourceLocation } from './utils';

export const extractCSFStories = (stories: StoriesStore) => {
Expand Down Expand Up @@ -35,17 +34,14 @@ export const extractCSFStories = (stories: StoriesStore) => {
return {
ExportDefaultDeclaration: (path: any) => {
const { declaration } = path.node;
const parameters = extractProperties(declaration);
const attributes = extractAttributes(declaration);

const title = parameters
? parameters.find((p: StoryArgument) => p.name === 'title')
: null;
const kindTitle =
title && typeof title.value === 'string' ? title.value : undefined;
const title = attributes ? attributes['title'] : null;
const kindTitle = typeof title === 'string' ? title : undefined;
if (kindTitle) {
stories.kinds[kindTitle] = {
title: kindTitle,
parameters,
attributes,
};
}
},
Expand All @@ -58,21 +54,19 @@ export const extractCSFStories = (stories: StoriesStore) => {
node.right.type === 'ObjectExpression'
) {
const storyName = node.left.object.name;
const parameters = extractProperties(node.right);
const nameProp = parameters
? parameters.find((p: StoryArgument) => p.name === 'name')
: null;
const name = nameProp ? nameProp.value : storyName;
const attributes = extractAttributes(node.right);
const nameProp = attributes ? attributes['name'] : null;
const name = nameProp ? nameProp : storyName;
globals[storyName] = {
parameters,
attributes,
name,
};

const story = stories.stories[storyName];

if (story) {
story.name = name;
story.parameters = parameters;
story.attributes = attributes;
}
}
},
Expand Down Expand Up @@ -101,7 +95,7 @@ export const extractCSFStories = (stories: StoriesStore) => {
const global = globals[localName];
if (global) {
story.name = global.name;
story.parameters = global.parameters;
story.attributes = global.attributes;
}
stories.stories[exportedName] = story;
}
Expand All @@ -117,7 +111,7 @@ export const extractCSFStories = (stories: StoriesStore) => {
const global = globals[name];
if (global) {
story.name = global.name;
story.parameters = global.parameters;
story.attributes = global.attributes;
}
stories.stories[name] = story;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import {
StoryArguments,
StoryArgument,
} from '@component-controls/specification';
import { sourceLocation } from './utils';
import { StoryAttributes } from '@component-controls/specification';

const nodeToParameter = (node: any): StoryArgument | undefined => {
interface StoryAttribute {
name: string;
value: any;
}
const nodeToAttribute = (node: any): StoryAttribute | undefined => {
const value = node.value || node;
const name = node.key ? node.key.name : node.name;
if (value) {
Expand All @@ -15,28 +15,24 @@ const nodeToParameter = (node: any): StoryArgument | undefined => {
return {
value: value.value,
name,
loc: sourceLocation(node.loc),
};
}
case 'Identifier':
return {
value: value.name,
name,
loc: sourceLocation(node.loc),
};
case 'MemberExpression':
return {
value: `${value.object.name}.${value.property.name}`,
name,
loc: sourceLocation(node.loc),
};
case 'ObjectExpression': {
const val = extractProperties(value);
const val = extractAttributes(value);
if (val) {
return {
value: val,
name,
loc: sourceLocation(node.loc),
};
}
break;
Expand All @@ -48,17 +44,25 @@ const nodeToParameter = (node: any): StoryArgument | undefined => {
}
return undefined;
};
export const extractProperties = (node: any): StoryArguments | undefined => {
export const extractAttributes = (node: any): StoryAttributes | undefined => {
if (node) {
if (node.properties) {
const properties: StoryArguments = node.properties
.map((propNode: any) => nodeToParameter(propNode))
.filter((p: any) => p);
return properties;
const attributes: StoryAttributes = node.properties.reduce(
(acc: StoryAttributes, propNode: any) => {
const attribute = nodeToAttribute(propNode);
if (attribute) {
return { ...acc, [attribute.name]: attribute.value };
} else {
return acc;
}
},
{},
);
return attributes;
}
const parameter = nodeToParameter(node);
if (parameter) {
return [parameter];
const attribute = nodeToAttribute(node);
if (attribute) {
return { [attribute.name]: attribute.value };
}
}
return undefined;
Expand Down
35 changes: 14 additions & 21 deletions core/instrument/src/babel/extract-component.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,34 @@
import { File } from '@babel/types';
import {
StoriesStore,
StoryArgument,
StoryArguments,
StoryAttributes,
StoryComponent,
} from '@component-controls/specification';
import { followImports } from './follow-imports';
import { packageInfo } from '../project/packageInfo';
import { InstrumentOptions } from '../types';

const componentFromParams = (
parameters?: StoryArguments,
attributes?: StoryAttributes,
): string | undefined => {
if (parameters) {
let component = parameters.find(
(p: StoryArgument) => p.name === 'component',
);
if (attributes) {
let component = attributes['component'];
if (!component) {
const params = parameters.find(
(p: StoryArgument) => p.name === 'parameters',
);
const params = attributes['parameters'];
if (params) {
component = (params.value as StoryArguments).find(
(p: StoryArgument) => p.name === 'component',
);
component = params['component'];
}
}
if (component) {
if (typeof component.value === 'string') {
return component.value as string;
if (typeof component === 'string') {
return component as string;
}
if (
Array.isArray(component.value) &&
component.value.length > 0 &&
typeof component.value[0].value === 'string'
Array.isArray(component) &&
component.length > 0 &&
typeof component[0].value === 'string'
) {
return component.value[0].value;
return component[0].value;
}
}
}
Expand Down Expand Up @@ -78,7 +71,7 @@ export const extractSotreComponent = async (
const kinds = Object.keys(store.kinds);
if (kinds.length > 0) {
const kind = store.kinds[kinds[0]];
const componentName = componentFromParams(kind.parameters);
const componentName = componentFromParams(kind.attributes);

if (componentName) {
const component = await extractComponent(
Expand All @@ -96,7 +89,7 @@ export const extractSotreComponent = async (
}
Object.keys(store.stories).forEach(async (name: string) => {
const story = store.stories[name];
const componentName = componentFromParams(story.parameters);
const componentName = componentFromParams(story.attributes);
if (componentName) {
const component = await extractComponent(
componentName,
Expand Down
56 changes: 22 additions & 34 deletions core/instrument/src/babel/mdx-stories.ts
Original file line number Diff line number Diff line change
@@ -1,82 +1,70 @@
import {
StoriesStore,
Story,
StoryArguments,
StoryArgument,
CodeLocation,
StoryAttributes,
} from '@component-controls/specification';
import traverse from '@babel/traverse';
import { extractFunctionParameters } from './get-function-parameters';
import { extractProperties } from './extract-properties';
import { extractAttributes } from './extract-attributes';
import { sourceLocation } from './utils';

export const extractMDXStories = (stories: StoriesStore) => {
return {
JSXElement: (path: any) => {
const node = path.node.openingElement;
if (['Meta', 'Story'].indexOf(node.name.name) > -1) {
const parameters: StoryArguments = node.attributes
.map((attribute: any) => {
const loc: CodeLocation = sourceLocation(attribute.loc);
const attributes: StoryAttributes = node.attributes.reduce(
(acc: StoryAttributes, attribute: any) => {
if (attribute.value.type === 'StringLiteral') {
return {
value: attribute.value?.value,
name: attribute.name?.name,
loc,
};
return { ...acc, [attribute.name?.name]: attribute.value?.value };
} else if (attribute.value.type === 'JSXExpressionContainer') {
return {
name: attribute.name?.name,
value: extractProperties(attribute.value.expression),
loc,
...acc,
[attribute.name?.name]: extractAttributes(
attribute.value.expression,
),
};
}
return null;
})
.filter((p: any) => p);
return acc;
},
{},
);

switch (node.name.name) {
case 'Story': {
const story: Story = {
loc: sourceLocation(path.node.loc),
};
const name = parameters.find(
(p: StoryArgument) => p.name === 'name',
);
const name = attributes['name'];

if (name && typeof name.value === 'string') {
if (typeof name === 'string') {
traverse(
path.node,
extractFunctionParameters(story),
path.scope,
path,
);
story.name = name.value;
story.parameters = parameters;
stories.stories[name.value] = story;
story.name = name;
story.attributes = attributes;
stories.stories[name] = story;
}
break;
}
case 'Meta': {
const title = parameters.find(
(p: StoryArgument) => p.name === 'title',
);
const kindTitle =
title && typeof title.value === 'string'
? title.value
: undefined;
const title = attributes['title'];
const kindTitle = typeof title === 'string' ? title : undefined;
if (kindTitle) {
stories.kinds[kindTitle] = {
title: kindTitle,
parameters,
attributes,
};
}
break;
}
default:
break;
}
// console.log(node.name.name, parameters);
// console.log(node.name.name, attributes);
}
},
};
Expand Down
24 changes: 17 additions & 7 deletions core/specification/src/stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export interface ArgUsageLocation {
}

/**
* arguments passed to the 'story' as extracted by an AST loader
* arguments passed to the 'story' function, extracted by an AST loader
*/
export interface StoryArgument {
/**
Expand All @@ -47,11 +47,19 @@ export interface StoryArgument {
}

/**
* list of story arguments. The first argument contains the 'controls' name/value pairs
* the second argument can contain the context
* list of story arguments. Each argument can be a deconstructed argument of itself
* the first argument are the control 'values'
*/
export type StoryArguments = StoryArgument[];

/**
* list of configuration attributes for stories and 'kinds'
* can be specified either through CSF or MDX tags
*/
export interface StoryAttributes {
[name: string]: any;
}

/**
* Story interface - usually extracted by the AST instrumenting loader
*/
Expand All @@ -70,9 +78,9 @@ export interface Story {
*/
arguments?: StoryArguments;
/**
* configuration parameters passed to the story
* configuration attributes passed to the story - either CSF or MDX Story attributes
*/
parameters?: StoryArguments;
attributes?: StoryAttributes;
/**
* location in the source file of the story definition
*/
Expand Down Expand Up @@ -110,9 +118,11 @@ export interface StoriesKind {
*/
stories?: string[];
/**
* any parameters passed to the story groupds, such as title, parameters etc
* any attributes passed to the story groupds, such as title, parameters etc
* configured either as CSF default export
* or MDX <Meta /> tag
*/
parameters?: StoryArguments;
attributes?: StoryAttributes;

/**
* source code of the entire file of stories
Expand Down
Loading

0 comments on commit 6486d6c

Please sign in to comment.