mAAdhaTTah/brookjs

View on GitHub
packages/brookjs-desalinate/src/storybook.tsx

Summary

Maintainability
A
3 hrs
Test Coverage
import React from 'react';
import addons, { makeDecorator } from '@storybook/addons';
import uuid from 'uuid/v1';
import { RootJunction } from 'brookjs-silt';
import { STORY_RENDERED } from '@storybook/core-events';
import Inspector from 'react-inspector';
import { withTheme, styled } from '@storybook/theming';
import { ActionBar } from '@storybook/components';
import { opacify } from 'polished';
 
const ADDON_ID = 'brookjs-desalinate/storybook';
const PANEL_ID = `${ADDON_ID}/panel`;
const EVENT_ID = `${ADDON_ID}/event`;
 
const safeDeepEqual = (a: any, b: any) => {
try {
return a === b;
} catch (e) {
return false;
}
};
 
const Actions = styled.pre({
flex: 1,
margin: 0,
padding: '10px 5px 20px',
overflowY: 'auto',
color: '#666',
});
 
const Action = styled.div({
display: 'flex',
padding: '0',
borderLeft: '5px solid transparent',
borderBottom: '1px solid transparent',
transition: 'all 0.1s',
alignItems: 'flex-start',
});
 
const Counter = styled.div(({ theme }) => ({
backgroundColor: opacify(0.5, theme.appBorderColor),
color: theme.color.inverseText,
fontSize: theme.typography.size.s1,
fontWeight: theme.typography.weight.bold,
lineHeight: 1,
padding: '1px 5px',
borderRadius: '20px',
margin: '2px 0px',
}));
 
const InspectorContainer = styled.div({
flex: 1,
padding: '0 0 0 5px',
});
 
const Wrapper = styled.div({
flex: 1,
display: 'flex',
position: 'relative',
height: '100%',
});
 
type LoggedAction = {
count: number;
data: {
name: string;
args: any;
};
options: {
clearOnStoryChange: boolean;
limit: number;
};
};
 
type ActionLoggerComponentProps = {
onClear: Function;
actions: LoggedAction[];
};
 
const ActionLoggerComponent: any /*React.SFC<ActionLoggerComponentProps> */ = withTheme(
({ actions, onClear, theme }) => (
<Wrapper>
<Actions>
{actions.map((action: any) => (
<Action key={action.id}>
{action.count > 1 && <Counter>{action.count}</Counter>}
<InspectorContainer>
<Inspector
theme={theme.addonActionsTheme || 'chromeLight'}
sortObjectKeys
showNonenumerable={false}
name={action.data.name}
data={action.data.args || action.data}
/>
</InspectorContainer>
</Action>
))}
</Actions>
 
<ActionBar actionItems={[{ title: 'Clear', onClick: onClear }]} />
</Wrapper>
),
);
 
type ActionLoggerProps = {
active: boolean;
channel: {
emit: Function;
on: Function;
removeListener: Function;
};
api: {
on: Function;
off: Function;
getQueryParam: Function;
setQueryParams: Function;
};
};
 
type State = { actions: LoggedAction[] };
 
class ActionLogger extends React.Component<ActionLoggerProps, State> {
state: State = { actions: [] };
 
mounted = false;
 
Similar blocks of code found in 2 locations. Consider refactoring.
componentDidMount() {
this.mounted = true;
const { api } = this.props;
 
api.on(EVENT_ID, this.addAction);
api.on(STORY_RENDERED, this.handleStoryChange);
}
 
Similar blocks of code found in 2 locations. Consider refactoring.
componentWillUnmount() {
this.mounted = false;
const { api } = this.props;
 
api.off(STORY_RENDERED, this.handleStoryChange);
api.off(EVENT_ID, this.addAction);
}
 
handleStoryChange = () => {
const { actions } = this.state;
if (actions.length > 0 && actions[0].options.clearOnStoryChange) {
this.clearActions();
}
};
 
addAction = (action: LoggedAction) => {
this.setState(prevState => {
const actions = [...prevState.actions];
const previous = actions.length && actions[0];
if (previous && safeDeepEqual(previous.data, action.data)) {
previous.count++; // eslint-disable-line
} else {
action.count = 1; // eslint-disable-line
actions.unshift(action);
}
return { actions: actions.slice(0, action.options.limit) };
});
};
 
clearActions = () => {
this.setState({ actions: [] });
};
 
render() {
const { actions = [] } = this.state;
const { active } = this.props;
const props = {
actions,
onClear: this.clearActions,
};
return active ? <ActionLoggerComponent {...props} /> : null;
}
}
 
const emit = (value: any) => {
const channel = addons.getChannel();
const id = uuid();
channel.emit(EVENT_ID, {
id,
count: 0,
data: { name: 'emit', args: [value] },
options: {},
});
};
 
export const withJunction = makeDecorator({
name: 'withJunction',
parameterName: 'withJunction',
wrapper: (Story: Function, context: any) => (
<RootJunction root$={root$ => root$.observe(emit)}>
{Story(context)}
</RootJunction>
),
});
 
export const register = () => {
addons.register(ADDON_ID, api => {
const channel = addons.getChannel();
addons.addPanel(PANEL_ID, {
title: 'Junction Logger',
render: ({ active, key }) => (
<ActionLogger key={key} api={api} channel={channel} active={active} />
),
});
});
};