2019-02-08 09:52:51 +01:00

128 lines
3.2 KiB
TypeScript

import * as React from 'react';
import * as PropTypes from 'prop-types';
import { types } from '@storybook/addons';
import { styled } from '@storybook/theming';
import { STORY_RENDERED } from '@storybook/core-events';
import { SyntaxHighlighter as SyntaxHighlighterBase, Placeholder, DocumentFormatting, Link } from '@storybook/components';
import Giphy from './giphy';
import Markdown from 'markdown-to-jsx';
import { PARAM_KEY, API, Parameters } from './shared';
const Panel = styled.div({
padding: '3rem 40px',
boxSizing: 'border-box',
width: '100%',
maxWidth: 980,
margin: '0 auto',
});
interface Props {
active: boolean;
api: API;
}
interface NotesPanelState {
value?: string;
}
function read(param: Parameters | undefined): string | undefined {
if (!param) {
return undefined;
} else if (typeof param === 'string') {
return param;
} else if ('disabled' in param) {
return undefined;
} else if ('text' in param) {
return param.text;
} else if ('markdown' in param) {
return param.markdown;
}
}
const SyntaxHighlighter = (props: any) => <SyntaxHighlighterBase bordered copyable {...props} />;
const defaultOptions = {
overrides: {
code: SyntaxHighlighter,
Giphy: {
component: Giphy,
},
},
};
export default class NotesPanel extends React.Component<Props, NotesPanelState> {
static propTypes = {
active: PropTypes.bool.isRequired,
api: PropTypes.shape({
on: PropTypes.func,
off: PropTypes.func,
emit: PropTypes.func,
getParameters: PropTypes.func,
}).isRequired,
};
readonly state: NotesPanelState = {
value: '',
};
mounted: boolean;
// use our SyntaxHighlighter component in place of a <code> element when
// converting markdown to react elements
componentDidMount() {
const { api } = this.props;
api.on(STORY_RENDERED, this.onStoryChange);
}
componentWillUnmount() {
const { api } = this.props;
api.off(STORY_RENDERED, this.onStoryChange);
}
onStoryChange = (id: string) => {
const { api } = this.props;
const params = api.getParameters(id, PARAM_KEY);
const value = read(params);
if (value) {
this.setState({ value });
} else {
this.setState({ value: undefined });
}
};
render() {
const { active, api } = this.props;
const { value } = this.state;
if (!active) {
return null;
}
// TODO: memoize
const extraElements = Object.entries(api.getElements(types.NOTES_ELEMENT)).reduce((acc, [k, v]) => ({ ...acc, [k]: v.render }), {});
const options = { ...defaultOptions, overrides: { ...defaultOptions.overrides, ...extraElements } };
return value ? (
<Panel className="addon-notes-container">
<DocumentFormatting>
<Markdown options={options}>{value}</Markdown>
</DocumentFormatting>
</Panel>
) : (
<Placeholder>
<React.Fragment>
No notes yet
</React.Fragment>
<React.Fragment>
Learn how to <Link href="https://github.com/storybooks/storybook/tree/master/addons/notes" target="_blank" withArrow>document components in Markdown</Link>
</React.Fragment>
</Placeholder>
);
}
}