mirror of
https://github.com/storybookjs/storybook.git
synced 2025-03-20 05:02:37 +08:00
Merge pull request #6403 from sairus2k/ts-migration/addon-jest
Migrate @storybook/addon-jest to TS
This commit is contained in:
commit
a315299d82
@ -23,18 +23,18 @@
|
||||
"license": "MIT",
|
||||
"author": "Renaud Tertrais <renaud.tertrais@gmail.com> (https://github.com/renaudtertrais)",
|
||||
"main": "dist/index.js",
|
||||
"jsnext:main": "src/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"prepare": "node ../../scripts/prepare.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@storybook/addons": "5.1.0-alpha.22",
|
||||
"@storybook/api": "^5.1.0-alpha.22",
|
||||
"@storybook/components": "5.1.0-alpha.22",
|
||||
"@storybook/core-events": "5.1.0-alpha.22",
|
||||
"@storybook/theming": "5.1.0-alpha.22",
|
||||
"core-js": "^2.6.5",
|
||||
"global": "^4.3.2",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^16.8.4",
|
||||
"upath": "^1.1.0",
|
||||
"util-deprecate": "^1.0.2"
|
||||
|
@ -1,8 +1,17 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
|
||||
import { styled } from '@storybook/theming';
|
||||
|
||||
const Indicator = styled.div(
|
||||
interface IndicatorProps {
|
||||
color: string;
|
||||
size: number;
|
||||
children?: React.ReactNode;
|
||||
right?: boolean;
|
||||
overrides?: any;
|
||||
styles?: React.CSSProperties;
|
||||
}
|
||||
|
||||
const Indicator = styled.div<IndicatorProps>(
|
||||
({ color, size }) => ({
|
||||
boxSizing: 'border-box',
|
||||
padding: `0 ${size / 2}px`,
|
||||
@ -25,11 +34,4 @@ Indicator.defaultProps = {
|
||||
children: '',
|
||||
};
|
||||
|
||||
Indicator.propTypes = {
|
||||
color: PropTypes.string.isRequired,
|
||||
size: PropTypes.number.isRequired,
|
||||
children: PropTypes.node,
|
||||
right: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default Indicator;
|
202
addons/jest/src/components/Message.tsx
Normal file
202
addons/jest/src/components/Message.tsx
Normal file
@ -0,0 +1,202 @@
|
||||
/* tslint:disable:object-literal-sort-keys */
|
||||
|
||||
import React from 'react';
|
||||
import { styled } from '@storybook/theming';
|
||||
import colors from '../colors';
|
||||
|
||||
const patterns = [/^\x08+/, /^\x1b\[[012]?K/, /^\x1b\[?[\d;]{0,3}/];
|
||||
|
||||
const Pre = styled.pre({
|
||||
margin: 0,
|
||||
});
|
||||
|
||||
const Positive = styled.strong({
|
||||
color: colors.success,
|
||||
fontWeight: 500,
|
||||
});
|
||||
const Negative = styled.strong({
|
||||
color: colors.error,
|
||||
fontWeight: 500,
|
||||
});
|
||||
|
||||
interface StackTraceProps {
|
||||
trace: MsgElement[];
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const StackTrace = styled(({ trace, className }: StackTraceProps) => (
|
||||
<details className={className}>
|
||||
<summary>Callstack</summary>
|
||||
{trace
|
||||
.join('')
|
||||
.trim()
|
||||
.split(/\n/)
|
||||
.map((traceLine, traceLineIndex) => (
|
||||
<div key={traceLineIndex}>{traceLine.trim()}</div>
|
||||
))}
|
||||
</details>
|
||||
))({
|
||||
background: 'silver',
|
||||
padding: 10,
|
||||
overflow: 'auto',
|
||||
});
|
||||
|
||||
const Main = styled(({ msg, className }) => <section className={className}>{msg}</section>)({
|
||||
padding: 10,
|
||||
borderBottom: '1px solid silver',
|
||||
});
|
||||
|
||||
interface SubProps {
|
||||
msg: MsgElement[];
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Sub = styled(({ msg, className }: SubProps) => (
|
||||
<section className={className}>
|
||||
{msg
|
||||
.filter(item => typeof item !== 'string' || item.trim() !== '')
|
||||
.map((item, index, list) => {
|
||||
if (typeof item === 'string') {
|
||||
if (index === 0 && index === list.length - 1) {
|
||||
return item.trim();
|
||||
}
|
||||
if (index === 0) {
|
||||
return item.replace(/^[\s\n]*/, '');
|
||||
}
|
||||
if (index === list.length - 1) {
|
||||
return item.replace(/[\s\n]*$/, '');
|
||||
}
|
||||
}
|
||||
return item;
|
||||
})}
|
||||
</section>
|
||||
))({
|
||||
padding: 10,
|
||||
});
|
||||
|
||||
interface SubgroupOptions {
|
||||
startTrigger: (e: MsgElement) => boolean;
|
||||
endTrigger: (e: MsgElement) => boolean;
|
||||
grouper: (list: MsgElement[], key: number) => JSX.Element;
|
||||
accList?: MsgElement[];
|
||||
grouped?: MsgElement[];
|
||||
grouperIndex?: number;
|
||||
mode?: 'inject' | 'stop';
|
||||
injectionPoint?: number;
|
||||
}
|
||||
|
||||
const createSubgroup = ({
|
||||
startTrigger,
|
||||
endTrigger,
|
||||
grouper,
|
||||
accList = [],
|
||||
grouped = [],
|
||||
grouperIndex = 0,
|
||||
mode,
|
||||
injectionPoint,
|
||||
}: SubgroupOptions) => (acc: MsgElement[], item: MsgElement, i: number, list: MsgElement[]) => {
|
||||
grouperIndex += 1;
|
||||
|
||||
// start or stop extraction
|
||||
if (startTrigger(item)) {
|
||||
mode = 'inject';
|
||||
injectionPoint = i;
|
||||
}
|
||||
if (endTrigger(item)) {
|
||||
mode = 'stop';
|
||||
}
|
||||
|
||||
// push item in correct aggregator
|
||||
if (mode === 'inject') {
|
||||
grouped.push(item);
|
||||
} else {
|
||||
accList.push(item);
|
||||
}
|
||||
|
||||
// on last iteration inject at detected injection point, and group
|
||||
if (i === list.length - 1) {
|
||||
// Provide a "safety net" when Jest returns a partially recognized "group"
|
||||
// (recognized by acc.startTrigger but acc.endTrigger was never found) and
|
||||
// it's the only group in output for a test result. In that case, accList
|
||||
// will be empty, so return whatever was found, even if it will be unstyled
|
||||
// and prevent next createSubgroup calls from throwing due to empty lists.
|
||||
accList.push(null);
|
||||
|
||||
return accList.reduce<MsgElement[]>((eacc, el, ei) => {
|
||||
if (injectionPoint === 0 && ei === 0) {
|
||||
// at index 0, inject before
|
||||
return eacc.concat(grouper(grouped, grouperIndex)).concat(el);
|
||||
}
|
||||
if (injectionPoint > 0 && injectionPoint === ei + 1) {
|
||||
// at index > 0, and next index WOULD BE injectionPoint, inject after
|
||||
return eacc.concat(el).concat(grouper(grouped, grouperIndex));
|
||||
}
|
||||
// do not inject
|
||||
return eacc.concat(el);
|
||||
}, []);
|
||||
}
|
||||
return acc;
|
||||
};
|
||||
|
||||
interface MessageProps {
|
||||
msg: string;
|
||||
}
|
||||
|
||||
type MsgElement = string | JSX.Element;
|
||||
|
||||
const Message = ({ msg }: MessageProps) => {
|
||||
const data = patterns
|
||||
.reduce((acc, regex) => acc.replace(regex, ''), msg)
|
||||
.split(/\[2m/)
|
||||
.join('')
|
||||
.split(/\[22m/)
|
||||
.reduce((acc, item) => acc.concat(item), [] as string[])
|
||||
.map((item, li) =>
|
||||
item
|
||||
.split(/\[32m(.*?)\[39m/)
|
||||
.map((i, index) => (index % 2 ? <Positive key={`p_${li}_${i}`}>{i}</Positive> : i))
|
||||
)
|
||||
.reduce((acc, item) => acc.concat(item))
|
||||
.map((item, li) =>
|
||||
typeof item === 'string'
|
||||
? item
|
||||
.split(/\[31m(.*?)\[39m/)
|
||||
.map((i, index) => (index % 2 ? <Negative key={`n_${li}_${i}`}>{i}</Negative> : i))
|
||||
: item
|
||||
)
|
||||
.reduce<MsgElement[]>((acc, item) => acc.concat(item), [])
|
||||
.reduce(
|
||||
createSubgroup({
|
||||
startTrigger: e => typeof e === 'string' && e.indexOf('Error: ') === 0,
|
||||
endTrigger: e => typeof e === 'string' && Boolean(e.match('Expected ')),
|
||||
grouper: (list, key) => <Main key={key} msg={list} />,
|
||||
}),
|
||||
[]
|
||||
)
|
||||
.reduce(
|
||||
(acc, it) =>
|
||||
typeof it === 'string' ? acc.concat(it.split(/(at(.|\n)+\d+:\d+\))/)) : acc.concat(it),
|
||||
[] as MsgElement[]
|
||||
)
|
||||
.reduce((acc, item) => acc.concat(item), [] as MsgElement[])
|
||||
.reduce(
|
||||
createSubgroup({
|
||||
startTrigger: e => typeof e === 'string' && e.indexOf('Expected ') !== -1,
|
||||
endTrigger: e => typeof e === 'string' && Boolean(e.match(/^at/)),
|
||||
grouper: (list, key) => <Sub key={key} msg={list} />,
|
||||
}),
|
||||
[]
|
||||
)
|
||||
.reduce(
|
||||
createSubgroup({
|
||||
startTrigger: e => typeof e === 'string' && Boolean(e.match(/at(.|\n)+\d+:\d+\)/)),
|
||||
endTrigger: () => false,
|
||||
grouper: (list, key) => <StackTrace key={key} trace={list} />,
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
return <Pre>{data}</Pre>;
|
||||
};
|
||||
|
||||
export default Message;
|
@ -1,11 +1,10 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { styled } from '@storybook/theming';
|
||||
import { ScrollArea } from '@storybook/components';
|
||||
|
||||
import Indicator from './Indicator';
|
||||
import Result, { FailedResult } from './Result';
|
||||
import provideJestResult from '../hoc/provideJestResult';
|
||||
import provideJestResult, { Test } from '../hoc/provideJestResult';
|
||||
import colors from '../colors';
|
||||
|
||||
const List = styled.ul({
|
||||
@ -95,7 +94,12 @@ const SuiteTitle = styled.div({
|
||||
alignItems: 'center',
|
||||
});
|
||||
|
||||
const Content = styled(({ tests, className }) => (
|
||||
interface ContentProps {
|
||||
tests: Test[];
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Content = styled(({ tests, className }: ContentProps) => (
|
||||
<div className={className}>
|
||||
{tests.map(({ name, result }) => {
|
||||
if (!result) {
|
||||
@ -142,7 +146,11 @@ const Content = styled(({ tests, className }) => (
|
||||
flex: '1 1 0%',
|
||||
});
|
||||
|
||||
const Panel = ({ tests }) => (
|
||||
interface PanelProps {
|
||||
tests: null | Test[];
|
||||
}
|
||||
|
||||
const Panel = ({ tests }: PanelProps) => (
|
||||
<ScrollArea vertical>
|
||||
{tests ? <Content tests={tests} /> : <NoTests>This story has no tests configured</NoTests>}
|
||||
</ScrollArea>
|
||||
@ -152,12 +160,4 @@ Panel.defaultProps = {
|
||||
tests: null,
|
||||
};
|
||||
|
||||
Panel.propTypes = {
|
||||
tests: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
result: PropTypes.object,
|
||||
})
|
||||
),
|
||||
};
|
||||
|
||||
export default provideJestResult(Panel);
|
@ -1,47 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { styled } from '@storybook/theming';
|
||||
|
||||
import provideJestResult from '../hoc/provideJestResult';
|
||||
import Indicator from './Indicator';
|
||||
import colors from '../colors';
|
||||
|
||||
const Wrapper = styled.div({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
});
|
||||
const PanelName = styled.div({
|
||||
paddingLeft: 5,
|
||||
});
|
||||
|
||||
const PanelTitle = ({ tests }) => {
|
||||
if (!tests) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const results = tests.map(report => report.result).filter(report => !!report);
|
||||
const success = results.reduce((acc, result) => acc && result.status === 'passed', true);
|
||||
const color = success ? colors.success : colors.error;
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<Indicator color={results.length < tests.length ? colors.warning : color} size={10} />
|
||||
<PanelName>Tests</PanelName>
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
PanelTitle.defaultProps = {
|
||||
tests: null,
|
||||
};
|
||||
|
||||
PanelTitle.propTypes = {
|
||||
tests: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
result: PropTypes.object,
|
||||
})
|
||||
),
|
||||
};
|
||||
|
||||
export default provideJestResult(PanelTitle);
|
@ -1,264 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { styled } from '@storybook/theming';
|
||||
|
||||
import Indicator from './Indicator';
|
||||
import colors from '../colors';
|
||||
|
||||
const Pre = styled.pre({
|
||||
margin: 0,
|
||||
});
|
||||
|
||||
const FlexContainer = styled.div({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
});
|
||||
|
||||
/* eslint no-control-regex:0 */
|
||||
const patterns = [/^\x08+/, /^\x1b\[[012]?K/, /^\x1b\[?[\d;]{0,3}/];
|
||||
|
||||
const Positive = styled.strong({
|
||||
color: colors.success,
|
||||
fontWeight: 500,
|
||||
});
|
||||
const Negative = styled.strong({
|
||||
color: colors.error,
|
||||
fontWeight: 500,
|
||||
});
|
||||
const StackTrace = styled(({ trace, className }) => (
|
||||
<details className={className}>
|
||||
<summary>Callstack</summary>
|
||||
{trace
|
||||
.join('')
|
||||
.trim()
|
||||
.split(/\n/)
|
||||
.map((traceLine, traceLineIndex) => (
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
<div key={traceLineIndex}>{traceLine.trim()}</div>
|
||||
))}
|
||||
</details>
|
||||
))({
|
||||
background: 'silver',
|
||||
padding: 10,
|
||||
overflow: 'auto',
|
||||
});
|
||||
const Main = styled(({ msg, className }) => <section className={className}>{msg}</section>)({
|
||||
padding: 10,
|
||||
borderBottom: '1px solid silver',
|
||||
});
|
||||
const Sub = styled(({ msg, className }) => (
|
||||
<section className={className}>
|
||||
{msg
|
||||
.filter(item => typeof item !== 'string' || (typeof item === 'string' && item.trim() !== ''))
|
||||
.map((item, index, list) => {
|
||||
switch (true) {
|
||||
case typeof item === 'string' && index === 0 && index === list.length - 1: {
|
||||
return item.trim();
|
||||
}
|
||||
case typeof item === 'string' && index === 0: {
|
||||
return item.replace(/^[\s\n]*/, '');
|
||||
}
|
||||
case typeof item === 'string' && index === list.length - 1: {
|
||||
return item.replace(/[\s\n]*$/, '');
|
||||
}
|
||||
default: {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
// typeof item === 'string' ? <span>{item}</span> : item;
|
||||
})}
|
||||
</section>
|
||||
))({
|
||||
padding: 10,
|
||||
});
|
||||
|
||||
const createSubgroup = (acc, item, i, list) => {
|
||||
// setup aggregators
|
||||
if (!acc.list) {
|
||||
acc.list = [];
|
||||
}
|
||||
if (!acc.grouped) {
|
||||
acc.grouped = [];
|
||||
}
|
||||
if (!('grouperIndex' in acc)) {
|
||||
acc.grouperIndex = 0;
|
||||
} else {
|
||||
acc.grouperIndex += 1;
|
||||
}
|
||||
|
||||
// start or stop extraction
|
||||
if (acc.startTrigger(item)) {
|
||||
acc.mode = 'inject';
|
||||
acc.injectionPoint = i;
|
||||
}
|
||||
if (acc.endTrigger(item)) {
|
||||
acc.mode = 'stop';
|
||||
}
|
||||
|
||||
// push item in correct aggregator
|
||||
if (acc.mode === 'inject') {
|
||||
acc.grouped.push(item);
|
||||
} else {
|
||||
acc.list.push(item);
|
||||
}
|
||||
|
||||
// on last iteration inject at detected injectionpoint, and group
|
||||
if (i === list.length - 1) {
|
||||
// Provide a "safety net" when Jest returns a partially recognized "group"
|
||||
// (recognized by acc.startTrigger but acc.endTrigger was never found) and
|
||||
// it's the only group in output for a test result. In that case, acc.list
|
||||
// will be empty, so return whatever was found, even if it will be unstyled
|
||||
// and prevent next createSubgroup calls from throwing due to empty lists.
|
||||
acc.list.push(null);
|
||||
|
||||
return acc.list.reduce((eacc, el, ei) => {
|
||||
switch (true) {
|
||||
case acc.injectionPoint === 0 && ei === 0: {
|
||||
// at index 0, inject before
|
||||
return eacc.concat(acc.grouper(acc.grouped, acc.grouperIndex)).concat(el);
|
||||
}
|
||||
case acc.injectionPoint > 0 && acc.injectionPoint === ei + 1: {
|
||||
// at index > 0, and next index WOULD BE injectionPoint, inject after
|
||||
return eacc.concat(el).concat(acc.grouper(acc.grouped, acc.grouperIndex));
|
||||
}
|
||||
default: {
|
||||
// do not inject
|
||||
return eacc.concat(el);
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
}
|
||||
return acc;
|
||||
};
|
||||
|
||||
const Message = ({ msg }) => {
|
||||
const data = patterns
|
||||
.reduce((acc, regex) => acc.replace(regex, ''), msg)
|
||||
.split(/\[2m/)
|
||||
.join('')
|
||||
.split(/\[22m/)
|
||||
.reduce((acc, item) => acc.concat(item), [])
|
||||
.map((item, li) =>
|
||||
typeof item === 'string'
|
||||
? item
|
||||
.split(/\[32m(.*?)\[39m/)
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
.map((i, index) => (index % 2 ? <Positive key={`p_${li}_${i}`}>{i}</Positive> : i))
|
||||
: item
|
||||
)
|
||||
.reduce((acc, item) => acc.concat(item), [])
|
||||
.map((item, li) =>
|
||||
typeof item === 'string'
|
||||
? item
|
||||
.split(/\[31m(.*?)\[39m/)
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
.map((i, index) => (index % 2 ? <Negative key={`n_${li}_${i}`}>{i}</Negative> : i))
|
||||
: item
|
||||
)
|
||||
.reduce((acc, item) => acc.concat(item), [])
|
||||
.reduce(createSubgroup, {
|
||||
startTrigger: e => typeof e === 'string' && e.indexOf('Error: ') === 0,
|
||||
endTrigger: e => typeof e === 'string' && e.match('Expected '),
|
||||
grouper: (list, key) => <Main key={key} msg={list} />,
|
||||
})
|
||||
.reduce(
|
||||
(acc, it) =>
|
||||
typeof it === 'string' ? acc.concat(it.split(/(at(.|\n)+\d+:\d+\))/)) : acc.concat(it),
|
||||
[]
|
||||
)
|
||||
.reduce((acc, item) => acc.concat(item), [])
|
||||
.reduce(createSubgroup, {
|
||||
startTrigger: e => typeof e === 'string' && e.indexOf('Expected ') !== -1,
|
||||
endTrigger: e => typeof e === 'string' && e.match(/^at/),
|
||||
grouper: (list, key) => <Sub key={key} msg={list} />,
|
||||
})
|
||||
.reduce(createSubgroup, {
|
||||
startTrigger: e => typeof e === 'string' && e.match(/at(.|\n)+\d+:\d+\)/),
|
||||
endTrigger: () => false,
|
||||
grouper: (list, key) => <StackTrace key={key} trace={list} />,
|
||||
});
|
||||
|
||||
return <Pre>{data}</Pre>;
|
||||
};
|
||||
Message.propTypes = {
|
||||
msg: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
const Head = styled.header({
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'flex-start',
|
||||
});
|
||||
|
||||
const Title = styled.h3({
|
||||
padding: '10px 10px 0 10px',
|
||||
margin: 0,
|
||||
});
|
||||
|
||||
export const FailedResult = styled(({ fullName, title, status, failureMessages, className }) => (
|
||||
<div className={className}>
|
||||
<Head>
|
||||
<FlexContainer>
|
||||
<Indicator
|
||||
color={colors.error}
|
||||
size={10}
|
||||
overrides={{ borderRadius: '5px 0', position: 'absolute', top: -1, left: -1 }}
|
||||
/>
|
||||
<Title>{fullName || title}</Title>
|
||||
</FlexContainer>
|
||||
<Indicator
|
||||
color={colors.error}
|
||||
size={16}
|
||||
overrides={{ borderRadius: '0 5px', position: 'absolute', top: -1, right: -1 }}
|
||||
>
|
||||
{status}
|
||||
</Indicator>
|
||||
</Head>
|
||||
{/* eslint-disable react/no-array-index-key */}
|
||||
{failureMessages.map((msg, i) => (
|
||||
<Message msg={msg} key={i} />
|
||||
))}
|
||||
</div>
|
||||
))({
|
||||
display: 'block',
|
||||
borderRadius: 5,
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
position: 'relative',
|
||||
border: '1px solid silver',
|
||||
boxSizing: 'border-box',
|
||||
});
|
||||
|
||||
const Result = ({ fullName, title, status }) => (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
}}
|
||||
>
|
||||
<FlexContainer>
|
||||
<Indicator color={colors.success} size={10} overrides={{ marginRight: 10 }} />
|
||||
<div>{fullName || title}</div>
|
||||
</FlexContainer>
|
||||
<FlexContainer>
|
||||
<Indicator color={colors.success} size={14} right>
|
||||
{status}
|
||||
</Indicator>
|
||||
</FlexContainer>
|
||||
</div>
|
||||
);
|
||||
|
||||
Result.defaultProps = {
|
||||
fullName: '',
|
||||
title: '',
|
||||
};
|
||||
|
||||
Result.propTypes = {
|
||||
fullName: PropTypes.string,
|
||||
title: PropTypes.string,
|
||||
status: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default Result;
|
88
addons/jest/src/components/Result.tsx
Normal file
88
addons/jest/src/components/Result.tsx
Normal file
@ -0,0 +1,88 @@
|
||||
import React from 'react';
|
||||
|
||||
import { styled } from '@storybook/theming';
|
||||
|
||||
import Message from './Message';
|
||||
import Indicator from './Indicator';
|
||||
import colors from '../colors';
|
||||
|
||||
const FlexContainer = styled.div({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
});
|
||||
|
||||
const Head = styled.header({
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'flex-start',
|
||||
});
|
||||
|
||||
const Title = styled.h3({
|
||||
padding: '10px 10px 0 10px',
|
||||
margin: 0,
|
||||
});
|
||||
|
||||
export const FailedResult = styled(({ fullName, title, status, failureMessages, className }) => (
|
||||
<div className={className}>
|
||||
<Head>
|
||||
<FlexContainer>
|
||||
<Indicator
|
||||
color={colors.error}
|
||||
size={10}
|
||||
overrides={{ borderRadius: '5px 0', position: 'absolute', top: -1, left: -1 }}
|
||||
/>
|
||||
<Title>{fullName || title}</Title>
|
||||
</FlexContainer>
|
||||
<Indicator
|
||||
color={colors.error}
|
||||
size={16}
|
||||
overrides={{ borderRadius: '0 5px', position: 'absolute', top: -1, right: -1 }}
|
||||
>
|
||||
{status}
|
||||
</Indicator>
|
||||
</Head>
|
||||
{failureMessages.map((msg: string, i: number) => (
|
||||
<Message msg={msg} key={i} />
|
||||
))}
|
||||
</div>
|
||||
))({
|
||||
display: 'block',
|
||||
borderRadius: 5,
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
position: 'relative',
|
||||
border: '1px solid silver',
|
||||
boxSizing: 'border-box',
|
||||
});
|
||||
|
||||
interface ResultProps {
|
||||
fullName?: string;
|
||||
title?: string;
|
||||
status: string;
|
||||
}
|
||||
const Result = ({ fullName, title, status }: ResultProps) => (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
}}
|
||||
>
|
||||
<FlexContainer>
|
||||
<Indicator color={colors.success} size={10} overrides={{ marginRight: 10 }} />
|
||||
<div>{fullName || title}</div>
|
||||
</FlexContainer>
|
||||
<FlexContainer>
|
||||
<Indicator color={colors.success} size={14} right>
|
||||
{status}
|
||||
</Indicator>
|
||||
</FlexContainer>
|
||||
</div>
|
||||
);
|
||||
|
||||
Result.defaultProps = {
|
||||
fullName: '',
|
||||
title: '',
|
||||
};
|
||||
|
||||
export default Result;
|
@ -1,59 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { STORY_CHANGED } from '@storybook/core-events';
|
||||
import { ADD_TESTS } from '../shared';
|
||||
|
||||
const provideTests = Component =>
|
||||
class TestProvider extends React.Component {
|
||||
static propTypes = {
|
||||
channel: PropTypes.shape({
|
||||
on: PropTypes.func,
|
||||
removeListener: PropTypes.func,
|
||||
}).isRequired,
|
||||
api: PropTypes.shape({
|
||||
on: PropTypes.func,
|
||||
}).isRequired,
|
||||
active: PropTypes.bool,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
active: false,
|
||||
};
|
||||
|
||||
state = {};
|
||||
|
||||
componentDidMount() {
|
||||
this.mounted = true;
|
||||
const { channel, api } = this.props;
|
||||
|
||||
this.stopListeningOnStory = api.on(STORY_CHANGED, () => {
|
||||
const { kind, storyName, tests } = this.state;
|
||||
if (this.mounted && (kind || storyName || tests)) {
|
||||
this.onAddTests({});
|
||||
}
|
||||
});
|
||||
|
||||
channel.on(ADD_TESTS, this.onAddTests);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.mounted = false;
|
||||
const { channel } = this.props;
|
||||
|
||||
this.stopListeningOnStory();
|
||||
channel.removeListener(ADD_TESTS, this.onAddTests);
|
||||
}
|
||||
|
||||
onAddTests = ({ kind, storyName, tests }) => {
|
||||
this.setState({ kind, storyName, tests });
|
||||
};
|
||||
|
||||
render() {
|
||||
const { active } = this.props;
|
||||
const { tests } = this.state;
|
||||
|
||||
return active && tests ? <Component {...this.state} /> : null;
|
||||
}
|
||||
};
|
||||
|
||||
export default provideTests;
|
82
addons/jest/src/hoc/provideJestResult.tsx
Normal file
82
addons/jest/src/hoc/provideJestResult.tsx
Normal file
@ -0,0 +1,82 @@
|
||||
import React from 'react';
|
||||
import { STORY_CHANGED } from '@storybook/core-events';
|
||||
import { ADD_TESTS } from '../shared';
|
||||
import { API } from '@storybook/api';
|
||||
|
||||
// TODO: import type from @types/jest
|
||||
interface AssertionResult {
|
||||
status: string;
|
||||
fullName: string;
|
||||
title: string;
|
||||
failureMessages: string[];
|
||||
}
|
||||
|
||||
export interface Test {
|
||||
name: string;
|
||||
result: {
|
||||
status: string;
|
||||
assertionResults: AssertionResult[];
|
||||
};
|
||||
}
|
||||
|
||||
interface InjectedProps {
|
||||
tests?: Test[];
|
||||
}
|
||||
|
||||
export interface HocProps {
|
||||
api: API;
|
||||
active?: boolean;
|
||||
}
|
||||
|
||||
export interface HocState {
|
||||
kind?: string;
|
||||
storyName?: string;
|
||||
tests?: Test[];
|
||||
}
|
||||
|
||||
const provideTests = (Component: React.ComponentType<InjectedProps>) =>
|
||||
class TestProvider extends React.Component<HocProps, HocState> {
|
||||
static defaultProps = {
|
||||
active: false,
|
||||
};
|
||||
|
||||
mounted: boolean;
|
||||
stopListeningOnStory: () => void;
|
||||
|
||||
state: HocState = {};
|
||||
|
||||
componentDidMount() {
|
||||
this.mounted = true;
|
||||
const { api } = this.props;
|
||||
|
||||
this.stopListeningOnStory = api.on(STORY_CHANGED, () => {
|
||||
const { kind, storyName, tests } = this.state;
|
||||
if (this.mounted && (kind || storyName || tests)) {
|
||||
this.onAddTests({});
|
||||
}
|
||||
});
|
||||
|
||||
api.on(ADD_TESTS, this.onAddTests);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.mounted = false;
|
||||
const { api } = this.props;
|
||||
|
||||
this.stopListeningOnStory();
|
||||
api.removeListener(ADD_TESTS, this.onAddTests);
|
||||
}
|
||||
|
||||
onAddTests = ({ kind, storyName, tests }: HocState) => {
|
||||
this.setState({ kind, storyName, tests });
|
||||
};
|
||||
|
||||
render() {
|
||||
const { active } = this.props;
|
||||
const { tests } = this.state;
|
||||
|
||||
return active && tests ? <Component tests={tests} /> : null;
|
||||
}
|
||||
};
|
||||
|
||||
export default provideTests;
|
@ -3,7 +3,11 @@ import deprecate from 'util-deprecate';
|
||||
import { normalize } from 'upath';
|
||||
import { ADD_TESTS } from './shared';
|
||||
|
||||
const findTestResults = (testFiles, jestTestResults, jestTestFilesExt) =>
|
||||
const findTestResults = (
|
||||
testFiles: string[],
|
||||
jestTestResults: { testResults: Array<{ name: string }> },
|
||||
jestTestFilesExt: string
|
||||
) =>
|
||||
Object.values(testFiles).map(name => {
|
||||
const fileName = `${name}${jestTestFilesExt}`;
|
||||
|
||||
@ -14,7 +18,7 @@ const findTestResults = (testFiles, jestTestResults, jestTestFilesExt) =>
|
||||
fileName,
|
||||
name,
|
||||
result: jestTestResults.testResults.find(test =>
|
||||
normalize(test.name).match(fileNamePattern)
|
||||
Boolean(normalize(test.name).match(fileNamePattern))
|
||||
),
|
||||
};
|
||||
}
|
||||
@ -22,7 +26,17 @@ const findTestResults = (testFiles, jestTestResults, jestTestFilesExt) =>
|
||||
return { fileName, name };
|
||||
});
|
||||
|
||||
const emitAddTests = ({ kind, story, testFiles, options }) => {
|
||||
const emitAddTests = ({
|
||||
kind,
|
||||
story,
|
||||
testFiles,
|
||||
options,
|
||||
}: {
|
||||
kind: string;
|
||||
story: () => void;
|
||||
testFiles: any;
|
||||
options: { results: { testResults: Array<{ name: string }> }; filesExt: string };
|
||||
}) => {
|
||||
addons.getChannel().emit(ADD_TESTS, {
|
||||
kind,
|
||||
storyName: story,
|
||||
@ -30,15 +44,16 @@ const emitAddTests = ({ kind, story, testFiles, options }) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const withTests = userOptions => {
|
||||
export const withTests = (userOptions: { results: any; filesExt: string }) => {
|
||||
const defaultOptions = {
|
||||
filesExt: '((\\.specs?)|(\\.tests?))?(\\.js)?$',
|
||||
};
|
||||
const options = Object.assign({}, defaultOptions, userOptions);
|
||||
const options = { ...defaultOptions, ...userOptions };
|
||||
|
||||
return (...args) => {
|
||||
return (...args: [(string | (() => void)), { kind: string; parameters: { jest?: any } }]) => {
|
||||
if (typeof args[0] === 'string') {
|
||||
return deprecate((storyFn, { kind }) => {
|
||||
// tslint:disable-next-line:no-shadowed-variable
|
||||
return deprecate((storyFn: () => void, { kind }: { kind: string }) => {
|
||||
emitAddTests({ kind, story: storyFn, testFiles: args, options });
|
||||
|
||||
return storyFn();
|
@ -2,15 +2,11 @@ import React from 'react';
|
||||
import addons from '@storybook/addons';
|
||||
import { ADDON_ID, PANEL_ID } from './shared';
|
||||
|
||||
// import PanelTitle from './components/PanelTitle';
|
||||
import Panel from './components/Panel';
|
||||
|
||||
addons.register(ADDON_ID, api => {
|
||||
const channel = addons.getChannel();
|
||||
|
||||
addons.addPanel(PANEL_ID, {
|
||||
title: 'tests',
|
||||
// eslint-disable-next-line react/prop-types
|
||||
render: ({ active, key }) => <Panel key={key} channel={channel} api={api} active={active} />,
|
||||
render: ({ active, key }) => <Panel key={key} api={api} active={active} />,
|
||||
});
|
||||
});
|
@ -1,573 +0,0 @@
|
||||
import { document } from 'global';
|
||||
|
||||
const styles = `
|
||||
@font-face {
|
||||
font-family: octicons-link;
|
||||
src: url(data:font/woff;charset=utf-8;base64,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) format('woff');
|
||||
}
|
||||
.markdown-body {
|
||||
-ms-text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
line-height: 1.5;
|
||||
color: #333;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
.markdown-body .pl-c {
|
||||
color: #969896;
|
||||
}
|
||||
.markdown-body .pl-c1,
|
||||
.markdown-body .pl-s .pl-v {
|
||||
color: #0086b3;
|
||||
}
|
||||
.markdown-body .pl-e,
|
||||
.markdown-body .pl-en {
|
||||
color: #795da3;
|
||||
}
|
||||
.markdown-body .pl-smi,
|
||||
.markdown-body .pl-s .pl-s1 {
|
||||
color: #333;
|
||||
}
|
||||
.markdown-body .pl-ent {
|
||||
color: #63a35c;
|
||||
}
|
||||
.markdown-body .pl-k {
|
||||
color: #a71d5d;
|
||||
}
|
||||
.markdown-body .pl-s,
|
||||
.markdown-body .pl-pds,
|
||||
.markdown-body .pl-s .pl-pse .pl-s1,
|
||||
.markdown-body .pl-sr,
|
||||
.markdown-body .pl-sr .pl-cce,
|
||||
.markdown-body .pl-sr .pl-sre,
|
||||
.markdown-body .pl-sr .pl-sra {
|
||||
color: #183691;
|
||||
}
|
||||
.markdown-body .pl-v {
|
||||
color: #ed6a43;
|
||||
}
|
||||
.markdown-body .pl-id {
|
||||
color: #b52a1d;
|
||||
}
|
||||
.markdown-body .pl-ii {
|
||||
color: #f8f8f8;
|
||||
background-color: #b52a1d;
|
||||
}
|
||||
.markdown-body .pl-sr .pl-cce {
|
||||
font-weight: bold;
|
||||
color: #63a35c;
|
||||
}
|
||||
.markdown-body .pl-ml {
|
||||
color: #693a17;
|
||||
}
|
||||
.markdown-body .pl-mh,
|
||||
.markdown-body .pl-mh .pl-en,
|
||||
.markdown-body .pl-ms {
|
||||
font-weight: bold;
|
||||
color: #1d3e81;
|
||||
}
|
||||
.markdown-body .pl-mq {
|
||||
color: #008080;
|
||||
}
|
||||
.markdown-body .pl-mi {
|
||||
font-style: italic;
|
||||
color: #333;
|
||||
}
|
||||
.markdown-body .pl-mb {
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
.markdown-body .pl-md {
|
||||
color: #bd2c00;
|
||||
background-color: #ffecec;
|
||||
}
|
||||
.markdown-body .pl-mi1 {
|
||||
color: #55a532;
|
||||
background-color: #eaffea;
|
||||
}
|
||||
.markdown-body .pl-mdr {
|
||||
font-weight: bold;
|
||||
color: #795da3;
|
||||
}
|
||||
.markdown-body .pl-mo {
|
||||
color: #1d3e81;
|
||||
}
|
||||
.markdown-body .octicon {
|
||||
display: inline-block;
|
||||
vertical-align: text-top;
|
||||
fill: currentColor;
|
||||
}
|
||||
.markdown-body a {
|
||||
background-color: transparent;
|
||||
-webkit-text-decoration-skip: objects;
|
||||
}
|
||||
.markdown-body a:active,
|
||||
.markdown-body a:hover {
|
||||
outline-width: 0;
|
||||
}
|
||||
.markdown-body strong {
|
||||
font-weight: inherit;
|
||||
}
|
||||
.markdown-body strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
.markdown-body h1 {
|
||||
font-size: 2em;
|
||||
margin: 0.67em 0;
|
||||
}
|
||||
.markdown-body img {
|
||||
border-style: none;
|
||||
}
|
||||
.markdown-body svg:not(:root) {
|
||||
overflow: hidden;
|
||||
}
|
||||
.markdown-body code,
|
||||
.markdown-body kbd,
|
||||
.markdown-body pre {
|
||||
font-family: monospace, monospace;
|
||||
font-size: 1em;
|
||||
}
|
||||
.markdown-body hr {
|
||||
box-sizing: content-box;
|
||||
height: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
.markdown-body input {
|
||||
font: inherit;
|
||||
margin: 0;
|
||||
}
|
||||
.markdown-body input {
|
||||
overflow: visible;
|
||||
}
|
||||
.markdown-body [type="checkbox"] {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
}
|
||||
.markdown-body * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.markdown-body input {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
.markdown-body a {
|
||||
color: #4078c0;
|
||||
text-decoration: none;
|
||||
}
|
||||
.markdown-body a:hover,
|
||||
.markdown-body a:active {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.markdown-body strong {
|
||||
font-weight: 600;
|
||||
}
|
||||
.markdown-body hr {
|
||||
height: 0;
|
||||
margin: 15px 0;
|
||||
overflow: hidden;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
.markdown-body hr::before {
|
||||
display: table;
|
||||
content: "";
|
||||
}
|
||||
.markdown-body hr::after {
|
||||
display: table;
|
||||
clear: both;
|
||||
content: "";
|
||||
}
|
||||
.markdown-body table {
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.markdown-body td,
|
||||
.markdown-body th {
|
||||
padding: 0;
|
||||
}
|
||||
.markdown-body h1,
|
||||
.markdown-body h2,
|
||||
.markdown-body h3,
|
||||
.markdown-body h4,
|
||||
.markdown-body h5,
|
||||
.markdown-body h6 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.markdown-body h1 {
|
||||
font-size: 32px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.markdown-body h2 {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.markdown-body h3 {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.markdown-body h4 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.markdown-body h5 {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.markdown-body h6 {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.markdown-body p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.markdown-body blockquote {
|
||||
margin: 0;
|
||||
}
|
||||
.markdown-body ul,
|
||||
.markdown-body ol {
|
||||
padding-left: 0;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.markdown-body ol ol,
|
||||
.markdown-body ul ol {
|
||||
list-style-type: lower-roman;
|
||||
}
|
||||
.markdown-body ul ul ol,
|
||||
.markdown-body ul ol ol,
|
||||
.markdown-body ol ul ol,
|
||||
.markdown-body ol ol ol {
|
||||
list-style-type: lower-alpha;
|
||||
}
|
||||
.markdown-body dd {
|
||||
margin-left: 0;
|
||||
}
|
||||
.markdown-body code {
|
||||
font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
.markdown-body pre {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
font: 12px Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||
}
|
||||
.markdown-body .octicon {
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
.markdown-body input {
|
||||
-webkit-font-feature-settings: "liga" 0;
|
||||
font-feature-settings: "liga" 0;
|
||||
}
|
||||
.markdown-body::before {
|
||||
display: table;
|
||||
content: "";
|
||||
}
|
||||
.markdown-body::after {
|
||||
display: table;
|
||||
clear: both;
|
||||
content: "";
|
||||
}
|
||||
.markdown-body>*:first-child {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
.markdown-body>*:last-child {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
.markdown-body a:not([href]) {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
.markdown-body .anchor {
|
||||
float: left;
|
||||
padding-right: 4px;
|
||||
margin-left: -20px;
|
||||
line-height: 1;
|
||||
}
|
||||
.markdown-body .anchor:focus {
|
||||
outline: none;
|
||||
}
|
||||
.markdown-body p,
|
||||
.markdown-body blockquote,
|
||||
.markdown-body ul,
|
||||
.markdown-body ol,
|
||||
.markdown-body dl,
|
||||
.markdown-body table,
|
||||
.markdown-body pre {
|
||||
margin-top: 0;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.markdown-body hr {
|
||||
height: 0.25em;
|
||||
padding: 0;
|
||||
margin: 24px 0;
|
||||
background-color: #e7e7e7;
|
||||
border: 0;
|
||||
}
|
||||
.markdown-body blockquote {
|
||||
padding: 0 1em;
|
||||
color: #777;
|
||||
border-left: 0.25em solid #ddd;
|
||||
}
|
||||
.markdown-body blockquote>:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
.markdown-body blockquote>:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.markdown-body kbd {
|
||||
display: inline-block;
|
||||
padding: 3px 5px;
|
||||
font-size: 11px;
|
||||
line-height: 10px;
|
||||
color: #555;
|
||||
vertical-align: middle;
|
||||
background-color: #fcfcfc;
|
||||
border: solid 1px #ccc;
|
||||
border-bottom-color: #bbb;
|
||||
border-radius: 3px;
|
||||
box-shadow: inset 0 -1px 0 #bbb;
|
||||
}
|
||||
.markdown-body h1,
|
||||
.markdown-body h2,
|
||||
.markdown-body h3,
|
||||
.markdown-body h4,
|
||||
.markdown-body h5,
|
||||
.markdown-body h6 {
|
||||
margin-top: 24px;
|
||||
margin-bottom: 16px;
|
||||
font-weight: 600;
|
||||
line-height: 1.25;
|
||||
}
|
||||
.markdown-body h1 .octicon-link,
|
||||
.markdown-body h2 .octicon-link,
|
||||
.markdown-body h3 .octicon-link,
|
||||
.markdown-body h4 .octicon-link,
|
||||
.markdown-body h5 .octicon-link,
|
||||
.markdown-body h6 .octicon-link {
|
||||
color: #000;
|
||||
vertical-align: middle;
|
||||
visibility: hidden;
|
||||
}
|
||||
.markdown-body h1:hover .anchor,
|
||||
.markdown-body h2:hover .anchor,
|
||||
.markdown-body h3:hover .anchor,
|
||||
.markdown-body h4:hover .anchor,
|
||||
.markdown-body h5:hover .anchor,
|
||||
.markdown-body h6:hover .anchor {
|
||||
text-decoration: none;
|
||||
}
|
||||
.markdown-body h1:hover .anchor .octicon-link,
|
||||
.markdown-body h2:hover .anchor .octicon-link,
|
||||
.markdown-body h3:hover .anchor .octicon-link,
|
||||
.markdown-body h4:hover .anchor .octicon-link,
|
||||
.markdown-body h5:hover .anchor .octicon-link,
|
||||
.markdown-body h6:hover .anchor .octicon-link {
|
||||
visibility: visible;
|
||||
}
|
||||
.markdown-body h1 {
|
||||
padding-bottom: 0.3em;
|
||||
font-size: 2em;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
.markdown-body h2 {
|
||||
padding-bottom: 0.3em;
|
||||
font-size: 1.5em;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
.markdown-body h3 {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
.markdown-body h4 {
|
||||
font-size: 1em;
|
||||
}
|
||||
.markdown-body h5 {
|
||||
font-size: 0.875em;
|
||||
}
|
||||
.markdown-body h6 {
|
||||
font-size: 0.85em;
|
||||
color: #777;
|
||||
}
|
||||
.markdown-body ul,
|
||||
.markdown-body ol {
|
||||
padding-left: 2em;
|
||||
}
|
||||
.markdown-body ul ul,
|
||||
.markdown-body ul ol,
|
||||
.markdown-body ol ol,
|
||||
.markdown-body ol ul {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.markdown-body li>p {
|
||||
margin-top: 16px;
|
||||
}
|
||||
.markdown-body li+li {
|
||||
margin-top: 0.25em;
|
||||
}
|
||||
.markdown-body dl {
|
||||
padding: 0;
|
||||
}
|
||||
.markdown-body dl dt {
|
||||
padding: 0;
|
||||
margin-top: 16px;
|
||||
font-size: 1em;
|
||||
font-style: italic;
|
||||
font-weight: bold;
|
||||
}
|
||||
.markdown-body dl dd {
|
||||
padding: 0 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.markdown-body table {
|
||||
display: block;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
.markdown-body table th {
|
||||
font-weight: bold;
|
||||
}
|
||||
.markdown-body table th,
|
||||
.markdown-body table td {
|
||||
padding: 6px 13px;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
.markdown-body table tr {
|
||||
background-color: #fff;
|
||||
border-top: 1px solid #ccc;
|
||||
}
|
||||
.markdown-body table tr:nth-child(2n) {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
.markdown-body img {
|
||||
max-width: 100%;
|
||||
box-sizing: content-box;
|
||||
background-color: #fff;
|
||||
}
|
||||
.markdown-body code {
|
||||
padding: 0;
|
||||
padding-top: 0.2em;
|
||||
padding-bottom: 0.2em;
|
||||
margin: 0;
|
||||
font-size: 85%;
|
||||
background-color: rgba(0,0,0,0.04);
|
||||
border-radius: 3px;
|
||||
}
|
||||
.markdown-body code::before,
|
||||
.markdown-body code::after {
|
||||
letter-spacing: -0.2em;
|
||||
content: " ";
|
||||
}
|
||||
.markdown-body pre {
|
||||
word-wrap: normal;
|
||||
}
|
||||
.markdown-body pre>code {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-size: 100%;
|
||||
word-break: normal;
|
||||
white-space: pre;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
}
|
||||
.markdown-body .highlight {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.markdown-body .highlight pre {
|
||||
margin-bottom: 0;
|
||||
word-break: normal;
|
||||
}
|
||||
.markdown-body .highlight pre,
|
||||
.markdown-body pre {
|
||||
padding: 16px;
|
||||
overflow: auto;
|
||||
font-size: 85%;
|
||||
line-height: 1.45;
|
||||
background-color: #f7f7f7;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.markdown-body pre code {
|
||||
display: inline;
|
||||
max-width: auto;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
overflow: visible;
|
||||
line-height: inherit;
|
||||
word-wrap: normal;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
}
|
||||
.markdown-body pre code::before,
|
||||
.markdown-body pre code::after {
|
||||
content: normal;
|
||||
}
|
||||
.markdown-body .pl-0 {
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
.markdown-body .pl-1 {
|
||||
padding-left: 3px !important;
|
||||
}
|
||||
.markdown-body .pl-2 {
|
||||
padding-left: 6px !important;
|
||||
}
|
||||
.markdown-body .pl-3 {
|
||||
padding-left: 12px !important;
|
||||
}
|
||||
.markdown-body .pl-4 {
|
||||
padding-left: 24px !important;
|
||||
}
|
||||
.markdown-body .pl-5 {
|
||||
padding-left: 36px !important;
|
||||
}
|
||||
.markdown-body .pl-6 {
|
||||
padding-left: 48px !important;
|
||||
}
|
||||
.markdown-body .full-commit .btn-outline:not(:disabled):hover {
|
||||
color: #4078c0;
|
||||
border: 1px solid #4078c0;
|
||||
}
|
||||
.markdown-body kbd {
|
||||
display: inline-block;
|
||||
padding: 3px 5px;
|
||||
font: 11px Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||
line-height: 10px;
|
||||
color: #555;
|
||||
vertical-align: middle;
|
||||
background-color: #fcfcfc;
|
||||
border: solid 1px #ccc;
|
||||
border-bottom-color: #bbb;
|
||||
border-radius: 3px;
|
||||
box-shadow: inset 0 -1px 0 #bbb;
|
||||
}
|
||||
.markdown-body :checked+.radio-label {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
border-color: #4078c0;
|
||||
}
|
||||
.markdown-body .task-list-item {
|
||||
list-style-type: none;
|
||||
}
|
||||
.markdown-body .task-list-item+.task-list-item {
|
||||
margin-top: 3px;
|
||||
}
|
||||
.markdown-body .task-list-item input {
|
||||
margin: 0 0.2em 0.25em -1.6em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.markdown-body hr {
|
||||
border-bottom-color: #eee;
|
||||
}
|
||||
`;
|
||||
|
||||
if (document && !document.getElementById('github-markdown-css')) {
|
||||
const styleNode = document.createElement('style');
|
||||
styleNode.id = 'github-markdown-css';
|
||||
styleNode.innerHTML = styles;
|
||||
|
||||
document.head.appendChild(styleNode);
|
||||
}
|
4
addons/jest/src/typings.d.ts
vendored
Normal file
4
addons/jest/src/typings.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
// TODO: following packages need definition files or a TS migration
|
||||
|
||||
declare module 'global';
|
||||
declare module '@storybook/components';
|
@ -1 +0,0 @@
|
||||
require('./dist/styles');
|
13
addons/jest/tsconfig.json
Normal file
13
addons/jest/tsconfig.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src",
|
||||
"types": ["webpack-env"]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"src/__tests__/**/*"
|
||||
]
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user